@klarkxy/dsh-model-hub 0.1.3 → 0.1.5
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 -7
- package/docs/README.zh-CN.md +8 -8
- package/lib/client.inner.cjs +118 -84
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +118 -84
- package/lib/index.js +64 -178
- package/lib/index.js.map +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[简体中文](docs/README.zh-CN.md)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
View and change the models your DeepSeek Harness plugins use, control which models subagents may use, and track the model and reasoning effort of each running subagent — all on one page.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -18,16 +18,16 @@ 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, and Safe Auto. Search by plugin, purpose, or current model, then pick a new model.
|
|
22
|
-
- **Subagent Cast**:
|
|
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. The plugin that owns the setting saves the change.
|
|
22
|
+
- **Subagent Cast**: toggle the subagent model allowlist 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 pick one explicitly when delegating. For independent reviews and alternative approaches it favors a different model when suitable, but allows the same one when it fits better. Per-model task tendencies you add are optional hints; none are supplied by default. Below the list, a live view shows each running subagent with its current model and reasoning effort; an effort marked adapter default comes from the adapter, not an explicit choice.
|
|
23
23
|
|
|
24
24
|
## Limits
|
|
25
25
|
|
|
26
26
|
- Only plugins that expose model settings appear.
|
|
27
|
-
- If a
|
|
28
|
-
- If the model list cannot load, use **Retry**. The last available list stays visible; warnings
|
|
29
|
-
-
|
|
30
|
-
- Guidance
|
|
27
|
+
- Changes made elsewhere update the page automatically. If an edit conflicts with a newer change, refresh and choose again.
|
|
28
|
+
- If the model list cannot load, use **Retry**. The last available list stays visible; warnings name providers that failed while others stay usable.
|
|
29
|
+
- Selection guidance needs host tools that support choosing a subagent model. Existing conversations keep their saved lists; start a new one to use an updated list.
|
|
30
|
+
- Guidance never forces a model choice or adds subtasks just for variety.
|
|
31
31
|
- Remote subagents and finished runs have no readable route; the live list covers only live local subagents.
|
|
32
32
|
- No usage statistics.
|
|
33
33
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[English](../README.md)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
在一个页面查看并修改各 DeepSeek Harness 插件使用的模型,管理子代理可用的模型,并跟踪运行中子代理的模型与思考强度。
|
|
8
8
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
@@ -18,16 +18,16 @@ 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
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
26
|
+
- 只显示提供模型设置的插件。
|
|
27
|
+
- 别处改动设置时页面自动更新;编辑与较新的改动冲突时,刷新后重新选择。
|
|
28
|
+
- 模型列表读取失败时点「重试」。上次可用的列表保留;部分提供方读取失败会显示提示,其余提供方仍可用。
|
|
29
|
+
- 选模引导需要宿主工具支持选择子代理模型。已有会话保留各自的模型名单;修改后需新建会话才能使用新名单。
|
|
30
|
+
- 引导不强制选模,也不为增加模型种类而新增子任务。
|
|
31
31
|
- 远程子代理和已结束的子代理没有可读取的路由;运行列表只覆盖存活的本地子代理。
|
|
32
32
|
- 不提供用量统计。
|
|
33
33
|
|
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");
|
|
@@ -220,7 +197,14 @@ function groupModelMenuChoices(choices, query) {
|
|
|
220
197
|
}
|
|
221
198
|
//#endregion
|
|
222
199
|
//#region ../dsh-model-route/src/catalog-ui.tsx
|
|
223
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Also accepts a mapped state from a caller that owns broader async orchestration.
|
|
202
|
+
*
|
|
203
|
+
* The dsh-ui-* classes carry the styling and resolve through the consuming
|
|
204
|
+
* plugin's own officialUiCss scope (every consumer mounts its root class, so
|
|
205
|
+
* e.g. `.dsh-model-hub .dsh-ui-loading` paints this); dsh-model-catalog-status
|
|
206
|
+
* stays as a structural hook for tests and layout, never a colour source.
|
|
207
|
+
*/
|
|
224
208
|
function ModelCatalogStatus({ state, locale }) {
|
|
225
209
|
const zh = locale?.toLowerCase().startsWith("zh") === true;
|
|
226
210
|
const copy = zh ? {
|
|
@@ -236,15 +220,17 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
236
220
|
};
|
|
237
221
|
if (!state.loading && !state.error && state.failures.length === 0) return null;
|
|
238
222
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
239
|
-
className: "dsh-model-catalog-status",
|
|
223
|
+
className: "dsh-model-catalog-status dsh-ui-stack",
|
|
240
224
|
lang: zh ? "zh" : "en",
|
|
241
225
|
children: [
|
|
242
226
|
state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
243
227
|
role: "status",
|
|
228
|
+
className: "dsh-ui-loading",
|
|
244
229
|
children: copy.loading
|
|
245
230
|
}) : null,
|
|
246
231
|
state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
247
232
|
role: "alert",
|
|
233
|
+
className: "dsh-ui-error dsh-ui-wrap",
|
|
248
234
|
children: [
|
|
249
235
|
copy.error,
|
|
250
236
|
": ",
|
|
@@ -253,20 +239,27 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
253
239
|
}) : null,
|
|
254
240
|
state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
255
241
|
role: "status",
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
failure.
|
|
260
|
-
|
|
242
|
+
className: "dsh-ui-meta",
|
|
243
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy.partial }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
244
|
+
className: "dsh-ui-list",
|
|
245
|
+
children: state.failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
246
|
+
failure.name || failure.id,
|
|
247
|
+
": ",
|
|
248
|
+
failure.message
|
|
249
|
+
] }, `${failure.id}:${index}`))
|
|
250
|
+
})]
|
|
261
251
|
}) : null,
|
|
262
|
-
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
state.
|
|
268
|
-
|
|
269
|
-
|
|
252
|
+
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
253
|
+
className: "dsh-ui-actions",
|
|
254
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
255
|
+
type: "button",
|
|
256
|
+
size: "sm",
|
|
257
|
+
disabled: state.loading,
|
|
258
|
+
onClick: () => {
|
|
259
|
+
state.refresh();
|
|
260
|
+
},
|
|
261
|
+
children: copy.retry
|
|
262
|
+
})
|
|
270
263
|
}) : null
|
|
271
264
|
]
|
|
272
265
|
});
|
|
@@ -286,7 +279,8 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
286
279
|
* Sticky group headings, subsequence ranking, highlight walking and focus
|
|
287
280
|
* hand-back all match the host. SYNC POINT: when the host's ModelSelect or
|
|
288
281
|
* ModelSelect.module.css changes, this copy must be re-transcribed; styles
|
|
289
|
-
* below may only reference
|
|
282
|
+
* below may only reference allowlisted theme tokens (the --dsw-* set plus the
|
|
283
|
+
* host's --ds-* motion tokens), never literal colors.
|
|
290
284
|
*
|
|
291
285
|
* It statically imports the primitives, so only browser clients should load
|
|
292
286
|
* this entry; host code and node tests use the core entry (`./index.ts`).
|
|
@@ -794,7 +788,8 @@ function ModelMenu(props) {
|
|
|
794
788
|
* sticky group headings (MenuGroup's own CSS), and the l1-stroke prominent
|
|
795
789
|
* elevation at portal z-index 1100. The trigger mirrors the composer's 28px
|
|
796
790
|
* model pill, chevron rotating open. MenuSurface paints fill and backdrop.
|
|
797
|
-
* Only --dsw
|
|
791
|
+
* Only allowlisted --dsw- and --ds- theme tokens may appear here — a literal
|
|
792
|
+
* color is a bug.
|
|
798
793
|
*/
|
|
799
794
|
const modelMenuCss = `
|
|
800
795
|
.dsh-model-menu-root { min-width: 0; position: relative; display: inline-flex; }
|
|
@@ -817,12 +812,14 @@ const modelMenuCss = `
|
|
|
817
812
|
display: flex;
|
|
818
813
|
}
|
|
819
814
|
.dsh-model-menu-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
820
|
-
.dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0
|
|
815
|
+
.dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0 var(--dsw-focus-ring-width) var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
|
|
821
816
|
.dsh-model-menu-trigger:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }
|
|
822
817
|
.dsh-model-menu-triggerLabel { text-overflow: ellipsis; white-space: nowrap; min-width: 0; overflow: hidden; }
|
|
823
818
|
.dsh-model-menu-triggerEffort { text-overflow: ellipsis; white-space: nowrap; min-width: 0; color: var(--dsw-alias-label-caption); flex-shrink: 1000; overflow: hidden; }
|
|
824
|
-
.dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform
|
|
819
|
+
.dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform var(--ds-transition-duration) var(--ds-ease-in-out); }
|
|
825
820
|
.dsh-model-menu-chevronOpen { transform: rotate(180deg); }
|
|
821
|
+
/* Portal overlay: the host publishes no z-index token, so the card keeps the
|
|
822
|
+
* native menu layer's 1100 verbatim. */
|
|
826
823
|
.dsh-model-menu-menu {
|
|
827
824
|
z-index: 1100;
|
|
828
825
|
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
@@ -848,6 +845,8 @@ const modelMenuCss = `
|
|
|
848
845
|
.dsh-model-menu-searchWithQuery { padding-right: 34px; }
|
|
849
846
|
.dsh-model-menu-searchRow .dsh-model-menu-search input { padding: 0; font-size: 12px; line-height: normal; }
|
|
850
847
|
.dsh-model-menu-searchRow .dsh-model-menu-search input::placeholder { color: var(--dsw-alias-label-caption); }
|
|
848
|
+
/* The clear button is a circle by geometry (border-radius: 50%), a shape the
|
|
849
|
+
* radius token scale does not describe. */
|
|
851
850
|
.dsh-model-menu-searchClear {
|
|
852
851
|
width: 24px;
|
|
853
852
|
height: 24px;
|
|
@@ -1095,6 +1094,17 @@ ${scope} {
|
|
|
1095
1094
|
/* --- Layout scaffolding. --- */
|
|
1096
1095
|
& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
|
|
1097
1096
|
& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
|
|
1097
|
+
/* Plugin detail pages share one reading column. These are layout recipes;
|
|
1098
|
+
* controls keep their own primitive styles. Form spacing follows the host's
|
|
1099
|
+
* settings-form/fields.module.css and SettingsForm.module.css. */
|
|
1100
|
+
& .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
|
|
1101
|
+
& .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }
|
|
1102
|
+
&.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }
|
|
1103
|
+
& .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }
|
|
1104
|
+
& .dsh-ui-fields-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
1105
|
+
@media (max-width: 560px) {
|
|
1106
|
+
& .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }
|
|
1107
|
+
}
|
|
1098
1108
|
& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
1099
1109
|
& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
|
1100
1110
|
& .dsh-ui-spacer { flex: 1; min-width: 0; }
|
|
@@ -1122,6 +1132,9 @@ ${scope} {
|
|
|
1122
1132
|
* official primitive, so this only owns the vertical rhythm. --- */
|
|
1123
1133
|
& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
1124
1134
|
& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
|
|
1135
|
+
/* Grid fields share a row, so the stacked-field divider must not win later in
|
|
1136
|
+
* the cascade. Reset it here, after the field recipe. */
|
|
1137
|
+
& .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }
|
|
1125
1138
|
& .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
1126
1139
|
& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
1127
1140
|
& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
|
|
@@ -1312,29 +1325,22 @@ ${scope} {
|
|
|
1312
1325
|
& *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
|
|
1313
1326
|
}
|
|
1314
1327
|
}
|
|
1315
|
-
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(
|
|
1328
|
+
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?=\s*\{)/gm, (_match, className) => `&${className}, & ${className}`);
|
|
1316
1329
|
}
|
|
1317
1330
|
//#endregion
|
|
1318
1331
|
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
1319
|
-
/**
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
method: "watch",
|
|
1332
|
-
mode: "stream",
|
|
1333
|
-
invocation: { kind: "direct" },
|
|
1334
|
-
parameters: [],
|
|
1335
|
-
cancellation: { parameter: "signal" }
|
|
1336
|
-
}]
|
|
1337
|
-
};
|
|
1332
|
+
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
1333
|
+
function modelSettingsChangesIdentity(packageName) {
|
|
1334
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
|
|
1335
|
+
const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
1336
|
+
if (!parts.length) throw new Error("Model settings package name has no RPC segment");
|
|
1337
|
+
const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
|
|
1338
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
|
|
1339
|
+
return {
|
|
1340
|
+
service: segment,
|
|
1341
|
+
namespace: segment
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1338
1344
|
//#endregion
|
|
1339
1345
|
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
1340
1346
|
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
@@ -1349,21 +1355,44 @@ function parseModelSettingsChangeFrame(value) {
|
|
|
1349
1355
|
};
|
|
1350
1356
|
throw new Error("Invalid model settings change frame");
|
|
1351
1357
|
}
|
|
1352
|
-
/** One contribution
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1358
|
+
/** One owner's contribution. Each package mounts only its own endpoint. */
|
|
1359
|
+
function modelSettingsChangesRemote(packageName, identity = modelSettingsChangesIdentity(packageName)) {
|
|
1360
|
+
return {
|
|
1361
|
+
package: packageName,
|
|
1362
|
+
descriptors: [{
|
|
1363
|
+
id: `${identity.namespace}/watch`,
|
|
1364
|
+
service: identity.service,
|
|
1365
|
+
namespace: identity.namespace,
|
|
1366
|
+
method: "watch",
|
|
1367
|
+
mode: "stream",
|
|
1368
|
+
invocation: { kind: "direct" },
|
|
1369
|
+
parameters: [],
|
|
1370
|
+
cancellation: { parameter: "signal" },
|
|
1371
|
+
result: {
|
|
1372
|
+
mode: "strict",
|
|
1373
|
+
typeSymbol: "ModelSettings.ChangeFrame",
|
|
1374
|
+
create: () => ({ parse: parseModelSettingsChangeFrame })
|
|
1375
|
+
}
|
|
1376
|
+
}]
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
/** Mount from a synchronous apply. Disposal waits for an in-flight mount and
|
|
1380
|
+
* then withdraws it, so the contribution cannot outlive the plugin. */
|
|
1381
|
+
function mountModelSettingsChanges(ctx, packageName) {
|
|
1382
|
+
const contribution = modelSettingsChangesRemote(packageName);
|
|
1383
|
+
ctx.inject(["remote"], (injected) => {
|
|
1384
|
+
const scope = injected;
|
|
1385
|
+
const mounting = scope.remote.$mount(contribution);
|
|
1386
|
+
mounting.catch(() => {});
|
|
1387
|
+
scope.effect(() => async () => {
|
|
1388
|
+
await (await mounting.catch(() => void 0))?.();
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1364
1392
|
/** Native RemoteStream owns carrier reconnects; no timers or browser transport.
|
|
1365
1393
|
* A factory shares one logical stream while at least one subscriber remains. */
|
|
1366
|
-
function createModelSettingsSubscriber(ctx,
|
|
1394
|
+
function createModelSettingsSubscriber(ctx, packageName, identity = modelSettingsChangesIdentity(packageName), accept = (ns) => ns === packageName) {
|
|
1395
|
+
const endpoint = identity;
|
|
1367
1396
|
const listeners = /* @__PURE__ */ new Set();
|
|
1368
1397
|
let injection;
|
|
1369
1398
|
let current;
|
|
@@ -1389,12 +1418,18 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
1389
1418
|
};
|
|
1390
1419
|
const start = () => {
|
|
1391
1420
|
const life = ++lifecycle;
|
|
1392
|
-
injection = ctx.inject(["remote",
|
|
1421
|
+
injection = ctx.inject(["remote", `remote.${endpoint.namespace}`], (injected) => {
|
|
1393
1422
|
if (life !== lifecycle || !listeners.size) return;
|
|
1394
1423
|
const scope = injected;
|
|
1424
|
+
const service = scope.remote[endpoint.namespace];
|
|
1425
|
+
if (!service || !("watch" in service) || !service.watch) {
|
|
1426
|
+
report("Model settings sync service unavailable");
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
const watch = service.watch;
|
|
1395
1430
|
const stream = scope.remote.$stream({
|
|
1396
1431
|
name: "Model settings sync",
|
|
1397
|
-
open: (signal) =>
|
|
1432
|
+
open: (signal) => watch(signal),
|
|
1398
1433
|
ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
|
|
1399
1434
|
});
|
|
1400
1435
|
const session = {
|
|
@@ -1429,7 +1464,7 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
1429
1464
|
invalidate();
|
|
1430
1465
|
} else {
|
|
1431
1466
|
if (!ready) throw new Error("Model settings sync change arrived before ready");
|
|
1432
|
-
if (
|
|
1467
|
+
if (accept(frame.ns)) invalidate();
|
|
1433
1468
|
}
|
|
1434
1469
|
}
|
|
1435
1470
|
} catch (error) {
|
|
@@ -1992,7 +2027,7 @@ function CastPage({ api, locale }) {
|
|
|
1992
2027
|
refresh: refreshNow
|
|
1993
2028
|
};
|
|
1994
2029
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1995
|
-
className: "cast-root dsh-ui-stack",
|
|
2030
|
+
className: "cast-root dsh-model-hub dsh-ui-stack",
|
|
1996
2031
|
"aria-label": t("title"),
|
|
1997
2032
|
"aria-busy": saving !== "" || refreshing,
|
|
1998
2033
|
children: [
|
|
@@ -2422,7 +2457,7 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2422
2457
|
refresh: refreshNow
|
|
2423
2458
|
};
|
|
2424
2459
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2425
|
-
className: "dsh-model-hub dsh-ui-
|
|
2460
|
+
className: "dsh-model-hub dsh-ui-page",
|
|
2426
2461
|
lang: t.language,
|
|
2427
2462
|
"aria-label": t("title"),
|
|
2428
2463
|
"aria-busy": writing !== "" || refreshing,
|
|
@@ -2588,11 +2623,10 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
|
|
|
2588
2623
|
]
|
|
2589
2624
|
});
|
|
2590
2625
|
}
|
|
2591
|
-
|
|
2626
|
+
function apply(ctx) {
|
|
2592
2627
|
const client = ctx;
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
const changes = createModelSettingsSubscriber(ctx, "*");
|
|
2628
|
+
mountModelSettingsChanges(ctx, BUNDLE);
|
|
2629
|
+
const changes = createModelSettingsSubscriber(ctx, BUNDLE, modelSettingsChangesIdentity(BUNDLE), () => true);
|
|
2596
2630
|
const call = async (endpoint, payload) => {
|
|
2597
2631
|
const result = await client.connection.rpc.call(CHANNEL, endpoint, payload);
|
|
2598
2632
|
if (!result?.ok) throw new Error(result?.error?.message || "Request failed");
|