1. 从零做一个带 AI 能力的 VsCode 插件,到底难在哪
很多人第一次听到「发布自己的 VsCode 插件」会觉得门槛很高,其实真正卡住新手的往往不是 TypeScript 语法,也不是 vsce 打包命令,而是插件里一旦要接 AI 能力,Key 管理、endpoint 配置、请求格式这几件事就开始互相打架。你要么在代码里硬编码一个 Key,要么让用户自己填一堆配置项,最后插件还没发布,光调试接口就耗掉一整天。
这篇就按「发布一个带 AI 能力的 VsCode 插件」这条主线走一遍:用 TypeScript 写命令和配置,在 package.json 里声明贡献点,用 TaoToken 统一 Key 和 API 通道把 AI 调用收口到一个 endpoint,最后用 vsce 打包、本地安装验证。目标很明确——你跟着做完,能独立完成一次可用的插件发布,而不是停在 Hello World。
先说清楚这个插件能做什么、适合谁。它是一个「选中代码 → 右键 → 让 AI 解释/润色」的小工具,命令面板里也能触发。适合两类人:一是想学 VsCode 插件完整发布流程的开发者,二是手里已经有零散 AI 脚本、想把它收进编辑器里的同学。核心检索词就是 VsCode 插件发布、vsce 打包、package.json 贡献点配置,这三个动作贯穿全文。
为什么 AI 能力要用统一 Key 通道?因为插件发布后是给别人用的,你不能把自己的 Key 写进代码。TaoToken 在这里的角色是提供一个兼容 OpenAI 风格的 API 通道,插件只需要认一个 Base URL 和一个 Key,模型 ID 通过配置项暴露给用户,这样插件代码里不出现任何厂商私有协议,换模型也不用改代码。我试过把 endpoint 抽成配置项之后,调试阶段和发布阶段的代码几乎不用动,省了很多返工。
下面按「原问题与场景 → TaoToken 前置 → 可复制配置 → 验证请求 → 常见错排查 → 收口」的顺序走,每一步都给可复制的片段和具体动作。你不需要先看完再动手,可以边看边在编辑器里敲。
2. TaoToken 前置准备:统一 Key 与 API 通道怎么配
在写插件代码之前,先把 AI 通道准备好,否则后面 F5 调试时命令能弹出来但请求发不出去,排查起来很浪费时间。TaoToken 的官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数,配置里就写这个干净的地址。
第一步是拿 Key。登录后进控制台,路径是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console ,在 API Keys 页面创建一个新 Key,复制出来先存到本地临时文件里。这个 Key 只用于你本地调试,绝对不要提交到 Git,也不要在插件代码里写死。发布出去的插件应该是让用户自己填 Key,这一点后面配置项会体现。
第二步是确认模型 ID。不同模型在请求体里的 model 字段不一样,你可以在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat 先手动发一条消息,确认通道通、模型可用,再回到插件里写代码。这一步别省,我踩过的坑就是插件里 model 写错,报错信息只显示 reading choices 相关,绕了半天才发现是模型名不对。
第三步是理解请求格式。TaoToken 的 API 兼容 OpenAI 的 chat completions 风格,请求地址是 https://taotoken.net/api/v1/chat/completions,请求头带 Authorization: Bearer <你的Key> 和 Content-Type: application/json,请求体是 {"model": "...", "messages": [...]}。插件里用 Node 的 fetch 或 https 模块发这个请求就行,不需要额外 SDK,依赖越少打包越干净。
这里要强调一个设计原则:插件代码里只出现「Base URL + Key + Model ID」三件套,其中 Base URL 可以写死成 TaoToken 的 API 地址,Key 和 Model ID 通过 VsCode 的配置项(configuration)暴露给用户。这样插件发布后,任何用户装上、填自己的 Key 就能用,你也不用为每个用户维护 Key。这个三件套的写法在后面的 settings 片段里会完整给出。
如果你后面要做更复杂的编码类插件,比如让 AI 直接改文件、跑多轮 Agent,可以了解下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan ,但本篇的插件是轻量调用,用普通 API Key 就够了,不必上更重的方案。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc ,遇到请求格式疑问可以对照看。
3. 可复制配置:package.json 贡献点与 settings 片段
这一节是全文的核心,所有片段都可以直接复制。先创建项目。最省事的方式是用官方脚手架,全局装好之后执行生成命令:
npm i -g yo generator-code
yo code
交互里依次选 New Extension (TypeScript)、填插件名(比如 ai-code-helper)、填 identifier、填描述、是否初始化 Git 选是、打包工具选 esbuild 或不用、包管理器选 npm。生成完进目录装依赖:
cd ai-code-helper
npm install
接下来改 package.json。VsCode 插件的 package.json 不只是 npm 清单,它还是插件的「能力声明书」,commands、configuration、activationEvents 都在这里声明。下面是一个可直接用的片段,注意 publisher 字段必须和你在 Marketplace 创建的 publisher ID 一致,否则 vsce 发布会报错:
{
"name": "ai-code-helper",
"displayName": "AI Code Helper",
"description": "选中代码,用统一 Key 通道调用 AI 解释或润色",
"version": "0.0.1",
"publisher": "your-publisher-id",
"engines": { "vscode": "^1.85.0" },
"categories": ["Other"],
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{ "command": "aiCodeHelper.explain", "title": "AI: 解释选中代码" },
{ "command": "aiCodeHelper.polish", "title": "AI: 润色选中代码" }
],
"menus": {
"editor/context": [
{ "command": "aiCodeHelper.explain", "when": "editorHasSelection", "group": "navigation" }
]
},
"configuration": {
"title": "AI Code Helper",
"properties": {
"aiCodeHelper.apiKey": {
"type": "string",
"default": "",
"description": "TaoToken API Key,在控制台创建"
},
"aiCodeHelper.model": {
"type": "string",
"default": "gpt-4o-mini",
"description": "模型 ID,按通道支持的模型填写"
},
"aiCodeHelper.baseUrl": {
"type": "string",
"default": "https://taotoken.net/api/v1",
"description": "API Base URL,一般不用改"
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/vscode": "^1.85.0",
"@types/node": "18.x",
"typescript": "^5.3.3"
}
}
几个容易忽略的点。activationEvents 留空是没问题的,因为命令和菜单本身就会触发激活,不需要再写 onCommand。menus.editor/context 里的 when: "editorHasSelection" 保证只有选中文本时右键菜单才出现,体验更干净。configuration 里的三个属性就是前面说的三件套,用户装完插件在设置里搜 aiCodeHelper 就能填。
然后是 extension.ts,命令注册和 AI 调用都写在这里。下面这段可以直接替换脚手架生成的同名文件:
import * as vscode from 'vscode';
async function callAI(prompt: string): Promise<string> {
const cfg = vscode.workspace.getConfiguration('aiCodeHelper');
const apiKey = cfg.get<string>('apiKey') || '';
const model = cfg.get<string>('model') || 'gpt-4o-mini';
const baseUrl = cfg.get<string>('baseUrl') || 'https://taotoken.net/api/v1';
if (!apiKey) {
throw new Error('请先在设置中填写 aiCodeHelper.apiKey');
}
const res = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model,
messages: [{ role: 'user', content: prompt }]
})
});
if (!res.ok) {
const text = await res.text();
throw new Error(`请求失败 ${res.status}: ${text}`);
}
const data: any = await res.json();
return data.choices?.[0]?.message?.content ?? '(无返回内容)';
}
export function activate(context: vscode.ExtensionContext) {
const explain = vscode.commands.registerCommand('aiCodeHelper.explain', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) { return; }
const code = editor.document.getText(editor.selection);
if (!code) { vscode.window.showWarningMessage('请先选中代码'); return; }
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: 'AI 解释中' },
async () => {
try {
const out = await callAI(`请解释这段代码:\n${code}`);
const doc = await vscode.workspace.openTextDocument({ content: out, language: 'markdown' });
vscode.window.showTextDocument(doc, { viewColumn: vscode.ViewColumn.Beside });
} catch (e: any) {
vscode.window.showErrorMessage(e.message);
}
}
);
});
context.subscriptions.push(explain);
}
export function deactivate() {}
这段代码的关键设计是 callAI 把三件套从配置里读出来,请求地址拼成 ${baseUrl}/chat/completions,默认 baseUrl 就是 TaoToken 的 API 地址。用户只需要填 Key 和模型 ID,Base URL 一般不动。withProgress 让请求期间有进度提示,避免用户以为卡死。返回内容用只读文档在侧边打开,不污染用户当前文件。
4. 验证请求与成功结果:F5 调试到 vsce 打包
代码写完先别急着打包,用 F5 在本地跑通。在 VsCode 里打开项目,按 F5,会弹出一个标题带「扩展开发宿主」的新窗口。在这个新窗口里随便打开一个文件,选中几行代码,右键应该能看到「AI: 解释选中代码」。点它,第一次会提示你先填 Key,去设置里搜 aiCodeHelper,把 Key 和模型 ID 填上,再点一次。
成功的结果是:右下角出现「AI 解释中」的进度通知,几秒后侧边打开一个 Markdown 文档,里面是模型返回的解释。如果这一步通了,说明 endpoint、Key、模型 ID 三件套都对,插件逻辑没问题。这一步是整个流程里最值得多花时间的,因为后面打包发布只是把已经跑通的东西封起来。
调试通过后开始打包。先全局装 vsce:
npm i -g @vscode/vsce
打包前检查两件事:package.json 里 publisher 已填、README.md 有内容(vsce 会警告空 README)。然后执行:
vsce package
如果报 Missing publisher name,就是 publisher 没填;报 README.md not found,补一个 README 即可。成功后会生成 ai-code-helper-0.0.1.vsix 文件。这个 vsix 就是可分发的插件包,先本地安装验证:
code --install-extension ai-code-helper-0.0.1.vsix
装完重启 VsCode,在扩展列表里能看到它,命令面板输入「AI: 解释」能触发,设置里能填 Key,就说明打包产物可用。这一步验证的是「别人拿到 vsix 装上能不能用」,和你 F5 调试是两回事,别跳过。
本地验证没问题后,去 Marketplace 发布。打开 https://marketplace.visualstudio.com/manage ,用微软账号登录,创建一个 publisher,ID 要和 package.json 里一致。然后拿到 Personal Access Token(在 Azure DevOps 里生成,scope 选 Marketplace 的 Manage),执行:
vsce login your-publisher-id
vsce publish
vsce publish 会自动递增版本号并上传,几分钟后就能在 Marketplace 搜到你的插件。如果只想先传 vsix 不命令行发布,也可以在 manage 页面点 New extension 手动上传 vsix,效果一样。
5. 本篇常见错排查:401、local proxy failed、reading choices
调试和发布过程中有几类报错特别高频,这里按真实报错对照给排查路径。
第一类是 401 Unauthorized。插件里表现为 请求失败 401: {"error":...}。原因基本是 Key 没填、填错、或者填了但没保存。排查顺序:打开设置搜 aiCodeHelper.apiKey 确认非空;确认 Key 是从控制台 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys 复制完整、没有多余空格;确认请求头是 Bearer <Key> 格式。还有一种情况是 Key 被删了或过期,重新建一个即可。
第二类是 local proxy failed 或连接超时。这类报错通常出现在请求根本没发出去的时候,检查 baseUrl 是否被改成了带路径的地址,正确写法是 https://taotoken.net/api/v1,代码里再拼 /chat/completions。如果公司网络有代理设置,确认 VsCode 的 proxy 配置没有干扰。注意不要在任何配置里写非官方通道地址,统一走 TaoToken 的 API 入口最稳。
第三类是 Cannot read properties of undefined (reading 'choices')。这个报错说明 res.json() 返回的结构里没有 choices,常见原因是 model 字段写错导致返回了错误对象,或者请求体格式不对。排查:先在模型对话页面用同样的 model 手动发一条,确认模型 ID 有效;再检查请求体是不是 messages 数组、role 和 content 拼写正确。我踩过的坑就是把 messages 写成了 message,返回体里自然没有 choices。
第四类是 vsce 打包阶段的报错。ERROR Missing publisher name 对应 publisher 没填;ERROR Make sure to edit the README.md file 对应 README 为空;ERROR Extension entrypoint(s) missing 对应 main 指向的 out/extension.js 不存在,先跑 npm run compile 再打包。还有一类是 engines.vscode 版本高于本地 VsCode 版本,F5 调试会直接失败,把 engines 调到不高于本地版本即可。
第五类是发布后用户装了但命令不出现。检查 contributes.commands 里的 command ID 和 registerCommand 里的字符串是否完全一致,大小写都算。菜单不出现则检查 menus 的 when 条件,editorHasSelection 要求真的有选中文本。
6. 收口:把三件套固定下来,后续迭代就顺了
走到这里,你已经完成了一次完整的插件发布:TypeScript 写命令、package.json 声明贡献点和配置、TaoToken 统一 Key 通道调 AI、F5 调试、vsce 打包、本地安装验证、Marketplace 发布。回头看,真正让流程顺起来的是把「Base URL + Key + Model ID」三件套固定成配置项,代码里不出现任何硬编码密钥,换模型只改设置不改代码。
后续迭代时,新增命令就在 package.json 的 commands 里加一条、extension.ts 里注册一个,AI 调用复用 callAI 函数即可。如果要做更重的编码类能力,比如多轮对话或 Agent 式改文件,可以看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan 的方案;接入细节对照文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc ,Key 管理仍在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys 。发布前记得把 README 写清楚「用户需要自己填 Key」,这是插件能不能被别人用起来的关键一步。



被折叠的 条评论
为什么被折叠?



