mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -12,69 +12,76 @@
|
||||
* bun scripts/check-bundle-integrity.ts ./dist # 指定目录
|
||||
*/
|
||||
|
||||
import { readdir, readFile } from "fs/promises"
|
||||
import { join, resolve, dirname } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import { readdir, readFile } from 'fs/promises'
|
||||
import { join, resolve, dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
// ─── 从 package.json 读取 dependencies 作为白名单 ────────────────
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const pkg = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8'))
|
||||
const pkg = JSON.parse(
|
||||
await readFile(join(__dirname, '..', 'package.json'), 'utf-8'),
|
||||
)
|
||||
const PKG_DEPS = new Set(Object.keys(pkg.dependencies ?? {}))
|
||||
|
||||
// ─── Node.js 内置模块白名单 ────────────────────────────────────────
|
||||
const NODE_BUILTINS = new Set([
|
||||
"assert",
|
||||
"async_hooks",
|
||||
"buffer",
|
||||
"child_process",
|
||||
"cluster",
|
||||
"console",
|
||||
"constants",
|
||||
"crypto",
|
||||
"dgram",
|
||||
"diagnostics_channel",
|
||||
"dns",
|
||||
"domain",
|
||||
"events",
|
||||
"fs",
|
||||
"fs/promises",
|
||||
"http",
|
||||
"http2",
|
||||
"https",
|
||||
"inspector",
|
||||
"module",
|
||||
"net",
|
||||
"os",
|
||||
"path",
|
||||
"perf_hooks",
|
||||
"process",
|
||||
"punycode",
|
||||
"querystring",
|
||||
"readline",
|
||||
"repl",
|
||||
"stream",
|
||||
"string_decoder",
|
||||
"sys",
|
||||
"timers",
|
||||
"tls",
|
||||
"tty",
|
||||
"url",
|
||||
"util",
|
||||
"v8",
|
||||
"vm",
|
||||
"worker_threads",
|
||||
"zlib",
|
||||
"node:test",
|
||||
'assert',
|
||||
'async_hooks',
|
||||
'buffer',
|
||||
'child_process',
|
||||
'cluster',
|
||||
'console',
|
||||
'constants',
|
||||
'crypto',
|
||||
'dgram',
|
||||
'diagnostics_channel',
|
||||
'dns',
|
||||
'domain',
|
||||
'events',
|
||||
'fs',
|
||||
'fs/promises',
|
||||
'http',
|
||||
'http2',
|
||||
'https',
|
||||
'inspector',
|
||||
'module',
|
||||
'net',
|
||||
'os',
|
||||
'path',
|
||||
'perf_hooks',
|
||||
'process',
|
||||
'punycode',
|
||||
'querystring',
|
||||
'readline',
|
||||
'repl',
|
||||
'stream',
|
||||
'string_decoder',
|
||||
'sys',
|
||||
'timers',
|
||||
'tls',
|
||||
'tty',
|
||||
'url',
|
||||
'util',
|
||||
'v8',
|
||||
'vm',
|
||||
'worker_threads',
|
||||
'zlib',
|
||||
'node:test',
|
||||
])
|
||||
|
||||
// Node 18+ 内置但不在传统列表中的模块
|
||||
const NODE_18_PLUS_BUILTINS = new Set(["undici"])
|
||||
const NODE_18_PLUS_BUILTINS = new Set(['undici'])
|
||||
|
||||
// Bun 专用模块(仅在 Bun 运行时可用,Node.js 环境会失败)
|
||||
const BUN_MODULES = new Set(["bun", "bun:ffi", "bun:test", "bun:sqlite"])
|
||||
const BUN_MODULES = new Set(['bun', 'bun:ffi', 'bun:test', 'bun:sqlite'])
|
||||
|
||||
// macOS JXA / native 框架(通过 ObjC.import,非真正的 require)
|
||||
const NATIVE_FRAMEWORKS = new Set(["AppKit", "CoreGraphics", "Foundation", "UIKit"])
|
||||
const NATIVE_FRAMEWORKS = new Set([
|
||||
'AppKit',
|
||||
'CoreGraphics',
|
||||
'Foundation',
|
||||
'UIKit',
|
||||
])
|
||||
|
||||
// ─── 模式 ──────────────────────────────────────────────────────────
|
||||
// 匹配 import { ... } from "./chunk-xxxxx.js" 或 import"./chunk-xxxxx.js"
|
||||
@@ -87,8 +94,13 @@ const DYNAMIC_IMPORT_RE = /import\("([^"]+)"\)/g
|
||||
const NODE_REQUIRE_RE = /nodeRequire\("([^"]+)"\)/g
|
||||
|
||||
interface Finding {
|
||||
type: "broken-chunk-ref" | "third-party-require" | "third-party-import" | "third-party-node-require" | "bun-runtime-only"
|
||||
severity: "error" | "warning"
|
||||
type:
|
||||
| 'broken-chunk-ref'
|
||||
| 'third-party-require'
|
||||
| 'third-party-import'
|
||||
| 'third-party-node-require'
|
||||
| 'bun-runtime-only'
|
||||
severity: 'error' | 'warning'
|
||||
file: string
|
||||
line: number
|
||||
module: string
|
||||
@@ -96,17 +108,17 @@ interface Finding {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const distDir = resolve(process.argv[2] || "./dist")
|
||||
const distDir = resolve(process.argv[2] || './dist')
|
||||
|
||||
console.log(`\n🔍 检查构建产物完整性: ${distDir}\n`)
|
||||
|
||||
// 1. 列出所有 chunk 文件
|
||||
let files: string[]
|
||||
try {
|
||||
files = (await readdir(distDir)).filter((f) => f.endsWith(".js"))
|
||||
files = (await readdir(distDir)).filter(f => f.endsWith('.js'))
|
||||
} catch {
|
||||
console.error(`❌ 无法读取目录: ${distDir}`)
|
||||
console.error(" 请先运行 bun run build")
|
||||
console.error(' 请先运行 bun run build')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -118,8 +130,8 @@ async function main() {
|
||||
// 2. 逐文件扫描
|
||||
for (const file of files) {
|
||||
const filePath = join(distDir, file)
|
||||
const content = await readFile(filePath, "utf-8")
|
||||
const lines = content.split("\n")
|
||||
const content = await readFile(filePath, 'utf-8')
|
||||
const lines = content.split('\n')
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
@@ -130,11 +142,11 @@ async function main() {
|
||||
for (const m of staticImportMatches) {
|
||||
const ref = m[1]
|
||||
// 提取文件名部分(去掉 ./)
|
||||
const refFile = ref.replace(/^\.\//, "")
|
||||
const refFile = ref.replace(/^\.\//, '')
|
||||
if (!fileSet.has(refFile)) {
|
||||
findings.push({
|
||||
type: "broken-chunk-ref",
|
||||
severity: "error",
|
||||
type: 'broken-chunk-ref',
|
||||
severity: 'error',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: ref,
|
||||
@@ -149,11 +161,17 @@ async function main() {
|
||||
const mod = m[1]
|
||||
// 跳过 ObjC.import(JXA 语法,不是真正的 require)
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (
|
||||
NODE_BUILTINS.has(mod) ||
|
||||
NODE_18_PLUS_BUILTINS.has(mod) ||
|
||||
PKG_DEPS.has(mod) ||
|
||||
mod.startsWith('node:')
|
||||
)
|
||||
continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
type: 'bun-runtime-only',
|
||||
severity: 'warning',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -163,8 +181,8 @@ async function main() {
|
||||
}
|
||||
// 第三方模块 — 在生产环境(全局 npm install)中找不到
|
||||
findings.push({
|
||||
type: "third-party-require",
|
||||
severity: "error",
|
||||
type: 'third-party-require',
|
||||
severity: 'error',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -177,15 +195,21 @@ async function main() {
|
||||
for (const m of dynImportMatches) {
|
||||
const mod = m[1]
|
||||
// 跳过内部 chunk 引用和相对路径
|
||||
if (mod.startsWith("./") || mod.startsWith("../")) continue
|
||||
if (mod.startsWith('./') || mod.startsWith('../')) continue
|
||||
// 跳过 ObjC.import
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (
|
||||
NODE_BUILTINS.has(mod) ||
|
||||
NODE_18_PLUS_BUILTINS.has(mod) ||
|
||||
PKG_DEPS.has(mod) ||
|
||||
mod.startsWith('node:')
|
||||
)
|
||||
continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
// bun:test 等只在 Bun 运行时可用,Node.js 运行时会失败
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
type: 'bun-runtime-only',
|
||||
severity: 'warning',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -195,8 +219,8 @@ async function main() {
|
||||
}
|
||||
// 第三方动态 import
|
||||
findings.push({
|
||||
type: "third-party-import",
|
||||
severity: "error",
|
||||
type: 'third-party-import',
|
||||
severity: 'error',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -209,11 +233,17 @@ async function main() {
|
||||
for (const m of nodeRequireMatches) {
|
||||
const mod = m[1]
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (
|
||||
NODE_BUILTINS.has(mod) ||
|
||||
NODE_18_PLUS_BUILTINS.has(mod) ||
|
||||
PKG_DEPS.has(mod) ||
|
||||
mod.startsWith('node:')
|
||||
)
|
||||
continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
type: 'bun-runtime-only',
|
||||
severity: 'warning',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -222,8 +252,8 @@ async function main() {
|
||||
continue
|
||||
}
|
||||
findings.push({
|
||||
type: "third-party-node-require",
|
||||
severity: "error",
|
||||
type: 'third-party-node-require',
|
||||
severity: 'error',
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
@@ -234,18 +264,22 @@ async function main() {
|
||||
}
|
||||
|
||||
// 3. 汇总报告
|
||||
const errors = findings.filter((f) => f.severity === "error")
|
||||
const warnings = findings.filter((f) => f.severity === "warning")
|
||||
const errors = findings.filter(f => f.severity === 'error')
|
||||
const warnings = findings.filter(f => f.severity === 'warning')
|
||||
|
||||
// 按 type 分组
|
||||
const brokenRefs = errors.filter((f) => f.type === "broken-chunk-ref")
|
||||
const thirdPartyRequires = errors.filter((f) => f.type === "third-party-require")
|
||||
const thirdPartyImports = errors.filter((f) => f.type === "third-party-import")
|
||||
const thirdPartyNodeRequires = errors.filter((f) => f.type === "third-party-node-require")
|
||||
const bunRuntimeOnly = warnings.filter((f) => f.type === "bun-runtime-only")
|
||||
const brokenRefs = errors.filter(f => f.type === 'broken-chunk-ref')
|
||||
const thirdPartyRequires = errors.filter(
|
||||
f => f.type === 'third-party-require',
|
||||
)
|
||||
const thirdPartyImports = errors.filter(f => f.type === 'third-party-import')
|
||||
const thirdPartyNodeRequires = errors.filter(
|
||||
f => f.type === 'third-party-node-require',
|
||||
)
|
||||
const bunRuntimeOnly = warnings.filter(f => f.type === 'bun-runtime-only')
|
||||
|
||||
if (brokenRefs.length > 0) {
|
||||
console.log("❌ 断裂的 chunk 引用(引用了不存在的文件):")
|
||||
console.log('❌ 断裂的 chunk 引用(引用了不存在的文件):')
|
||||
for (const f of brokenRefs) {
|
||||
console.log(` ${f.file}:${f.line} → ${f.module}`)
|
||||
}
|
||||
@@ -253,7 +287,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (thirdPartyRequires.length > 0) {
|
||||
console.log("❌ 通过 __require() 引用的第三方模块(生产环境会找不到):")
|
||||
console.log('❌ 通过 __require() 引用的第三方模块(生产环境会找不到):')
|
||||
const grouped = groupByModule(thirdPartyRequires)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
@@ -266,7 +300,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (thirdPartyImports.length > 0) {
|
||||
console.log("❌ 通过 import() 动态引用的第三方模块(生产环境会找不到):")
|
||||
console.log('❌ 通过 import() 动态引用的第三方模块(生产环境会找不到):')
|
||||
const grouped = groupByModule(thirdPartyImports)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
@@ -279,7 +313,9 @@ async function main() {
|
||||
}
|
||||
|
||||
if (thirdPartyNodeRequires.length > 0) {
|
||||
console.log("❌ 通过 nodeRequire() 引用的第三方模块(绕过打包,生产环境会找不到):")
|
||||
console.log(
|
||||
'❌ 通过 nodeRequire() 引用的第三方模块(绕过打包,生产环境会找不到):',
|
||||
)
|
||||
const grouped = groupByModule(thirdPartyNodeRequires)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
@@ -292,7 +328,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (bunRuntimeOnly.length > 0) {
|
||||
console.log("⚠️ Bun 运行时专用模块(Node.js 环境会失败):")
|
||||
console.log('⚠️ Bun 运行时专用模块(Node.js 环境会失败):')
|
||||
const grouped = groupByModule(bunRuntimeOnly)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次`)
|
||||
@@ -301,9 +337,9 @@ async function main() {
|
||||
}
|
||||
|
||||
// 4. 总结
|
||||
console.log("─".repeat(50))
|
||||
console.log('─'.repeat(50))
|
||||
if (errors.length === 0 && warnings.length === 0) {
|
||||
console.log("✅ 构建产物完整性检查通过,未发现问题。")
|
||||
console.log('✅ 构建产物完整性检查通过,未发现问题。')
|
||||
} else {
|
||||
console.log(`📊 总计: ${errors.length} 个错误, ${warnings.length} 个警告`)
|
||||
if (errors.length > 0) {
|
||||
@@ -330,7 +366,7 @@ function groupByModule(items: Finding[]): Map<string, Finding[]> {
|
||||
return new Map([...map.entries()].sort((a, b) => b[1].length - a[1].length))
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("Fatal error:", err)
|
||||
main().catch(err => {
|
||||
console.error('Fatal error:', err)
|
||||
process.exit(2)
|
||||
})
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
* corresponding MACRO.* identifier at transpile / bundle time.
|
||||
*/
|
||||
export function getMacroDefines(): Record<string, string> {
|
||||
return {
|
||||
"MACRO.VERSION": JSON.stringify("2.1.888"),
|
||||
"MACRO.BUILD_TIME": JSON.stringify(new Date().toISOString()),
|
||||
"MACRO.FEEDBACK_CHANNEL": JSON.stringify(""),
|
||||
"MACRO.ISSUES_EXPLAINER": JSON.stringify(""),
|
||||
"MACRO.NATIVE_PACKAGE_URL": JSON.stringify(""),
|
||||
"MACRO.PACKAGE_URL": JSON.stringify(""),
|
||||
"MACRO.VERSION_CHANGELOG": JSON.stringify(""),
|
||||
};
|
||||
return {
|
||||
'MACRO.VERSION': JSON.stringify('2.1.888'),
|
||||
'MACRO.BUILD_TIME': JSON.stringify(new Date().toISOString()),
|
||||
'MACRO.FEEDBACK_CHANNEL': JSON.stringify(''),
|
||||
'MACRO.ISSUES_EXPLAINER': JSON.stringify(''),
|
||||
'MACRO.NATIVE_PACKAGE_URL': JSON.stringify(''),
|
||||
'MACRO.PACKAGE_URL': JSON.stringify(''),
|
||||
'MACRO.VERSION_CHANGELOG': JSON.stringify(''),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,59 +27,59 @@ export function getMacroDefines(): Record<string, string> {
|
||||
* - scripts/dev.ts (bun run dev)
|
||||
*/
|
||||
export const DEFAULT_BUILD_FEATURES = [
|
||||
'BUDDY', // 陪伴宠物角色(Squirtle Waddles)
|
||||
'TRANSCRIPT_CLASSIFIER', // 对话分类器,用于标注会话类型
|
||||
'BRIDGE_MODE', // Remote Control / Bridge 模式,远程控制会话
|
||||
'AGENT_TRIGGERS_REMOTE', // sessionIngress 模块级 Map 累积(非 GB 级主因)
|
||||
'CHICAGO_MCP', // Chicago MCP 集成(内部代号)
|
||||
'VOICE_MODE', // Push-to-Talk 语音输入模式
|
||||
'SHOT_STATS', // 单次请求统计信息收集
|
||||
'PROMPT_CACHE_BREAK_DETECTION', // 检测 prompt cache 是否被打破(有 10 条上限,可控)
|
||||
'TOKEN_BUDGET', // Token 预算管理与控制
|
||||
// P0: local features
|
||||
'AGENT_TRIGGERS', // 本地 Agent 触发器(工具调用时启动子代理)
|
||||
'ULTRATHINK', // 超深度思考模式,增加推理链长度
|
||||
'BUILTIN_EXPLORE_PLAN_AGENTS', // 内置 Explore/Plan 子代理类型
|
||||
'LODESTONE', // 上下文锚点,优化长对话的相关性检索
|
||||
'EXTRACT_MEMORIES', // 每次 turn 结束 fork 完整消息历史(非 GB 级主因)
|
||||
'VERIFICATION_AGENT', // 任务完成后 fork 完整消息(非 GB 级主因)
|
||||
'KAIROS_BRIEF', // Kairos 定时摘要(定时汇报当前状态)
|
||||
'AWAY_SUMMARY', // 离线摘要(用户离开后生成总结)
|
||||
'ULTRAPLAN', // 超级规划模式,深度分析后生成实施计划
|
||||
'DAEMON', // 守护进程模式,长驻 supervisor 管理后台 worker(非 GB 级主因)
|
||||
'ACP', // ACP 代理协议,支持外部 agent 接入
|
||||
'WORKFLOW_SCRIPTS', // 工作流脚本(.claude/workflows/ 中的 YAML/MD)
|
||||
'HISTORY_SNIP', // 历史消息裁剪,压缩上下文窗口
|
||||
'CONTEXT_COLLAPSE', // 上下文折叠,自动压缩旧消息
|
||||
'MONITOR_TOOL', // Monitor 工具,流式监控后台进程输出
|
||||
// 'FORK_SUBAGENT', // 已禁用:启用后 prompt 引导模型用 fork(继承父模型)替代 Explore(haiku),导致探索任务使用同等级模型
|
||||
// 'UDS_INBOX', // inbox 数组只增不减(非 GB 级主因)
|
||||
'KAIROS', // Kairos 定时任务系统核心
|
||||
// 'COORDINATOR_MODE', // 已禁用:AgentSummary 30s fork 循环,GB 级泄露主因
|
||||
// 'LAN_PIPES', // 依赖 UDS_INBOX(已随 UDS_INBOX 恢复)
|
||||
'BG_SESSIONS', // 后台会话管理(ps/logs/attach/kill)
|
||||
'TEMPLATES', // 模板任务(new/list/reply 子命令)
|
||||
// 'REVIEW_ARTIFACT', // 代码审查产物(API 请求无响应,待排查 schema 兼容性)
|
||||
// API content block types
|
||||
'CONNECTOR_TEXT', // Connector 文本块类型,扩展 API 内容格式
|
||||
// Attribution tracking
|
||||
'COMMIT_ATTRIBUTION', // Git 提交归属追踪(记录 AI 辅助贡献)
|
||||
// Server mode (claude server / claude open)
|
||||
'DIRECT_CONNECT', // 直连模式(claude server / claude open)
|
||||
// Skill search & learning — feature flags compiled in (so the slash
|
||||
// commands /skill-* etc. exist), but the runtime "enabled" toggle
|
||||
// defaults to OFF (see featureCheck.ts). Operators turn on via the
|
||||
// slash-command toggle or env vars (SKILL_SEARCH_ENABLED=1,
|
||||
// SKILL_LEARNING_ENABLED=1). Rationale: bounded caches added on
|
||||
// this branch (see docs/agent/sur-skill-overflow-bugs.md) close the
|
||||
// overflow risk, but Haiku-on-first-Chinese-query and disk-side
|
||||
// observation accumulation remain operator-discretion concerns.
|
||||
// 'EXPERIMENTAL_SKILL_SEARCH',
|
||||
// 'SKILL_LEARNING',
|
||||
// P3: poor mode
|
||||
'POOR', // 穷鬼模式,跳过 extract_memories/prompt_suggestion 减少消耗
|
||||
// Team Memory
|
||||
// 'TEAMMEM', // 已禁用:依赖 COORDINATOR_MODE,邮箱文件无限增长
|
||||
// SSH Remote
|
||||
'SSH_REMOTE', // SSH 远程连接,本地 REPL + 远端工具执行
|
||||
]as const;
|
||||
'BUDDY', // 陪伴宠物角色(Squirtle Waddles)
|
||||
'TRANSCRIPT_CLASSIFIER', // 对话分类器,用于标注会话类型
|
||||
'BRIDGE_MODE', // Remote Control / Bridge 模式,远程控制会话
|
||||
'AGENT_TRIGGERS_REMOTE', // sessionIngress 模块级 Map 累积(非 GB 级主因)
|
||||
'CHICAGO_MCP', // Chicago MCP 集成(内部代号)
|
||||
'VOICE_MODE', // Push-to-Talk 语音输入模式
|
||||
'SHOT_STATS', // 单次请求统计信息收集
|
||||
'PROMPT_CACHE_BREAK_DETECTION', // 检测 prompt cache 是否被打破(有 10 条上限,可控)
|
||||
'TOKEN_BUDGET', // Token 预算管理与控制
|
||||
// P0: local features
|
||||
'AGENT_TRIGGERS', // 本地 Agent 触发器(工具调用时启动子代理)
|
||||
'ULTRATHINK', // 超深度思考模式,增加推理链长度
|
||||
'BUILTIN_EXPLORE_PLAN_AGENTS', // 内置 Explore/Plan 子代理类型
|
||||
'LODESTONE', // 上下文锚点,优化长对话的相关性检索
|
||||
'EXTRACT_MEMORIES', // 每次 turn 结束 fork 完整消息历史(非 GB 级主因)
|
||||
'VERIFICATION_AGENT', // 任务完成后 fork 完整消息(非 GB 级主因)
|
||||
'KAIROS_BRIEF', // Kairos 定时摘要(定时汇报当前状态)
|
||||
'AWAY_SUMMARY', // 离线摘要(用户离开后生成总结)
|
||||
'ULTRAPLAN', // 超级规划模式,深度分析后生成实施计划
|
||||
'DAEMON', // 守护进程模式,长驻 supervisor 管理后台 worker(非 GB 级主因)
|
||||
'ACP', // ACP 代理协议,支持外部 agent 接入
|
||||
'WORKFLOW_SCRIPTS', // 工作流脚本(.claude/workflows/ 中的 YAML/MD)
|
||||
'HISTORY_SNIP', // 历史消息裁剪,压缩上下文窗口
|
||||
'CONTEXT_COLLAPSE', // 上下文折叠,自动压缩旧消息
|
||||
'MONITOR_TOOL', // Monitor 工具,流式监控后台进程输出
|
||||
// 'FORK_SUBAGENT', // 已禁用:启用后 prompt 引导模型用 fork(继承父模型)替代 Explore(haiku),导致探索任务使用同等级模型
|
||||
// 'UDS_INBOX', // inbox 数组只增不减(非 GB 级主因)
|
||||
'KAIROS', // Kairos 定时任务系统核心
|
||||
// 'COORDINATOR_MODE', // 已禁用:AgentSummary 30s fork 循环,GB 级泄露主因
|
||||
// 'LAN_PIPES', // 依赖 UDS_INBOX(已随 UDS_INBOX 恢复)
|
||||
'BG_SESSIONS', // 后台会话管理(ps/logs/attach/kill)
|
||||
'TEMPLATES', // 模板任务(new/list/reply 子命令)
|
||||
// 'REVIEW_ARTIFACT', // 代码审查产物(API 请求无响应,待排查 schema 兼容性)
|
||||
// API content block types
|
||||
'CONNECTOR_TEXT', // Connector 文本块类型,扩展 API 内容格式
|
||||
// Attribution tracking
|
||||
'COMMIT_ATTRIBUTION', // Git 提交归属追踪(记录 AI 辅助贡献)
|
||||
// Server mode (claude server / claude open)
|
||||
'DIRECT_CONNECT', // 直连模式(claude server / claude open)
|
||||
// Skill search & learning — feature flags compiled in (so the slash
|
||||
// commands /skill-* etc. exist), but the runtime "enabled" toggle
|
||||
// defaults to OFF (see featureCheck.ts). Operators turn on via the
|
||||
// slash-command toggle or env vars (SKILL_SEARCH_ENABLED=1,
|
||||
// SKILL_LEARNING_ENABLED=1). Rationale: bounded caches added on
|
||||
// this branch (see docs/agent/sur-skill-overflow-bugs.md) close the
|
||||
// overflow risk, but Haiku-on-first-Chinese-query and disk-side
|
||||
// observation accumulation remain operator-discretion concerns.
|
||||
// 'EXPERIMENTAL_SKILL_SEARCH',
|
||||
// 'SKILL_LEARNING',
|
||||
// P3: poor mode
|
||||
'POOR', // 穷鬼模式,跳过 extract_memories/prompt_suggestion 减少消耗
|
||||
// Team Memory
|
||||
// 'TEAMMEM', // 已禁用:依赖 COORDINATOR_MODE,邮箱文件无限增长
|
||||
// SSH Remote
|
||||
'SSH_REMOTE', // SSH 远程连接,本地 REPL + 远端工具执行
|
||||
] as const
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
process.env.BUN_INSPECT="localhost:8888/2dc3gzl5xot"
|
||||
await import("./dev")
|
||||
process.env.BUN_INSPECT = 'localhost:8888/2dc3gzl5xot'
|
||||
await import('./dev')
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
* via Bun's -d flag (bunfig.toml [define] doesn't propagate to
|
||||
* dynamically imported modules at runtime).
|
||||
*/
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { getMacroDefines, DEFAULT_BUILD_FEATURES } from "./defines.ts";
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { getMacroDefines, DEFAULT_BUILD_FEATURES } from './defines.ts'
|
||||
|
||||
// Resolve project root from this script's location
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const projectRoot = join(__dirname, "..");
|
||||
const cliPath = join(projectRoot, "src/entrypoints/cli.tsx");
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
const projectRoot = join(__dirname, '..')
|
||||
const cliPath = join(projectRoot, 'src/entrypoints/cli.tsx')
|
||||
|
||||
const defines = getMacroDefines();
|
||||
const defines = getMacroDefines()
|
||||
|
||||
const defineArgs = Object.entries(defines).flatMap(([k, v]) => [
|
||||
"-d",
|
||||
`${k}:${v}`,
|
||||
]);
|
||||
'-d',
|
||||
`${k}:${v}`,
|
||||
])
|
||||
|
||||
// Bun --feature flags: enable feature() gates at runtime.
|
||||
// Uses the shared DEFAULT_BUILD_FEATURES list from defines.ts.
|
||||
@@ -27,20 +27,28 @@ const defineArgs = Object.entries(defines).flatMap(([k, v]) => [
|
||||
// Any env var matching FEATURE_<NAME>=1 will also enable that feature.
|
||||
// e.g. FEATURE_PROACTIVE=1 bun run dev
|
||||
const envFeatures = Object.entries(process.env)
|
||||
.filter(([k]) => k.startsWith("FEATURE_"))
|
||||
.map(([k]) => k.replace("FEATURE_", ""));
|
||||
.filter(([k]) => k.startsWith('FEATURE_'))
|
||||
.map(([k]) => k.replace('FEATURE_', ''))
|
||||
|
||||
const allFeatures = [...new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures])];
|
||||
const featureArgs = allFeatures.flatMap((name) => ["--feature", name]);
|
||||
const allFeatures = [...new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures])]
|
||||
const featureArgs = allFeatures.flatMap(name => ['--feature', name])
|
||||
|
||||
// If BUN_INSPECT is set, pass --inspect-wait to the child process
|
||||
const inspectArgs = process.env.BUN_INSPECT
|
||||
? ["--inspect-wait=" + process.env.BUN_INSPECT]
|
||||
: [];
|
||||
? ['--inspect-wait=' + process.env.BUN_INSPECT]
|
||||
: []
|
||||
|
||||
const result = Bun.spawnSync(
|
||||
["bun", ...inspectArgs, "run", ...defineArgs, ...featureArgs, cliPath, ...process.argv.slice(2)],
|
||||
{ stdio: ["inherit", "inherit", "inherit"], cwd: projectRoot },
|
||||
);
|
||||
[
|
||||
'bun',
|
||||
...inspectArgs,
|
||||
'run',
|
||||
...defineArgs,
|
||||
...featureArgs,
|
||||
cliPath,
|
||||
...process.argv.slice(2),
|
||||
],
|
||||
{ stdio: ['inherit', 'inherit', 'inherit'], cwd: projectRoot },
|
||||
)
|
||||
|
||||
process.exit(result.exitCode ?? 0);
|
||||
process.exit(result.exitCode ?? 0)
|
||||
|
||||
@@ -71,10 +71,10 @@ mock.module('src/constants/systemPromptSections.js', () => ({
|
||||
__deferred: true,
|
||||
fn,
|
||||
}),
|
||||
DANGEROUS_uncachedSystemPromptSection: (
|
||||
_name: string,
|
||||
fn: () => any,
|
||||
) => ({ __deferred: true, fn }),
|
||||
DANGEROUS_uncachedSystemPromptSection: (_name: string, fn: () => any) => ({
|
||||
__deferred: true,
|
||||
fn,
|
||||
}),
|
||||
resolveSystemPromptSections: async (sections: any[]) => {
|
||||
const results = await Promise.all(
|
||||
sections.map((s: any) => (s?.__deferred ? s.fn() : s)),
|
||||
@@ -100,14 +100,12 @@ mock.module(
|
||||
'@claude-code-best/builtin-tools/tools/FileWriteTool/prompt.js',
|
||||
() => ({ FILE_WRITE_TOOL_NAME: 'Write' }),
|
||||
)
|
||||
mock.module(
|
||||
'@claude-code-best/builtin-tools/tools/GlobTool/prompt.js',
|
||||
() => ({ GLOB_TOOL_NAME: 'Glob' }),
|
||||
)
|
||||
mock.module(
|
||||
'@claude-code-best/builtin-tools/tools/GrepTool/prompt.js',
|
||||
() => ({ GREP_TOOL_NAME: 'Grep' }),
|
||||
)
|
||||
mock.module('@claude-code-best/builtin-tools/tools/GlobTool/prompt.js', () => ({
|
||||
GLOB_TOOL_NAME: 'Glob',
|
||||
}))
|
||||
mock.module('@claude-code-best/builtin-tools/tools/GrepTool/prompt.js', () => ({
|
||||
GREP_TOOL_NAME: 'Grep',
|
||||
}))
|
||||
mock.module(
|
||||
'@claude-code-best/builtin-tools/tools/AgentTool/constants.js',
|
||||
() => ({ AGENT_TOOL_NAME: 'Agent', VERIFICATION_AGENT_TYPE: 'verification' }),
|
||||
@@ -188,4 +186,6 @@ const full = sections.join('\n\n')
|
||||
const outputPath = 'scripts/system-prompt-dump.txt'
|
||||
await Bun.write(outputPath, full)
|
||||
console.log(`Written to ${outputPath}`)
|
||||
console.log(`Sections: ${sections.length} | Chars: ${full.length} | Lines: ${full.split('\n').length}`)
|
||||
console.log(
|
||||
`Sections: ${sections.length} | Chars: ${full.length} | Lines: ${full.split('\n').length}`,
|
||||
)
|
||||
|
||||
@@ -6,60 +6,62 @@
|
||||
* 2. Copy native addon files
|
||||
* 3. Generate dual entry points (cli-bun.js, cli-node.js)
|
||||
*/
|
||||
import { readdir, readFile, writeFile, cp } from "node:fs/promises";
|
||||
import { chmodSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
import { readdir, readFile, writeFile, cp } from 'node:fs/promises'
|
||||
import { chmodSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { execSync } from 'node:child_process'
|
||||
|
||||
const outdir = "dist";
|
||||
const outdir = 'dist'
|
||||
|
||||
async function postBuild() {
|
||||
// Step 1: Patch globalThis.Bun destructuring from third-party deps
|
||||
const files = await readdir(outdir, { recursive: true });
|
||||
const BUN_DESTRUCTURE = /var \{([^}]+)\} = globalThis\.Bun;?/g;
|
||||
const files = await readdir(outdir, { recursive: true })
|
||||
const BUN_DESTRUCTURE = /var \{([^}]+)\} = globalThis\.Bun;?/g
|
||||
const BUN_DESTRUCTURE_SAFE =
|
||||
'var {$1} = typeof globalThis.Bun !== "undefined" ? globalThis.Bun : {};';
|
||||
'var {$1} = typeof globalThis.Bun !== "undefined" ? globalThis.Bun : {};'
|
||||
|
||||
let bunPatched = 0;
|
||||
let bunPatched = 0
|
||||
for (const file of files) {
|
||||
const filePath = join(outdir, file);
|
||||
if (typeof file !== "string" || !file.endsWith(".js")) continue;
|
||||
const content = await readFile(filePath, "utf-8");
|
||||
const filePath = join(outdir, file)
|
||||
if (typeof file !== 'string' || !file.endsWith('.js')) continue
|
||||
const content = await readFile(filePath, 'utf-8')
|
||||
if (BUN_DESTRUCTURE.test(content)) {
|
||||
await writeFile(
|
||||
filePath,
|
||||
content.replace(BUN_DESTRUCTURE, BUN_DESTRUCTURE_SAFE),
|
||||
);
|
||||
bunPatched++;
|
||||
)
|
||||
bunPatched++
|
||||
}
|
||||
BUN_DESTRUCTURE.lastIndex = 0;
|
||||
BUN_DESTRUCTURE.lastIndex = 0
|
||||
}
|
||||
|
||||
// Step 2: Copy native addon files
|
||||
const audioCaptureDir = join(outdir, "vendor", "audio-capture");
|
||||
await cp("vendor/audio-capture", audioCaptureDir, { recursive: true } as never);
|
||||
console.log(`Copied vendor/audio-capture/ → ${audioCaptureDir}/`);
|
||||
const audioCaptureDir = join(outdir, 'vendor', 'audio-capture')
|
||||
await cp('vendor/audio-capture', audioCaptureDir, {
|
||||
recursive: true,
|
||||
} as never)
|
||||
console.log(`Copied vendor/audio-capture/ → ${audioCaptureDir}/`)
|
||||
|
||||
const ripgrepDir = join(outdir, "vendor", "ripgrep");
|
||||
await cp("src/utils/vendor/ripgrep", ripgrepDir, { recursive: true } as never);
|
||||
console.log(`Copied src/utils/vendor/ripgrep/ → ${ripgrepDir}/`);
|
||||
const ripgrepDir = join(outdir, 'vendor', 'ripgrep')
|
||||
await cp('src/utils/vendor/ripgrep', ripgrepDir, { recursive: true } as never)
|
||||
console.log(`Copied src/utils/vendor/ripgrep/ → ${ripgrepDir}/`)
|
||||
|
||||
// Step 3: Generate dual entry points
|
||||
const cliBun = join(outdir, "cli-bun.js");
|
||||
const cliNode = join(outdir, "cli-node.js");
|
||||
const cliBun = join(outdir, 'cli-bun.js')
|
||||
const cliNode = join(outdir, 'cli-node.js')
|
||||
|
||||
await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n');
|
||||
await writeFile(cliNode, '#!/usr/bin/env node\nimport "./cli.js"\n');
|
||||
await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n')
|
||||
await writeFile(cliNode, '#!/usr/bin/env node\nimport "./cli.js"\n')
|
||||
|
||||
chmodSync(cliBun, 0o755);
|
||||
chmodSync(cliNode, 0o755);
|
||||
chmodSync(cliBun, 0o755)
|
||||
chmodSync(cliNode, 0o755)
|
||||
|
||||
console.log(
|
||||
`Post-build complete: patched ${bunPatched} Bun destructure, generated entry points`,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
postBuild().catch((err) => {
|
||||
console.error("Post-build failed:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
postBuild().catch(err => {
|
||||
console.error('Post-build failed:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -11,29 +11,39 @@
|
||||
* bun run scripts/postinstall.js
|
||||
*/
|
||||
|
||||
const { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, chmodSync } =
|
||||
require("fs")
|
||||
const { spawnSync } = require("child_process")
|
||||
const { setDefaultResultOrder } = require("node:dns")
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
const {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
chmodSync,
|
||||
} = require('fs')
|
||||
const { spawnSync } = require('child_process')
|
||||
const { setDefaultResultOrder } = require('node:dns')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
// Prefer IPv4 first — Bun on Windows sometimes fails GitHub over broken IPv6 paths.
|
||||
try {
|
||||
setDefaultResultOrder("ipv4first")
|
||||
setDefaultResultOrder('ipv4first')
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
// --- Config ---
|
||||
|
||||
const RG_VERSION = "15.0.1"
|
||||
const RG_VERSION = '15.0.1'
|
||||
const DEFAULT_RELEASE_BASE = `https://github.com/microsoft/ripgrep-prebuilt/releases/download/v${RG_VERSION}`
|
||||
const MIRROR_RELEASE_BASE = `https://ghproxy.net/https://github.com/microsoft/ripgrep-prebuilt/releases/download/v${RG_VERSION}`
|
||||
const RELEASE_BASE = (process.env.RIPGREP_DOWNLOAD_BASE ?? DEFAULT_RELEASE_BASE).replace(/\/$/, "")
|
||||
const RELEASE_BASE = (
|
||||
process.env.RIPGREP_DOWNLOAD_BASE ?? DEFAULT_RELEASE_BASE
|
||||
).replace(/\/$/, '')
|
||||
|
||||
const scriptDir = path.dirname(__filename)
|
||||
const projectRoot = path.resolve(scriptDir, "..")
|
||||
const projectRoot = path.resolve(scriptDir, '..')
|
||||
|
||||
// --- Platform mapping ---
|
||||
|
||||
@@ -41,27 +51,29 @@ function getPlatformMapping() {
|
||||
const arch = process.arch
|
||||
const platform = process.platform
|
||||
|
||||
if (platform === "darwin") {
|
||||
if (arch === "arm64") return { target: "aarch64-apple-darwin", ext: "tar.gz" }
|
||||
if (arch === "x64") return { target: "x86_64-apple-darwin", ext: "tar.gz" }
|
||||
if (platform === 'darwin') {
|
||||
if (arch === 'arm64')
|
||||
return { target: 'aarch64-apple-darwin', ext: 'tar.gz' }
|
||||
if (arch === 'x64') return { target: 'x86_64-apple-darwin', ext: 'tar.gz' }
|
||||
throw new Error(`Unsupported macOS arch: ${arch}`)
|
||||
}
|
||||
|
||||
if (platform === "win32") {
|
||||
if (arch === "x64") return { target: "x86_64-pc-windows-msvc", ext: "zip" }
|
||||
if (arch === "arm64") return { target: "aarch64-pc-windows-msvc", ext: "zip" }
|
||||
if (platform === 'win32') {
|
||||
if (arch === 'x64') return { target: 'x86_64-pc-windows-msvc', ext: 'zip' }
|
||||
if (arch === 'arm64')
|
||||
return { target: 'aarch64-pc-windows-msvc', ext: 'zip' }
|
||||
throw new Error(`Unsupported Windows arch: ${arch}`)
|
||||
}
|
||||
|
||||
if (platform === "linux") {
|
||||
if (platform === 'linux') {
|
||||
const isMusl = detectMusl()
|
||||
if (arch === "x64") {
|
||||
return { target: "x86_64-unknown-linux-musl", ext: "tar.gz" }
|
||||
if (arch === 'x64') {
|
||||
return { target: 'x86_64-unknown-linux-musl', ext: 'tar.gz' }
|
||||
}
|
||||
if (arch === "arm64") {
|
||||
if (arch === 'arm64') {
|
||||
return isMusl
|
||||
? { target: "aarch64-unknown-linux-musl", ext: "tar.gz" }
|
||||
: { target: "aarch64-unknown-linux-gnu", ext: "tar.gz" }
|
||||
? { target: 'aarch64-unknown-linux-musl', ext: 'tar.gz' }
|
||||
: { target: 'aarch64-unknown-linux-gnu', ext: 'tar.gz' }
|
||||
}
|
||||
throw new Error(`Unsupported Linux arch: ${arch}`)
|
||||
}
|
||||
@@ -70,7 +82,7 @@ function getPlatformMapping() {
|
||||
}
|
||||
|
||||
function detectMusl() {
|
||||
const muslArch = process.arch === "x64" ? "x86_64" : "aarch64"
|
||||
const muslArch = process.arch === 'x64' ? 'x86_64' : 'aarch64'
|
||||
try {
|
||||
statSync(`/lib/libc.musl-${muslArch}.so.1`)
|
||||
return true
|
||||
@@ -82,24 +94,30 @@ function detectMusl() {
|
||||
// --- Paths ---
|
||||
|
||||
function getVendorDir() {
|
||||
if (existsSync(path.join(projectRoot, "src"))) {
|
||||
return path.resolve(projectRoot, "src", "utils", "vendor", "ripgrep")
|
||||
if (existsSync(path.join(projectRoot, 'src'))) {
|
||||
return path.resolve(projectRoot, 'src', 'utils', 'vendor', 'ripgrep')
|
||||
}
|
||||
return path.resolve(projectRoot, "dist", "vendor", "ripgrep")
|
||||
return path.resolve(projectRoot, 'dist', 'vendor', 'ripgrep')
|
||||
}
|
||||
|
||||
function getBinaryPath() {
|
||||
const dir = getVendorDir()
|
||||
const subdir = `${process.arch}-${process.platform}`
|
||||
const binary = process.platform === "win32" ? "rg.exe" : "rg"
|
||||
const binary = process.platform === 'win32' ? 'rg.exe' : 'rg'
|
||||
return path.resolve(dir, subdir, binary)
|
||||
}
|
||||
|
||||
// --- Download helpers ---
|
||||
|
||||
function proxyEnvSet() {
|
||||
const v = (s) => (s ?? "").trim()
|
||||
return !!(v(process.env.HTTPS_PROXY) || v(process.env.HTTP_PROXY) || v(process.env.ALL_PROXY) || v(process.env.https_proxy) || v(process.env.http_proxy))
|
||||
const v = s => (s ?? '').trim()
|
||||
return !!(
|
||||
v(process.env.HTTPS_PROXY) ||
|
||||
v(process.env.HTTP_PROXY) ||
|
||||
v(process.env.ALL_PROXY) ||
|
||||
v(process.env.https_proxy) ||
|
||||
v(process.env.http_proxy)
|
||||
)
|
||||
}
|
||||
|
||||
function tryPowerShellDownload(url, dest) {
|
||||
@@ -107,17 +125,24 @@ function tryPowerShellDownload(url, dest) {
|
||||
const d = dest.replace(/'/g, "''")
|
||||
const cmd = `Invoke-WebRequest -Uri '${u}' -OutFile '${d}' -UseBasicParsing`
|
||||
const result = spawnSync(
|
||||
"powershell.exe",
|
||||
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", cmd],
|
||||
{ stdio: "pipe", windowsHide: true },
|
||||
'powershell.exe',
|
||||
[
|
||||
'-NoProfile',
|
||||
'-NonInteractive',
|
||||
'-ExecutionPolicy',
|
||||
'Bypass',
|
||||
'-Command',
|
||||
cmd,
|
||||
],
|
||||
{ stdio: 'pipe', windowsHide: true },
|
||||
)
|
||||
return result.status === 0 && existsSync(dest) && statSync(dest).size > 0
|
||||
}
|
||||
|
||||
function tryCurlDownload(url, dest) {
|
||||
const curl = process.platform === "win32" ? "curl.exe" : "curl"
|
||||
const result = spawnSync(curl, ["-fsSL", "-L", "--fail", "-o", dest, url], {
|
||||
stdio: "pipe",
|
||||
const curl = process.platform === 'win32' ? 'curl.exe' : 'curl'
|
||||
const result = spawnSync(curl, ['-fsSL', '-L', '--fail', '-o', dest, url], {
|
||||
stdio: 'pipe',
|
||||
windowsHide: true,
|
||||
})
|
||||
return result.status === 0 && existsSync(dest) && statSync(dest).size > 0
|
||||
@@ -126,20 +151,22 @@ function tryCurlDownload(url, dest) {
|
||||
async function fetchRelease(url) {
|
||||
if (proxyEnvSet()) {
|
||||
// Dynamic require so it works in node without bundling issues
|
||||
const undici = require("undici")
|
||||
const undici = require('undici')
|
||||
return await undici.fetch(url, {
|
||||
redirect: "follow",
|
||||
redirect: 'follow',
|
||||
dispatcher: new undici.EnvHttpProxyAgent(),
|
||||
})
|
||||
}
|
||||
// Node 18+ has global fetch, Bun has it too
|
||||
return await fetch(url, { redirect: "follow" })
|
||||
return await fetch(url, { redirect: 'follow' })
|
||||
}
|
||||
|
||||
async function downloadUrlToBuffer(url) {
|
||||
const response = await fetchRelease(url)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Download failed: ${response.status} ${response.statusText}`)
|
||||
throw new Error(
|
||||
`Download failed: ${response.status} ${response.statusText}`,
|
||||
)
|
||||
}
|
||||
return Buffer.from(await response.arrayBuffer())
|
||||
}
|
||||
@@ -152,11 +179,14 @@ async function downloadUrlToBufferWithFallback(url) {
|
||||
firstError = e
|
||||
}
|
||||
|
||||
const tmpRoot = path.join(os.tmpdir(), `ripgrep-dl-${process.pid}-${Date.now()}`)
|
||||
const tmpFile = path.join(tmpRoot, "archive")
|
||||
const tmpRoot = path.join(
|
||||
os.tmpdir(),
|
||||
`ripgrep-dl-${process.pid}-${Date.now()}`,
|
||||
)
|
||||
const tmpFile = path.join(tmpRoot, 'archive')
|
||||
mkdirSync(tmpRoot, { recursive: true })
|
||||
try {
|
||||
if (process.platform === "win32" && tryPowerShellDownload(url, tmpFile)) {
|
||||
if (process.platform === 'win32' && tryPowerShellDownload(url, tmpFile)) {
|
||||
return readFileSync(tmpFile)
|
||||
}
|
||||
if (tryCurlDownload(url, tmpFile)) {
|
||||
@@ -172,8 +202,8 @@ async function downloadUrlToBufferWithFallback(url) {
|
||||
// --- Extract ---
|
||||
|
||||
function findZipEntryKey(files, want) {
|
||||
return Object.keys(files).find((k) => {
|
||||
const norm = k.replace(/\\/g, "/")
|
||||
return Object.keys(files).find(k => {
|
||||
const norm = k.replace(/\\/g, '/')
|
||||
return norm === want || norm.endsWith(`/${want}`)
|
||||
})
|
||||
}
|
||||
@@ -183,7 +213,7 @@ async function extractZip(buffer, binaryPath, extractedBinary) {
|
||||
// Try fflate first (bundled dep)
|
||||
let fflateError
|
||||
try {
|
||||
const { unzipSync } = require("fflate")
|
||||
const { unzipSync } = require('fflate')
|
||||
const unzipped = unzipSync(new Uint8Array(buffer))
|
||||
const key = findZipEntryKey(unzipped, extractedBinary)
|
||||
if (!key) {
|
||||
@@ -196,7 +226,7 @@ async function extractZip(buffer, binaryPath, extractedBinary) {
|
||||
}
|
||||
|
||||
// Fallback: PowerShell Expand-Archive or unzip CLI
|
||||
const tmpDir = path.join(binaryDir, ".tmp-download")
|
||||
const tmpDir = path.join(binaryDir, '.tmp-download')
|
||||
rmSync(tmpDir, { recursive: true, force: true })
|
||||
mkdirSync(tmpDir, { recursive: true })
|
||||
try {
|
||||
@@ -205,12 +235,19 @@ async function extractZip(buffer, binaryPath, extractedBinary) {
|
||||
writeFileSync(archivePath, buffer)
|
||||
|
||||
let extracted = false
|
||||
if (process.platform === "win32") {
|
||||
if (process.platform === 'win32') {
|
||||
const psCmd = `Expand-Archive -Path '${archivePath.replace(/'/g, "''")}' -DestinationPath '${tmpDir.replace(/'/g, "''")}' -Force`
|
||||
const psResult = spawnSync(
|
||||
"powershell.exe",
|
||||
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", psCmd],
|
||||
{ stdio: "pipe", windowsHide: true },
|
||||
'powershell.exe',
|
||||
[
|
||||
'-NoProfile',
|
||||
'-NonInteractive',
|
||||
'-ExecutionPolicy',
|
||||
'Bypass',
|
||||
'-Command',
|
||||
psCmd,
|
||||
],
|
||||
{ stdio: 'pipe', windowsHide: true },
|
||||
)
|
||||
if (psResult.status === 0) {
|
||||
extracted = true
|
||||
@@ -218,11 +255,18 @@ async function extractZip(buffer, binaryPath, extractedBinary) {
|
||||
}
|
||||
|
||||
if (!extracted) {
|
||||
const result = spawnSync("unzip", ["-o", archivePath, "-d", tmpDir], { stdio: "pipe" })
|
||||
const result = spawnSync('unzip', ['-o', archivePath, '-d', tmpDir], {
|
||||
stdio: 'pipe',
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
const unzipErr = result.stderr?.toString().trim() || "command not found"
|
||||
const fflateMsg = fflateError instanceof Error ? fflateError.message : String(fflateError)
|
||||
throw new Error(`zip extraction failed (fflate: ${fflateMsg}; unzip: ${unzipErr})`)
|
||||
const unzipErr = result.stderr?.toString().trim() || 'command not found'
|
||||
const fflateMsg =
|
||||
fflateError instanceof Error
|
||||
? fflateError.message
|
||||
: String(fflateError)
|
||||
throw new Error(
|
||||
`zip extraction failed (fflate: ${fflateMsg}; unzip: ${unzipErr})`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,13 +282,15 @@ async function extractZip(buffer, binaryPath, extractedBinary) {
|
||||
|
||||
async function extractTarGz(buffer, binaryPath, extractedBinary, assetName) {
|
||||
const binaryDir = path.dirname(binaryPath)
|
||||
const tmpDir = path.join(binaryDir, ".tmp-download")
|
||||
const tmpDir = path.join(binaryDir, '.tmp-download')
|
||||
rmSync(tmpDir, { recursive: true, force: true })
|
||||
mkdirSync(tmpDir, { recursive: true })
|
||||
try {
|
||||
const archivePath = path.join(tmpDir, assetName)
|
||||
writeFileSync(archivePath, buffer)
|
||||
const result = spawnSync("tar", ["xzf", archivePath, "-C", tmpDir], { stdio: "pipe" })
|
||||
const result = spawnSync('tar', ['xzf', archivePath, '-C', tmpDir], {
|
||||
stdio: 'pipe',
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`tar extract failed: ${result.stderr?.toString()}`)
|
||||
}
|
||||
@@ -267,7 +313,7 @@ async function downloadAndExtract() {
|
||||
const binaryPath = getBinaryPath()
|
||||
const binaryDir = path.dirname(binaryPath)
|
||||
|
||||
const force = process.argv.includes("--force")
|
||||
const force = process.argv.includes('--force')
|
||||
if (!force && existsSync(binaryPath)) {
|
||||
const stat = statSync(binaryPath)
|
||||
if (stat.size > 0) {
|
||||
@@ -278,11 +324,11 @@ async function downloadAndExtract() {
|
||||
|
||||
console.log(`[ripgrep] Downloading v${RG_VERSION} for ${target}...`)
|
||||
|
||||
const extractedBinary = process.platform === "win32" ? "rg.exe" : "rg"
|
||||
const extractedBinary = process.platform === 'win32' ? 'rg.exe' : 'rg'
|
||||
|
||||
const mirrors = [RELEASE_BASE]
|
||||
if (RELEASE_BASE === DEFAULT_RELEASE_BASE.replace(/\/$/, "")) {
|
||||
mirrors.push(MIRROR_RELEASE_BASE.replace(/\/$/, ""))
|
||||
if (RELEASE_BASE === DEFAULT_RELEASE_BASE.replace(/\/$/, '')) {
|
||||
mirrors.push(MIRROR_RELEASE_BASE.replace(/\/$/, ''))
|
||||
}
|
||||
|
||||
let buffer
|
||||
@@ -294,7 +340,9 @@ async function downloadAndExtract() {
|
||||
buffer = await downloadUrlToBufferWithFallback(url)
|
||||
break
|
||||
} catch (e) {
|
||||
console.warn(`[ripgrep] Download from ${base} failed: ${e instanceof Error ? e.message : e}`)
|
||||
console.warn(
|
||||
`[ripgrep] Download from ${base} failed: ${e instanceof Error ? e.message : e}`,
|
||||
)
|
||||
lastError = e
|
||||
}
|
||||
}
|
||||
@@ -307,13 +355,13 @@ async function downloadAndExtract() {
|
||||
|
||||
mkdirSync(binaryDir, { recursive: true })
|
||||
|
||||
if (ext === "tar.gz") {
|
||||
if (ext === 'tar.gz') {
|
||||
await extractTarGz(buffer, binaryPath, extractedBinary, assetName)
|
||||
} else {
|
||||
await extractZip(buffer, binaryPath, extractedBinary)
|
||||
}
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
if (process.platform !== 'win32') {
|
||||
chmodSync(binaryPath, 0o755)
|
||||
}
|
||||
|
||||
@@ -321,7 +369,7 @@ async function downloadAndExtract() {
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
const hint =
|
||||
"Check network or set HTTPS_PROXY. If GitHub is blocked, set RIPGREP_DOWNLOAD_BASE to a mirror (see script header)."
|
||||
'Check network or set HTTPS_PROXY. If GitHub is blocked, set RIPGREP_DOWNLOAD_BASE to a mirror (see script header).'
|
||||
throw new Error(`${msg} ${hint}`)
|
||||
}
|
||||
}
|
||||
@@ -330,10 +378,12 @@ async function main() {
|
||||
await downloadAndExtract()
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
main().catch(error => {
|
||||
const msg = error instanceof Error ? error.message : String(error)
|
||||
console.error(`[postinstall] ripgrep download failed (non-fatal): ${msg}`)
|
||||
console.error(`[postinstall] You can install ripgrep manually: https://github.com/BurntSushi/ripgrep#installation`)
|
||||
console.error(
|
||||
`[postinstall] You can install ripgrep manually: https://github.com/BurntSushi/ripgrep#installation`,
|
||||
)
|
||||
// Never exit with error code — postinstall must not break install
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
* bun run scripts/rcs.ts
|
||||
* RCS_API_KEYS=key1,key2 RCS_PORT=4000 bun run scripts/rcs.ts
|
||||
*/
|
||||
import { config } from "../packages/remote-control-server/src/config";
|
||||
import { config } from '../packages/remote-control-server/src/config'
|
||||
|
||||
console.log(`[RCS] Starting Remote Control Server...`);
|
||||
console.log(`[RCS] Port: ${config.port}`);
|
||||
console.log(`[RCS] API Key configuration loaded`);
|
||||
console.log(`[RCS] Starting Remote Control Server...`)
|
||||
console.log(`[RCS] Port: ${config.port}`)
|
||||
console.log(`[RCS] API Key configuration loaded`)
|
||||
|
||||
const server = await import("../packages/remote-control-server/src/index.ts");
|
||||
const server = await import('../packages/remote-control-server/src/index.ts')
|
||||
|
||||
Bun.serve(
|
||||
server.default
|
||||
)
|
||||
Bun.serve(server.default)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { spawn } from "node:child_process"
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const scripts = process.argv.slice(2)
|
||||
if (scripts.length === 0) {
|
||||
@@ -6,5 +6,5 @@ if (scripts.length === 0) {
|
||||
}
|
||||
|
||||
for (const script of scripts) {
|
||||
spawn(process.execPath, [script], { stdio: "inherit", shell: false })
|
||||
spawn(process.execPath, [script], { stdio: 'inherit', shell: false })
|
||||
}
|
||||
|
||||
@@ -8,59 +8,63 @@
|
||||
* node scripts/setup-chrome-mcp.mjs doctor # Run a single sub-command
|
||||
*/
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { mkdirSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { homedir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
if (process.env.CLAUDE_CODE_SKIP_CHROME_MCP_SETUP === "1") {
|
||||
process.exit(0);
|
||||
if (process.env.CLAUDE_CODE_SKIP_CHROME_MCP_SETUP === '1') {
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const cliPath = require.resolve("@claude-code-best/mcp-chrome-bridge/dist/cli.js");
|
||||
const require = createRequire(import.meta.url)
|
||||
const cliPath = require.resolve(
|
||||
'@claude-code-best/mcp-chrome-bridge/dist/cli.js',
|
||||
)
|
||||
|
||||
const userArgs = process.argv.slice(2);
|
||||
const userArgs = process.argv.slice(2)
|
||||
|
||||
function getChromeMcpLogDir() {
|
||||
const home = homedir();
|
||||
if (process.platform === "darwin") {
|
||||
return join(home, "Library", "Logs", "mcp-chrome-bridge");
|
||||
const home = homedir()
|
||||
if (process.platform === 'darwin') {
|
||||
return join(home, 'Library', 'Logs', 'mcp-chrome-bridge')
|
||||
}
|
||||
if (process.platform === "win32") {
|
||||
if (process.platform === 'win32') {
|
||||
return join(
|
||||
process.env.LOCALAPPDATA || join(home, "AppData", "Local"),
|
||||
"mcp-chrome-bridge",
|
||||
"logs",
|
||||
);
|
||||
process.env.LOCALAPPDATA || join(home, 'AppData', 'Local'),
|
||||
'mcp-chrome-bridge',
|
||||
'logs',
|
||||
)
|
||||
}
|
||||
return join(
|
||||
process.env.XDG_STATE_HOME || join(home, ".local", "state"),
|
||||
"mcp-chrome-bridge",
|
||||
"logs",
|
||||
);
|
||||
process.env.XDG_STATE_HOME || join(home, '.local', 'state'),
|
||||
'mcp-chrome-bridge',
|
||||
'logs',
|
||||
)
|
||||
}
|
||||
|
||||
if (userArgs.length > 0) {
|
||||
// Forward single sub-command
|
||||
execFileSync("node", [cliPath, ...userArgs], { stdio: "inherit" });
|
||||
execFileSync('node', [cliPath, ...userArgs], { stdio: 'inherit' })
|
||||
} else {
|
||||
// Full setup sequence
|
||||
const steps = [
|
||||
["fix-permissions"],
|
||||
["register", "--browser", "chrome"],
|
||||
["doctor"],
|
||||
];
|
||||
['fix-permissions'],
|
||||
['register', '--browser', 'chrome'],
|
||||
['doctor'],
|
||||
]
|
||||
|
||||
mkdirSync(getChromeMcpLogDir(), { recursive: true });
|
||||
mkdirSync(getChromeMcpLogDir(), { recursive: true })
|
||||
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const args = steps[i];
|
||||
const isLast = i === steps.length - 1;
|
||||
if (isLast) console.log(`\n[${i + 1}/${steps.length}] ${args.join(" ")}`);
|
||||
execFileSync("node", [cliPath, ...args], { stdio: isLast ? "inherit" : "pipe" });
|
||||
const args = steps[i]
|
||||
const isLast = i === steps.length - 1
|
||||
if (isLast) console.log(`\n[${i + 1}/${steps.length}] ${args.join(' ')}`)
|
||||
execFileSync('node', [cliPath, ...args], {
|
||||
stdio: isLast ? 'inherit' : 'pipe',
|
||||
})
|
||||
}
|
||||
|
||||
console.log("\nChrome MCP setup complete!");
|
||||
console.log('\nChrome MCP setup complete!')
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { Plugin } from "rollup";
|
||||
import { DEFAULT_BUILD_FEATURES } from "./defines.ts";
|
||||
import type { Plugin } from 'rollup'
|
||||
import { DEFAULT_BUILD_FEATURES } from './defines.ts'
|
||||
|
||||
/**
|
||||
* Collect enabled feature flags from defaults + env vars.
|
||||
*/
|
||||
export function getEnabledFeatures(): Set<string> {
|
||||
const envFeatures = Object.keys(process.env)
|
||||
.filter((k) => k.startsWith("FEATURE_"))
|
||||
.map((k) => k.replace("FEATURE_", ""));
|
||||
return new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures]);
|
||||
.filter(k => k.startsWith('FEATURE_'))
|
||||
.map(k => k.replace('FEATURE_', ''))
|
||||
return new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures])
|
||||
}
|
||||
|
||||
// Regex to match feature('FLAG_NAME') calls with string literal arguments
|
||||
const FEATURE_CALL_RE = /feature\s*\(\s*['"]([\w]+)['"]\s*\)/g;
|
||||
const FEATURE_CALL_RE = /feature\s*\(\s*['"]([\w]+)['"]\s*\)/g
|
||||
|
||||
/**
|
||||
* Vite/Rollup plugin that replaces `feature('X')` calls with boolean literals
|
||||
@@ -27,25 +27,25 @@ const FEATURE_CALL_RE = /feature\s*\(\s*['"]([\w]+)['"]\s*\)/g;
|
||||
* to prevent "module not found" errors.
|
||||
*/
|
||||
export default function featureFlagsPlugin(): Plugin {
|
||||
const features = getEnabledFeatures();
|
||||
const features = getEnabledFeatures()
|
||||
|
||||
const virtualModuleId = "bun:bundle";
|
||||
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
||||
const virtualModuleId = 'bun:bundle'
|
||||
const resolvedVirtualModuleId = '\0' + virtualModuleId
|
||||
|
||||
return {
|
||||
name: "feature-flags",
|
||||
name: 'feature-flags',
|
||||
|
||||
// Resolve bun:bundle as a virtual module (prevents "module not found")
|
||||
resolveId(id) {
|
||||
if (id === virtualModuleId) {
|
||||
return resolvedVirtualModuleId;
|
||||
return resolvedVirtualModuleId
|
||||
}
|
||||
},
|
||||
|
||||
// Provide a stub export for bun:bundle (unused at runtime after transform)
|
||||
load(id) {
|
||||
if (id === resolvedVirtualModuleId) {
|
||||
return "export function feature(name) { return false; }";
|
||||
return 'export function feature(name) { return false; }'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -53,30 +53,30 @@ export default function featureFlagsPlugin(): Plugin {
|
||||
// and transpile `using` declarations for Node.js compatibility.
|
||||
transform(code, id) {
|
||||
// Skip node_modules
|
||||
if (id.includes("node_modules")) return null;
|
||||
if (id.includes('node_modules')) return null
|
||||
|
||||
let modified = false;
|
||||
let modified = false
|
||||
|
||||
// 1. Replace feature('X') calls with boolean literals
|
||||
let matchCount = 0;
|
||||
let matchCount = 0
|
||||
let transformed = code.replace(FEATURE_CALL_RE, (match, flagName) => {
|
||||
matchCount++;
|
||||
return features.has(flagName) ? "true" : "false";
|
||||
});
|
||||
if (matchCount > 0) modified = true;
|
||||
matchCount++
|
||||
return features.has(flagName) ? 'true' : 'false'
|
||||
})
|
||||
if (matchCount > 0) modified = true
|
||||
|
||||
// 2. Transpile `using _ = expr;` to `const _ = expr;` for Node.js compat.
|
||||
// Node.js v22 does not support `using` declarations (Explicit Resource Management).
|
||||
// Safe because: SLOW_OPERATION_LOGGING is not enabled, so slowLogging returns
|
||||
// a no-op disposable whose [Symbol.dispose]() is empty.
|
||||
if (transformed.includes("using _")) {
|
||||
transformed = transformed.replace(/\busing\s+(_\w*)\s*=/g, "const $1 =");
|
||||
modified = true;
|
||||
if (transformed.includes('using _')) {
|
||||
transformed = transformed.replace(/\busing\s+(_\w*)\s*=/g, 'const $1 =')
|
||||
modified = true
|
||||
}
|
||||
|
||||
if (!modified) return null;
|
||||
if (!modified) return null
|
||||
|
||||
return { code: transformed, map: null };
|
||||
return { code: transformed, map: null }
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Plugin } from "rollup";
|
||||
import type { Plugin } from 'rollup'
|
||||
|
||||
/**
|
||||
* Rollup plugin that replaces `var __require = import.meta.require;`
|
||||
@@ -9,17 +9,17 @@ import type { Plugin } from "rollup";
|
||||
*/
|
||||
export default function importMetaRequirePlugin(): Plugin {
|
||||
return {
|
||||
name: "import-meta-require",
|
||||
name: 'import-meta-require',
|
||||
|
||||
renderChunk(code) {
|
||||
const pattern = "var __require = import.meta.require;";
|
||||
const pattern = 'var __require = import.meta.require;'
|
||||
const replacement =
|
||||
'var __require = typeof import.meta.require === "function" ? import.meta.require : (await import("module")).createRequire(import.meta.url);';
|
||||
'var __require = typeof import.meta.require === "function" ? import.meta.require : (await import("module")).createRequire(import.meta.url);'
|
||||
|
||||
if (code.includes(pattern)) {
|
||||
return code.replace(pattern, replacement);
|
||||
return code.replace(pattern, replacement)
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user