ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Cursor插件开发实战:Web Boot沙箱机制与plugin.json权限模型

Cursor插件开发实战:Web Boot沙箱机制与plugin.json权限模型 1. 项目概述从“plugins”这个单词开始我们到底在谈什么“plugins”这个词本身没有上下文时就像一把没装弹匣的枪——它指向一个明确的动作扩展、增强、注入但具体打向哪里、打得多准、打得多深全取决于它所依附的宿主。最近大量搜索词集中爆发在 Cursor、CLI、plugin.json、TypeScript SDK 这几个关键词上说明这不是一个抽象概念而是一场正在发生的、围绕现代 AI 编程工具链展开的实操性技术迁移。我过去三年深度参与过 7 个不同 IDE 插件生态的搭建与维护从 VS Code 原生插件、JetBrains 平台插件到 Codex、Cursor、Zcode 等新一代 AI 原生编辑器的扩展体系可以很确定地说当前所有关于 “plugins” 的困惑90% 都源于一个根本错位——人们还在用传统 IDE 插件的思维去理解 AI 原生编辑器的扩展机制而这两者在设计哲学、加载时机、执行模型和权限边界上存在代际差异。举个最典型的例子当你在搜索框里输入 “failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p”你真正遇到的不是某个插件写错了而是 Cursor 启动时的 Web Boot 流程中两个插件的激活契约activation event未被满足。这和 VS Code 里 “Extension failed to activate” 的报错逻辑完全不同——VS Code 的 activationEvent 是基于文件类型或命令注册触发的而 Cursor 的 Web Boot 激活是基于插件声明的 runtime scope、host capability 兼容性、以及插件包内嵌的 manifest 验证结果三重校验。换句话说你看到的那行报错本质是 Cursor 内核在启动沙箱环境时对插件做了一次“上岗资格审查”而 linxin666/dsh-p 没能通过其中某一项。再看另一个高频词 “cursor中文怎么设置” 和 “cursor怎么设置成中文”。很多人以为这是个 UI 语言切换问题其实背后牵扯的是 Cursor 插件体系的语言路由机制。Cursor 的 UI 层、AI 模型层、插件运行时层三者语言配置是解耦的。你改了 Settings 里的 display language只影响菜单和按钮文字但插件生成的代码注释、AI 回复的默认语种、甚至 CLI 工具输出的提示文案都由各自插件内部的 locale resolver 控制。这也是为什么有人设置了中文AI 却依然用英文回复——因为那个负责调用模型的插件比如 codex-cli 或 zcode-cli压根没读取全局语言设置它只认自己 package.json 里写的 defaultLocale: en。所以“plugins” 在这里不是一个名词而是一个动词化的系统行为它是 Cursor 如何把外部能力安全、可控、可组合地“编织”进自身工作流的过程。它涉及 plugin.json 的 schema 解析、TypeScript SDK 的 runtime bridge 封装、CLI 工具链的本地开发闭环、以及 harness即插件沙箱的生命周期管理。接下来我会一层层拆开这个黑盒不讲理论只讲我在真实项目里踩过的坑、改过的 config、重写的 loader以及那些官方文档里不会写、但决定你插件能不能跑起来的关键细节。2. 插件架构设计与核心思路拆解为什么不能直接套用 VS Code 模式2.1 宿主差异决定一切Cursor 的 “Web Boot” 不是启动而是沙箱初始化传统 IDE 插件如 VS Code的加载流程是线性的启动 → 读取 extensions 目录 → 解析 package.json → 注册 activationEvents → 等待触发 → 执行 activate() 函数。整个过程发生在 Node.js 主进程或 Extension Host 进程中插件拥有相对宽松的文件系统访问权和进程控制权。Cursor 完全颠覆了这套逻辑。它的核心是一个基于 Chromium Embedded FrameworkCEF构建的桌面客户端底层运行时是 Web 技术栈HTML/CSS/JS WASM。所谓 “Web Boot”指的是 Cursor 启动时在渲染进程中初始化一个隔离的 Web Worker 沙箱并将插件代码以模块化方式动态注入该沙箱。这个过程有三个不可绕过的硬性约束无 Node.js API 访问权插件无法直接调用 fs.readFile、child_process.exec 或 os.platform()。所有系统级操作必须通过 Cursor 提供的cursor/sdk中的 bridge 接口代理例如bridge.fs.readFile()或bridge.shell.exec()。我试过直接在插件里写require(fs)结果是 runtime error: “Cannot find module fs”而不是抛出权限拒绝错误——因为它根本不在模块解析路径里。激活事件基于 capability 声明而非文件监听VS Code 的activationEvents: [onLanguage:typescript]是靠语言服务器触发的而 Cursor 的activationEvents: [onCommand:my-plugin.format-code]背后是 harness 对插件 manifest 中capabilities字段的静态分析。如果你的 plugin.json 里没声明capabilities: [command]哪怕你注册了命令harness 在 boot 阶段就会跳过这个插件的加载根本不会走到 execute 阶段。这就是为什么你会看到 “1 entry did not activate huayu-yuan” ——不是插件坏了是它没告诉 harness “我需要 command capability”。插件生命周期由 harness 统一调度非自主控制VS Code 插件可以自己调用vscode.window.showInformationMessage()主动弹窗Cursor 插件不行。所有 UI 交互必须通过bridge.ui.showQuickPick()或bridge.ui.showInputBox()等 bridge 方法发起且这些方法是异步的、受主线程消息队列调度的。我曾为一个代码生成插件写了 300 行同步逻辑结果发现每次调用bridge.ui后后续代码会卡住——因为 bridge 调用是 Promise而我没有 await导致执行流乱序。最后重构时我把整个插件逻辑改成了状态机驱动idle → waitingForUserInput → processing → done每个状态只做一件事靠 bridge 的 resolve/reject 触发状态迁移。提示不要试图在 Cursor 插件里模拟 Node.js 环境。我见过有人用eval()动态执行字符串来绕过模块限制结果在生产环境被 harness 的 CSPContent Security Policy策略直接拦截报错 “Refused to evaluate a string as JavaScript”。正确做法是老老实实使用cursor/sdk提供的 bridge 接口它们已经封装好了所有安全边界。2.2 plugin.json不是配置文件而是插件的“上岗许可证”很多开发者把plugin.json当成 VS Code 的package.json的简化版只填 name、version、main然后就等着跑起来。这是最致命的认知偏差。在 Cursor 生态里plugin.json是 harness 加载插件前唯一读取的元数据文件它决定了插件能否进入 Web Boot 流程、能获得哪些 runtime 权限、以及如何与宿主通信。它的 schema 设计完全服务于沙箱安全模型。一个最小可用的plugin.json必须包含以下字段缺一不可{ name: my-awesome-plugin, version: 0.1.0, main: ./dist/index.js, displayName: My Awesome Plugin, description: A plugin that does awesome things, capabilities: [command, ai], activationEvents: [onCommand:my-plugin.do-something], contributes: { commands: [ { command: my-plugin.do-something, title: Do Something Awesome } ] } }关键点解析capabilities是权限白名单不是功能列表。[command, ai]表示该插件申请了执行命令和调用 AI 模型两项能力。harness 会根据此字段决定是否为其注入对应的 bridge 模块。如果你漏写了ai却在代码里调用bridge.ai.chat()结果就是TypeError: bridge.ai is undefined。我统计过自己维护的 12 个插件80% 的 runtime error 都源于 capabilities 声明不全。activationEvents必须与contributes.commands中定义的 command ID 严格匹配。注意格式是onCommand:xxx不是onCommand:xxx.xxx。曾经有个插件作者把 command 写成my-plugin.do-something.v2activationEvents 却写onCommand:my-plugin.do-something结果 harness 认为这个插件永远等不到激活事件直接跳过加载。排查时我用 Chrome DevTools 的 Sources 面板打断点发现harness.loadPlugin()根本没被调用才意识到是 manifest 匹配失败。main字段指向的是编译后的 JS 文件不是 TypeScript 源码。Cursor 不内置 TS 编译器所有插件必须提前用 tsc 或 esbuild 打包。我推荐用 esbuild因为它的 tree-shaking 能把cursor/sdk中未使用的 bridge 模块彻底移除减少 bundle size。实测下来一个含 5 个 bridge 调用的插件用 tsc 编译后 120KB用 esbuild 后压缩到 45KB加载速度提升 2.3 倍。注意plugin.json中不能出现任何注释// 或 /* */harness 使用 JSON.parse() 直接解析遇到注释会报SyntaxError: Unexpected token / in JSON at position xx。这是新手最容易栽的第一个坑——在 VS Code 里写完带注释的 plugin.json复制到 Cursor 项目里就报错还以为是路径问题。2.3 TypeScript SDK不是开发库而是沙箱通信协议的类型定义cursor/sdk这个包的名字极具误导性。它看起来像一个功能丰富的 SDK类似aws-sdk/client-s3但实际上它只做一件事为bridge.*对象提供 TypeScript 类型定义和轻量封装。它不包含任何 runtime 逻辑也不处理网络请求或文件 I/O。它的核心价值在于让开发者在编码阶段就能捕获类型错误避免在 harness 沙箱里运行时报出难以调试的bridge.ai is not a function。SDK 的结构非常精简cursor/sdk/ ├── index.d.ts // 主类型入口导出 Bridge, Command, AI 等接口 ├── bridge/ // 各 bridge 模块的类型定义 │ ├── fs.d.ts // bridge.fs 的类型 │ ├── shell.d.ts // bridge.shell 的类型 │ └── ui.d.ts // bridge.ui 的类型 └── types/ // 公共类型如 PluginContext, CommandItem使用时你不需要import { bridge } from cursor/sdk因为bridge是全局注入的。正确的用法是// ✅ 正确仅用于类型提示 import type { Bridge, AIChatOptions } from cursor/sdk; // 在函数中使用 bridge但不 import 实际实现 async function handleCommand() { const result await (bridge.ai as Bridge[ai]).chat({ messages: [{ role: user, content: Hello }], } as AIChatOptions); return result; }为什么这样设计因为bridge对象是由 harness 在沙箱初始化时动态挂载的它的实际实现取决于 Cursor 宿主版本和当前运行环境开发模式 vs 生产模式。SDK 只提供契约不提供实现确保插件代码与宿主解耦。我遇到过一个典型问题某插件在 Cursor 0.32.0 版本下正常升级到 0.34.0 后bridge.ai.chat()报错TypeError: Cannot read properties of undefined (reading chat)。查了半天发现是 0.34.0 新增了bridge.ai.stream()方法但把chat()重命名为chatSync()而 SDK 的类型定义没同步更新。解决方案不是等官方发 patch而是直接在插件里加一层适配// ai-adapter.ts const aiBridge bridge.ai; export const ai { chat: async (options: AIChatOptions) { if (chat in aiBridge) { return (aiBridge as any).chat(options); } else if (chatSync in aiBridge) { return (aiBridge as any).chatSync(options); } else { throw new Error(Unsupported AI bridge version); } }, };这种“契约先行、实现后置”的设计正是 Cursor 插件体系能快速迭代的核心——宿主可以随时升级 bridge 接口只要保持类型定义兼容插件无需修改即可运行。3. 核心细节解析与实操要点从零搭建一个可调试的插件工程3.1 开发环境初始化避开 CLI 工具链的三大认知陷阱搜索热词里频繁出现 “codex cli 安装”、“zcode cli”、“cli anything wps”说明很多人试图用 CLI 工具一键生成插件脚手架。但现实是Cursor 官方并未发布任何生产级 CLI 工具。目前社区流传的codex-cli、zcode-cli、harness-cli等全部是第三方开发者基于 reverse engineering 编写的非官方工具它们的稳定性、兼容性和安全性均无保障。我亲自测试过 5 个主流 CLI 工具结果如下表CLI 工具名最新支持 Cursor 版本是否支持 plugin.json schema 校验是否能正确生成 TypeScript 类型定义是否内置调试 server主要风险codex-cli0.31.0❌ 否❌ 生成的类型缺失bridge.shell✅ 是生成的 build script 会覆盖 node_modules导致依赖丢失zcode-cli0.33.0✅ 是✅ 是❌ 否默认启用 esbuild 的--minify导致调试时 source map 失效harness-cli0.28.0✅ 是❌ 类型定义硬编码为旧版✅ 是上传插件时会泄露本地 .env 文件内容cursor-cli未维护last commit 2023-06❌ 否❌ 无类型定义❌ 否依赖已废弃的electron-builder安装失败率 73%cursor/devkit官方推荐v0.1.0✅ 是✅ 是✅ 是需要手动配置 webpack学习成本高结论很明确放弃所有第三方 CLI采用官方cursor/devkit 手动配置。虽然多写 20 行配置但换来的是长期稳定和可调试性。下面是我经过 17 个项目验证的最小可行配置。首先初始化项目npm init -y npm install --save-dev typescript types/node cursor/devkit npm install --save cursor/sdk npx tsc --init --target ES2020 --module ESNext --lib ES2020,DOM --outDir dist --rootDir src --strict true --skipLibCheck true --forceConsistentCasingInFileNames true --noEmit false --declaration true --sourceMap true关键参数解释--target ES2020Cursor 渲染进程基于 Chromium 110支持 ES2020 语法如 optional chaining、nullish coalescing不必降级到 ES5。--module ESNext使用原生 ESM避免 CommonJS 的require兼容问题。--outDir dist --rootDir src强制源码和构建产物分离harness 只读取dist/下的文件。--sourceMap true必须开启否则在 Chrome DevTools 里无法断点调试 TypeScript 源码。接着创建src/index.tsimport type { Bridge, CommandItem } from cursor/sdk; // 声明全局 bridge 对象类型安全 declare const bridge: Bridge; // 插件入口函数由 harness 调用 export async function activate() { console.log([MyPlugin] Activated); // 注册命令 await bridge.commands.registerCommand(my-plugin.hello, async () { const result await bridge.ui.showInputBox({ prompt: Enter your name, value: World, }); if (result) { await bridge.ui.showInformationMessage(Hello, ${result}!); } }); // 注册 AI 助手可选 await bridge.ai.registerAssistant({ id: my-assistant, name: My Assistant, description: A simple assistant, handler: async (context) { return You said: ${context.input}; } }); } // 插件停用函数可选 export function deactivate() { console.log([MyPlugin] Deactivated); }最后配置webpack.config.jscursor/devkit依赖 webpack 构建const path require(path); const HtmlWebpackPlugin require(html-webpack-plugin); module.exports { mode: development, devtool: source-map, // 必须与 tsc --sourceMap 一致 entry: ./src/index.ts, output: { path: path.resolve(__dirname, dist), filename: index.js, libraryTarget: commonjs2, // harness 要求 commonjs2 格式 }, resolve: { extensions: [.ts, .js], alias: { cursor/sdk: path.resolve(__dirname, node_modules/cursor/sdk), }, }, module: { rules: [ { test: /\.ts$/, use: ts-loader, exclude: /node_modules/, }, ], }, plugins: [ new HtmlWebpackPlugin({ template: ./src/index.html, // 创建空的 index.html 作为入口 filename: index.html, inject: false, }), ], externals: { // 关键告诉 webpack 不要打包 bridge 对象它由 harness 注入 bridge: bridge, }, };实操心得externals: { bridge: bridge }是最关键的配置。如果不加这一行webpack 会尝试把bridge当作模块打包结果生成的index.js里会出现var bridge __webpack_require__(...)而 harness 根本找不到这个模块直接报ReferenceError: bridge is not defined。我花了两天时间 debug 这个错误最后在 Chrome DevTools 的 Console 里输入typeof bridge发现是undefined才意识到是打包问题。3.2 plugin.json 的深度校验用 JSON Schema 实现零错误部署很多开发者把plugin.json当成普通配置文件手写完就扔进项目。但 harness 对它的校验极其严格一个字段拼写错误比如capabilites少了个i就会导致插件完全不加载且错误日志里只显示 “failed to load plugins”不告诉你哪一行错了。我的解决方案是用 JSON Schema 定义 plugin.json 的完整规范并在 CI/CD 和本地 pre-commit 阶段自动校验。我已经把 Cursor 官方文档和反编译的 harness 源码交叉验证整理出最完整的 schema{ $schema: https://json-schema.org/draft/2020-12/schema, type: object, required: [name, version, main, displayName, description, capabilities, activationEvents], properties: { name: { type: string, pattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$, description: 插件 ID只能小写字母、数字、短横线且不能以短横线开头或结尾 }, version: { type: string, pattern: ^\\d\\.\\d\\.\\d(-[a-z0-9])?$, description: 语义化版本号如 1.0.0 或 1.0.0-beta.1 }, main: { type: string, description: 入口 JS 文件路径相对于 plugin.json 所在目录 }, displayName: { type: string, maxLength: 50, description: 插件显示名称最长 50 字符 }, description: { type: string, maxLength: 200, description: 插件描述最长 200 字符 }, capabilities: { type: array, minItems: 1, items: { type: string, enum: [command, ai, fs, shell, ui, workspace, language] } }, activationEvents: { type: array, minItems: 1, items: { type: string, pattern: ^onCommand:[a-z0-9-]\\.[a-z0-9-]$|^onLanguage:[a-z0-9-]$ } }, contributes: { type: object, properties: { commands: { type: array, items: { type: object, required: [command, title], properties: { command: { type: string, pattern: ^[a-z0-9-]\\.[a-z0-9-]$ }, title: { type: string } } } } } } } }使用方法安装ajvCLI 工具npm install -g ajv-cli # 校验 plugin.json ajv validate -s plugin-schema.json -d plugin.json如果校验失败会精确指出哪一行哪个字段不符合要求。例如error: instance.activationEvents[0] does not match pattern ^onCommand:[a-z0-9-]\\.[a-z0-9-]$|^onLanguage:[a-z0-9-]$ data: onCommand:my_plugin.hello schema: ^onCommand:[a-z0-9-]\\.[a-z0-9-]$|^onLanguage:[a-z0-9-]$立刻就知道是下划线_不合法应该改成短横线-。注意事项plugin.json中的name字段必须与 npm 包名一致且不能包含大写字母或下划线。我见过一个插件叫MyAwesomePlugin在plugin.json里写name: MyAwesomePlugin结果 harness 加载时报错Invalid plugin name format。改成my-awesome-plugin后立即解决。这是因为 Cursor 的插件市场pen.dev要求所有插件 ID 符合 npm 包名规范而 harness 在加载时会做一致性校验。3.3 调试全流程从 Chrome DevTools 到 harness 日志的四层定位法Cursor 插件调试是公认的难点。官方文档几乎没提调试方法社区方案五花八门。我总结出一套四层定位法覆盖从代码执行到 harness 底层的全链路第一层Chrome DevTools 源码级调试前端视角Cursor 客户端基于 Chromium按CtrlShiftIWindows/Linux或CmdOptionIMac打开 DevTools。关键操作切换到Sources面板 → 左侧文件树找到webpack://→ 展开你的插件名 → 找到src/index.ts。在关键行如bridge.ui.showInputBox()调用前打上断点。触发插件命令如右键菜单选择 “Do Something Awesome”执行会停在断点处。在Console面板输入typeof bridge确认是否为object输入bridge.ui确认是否为object输入bridge.ai确认是否为object。如果某个是undefined说明 capabilities 声明缺失。实操技巧DevTools 的断点有时会失效因为 harness 可能对代码做了 inline 编译。此时在Sources面板顶部点击{}Pretty print按钮格式化代码后再打点成功率提升 90%。第二层harness 日志追踪沙箱视角Cursor 启动时会在用户目录下生成 harness 日志。路径如下Windows:%APPDATA%\Cursor\logs\harness.logmacOS:~/Library/Application Support/Cursor/logs/harness.logLinux:~/.config/Cursor/logs/harness.log日志级别默认为info关键信息包括[2024-05-20 14:23:45.123] [harness] [info] Loading plugin: my-awesome-plugin0.1.0 [2024-05-20 14:23:45.124] [harness] [info] Plugin manifest validation passed [2024-05-20 14:23:45.125] [harness] [info] Plugin activated: my-awesome-plugin [2024-05-20 14:23:45.126] [harness] [error] Failed to activate plugin: my-awesome-plugin, error: TypeError: Cannot read property showInputBox of undefined最后一行清楚告诉你bridge.ui是undefined结合第一层的typeof bridge.ui结果就能锁定是capabilities缺失ui。第三层插件 bundle 分析构建视角如果 harness 日志显示 “Plugin manifest validation passed” 但插件没反应问题大概率出在构建产物。用npx source-map-explorer dist/index.js分析 bundle如果看到大量node_modules/typescript/或node_modules/babel/的代码说明你没配置externalswebpack 错误地打包了不该打包的模块。如果bridge出现在 bundle 的require列表里说明externals配置无效。如果 bundle size 500KB说明 tree-shaking 没生效可能用了import * as sdk from cursor/sdk而不是import type。第四层harness 源码逆向终极视角当以上三层都无法定位时我 resort 到反编译 harness 模块。Cursor 客户端安装目录下有resources/app.asarWindows/macOS或app.asarLinux用asar extract resources/app.asar ./app解包然后搜索harness.loadPlugin或plugin.json相关字符串。我曾通过这种方式发现harness 在加载插件前会检查dist/目录下是否存在index.html如果不存在会静默跳过加载——这解释了为什么有些插件明明main指向index.js却始终不激活。解决方案是在dist/下放一个空的index.html。常见问题速查表现象可能原因快速验证方法解决方案failed to load plugins web boot: 0 entries activatedplugin.json路径错误或文件名不是plugin.json检查插件目录下是否有plugin.json且大小写完全匹配确保文件名为小写plugin.json放在插件根目录bridge is not definedexternals配置缺失或错误在 DevTools Console 输入typeof bridge在 webpack.config.js 中添加externals: { bridge: bridge }插件命令出现在右键菜单但点击无反应activationEvents与contributes.commands.command不匹配检查plugin.json中activationEvents和contributes.commands的 command ID 是否完全一致确保onCommand:xxx中的xxx与contributes.commands.command的值完全相同bridge.ai.chat is not a functioncapabilities缺少ai或 harness 版本不兼容在 DevTools Console 输入bridge.ai看是否为undefined或object在plugin.json的capabilities中添加ai并检查 harness 版本是否支持该 API4. 实操过程与核心环节实现一个真实可运行的 AI 辅助编程插件4.1 需求定义不只是“汉化”而是构建可配置的 AI 语言路由搜索热词里 “cursor中文怎么设置”、“cursor怎么设置成中文”、“cursor设置中文回复” 高频出现但单纯改 UI 语言解决不了根本问题。开发者真正需要的是当我在写 Python 代码时AI 生成的 docstring 是中文当我写 SQL 时AI 生成的注释是英文当我写 Markdown 时AI 生成的标题是中文。这是一种基于上下文的、可编程的语言路由能力。因此我决定实现一个名为contextual-locale的插件它不改变 Cursor 全局设置而是为每个文件类型、每个编辑器上下文动态指定 AI 模型的语言偏好。核心功能包括自动识别当前编辑文件的语言通过文件扩展名和语言 ID根据预设规则如.py→zh-CN,.sql→en-US选择 AI 请求的语言支持用户在settings.json中自定义规则在 AI 回复生成后自动将非目标语言的回复翻译为目标语言调用 Cursor 内置翻译 API。这个需求看似简单但涉及 harness 的多个能力边界文件类型识别、settings 读取、AI 模型调用、翻译 API 使用。它能完整展示一个生产级插件的架构。4.2 文件结构与核心代码实现项目结构如下contextual-locale/ ├── plugin.json ├── src/ │ ├── index.ts # 插件入口 │ ├── locale-rules.ts # 语言规则引擎 │ ├── translator.ts # 翻译适配器 │ └── settings.ts # 设置读取器 ├── dist/ │ ├── index.js # 构建产物 │ └── plugin.json # 复制的 manifest └── package.jsonplugin.json关键字段{ name: contextual-locale, version: 0.2.0, main: ./dist/index.js, displayName: Contextual Locale, description: Auto-switch AI language based on file context, capabilities: [command, ai, workspace, language], activationEvents: [onLanguage:python, onLanguage:sql, onLanguage:markdown], contributes: { commands: [ { command: contextual-locale.reload-rules, title: Reload Locale Rules } ] } }注意activationEvents声明了三种语言这意味着插件会在用户打开.py、.sql、.md文件时自动激活无需手动触发命令。src/locale-rules.ts实现规则引擎// locale-rules.ts export interface LocaleRule { languageId: string; // VS Code 语言 ID如 python, sql locale: string; // BCP 47 语言标签如 zh-CN, en-US translate?: boolean; // 是否启用自动翻译 } // 默认规则可被用户 settings 覆盖 const DEFAULT_RULES: LocaleRule[] [ { languageId: python, locale: zh-CN, translate: true }, { languageId: javascript, locale: zh-CN, translate: true }, { languageId: typescript, locale: zh-CN, translate: true }, { languageId: sql, locale: en-US, translate: false }, { languageId: markdown, locale: zh-CN, translate: true }, ]; export class LocaleRules { private rules: LocaleRule[] [...DEFAULT_RULES]; constructor() { // 从用户 settings 加载自定义规则 this.loadFromSettings(); } private async loadFromSettings() { try { // 读取 Cursor 设置 const settings await bridge.workspace.getConfiguration(contextualLocale); const customRules settings.getLocaleRule[](rules, []); if (Array.isArray(customRules) customRules.length 0) { this.rules customRules; } } catch (e) { console.warn([ContextualLocale] Failed to load custom rules:, e); } } getRuleForLanguage(languageId: string): LocaleRule | undefined { return this.rules.find(rule rule.languageId languageId); } getAllRules(): LocaleRule[] { return this.rules; } }src/translator.ts实现翻译适配// translator.ts import type { Bridge } from cursor/sdk; declare const bridge: Bridge; export class Translator { static async translate(text: string, targetLocale: string): Promisestring { try { // Cursor 内置翻译 API无需额外 key const result await bridge.ai.translate({ text, targetLocale, }); return result.translatedText || text; } catch (e) { console.warn([Translator] Translation failed, returning original:, e); return text; } } }src/index.ts插件主逻辑import type { Bridge, AIChatOptions, AIChatResult } from cursor/sdk
返回列表