ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

刚刚 Claude Code 源码泄露!我扒出了 11 个隐藏秘密

刚刚 Claude Code 源码泄露!我扒出了 11 个隐藏秘密 1. 从 npm 包里把 TypeScript 源码捞出来Claude Code 源码泄露还原全流程Claude Code 源码泄露这件事本质上是 npm 发布环节的一次打包配置失误。Claude Code 通过 npm 分发正常上线前代码会被压缩混淆但打包工具在构建时额外生成了一个 Source Map 文件而发布配置里没有把.map排除掉于是这个映射文件被一起推到了公开仓库。Source Map 是什么你可以把它理解成一张「翻译对照表」压缩后的代码是给机器看的Source Map 则记录了每个压缩片段对应原始文件的路径和内容。它本来是开发阶段用来定位 Bug 的上线时必须删掉。这次泄露的cli.js.map体积接近 60MB内部是一个 JSON核心是两个数组sources存所有源文件路径sourcesContent存每个文件的完整源码两者下标一一对应。只要解析这个 JSON按路径把内容写回磁盘就能把 TypeScript 源码原样还原出来。这篇文章面向想复现分析过程的前端、Node.js 和 AI 工具开发者我会把还原步骤、关键文件定位命令、验证清单完整走一遍并顺带梳理源码里值得学习的 11 个隐藏机制。整个过程不需要任何特殊网络手段一台能跑 Node.js 的机器就够。1.1 先确认你拿到的是哪个版本泄露对应的是 Claude Code 2.1.88 版本的 npm 包。你可以在本地建一个空目录用 npm 把指定版本拉下来注意不要全局安装避免污染现有环境mkdir cc-sourcemap cd cc-sourcemap npm pack anthropic-ai/claude-code2.1.88执行完你会得到一个.tgz压缩包。解压它tar -xzf anthropic-ai-claude-code-2.1.88.tgz ls -lh package/如果包里存在cli.js.map说明这个版本的 Source Map 确实被发布了。用du -h看一下大小接近 60MB 就对了。这一步是整个还原流程的入口也是判断「泄露是否真实存在」的第一手证据。1.2 解析 Source Map 的 JSON 结构Source Map 文件本身是标准 JSON字段含义在 Source Map 规范里有明确定义。我们关心的只有两个字段含义用途sources原始文件路径数组决定还原后的目录结构sourcesContent每个文件的完整源码直接写入文件的内容mappings压缩后位置到原位置的映射调试用还原源码可忽略file生成的产物名一般是cli.js写一个 Node.js 脚本读取并还原。注意路径里可能带webpack://或../前缀需要做一次清洗防止写到目录外面去// restore.js const fs require(fs); const path require(path); const map JSON.parse(fs.readFileSync(package/cli.js.map, utf8)); const outDir path.resolve(restored); map.sources.forEach((src, i) { const content map.sourcesContent[i]; if (content null) return; // 清洗路径前缀避免目录穿越 const clean src.replace(/^webpack:\/\//, ).replace(/^\.\.\//, ); const target path.join(outDir, clean); if (!target.startsWith(outDir)) return; fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, content); console.log(written:, clean); });跑起来node restore.js跑完后restored/目录里就是还原出来的 TypeScript 源码树。实测下来1900 多个文件、约 51 万行代码几秒钟就能全部落盘。1.3 用命令快速定位关键文件还原只是第一步真正有价值的是找到核心逻辑所在。下面这几条命令可以帮你快速定位# 统计还原出的文件数量 find restored -name *.ts -o -name *.tsx | wc -l # 找 Agent 主循环 grep -rn while (true) restored --include*.ts | head # 找工具注册表 grep -rln toolUseContext restored --include*.ts | head # 找系统提示词组装 grep -rln systemPrompt restored --include*.ts | head # 找上下文压缩逻辑 grep -rln compact restored --include*.ts | headquery.ts是对话循环的核心tools/目录下是 40 多个内置工具的实现prompts/里能看到系统提示词是怎么拼出来的。把这些文件按目录归类你就能得到一张完整的架构地图。1.4 验证还原结果是否完整还原完不要急着下结论先做一轮校验。检查文件数是否和sources数组长度接近检查有没有空文件检查关键文件是否可读# sources 数组长度 node -e const mrequire(./package/cli.js.map);console.log(m.sources.length) # 还原出的文件数 find restored -type f | wc -l # 找空文件 find restored -type f -empty | head # 抽查核心文件行数 wc -l restored/**/query.ts 2/dev/null || find restored -name query.ts -exec wc -l {} 如果文件数对得上、没有大面积空文件、核心文件能正常打开说明还原是成功的。这一步做完你手里就有了一份可离线阅读的 TypeScript 源码副本后续所有分析都基于它。2. 接入 TaoToken 前置准备把模型调用链路先跑通分析源码是一回事把源码里的机制用到自己的项目里是另一回事。Claude Code 之所以能连续执行任务靠的是稳定的模型调用链路。如果你想在自己的工具里复现类似的 Agent 循环第一步是把模型接入层搭好。TaoToken 提供统一的 API 入口兼容常见的调用格式适合用来做这类实验。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。2.1 先搞清楚你要复现的是哪一层Claude Code 的架构从上到下大致分六层CLI 与界面层、Agent 循环引擎、工具系统、记忆系统、上下文压缩系统、权限与安全系统。你不需要一次性全复现先想清楚目标只想跑通「模型调用 工具执行」的最小闭环重点看第 3 节的配置。想研究上下文压缩和记忆机制重点看第 4 节的验证请求。想长期做编码类 Agent建议直接看 Coding Plan省去自己维护调用链的成本。我试过从最小闭环入手先让模型能稳定返回工具调用再逐步加记忆和压缩这样排错成本最低。2.2 拿到 API Key 并确认模型 ID登录后在控制台创建 API Key路径是 https://taotoken.net/console/api-keys 。创建时注意两点一是 Key 只在创建时完整显示一次复制后妥善保存二是确认你要用的模型 ID不同模型在工具调用能力上有差异。模型对话入口在 https://taotoken.net/models 可以先用它验证 Key 是否可用再写进代码。2.3 环境变量与依赖准备把 Key 放进环境变量不要硬编码进源码这一点在还原出来的 Claude Code 源码里也是同样的做法export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiNode.js 侧确认版本建议 18 以上因为要用到原生 fetchnode -v npm init -y npm i openai到这里前置准备就完成了。接下来进入可复制配置环节我会给出完整的 JSON 和代码片段。3. 可复制配置settings.json 与调用代码片段这一节是全文最需要你动手的部分。配置写错一个字段后面就会遇到 401 或者 local proxy failed 之类的报错。下面给出可直接复制的片段路径和字段名保持一致。3.1 settings.json 配置片段如果你用的是支持 settings 文件的客户端可以这样写。注意 Base URL 结尾不要多加斜杠Key 用环境变量引用{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: 你的Key, ANTHROPIC_MODEL: 你的模型ID }, permissions: { allow: [Read, Write, Bash] } }三件套必须齐全Base URL、Key、Model ID。缺任何一个都会导致请求失败。Base URL 填https://taotoken.net/api不要带 UTM 参数也不要自己拼/v1之外的路径。3.2 Node.js 调用代码用 openai 兼容格式调用把工具定义一起传进去模拟 Claude Code 的工具调用循环// agent-loop.js import OpenAI from openai; const client new OpenAI({ apiKey: process.env.TAOTOKEN_API_KEY, baseURL: process.env.TAOTOKEN_BASE_URL, }); const tools [ { type: function, function: { name: read_file, description: 读取指定路径的文件内容, parameters: { type: object, properties: { path: { type: string } }, required: [path], }, }, }, ]; async function loop(userInput) { const messages [{ role: user, content: userInput }]; while (true) { const res await client.chat.completions.create({ model: process.env.ANTHROPIC_MODEL, messages, tools, }); const msg res.choices[0].message; messages.push(msg); if (!msg.tool_calls || msg.tool_calls.length 0) { console.log(final:, msg.content); break; } for (const call of msg.tool_calls) { // 这里替换成真实工具执行 const result executed ${call.function.name}; messages.push({ role: tool, tool_call_id: call.id, content: result, }); } } } loop(帮我看看 query.ts 的主循环结构);这段代码就是 Claude Codewhile(true)循环的简化版调用模型、解析工具调用、执行、把结果追加回历史、继续循环直到没有新的工具调用为止。3.3 用 TOML 管理多环境如果你要在多个模型之间切换用 TOML 管理配置更清晰[default] base_url https://taotoken.net/api model 你的模型ID [agent] max_turns 20 auto_compact truemax_turns对应源码里的循环上限保护auto_compact对应上下文压缩开关。这两个参数在长任务里非常关键后面排障会用到。4. 验证请求与成功结果确认链路真的通了配置写完必须验证否则你分不清是配置错还是模型能力问题。这一节给出验证步骤和预期结果。4.1 最小请求验证先用一条最简单的请求确认 Key 和 Base URL 正确curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $ANTHROPIC_MODEL, messages: [{role:user,content:只回复 ok}] }成功的话你会拿到一个 JSONchoices[0].message.content里是模型回复。如果返回 401说明 Key 有问题如果返回 404多半是 Base URL 拼错了。4.2 工具调用验证再跑一次带工具的请求确认模型能返回tool_callsnode agent-loop.js预期输出是先打印工具执行信息最后打印final:开头的总结。如果模型一直不返回工具调用检查tools定义是否符合 JSON Schema参数类型写错会导致模型无法识别。4.3 对照源码验证 11 个隐藏机制链路通了之后你可以对照还原出的源码逐个验证这些机制机制关键文件线索验证方式Agent 主循环query.tsgrepwhile (true)工具注册表tools/目录统计工具数量系统提示词组装prompts/查看拼接顺序上下文压缩含compact的文件看触发阈值记忆系统含memory的文件看持久化方式权限管控含permission的文件看 allow/deny 逻辑流式响应含stream的文件看 chunk 处理重试机制含retry的文件看退避策略token 计数含token的文件看估算方式多轮状态State类型定义看字段变化未上线功能被 feature flag 包裹的代码搜flag每验证一条就在你的复现项目里对应实现一小块。这样学到的不是零散知识点而是一套可运行的架构。5. 本篇常见错排查401、local proxy failed 与 reading choices排障是绕不开的环节。下面这些报错我都遇到过按顺序对照基本能定位。5.1 401 Unauthorized最常见的原因是 Key 没生效。检查三件事环境变量是否在当前 shell 生效echo $TAOTOKEN_API_KEY、Key 是否复制完整、请求头字段名是否正确。有些客户端要求Authorization: Bearer有些要求x-api-key字段名错了同样返回 401。5.2 local proxy failed这个报错通常出现在客户端配置了本地转发但转发进程没起来。检查你的 settings 里 Base URL 是否被错误地指向了localhost。正确做法是直接填https://taotoken.net/api不要经过任何本地中间层。如果你确实需要本地代理做日志确认代理进程在监听且转发目标写对。5.3 reading choices of undefined这个报错说明代码在解析响应时choices字段不存在。原因一般是请求体格式不对比如model字段为空或者messages结构错误。加一行日志把原始响应打出来const res await client.chat.completions.create({...}); console.log(JSON.stringify(res, null, 2));看到原始结构后问题基本一目了然。另一个可能是把流式响应当非流式解析了检查stream参数。5.4 OAuth 相关报错如果你用的是需要 OAuth 的客户端报错往往和 token 过期有关。重新走一次授权流程确认回调地址和客户端配置一致。OAuth 和 API Key 是两套体系不要混用。5.5 还原脚本报路径错误回到第 1 节如果restore.js报ENOENT或路径穿越被拦截检查sources里的路径前缀。不同打包工具生成的前缀不一样webpack://、../、./都可能出现按实际情况调整清洗规则。6. 把源码分析变成可复用的工程能力还原源码只是起点真正有价值的是把里面的设计思路迁移到自己的项目。如果你打算长期做编码类 Agent建议直接用 Coding Plan省去自己维护调用链和上下文管理的成本入口在 https://taotoken.net/coding-plan 。想先验证模型能力用模型对话入口 https://taotoken.net/models 快速试。需要管理多个 Key 或查看用量去控制台 https://taotoken.net/console/api-keys 。接入文档在 https://taotoken.net/doc 里面有完整的参数说明和示例。最后给一个实用建议把第 1 节的还原脚本和第 4 节的验证清单存成一个小仓库每次遇到新的 npm 包发布事故改一下包名和版本号就能复用。这套流程不只适用于 Claude Code任何通过 npm 发布且误带 Source Map 的包都能用同样的方式还原和分析。分析完记得把结论落到代码里光看不动手过两天就忘了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进