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/README.en.md +21 -15
- package/README.md +24 -14
- package/docs/installation.md +0 -2
- package/lib/client.js +2490 -736
- package/lib/community-meta.js +222 -0
- package/lib/expert-market.js +119 -23
- package/lib/index.js +261 -95
- package/lib/loadout.js +178 -0
- package/lib/mcp-config.js +0 -171
- package/lib/mcp-oauth.js +131 -26
- package/lib/npm-downloads.js +262 -0
- package/lib/suites-registry.js +10 -4
- package/lib/typert.js +13 -10
- package/lib/wb-awesome-market.js +706 -214
- package/lib/wb-install-state.js +10 -4
- package/lib/wb-market.js +164 -41
- package/lib/wb-registry.js +32 -31
- package/lib/whats-new-en.js +27 -0
- package/lib/whats-new.js +33 -0
- package/package.json +2 -2
- package/docs/mcp-market-design.md +0 -617
- package/lib/mcp-catalog-en.js +0 -244
- package/lib/mcp-catalog.js +0 -806
- package/lib/wb-plugin-market.js +0 -291
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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 {
|
|
2272
|
+
export { McpManagerGateway, McpManagerGateway as default };
|