
qwen-code 扩展管理 V2基于 extension-store 的原子事务、多工作区激活与兼容性设计【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文围绕 qwen-code 仓库中 docs/design/extension-management-v2.md 这一设计文档展开系统讲解该开源终端 AI 编程代理的新一代扩展管理协议以QWEN_HOME/extensions下的单一用户级工件为资源模型、以ExtensionStore为唯一写入者、通过 prepare/commit 事务与单调 generation 保证扩展安装、更新、激活的原子性与跨进程一致性。读完本文你将掌握 V2 的目录布局与锁模型、V1 迁移与降级投影机制、Daemon REST API 全貌与操作语义202/Location/Retry-After、FIFO 队列、敏感设置 bundle、下载上限以及客户端如何通过能力标签capability实现向后兼容的调用方式。设计定位协议 v1 之上的增量能力Extension Management V2 并不是一次推翻重来的协议升级而是在 daemon 协议v1之上以**增量能力additive capability**方式引入的扩展管理能力。设计文档明确说明新增能力标签为extension_management_v2客户端必须显式检查该标签neither daemon mode nor another workspace capability implies this API——即 daemon 的运行模式或其他 workspace 能力的存在都不能推断出 V2 扩展管理 API 可用已经发布的workspace_extensions能力与/workspace/extensions/*路由仍然保留作为主工作区primary-workspace的兼容适配器曾经提出但被放弃的workspace_qualified_extensions方案不属于本协议。从源码看能力标签在 packages/cli/src/serve/capabilities.ts 中统一声明workspace_extensions: { since: v1 }与extension_management_v2: { since: v1 }并列且注释明确说明 V2 是Global extension catalog/mutations plus workspace-qualified activation projections对 legacy 主工作区workspace_extensions契约是**增量additive**关系。与之配套的还有三个相关能力标签extension_batch_activation_v2批量激活所需因为较老的 V2 daemon 只暴露单数singular激活路由extension_activation_explicit_refresh当该标签出现时激活提交不会刷新活动会话客户端需在激活操作提交后单独发起刷新操作较老 daemon 则在激活操作内部就完成刷新extension_state、extension_git_credentials、extension_local_path_install等相邻标签覆盖扩展状态、Git 凭据与本地路径安装面。资源模型一个工件 激活即策略V2 的核心资源模型可以浓缩为一句话一个已安装的扩展是QWEN_HOME/extensions下的一个用户级工件artifact激活activation是策略policy而不是该工件的第二份拷贝。激活状态的判定顺序优先级从高到低为精确的工作区覆盖exact workspace override值为enabled或disabled内部精确inherit掩码在迁移旧版路径规则V1 path rule时创建的内部记录用于保住DELETE 即继承全局默认值的语义有序的 V1 路径规则ordered V1 path rule来自旧版extension-enablement.json的按顺序匹配规则全局默认global default。工作区身份使用 daemon 的规范化工作区路径canonical workspace path工作区路由先按 workspace id 选择已有运行时其次按规范化 cwd 选择。权限模型上读取读投影、读目录允许**不受信任untrusted**的运行时执行激活变更、刷新、以及工作区作用域内的安装要求**受信任trusted**的目标全局变更使用 daemon 常规的变更认证与安装许可mutation authentication and install consent与发起请求的工作区的信任状态无关。这保证了查看扩展状态是低权限操作而改变全局激活/安装始终走严格的认证与许可流程避免某个不受信任工作区借道扩展管理接口越权。存储与事务边界ExtensionStore 作为唯一写入者单一写入者原则ExtensionStore是扩展最终目录与 V2 激活状态的唯一写入者only writer。ExtensionManager仍然是面向工作区的门面facade但 CLI、TUI、自动更新auto-update、daemon 以及 SDK 支撑的操作全部委托给 store 执行变更。也就是说所有可能改变扩展工件或激活状态的路径最终都收敛到同一个事务入口避免多方并发写造成的状态分叉。目录布局设计文档给出的布局~/.qwen/即QWEN_HOME的默认值实际路径以QWEN_HOME环境变量为准见 extension-store.test.ts 中对QWEN_HOME的 stub 用法~/.qwen/ ├── extensions/ └── extension-store/ ├── lock ├── state.json ├── state.previous.json ├── staging/ ├── rollback/ └── transactions/extensions/最终工件目录同时存放旧版extension-enablement.jsonV1 迁移来源extension-store/lock跨进程文件锁proper-lockfileextension-store/state.jsonV2 权威状态包含单调递增的 generationextension-store/state.previous.json上一次状态快照用于恢复/对比staging/安装/更新准备的暂存区rollback/提交前旧工件移动到此处的回滚区transactions/事务日志journal目录。在源码 extension-store.ts 中ExtensionStore构造器默认将storeDir解析为Storage.getGlobalQwenDir()/extension-store、enablementPath解析为extensions/extension-enablement.json、状态文件为state.json与state.previous.json、锁文件为lock与设计文档完全对应。锁与单调 generationStore 与工件共享同一文件系统因此工件替换采用**目录重命名directory rename**实现无需拷贝大目录。并发控制分两层进程内互斥锁in-process mutexproper-lockfile文件锁序列化所有 V2-aware 进程的提交每次变更都在持锁状态下重读状态并把一个单调递增的 generation加一防止丢失更新lost updates。extension-store.test.ts中有一条跨进程测试用例——serializes mutations from two Node processes sharing QWEN_HOMEextension-store.test.ts正是对两个进程共享同一个QWEN_HOME时仍能串行化变更这一保证的验证。prepare → commit 的事务生命周期安装/更新的准备preparation阶段在最终工件目录之外进行。提交commit流程为写入prepared日志journal把旧工件移动到rollback/把staging/中的新工件移动到位目录重命名原子地写入state.json——这次状态文件重命名就是提交点commit point。提交点前后语义截然不同提交点之前任何失败都走**回滚rollback**恢复提交点之后恢复只做投影projection补全与清理绝不回滚已提交的策略。因此一次运行时的刷新失败绝不会导致已提交的策略被回滚A committed policy is never rolled back because one runtime refresh failed。如果提交前操作与其回滚双双失败调用方会同时收到两个错误journal 保留以便 fail-closed 恢复store 不会在工件状态歧义时继续写入。安全细节还包括store 文件使用仅属主权限owner-only permissions与原子 no-follow 写入对扩展 id、直接子工件路径、事务路径与名称做校验失败信息以**脱敏凭据credential-redacted**的来源上报避免泄露敏感信息。V1 迁移与降级投影首次迁移导入有序规则而非物化覆盖第一个 V2-aware 进程启动时会从extension-enablement.json导入有序规则ordered rules但不会把当前已注册工作区集合物化为精确覆盖exact overrides。也就是说迁移保持最小侵入旧规则被导入为 V1 路径规则层而不是把每个工作区当前生效值固化成写死的 override。双向投影与哈希比较每次状态提交后V2 会写出一个兼容投影compatible projection并将其哈希存入state.json。当检测到哈希不一致时修改顺序决定恢复方向若投影早于V2 状态投影较旧从权威的 V2 状态修复投影若投影晚于V2 状态投影在 V2 状态之后被修改视为降级二进制downgraded binary的顺序写入以新 generation重新导入。设计文档明确声明并发 V1 与 V2 写入者共享同一个QWEN_HOME是不被支持的列于 Non-goals单向降级是唯一受支持的过渡路径。inherit掩码保住 DELETE 语义清除一个公开工作区覆盖public workspace override通常意味着删除精确记录。但如果删除后较旧的路径规则会让生效值发生变化store 会写入一个内部inherit掩码使得DELETE仍然表示继承全局默认值。从源码看WorkspaceActivation类型包含inherit分支extension-store.ts且 store 在清理覆盖时会显式写入inherit如 extension-store.ts 中workspace: inherit的写入路径。这是对用户直觉的精确保护我删除了某工作区的专属设置它就该回到跟全局走而不是被一条看不见的旧规则接管。Daemon API 全貌全局扩展面Global surfaceGET /extensions PUT /extensions/activation POST /extensions/install POST /extensions/check-updates POST /extensions/:extensionId/update DELETE /extensions/:extensionId PUT /extensions/:extensionId/activation GET /extensions/operations/:operationId安装端点要求显式同意consent与初始激活initial activation初始激活类型为type InitialActivation | { scope: user } | { scope: workspace; workspaceId: string };即安装时可以决定是用户级全局激活还是针对某个具体工作区激活。安装源支持HTTPS GitGitHub Releasenpm绝对路径本地源absolute-path local source。而 SSH 与 link 源仍是本地 CLI 特性不通过 daemon 端点暴露。更新update语义保证保留扩展 id、manifest 名称、设置settings与激活策略不变已经是最新返回成功的updated: false结果Already current is a successfulupdated: falseresult卸载uninstall是幂等的同时移除工件与策略。工作区投影面Workspace projectionGET /workspaces/:workspace/extensions PUT /workspaces/:workspace/extensions/activation PUT /workspaces/:workspace/extensions/:extensionId/activation DELETE /workspaces/:workspace/extensions/:extensionId/activation POST /workspaces/:workspace/extensions/refresh设计上它刻意没有工作区工件变更路由——工作区只有激活与刷新的投影能力工件变更一律走全局面。投影条目包含默认值default、精确工作区值exact workspace value、生效值effective value与来源source。期望 generationdesired generation与本地已应用 generationlocally applied generation是响应中的顶层字段客户端可以据此判断策略已持久化与运行时已应用之间的差距。在源码 workspace-extensions.ts 中可以看到/operations/:operationId路由如GET ${base}/operations/:operationIdbase 随全局/工作区面不同而变化的实现以及操作交互interactions子路由operations/:operationId/interactions/:interactionId用于安装过程中的交互式确认。操作语义202、Location 与 Retry-After可能较慢的变更返回202并附带Location指向操作记录与Retry-After建议轮询间隔。操作记录的特点保存在daemon 进程内内存中最多保留100 条终端记录daemon 重启后可能消失——因此目录/存储恢复catalog/store recovery才是权威数据源SDK 轮询超时只停止轮询绝不取消已受理的工作never cancels accepted work。并发上限与两级 FIFO 队列daemon 同时受理的未完成扩展操作最多 10 个。在这之上两级 FIFO 队列控制资源准备队列preparation queuedaemon 全局 FIFO同时最多执行 2 个下载、解压、转换或单扩展更新检查提交队列commit queue独立的单并发 FIFO按准备完成的顺序进入。各类操作进入的队列不同安装与更新走prepare - commit/dispose完整生命周期激活与卸载只进提交队列check-updates只进准备队列。手动刷新manual refresh通过提交队列串行化且其 HTTP 超时释放该通道lane因此一个卡住的运行时刷新不会永久阻塞后续扩展变更已经开始的那次刷新之后仍可能自行收敛。敏感设置的原子 bundle扩展设置中的敏感项如 API key、令牌被暂存为每次准备per-prepare revision下的一个原子机密 bundlesecret bundle。stage 工件内部只记录一个非机密选择器non-secret selector它指向该修订号与安全存储后端。这样只有胜出的工件提交才会激活一个完整 bundle不会出现新代码配旧密钥的混搭store 提交是持久化点并立即释放提交通道后续的扩展重载、legacy 逐键设置同步、manager 运行时刷新、已准备文件清理、daemon 运行时对账都在提交之后异步执行不占用两个槽位——因此后面的提交可以在前面 generation 仍在应用/清理时继续推进。Dispose 一个已准备的变更会移除其未被选中的凭据快照成功提交会尽力移除先前选中的快照。若进程在 dispose 前硬崩溃安全后端可能残留一条不可达条目但没有任何工件选择器引用它所以它既不会激活也不会被误认为已提交凭据。超时、取消与下载上限准备截止时间preparation deadline从操作首次获得准备槽位时开始计时等待槽位的时间不计入中止abort会传播给网络操作以及进行中的归档扫描与解压流即使任务忽略中止已启动任务也会继续占用槽位直到其底层 promise 落定提交不可取消已准备的更新携带目标工件 generation无关的扩展或激活变更可以安全 rebase而同一工件的过期更新会以extension_conflict失败。远程下载限制在源码中有明确对应npm 元数据流式读取10 MiB 响应上限NPM_METADATA_MAX_BYTES 10 * 1024 * 1024见 npm.tsnpm 与 GitHub 归档分别有100 MiB 下载上限NPM_ARCHIVE_DOWNLOAD_MAX_BYTES与ARCHIVE_DOWNLOAD_MAX_BYTES见 npm.ts 与 github.tsmarketplace 元数据同样有 10 MiB 上限MARKETPLACE_MAX_BODY_BYTES见 marketplace.ts同时还有请求截止时间、重定向上限以及解压前的归档条目校验archive-entry validation防止 zip 炸弹与路径穿越类归档。运行时协调Runtime reconciliation工件提交触发刷新激活提交只持久化策略关键区分在于工件提交成功install/update会使本地状态失效并刷新受影响的运行时激活提交只持久化策略需要立即生效的客户端应另行提交独立的运行时刷新操作即POST /workspaces/:workspace/extensions/refresh全局工件变更会协调本 daemon 内的所有运行时。运行时刷新reconcile会刷新扩展与技能缓存、扩展工具、层级记忆hierarchical memory、活动会话的系统指令、可用命令。某个组件失败不会跳过其余组件会话 RPC 在所有组件尝试完毕后返回合并后的失败结果。generation 顺序保证与刷新超时运行时 generation 协调使用 daemon 全局 FIFO由变更与 generation 轮询器共享。变更在持久化提交回调durable commit callback处预留位置因此即使较早的提交后工作较晚完成较晚的 generation 也不可能先刷新运行时。配套保证应用 generation N 同时满足等待较旧 generation 的等待者迟到的低 generation 刷新不能把已应用 generation 往回拨ACP bridge 将每次会话刷新限制在30 秒内若聚合刷新仍超过路由截止时间控制器释放提交通道但不取消底层 RPC部分刷新失败或提交后重载/清理失败产生succeeded_with_warnings附工作区级或提交级诊断不回滚工件。迁移失败判定与警告分层Legacy 工作区迁移中只有工件无法重载才把已提交工件判为失败设置兼容同步、清理或运行时刷新警告不会触发对已持久化安装工件的重试。更新调用方收到的警告分两类兼容性/清理警告updated with warnings状态重载或运行时刷新失败updated, needs restart状态。文件监视与 30 秒轮询扩展文件监视器watcher对策略只观察extension-store/state.json策略 generation同时继续观察已安装/链接扩展的内容变更命令、技能、agent、hook、MCP 变化。30 秒 generation 轮询修复遗漏的文件系统事件并约束其他共享该 store 的 daemon 的收敛时间——这是多进程共享QWEN_HOME场景下状态最终一致的关键兜底。兼容性与客户端调用指南能力标签检查清单客户端在调用 V2 扩展管理 API 前应按顺序检查extension_management_v2V2 全局目录/变更 工作区限定激活投影存在。必须显式检查——daemon 模式或其他 workspace 能力都不代表该 API 存在extension_batch_activation_v2批量激活PUT /extensions/activation与PUT /workspaces/:workspace/extensions/activation可用较老 V2 daemon 只有单数激活路由PUT .../:extensionId/activationextension_activation_explicit_refresh决定激活后是否需要显式刷新。该标签存在时激活操作只提交策略客户端应等激活操作提交后再提交独立的 refresh 操作标签不存在时较老 daemon 在激活操作内部已包含刷新。与 legacyworkspace_extensions的适配workspace_extensions仍是既有单数面的能力标签其处理器调用同一套 manager/coordinator并适配响应投影激活project activation变为主工作区覆盖primary workspace override用户激活user activation保留 legacy 的规则清除行为rule-clearing当extension_activation_explicit_refresh被通告时通过任一表面的激活都是仅提交commit-onlylegacy 操作端点把 V2 的 warning 完成状态映射回已发布的 legacy 刷新错误状态。非目标Non-goals设计文档明确列出了 V2 不做的事每工作区工件拷贝per-workspace artifact copies——资源模型上就否定了多份副本daemon 注册表或远程确认协议registry / remote ack用户取消已受理操作旧二进制与 V2-aware 写入者并发写入同一个QWEN_HOME在未来的 protocol-v2 迁移之前移除 V1 适配器。这些非目标界定了 V2 的边界它解决的是多工作区、多进程下的原子性与一致性而不是引入中心化注册表或分布式协调。实现参考索引想深入阅读源码的读者建议按以下路径展开设计文档docs/design/extension-management-v2.md核心存储实现1783 行含锁、generation、迁移、恢复packages/core/src/extension/extension-store.ts能力标签声明extension_management_v2、extension_batch_activation_v2、extension_activation_explicit_refresh、workspace_extensionspackages/cli/src/serve/capabilities.tsDaemon 路由实现含/operations/:operationId与交互子路由packages/cli/src/serve/routes/workspace-extensions.ts运行时刷新协调packages/core/src/extension/extension-runtime-refresh.ts下载上限常量npm 10 MiB/100 MiB、GitHub 100 MiB、marketplace 10 MiBnpm.ts、github.ts、marketplace.ts跨进程串行化与 V1 迁移测试packages/core/src/extension/extension-store.test.ts扩展 Git 凭据配合敏感设置 bundle 阅读packages/core/src/extension/extension-git-credentials.ts综上Extension Management V2 的设计核心是把工件与策略彻底解耦工件只有一个、存放在QWEN_HOME/extensions所有变更经ExtensionStore以 prepare/commit 事务原子落盘激活策略则按精确覆盖 → inherit 掩码 → V1 规则 → 全局默认分层解析并通过能力标签把提交、刷新、批量激活的语义变化显式暴露给客户端。理解这一模型是在多工作区、多进程场景下正确使用 qwen-code 扩展体系的前提。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考