@klarkxy/dsh-model-hub 0.1.3 → 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/lib/client.js CHANGED
@@ -5,30 +5,7 @@ window.__ModuleLoader__.load({
5
5
  var module = { exports: {} };
6
6
  var exports = module.exports;
7
7
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
- //#region \0rolldown/runtime.js
9
- var __create = Object.create;
10
- var __defProp = Object.defineProperty;
11
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
- var __getOwnPropNames = Object.getOwnPropertyNames;
13
- var __getProtoOf = Object.getPrototypeOf;
14
- var __hasOwnProp = Object.prototype.hasOwnProperty;
15
- var __copyProps = (to, from, except, desc) => {
16
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
- key = keys[i];
18
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
19
- get: ((k) => from[k]).bind(null, key),
20
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
21
- });
22
- }
23
- return to;
24
- };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
26
- value: mod,
27
- enumerable: true
28
- }) : target, mod));
29
- //#endregion
30
8
  let react = require("react");
31
- react = __toESM(react, 1);
32
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
33
10
  let react_dom = require("react-dom");
34
11
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -1322,25 +1299,18 @@ ${scope} {
1322
1299
  }
1323
1300
  //#endregion
1324
1301
  //#region ../dsh-plugin-kit/src/contracts.ts
1325
- /**
1326
- * Remote contribution for model-settings change notifications.
1327
- * Provided by `@klarkxy/dsh-model-hub` (`ModelSettingsChangesController`);
1328
- * subscribers degrade silently when that package is not installed.
1329
- * The stream carries invalidations only — `ready` is never a settings snapshot.
1330
- */
1331
- const MODEL_SETTINGS_CHANGES_REMOTE = {
1332
- package: "@klarkxy/dsh-model-hub",
1333
- descriptors: [{
1334
- id: "modelSettingsChanges/watch",
1335
- service: "modelSettingsChangesController",
1336
- namespace: "modelSettingsChanges",
1337
- method: "watch",
1338
- mode: "stream",
1339
- invocation: { kind: "direct" },
1340
- parameters: [],
1341
- cancellation: { parameter: "signal" }
1342
- }]
1343
- };
1302
+ /** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
1303
+ function modelSettingsChangesIdentity(packageName) {
1304
+ const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
1305
+ const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
1306
+ if (!parts.length) throw new Error("Model settings package name has no RPC segment");
1307
+ const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
1308
+ if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
1309
+ return {
1310
+ service: segment,
1311
+ namespace: segment
1312
+ };
1313
+ }
1344
1314
  //#endregion
1345
1315
  //#region ../dsh-plugin-kit/src/model-settings-client.ts
1346
1316
  /** This stream carries invalidations only; ready is never a settings snapshot. */
@@ -1355,21 +1325,44 @@ function parseModelSettingsChangeFrame(value) {
1355
1325
  };
1356
1326
  throw new Error("Invalid model settings change frame");
1357
1327
  }
