dsh-armory 2.3.0 → 2.3.2

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.
package/lib/index.js CHANGED
@@ -7,7 +7,7 @@ import { interpolate } from "@deepseek-ai/cordis-plugin-loader";
7
7
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
8
8
  import { basename, dirname, join } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
- import { appendSelectedBuiltinMcpServers, BUILTIN_MCP_SERVERS, builtinMcpCatalog, normalizeMcpImport, readManagedMcpServers, setManagedMcpDisabled, updateManagedMcpPatch } from "./mcp-config.js";
10
+ import { normalizeMcpImport, readManagedMcpServers, setManagedMcpDisabled, updateManagedMcpPatch } from "./mcp-config.js";
11
11
  import { admitImportedServers } from "./import-admission.js";
12
12
  import { collectImportLocations, missingEnvNames, resolveImportLocation } from "./mcp-import-sources.js";
13
13
  import { deriveMcpPhase, formatMcpLog, sanitizeMcpLog } from "./mcp-observability.js";
@@ -16,12 +16,17 @@ import { allMcpEntries, entriesForName, projectToolSchema, revisionOf, toolInven
16
16
  import { removeSkillFromDisk } from "./skill-remove.js";
17
17
  import { WHATS_NEW } from "./whats-new.js";
18
18
  import { attachWhatsNewEnglish } from "./whats-new-en.js";
19
- import { probeMcpServersAuthorization, wbConnectorCatalog, wbConnectorCachedCount, wbConnectorPreview, wbSkillCatalog, wbSkillDetail, wbSkillInstall, wbSkillInstalled, wbSkillUninstall } from "./wb-market.js";
20
- // T03:多市场注册表(R4)与 codebuddy 插件市场(R5)的宿主侧数据层。
19
+ import { probeMcpServersAuthorization, wbConnectorCatalog, wbConnectorCachedCount, wbConnectorIcon, wbConnectorPreview, wbSkillCatalog, wbSkillDetail, wbSkillInstall, wbSkillInstalled, wbSkillUninstall } from "./wb-market.js";
20
+ // T03/R4:多市场注册表(连接器源)的宿主侧数据层。
21
21
  import { readRegistry, upsertMarketplace } from "./wb-registry.js";
22
- import { wbPluginCatalog, wbPluginDetail, wbPluginUpdateCheck } from "./wb-plugin-market.js";
23
22
  // T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)的宿主侧数据层。
24
23
  import { wbAwesomeCatalog, wbAwesomeDetail, wbAwesomeReadme, wbAwesomeUpdateCheck } from "./wb-awesome-market.js";
24
+ // 社区元信息(维护者 / 最近提交 / issue / star):打开详情时按需查,6 小时缓存。
25
+ import { wbRepoMeta } from "./community-meta.js";
26
+ // npm 近 30 天下载量:插件卡片底栏的「↓N」,目录里没有这个字段,只能去 npm 统计接口取。
27
+ import { wbNpmDownloads } from "./npm-downloads.js";
28
+ // 装备清单:只读汇总本机已装条目,校验与差异计算保持纯函数(lib/loadout.js)。
29
+ import { buildLoadout, diffLoadout } from "./loadout.js";
25
30
  import { readState as readInstallState, record as recordInstallState, forget as forgetInstallState } from "./wb-install-state.js";
26
31
  // 自定义套件(Skill 看板):读 / 整集合覆盖保存,数据层 lib/suites-registry.js。
27
32
  import { readSuites as readSkillSuites, saveSuites as writeSkillSuites } from "./suites-registry.js";
@@ -33,10 +38,12 @@ import {
33
38
  cancelAuthorization as oauthCancel,
34
39
  clearTokenRecord as oauthClearToken,
35
40
  ensureAccessToken as oauthEnsureToken,
41
+ isSerializedGarbageCredential as oauthIsGarbageCredential,
42
+ isUsableBearerToken as oauthIsUsableToken,
36
43
  saveManualToken as oauthSaveManualToken,
37
44
  submitCallback as oauthSubmitCallback,
38
45
  } from "./mcp-oauth.js";
39
- import { hasTeamBridge, installExpertSkills, listExpertRecent, listExpertSkills, prepareExpertPersona, readExpertPresets, recordExpertRecent, removeExpertPreset, uninstallExpertSkills, uninstallTeamMemberPersonas, wbExpertCatalog, wbExpertDetail, writeExpertPreset } from "./expert-market.js";
46
+ import { hasTeamBridge, installExpertSkills, listExpertRecent, listExpertSkills, prepareExpertPersona, readExpertDisplayNames, readExpertPresets, recordExpertRecent, removeExpertPreset, uninstallExpertSkills, uninstallTeamMemberPersonas, wbExpertCatalog, wbExpertDetail, writeExpertPreset } from "./expert-market.js";
40
47
  import { attachPersona, detachPersona, listPersonas, reconcilePersonas } from "./session-persona.js";
41
48
  // clearTextCache 的 import 已随 toolTailor 的「清空缓存」动作一起删掉(见下方 toolTailor 注释)。
42
49
  import { scanSessionLedger } from "./tool-ledger.js";
@@ -116,6 +123,24 @@ function withBearerHeader(rawServer, token) {
116
123
  return { ...rawServer, headers };
117
124
  }
118
125
 
126
+ /**
127
+ * 摘掉一个已经坏掉的 authorization 头(保留其它头、保留原键名大小写)。
128
+ * 只给「确认是序列化残骸」的配置用:历史版本把 `Bearer [object Object]` 写进了
129
+ * 用户的 patch,那条头不删掉,服务器就从「你没带凭据」(401) 变成「你凭据是坏的」(400),
130
+ * 装前探测因此再也认不出这是鉴权问题,用户既装不上也看不到授权入口。
131
+ */
132
+ function stripBearerHeader(rawServer) {
133
+ const container = rawServer && typeof rawServer.transport === "object" ? rawServer.transport : rawServer;
134
+ const headers = container?.headers;
135
+ if (!headers || typeof headers !== "object" || Array.isArray(headers)) return rawServer;
136
+ const next = {};
137
+ for (const [key, value] of Object.entries(headers)) {
138
+ if (String(key).toLowerCase() !== "authorization") next[key] = value;
139
+ }
140
+ const patched = { ...container, headers: next };
141
+ return rawServer && typeof rawServer.transport === "object" ? { ...rawServer, transport: patched } : patched;
142
+ }
143
+
119
144
  /**
120
145
  * 安装前把已落盘的 access_token 注进对应 server 的 headers。
121
146
  * 读不到 / 过期且刷不动就原样返回,交给 probe 去判定「需要授权」——
@@ -126,17 +151,27 @@ async function injectStoredOAuthTokens(mcpServers) {
126
151
  const out = {};
127
152
  for (const [serverName, rawServer] of Object.entries(mcpServers)) {
128
153
  const spec = nestedServerSpec(rawServer);
129
- if (!isOAuthCandidate(spec) || spec.headers.authorization) {
154
+ // 配置里已有的 authorization 一律优先:可能是用户手填的真令牌,也可能是 R7
155
+ // 的 ${...} 占位符(那种要原样保留、由 missingSlots 回报面板补填)。
156
+ const declared = spec.headers.authorization;
157
+ const poisoned = Boolean(declared) && oauthIsGarbageCredential(declared);
158
+ if (!isOAuthCandidate(spec) || (declared && !poisoned)) {
130
159
  out[serverName] = rawServer;
131
160
  continue;
132
161
  }
133
162
  let token = "";
134
163
  try {
135
- token = String((await oauthEnsureToken(spec.url)) || "");
164
+ // ensureAccessToken 回的是 {ok, accessToken, refreshed, error} 对象,不是字符串。
165
+ // 这里曾经直接对那个对象取字符串:得到 "[object Object]",真值、非空,
166
+ // 于是每个 HTTP 连接器都被注进 `authorization: Bearer [object Object]`
167
+ // —— 装上必挂,还把服务器的 401 伪装成 400(见 test/auth-gate-regression.mjs 第 7 节)。
168
+ const result = await oauthEnsureToken(spec.url);
169
+ if (result?.ok && oauthIsUsableToken(result.accessToken)) token = String(result.accessToken).replace(/^Bearer\s+/i, "").trim();
136
170
  } catch {
137
171
  token = "";
138
172
  }
139
- out[serverName] = token ? withBearerHeader(rawServer, token) : rawServer;
173
+ if (token) out[serverName] = withBearerHeader(rawServer, token);
174
+ else out[serverName] = poisoned ? stripBearerHeader(rawServer) : rawServer;
140
175
  }
141
176
  return out;
142
177
  }
@@ -150,7 +185,12 @@ async function resolveOAuthTarget(connectorId, serverName) {
150
185
  if (!rawServer) throw new Error(`连接器 ${connectorId} 里没有 server「${name || "(空)"}」`);
151
186
  const spec = nestedServerSpec(rawServer);
152
187
  if (!spec.url) throw new Error(`server「${name}」没有可授权的 URL(stdio 型不需要 OAuth 授权)`);
153
- return { serverName: name, url: spec.url, headers: spec.headers, transport: spec.transport };
188
+ // 坏掉的 authorization 不能跟着去探测:带着 `Bearer [object Object]` 打过去,服务器
189
+ // 回的是 400「token parse failed」而不是 401「empty access token」,授权发现
190
+ // 那条路就在第一步被判定成「非鉴权错误」而中止。
191
+ const headers = { ...spec.headers };
192
+ if (headers.authorization && oauthIsGarbageCredential(headers.authorization)) delete headers.authorization;
193
+ return { serverName: name, url: spec.url, headers, transport: spec.transport };
154
194
  }
155
195
 
156
196
  /** 宿主 shell 服务的几种可能落点,逐个试;拿不到就返回 undefined 走系统兜底。 */
@@ -381,12 +421,6 @@ function externalMcpEntries(ctx, state) {
381
421
  return !managedIds.has(entry.options.id) && !(!entry.options.group && managedNames.has(entry.options.config.serverName));
382
422
  });
