dsh-mcp-panel 0.3.0 → 0.4.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/CHANGELOG.md +18 -0
- package/README.es.md +48 -109
- package/README.hi.md +48 -109
- package/README.md +90 -79
- package/README.pt.md +48 -109
- package/README.zh.md +96 -86
- package/cordis.patch.yml +17 -4
- package/lib/client.js +1350 -48
- package/lib/client.js.map +1 -1
- package/lib/index.js +1191 -46
- package/lib/schemas-BhI7GrM5.js +4091 -0
- package/lib/typert.host.js +193 -4099
- package/lib/types/aggregate.d.ts +18 -1
- package/lib/types/aggregate.d.ts.map +1 -1
- package/lib/types/client/McpPanelTab.d.ts +12 -6
- package/lib/types/client/McpPanelTab.d.ts.map +1 -1
- package/lib/types/client/ServerEditor.d.ts +25 -0
- package/lib/types/client/ServerEditor.d.ts.map +1 -0
- package/lib/types/client/TrialConsole.d.ts +20 -0
- package/lib/types/client/TrialConsole.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +7 -5
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +157 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/present.d.ts +6 -0
- package/lib/types/client/present.d.ts.map +1 -1
- package/lib/types/client/remote.d.ts +175 -2
- package/lib/types/client/remote.d.ts.map +1 -1
- package/lib/types/command.d.ts +32 -3
- package/lib/types/command.d.ts.map +1 -1
- package/lib/types/config.d.ts +41 -1
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/diagnostics.d.ts +55 -0
- package/lib/types/diagnostics.d.ts.map +1 -0
- package/lib/types/index.d.ts +32 -15
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/patch.d.ts +176 -0
- package/lib/types/patch.d.ts.map +1 -0
- package/lib/types/service.d.ts +73 -8
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/trial.d.ts +72 -0
- package/lib/types/trial.d.ts.map +1 -0
- package/lib/types/typert.host.d.ts +164 -0
- package/lib/types/typert.host.d.ts.map +1 -1
- package/lib/types/upstream.d.ts +44 -15
- package/lib/types/upstream.d.ts.map +1 -1
- package/lib/types/wire.d.ts +507 -10
- package/lib/types/wire.d.ts.map +1 -1
- package/lib/types/write.d.ts +30 -0
- package/lib/types/write.d.ts.map +1 -0
- package/package.json +44 -2
- package/src/aggregate.ts +79 -2
- package/src/client/McpPanelTab.tsx +138 -17
- package/src/client/ServerEditor.tsx +290 -0
- package/src/client/TrialConsole.tsx +145 -0
- package/src/client/index.ts +45 -18
- package/src/client/locales.ts +169 -5
- package/src/client/present.ts +9 -0
- package/src/client/remote.ts +17 -2
- package/src/client/styles.ts +201 -0
- package/src/command.ts +139 -18
- package/src/config.ts +88 -2
- package/src/diagnostics.ts +126 -0
- package/src/index.ts +49 -16
- package/src/patch.ts +493 -0
- package/src/probe.ts +1 -1
- package/src/service.ts +254 -9
- package/src/trial.ts +192 -0
- package/src/upstream.ts +45 -15
- package/src/wire.ts +292 -10
- package/src/write.ts +97 -0
package/lib/client.js
CHANGED
|
@@ -113,7 +113,10 @@ window.__ModuleLoader__.load({
|
|
|
113
113
|
empty: snapshot.servers.length === 0,
|
|
114
114
|
observed: snapshot.observed,
|
|
115
115
|
patchFile: snapshot.patchFile,
|
|
116
|
-
refreshIntervalMs: snapshot.refreshIntervalMs
|
|
116
|
+
refreshIntervalMs: snapshot.refreshIntervalMs,
|
|
117
|
+
capabilities: snapshot.capabilities,
|
|
118
|
+
trial: snapshot.trial,
|
|
119
|
+
writeEnabled: snapshot.writeEnabled
|
|
117
120
|
};
|
|
118
121
|
}
|
|
119
122
|
/**
|
|
@@ -150,8 +153,585 @@ window.__ModuleLoader__.load({
|
|
|
150
153
|
return servers.filter((row) => row.view.serverName.toLocaleLowerCase().includes(needle) || row.view.target.toLocaleLowerCase().includes(needle));
|
|
151
154
|
}
|
|
152
155
|
//#endregion
|
|
156
|
+
//#region src/client/ServerEditor.tsx
|
|
157
|
+
/** Server CRUD editor: form → patch fragment → copy or approval-gated write. */
|
|
158
|
+
let rowCounter = 0;
|
|
159
|
+
/** Render the CRUD editor. */
|
|
160
|
+
function ServerEditor({ t, view, entryId, writeEnabled, actions, onClose, onWritten }) {
|
|
161
|
+
const isEdit = view !== null;
|
|
162
|
+
const [serverName, setServerName] = (0, react.useState)(view?.serverName ?? "");
|
|
163
|
+
const [transport, setTransport] = (0, react.useState)(view?.transport === "streamable-http" ? "streamable-http" : "stdio");
|
|
164
|
+
const [command, setCommand] = (0, react.useState)(view?.command ?? "");
|
|
165
|
+
const [args, setArgs] = (0, react.useState)((view?.args ?? []).join("\n"));
|
|
166
|
+
const [cwd, setCwd] = (0, react.useState)(view?.cwd ?? "");
|
|
167
|
+
const [url, setUrl] = (0, react.useState)(view?.url ?? "");
|
|
168
|
+
const [timeout, setTimeoutText] = (0, react.useState)(view?.toolCallTimeoutMs === null ? "" : String(view?.toolCallTimeoutMs));
|
|
169
|
+
const [failFast, setFailFast] = (0, react.useState)(view?.failOnStartupError === true);
|
|
170
|
+
const [reconnectEnabled, setReconnectEnabled] = (0, react.useState)(view === null || view.reconnectEnabled === null ? true : view.reconnectEnabled);
|
|
171
|
+
const [reconnectAttempts, setReconnectAttempts] = (0, react.useState)(view === null || view.reconnectMaxAttempts === null ? "" : String(view.reconnectMaxAttempts));
|
|
172
|
+
const [envRows, setEnvRows] = (0, react.useState)(() => initialRows(view?.envKeys ?? []));
|
|
173
|
+
const [headerRows, setHeaderRows] = (0, react.useState)(() => initialRows(view?.headerKeys ?? []));
|
|
174
|
+
const [preview, setPreview] = (0, react.useState)(null);
|
|
175
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
176
|
+
const [armed, setArmed] = (0, react.useState)(false);
|
|
177
|
+
const [error, setError] = (0, react.useState)(null);
|
|
178
|
+
const [notice, setNotice] = (0, react.useState)(null);
|
|
179
|
+
const [written, setWritten] = (0, react.useState)(null);
|
|
180
|
+
const rowsToMap = (rows, existingKeys) => {
|
|
181
|
+
const entries = {};
|
|
182
|
+
const keep = [];
|
|
183
|
+
for (const row of rows) {
|
|
184
|
+
const key = row.key.trim();
|
|
185
|
+
if (key === "") continue;
|
|
186
|
+
if (existingKeys.includes(key) && row.value === "") keep.push(key);
|
|
187
|
+
else entries[key] = row.value;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
entries,
|
|
191
|
+
keep
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
const buildOp = () => {
|
|
195
|
+
const existingEnv = view?.envKeys ?? [];
|
|
196
|
+
const existingHeaders = view?.headerKeys ?? [];
|
|
197
|
+
const env = rowsToMap(envRows, existingEnv);
|
|
198
|
+
const headers = rowsToMap(headerRows, existingHeaders);
|
|
199
|
+
const config = {
|
|
200
|
+
serverName: serverName.trim(),
|
|
201
|
+
transport,
|
|
202
|
+
...transport === "stdio" ? {
|
|
203
|
+
command: command.trim(),
|
|
204
|
+
...args.trim() === "" ? {} : { args: args.split("\n").map((line) => line.trim()).filter((line) => line !== "") },
|
|
205
|
+
...cwd.trim() === "" ? {} : { cwd: cwd.trim() },
|
|
206
|
+
env: env.entries,
|
|
207
|
+
...isEdit ? { keepEnv: env.keep } : {}
|
|
208
|
+
} : {
|
|
209
|
+
url: url.trim(),
|
|
210
|
+
headers: headers.entries,
|
|
211
|
+
...isEdit ? { keepHeaders: headers.keep } : {}
|
|
212
|
+
},
|
|
213
|
+
...timeout.trim() === "" ? {} : { toolCallTimeoutMs: Number(timeout) },
|
|
214
|
+
...failFast ? { failOnStartupError: true } : {},
|
|
215
|
+
...reconnectEnabled === false ? { reconnectEnabled: false } : {},
|
|
216
|
+
...reconnectAttempts.trim() === "" ? {} : { reconnectMaxAttempts: Number(reconnectAttempts) }
|
|
217
|
+
};
|
|
218
|
+
return isEdit ? {
|
|
219
|
+
kind: "edit",
|
|
220
|
+
entryId,
|
|
221
|
+
config
|
|
222
|
+
} : {
|
|
223
|
+
kind: "add",
|
|
224
|
+
config
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
const doPreview = () => {
|
|
228
|
+
setError(null);
|
|
229
|
+
setNotice(null);
|
|
230
|
+
setWritten(null);
|
|
231
|
+
setArmed(false);
|
|
232
|
+
setBusy(true);
|
|
233
|
+
Promise.resolve().then(() => actions.previewPatch(JSON.stringify(buildOp()))).then((result) => {
|
|
234
|
+
setPreview(result);
|
|
235
|
+
setBusy(false);
|
|
236
|
+
}, (failure) => {
|
|
237
|
+
setError(messageOf(failure));
|
|
238
|
+
setBusy(false);
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
const doWrite = () => {
|
|
242
|
+
if (preview === null) return;
|
|
243
|
+
setError(null);
|
|
244
|
+
setNotice(null);
|
|
245
|
+
setBusy(true);
|
|
246
|
+
Promise.resolve().then(() => actions.writePatch(JSON.stringify(buildOp()), true)).then((result) => {
|
|
247
|
+
setWritten(result);
|
|
248
|
+
setBusy(false);
|
|
249
|
+
setArmed(false);
|
|
250
|
+
setNotice(t("writeDone").replace("{file}", result.file).replace("{backup}", result.backupPath));
|
|
251
|
+
onWritten();
|
|
252
|
+
}, (failure) => {
|
|
253
|
+
setError(t("writeFailed").replace("{message}", messageOf(failure)));
|
|
254
|
+
setBusy(false);
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
const copy = () => {
|
|
258
|
+
if (preview === null) return;
|
|
259
|
+
navigator.clipboard?.writeText(preview.fragment).then(() => {
|
|
260
|
+
setNotice(t("patchCopied"));
|
|
261
|
+
}, (failure) => {
|
|
262
|
+
setError(t("copyFailed").replace("{message}", messageOf(failure)));
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
266
|
+
className: "dmcp-editor",
|
|
267
|
+
"data-mcp-editor": isEdit ? entryId : "new",
|
|
268
|
+
children: [
|
|
269
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
270
|
+
className: "dmcp-heading",
|
|
271
|
+
children: isEdit ? t("editorTitleEdit").replace("{name}", view?.serverName ?? "") : t("editorTitleAdd")
|
|
272
|
+
}),
|
|
273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
274
|
+
className: "dmcp-form",
|
|
275
|
+
children: [
|
|
276
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
277
|
+
className: "dmcp-field",
|
|
278
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldServerName") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
279
|
+
type: "text",
|
|
280
|
+
value: serverName,
|
|
281
|
+
onChange: (event) => {
|
|
282
|
+
setServerName(event.currentTarget.value);
|
|
283
|
+
}
|
|
284
|
+
})]
|
|
285
|
+
}),
|
|
286
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
287
|
+
className: "dmcp-field",
|
|
288
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldTransport") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
289
|
+
value: transport,
|
|
290
|
+
onChange: (event) => {
|
|
291
|
+
setTransport(event.currentTarget.value === "streamable-http" ? "streamable-http" : "stdio");
|
|
292
|
+
},
|
|
293
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
294
|
+
value: "stdio",
|
|
295
|
+
children: "stdio"
|
|
296
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
297
|
+
value: "streamable-http",
|
|
298
|
+
children: "streamable-http"
|
|
299
|
+
})]
|
|
300
|
+
})]
|
|
301
|
+
}),
|
|
302
|
+
transport === "stdio" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
303
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
304
|
+
className: "dmcp-field",
|
|
305
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldCommand") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
306
|
+
type: "text",
|
|
307
|
+
value: command,
|
|
308
|
+
onChange: (event) => {
|
|
309
|
+
setCommand(event.currentTarget.value);
|
|
310
|
+
}
|
|
311
|
+
})]
|
|
312
|
+
}),
|
|
313
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
314
|
+
className: "dmcp-field",
|
|
315
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldArgs") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
316
|
+
rows: 3,
|
|
317
|
+
value: args,
|
|
318
|
+
onChange: (event) => {
|
|
319
|
+
setArgs(event.currentTarget.value);
|
|
320
|
+
}
|
|
321
|
+
})]
|
|
322
|
+
}),
|
|
323
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
324
|
+
className: "dmcp-field",
|
|
325
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldCwd") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
326
|
+
type: "text",
|
|
327
|
+
value: cwd,
|
|
328
|
+
onChange: (event) => {
|
|
329
|
+
setCwd(event.currentTarget.value);
|
|
330
|
+
}
|
|
331
|
+
})]
|
|
332
|
+
}),
|
|
333
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MapEditor, {
|
|
334
|
+
label: t("fieldEnv"),
|
|
335
|
+
rows: envRows,
|
|
336
|
+
setRows: setEnvRows,
|
|
337
|
+
t
|
|
338
|
+
})
|
|
339
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
340
|
+
className: "dmcp-field",
|
|
341
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldUrl") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
342
|
+
type: "text",
|
|
343
|
+
value: url,
|
|
344
|
+
onChange: (event) => {
|
|
345
|
+
setUrl(event.currentTarget.value);
|
|
346
|
+
}
|
|
347
|
+
})]
|
|
348
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MapEditor, {
|
|
349
|
+
label: t("fieldHeaders"),
|
|
350
|
+
rows: headerRows,
|
|
351
|
+
setRows: setHeaderRows,
|
|
352
|
+
t
|
|
353
|
+
})] }),
|
|
354
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
355
|
+
className: "dmcp-field",
|
|
356
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldTimeout") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
357
|
+
type: "number",
|
|
358
|
+
min: 1,
|
|
359
|
+
value: timeout,
|
|
360
|
+
onChange: (event) => {
|
|
361
|
+
setTimeoutText(event.currentTarget.value);
|
|
362
|
+
}
|
|
363
|
+
})]
|
|
364
|
+
}),
|
|
365
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
366
|
+
className: "dmcp-check",
|
|
367
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
368
|
+
type: "checkbox",
|
|
369
|
+
checked: failFast,
|
|
370
|
+
onChange: (event) => {
|
|
371
|
+
setFailFast(event.currentTarget.checked);
|
|
372
|
+
}
|
|
373
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldFailFast") })]
|
|
374
|
+
}),
|
|
375
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
376
|
+
className: "dmcp-check",
|
|
377
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
378
|
+
type: "checkbox",
|
|
379
|
+
checked: reconnectEnabled,
|
|
380
|
+
onChange: (event) => {
|
|
381
|
+
setReconnectEnabled(event.currentTarget.checked);
|
|
382
|
+
}
|
|
383
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldReconnectEnabled") })]
|
|
384
|
+
}),
|
|
385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
386
|
+
className: "dmcp-field",
|
|
387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("fieldReconnectMaxAttempts") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
388
|
+
type: "number",
|
|
389
|
+
min: 1,
|
|
390
|
+
value: reconnectAttempts,
|
|
391
|
+
onChange: (event) => {
|
|
392
|
+
setReconnectAttempts(event.currentTarget.value);
|
|
393
|
+
}
|
|
394
|
+
})]
|
|
395
|
+
})
|
|
396
|
+
]
|
|
397
|
+
}),
|
|
398
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
399
|
+
className: "dmcp-editor-actions",
|
|
400
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
401
|
+
type: "button",
|
|
402
|
+
className: "dmcp-action",
|
|
403
|
+
onClick: doPreview,
|
|
404
|
+
disabled: busy,
|
|
405
|
+
children: t("previewPatch")
|
|
406
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
407
|
+
type: "button",
|
|
408
|
+
className: "dmcp-action",
|
|
409
|
+
onClick: onClose,
|
|
410
|
+
disabled: busy,
|
|
411
|
+
children: t("cancel")
|
|
412
|
+
})]
|
|
413
|
+
}),
|
|
414
|
+
error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
415
|
+
className: "dmcp-error-text",
|
|
416
|
+
role: "alert",
|
|
417
|
+
children: error
|
|
418
|
+
}) : null,
|
|
419
|
+
notice !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
420
|
+
className: "dmcp-notice",
|
|
421
|
+
children: notice
|
|
422
|
+
}) : null,
|
|
423
|
+
preview !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
424
|
+
className: "dmcp-patch",
|
|
425
|
+
children: [
|
|
426
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
427
|
+
className: "dmcp-patch-hint",
|
|
428
|
+
children: preview.file === null ? t("patchFileUnknown") : t("patchFor").replace("{file}", preview.file)
|
|
429
|
+
}),
|
|
430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
431
|
+
className: "dmcp-fragment",
|
|
432
|
+
children: preview.fragment
|
|
433
|
+
}),
|
|
434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
435
|
+
className: "dmcp-editor-actions",
|
|
436
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
437
|
+
type: "button",
|
|
438
|
+
className: "dmcp-action",
|
|
439
|
+
onClick: copy,
|
|
440
|
+
children: t("copyPatch")
|
|
441
|
+
}), writeEnabled && preview.file !== null ? armed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
442
|
+
type: "button",
|
|
443
|
+
className: "dmcp-action dmcp-confirm",
|
|
444
|
+
onClick: doWrite,
|
|
445
|
+
disabled: busy,
|
|
446
|
+
children: t("confirmWrite")
|
|
447
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
448
|
+
type: "button",
|
|
449
|
+
className: "dmcp-action",
|
|
450
|
+
onClick: () => {
|
|
451
|
+
setArmed(true);
|
|
452
|
+
},
|
|
453
|
+
disabled: busy,
|
|
454
|
+
children: t("writeToProfile")
|
|
455
|
+
}) : null]
|
|
456
|
+
}),
|
|
457
|
+
armed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
458
|
+
className: "dmcp-notice",
|
|
459
|
+
children: t("writeRequiresConfirm")
|
|
460
|
+
}) : null,
|
|
461
|
+
!writeEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
462
|
+
className: "dmcp-error-text",
|
|
463
|
+
children: t("writeDisabled")
|
|
464
|
+
}) : null,
|
|
465
|
+
written !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
466
|
+
className: "dmcp-notice",
|
|
467
|
+
children: t(prevWrittenLabel(written.approvalPath))
|
|
468
|
+
}) : null
|
|
469
|
+
]
|
|
470
|
+
}) : null
|
|
471
|
+
]
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
/** Localized approval-channel label for one write result. */
|
|
475
|
+
function prevWrittenLabel(path) {
|
|
476
|
+
return path === "harness-approval" ? "approvalHarness" : "approvalInteractive";
|
|
477
|
+
}
|
|
478
|
+
/** Build the initial row list from existing keys (values left blank = keep). */
|
|
479
|
+
function initialRows(keys) {
|
|
480
|
+
return keys.map((key) => ({
|
|
481
|
+
id: ++rowCounter,
|
|
482
|
+
key,
|
|
483
|
+
value: ""
|
|
484
|
+
}));
|
|
485
|
+
}
|
|
486
|
+
/** Key/value row editor with add/remove; blank values on existing keys mean "keep". */
|
|
487
|
+
function MapEditor({ label, rows, setRows, t }) {
|
|
488
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
489
|
+
className: "dmcp-map",
|
|
490
|
+
children: [
|
|
491
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: label }),
|
|
492
|
+
rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
493
|
+
className: "dmcp-status",
|
|
494
|
+
children: t("none")
|
|
495
|
+
}) : null,
|
|
496
|
+
rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
497
|
+
className: "dmcp-map-row",
|
|
498
|
+
children: [
|
|
499
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
500
|
+
type: "text",
|
|
501
|
+
className: "dmcp-map-key",
|
|
502
|
+
"aria-label": t("keyColumn"),
|
|
503
|
+
placeholder: t("keyColumn"),
|
|
504
|
+
value: row.key,
|
|
505
|
+
onChange: (event) => {
|
|
506
|
+
setRows((current) => current.map((entry) => entry.id === row.id ? {
|
|
507
|
+
...entry,
|
|
508
|
+
key: event.currentTarget.value
|
|
509
|
+
} : entry));
|
|
510
|
+
}
|
|
511
|
+
}),
|
|
512
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
513
|
+
type: "text",
|
|
514
|
+
className: "dmcp-map-value",
|
|
515
|
+
"aria-label": t("valueColumn"),
|
|
516
|
+
placeholder: t("unchanged"),
|
|
517
|
+
value: row.value,
|
|
518
|
+
onChange: (event) => {
|
|
519
|
+
setRows((current) => current.map((entry) => entry.id === row.id ? {
|
|
520
|
+
...entry,
|
|
521
|
+
value: event.currentTarget.value
|
|
522
|
+
} : entry));
|
|
523
|
+
}
|
|
524
|
+
}),
|
|
525
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
526
|
+
type: "button",
|
|
527
|
+
className: "dmcp-action",
|
|
528
|
+
"aria-label": t("removeRow"),
|
|
529
|
+
onClick: () => {
|
|
530
|
+
setRows((current) => current.filter((entry) => entry.id !== row.id));
|
|
531
|
+
},
|
|
532
|
+
children: t("removeRow")
|
|
533
|
+
})
|
|
534
|
+
]
|
|
535
|
+
}, row.id)),
|
|
536
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
537
|
+
type: "button",
|
|
538
|
+
className: "dmcp-action",
|
|
539
|
+
onClick: () => {
|
|
540
|
+
setRows((current) => [...current, {
|
|
541
|
+
id: ++rowCounter,
|
|
542
|
+
key: "",
|
|
543
|
+
value: ""
|
|
544
|
+
}]);
|
|
545
|
+
},
|
|
546
|
+
children: t("addRow")
|
|
547
|
+
})
|
|
548
|
+
]
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
/** Message text of an arbitrary thrown value. */
|
|
552
|
+
function messageOf(failure) {
|
|
553
|
+
return failure instanceof Error ? failure.message : String(failure);
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/client/TrialConsole.tsx
|
|
557
|
+
/** Tool trial console: server → registered mcp__* tool → JSON args → official pipeline. */
|
|
558
|
+
/** Render the trial console. */
|
|
559
|
+
function TrialConsole({ t, servers, policy, callTool }) {
|
|
560
|
+
const configured = servers.filter((server) => server.entryId !== "");
|
|
561
|
+
const [serverName, setServerName] = (0, react.useState)("");
|
|
562
|
+
const [toolName, setToolName] = (0, react.useState)("");
|
|
563
|
+
const [argsText, setArgsText] = (0, react.useState)("{}");
|
|
564
|
+
const [running, setRunning] = (0, react.useState)(false);
|
|
565
|
+
const [result, setResult] = (0, react.useState)(null);
|
|
566
|
+
const [error, setError] = (0, react.useState)(null);
|
|
567
|
+
const server = configured.find((candidate) => candidate.serverName === serverName) ?? configured[0];
|
|
568
|
+
(0, react.useEffect)(() => {
|
|
569
|
+
if (configured.length > 0 && serverName === "") setServerName(configured[0]?.serverName ?? "");
|
|
570
|
+
}, [configured, serverName]);
|
|
571
|
+
const tools = server?.tools ?? [];
|
|
572
|
+
(0, react.useEffect)(() => {
|
|
573
|
+
if (tools.length > 0 && !tools.some((tool) => tool.name === toolName)) setToolName(tools[0]?.name ?? "");
|
|
574
|
+
if (tools.length === 0 && toolName !== "") setToolName("");
|
|
575
|
+
}, [tools, toolName]);
|
|
576
|
+
const run = () => {
|
|
577
|
+
const selectedServer = server;
|
|
578
|
+
if (selectedServer === void 0 || toolName === "") return;
|
|
579
|
+
try {
|
|
580
|
+
JSON.parse(argsText.trim() === "" ? "{}" : argsText);
|
|
581
|
+
} catch {
|
|
582
|
+
setError(t("trialArgsInvalid"));
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
setError(null);
|
|
586
|
+
setResult(null);
|
|
587
|
+
setRunning(true);
|
|
588
|
+
Promise.resolve().then(() => callTool(JSON.stringify({
|
|
589
|
+
serverName: selectedServer.serverName,
|
|
590
|
+
toolName,
|
|
591
|
+
argsJson: argsText.trim() === "" ? "{}" : argsText
|
|
592
|
+
}))).then((outcome) => {
|
|
593
|
+
setResult(outcome);
|
|
594
|
+
setRunning(false);
|
|
595
|
+
}, (failure) => {
|
|
596
|
+
setError(failure instanceof Error ? failure.message : String(failure));
|
|
597
|
+
setRunning(false);
|
|
598
|
+
});
|
|
599
|
+
};
|
|
600
|
+
if (!policy.enabled) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
601
|
+
className: "dmcp-trial",
|
|
602
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
603
|
+
className: "dmcp-heading",
|
|
604
|
+
children: t("trial")
|
|
605
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
606
|
+
className: "dmcp-status",
|
|
607
|
+
children: t("trialDisabled")
|
|
608
|
+
})]
|
|
609
|
+
});
|
|
610
|
+
const pretty = result === null ? null : prettyJson(result.resultJson);
|
|
611
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
612
|
+
className: "dmcp-trial",
|
|
613
|
+
children: [
|
|
614
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
615
|
+
className: "dmcp-heading",
|
|
616
|
+
children: t("trial")
|
|
617
|
+
}),
|
|
618
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
619
|
+
className: "dmcp-status",
|
|
620
|
+
children: t("trialHint")
|
|
621
|
+
}),
|
|
622
|
+
configured.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
623
|
+
className: "dmcp-status",
|
|
624
|
+
children: t("empty")
|
|
625
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
626
|
+
className: "dmcp-trial-row",
|
|
627
|
+
children: [
|
|
628
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
629
|
+
className: "dmcp-field",
|
|
630
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trialServer") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
631
|
+
value: server?.serverName ?? "",
|
|
632
|
+
onChange: (event) => {
|
|
633
|
+
setServerName(event.currentTarget.value);
|
|
634
|
+
setToolName("");
|
|
635
|
+
},
|
|
636
|
+
children: configured.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
637
|
+
value: candidate.serverName,
|
|
638
|
+
children: candidate.serverName
|
|
639
|
+
}, candidate.serverName))
|
|
640
|
+
})]
|
|
641
|
+
}),
|
|
642
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
643
|
+
className: "dmcp-field",
|
|
644
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trialTool") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
645
|
+
value: toolName,
|
|
646
|
+
onChange: (event) => {
|
|
647
|
+
setToolName(event.currentTarget.value);
|
|
648
|
+
},
|
|
649
|
+
children: [tools.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
650
|
+
value: "",
|
|
651
|
+
children: t("trialNoTools")
|
|
652
|
+
}) : null, tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
653
|
+
value: tool.name,
|
|
654
|
+
children: tool.name
|
|
655
|
+
}, tool.name))]
|
|
656
|
+
})]
|
|
657
|
+
}),
|
|
658
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
659
|
+
type: "button",
|
|
660
|
+
className: "dmcp-action",
|
|
661
|
+
onClick: run,
|
|
662
|
+
disabled: running || toolName === "",
|
|
663
|
+
children: running ? t("trialRunning") : t("trialRun")
|
|
664
|
+
})
|
|
665
|
+
]
|
|
666
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
667
|
+
className: "dmcp-field",
|
|
668
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trialArgs") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
669
|
+
rows: 4,
|
|
670
|
+
spellCheck: false,
|
|
671
|
+
value: argsText,
|
|
672
|
+
onChange: (event) => {
|
|
673
|
+
setArgsText(event.currentTarget.value);
|
|
674
|
+
}
|
|
675
|
+
})]
|
|
676
|
+
})] }),
|
|
677
|
+
error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
678
|
+
className: "dmcp-error-text",
|
|
679
|
+
role: "alert",
|
|
680
|
+
children: error
|
|
681
|
+
}) : null,
|
|
682
|
+
result !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
683
|
+
className: "dmcp-trial-result",
|
|
684
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
685
|
+
className: "dmcp-trial-meta",
|
|
686
|
+
children: [
|
|
687
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BadgeTone, {
|
|
688
|
+
ok: !result.isError,
|
|
689
|
+
label: result.isError ? "error" : "ok"
|
|
690
|
+
}),
|
|
691
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trialDuration").replace("{ms}", String(result.durationMs)) }),
|
|
692
|
+
result.truncated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
693
|
+
className: "dmcp-warn-text",
|
|
694
|
+
children: t("trialTruncated")
|
|
695
|
+
}) : null,
|
|
696
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
697
|
+
className: "dmcp-trial-call",
|
|
698
|
+
children: result.callId
|
|
699
|
+
})
|
|
700
|
+
]
|
|
701
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
702
|
+
className: "dmcp-fragment dmcp-trial-json",
|
|
703
|
+
children: pretty
|
|
704
|
+
})]
|
|
705
|
+
}) : null,
|
|
706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
707
|
+
className: "dmcp-status",
|
|
708
|
+
children: t("trialApprovalNote")
|
|
709
|
+
})
|
|
710
|
+
]
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
/** One tone-colored badge chip (label is visible text). */
|
|
714
|
+
function BadgeTone({ ok, label }) {
|
|
715
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
716
|
+
className: "dmcp-badge",
|
|
717
|
+
"data-tone": ok ? "ok" : "error",
|
|
718
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
719
|
+
className: "dmcp-dot",
|
|
720
|
+
"aria-hidden": "true"
|
|
721
|
+
}), label]
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
/** Pretty-print one result JSON; unparseable (truncated) text renders raw. */
|
|
725
|
+
function prettyJson(json) {
|
|
726
|
+
try {
|
|
727
|
+
return JSON.stringify(JSON.parse(json), null, 2);
|
|
728
|
+
} catch {
|
|
729
|
+
return json;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
//#endregion
|
|
153
733
|
//#region src/client/McpPanelTab.tsx
|
|
154
|
-
/** The
|
|
734
|
+
/** The MCP management console tab: server cards, CRUD editor, trial console, capabilities, probes. */
|
|
155
735
|
/** Localized label for one server badge code. */
|
|
156
736
|
function badgeLabel(badge, t) {
|
|
157
737
|
switch (badge) {
|
|
@@ -182,8 +762,14 @@ window.__ModuleLoader__.load({
|
|
|
182
762
|
const start = format(view.startedAt);
|
|
183
763
|
return view.finishedAt === null ? start : `${start}–${format(view.finishedAt)}`;
|
|
184
764
|
}
|
|
185
|
-
/**
|
|
186
|
-
function
|
|
765
|
+
/** Localized text for one diagnostic code (fallback: the wire's English text). */
|
|
766
|
+
function diagnosticText(code, text, t) {
|
|
767
|
+
const key = `diag_${code}`;
|
|
768
|
+
const candidate = t(key);
|
|
769
|
+
return candidate === key ? text : candidate;
|
|
770
|
+
}
|
|
771
|
+
/** Render the MCP management console tab. */
|
|
772
|
+
function McpPanelTab({ status, probe, previewPatch, writePatch, callTool, t }) {
|
|
187
773
|
const listId = (0, react.useId)();
|
|
188
774
|
const [request, setRequest] = (0, react.useState)(0);
|
|
189
775
|
const [expanded, setExpanded] = (0, react.useState)({});
|
|
@@ -191,6 +777,9 @@ window.__ModuleLoader__.load({
|
|
|
191
777
|
const [state, setState] = (0, react.useState)({ status: "loading" });
|
|
192
778
|
const [toolQueries, setToolQueries] = (0, react.useState)({});
|
|
193
779
|
const [serverQuery, setServerQuery] = (0, react.useState)("");
|
|
780
|
+
const [editor, setEditor] = (0, react.useState)(null);
|
|
781
|
+
const [removeArm, setRemoveArm] = (0, react.useState)(null);
|
|
782
|
+
const [removeError, setRemoveError] = (0, react.useState)(null);
|
|
194
783
|
const reload = () => {
|
|
195
784
|
setRequest((value) => value + 1);
|
|
196
785
|
};
|
|
@@ -231,7 +820,7 @@ window.__ModuleLoader__.load({
|
|
|
231
820
|
const visibleServers = model !== void 0 ? filterServers(model.servers, serverQuery) : [];
|
|
232
821
|
const summary = model !== void 0 ? summarizePanel(visibleServers) : null;
|
|
233
822
|
const summaryText = summary === null ? "" : t("summary").replace("{total}", String(summary.total)).replace("{connected}", String(summary.connected)).replace("{errored}", String(summary.errored));
|
|
234
|
-
const probeRunning = model !== void 0 && model.probes.some((
|
|
823
|
+
const probeRunning = model !== void 0 && model.probes.some((probeRow) => probeRow.view.status === "running");
|
|
235
824
|
(0, react.useEffect)(() => {
|
|
236
825
|
if (!probeRunning) return void 0;
|
|
237
826
|
const timer = setInterval(() => {
|
|
@@ -328,7 +917,7 @@ window.__ModuleLoader__.load({
|
|
|
328
917
|
const open = expanded[row.view.serverName] === true;
|
|
329
918
|
const detailId = `${listId}-${encodeURIComponent(row.view.serverName)}`;
|
|
330
919
|
const toolQuery = toolQueries[row.view.serverName] ?? "";
|
|
331
|
-
const rowProbeRunning = model.probes.some((
|
|
920
|
+
const rowProbeRunning = model.probes.some((probeRow) => probeRow.view.status === "running" && probeRow.view.serverName === row.view.serverName);
|
|
332
921
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
333
922
|
className: "dmcp-card",
|
|
334
923
|
"data-mcp-server": row.view.serverName,
|
|
@@ -395,9 +984,36 @@ window.__ModuleLoader__.load({
|
|
|
395
984
|
row.view.delayMs,
|
|
396
985
|
" ",
|
|
397
986
|
t("ms")
|
|
398
|
-
] })] }) : null
|
|
987
|
+
] })] }) : null,
|
|
988
|
+
row.view.exitCode !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("exitCode") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: row.view.exitCode })] }) : null,
|
|
989
|
+
row.view.stderrTail !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("stderrTail") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: row.view.stderrTail })] }) : null
|
|
399
990
|
]
|
|
400
991
|
}),
|
|
992
|
+
row.view.diagnostics.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
993
|
+
className: "dmcp-health",
|
|
994
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
995
|
+
className: "dmcp-health-title",
|
|
996
|
+
children: t("healthSuggestions")
|
|
997
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
998
|
+
className: "dmcp-health-list",
|
|
999
|
+
children: row.view.diagnostics.map((diagnostic) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
1000
|
+
diagnosticText(diagnostic.code, diagnostic.text, t),
|
|
1001
|
+
" ",
|
|
1002
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("code", {
|
|
1003
|
+
className: "dmcp-diag-code",
|
|
1004
|
+
children: [
|
|
1005
|
+
"(",
|
|
1006
|
+
diagnostic.code,
|
|
1007
|
+
")"
|
|
1008
|
+
]
|
|
1009
|
+
})
|
|
1010
|
+
] }, diagnostic.code))
|
|
1011
|
+
})]
|
|
1012
|
+
}) : null,
|
|
1013
|
+
row.view.exitCode === null && row.view.stderrTail === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1014
|
+
className: "dmcp-status",
|
|
1015
|
+
children: t("healthPending")
|
|
1016
|
+
}) : null,
|
|
401
1017
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("code", {
|
|
402
1018
|
className: "dmcp-target",
|
|
403
1019
|
title: row.view.target,
|
|
@@ -407,6 +1023,70 @@ window.__ModuleLoader__.load({
|
|
|
407
1023
|
row.view.target
|
|
408
1024
|
]
|
|
409
1025
|
}),
|
|
1026
|
+
row.view.config !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1027
|
+
className: "dmcp-editor-actions",
|
|
1028
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1029
|
+
type: "button",
|
|
1030
|
+
className: "dmcp-action",
|
|
1031
|
+
onClick: () => {
|
|
1032
|
+
setEditor({
|
|
1033
|
+
entryId: row.view.entryId,
|
|
1034
|
+
view: row.view.config
|
|
1035
|
+
});
|
|
1036
|
+
},
|
|
1037
|
+
children: t("editServer")
|
|
1038
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1039
|
+
type: "button",
|
|
1040
|
+
className: "dmcp-action dmcp-danger",
|
|
1041
|
+
onClick: () => {
|
|
1042
|
+
setRemoveArm(row.view.serverName);
|
|
1043
|
+
setRemoveError(null);
|
|
1044
|
+
},
|
|
1045
|
+
children: t("removeServer")
|
|
1046
|
+
})]
|
|
1047
|
+
}) : null,
|
|
1048
|
+
removeArm === row.view.serverName && row.view.entryId !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1049
|
+
className: "dmcp-remove",
|
|
1050
|
+
children: [
|
|
1051
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1052
|
+
className: "dmcp-status",
|
|
1053
|
+
children: t("removeConfirm")
|
|
1054
|
+
}),
|
|
1055
|
+
removeError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1056
|
+
className: "dmcp-error-text",
|
|
1057
|
+
role: "alert",
|
|
1058
|
+
children: removeError
|
|
1059
|
+
}) : null,
|
|
1060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1061
|
+
className: "dmcp-editor-actions",
|
|
1062
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1063
|
+
type: "button",
|
|
1064
|
+
className: "dmcp-action dmcp-danger",
|
|
1065
|
+
onClick: () => {
|
|
1066
|
+
setRemoveError(null);
|
|
1067
|
+
Promise.resolve().then(() => writePatch(JSON.stringify({
|
|
1068
|
+
kind: "disable",
|
|
1069
|
+
entryId: row.view.entryId,
|
|
1070
|
+
serverName: row.view.serverName
|
|
1071
|
+
}), true)).then(() => {
|
|
1072
|
+
setRemoveArm(null);
|
|
1073
|
+
reload();
|
|
1074
|
+
}, (error) => {
|
|
1075
|
+
setRemoveError(error instanceof Error ? error.message : String(error));
|
|
1076
|
+
});
|
|
1077
|
+
},
|
|
1078
|
+
children: t("confirmWrite")
|
|
1079
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1080
|
+
type: "button",
|
|
1081
|
+
className: "dmcp-action",
|
|
1082
|
+
onClick: () => {
|
|
1083
|
+
setRemoveArm(null);
|
|
1084
|
+
},
|
|
1085
|
+
children: t("cancel")
|
|
1086
|
+
})]
|
|
1087
|
+
})
|
|
1088
|
+
]
|
|
1089
|
+
}) : null,
|
|
410
1090
|
row.view.transport === "streamable-http" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
411
1091
|
type: "button",
|
|
412
1092
|
className: "dmcp-probe-now",
|
|
@@ -462,6 +1142,77 @@ window.__ModuleLoader__.load({
|
|
|
462
1142
|
probeError
|
|
463
1143
|
]
|
|
464
1144
|
}) : null,
|
|
1145
|
+
model.patchFile !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1146
|
+
className: "dmcp-patch-hint",
|
|
1147
|
+
children: [
|
|
1148
|
+
t("patchHint"),
|
|
1149
|
+
" ",
|
|
1150
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.patchFile })
|
|
1151
|
+
]
|
|
1152
|
+
}) : null,
|
|
1153
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1154
|
+
className: "dmcp-toolbar",
|
|
1155
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1156
|
+
type: "button",
|
|
1157
|
+
className: "dmcp-action",
|
|
1158
|
+
onClick: () => {
|
|
1159
|
+
setEditor({
|
|
1160
|
+
entryId: "",
|
|
1161
|
+
view: null
|
|
1162
|
+
});
|
|
1163
|
+
},
|
|
1164
|
+
children: t("addServer")
|
|
1165
|
+
})
|
|
1166
|
+
}),
|
|
1167
|
+
editor !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServerEditor, {
|
|
1168
|
+
t,
|
|
1169
|
+
view: editor.view,
|
|
1170
|
+
entryId: editor.entryId,
|
|
1171
|
+
writeEnabled: model.writeEnabled,
|
|
1172
|
+
actions: {
|
|
1173
|
+
previewPatch,
|
|
1174
|
+
writePatch
|
|
1175
|
+
},
|
|
1176
|
+
onClose: () => {
|
|
1177
|
+
setEditor(null);
|
|
1178
|
+
reload();
|
|
1179
|
+
},
|
|
1180
|
+
onWritten: () => {
|
|
1181
|
+
reload();
|
|
1182
|
+
}
|
|
1183
|
+
}) : null,
|
|
1184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrialConsole, {
|
|
1185
|
+
t,
|
|
1186
|
+
servers: model.servers.map((row) => row.view),
|
|
1187
|
+
policy: state.status === "ready" ? state.snapshot.trial : {
|
|
1188
|
+
enabled: false,
|
|
1189
|
+
timeoutMs: 0,
|
|
1190
|
+
maxResultChars: 0
|
|
1191
|
+
},
|
|
1192
|
+
callTool
|
|
1193
|
+
}),
|
|
1194
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1195
|
+
className: "dmcp-capabilities",
|
|
1196
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1197
|
+
className: "dmcp-heading",
|
|
1198
|
+
children: t("capabilities")
|
|
1199
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
1200
|
+
className: "dmcp-capabilities-list",
|
|
1201
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
1202
|
+
tone: model.capabilities.resources.available ? "ok" : "muted",
|
|
1203
|
+
label: t("capResources")
|
|
1204
|
+
}), !model.capabilities.resources.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1205
|
+
className: "dmcp-tool-description",
|
|
1206
|
+
children: t("capPending")
|
|
1207
|
+
}) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
1208
|
+
tone: model.capabilities.prompts.available ? "ok" : "muted",
|
|
1209
|
+
label: t("capPrompts")
|
|
1210
|
+
}), !model.capabilities.prompts.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1211
|
+
className: "dmcp-tool-description",
|
|
1212
|
+
children: t("capPending")
|
|
1213
|
+
}) : null] })]
|
|
1214
|
+
})]
|
|
1215
|
+
}),
|
|
465
1216
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
466
1217
|
className: "dmcp-heading",
|
|
467
1218
|
children: t("probes")
|
|
@@ -471,37 +1222,29 @@ window.__ModuleLoader__.load({
|
|
|
471
1222
|
children: t("probeEmpty")
|
|
472
1223
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
473
1224
|
className: "dmcp-probes",
|
|
474
|
-
children: model.probes.map((
|
|
475
|
-
const badge = probeBadge(
|
|
1225
|
+
children: model.probes.map((probeRow) => {
|
|
1226
|
+
const badge = probeBadge(probeRow.view.status);
|
|
476
1227
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
477
1228
|
className: "dmcp-probe",
|
|
478
|
-
"data-mcp-probe":
|
|
1229
|
+
"data-mcp-probe": probeRow.view.id,
|
|
479
1230
|
children: [
|
|
480
1231
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
481
1232
|
tone: badge.tone,
|
|
482
1233
|
label: probeLabel(badge.badge, t)
|
|
483
1234
|
}),
|
|
484
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children:
|
|
1235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: probeRow.view.serverName }),
|
|
485
1236
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
486
1237
|
className: "dmcp-probe-time",
|
|
487
|
-
children: formatProbeTime(
|
|
1238
|
+
children: formatProbeTime(probeRow.view)
|
|
488
1239
|
}),
|
|
489
1240
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
490
1241
|
className: "dmcp-probe-detail",
|
|
491
|
-
children:
|
|
1242
|
+
children: probeRow.view.detail ?? t("none")
|
|
492
1243
|
})
|
|
493
1244
|
]
|
|
494
|
-
},
|
|
1245
|
+
}, probeRow.view.id);
|
|
495
1246
|
})
|
|
496
|
-
})
|
|
497
|
-
model.patchFile !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
498
|
-
className: "dmcp-patch-hint",
|
|
499
|
-
children: [
|
|
500
|
-
t("patchHint"),
|
|
501
|
-
" ",
|
|
502
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.patchFile })
|
|
503
|
-
]
|
|
504
|
-
}) : null
|
|
1247
|
+
})
|
|
505
1248
|
]
|
|
506
1249
|
}) : null
|
|
507
1250
|
]
|
|
@@ -522,7 +1265,7 @@ window.__ModuleLoader__.load({
|
|
|
522
1265
|
}
|
|
523
1266
|
//#endregion
|
|
524
1267
|
//#region src/client/locales.ts
|
|
525
|
-
/** Copy dictionaries for the MCP management
|
|
1268
|
+
/** Copy dictionaries for the MCP management console tab. */
|
|
526
1269
|
/** Simplified Chinese dictionary and key source of truth. */
|
|
527
1270
|
const zh = {
|
|
528
1271
|
tab: "MCP",
|
|
@@ -557,7 +1300,7 @@ window.__ModuleLoader__.load({
|
|
|
557
1300
|
lastEvent: "最后事件",
|
|
558
1301
|
filterTools: "过滤工具",
|
|
559
1302
|
ms: "ms",
|
|
560
|
-
derivedNote: "连接状态/重连计数来自上游 mcp/status
|
|
1303
|
+
derivedNote: "连接状态/重连计数来自上游 mcp/status 事件;未观察到上游数据时连接字段显示为\"未知\",其余为配置与工具注册表事实。",
|
|
561
1304
|
probes: "连通性探测",
|
|
562
1305
|
probeEmpty: "暂无探测记录。用 mcp_probe 工具发起一次探测。",
|
|
563
1306
|
probeRunning: "运行中",
|
|
@@ -570,7 +1313,85 @@ window.__ModuleLoader__.load({
|
|
|
570
1313
|
probeReachable: "可达",
|
|
571
1314
|
probeUnreachable: "不可达",
|
|
572
1315
|
probeFailedAction: "探测失败",
|
|
573
|
-
patchHint: "
|
|
1316
|
+
patchHint: "本控制台以\"追加操作\"方式写入 profile patch 层(永不改写既有内容),每次写入前自动备份。",
|
|
1317
|
+
health: "健康诊断",
|
|
1318
|
+
healthSuggestions: "自愈建议",
|
|
1319
|
+
healthNone: "未发现已知故障模式——可观测事实一切正常。",
|
|
1320
|
+
healthPending: "进程退出码 / stderr 尾部:待官方支持(官方 client 尚未暴露进程诊断)。",
|
|
1321
|
+
exitCode: "退出码",
|
|
1322
|
+
stderrTail: "stderr 尾部",
|
|
1323
|
+
diag_commandNotFound: "配置的命令不存在——安装对应包或修正可执行文件路径。",
|
|
1324
|
+
diag_commandSpawnFailed: "服务器进程启动失败或已退出——检查命令行、参数与服务器自身日志。",
|
|
1325
|
+
diag_connectionRefused: "服务器拒绝连接——先启动服务器,或检查 URL 中的主机/端口。",
|
|
1326
|
+
diag_connectionDropped: "连接中途断开——服务器可能崩溃或重启。",
|
|
1327
|
+
diag_timeout: "请求超时——服务器可能过载或 URL 错误;调用慢时可调整 toolCallTimeoutMs。",
|
|
1328
|
+
diag_dns: "主机名无法解析——检查 URL 中的域名与网络。",
|
|
1329
|
+
diag_auth401: "服务器拒绝了凭据(HTTP 401)——检查 Authorization 头或 token。",
|
|
1330
|
+
diag_auth403: "服务器拒绝访问(HTTP 403)——凭据有效但缺少权限。",
|
|
1331
|
+
diag_path404: "端点路径返回 404——检查 URL 路径(例如 /mcp)。",
|
|
1332
|
+
diag_rateLimit: "服务器限流——等待,或降低重连/请求频率。",
|
|
1333
|
+
diag_permission: "权限错误——检查文件权限、PATH 与命令是否可执行。",
|
|
1334
|
+
diag_reconnectExhausted: "重连预算已耗尽、客户端放弃——先修复根因,再重载该行(web 面板热重载 cordis.patch.yml)或重启。",
|
|
1335
|
+
diag_reconnectWaiting: "客户端正在退避等待重连——会自动重试;修复根因即可终止循环。",
|
|
1336
|
+
diag_entryFailed: "cordis 行挂载失败——查看 loader 诊断,修复该行后重载。",
|
|
1337
|
+
diagCodeFallback: "建议",
|
|
1338
|
+
addServer: "添加服务器",
|
|
1339
|
+
editServer: "编辑",
|
|
1340
|
+
removeServer: "删除(停用)",
|
|
1341
|
+
removeConfirm: "删除 = 追加 `set disabled: true` 操作(patch 词汇表没有 remove)。行保留在文件中,可随时重新启用。",
|
|
1342
|
+
editorTitleAdd: "添加 MCP 服务器",
|
|
1343
|
+
editorTitleEdit: "编辑 {name}",
|
|
1344
|
+
fieldServerName: "serverName(命名空间)",
|
|
1345
|
+
fieldTransport: "transport",
|
|
1346
|
+
fieldCommand: "command",
|
|
1347
|
+
fieldArgs: "args(每行一个参数)",
|
|
1348
|
+
fieldCwd: "cwd(可选)",
|
|
1349
|
+
fieldUrl: "url",
|
|
1350
|
+
fieldTimeout: "toolCallTimeoutMs(每次调用超时)",
|
|
1351
|
+
fieldFailFast: "failOnStartupError(启动失败即挂载失败)",
|
|
1352
|
+
fieldReconnectEnabled: "自动重连",
|
|
1353
|
+
fieldReconnectMaxAttempts: "reconnect.maxAttempts",
|
|
1354
|
+
fieldEnv: "env(环境变量)",
|
|
1355
|
+
fieldHeaders: "headers(请求头)",
|
|
1356
|
+
keyColumn: "键",
|
|
1357
|
+
valueColumn: "值",
|
|
1358
|
+
unchanged: "(未改动 = 保留原值)",
|
|
1359
|
+
addRow: "添加一行",
|
|
1360
|
+
removeRow: "删除",
|
|
1361
|
+
save: "保存",
|
|
1362
|
+
cancel: "取消",
|
|
1363
|
+
previewPatch: "生成 patch 片段",
|
|
1364
|
+
copyPatch: "复制片段",
|
|
1365
|
+
patchCopied: "已复制到剪贴板。",
|
|
1366
|
+
copyFailed: "复制失败(剪贴板不可用):{message}",
|
|
1367
|
+
patchFor: "将追加到 {file}:",
|
|
1368
|
+
patchFileUnknown: "无法确定 profile patch 文件路径(无 baseUrl)——只能复制片段。",
|
|
1369
|
+
writeToProfile: "写入 profile",
|
|
1370
|
+
confirmWrite: "确认写入",
|
|
1371
|
+
writeRequiresConfirm: "写入需要确认:请核对上面的片段,然后点击\"确认写入\"。",
|
|
1372
|
+
writeDone: "已写入 {file}(备份:{backup})。web 面板热重载 cordis.patch.yml;其他面板重启生效。",
|
|
1373
|
+
writeFailed: "写入失败:{message}",
|
|
1374
|
+
writeDisabled: "写入已被禁用(config.writeEnabled: false)——只能复制片段。",
|
|
1375
|
+
approvalHarness: "审批通道:ctx.approval",
|
|
1376
|
+
approvalInteractive: "审批通道:界面确认",
|
|
1377
|
+
trial: "工具试用台",
|
|
1378
|
+
trialHint: "通过官方工具管线调用(权限与审批生效);结果只显示在本页,不会进入模型上下文。",
|
|
1379
|
+
trialServer: "服务器",
|
|
1380
|
+
trialTool: "工具",
|
|
1381
|
+
trialArgs: "参数(JSON)",
|
|
1382
|
+
trialRun: "调用",
|
|
1383
|
+
trialRunning: "调用中…",
|
|
1384
|
+
trialResult: "结果(规范 JSON 与 render)",
|
|
1385
|
+
trialDuration: "耗时 {ms} ms",
|
|
1386
|
+
trialTruncated: "结果超过显示上限,已截断。",
|
|
1387
|
+
trialApprovalNote: "审批类调用需要当前会话处于开启轮次;否则 fail-closed 拒绝。",
|
|
1388
|
+
trialDisabled: "试用台已被禁用(config.trialEnabled: false)。",
|
|
1389
|
+
trialNoTools: "该服务器没有已注册工具——服务器可能未连接或同步失败。",
|
|
1390
|
+
trialArgsInvalid: "参数必须是合法 JSON。",
|
|
1391
|
+
capabilities: "能力一览",
|
|
1392
|
+
capResources: "Resources",
|
|
1393
|
+
capPrompts: "Prompts",
|
|
1394
|
+
capPending: "待官方支持——官方 client 尚未桥接该能力(Tools 是当前唯一桥接的 MCP 能力)。"
|
|
574
1395
|
};
|
|
575
1396
|
/** English dictionary checked against the Chinese key set. */
|
|
576
1397
|
const en = {
|
|
@@ -606,7 +1427,7 @@ window.__ModuleLoader__.load({
|
|
|
606
1427
|
lastEvent: "Last event",
|
|
607
1428
|
filterTools: "Filter tools",
|
|
608
1429
|
ms: "ms",
|
|
609
|
-
derivedNote: "Connection status and reconnect counts come from the
|
|
1430
|
+
derivedNote: "Connection status and reconnect counts come from the upstream mcp/status seam; without observed upstream data, connection fields read \"unknown\" and the rest are config and tool-registry facts.",
|
|
610
1431
|
probes: "Connectivity probes",
|
|
611
1432
|
probeEmpty: "No probes yet. Start one with the mcp_probe tool.",
|
|
612
1433
|
probeRunning: "Running",
|
|
@@ -619,7 +1440,85 @@ window.__ModuleLoader__.load({
|
|
|
619
1440
|
probeReachable: "Reachable",
|
|
620
1441
|
probeUnreachable: "Unreachable",
|
|
621
1442
|
probeFailedAction: "Probe failed",
|
|
622
|
-
patchHint: "
|
|
1443
|
+
patchHint: "This console writes the profile patch layer by APPENDING operations (existing content is never rewritten), and backs up the file before every write.",
|
|
1444
|
+
health: "Health diagnostics",
|
|
1445
|
+
healthSuggestions: "Self-heal suggestions",
|
|
1446
|
+
healthNone: "No known failure pattern detected from the observable facts.",
|
|
1447
|
+
healthPending: "Exit code / stderr tail: pending upstream support (the official client does not expose process diagnostics yet).",
|
|
1448
|
+
exitCode: "Exit code",
|
|
1449
|
+
stderrTail: "stderr tail",
|
|
1450
|
+
diag_commandNotFound: "The configured command was not found — install the package or fix the executable path.",
|
|
1451
|
+
diag_commandSpawnFailed: "The server process failed to start or exited — check the command line, its arguments, and the server's own logs.",
|
|
1452
|
+
diag_connectionRefused: "The server refused the connection — start the server or check the host/port in the URL.",
|
|
1453
|
+
diag_connectionDropped: "The connection dropped mid-session — the server may have crashed or restarted.",
|
|
1454
|
+
diag_timeout: "A request timed out — the server may be overloaded, or the URL may be wrong. Check toolCallTimeoutMs if calls are slow.",
|
|
1455
|
+
diag_dns: "The server hostname did not resolve — check the DNS name in the URL and the network.",
|
|
1456
|
+
diag_auth401: "The server rejected the credentials (HTTP 401) — check the Authorization header or token.",
|
|
1457
|
+
diag_auth403: "The server denied access (HTTP 403) — the credential is valid but lacks permission.",
|
|
1458
|
+
diag_path404: "The endpoint path returned 404 — check the URL path (e.g. /mcp).",
|
|
1459
|
+
diag_rateLimit: "The server rate-limited the client — wait, or reduce the reconnect/request rate.",
|
|
1460
|
+
diag_permission: "A permission error occurred — check file permissions, PATH access, and whether the command is executable.",
|
|
1461
|
+
diag_reconnectExhausted: "The reconnect budget is exhausted and the client has given up — fix the root cause, then reload the row (the web surface hot-reloads cordis.patch.yml edits) or restart.",
|
|
1462
|
+
diag_reconnectWaiting: "The client is waiting in reconnect backoff — it will retry automatically; fix the root cause to stop the cycle.",
|
|
1463
|
+
diag_entryFailed: "The cordis row failed to mount — read the loader diagnostics, then fix the row and reload.",
|
|
1464
|
+
diagCodeFallback: "Suggestion",
|
|
1465
|
+
addServer: "Add server",
|
|
1466
|
+
editServer: "Edit",
|
|
1467
|
+
removeServer: "Remove (disable)",
|
|
1468
|
+
removeConfirm: "Removal = appending a `set disabled: true` operation (the patch vocabulary has no remove). The row stays in the file and can be re-enabled anytime.",
|
|
1469
|
+
editorTitleAdd: "Add MCP server",
|
|
1470
|
+
editorTitleEdit: "Edit {name}",
|
|
1471
|
+
fieldServerName: "serverName (namespace)",
|
|
1472
|
+
fieldTransport: "transport",
|
|
1473
|
+
fieldCommand: "command",
|
|
1474
|
+
fieldArgs: "args (one argument per line)",
|
|
1475
|
+
fieldCwd: "cwd (optional)",
|
|
1476
|
+
fieldUrl: "url",
|
|
1477
|
+
fieldTimeout: "toolCallTimeoutMs (per-call timeout)",
|
|
1478
|
+
fieldFailFast: "failOnStartupError (fail the mount on startup errors)",
|
|
1479
|
+
fieldReconnectEnabled: "Auto reconnect",
|
|
1480
|
+
fieldReconnectMaxAttempts: "reconnect.maxAttempts",
|
|
1481
|
+
fieldEnv: "env (environment variables)",
|
|
1482
|
+
fieldHeaders: "headers (request headers)",
|
|
1483
|
+
keyColumn: "Key",
|
|
1484
|
+
valueColumn: "Value",
|
|
1485
|
+
unchanged: "(unchanged = keep the original value)",
|
|
1486
|
+
addRow: "Add a row",
|
|
1487
|
+
removeRow: "Remove",
|
|
1488
|
+
save: "Save",
|
|
1489
|
+
cancel: "Cancel",
|
|
1490
|
+
previewPatch: "Generate patch fragment",
|
|
1491
|
+
copyPatch: "Copy fragment",
|
|
1492
|
+
patchCopied: "Copied to the clipboard.",
|
|
1493
|
+
copyFailed: "Copy failed (clipboard unavailable): {message}",
|
|
1494
|
+
patchFor: "Will be appended to {file}:",
|
|
1495
|
+
patchFileUnknown: "The profile patch path is unknown (no baseUrl) — the fragment can only be copied.",
|
|
1496
|
+
writeToProfile: "Write to profile",
|
|
1497
|
+
confirmWrite: "Confirm write",
|
|
1498
|
+
writeRequiresConfirm: "Writing needs confirmation: review the fragment above, then press \"Confirm write\".",
|
|
1499
|
+
writeDone: "Written to {file} (backup: {backup}). The web surface hot-reloads cordis.patch.yml edits; other surfaces restart.",
|
|
1500
|
+
writeFailed: "Write failed: {message}",
|
|
1501
|
+
writeDisabled: "Writes are disabled (config.writeEnabled: false) — the fragment can only be copied.",
|
|
1502
|
+
approvalHarness: "Approval channel: ctx.approval",
|
|
1503
|
+
approvalInteractive: "Approval channel: interactive confirmation",
|
|
1504
|
+
trial: "Tool trial console",
|
|
1505
|
+
trialHint: "Calls run through the official tool pipeline (permission policy and approval stay in force); results are shown only here and never enter model context.",
|
|
1506
|
+
trialServer: "Server",
|
|
1507
|
+
trialTool: "Tool",
|
|
1508
|
+
trialArgs: "Arguments (JSON)",
|
|
1509
|
+
trialRun: "Call",
|
|
1510
|
+
trialRunning: "Calling…",
|
|
1511
|
+
trialResult: "Result (canonical JSON and render)",
|
|
1512
|
+
trialDuration: "{ms} ms",
|
|
1513
|
+
trialTruncated: "The result exceeded the display cap and was truncated.",
|
|
1514
|
+
trialApprovalNote: "Approval-style calls need the current session to be inside an open turn; otherwise they fail closed.",
|
|
1515
|
+
trialDisabled: "The trial console is disabled (config.trialEnabled: false).",
|
|
1516
|
+
trialNoTools: "This server has no registered tools — it may be disconnected or its sync failed.",
|
|
1517
|
+
trialArgsInvalid: "Arguments must be valid JSON.",
|
|
1518
|
+
capabilities: "Capabilities",
|
|
1519
|
+
capResources: "Resources",
|
|
1520
|
+
capPrompts: "Prompts",
|
|
1521
|
+
capPending: "Pending upstream support — the official client does not bridge this capability yet (tools are the only bridged MCP capability today)."
|
|
623
1522
|
};
|
|
624
1523
|
//#endregion
|
|
625
1524
|
//#region node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
@@ -4714,12 +5613,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4714
5613
|
//#endregion
|
|
4715
5614
|
//#region src/wire.ts
|
|
4716
5615
|
/**
|
|
4717
|
-
* The
|
|
4718
|
-
* Remote namespace, their zod v4 validation schema (the strict
|
|
4719
|
-
* Typert faces carry), and the
|
|
4720
|
-
* host `./typert` manifest and the
|
|
4721
|
-
*
|
|
4722
|
-
* apart.
|
|
5616
|
+
* The console's wire vocabulary: the snapshot types served over the
|
|
5617
|
+
* `mcpPanel` Remote namespace, their zod v4 validation schema (the strict
|
|
5618
|
+
* codec both Typert faces carry), and the invocation descriptors shared
|
|
5619
|
+
* verbatim by the host `./typert` manifest (`src/typert.host.ts`) and the
|
|
5620
|
+
* client Remote contribution (`src/client/remote.ts`). One canonical source
|
|
5621
|
+
* for both faces keeps the host and client codecs from ever drifting apart.
|
|
5622
|
+
*
|
|
5623
|
+
* Values that could be large or heterogeneous (trial results) cross the wire
|
|
5624
|
+
* as single bounded JSON strings; every scalar here has an explicit shape.
|
|
4723
5625
|
*
|
|
4724
5626
|
* @module dsh-mcp-panel/wire
|
|
4725
5627
|
*/
|
|
@@ -4773,7 +5675,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4773
5675
|
_null()
|
|
4774
5676
|
]),
|
|
4775
5677
|
probeCheckedAt: number().int().nullable(),
|
|
4776
|
-
statusSource: union([literal("upstream-event"), literal("derived")])
|
|
5678
|
+
statusSource: union([literal("upstream-event"), literal("derived")]),
|
|
5679
|
+
config: object({
|
|
5680
|
+
serverName: string(),
|
|
5681
|
+
transport: union([
|
|
5682
|
+
literal("stdio"),
|
|
5683
|
+
literal("streamable-http"),
|
|
5684
|
+
literal("unknown")
|
|
5685
|
+
]),
|
|
5686
|
+
command: string().nullable(),
|
|
5687
|
+
args: array(string()),
|
|
5688
|
+
cwd: string().nullable(),
|
|
5689
|
+
url: string().nullable(),
|
|
5690
|
+
envKeys: array(string()),
|
|
5691
|
+
headerKeys: array(string()),
|
|
5692
|
+
toolCallTimeoutMs: number().int().nullable(),
|
|
5693
|
+
failOnStartupError: boolean().nullable(),
|
|
5694
|
+
reconnectEnabled: boolean().nullable(),
|
|
5695
|
+
reconnectMaxAttempts: number().int().nullable()
|
|
5696
|
+
}).nullable(),
|
|
5697
|
+
diagnostics: array(object({
|
|
5698
|
+
code: string(),
|
|
5699
|
+
text: string()
|
|
5700
|
+
})),
|
|
5701
|
+
exitCode: number().int().nullable(),
|
|
5702
|
+
stderrTail: string().nullable()
|
|
4777
5703
|
})),
|
|
4778
5704
|
probes: array(object({
|
|
4779
5705
|
id: string(),
|
|
@@ -4789,7 +5715,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4789
5715
|
startedAt: number().int(),
|
|
4790
5716
|
finishedAt: number().int().nullable(),
|
|
4791
5717
|
detail: string().nullable()
|
|
4792
|
-
}))
|
|
5718
|
+
})),
|
|
5719
|
+
capabilities: object({
|
|
5720
|
+
resources: object({ available: boolean() }),
|
|
5721
|
+
prompts: object({ available: boolean() })
|
|
5722
|
+
}),
|
|
5723
|
+
trial: object({
|
|
5724
|
+
enabled: boolean(),
|
|
5725
|
+
timeoutMs: number().int(),
|
|
5726
|
+
maxResultChars: number().int()
|
|
5727
|
+
}),
|
|
5728
|
+
writeEnabled: boolean()
|
|
4793
5729
|
});
|
|
4794
5730
|
/**
|
|
4795
5731
|
* The `mcpPanel/status` invocation descriptor, shared verbatim by the host
|
|
@@ -4852,12 +5788,158 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4852
5788
|
column: 1
|
|
4853
5789
|
})
|
|
4854
5790
|
});
|
|
5791
|
+
/** Strict wire schema for {@link PatchPreview}. */
|
|
5792
|
+
const PATCH_PREVIEW_SCHEMA = object({
|
|
5793
|
+
fragment: string(),
|
|
5794
|
+
file: string().nullable(),
|
|
5795
|
+
ops: number().int()
|
|
5796
|
+
});
|
|
5797
|
+
/** The `mcpPanel/previewPatch` invocation descriptor: render one CRUD op. */
|
|
5798
|
+
const MCP_PANEL_PREVIEW_DESCRIPTOR = Object.freeze({
|
|
5799
|
+
id: "dsh-mcp-panel#mcpPanel/previewPatch",
|
|
5800
|
+
service: "mcpPanel",
|
|
5801
|
+
namespace: "mcpPanel",
|
|
5802
|
+
method: "previewPatch",
|
|
5803
|
+
invocation: Object.freeze({ kind: "direct" }),
|
|
5804
|
+
parameters: Object.freeze([Object.freeze({
|
|
5805
|
+
name: "opJson",
|
|
5806
|
+
wire: "opJson",
|
|
5807
|
+
source: "json",
|
|
5808
|
+
codec: Object.freeze({
|
|
5809
|
+
mode: "strict",
|
|
5810
|
+
typeSymbol: "dsh-mcp-panel/types#PatchOpJson",
|
|
5811
|
+
schema: string()
|
|
5812
|
+
})
|
|
5813
|
+
})]),
|
|
5814
|
+
result: Object.freeze({
|
|
5815
|
+
mode: "strict",
|
|
5816
|
+
typeSymbol: "dsh-mcp-panel/types#PatchPreview",
|
|
5817
|
+
schema: PATCH_PREVIEW_SCHEMA
|
|
5818
|
+
}),
|
|
5819
|
+
sourceLocation: Object.freeze({
|
|
5820
|
+
file: "src/wire.ts",
|
|
5821
|
+
line: 1,
|
|
5822
|
+
column: 1
|
|
5823
|
+
})
|
|
5824
|
+
});
|
|
5825
|
+
/** Strict wire schema for {@link PatchWriteResult}. */
|
|
5826
|
+
const PATCH_WRITE_RESULT_SCHEMA = object({
|
|
5827
|
+
file: string(),
|
|
5828
|
+
backupPath: string(),
|
|
5829
|
+
approvalPath: union([literal("harness-approval"), literal("interactive-confirmation")]),
|
|
5830
|
+
bytes: number().int(),
|
|
5831
|
+
ops: number().int(),
|
|
5832
|
+
note: string()
|
|
5833
|
+
});
|
|
5834
|
+
/** The `mcpPanel/writePatch` invocation descriptor: approval-gated append. */
|
|
5835
|
+
const MCP_PANEL_WRITE_DESCRIPTOR = Object.freeze({
|
|
5836
|
+
id: "dsh-mcp-panel#mcpPanel/writePatch",
|
|
5837
|
+
service: "mcpPanel",
|
|
5838
|
+
namespace: "mcpPanel",
|
|
5839
|
+
method: "writePatch",
|
|
5840
|
+
invocation: Object.freeze({ kind: "direct" }),
|
|
5841
|
+
parameters: Object.freeze([
|
|
5842
|
+
Object.freeze({
|
|
5843
|
+
name: "opJson",
|
|
5844
|
+
wire: "opJson",
|
|
5845
|
+
source: "json",
|
|
5846
|
+
codec: Object.freeze({
|
|
5847
|
+
mode: "strict",
|
|
5848
|
+
typeSymbol: "dsh-mcp-panel/types#PatchOpJson",
|
|
5849
|
+
schema: string()
|
|
5850
|
+
})
|
|
5851
|
+
}),
|
|
5852
|
+
Object.freeze({
|
|
5853
|
+
name: "confirmed",
|
|
5854
|
+
wire: "confirmed",
|
|
5855
|
+
source: "json",
|
|
5856
|
+
codec: Object.freeze({
|
|
5857
|
+
mode: "strict",
|
|
5858
|
+
typeSymbol: "dsh-mcp-panel/types#PatchWriteConfirmed",
|
|
5859
|
+
schema: boolean()
|
|
5860
|
+
})
|
|
5861
|
+
}),
|
|
5862
|
+
Object.freeze({
|
|
5863
|
+
name: "sessionId",
|
|
5864
|
+
wire: "sessionId",
|
|
5865
|
+
source: "json",
|
|
5866
|
+
codec: Object.freeze({
|
|
5867
|
+
mode: "strict",
|
|
5868
|
+
typeSymbol: "dsh-mcp-panel/types#PatchWriteSessionId",
|
|
5869
|
+
schema: string()
|
|
5870
|
+
}),
|
|
5871
|
+
acceptsUndefined: true
|
|
5872
|
+
})
|
|
5873
|
+
]),
|
|
5874
|
+
result: Object.freeze({
|
|
5875
|
+
mode: "strict",
|
|
5876
|
+
typeSymbol: "dsh-mcp-panel/types#PatchWriteResult",
|
|
5877
|
+
schema: PATCH_WRITE_RESULT_SCHEMA
|
|
5878
|
+
}),
|
|
5879
|
+
sourceLocation: Object.freeze({
|
|
5880
|
+
file: "src/wire.ts",
|
|
5881
|
+
line: 1,
|
|
5882
|
+
column: 1
|
|
5883
|
+
})
|
|
5884
|
+
});
|
|
5885
|
+
/** Strict wire schema for {@link McpTrialResultWire}. */
|
|
5886
|
+
const MCP_TRIAL_RESULT_SCHEMA = object({
|
|
5887
|
+
callId: string(),
|
|
5888
|
+
isError: boolean(),
|
|
5889
|
+
truncated: boolean(),
|
|
5890
|
+
durationMs: number().int(),
|
|
5891
|
+
resultJson: string()
|
|
5892
|
+
});
|
|
5893
|
+
/** The `mcpPanel/callTool` invocation descriptor: official-pipeline trial call. */
|
|
5894
|
+
const MCP_PANEL_CALLTOOL_DESCRIPTOR = Object.freeze({
|
|
5895
|
+
id: "dsh-mcp-panel#mcpPanel/callTool",
|
|
5896
|
+
service: "mcpPanel",
|
|
5897
|
+
namespace: "mcpPanel",
|
|
5898
|
+
method: "callTool",
|
|
5899
|
+
invocation: Object.freeze({ kind: "direct" }),
|
|
5900
|
+
parameters: Object.freeze([Object.freeze({
|
|
5901
|
+
name: "requestJson",
|
|
5902
|
+
wire: "requestJson",
|
|
5903
|
+
source: "json",
|
|
5904
|
+
codec: Object.freeze({
|
|
5905
|
+
mode: "strict",
|
|
5906
|
+
typeSymbol: "dsh-mcp-panel/types#TrialRequestJson",
|
|
5907
|
+
schema: string()
|
|
5908
|
+
})
|
|
5909
|
+
}), Object.freeze({
|
|
5910
|
+
name: "sessionId",
|
|
5911
|
+
wire: "sessionId",
|
|
5912
|
+
source: "json",
|
|
5913
|
+
codec: Object.freeze({
|
|
5914
|
+
mode: "strict",
|
|
5915
|
+
typeSymbol: "dsh-mcp-panel/types#TrialSessionId",
|
|
5916
|
+
schema: string()
|
|
5917
|
+
}),
|
|
5918
|
+
acceptsUndefined: true
|
|
5919
|
+
})]),
|
|
5920
|
+
result: Object.freeze({
|
|
5921
|
+
mode: "strict",
|
|
5922
|
+
typeSymbol: "dsh-mcp-panel/types#McpTrialResultWire",
|
|
5923
|
+
schema: MCP_TRIAL_RESULT_SCHEMA
|
|
5924
|
+
}),
|
|
5925
|
+
sourceLocation: Object.freeze({
|
|
5926
|
+
file: "src/wire.ts",
|
|
5927
|
+
line: 1,
|
|
5928
|
+
column: 1
|
|
5929
|
+
})
|
|
5930
|
+
});
|
|
4855
5931
|
/**
|
|
4856
5932
|
* The canonical invocation list both Typert faces register — the host
|
|
4857
5933
|
* manifest and the client contribution share these exact descriptor objects,
|
|
4858
5934
|
* so the two wire codecs can never drift apart.
|
|
4859
5935
|
*/
|
|
4860
|
-
const MCP_PANEL_INVOCATIONS = Object.freeze([
|
|
5936
|
+
const MCP_PANEL_INVOCATIONS = Object.freeze([
|
|
5937
|
+
MCP_PANEL_STATUS_DESCRIPTOR,
|
|
5938
|
+
MCP_PANEL_PROBE_DESCRIPTOR,
|
|
5939
|
+
MCP_PANEL_PREVIEW_DESCRIPTOR,
|
|
5940
|
+
MCP_PANEL_WRITE_DESCRIPTOR,
|
|
5941
|
+
MCP_PANEL_CALLTOOL_DESCRIPTOR
|
|
5942
|
+
]);
|
|
4861
5943
|
//#endregion
|
|
4862
5944
|
//#region src/client/remote.ts
|
|
4863
5945
|
/** The client Remote contribution for the `mcpPanel` namespace. */
|
|
@@ -5132,6 +6214,207 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5132
6214
|
border-radius: 50%;
|
|
5133
6215
|
background: currentColor;
|
|
5134
6216
|
}
|
|
6217
|
+
[data-dsh-mcp-panel] .dmcp-editor {
|
|
6218
|
+
display: flex;
|
|
6219
|
+
flex-direction: column;
|
|
6220
|
+
gap: 10px;
|
|
6221
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
6222
|
+
border-radius: 6px;
|
|
6223
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
6224
|
+
padding: 12px;
|
|
6225
|
+
}
|
|
6226
|
+
[data-dsh-mcp-panel] .dmcp-form {
|
|
6227
|
+
display: flex;
|
|
6228
|
+
flex-direction: column;
|
|
6229
|
+
gap: 8px;
|
|
6230
|
+
}
|
|
6231
|
+
[data-dsh-mcp-panel] .dmcp-field {
|
|
6232
|
+
display: flex;
|
|
6233
|
+
flex-direction: column;
|
|
6234
|
+
gap: 4px;
|
|
6235
|
+
font-size: 0.9em;
|
|
6236
|
+
color: var(--dsw-alias-label-secondary);
|
|
6237
|
+
}
|
|
6238
|
+
[data-dsh-mcp-panel] .dmcp-field input,
|
|
6239
|
+
[data-dsh-mcp-panel] .dmcp-field select,
|
|
6240
|
+
[data-dsh-mcp-panel] .dmcp-field textarea {
|
|
6241
|
+
font: inherit;
|
|
6242
|
+
color: var(--dsw-alias-label-primary);
|
|
6243
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
6244
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
6245
|
+
border-radius: 4px;
|
|
6246
|
+
padding: 4px 8px;
|
|
6247
|
+
}
|
|
6248
|
+
[data-dsh-mcp-panel] .dmcp-check {
|
|
6249
|
+
display: flex;
|
|
6250
|
+
align-items: center;
|
|
6251
|
+
gap: 8px;
|
|
6252
|
+
font-size: 0.9em;
|
|
6253
|
+
color: var(--dsw-alias-label-secondary);
|
|
6254
|
+
}
|
|
6255
|
+
[data-dsh-mcp-panel] .dmcp-map {
|
|
6256
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
6257
|
+
border-radius: 4px;
|
|
6258
|
+
padding: 8px;
|
|
6259
|
+
display: flex;
|
|
6260
|
+
flex-direction: column;
|
|
6261
|
+
gap: 6px;
|
|
6262
|
+
margin: 0;
|
|
6263
|
+
}
|
|
6264
|
+
[data-dsh-mcp-panel] .dmcp-map legend {
|
|
6265
|
+
font-size: 0.9em;
|
|
6266
|
+
color: var(--dsw-alias-label-secondary);
|
|
6267
|
+
padding: 0 4px;
|
|
6268
|
+
}
|
|
6269
|
+
[data-dsh-mcp-panel] .dmcp-map-row {
|
|
6270
|
+
display: flex;
|
|
6271
|
+
gap: 6px;
|
|
6272
|
+
}
|
|
6273
|
+
[data-dsh-mcp-panel] .dmcp-map-row input {
|
|
6274
|
+
font: inherit;
|
|
6275
|
+
color: var(--dsw-alias-label-primary);
|
|
6276
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
6277
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
6278
|
+
border-radius: 4px;
|
|
6279
|
+
padding: 4px 8px;
|
|
6280
|
+
min-width: 0;
|
|
6281
|
+
}
|
|
6282
|
+
[data-dsh-mcp-panel] .dmcp-map-key {
|
|
6283
|
+
flex: 1 1 40%;
|
|
6284
|
+
}
|
|
6285
|
+
[data-dsh-mcp-panel] .dmcp-map-value {
|
|
6286
|
+
flex: 2 1 60%;
|
|
6287
|
+
}
|
|
6288
|
+
[data-dsh-mcp-panel] .dmcp-editor-actions {
|
|
6289
|
+
display: flex;
|
|
6290
|
+
gap: 8px;
|
|
6291
|
+
flex-wrap: wrap;
|
|
6292
|
+
}
|
|
6293
|
+
[data-dsh-mcp-panel] .dmcp-action:disabled {
|
|
6294
|
+
opacity: 0.6;
|
|
6295
|
+
cursor: default;
|
|
6296
|
+
}
|
|
6297
|
+
[data-dsh-mcp-panel] .dmcp-danger {
|
|
6298
|
+
color: var(--dsw-alias-state-error-primary);
|
|
6299
|
+
border-color: currentColor;
|
|
6300
|
+
}
|
|
6301
|
+
[data-dsh-mcp-panel] .dmcp-confirm {
|
|
6302
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
6303
|
+
border-color: currentColor;
|
|
6304
|
+
}
|
|
6305
|
+
[data-dsh-mcp-panel] .dmcp-patch {
|
|
6306
|
+
display: flex;
|
|
6307
|
+
flex-direction: column;
|
|
6308
|
+
gap: 8px;
|
|
6309
|
+
}
|
|
6310
|
+
[data-dsh-mcp-panel] .dmcp-fragment {
|
|
6311
|
+
margin: 0;
|
|
6312
|
+
padding: 8px;
|
|
6313
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
6314
|
+
border-radius: 4px;
|
|
6315
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
6316
|
+
color: var(--dsw-alias-label-primary);
|
|
6317
|
+
overflow: auto;
|
|
6318
|
+
max-height: 240px;
|
|
6319
|
+
white-space: pre-wrap;
|
|
6320
|
+
overflow-wrap: anywhere;
|
|
6321
|
+
font-family: var(--dsw-alias-font-mono, monospace);
|
|
6322
|
+
font-size: 0.85em;
|
|
6323
|
+
}
|
|
6324
|
+
[data-dsh-mcp-panel] .dmcp-notice {
|
|
6325
|
+
margin: 0;
|
|
6326
|
+
color: var(--dsw-alias-label-secondary);
|
|
6327
|
+
overflow-wrap: anywhere;
|
|
6328
|
+
}
|
|
6329
|
+
[data-dsh-mcp-panel] .dmcp-warn-text {
|
|
6330
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
6331
|
+
}
|
|
6332
|
+
[data-dsh-mcp-panel] .dmcp-health {
|
|
6333
|
+
display: flex;
|
|
6334
|
+
flex-direction: column;
|
|
6335
|
+
gap: 4px;
|
|
6336
|
+
}
|
|
6337
|
+
[data-dsh-mcp-panel] .dmcp-health-title {
|
|
6338
|
+
margin: 0;
|
|
6339
|
+
color: var(--dsw-alias-label-secondary);
|
|
6340
|
+
}
|
|
6341
|
+
[data-dsh-mcp-panel] .dmcp-health-list {
|
|
6342
|
+
list-style: disc;
|
|
6343
|
+
margin: 0;
|
|
6344
|
+
padding-left: 20px;
|
|
6345
|
+
display: flex;
|
|
6346
|
+
flex-direction: column;
|
|
6347
|
+
gap: 2px;
|
|
6348
|
+
color: var(--dsw-alias-label-primary);
|
|
6349
|
+
}
|
|
6350
|
+
[data-dsh-mcp-panel] .dmcp-diag-code {
|
|
6351
|
+
color: var(--dsw-alias-label-tertiary);
|
|
6352
|
+
}
|
|
6353
|
+
[data-dsh-mcp-panel] .dmcp-remove {
|
|
6354
|
+
display: flex;
|
|
6355
|
+
flex-direction: column;
|
|
6356
|
+
gap: 6px;
|
|
6357
|
+
border: 1px solid var(--dsw-alias-state-error-primary);
|
|
6358
|
+
border-radius: 4px;
|
|
6359
|
+
padding: 8px;
|
|
6360
|
+
}
|
|
6361
|
+
[data-dsh-mcp-panel] .dmcp-trial {
|
|
6362
|
+
display: flex;
|
|
6363
|
+
flex-direction: column;
|
|
6364
|
+
gap: 8px;
|
|
6365
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
6366
|
+
padding-top: 12px;
|
|
6367
|
+
}
|
|
6368
|
+
[data-dsh-mcp-panel] .dmcp-trial-row {
|
|
6369
|
+
display: flex;
|
|
6370
|
+
gap: 8px;
|
|
6371
|
+
flex-wrap: wrap;
|
|
6372
|
+
align-items: flex-end;
|
|
6373
|
+
}
|
|
6374
|
+
[data-dsh-mcp-panel] .dmcp-trial-row .dmcp-field {
|
|
6375
|
+
flex: 1 1 180px;
|
|
6376
|
+
min-width: 0;
|
|
6377
|
+
}
|
|
6378
|
+
[data-dsh-mcp-panel] .dmcp-trial-result {
|
|
6379
|
+
display: flex;
|
|
6380
|
+
flex-direction: column;
|
|
6381
|
+
gap: 6px;
|
|
6382
|
+
}
|
|
6383
|
+
[data-dsh-mcp-panel] .dmcp-trial-meta {
|
|
6384
|
+
display: flex;
|
|
6385
|
+
align-items: center;
|
|
6386
|
+
gap: 10px;
|
|
6387
|
+
flex-wrap: wrap;
|
|
6388
|
+
margin: 0;
|
|
6389
|
+
color: var(--dsw-alias-label-secondary);
|
|
6390
|
+
}
|
|
6391
|
+
[data-dsh-mcp-panel] .dmcp-trial-call {
|
|
6392
|
+
color: var(--dsw-alias-label-tertiary);
|
|
6393
|
+
font-family: var(--dsw-alias-font-mono, monospace);
|
|
6394
|
+
font-size: 0.85em;
|
|
6395
|
+
}
|
|
6396
|
+
[data-dsh-mcp-panel] .dmcp-trial-json {
|
|
6397
|
+
max-height: 320px;
|
|
6398
|
+
}
|
|
6399
|
+
[data-dsh-mcp-panel] .dmcp-capabilities {
|
|
6400
|
+
display: flex;
|
|
6401
|
+
flex-direction: column;
|
|
6402
|
+
gap: 6px;
|
|
6403
|
+
}
|
|
6404
|
+
[data-dsh-mcp-panel] .dmcp-capabilities-list {
|
|
6405
|
+
list-style: none;
|
|
6406
|
+
margin: 0;
|
|
6407
|
+
padding: 0;
|
|
6408
|
+
display: flex;
|
|
6409
|
+
flex-direction: column;
|
|
6410
|
+
gap: 6px;
|
|
6411
|
+
}
|
|
6412
|
+
[data-dsh-mcp-panel] .dmcp-capabilities-list li {
|
|
6413
|
+
display: flex;
|
|
6414
|
+
align-items: baseline;
|
|
6415
|
+
gap: 8px;
|
|
6416
|
+
flex-wrap: wrap;
|
|
6417
|
+
}
|
|
5135
6418
|
`;
|
|
5136
6419
|
//#endregion
|
|
5137
6420
|
//#region src/client/index.ts
|
|
@@ -5139,11 +6422,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5139
6422
|
const NS = "settings.mcpPanel";
|
|
5140
6423
|
/** Plugin name: matches the package name, the graph row id, and the bundle id. */
|
|
5141
6424
|
const name = "dsh-mcp-panel";
|
|
5142
|
-
/** Services the
|
|
6425
|
+
/** Services the console reads; `remote.mcpPanel` appears once this plugin mounts its contribution. */
|
|
5143
6426
|
const inject = [
|
|
5144
6427
|
"slots",
|
|
5145
6428
|
"locale",
|
|
5146
|
-
"remote"
|
|
6429
|
+
"remote",
|
|
6430
|
+
"sessions"
|
|
5147
6431
|
];
|
|
5148
6432
|
/**
|
|
5149
6433
|
* Browser plugin body: dictionaries, the scoped stylesheet, the Remote
|
|
@@ -5160,16 +6444,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5160
6444
|
await ctx.remote.$mount(MCP_PANEL_REMOTE);
|
|
5161
6445
|
ctx.inject(["remote.mcpPanel"], (scope) => {
|
|
5162
6446
|
const t = scope.locale.bind(NS);
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5165
|
-
if (!result.ok) throw new Error(`mcpPanel.status failed: ${result.error.code}: ${result.error.message}`);
|
|
5166
|
-
return result.value;
|
|
5167
|
-
};
|
|
5168
|
-
const probe = async (serverName) => {
|
|
5169
|
-
const result = await scope.remote.mcpPanel.probe(serverName);
|
|
5170
|
-
if (!result.ok) throw new Error(`mcpPanel.probe failed: ${result.error.code}: ${result.error.message}`);
|
|
6447
|
+
const unwrap = (result, method) => {
|
|
6448
|
+
if (!result.ok) throw new Error(`mcpPanel.${method} failed: ${result.error.code}: ${result.error.message}`);
|
|
5171
6449
|
return result.value;
|
|
5172
6450
|
};
|
|
6451
|
+
const status = async () => unwrap(await scope.remote.mcpPanel.status(), "status");
|
|
6452
|
+
const probe = async (serverName) => unwrap(await scope.remote.mcpPanel.probe(serverName), "probe");
|
|
6453
|
+
const previewPatch = async (opJson) => unwrap(await scope.remote.mcpPanel.previewPatch(opJson), "previewPatch");
|
|
6454
|
+
const writePatch = async (opJson, confirmed) => unwrap(await scope.remote.mcpPanel.writePatch(opJson, confirmed, currentSessionId(scope.get("sessions"))), "writePatch");
|
|
6455
|
+
const callTool = async (requestJson) => unwrap(await scope.remote.mcpPanel.callTool(requestJson, currentSessionId(scope.get("sessions"))), "callTool");
|
|
5173
6456
|
scope.slots.inject("settings.plugins.tab", () => scope.slots.register({
|
|
5174
6457
|
name: "settings.plugins.tab",
|
|
5175
6458
|
id: "mcp",
|
|
@@ -5178,11 +6461,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5178
6461
|
locale: NS,
|
|
5179
6462
|
inject: () => ({
|
|
5180
6463
|
status,
|
|
5181
|
-
probe
|
|
6464
|
+
probe,
|
|
6465
|
+
previewPatch,
|
|
6466
|
+
writePatch,
|
|
6467
|
+
callTool
|
|
5182
6468
|
})
|
|
5183
6469
|
}, McpPanelTab));
|
|
5184
6470
|
});
|
|
5185
6471
|
}
|
|
6472
|
+
/**
|
|
6473
|
+
* Read the current session id from the sessions store face (structural:
|
|
6474
|
+
* the store shape differs across harness lines, so only the leaf is read).
|
|
6475
|
+
*/
|
|
6476
|
+
function currentSessionId(sessions) {
|
|
6477
|
+
try {
|
|
6478
|
+
const list = sessions?.list;
|
|
6479
|
+
if (typeof list !== "object" || list === null) return void 0;
|
|
6480
|
+
const getSnapshot = list.getSnapshot;
|
|
6481
|
+
if (typeof getSnapshot !== "function") return void 0;
|
|
6482
|
+
const current = getSnapshot().current;
|
|
6483
|
+
return typeof current === "string" ? current : void 0;
|
|
6484
|
+
} catch {
|
|
6485
|
+
return;
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
5186
6488
|
//#endregion
|
|
5187
6489
|
exports.NS = NS;
|
|
5188
6490
|
exports.apply = apply;
|