dsh-plugin-capabilities 0.1.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/lib/client.js ADDED
@@ -0,0 +1,863 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-plugin-capabilities", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ "use strict";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name2 in all)
10
+ __defProp(target, name2, { get: all[name2], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/client/index.ts
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ NS: () => NS,
26
+ apply: () => apply,
27
+ inject: () => inject,
28
+ name: () => name
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+ var import_react3 = require("react");
32
+
33
+ // src/client/McpTab.tsx
34
+ var import_react = require("react");
35
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
36
+
37
+ // src/client/css.ts
38
+ var CSS = `
39
+ .dpc-section{display:flex;flex-direction:column;gap:14px;width:100%;max-width:760px;color:var(--dsw-alias-label-primary)}
40
+ .dpc-head{display:flex;align-items:center;gap:8px}
41
+ .dpc-head h3{margin:0;font-size:13px;line-height:20px;font-weight:600}
42
+ .dpc-head>svg{flex:none;color:var(--dsw-alias-label-tertiary)}
43
+ .dpc-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
44
+ .dpc-listHead{display:flex;align-items:baseline;gap:7px;padding:0 2px;margin-top:2px}
45
+ .dpc-listHead h3{margin:0;font-size:13px;line-height:20px;font-weight:600}
46
+ .dpc-count{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
47
+ .dpc-spacer{flex:1}
48
+ .dpc-refresh{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer}
49
+ .dpc-refresh:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
50
+ .dpc-refresh:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
51
+ .dpc-empty{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
52
+ .dpc-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch;gap:10px;margin:0;padding:0;list-style:none}
53
+ .dpc-card{display:flex;flex-direction:column;gap:8px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-3);padding:12px 14px}
54
+ .dpc-card:hover{background:var(--dsw-alias-interactive-bg-hover)}
55
+ .dpc-cardTop{display:flex;align-items:center;gap:8px}
56
+ .dpc-cardTitle{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;font-weight:600;font-family:var(--ds-font-family-code)}
57
+ .dpc-cardDesc{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
58
+ .dpc-cardRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
59
+ .dpc-tag{display:inline-flex;align-items:center;min-height:20px;border-radius:5px;padding:1px 6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px;white-space:nowrap}
60
+ .dpc-tag[data-kind='source']{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary)}
61
+ .dpc-tag[data-kind='off']{background:color-mix(in srgb,var(--dsw-alias-state-warning-primary,var(--dsw-alias-label-tertiary)) 12%,transparent);color:var(--dsw-alias-label-secondary)}
62
+ .dpc-cardActions{display:flex;align-items:center;gap:6px;margin-left:auto}
63
+ .dpc-banner{display:flex;align-items:flex-start;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px;background:var(--dsw-alias-bg-layer-3);font-size:13px;line-height:20px}
64
+ .dpc-banner[data-kind='ok']{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}
65
+ .dpc-banner[data-kind='error']{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent)}
66
+ .dpc-banner[data-kind='info']{border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 35%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,transparent)}
67
+ .dpc-bannerBody{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
68
+ .dpc-bannerHint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
69
+ .dpc-form{display:flex;flex-direction:column;gap:10px}
70
+ .dpc-label{display:flex;flex-direction:column;gap:4px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
71
+ .dpc-label>span:first-child{color:var(--dsw-alias-label-tertiary)}
72
+ .dpc-input,.dpc-textarea,.dpc-select{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:none;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
73
+ .dpc-textarea{min-height:120px;resize:vertical;font-family:var(--ds-font-family-code);line-height:1.5}
74
+ .dpc-textarea[data-short='true']{min-height:64px}
75
+ .dpc-input:focus-visible,.dpc-textarea:focus-visible,.dpc-select:focus-visible{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
76
+ .dpc-checks{display:flex;gap:16px;font-size:13px;line-height:20px}
77
+ .dpc-checks label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}
78
+ .dpc-formError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
79
+ @media(max-width:680px){.dpc-cards{grid-template-columns:minmax(0,1fr)}}
80
+ `;
81
+
82
+ // src/client/McpTab.tsx
83
+ var import_jsx_runtime = require("react/jsx-runtime");
84
+ function parsePairs(text, separator) {
85
+ const map = {};
86
+ for (const line of text.split(/\r?\n/)) {
87
+ const trimmed = line.trim();
88
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
89
+ const at = trimmed.indexOf(separator);
90
+ if (at <= 0) continue;
91
+ map[trimmed.slice(0, at).trim()] = trimmed.slice(at + 1).trim();
92
+ }
93
+ return map;
94
+ }
95
+ function mapToPairs(map, separator) {
96
+ if (map === void 0) return "";
97
+ return Object.entries(map).map(([key, value]) => `${key}${separator}${value.includes("\n") ? JSON.stringify(value) : value}`).join("\n");
98
+ }
99
+ function McpTab(props) {
100
+ const { t, injected } = props;
101
+ const [servers, setServers] = (0, import_react.useState)(null);
102
+ const [editor, setEditor] = (0, import_react.useState)(null);
103
+ const [confirmId, setConfirmId] = (0, import_react.useState)(null);
104
+ const [importOpen, setImportOpen] = (0, import_react.useState)(false);
105
+ const [importItems, setImportItems] = (0, import_react.useState)(null);
106
+ const [busy, setBusy] = (0, import_react.useState)(false);
107
+ const [pending, setPending] = (0, import_react.useState)(false);
108
+ const [outcome, setOutcome] = (0, import_react.useState)(null);
109
+ const [formError, setFormError] = (0, import_react.useState)(null);
110
+ const [reload, setReload] = (0, import_react.useState)(0);
111
+ const openImport = async () => {
112
+ setImportOpen(true);
113
+ setImportItems(null);
114
+ try {
115
+ const body = await injected.scanImport();
116
+ const existing = new Set(body.existing);
117
+ setImportItems(body.servers.map((server) => ({
118
+ server,
119
+ existing: existing.has(server.name),
120
+ checked: !existing.has(server.name)
121
+ })));
122
+ } catch (error) {
123
+ setImportItems([]);
124
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
125
+ }
126
+ };
127
+ const doImport = async () => {
128
+ if (importItems === null) return;
129
+ const items = importItems.filter((item) => item.checked && !item.existing).map((item) => ({ agent: item.server.agent, name: item.server.name }));
130
+ setBusy(true);
131
+ try {
132
+ const body = await injected.applyImport(items);
133
+ const failed = body.results.filter((item) => !item.ok);
134
+ setOutcome(failed.length === 0 ? { ok: true, text: t("restartNeeded") } : { ok: false, text: `${t("failed")}: ${failed.map((item) => `${item.name} (${item.error})`).join(", ")}` });
135
+ setImportOpen(false);
136
+ setPending(true);
137
+ setReload((value) => value + 1);
138
+ } catch (error) {
139
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
140
+ } finally {
141
+ setBusy(false);
142
+ }
143
+ };
144
+ const reloadList = (showPending) => {
145
+ setReload((value) => value + 1);
146
+ if (showPending) setPending(true);
147
+ };
148
+ (0, import_react.useEffect)(() => {
149
+ let current = true;
150
+ void injected.list().then(
151
+ (body) => {
152
+ if (current) setServers(body.servers);
153
+ },
154
+ (error) => {
155
+ if (current) {
156
+ setServers([]);
157
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error.message ?? error)}` });
158
+ }
159
+ }
160
+ );
161
+ return () => {
162
+ current = false;
163
+ };
164
+ }, [injected, reload, t]);
165
+ const openCreate = () => {
166
+ setFormError(null);
167
+ setEditor({ id: "", serverName: "", transport: "stdio", command: "", args: "", env: "", url: "", headers: "" });
168
+ };
169
+ const openEdit = (row) => {
170
+ setFormError(null);
171
+ setEditor({
172
+ id: row.id,
173
+ serverName: row.serverName,
174
+ transport: row.transport,
175
+ command: row.command ?? "",
176
+ args: (row.args ?? []).join("\n"),
177
+ env: mapToPairs(row.env, "="),
178
+ url: row.url ?? "",
179
+ headers: mapToPairs(row.headers, ":")
180
+ });
181
+ };
182
+ const doSave = async () => {
183
+ if (editor === null) return;
184
+ setBusy(true);
185
+ setFormError(null);
186
+ try {
187
+ await injected.save({
188
+ id: editor.id,
189
+ serverName: editor.serverName.trim(),
190
+ transport: editor.transport,
191
+ ...editor.transport === "stdio" ? {
192
+ command: editor.command.trim(),
193
+ args: editor.args.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== ""),
194
+ env: parsePairs(editor.env, "=")
195
+ } : {
196
+ url: editor.url.trim(),
197
+ headers: parsePairs(editor.headers, ":")
198
+ }
199
+ });
200
+ setEditor(null);
201
+ setOutcome({ ok: true, text: t("restartNeeded") });
202
+ reloadList(true);
203
+ } catch (error) {
204
+ setFormError(String(error instanceof Error ? error.message : error));
205
+ } finally {
206
+ setBusy(false);
207
+ }
208
+ };
209
+ const doToggle = async (row) => {
210
+ setBusy(true);
211
+ try {
212
+ await injected.toggle(row.id, !row.disabled);
213
+ setOutcome({ ok: true, text: t("restartNeeded") });
214
+ reloadList(true);
215
+ } catch (error) {
216
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
217
+ } finally {
218
+ setBusy(false);
219
+ }
220
+ };
221
+ const doRemove = async () => {
222
+ if (confirmId === null) return;
223
+ setBusy(true);
224
+ try {
225
+ await injected.remove(confirmId);
226
+ setOutcome({ ok: true, text: t("restartNeeded") });
227
+ reloadList(true);
228
+ } catch (error) {
229
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
230
+ } finally {
231
+ setBusy(false);
232
+ setConfirmId(null);
233
+ }
234
+ };
235
+ const restartBanner = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": "info", role: "status", children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "ongoing", size: 10 }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-bannerBody", children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("restartNeeded") }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-bannerHint", children: injected.desktop ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
240
+ t("restartDesktopHint"),
241
+ " ",
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", onClick: injected.restart, children: t("restartNow") })
243
+ ] }) : t("restartOtherHint") })
244
+ ] })
245
+ ] });
246
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-section", children: [
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-head", children: [
249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconApiOutline14, { "aria-hidden": "true" }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("mcpTitle") }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", onClick: () => void openImport(), children: t("importServers") }),
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("addServer") })
254
+ ] }),
255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", children: t("mcpIntro") }),
256
+ outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: outcome.text }) })
259
+ ] }),
260
+ pending && restartBanner,
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-listHead", children: [
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("mcpTab") }),
263
+ servers !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-count", children: servers.length }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("view"), title: t("view"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
266
+ ] }),
267
+ servers === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
268
+ servers !== null && servers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("emptyMcp") }),
269
+ servers !== null && servers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", children: servers.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", children: [
270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: row.id, children: row.serverName }),
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: row.transport }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": row.disabled ? "off" : void 0, children: row.disabled ? t("disabled") : t("enabled") })
274
+ ] }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: row.transport === "stdio" ? `${row.command ?? ""} ${(row.args ?? []).join(" ")}` : row.url ?? "" }),
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
277
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
278
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void doToggle(row), children: t("toggle") }),
279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => openEdit(row), children: t("edit") }),
280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmId(row.id), children: t("delete") })
281
+ ] })
282
+ ] }, row.id)) }),
283
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
284
+ import_dsh_client_ui_primitives.Modal,
285
+ {
286
+ open: editor !== null,
287
+ onClose: () => setEditor(null),
288
+ title: editor !== null && editor.id !== "" ? t("editServer") : t("addServer"),
289
+ children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("serverName") }),
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
293
+ "input",
294
+ {
295
+ className: "dpc-input",
296
+ value: editor.serverName,
297
+ disabled: editor.id !== "",
298
+ onChange: (event) => setEditor({ ...editor, serverName: event.target.value })
299
+ }
300
+ )
301
+ ] }),
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("transport") }),
304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
305
+ "select",
306
+ {
307
+ className: "dpc-select",
308
+ value: editor.transport,
309
+ disabled: editor.id !== "",
310
+ onChange: (event) => setEditor({ ...editor, transport: event.target.value }),
311
+ children: [
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "stdio", children: t("transportStdio") }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "streamable-http", children: t("transportHttp") })
314
+ ]
315
+ }
316
+ )
317
+ ] }),
318
+ editor.transport === "stdio" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("command") }),
321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dpc-input", value: editor.command, onChange: (event) => setEditor({ ...editor, command: event.target.value }) })
322
+ ] }),
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
324
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("args") }),
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.args, onChange: (event) => setEditor({ ...editor, args: event.target.value }) })
326
+ ] }),
327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("envPairs") }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.env, onChange: (event) => setEditor({ ...editor, env: event.target.value }) })
330
+ ] })
331
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("url") }),
334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dpc-input", value: editor.url, onChange: (event) => setEditor({ ...editor, url: event.target.value }) })
335
+ ] }),
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dpc-label", children: [
337
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("headersPairs") }),
338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dpc-textarea", "data-short": "true", value: editor.headers, onChange: (event) => setEditor({ ...editor, headers: event.target.value }) })
339
+ ] })
340
+ ] }),
341
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-formError", children: formError }),
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setEditor(null), children: t("cancel") }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
346
+ ] })
347
+ ] })
348
+ }
349
+ ),
350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
351
+ import_dsh_client_ui_primitives.Modal,
352
+ {
353
+ open: confirmId !== null,
354
+ onClose: () => setConfirmId(null),
355
+ title: t("confirmRemove"),
356
+ description: confirmId ?? void 0,
357
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setConfirmId(null), children: t("cancel") }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void doRemove(), children: t("delete") })
360
+ ] }),
361
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("removeWarn") })
362
+ }
363
+ ),
364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
365
+ import_dsh_client_ui_primitives.Modal,
366
+ {
367
+ open: importOpen,
368
+ onClose: () => setImportOpen(false),
369
+ title: t("importServers"),
370
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-form", children: [
371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-intro", style: { margin: 0 }, children: t("importIntro") }),
372
+ importItems === null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("loading") }),
373
+ importItems !== null && importItems.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-empty", children: t("importEmpty") }),
374
+ importItems !== null && importItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dpc-cards", style: { gridTemplateColumns: "minmax(0, 1fr)" }, children: importItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "dpc-card", style: { opacity: item.existing ? 0.55 : 1 }, children: [
375
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardTop", children: [
376
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 8, minWidth: 0, cursor: item.existing ? "default" : "pointer" }, children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
378
+ "input",
379
+ {
380
+ type: "checkbox",
381
+ checked: item.checked,
382
+ disabled: item.existing,
383
+ onChange: (event) => {
384
+ const next = importItems.slice();
385
+ next[index] = { ...item, checked: event.target.checked };
386
+ setImportItems(next);
387
+ }
388
+ }
389
+ ),
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "dpc-cardTitle", title: item.server.name, children: item.server.name })
391
+ ] }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: item.server.agent === "claude-code" ? "Claude Code" : "Codex" }),
393
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: item.server.transport }),
394
+ item.existing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-tag", children: t("importExisting") })
395
+ ] }),
396
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-cardDesc", children: item.server.transport === "stdio" ? `${item.server.command ?? ""} ${(item.server.args ?? []).join(" ")}` : item.server.url ?? "" })
397
+ ] }, `${item.server.agent}/${item.server.name}`)) }),
398
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dpc-formError", children: formError }),
399
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dpc-cardRow", children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dpc-spacer" }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", onClick: () => setImportOpen(false), children: t("cancel") }),
402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
403
+ import_dsh_client_ui_primitives.Button,
404
+ {
405
+ variant: "primary",
406
+ disabled: busy || importItems === null || !importItems.some((item) => item.checked && !item.existing),
407
+ onClick: () => void doImport(),
408
+ children: t("importSelected")
409
+ }
410
+ )
411
+ ] })
412
+ ] })
413
+ }
414
+ )
415
+ ] });
416
+ }
417
+
418
+ // src/client/SkillsTab.tsx
419
+ var import_react2 = require("react");
420
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
421
+ var import_jsx_runtime2 = require("react/jsx-runtime");
422
+ var SOURCE_KEYS = {
423
+ "project-dsh": "sourceProjectDsh",
424
+ "project-agents": "sourceProjectAgents",
425
+ "user-dsh": "sourceUserDsh",
426
+ "user-agents": "sourceUserAgents",
427
+ runtime: "sourceRuntime",
428
+ bundled: "sourceBundled",
429
+ custom: "sourceCustom"
430
+ };
431
+ function SkillsTab(props) {
432
+ const { t, injected } = props;
433
+ const [skills, setSkills] = (0, import_react2.useState)(null);
434
+ const [editor, setEditor] = (0, import_react2.useState)(null);
435
+ const [confirmName, setConfirmName] = (0, import_react2.useState)(null);
436
+ const [busy, setBusy] = (0, import_react2.useState)(false);
437
+ const [outcome, setOutcome] = (0, import_react2.useState)(null);
438
+ const [formError, setFormError] = (0, import_react2.useState)(null);
439
+ const [reload, setReload] = (0, import_react2.useState)(0);
440
+ (0, import_react2.useEffect)(() => {
441
+ let current = true;
442
+ void injected.list().then(
443
+ (body) => {
444
+ if (current) setSkills(body.skills);
445
+ },
446
+ (error) => {
447
+ if (current) {
448
+ setSkills([]);
449
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error.message ?? error)}` });
450
+ }
451
+ }
452
+ );
453
+ return () => {
454
+ current = false;
455
+ };
456
+ }, [injected, reload, t]);
457
+ const openCreate = () => {
458
+ setFormError(null);
459
+ setEditor({ mode: "create", name: "", description: "", whenToUse: "", modelInvocable: true, userInvocable: true, content: "" });
460
+ };
461
+ const openExisting = async (skill) => {
462
+ setBusy(true);
463
+ setFormError(null);
464
+ try {
465
+ const body = await injected.get(skill.name);
466
+ setEditor({
467
+ mode: skill.editable ? "edit" : "view",
468
+ name: skill.name,
469
+ description: skill.description,
470
+ whenToUse: skill.whenToUse ?? "",
471
+ modelInvocable: skill.invocation.modelInvocable,
472
+ userInvocable: skill.invocation.userInvocable,
473
+ content: body.content
474
+ });
475
+ } catch (error) {
476
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
477
+ } finally {
478
+ setBusy(false);
479
+ }
480
+ };
481
+ const refreshUntil = (predicate, timeoutMs = 1e4) => {
482
+ const deadline = Date.now() + timeoutMs;
483
+ const tick = () => {
484
+ if (Date.now() > deadline) return;
485
+ setTimeout(() => {
486
+ void injected.list().then(
487
+ (body) => {
488
+ setSkills(body.skills);
489
+ if (!predicate(body.skills)) tick();
490
+ },
491
+ () => tick()
492
+ );
493
+ }, 1e3);
494
+ };
495
+ tick();
496
+ };
497
+ const doSave = async () => {
498
+ if (editor === null) return;
499
+ setBusy(true);
500
+ setFormError(null);
501
+ try {
502
+ const name2 = editor.name.trim();
503
+ await injected.save({
504
+ name: name2,
505
+ description: editor.description,
506
+ whenToUse: editor.whenToUse.trim() === "" ? void 0 : editor.whenToUse,
507
+ modelInvocable: editor.modelInvocable,
508
+ userInvocable: editor.userInvocable,
509
+ content: editor.content
510
+ });
511
+ setOutcome({ ok: true, text: t("saved") });
512
+ setEditor(null);
513
+ refreshUntil((skills2) => skills2.some((skill) => skill.name === name2));
514
+ } catch (error) {
515
+ setFormError(String(error instanceof Error ? error.message : error));
516
+ } finally {
517
+ setBusy(false);
518
+ }
519
+ };
520
+ const doDelete = async () => {
521
+ if (confirmName === null) return;
522
+ const name2 = confirmName;
523
+ setBusy(true);
524
+ try {
525
+ await injected.remove(name2);
526
+ setOutcome({ ok: true, text: t("saved") });
527
+ refreshUntil((skills2) => !skills2.some((skill) => skill.name === name2));
528
+ } catch (error) {
529
+ setOutcome({ ok: false, text: `${t("failed")}: ${String(error instanceof Error ? error.message : error)}` });
530
+ } finally {
531
+ setBusy(false);
532
+ setConfirmName(null);
533
+ }
534
+ };
535
+ const readOnly = editor?.mode === "view";
536
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-section", children: [
537
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CSS }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-head", children: [
539
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconSkillOutline16, { "aria-hidden": "true" }),
540
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("skillsTitle") }),
541
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
542
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", size: "sm", onClick: openCreate, children: t("newSkill") })
543
+ ] }),
544
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-intro", children: t("skillsIntro") }),
545
+ outcome !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-banner", "data-kind": outcome.ok ? "ok" : "error", role: "status", children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.StateDot, { state: outcome.ok ? "done" : "error", size: 10 }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dpc-bannerBody", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: outcome.text }) })
548
+ ] }),
549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-listHead", children: [
550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("skillsTab") }),
551
+ skills !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-count", children: skills.length }),
552
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
553
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dpc-refresh", "aria-label": t("view"), title: t("view"), disabled: busy, onClick: () => setReload((value) => value + 1), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconRefreshOutline14, { size: 14, "aria-hidden": "true" }) })
554
+ ] }),
555
+ skills === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("loading") }),
556
+ skills !== null && skills.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-empty", children: t("emptySkills") }),
557
+ skills !== null && skills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "dpc-cards", children: skills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "dpc-card", children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardTop", children: [
559
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { className: "dpc-cardTitle", title: skill.name, children: skill.name }),
560
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", "data-kind": "source", children: t(SOURCE_KEYS[skill.source] ?? "sourceCustom") })
561
+ ] }),
562
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-cardDesc", title: skill.description, children: skill.description }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
564
+ !skill.invocation.modelInvocable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: "\u2699" }),
565
+ !skill.invocation.userInvocable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-tag", children: "/" }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
567
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => void openExisting(skill), children: skill.editable ? t("edit") : t("view") }),
568
+ skill.editable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setConfirmName(skill.name), children: t("delete") })
569
+ ] })
570
+ ] }, `${skill.source}/${skill.name}`)) }),
571
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
572
+ import_dsh_client_ui_primitives2.Modal,
573
+ {
574
+ open: editor !== null,
575
+ onClose: () => setEditor(null),
576
+ title: editor === null ? "" : editor.mode === "create" ? t("newSkill") : editor.mode === "edit" ? t("editSkill") : t("viewSkill"),
577
+ children: editor !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-form", children: [
578
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
579
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillName") }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
581
+ "input",
582
+ {
583
+ className: "dpc-input",
584
+ value: editor.name,
585
+ disabled: readOnly || editor.mode === "edit",
586
+ onChange: (event) => setEditor({ ...editor, name: event.target.value })
587
+ }
588
+ )
589
+ ] }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillDescription") }),
592
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
593
+ "input",
594
+ {
595
+ className: "dpc-input",
596
+ value: editor.description,
597
+ disabled: readOnly,
598
+ onChange: (event) => setEditor({ ...editor, description: event.target.value })
599
+ }
600
+ )
601
+ ] }),
602
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillWhenToUse") }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
605
+ "input",
606
+ {
607
+ className: "dpc-input",
608
+ value: editor.whenToUse,
609
+ disabled: readOnly,
610
+ onChange: (event) => setEditor({ ...editor, whenToUse: event.target.value })
611
+ }
612
+ )
613
+ ] }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-checks", children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
616
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
617
+ "input",
618
+ {
619
+ type: "checkbox",
620
+ checked: editor.modelInvocable,
621
+ disabled: readOnly,
622
+ onChange: (event) => setEditor({ ...editor, modelInvocable: event.target.checked })
623
+ }
624
+ ),
625
+ t("modelInvocable")
626
+ ] }),
627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
629
+ "input",
630
+ {
631
+ type: "checkbox",
632
+ checked: editor.userInvocable,
633
+ disabled: readOnly,
634
+ onChange: (event) => setEditor({ ...editor, userInvocable: event.target.checked })
635
+ }
636
+ ),
637
+ t("userInvocable")
638
+ ] })
639
+ ] }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "dpc-label", children: [
641
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("skillContent") }),
642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
643
+ "textarea",
644
+ {
645
+ className: "dpc-textarea",
646
+ value: editor.content,
647
+ readOnly,
648
+ onChange: (event) => setEditor({ ...editor, content: event.target.value })
649
+ }
650
+ )
651
+ ] }),
652
+ formError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dpc-formError", children: formError }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dpc-cardRow", children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dpc-spacer" }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setEditor(null), children: readOnly ? t("close") : t("cancel") }),
656
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doSave(), children: t("save") })
657
+ ] })
658
+ ] })
659
+ }
660
+ ),
661
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
662
+ import_dsh_client_ui_primitives2.Modal,
663
+ {
664
+ open: confirmName !== null,
665
+ onClose: () => setConfirmName(null),
666
+ title: t("confirmDelete"),
667
+ description: confirmName ?? void 0,
668
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", onClick: () => setConfirmName(null), children: t("cancel") }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy, onClick: () => void doDelete(), children: t("delete") })
671
+ ] }),
672
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("deleteWarn") })
673
+ }
674
+ )
675
+ ] });
676
+ }
677
+
678
+ // src/client/locales.ts
679
+ var zh = {
680
+ skillsTab: "\u6280\u80FD",
681
+ mcpTab: "MCP",
682
+ skillsTitle: "\u6280\u80FD\u7BA1\u7406",
683
+ skillsIntro: "\u67E5\u770B\u4E0E\u7F16\u8F91 dsh \u53D1\u73B0\u7684\u6280\u80FD\uFF1B\u7528\u6237\u7EA7\u6280\u80FD\uFF08DSH_HOME/skills\uFF09\u53EF\u5728\u6B64\u65B0\u5EFA\u3001\u4FEE\u6539\u3001\u5220\u9664\uFF0C\u6587\u4EF6\u88AB\u76D1\u542C\uFF0C\u4FDD\u5B58\u5373\u751F\u6548\u3002\u82E5\u5B58\u5728 ~/.claude/skills \u6216 ~/.codex/skills\uFF0C\u4F1A\u81EA\u52A8\u7EB3\u5165\u626B\u63CF\uFF08\u96F6\u62F7\u8D1D\u3001\u5B9E\u65F6\u540C\u6B65\uFF09\u3002",
684
+ newSkill: "\u65B0\u5EFA\u6280\u80FD",
685
+ editSkill: "\u7F16\u8F91\u6280\u80FD",
686
+ viewSkill: "\u67E5\u770B\u6280\u80FD",
687
+ skillName: "\u540D\u79F0\uFF08kebab-case\uFF09",
688
+ skillDescription: "\u63CF\u8FF0",
689
+ skillWhenToUse: "\u4F7F\u7528\u65F6\u673A\uFF08\u53EF\u9009\uFF09",
690
+ skillContent: "\u6B63\u6587\uFF08Markdown\uFF09",
691
+ modelInvocable: "\u5141\u8BB8\u6A21\u578B\u8C03\u7528",
692
+ userInvocable: "\u5141\u8BB8\u7528\u6237 / \u8C03\u7528",
693
+ save: "\u4FDD\u5B58",
694
+ cancel: "\u53D6\u6D88",
695
+ delete: "\u5220\u9664",
696
+ edit: "\u7F16\u8F91",
697
+ view: "\u67E5\u770B",
698
+ close: "\u5173\u95ED",
699
+ emptySkills: "\u8FD8\u6CA1\u6709\u53D1\u73B0\u4EFB\u4F55\u6280\u80FD",
700
+ loading: "\u8F7D\u5165\u4E2D\u2026",
701
+ source: "\u6765\u6E90",
702
+ sourceProjectDsh: "\u9879\u76EE",
703
+ sourceProjectAgents: "\u9879\u76EE",
704
+ sourceUserDsh: "\u7528\u6237",
705
+ sourceUserAgents: "\u7528\u6237",
706
+ sourceRuntime: "\u8FD0\u884C\u65F6",
707
+ sourceBundled: "\u5185\u7F6E",
708
+ sourceCustom: "\u81EA\u5B9A\u4E49",
709
+ readOnly: "\u53EA\u8BFB",
710
+ confirmDelete: "\u786E\u8BA4\u5220\u9664\u6280\u80FD\uFF1F",
711
+ deleteWarn: "\u5C06\u5220\u9664 DSH_HOME/skills \u4E0B\u7684\u6574\u4E2A\u6280\u80FD\u76EE\u5F55\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",
712
+ saved: "\u5DF2\u4FDD\u5B58\uFF0C\u6280\u80FD\u76EE\u5F55\u88AB\u76D1\u542C\uFF0C\u7A0D\u5019\u5373\u53EF\u5728\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u3002",
713
+ mcpTitle: "MCP \u670D\u52A1\u5668",
714
+ mcpIntro: "\u7BA1\u7406 profile \u4E2D\u7684 MCP \u670D\u52A1\u5668\u884C\uFF08@deepseek-ai/dsh-mcp-client\uFF09\u3002\u65B0\u589E\u3001\u4FEE\u6539\u6216\u5220\u9664\u540E\u9700\u8981\u91CD\u542F dsh \u624D\u751F\u6548\u3002",
715
+ addServer: "\u6DFB\u52A0\u670D\u52A1\u5668",
716
+ editServer: "\u7F16\u8F91\u670D\u52A1\u5668",
717
+ serverName: "\u670D\u52A1\u5668\u540D\uFF08\u5DE5\u5177\u540D\u524D\u7F00\uFF09",
718
+ transport: "\u4F20\u8F93\u65B9\u5F0F",
719
+ transportStdio: "stdio\uFF08\u672C\u5730\u547D\u4EE4\uFF09",
720
+ transportHttp: "streamable-http\uFF08URL\uFF09",
721
+ command: "\u547D\u4EE4",
722
+ args: "\u53C2\u6570\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF09",
723
+ envPairs: "\u73AF\u5883\u53D8\u91CF\uFF08\u6BCF\u884C KEY=VALUE\uFF09",
724
+ url: "URL",
725
+ headersPairs: "\u8BF7\u6C42\u5934\uFF08\u6BCF\u884C KEY: VALUE\uFF09",
726
+ disabled: "\u5DF2\u505C\u7528",
727
+ enabled: "\u542F\u7528\u4E2D",
728
+ toggle: "\u505C\u7528/\u542F\u7528",
729
+ confirmRemove: "\u786E\u8BA4\u79FB\u9664\u8BE5\u670D\u52A1\u5668\uFF1F",
730
+ removeWarn: "\u5C06\u4ECE profile \u914D\u7F6E\u4E2D\u79FB\u9664\u8FD9\u4E00\u884C\uFF0C\u91CD\u542F dsh \u540E\u5176\u5DE5\u5177\u4E0D\u518D\u51FA\u73B0\u3002",
731
+ emptyMcp: "\u8FD8\u6CA1\u6709\u914D\u7F6E MCP \u670D\u52A1\u5668",
732
+ importServers: "\u4ECE\u5176\u4ED6 Agent \u5BFC\u5165",
733
+ importIntro: "\u626B\u63CF Claude Code\uFF08~/.claude.json\uFF09\u4E0E Codex\uFF08~/.codex/config.toml\uFF09\u7684 MCP \u670D\u52A1\u5668\u914D\u7F6E\uFF0C\u52FE\u9009\u540E\u5BFC\u5165\u4E3A\u672C profile \u7684\u670D\u52A1\u5668\u884C\u3002",
734
+ importEmpty: "\u6CA1\u6709\u53D1\u73B0\u53EF\u5BFC\u5165\u7684 MCP \u670D\u52A1\u5668",
735
+ importExisting: "\u5DF2\u5B58\u5728",
736
+ importSelected: "\u5BFC\u5165\u9009\u4E2D\u9879",
737
+ restartNeeded: "\u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u91CD\u542F dsh \u540E\u751F\u6548\u3002",
738
+ restartDesktopHint: "\u91CD\u542F\u7531\u684C\u9762\u5E94\u7528\u8D1F\u8D23\uFF1A\u6258\u76D8\u83DC\u5355\u300C\u91CD\u542F\u670D\u52A1\u300D\u3002",
739
+ restartOtherHint: "\u91CD\u542F\u65B9\u5F0F\uFF1A\u5173\u95ED\u5F53\u524D dsh \u8FDB\u7A0B\u540E\u91CD\u65B0\u8FD0\u884C\u3002",
740
+ restartNow: "\u91CD\u542F\u670D\u52A1",
741
+ failed: "\u64CD\u4F5C\u5931\u8D25"
742
+ };
743
+ var en = {
744
+ skillsTab: "Skills",
745
+ mcpTab: "MCP",
746
+ skillsTitle: "Skills",
747
+ skillsIntro: "View and edit the skills dsh discovers; user-level skills (DSH_HOME/skills) can be created, edited, and deleted here \u2014 the directory is watched, saves apply without a restart. ~/.claude/skills and ~/.codex/skills are scanned too when present (zero-copy, live-synced).",
748
+ newSkill: "New skill",
749
+ editSkill: "Edit skill",
750
+ viewSkill: "View skill",
751
+ skillName: "Name (kebab-case)",
752
+ skillDescription: "Description",
753
+ skillWhenToUse: "When to use (optional)",
754
+ skillContent: "Body (Markdown)",
755
+ modelInvocable: "Model-invocable",
756
+ userInvocable: "User-invocable (/)",
757
+ save: "Save",
758
+ cancel: "Cancel",
759
+ delete: "Delete",
760
+ edit: "Edit",
761
+ view: "View",
762
+ close: "Close",
763
+ emptySkills: "No skills discovered yet",
764
+ loading: "Loading\u2026",
765
+ source: "Source",
766
+ sourceProjectDsh: "project",
767
+ sourceProjectAgents: "project",
768
+ sourceUserDsh: "user",
769
+ sourceUserAgents: "user",
770
+ sourceRuntime: "runtime",
771
+ sourceBundled: "bundled",
772
+ sourceCustom: "custom",
773
+ readOnly: "read-only",
774
+ confirmDelete: "Delete this skill?",
775
+ deleteWarn: "Removes the whole skill directory under DSH_HOME/skills. This cannot be undone.",
776
+ saved: "Saved. The skills directory is watched; the skill is usable in sessions shortly.",
777
+ mcpTitle: "MCP servers",
778
+ mcpIntro: "Manage the MCP server rows (@deepseek-ai/dsh-mcp-client) in this profile. Additions, edits, and removals take effect after a dsh restart.",
779
+ addServer: "Add server",
780
+ editServer: "Edit server",
781
+ serverName: "Server name (tool name prefix)",
782
+ transport: "Transport",
783
+ transportStdio: "stdio (local command)",
784
+ transportHttp: "streamable-http (URL)",
785
+ command: "Command",
786
+ args: "Arguments (one per line)",
787
+ envPairs: "Environment (KEY=VALUE per line)",
788
+ url: "URL",
789
+ headersPairs: "Headers (KEY: VALUE per line)",
790
+ disabled: "Disabled",
791
+ enabled: "Enabled",
792
+ toggle: "Enable/disable",
793
+ confirmRemove: "Remove this server?",
794
+ removeWarn: "Removes the row from the profile configuration; its tools disappear after the next dsh restart.",
795
+ emptyMcp: "No MCP servers configured yet",
796
+ importServers: "Import from other agents",
797
+ importIntro: "Scans Claude Code (~/.claude.json) and Codex (~/.codex/config.toml) MCP server configs; selected entries become server rows in this profile.",
798
+ importEmpty: "No importable MCP servers found",
799
+ importExisting: "already here",
800
+ importSelected: "Import selected",
801
+ restartNeeded: "Configuration written \u2014 restart dsh to apply.",
802
+ restartDesktopHint: "The desktop app owns restarts: use the tray \u201CRestart service\u201D.",
803
+ restartOtherHint: "Restart by closing this dsh process and running it again.",
804
+ restartNow: "Restart service",
805
+ failed: "Operation failed"
806
+ };
807
+
808
+ // src/client/index.ts
809
+ var NS = "settings.pluginCapabilities";
810
+ async function fetchJson(path, init) {
811
+ const response = await fetch(path, init);
812
+ const body = await response.json();
813
+ if (!response.ok) throw new Error(body.error ?? `HTTP ${response.status}`);
814
+ return body;
815
+ }
816
+ var post = (path, body) => fetchJson(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
817
+ var name = "dsh-plugin-capabilities";
818
+ var inject = ["slots", "locale"];
819
+ function apply(ctx) {
820
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-plugin-capabilities: dictionaries");
821
+ const t = ctx.locale.bind(NS);
822
+ const skillsInjected = {
823
+ list: () => fetchJson("/dsh-plugin-capabilities/skills"),
824
+ get: (name2) => fetchJson(`/dsh-plugin-capabilities/skill?name=${encodeURIComponent(name2)}`),
825
+ save: (input) => post("/dsh-plugin-capabilities/skill/save", input),
826
+ remove: (name2) => post("/dsh-plugin-capabilities/skill/delete", { name: name2 })
827
+ };
828
+ const mcpInjected = {
829
+ list: () => fetchJson("/dsh-plugin-capabilities/mcp"),
830
+ save: (input) => post("/dsh-plugin-capabilities/mcp/save", input),
831
+ toggle: (id, disabled) => post("/dsh-plugin-capabilities/mcp/toggle", { id, disabled }),
832
+ remove: (id) => post("/dsh-plugin-capabilities/mcp/remove", { id }),
833
+ scanImport: () => fetchJson("/dsh-plugin-capabilities/import/scan"),
834
+ applyImport: (items) => post("/dsh-plugin-capabilities/import/apply", { items }),
835
+ restart: () => {
836
+ window.dshDesktop?.restartSidecar?.();
837
+ },
838
+ desktop: window.dshDesktop !== void 0
839
+ };
840
+ ctx.slots.inject("settings.plugins.tab", () => {
841
+ return ctx.slots.register({
842
+ name: "settings.plugins.tab",
843
+ id: "capabilities-skills",
844
+ order: 30,
845
+ label: () => t("skillsTab"),
846
+ locale: NS,
847
+ inject: () => skillsInjected
848
+ }, () => (0, import_react3.createElement)(SkillsTab, { t, injected: skillsInjected }));
849
+ });
850
+ ctx.slots.inject("settings.plugins.tab", () => {
851
+ return ctx.slots.register({
852
+ name: "settings.plugins.tab",
853
+ id: "capabilities-mcp",
854
+ order: 40,
855
+ label: () => t("mcpTab"),
856
+ locale: NS,
857
+ inject: () => mcpInjected
858
+ }, () => (0, import_react3.createElement)(McpTab, { t, injected: mcpInjected }));
859
+ });
860
+ }
861
+
862
+ return module.exports; } });
863
+ //# sourceMappingURL=client.js.map