@klarkxy/dsh-model-hub 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/docs/README.zh-CN.md +8 -4
- package/lib/client.inner.cjs +298 -115
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +298 -115
- package/lib/index.js +206 -182
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +12 -12
package/lib/client.inner.cjs
CHANGED
|
@@ -1,28 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
-
key = keys[i];
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
-
get: ((k) => from[k]).bind(null, key),
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
-
value: mod,
|
|
21
|
-
enumerable: true
|
|
22
|
-
}) : target, mod));
|
|
23
|
-
//#endregion
|
|
24
2
|
let react = require("react");
|
|
25
|
-
react = __toESM(react, 1);
|
|
26
3
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
27
4
|
let react_dom = require("react-dom");
|
|
28
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -61,12 +38,68 @@ function normalizeModelRoute(value) {
|
|
|
61
38
|
}
|
|
62
39
|
//#endregion
|
|
63
40
|
//#region ../dsh-model-route/src/catalog.ts
|
|
41
|
+
/** Both services are required: Cordis checks the nested namespace separately. */
|
|
42
|
+
const MODEL_CATALOG_INJECT = ["remote", "remote.session"];
|
|
64
43
|
function asRecord(value) {
|
|
65
44
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
66
45
|
}
|
|
67
46
|
function asText(value) {
|
|
68
47
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
69
48
|
}
|
|
49
|
+
/** A useful diagnostic even when a transport rejects with a plain object. */
|
|
50
|
+
function modelCatalogErrorMessage(value) {
|
|
51
|
+
if (value instanceof Error && value.message) return value.message;
|
|
52
|
+
return asText(asRecord(value)?.message) ?? asText(value) ?? "Unable to load the model catalog.";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate and unwrap a successful catalog without discarding provider failures
|
|
56
|
+
* or newer host metadata. Names and metadata remain optional for older hosts.
|
|
57
|
+
* A broken response is never presented as a successfully loaded empty menu.
|
|
58
|
+
*/
|
|
59
|
+
function checkedModelCatalog(value) {
|
|
60
|
+
const envelope = asRecord(value);
|
|
61
|
+
if (envelope?.ok === false) throw new Error(modelCatalogErrorMessage(envelope.error ?? envelope));
|
|
62
|
+
const root = envelope?.ok === true ? asRecord(envelope.value) : envelope;
|
|
63
|
+
const malformed = (field) => {
|
|
64
|
+
throw new Error(`Invalid model catalog: ${field}.`);
|
|
65
|
+
};
|
|
66
|
+
if (!root || !Array.isArray(root.groups)) return malformed("groups must be an array");
|
|
67
|
+
for (const value of root.groups) {
|
|
68
|
+
const group = asRecord(value);
|
|
69
|
+
if (!group || !asText(group.id) || !Array.isArray(group.models)) return malformed("each group needs an id and models array");
|
|
70
|
+
for (const value of group.models) {
|
|
71
|
+
const model = asRecord(value);
|
|
72
|
+
if (!model || !asText(model.id)) return malformed("each model needs an id");
|
|
73
|
+
if (model.reasoning !== void 0) {
|
|
74
|
+
const reasoning = asRecord(model.reasoning);
|
|
75
|
+
if (!reasoning || !Array.isArray(reasoning.efforts) || reasoning.efforts.some((value) => !parseEffort(value))) return malformed("reasoning efforts must be an array of identified efforts");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (root.failures !== void 0 && (!Array.isArray(root.failures) || root.failures.some((value) => {
|
|
80
|
+
const failure = asRecord(value);
|
|
81
|
+
return !failure || !asText(failure.id) || typeof failure.name !== "string" || typeof failure.message !== "string";
|
|
82
|
+
}))) return malformed("failures must be an array of provider failures");
|
|
83
|
+
return root;
|
|
84
|
+
}
|
|
85
|
+
/** Read through the injected session namespace; absence and rejection are errors. */
|
|
86
|
+
async function readModelCatalog(client) {
|
|
87
|
+
const session = client.remote?.session;
|
|
88
|
+
if (typeof session?.modelCatalog !== "function") throw new Error("Model catalog unavailable: inject both remote and remote.session.");
|
|
89
|
+
return checkedModelCatalog(await session.modelCatalog());
|
|
90
|
+
}
|
|
91
|
+
/** Provider failures are partial results, not a transport failure. */
|
|
92
|
+
function modelCatalogFailures(value) {
|
|
93
|
+
try {
|
|
94
|
+
return checkedModelCatalog(value).failures ?? [];
|
|
95
|
+
} catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Text adapter for existing model-hub status messages. */
|
|
100
|
+
function catalogFailures(value) {
|
|
101
|
+
return modelCatalogFailures(value).map((failure) => `${failure.name || failure.id}: ${failure.message}`);
|
|
102
|
+
}
|
|
70
103
|
function parseEffort(value) {
|
|
71
104
|
const row = asRecord(value);
|
|
72
105
|
const id = row ? asText(row.id) : void 0;
|
|
@@ -163,6 +196,59 @@ function groupModelMenuChoices(choices, query) {
|
|
|
163
196
|
return groups;
|
|
164
197
|
}
|
|
165
198
|
//#endregion
|
|
199
|
+
//#region ../dsh-model-route/src/catalog-ui.tsx
|
|
200
|
+
/** Also accepts a mapped state from a caller that owns broader async orchestration. */
|
|
201
|
+
function ModelCatalogStatus({ state, locale }) {
|
|
202
|
+
const zh = locale?.toLowerCase().startsWith("zh") === true;
|
|
203
|
+
const copy = zh ? {
|
|
204
|
+
loading: "正在加载模型…",
|
|
205
|
+
error: "模型列表加载失败",
|
|
206
|
+
partial: "部分提供商的模型未能加载",
|
|
207
|
+
retry: "重试"
|
|
208
|
+
} : {
|
|
209
|
+
loading: "Loading models…",
|
|
210
|
+
error: "Unable to load models",
|
|
211
|
+
partial: "Some providers could not load their models",
|
|
212
|
+
retry: "Retry"
|
|
213
|
+
};
|
|
214
|
+
if (!state.loading && !state.error && state.failures.length === 0) return null;
|
|
215
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
216
|
+
className: "dsh-model-catalog-status",
|
|
217
|
+
lang: zh ? "zh" : "en",
|
|
218
|
+
children: [
|
|
219
|
+
state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
220
|
+
role: "status",
|
|
221
|
+
children: copy.loading
|
|
222
|
+
}) : null,
|
|
223
|
+
state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
224
|
+
role: "alert",
|
|
225
|
+
children: [
|
|
226
|
+
copy.error,
|
|
227
|
+
": ",
|
|
228
|
+
state.error
|
|
229
|
+
]
|
|
230
|
+
}) : null,
|
|
231
|
+
state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
232
|
+
role: "status",
|
|
233
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy.partial }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: state.failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
234
|
+
failure.name || failure.id,
|
|
235
|
+
": ",
|
|
236
|
+
failure.message
|
|
237
|
+
] }, `${failure.id}:${index}`)) })]
|
|
238
|
+
}) : null,
|
|
239
|
+
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
240
|
+
type: "button",
|
|
241
|
+
size: "sm",
|
|
242
|
+
disabled: state.loading,
|
|
243
|
+
onClick: () => {
|
|
244
|
+
state.refresh();
|
|
245
|
+
},
|
|
246
|
+
children: copy.retry
|
|
247
|
+
}) : null
|
|
248
|
+
]
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
166
252
|
//#region ../dsh-model-route/src/ui.tsx
|
|
167
253
|
/**
|
|
168
254
|
* ModelMenu: the standard editor of one model-route field — the visible half
|
|
@@ -1172,6 +1258,29 @@ ${scope} {
|
|
|
1172
1258
|
& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
1173
1259
|
& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
1174
1260
|
|
|
1261
|
+
/* --- Native textarea. The primitives ship no textarea either, so a settings
|
|
1262
|
+
* prompt keeps the platform element and the same field shell as the select:
|
|
1263
|
+
* 0.5px l4, radius-md, layer-3, 13/1.5 type. It grows vertically instead of
|
|
1264
|
+
* locking to the select's 34px row. --- */
|
|
1265
|
+
& .dsh-ui-textarea {
|
|
1266
|
+
box-sizing: border-box;
|
|
1267
|
+
width: 100%;
|
|
1268
|
+
min-width: 0;
|
|
1269
|
+
resize: vertical;
|
|
1270
|
+
padding: 8px 12px;
|
|
1271
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
1272
|
+
border-radius: var(--dsw-radius-md);
|
|
1273
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1274
|
+
font: inherit;
|
|
1275
|
+
font-size: 13px;
|
|
1276
|
+
line-height: 1.5;
|
|
1277
|
+
color: var(--dsw-alias-label-primary);
|
|
1278
|
+
}
|
|
1279
|
+
& .dsh-ui-textarea:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }
|
|
1280
|
+
& .dsh-ui-textarea:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
1281
|
+
& .dsh-ui-textarea::placeholder { color: var(--dsw-alias-label-dimmed); }
|
|
1282
|
+
& .dsh-ui-textarea[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
1283
|
+
|
|
1175
1284
|
/* Long panels scroll inside themselves instead of growing the host page. */
|
|
1176
1285
|
& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
|
|
1177
1286
|
& .dsh-ui-panel-wide { max-width: 100%; }
|
|
@@ -1184,25 +1293,18 @@ ${scope} {
|
|
|
1184
1293
|
}
|
|
1185
1294
|
//#endregion
|
|
1186
1295
|
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
1187
|
-
/**
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
method: "watch",
|
|
1200
|
-
mode: "stream",
|
|
1201
|
-
invocation: { kind: "direct" },
|
|
1202
|
-
parameters: [],
|
|
1203
|
-
cancellation: { parameter: "signal" }
|
|
1204
|
-
}]
|
|
1205
|
-
};
|
|
1296
|
+
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
1297
|
+
function modelSettingsChangesIdentity(packageName) {
|
|
1298
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
|
|
1299
|
+
const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
1300
|
+
if (!parts.length) throw new Error("Model settings package name has no RPC segment");
|
|
1301
|
+
const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
|
|
1302
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
|
|
1303
|
+
return {
|
|
1304
|
+
service: segment,
|
|
1305
|
+
namespace: segment
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1206
1308
|
//#endregion
|
|
1207
1309
|
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
1208
1310
|
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
@@ -1217,21 +1319,44 @@ function parseModelSettingsChangeFrame(value) {
|
|
|
1217
1319
|
};
|
|
1218
1320
|
throw new Error("Invalid model settings change frame");
|
|
1219
1321
|
}
|
|
1220
|
-
/** One contribution
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1322
|
+
/** One owner's contribution. Each package mounts only its own endpoint. */
|
|
1323
|
+
function modelSettingsChangesRemote(packageName, identity = modelSettingsChangesIdentity(packageName)) {
|
|
1324
|
+
return {
|
|
1325
|
+
package: packageName,
|
|
1326
|
+
descriptors: [{
|
|
1327
|
+
id: `${identity.namespace}/watch`,
|
|
1328
|
+
service: identity.service,
|
|
1329
|
+
namespace: identity.namespace,
|
|
1330
|
+
method: "watch",
|
|
1331
|
+
mode: "stream",
|
|
1332
|
+
invocation: { kind: "direct" },
|
|
1333
|
+
parameters: [],
|
|
1334
|
+
cancellation: { parameter: "signal" },
|
|
1335
|
+
result: {
|
|
1336
|
+
mode: "strict",
|
|
1337
|
+
typeSymbol: "ModelSettings.ChangeFrame",
|
|
1338
|
+
create: () => ({ parse: parseModelSettingsChangeFrame })
|
|
1339
|
+
}
|
|
1340
|
+
}]
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
/** Mount from a synchronous apply. Disposal waits for an in-flight mount and
|
|
1344
|
+
* then withdraws it, so the contribution cannot outlive the plugin. */
|
|
1345
|
+
function mountModelSettingsChanges(ctx, packageName) {
|
|
1346
|
+
const contribution = modelSettingsChangesRemote(packageName);
|
|
1347
|
+
ctx.inject(["remote"], (injected) => {
|
|
1348
|
+
const scope = injected;
|
|
1349
|
+
const mounting = scope.remote.$mount(contribution);
|
|
1350
|
+
mounting.catch(() => {});
|
|
1351
|
+
scope.effect(() => async () => {
|
|
1352
|
+
await (await mounting.catch(() => void 0))?.();
|
|
1353
|
+
});
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1232
1356
|
/** Native RemoteStream owns carrier reconnects; no timers or browser transport.
|
|
1233
1357
|
* A factory shares one logical stream while at least one subscriber remains. */
|
|
1234
|
-
function createModelSettingsSubscriber(ctx,
|
|
1358
|
+
function createModelSettingsSubscriber(ctx, packageName, identity = modelSettingsChangesIdentity(packageName), accept = (ns) => ns === packageName) {
|
|
1359
|
+
const endpoint = identity;
|
|
1235
1360
|
const listeners = /* @__PURE__ */ new Set();
|
|
1236
1361
|
let injection;
|
|
1237
1362
|
let current;
|
|
@@ -1257,12 +1382,18 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
1257
1382
|
};
|
|
1258
1383
|
const start = () => {
|
|
1259
1384
|
const life = ++lifecycle;
|
|
1260
|
-
injection = ctx.inject(["remote",
|
|
1385
|
+
injection = ctx.inject(["remote", `remote.${endpoint.namespace}`], (injected) => {
|
|
1261
1386
|
if (life !== lifecycle || !listeners.size) return;
|
|
1262
1387
|
const scope = injected;
|
|
1388
|
+
const service = scope.remote[endpoint.namespace];
|
|
1389
|
+
if (!service || !("watch" in service) || !service.watch) {
|
|
1390
|
+
report("Model settings sync service unavailable");
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
const watch = service.watch;
|
|
1263
1394
|
const stream = scope.remote.$stream({
|
|
1264
1395
|
name: "Model settings sync",
|
|
1265
|
-
open: (signal) =>
|
|
1396
|
+
open: (signal) => watch(signal),
|
|
1266
1397
|
ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
|
|
1267
1398
|
});
|
|
1268
1399
|
const session = {
|
|
@@ -1297,7 +1428,7 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
1297
1428
|
invalidate();
|
|
1298
1429
|
} else {
|
|
1299
1430
|
if (!ready) throw new Error("Model settings sync change arrived before ready");
|
|
1300
|
-
if (
|
|
1431
|
+
if (accept(frame.ns)) invalidate();
|
|
1301
1432
|
}
|
|
1302
1433
|
}
|
|
1303
1434
|
} catch (error) {
|
|
@@ -1429,20 +1560,6 @@ function messageOf(error) {
|
|
|
1429
1560
|
if (error && typeof error === "object" && "message" in error) return String(error.message);
|
|
1430
1561
|
return String(error);
|
|
1431
1562
|
}
|
|
1432
|
-
function checkedCatalog(value) {
|
|
1433
|
-
if (value && typeof value === "object" && "ok" in value && value.ok === false) throw new Error("error" in value ? messageOf(value.error) : "Request failed");
|
|
1434
|
-
return value;
|
|
1435
|
-
}
|
|
1436
|
-
function catalogFailures(value) {
|
|
1437
|
-
if (!value || typeof value !== "object") return [];
|
|
1438
|
-
const root = "ok" in value && value.ok === true && "value" in value ? value.value : value;
|
|
1439
|
-
if (!root || typeof root !== "object" || !("failures" in root) || !Array.isArray(root.failures)) return [];
|
|
1440
|
-
return root.failures.map((failure) => {
|
|
1441
|
-
if (!failure || typeof failure !== "object") return messageOf(failure);
|
|
1442
|
-
const provider = "name" in failure ? failure.name : "id" in failure ? failure.id : "provider" in failure ? failure.provider : "";
|
|
1443
|
-
return provider ? `${provider}: ${messageOf(failure)}` : messageOf(failure);
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
1563
|
//#endregion
|
|
1447
1564
|
//#region src/client-copy.ts
|
|
1448
1565
|
function hubText(value, language) {
|
|
@@ -1547,7 +1664,6 @@ const TEXT$1 = {
|
|
|
1547
1664
|
loading: "正在读取…",
|
|
1548
1665
|
failed: (message) => `读取失败:${message}`,
|
|
1549
1666
|
syncFailed: (message) => `自动同步不可用:${message}`,
|
|
1550
|
-
catalogFailed: (message) => `模型列表读取失败:${message}`,
|
|
1551
1667
|
writeFailed: (message) => `未保存:${message}。请刷新后重试。`,
|
|
1552
1668
|
saved: "已保存,正在刷新…",
|
|
1553
1669
|
savedRefreshFailed: (message) => `已保存;阵容刷新失败:${message}。请刷新以读取最新设置。`,
|
|
@@ -1570,7 +1686,13 @@ const TEXT$1 = {
|
|
|
1570
1686
|
effortDefault: "默认",
|
|
1571
1687
|
searchModels: "搜索模型…",
|
|
1572
1688
|
noModels: "没有匹配的模型",
|
|
1573
|
-
clearSearch: "清除搜索"
|
|
1689
|
+
clearSearch: "清除搜索",
|
|
1690
|
+
runningTitle: "运行中的子代理",
|
|
1691
|
+
runningEmpty: "当前没有运行中的子代理。",
|
|
1692
|
+
runningPending: "尚未发起模型请求",
|
|
1693
|
+
runningRemote: "远程子代理,无法读取路由",
|
|
1694
|
+
runningFailed: (message) => `运行状态读取失败:${message}`,
|
|
1695
|
+
effortAdapterDefault: (effort) => `${effort}(适配器默认)`
|
|
1574
1696
|
},
|
|
1575
1697
|
en: {
|
|
1576
1698
|
title: "Subagent Cast",
|
|
@@ -1579,7 +1701,6 @@ const TEXT$1 = {
|
|
|
1579
1701
|
loading: "Loading…",
|
|
1580
1702
|
failed: (message) => `Load failed: ${message}`,
|
|
1581
1703
|
syncFailed: (message) => `Live sync unavailable: ${message}`,
|
|
1582
|
-
catalogFailed: (message) => `Model list failed: ${message}`,
|
|
1583
1704
|
writeFailed: (message) => `Not saved: ${message}. Refresh, then try again.`,
|
|
1584
1705
|
saved: "Saved; refreshing…",
|
|
1585
1706
|
savedRefreshFailed: (message) => `Saved; cast refresh failed: ${message}. Refresh to read the latest settings.`,
|
|
@@ -1602,7 +1723,13 @@ const TEXT$1 = {
|
|
|
1602
1723
|
effortDefault: "Default",
|
|
1603
1724
|
searchModels: "Search models…",
|
|
1604
1725
|
noModels: "No matching models",
|
|
1605
|
-
clearSearch: "Clear search"
|
|
1726
|
+
clearSearch: "Clear search",
|
|
1727
|
+
runningTitle: "Running subagents",
|
|
1728
|
+
runningEmpty: "No subagents are running.",
|
|
1729
|
+
runningPending: "No model request yet",
|
|
1730
|
+
runningRemote: "Remote subagent; route unavailable",
|
|
1731
|
+
runningFailed: (message) => `Running state failed: ${message}`,
|
|
1732
|
+
effortAdapterDefault: (effort) => `${effort} (adapter default)`
|
|
1606
1733
|
}
|
|
1607
1734
|
};
|
|
1608
1735
|
function useText$1(locale) {
|
|
@@ -1625,6 +1752,11 @@ const castCss = `
|
|
|
1625
1752
|
.dsh-model-hub .cast-banner{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}
|
|
1626
1753
|
.dsh-model-hub .cast-add{display:flex}
|
|
1627
1754
|
.dsh-model-hub .cast-add .dsh-model-menu-root{max-width:100%}
|
|
1755
|
+
.dsh-model-hub .cast-running{margin-top:16px}
|
|
1756
|
+
.dsh-model-hub .cast-running-title{font-size:14px;line-height:22px;font-weight:600;margin:0 0 4px;color:var(--dsw-alias-label-primary)}
|
|
1757
|
+
.dsh-model-hub .cast-running-row{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;padding:6px 0;border-bottom:.5px solid var(--dsw-alias-border-l1)}
|
|
1758
|
+
.dsh-model-hub .cast-running-label{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;min-width:0}
|
|
1759
|
+
.dsh-model-hub .cast-running-route{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;min-width:0}
|
|
1628
1760
|
@media(max-width:520px){.dsh-model-hub .cast-row{grid-template-columns:minmax(0,1fr);gap:8px}}
|
|
1629
1761
|
`;
|
|
1630
1762
|
/** CAS rejections all carry a refresh hint in either language. */
|
|
@@ -1648,6 +1780,14 @@ function routeInput(route) {
|
|
|
1648
1780
|
model: normalized.model
|
|
1649
1781
|
};
|
|
1650
1782
|
}
|
|
1783
|
+
/** One live subagent row: label (or short id) plus its effective route. */
|
|
1784
|
+
function runningRouteText(entry, t) {
|
|
1785
|
+
if (!entry.local) return t("runningRemote");
|
|
1786
|
+
const route = entry.route;
|
|
1787
|
+
if (!route) return t("runningPending");
|
|
1788
|
+
const effort = route.reasoningEffort ? entry.effortIsAdapterDefault ? t("effortAdapterDefault", route.reasoningEffort) : route.reasoningEffort : "";
|
|
1789
|
+
return `${route.provider} / ${route.model}${effort ? ` · ${effort}` : ""}`;
|
|
1790
|
+
}
|
|
1651
1791
|
/** One cast entry: model picker, tendency input saved on blur, remove button. */
|
|
1652
1792
|
function CastRow({ t, catalog, route, tendencyRevision, draftReview, disabled, tendencyDisabled, menuDisabled, onSetRoute, onSetTendency, onRemove }) {
|
|
1653
1793
|
const label = `${route.provider} / ${route.model}`;
|
|
@@ -1731,6 +1871,8 @@ function CastPage({ api, locale }) {
|
|
|
1731
1871
|
const t = useText$1(locale);
|
|
1732
1872
|
const [cast, setCast] = (0, react.useState)(null);
|
|
1733
1873
|
const [catalog, setCatalog] = (0, react.useState)(null);
|
|
1874
|
+
const [running, setRunning] = (0, react.useState)(null);
|
|
1875
|
+
const [runningError, setRunningError] = (0, react.useState)("");
|
|
1734
1876
|
const [loadError, setLoadError] = (0, react.useState)("");
|
|
1735
1877
|
const [catalogError, setCatalogError] = (0, react.useState)("");
|
|
1736
1878
|
const [syncError, setSyncError] = (0, react.useState)("");
|
|
@@ -1750,7 +1892,11 @@ function CastPage({ api, locale }) {
|
|
|
1750
1892
|
scope.queued = false;
|
|
1751
1893
|
scope.busy = true;
|
|
1752
1894
|
setRefreshing(true);
|
|
1753
|
-
const [castResult, models] = await Promise.allSettled([
|
|
1895
|
+
const [castResult, models, live] = await Promise.allSettled([
|
|
1896
|
+
Promise.resolve().then(() => scope.api.getCast()),
|
|
1897
|
+
Promise.resolve().then(() => scope.api.modelCatalog()).then(checkedModelCatalog),
|
|
1898
|
+
Promise.resolve().then(() => scope.api.listRunning())
|
|
1899
|
+
]);
|
|
1754
1900
|
if (!scope.active) return;
|
|
1755
1901
|
if (castResult.status === "fulfilled") {
|
|
1756
1902
|
setCast(castResult.value);
|
|
@@ -1764,6 +1910,10 @@ function CastPage({ api, locale }) {
|
|
|
1764
1910
|
setCatalog(models.value);
|
|
1765
1911
|
setCatalogError("");
|
|
1766
1912
|
} else setCatalogError(messageOf(models.reason));
|
|
1913
|
+
if (live.status === "fulfilled") {
|
|
1914
|
+
setRunning(live.value);
|
|
1915
|
+
setRunningError("");
|
|
1916
|
+
} else setRunningError(messageOf(live.reason));
|
|
1767
1917
|
scope.busy = false;
|
|
1768
1918
|
setRefreshing(false);
|
|
1769
1919
|
if (scope.queued) refresh(scope);
|
|
@@ -1785,6 +1935,8 @@ function CastPage({ api, locale }) {
|
|
|
1785
1935
|
setSyncError("");
|
|
1786
1936
|
setCast(null);
|
|
1787
1937
|
setCatalog(null);
|
|
1938
|
+
setRunning(null);
|
|
1939
|
+
setRunningError("");
|
|
1788
1940
|
refresh(scope);
|
|
1789
1941
|
const unsubscribe = api.subscribeChanges?.(() => {
|
|
1790
1942
|
if (!scope.active) return;
|
|
@@ -1827,12 +1979,17 @@ function CastPage({ api, locale }) {
|
|
|
1827
1979
|
}
|
|
1828
1980
|
});
|
|
1829
1981
|
};
|
|
1830
|
-
const refreshNow = () => {
|
|
1982
|
+
const refreshNow = async () => {
|
|
1831
1983
|
const scope = scopeRef.current;
|
|
1832
|
-
if (scope?.api === api) refresh(scope, true);
|
|
1984
|
+
if (scope?.api === api && !scope.busy) await refresh(scope, true);
|
|
1833
1985
|
};
|
|
1834
1986
|
const disabled = saving !== "" || refreshing || writeError !== "" || saved && loadError !== "";
|
|
1835
|
-
const
|
|
1987
|
+
const catalogStatus = {
|
|
1988
|
+
loading: refreshing,
|
|
1989
|
+
error: catalogError ? hubMessage(catalogError, t.language) : "",
|
|
1990
|
+
failures: modelCatalogFailures(catalog),
|
|
1991
|
+
refresh: refreshNow
|
|
1992
|
+
};
|
|
1836
1993
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1837
1994
|
className: "cast-root dsh-ui-stack",
|
|
1838
1995
|
"aria-label": t("title"),
|
|
@@ -1877,16 +2034,10 @@ function CastPage({ api, locale }) {
|
|
|
1877
2034
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
1878
2035
|
children: t("syncFailed", hubMessage(syncError, t.language))
|
|
1879
2036
|
}) : null,
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
}) : null,
|
|
1885
|
-
failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1886
|
-
role: "alert",
|
|
1887
|
-
className: "dsh-ui-error dsh-ui-wrap",
|
|
1888
|
-
children: t("catalogFailed", hubMessage(failure, t.language))
|
|
1889
|
-
}, index)),
|
|
2037
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
2038
|
+
state: catalogStatus,
|
|
2039
|
+
locale: t.language
|
|
2040
|
+
}),
|
|
1890
2041
|
writeError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1891
2042
|
className: "dsh-ui-banner dsh-ui-banner--danger cast-banner",
|
|
1892
2043
|
role: "alert",
|
|
@@ -1963,7 +2114,40 @@ function CastPage({ api, locale }) {
|
|
|
1963
2114
|
expectedRevision: cast.revision
|
|
1964
2115
|
}))
|
|
1965
2116
|
})
|
|
1966
|
-
}) : null
|
|
2117
|
+
}) : null,
|
|
2118
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2119
|
+
className: "cast-running",
|
|
2120
|
+
"aria-label": t("runningTitle"),
|
|
2121
|
+
children: [
|
|
2122
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2123
|
+
className: "cast-running-title",
|
|
2124
|
+
children: t("runningTitle")
|
|
2125
|
+
}),
|
|
2126
|
+
runningError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2127
|
+
role: "alert",
|
|
2128
|
+
className: "dsh-ui-error dsh-ui-wrap",
|
|
2129
|
+
children: t("runningFailed", hubMessage(runningError, t.language))
|
|
2130
|
+
}) : null,
|
|
2131
|
+
running === null && !runningError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2132
|
+
className: "dsh-ui-loading",
|
|
2133
|
+
children: t("loading")
|
|
2134
|
+
}) : null,
|
|
2135
|
+
running !== null && running.entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2136
|
+
className: "dsh-ui-help",
|
|
2137
|
+
children: t("runningEmpty")
|
|
2138
|
+
}) : null,
|
|
2139
|
+
running?.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2140
|
+
className: "cast-running-row",
|
|
2141
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2142
|
+
className: "cast-running-label",
|
|
2143
|
+
children: entry.label || entry.id
|
|
2144
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2145
|
+
className: "cast-running-route",
|
|
2146
|
+
children: runningRouteText(entry, t)
|
|
2147
|
+
})]
|
|
2148
|
+
}, entry.id))
|
|
2149
|
+
]
|
|
2150
|
+
})
|
|
1967
2151
|
]
|
|
1968
2152
|
});
|
|
1969
2153
|
}
|
|
@@ -1981,8 +2165,7 @@ const name = "dsh-model-hub-client";
|
|
|
1981
2165
|
const inject = [
|
|
1982
2166
|
"slots",
|
|
1983
2167
|
"connection",
|
|
1984
|
-
|
|
1985
|
-
"remote.session",
|
|
2168
|
+
...MODEL_CATALOG_INJECT,
|
|
1986
2169
|
"locale"
|
|
1987
2170
|
];
|
|
1988
2171
|
const CHANNEL = "/dsh-model-hub";
|
|
@@ -2114,7 +2297,7 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2114
2297
|
scope.queued = false;
|
|
2115
2298
|
scope.busy = true;
|
|
2116
2299
|
setRefreshing(true);
|
|
2117
|
-
const [fields, models] = await Promise.allSettled([Promise.resolve().then(() => scope.api.list()), Promise.resolve().then(() => scope.api.modelCatalog()).then(
|
|
2300
|
+
const [fields, models] = await Promise.allSettled([Promise.resolve().then(() => scope.api.list()), Promise.resolve().then(() => scope.api.modelCatalog()).then(checkedModelCatalog)]);
|
|
2118
2301
|
if (!scope.active) return;
|
|
2119
2302
|
if (fields.status === "fulfilled") {
|
|
2120
2303
|
setEntries(fields.value);
|
|
@@ -2227,7 +2410,16 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2227
2410
|
})
|
|
2228
2411
|
})).filter((entry) => entry.fields.length > 0 || (entry.error || entry.catalogError) && (!needle || [entry.ns, pluginTitle(entry, t)].some((text) => text.toLocaleLowerCase().includes(needle))));
|
|
2229
2412
|
const disabled = writing !== "" || refreshing || writeError !== "" || saved && listError !== "";
|
|
2230
|
-
const
|
|
2413
|
+
const refreshNow = async () => {
|
|
2414
|
+
const scope = scopeRef.current;
|
|
2415
|
+
if (scope?.api === api && !scope.busy) await refresh(scope);
|
|
2416
|
+
};
|
|
2417
|
+
const catalogStatus = {
|
|
2418
|
+
loading: refreshing,
|
|
2419
|
+
error: catalogError ? hubMessage(catalogError, t.language) : "",
|
|
2420
|
+
failures: modelCatalogFailures(catalog),
|
|
2421
|
+
refresh: refreshNow
|
|
2422
|
+
};
|
|
2231
2423
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2232
2424
|
className: "dsh-model-hub dsh-ui-stack",
|
|
2233
2425
|
lang: t.language,
|
|
@@ -2271,10 +2463,7 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2271
2463
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2272
2464
|
variant: "ghost",
|
|
2273
2465
|
disabled: writing !== "" || refreshing,
|
|
2274
|
-
onClick:
|
|
2275
|
-
const scope = scopeRef.current;
|
|
2276
|
-
if (scope?.api === api) refresh(scope);
|
|
2277
|
-
},
|
|
2466
|
+
onClick: refreshNow,
|
|
2278
2467
|
children: refreshing ? t("refreshing") : t("refresh")
|
|
2279
2468
|
})]
|
|
2280
2469
|
}),
|
|
@@ -2288,16 +2477,10 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2288
2477
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
2289
2478
|
children: t("syncFailed", hubMessage(syncError, t.language))
|
|
2290
2479
|
}) : null,
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
}) : null,
|
|
2296
|
-
failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2297
|
-
role: "alert",
|
|
2298
|
-
className: "dsh-ui-error dsh-ui-wrap",
|
|
2299
|
-
children: t("catalogFailed", hubMessage(failure, t.language))
|
|
2300
|
-
}, index)),
|
|
2480
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
2481
|
+
state: catalogStatus,
|
|
2482
|
+
locale: t.language
|
|
2483
|
+
}),
|
|
2301
2484
|
writeError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2302
2485
|
role: "alert",
|
|
2303
2486
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
@@ -2404,11 +2587,10 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2404
2587
|
]
|
|
2405
2588
|
});
|
|
2406
2589
|
}
|
|
2407
|
-
|
|
2590
|
+
function apply(ctx) {
|
|
2408
2591
|
const client = ctx;
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
const changes = createModelSettingsSubscriber(ctx, "*");
|
|
2592
|
+
mountModelSettingsChanges(ctx, BUNDLE);
|
|
2593
|
+
const changes = createModelSettingsSubscriber(ctx, BUNDLE, modelSettingsChangesIdentity(BUNDLE), () => true);
|
|
2412
2594
|
const call = async (endpoint, payload) => {
|
|
2413
2595
|
const result = await client.connection.rpc.call(CHANNEL, endpoint, payload);
|
|
2414
2596
|
if (!result?.ok) throw new Error(result?.error?.message || "Request failed");
|
|
@@ -2417,14 +2599,15 @@ async function apply(ctx) {
|
|
|
2417
2599
|
const api = Object.freeze({
|
|
2418
2600
|
list: () => call("fields.list", {}),
|
|
2419
2601
|
setField: (payload) => call("fields.set", payload),
|
|
2420
|
-
modelCatalog: () => client
|
|
2602
|
+
modelCatalog: () => readModelCatalog(client),
|
|
2421
2603
|
subscribeChanges: changes.subscribe,
|
|
2422
2604
|
getCast: () => call("cast.get", {}),
|
|
2423
2605
|
setCastEnabled: (payload) => call("cast.setEnabled", payload),
|
|
2424
2606
|
addCastRoute: (payload) => call("cast.addRoute", payload),
|
|
2425
2607
|
setCastRoute: (payload) => call("cast.setRoute", payload),
|
|
2426
2608
|
removeCastRoute: (payload) => call("cast.removeRoute", payload),
|
|
2427
|
-
setCastTendency: (payload) => call("cast.setTendency", payload)
|
|
2609
|
+
setCastTendency: (payload) => call("cast.setTendency", payload),
|
|
2610
|
+
listRunning: () => call("cast.running", {})
|
|
2428
2611
|
});
|
|
2429
2612
|
client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
2430
2613
|
name: "plugins.bundle.config",
|