From 5fc7c8e13da888ee6ff7f21f8e2764b6bdd13d4f Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Tue, 21 Apr 2026 12:42:10 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=20highlight.js=20?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 59 +++--- bun.lock | 1 + package.json | 2 + scripts/check-bundle-integrity.ts | 336 ++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 29 deletions(-) create mode 100644 scripts/check-bundle-integrity.ts diff --git a/README.md b/README.md index 8c71f7154..e1d74d821 100644 --- a/README.md +++ b/README.md @@ -14,28 +14,28 @@ [文档在这里, 支持投稿 PR](https://ccb.agent-aura.top/) | [留影文档在这里](./Friends.md) | [Discord 群组](https://discord.gg/uApuzJWGKX) -| 特性 | 说明 | 文档 | -|------|------|------| -| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/pipes-and-lan) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) | -| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) | -| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) | -| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) | -| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) | -| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 | -| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) | -| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) | -| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) | -| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) | -| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) | -| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) | -| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) | -| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) | + +| 特性 | 说明 | 文档 | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/pipes-and-lan) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) | +| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) | +| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) | +| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) | +| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) | +| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 | +| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) | +| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) | +| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) | +| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) | +| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) | +| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) | +| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) | +| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) | - 🚀 [想要启动项目](#快速开始源码版) - 🐛 [想要调试项目](#vs-code-调试) - 📖 [想要学习项目](#teach-me-学习项目) - ## ⚡ 快速开始(安装版) 不用克隆仓库, 从 NPM 下载后, 直接使用 @@ -45,7 +45,7 @@ npm i -g claude-code-best # bun 安装比较多问题, 推荐 npm 装 # bun i -g claude-code-best -# bun pm -g trust claude-code-best +# bun pm -g trust claude-code-best @claude-code-best/mcp-chrome-bridge ccb # 以 nodejs 打开 claude code ccb-bun # 以 bun 形态打开 @@ -91,17 +91,17 @@ bun run build 需要填写的字段: -| 📌 字段 | 📝 说明 | 💡 示例 | -|------|------|------| -| Base URL | API 服务地址 | `https://api.example.com/v1` | -| API Key | 认证密钥 | `sk-xxx` | -| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` | -| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` | -| Opus Model | 高性能模型 ID | `claude-opus-4-6` | + +| 📌 字段 | 📝 说明 | 💡 示例 | +| ------------ | ------------- | ---------------------------- | +| Base URL | API 服务地址 | `https://api.example.com/v1` | +| API Key | 认证密钥 | `sk-xxx` | +| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` | +| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` | +| Opus Model | 高性能模型 ID | `claude-opus-4-6` | - ⌨️ **Tab / Shift+Tab** 切换字段,**Enter** 确认并跳到下一个,最后一个字段按 Enter 保存 - > ℹ️ 支持所有 Anthropic API 兼容服务(如 OpenRouter、AWS Bedrock 代理等),只要接口兼容 Messages API 即可。 ## Feature Flags @@ -121,16 +121,17 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动 ### 步骤 1. **终端启动 inspect 服务**: + ```bash bun run dev:inspect ``` - 会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。 + 会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。 2. **VS Code 附着调试器**: + - 在 `src/` 文件中打断点 - F5 → 选择 **"Attach to Bun (TUI debug)"** - ## Teach Me 学习项目 我们新加了一个 teach-me skills, 通过问答式引导帮你理解这个项目的任何模块。(调整 [sigma skill 而来](https://github.com/sanyuan0704/sanyuan-skills)) @@ -157,7 +158,7 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动 ## 相关文档及网站 - **在线文档(Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — 文档源码位于 [`docs/`](docs/) 目录,欢迎投稿 PR -- **DeepWiki**: +- **DeepWiki**: [https://deepwiki.com/claude-code-best/claude-code](https://deepwiki.com/claude-code-best/claude-code) ## Contributors diff --git a/bun.lock b/bun.lock index 769e6808a..c45e0972b 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "@agentclientprotocol/sdk": "^0.19.0", "@claude-code-best/mcp-chrome-bridge": "^3.0.1", + "highlight.js": "^11.11.1", "ws": "^8.20.0", }, "devDependencies": { diff --git a/package.json b/package.json index 43c974fc4..16d8c8026 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "format": "biome format --write src/", "prepare": "git config core.hooksPath .githooks", "test": "bun test", + "check:bundle": "bun run scripts/check-bundle-integrity.ts", "check:unused": "knip-bun", "health": "bun run scripts/health-check.ts", "postinstall": "node scripts/run-parallel.mjs scripts/postinstall.cjs scripts/setup-chrome-mcp.mjs", @@ -64,6 +65,7 @@ "dependencies": { "@agentclientprotocol/sdk": "^0.19.0", "@claude-code-best/mcp-chrome-bridge": "^3.0.1", + "highlight.js": "^11.11.1", "ws": "^8.20.0" }, "devDependencies": { diff --git a/scripts/check-bundle-integrity.ts b/scripts/check-bundle-integrity.ts new file mode 100644 index 000000000..aa0f908d8 --- /dev/null +++ b/scripts/check-bundle-integrity.ts @@ -0,0 +1,336 @@ +#!/usr/bin/env bun +/** + * 构建产物完整性检查脚本 + * + * 检查 Bun.build({ splitting: true }) 输出的 dist/ 目录中是否存在: + * 1. 引用了不存在的 chunk 文件(断链) + * 2. 通过 __require() 或 import() 引用的第三方模块(非 Node.js 内置),在生产环境中会找不到 + * 3. 缺失的静态 import 依赖(跨 chunk 引用目标不存在) + * + * 用法: + * bun scripts/check-bundle-integrity.ts # 检查当前 dist/ + * bun scripts/check-bundle-integrity.ts ./dist # 指定目录 + */ + +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_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", +]) + +// Node 18+ 内置但不在传统列表中的模块 +const NODE_18_PLUS_BUILTINS = new Set(["undici"]) + +// Bun 专用模块(仅在 Bun 运行时可用,Node.js 环境会失败) +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"]) + +// ─── 模式 ────────────────────────────────────────────────────────── +// 匹配 import { ... } from "./chunk-xxxxx.js" 或 import"./chunk-xxxxx.js" +const STATIC_IMPORT_RE = /(?:from\s+|import\s+)"(\.\/[^"]+\.js)"/g +// 匹配 __require("xxx") +const REQUIRE_RE = /__require\("([^"]+)"\)/g +// 匹配动态 import("xxx"),排除 ./chunk-xxx.js 的内部引用 +const DYNAMIC_IMPORT_RE = /import\("([^"]+)"\)/g +// 匹配 nodeRequire("xxx")(createRequire 创建的 require 别名) +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" + file: string + line: number + module: string + snippet: string +} + +async function main() { + 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")) + } catch { + console.error(`❌ 无法读取目录: ${distDir}`) + console.error(" 请先运行 bun run build") + process.exit(1) + } + + const fileSet = new Set(files) + console.log(`📦 找到 ${files.length} 个 JS 文件\n`) + + const findings: Finding[] = [] + + // 2. 逐文件扫描 + for (const file of files) { + const filePath = join(distDir, file) + const content = await readFile(filePath, "utf-8") + const lines = content.split("\n") + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + const lineNum = i + 1 + + // 2a. 检查静态 chunk 引用是否断链 + const staticImportMatches = line.matchAll(STATIC_IMPORT_RE) + for (const m of staticImportMatches) { + const ref = m[1] + // 提取文件名部分(去掉 ./) + const refFile = ref.replace(/^\.\//, "") + if (!fileSet.has(refFile)) { + findings.push({ + type: "broken-chunk-ref", + severity: "error", + file, + line: lineNum, + module: ref, + snippet: line.trim().slice(0, 120), + }) + } + } + + // 2b. 检查 __require 中的第三方模块 + const requireMatches = line.matchAll(REQUIRE_RE) + for (const m of requireMatches) { + 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 (BUN_MODULES.has(mod)) { + findings.push({ + type: "bun-runtime-only", + severity: "warning", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + continue + } + // 第三方模块 — 在生产环境(全局 npm install)中找不到 + findings.push({ + type: "third-party-require", + severity: "error", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + } + + // 2c. 检查动态 import() 中的第三方模块 + const dynImportMatches = line.matchAll(DYNAMIC_IMPORT_RE) + for (const m of dynImportMatches) { + const mod = m[1] + // 跳过内部 chunk 引用和相对路径 + 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 (BUN_MODULES.has(mod)) { + // bun:test 等只在 Bun 运行时可用,Node.js 运行时会失败 + findings.push({ + type: "bun-runtime-only", + severity: "warning", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + continue + } + // 第三方动态 import + findings.push({ + type: "third-party-import", + severity: "error", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + } + + // 2d. 检查 nodeRequire("xxx") 中的第三方模块(createRequire 别名) + const nodeRequireMatches = line.matchAll(NODE_REQUIRE_RE) + 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 (BUN_MODULES.has(mod)) { + findings.push({ + type: "bun-runtime-only", + severity: "warning", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + continue + } + findings.push({ + type: "third-party-node-require", + severity: "error", + file, + line: lineNum, + module: mod, + snippet: line.trim().slice(0, 120), + }) + } + } + } + + // 3. 汇总报告 + 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") + + if (brokenRefs.length > 0) { + console.log("❌ 断裂的 chunk 引用(引用了不存在的文件):") + for (const f of brokenRefs) { + console.log(` ${f.file}:${f.line} → ${f.module}`) + } + console.log() + } + + if (thirdPartyRequires.length > 0) { + console.log("❌ 通过 __require() 引用的第三方模块(生产环境会找不到):") + const grouped = groupByModule(thirdPartyRequires) + for (const [mod, items] of grouped) { + console.log(` "${mod}" — 出现 ${items.length} 次:`) + for (const f of items.slice(0, 5)) { + console.log(` ${f.file}:${f.line}`) + } + if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`) + } + console.log() + } + + if (thirdPartyImports.length > 0) { + console.log("❌ 通过 import() 动态引用的第三方模块(生产环境会找不到):") + const grouped = groupByModule(thirdPartyImports) + for (const [mod, items] of grouped) { + console.log(` "${mod}" — 出现 ${items.length} 次:`) + for (const f of items.slice(0, 5)) { + console.log(` ${f.file}:${f.line}`) + } + if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`) + } + console.log() + } + + if (thirdPartyNodeRequires.length > 0) { + console.log("❌ 通过 nodeRequire() 引用的第三方模块(绕过打包,生产环境会找不到):") + const grouped = groupByModule(thirdPartyNodeRequires) + for (const [mod, items] of grouped) { + console.log(` "${mod}" — 出现 ${items.length} 次:`) + for (const f of items.slice(0, 5)) { + console.log(` ${f.file}:${f.line}`) + } + if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`) + } + console.log() + } + + if (bunRuntimeOnly.length > 0) { + console.log("⚠️ Bun 运行时专用模块(Node.js 环境会失败):") + const grouped = groupByModule(bunRuntimeOnly) + for (const [mod, items] of grouped) { + console.log(` "${mod}" — 出现 ${items.length} 次`) + } + console.log() + } + + // 4. 总结 + console.log("─".repeat(50)) + if (errors.length === 0 && warnings.length === 0) { + console.log("✅ 构建产物完整性检查通过,未发现问题。") + } else { + console.log(`📊 总计: ${errors.length} 个错误, ${warnings.length} 个警告`) + if (errors.length > 0) { + console.log( + `\n💡 修复建议: + - 第三方模块问题:在 build.ts 中通过 external 选项排除,或确保它们被正确打包到 chunk 中 + - 断链问题:检查 build 时是否有文件被意外删除或构建不完整 + - Bun 专用模块:确保运行时使用 bun 而非 node`, + ) + } + } + + process.exit(errors.length > 0 ? 1 : 0) +} + +function groupByModule(items: Finding[]): Map { + const map = new Map() + for (const item of items) { + const list = map.get(item.module) || [] + list.push(item) + map.set(item.module, list) + } + // 按出现次数降序 + return new Map([...map.entries()].sort((a, b) => b[1].length - a[1].length)) +} + +main().catch((err) => { + console.error("Fatal error:", err) + process.exit(2) +})