dsh-himarket 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,268 @@
1
+ /**
2
+ * dsh-himarket 宿主端:HiMarket 桥接的编排器。
3
+ *
4
+ * 职责:
5
+ * - 注册 settings 命名空间持久化凭证;
6
+ * - 注册环回 HTTP 路由 /himarket/* 供设置页卡片调用(state/save-config/sync/install-skill);
7
+ * - 注册对话式工具 himarket_sync / himarket_install_skill;
8
+ * - 用 McpManager 把已订阅 MCP 映射为官方 dsh-mcp-client 动态 fiber;
9
+ * - 用 installSkill 把 Skill ZIP 落盘到 ~/.dsh/skills/。
10
+ *
11
+ * 归属:host 平面、不发布服务(只消费 webServer/tools/settings),行 loose、无 isolate realm。
12
+ *
13
+ * @module dsh-himarket
14
+ */
15
+ import { HimarketClient } from './himarket-client.js';
16
+ import { McpManager } from './mcp.js';
17
+ import { attachSettings } from './settings.js';
18
+ import { installSkill, defaultSkillRoot } from './skill.js';
19
+ import { registerHimarketTools } from './tools.js';
20
+ export const name = 'himarket';
21
+ export function apply(ctx, config) {
22
+ const baseUrl = ctx.baseUrl ?? 'file:///';
23
+ const fallback = {
24
+ baseUrl: '',
25
+ username: '',
26
+ password: '',
27
+ token: '',
28
+ skillInstallDir: config.skillInstallDir ?? '',
29
+ };
30
+ const settings = attachSettings(ctx, fallback, baseUrl);
31
+ let client;
32
+ let mcpManager;
33
+ let subscribedMcps = [];
34
+ let publishedSkills = [];
35
+ let installedSkills = new Set();
36
+ let lastError = '';
37
+ const skillRoot = () => {
38
+ const dir = settings.current().skillInstallDir;
39
+ return dir.trim() === '' ? defaultSkillRoot() : dir;
40
+ };
41
+ function buildClient() {
42
+ const s = settings.current();
43
+ if (s.baseUrl.trim() === '' || s.username.trim() === '' || s.password.trim() === '') {
44
+ return undefined;
45
+ }
46
+ return new HimarketClient({ baseUrl: s.baseUrl, username: s.username, password: s.password, token: s.token });
47
+ }
48
+ async function ensureReady() {
49
+ const s = settings.current();
50
+ if (s.baseUrl.trim() === '')
51
+ throw new Error('还没配置 HiMarket 地址,请先在设置里填「HiMarket 地址」');
52
+ if (s.username.trim() === '' || s.password.trim() === '') {
53
+ throw new Error('还没配置 HiMarket 账号,请先在设置里填「用户名」和「密码」');
54
+ }
55
+ if (client === undefined)
56
+ client = buildClient();
57
+ if (mcpManager === undefined)
58
+ mcpManager = new McpManager(ctx, baseUrl);
59
+ if (!client.hasToken) {
60
+ const token = await client.login();
61
+ await settings.save({ token });
62
+ }
63
+ return client;
64
+ }
65
+ async function sync() {
66
+ try {
67
+ const c = await ensureReady();
68
+ const { mcpServers, authHeaders } = await c.listSubscribedMcps();
69
+ const skills = await c.listPublishedSkills();
70
+ subscribedMcps = mcpServers;
71
+ publishedSkills = skills;
72
+ const report = await (mcpManager ?? new McpManager(ctx, baseUrl)).reconcile(mcpServers, authHeaders);
73
+ installedSkills = await refreshInstalledSkills();
74
+ lastError = '';
75
+ const parts = [];
76
+ parts.push(`已同步:${mcpServers.length} 个已订阅 MCP,${skills.length} 个可安装技能。`);
77
+ if (report.added.length > 0)
78
+ parts.push(`新接入 MCP 工具:${report.added.join('、')}`);
79
+ if (report.removed.length > 0)
80
+ parts.push(`已移除 MCP:${report.removed.join('、')}`);
81
+ if (report.errors.length > 0)
82
+ parts.push(`有 ${report.errors.length} 个 MCP 连接失败:${report.errors.join(';')}`);
83
+ return parts.join('\n');
84
+ }
85
+ catch (error) {
86
+ lastError = error instanceof Error ? error.message : String(error);
87
+ return `同步失败:${lastError}`;
88
+ }
89
+ }
90
+ async function installByNameOrId(nameOrId) {
91
+ try {
92
+ const c = await ensureReady();
93
+ const target = publishedSkills.find((s) => s.productId === nameOrId || s.name === nameOrId);
94
+ if (target === undefined) {
95
+ throw new Error(`找不到技能「${nameOrId}」,请先同步(或核对技能名/ID)`);
96
+ }
97
+ const result = await installSkill(c, target.productId, skillRoot());
98
+ installedSkills.add(result.name);
99
+ return result.note;
100
+ }
101
+ catch (error) {
102
+ lastError = error instanceof Error ? error.message : String(error);
103
+ return `安装失败:${lastError}`;
104
+ }
105
+ }
106
+ async function refreshInstalledSkills() {
107
+ const root = skillRoot();
108
+ const names = new Set();
109
+ try {
110
+ const { readdir } = await import('node:fs/promises');
111
+ const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
112
+ for (const e of entries) {
113
+ if (e.isDirectory() && !e.name.startsWith('.'))
114
+ names.add(e.name);
115
+ }
116
+ }
117
+ catch { }
118
+ return names;
119
+ }
120
+ async function snapshot() {
121
+ const s = settings.current();
122
+ return {
123
+ configured: s.baseUrl.trim() !== '' && s.username.trim() !== '' && s.password.trim() !== '',
124
+ loggedIn: (client?.hasToken ?? false) || s.token !== '',
125
+ baseUrl: s.baseUrl,
126
+ username: s.username,
127
+ mcpServers: subscribedMcps,
128
+ activeMcpNames: mcpManager?.activeServerNames() ?? [],
129
+ publishedSkills,
130
+ installedSkills: [...installedSkills],
131
+ lastError,
132
+ };
133
+ }
134
+ // 启动时:若已有凭证,异步尝试恢复(不阻塞启动)。
135
+ const boot = settings.current();
136
+ if (boot.baseUrl.trim() !== '' && boot.username.trim() !== '' && boot.password.trim() !== '') {
137
+ void (async () => {
138
+ try {
139
+ client = buildClient();
140
+ mcpManager = new McpManager(ctx, baseUrl);
141
+ const { mcpServers, authHeaders } = await client.listSubscribedMcps();
142
+ subscribedMcps = mcpServers;
143
+ publishedSkills = await client.listPublishedSkills();
144
+ await mcpManager.reconcile(mcpServers, authHeaders);
145
+ installedSkills = await refreshInstalledSkills();
146
+ ctx.logger.info('[dsh-himarket] 启动恢复完成:%d 个 MCP、%d 个技能', mcpServers.length, publishedSkills.length);
147
+ }
148
+ catch (error) {
149
+ ctx.logger.warn('[dsh-himarket] 启动恢复失败(将静默降级):%s', error instanceof Error ? error.message : String(error));
150
+ lastError = error instanceof Error ? error.message : String(error);
151
+ }
152
+ })();
153
+ }
154
+ void registerHimarketTools(ctx, baseUrl, {
155
+ sync,
156
+ installSkill: installByNameOrId,
157
+ });
158
+ ctx.inject(['webServer'], (scope) => {
159
+ const webServer = scope.get('webServer');
160
+ if (webServer === undefined) {
161
+ ctx.logger.warn('[dsh-himarket] webServer 服务不可用,设置页卡片路由未注册');
162
+ return;
163
+ }
164
+ const isLoopback = (addr) => addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1';
165
+ const sendJson = (res, status, body) => {
166
+ const json = JSON.stringify(body);
167
+ res.writeHead(status, {
168
+ 'content-type': 'application/json; charset=utf-8',
169
+ 'cache-control': 'no-cache',
170
+ });
171
+ res.end(json);
172
+ };
173
+ const sendError = (res, status, message) => {
174
+ sendJson(res, status, { ok: false, error: message });
175
+ };
176
+ return webServer.register({
177
+ kind: 'prefix',
178
+ path: '/himarket',
179
+ handler: async (req, res) => {
180
+ if (!isLoopback(req.socket?.remoteAddress ?? '')) {
181
+ sendError(res, 403, '仅允许本机访问');
182
+ return;
183
+ }
184
+ const url = new URL(req.url ?? '/', 'http://x');
185
+ const pathname = url.pathname;
186
+ const method = req.method ?? 'GET';
187
+ const readBody = async () => {
188
+ let raw = '';
189
+ for await (const chunk of req)
190
+ raw += chunk;
191
+ if (raw.trim() === '')
192
+ return {};
193
+ try {
194
+ return JSON.parse(raw);
195
+ }
196
+ catch {
197
+ throw new Error('请求体不是合法 JSON');
198
+ }
199
+ };
200
+ try {
201
+ if (method === 'GET' && pathname === '/himarket/state') {
202
+ sendJson(res, 200, { ok: true, ...(await snapshot()) });
203
+ return;
204
+ }
205
+ if (method === 'POST' && pathname === '/himarket/save-config') {
206
+ const body = await readBody();
207
+ const patch = {};
208
+ if (typeof body.baseUrl === 'string')
209
+ patch.baseUrl = body.baseUrl;
210
+ if (typeof body.username === 'string')
211
+ patch.username = body.username;
212
+ if (typeof body.password === 'string')
213
+ patch.password = body.password;
214
+ if (typeof body.skillInstallDir === 'string')
215
+ patch.skillInstallDir = body.skillInstallDir;
216
+ // 地址被清空时,一并清空 token(避免残留旧凭证)。
217
+ if (patch.baseUrl !== undefined && patch.baseUrl.trim() === '') {
218
+ patch.token = '';
219
+ }
220
+ await settings.save(patch);
221
+ // 凭证变化:丢弃旧 client(含旧 token);若地址被清空,同时卸载 MCP fiber 并清空内存缓存。
222
+ client = undefined;
223
+ if (patch.baseUrl !== undefined && patch.baseUrl.trim() === '') {
224
+ mcpManager?.dispose();
225
+ mcpManager = undefined;
226
+ subscribedMcps = [];
227
+ publishedSkills = [];
228
+ installedSkills = new Set();
229
+ lastError = '';
230
+ }
231
+ sendJson(res, 200, { ok: true, configured: true });
232
+ return;
233
+ }
234
+ if (method === 'POST' && pathname === '/himarket/sync') {
235
+ const summary = await sync();
236
+ sendJson(res, 200, { ok: true, summary });
237
+ return;
238
+ }
239
+ if (method === 'POST' && pathname === '/himarket/install-skill') {
240
+ const body = await readBody();
241
+ const nameOrId = typeof body.nameOrId === 'string' ? body.nameOrId : '';
242
+ if (nameOrId === '') {
243
+ sendError(res, 400, '缺少 nameOrId');
244
+ return;
245
+ }
246
+ const summary = await installByNameOrId(nameOrId);
247
+ sendJson(res, 200, { ok: true, summary });
248
+ return;
249
+ }
250
+ sendError(res, 404, '未知的 /himarket 端点');
251
+ }
252
+ catch (error) {
253
+ sendError(res, 500, error instanceof Error ? error.message : String(error));
254
+ }
255
+ },
256
+ });
257
+ });
258
+ ctx.effect(() => {
259
+ return () => {
260
+ mcpManager?.dispose();
261
+ mcpManager = undefined;
262
+ client = undefined;
263
+ };
264
+ }, 'himarket.dispose');
265
+ }
266
+ export { defaultSkillRoot } from './skill.js';
267
+ export { sanitizeServerName } from './mcp.js';
268
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAA;AAoB9B,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAc;IAChD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,UAAU,CAAA;IACzC,MAAM,QAAQ,GAAqB;QACjC,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,KAAK,EAAE,EAAE;QACT,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,EAAE;KAC9C,CAAA;IAED,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IAEvD,IAAI,MAAkC,CAAA;IACtC,IAAI,UAAkC,CAAA;IACtC,IAAI,cAAc,GAAoB,EAAE,CAAA;IACxC,IAAI,eAAe,GAAqB,EAAE,CAAA;IAC1C,IAAI,eAAe,GAAG,IAAI,GAAG,EAAU,CAAA;IACvC,IAAI,SAAS,GAAG,EAAE,CAAA;IAElB,MAAM,SAAS,GAAG,GAAW,EAAE;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,eAAe,CAAA;QAC9C,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;IACrD,CAAC,CAAA;IAED,SAAS,WAAW;QAClB,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAA;QAC5B,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACpF,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,IAAI,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;IAC/G,CAAC;IAED,KAAK,UAAU,WAAW;QACxB,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAA;QAC5B,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;QACrF,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;QACvD,CAAC;QACD,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,GAAG,WAAW,EAAG,CAAA;QACjD,IAAI,UAAU,KAAK,SAAS;YAAE,UAAU,GAAG,IAAI,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACvE,IAAI,CAAC,MAAO,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,MAAO,CAAC,KAAK,EAAE,CAAA;YACnC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,MAAO,CAAA;IAChB,CAAC;IAED,KAAK,UAAU,IAAI;QACjB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,WAAW,EAAE,CAAA;YAC7B,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC,kBAAkB,EAAE,CAAA;YAChE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,mBAAmB,EAAE,CAAA;YAC5C,cAAc,GAAG,UAAU,CAAA;YAC3B,eAAe,GAAG,MAAM,CAAA;YAExB,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;YACpG,eAAe,GAAG,MAAM,sBAAsB,EAAE,CAAA;YAEhD,SAAS,GAAG,EAAE,CAAA;YACd,MAAM,KAAK,GAAa,EAAE,CAAA;YAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,MAAM,aAAa,MAAM,CAAC,MAAM,UAAU,CAAC,CAAA;YACxE,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC/E,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAChF,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,eAAe,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC3G,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClE,OAAO,QAAQ,SAAS,EAAE,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,iBAAiB,CAAC,QAAgB;QAC/C,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,WAAW,EAAE,CAAA;YAC7B,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;YAC3F,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,mBAAmB,CAAC,CAAA;YACvD,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;YACnE,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAChC,OAAO,MAAM,CAAC,IAAI,CAAA;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClE,OAAO,QAAQ,SAAS,EAAE,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,sBAAsB;QACnC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAA;QACxB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;YACpD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;YAC5E,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;YACnE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QACV,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,UAAU,QAAQ;QACrB,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAA;QAC5B,OAAO;YACL,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;YAC3F,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE;YACvD,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,UAAU,EAAE,cAAc;YAC1B,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE;YACrD,eAAe;YACf,eAAe,EAAE,CAAC,GAAG,eAAe,CAAC;YACrC,SAAS;SACV,CAAA;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAA;IAC/B,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7F,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,MAAM,GAAG,WAAW,EAAG,CAAA;gBACvB,UAAU,GAAG,IAAI,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;gBACzC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAA;gBACrE,cAAc,GAAG,UAAU,CAAA;gBAC3B,eAAe,GAAG,MAAM,MAAM,CAAC,mBAAmB,EAAE,CAAA;gBACpD,MAAM,UAAU,CAAC,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;gBACnD,eAAe,GAAG,MAAM,sBAAsB,EAAE,CAAA;gBAChD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE,UAAU,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAAA;YACrG,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC1G,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpE,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;IACN,CAAC;IAED,KAAK,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE;QACvC,IAAI;QACJ,YAAY,EAAE,iBAAiB;KAChC,CAAC,CAAA;IAEF,GAAG,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QAClC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CACiF,CAAA;QACxH,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;YAC5D,OAAM;QACR,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,IAAY,EAAW,EAAE,CAC3C,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,kBAAkB,CAAA;QAEvE,MAAM,QAAQ,GAAG,CAAC,GAAQ,EAAE,MAAc,EAAE,IAAa,EAAQ,EAAE;YACjE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACjC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;gBACpB,cAAc,EAAE,iCAAiC;gBACjD,eAAe,EAAE,UAAU;aAC5B,CAAC,CAAA;YACF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACf,CAAC,CAAA;QAED,MAAM,SAAS,GAAG,CAAC,GAAQ,EAAE,MAAc,EAAE,OAAe,EAAQ,EAAE;YACpE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QACtD,CAAC,CAAA;QAED,OAAO,SAAS,CAAC,QAAQ,CAAC;YACxB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;gBAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,IAAI,EAAE,CAAC,EAAE,CAAC;oBACjD,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAA;oBAC9B,OAAM;gBACR,CAAC;gBACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,CAAC,CAAA;gBAC/C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;gBAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAA;gBAClC,MAAM,QAAQ,GAAG,KAAK,IAAsC,EAAE;oBAC5D,IAAI,GAAG,GAAG,EAAE,CAAA;oBACZ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG;wBAAE,GAAG,IAAI,KAAK,CAAA;oBAC3C,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;wBAAE,OAAO,EAAE,CAAA;oBAChC,IAAI,CAAC;wBACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAA;oBACnD,CAAC;oBAAC,MAAM,CAAC;wBACP,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAA;oBACjC,CAAC;gBACH,CAAC,CAAA;gBAED,IAAI,CAAC;oBACH,IAAI,MAAM,KAAK,KAAK,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;wBACvD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;wBACvD,OAAM;oBACR,CAAC;oBACD,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ,KAAK,uBAAuB,EAAE,CAAC;wBAC9D,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAA;wBAC7B,MAAM,KAAK,GAA8B,EAAE,CAAA;wBAC3C,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;4BAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;wBAClE,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;4BAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;wBACrE,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;4BAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;wBACrE,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ;4BAAE,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAA;wBAC1F,8BAA8B;wBAC9B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;4BAC/D,KAAK,CAAC,KAAK,GAAG,EAAE,CAAA;wBAClB,CAAC;wBACD,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;wBAC1B,2DAA2D;wBAC3D,MAAM,GAAG,SAAS,CAAA;wBAClB,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;4BAC/D,UAAU,EAAE,OAAO,EAAE,CAAA;4BACrB,UAAU,GAAG,SAAS,CAAA;4BACtB,cAAc,GAAG,EAAE,CAAA;4BACnB,eAAe,GAAG,EAAE,CAAA;4BACpB,eAAe,GAAG,IAAI,GAAG,EAAE,CAAA;4BAC3B,SAAS,GAAG,EAAE,CAAA;wBAChB,CAAC;wBACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;wBAClD,OAAM;oBACR,CAAC;oBACD,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ,KAAK,gBAAgB,EAAE,CAAC;wBACvD,MAAM,OAAO,GAAG,MAAM,IAAI,EAAE,CAAA;wBAC5B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;wBACzC,OAAM;oBACR,CAAC;oBACD,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ,KAAK,yBAAyB,EAAE,CAAC;wBAChE,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAA;wBAC7B,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;wBACvE,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;4BACpB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,CAAA;4BAClC,OAAM;wBACR,CAAC;wBACD,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAA;wBACjD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;wBACzC,OAAM;oBACR,CAAC;oBACD,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,kBAAkB,CAAC,CAAA;gBACzC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC7E,CAAC;YACH,CAAC;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,EAAE;YACV,UAAU,EAAE,OAAO,EAAE,CAAA;YACrB,UAAU,GAAG,SAAS,CAAA;YACtB,MAAM,GAAG,SAAS,CAAA;QACpB,CAAC,CAAA;IACH,CAAC,EAAE,kBAAkB,CAAC,CAAA;AACxB,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA"}
package/lib/mcp.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MCP 接入:把「已订阅 MCP 清单」映射为一组 @deepseek-ai/dsh-mcp-client 动态 fiber。
3
+ *
4
+ * 复用官方 mcp-client(不重造客户端):经 ctx.plugin(plugin, config) 动态实例化,
5
+ * 每次 sync 做「期望清单 vs 现存 fiber」的 diff——新增/变更 dispose 旧 fiber 再建,
6
+ * 移除则 dispose。mcp-client 的 inject 是 ['tools'],工具注册到 host tools 全局层,
7
+ * 对所有会话可见(与 dsh-matrix 注册工具同一机制)。
8
+ *
9
+ * 官方包在运行时经 createRequire(ctx.baseUrl) 解析(web profile 的 @deepseek-ai 是空目录)。
10
+ *
11
+ * @module dsh-himarket/mcp
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import type { SubscribedMcp } from './himarket-client.js';
15
+ /** 清洗 HiMarket 的 name 为合法 serverName(去非法字符,压缩下划线)。 */
16
+ export declare function sanitizeServerName(raw: string): string;
17
+ /**
18
+ * 订阅态 → 动态 mcp-client fiber 集合管理器。惰性加载官方 mcp-client,
19
+ * 只在第一次 reconcile 时 import,避免启动期拖慢。
20
+ */
21
+ export declare class McpManager {
22
+ private readonly ctx;
23
+ private readonly baseUrl;
24
+ private readonly handles;
25
+ private mcpApply;
26
+ constructor(ctx: Context, baseUrl: string);
27
+ /** 当前已连接的 serverName 列表。 */
28
+ activeServerNames(): string[];
29
+ /** 与期望清单做 diff,重建 fiber 集合;返回 { added, removed, errors }。 */
30
+ reconcile(desired: SubscribedMcp[], authHeaders: Record<string, string>): Promise<{
31
+ added: string[];
32
+ removed: string[];
33
+ errors: string[];
34
+ }>;
35
+ /** 卸载全部 fiber(插件销毁时)。 */
36
+ dispose(): void;
37
+ /** 惰性加载官方 mcp-client 并缓存 apply。 */
38
+ private ensureLoaded;
39
+ }
package/lib/mcp.js ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * MCP 接入:把「已订阅 MCP 清单」映射为一组 @deepseek-ai/dsh-mcp-client 动态 fiber。
3
+ *
4
+ * 复用官方 mcp-client(不重造客户端):经 ctx.plugin(plugin, config) 动态实例化,
5
+ * 每次 sync 做「期望清单 vs 现存 fiber」的 diff——新增/变更 dispose 旧 fiber 再建,
6
+ * 移除则 dispose。mcp-client 的 inject 是 ['tools'],工具注册到 host tools 全局层,
7
+ * 对所有会话可见(与 dsh-matrix 注册工具同一机制)。
8
+ *
9
+ * 官方包在运行时经 createRequire(ctx.baseUrl) 解析(web profile 的 @deepseek-ai 是空目录)。
10
+ *
11
+ * @module dsh-himarket/mcp
12
+ */
13
+ import { importOfficial } from './resolver.js';
14
+ /** 清洗 HiMarket 的 name 为合法 serverName(去非法字符,压缩下划线)。 */
15
+ export function sanitizeServerName(raw) {
16
+ const cleaned = raw
17
+ .trim()
18
+ .replace(/[^A-Za-z0-9_-]/gu, '_')
19
+ .replace(/^_+|_+$/gu, '');
20
+ if (cleaned === '')
21
+ return 'mcp';
22
+ return cleaned.slice(0, 32);
23
+ }
24
+ /** 组装 mcp-client 的 config(streamable-http)。 */
25
+ function buildMcpConfig(mcp, authHeaders) {
26
+ const transport = mcp.transportType === 'stdio' ? 'stdio' : 'streamable-http';
27
+ if (transport === 'stdio') {
28
+ throw new Error(`暂不支持 stdio 型 MCP(${mcp.name})`);
29
+ }
30
+ return {
31
+ transport: 'streamable-http',
32
+ serverName: sanitizeServerName(mcp.name),
33
+ url: mcp.url,
34
+ headers: { ...authHeaders },
35
+ failOnStartupError: false,
36
+ };
37
+ }
38
+ /**
39
+ * 订阅态 → 动态 mcp-client fiber 集合管理器。惰性加载官方 mcp-client,
40
+ * 只在第一次 reconcile 时 import,避免启动期拖慢。
41
+ */
42
+ export class McpManager {
43
+ ctx;
44
+ baseUrl;
45
+ handles = new Map();
46
+ mcpApply;
47
+ constructor(ctx, baseUrl) {
48
+ this.ctx = ctx;
49
+ this.baseUrl = baseUrl;
50
+ }
51
+ /** 当前已连接的 serverName 列表。 */
52
+ activeServerNames() {
53
+ return [...this.handles.keys()];
54
+ }
55
+ /** 与期望清单做 diff,重建 fiber 集合;返回 { added, removed, errors }。 */
56
+ async reconcile(desired, authHeaders) {
57
+ const apply = await this.ensureLoaded();
58
+ const next = new Map();
59
+ const errors = [];
60
+ for (const mcp of desired) {
61
+ const serverName = sanitizeServerName(mcp.name);
62
+ if (!next.has(serverName))
63
+ next.set(serverName, mcp);
64
+ }
65
+ const added = [];
66
+ const removed = [];
67
+ for (const [serverName, handle] of [...this.handles]) {
68
+ if (!next.has(serverName)) {
69
+ try {
70
+ handle.fiber.dispose();
71
+ }
72
+ catch { }
73
+ this.handles.delete(serverName);
74
+ removed.push(serverName);
75
+ }
76
+ }
77
+ for (const [serverName, mcp] of next) {
78
+ const existing = this.handles.get(serverName);
79
+ const existingKey = existing?.key;
80
+ const newKey = JSON.stringify([mcp.url, mcp.transportType, authHeaders]);
81
+ if (existingKey === newKey)
82
+ continue;
83
+ if (existing !== undefined) {
84
+ try {
85
+ existing.fiber.dispose();
86
+ }
87
+ catch { }
88
+ this.handles.delete(serverName);
89
+ }
90
+ try {
91
+ const config = buildMcpConfig(mcp, authHeaders);
92
+ const fiber = this.ctx.plugin({ inject: ['tools'], apply }, config);
93
+ this.handles.set(serverName, { serverName, key: newKey, fiber: { dispose: () => fiber.dispose() } });
94
+ added.push(serverName);
95
+ }
96
+ catch (error) {
97
+ errors.push(`${serverName}: ${error instanceof Error ? error.message : String(error)}`);
98
+ }
99
+ }
100
+ return { added, removed, errors };
101
+ }
102
+ /** 卸载全部 fiber(插件销毁时)。 */
103
+ dispose() {
104
+ for (const handle of this.handles.values()) {
105
+ try {
106
+ handle.fiber.dispose();
107
+ }
108
+ catch { }
109
+ }
110
+ this.handles.clear();
111
+ }
112
+ /** 惰性加载官方 mcp-client 并缓存 apply。 */
113
+ async ensureLoaded() {
114
+ if (this.mcpApply !== undefined)
115
+ return this.mcpApply;
116
+ const mod = await importOfficial('@deepseek-ai/dsh-mcp-client', this.baseUrl);
117
+ const apply = mod.apply;
118
+ if (typeof apply !== 'function') {
119
+ throw new Error('官方 mcp-client 未导出 apply');
120
+ }
121
+ this.mcpApply = apply;
122
+ return apply;
123
+ }
124
+ }
125
+ //# sourceMappingURL=mcp.js.map
package/lib/mcp.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Official @deepseek-ai/* package resolver.
3
+ *
4
+ * The web profile's node_modules/@deepseek-ai is an empty directory: official
5
+ * packages actually live in the global dsh install, and Node's CJS resolution
6
+ * walks parent directories, so createRequire(ctx.baseUrl) resolves them from
7
+ * the profile directory.
8
+ *
9
+ * This bridge package carries only light deps; official packages (mcp-client /
10
+ * settings / tools / schemastery) are ALWAYS resolved at runtime through this
11
+ * module, never statically imported (a static import would resolve against this
12
+ * package's own node_modules and fail under the profile).
13
+ *
14
+ * @module dsh-himarket/resolver
15
+ */
16
+ /** Resolve an official package entry from the profile directory (file:// URL). */
17
+ export declare function resolveOfficial(specifier: string, baseUrl: string): string;
18
+ /** Dynamically import an official package (ESM or CJS). */
19
+ export declare function importOfficial(specifier: string, baseUrl: string): Promise<Record<string, unknown>>;
20
+ /** Synchronously require an official CJS package (e.g. schemastery). */
21
+ export declare function requireOfficial(specifier: string, baseUrl: string): unknown;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Official @deepseek-ai/* package resolver.
3
+ *
4
+ * The web profile's node_modules/@deepseek-ai is an empty directory: official
5
+ * packages actually live in the global dsh install, and Node's CJS resolution
6
+ * walks parent directories, so createRequire(ctx.baseUrl) resolves them from
7
+ * the profile directory.
8
+ *
9
+ * This bridge package carries only light deps; official packages (mcp-client /
10
+ * settings / tools / schemastery) are ALWAYS resolved at runtime through this
11
+ * module, never statically imported (a static import would resolve against this
12
+ * package's own node_modules and fail under the profile).
13
+ *
14
+ * @module dsh-himarket/resolver
15
+ */
16
+ import { createRequire } from 'node:module';
17
+ import { pathToFileURL } from 'node:url';
18
+ /** Resolve an official package entry from the profile directory (file:// URL). */
19
+ export function resolveOfficial(specifier, baseUrl) {
20
+ const req = createRequire(baseUrl);
21
+ const resolved = req.resolve(specifier);
22
+ return pathToFileURL(resolved).href;
23
+ }
24
+ /** Dynamically import an official package (ESM or CJS). */
25
+ export async function importOfficial(specifier, baseUrl) {
26
+ return (await import(resolveOfficial(specifier, baseUrl)));
27
+ }
28
+ /** Synchronously require an official CJS package (e.g. schemastery). */
29
+ export function requireOfficial(specifier, baseUrl) {
30
+ const req = createRequire(baseUrl);
31
+ return req(specifier);
32
+ }
33
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,OAAe;IAChE,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACvC,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAA;AACrC,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB,EAAE,OAAe;IACrE,OAAO,CAAC,MAAM,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAA4B,CAAA;AACvF,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,OAAe;IAChE,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;IAClC,OAAO,GAAG,CAAC,SAAS,CAAC,CAAA;AACvB,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * HiMarket bridge config & credential persistence, mounted on the DSH settings
3
+ * service (settings.yaml, hot-reloaded). The bridge publishes no service; it
4
+ * only consumes settings, and credentials are a runtime choice that users change
5
+ * after deployment — so they ride the same namespace mechanism as the Web Models
6
+ * page and survive restart.
7
+ *
8
+ * The schemastery schema is loaded SYNCHRONOUSLY via requireOfficial (CJS),
9
+ * because settings.register() needs the schema object at attach time. This keeps
10
+ * the bridge free of any static @deepseek-ai/* import (see resolver.ts).
11
+ *
12
+ * @module dsh-himarket/settings
13
+ */
14
+ import type { Context } from '@deepseek-ai/cordis';
15
+ /** Bridge config & credential shape (resolved settings namespace value). */
16
+ export interface HimarketSettings {
17
+ /** HiMarket backend base URL, e.g. http://ai-market.ict.cmcc (no trailing slash). */
18
+ baseUrl: string;
19
+ /** Developer account username. */
20
+ username: string;
21
+ /** Developer account password (plaintext, local settings.yaml only). */
22
+ password: string;
23
+ /** Cached JWT; re-login happens on 401. */
24
+ token: string;
25
+ /** Skill install root; empty falls back to ~/.dsh/skills. */
26
+ skillInstallDir: string;
27
+ }
28
+ /** Settings namespace name. */
29
+ export declare const NAMESPACE = "himarket";
30
+ /**
31
+ * Register the settings namespace and return a read/write handle; falls back to
32
+ * read-only row config when the settings service is unavailable.
33
+ */
34
+ export declare function attachSettings(ctx: Context, fallback: HimarketSettings, baseUrl: string): {
35
+ current(): HimarketSettings;
36
+ save(patch: Partial<HimarketSettings>): Promise<void>;
37
+ onChange(cb: () => void): () => void;
38
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * HiMarket bridge config & credential persistence, mounted on the DSH settings
3
+ * service (settings.yaml, hot-reloaded). The bridge publishes no service; it
4
+ * only consumes settings, and credentials are a runtime choice that users change
5
+ * after deployment — so they ride the same namespace mechanism as the Web Models
6
+ * page and survive restart.
7
+ *
8
+ * The schemastery schema is loaded SYNCHRONOUSLY via requireOfficial (CJS),
9
+ * because settings.register() needs the schema object at attach time. This keeps
10
+ * the bridge free of any static @deepseek-ai/* import (see resolver.ts).
11
+ *
12
+ * @module dsh-himarket/settings
13
+ */
14
+ import { requireOfficial } from './resolver.js';
15
+ /** Settings namespace name. */
16
+ export const NAMESPACE = 'himarket';
17
+ /**
18
+ * Register the settings namespace and return a read/write handle; falls back to
19
+ * read-only row config when the settings service is unavailable.
20
+ */
21
+ export function attachSettings(ctx, fallback, baseUrl) {
22
+ let resolved = () => fallback;
23
+ let save = async () => { };
24
+ let onChange = () => () => { };
25
+ // settings 服务是异步 init 的,apply 时可能尚未就绪;用 ctx.inject 等它就绪。
26
+ ctx.inject(['settings'], (scope) => {
27
+ const settings = scope.get('settings');
28
+ if (settings === undefined) {
29
+ ctx.logger.warn('[dsh-himarket] settings service unavailable, using cordis.patch.yml row config');
30
+ return;
31
+ }
32
+ try {
33
+ const Schema = requireOfficial('@deepseek-ai/schemastery', baseUrl);
34
+ const schema = Schema.object({
35
+ baseUrl: Schema.string().default(''),
36
+ username: Schema.string().default(''),
37
+ password: Schema.string().default(''),
38
+ token: Schema.string().default(''),
39
+ skillInstallDir: Schema.string().default(''),
40
+ });
41
+ const scopeHandle = settings.register(NAMESPACE, schema, { base: fallback });
42
+ resolved = () => scopeHandle.get();
43
+ save = async (patch) => {
44
+ await scopeHandle.update(patch);
45
+ };
46
+ onChange = (cb) => scopeHandle.watch(() => { cb(); });
47
+ }
48
+ catch (error) {
49
+ ctx.logger.warn('[dsh-himarket] settings registration failed, falling back to row config: %s', messageOf(error));
50
+ }
51
+ });
52
+ return { current: () => resolved(), save, onChange };
53
+ }
54
+ function messageOf(error) {
55
+ return error instanceof Error ? error.message : String(error);
56
+ }
57
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAgB/C,+BAA+B;AAC/B,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAA;AAkBnC;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAY,EACZ,QAA0B,EAC1B,OAAe;IAMf,IAAI,QAAQ,GAA2B,GAAG,EAAE,CAAC,QAAQ,CAAA;IACrD,IAAI,IAAI,GAAwD,KAAK,IAAI,EAAE,GAAE,CAAC,CAAA;IAC9E,IAAI,QAAQ,GAAmC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAA;IAE7D,yDAAyD;IACzD,GAAG,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAA6B,CAAA;QAClE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAA;YACjG,OAAM;QACR,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,eAAe,CAAC,0BAA0B,EAAE,OAAO,CAAoB,CAAA;YACtF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;gBAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;gBAClC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;aAC7C,CAAC,CAAA;YACF,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAmB,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC9F,QAAQ,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAA;YAClC,IAAI,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE;gBACrB,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACjC,CAAC,CAAA;YACD,QAAQ,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6EAA6E,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;QAClH,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;AACtD,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC"}
package/lib/skill.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Skill 安装:下载 HiMarket 的 Skill ZIP → 解压 → 定位 SKILL.md → 落盘到
3
+ * ~/.dsh/skills/<name>/。技能由 @deepseek-ai/dsh-skill-filesystem 扫描发现,
4
+ * 写入即生效(该插件有 watcher + fs 工具同步失效),无需重启。
5
+ *
6
+ * 解压用系统 bsdtar(Windows/macOS 自带,Linux 也默认有 tar),避免引入原生
7
+ * 依赖;先落临时目录,校验通过后再搬到目标目录,失败不污染已装内容。
8
+ *
9
+ * @module dsh-himarket/skill
10
+ */
11
+ import type { HimarketClient } from './himarket-client.js';
12
+ /** 默认安装根:~/.dsh/skills(DSH_HOME 优先)。 */
13
+ export declare function defaultSkillRoot(): string;
14
+ export interface SkillInstallResult {
15
+ ok: boolean;
16
+ name: string;
17
+ dir: string;
18
+ note: string;
19
+ }
20
+ /**
21
+ * 安装一个 Skill(按 productId 下载 ZIP → 定位 SKILL.md → 落盘)。
22
+ * 已装同名 skill 会被覆盖(幂等重装);失败时保留旧版。
23
+ */
24
+ export declare function installSkill(client: HimarketClient, productId: string, installRoot: string): Promise<SkillInstallResult>;