ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

51 万行源码泄露后,TaoToken 视角拆解 Claude Code 的 CLI 与 TypeScript 架构

51 万行源码泄露后,TaoToken 视角拆解 Claude Code 的 CLI 与 TypeScript 架构 1. 从 51 万行源码泄露说起一个 .map 文件如何掀翻 CLI 工程底裤Claude Code 源码泄露这件事技术圈讨论最多的不是“谁泄露了”而是“原来顶级 AI 编程工具的 CLI 架构长这样”。这次泄露的核心不是模型权重也不是训练数据而是anthropic-ai/claude-code这个 npm 包里一个 59.8 MB 的cli.js.map文件。Source Map 本来是给调试用的它把压缩后的cli.js映射回原始 TypeScript 源码结果 1900 多个文件、51.2 万行未混淆代码直接暴露在任何人面前。如果你平时只用 Claude Code 写代码可能觉得这事离你很远。但从工程视角看这是一次极其罕见的“AI Agent CLI 架构公开课”。Claude Code 本质上是一个跑在终端里的 Node.js 程序用 TypeScript 写业务逻辑通过 npm 分发内部再调用 Anthropic 的 API 完成推理和工具调用。它的 CLI 入口、命令注册、工具编排、权限校验、遥测上报全部藏在那个cli.js和它的 Source Map 里。我试过把 v2.1.88 的 npm 包拉下来解包整个过程不需要任何特殊手段就是标准的 npm 操作。解包之后你会看到一个非常典型的 TypeScript 项目结构cli.js是构建产物cli.js.map里藏着sourcesContent字段里面就是原始.ts/.tsx文件的内容。换句话说只要 npm 包里带了 Source Map前端构建的那套“源码保护”在 CLI 场景下基本等于没有。这件事对三类人特别有价值。第一类是正在做 AI Agent 工具的开发者你可以看到一家顶级公司怎么组织 CLI 命令、怎么设计工具调用链路、怎么处理流式响应。第二类是安全工程师Source Map 泄露意味着攻击面直接暴露权限策略、遥测逻辑、内部开关全部可读。第三类是普通用户你需要知道为什么官方强烈建议放弃 npm 安装、改用原生安装器——因为同一时间窗口 npm 生态还出了 axios 供应链攻击。下面我会从 npm 包解包开始一步步还原 Claude Code 的 CLI 入口、命令注册和工具调用链路。整个过程你可以在本地独立复现不需要访问任何被下架的仓库。同时我会把分析过程中用到的模型调用能力接到 TaoToken 上这样你在复现分析时也能直接跑通请求验证。2. 前置准备用 TaoToken 打通模型调用与本地分析环境分析 Claude Code 源码是一回事但你在复现过程中大概率会想“能不能直接跑一下它的请求格式”或者“用另一个模型对照一下工具调用链路”。这时候就需要一个稳定的 API 入口。TaoToken 在这里的角色很简单它提供兼容 OpenAI 风格的接口你可以在本地脚本里直接调用用来验证请求结构、对比工具定义、甚至让模型帮你解释某段混淆代码。先说你本地需要什么。Node.js 18 以上npm 或 pnpm 都行一个能解压 npm 包的目录再加上source-map这个 npm 包用来还原 Source Map。如果你只想看目录树连 Node 都不需要tar和grep就够了。但如果你想深入看 TypeScript 源码建议装一个source-map或者直接用 Node 的--enable-source-maps配合调试。TaoToken 的接入点有三个常用入口。模型对话适合你临时验证请求格式比如你想看看 Claude Code 的 API 请求里tools字段长什么样可以自己构造一个类似请求发过去对比响应。Coding Plan 适合你长期做源码分析或者 Agent 开发因为你会反复调用模型来解释代码、生成分析脚本。API Keys 页面则是你拿 Key 的地方所有请求都要带这个 Key。具体操作上你先去 TaoToken 的 API Keys 页面创建一个 Key然后把它写进环境变量。我习惯用.env文件管理但你在终端里直接export也行。Base URL 用https://taotoken.net/api注意这个地址不带任何查询参数。模型 ID 根据你用的场景选分析代码一般用长上下文模型工具调用验证则用支持 function calling 的模型。这里有个细节Claude Code 的 API 请求里有一个cch00000的占位符源码分析显示它在请求离开 JS 运行环境前会被底层 Zig 代码替换成加密哈希。你在 TaoToken 上复现请求时不需要关心这个因为那是 Anthropic 原生客户端的 DRM 机制。你只需要关注标准的messages、tools、stream这几个字段就能还原出它的工具调用链路。环境准备好之后你可以先跑一个最简单的请求验证连通性。下面这段代码用 Node.js 的fetch直接调 TaoToken 的接口你可以把它保存成test-request.mjs然后运行。如果返回正常说明你的 Key 和网络都没问题接下来就可以专心分析源码了。// test-request.mjs const API_KEY process.env.TAOTOKEN_API_KEY; const BASE_URL https://taotoken.net/api; const response await fetch(${BASE_URL}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_KEY} }, body: JSON.stringify({ model: claude-sonnet-4-20250514, messages: [ { role: user, content: 用一句话解释 Source Map 是什么 } ], max_tokens: 200 }) }); const data await response.json(); console.log(data.choices[0].message.content);运行命令是node test-request.mjs。如果你看到类似“Source Map 是一种将压缩代码映射回原始源码的 JSON 文件”这样的回答说明链路通了。这一步看起来简单但它是后面所有分析工作的基础——因为你在解包源码时遇到不认识的模块随时可以丢给模型帮你解释。3. 可复制配置npm 包解包与 Source Map 还原完整步骤现在进入正题。你要做的第一件事是把anthropic-ai/claude-code的 npm 包拉下来。注意这里不是为了使用它而是为了分析它的构建产物。你可以在一个临时目录里操作避免污染全局环境。mkdir -p ~/cc-analysis cd ~/cc-analysis npm pack anthropic-ai/claude-code2.1.88npm pack会下载一个.tgz文件文件名类似anthropic-ai-claude-code-2.1.88.tgz。解压它tar -xzf anthropic-ai-claude-code-2.1.88.tgz ls -la package/你会看到package/目录下有cli.js、cli.js.map、package.json等文件。先确认cli.js.map的大小du -h package/cli.js.map如果显示接近 59.8 MB说明你拿到的就是带 Source Map 的版本。接下来提取sourcesContent。Source Map 是一个 JSON 文件里面有一个sourcesContent数组每个元素对应一个原始 TypeScript 文件的完整内容。你可以用 Node.js 写一个提取脚本// extract-sources.mjs import fs from fs; import path from path; const mapPath ./package/cli.js.map; const outDir ./extracted-sources; const map JSON.parse(fs.readFileSync(mapPath, utf-8)); const sources map.sources || []; const contents map.sourcesContent || []; console.log(Total sources: ${sources.length}); for (let i 0; i sources.length; i) { const src sources[i]; const content contents[i]; if (!content) continue; const safePath src.replace(/^webpack:\/\//, ).replace(/^\.\//, ); const fullPath path.join(outDir, safePath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content, utf-8); } console.log(Extraction done.);运行node extract-sources.mjs然后看目录树find extracted-sources -type f | head -50你会看到类似src/entrypoints/cli.tsx、src/commands/、src/tools/、src/services/这样的结构。这就是 Claude Code 的原始工程目录。如果你想快速定位 CLI 入口直接搜grep -r program.command extracted-sources/src --include*.ts --include*.tsx -l或者找命令注册的地方grep -rn \.command( extracted-sources/src/entrypoints --include*.ts --include*.tsx | head -30如果你想把分析环境接到 TaoToken 上方便随时让模型帮你读代码可以创建一个settings.json放在项目根目录。这个文件不是 Claude Code 的配置而是你本地分析脚本的配置用来统一管理 API 调用参数{ api: { baseUrl: https://taotoken.net/api, apiKey: sk-your-taotoken-key, defaultModel: claude-sonnet-4-20250514, maxTokens: 4096 }, analysis: { sourceDir: ./extracted-sources/src, outputDir: ./analysis-output, ignorePatterns: [node_modules, *.test.ts, *.spec.ts] } }注意baseUrl不要加任何路径后缀TaoToken 的接口路径是标准的/v1/chat/completions。apiKey从环境变量读也行但写在本地配置文件里更方便你反复调试。如果你用 Cline 或者 Claude Code 本身来做分析记得把 Base URL、Key、Model ID 三件套填完整缺一个都会报 401。解包和提取只是第一步。真正有价值的是看懂它的命令注册和工具调用链路。下面我会带你定位几个关键文件并用 TaoToken 的模型对话能力来辅助解释。4. 验证请求与成功结果还原 CLI 入口与工具调用链路提取出源码后先找 CLI 入口。Claude Code 用的是commander或者类似的 CLI 框架你可以在package.json的bin字段看到入口文件cat package/package.json | grep -A 5 bin通常会指向cli.js而cli.js对应的源码入口在src/entrypoints/cli.tsx。打开这个文件你会看到类似这样的结构// 伪代码示意基于提取后的源码结构 import { program } from commander; import { registerCommands } from ../commands; program .name(claude) .description(Claude Code CLI) .version(VERSION); registerCommands(program); program.parse(process.argv);命令注册集中在src/commands/目录下。你可以列出所有命令文件ls extracted-sources/src/commands/常见的有chat.ts、config.ts、mcp.ts、doctor.ts等。每个命令文件导出一个注册函数接收program对象然后调用program.command(xxx)注册子命令。工具调用链路则在src/tools/目录下每个工具是一个独立的模块导出name、description、inputSchema和execute函数。如果你想验证工具调用的请求格式可以用 TaoToken 的模型对话功能构造一个类似的请求。比如你想看tools字段的结构// verify-tools.mjs const API_KEY process.env.TAOTOKEN_API_KEY; const BASE_URL https://taotoken.net/api; const tools [ { type: function, function: { name: read_file, description: Read a file from the local filesystem, parameters: { type: object, properties: { path: { type: string, description: File path to read } }, required: [path] } } } ]; const response await fetch(${BASE_URL}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_KEY} }, body: JSON.stringify({ model: claude-sonnet-4-20250514, messages: [ { role: user, content: 读取 /tmp/test.txt 的内容 } ], tools: tools, tool_choice: auto }) }); const data await response.json(); console.log(JSON.stringify(data.choices[0].message, null, 2));如果返回的message里包含tool_calls字段说明模型正确识别了工具定义并决定调用。这就是 Claude Code 内部工具调用的基本形态定义工具 schema发给模型模型返回tool_callsCLI 执行对应函数再把结果塞回messages继续对话。成功的结果应该类似这样{ role: assistant, content: null, tool_calls: [ { id: call_abc123, type: function, function: { name: read_file, arguments: {\path\:\/tmp/test.txt\} } } ] }拿到这个结果后你在本地执行read_file把文件内容作为tool角色的消息追加到messages再次请求模型模型就会基于文件内容生成最终回答。这就是一个完整的 Agent 循环。Claude Code 的源码里这个循环被封装在src/services/或者src/core/下你可以搜tool_calls或者tool_use来定位。如果你在验证过程中遇到reading choices报错通常是因为响应结构不对。先打印完整响应体确认choices数组存在。如果返回的是流式响应你需要逐行解析data:前缀的 SSE 消息。TaoToken 支持流式和非流式两种模式你在请求体里加stream: true就会走 SSE。5. 本篇常见错排查401、local proxy failed 与 OAuth 报错对照分析源码和调 API 的过程中最容易卡住的地方不是代码本身而是环境配置。下面这几个报错是我在实际操作中遇到过的你可以对照排查。401 Unauthorized这个最常见。先检查你的Authorization头是不是Bearer sk-xxx格式注意Bearer和 Key 之间有一个空格。然后确认 Key 没有过期去 TaoToken 的 API Keys 页面看一眼状态。如果你用的是 Cline 或者 Claude Code 本身检查settings.json里的apiKey字段有没有被环境变量覆盖。有时候你在终端export了旧的 Key配置文件里的新 Key 反而不生效。local proxy failed这个报错通常出现在你本地开了代理工具但配置不对的时候。TaoToken 的接口是直连的不需要额外代理。如果你在settings.json或者环境变量里设置了HTTP_PROXY/HTTPS_PROXY先取消掉。Node.js 的fetch默认会读这两个环境变量导致请求被转发到不可用的本地端口。检查命令echo $HTTP_PROXY echo $HTTPS_PROXY unset HTTP_PROXY HTTPS_PROXYreading choices这个报错说明你拿到的响应里没有choices字段。先打印完整响应const text await response.text(); console.log(text);如果返回的是 HTML 或者错误信息说明请求根本没到模型接口。常见原因是 Base URL 写错了比如多加了/v1或者少了/api。TaoToken 的正确 Base URL 是https://taotoken.net/api完整的请求路径是/v1/chat/completions。如果你在 Cline 里配置Base URL 填https://taotoken.net/api不要填完整的 chat 路径。OAuth 相关报错如果你在分析 Claude Code 源码时看到OAuth或者token refresh相关的逻辑那是它原生客户端的认证机制。你在用 TaoToken 时不需要走 OAuth直接用 API Key 就行。但如果你在本地跑 Claude Code 的原生安装器可能会遇到 OAuth 回调失败。这时候检查你的浏览器能不能正常跳转或者改用 API Key 模式。npm 包解包后找不到 cli.js.map确认你下载的版本是 v2.1.88。后续版本可能已经修复了这个打包错误不再附带 Source Map。你可以用npm view anthropic-ai/claude-code versions查看所有版本然后指定版本下载。提取源码时 sourcesContent 为空有些 Source Map 只包含mappings不包含sourcesContent这种情况下你只能拿到映射关系拿不到原始代码。检查cli.js.map的 JSON 里有没有sourcesContent字段node -e const mrequire(./package/cli.js.map); console.log(!!m.sourcesContent, m.sourcesContent?.length)如果输出false或者0说明这个版本没有内嵌源码你需要换版本或者用其他方式还原。排查完这些你的分析环境基本就稳定了。接下来你可以专心看它的命令注册逻辑、工具编排方式、以及那个被讨论很多的cch00000占位符在请求链路里的位置。6. 继续深入用 TaoToken 做长期源码分析与 Agent 开发源码分析不是一次性的活。Claude Code 的工程结构里有很多值得反复看的设计比如它怎么处理流式响应、怎么管理多轮工具调用、怎么做权限校验。你不可能一次看完 51 万行所以需要一个稳定的模型调用入口来辅助你读代码、生成分析脚本、对比不同模块的实现。TaoToken 的 Coding Plan 适合这种长期场景。你可以把常用的分析脚本固化下来比如“提取某个目录下所有导出函数”、“生成某个模块的调用关系图”、“解释这段混淆代码的逻辑”。每次遇到新模块直接跑脚本加模型解释效率比纯人工读代码高很多。如果你只是想临时验证某个请求格式用模型对话页面就够了。比如你想确认 Claude Code 的tools字段里input_schema和parameters的区别直接发一个请求对比响应即可。API Keys 页面则是你管理所有 Key 的地方建议为分析项目单独创建一个 Key方便追踪用量。接入文档里有完整的请求示例和错误码说明遇到不确定的参数格式可以先查文档。ClaudeCodeAnthropic 这个 deep link 则提供了 Claude Code 相关的接入说明如果你想把分析环境直接接到 Claude Code 里可以参考那里的配置步骤。最后说一个实际经验分析源码时先把目录树和关键文件定位命令跑通再用模型辅助解释具体模块。不要一上来就让模型读整个cli.js那样既慢又容易丢上下文。正确的做法是先用grep和find定位到具体文件提取出关键函数再让模型解释。这样你既能控制 token 消耗又能保证分析深度。
返回列表