1358
- /** One contribution mounted by Model Hub; consumers only inject its service. */
1359
- const modelSettingsChangesRemote = {
1360
- package: MODEL_SETTINGS_CHANGES_REMOTE.package,
1361
- descriptors: MODEL_SETTINGS_CHANGES_REMOTE.descriptors.map((descriptor) => ({
1362
- ...descriptor,
1363
- result: {
1364
- mode: "strict",
1365
- typeSymbol: "ModelSettings.ChangeFrame",
1366
- create: () => ({ parse: parseModelSettingsChangeFrame })
1367
- }
1368
- }))
1369
- };
1328
+ /** One owner's contribution. Each package mounts only its own endpoint. */
1329
+ function modelSettingsChangesRemote(packageName, identity = modelSettingsChangesIdentity(packageName)) {
1330
+ return {
1331
+ package: packageName,
1332
+ descriptors: [{
1333
+ id: `${identity.namespace}/watch`,
1334
+ service: identity.service,
1335
+ namespace: identity.namespace,
1336
+ method: "watch",
1337
+ mode: "stream",
1338
+ invocation: { kind: "direct" },
1339
+ parameters: [],
1340
+ cancellation: { parameter: "signal" },
1341
+ result: {
1342
+ mode: "strict",
1343
+ typeSymbol: "ModelSettings.ChangeFrame",
1344
+ create: () => ({ parse: parseModelSettingsChangeFrame })
1345
+ }
1346
+ }]
1347
+ };
1348
+ }
1349
+ /** Mount from a synchronous apply. Disposal waits for an in-flight mount and
1350
+ * then withdraws it, so the contribution cannot outlive the plugin. */
1351
+ function mountModelSettingsChanges(ctx, packageName) {
1352
+ const contribution = modelSettingsChangesRemote(packageName);
1353
+ ctx.inject(["remote"], (injected) => {
1354
+ const scope = injected;
1355
+ const mounting = scope.remote.$mount(contribution);
1356
+ mounting.catch(() => {});
1357
+ scope.effect(() => async () => {
1358
+ await (await mounting.catch(() => void 0))?.();
1359
+ });
1360
+ });
1361
+ }
1370
1362
  /** Native RemoteStream owns carrier reconnects; no timers or browser transport.
1371
1363
  * A factory shares one logical stream while at least one subscriber remains. */
