@klarkxy/dsh-model-hub 0.1.1 → 0.1.3
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 +7 -3
- package/docs/README.zh-CN.md +7 -3
- package/lib/client.inner.cjs +238 -53
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +238 -53
- package/lib/index.js +143 -5
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[简体中文](docs/README.zh-CN.md)
|
|
6
6
|
|
|
7
|
-
See and change the models used by your DeepSeek Harness plugins from one page,
|
|
7
|
+
See and change the models used by your DeepSeek Harness plugins from one page, manage which models subagents may use, and watch which model and reasoning effort each running subagent is on.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -18,13 +18,17 @@ Replace `web` with your profile name and restart the profile.
|
|
|
18
18
|
|
|
19
19
|
Open **Plugins → Model Hub**.
|
|
20
20
|
|
|
21
|
-
- **Fields**: every model setting exposed by installed plugins, such as Auto Title, Git Commit, Safe Auto
|
|
22
|
-
- **Subagent Cast**: turn the subagent model allowlist on or off and add, replace, or remove models.
|
|
21
|
+
- **Fields**: every model setting exposed by installed plugins, such as Auto Title, Git Commit, and Safe Auto. Search by plugin, purpose, or current model, then pick a new model. Changes are saved by the plugin that owns the setting.
|
|
22
|
+
- **Subagent Cast**: turn the subagent model allowlist on or off and add, replace, or remove models. When enabled with at least one model, it guides the agent to discover the models available in the current conversation and explicitly choose one when delegating. For independent reviews and alternative approaches, it encourages a different model when suitable, while allowing the same model when it fits better. You can add your own task tendencies for individual models; they remain optional hints, and no model-specific tendencies are supplied by default. Below the list, a live view shows each running subagent with the model and reasoning effort it is currently using; an effort marked as adapter default comes from the adapter rather than an explicit choice.
|
|
23
23
|
|
|
24
24
|
## Limits
|
|
25
25
|
|
|
26
26
|
- Only plugins that expose model settings appear.
|
|
27
27
|
- If a setting changed elsewhere, refresh and choose again.
|
|
28
|
+
- If the model list cannot load, use **Retry**. The last available list stays visible; warnings identify providers that could not be loaded while other providers remain usable.
|
|
29
|
+
- Model selection guidance requires host tools that support choosing a subagent model. Existing conversations retain their saved model lists; start a new conversation to use an updated list.
|
|
30
|
+
- Guidance does not force a model choice or add extra subtasks solely for variety.
|
|
31
|
+
- Remote subagents and finished runs have no readable route; the live list covers only live local subagents.
|
|
28
32
|
- No usage statistics.
|
|
29
33
|
|
|
30
34
|
## License
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[English](../README.md)
|
|
6
6
|
|
|
7
|
-
在一个页面查看并修改各个 DeepSeek Harness
|
|
7
|
+
在一个页面查看并修改各个 DeepSeek Harness 插件使用的模型,管理子代理可用的模型,并查看运行中子代理当前使用的模型与思考强度。
|
|
8
8
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
@@ -18,13 +18,17 @@ dsh plugin --profile web add @klarkxy/dsh-model-hub
|
|
|
18
18
|
|
|
19
19
|
打开「插件 → 模型枢纽」。
|
|
20
20
|
|
|
21
|
-
- **字段总览**:列出已安装插件提供的模型设置,例如自动标题、Git
|
|
22
|
-
-
|
|
21
|
+
- **字段总览**:列出已安装插件提供的模型设置,例如自动标题、Git 提交和安全自动。可以按插件、用途或当前模型搜索,再选择新模型;改动由对应插件保存。
|
|
22
|
+
- **子代理阵容**:开关子代理模型白名单,添加、替换或移除模型。开启且至少有一个模型时,会引导智能体发现当前会话可用的模型,并在派发时显式选择。独立复核和方案对比会优先考虑合适的不同模型,同模型更适合时仍可使用。你可以自行填写各模型的「任务倾向」作为可选参考;插件不预置模型专用倾向。页面下方实时列出运行中的子代理及各自当前使用的模型和思考强度;标注「适配器默认」的强度来自适配器而非显式选择。
|
|
23
23
|
|
|
24
24
|
## 限制
|
|
25
25
|
|
|
26
26
|
- 只显示提供了模型设置的插件。
|
|
27
27
|
- 设置在别处被改动时,刷新后重新选择。
|
|
28
|
+
- 模型列表读取失败时,点击「重试」。上次可用的列表会保留;部分提供方读取失败时会显示提示,其他提供方仍可使用。
|
|
29
|
+
- 选模引导需要宿主工具支持选择子代理模型。已有会话保留各自的模型名单;修改名单后,新建会话才能使用更新后的名单。
|
|
30
|
+
- 引导不强制模型选择,也不要求仅为增加模型种类而新增子任务。
|
|
31
|
+
- 远程子代理和已结束的子代理没有可读取的路由;运行列表只覆盖存活的本地子代理。
|
|
28
32
|
- 不提供用量统计。
|
|
29
33
|
|
|
30
34
|
## 许可证
|
package/lib/client.inner.cjs
CHANGED
|
@@ -61,12 +61,68 @@ function normalizeModelRoute(value) {
|
|
|
61
61
|
}
|
|
62
62
|
//#endregion
|
|
63
63
|
//#region ../dsh-model-route/src/catalog.ts
|
|
64
|
+
/** Both services are required: Cordis checks the nested namespace separately. */
|
|
65
|
+
const MODEL_CATALOG_INJECT = ["remote", "remote.session"];
|
|
64
66
|
function asRecord(value) {
|
|
65
67
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
66
68
|
}
|
|
67
69
|
function asText(value) {
|
|
68
70
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
69
71
|
}
|
|
72
|
+
/** A useful diagnostic even when a transport rejects with a plain object. */
|
|
73
|
+
function modelCatalogErrorMessage(value) {
|
|
74
|
+
if (value instanceof Error && value.message) return value.message;
|
|
75
|
+
return asText(asRecord(value)?.message) ?? asText(value) ?? "Unable to load the model catalog.";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate and unwrap a successful catalog without discarding provider failures
|
|
79
|
+
* or newer host metadata. Names and metadata remain optional for older hosts.
|
|
80
|
+
* A broken response is never presented as a successfully loaded empty menu.
|
|
81
|
+
*/
|
|
82
|
+
function checkedModelCatalog(value) {
|
|
83
|
+
const envelope = asRecord(value);
|
|
84
|
+
if (envelope?.ok === false) throw new Error(modelCatalogErrorMessage(envelope.error ?? envelope));
|
|
85
|
+
const root = envelope?.ok === true ? asRecord(envelope.value) : envelope;
|
|
86
|
+
const malformed = (field) => {
|
|
87
|
+
throw new Error(`Invalid model catalog: ${field}.`);
|
|
88
|
+
};
|
|
89
|
+
if (!root || !Array.isArray(root.groups)) return malformed("groups must be an array");
|
|
90
|
+
for (const value of root.groups) {
|
|
91
|
+
const group = asRecord(value);
|
|
92
|
+
if (!group || !asText(group.id) || !Array.isArray(group.models)) return malformed("each group needs an id and models array");
|
|
93
|
+
for (const value of group.models) {
|
|
94
|
+
const model = asRecord(value);
|
|
95
|
+
if (!model || !asText(model.id)) return malformed("each model needs an id");
|
|
96
|
+
if (model.reasoning !== void 0) {
|
|
97
|
+
const reasoning = asRecord(model.reasoning);
|
|
98
|
+
if (!reasoning || !Array.isArray(reasoning.efforts) || reasoning.efforts.some((value) => !parseEffort(value))) return malformed("reasoning efforts must be an array of identified efforts");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (root.failures !== void 0 && (!Array.isArray(root.failures) || root.failures.some((value) => {
|
|
103
|
+
const failure = asRecord(value);
|
|
104
|
+
return !failure || !asText(failure.id) || typeof failure.name !== "string" || typeof failure.message !== "string";
|
|
105
|
+
}))) return malformed("failures must be an array of provider failures");
|
|
106
|
+
return root;
|
|
107
|
+
}
|
|
108
|
+
/** Read through the injected session namespace; absence and rejection are errors. */
|
|
109
|
+
async function readModelCatalog(client) {
|
|
110
|
+
const session = client.remote?.session;
|
|
111
|
+
if (typeof session?.modelCatalog !== "function") throw new Error("Model catalog unavailable: inject both remote and remote.session.");
|
|
112
|
+
return checkedModelCatalog(await session.modelCatalog());
|
|
113
|
+
}
|
|
114
|
+
/** Provider failures are partial results, not a transport failure. */
|
|
115
|
+
function modelCatalogFailures(value) {
|
|
116
|
+
try {
|
|
117
|
+
return checkedModelCatalog(value).failures ?? [];
|
|
118
|
+
} catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Text adapter for existing model-hub status messages. */
|
|
123
|
+
function catalogFailures(value) {
|
|
124
|
+
return modelCatalogFailures(value).map((failure) => `${failure.name || failure.id}: ${failure.message}`);
|
|
125
|
+
}
|
|
70
126
|
function parseEffort(value) {
|
|
71
127
|
const row = asRecord(value);
|
|
72
128
|
const id = row ? asText(row.id) : void 0;
|
|
@@ -163,6 +219,59 @@ function groupModelMenuChoices(choices, query) {
|
|
|
163
219
|
return groups;
|
|
164
220
|
}
|
|
165
221
|
//#endregion
|
|
222
|
+
//#region ../dsh-model-route/src/catalog-ui.tsx
|
|
223
|
+
/** Also accepts a mapped state from a caller that owns broader async orchestration. */
|
|
224
|
+
function ModelCatalogStatus({ state, locale }) {
|
|
225
|
+
const zh = locale?.toLowerCase().startsWith("zh") === true;
|
|
226
|
+
const copy = zh ? {
|
|
227
|
+
loading: "正在加载模型…",
|
|
228
|
+
error: "模型列表加载失败",
|
|
229
|
+
partial: "部分提供商的模型未能加载",
|
|
230
|
+
retry: "重试"
|
|
231
|
+
} : {
|
|
232
|
+
loading: "Loading models…",
|
|
233
|
+
error: "Unable to load models",
|
|
234
|
+
partial: "Some providers could not load their models",
|
|
235
|
+
retry: "Retry"
|
|
236
|
+
};
|
|
237
|
+
if (!state.loading && !state.error && state.failures.length === 0) return null;
|
|
238
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
239
|
+
className: "dsh-model-catalog-status",
|
|
240
|
+
lang: zh ? "zh" : "en",
|
|
241
|
+
children: [
|
|
242
|
+
state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
243
|
+
role: "status",
|
|
244
|
+
children: copy.loading
|
|
245
|
+
}) : null,
|
|
246
|
+
state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
247
|
+
role: "alert",
|
|
248
|
+
children: [
|
|
249
|
+
copy.error,
|
|
250
|
+
": ",
|
|
251
|
+
state.error
|
|
252
|
+
]
|
|
253
|
+
}) : null,
|
|
254
|
+
state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
255
|
+
role: "status",
|
|
256
|
+
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: [
|
|
257
|
+
failure.name || failure.id,
|
|
258
|
+
": ",
|
|
259
|
+
failure.message
|
|
260
|
+
] }, `${failure.id}:${index}`)) })]
|
|
261
|
+
}) : null,
|
|
262
|
+
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
263
|
+
type: "button",
|
|
264
|
+
size: "sm",
|
|
265
|
+
disabled: state.loading,
|
|
266
|
+
onClick: () => {
|
|
267
|
+
state.refresh();
|
|
268
|
+
},
|
|
269
|
+
children: copy.retry
|
|
270
|
+
}) : null
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
166
275
|
//#region ../dsh-model-route/src/ui.tsx
|
|
167
276
|
/**
|
|
168
277
|
* ModelMenu: the standard editor of one model-route field — the visible half
|
|
@@ -1172,6 +1281,29 @@ ${scope} {
|
|
|
1172
1281
|
& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
1173
1282
|
& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
1174
1283
|
|
|
1284
|
+
/* --- Native textarea. The primitives ship no textarea either, so a settings
|
|
1285
|
+
* prompt keeps the platform element and the same field shell as the select:
|
|
1286
|
+
* 0.5px l4, radius-md, layer-3, 13/1.5 type. It grows vertically instead of
|
|
1287
|
+
* locking to the select's 34px row. --- */
|
|
1288
|
+
& .dsh-ui-textarea {
|
|
1289
|
+
box-sizing: border-box;
|
|
1290
|
+
width: 100%;
|
|
1291
|
+
min-width: 0;
|
|
1292
|
+
resize: vertical;
|
|
1293
|
+
padding: 8px 12px;
|
|
1294
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
1295
|
+
border-radius: var(--dsw-radius-md);
|
|
1296
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1297
|
+
font: inherit;
|
|
1298
|
+
font-size: 13px;
|
|
1299
|
+
line-height: 1.5;
|
|
1300
|
+
color: var(--dsw-alias-label-primary);
|
|
1301
|
+
}
|
|
1302
|
+
& .dsh-ui-textarea:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }
|
|
1303
|
+
& .dsh-ui-textarea:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
1304
|
+
& .dsh-ui-textarea::placeholder { color: var(--dsw-alias-label-dimmed); }
|
|
1305
|
+
& .dsh-ui-textarea[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
1306
|
+
|
|
1175
1307
|
/* Long panels scroll inside themselves instead of growing the host page. */
|
|
1176
1308
|
& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
|
|
1177
1309
|
& .dsh-ui-panel-wide { max-width: 100%; }
|
|
@@ -1429,20 +1561,6 @@ function messageOf(error) {
|
|
|
1429
1561
|
if (error && typeof error === "object" && "message" in error) return String(error.message);
|
|
1430
1562
|
return String(error);
|
|
1431
1563
|
}
|
|
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
1564
|
//#endregion
|
|
1447
1565
|
//#region src/client-copy.ts
|
|
1448
1566
|
function hubText(value, language) {
|
|
@@ -1547,7 +1665,6 @@ const TEXT$1 = {
|
|
|
1547
1665
|
loading: "正在读取…",
|
|
1548
1666
|
failed: (message) => `读取失败:${message}`,
|
|
1549
1667
|
syncFailed: (message) => `自动同步不可用:${message}`,
|
|
1550
|
-
catalogFailed: (message) => `模型列表读取失败:${message}`,
|
|
1551
1668
|
writeFailed: (message) => `未保存:${message}。请刷新后重试。`,
|
|
1552
1669
|
saved: "已保存,正在刷新…",
|
|
1553
1670
|
savedRefreshFailed: (message) => `已保存;阵容刷新失败:${message}。请刷新以读取最新设置。`,
|
|
@@ -1570,7 +1687,13 @@ const TEXT$1 = {
|
|
|
1570
1687
|
effortDefault: "默认",
|
|
1571
1688
|
searchModels: "搜索模型…",
|
|
1572
1689
|
noModels: "没有匹配的模型",
|
|
1573
|
-
clearSearch: "清除搜索"
|
|
1690
|
+
clearSearch: "清除搜索",
|
|
1691
|
+
runningTitle: "运行中的子代理",
|
|
1692
|
+
runningEmpty: "当前没有运行中的子代理。",
|
|
1693
|
+
runningPending: "尚未发起模型请求",
|
|
1694
|
+
runningRemote: "远程子代理,无法读取路由",
|
|
1695
|
+
runningFailed: (message) => `运行状态读取失败:${message}`,
|
|
1696
|
+
effortAdapterDefault: (effort) => `${effort}(适配器默认)`
|
|
1574
1697
|
},
|
|
1575
1698
|
en: {
|
|
1576
1699
|
title: "Subagent Cast",
|
|
@@ -1579,7 +1702,6 @@ const TEXT$1 = {
|
|
|
1579
1702
|
loading: "Loading…",
|
|
1580
1703
|
failed: (message) => `Load failed: ${message}`,
|
|
1581
1704
|
syncFailed: (message) => `Live sync unavailable: ${message}`,
|
|
1582
|
-
catalogFailed: (message) => `Model list failed: ${message}`,
|
|
1583
1705
|
writeFailed: (message) => `Not saved: ${message}. Refresh, then try again.`,
|
|
1584
1706
|
saved: "Saved; refreshing…",
|
|
1585
1707
|
savedRefreshFailed: (message) => `Saved; cast refresh failed: ${message}. Refresh to read the latest settings.`,
|
|
@@ -1602,7 +1724,13 @@ const TEXT$1 = {
|
|
|
1602
1724
|
effortDefault: "Default",
|
|
1603
1725
|
searchModels: "Search models…",
|
|
1604
1726
|
noModels: "No matching models",
|
|
1605
|
-
clearSearch: "Clear search"
|
|
1727
|
+
clearSearch: "Clear search",
|
|
1728
|
+
runningTitle: "Running subagents",
|
|
1729
|
+
runningEmpty: "No subagents are running.",
|
|
1730
|
+
runningPending: "No model request yet",
|
|
1731
|
+
runningRemote: "Remote subagent; route unavailable",
|
|
1732
|
+
runningFailed: (message) => `Running state failed: ${message}`,
|
|
1733
|
+
effortAdapterDefault: (effort) => `${effort} (adapter default)`
|
|
1606
1734
|
}
|
|
1607
1735
|
};
|
|
1608
1736
|
function useText$1(locale) {
|
|
@@ -1625,6 +1753,11 @@ const castCss = `
|
|
|
1625
1753
|
.dsh-model-hub .cast-banner{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}
|
|
1626
1754
|
.dsh-model-hub .cast-add{display:flex}
|
|
1627
1755
|
.dsh-model-hub .cast-add .dsh-model-menu-root{max-width:100%}
|
|
1756
|
+
.dsh-model-hub .cast-running{margin-top:16px}
|
|
1757
|
+
.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)}
|
|
1758
|
+
.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)}
|
|
1759
|
+
.dsh-model-hub .cast-running-label{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;min-width:0}
|
|
1760
|
+
.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
1761
|
@media(max-width:520px){.dsh-model-hub .cast-row{grid-template-columns:minmax(0,1fr);gap:8px}}
|
|
1629
1762
|
`;
|
|
1630
1763
|
/** CAS rejections all carry a refresh hint in either language. */
|
|
@@ -1648,6 +1781,14 @@ function routeInput(route) {
|
|
|
1648
1781
|
model: normalized.model
|
|
1649
1782
|
};
|
|
1650
1783
|
}
|
|
1784
|
+
/** One live subagent row: label (or short id) plus its effective route. */
|
|
1785
|
+
function runningRouteText(entry, t) {
|
|
1786
|
+
if (!entry.local) return t("runningRemote");
|
|
1787
|
+
const route = entry.route;
|
|
1788
|
+
if (!route) return t("runningPending");
|
|
1789
|
+
const effort = route.reasoningEffort ? entry.effortIsAdapterDefault ? t("effortAdapterDefault", route.reasoningEffort) : route.reasoningEffort : "";
|
|
1790
|
+
return `${route.provider} / ${route.model}${effort ? ` · ${effort}` : ""}`;
|
|
1791
|
+
}
|
|
1651
1792
|
/** One cast entry: model picker, tendency input saved on blur, remove button. */
|
|
1652
1793
|
function CastRow({ t, catalog, route, tendencyRevision, draftReview, disabled, tendencyDisabled, menuDisabled, onSetRoute, onSetTendency, onRemove }) {
|
|
1653
1794
|
const label = `${route.provider} / ${route.model}`;
|
|
@@ -1731,6 +1872,8 @@ function CastPage({ api, locale }) {
|
|
|
1731
1872
|
const t = useText$1(locale);
|
|
1732
1873
|
const [cast, setCast] = (0, react.useState)(null);
|
|
1733
1874
|
const [catalog, setCatalog] = (0, react.useState)(null);
|
|
1875
|
+
const [running, setRunning] = (0, react.useState)(null);
|
|
1876
|
+
const [runningError, setRunningError] = (0, react.useState)("");
|
|
1734
1877
|
const [loadError, setLoadError] = (0, react.useState)("");
|
|
1735
1878
|
const [catalogError, setCatalogError] = (0, react.useState)("");
|
|
1736
1879
|
const [syncError, setSyncError] = (0, react.useState)("");
|
|
@@ -1750,7 +1893,11 @@ function CastPage({ api, locale }) {
|
|
|
1750
1893
|
scope.queued = false;
|
|
1751
1894
|
scope.busy = true;
|
|
1752
1895
|
setRefreshing(true);
|
|
1753
|
-
const [castResult, models] = await Promise.allSettled([
|
|
1896
|
+
const [castResult, models, live] = await Promise.allSettled([
|
|
1897
|
+
Promise.resolve().then(() => scope.api.getCast()),
|
|
1898
|
+
Promise.resolve().then(() => scope.api.modelCatalog()).then(checkedModelCatalog),
|
|
1899
|
+
Promise.resolve().then(() => scope.api.listRunning())
|
|
1900
|
+
]);
|
|
1754
1901
|
if (!scope.active) return;
|
|
1755
1902
|
if (castResult.status === "fulfilled") {
|
|
1756
1903
|
setCast(castResult.value);
|
|
@@ -1764,6 +1911,10 @@ function CastPage({ api, locale }) {
|
|
|
1764
1911
|
setCatalog(models.value);
|
|
1765
1912
|
setCatalogError("");
|
|
1766
1913
|
} else setCatalogError(messageOf(models.reason));
|
|
1914
|
+
if (live.status === "fulfilled") {
|
|
1915
|
+
setRunning(live.value);
|
|
1916
|
+
setRunningError("");
|
|
1917
|
+
} else setRunningError(messageOf(live.reason));
|
|
1767
1918
|
scope.busy = false;
|
|
1768
1919
|
setRefreshing(false);
|
|
1769
1920
|
if (scope.queued) refresh(scope);
|
|
@@ -1785,6 +1936,8 @@ function CastPage({ api, locale }) {
|
|
|
1785
1936
|
setSyncError("");
|
|
1786
1937
|
setCast(null);
|
|
1787
1938
|
setCatalog(null);
|
|
1939
|
+
setRunning(null);
|
|
1940
|
+
setRunningError("");
|
|
1788
1941
|
refresh(scope);
|
|
1789
1942
|
const unsubscribe = api.subscribeChanges?.(() => {
|
|
1790
1943
|
if (!scope.active) return;
|
|
@@ -1827,12 +1980,17 @@ function CastPage({ api, locale }) {
|
|
|
1827
1980
|
}
|
|
1828
1981
|
});
|
|
1829
1982
|
};
|
|
1830
|
-
const refreshNow = () => {
|
|
1983
|
+
const refreshNow = async () => {
|
|
1831
1984
|
const scope = scopeRef.current;
|
|
1832
|
-
if (scope?.api === api) refresh(scope, true);
|
|
1985
|
+
if (scope?.api === api && !scope.busy) await refresh(scope, true);
|
|
1833
1986
|
};
|
|
1834
1987
|
const disabled = saving !== "" || refreshing || writeError !== "" || saved && loadError !== "";
|
|
1835
|
-
const
|
|
1988
|
+
const catalogStatus = {
|
|
1989
|
+
loading: refreshing,
|
|
1990
|
+
error: catalogError ? hubMessage(catalogError, t.language) : "",
|
|
1991
|
+
failures: modelCatalogFailures(catalog),
|
|
1992
|
+
refresh: refreshNow
|
|
1993
|
+
};
|
|
1836
1994
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1837
1995
|
className: "cast-root dsh-ui-stack",
|
|
1838
1996
|
"aria-label": t("title"),
|
|
@@ -1877,16 +2035,10 @@ function CastPage({ api, locale }) {
|
|
|
1877
2035
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
1878
2036
|
children: t("syncFailed", hubMessage(syncError, t.language))
|
|
1879
2037
|
}) : 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)),
|
|
2038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
2039
|
+
state: catalogStatus,
|
|
2040
|
+
locale: t.language
|
|
2041
|
+
}),
|
|
1890
2042
|
writeError ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1891
2043
|
className: "dsh-ui-banner dsh-ui-banner--danger cast-banner",
|
|
1892
2044
|
role: "alert",
|
|
@@ -1963,7 +2115,40 @@ function CastPage({ api, locale }) {
|
|
|
1963
2115
|
expectedRevision: cast.revision
|
|
1964
2116
|
}))
|
|
1965
2117
|
})
|
|
1966
|
-
}) : null
|
|
2118
|
+
}) : null,
|
|
2119
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2120
|
+
className: "cast-running",
|
|
2121
|
+
"aria-label": t("runningTitle"),
|
|
2122
|
+
children: [
|
|
2123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2124
|
+
className: "cast-running-title",
|
|
2125
|
+
children: t("runningTitle")
|
|
2126
|
+
}),
|
|
2127
|
+
runningError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2128
|
+
role: "alert",
|
|
2129
|
+
className: "dsh-ui-error dsh-ui-wrap",
|
|
2130
|
+
children: t("runningFailed", hubMessage(runningError, t.language))
|
|
2131
|
+
}) : null,
|
|
2132
|
+
running === null && !runningError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2133
|
+
className: "dsh-ui-loading",
|
|
2134
|
+
children: t("loading")
|
|
2135
|
+
}) : null,
|
|
2136
|
+
running !== null && running.entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2137
|
+
className: "dsh-ui-help",
|
|
2138
|
+
children: t("runningEmpty")
|
|
2139
|
+
}) : null,
|
|
2140
|
+
running?.entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2141
|
+
className: "cast-running-row",
|
|
2142
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2143
|
+
className: "cast-running-label",
|
|
2144
|
+
children: entry.label || entry.id
|
|
2145
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2146
|
+
className: "cast-running-route",
|
|
2147
|
+
children: runningRouteText(entry, t)
|
|
2148
|
+
})]
|
|
2149
|
+
}, entry.id))
|
|
2150
|
+
]
|
|
2151
|
+
})
|
|
1967
2152
|
]
|
|
1968
2153
|
});
|
|
1969
2154
|
}
|
|
@@ -1981,8 +2166,7 @@ const name = "dsh-model-hub-client";
|
|
|
1981
2166
|
const inject = [
|
|
1982
2167
|
"slots",
|
|
1983
2168
|
"connection",
|
|
1984
|
-
|
|
1985
|
-
"remote.session",
|
|
2169
|
+
...MODEL_CATALOG_INJECT,
|
|
1986
2170
|
"locale"
|
|
1987
2171
|
];
|
|
1988
2172
|
const CHANNEL = "/dsh-model-hub";
|
|
@@ -2114,7 +2298,7 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2114
2298
|
scope.queued = false;
|
|
2115
2299
|
scope.busy = true;
|
|
2116
2300
|
setRefreshing(true);
|
|
2117
|
-
const [fields, models] = await Promise.allSettled([Promise.resolve().then(() => scope.api.list()), Promise.resolve().then(() => scope.api.modelCatalog()).then(
|
|
2301
|
+
const [fields, models] = await Promise.allSettled([Promise.resolve().then(() => scope.api.list()), Promise.resolve().then(() => scope.api.modelCatalog()).then(checkedModelCatalog)]);
|
|
2118
2302
|
if (!scope.active) return;
|
|
2119
2303
|
if (fields.status === "fulfilled") {
|
|
2120
2304
|
setEntries(fields.value);
|
|
@@ -2227,7 +2411,16 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2227
2411
|
})
|
|
2228
2412
|
})).filter((entry) => entry.fields.length > 0 || (entry.error || entry.catalogError) && (!needle || [entry.ns, pluginTitle(entry, t)].some((text) => text.toLocaleLowerCase().includes(needle))));
|
|
2229
2413
|
const disabled = writing !== "" || refreshing || writeError !== "" || saved && listError !== "";
|
|
2230
|
-
const
|
|
2414
|
+
const refreshNow = async () => {
|
|
2415
|
+
const scope = scopeRef.current;
|
|
2416
|
+
if (scope?.api === api && !scope.busy) await refresh(scope);
|
|
2417
|
+
};
|
|
2418
|
+
const catalogStatus = {
|
|
2419
|
+
loading: refreshing,
|
|
2420
|
+
error: catalogError ? hubMessage(catalogError, t.language) : "",
|
|
2421
|
+
failures: modelCatalogFailures(catalog),
|
|
2422
|
+
refresh: refreshNow
|
|
2423
|
+
};
|
|
2231
2424
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2232
2425
|
className: "dsh-model-hub dsh-ui-stack",
|
|
2233
2426
|
lang: t.language,
|
|
@@ -2271,10 +2464,7 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2271
2464
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2272
2465
|
variant: "ghost",
|
|
2273
2466
|
disabled: writing !== "" || refreshing,
|
|
2274
|
-
onClick:
|
|
2275
|
-
const scope = scopeRef.current;
|
|
2276
|
-
if (scope?.api === api) refresh(scope);
|
|
2277
|
-
},
|
|
2467
|
+
onClick: refreshNow,
|
|
2278
2468
|
children: refreshing ? t("refreshing") : t("refresh")
|
|
2279
2469
|
})]
|
|
2280
2470
|
}),
|
|
@@ -2288,16 +2478,10 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2288
2478
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
2289
2479
|
children: t("syncFailed", hubMessage(syncError, t.language))
|
|
2290
2480
|
}) : 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)),
|
|
2481
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
2482
|
+
state: catalogStatus,
|
|
2483
|
+
locale: t.language
|
|
2484
|
+
}),
|
|
2301
2485
|
writeError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2302
2486
|
role: "alert",
|
|
2303
2487
|
className: "dsh-ui-error dsh-ui-wrap",
|
|
@@ -2417,14 +2601,15 @@ async function apply(ctx) {
|
|
|
2417
2601
|
const api = Object.freeze({
|
|
2418
2602
|
list: () => call("fields.list", {}),
|
|
2419
2603
|
setField: (payload) => call("fields.set", payload),
|
|
2420
|
-
modelCatalog: () => client
|
|
2604
|
+
modelCatalog: () => readModelCatalog(client),
|
|
2421
2605
|
subscribeChanges: changes.subscribe,
|
|
2422
2606
|
getCast: () => call("cast.get", {}),
|
|
2423
2607
|
setCastEnabled: (payload) => call("cast.setEnabled", payload),
|
|
2424
2608
|
addCastRoute: (payload) => call("cast.addRoute", payload),
|
|
2425
2609
|
setCastRoute: (payload) => call("cast.setRoute", payload),
|
|
2426
2610
|
removeCastRoute: (payload) => call("cast.removeRoute", payload),
|
|
2427
|
-
setCastTendency: (payload) => call("cast.setTendency", payload)
|
|
2611
|
+
setCastTendency: (payload) => call("cast.setTendency", payload),
|
|
2612
|
+
listRunning: () => call("cast.running", {})
|
|
2428
2613
|
});
|
|
2429
2614
|
client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
2430
2615
|
name: "plugins.bundle.config",
|