dsh-armory 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,128 +0,0 @@
1
- # PRD:dsh-armory WorkBuddy 市场对标(增量 PRD)
2
-
3
- > 档位:简单 PRD | 语言:中文 | 版本:v1.0(2026-10-20,Alice)
4
- > 唯一权威输入:`docs/workbuddy-market-gap-analysis.md`。本 PRD 只写「要在 dsh-armory 上新增/修改什么」,不重复报告论证过程。
5
- > 范围 = 报告第三节差距清单(G1–G11)+ 第五节落地顺序(三批推进);排除项 X1–X9 全部列为 Non-Goals,禁止进入需求池。
6
-
7
- ---
8
-
9
- ## 1. 项目信息
10
-
11
- - **Language**: 中文(与用户语言一致)
12
- - **Programming Language**: 纯 JavaScript(无构建步骤、无 TypeScript、无 UI 框架),Node ≥ 22.15,ESM。
13
- - UI:`lib/client.js` 内手写 Web 面板(注入 DSH Web GUI)
14
- - 市场数据层:`lib/wb-market.js`(WB 连接器/技能)、`lib/wb-zip.js`(zip 下载解压,零新依赖)、`lib/expert-market.js`(专家)
15
- - Host Remote 契约三件套:`lib/index.js` + `lib/typert.js` + `lib/client.js`(新增 Remote 方法必须三处同步,漏一处会静默 404)
16
- - **Project Name**: `dsh_armory_workbuddy_parity`
17
- - **原始需求复述**: 对标 WorkBuddy 市场体系(已卸载,数据目录 `C:\Users\LENOVO\.workbuddy`),在 dsh-armory 现有「市场」标签页(连接器/技能/内置 MCP 三类)之上,按差距报告结论补齐**当前技术条件下可实现**的差距:三批推进(正确性补丁 → 市场扩展 → 基础设施),并明确不做的边界(X1–X9)。
18
-
19
- ---
20
-
21
- ## 2. 产品定义
22
-
23
- ### Product Goals
24
-
25
- 1. **正确性优先**:修复连接器「残缺安装」——解析 `auth_injection_rules`,让 tencent-docs / ima-mcp 等连接器装完即真正可用(G2),同时把已有连接器数据(visible_in 标签、示例问法、版本)利用起来(G3/G5)。
26
- 2. **补齐最大内容品类与可用性**:新增「插件」市场子 tab(依托多市场注册表接入 codebuddy-plugins-official,150+ 插件,一次装齐技能+命令+钩子+MCP 组合包),并以「令牌引导安装」解锁 80+ 条需认证连接器(G7/G1/G10/G4)。
27
- 3. **建立可管理的基础设施**:本地安装状态与来源锁可查可筛、目录打开时自动保鲜提示、列表排序对齐技能侧体验(G6/G9/G8/G11)。
28
-
29
- ### User Stories
30
-
31
- 1. **As a** DSH 用户, **I want** 在 armory 里浏览并安装 codebuddy-plugins-official 的插件(如官方 docx/pptx 技能包、security-scan), **so that** 一次装齐「技能+命令+钩子+MCP」组合包,而不必逐个装 MCP 或技能。
32
- 2. **As a** DSH 用户, **I want** 安装 tencent-docs / ima-mcp 等连接器时自动应用其 token 注入规则, **so that** 装完即可用,而不是残缺安装后手工补 token。
33
- 3. **As a** DSH 用户, **I want** 安装 auth_mode=token 的连接器前自行填入令牌, **so that** 80+ 条需认证连接器在 armory 里真正可用。
34
- 4. **As a** DSH 用户, **I want** 看到连接器的部署/档位标签、版本与示例问法, **so that** 快速判断哪些连接器适合我,并直接复制示例 prompt 试用。
35
- 5. **As a** DSH 用户, **I want** 在列表中看到哪些连接器/插件已安装并按状态全量筛选, **so that** 避免重复安装、便于管理已装内容。
36
-
37
- ---
38
-
39
- ## 3. 技术规范
40
-
41
- ### 3.1 全局技术约束
42
-
43
- - **零新依赖**贯穿全程(zip 下载解压继续走 `lib/wb-zip.js`)。
44
- - 新增 Host Remote 方法必须 `lib/index.js` / `lib/typert.js` / `lib/client.js` **三处同步**,漏一处静默 404。
45
- - 注册表与本地状态文件均写入 `~/.dsh`(armory 已有该目录写入能力)。
46
- - 批次顺序与依赖:批次一独立先行;批次二内 G1 依赖 G7(不为插件源硬编码 URL)、G10 内嵌于 G1;批次三内 G6 与 G9 共用同一份本地状态文件设计。
47
-
48
- ### 3.2 Requirements Pool
49
-
50
- > 优先级:P0 = Must have(本迭代必做),P1 = Should have,P2 = Could have。每条映射差距编号与批次。
51
-
52
- #### P0 —— 批次一:正确性补丁(1 次小迭代,零新依赖,风险最低)
53
-
54
- - **R1|G2|批次一|must:解析并应用 `auth_injection_rules`**
55
- - 解析 connectors.json 顶层 `auth_injection_rules`(已证实 2 条:tencent-docs、ima-mcp,均双 token);安装/预览时应用到作用域合并逻辑,或在预览/安装界面明示「该连接器需要 N 个 token」。
56
- - **验收标准**:must——预览界面能显示注入规则条数与用途;安装 tencent-docs 后 profile 的 mcpServers 配置不再残缺(含 token 占位或注入说明),配合 R7 可完成完整认证配置。
57
- - **R2|G3|批次一|must:`visible_in` 标签展示 + 本地筛选**
58
- - 每条连接器解析 `visible_in` 数组(internal / iOA / plan:ultimate / plan:exclusive / selfhosted / cloudhosted),卡片与详情展示标签;筛选栏支持按标签本地过滤。
59
- - **边界**:只做数据标签展示 + 本地筛选,**不做**渠道/档位准入逻辑(X2);无 `visible_in` 的条目正常默认展示。
60
- - **验收标准**:must——含 visible_in 的条目显示标签;按标签筛选结果正确;不含标签条目不受影响。
61
- - **R3|G5|批次一|must:连接器元数据增强(examples 优先)**
62
- - 解析并展示 `examples_zh` / `examples_en`(可一键复制的示例问法)、`version`、`minWorkbuddyVersion`(版本仅作元数据新鲜度提示)。
63
- - **验收标准**:must——连接器详情可见中英示例问法并可一键复制、可见 version;could——zip 内 `icons/` 图标利用为可选加分项,不阻塞验收。
64
-
65
- #### P1 —— 批次二:市场扩展(核心增量)
66
-
67
- - **R4|G7|批次二|must:多市场注册表**
68
- - 借鉴 WB `known_marketplaces.json` schema(每项:manifestName / type(zip/directory)/ source / autoUpdate / isBuiltIn),把连接器源从硬编码单 URL 改为注册表配置(存 `~/.dsh`);G1 插件源直接接入注册表,不再新增硬编码 URL。
69
- - **验收标准**:must——连接器源可在注册表中配置并生效;新增一个市场源无需改代码。
70
- - **R5|G1|批次二|must:插件市场子 tab**
71
- - 新增「插件」子 tab:经 R4 注册表下载 codebuddy-plugins-official zip → 解析 marketplace.json → 列表/详情展示组成(skills / commands / agents / hooks / mcpServers / lspServers 数量或清单)、category / keywords / author → 安装经既有宿主通道 `remote.pluginManager.installBundle(targetSpec, {})`(严格双参调用,注意 `lib/client.js:3577` 已记录的踩坑注释)。
72
- - **边界与必写提示**:① 安装成功后**必须明确提示「需重启 DSH 宿主才生效」**(宿主 installBundle 机制决定,是与 WB「装完即用」唯一无法抹平的差距,提示须醒目持久);② `lspServers` 字段仅展示、不承诺安装后生效(X7);③ oauth/oneid 相关连接器不代登录、仅标注(X3)。
73
- - **验收标准**:must——150+ 插件可浏览可搜索;至少 1 个插件(如官方 docx 包)安装成功且出现重启提示,重启宿主后生效。
74
- - **R6|G10|批次二|must(随 R5):插件依赖提示**
75
- - 解析插件条目的依赖声明,安装前提示依赖清单,用户确认后一并安装(marketplace.json 中 dependencies 的具体表达形式在解析真实 zip 时确认,见 Open Questions)。
76
- - **验收标准**:must——安装含依赖的插件(如 security-scan 类)时先列出依赖并需确认;无依赖插件不弹确认。
77
- - **R7|G4|批次二|must:令牌引导安装**
78
- - 针对已证实存在的 73 条 auth_mode=token + 8 条 oauth 连接器(现仅拦截不引导:`lib/wb-market.js:128-129` 拒装、`:166` authMode 仅透传展示):安装前弹出令牌输入 → 替换 mcpServers 的 env/header 占位符 → 写入 profile mcpServers;并按 auth_mode 区分「可自填 token」与「oauth/oneid 不支持代注入、需自备凭据」的标注。
79
- - **边界**:只做**用户自填简单 token**(env/header 占位符替换);WB 的 mcp-oauth / oneid-token 双 token 代注入机制整条链路**不做**(X9)。
80
- - **验收标准**:must——任选 1 条 auth_mode=token 连接器,输入令牌安装后认证可用;oauth 连接器显示明确的「不支持代注入」标注而非静默拒装。
81
-
82
- #### P2 —— 批次三:基础设施
83
-
84
- - **R8|G6+G9|批次二→批次三|must:连接器/插件已安装状态 + 来源锁(同一份本地状态文件)**
85
- - 安装连接器/插件时同步写一份本地状态文件(存 `~/.dsh`):scope / installPath / version / dependencies / installedAt / lastUpdated + 上游来源锁(来源市场、版本,参考 WB `upstream-skills-lock.json` 机制,字段细节实现前补查);列表支持「已安装」**全量**筛选(非当前页,对齐技能侧 `skillOnlyInstalled` 但做得更完整)。
86
- - **验收标准**:must——安装后状态文件可查(含来源版本);「已安装」筛选在全量范围内正确;could——卸载/更新时同步维护该文件为加分项。
87
- - **R9|G8|批次三|should:懒触发 ETag 更新检查**
88
- - 打开市场页时后台比对上游 ETag(复用 `lib/wb-market.js:83` 既有 ETag 缓存),有更新则提示,由用户手动确认更新。
89
- - **边界**:不做静默 autoUpdate、不做常驻定时器(X4)。
90
- - **验收标准**:should——上游目录变更后再次打开市场页出现更新提示;确认后目录刷新为最新。
91
- - **R10|G11|批次三|could:连接器/内置目录排序**
92
- - 本地排序(如按名称 / auth_mode / type),对齐技能侧已有排序体验;因 WB 本体 UI 无排序证据,仅作体验补全、不作为对标宣传点。
93
- - **验收标准**:could——排序切换后列表正确重排。
94
-
95
- ### 3.3 UI Design Draft
96
-
97
- - **市场 tab 顶部**:现有「连接器 / 技能 / 内置 MCP」子 tab 增加「**插件**」(第四个子 tab)。
98
- - **连接器子 tab**:
99
- - 卡片:名称 + 现有信息之外,新增标签行(`visible_in` 标签以小徽章展示,如 `selfhosted`、`plan:ultimate`)、版本号(v1.0.1)与 auth_mode 标注(如 `token`)。
100
- - 筛选栏(现有搜索/分页旁):新增「已安装」开关(全量筛选)+ `visible_in` 标签多选筛选 + 排序下拉(R10)。
101
- - 详情/预览面板:新增「示例问法」区块(中文/英文并列或切换,每条带一键复制按钮);新增注入规则提示行(「该连接器需要 N 个 token」及用途说明)。
102
- - **令牌输入弹窗**(R7):模态弹窗,列出该连接器全部 env/header 占位符(键名 + 用途说明),每行一个输入框(内容遮罩显示);底部「确认安装 / 取消」。oauth/oneid 条目的弹窗改为说明页:标注「需自备凭据,DSH 不支持代登录」。
103
- - **插件子 tab**(R5/R6):
104
- - 列表:插件名 + author + category/keywords 标签 + 组成 chips(如 `skills×3`、`commands×5`、`hooks×2`、`MCP×1`、`LSP(仅展示)`);支持搜索。
105
- - 详情面板:完整组成清单、依赖清单、keywords/author;「安装」按钮。
106
- - **重启提示**:安装成功 toast 之外,页面顶部出现醒目持久提示条「已安装,需重启 DSH 宿主后生效」,直到用户重启前不消失;lspServers 条目旁固定标注「仅展示,DSH 暂不托管 LSP」。
107
- - **更新提示**(R9):检测到上游更新时,市场页顶部出现轻量提示条「目录有更新」,点击确认后刷新;无更新时无感知。
108
-
109
- ### 3.4 Open Questions
110
-
111
- 1. **插件市场源与注册表已实测确认**(2026-10-20 主理人核查):注册表文件实为 `C:\Users\LENOVO\.workbuddy\plugins\known_marketplaces.json`(7 个市场,schema:manifestName / type(zip|directory) / source{path,url} / installLocation / description / lastUpdated / autoUpdate / isBuiltIn)。`codebuddy-plugins-official` 源 = `https://download.codebuddy.cn/plugin-marketplace/codebuddy-plugins-official-4b9366ac-f7e5-4cef-b60a-99a5229ce2b2.zip`;其 zip 内清单在 `.codebuddy-plugin\marketplace.json`(非根目录),插件条目在 `external_plugins\<plugin>\.codebuddy-plugin\plugin.json`(实测含 name/description/author/repository/homepage/license 字段,未见 dependencies 字段——条目级依赖表达形式以清单文件实测为准)。R4/R5 按此实现。
112
- 2. **auth_injection_rules 结构已实测**(同日核查):connectors.json 顶层键 = ["name","description","owner","auth_injection_rules","connectors"];规则条目 = {id, when:{active_in:[...]}, applies_to_connectors:[...], requires_connected_connectors:[...], timing:"request", inject:[{from_connector, token_type:"mcp-oauth"|"oneid-token", header, value_template:"Bearer ${access_token}"}]},实测仅 2 条(tencent-docs-dual-token、ima-mcp-dual-token),均为代注入机制 → 归 X9 Non-Goals。R1 相应收窄为:**解析该数组并在预览/详情展示「存在 N 条 token 注入规则(依赖 WB 账号体系,DSH 不代注入)」提示**,不实现注入逻辑;connector 条目实测键 = id,name,name_en,description,description_zh,description_en,source,auth_mode,type,examples_zh,examples_en,visible_in,与 R2/R3 字段完全吻合。
113
- 3. **marketplace.json 条目级 dependencies 的具体表达形式待解析真实 zip 时确认**(插件间依赖已在 WB `installed_plugins.json` v2 中证实存在,影响 R6 的解析实现)。
114
- 4. **`upstream-skills-lock.json` 字段级结构未逐项验证**(文件存在于 WB 数据目录、28971 字节),R8 的来源锁字段可在实现前参照该文件对齐。
115
-
116
- ### 3.5 Non-Goals(排除项 X1–X9,禁止进入需求池)
117
-
118
- | # | 排除项 | 一句话理由 |
119
- |---|---|---|
120
- | X1 | 多账号隔离(WB 的 5 个 user-uuid 目录) | DSH 宿主无多账号概念,armory 是单用户本地工具,`~/.dsh` 单命名空间与宿主会话模型冲突 |
121
- | X2 | `visible_in` 的渠道/档位准入逻辑(iOA 渠道、付费档位控制) | armory 不是分发渠道方;G3 只做数据标签展示与本地筛选 |
122
- | X3 | server-side / oauth / oneid-token 连接器的服务端代登录 | 依赖 WB 闭源客户端与账号服务,DSH 侧无对应服务端;极限是安装时按 auth_mode 标注 |
123
- | X4 | WB 式静默自动更新(autoUpdate) | 静默改写用户环境风险高;G8 只做「检查 + 提示」 |
124
- | X5 | WB 内置市场内容搬运 | 内容嵌在闭源本体运行时内;armory 专家市场已独立实现同类能力 |
125
- | X6 | tabbit-browser 第三方浏览器技能市场 | 强绑定 tabbit 生态运行时,DSH 侧有独立浏览器技能体系 |
126
- | X7 | `lspServers` 的 DSH 原生 LSP 托管 | DSH 宿主无 LSP 托管运行时;G1 中仅展示该字段、不承诺生效 |
127
- | X8 | 企微技能包(wecom-unified)市场 | 强绑定企业微信场景;仅其「技能锁」工程机制纳入 G9 |
128
- | X9 | WB 的 mcp-oauth / oneid-token 双 token 代注入机制 | 依赖 WB 账号体系与闭源服务端;G4 只做用户自填简单 token |
@@ -1,269 +0,0 @@
1
- # 增量设计:dsh-armory 接入 awesome-dsh-plugins 插件市场(Awesome Market)
2
-
3
- > 架构师:Bob | 2026-10-20 | 输入:`docs/prd-awesome-dsh-plugins.md` v1.1(R11–R20)+ 主理人实测事实清单
4
- > 性质:**增量设计**,不触碰权威文档 `docs/system_design.md`。本文件配套 `docs/sequence-diagram-awesome.mermaid` 与 `docs/class-diagram-awesome.mermaid`。
5
- > 三项用户拍板(绑定,不再重开):① unverified 403 条不进缓存;② 默认序 stars 降序(featured/official 仅徽章);③ skill 类 694 条由插件市场承接。
6
-
7
- ---
8
-
9
- ## Part A: System Design
10
-
11
- ### 1. Implementation Approach
12
-
13
- #### 1.1 核心技术挑战
14
-
15
- | # | 挑战 | 应对 |
16
- |---|------|------|
17
- | C1 | 13.4MB 单文件裸 JSON,无 zip 清单结构,现有 `wb-plugin-market.js` 的 ZipReader 管线完全不适用 | 新建独立 JSON 数据层 `lib/wb-awesome-market.js`,不改造旧管线 |
18
- | C2 | 20,769 条全量数据不可直渲染;P0 收录口径 = verified ∧ 带 npm(≈6 千条) | 一次全量拉取 → 运行时过滤 → 瘦身影射落盘(R11),面板只读瘦身缓存,本地分页/搜索/筛选全即时 |
19
- | C3 | 本机实测 github.com 直连 ❌ / raw.githubusercontent.com ❌,拉取主路径必须避开 | 双 CDN 互切:jsdelivr → fastly(同路径不同 CDN 域,独立故障域);api.github.com git blobs 兜底**裁定不进 P0**(见 1.3) |
20
- | C4 | 8111 行 client.js 大文件增量,执行子代理(glm-5.3-flash)大文件易失焦 | 任务按文件切片,client.js 操作一律「grep 定位 → read ≤60 行窗口 → edit 定点」,契约条目与 UI 拆成不同任务 |
21
- | C5 | Host Remote 契约三处同步(`lib/index.js` / `lib/typert.js` / `lib/client.js`),缺一即静默 404 | 独立任务 T02(Host 侧)+ T03(client 侧 descriptors),`test/contract-consistency.mjs` 三处子集校验兜底 |
22
- | C6 | 新增 `t('中文')` 必须同步 EN 译文,否则 i18n-coverage 套件红 | T04 任务书内逐条列出新增文案,EN_US_MESSAGES 同步补译 |
23
-
24
- #### 1.2 框架与库选择
25
-
26
- **零新依赖**(绑定约束):Node ≥ 22.15 内置 `fetch`(含 `AbortSignal.timeout`)/ `node:fs/promises` / `node:path` / `node:os` / `JSON` 全部够用。13.4MB 单文件 `response.arrayBuffer()` + `JSON.parse` 单次内存峰值 ≈ 40–60MB(Buffer 13.4MB + parse 后对象图),可接受且仅发生在刷新/首载瞬间。ETag 条件请求、双源顺序切换均为既有 `wb-plugin-market.js`(:47-96、:251-291)已验证范式的同构复刻,无第三方需求。
27
-
28
- 架构模式沿用仓库既有分层:**纯数据层(lib 模块,零 Remote 依赖)→ Host Remote 网关(index.js 薄转发)→ 浏览器半契约(client.js descriptors)→ UI 组件(client.js 内 h() 渲染)**。不引入新抽象层。
29
-
30
- #### 1.3 设计裁定(六项决策点逐条落锤)
31
-
32
- **裁定 1|源注册方式:常量内置,不进 `wb-registry.js`。**
33
- 依据:
34
- - `test/wb-registry.mjs` 多处硬编码断言 `marketplaces.length === 2`(:31、:46、:55、:62、:95 等)与 `KINDS` 集合行为(:177 未知 kind 回落 null);新增第三种 kind `"dsh-plugins"` + 内置第三条,意味着同步修改 wb-registry.js 与 6+ 处测试断言——改动面大、纯风险、P0 收益为零。
35
- - registry 化的唯一收益是「用户改源/禁用」,而 P0 的容错需求(R14)由模块内双源互切已完整满足;用户级自定义源 URL 留作 P2(`marketplaces.json` 覆盖口子在 wb-registry 现有 `upsertMarketplace` 机制下天然可加,届时再扩 KINDS + 测试,一次性做对)。
36
- - 两处真相风险:若同时走 `sourceFor("dsh-plugins")` 与模块内常量,源 URL 会出现第二份硬编码。裁定:**源 URL 只活在 `lib/wb-awesome-market.js` 常量 `AWESOME_SOURCES` 内,registry 完全不感知 awesome**。落地形态:`wb-registry.js` 本迭代 **零改动**。
37
-
38
- **裁定 2|新模块边界:`lib/wb-awesome-market.js`(纯数据层)。**
39
- - 缓存目录 `~/.dsh/armory/awesome-market/`:`slim.json`(瘦身影射数组)+ `meta.json`(`{etag, fetchedAt, updated, counts}`)。
40
- - 瘦身口径(R11):过滤 `status === 'verified'` ∧ `npm` 为非空字符串;映射字段 `name / repo / npm / description(截断≤120) / category / status / tags(≤2) / stars / official / verifiedAgainst / lastVerified`。broken 102 条与 unverified 403 条在过滤一步天然出局(**永不进缓存,任何视图不可见**,R17)。
41
- - 防御规则:npm 字段若实测为对象(schema 未强约束类型),取 `.name` 提取;`stars` 非有限数记 0;`category/tags` 畸形折叠为默认值。单条畸形不拖垮整目录(沿用 `readPluginCatalog` 的 skipped 思路,此处直接丢弃)。
42
- - 落盘走 staging + rename 原子写(`wb-registry.js` :119-124 同款)。slim.json 验收上限 3MB;超限回退 = description 截断降至 80 字符(见 UNCLEAR#3)。
43
- - 模块导出:`wbAwesomeCatalog(options)` / `wbAwesomeDetail(name, options)` / `wbAwesomeUpdateCheck(options)` / `wbAwesomeCachedInfo(options)` + 纯函数 `slimEntries(rawArray)`(测试锚定)。`options.rootDir / DSH_ARMORY_HOME` 注入,测试绝不碰真实 `~/.dsh`。
44
-
45
- **裁定 3|Remote 方法面:3 个方法。**
46
-
47
- | 方法 | 参数(typert mv) | Result symbol | 语义 |
48
- |------|------------------|---------------|------|
49
- | `wbAwesomeCatalog` | `[payloadParam]`(`payload.refresh` 强制刷新) | `AwesomeCatalogResult`(新增) | 读瘦身缓存(缺则全量拉取+瘦身),返回 `{ok, loadedFrom, updated, counts, plugins[], marketplace}` |
50
- | `wbAwesomeDetail` | `[payloadParam]`(`payload.name`) | `AwesomeDetailResult`(新增) | 按名字查瘦身缓存单条全字段 |
51
- | `wbAwesomeUpdateCheck` | `[]` | `WbUpdateHintResult`(**复用既有 symbol**,形状同构) | ETag 条件探测,五枚举 reason,绝不落盘 |
52
-
53
- - 复用 `WbUpdateHintResult` 的依据:与 `wbPluginUpdateCheck`(typert.js :108,client.js :8004)返回形状逐字段相同,新增 symbol 徒增 typert 面积;契约测试只查「client ⊆ typert」可达性,复用合法。
54
- - `wbAwesomeRefresh` 不单列:刷新 = `wbAwesomeCatalog({refresh:true})`,省一条契约。
55
- - `marketplace` 标记放**响应级**(`res.marketplace = 'awesome-dsh-plugins'`),不重复写入每条记录——省 ≈150KB 落盘体积,client 接收时统一贴标。
56
-
57
- **裁定 4|安装链:`npm:<name>` 主路径 + 全量灰置兜底。**
58
- - `pluginInstallSpec`(client.js :6232-6241)顶部新增分支:`if (plugin?.npm) return \`npm:${String(plugin.npm).trim()}\`;`——codebuddy 条目无 npm 字段,既有 repository 分支行为不变;awesome 条目 repository 字段不被消费(避免 github 直连死路)。
59
- - 走现有 `pluginManager[UPGRADE_METHOD](spec, {})` 严格双参(:6252);安装成功沿用 `restartNotice`(:6273)。
60
- - 登记:`wbInstallStateRecord` 的 `source.marketplace` 按 `plugin.marketplace || 'codebuddy-plugins-official'` 取值——awesome 条目在 client 接收 catalog 时统一标 `item.marketplace = res.marketplace`,既有 codebuddy 路径零感知。「只看已安装」复用 `installedPluginKeys`(:6284)无需改动。
61
- - 灰置口径(P0):awesome 条目 **npm 缺失即灰置**「暂不支持安装」,无任何例外(R16 是 P1;monorepo path 条目在无 npm 时同样落进灰置集合,永久灰置直到宿主支持度实测)。
62
-
63
- **裁定 5|更新探测:复用五枚举,不扩展枚举。**
64
- - `wbAwesomeUpdateCheck` 逐字段照抄 `wbPluginUpdateCheck`(wb-plugin-market.js :251-291)语义:`no-source`(本实现中源为常量,恒有源,该分支仅保留防御)/ `no-cache` / `probe-failed` / `up-to-date` / `update-available`。HEAD 405/501 降级 GET 并 `body.cancel()`。
65
- - **不引入 `stale-cache` 新枚举**:「全源失败但有缓存」是 catalog 加载链(取数)的表现层状态(`stale:true` 字段),不是探测(update check)的 reason——两者语义不同轨,混进一个枚举会让 T05 范式的测试全部要改。
66
- - jsdelivr ETag 稳定性未实测 → 防御性回退:响应无 ETag 时**放弃轻探测**(返回 `probe-failed`),面板下次打开仍走 catalog 条件 GET(缺省 If-None-Match 的 200/304 已承担流量优化),不阻塞功能。
67
-
68
- **裁定 6|容错链:jsdelivr → fastly 双 CDN 互切;api.github.com git blobs 兜底降为 P1 候补。**
69
- 依据:
70
- - Contents API 对 >1MB 文件硬拒(直接 GET 返回 too_large),13.4MB **不可行**——PRD OQ2 的「Contents API 兜底」判死。
71
- - Git blobs API 理论可载(上限 100MB),但:需两跳(先取 commit/tree 解出 blob SHA,再取 blob);响应为 base64 JSON,13.4MB 二进制 ≈ 18MB 传输体积;未认证限流 60 req/h(探测+拉取+更新探测共享配额);api.github.com 无镜像、单点。可靠性全面劣于双 CDN。
72
- - R14 验收口径(「屏蔽首源后自动切换次源成功」)双 CDN 已满足:jsdelivr 与 fastly.jsdelivr 是不同 CDN 域、独立故障域,实测均 ✅。
73
- - 落地形态:`AWESOME_SOURCES = [jsdelivr, fastly]` 顺序 fetch,单源失败(网络错/非 2xx)进下一源,全失败抛聚合错误 → 上层按「有无缓存」给两档降级(顶部「目录已过期(上次更新 …)」提示条 / 空态重试)。SOURCES 为数组常量,P1 若要接 git blobs 只追加一项 provider,不重构。
74
-
75
- #### 1.4 UI 集成要点
76
-
77
- - 入口:市场「插件」子 tab 工具行顶部来源切换两态控件 `pluginSource: 'codebuddy' | 'awesome'`,默认 codebuddy,localStorage 键 `dsh-armory.pluginSource` 持久化(先例 `UPDATE_HINT_STATE_KEY` :3258)。**并列视图、不合并混排**(PRD R11 拍板)。
78
- - `MARKET_TABS`(:6334)不动——来源切换是插件 tab 内部状态,不是第五个子 tab。
79
- - awesome 状态切片全部带 `awesome` 前缀(awesomePlugins/awesomeLoading/awesomeError/awesomeStaleUpdated/awesomeUpdateHint/awesomeDetail/awesomeQuery/awesomeCategory/awesomeTags/awesomeSort/awesomePage/awesomeOnlyInstalled 复用主开关思想但独立状态),与 codebuddy 既有状态零交集。
80
- - 排序 `AWESOME_SORTS`:`stars 降序(默认)/ added 最新收录 / 名称 A→Z`,参照 `WB_CONNECTOR_SORTS`(:4702-4706)范式;全量本地排序,featured/official 不参与默认序(R18 拍板)。`stars` null 记 0 置底。
81
- - 分页:本地 ≤50 条/页(R11 验收口径),与连接器侧分页控件形态一致。
82
- - 懒触发更新探测(R15/X4):仅在 `source==='awesome'` 且目录已加载时探一次,组件存续期内 `useRef` 去重(照抄 :6206-6226 范式),无常驻定时器、不静默更新。
83
-
84
- ### 2. File List
85
-
86
- ```
87
- dsh-armory/
88
- ├── lib/
89
- │ ├── wb-awesome-market.js [新建] awesome 源数据层:双 CDN 拉取链、瘦身过滤、
90
- │ │ slim.json+meta.json 缓存、ETag 条件探测、模块 API
91
- │ ├── index.js [修改] :19 import;:868 decorator 声明;:971 后 3 组
92
- │ │ decorator+esDecorate;:1598 后 3 个 async 方法体
93
- │ ├── typert.js [修改] 2 个新 Result codec + 3 条 mv 声明(:108 区)
94
- │ └── client.js [修改] :7984 区 3 条 descriptors;:6186 区 awesome 状态
95
- │ 切片 + 来源切换 + 懒探测;:4697 区 AWESOME_SORTS;
96
- │ 插件 tab 渲染区 UI;:6232 npm 分支;:6261 来源登记
97
- ├── test/
98
- │ ├── wb-awesome-market.mjs [新建] 数据层套件(过滤口径/瘦身形状/原子写/304/双源切换)
99
- │ ├── run-all.mjs [修改] SUITES :116 后挂新套件
100
- │ └── auth-gate-regression.mjs [修改] :86-95 EXPECTED_SHA256 扩锚 wb-awesome-market.js
101
- └── docs/ (本设计三文件,禁改既有 docs)
102
- ```
103
-
104
- 涉及既有文件共 5 个修改 + 2 个新建;每个任务 ≤3 文件切片(见 Part B)。
105
-
106
- ### 3. Data Structures and Interfaces
107
-
108
- 完整类图见 `docs/class-diagram-awesome.mermaid`。核心形状:
109
-
110
- ```mermaid
111
- classDiagram
112
- class AwesomeMarketStore {
113
- <<module lib/wb-awesome-market.js>>
114
- +AWESOME_SOURCES$ string[2]
115
- +AWESOME_MARKETPLACE_ID$ string
116
- +slimEntries(rawArray) SlimEntry[]
117
- +wbAwesomeCatalog(options) AwesomeCatalogResult
118
- +wbAwesomeDetail(name, options) AwesomeDetailResult
119
- +wbAwesomeUpdateCheck(options) AwesomeUpdateHintResult
120
- +wbAwesomeCachedInfo(options) AwesomeCachedInfo
121
- -fetchChain(etag) FetchOutcome
122
- -ensureSlim(options) SlimState
123
- -writeCacheAtomic(path, data) Promise
124
- }
125
- class SlimEntry {
126
- +string name
127
- +string repo
128
- +string npm
129
- +string description
130
- +string category
131
- +string status
132
- +string[] tags
133
- +number stars
134
- +boolean official
135
- +string verifiedAgainst
136
- +number lastVerified
137
- }
138
- class McpManagerGateway {
139
- <<lib/index.js 增量>>
140
- +wbAwesomeCatalog(payload) AwesomeCatalogResult
141
- +wbAwesomeDetail(payload) AwesomeDetailResult
142
- +wbAwesomeUpdateCheck() AwesomeUpdateHintResult
143
- }
144
- class TypertRegistry {
145
- <<lib/typert.js 增量>>
146
- +mv('wbAwesomeCatalog', [payload], 'AwesomeCatalogResult')
147
- +mv('wbAwesomeDetail', [payload], 'AwesomeDetailResult')
148
- +mv('wbAwesomeUpdateCheck', [], 'WbUpdateHintResult')
149
- }
150
- class PluginBoardClient {
151
- <<lib/client.js 插件 tab 增量>>
152
- +string pluginSource
153
- +loadAwesomeCatalog(refresh) void
154
- +checkAwesomeUpdate() void
155
- +awesomeInstallSpec(item) string
156
- +installPlugin(item) void
157
- }
158
- class InstallStateStore {
159
- <<既有 lib/wb-install-state.js>>
160
- +record(item) Promise
161
- }
162
- AwesomeMarketStore --> SlimEntry : 投影产出
163
- McpManagerGateway --> AwesomeMarketStore : 薄转发
164
- TypertRegistry .. McpManagerGateway : strict schema 声明
165
- PluginBoardClient .. McpManagerGateway : call('wbAwesome*')
166
- PluginBoardClient --> InstallStateStore : wbInstallStateRecord
167
- ```
168
-
169
- ### 4. Program Call Flow
170
-
171
- 完整时序见 `docs/sequence-diagram-awesome.mermaid`。三条主链:
172
-
173
- **链 1|目录加载(含双源容错与缓存降级)**
174
- `用户切到 DSH 社区 → loadAwesomeCatalog() → call('wbAwesomeCatalog') → 网关 → ensureSlim():读 meta.json+slim.json → 无缓存:fetchChain(jsdelivr→fastly)拉 13.4MB → slimEntries 过滤 → 原子写 slim.json+meta.json;有缓存:条件 GET(If-None-Match)→ 304 直接用盘缓存 / 200 重瘦身重写;全源失败 → 有缓存 stale:true 返回 / 无缓存 ok:false → UI 提示条/空态`
175
-
176
- **链 2|懒触发更新探测(X4)**
177
- `目录加载完成后探一次(useRef 去重)→ call('wbAwesomeUpdateCheck') → HEAD If-None-Match(405/501 降级 GET cancel body)→ 304=up-to-date / 200 ETag 变化=update-available → 顶部更新提示 → 用户确认 → loadAwesomeCatalog({refresh:true})`
178
-
179
- **链 3|npm 安装**
180
- `点击安装(仅 npm 条目可点)→ awesomeInstallSpec → npm:<name> → pluginManager.installBundle(spec, {}) 严格双参 → 成功 → call('wbInstallStateRecord', {kind:'plugin', key:name, marketplace:'awesome-dsh-plugins', …}) → restartNotice「需重启 DSH 宿主」`
181
-
182
- ### 5. Anything UNCLEAR
183
-
184
- 1. **jsdelivr ETag 稳定性未实测**(上游 CDN 是否对 `gh/` 路径输出稳定 ETag)。已定防御回退:无 ETag → 探测返回 `probe-failed`、功能不阻塞(见裁定 5)。T01 实测后如有稳定 ETag,回退分支即成死代码(保留无害)。
185
- 2. **api.github.com git blobs 兜底**:已裁定不进 P0(依据 1.3 裁定 6)。若 P1 要接,需先实测 blobs API 对 13.4MB 的实际吞吐与限流表现。
186
- 3. **瘦身缓存实际体积**:预估 1.5–2MB、上限 3MB 以 T01 实测为准;超限回退 = description 截断 120→80 字符(体积弹性最大的单一旋钮,可再降)。
187
- 4. **npm 字段类型**:PRD 字段表标注 `npm?`,schema 未强制类型;按字符串设计,T01 防御逻辑兼容对象形态(取 `.name`),实测后收敛。
188
- 5. **stars 值域**:可能缺失/null;默认序中记 0 置底,不臆造展示(显示 `—`)。
189
- 6. **verifiedAgainst 值域**:假定为 `dsh x.y.z` 形态字符串,展示原样不做校验;畸形值原样透出(徽章降级为纯文本)。
190
- 7. **详情字段边界**:P0 详情弹层只有瘦身字段(含 lastVerified/verifiedAgainst/repo/npm);pushedAt/added/starsUpdated/evidence 链接属 R19(P2),slim 中不落盘。
191
-
192
- ---
193
-
194
- ## Part B: Task Decomposition
195
-
196
- ### 6. Required Packages
197
-
198
- **无**。零新依赖(Node ≥ 22.15 内置 `node:fs/promises` / `node:path` / `node:os` / 全局 `fetch` / `AbortSignal` 全覆盖)。测试侧同样仅 node: 内置模块,与仓库全部既有套件一致。
199
-
200
- ### 7. Task List(按依赖排序,共 5 个)
201
-
202
- > 执行纪律(对每个任务通用):grep 定位 → read ≤60 行窗口(client.js 禁整读)→ edit 定点;所有文件行尾 LF;验收 = `node --check <改动文件>` + 既有 29 套件不红 + 新套件绿。
203
-
204
- **T01|awesome 数据层(纯新增,零风险)**
205
- - Source Files:`lib/wb-awesome-market.js`(新建)、`test/wb-awesome-market.mjs`(新建)、`test/run-all.mjs`(:116 后挂套件,仅 +1 行)
206
- - 依赖:无 | 优先级:P0
207
- - 要点:常量 `AWESOME_SOURCES`(jsdelivr→fastly 顺序)、`AWESOME_MARKETPLACE_ID='awesome-dsh-plugins'`;纯函数 `slimEntries`(R11 口径过滤+映射+防御);`ensureSlim`(读缓存→条件 GET→304 复用/200 重瘦身→staging+rename 原子写);四导出 API + `rootDir/DSH_ARMORY_HOME` 注入。测试覆盖:过滤口径(verified∧npm 交集、broken/unverified 出局)、瘦身字段形状、畸形行防御、原子写、304 复用不重写、双源切换(mock fetch 首源失败)、缓存 ≤3MB 断言。
208
- - 验收:`node --check` ×2、新套件绿、既有 29 套件不红。
209
-
210
- **T02|Host 侧契约三件套(Host 半边)**
211
- - Source Files:`lib/index.js`、`lib/typert.js`
212
- - 依赖:T01 | 优先级:P0
213
- - 要点:index.js——:19 加 `import { wbAwesomeCatalog, wbAwesomeDetail, wbAwesomeUpdateCheck } from "./wb-awesome-market.js"`;:868 decorator 声明行尾追加 3 个;:971 `_wbPluginUpdateCheck_decorators` 组后照范式加 3 组 `[Remote(...)]` + `__esDecorate`;:1598 `wbPluginUpdateCheck` 方法体后加 3 个 async 薄转发。typert.js——:15 codec 范式新增 `AwesomeCatalogResult`/`AwesomeDetailResult` 两个 Result symbol(`typeSymbol: 'dsh-armory/typert#AwesomeCatalogResult'` 等);:108 区追加 3 条 mv(详见裁定 3 表)。
214
- - 验收:`node --check` ×2、`test/contract-consistency.mjs` 绿、wb-plugin-market 套件不红。
215
-
216
- **T03|client 契约条目 + 来源切换状态机(client 半边契约)**
217
- - Source Files:`lib/client.js`(仅此一个文件)
218
- - 依赖:T02 | 优先级:P0
219
- - 要点:①:7984 区 descriptors 追加 3 条(id `'dsh-armory#mcpManager/wbAwesomeCatalog'` / `wbAwesomeDetail`(parameters `[mcpParam('payload','ImportPayload')]`)/ `wbAwesomeUpdateCheck`(`[]`),result 分别 `mcpCodec('AwesomeCatalogResult'|'AwesomeDetailResult'|'WbUpdateHintResult')`)。②:6186 区新增 `pluginSource` state + localStorage `dsh-armory.pluginSource` 持久化(参照 :3258-3270 范式)+ awesome 状态切片 + `loadAwesomeCatalog`/`checkAwesomeUpdate`(照抄 :6194-6226 懒触发范式,source==='awesome' 时加载、加载完成后探一次、useRef 去重)。③接收 catalog 响应后 `item.marketplace = res.marketplace` 统一贴标。**本任务不动任何 UI 文案(i18n 零新增)**。
220
- - 验收:`node --check`、contract-consistency/client-entry/client-i18n/i18n-coverage 不红。
221
-
222
- **T04|DSH 社区视图 UI(工具行/列表/筛选/排序/分页/提示条/i18n)**
223
- - Source Files:`lib/client.js`(仅此一个文件)
224
- - 依赖:T03 | 优先级:P0
225
- - 要点:①来源切换控件(「CodeBuddy 官方|DSH 社区」两态,默认 codebuddy)。②工具行:搜索框(独立 awesomeQuery)+ category 5 chips 单选 + tags 17 枚举多选 + 排序下拉 `AWESOME_SORTS`(:4697 区新增,参照 WB_CONNECTOR_SORTS :4702-4706;**默认 stars 降序**)+ 分页 ≤50/页。③卡片渲染:名称/description 截断/category 徽章/tags/stars/「✓ verified (dsh x)」/官方/精选徽章/安装按钮(npm 缺失 → 灰置「暂不支持安装」,本任务只渲染态,行为接线在 T05)。④提示条:stale「目录已过期(上次更新 …)」/ 更新提示(确认后 `loadAwesomeCatalog({refresh:true})`)/ 空态重试 / 首载骨架。⑤**新增的全部 t('中文') 逐条同步 EN_US_MESSAGES 译文**(i18n-coverage 红线;文案清单随任务书给全)。
226
- - 验收:`node --check`、i18n-coverage/client-i18n/error-boundary-copy 不红、手测浏览/筛选/排序/分页。
227
-
228
- **T05|安装链接通 + 回归锚扩展**
229
- - Source Files:`lib/client.js`、`test/auth-gate-regression.mjs`
230
- - 依赖:T04 | 优先级:P0
231
- - 要点:①:6232 `pluginInstallSpec` 顶部加 npm 分支 `if (plugin?.npm) return \`npm:${String(plugin.npm).trim()}\`;`(repository 分支与 codebuddy 路径行为不变)。②:6261-6267 登记来源改 `marketplace: plugin.marketplace || 'codebuddy-plugins-official'`;安装成功沿用 `restartNotice`(:6273)。③灰置按钮接 installPlugin(仅 npm 条目可触发)。④:86-95 `EXPECTED_SHA256` 增补 `"wb-awesome-market.js"` 锚——哈希值以 `.deploy-apply.mjs` 部署后运行时副本实测为准,工程师先按仓库文件算占位并标注 TODO 由主理人部署后回填(先例:全部新增模块都纳入锚表)。
232
- - 验收:`node --check` ×2、auth-gate-regression(部署后)绿、任选 1 条 npm 插件安装成功出现「需重启 DSH 宿主」提示、重启宿主后生效(R12 验收)。
233
-
234
- ### 8. Shared Knowledge(给 Engineer 的横切须知)
235
-
236
- 1. **契约三件套铁律**:任何新 Remote 方法必须 `lib/index.js`(方法体 + `_x_decorators = [Remote("x")]`)+ `lib/typert.js`(`mv('<m>', params, 'ResultSymbol')`)+ `lib/client.js`(`MCP_CONTRIBUTION.descriptors` 条目,id 形如 `'dsh-armory#mcpManager/<m>'`)三处同步;`test/contract-consistency.mjs` 校验 client ⊆ typert、client ⊆ index 方法集、index 装饰器 ⊆ typert。漏一层 = 静默 404。
237
- 2. **client.js 增量纪律**:8111 行禁整读。锚点行号(截至 2026-10-20 15:05,实现时以 grep 重新定位):来源状态区 :3258(localStorage 范式)、排序范式 :4697-4706、插件 tab 状态 :6186-6226、`pluginInstallSpec` :6232-6241、`installPlugin` :6242-6280、`filteredPlugins` :6281-6287、`MARKET_TABS` :6334、restartNotice 渲染 :6675-6678、descriptors :7938-8005。
238
- 3. **i18n 红线**:新增任何 `t('中文')` 必须同步 EN_US_MESSAGES(client.js 头部 L58-812 区间)补 EN 译文,`test/i18n-coverage.mjs` 会红。
239
- 4. **X 约束继承**:X4 无常驻定时器(联网仅懒触发一次性探测/手动刷新);X9 不代填凭据(awesome 数据无凭据字段);安装成功必须 restartNotice 提示;安装登记统一走 `wbInstallStateRecord`。
240
- 5. **测试注入**:所有新数据层 API 用 `options.rootDir` / `DSH_ARMORY_HOME` 注入临时目录,绝不碰真实 `~/.dsh`(wb-registry/wb-install-state 同款)。
241
- 6. **installBundle 严格双参**:`pluginManager[UPGRADE_METHOD](spec, {})`,少传第二参被 wire 拒绝;失败判定只认 `res.value.application === 'failed'/'cancelled'`(:6255 已修好,勿回退)。
242
- 7. **部署机制**:仓库改动不自动生效;运行时副本 `C:\Users\LENOVO\.dsh\profiles\desktop\node_modules\dsh-armory` 由主理人 `.deploy-apply.mjs` 复制;`auth-gate-regression.mjs` 锚哈希在部署后回填。
243
- 8. **行尾 LF**:仓库 .editorconfig 规约,新文件与编辑处一律 LF。
244
- 9. **修复中的并行 bug**(目录投影漏 repository、installPlugin 假成功)由其他工程师处理,本增量不以其为前置,验收按修复后正常行为口径。
245
-
246
- ### 9. Task Dependency Graph
247
-
248
- ```mermaid
249
- graph TD
250
- T01["T01 数据层<br/>wb-awesome-market.js + 新套件 + run-all 挂载"] --> T02["T02 Host 契约<br/>index.js + typert.js"]
251
- T02 --> T03["T03 client 契约+状态<br/>descriptors + pluginSource + 懒探测"]
252
- T03 --> T04["T04 社区视图 UI<br/>工具行/列表/排序/分页/提示条/i18n"]
253
- T04 --> T05["T05 安装链+回归锚<br/>npm spec 分支 + 来源登记 + SHA256 扩锚"]
254
- ```
255
-
256
- 所有任务线性依赖但每个任务文件互不重叠(除 client.js 被 T03/T04/T05 依次推进、每步独立可验收),T01 为零风险纯新增可先行合入。
257
-
258
- ---
259
-
260
- ## 裁定速查表
261
-
262
- | 决策点 | 裁定 | 一句话依据 |
263
- |--------|------|-----------|
264
- | 源注册 | 常量内置于新模块,wb-registry 零改动 | 避开 wb-registry.mjs 6+ 处 length===2 断言;registry 化留 P2 |
265
- | 新模块 | lib/wb-awesome-market.js 纯数据层 | zip 管线不适用;与既有分层同构 |
266
- | Remote 面 | 3 方法;Refresh 并入 Catalog(payload.refresh);复用 WbUpdateHintResult | 最小契约面积 |
267
- | 安装链 | npm 分支进 pluginInstallSpec 顶部;无 npm 一律灰置 | R12 P0 / R16 P1 分界 |
268
- | 更新探测 | 五枚举原样复用;无 ETag → probe-failed 不阻塞 | stale-cache 属 catalog 层状态非探测 reason |
269
- | 容错链 | jsdelivr→fastly 双 CDN;git blobs 降 P1 | Contents >1MB 硬拒;blobs 两跳+60/h 限流+18MB base64 劣于双 CDN |