
1. 为什么 OpenCode 的构建打包值得单独讲OpenCode 是一个用 TypeScript 写的开源 AI 编程 CLI 工具定位上对标 Claude Code但代码开放、可以自己编译、自己打包、自己分发。它跑在 Bun 上而不是传统的 Node.js 运行时这一点决定了它的构建链路和大多数前端项目不太一样。如果你只是bun install然后bun dev那只能算把源码跑起来真正要把它变成能发给同事、能塞进 CI、能丢到服务器上直接执行的可分发产物就得走一遍完整的 build 流程。这篇面向的是需要把 OpenCode 源码产出为可执行二进制的开发者。我会给出可复制的bun build配置、package.json脚本、tsconfig骨架然后实际演示一次从源码到可执行文件的验证动作。同时OpenCode 本身要接 AI 能力才有意义我会说明怎么通过 TaoToken 统一 Key 和 API 通道把模型接进来避免在多个供应商之间来回切换配置。先说清楚 Bun 在这里的角色。Bun 既是运行时也是包管理器还自带打包器bundler和测试器。OpenCode 选它是因为启动快、原生支持 TypeScript、单文件编译能力强。你可以把 Bun 理解成「Node.js npm esbuild jest」的合体但接口更统一。构建 OpenCode 时我们主要用到它的bun build和--compile能力把一堆.ts文件收敛成一个可执行文件。适合谁看已经能跑起 OpenCode 源码、想进一步做打包分发的开发者或者想学 Bun 打包 TypeScript CLI 的通用流程的人。下面每一步都可以直接抄。2. TaoToken 前置先把模型通道准备好OpenCode 是壳模型是芯。没有模型通道编译出来的二进制也只是个空转的 CLI。TaoToken 在这里的作用是提供一个统一的 Key 和 API 入口让你不用为每个模型单独配一套环境变量和 base URL。你需要先拿到一个 API Key。登录 TaoToken 控制台在 API Keys 页面创建一个新 Key复制出来。这个 Key 后面会写进 OpenCode 的配置里作为调用模型的凭证。TaoToken 的 API 入口是https://taotoken.net/api注意这个地址不带任何查询参数是干净的 base URL。模型对话、Coding Plan、控制台、API Keys、接入文档这些页面都可以从官网进入https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。如果你要长期做编码和 Agent 类任务建议看一下 Coding Plan它的额度模型更适合高频调用。注意Key 只存在本地配置文件或环境变量里不要提交到 Git 仓库。打包出来的二进制本身不应该内嵌 Key否则分发出去等于泄露。配置上OpenCode 读取模型配置的方式通常是环境变量加配置文件。你可以先在 shell 里导出export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在 OpenCode 的模型配置里引用这两个变量。这样打包时不需要改代码运行时注入即可。这一步做完后面编译出来的二进制才有实际调用能力。3. 可复制配置bun build、package.json 与 tsconfig这一节是核心。OpenCode 的核心代码在packages/opencode目录下构建脚本在packages/opencode/script/build.ts。我们先看依赖安装和基础运行再进入打包。安装依赖在项目根目录执行bun install装完之后可以先用开发模式验证源码能跑bun dev如果这一步就报错先别急着打包把运行问题解决掉。开发模式通了说明依赖和 TypeScript 编译链路是好的。接下来是打包。OpenCode 自带的构建脚本支持--single参数用来产出单文件可执行产物bun run packages/opencode/script/build.ts --single如果你想自己写一个更可控的bun build配置可以参考下面这个build.ts骨架。它做的事情是指定入口、目标运行时为 bun、输出目录、开启压缩、生成 sourcemap 便于排障。// packages/opencode/script/build.ts import { $ } from bun; const outdir ./dist; const entry ./src/index.ts; await $rm -rf ${outdir}; const result await Bun.build({ entrypoints: [entry], outdir, target: bun, format: esm, minify: true, sourcemap: external, define: { process.env.NODE_ENV: JSON.stringify(production), }, }); if (!result.success) { console.error(构建失败); for (const log of result.logs) { console.error(log); } process.exit(1); } console.log(构建完成产物在 ${outdir});对应的package.json脚本可以这样写把开发、构建、单文件编译分开{ name: opencode, type: module, scripts: { dev: bun run src/index.ts, build: bun run script/build.ts, build:single: bun run script/build.ts --single, compile: bun build ./src/index.ts --compile --outfile ./dist/opencode, typecheck: tsc --noEmit }, devDependencies: { typescript: ^5.4.0, types/bun: latest } }tsconfig.json骨架要贴合 Bun 的运行时特性模块解析用bundler目标设成较新的 ES 版本{ compilerOptions: { target: ESNext, module: ESNext, moduleResolution: bundler, lib: [ESNext], types: [bun-types], strict: true, skipLibCheck: true, esModuleInterop: true, resolveJsonModule: true, allowImportingTsExtensions: true, noEmit: true, baseUrl: ., paths: { /*: [./src/*] } }, include: [src/**/*.ts, script/**/*.ts], exclude: [node_modules, dist] }几个参数值得单独说。target: bun让打包器知道运行环境是 Bun可以放心用 Bun 特有的 API比如Bun.file、Bun.spawn。format: esm是因为 OpenCode 整体是 ESM 风格。minify: true会压缩体积但如果你要调试线上问题可以临时关掉。sourcemap: external生成独立的 map 文件不塞进产物里分发时可以选择不带。如果你要的是真正的单文件可执行程序用--compilebun build ./src/index.ts --compile --outfile ./dist/opencode这条命令会把 Bun 运行时和你的代码一起打进去产出一个可以直接./dist/opencode执行的二进制。这是 Bun 相比 Node.js 最舒服的地方之一不需要目标机器预装运行时。4. 验证请求从源码到可执行文件跑一遍配置写完了得实际验证。我按顺序走一遍你可以对照着做。第一步类型检查确保没有 TS 报错bun run typecheck第二步执行构建bun run build正常输出应该是构建完成产物在 ./dist。如果result.success为 false日志里会打印具体哪个模块解析失败通常是路径别名没配好或者某个依赖没装。第三步编译单文件bun run compile产物在./dist/opencode。给它加执行权限chmod x ./dist/opencode第四步运行验证./dist/opencode --version如果能看到版本号输出说明二进制本身是好的。接下来验证模型通道是否接通。用 TaoToken 的 Key 跑一次最简单的对话请求确认 OpenCode 能拿到模型响应export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api ./dist/opencode 用一句话解释什么是 TypeScript如果返回了模型生成的文本说明从源码编译、打包、到模型调用的整条链路都通了。这一步是整个流程的验收点前面所有配置都是为了这一刻。实测下来Linux 环境下这条链路最顺基本不会遇到权限和缓存问题。Windows 下如果卡在移动 cache 文件那一步可以往下看排障部分。5. 本篇常见错排查打包过程中最容易撞上的几类问题我按现象、原因、解法列一下。现象一Windows 下构建时报 cache 文件权限错误。原因是 Bun 在 Windows 上移动缓存文件时被权限或杀毒软件拦截。解法有三个用管理员身份运行 PowerShell临时关闭杀毒软件再构建或者显式指定 cache 目录比如设置BUN_INSTALL_CACHE_DIR到一个你有完全控制权的路径。三个都试过还不行就换 Linux 环境兼容性最好。现象二bun build报模块找不到。多半是tsconfig里的paths别名和打包器的解析没对齐。Bun 的打包器默认不读tsconfig的paths你需要在Bun.build里显式配alias或者改用相对路径导入。检查一下报错的那个模块是不是用了/前缀。现象三编译出的二进制运行时报缺少运行时。如果你用的是bun build而不是--compile产物是 JS 文件目标机器必须有 Bun。要真正独立分发必须用--compile。确认一下你的package.json里compile脚本带了这个参数。现象四模型调用返回 401 或 403。检查TAOTOKEN_API_KEY是否导出成功TAOTOKEN_BASE_URL是否是https://taotoken.net/api注意结尾不要多加斜杠。Key 如果是在控制台刚创建的确认没有复制到多余空格。现象五bun install卡住或超时。通常是网络问题可以配置镜像源或者重试。如果某个包一直装不上单独bun add那个包看具体报错。提示排障时优先用bun run build而不是直接调build.ts这样脚本里的错误处理逻辑会生效日志更完整。6. 把 Key 和通道固定下来继续往下走走到这里你应该已经拿到了一个能跑的可执行文件并且验证过它能通过 TaoToken 调用模型。接下来要做的是把 Key 和 API 通道固定成一套稳定配置避免每次换环境都重新折腾。如果你主要在做接入和排障建议直接去 API Keys 页面管理你的凭证配合接入文档把 base URL 和鉴权方式确认清楚。如果你要验证不同模型的表现用模型对话页面快速试如果是长期编码和 Agent 任务Coding Plan 的额度模型更合适。这几个入口都可以从官网进https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。打包这件事我的经验是把build.ts、package.json、tsconfig三个文件当成项目的基础设施来维护每次改依赖或改入口都同步更新别等到要发版了才临时补。Bun 的--compile让分发变得很简单但前提是你的构建脚本本身是干净、可复现的。先保证bun run build在干净环境里能一次通过再谈分发。