
1. 从一次 npm 发布事故说起sourcemap 为什么会把源码送出去Claude Code 是 Anthropic 推出的 CLI 形式 AI 编程助手主打终端里的深度代码理解与上下文管理。前段时间 GitHub 上出现了一个名为 claude-code-source 的仓库短时间内拿到大量 Star 和 Fork原因不是官方开源而是有人从 npm 包里还原出了完整的 TypeScript 源码。核心链路其实很朴素生产构建产物没有剥离 source map.js.map文件跟着 npm 包一起发布任何人拿到包后顺着sourceMappingURL字段就能把原始 TS 源码还原出来。这件事对做 TypeScript CLI 工具链的团队是个很直接的提醒。sourcemap 本身不是漏洞它是调试利器问题出在「开发态配置」和「发布态配置」没有分叉。很多 CLI 项目用 tsc 或打包器构建tsconfig.json里sourceMap: true开着打包器devtool又设成source-map本地跑得好好的一npm publish就把.map一起推上去了。更隐蔽的是有些包在files白名单里写了dist而dist里恰好躺着.map.npmignore又没兜住。我试过在自己的一个 CLI 项目里复现这条路径npm pack --dry-run一看dist/index.js.map赫然在列里面sourcesContent字段直接内联了全部源码。也就是说哪怕不下载.map只要包里有它源码就是明文。下面按「问题定位 → 统一 Key 通道准备 → 可复制配置 → 验证 → 排障 → 收尾」的顺序把 sourcemap 关闭动作和 CLI 接入 TaoToken 统一通道的实践一起走一遍。2. 前置准备TaoToken 统一 Key 通道与 CLI 工具链在讲配置之前先把「统一 Key 通道」这件事说清楚。做 CLI 工具的人常遇到一个麻烦工具本身要调模型但模型厂商有好几家Anthropic、OpenAI、通义各有各的鉴权方式、各自的 base URL、各自的 Key 管理。每接一家就要在 CLI 里写一套适配Key 散落在环境变量、配置文件、CI Secret 里排查问题时很难定位是哪个通道挂了。TaoToken 提供的是一个统一入口一个 Key、一个 API 地址背后做多厂商路由。对 CLI 工具来说好处是配置面收敛——settings.json或config.toml里只维护一份base_url和api_key换模型只改模型名不动鉴权逻辑。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个地址不加 UTM。工具链这边复现 sourcemap 检查和 CLI 接入需要Node.js 20、npm 10、TypeScript 5.x。如果你只是想验证模型通道是否通用 curl 就够如果要跑完整 CLI建议把 Node 版本对齐到 20 LTS避免 npm 10 的 pack 行为差异导致检查结果不一致。注意统一 Key 通道解决的是「鉴权与路由收敛」不改变你本地构建产物的安全边界。sourcemap 该关还得关两件事分开做。3. 可复制配置关闭 sourcemap CLI 接入骨架先处理 sourcemap。最直接的是在tsconfig.json里关掉生成和内联{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, outDir: ./dist, rootDir: ./src, sourceMap: false, inlineSources: false, declaration: true, declarationMap: false }, include: [src/**/*.ts] }declarationMap也一起关掉否则.d.ts.map同样会指向源码。如果你用打包器生产模式下的devtool要显式设成false不要留source-map// webpack.config.js module.exports { mode: production, devtool: false, entry: ./src/cli.ts, output: { path: require(path).resolve(__dirname, dist), filename: cli.js } };再补一层发布白名单package.json里用files精确控制别用.npmignore兜底它容易漏{ name: my-cli, version: 1.0.0, bin: { my-cli: ./dist/cli.js }, files: [ dist/**/*.js, dist/**/*.d.ts, !dist/**/*.map ], scripts: { build: tsc, prepublishOnly: bash scripts/check-sourcemap.sh } }check-sourcemap.sh内容如下发布前直接断言#!/usr/bin/env bash set -euo pipefail if find ./dist -name *.map -type f | grep -q .; then echo 发现 sourcemap 文件终止发布 exit 1 fi echo sourcemap 检查通过接下来是 CLI 接入 TaoToken 统一通道。以settings.json为例把 base URL 和 Key 收敛到一处{ provider: { base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, default_model: claude-sonnet-4-20250514 }, cli: { timeout_ms: 60000, max_retries: 2 } }如果你更习惯 TOMLconfig.toml骨架等价[provider] base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} default_model claude-sonnet-4-20250514 [cli] timeout_ms 60000 max_retries 2Key 不要写死在文件里用环境变量注入。CI 里通过 Secret 传TAOTOKEN_API_KEY本地用.env但记得加进.gitignore。这样 CLI 代码里只读provider.base_url和provider.api_key换厂商不动业务逻辑。4. 验证请求确认通道通、确认包干净配置写完要验证两件事模型通道是否可用发布产物是否干净。先验证通道。用 curl 打一次最小请求export TAOTOKEN_API_KEY你的Key curl -sS https://taotoken.net/api/v1/messages \ -H content-type: application/json \ -H x-api-key: ${TAOTOKEN_API_KEY} \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-20250514, max_tokens: 64, messages: [{role: user, content: 只回复两个字通了}] }返回里能看到content数组和文本内容说明统一通道鉴权和路由都正常。如果返回 401先查 Key 是否带上了Bearer前缀不同厂商头不一样TaoToken 统一入口按文档用x-api-key或Authorization均可以接入文档为准。再验证包干净。在项目根目录跑npm run build npm pack --dry-run 21 | grep -E \.map|Tarball Contents find ./dist -name *.map -type f预期结果是grep不到任何.mapfind也无输出。如果npm pack --dry-run里出现了.map说明files白名单或构建配置还有漏网回到第 3 节检查tsconfig和打包器devtool。CLI 侧再跑一次真实调用确认配置被正确读取node ./dist/cli.js --prompt 列出当前目录的 ts 文件数量 --config ./settings.json成功时终端会打印模型返回并且日志里能看到请求打到了https://taotoken.net/api。这一步同时验证了「sourcemap 已关」和「统一 Key 通道可用」两条线。5. 本篇常见错排查报错一npm pack里仍有.map。最常见原因是tsconfig改了但没重新 builddist里是旧产物。先rm -rf dist npm run build再 pack。其次是打包器配置覆盖了 tsc检查devtool是否被别的 config 合并成source-map。报错二sourcesContent内联源码。即使sourceMap: false如果inlineSources: true且某个 loader 仍生成 map源码会内联进.js。把inlineSources设为false并检查是否有source-map-loader之类的插件在反向注入。报错三CLI 请求 401 或 403。先确认TAOTOKEN_API_KEY在运行环境里真的存在echo $TAOTOKEN_API_KEY看是否为空。再确认请求头字段与接入文档一致。如果本地通、CI 不通多半是 CI Secret 没注入或变量名拼错。报错四base_url拼成带 UTM 的地址。API 基址是https://taotoken.net/api不要带查询参数否则部分 HTTP 客户端会把 query 拼进路径导致 404。报错五prepublishOnly没触发。检查package.json的scripts里脚本名是否拼对以及执行权限。CI 里如果直接npm publish --ignore-scripts钩子会被跳过所以流水线里要单独加一步 sourcemap 断言别只依赖本地钩子。6. 收尾把检查做成流水线的一部分sourcemap 泄露这件事根因不在「谁不小心」而在发布流程缺少分层断言。本地钩子会被--ignore-scripts绕过所以 CI 里要独立加一步- name: Assert no sourcemaps run: | if find ./dist -name *.map -type f | grep -q .; then echo dist 中存在 sourcemap终止 exit 1 fi - name: Verify npm tarball run: npm pack --dry-run 21 | tee /tmp/pack.log ! grep -q \.map /tmp/pack.log统一 Key 通道这边把settings.json/config.toml纳入版本管理时Key 一律走环境变量占位CI 用 Secret 注入。需要长期跑编码任务或 Agent 的可以看 Coding Plan 的额度与路由策略只是验证模型是否通的用模型对话页面直接试要生成和管理 Key 的去 API Keys 页面接入细节以接入文档为准。这样一套下来构建产物干净、鉴权收敛、排查路径清晰比事后复盘省事得多。