383
423
  }
384
- function effectiveMcpServers(ctx, state) {
385
- return [
386
- ...state.servers,
387
- ...externalMcpEntries(ctx, state).map((entry) => ({ ...entry.options.config, name: entry.options.config.serverName })),
388
- ];
389
- }
390
424
  function revealRevision(state, groups) {
391
425
  const values = [];
392
426
  for (const current of state.servers) {
@@ -589,7 +623,6 @@ function summarize(entry, inventory, managed, conflict = false, lastLog = null)
589
623
  // 已经是不可用的终态证据),而不是去猜 mcp-client 的日志文案;日志只用来填 lastError 的
590
624
  // 具体原因。挂载失败(我们自己 setup 阶段就挂不上:配置求值为空、工具注册被拒)与连接
591
625
  // 失败(mcp-client 那边的事)分开标记,不混为一谈。
592
- // 已导出作为测试接缝(同 checkForUpdate)。
593
626
  function summarizeWorkspaceRow(ctx, wsPath, server, mountError) {
594
627
  // 诊断通路就在这里被依赖,就在这里确保开着(按 root 幂等,与全局 list/status 同一份)。
595
628
  ensureLogCapture(ctx);
@@ -1048,10 +1081,148 @@ async function listMcpServers(ctx) {
1048
1081
  };
1049
1082
  }
1050
1083
 
1084
+ // ── 装备清单:本机条目收集(只读,只投影身份字段)────────────────────────
1085
+ // 导出与导入对比共用这一份收集逻辑,避免两个入口对「本机装了什么」给出不同答案。
1086
+ // 绝不把 env / headers 的值带出去:MCP 只保留键名,技能只保留名称与来源,
1087
+ // 专家从 profile 补丁的召唤预设读 plugin、从目录缓存补展示名,插件读安装账本。
1088
+ function endpointHostOf(value) {
1089
+ if (typeof value !== "string" || !value) return "";
1090
+ try {
1091
+ return new URL(value).host || "";
1092
+ } catch {
1093
+ return "";
1094
+ }
1095
+ }
1096
+
1097
+ async function collectLoadoutItems(ctx) {
1098
+ const items = [];
1099
+ let records = [];
1100
+ try {
1101
+ const state = await readInstallState();
1102
+ records = Array.isArray(state?.items) ? state.items : [];
1103
+ } catch {
1104
+ // 账本读不到仍然可以导出 MCP / 技能 / 专家;插件条目会缺,但不该整单失败。
1105
+ }
1106
+
1107
+ // 1) MCP:以真实注册表为准;连接器账本用于补来源,名称对不上时保留账本条目。
1108
+ try {
1109
+ const { servers } = await listMcpServers(ctx);
1110
+ const connectorByName = new Map();
1111
+ for (const record of records) {
1112
+ if (record?.kind !== "connector") continue;
1113
+ const key = String(record.key || "").trim();
1114
+ if (key) connectorByName.set(key, record);
1115
+ const rowName = String(record.name || "").trim();
1116
+ if (rowName) connectorByName.set(rowName, record);
1117
+ }
1118
+ const seen = new Set();
1119
+ for (const server of servers) {
1120
+ const name = String(server?.serverName || "").trim();
1121
+ if (!name) continue;
1122
+ seen.add(name);
1123
+ const connector = connectorByName.get(name);
1124
+ const configKeys = [
1125
+ ...Object.keys(server.env || {}),
1126
+ ...Object.keys(server.headers || {}),
1127
+ ].filter(Boolean);
1128
+ const endpointHost = endpointHostOf(server.url);
1129
+ items.push({
1130
+ kind: "mcp",
1131
+ name,
1132
+ ...(server.transport ? { transport: server.transport } : {}),
1133
+ source: connector ? `connector:${connector.key}` : "manual",
1134
+ ...(endpointHost ? { endpointHost } : {}),
1135
+ ...(configKeys.length ? { configKeys: [...new Set(configKeys)] } : {}),
1136
+ ...(typeof server.enabled === "boolean" ? { enabled: server.enabled } : {}),
1137
+ });
1138
+ }
1139
+ for (const record of records) {
1140
+ if (record?.kind !== "connector") continue;
1141
+ const key = String(record.key || "").trim();
1142
+ const name = String(record.name || key).trim();
1143
+ if (!name || seen.has(name) || (key && seen.has(key))) continue;
1144
+ seen.add(name);
1145
+ items.push({ kind: "mcp", name, source: `connector:${key || name}` });
1146
+ }
1147
+ } catch {
1148
+ // MCP 注册表不可用时跳过这一组,其余组照常导出。
1149
+ }
1150
+
1151
+ // 2) 技能:宿主 skill 注册表的全局视图;账本用于补市场来源与版本。
1152
+ try {
1153
+ const ledger = new Map();
1154
+ for (const record of records) {
1155
+ if (record?.kind === "skill") ledger.set(String(record.key || ""), record);
1156
+ }
1157
+ await withSkillScope(ctx, { scope: "global" }, async (registry, options) => {
1158
+ if (typeof registry.list !== "function") return;
1159
+ const rows = await registry.list(options);
1160
+ for (const skill of rows) {
1161
+ const name = String(skill?.name || "").trim();
1162
+ if (!name) continue;
1163
+ const record = ledger.get(name);
1164
+ items.push({
1165
+ kind: "skill",
1166
+ name,
1167
+ source: String(record?.source?.marketplace || skill.source || "local"),
1168
+ ...(record?.version ? { version: String(record.version) } : {}),
1169
+ });
1170
+ }
1171
+ });
1172
+ } catch {
1173
+ // 宿主没挂 skill 注册表时跳过技能组。
1174
+ }
1175
+
1176
+ // 3) 专家:profile 补丁里的召唤预设 + 目录缓存补展示名 / 更新时间。
1177
+ try {
1178
+ const patch = await readPatch(ctx);
1179
+ const presets = readExpertPresets(patch.content);
1180
+ let experts = [];
1181
+ try {
1182
+ const catalog = await wbExpertCatalog();
1183
+ experts = Array.isArray(catalog?.experts) ? catalog.experts : [];
1184
+ } catch {
1185
+ // 目录拉不到就退回 plugin id 作名称。
1186
+ }
1187
+ const byPlugin = new Map(experts.map((expert) => [String(expert.plugin || ""), expert]));
1188
+ for (const preset of presets) {
1189
+ const plugin = String(preset?.plugin || "").trim();
1190
+ if (!plugin) continue;
1191
+ const expert = byPlugin.get(plugin);
1192
+ items.push({
1193
+ kind: "expert",
1194
+ name: String(expert?.name || plugin),
1195
+ plugin,
1196
+ source: "workbuddy-expert",
1197
+ ...(expert?.updatedAt ? { updatedAt: String(expert.updatedAt) } : {}),
1198
+ });
1199
+ }
1200
+ } catch {
1201
+ // 没有 profile 补丁(或读不到)时跳过专家组。
1202
+ }
1203
+
1204
+ // 4) 插件:只有客户端在安装成功后回写的账本知道装了什么。
1205
+ for (const record of records) {
1206
+ if (record?.kind !== "plugin") continue;
1207
+ const key = String(record.key || "").trim();
1208
+ const name = String(record.name || key).trim();
1209
+ if (!name) continue;
1210
+ items.push({
1211
+ kind: "plugin",
1212
+ name,
1213
+ ...(key ? { key } : {}),
1214
+ source: String(record.source?.marketplace || ""),
1215
+ ...(record.version ? { version: String(record.version) } : {}),
1216
+ });
1217
+ }
1218
+
1219
+ return items;
1220
+ }
1221
+
1051
1222
  let McpManagerGateway = (() => {
1052
1223
  let _classSuper = TypertRemoteService;
1053
1224
  let _instanceExtraInitializers = [];
1054
- let _list_decorators, _status_decorators, _enable_decorators, _disable_decorators, _reconnect_decorators, _tools_decorators, _builtins_decorators, _installBuiltins_decorators, _add_decorators, _update_decorators, _remove_decorators, _reveal_decorators, _previewImport_decorators, _importJson_decorators, _listWorkspaces_decorators, _getWorkspaceView_decorators, _addWorkspaceServer_decorators, _updateWorkspaceServer_decorators, _removeWorkspaceServer_decorators, _setWorkspaceExclude_decorators, _installWorkspaceBuiltins_decorators, _previewWorkspaceImport_decorators, _importWorkspaceJson_decorators, _revealWorkspaceServer_decorators, _projectConnections_decorators, _reconnectWorkspaceServer_decorators, _updateHint_decorators, _whatsNew_decorators, _scanImportSources_decorators, _previewImportSource_decorators, _importSource_decorators, _toolTailor_decorators, _wbConnectorCatalog_decorators, _wbConnectorCachedCount_decorators, _wbConnectorPreview_decorators, _wbConnectorInstall_decorators, _wbOAuthBegin_decorators, _wbOAuthPoll_decorators, _wbOAuthCancel_decorators, _wbOAuthToken_decorators, _wbOAuthOverview_decorators, _wbOAuthOpen_decorators, _wbSkillCatalog_decorators, _wbSkillDetail_decorators, _wbSkillInstall_decorators, _wbSkillUninstall_decorators, _wbSkillInstalled_decorators, _wbMarketRegistry_decorators, _wbMarketRegistryUpsert_decorators, _wbPluginCatalog_decorators, _wbPluginDetail_decorators, _wbInstallState_decorators, _wbInstallStateRecord_decorators, _wbInstallStateForget_decorators, _wbPluginUpdateCheck_decorators, _wbAwesomeCatalog_decorators, _wbAwesomeDetail_decorators, _wbAwesomeReadme_decorators, _wbAwesomeUpdateCheck_decorators;
1225
+ let _list_decorators, _status_decorators, _enable_decorators, _disable_decorators, _reconnect_decorators, _tools_decorators, _add_decorators, _update_decorators, _remove_decorators, _reveal_decorators, _previewImport_decorators, _importJson_decorators, _listWorkspaces_decorators, _getWorkspaceView_decorators, _addWorkspaceServer_decorators, _updateWorkspaceServer_decorators, _removeWorkspaceServer_decorators, _setWorkspaceExclude_decorators, _previewWorkspaceImport_decorators, _importWorkspaceJson_decorators, _revealWorkspaceServer_decorators, _projectConnections_decorators, _reconnectWorkspaceServer_decorators, _updateHint_decorators, _whatsNew_decorators, _scanImportSources_decorators, _previewImportSource_decorators, _importSource_decorators, _toolTailor_decorators, _wbConnectorCatalog_decorators, _wbConnectorCachedCount_decorators, _wbConnectorIcon_decorators, _wbConnectorPreview_decorators, _wbConnectorInstall_decorators, _wbOAuthBegin_decorators, _wbOAuthPoll_decorators, _wbOAuthCancel_decorators, _wbOAuthToken_decorators, _wbOAuthOverview_decorators, _wbOAuthOpen_decorators, _wbSkillCatalog_decorators, _wbSkillDetail_decorators, _wbSkillInstall_decorators, _wbSkillUninstall_decorators, _wbSkillInstalled_decorators, _wbMarketRegistry_decorators, _wbMarketRegistryUpsert_decorators, _wbInstallState_decorators, _wbInstallStateRecord_decorators, _wbInstallStateForget_decorators, _wbAwesomeCatalog_decorators, _wbAwesomeDetail_decorators, _wbAwesomeReadme_decorators, _wbAwesomeUpdateCheck_decorators, _wbRepoMeta_decorators, _wbNpmDownloads_decorators, _wbLoadoutExport_decorators, _wbLoadoutDiff_decorators;
1055
1226
  return class McpManagerGateway extends _classSuper {
1056
1227
  static {
1057
1228
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
@@ -1067,10 +1238,6 @@ let McpManagerGateway = (() => {
1067
1238
  __esDecorate(this, null, _reconnect_decorators, { kind: "method", name: "reconnect", static: false, private: false, access: { has: (obj) => "reconnect" in obj, get: (obj) => obj.reconnect }, metadata: _metadata }, null, _instanceExtraInitializers);
1068
1239
  _tools_decorators = [Remote("tools")];
1069
1240
  __esDecorate(this, null, _tools_decorators, { kind: "method", name: "tools", static: false, private: false, access: { has: (obj) => "tools" in obj, get: (obj) => obj.tools }, metadata: _metadata }, null, _instanceExtraInitializers);
1070
- _builtins_decorators = [Remote("builtins")];
1071
- __esDecorate(this, null, _builtins_decorators, { kind: "method", name: "builtins", static: false, private: false, access: { has: (obj) => "builtins" in obj, get: (obj) => obj.builtins }, metadata: _metadata }, null, _instanceExtraInitializers);
1072
- _installBuiltins_decorators = [Remote("installBuiltins")];
1073
- __esDecorate(this, null, _installBuiltins_decorators, { kind: "method", name: "installBuiltins", static: false, private: false, access: { has: (obj) => "installBuiltins" in obj, get: (obj) => obj.installBuiltins }, metadata: _metadata }, null, _instanceExtraInitializers);
1074
1241
  _add_decorators = [Remote("add")];
1075
1242
  __esDecorate(this, null, _add_decorators, { kind: "method", name: "add", static: false, private: false, access: { has: (obj) => "add" in obj, get: (obj) => obj.add }, metadata: _metadata }, null, _instanceExtraInitializers);
1076
1243
  _update_decorators = [Remote("update")];
@@ -1095,8 +1262,6 @@ let McpManagerGateway = (() => {
1095
1262
  __esDecorate(this, null, _removeWorkspaceServer_decorators, { kind: "method", name: "removeWorkspaceServer", static: false, private: false, access: { has: (obj) => "removeWorkspaceServer" in obj, get: (obj) => obj.removeWorkspaceServer }, metadata: _metadata }, null, _instanceExtraInitializers);
1096
1263
  _setWorkspaceExclude_decorators = [Remote("setWorkspaceExclude")];
1097
1264
  __esDecorate(this, null, _setWorkspaceExclude_decorators, { kind: "method", name: "setWorkspaceExclude", static: false, private: false, access: { has: (obj) => "setWorkspaceExclude" in obj, get: (obj) => obj.setWorkspaceExclude }, metadata: _metadata }, null, _instanceExtraInitializers);
1098
- _installWorkspaceBuiltins_decorators = [Remote("installWorkspaceBuiltins")];
1099
- __esDecorate(this, null, _installWorkspaceBuiltins_decorators, { kind: "method", name: "installWorkspaceBuiltins", static: false, private: false, access: { has: (obj) => "installWorkspaceBuiltins" in obj, get: (obj) => obj.installWorkspaceBuiltins }, metadata: _metadata }, null, _instanceExtraInitializers);
1100
1265
  _previewWorkspaceImport_decorators = [Remote("previewWorkspaceImport")];
1101
1266
  __esDecorate(this, null, _previewWorkspaceImport_decorators, { kind: "method", name: "previewWorkspaceImport", static: false, private: false, access: { has: (obj) => "previewWorkspaceImport" in obj, get: (obj) => obj.previewWorkspaceImport }, metadata: _metadata }, null, _instanceExtraInitializers);
1102
1267
  _importWorkspaceJson_decorators = [Remote("importWorkspaceJson")];
@@ -1123,6 +1288,8 @@ let McpManagerGateway = (() => {
1123
1288
  __esDecorate(this, null, _wbConnectorCatalog_decorators, { kind: "method", name: "wbConnectorCatalog", static: false, private: false, access: { has: (obj) => "wbConnectorCatalog" in obj, get: (obj) => obj.wbConnectorCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
1124
1289
  _wbConnectorCachedCount_decorators = [Remote("wbConnectorCachedCount")];
1125
1290
  __esDecorate(this, null, _wbConnectorCachedCount_decorators, { kind: "method", name: "wbConnectorCachedCount", static: false, private: false, access: { has: (obj) => "wbConnectorCachedCount" in obj, get: (obj) => obj.wbConnectorCachedCount }, metadata: _metadata }, null, _instanceExtraInitializers);
1291
+ _wbConnectorIcon_decorators = [Remote("wbConnectorIcon")];
1292
+ __esDecorate(this, null, _wbConnectorIcon_decorators, { kind: "method", name: "wbConnectorIcon", static: false, private: false, access: { has: (obj) => "wbConnectorIcon" in obj, get: (obj) => obj.wbConnectorIcon }, metadata: _metadata }, null, _instanceExtraInitializers);
1126
1293
  _wbConnectorPreview_decorators = [Remote("wbConnectorPreview")];
1127
1294
  __esDecorate(this, null, _wbConnectorPreview_decorators, { kind: "method", name: "wbConnectorPreview", static: false, private: false, access: { has: (obj) => "wbConnectorPreview" in obj, get: (obj) => obj.wbConnectorPreview }, metadata: _metadata }, null, _instanceExtraInitializers);
1128
1295
  _wbConnectorInstall_decorators = [Remote("wbConnectorInstall")];
@@ -1154,11 +1321,6 @@ let McpManagerGateway = (() => {
1154
1321
  __esDecorate(this, null, _wbMarketRegistry_decorators, { kind: "method", name: "wbMarketRegistry", static: false, private: false, access: { has: (obj) => "wbMarketRegistry" in obj, get: (obj) => obj.wbMarketRegistry }, metadata: _metadata }, null, _instanceExtraInitializers);
1155
1322
  _wbMarketRegistryUpsert_decorators = [Remote("wbMarketRegistryUpsert")];
1156
1323
  __esDecorate(this, null, _wbMarketRegistryUpsert_decorators, { kind: "method", name: "wbMarketRegistryUpsert", static: false, private: false, access: { has: (obj) => "wbMarketRegistryUpsert" in obj, get: (obj) => obj.wbMarketRegistryUpsert }, metadata: _metadata }, null, _instanceExtraInitializers);
1157
- // T03/R5:codebuddy 插件市场(列表 / 详情)。
1158
- _wbPluginCatalog_decorators = [Remote("wbPluginCatalog")];
1159
- __esDecorate(this, null, _wbPluginCatalog_decorators, { kind: "method", name: "wbPluginCatalog", static: false, private: false, access: { has: (obj) => "wbPluginCatalog" in obj, get: (obj) => obj.wbPluginCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
1160
- _wbPluginDetail_decorators = [Remote("wbPluginDetail")];
1161
- __esDecorate(this, null, _wbPluginDetail_decorators, { kind: "method", name: "wbPluginDetail", static: false, private: false, access: { has: (obj) => "wbPluginDetail" in obj, get: (obj) => obj.wbPluginDetail }, metadata: _metadata }, null, _instanceExtraInitializers);
1162
1324
  // T04/R8:安装状态登记(connector/skill/plugin 三类安装的统一账本)。
1163
1325
  _wbInstallState_decorators = [Remote("wbInstallState")];
1164
1326
  __esDecorate(this, null, _wbInstallState_decorators, { kind: "method", name: "wbInstallState", static: false, private: false, access: { has: (obj) => "wbInstallState" in obj, get: (obj) => obj.wbInstallState }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -1166,8 +1328,6 @@ let McpManagerGateway = (() => {
1166
1328
  __esDecorate(this, null, _wbInstallStateRecord_decorators, { kind: "method", name: "wbInstallStateRecord", static: false, private: false, access: { has: (obj) => "wbInstallStateRecord" in obj, get: (obj) => obj.wbInstallStateRecord }, metadata: _metadata }, null, _instanceExtraInitializers);
1167
1329
  _wbInstallStateForget_decorators = [Remote("wbInstallStateForget")];
1168
1330
  __esDecorate(this, null, _wbInstallStateForget_decorators, { kind: "method", name: "wbInstallStateForget", static: false, private: false, access: { has: (obj) => "wbInstallStateForget" in obj, get: (obj) => obj.wbInstallStateForget }, metadata: _metadata }, null, _instanceExtraInitializers);
1169
- _wbPluginUpdateCheck_decorators = [Remote("wbPluginUpdateCheck")];
1170
- __esDecorate(this, null, _wbPluginUpdateCheck_decorators, { kind: "method", name: "wbPluginUpdateCheck", static: false, private: false, access: { has: (obj) => "wbPluginUpdateCheck" in obj, get: (obj) => obj.wbPluginUpdateCheck }, metadata: _metadata }, null, _instanceExtraInitializers);
1171
1331
  // T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)。
1172
1332
  _wbAwesomeCatalog_decorators = [Remote("wbAwesomeCatalog")];
1173
1333
  __esDecorate(this, null, _wbAwesomeCatalog_decorators, { kind: "method", name: "wbAwesomeCatalog", static: false, private: false, access: { has: (obj) => "wbAwesomeCatalog" in obj, get: (obj) => obj.wbAwesomeCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -1177,6 +1337,14 @@ let McpManagerGateway = (() => {
1177
1337
  __esDecorate(this, null, _wbAwesomeReadme_decorators, { kind: "method", name: "wbAwesomeReadme", static: false, private: false, access: { has: (obj) => "wbAwesomeReadme" in obj, get: (obj) => obj.wbAwesomeReadme }, metadata: _metadata }, null, _instanceExtraInitializers);
1178
1338
  _wbAwesomeUpdateCheck_decorators = [Remote("wbAwesomeUpdateCheck")];
1179
1339
  __esDecorate(this, null, _wbAwesomeUpdateCheck_decorators, { kind: "method", name: "wbAwesomeUpdateCheck", static: false, private: false, access: { has: (obj) => "wbAwesomeUpdateCheck" in obj, get: (obj) => obj.wbAwesomeUpdateCheck }, metadata: _metadata }, null, _instanceExtraInitializers);
1340
+ _wbRepoMeta_decorators = [Remote("wbRepoMeta")];
1341
+ __esDecorate(this, null, _wbRepoMeta_decorators, { kind: "method", name: "wbRepoMeta", static: false, private: false, access: { has: (obj) => "wbRepoMeta" in obj, get: (obj) => obj.wbRepoMeta }, metadata: _metadata }, null, _instanceExtraInitializers);
1342
+ _wbNpmDownloads_decorators = [Remote("wbNpmDownloads")];
1343
+ __esDecorate(this, null, _wbNpmDownloads_decorators, { kind: "method", name: "wbNpmDownloads", static: false, private: false, access: { has: (obj) => "wbNpmDownloads" in obj, get: (obj) => obj.wbNpmDownloads }, metadata: _metadata }, null, _instanceExtraInitializers);
1344
+ _wbLoadoutExport_decorators = [Remote("wbLoadoutExport")];
1345
+ __esDecorate(this, null, _wbLoadoutExport_decorators, { kind: "method", name: "wbLoadoutExport", static: false, private: false, access: { has: (obj) => "wbLoadoutExport" in obj, get: (obj) => obj.wbLoadoutExport }, metadata: _metadata }, null, _instanceExtraInitializers);
1346
+ _wbLoadoutDiff_decorators = [Remote("wbLoadoutDiff")];
1347
+ __esDecorate(this, null, _wbLoadoutDiff_decorators, { kind: "method", name: "wbLoadoutDiff", static: false, private: false, access: { has: (obj) => "wbLoadoutDiff" in obj, get: (obj) => obj.wbLoadoutDiff }, metadata: _metadata }, null, _instanceExtraInitializers);
1180
1348
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
1181
1349
  }
1182
1350
  static inject = ["loader", "tools"];
@@ -1253,27 +1421,6 @@ let McpManagerGateway = (() => {
1253
1421
  const wsPath = typeof rawPath === "string" && rawPath ? canonicalWorkspacePath(rawPath) : undefined;
1254
1422
  return { tools: workspaceToolSchemas(this.ctx, value, wsPath).map(projectToolSchema), ambiguous: false };
1255
1423
  }
1256
- async builtins() {
1257
- const state = await managedState(this.ctx);
1258
- return { builtins: builtinMcpCatalog(effectiveMcpServers(this.ctx, state)) };
1259
- }
1260
- async installBuiltins(payload) {
1261
- return withPatchWrite(this.ctx, async () => {
1262
- const state = await managedState(this.ctx);
1263
- const result = appendSelectedBuiltinMcpServers(
1264
- state.content,
1265
- effectiveMcpServers(this.ctx, state),
1266
- payload?.ids,
1267
- { reservedIds: reservedEntryIds(this.ctx) },
1268
- );
1269
- if (result.content !== state.content) await writePatch(state, result.content);
1270
- const notes = [];
1271
- if (result.added.length) notes.push(`已追加:${result.added.join(", ")}`);
1272
- if (result.skipped.length) notes.push(`已存在并跳过:${result.skipped.join(", ")}`);
1273
- const { content: _content, ...summary } = result;
1274
- return { ...summary, note: notes.join(";") || "未修改配置" };
1275
- });
1276
- }
1277
1424
  async add(spec) {
1278
1425
  if (containsRedactedValue(spec)) throw new Error("新增 MCP 不能包含保留原值标记");
1279
1426
  const normalized = normalizeOne(spec);
@@ -1511,36 +1658,6 @@ let McpManagerGateway = (() => {
1511
1658
  catch (error) { this.ctx.logger?.error?.(`mcp-manager: 屏蔽已写入,但对齐会话作用域失败(下次挂载会重试): ${error?.message ?? error}`); }
1512
1659
  return result;
1513
1660
  }
1514
- async installWorkspaceBuiltins(payload) {
1515
- const wsPath = canonicalWorkspacePath(String(payload?.wsPath || ""));
1516
- const ids = payload?.ids;
1517
- if (!Array.isArray(ids) || !ids.length || !ids.every((id) => typeof id === "string")) throw new Error("内置 MCP ids 必须是非空字符串数组");
1518
- const result = await withWorkspaceWrite(wsPath, async () => {
1519
- const config = await readWorkspaceConfigCached(this.ctx, wsPath);
1520
- if (config.error) throw new Error(config.error);
1521
- const known = new Map(BUILTIN_MCP_SERVERS.map((builtin) => [builtin.id, builtin]));
1522
- const unknown = ids.filter((id) => !known.has(id));
1523
- if (unknown.length) throw new Error(`未知的内置 MCP:${unknown.join(", ")}`);
1524
- const existing = new Set(config.servers.map((server) => server.name));
1525
- const added = [];
1526
- const skipped = [];
1527
- for (const id of ids) {
1528
- const builtin = known.get(id);
1529
- if (existing.has(builtin.name)) { skipped.push(id); continue; }
1530
- const spec = { name: builtin.name };
1531
- for (const field of ["transport", "command", "args", "env", "cwd", "url", "headers", "toolCallTimeoutMs", "failOnStartupError", "reconnect"]) {
1532
- if (builtin[field] !== undefined) spec[field] = structuredClone(builtin[field]);
1533
- }
1534
- added.push(spec);
1535
- }
1536
- if (!added.length) return { note: skipped.length ? `已存在并跳过:${skipped.join(", ")}` : "未修改配置", unchanged: true };
1537
- await writeWorkspaceConfigFile(wsPath, [...config.servers, ...added], config.exclude);
1538
- return { note: `已追加:${added.map((server) => server.name).join(", ")}${skipped.length ? `;已存在并跳过:${skipped.join(", ")}` : ""}` };
1539
- });
1540
- // 没写盘就不用对齐(否则每次点"安装内置"都会白跑一遍挂载)。
1541
- if (!result.unchanged) await alignWorkspaceConnections(this.ctx, wsPath);
1542
- return { note: result.note };
1543
- }
1544
1661
  async previewWorkspaceImport(payload) {
1545
1662
  const wsPath = canonicalWorkspacePath(String(payload?.wsPath || ""));
1546
1663
  const json = typeof payload?.json === "string" ? JSON.parse(payload.json) : payload?.json;
@@ -1731,6 +1848,10 @@ let McpManagerGateway = (() => {
1731
1848
  async wbConnectorPreview(payload) {
1732
1849
  return wbConnectorPreview(payload?.connectorId);
1733
1850
  }
1851
+ // 某条连接器的图标(data URL)。市场卡片进视口才调,payload 形状同 wbConnectorPreview。
1852
+ async wbConnectorIcon(payload) {
1853
+ return wbConnectorIcon(payload?.connectorId);
1854
+ }
1734
1855
  async wbConnectorInstall(payload) {
1735
1856
  const connectorId = String(payload?.connectorId || "").trim();
1736
1857
  if (!connectorId) throw new Error("缺少 connectorId");
@@ -1798,6 +1919,10 @@ let McpManagerGateway = (() => {
1798
1919
  error: started?.error || "授权流程启动失败",
1799
1920
  mode: started?.mode || "",
1800
1921
  redirectUri: started?.redirectUri || "",
1922
+ // 授权页这条路走不通(服务器不提供 OAuth 发现、动态注册被拒…)时,
1923
+ // 手填令牌仍然是可用通道:把它交给面板,别只留一行红字。
1924
+ manualTokenOnly: started?.manualTokenOnly === true,
1925
+ serverUrl: started?.serverUrl || target.url,
1801
1926
  };
1802
1927
  }
1803
1928
  // 默认由宿主直接把授权页推给系统浏览器;open:false 留给面板自己渲染链接。
@@ -1902,15 +2027,6 @@ let McpManagerGateway = (() => {
1902
2027
  async wbMarketRegistryUpsert(payload) {
1903
2028
  return upsertMarketplace(payload || {});
1904
2029
  }
1905
- // ── T03/R5:codebuddy 插件市场 ──────────────────────────────────────
1906
- // 数据层在 lib/wb-plugin-market.js;这里只做 RPC 转发(下载/缓存/解析都在那边)。
1907
- async wbPluginCatalog() {
1908
- return wbPluginCatalog();
1909
- }
1910
- async wbPluginDetail(payload) {
1911
- return wbPluginDetail(payload?.name);
1912
- }
1913
-
1914
2030
  // ── T04/R8:安装状态账本(connector / skill / plugin 三类安装的统一登记) ──
1915
2031
  async wbInstallState() {
1916
2032
  return readInstallState();
@@ -1930,9 +2046,6 @@ let McpManagerGateway = (() => {
1930
2046
  return { note: `已移除安装状态:${kind}/${key}` };
1931
2047
  }
1932
2048
 
1933
- async wbPluginUpdateCheck() {
1934
- return wbPluginUpdateCheck();
1935
- }
1936
2049
  // ── T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)──────────────
1937
2050
  // 数据层在 lib/wb-awesome-market.js;这里只做 RPC 转发。
1938
2051
  async wbAwesomeCatalog(payload) {
@@ -1947,6 +2060,35 @@ let McpManagerGateway = (() => {
1947
2060
  async wbAwesomeUpdateCheck() {
1948
2061
  return wbAwesomeUpdateCheck();
1949
2062
  }
2063
+ // ── 社区元信息:仓库「人味」信号(维护者 / 最近提交 / issue / star)────────
2064
+ // 详情弹层按需调用,只读公开接口,6 小时缓存;不接收也不保存任何凭据。
2065
+ async wbRepoMeta(payload) {
2066
+ return wbRepoMeta(payload || {});
2067
+ }
2068
+ // ── npm 近 30 天下载量(只读)───────────────────────────────────────
2069
+ // 卡片进视口才按需批量取,7 天缓存;只读公开统计接口,不接收也不保存任何凭据。
2070
+ async wbNpmDownloads(payload) {
2071
+ return wbNpmDownloads(payload || {});
2072
+ }
2073
+ // ── 装备清单(只读)────────────────────────────────────────────────
2074
+ // 导出:收集本机全局 MCP / 技能 / 已召唤专家 / 已装插件,只留身份字段。
2075
+ // 对比:校验用户贴入的清单并与本机做差异,返回逐条状态;不安装、不写盘。
2076
+ async wbLoadoutExport(payload) {
2077
+ const items = await collectLoadoutItems(this.ctx);
2078
+ return {
2079
+ ok: true,
2080
+ loadout: buildLoadout({
2081
+ name: payload?.name,
2082
+ items,
2083
+ appVersion: pluginVersion(),
2084
+ }),
2085
+ };
2086
+ }
2087
+ async wbLoadoutDiff(payload) {
2088
+ const raw = payload?.loadout ?? payload?.text ?? payload;
2089
+ const items = await collectLoadoutItems(this.ctx);
2090
+ return { ok: true, ...diffLoadout(raw, items) };
2091
+ }
1950
2092
  // ── WorkBuddy 专家直连(召唤预设 + 捆绑技能)────────────────────────
1951
2093
  // 数据与安装逻辑都在 lib/expert-market.js;这里只做 RPC 转发与 patch 写盘收尾。
1952
2094
  // 召唤 = 往 profile patch 插入一条独立 @deepseek-ai/dsh-agent-preset 行(persona.prefix
@@ -2088,7 +2230,31 @@ let McpManagerGateway = (() => {
2088
2230
  // 不存在的 TeamCreate/Agent(成员开不出来)。标记桥接判定放前端(需要市场目录的
2089
2231
  // expertType),这里只把 personaText 原样带出去。
2090
2232
  const patch = await readPatch(this.ctx);
2091
- return { presets: readExpertPresets(patch.content), skills: await listExpertSkills() };
2233
+ const presets = readExpertPresets(patch.content);
2234
+ // patch 只记 plugin / presetId,中英显示名在专家目录缓存里。这里补上两个字段,
2235
+ // 客户端按宿主语言挑:中文名给中文界面,英文名给英文界面。读缓存失败只回落
2236
+ // plugin 名,不影响「已召唤 / 已装技能」本身的状态判断。
2237
+ // 回落有三层(目录缓存 → 预设行自带的名字 → 英文标识名):召唤那一刻
2238
+ // `WorkBuddy 专家 · <显示名>` 就写进了 patch 行,readExpertPresets 剥前缀即得,
2239
+ // 所以目录没落盘 / 专家已下架时菜单仍能出「吴八哥」,而不是 senior-developer。
2240
+ let displayNames = new Map();
2241
+ try {
2242
+ displayNames = await readExpertDisplayNames();
2243
+ } catch (error) {
2244
+ this.ctx.logger?.warn?.(`wb-expert: 读取专家显示名失败:${error?.message ?? error}`);
2245
+ }
2246
+ return {
2247
+ presets: presets.map((row) => {
2248
+ const info = displayNames.get(row.plugin) || {};
2249
+ return {
2250
+ ...row,
2251
+ ...info,
2252
+ nameZh: info.nameZh || row.displayName || "",
2253
+ nameEn: info.nameEn || row.displayName || "",
2254
+ };
2255
+ }),
2256
+ skills: await listExpertSkills(),
2257
+ };
2092
2258
  }
2093
2259
  // 最近使用记录(输入框召唤菜单的数据源):召唤/送还时自动记录专家;技能与 MCP 由
2094
2260
  // 客户端在用户从菜单里显式选用时上报(recordUse)。只存展示所需最小字段。
@@ -2103,4 +2269,4 @@ let McpManagerGateway = (() => {
2103
2269
  };
2104
2270
  })();
2105
2271
 
2106
- export { checkForUpdate, McpManagerGateway, McpManagerGateway as default, summarizeWorkspaceRow };
2272
+ export { McpManagerGateway, McpManagerGateway as default };