dsh-armory 1.9.4 → 2.0.0
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 +26 -8
- package/README.md +39 -16
- package/docs/chunk-split-feasibility.md +153 -0
- package/docs/host-proposal-mcp-teardown.md +184 -0
- package/docs/host-proposal-skill-toggle.md +166 -0
- package/docs/mcp-market-design.md +62 -0
- package/docs/prd-awesome-dsh-plugins.md +105 -0
- package/docs/prd-workbuddy-parity.md +128 -0
- package/docs/system-design-awesome.md +269 -0
- package/docs/system_design.md +530 -0
- package/docs/workbuddy-market-gap-analysis.md +197 -0
- package/lib/client.js +4507 -271
- package/lib/expert-market.js +13 -2
- package/lib/index.js +395 -9
- package/lib/mcp-catalog.js +104 -0
- package/lib/mcp-config.js +10 -1
- package/lib/mcp-import-sources.js +154 -14
- package/lib/mcp-oauth.js +806 -0
- package/lib/tool-ledger.js +119 -0
- package/lib/typert.js +26 -0
- package/lib/wb-auth-rules.js +128 -0
- package/lib/wb-awesome-market.js +453 -0
- package/lib/wb-install-state.js +151 -0
- package/lib/wb-market.js +232 -8
- package/lib/wb-plugin-market.js +291 -0
- package/lib/wb-registry.js +224 -0
- package/lib/whats-new.js +82 -0
- package/lib/workspace-runtime.js +133 -4
- package/package.json +10 -9
package/lib/expert-market.js
CHANGED
|
@@ -790,11 +790,22 @@ export async function listExpertRecent() {
|
|
|
790
790
|
// expert 记录与召唤预设对账:预设已摘除(送还)的记录不再展示,也顺手清掉。
|
|
791
791
|
let patch = null;
|
|
792
792
|
try {
|
|
793
|
-
|
|
793
|
+
// 注意必须 await:漏 await 会拿到 Promise(truthy 且非字符串),后续
|
|
794
|
+
// parsePatchDoc 直接抛 TypeError,导致整条 listExpertRecent 恒失败。
|
|
795
|
+
patch = await readFile(join(homedir(), ".dsh", "profiles", "desktop", "cordis.patch.yml"), "utf8");
|
|
794
796
|
} catch {
|
|
795
797
|
patch = null;
|
|
796
798
|
}
|
|
797
|
-
|
|
799
|
+
// 对账用的 YAML 解析要单独兜底:patch 文件被用户改坏(yaml 抛 TypeError)只影响
|
|
800
|
+
// 「预设还活着吗」这一个判断,回落 live=null 即可(= 不清记录、全部展示);
|
|
801
|
+
// 若让它抛出去,整个 listExpertRecent → wbExpertRecent RPC 会恒失败,
|
|
802
|
+
// 客户端拿不到任何 kinds,「最近使用」排序整条链路失效——得不偿失。
|
|
803
|
+
let live = null;
|
|
804
|
+
try {
|
|
805
|
+
live = patch ? new Set(readExpertPresets(patch).map((row) => row.plugin)) : null;
|
|
806
|
+
} catch {
|
|
807
|
+
live = null;
|
|
808
|
+
}
|
|
798
809
|
kinds[key] = live ? list.filter((item) => live.has(String(item?.plugin || ""))) : list;
|
|
799
810
|
if (live && kinds[key].length !== list.length) {
|
|
800
811
|
data.kinds[key] = kinds[key];
|
package/lib/index.js
CHANGED
|
@@ -13,10 +13,28 @@ import { collectImportLocations, missingEnvNames, resolveImportLocation } from "
|
|
|
13
13
|
import { deriveMcpPhase, formatMcpLog, sanitizeMcpLog } from "./mcp-observability.js";
|
|
14
14
|
import { allMcpEntries, entriesForName, projectToolSchema, revisionOf, toolInventory } from "./mcp-registry.js";
|
|
15
15
|
import { WHATS_NEW } from "./whats-new.js";
|
|
16
|
-
import { wbConnectorCatalog, wbConnectorPreview, wbSkillCatalog, wbSkillDetail, wbSkillInstall, wbSkillInstalled, wbSkillUninstall } from "./wb-market.js";
|
|
16
|
+
import { probeMcpServersAuthorization, wbConnectorCatalog, wbConnectorCachedCount, wbConnectorPreview, wbSkillCatalog, wbSkillDetail, wbSkillInstall, wbSkillInstalled, wbSkillUninstall } from "./wb-market.js";
|
|
17
|
+
// T03:多市场注册表(R4)与 codebuddy 插件市场(R5)的宿主侧数据层。
|
|
18
|
+
import { readRegistry, upsertMarketplace } from "./wb-registry.js";
|
|
19
|
+
import { wbPluginCatalog, wbPluginDetail, wbPluginUpdateCheck } from "./wb-plugin-market.js";
|
|
20
|
+
// T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)的宿主侧数据层。
|
|
21
|
+
import { wbAwesomeCatalog, wbAwesomeDetail, wbAwesomeUpdateCheck } from "./wb-awesome-market.js";
|
|
22
|
+
import { readState as readInstallState, record as recordInstallState, forget as forgetInstallState } from "./wb-install-state.js";
|
|
23
|
+
import { applyTokens } from "./wb-auth-rules.js";
|
|
24
|
+
import {
|
|
25
|
+
authorizationOverview as oauthOverview,
|
|
26
|
+
authorizationStatus as oauthStatus,
|
|
27
|
+
beginAuthorization as oauthBegin,
|
|
28
|
+
cancelAuthorization as oauthCancel,
|
|
29
|
+
clearTokenRecord as oauthClearToken,
|
|
30
|
+
ensureAccessToken as oauthEnsureToken,
|
|
31
|
+
saveManualToken as oauthSaveManualToken,
|
|
32
|
+
submitCallback as oauthSubmitCallback,
|
|
33
|
+
} from "./mcp-oauth.js";
|
|
17
34
|
import { hasTeamBridge, installExpertSkills, listExpertRecent, listExpertSkills, prepareExpertPersona, readExpertPresets, recordExpertRecent, removeExpertPreset, uninstallExpertSkills, uninstallTeamMemberPersonas, wbExpertCatalog, wbExpertDetail, writeExpertPreset } from "./expert-market.js";
|
|
18
35
|
import { attachPersona, detachPersona, listPersonas, reconcilePersonas } from "./session-persona.js";
|
|
19
|
-
|
|
36
|
+
// OPT-19:clearTextCache 一并引入——toolTailor 的可选动作要回收解压文本缓存。
|
|
37
|
+
import { clearTextCache, scanSessionLedger } from "./tool-ledger.js";
|
|
20
38
|
import { evaluateEnvExpression, jsExpressionToTemplate } from "./workspace-config.js";
|
|
21
39
|
import { canonicalWorkspacePath, installAgentRuntime, listWorkspaceRecords, liveWorkspaceAgentCount, projectConnectionsView, readWorkspaceConfigCached, reconcileWorkspaceConnections, reconcileWorkspaceRestricts, reconnectWorkspaceServer, withWorkspaceWrite, workspaceConnectionStatus, workspaceMountErrorsView, workspaceRestrictErrorView, workspaceScopeErrorsView, workspaceToolSchemas, writeWorkspaceConfigFile } from "./workspace-runtime.js";
|
|
22
40
|
|
|
@@ -63,6 +81,118 @@ const ROOT_INCLUDE_ID = "include";
|
|
|
63
81
|
const REDACTED_VALUE = "__DSH_MCP_REDACTED__";
|
|
64
82
|
const REVEAL_REVISION_KEY = randomBytes(32);
|
|
65
83
|
const patchWriteQueues = new WeakMap();
|
|
84
|
+
// ── R8:连接器 MCP 的 OAuth 授权接线 ─────────────────────────────────────
|
|
85
|
+
// 所有网络与落盘动作都在 lib/mcp-oauth.js;本文件只做三件事:
|
|
86
|
+
// ① 从 mcpServers 里取出某个 server 的 url / headers(授权的靶点);
|
|
87
|
+
// ② 安装前把已授权 server 的 Bearer token 注进 headers(否则探测永远 401);
|
|
88
|
+
// ③ 把授权会话包装成 RPC 结果给面板轮询。
|
|
89
|
+
const OAUTH_HTTP_TRANSPORT = new Set(["streamable-http", "streamablehttp", "http"]);
|
|
90
|
+
|
|
91
|
+
/** 归一化一个 mcp.json server 条目:transport 可能是对象(nested)也可能是裸字段。 */
|
|
92
|
+
function nestedServerSpec(rawServer) {
|
|
93
|
+
const nested = rawServer && typeof rawServer.transport === "object" ? rawServer.transport : rawServer || {};
|
|
94
|
+
const transport = String(nested?.type ?? rawServer?.type ?? "").toLowerCase();
|
|
95
|
+
const url = String(nested?.url ?? rawServer?.url ?? "").trim();
|
|
96
|
+
const headers = {};
|
|
97
|
+
for (const [key, value] of Object.entries(nested?.headers || {})) headers[String(key).toLowerCase()] = String(value);
|
|
98
|
+
return { nested, transport, url, headers };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 只有带 url 的 HTTP 族 server 才可能要走 OAuth;stdio 型永远不需要。 */
|
|
102
|
+
function isOAuthCandidate(spec) {
|
|
103
|
+
return Boolean(spec.url) && OAUTH_HTTP_TRANSPORT.has(spec.transport || "streamable-http");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 保留原 headers,覆盖 authorization —— 这是 mcp-client 唯一能带出去的凭据通道。 */
|
|
107
|
+
function withBearerHeader(rawServer, token) {
|
|
108
|
+
const spec = nestedServerSpec(rawServer);
|
|
109
|
+
const headers = { ...spec.headers, authorization: `Bearer ${token}` };
|
|
110
|
+
if (rawServer && typeof rawServer.transport === "object") return { ...rawServer, transport: { ...rawServer.transport, headers } };
|
|
111
|
+
return { ...rawServer, headers };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 安装前把已落盘的 access_token 注进对应 server 的 headers。
|
|
116
|
+
* 读不到 / 过期且刷不动就原样返回,交给 probe 去判定「需要授权」——
|
|
117
|
+
* 绝不因为令牌库有问题而阻断一次本来能成功的安装。
|
|
118
|
+
*/
|
|
119
|
+
async function injectStoredOAuthTokens(mcpServers) {
|
|
120
|
+
if (!mcpServers || typeof mcpServers !== "object") return mcpServers;
|
|
121
|
+
const out = {};
|
|
122
|
+
for (const [serverName, rawServer] of Object.entries(mcpServers)) {
|
|
123
|
+
const spec = nestedServerSpec(rawServer);
|
|
124
|
+
if (!isOAuthCandidate(spec) || spec.headers.authorization) {
|
|
125
|
+
out[serverName] = rawServer;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
let token = "";
|
|
129
|
+
try {
|
|
130
|
+
token = String((await oauthEnsureToken(spec.url)) || "");
|
|
131
|
+
} catch {
|
|
132
|
+
token = "";
|
|
133
|
+
}
|
|
134
|
+
out[serverName] = token ? withBearerHeader(rawServer, token) : rawServer;
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** 解析某连接器里某 server 的授权靶点(url + 现有 headers)。 */
|
|
140
|
+
async function resolveOAuthTarget(connectorId, serverName) {
|
|
141
|
+
const preview = await wbConnectorPreview(connectorId);
|
|
142
|
+
const servers = preview?.mcpServers && typeof preview.mcpServers === "object" ? preview.mcpServers : {};
|
|
143
|
+
const name = String(serverName || "").trim() || Object.keys(servers)[0] || "";
|
|
144
|
+
const rawServer = servers[name];
|
|
145
|
+
if (!rawServer) throw new Error(`连接器 ${connectorId} 里没有 server「${name || "(空)"}」`);
|
|
146
|
+
const spec = nestedServerSpec(rawServer);
|
|
147
|
+
if (!spec.url) throw new Error(`server「${name}」没有可授权的 URL(stdio 型不需要 OAuth 授权)`);
|
|
148
|
+
return { serverName: name, url: spec.url, headers: spec.headers, transport: spec.transport };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** 宿主 shell 服务的几种可能落点,逐个试;拿不到就返回 undefined 走系统兜底。 */
|
|
152
|
+
function resolveShellService(ctx) {
|
|
153
|
+
if (typeof ctx?.get !== "function") return undefined;
|
|
154
|
+
for (const id of ["shell", "electron", "appShell", "desktop"]) {
|
|
155
|
+
try {
|
|
156
|
+
const value = ctx.get(id);
|
|
157
|
+
if (!value) continue;
|
|
158
|
+
if (typeof value.openExternal === "function") return value;
|
|
159
|
+
if (typeof value?.shell?.openExternal === "function") return value.shell;
|
|
160
|
+
} catch {
|
|
161
|
+
/* 该服务不存在,继续试下一个 */
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** 用宿主能力打开授权页;没有 shell 就退回系统默认浏览器。 */
|
|
168
|
+
async function openExternalUrl(ctx, url) {
|
|
169
|
+
const target = String(url || "").trim();
|
|
170
|
+
if (!target) return { opened: false, reason: "empty-url" };
|
|
171
|
+
const shell = resolveShellService(ctx);
|
|
172
|
+
if (shell && typeof shell.openExternal === "function") {
|
|
173
|
+
try {
|
|
174
|
+
await shell.openExternal(target);
|
|
175
|
+
return { opened: true, via: "shell" };
|
|
176
|
+
} catch {
|
|
177
|
+
/* 落到系统兜底 */
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const { spawn } = await import("node:child_process");
|
|
182
|
+
if (process.platform === "win32") {
|
|
183
|
+
// 绝不能用 `cmd /c start "$url"`:Node 只在参数含空格时才给参数加引号,而 URL 不含空格,
|
|
184
|
+
// cmd 于是把 URL 里的 & 当成命令分隔符,授权地址被截断成 `?response_type=code`,
|
|
185
|
+
// 服务器随后回 `invalid_request: client_id is required`。
|
|
186
|
+
// rundll32 走 ShellExecute 且参数直传,不经 cmd 解析。
|
|
187
|
+
spawn("rundll32", ["url.dll,FileProtocolHandler", target], { detached: true, stdio: "ignore" }).unref();
|
|
188
|
+
} else {
|
|
189
|
+
spawn(process.platform === "darwin" ? "open" : "xdg-open", [target], { detached: true, stdio: "ignore" }).unref();
|
|
190
|
+
}
|
|
191
|
+
return { opened: true, via: "spawn" };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return { opened: false, reason: String(error?.message || error) };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
66
196
|
const PHASE_TEXT = { 0: "waiting", 1: "loading", 2: "connected", 3: "failed", 4: "stopped", 5: "unloading" };
|
|
67
197
|
|
|
68
198
|
// mcp-client 至今不暴露任何可订阅的状态面(只导出 name/inject/Config/apply,无事件、无服务),
|
|
@@ -603,7 +733,19 @@ function selectSourceServers(row, names) {
|
|
|
603
733
|
if (!Array.isArray(names) || !names.every((name) => typeof name === "string")) throw new Error("导入条目名必须是字符串数组");
|
|
604
734
|
wanted = new Set(names);
|
|
605
735
|
const unknown = [...wanted].filter((name) => !row.entries.some((entry) => entry.name === name && !entry.error));
|
|
606
|
-
if (unknown.length)
|
|
736
|
+
if (unknown.length) {
|
|
737
|
+
// OPT-17:这个名字可能真在这份配置里,只是 `${input:}` 落在 url/command 上、
|
|
738
|
+
// 位置剔不掉而整条降级。此时再说「来源中没有」会把用户送去改一个没写错的地方,
|
|
739
|
+
// 所以按 `unsupportedEntries` 把两边分开报(两个集合互斥:一个条目只会进其中一边)。
|
|
740
|
+
const blocked = new Set((row.unsupportedEntries ?? []).map((entry) => entry.name));
|
|
741
|
+
const interactive = unknown.filter((name) => blocked.has(name));
|
|
742
|
+
if (interactive.length) {
|
|
743
|
+
const absent = unknown.filter((name) => !blocked.has(name));
|
|
744
|
+
throw new Error(`以下 MCP 需要交互取值,DSH 无法导入:${interactive.join(", ")}${
|
|
745
|
+
absent.length ? `;另有不存在的条目:${absent.join(", ")}` : ""}`);
|
|
746
|
+
}
|
|
747
|
+
throw new Error(`来源中没有可导入的 MCP:${unknown.join(", ")}`);
|
|
748
|
+
}
|
|
607
749
|
}
|
|
608
750
|
const served = row.entries.filter((entry) => !entry.error && (wanted === null || wanted.has(entry.name)));
|
|
609
751
|
if (!served.length) throw new Error("没有选中任何可导入的 MCP");
|
|
@@ -640,6 +782,18 @@ function projectSourceRow(row) {
|
|
|
640
782
|
maskedFields: entry.spec ? maskedSourceFields(entry.spec) : [],
|
|
641
783
|
missingEnv: entry.spec ? missingEnvNames(entry.spec, process.env) : [],
|
|
642
784
|
})),
|
|
785
|
+
// OPT-17:`${input:}` 的位置不可剔时,那条会整条降级、不进 `entries`,只在
|
|
786
|
+
// `unsupportedEntries` 里留一份可展示信息。不透出来的话,界面只能笼统说「这里
|
|
787
|
+
// 没有可导入的 MCP」,用户看不出是「有 N 条需要交互取值」。仍只发最小的四个键:
|
|
788
|
+
// name/transport 是标签,reason 是已经写好、可直接渲染的说明,names 是占位符名
|
|
789
|
+
// (名字不是密钥);条目本体 `spec` 不出去,泄漏面不扩大。
|
|
790
|
+
// `?? []` 是给没挂这个字段的 row 兜底,避免投影本身抛错。
|
|
791
|
+
unsupportedEntries: (row.unsupportedEntries ?? []).map((entry) => ({
|
|
792
|
+
name: entry.name,
|
|
793
|
+
transport: entry.transport,
|
|
794
|
+
reason: entry.reason,
|
|
795
|
+
names: entry.names ?? [],
|
|
796
|
+
})),
|
|
643
797
|
};
|
|
644
798
|
}
|
|
645
799
|
// 来源层与导入目标必须同一层,否则报错。「看到的来源 == 会写进去的那一层」这条不变量
|
|
@@ -835,7 +989,7 @@ async function listMcpServers(ctx) {
|
|
|
835
989
|
let McpManagerGateway = (() => {
|
|
836
990
|
let _classSuper = TypertRemoteService;
|
|
837
991
|
let _instanceExtraInitializers = [];
|
|
838
|
-
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, _wbConnectorPreview_decorators, _wbConnectorInstall_decorators, _wbSkillCatalog_decorators, _wbSkillDetail_decorators, _wbSkillInstall_decorators, _wbSkillUninstall_decorators, _wbSkillInstalled_decorators;
|
|
992
|
+
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, _wbAwesomeUpdateCheck_decorators;
|
|
839
993
|
return class McpManagerGateway extends _classSuper {
|
|
840
994
|
static {
|
|
841
995
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
@@ -905,10 +1059,24 @@ let McpManagerGateway = (() => {
|
|
|
905
1059
|
__esDecorate(this, null, _toolTailor_decorators, { kind: "method", name: "toolTailor", static: false, private: false, access: { has: (obj) => "toolTailor" in obj, get: (obj) => obj.toolTailor }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
906
1060
|
_wbConnectorCatalog_decorators = [Remote("wbConnectorCatalog")];
|
|
907
1061
|
__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);
|
|
1062
|
+
_wbConnectorCachedCount_decorators = [Remote("wbConnectorCachedCount")];
|
|
1063
|
+
__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);
|
|
908
1064
|
_wbConnectorPreview_decorators = [Remote("wbConnectorPreview")];
|
|
909
1065
|
__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);
|
|
910
1066
|
_wbConnectorInstall_decorators = [Remote("wbConnectorInstall")];
|
|
911
1067
|
__esDecorate(this, null, _wbConnectorInstall_decorators, { kind: "method", name: "wbConnectorInstall", static: false, private: false, access: { has: (obj) => "wbConnectorInstall" in obj, get: (obj) => obj.wbConnectorInstall }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1068
|
+
_wbOAuthBegin_decorators = [Remote("wbOAuthBegin")];
|
|
1069
|
+
__esDecorate(this, null, _wbOAuthBegin_decorators, { kind: "method", name: "wbOAuthBegin", static: false, private: false, access: { has: (obj) => "wbOAuthBegin" in obj, get: (obj) => obj.wbOAuthBegin }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1070
|
+
_wbOAuthPoll_decorators = [Remote("wbOAuthPoll")];
|
|
1071
|
+
__esDecorate(this, null, _wbOAuthPoll_decorators, { kind: "method", name: "wbOAuthPoll", static: false, private: false, access: { has: (obj) => "wbOAuthPoll" in obj, get: (obj) => obj.wbOAuthPoll }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1072
|
+
_wbOAuthCancel_decorators = [Remote("wbOAuthCancel")];
|
|
1073
|
+
__esDecorate(this, null, _wbOAuthCancel_decorators, { kind: "method", name: "wbOAuthCancel", static: false, private: false, access: { has: (obj) => "wbOAuthCancel" in obj, get: (obj) => obj.wbOAuthCancel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1074
|
+
_wbOAuthToken_decorators = [Remote("wbOAuthToken")];
|
|
1075
|
+
__esDecorate(this, null, _wbOAuthToken_decorators, { kind: "method", name: "wbOAuthToken", static: false, private: false, access: { has: (obj) => "wbOAuthToken" in obj, get: (obj) => obj.wbOAuthToken }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1076
|
+
_wbOAuthOverview_decorators = [Remote("wbOAuthOverview")];
|
|
1077
|
+
__esDecorate(this, null, _wbOAuthOverview_decorators, { kind: "method", name: "wbOAuthOverview", static: false, private: false, access: { has: (obj) => "wbOAuthOverview" in obj, get: (obj) => obj.wbOAuthOverview }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1078
|
+
_wbOAuthOpen_decorators = [Remote("wbOAuthOpen")];
|
|
1079
|
+
__esDecorate(this, null, _wbOAuthOpen_decorators, { kind: "method", name: "wbOAuthOpen", static: false, private: false, access: { has: (obj) => "wbOAuthOpen" in obj, get: (obj) => obj.wbOAuthOpen }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
912
1080
|
_wbSkillCatalog_decorators = [Remote("wbSkillCatalog")];
|
|
913
1081
|
__esDecorate(this, null, _wbSkillCatalog_decorators, { kind: "method", name: "wbSkillCatalog", static: false, private: false, access: { has: (obj) => "wbSkillCatalog" in obj, get: (obj) => obj.wbSkillCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
914
1082
|
_wbSkillDetail_decorators = [Remote("wbSkillDetail")];
|
|
@@ -919,6 +1087,32 @@ let McpManagerGateway = (() => {
|
|
|
919
1087
|
__esDecorate(this, null, _wbSkillUninstall_decorators, { kind: "method", name: "wbSkillUninstall", static: false, private: false, access: { has: (obj) => "wbSkillUninstall" in obj, get: (obj) => obj.wbSkillUninstall }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
920
1088
|
_wbSkillInstalled_decorators = [Remote("wbSkillInstalled")];
|
|
921
1089
|
__esDecorate(this, null, _wbSkillInstalled_decorators, { kind: "method", name: "wbSkillInstalled", static: false, private: false, access: { has: (obj) => "wbSkillInstalled" in obj, get: (obj) => obj.wbSkillInstalled }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1090
|
+
// T03/R4:多市场注册表(读 / 增改)。
|
|
1091
|
+
_wbMarketRegistry_decorators = [Remote("wbMarketRegistry")];
|
|
1092
|
+
__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);
|
|
1093
|
+
_wbMarketRegistryUpsert_decorators = [Remote("wbMarketRegistryUpsert")];
|
|
1094
|
+
__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);
|
|
1095
|
+
// T03/R5:codebuddy 插件市场(列表 / 详情)。
|
|
1096
|
+
_wbPluginCatalog_decorators = [Remote("wbPluginCatalog")];
|
|
1097
|
+
__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);
|
|
1098
|
+
_wbPluginDetail_decorators = [Remote("wbPluginDetail")];
|
|
1099
|
+
__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);
|
|
1100
|
+
// T04/R8:安装状态登记(connector/skill/plugin 三类安装的统一账本)。
|
|
1101
|
+
_wbInstallState_decorators = [Remote("wbInstallState")];
|
|
1102
|
+
__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);
|
|
1103
|
+
_wbInstallStateRecord_decorators = [Remote("wbInstallStateRecord")];
|
|
1104
|
+
__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);
|
|
1105
|
+
_wbInstallStateForget_decorators = [Remote("wbInstallStateForget")];
|
|
1106
|
+
__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);
|
|
1107
|
+
_wbPluginUpdateCheck_decorators = [Remote("wbPluginUpdateCheck")];
|
|
1108
|
+
__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);
|
|
1109
|
+
// T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)。
|
|
1110
|
+
_wbAwesomeCatalog_decorators = [Remote("wbAwesomeCatalog")];
|
|
1111
|
+
__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);
|
|
1112
|
+
_wbAwesomeDetail_decorators = [Remote("wbAwesomeDetail")];
|
|
1113
|
+
__esDecorate(this, null, _wbAwesomeDetail_decorators, { kind: "method", name: "wbAwesomeDetail", static: false, private: false, access: { has: (obj) => "wbAwesomeDetail" in obj, get: (obj) => obj.wbAwesomeDetail }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
1114
|
+
_wbAwesomeUpdateCheck_decorators = [Remote("wbAwesomeUpdateCheck")];
|
|
1115
|
+
__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);
|
|
922
1116
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
923
1117
|
}
|
|
924
1118
|
static inject = ["loader", "tools"];
|
|
@@ -1114,6 +1308,12 @@ let McpManagerGateway = (() => {
|
|
|
1114
1308
|
async toolTailor(payload) {
|
|
1115
1309
|
// 只读账本:扫描本机会话日志 + 当前注册表 Schema,见 tool-ledger.js。
|
|
1116
1310
|
// days 由调用方传(7/30/90),缺省 30,服务端钳到 [1,365]。
|
|
1311
|
+
// OPT-19:可选清空动作——payload.action === 'clearTextCache' 时先回收解压文本缓存
|
|
1312
|
+
// 再出报告。透传性已核实(typert.js:35 payloadParam 的 schema 是 $any =
|
|
1313
|
+
// z.unknown(),strict 只约束「必须有值」不做键剥离),额外键 action 能从客户端
|
|
1314
|
+
// 原样到达这里,无需新增 RPC 契约。先清后扫:报告里的 textCache 快照直接呈现
|
|
1315
|
+
// 清空后的状态,面板能立即看到「占用已归零」,不必再刷一次。
|
|
1316
|
+
if (payload?.action === "clearTextCache") clearTextCache();
|
|
1117
1317
|
return buildToolTailorReport(this.ctx, payload || {});
|
|
1118
1318
|
}
|
|
1119
1319
|
async listWorkspaces() {
|
|
@@ -1423,6 +1623,10 @@ let McpManagerGateway = (() => {
|
|
|
1423
1623
|
async wbConnectorCatalog() {
|
|
1424
1624
|
return wbConnectorCatalog();
|
|
1425
1625
|
}
|
|
1626
|
+
// 顶栏「市场」chip 的常驻计数:只读磁盘缓存,绝不联网(无缓存返回 null)。
|
|
1627
|
+
async wbConnectorCachedCount() {
|
|
1628
|
+
return wbConnectorCachedCount();
|
|
1629
|
+
}
|
|
1426
1630
|
async wbConnectorPreview(payload) {
|
|
1427
1631
|
return wbConnectorPreview(payload?.connectorId);
|
|
1428
1632
|
}
|
|
@@ -1431,11 +1635,126 @@ let McpManagerGateway = (() => {
|
|
|
1431
1635
|
if (!connectorId) throw new Error("缺少 connectorId");
|
|
1432
1636
|
const mode = payload?.mode === "replace" ? "replace" : "merge";
|
|
1433
1637
|
const wsPath = payload?.wsPath ? canonicalWorkspacePath(String(payload.wsPath)) : "";
|
|
1638
|
+
const tokens = payload?.tokens && typeof payload.tokens === "object" ? payload.tokens : {};
|
|
1434
1639
|
const preview = await wbConnectorPreview(connectorId);
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1640
|
+
// T04/R7:把面板收集到的 token 填进 ${...} 占位符。只填用户明确提供的槽位,
|
|
1641
|
+
// 未填的保持原样并把槽位名回传面板(missingSlots)——绝不代填、绝不猜值。
|
|
1642
|
+
const { mcpServers, missingSlots } = applyTokens(preview.mcpServers, tokens);
|
|
1643
|
+
// R8:先把已授权的 OAuth token 注进 headers 再探测——否则授权过的连接器每次重装
|
|
1644
|
+
// 都会被判成「需要授权」,用户得反复点授权。
|
|
1645
|
+
const authorized = await injectStoredOAuthTokens(mcpServers);
|
|
1646
|
+
// 装前 401 探测:索引 auth_mode 会漏标鉴权(tec-do 裸 URL 装完必 401)。断言从
|
|
1647
|
+
// 「任一不就整体中止」改为「回传需授权清单」:面板据此弹浏览器授权页,授权完成
|
|
1648
|
+
// 后重装即可,绝不再抛「DSH 无法自动注入」把用户挡在门外。
|
|
1649
|
+
const probe = await probeMcpServersAuthorization(authorized);
|
|
1650
|
+
if (probe?.needsAuth?.length) {
|
|
1651
|
+
return {
|
|
1652
|
+
ok: false,
|
|
1653
|
+
connectorId,
|
|
1654
|
+
missingSlots,
|
|
1655
|
+
needsAuth: probe.needsAuth,
|
|
1656
|
+
error: `连接器需要授权:${probe.needsAuth.map((item) => item.serverName).join("、")}`,
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
const json = { mcpServers: authorized };
|
|
1660
|
+
const result = wsPath
|
|
1661
|
+
? await applyWorkspaceImport(this.ctx, wsPath, admittedImport(json), mode)
|
|
1662
|
+
: await applyManagedImport(this.ctx, admittedImport(json), mode);
|
|
1663
|
+
// T04/R8:装成功后登记本地状态(供「已安装」全量筛选与来源锁)。登记失败只记
|
|
1664
|
+
// 日志、绝不影响安装结果——状态文件坏了不该让用户白装一次。
|
|
1665
|
+
try {
|
|
1666
|
+
await recordInstallState({
|
|
1667
|
+
kind: "connector",
|
|
1668
|
+
key: connectorId,
|
|
1669
|
+
name: connectorId,
|
|
1670
|
+
scope: wsPath ? "workspace" : "managed",
|
|
1671
|
+
installPath: wsPath || "",
|
|
1672
|
+
source: { kind: "marketplace", marketplace: "workbuddy-connectors" },
|
|
1673
|
+
});
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
this.ctx.logger?.warn?.(`wb-market: 连接器 ${connectorId} 安装状态登记失败(不影响安装结果):${error?.message || error}`);
|
|
1676
|
+
}
|
|
1677
|
+
return { ...result, missingSlots };
|
|
1678
|
+
}
|
|
1679
|
+
// ── R8:连接器 MCP 的浏览器授权(RFC 9728 / 8414 / 7591 / 7636)──────────
|
|
1680
|
+
// 面板只负责展示与点击:安装前探测到 401 → 这里拿到 authorizeUrl 交给系统浏览器,
|
|
1681
|
+
// 浏览器回来后面板轮询 wbOAuthPoll,token 落在 ~/.dsh/armory-oauth/。
|
|
1682
|
+
async wbOAuthBegin(payload) {
|
|
1683
|
+
const connectorId = String(payload?.connectorId || "").trim();
|
|
1684
|
+
if (!connectorId) throw new Error("缺少 connectorId");
|
|
1685
|
+
const target = await resolveOAuthTarget(connectorId, payload?.serverName);
|
|
1686
|
+
const started = await oauthBegin({
|
|
1687
|
+
serverName: target.serverName,
|
|
1688
|
+
serverUrl: target.url,
|
|
1689
|
+
headers: target.headers,
|
|
1690
|
+
clientName: "DSH Armory",
|
|
1691
|
+
});
|
|
1692
|
+
if (!started?.ok) {
|
|
1693
|
+
return {
|
|
1694
|
+
ok: false,
|
|
1695
|
+
connectorId,
|
|
1696
|
+
serverName: target.serverName,
|
|
1697
|
+
error: started?.error || "授权流程启动失败",
|
|
1698
|
+
mode: started?.mode || "",
|
|
1699
|
+
redirectUri: started?.redirectUri || "",
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
// 默认由宿主直接把授权页推给系统浏览器;open:false 留给面板自己渲染链接。
|
|
1703
|
+
const opened = payload?.open === false ? { opened: false } : await openExternalUrl(this.ctx, started.authorizeUrl);
|
|
1704
|
+
return {
|
|
1705
|
+
ok: true,
|
|
1706
|
+
connectorId,
|
|
1707
|
+
serverName: target.serverName,
|
|
1708
|
+
sessionId: started.sessionId,
|
|
1709
|
+
authorizeUrl: started.authorizeUrl,
|
|
1710
|
+
mode: started.mode,
|
|
1711
|
+
redirectUri: started.redirectUri,
|
|
1712
|
+
hint: started.hint || "",
|
|
1713
|
+
opened: opened.opened === true,
|
|
1714
|
+
openVia: opened.via || (opened.opened ? "" : String(opened.reason || "")),
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
async wbOAuthPoll(payload) {
|
|
1718
|
+
const sessionId = String(payload?.sessionId || "").trim();
|
|
1719
|
+
if (!sessionId) throw new Error("缺少 sessionId");
|
|
1720
|
+
const callbackUrl = String(payload?.callbackUrl || "").trim();
|
|
1721
|
+
// 自定义 scheme 回调(如 workbuddy://…)浏览器打不开,面板让用户把整条回调
|
|
1722
|
+
// URL 粘回来,这里顺手换成 code 再去换 token。
|
|
1723
|
+
if (callbackUrl) {
|
|
1724
|
+
const submitted = await oauthSubmitCallback(sessionId, callbackUrl);
|
|
1725
|
+
if (submitted && submitted.ok === false && submitted.status !== "authorized") return submitted;
|
|
1726
|
+
}
|
|
1727
|
+
return oauthStatus(sessionId);
|
|
1728
|
+
}
|
|
1729
|
+
async wbOAuthCancel(payload) {
|
|
1730
|
+
const sessionId = String(payload?.sessionId || "").trim();
|
|
1731
|
+
if (sessionId) oauthCancel(sessionId);
|
|
1732
|
+
return { ok: true };
|
|
1733
|
+
}
|
|
1734
|
+
async wbOAuthToken(payload) {
|
|
1735
|
+
const connectorId = String(payload?.connectorId || "").trim();
|
|
1736
|
+
if (!connectorId) throw new Error("缺少 connectorId");
|
|
1737
|
+
const target = await resolveOAuthTarget(connectorId, payload?.serverName);
|
|
1738
|
+
const token = String(payload?.token || "").trim();
|
|
1739
|
+
if (!token) {
|
|
1740
|
+
// 空 token = 撤销授权(清掉本地记录),面板的「撤销授权」按钮走这条路。
|
|
1741
|
+
await oauthClearToken(target.url);
|
|
1742
|
+
return { ok: true, cleared: true, connectorId, serverName: target.serverName };
|
|
1743
|
+
}
|
|
1744
|
+
const saved = await oauthSaveManualToken({ serverUrl: target.url, serverName: target.serverName, token });
|
|
1745
|
+
return { ok: saved?.ok !== false, connectorId, serverName: target.serverName, ...(saved || {}) };
|
|
1746
|
+
}
|
|
1747
|
+
async wbOAuthOverview(payload) {
|
|
1748
|
+
const connectorId = String(payload?.connectorId || "").trim();
|
|
1749
|
+
if (!connectorId) throw new Error("缺少 connectorId");
|
|
1750
|
+
const target = await resolveOAuthTarget(connectorId, payload?.serverName).catch(() => null);
|
|
1751
|
+
if (!target) return { ok: true, authorized: false, connectorId, serverName: "" };
|
|
1752
|
+
const overview = await oauthOverview(target.url).catch(() => null);
|
|
1753
|
+
return { ok: true, connectorId, serverName: target.serverName, ...(overview || {}) };
|
|
1754
|
+
}
|
|
1755
|
+
async wbOAuthOpen(payload) {
|
|
1756
|
+
const opened = await openExternalUrl(this.ctx, payload?.url);
|
|
1757
|
+
return { ok: opened.opened === true, ...opened };
|
|
1439
1758
|
}
|
|
1440
1759
|
async wbSkillCatalog(payload) {
|
|
1441
1760
|
return wbSkillCatalog(payload || {});
|
|
@@ -1449,6 +1768,22 @@ let McpManagerGateway = (() => {
|
|
|
1449
1768
|
const result = await wbSkillInstall(payload || {}, {
|
|
1450
1769
|
onProgress: (progress) => this.ctx.logger?.debug?.(`wb-market: ${progress.phase} ${progress.index}/${progress.total}`),
|
|
1451
1770
|
});
|
|
1771
|
+
// T04/R8:装成功后登记本地状态。key 取不到就跳过(record 对空 key 会抛错)。
|
|
1772
|
+
const key = result?.name || payload?.slug || payload?.name || "";
|
|
1773
|
+
if (key) {
|
|
1774
|
+
try {
|
|
1775
|
+
await recordInstallState({
|
|
1776
|
+
kind: "skill",
|
|
1777
|
+
key,
|
|
1778
|
+
name: result?.name || payload?.name || key,
|
|
1779
|
+
version: result?.version || "",
|
|
1780
|
+
installPath: result?.dir || "",
|
|
1781
|
+
source: { kind: "marketplace", marketplace: "workbuddy-skills" },
|
|
1782
|
+
});
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
this.ctx.logger?.warn?.(`wb-market: 技能 ${key} 安装状态登记失败(不影响安装结果):${error?.message || error}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1452
1787
|
return result;
|
|
1453
1788
|
}
|
|
1454
1789
|
async wbSkillUninstall(payload) {
|
|
@@ -1457,6 +1792,57 @@ let McpManagerGateway = (() => {
|
|
|
1457
1792
|
async wbSkillInstalled() {
|
|
1458
1793
|
return wbSkillInstalled();
|
|
1459
1794
|
}
|
|
1795
|
+
// ── T03/R4:多市场注册表 ────────────────────────────────────────────
|
|
1796
|
+
// 数据层在 lib/wb-registry.js(T01);这里只做 RPC 转发。读 = 合并投影(内置默认
|
|
1797
|
+
// 永远在场);增改 = upsert 一条后返回新的合并投影。缺 id 时 wb-registry 抛错。
|
|
1798
|
+
async wbMarketRegistry() {
|
|
1799
|
+
return readRegistry();
|
|
1800
|
+
}
|
|
1801
|
+
async wbMarketRegistryUpsert(payload) {
|
|
1802
|
+
return upsertMarketplace(payload || {});
|
|
1803
|
+
}
|
|
1804
|
+
// ── T03/R5:codebuddy 插件市场 ──────────────────────────────────────
|
|
1805
|
+
// 数据层在 lib/wb-plugin-market.js;这里只做 RPC 转发(下载/缓存/解析都在那边)。
|
|
1806
|
+
async wbPluginCatalog() {
|
|
1807
|
+
return wbPluginCatalog();
|
|
1808
|
+
}
|
|
1809
|
+
async wbPluginDetail(payload) {
|
|
1810
|
+
return wbPluginDetail(payload?.name);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// ── T04/R8:安装状态账本(connector / skill / plugin 三类安装的统一登记) ──
|
|
1814
|
+
async wbInstallState() {
|
|
1815
|
+
return readInstallState();
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
async wbInstallStateRecord(payload) {
|
|
1819
|
+
if (!payload || typeof payload !== "object") throw new Error("缺少安装状态条目");
|
|
1820
|
+
await recordInstallState(payload);
|
|
1821
|
+
return { note: `已登记安装状态:${payload.kind || "connector"}/${payload.key}` };
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
async wbInstallStateForget(payload) {
|
|
1825
|
+
const kind = String(payload?.kind || "").trim();
|
|
1826
|
+
const key = String(payload?.key || "").trim();
|
|
1827
|
+
if (!kind || !key) throw new Error("缺少 kind 或 key");
|
|
1828
|
+
await forgetInstallState(kind, key);
|
|
1829
|
+
return { note: `已移除安装状态:${kind}/${key}` };
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
async wbPluginUpdateCheck() {
|
|
1833
|
+
return wbPluginUpdateCheck();
|
|
1834
|
+
}
|
|
1835
|
+
// ── T06/Awesome:DSH 社区插件市场(awesome-dsh-plugins)──────────────
|
|
1836
|
+
// 数据层在 lib/wb-awesome-market.js;这里只做 RPC 转发。
|
|
1837
|
+
async wbAwesomeCatalog(payload) {
|
|
1838
|
+
return wbAwesomeCatalog(payload || {});
|
|
1839
|
+
}
|
|
1840
|
+
async wbAwesomeDetail(payload) {
|
|
1841
|
+
return wbAwesomeDetail(payload?.name);
|
|
1842
|
+
}
|
|
1843
|
+
async wbAwesomeUpdateCheck() {
|
|
1844
|
+
return wbAwesomeUpdateCheck();
|
|
1845
|
+
}
|
|
1460
1846
|
// ── WorkBuddy 专家直连(召唤预设 + 捆绑技能)────────────────────────
|
|
1461
1847
|
// 数据与安装逻辑都在 lib/expert-market.js;这里只做 RPC 转发与 patch 写盘收尾。
|
|
1462
1848
|
// 召唤 = 往 profile patch 插入一条独立 @deepseek-ai/dsh-agent-preset 行(persona.prefix
|
|
@@ -1613,4 +1999,4 @@ let McpManagerGateway = (() => {
|
|
|
1613
1999
|
};
|
|
1614
2000
|
})();
|
|
1615
2001
|
|
|
1616
|
-
export { checkForUpdate, McpManagerGateway, McpManagerGateway as default, summarizeWorkspaceRow };
|
|
2002
|
+
export { checkForUpdate, McpManagerGateway, McpManagerGateway as default, summarizeWorkspaceRow };
|