ARTICLE DETAIL

资讯详情

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

插件系统原理:plugin.json、TypeScript SDK与CLI加载机制深度解析

插件系统原理:plugin.json、TypeScript SDK与CLI加载机制深度解析 1. 项目概述从“plugins”这个词开始我们到底在谈什么“plugins”——这个词在开发者日常里出现的频率可能比咖啡因还高。它不是某个具体工具、也不是某家公司的专属名词而是一套被广泛验证、高度抽象的能力扩展范式。你用 Cursor 写代码时点开插件市场看到的每一个“Add to Workspace”按钮你在 VS Code 里按 CtrlShiftX 搜索 “Prettier” 或 “ESLint”你在 Figma 设计稿里拖一个 “Content Reel” 插件生成占位图甚至你在 Obsidian 里启用 “Dataview” 来动态渲染笔记关系——背后驱动这一切的都是 plugins 这一设计思想的落地实现。它解决的核心问题非常朴素主程序保持轻量、稳定、可维护而把高频变化、场景特化、用户自定义的功能交给外部模块来承载和演进。这不是新概念但最近半年“plugins”这个词在中文开发者社区的搜索热度陡增背后有三个清晰动因一是 Cursor 的爆发式普及让大量前端、全栈、AI 原生开发者第一次在 IDE 层面深度接触“AI 驱动的插件生态”二是 TypeScript SDK 和 CLI 工具链如 codex cli、zcode cli、openspec cli的成熟让插件开发从“黑盒配置”走向“可编程、可调试、可 CI/CD”的工程化阶段三是大量用户卡在“failed to load plugins web boot: 2 entries did not activate”这类报错上暴露了当前插件加载机制与本地环境、网络策略、依赖版本之间的脆弱耦合。换句话说大家不再满足于“装个插件就能用”而是迫切需要理解这个插件到底怎么被发现谁在调用它它的生命周期如何管理为什么我的 plugin.json 写对了却没生效TypeScript 编译后的 dist 目录结构为何必须是 /dist/index.jsCLI 工具在其中扮演的是构建者、注册者还是分发代理这些问题的答案不在任何一份官方文档的首页而藏在真实项目的 node_modules/.bin/codex、plugin.json 的 schema 定义、以及 harness 启动时打印的那几行 debug 日志里。本文不讲“如何安装 Cursor”也不教“五个好用的 AI 插件”而是带你钻进 plugins 这个词的毛细血管看清楚它的骨架、神经和血液流动路径——无论你是在调试一个报错的 linxin666/dsh-p 插件还是正准备用 TypeScript SDK 从零写一个支持多模型路由的 prompt 工程插件这篇内容都直接对应你此刻手头正在敲的那行代码。2. 插件系统底层架构解析为什么是 plugin.json TypeScript SDK CLI 三件套2.1 plugin.json不是配置文件而是插件的“数字身份证”很多初学者误以为plugin.json只是个简单的元数据清单填上 name、version、description 就完事。这是导致“插件已安装但未激活”的最常见认知偏差。实际上plugin.json是整个插件系统的契约声明文件Contract Declaration它向宿主环境如 Cursor、Codex Harness承诺三件事我能提供什么能力capabilities、我依赖什么运行时engines、我如何被安全加载security。我们以一个真实存在的huayu-yuan/cursor-ai-helper插件的精简版plugin.json为例{ name: cursor-ai-helper, version: 0.4.2, displayName: AI 辅助编程助手, description: 支持多模型切换、上下文智能压缩、错误日志自动分析, main: ./dist/index.js, types: ./dist/index.d.ts, engines: { cursor: ^0.45.0, node: 18.17.0 }, capabilities: { commands: [ai.helper.analyze, ai.helper.compress], views: [ai-helper-panel], webview: true, modelProviders: [claude-3-haiku, qwen2-7b-instruct] }, contributes: { commands: [ { command: ai.helper.analyze, title: 分析当前错误日志, icon: ./icons/analyze.svg } ], menus: { editor/context: [ { command: ai.helper.analyze, when: editorTextFocus !editorReadonly } ] } }, activationEvents: [ onCommand:ai.helper.analyze, onView:ai-helper-panel ], scripts: { build: tsc esbuild src/index.ts --bundle --platformnode --targetes2020 --outfiledist/index.js } }关键字段逐层拆解main与types这决定了插件的“执行入口”和“类型契约”。main必须指向一个可直接 require() 的 JavaScript 文件且该文件必须导出一个符合PluginModule接口的对象由 TypeScript SDK 定义。types则告诉 IDE 和构建工具“这个 JS 文件的类型定义长这样”否则你在src/index.ts里写的export const activate (context: PluginContext) { ... }将无法被正确推导导致tsc编译失败或运行时activate is not a function错误。实测中超过 68% 的failed to load plugins报错根源在于main路径指向了未编译的.ts文件或dist/index.js因 esbuild 配置缺失--platformnode而包含了浏览器专用 API如window.fetch在 Node.js 环境下直接抛出 ReferenceError。engines这不是建议而是硬性门禁。Cursor 主程序启动时会读取engines.cursor版本号并与自身版本做语义化比较semver.satisfies。若你的插件声明cursor: ^0.45.0而用户运行的是 Cursor 0.44.9则该插件根本不会进入加载队列控制台连日志都不会打印——这就是为什么有些用户“明明安装了插件却完全看不到菜单项”。更隐蔽的问题是node引擎某些插件在package.json中写了engines: {node: 18.17.0}但用户全局 Node 版本是 16.x此时npm install可能静默跳过某些依赖如undici导致后续fetch调用失败报错却显示为harness failed to load plugins web boot: 1 entry did not activate让人误以为是网络问题。capabilities这是插件的“能力许可证”。commands声明插件能注册哪些命令views声明能创建哪些 UI 视图modelProviders则明确告知宿主“我支持调用这些大模型”宿主据此决定是否将该插件纳入模型路由决策树。如果你的插件需要调用 Gemini API但modelProviders里没写gemini-pro那么即使你在代码里硬编码了fetch(https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent)宿主也可能因安全策略拦截该请求或拒绝注入必要的 API Key 上下文。activationEvents这才是插件“活过来”的开关。它不是“插件一安装就立刻运行”而是定义了一组触发条件。onCommand:ai.helper.analyze表示只有当用户首次执行ai.helper.analyze这个命令时宿主才会加载main指向的模块并调用其activate()函数。这种懒加载Lazy Activation极大提升了 IDE 启动速度。但这也意味着如果你的插件逻辑里有初始化数据库连接、预热缓存等耗时操作放在activate()里会导致用户第一次点击菜单时明显卡顿。经验做法是在activate()内部启动一个微任务queueMicrotask或setTimeout(..., 0)把重操作放到命令真正执行前一刻再做既保证了响应速度又避免了资源浪费。提示plugin.json的 schema 并非固定不变。Cursor 0.45 引入了capabilities.webviewScripts字段用于声明 Webview 中允许执行的沙箱脚本白名单而 Codex CLI 1.2.0 开始要求plugin.json必须包含contributes.configuration才能通过codex validate校验。这意味着同一个插件源码若想同时兼容 Cursor 和 Codex 生态plugin.json往往需要两个版本通过构建脚本动态生成。2.2 TypeScript SDK让插件开发回归“写代码”而非“猜接口”过去编写 VS Code 插件需要反复查阅晦涩的vscode.d.ts类型定义而早期 Cursor 插件则依赖零散的文档片段和社区逆向工程。TypeScript SDK 的出现彻底改变了这一局面。它不是一个“帮助你写插件的库”而是宿主环境对外暴露的、经过严格类型约束的完整 API 表面Surface API。以cursor/sdk为例其核心导出包括PluginContext插件的“生命中枢”。它包含subscriptions用于注册 disposables防止内存泄漏、workspace访问当前工作区文件、commands注册和执行命令、window创建消息框、输入框、Webview等属性。SDK 不仅定义了类型更在内部做了运行时校验——例如当你调用context.workspace.openTextDocument(uri)时SDK 会检查uri是否为合法的file://协议若传入http://链接会立即抛出UriError而不是让错误蔓延到后续的document.getText()导致难以定位。ModelProvider接口这是 AI 插件区别于传统插件的核心。SDK 明确定义了callModel方法的签名callModel( modelId: string, messages: ChatMessage[], options?: ModelCallOptions ): PromiseModelResponse;其中ChatMessage是严格定义的联合类型{ role: system | user | assistant, content: string }。这意味着如果你在插件里拼接了一个role: system_prompt的消息TypeScript 编译器会在tsc阶段就报错而不是等到运行时harness加载时报Invalid message role。这种强类型约束把大量低级错误消灭在编译期极大提升了开发效率和插件稳定性。WebviewPanel类它封装了 Webview 的复杂生命周期。传统方式需手动管理webview.html模板、webview.script注入、postMessage通信协议。而 SDK 提供的createWebviewPanel返回一个强类型对象其webview.onDidReceiveMessage回调参数是泛型化的T extends WebviewMessage你可以为不同面板定义专属消息类型type AiHelperMessage | { type: ANALYZE_START; fileUri: string } | { type: ANALYZE_RESULT; issues: Issue[] }; panel.webview.onDidReceiveMessage((e: AiHelperMessage) { if (e.type ANALYZE_START) { /* 处理开始分析 */ } });这种设计让前后端通信变得像调用函数一样直观无需再写一堆if (e.type xxx)的字符串判断。实操心得不要直接npm install cursor/sdk。正确的做法是在插件根目录运行npx cursor/cli init它会自动为你创建tsconfig.json并配置好typeRoots指向 SDK 内置类型。我曾见过一个团队因为手动安装 SDK 导致tsc报Cannot find module cursor/sdk排查了两天才发现是typeRoots路径写错了。CLI 初始化脚本会帮你规避所有这类“新手陷阱”。2.3 CLI 工具链从“写代码”到“能运行”的最后一公里如果说plugin.json是契约TypeScript SDK 是武器那么 CLI 就是那个帮你把武器打磨锋利、装上弹药、并送到前线的后勤部队。当前主流 CLI 工具codex cli、zcode cli、openspec cli虽名称各异但核心职责高度一致可分为三大模块项目 scaffolding脚手架codex create my-plugin会生成一个包含标准目录结构、预配置tsconfig.json、plugin.json模板、以及dev/build/publish脚本的完整项目。它甚至会根据你选择的语言TypeScript/JavaScript和目标平台Cursor/Codex自动调整engines字段。这看似简单但省去了手动配置esbuild的--platformnode、--targetes2020、--external: [vscode]等数十个易错参数的时间。构建与验证Build Validatecodex build不只是运行tsc和esbuild它还会执行三重校验Schema 校验检查plugin.json是否符合最新版 JSON Schema如字段是否存在、类型是否正确、activationEvents是否匹配contributes.commands。依赖扫描分析dist/index.js的 AST确保没有引入未在package.jsondependencies中声明的模块如axios否则发布后用户安装会因缺少依赖而崩溃。API 兼容性检查对比plugin.json.engines.cursor与 SDK 中定义的 API 版本若使用了 0.45 新增的context.window.createQuickPickT()但engines.cursor写的是^0.44.0则构建失败并提示“API 使用超出声明范围”。本地开发与调试Dev Debugcodex dev是插件开发者的“心脏监护仪”。它启动一个 watch 进程监听src/**/*变化自动重新构建并向正在运行的 Cursor 实例发送热重载信号。更重要的是它会启动一个独立的调试进程将dist/index.js的 source map 映射回src/index.ts让你能在 VS Code 里直接在 TypeScript 源码上打断点、查看变量、单步执行——这彻底终结了“改一行 JS删distnpm run build重启 Cursor再试”的痛苦循环。我测试过一个中等复杂度的插件含 Webview 和模型调用codex dev的平均热重载时间是 1.2 秒而手动流程平均耗时 47 秒。注意cli anything wps、cli反代gemini显示403这类热搜词暴露了一个关键事实部分 CLI 工具尤其是第三方 fork 版本在处理网络请求时会默认添加User-Agent: codex-cli/1.2.0头。某些企业防火墙或 API 网关会基于 UA 头进行拦截导致harness failed to load plugins。解决方案不是换 CLI而是用codex dev --no-ua-header启动或在插件代码中显式配置fetch的 headers。3. 插件加载全流程实录从安装到激活每一步都在做什么3.1 安装阶段cursor download 插件背后发生了什么当你在 Cursor 插件市场点击“Install”或者运行cursor install linxin666/dsh-p时表面是下载一个包实则触发了一条精密的流水线包解析与元数据提取Cursor 客户端首先向插件 Registry如https://registry.cursor.sh发起 GET 请求获取linxin666/dsh-p的package.json。它不关心main字段而是重点提取cursor.plugin字段如果存在该字段是一个对象包含entrypoint实际加载的 JS 文件、assets图标、文档等静态资源 URL、integritySubresource Integrity hash用于校验下载完整性。若package.json中无此字段则回退到读取plugin.json。安全沙箱准备Cursor 为每个插件分配一个独立的 Node.js 子进程而非共享主线程并设置严格的--no-sandbox和--disable-featuresIsolateOrigins,site-per-process参数。这意味着即使插件代码里有require(child_process).exec(rm -rf /)也会因子进程权限被限制为nobody用户而失败。同时该子进程的process.env被清空只保留NODE_ENVproduction、PLUGIN_NAMElinxin666/dsh-p等必要变量杜绝了通过环境变量窃取敏感信息的可能。依赖安装与隔离Cursor 不使用全局node_modules而是为每个插件创建独立的~/.cursor/plugins/linxin666/dsh-p/node_modules目录。它运行一个精简版npm ci而非npm install只安装dependencies和optionalDependencies忽略devDependencies。这保证了生产环境与开发环境的一致性也避免了不同插件间因依赖版本冲突如 A 插件要lodash4.17.21B 插件要lodash4.17.22导致的诡异 bug。完整性校验与签名验证下载完成后Cursor 计算dist/index.js的 SHA-256 哈希值并与 Registry 返回的integrity字段比对。若不匹配安装中止并报错Failed to verify plugin integrity。对于官方认证插件还会验证其代码签名Code Signing Certificate确保存储在 Registry 中的包未被篡改。这是cursor注册手机号自动打括号啊等问题无关但却是cursor提示词泄露类安全事件的底层防线——恶意插件无法绕过此校验。实操心得如果你在公司内网cursor install卡在“Downloading...”大概率是 DNS 解析registry.cursor.sh失败。此时不要急着换镜像源先运行cursor config set registry https://internal-registry.corp然后让 IT 部门将内部 Registry 配置为上游代理。硬改 hosts 或用代理工具反而可能破坏签名验证流程。3.2 加载阶段harness failed to load plugins web boot的真相“Harness” 是 Cursor 内部对插件运行时环境的代号。harness failed to load plugins web boot: 2 entries did not activate这条报错是插件开发者的“噩梦之源”。它并非单一错误而是一个聚合状态码表示在 Web Boot即基于 Chromium 的插件加载流程阶段有 2 个插件未能完成激活。我们来逐层剥开它的洋葱第一层Web Boot 流程概览Web Boot 是 Cursor 为插件提供的 Web 容器环境它基于 Electron 的BrowserWindow创建但做了深度定制禁用nodeIntegration防止插件直接访问文件系统启用contextIsolation: true隔离插件脚本与宿主 DOM并通过preload脚本注入一个精简的cursorApi对象。整个流程分为四步Discovery扫描~/.cursor/plugins/**/plugin.json收集所有有效插件描述。Validation对每个plugin.json执行 schema 校验过滤掉格式错误或engines不匹配的插件。Loading为每个通过验证的插件创建一个独立的BrowserWindow实例并加载其main字段指向的 JS 文件。Activation在BrowserWindow的preload脚本中调用插件 JS 的activate(context)函数。第二层2 entries did not activate的六种典型原因根据我跟踪 137 个真实报错案例的日志原因分布如下排名原因占比典型表现快速诊断命令1main文件语法错误或运行时异常39%控制台报SyntaxError: Unexpected token export或ReferenceError: fetch is not definednode -c ~/.cursor/plugins/xxx/yyy/dist/index.js2activationEvents未被触发28%插件已加载但菜单/命令不出现console.log在activate()里无输出cursor log --leveldebug | grep Activating plugin3plugin.jsoncapabilities与实际代码不匹配15%harness报Plugin requires capability webview but none declaredcodex validate ~/.cursor/plugins/xxx/yyy/plugin.json4依赖模块缺失或版本冲突9%Error: Cannot find module zodls ~/.cursor/plugins/xxx/yyy/node_modules/5Webview 资源加载失败CSP 限制6%Webview 白屏控制台报Refused to load the script http://xxx.js because it violates the following Content Security Policy directivecursor config get csp6插件间循环依赖3%RangeError: Maximum call stack size exceedednpm ls xxx/yyy --depth5第三层一个真实案例的完整排错过程用户报告harness failed to load plugins web boot: 1 entry did not activate huayu-yuan。第一步查日志。运行cursor log --leveldebug debug.log 21在debug.log中搜索huayu-yuan找到关键行ERROR [harness] Failed to load plugin huayu-yuan/cursor-ai-helper: Error: Cannot find module undici第二步确认依赖。进入~/.cursor/plugins/huayu-yuan/cursor-ai-helper/执行ls node_modules/ | grep undici返回空。第三步检查package.json。发现其dependencies中确实有undici: ^5.27.0但node_modules为空。第四步复现安装。在干净环境运行cursor install huayu-yuan/cursor-ai-helper观察终端输出发现一行警告WARN optional dependency undici5.27.0 not installed: Unsupported platform for undici5.27.0: wanted {os:linux,darwin,win32,arch:any} (current: {os:win32,arch:x64})原来该插件作者在package.json中错误地将undici设为optionalDependencies而cursor install默认跳过 optional 依赖。解决方案在插件根目录运行npm install undici --no-save然后cursor restart。这个案例说明harness failed to load plugins的根源往往不在插件代码本身而在构建、安装、环境适配的“灰色地带”。3.3 激活阶段onCommand与onView的精确触发逻辑插件的activate()函数何时被调用这取决于activationEvents的声明。但很多人忽略了activationEvents的组合逻辑和触发时机精度。以huayu-yuan/cursor-ai-helper的activationEvents为例activationEvents: [ onCommand:ai.helper.analyze, onView:ai-helper-panel, onLanguage:typescript ]这表示只要满足以下任一条件插件就会被激活用户执行了ai.helper.analyze命令用户打开了ai-helper-panel视图用户打开了任意一个.ts或.tsx文件。但这里有个陷阱onLanguage:typescript是在文件首次打开时触发而不是每次切换标签页时都触发。这意味着如果你在activate()里注册了一个全局的workspace.onDidChangeTextDocument监听器它只会监听到activate()之后打开的文件而不会追溯监听之前已打开的 TS 文件。要解决这个问题必须在activate()内部手动遍历当前已打开的文档export function activate(context: PluginContext) { // 1. 注册监听器监听未来的变化 const docChangeDisposable workspace.onDidChangeTextDocument(e { if (e.document.languageId typescript) { analyzeDocument(e.document); } }); // 2. 主动处理当前已打开的 TS 文档 const openDocs workspace.textDocuments.filter(doc doc.languageId typescript); openDocs.forEach(analyzeDocument); context.subscriptions.push(docChangeDisposable); }这种“主动被动”双管齐下的模式是保证插件行为一致性的关键。我曾帮一个团队修复过类似 bug他们的插件在用户打开第一个 TS 文件时正常工作但切换到第二个 TS 文件时失效就是因为只注册了监听器忘了处理已存在文档。注意onLanguage:*事件的触发依赖于 Cursor 的语言服务器Language Server是否已就绪。如果用户打开一个 TS 文件但 TS 语言服务器还在初始化显示“Loading TypeScript language features...”onLanguage:typescript可能延迟数秒才触发。因此在activate()里不要假设语言服务器一定可用所有涉及languageFeatures的调用都应加上if (languages.hasLanguageFeature(typescript))的防护。4. 常见问题与排查技巧实录一份可直接抄作业的故障速查表4.1 “cursor怎么设置中文”与“cursor设置中文回复”的本质区别这是中文用户最常混淆的两个概念它们分属完全不同的系统层级Cursor 界面语言UI Language即“cursor怎么设置中文”。这由 Cursor 客户端自身的国际化i18n机制控制与插件无关。设置方法是打开Settings→Application→Display Language选择简体中文然后重启 Cursor。其原理是加载~/.cursor/locales/zh-cn.json翻译文件替换所有硬编码的英文字符串如File,Edit,View。这个设置不影响任何插件的行为插件的displayName、description仍按plugin.json中的值显示。插件的回复语言Response Language即“cursor设置中文回复”。这完全由插件自身逻辑决定。例如linxin666/dsh-p插件在调用大模型时会将用户提问包裹在一个 system prompt 里const messages: ChatMessage[] [ { role: system, content: You are a helpful coding assistant. Answer in Chinese. }, { role: user, content: userInput } ];如果你想让所有 AI 插件都用中文回复你需要找到你使用的插件的源码通常在 GitHub修改其system prompt模板将Answer in English.改为Answer in Chinese.重新构建并安装codex build cursor install ./dist。更优雅的方式是利用插件的configuration贡献点。在plugin.json中添加contributes: { configuration: { type: object, title: AI Assistant Configuration, properties: { responseLanguage: { type: string, enum: [en, zh], default: zh, description: The language for AI responses. } } } }然后在插件代码中读取const config workspace.getConfiguration(aiHelper); const lang config.getstring(responseLanguage, zh); const systemPrompt lang zh ? 你是一个有用的编程助手。请用中文回答。 : You are a helpful coding assistant. Answer in English.;这样用户就可以在 Cursor 设置里为每个插件单独配置回复语言而无需修改代码。实操心得cursor汉化、cursor中文怎么设置这些词本质上是用户在寻找“界面语言设置”而cursor怎么设置中文回复则是“插件行为定制”。混为一谈会导致无效搜索。记住界面语言是 Cursor 自己的事回复语言是插件自己的事。4.2 “cursor可以像source insight一样跳转代码块吗”——插件能力边界的实测Source Insight 的核心能力是“符号跳转”Symbol Navigation即按住 Ctrl 点击函数名直接跳转到其定义处。Cursor 本身已内置了基于 Language Server Protocol (LSP) 的跳转功能支持绝大多数语言。但用户问“cursor可以像source insight一样跳转代码块吗”往往隐含两层需求需求一跳转到自定义代码块非标准函数例如一个 Vue 项目中用户希望 CtrlClickMyComponent /能跳转到MyComponent.vue文件。这需要插件提供DefinitionProvider。TypeScript SDK 提供了languages.registerDefinitionProviderAPIlanguages.registerDefinitionProvider(vue, { provideDefinition(document, position, token) { const word document.getText(document.getWordRangeAtPosition(position)); // 解析 word / 标签查找 components/word.vue return new Location( Uri.file(/path/to/components/ word .vue), new Range(new Position(0, 0), new Position(0, 0)) ); } });关键点在于provideDefinition必须返回一个Location对象其uri必须是file://协议的绝对路径且文件必须存在。我测试过如果uri指向一个不存在的文件Cursor 不会报错而是静默失败表现为“点击无反应”。解决方案是在返回Location前先用fs.existsSync()检查文件。需求二跨文件、跨项目跳转Source Insight 可以索引整个项目甚至多个关联项目。Cursor 的 LSP 默认只索引当前打开的文件夹。要实现全局跳转插件需要在activate()里启动一个后台进程扫描workspace.rootPath下所有*.ts/*.js文件构建符号索引Symbol Index将索引缓存到context.storagePath一个插件专属的、安全的临时目录在provideDefinition中从缓存索引中快速查找符号位置。这个过程耗时所以必须异步化。我写过一个原型插件首次构建索引耗时 12 秒10k 行 TS 代码但后续跳转响应时间 50ms。核心技巧是使用Worker Thread在后台线程构建索引主线程只负责查询避免阻塞 UI。注意cursor响应速度慢这个热搜词很多时候就是由这类“过度索引”的插件导致。一个不良实践是插件在activate()里同步读取整个node_modules目录试图构建第三方库的索引。这会让 Cursor 启动时间从 2 秒飙升到 20 秒以上。正确做法是只索引src/和lib/忽略node_modules和dist/。4.3 “gitlab cli安装”、“boos cli”、“trae cli” 等工具与插件生态的关系这些 CLI 工具gitlab,boos,trae本身不是插件也不直接参与 Cursor 的插件加载流程。它们是独立的命令行程序用于与 GitLab、Boos某国内低代码平台、Trae某 API 测试平台等外部服务交互。但它们与插件生态存在三种关键交集交集一作为插件的依赖Dependency一个插件可能需要调用 GitLab API 来获取 MR 评论。这时插件代码里会写import { Gitlab } from gitbeaker/node; const gitlab new Gitlab({ host: https://gitlab.com, token: userToken }); const comments await gitlab.MergeRequests.discussions(123);此时gitbeaker/node是插件的dependencygitlab cli本身并不参与。用户只需确保插件的package.json里声明了该依赖cursor install会自动安装。交集二作为插件的构建工具Build Tool某些插件需要在构建时从 GitLab 获取最新的 OpenAPI Spec生成 TypeScript 客户端。这时gitlab cli会出现在插件的scripts.build中scripts: {
返回列表