1372
- function createModelSettingsSubscriber(ctx, namespace) {
1364
+ function createModelSettingsSubscriber(ctx, packageName, identity = modelSettingsChangesIdentity(packageName), accept = (ns) => ns === packageName) {
1365
+ const endpoint = identity;
1373
1366
  const listeners = /* @__PURE__ */ new Set();
1374
1367
  let injection;
1375
1368
  let current;
@@ -1395,12 +1388,18 @@ function createModelSettingsSubscriber(ctx, namespace) {
1395
1388
  };
1396
1389
  const start = () => {
1397
1390
  const life = ++lifecycle;
1398
- injection = ctx.inject(["remote", "remote.modelSettingsChanges"], (injected) => {
1391
+ injection = ctx.inject(["remote", `remote.${endpoint.namespace}`], (injected) => {
1399
1392
  if (life !== lifecycle || !listeners.size) return;
1400
1393
  const scope = injected;
1394
+ const service = scope.remote[endpoint.namespace];
1395
+ if (!service || !("watch" in service) || !service.watch) {
1396
+ report("Model settings sync service unavailable");
1397
+ return;
1398
+ }
1399
+ const watch = service.watch;
1401
1400
  const stream = scope.remote.$stream({
1402
1401
  name: "Model settings sync",
1403
- open: (signal) => scope.remote.modelSettingsChanges.watch(signal),
1402
+ open: (signal) => watch(signal),
1404
1403
  ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
1405
1404
  });
1406
1405
  const session = {
@@ -1435,7 +1434,7 @@ function createModelSettingsSubscriber(ctx, namespace) {
1435
1434
  invalidate();
1436
1435
  } else {
1437
1436
  if (!ready) throw new Error("Model settings sync change arrived before ready");
1438
- if (namespace === "*" || frame.ns === namespace) invalidate();
1437
+ if (accept(frame.ns)) invalidate();
1439
1438
  }
1440
1439
  }
1441
1440
  } catch (error) {
@@ -2594,11 +2593,10 @@ function HubPage({ view, api, locale, initialEntries = null, initialCatalog = nu
2594
2593
  ]
2595
2594
  });
2596
2595
  }
2597
- async function apply(ctx) {
2596
+ function apply(ctx) {
2598
2597
  const client = ctx;
2599
- const disposeRemote = await client.remote.$mount(modelSettingsChangesRemote);
2600
- client.effect(() => disposeRemote);
2601
- const changes = createModelSettingsSubscriber(ctx, "*");
2598
+ mountModelSettingsChanges(ctx, BUNDLE);
2599
+ const changes = createModelSettingsSubscriber(ctx, BUNDLE, modelSettingsChangesIdentity(BUNDLE), () => true);
2602
2600
  const call = async (endpoint, payload) => {
2603
2601
  const result = await client.connection.rpc.call(CHANNEL, endpoint, payload);
2604
2602
  if (!result?.ok) throw new Error(result?.error?.message || "Request failed");
package/lib/index.js CHANGED
@@ -1,13 +1,45 @@
1
- import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import Schema from "@deepseek-ai/schemastery";
4
4
  import { registerHostRpc } from "@klarkxy/dsh-plugin-kit/host-rpc";
5
5
  import { modelRouteRoleSchemastery, modelRouteSchemastery } from "@klarkxy/dsh-model-route/schemastery";
6
6
  import { detectModelFields, modelRouteOverride, normalizeModelRoute } from "@klarkxy/dsh-model-route";
7
- import { MODEL_SETTINGS_CHANGED_EVENT, collectModelSettings, notifyModelSettingsChanged } from "@klarkxy/dsh-plugin-kit/model-settings";
8
- import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
9
- import { MODEL_SETTINGS_CHANGES_REMOTE, isSubagentSession } from "@klarkxy/dsh-plugin-kit/contracts";
10
- import { parseModelSettingsChangeFrame } from "@klarkxy/dsh-plugin-kit/model-settings-client";
7
+ import { collectModelSettings, notifyModelSettingsChanged } from "@klarkxy/dsh-plugin-kit/model-settings";
8
+ import { isSubagentSession, modelSettingsChangesIdentity } from "@klarkxy/dsh-plugin-kit/contracts";
9
+ import { createModelSettingsChanges } from "@klarkxy/dsh-plugin-kit/model-settings-changes";
10
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
+ //#region src/diagnostics.ts
12
+ /** Invalidation is constant-time. Only startup and explicit reads request a
13
+ * diagnostic; a burst shares one deferred run. Work never reschedules itself. */
14
+ function createProbeDiagnostics(run, onError) {
15
+ let dirty = true;
16
+ let stopped = false;
17
+ let timer;
18
+ let running;
19
+ return {
20
+ invalidate() {
21
+ if (!stopped) dirty = true;
22
+ },
23
+ request() {
24
+ if (stopped || !dirty || timer !== void 0 || running) return;
25
+ timer = setTimeout(() => {
26
+ timer = void 0;
27
+ if (stopped || !dirty) return;
28
+ dirty = false;
29
+ running = Promise.resolve().then(run).catch(onError).finally(() => {
30
+ running = void 0;
31
+ });
32
+ }, 0);
33
+ },
34
+ async dispose() {
35
+ stopped = true;
36
+ if (timer !== void 0) clearTimeout(timer);
37
+ timer = void 0;
38
+ await running;
39
+ }
40
+ };
41
+ }
42
+ //#endregion
11
43
  //#region src/probe.ts
12
44
  /**
13
45
  * Pure probe-report builder for the model hub. Takes the host's
@@ -209,170 +241,10 @@ function createHubSettings(events, settings, ownNamespace = "@klarkxy/dsh-model-
209
241
  }
210
242
  //#endregion
211
243
  //#region src/changes.ts
212
- var __create = Object.create;
213
- var __defProp = Object.defineProperty;
214
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
215
- var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
216
- var __typeError = (msg) => {
217
- throw TypeError(msg);
218
- };
219
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
220
- enumerable: true,
221
- configurable: true,
222
- writable: true,
223
- value
224
- }) : obj[key] = value;
225
- var __name = (target, value) => __defProp(target, "name", {
226
- value,
227
- configurable: true
228
- });
229
- var __decoratorStart = (base) => [
230
- ,
231
- ,
232
- ,
233
- __create(base?.[__knownSymbol("metadata")] ?? null)
234
- ];
235
- var __decoratorStrings = [
236
- "class",
237
- "method",
238
- "getter",
239
- "setter",
240
- "accessor",
241
- "field",
242
- "value",
243
- "get",
244
- "set"
245
- ];
246
- var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
247
- var __decoratorContext = (kind, name, done, metadata, fns) => ({
248
- kind: __decoratorStrings[kind],
249
- name,
250
- metadata,
251
- addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
252
- });
253
- var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
254
- var __runInitializers = (array, flags, self, value) => {
255
- for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
256
- return value;
257
- };
258
- var __decorateElement = (array, flags, name, decorators, target, extra) => {
259
- var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
260
- var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
261
- var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
262
- var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
263
- get [name]() {
264
- return __privateGet(this, extra);
265
- },
266
- set [name](x) {
267
- return __privateSet(this, extra, x);
268
- }
269
- }, name));
270
- k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
271
- for (var i = decorators.length - 1; i >= 0; i--) {
272
- ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
273
- if (k) {
274
- ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
275
- if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
276
- if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
277
- }
278
- it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : {
279
- get: desc.get,
280
- set: desc.set
281
- } : target, ctx), done._ = 1;
282
- if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
283
- else if (typeof it !== "object" || it === null) __typeError("Object expected");
284
- else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
285
- }
286
- return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
287
- };
288
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
289
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
290
- var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError("Cannot use the \"in\" operator on this value") : member.has(obj);
291
- var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
292
- var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
293
- var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
294
- var _watch_dec;
295
- var _a;
296
- var _init;
297
- async function* watchModelSettingsChanges(events, signal) {
298
- signal.throwIfAborted();
299
- const pending = /* @__PURE__ */ new Map();
300
- let full = false;
301
- let wake;
302
- const changed = (ns, revision) => {
303
- let frame;
304
- try {
305
- frame = parseModelSettingsChangeFrame({
306
- kind: "changed",
307
- ns,
308
- revision
309
- });
310
- } catch {
311
- return;
312
- }
313
- if (!full) {
314
- if (!pending.has(ns) && pending.size >= 256) {
315
- pending.clear();
316
- full = true;
317
- } else pending.set(ns, frame);
318
- }
319
- wake?.();
320
- };
321
- const disposers = [];
322
- const cleanup = () => {
323
- for (const off of disposers.splice(0).reverse()) off();
324
- pending.clear();
325
- };
326
- const aborted = () => {
327
- cleanup();
328
- wake?.();
329
- };
330
- signal.addEventListener("abort", aborted, { once: true });
331
- try {
332
- disposers.push(events.on(MODEL_SETTINGS_CHANGED_EVENT, ({ ns, revision }) => changed(ns, revision)));
333
- disposers.push(events.on("settings/document-updated", changed));
334
- signal.throwIfAborted();
335
- yield { kind: "ready" };
336
- while (true) {
337
- signal.throwIfAborted();
338
- if (full) {
339
- full = false;
340
- yield { kind: "ready" };
341
- continue;
342
- }
343
- if (pending.size) {
344
- const [ns, frame] = pending.entries().next().value;
345
- pending.delete(ns);
346
- yield frame;
347
- continue;
348
- }
349
- await new Promise((resolve) => {
350
- wake = resolve;
351
- if (signal.aborted || full || pending.size) resolve();
352
- });
353
- wake = void 0;
354
- }
355
- } finally {
356
- signal.removeEventListener("abort", aborted);
357
- cleanup();
358
- }
244
+ /** Model Hub forwards every settings namespace; feature plugins filter their own. */
245
+ function registerHubModelSettingsChanges(ctx) {
246
+ createModelSettingsChanges(ctx, modelSettingsChangesIdentity("@klarkxy/dsh-model-hub"));
359
247
  }
360
- var ModelSettingsChangesController = class extends (_a = TypertRemoteService, _watch_dec = [Remote({ mode: "stream" })], _a) {
361
- constructor(ctx) {
362
- super(ctx, MODEL_SETTINGS_CHANGES_REMOTE.descriptors[0].service, { namespace: MODEL_SETTINGS_CHANGES_REMOTE.descriptors[0].namespace });
363
- __runInitializers(_init, 5, this);
364
- __publicField(this, "lifetime", new AbortController());
365
- ctx.effect(() => () => {
366
- this.lifetime.abort(/* @__PURE__ */ new Error("Model settings sync owner unloaded"));
367
- });
368
- }
369
- watch(signal) {
370
- return watchModelSettingsChanges(this.ctx, AbortSignal.any([this.lifetime.signal, signal]));
371
- }
372
- };
373
- _init = __decoratorStart(_a);
374
- __decorateElement(_init, 1, "watch", _watch_dec, ModelSettingsChangesController);
375
- __decoratorMetadata(_init, ModelSettingsChangesController);
376
248
  //#endregion
377
249
  //#region src/cast.ts
378
250
  /**
@@ -863,7 +735,7 @@ const Config = Schema.object({
863
735
  }).volatile()
864
736
  });
865
737
  function apply(ctx) {
866
- new ModelSettingsChangesController(ctx);
738
+ registerHubModelSettingsChanges(ctx);
867
739
  const profile = ctx.get("profileContext");
868
740
  const settings = ctx.settings;
869
741
  const describe = () => settings.describe({ redactSecrets: true });
@@ -891,7 +763,8 @@ function apply(ctx) {
891
763
  interpolate: false,
892
764
  text: () => cast.promptText(() => ctx.get("subagentModelSelection")?.current())
893
765
  });
894
- const run = (reason) => {
766
+ const run = async () => {
767
+ const reason = "requested";
895
768
  let report;
896
769
  try {
897
770
  report = buildProbeReport(describe());
@@ -902,20 +775,33 @@ function apply(ctx) {
902
775
  ctx.logger.info("model-hub: probe (%s): %d namespaces, %d with model fields; self-test extra-key=%s role=%s", reason, report.totalNamespaces, report.namespacesWithModelFields, report.selfTest.extraKey, report.selfTest.role);
903
776
  if (profile?.dir) try {
904
777
  const dir = join(profile.dir, "data", "model-hub");
905
- mkdirSync(dir, { recursive: true });
906
- writeFileSync(join(dir, "probe-report.json"), JSON.stringify(report, null, 2));
778
+ await mkdir(dir, { recursive: true });
779
+ await writeFile(join(dir, "probe-report.json"), JSON.stringify(report, null, 2));
907
780
  } catch (error) {
908
781
  ctx.logger.warn("model-hub: could not write probe report: %s", error instanceof Error ? error.message : String(error));
909
782
  }
910
783
  };
911
- run("boot");
912
- ctx.on("settings/document-updated", () => run("document-updated"));
784
+ const diagnostics = createProbeDiagnostics(run, (error) => {
785
+ ctx.logger.warn("model-hub: diagnostics failed: %s", String(error));
786
+ });
787
+ ctx.effect(() => {
788
+ const off = ctx.on("settings/document-updated", () => diagnostics.invalidate());
789
+ diagnostics.request();
790
+ return async () => {
791
+ off();
792
+ await diagnostics.dispose();
793
+ };
794
+ });
913
795
  ctx.effect(() => registerHostRpc(ctx, CHANNEL, async (endpoint, payload, signal) => {
914
796
  try {
915
- if (endpoint === "fields.list") return {
916
- ok: true,
917
- value: await hub.list(signal)
918
- };
797
+ if (endpoint === "fields.list") {
798
+ const value = await hub.list(signal);
799
+ diagnostics.request();
800
+ return {
801
+ ok: true,
802
+ value
803
+ };
804
+ }
919
805
  if (endpoint === "fields.set") return {
920
806
  ok: true,
921
807
  value: await hub.set(payload, signal)