style: 完成所有文件的lint

This commit is contained in:
claude-code-best
2026-05-01 21:39:30 +08:00
parent d136872cc9
commit 6182015005
1333 changed files with 68255 additions and 77882 deletions

View File

@@ -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.importJXA 语法,不是真正的 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)
})

View File

@@ -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继承父模型替代 Explorehaiku导致探索任务使用同等级模型
// '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继承父模型替代 Explorehaiku导致探索任务使用同等级模型
// '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

View File

@@ -1,2 +1,2 @@
process.env.BUN_INSPECT="localhost:8888/2dc3gzl5xot"
await import("./dev")
process.env.BUN_INSPECT = 'localhost:8888/2dc3gzl5xot'
await import('./dev')

View File

@@ -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)

View File

@@ -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}`,
)

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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)

View File

@@ -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 })
}

View File

@@ -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!')
}

View File

@@ -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 }
},
};
}
}

View File

@@ -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
},
};
}
}