:从 TypeScript 到 npm 发布)
1. 为什么我要自己写 OpenClaw 插件从重复劳动到一次封装OpenClaw 自定义插件开发说白了就是给这个「核心极简、扩展按需」的智能体框架加装自己的零件。它能做什么你可以往里面塞 AI 工具、斜杠命令、HTTP 接口、消息通道、生命周期钩子甚至扩展 CLI。适合谁适合那些每天在聊天窗口里重复粘贴同一段提示词、反复调用同一个内部接口、或者想把公司内部系统接进 Agent 的人。我试过最笨的办法——每次手动把一段 JSON 贴给模型让它解析一天下来手指都酸了后来把它封装成一个插件工具一句「帮我查下订单」就搞定。这篇指南聚焦从零到发布的完整链路用 TypeScript 定义插件接口、CLI 本地调试、npm 打包发布。中间会给出可直接复制的tsconfig.json、package.json、插件入口模板和 CLI 验证命令最后把插件 endpoint 改到 TaoToken 统一 Key/API 通道完成联调。环境要求很明确Node.js 22OpenClaw 用npm install -g openclawlatest装最新版装完先跑openclaw gateway status确认 Gateway 活着。插件放哪儿也有讲究。全局插件放~/.openclaw/extensions/所有工作区都能用工作区插件放./.openclaw/extensions/只对当前项目生效开发中的插件可以放任意路径通过plugins.load.paths配置指过去。我一般开发阶段用任意路径加配置稳定了再挪到全局目录。先讲清楚一个概念OpenClaw 加载插件前会先读openclaw.plugin.json这个清单文件相当于插件的身份证里面写 id、version、main 入口、configSchema。只有清单合法它才会去加载代码。所以哪怕你入口文件写得再漂亮清单缺字段照样加载失败。这一点和很多「直接 import 就完事」的框架不一样习惯之后反而觉得清晰——配置和逻辑分离排查问题时一眼能看出是清单问题还是代码问题。下面从最小可运行的 Hello World 开始逐步加上 TypeScript、配置项、CLI 调试最后打包发布并接入统一 API 通道。每一步都给完整命令和文件内容你可以边看边敲。2. 前置准备与 TaoToken 统一通道配置把 Key 和 Base URL 一次配好在写第一行插件代码之前先把「模型从哪来」这件事定下来。插件里如果要用到模型能力比如工具内部再调一次 LLM 做摘要或者你想让 OpenClaw 的 Agent 走统一通道就需要一个稳定的 API 入口。TaoToken 提供统一的 Key 和 API 通道官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址后面不加任何查询参数。配置的核心是三件套Base URL、API Key、Model ID。不管你是用 Claude Code、Cline、Codex 还是自己写的插件只要走 OpenAI 兼容协议这三样填对就能通。我踩过的坑是有人只填了 Key 忘了改 Base URL结果请求打到默认地址报 401也有人 Base URL 末尾多写了个斜杠导致路径拼接成//v1/chat/completions服务端返回 404。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个新 Key复制出来存到环境变量里别硬编码进代码。我习惯在~/.zshrc或~/.bashrc里加一行export TAOTOKEN_API_KEYsk-你的key然后source ~/.zshrc让它生效。验证一下echo $TAOTOKEN_API_KEY能打印出 Key 就说明环境变量没问题。接下来在插件里读这个变量而不是写死在openclaw.plugin.json的 configSchema 默认值里。configSchema 适合放「问候语」「查询天数」这种非敏感配置密钥一律走环境变量。如果你用的是 Claude Code 这类工具配置方式略有不同需要写进 settings 文件。以 Claude Code 为例配置文件通常在~/.claude/settings.json内容形如{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key } }注意这里的 Base URL 同样是https://taotoken.net/api不要加 UTM 参数UTM 只用于官网链接的归因。Model ID 根据你实际使用的模型填比如claude-sonnet-4-5这类标识具体以文档为准。文档入口在 https://taotoken.net/doc 里面有各模型的准确 ID 和参数说明。对于 OpenClaw 插件本身如果你要在插件内部发起模型请求推荐把 Base URL 和 Key 都从环境变量读const baseUrl process.env.TAOTOKEN_BASE_URL ?? https://taotoken.net/api; const apiKey process.env.TAOTOKEN_API_KEY; if (!apiKey) { throw new Error(缺少 TAOTOKEN_API_KEY请先配置环境变量); }这样插件在本地调试和发布后都能用同一套逻辑不会因为换机器就失效。前置准备做到这里就够了Node 22、OpenClaw 最新版、Gateway 运行中、TaoToken Key 已导出。接下来进入真正的插件开发。3. 可复制的 TypeScript 插件模板tsconfig、package.json 与入口文件这一节给全套可复制配置。先建目录mkdir -p ~/.openclaw/extensions/hello-ts cd ~/.openclaw/extensions/hello-ts然后创建package.json。注意type设为modulemain指向编译后的入口openclaw.plugin标记为 true 方便识别{ name: openclaw-plugin-hello-ts, version: 1.0.0, description: OpenClaw TypeScript 插件示例, type: module, main: ./dist/index.js, types: ./dist/index.d.ts, keywords: [openclaw, openclaw-plugin, typescript], openclaw: { plugin: true }, scripts: { build: tsc -p tsconfig.json, dev: tsc -p tsconfig.json --watch }, dependencies: { sinclair/typebox: ^0.34.0 }, devDependencies: { openclaw/plugin-sdk: ^2026.3.8, typescript: ^5.6.0 } }tsconfig.json关键在module和moduleResolution都设为NodeNextoutDir指向diststrict打开{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true, declaration: true, sourceMap: true, resolveJsonModule: true }, include: [src/**/*.ts], exclude: [node_modules, dist] }插件清单openclaw.plugin.json放在项目根目录main指向编译产物{ id: hello-ts, version: 1.0.0, name: Hello TypeScript, description: TypeScript 版 OpenClaw 插件, main: ./dist/index.js, configSchema: { type: object, additionalProperties: false, properties: { greeting: { type: string, default: Hello, description: 默认问候语 }, baseUrl: { type: string, default: https://taotoken.net/api, description: 模型 API 基础地址 } } } }入口文件src/index.ts用definePluginEntry包裹这样有完整类型提示import { definePluginEntry, OpenClawPluginApi } from openclaw/plugin-sdk; import { Type } from sinclair/typebox; export default definePluginEntry({ id: hello-ts, name: Hello TypeScript, register(api: OpenClawPluginApi) { api.registerTool({ name: say_hello, description: 向指定用户发送问候语, parameters: Type.Object({ name: Type.String({ description: 要问候的人的名字 }) }), async execute(_id, params) { const config api.getConfig(); const greeting config.greeting ?? Hello; return { content: [ { type: text, text: ${greeting}, ${params.name}! 来自 TS 插件 } ] }; } }); api.registerCommand({ name: hello, description: 发送问候, acceptsArgs: true, handler(ctx) { const name ctx.args || World; return { text: 你好${name}这是插件的直接回复 }; } }); api.getLogger().info(Hello TS 插件加载成功); } });装依赖并编译npm install npm run build编译成功后dist/index.js和dist/index.d.ts都会生成。如果tsc报找不到openclaw/plugin-sdk的类型检查devDependencies是否装上了以及moduleResolution是不是NodeNext。这一步做完插件代码就绪接下来安装并验证。4. CLI 本地调试与验证请求从 plugins install 到成功返回插件写完了得让 OpenClaw 认识它。从本地路径安装openclaw plugins install -l ~/.openclaw/extensions/hello-ts-l表示本地链接模式改代码后不用重新安装重启 Gateway 即可。查看已安装列表openclaw plugins list应该能看到hello-ts出现在列表里。如果没出现先跑诊断openclaw plugins doctor它会告诉你清单哪里不合法、入口文件是否存在、依赖是否缺失。我遇到过一次main路径写成了./index.js但实际编译产物在dist/doctor 直接指出「入口文件不存在」改完就好。重启 Gateway 让插件生效openclaw gateway restart然后看日志确认加载成功openclaw gateway logs -f日志里应该出现Hello TS 插件加载成功。接着在聊天窗口测试斜杠命令输入/hello OpenClaw预期返回「你好OpenClaw这是插件的直接回复」。再测 AI 工具输入「向张三发送问候」Agent 会自动调用say_hello工具返回带问候语的结果。如果你想在插件内部验证 TaoToken 通道是否通可以加一个临时工具用fetch打一次模型接口。注意 Base URL 用https://taotoken.net/api路径拼/v1/chat/completionsapi.registerTool({ name: ping_model, description: 测试模型通道连通性, parameters: Type.Object({}), async execute() { const baseUrl process.env.TAOTOKEN_BASE_URL ?? https://taotoken.net/api; const apiKey process.env.TAOTOKEN_API_KEY; const res await fetch(${baseUrl}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: claude-sonnet-4-5, messages: [{ role: user, content: 回复 pong }] }) }); const data await res.json(); return { content: [{ type: text, text: JSON.stringify(data).slice(0, 200) }] }; } });重新npm run buildopenclaw gateway restart然后让 Agent 调用ping_model。如果返回里能看到模型回复说明通道打通。这一步的验证很关键因为后面发布出去的插件如果依赖模型能力通道不通就是白搭。CLI 调试还有几个常用命令openclaw plugins inspect hello-ts看插件详情openclaw plugins doctor做健康检查openclaw gateway logs -f实时看日志。开发阶段我基本就靠这三个命令定位问题。5. 常见报错排查401、local proxy failed、reading choices 与 OAuth这一节对照真实报错逐个拆。第一个高频错误是 401Error: 401 Unauthorized原因通常是 Key 没读到或 Base URL 不对。排查顺序先echo $TAOTOKEN_API_KEY确认环境变量存在再检查插件里读的是不是这个变量名最后确认 Base URL 是https://taotoken.net/api而不是别的地址。如果 Key 是从 https://taotoken.net/api-keys 新建的确认没有多余空格复制时容易带上换行。第二个是local proxy failedError: local proxy failed to connect这个多半是 Gateway 没起来或者插件配置的 endpoint 指向了一个不可达的本地地址。先openclaw gateway status看 Gateway 状态没起来就openclaw gateway restart。如果插件里写了自定义 endpoint确认地址可达别指向一个没启动的服务。第三个是reading choicesTypeError: Cannot read properties of undefined (reading choices)这是解析模型响应时data.choices为 undefined。原因一般是请求根本没成功返回的是错误对象而不是正常响应。加一层判断if (!data.choices || !data.choices[0]) { return { content: [{ type: text, text: 模型返回异常${JSON.stringify(data)} }] }; }这样至少能看到真实错误内容而不是一个模糊的 TypeError。常见根因还是 401 或 404路径拼错导致。第四个是 OAuth 相关报错Error: OAuth token expired or invalid如果你用的是需要 OAuth 的工具链比如某些 CLI 登录态token 过期会报这个。解决方式是重新走一遍登录流程或者改用 API Key 方式。对于 OpenClaw 插件推荐直接用 API Key少一层 OAuth 刷新逻辑稳定得多。还有一个容易忽略的插件加载失败但没明显报错。这时候跑openclaw plugins doctor它会列出清单校验结果。我见过configSchema里additionalProperties设成 true 导致配置校验宽松问题被掩盖也见过id和目录名不一致导致加载混乱。保持 id、目录名、package name 三者语义一致能省很多事。排查完这些插件基本就稳了。接下来打包发布。6. 打包发布到 npm 与长期编码接入 Coding Plan本地跑通后发布到 npm 让别人也能装。先确认package.json里name没被占用然后登录npm login按提示输入账号密码。发布前建议先npm run build确保dist是最新的并且.npmignore或files字段把src排除、只发dist{ files: [dist, openclaw.plugin.json, README.md] }发布npm publish如果包名带 scope比如yourname/openclaw-plugin-hello-ts记得加--access public。发布成功后别人就能这样装openclaw plugins install openclaw-plugin-hello-ts装完openclaw gateway restart即可生效。如果你长期做插件开发、经常要跑 Agent 任务可以考虑 Coding Plan入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它适合需要持续调用模型能力的场景比每次单独配 Key 省心。模型对话调试入口在 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc 控制台在 https://taotoken.net/console API Keys 管理在 https://taotoken.net/api-keys 。发布之后维护也有讲究。版本号遵循 semver改 bug 发 patch加功能发 minor破坏性变更发 major。README 里写清楚插件 id、安装命令、配置项说明和示例别人装的时候不用猜。我一般还会在 README 里放一段最小可运行的配置示例降低上手门槛。最后回到插件本身保持单一职责一个插件只做一件事所有异步操作加 try/catch可配置项放 configSchema密钥走环境变量工具和命令的描述写清楚方便 AI 理解。做到这些你的插件就不只是「能跑」而是「好用」。