dsh-multi-router 0.5.8 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +49 -8
  3. package/cordis.patch.yml +2 -2
  4. package/lib/accounts.js +6 -4
  5. package/lib/api-service.js +11 -0
  6. package/lib/api-service.js.map +7 -0
  7. package/lib/chunk-4V52V5GO.js +105 -0
  8. package/lib/chunk-4V52V5GO.js.map +7 -0
  9. package/lib/{chunk-OFFVP6YN.js → chunk-JUOESKAK.js} +22 -5
  10. package/lib/chunk-JUOESKAK.js.map +7 -0
  11. package/lib/{chunk-P43AWJ4Q.js → chunk-LLDJVBTQ.js} +3 -3
  12. package/lib/{chunk-U2MU2M5C.js → chunk-NSKCFVHP.js} +13 -111
  13. package/lib/chunk-NSKCFVHP.js.map +7 -0
  14. package/lib/{chunk-ILRW3ELD.js → chunk-OCMOMSB4.js} +293 -77
  15. package/lib/chunk-OCMOMSB4.js.map +7 -0
  16. package/lib/{chunk-FTM7RHQV.js → chunk-OK2T2UAZ.js} +4 -4
  17. package/lib/{chunk-J3XO7RTF.js → chunk-ROZINFTV.js} +3 -2
  18. package/lib/chunk-ROZINFTV.js.map +7 -0
  19. package/lib/chunk-SNEZMYNU.js +332 -0
  20. package/lib/chunk-SNEZMYNU.js.map +7 -0
  21. package/lib/{chunk-MCHV2R25.js → chunk-WVNY4LSL.js} +3 -1
  22. package/lib/{chunk-MCHV2R25.js.map → chunk-WVNY4LSL.js.map} +2 -2
  23. package/lib/cli.js +7 -6
  24. package/lib/cli.js.map +1 -1
  25. package/lib/client.js +743 -315
  26. package/lib/client.js.map +2 -2
  27. package/lib/clients.js +6 -5
  28. package/lib/{host-v0.5.8.js → host-v0.6.0.js} +22 -123
  29. package/lib/host.js +22 -123
  30. package/lib/index.js +22 -123
  31. package/lib/index.js.map +4 -4
  32. package/lib/manager.js +11 -7
  33. package/lib/router.js +2 -2
  34. package/lib/shim.js +3 -3
  35. package/package.json +1 -1
  36. package/lib/chunk-ILRW3ELD.js.map +0 -7
  37. package/lib/chunk-J3XO7RTF.js.map +0 -7
  38. package/lib/chunk-OFFVP6YN.js.map +0 -7
  39. package/lib/chunk-U2MU2M5C.js.map +0 -7
  40. /package/lib/{chunk-P43AWJ4Q.js.map → chunk-LLDJVBTQ.js.map} +0 -0
  41. /package/lib/{chunk-FTM7RHQV.js.map → chunk-OK2T2UAZ.js.map} +0 -0
package/lib/clients.js CHANGED
@@ -7,12 +7,13 @@ import {
7
7
  jsonCredential,
8
8
  refreshWorkBuddyCheckinCredential,
9
9
  workbuddyToken
10
- } from "./chunk-U2MU2M5C.js";
11
- import "./chunk-FTM7RHQV.js";
12
- import "./chunk-S5KMFAQM.js";
13
- import "./chunk-J3XO7RTF.js";
14
- import "./chunk-MCHV2R25.js";
10
+ } from "./chunk-NSKCFVHP.js";
11
+ import "./chunk-OK2T2UAZ.js";
15
12
  import "./chunk-TEZ2FMEO.js";
13
+ import "./chunk-S5KMFAQM.js";
14
+ import "./chunk-4V52V5GO.js";
15
+ import "./chunk-ROZINFTV.js";
16
+ import "./chunk-WVNY4LSL.js";
16
17
  export {
17
18
  cachedModels,
18
19
  claimTraeCheckin,
@@ -1,38 +1,42 @@
1
1
  import {
2
2
  createShim
3
- } from "./chunk-P43AWJ4Q.js";
3
+ } from "./chunk-LLDJVBTQ.js";
4
4
  import {
5
5
  MANAGEMENT_PATH,
6
6
  Manager,
7
+ PluginReload,
8
+ SUMMARY_PATH,
7
9
  managementHandler,
8
10
  version
9
- } from "./chunk-ILRW3ELD.js";
11
+ } from "./chunk-OCMOMSB4.js";
10
12
  import {
11
13
  createAccountClient
12
- } from "./chunk-U2MU2M5C.js";
13
- import "./chunk-OFFVP6YN.js";
14
- import "./chunk-FTM7RHQV.js";
14
+ } from "./chunk-NSKCFVHP.js";
15
+ import "./chunk-JUOESKAK.js";
16
+ import "./chunk-OK2T2UAZ.js";
17
+ import {
18
+ normalizeCredits
19
+ } from "./chunk-TEZ2FMEO.js";
15
20
  import "./chunk-S5KMFAQM.js";
21
+ import "./chunk-SNEZMYNU.js";
22
+ import "./chunk-4V52V5GO.js";
16
23
  import "./chunk-DNQPZMPF.js";
17
24
  import {
18
25
  Router,
19
26
  SessionMetrics,
20
27
  abortable
21
- } from "./chunk-J3XO7RTF.js";
28
+ } from "./chunk-ROZINFTV.js";
22
29
  import {
23
30
  PROVIDERS,
24
31
  PROVIDER_NAMES,
25
32
  ensureConfig
26
- } from "./chunk-MCHV2R25.js";
27
- import {
28
- normalizeCredits
29
- } from "./chunk-TEZ2FMEO.js";
33
+ } from "./chunk-WVNY4LSL.js";
30
34
 
31
35
  // src/index.mjs
32
36
  import z from "@deepseek-ai/schemastery";
33
37
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
34
38
  import { resolveRetryPolicy as resolveRetryPolicy2, resolveImageAttachmentAccess } from "@deepseek-ai/dsh-llm";
35
- import { join as join2, resolve } from "node:path";
39
+ import { join, resolve } from "node:path";
36
40
 
37
41
  // upstream/workbuddy/src/adapter.ts
38
42
  import { createProvider } from "@earendil-works/pi-ai";
@@ -246,117 +250,10 @@ var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
246
250
  }
247
251
  };
248
252
 
249
- // src/plugin-reload.mjs
250
- import { readFile } from "node:fs/promises";
251
- import { watch } from "node:fs";
252
- import { join } from "node:path";
253
- function outsideActivation(hmr, operation) {
254
- const scope = hmr?.executing;
255
- return typeof scope?.exit === "function" ? scope.exit(operation) : operation();
256
- }
257
- var PluginReload = class {
258
- constructor({ service, profile, canReload, hmr = () => void 0, busy = () => false, now = Date.now, runningVersion }) {
259
- Object.assign(this, { service, profile, canReload, hmr, busy, now, runningVersion });
260
- this.phase = "current";
261
- this.installedVersion = runningVersion;
262
- this.retryAt = 0;
263
- }
264
- state() {
265
- return { runningVersion: this.runningVersion, installedVersion: this.installedVersion, phase: this.phase, canReload: !!this.canReload(), message: this.message };
266
- }
267
- start() {
268
- const dir = this.profile()?.dir;
269
- if (dir) try {
270
- this.watcher = watch(dir, () => this.schedule());
271
- this.watcher.unref();
272
- } catch {
273
- }
274
- this.schedule();
275
- }
276
- schedule(delay = 500) {
277
- if (this.closed) return;
278
- clearTimeout(this.timer);
279
- this.timer = setTimeout(() => {
280
- void this.check().catch(() => {
281
- });
282
- }, delay);
283
- this.timer.unref();
284
- }
285
- stop() {
286
- this.closed = true;
287
- clearTimeout(this.timer);
288
- this.watcher?.close();
289
- }
290
- check() {
291
- if (this.closed) return Promise.resolve();
292
- if (this.flight) return this.flight;
293
- this.flight = this.inspect().finally(() => {
294
- this.flight = void 0;
295
- });
296
- return this.flight;
297
- }
298
- async inspect() {
299
- const dir = this.profile()?.dir;
300
- if (!dir) return;
301
- let manifest, installed;
302
- try {
303
- manifest = JSON.parse(await readFile(join(dir, "package.json"), "utf8"));
304
- installed = JSON.parse(await readFile(join(dir, "node_modules/dsh-multi-router/package.json"), "utf8"));
305
- } catch {
306
- return;
307
- }
308
- if (this.closed || !manifest.dsh?.profile?.bundles?.includes("dsh-multi-router")) return;
309
- if (installed.name !== "dsh-multi-router" || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(installed.version)) return;
310
- this.installedVersion = installed.version;
311
- if (installed.version === this.runningVersion) {
312
- this.phase = "current";
313
- this.message = void 0;
314
- return;
315
- }
316
- if (!this.canReload() || !this.service()?.setBundleEnabled) {
317
- this.phase = "restart-required";
318
- this.message = "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u8FD0\u884C\u4E2D\u91CD\u8F7D\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
319
- return;
320
- }
321
- if (this.busy()) {
322
- this.phase = "waiting";
323
- this.message = "\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u7B49\u5F85\u5F53\u524D\u8BF7\u6C42\u548C\u8D26\u53F7\u64CD\u4F5C\u7ED3\u675F\u540E\u81EA\u52A8\u751F\u6548\u3002";
324
- this.schedule(1e3);
325
- return;
326
- }
327
- const root = join(dir, "node_modules/dsh-multi-router");
328
- try {
329
- const patch = await readFile(join(root, "cordis.patch.yml"), "utf8");
330
- if (!patch.includes(`id: llm-multi-router-v2-${installed.version}
331
- `) || !patch.includes(`name: ./lib/host-v${installed.version}.js`)) throw Error("legacy entry");
332
- await readFile(join(root, `lib/host-v${installed.version}.js`));
333
- } catch {
334
- this.phase = "restart-required";
335
- this.message = "\u5B89\u88C5\u7248\u672C\u7F3A\u5C11\u7248\u672C\u5316\u5165\u53E3\uFF0C\u91CD\u65B0\u5B89\u88C5\u6700\u65B0\u7248\u540E\u518D\u8BD5\u3002";
336
- return;
337
- }
338
- if (this.now() < this.retryAt) return;
339
- this.phase = "reloading";
340
- this.message = "\u6B63\u5728\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u4E0E\u914D\u7F6E\u9762\u677F\u2026";
341
- this.retryAt = this.now() + 6e4;
342
- try {
343
- const result = await outsideActivation(this.hmr(), () => this.service().setBundleEnabled("dsh-multi-router", true));
344
- if (this.closed) return;
345
- this.phase = "restart-required";
346
- this.message = result.application === "applied" ? "DSH \u5C1A\u672A\u5207\u6362\u5230\u65B0\u7248\u672C\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u6216\u67E5\u770B\u7EC4\u4EF6\u9519\u8BEF\u3002" : "DSH \u672A\u80FD\u91CD\u8F7D\u63D2\u4EF6\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
347
- } catch {
348
- if (!this.closed) {
349
- this.phase = "failed";
350
- this.message = "\u63D2\u4EF6\u91CD\u8F7D\u672A\u5B8C\u6210\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002";
351
- }
352
- }
353
- }
354
- };
355
-
356
253
  // src/index.mjs
357
254
  var name = "dsh-multi-router";
358
255
  var inject = ["llm"];
359
- var defaultConfigFile = () => join2(resolveDshHome(), "multi-router", "router.json");
256
+ var defaultConfigFile = () => join(resolveDshHome(), "multi-router", "router.json");
360
257
  var Config = z.object({ configFile: z.string().description("Absolute path to multi-account router.json") });
361
258
  async function apply(ctx, options = {}) {
362
259
  let disposed = false, manager, stopWatch, monitorTimer, startup;
@@ -371,7 +268,7 @@ async function apply(ctx, options = {}) {
371
268
  manager?.checkins.abort.abort();
372
269
  manager?.workbuddyLogin.abort.abort();
373
270
  for (const release of releases) release();
374
- return Promise.all([...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
271
+ return Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
375
272
  }), manager?.telemetry.queue.catch(() => {
376
273
  })]);
377
274
  });
@@ -387,7 +284,7 @@ async function apply(ctx, options = {}) {
387
284
  manager = new Manager(file, router, () => {
388
285
  for (const wrapper of wrappers) wrapper.invalidate();
389
286
  ctx.emit("llm/adapters-updated");
390
- }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext") });
287
+ }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext"), hmr: () => ctx.get("hmr") });
391
288
  await manager.load();
392
289
  if (disposed) return;
393
290
  manager.startup = { phase: "discovering" };
@@ -397,9 +294,10 @@ async function apply(ctx, options = {}) {
397
294
  profile: () => ctx.get("profileContext"),
398
295
  canReload: () => !!ctx.get("hmr"),
399
296
  hmr: () => ctx.get("hmr"),
400
- busy: () => !!(manager.localScan || manager.requests || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
297
+ busy: () => !!(manager.localScan || manager.requests || manager.api.controllers.size || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
401
298
  });
402
299
  ctx.inject?.(["webServer"], (webCtx) => {
300
+ webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: SUMMARY_PATH, handler: managementHandler(manager, { summaryOnly: true }) }));
403
301
  webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: MANAGEMENT_PATH, handler: managementHandler(manager) }));
404
302
  });
405
303
  const metrics = new SessionMetrics(manager.telemetry);
@@ -469,6 +367,7 @@ async function apply(ctx, options = {}) {
469
367
  releases.push(ctx.llm.registerAdapter([provider], wrapper.adapter));
470
368
  }
471
369
  stopWatch = manager.watch(() => ctx.logger.warn("multi-router: invalid config change; keeping last valid configuration"));
370
+ await manager.api.reconcile();
472
371
  startup = (async () => {
473
372
  await manager.syncLocalAccounts().catch(() => ctx.logger.warn("multi-router: local login discovery failed; manual import remains available"));
474
373
  if (disposed) return;
@@ -499,7 +398,7 @@ async function apply(ctx, options = {}) {
499
398
  stopWatch?.();
500
399
  manager?.runtime?.stop();
501
400
  for (const release of releases) release();
502
- await Promise.all(shims.map((shim) => shim.close()));
401
+ await Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close())]);
503
402
  ctx.logger.error("multi-router: initialization failed", error.code ?? error.message);
504
403
  throw error;
505
404
  }
package/lib/host.js CHANGED
@@ -1,38 +1,42 @@
1
1
  import {
2
2
  createShim
3
- } from "./chunk-P43AWJ4Q.js";
3
+ } from "./chunk-LLDJVBTQ.js";
4
4
  import {
5
5
  MANAGEMENT_PATH,
6
6
  Manager,
7
+ PluginReload,
8
+ SUMMARY_PATH,
7
9
  managementHandler,
8
10
  version
9
- } from "./chunk-ILRW3ELD.js";
11
+ } from "./chunk-OCMOMSB4.js";
10
12
  import {
11
13
  createAccountClient
12
- } from "./chunk-U2MU2M5C.js";
13
- import "./chunk-OFFVP6YN.js";
14
- import "./chunk-FTM7RHQV.js";
14
+ } from "./chunk-NSKCFVHP.js";
15
+ import "./chunk-JUOESKAK.js";
16
+ import "./chunk-OK2T2UAZ.js";
17
+ import {
18
+ normalizeCredits
19
+ } from "./chunk-TEZ2FMEO.js";
15
20
  import "./chunk-S5KMFAQM.js";
21
+ import "./chunk-SNEZMYNU.js";
22
+ import "./chunk-4V52V5GO.js";
16
23
  import "./chunk-DNQPZMPF.js";
17
24
  import {
18
25
  Router,
19
26
  SessionMetrics,
20
27
  abortable
21
- } from "./chunk-J3XO7RTF.js";
28
+ } from "./chunk-ROZINFTV.js";
22
29
  import {
23
30
  PROVIDERS,
24
31
  PROVIDER_NAMES,
25
32
  ensureConfig
26
- } from "./chunk-MCHV2R25.js";
27
- import {
28
- normalizeCredits
29
- } from "./chunk-TEZ2FMEO.js";
33
+ } from "./chunk-WVNY4LSL.js";
30
34
 
31
35
  // src/index.mjs
32
36
  import z from "@deepseek-ai/schemastery";
33
37
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
34
38
  import { resolveRetryPolicy as resolveRetryPolicy2, resolveImageAttachmentAccess } from "@deepseek-ai/dsh-llm";
35
- import { join as join2, resolve } from "node:path";
39
+ import { join, resolve } from "node:path";
36
40
 
37
41
  // upstream/workbuddy/src/adapter.ts
38
42
  import { createProvider } from "@earendil-works/pi-ai";
@@ -246,117 +250,10 @@ var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
246
250
  }
247
251
  };
248
252
 
249
- // src/plugin-reload.mjs
250
- import { readFile } from "node:fs/promises";
251
- import { watch } from "node:fs";
252
- import { join } from "node:path";
253
- function outsideActivation(hmr, operation) {
254
- const scope = hmr?.executing;
255
- return typeof scope?.exit === "function" ? scope.exit(operation) : operation();
256
- }
257
- var PluginReload = class {
258
- constructor({ service, profile, canReload, hmr = () => void 0, busy = () => false, now = Date.now, runningVersion }) {
259
- Object.assign(this, { service, profile, canReload, hmr, busy, now, runningVersion });
260
- this.phase = "current";
261
- this.installedVersion = runningVersion;
262
- this.retryAt = 0;
263
- }
264
- state() {
265
- return { runningVersion: this.runningVersion, installedVersion: this.installedVersion, phase: this.phase, canReload: !!this.canReload(), message: this.message };
266
- }
267
- start() {
268
- const dir = this.profile()?.dir;
269
- if (dir) try {
270
- this.watcher = watch(dir, () => this.schedule());
271
- this.watcher.unref();
272
- } catch {
273
- }
274
- this.schedule();
275
- }
276
- schedule(delay = 500) {
277
- if (this.closed) return;
278
- clearTimeout(this.timer);
279
- this.timer = setTimeout(() => {
280
- void this.check().catch(() => {
281
- });
282
- }, delay);
283
- this.timer.unref();
284
- }
285
- stop() {
286
- this.closed = true;
287
- clearTimeout(this.timer);
288
- this.watcher?.close();
289
- }
290
- check() {
291
- if (this.closed) return Promise.resolve();
292
- if (this.flight) return this.flight;
293
- this.flight = this.inspect().finally(() => {
294
- this.flight = void 0;
295
- });
296
- return this.flight;
297
- }
298
- async inspect() {
299
- const dir = this.profile()?.dir;
300
- if (!dir) return;
301
- let manifest, installed;
302
- try {
303
- manifest = JSON.parse(await readFile(join(dir, "package.json"), "utf8"));
304
- installed = JSON.parse(await readFile(join(dir, "node_modules/dsh-multi-router/package.json"), "utf8"));
305
- } catch {
306
- return;
307
- }
308
- if (this.closed || !manifest.dsh?.profile?.bundles?.includes("dsh-multi-router")) return;
309
- if (installed.name !== "dsh-multi-router" || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(installed.version)) return;
310
- this.installedVersion = installed.version;
311
- if (installed.version === this.runningVersion) {
312
- this.phase = "current";
313
- this.message = void 0;
314
- return;
315
- }
316
- if (!this.canReload() || !this.service()?.setBundleEnabled) {
317
- this.phase = "restart-required";
318
- this.message = "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u8FD0\u884C\u4E2D\u91CD\u8F7D\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
319
- return;
320
- }
321
- if (this.busy()) {
322
- this.phase = "waiting";
323
- this.message = "\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u7B49\u5F85\u5F53\u524D\u8BF7\u6C42\u548C\u8D26\u53F7\u64CD\u4F5C\u7ED3\u675F\u540E\u81EA\u52A8\u751F\u6548\u3002";
324
- this.schedule(1e3);
325
- return;
326
- }
327
- const root = join(dir, "node_modules/dsh-multi-router");
328
- try {
329
- const patch = await readFile(join(root, "cordis.patch.yml"), "utf8");
330
- if (!patch.includes(`id: llm-multi-router-v2-${installed.version}
331
- `) || !patch.includes(`name: ./lib/host-v${installed.version}.js`)) throw Error("legacy entry");
332
- await readFile(join(root, `lib/host-v${installed.version}.js`));
333
- } catch {
334
- this.phase = "restart-required";
335
- this.message = "\u5B89\u88C5\u7248\u672C\u7F3A\u5C11\u7248\u672C\u5316\u5165\u53E3\uFF0C\u91CD\u65B0\u5B89\u88C5\u6700\u65B0\u7248\u540E\u518D\u8BD5\u3002";
336
- return;
337
- }
338
- if (this.now() < this.retryAt) return;
339
- this.phase = "reloading";
340
- this.message = "\u6B63\u5728\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u4E0E\u914D\u7F6E\u9762\u677F\u2026";
341
- this.retryAt = this.now() + 6e4;
342
- try {
343
- const result = await outsideActivation(this.hmr(), () => this.service().setBundleEnabled("dsh-multi-router", true));
344
- if (this.closed) return;
345
- this.phase = "restart-required";
346
- this.message = result.application === "applied" ? "DSH \u5C1A\u672A\u5207\u6362\u5230\u65B0\u7248\u672C\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u6216\u67E5\u770B\u7EC4\u4EF6\u9519\u8BEF\u3002" : "DSH \u672A\u80FD\u91CD\u8F7D\u63D2\u4EF6\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
347
- } catch {
348
- if (!this.closed) {
349
- this.phase = "failed";
350
- this.message = "\u63D2\u4EF6\u91CD\u8F7D\u672A\u5B8C\u6210\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002";
351
- }
352
- }
353
- }
354
- };
355
-
356
253
  // src/index.mjs
357
254
  var name = "dsh-multi-router";
358
255
  var inject = ["llm"];
359
- var defaultConfigFile = () => join2(resolveDshHome(), "multi-router", "router.json");
256
+ var defaultConfigFile = () => join(resolveDshHome(), "multi-router", "router.json");
360
257
  var Config = z.object({ configFile: z.string().description("Absolute path to multi-account router.json") });
361
258
  async function apply(ctx, options = {}) {
362
259
  let disposed = false, manager, stopWatch, monitorTimer, startup;
@@ -371,7 +268,7 @@ async function apply(ctx, options = {}) {
371
268
  manager?.checkins.abort.abort();
372
269
  manager?.workbuddyLogin.abort.abort();
373
270
  for (const release of releases) release();
374
- return Promise.all([...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
271
+ return Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
375
272
  }), manager?.telemetry.queue.catch(() => {
376
273
  })]);
377
274
  });
@@ -387,7 +284,7 @@ async function apply(ctx, options = {}) {
387
284
  manager = new Manager(file, router, () => {
388
285
  for (const wrapper of wrappers) wrapper.invalidate();
389
286
  ctx.emit("llm/adapters-updated");
390
- }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext") });
287
+ }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext"), hmr: () => ctx.get("hmr") });
391
288
  await manager.load();
392
289
  if (disposed) return;
393
290
  manager.startup = { phase: "discovering" };
@@ -397,9 +294,10 @@ async function apply(ctx, options = {}) {
397
294
  profile: () => ctx.get("profileContext"),
398
295
  canReload: () => !!ctx.get("hmr"),
399
296
  hmr: () => ctx.get("hmr"),
400
- busy: () => !!(manager.localScan || manager.requests || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
297
+ busy: () => !!(manager.localScan || manager.requests || manager.api.controllers.size || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
401
298
  });
402
299
  ctx.inject?.(["webServer"], (webCtx) => {
300
+ webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: SUMMARY_PATH, handler: managementHandler(manager, { summaryOnly: true }) }));
403
301
  webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: MANAGEMENT_PATH, handler: managementHandler(manager) }));
404
302
  });
405
303
  const metrics = new SessionMetrics(manager.telemetry);
@@ -469,6 +367,7 @@ async function apply(ctx, options = {}) {
469
367
  releases.push(ctx.llm.registerAdapter([provider], wrapper.adapter));
470
368
  }
471
369
  stopWatch = manager.watch(() => ctx.logger.warn("multi-router: invalid config change; keeping last valid configuration"));
370
+ await manager.api.reconcile();
472
371
  startup = (async () => {
473
372
  await manager.syncLocalAccounts().catch(() => ctx.logger.warn("multi-router: local login discovery failed; manual import remains available"));
474
373
  if (disposed) return;
@@ -499,7 +398,7 @@ async function apply(ctx, options = {}) {
499
398
  stopWatch?.();
500
399
  manager?.runtime?.stop();
501
400
  for (const release of releases) release();
502
- await Promise.all(shims.map((shim) => shim.close()));
401
+ await Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close())]);
503
402
  ctx.logger.error("multi-router: initialization failed", error.code ?? error.message);
504
403
  throw error;
505
404
  }
package/lib/index.js CHANGED
@@ -1,38 +1,42 @@
1
1
  import {
2
2
  createShim
3
- } from "./chunk-P43AWJ4Q.js";
3
+ } from "./chunk-LLDJVBTQ.js";
4
4
  import {
5
5
  MANAGEMENT_PATH,
6
6
  Manager,
7
+ PluginReload,
8
+ SUMMARY_PATH,
7
9
  managementHandler,
8
10
  version
9
- } from "./chunk-ILRW3ELD.js";
11
+ } from "./chunk-OCMOMSB4.js";
10
12
  import {
11
13
  createAccountClient
12
- } from "./chunk-U2MU2M5C.js";
13
- import "./chunk-OFFVP6YN.js";
14
- import "./chunk-FTM7RHQV.js";
14
+ } from "./chunk-NSKCFVHP.js";
15
+ import "./chunk-JUOESKAK.js";
16
+ import "./chunk-OK2T2UAZ.js";
17
+ import {
18
+ normalizeCredits
19
+ } from "./chunk-TEZ2FMEO.js";
15
20
  import "./chunk-S5KMFAQM.js";
21
+ import "./chunk-SNEZMYNU.js";
22
+ import "./chunk-4V52V5GO.js";
16
23
  import "./chunk-DNQPZMPF.js";
17
24
  import {
18
25
  Router,
19
26
  SessionMetrics,
20
27
  abortable
21
- } from "./chunk-J3XO7RTF.js";
28
+ } from "./chunk-ROZINFTV.js";
22
29
  import {
23
30
  PROVIDERS,
24
31
  PROVIDER_NAMES,
25
32
  ensureConfig
26
- } from "./chunk-MCHV2R25.js";
27
- import {
28
- normalizeCredits
29
- } from "./chunk-TEZ2FMEO.js";
33
+ } from "./chunk-WVNY4LSL.js";
30
34
 
31
35
  // src/index.mjs
32
36
  import z from "@deepseek-ai/schemastery";
33
37
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
34
38
  import { resolveRetryPolicy as resolveRetryPolicy2, resolveImageAttachmentAccess } from "@deepseek-ai/dsh-llm";
35
- import { join as join2, resolve } from "node:path";
39
+ import { join, resolve } from "node:path";
36
40
 
37
41
  // upstream/workbuddy/src/adapter.ts
38
42
  import { createProvider } from "@earendil-works/pi-ai";
@@ -246,117 +250,10 @@ var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
246
250
  }
247
251
  };
248
252
 
249
- // src/plugin-reload.mjs
250
- import { readFile } from "node:fs/promises";
251
- import { watch } from "node:fs";
252
- import { join } from "node:path";
253
- function outsideActivation(hmr, operation) {
254
- const scope = hmr?.executing;
255
- return typeof scope?.exit === "function" ? scope.exit(operation) : operation();
256
- }
257
- var PluginReload = class {
258
- constructor({ service, profile, canReload, hmr = () => void 0, busy = () => false, now = Date.now, runningVersion }) {
259
- Object.assign(this, { service, profile, canReload, hmr, busy, now, runningVersion });
260
- this.phase = "current";
261
- this.installedVersion = runningVersion;
262
- this.retryAt = 0;
263
- }
264
- state() {
265
- return { runningVersion: this.runningVersion, installedVersion: this.installedVersion, phase: this.phase, canReload: !!this.canReload(), message: this.message };
266
- }
267
- start() {
268
- const dir = this.profile()?.dir;
269
- if (dir) try {
270
- this.watcher = watch(dir, () => this.schedule());
271
- this.watcher.unref();
272
- } catch {
273
- }
274
- this.schedule();
275
- }
276
- schedule(delay = 500) {
277
- if (this.closed) return;
278
- clearTimeout(this.timer);
279
- this.timer = setTimeout(() => {
280
- void this.check().catch(() => {
281
- });
282
- }, delay);
283
- this.timer.unref();
284
- }
285
- stop() {
286
- this.closed = true;
287
- clearTimeout(this.timer);
288
- this.watcher?.close();
289
- }
290
- check() {
291
- if (this.closed) return Promise.resolve();
292
- if (this.flight) return this.flight;
293
- this.flight = this.inspect().finally(() => {
294
- this.flight = void 0;
295
- });
296
- return this.flight;
297
- }
298
- async inspect() {
299
- const dir = this.profile()?.dir;
300
- if (!dir) return;
301
- let manifest, installed;
302
- try {
303
- manifest = JSON.parse(await readFile(join(dir, "package.json"), "utf8"));
304
- installed = JSON.parse(await readFile(join(dir, "node_modules/dsh-multi-router/package.json"), "utf8"));
305
- } catch {
306
- return;
307
- }
308
- if (this.closed || !manifest.dsh?.profile?.bundles?.includes("dsh-multi-router")) return;
309
- if (installed.name !== "dsh-multi-router" || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(installed.version)) return;
310
- this.installedVersion = installed.version;
311
- if (installed.version === this.runningVersion) {
312
- this.phase = "current";
313
- this.message = void 0;
314
- return;
315
- }
316
- if (!this.canReload() || !this.service()?.setBundleEnabled) {
317
- this.phase = "restart-required";
318
- this.message = "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u8FD0\u884C\u4E2D\u91CD\u8F7D\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
319
- return;
320
- }
321
- if (this.busy()) {
322
- this.phase = "waiting";
323
- this.message = "\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u7B49\u5F85\u5F53\u524D\u8BF7\u6C42\u548C\u8D26\u53F7\u64CD\u4F5C\u7ED3\u675F\u540E\u81EA\u52A8\u751F\u6548\u3002";
324
- this.schedule(1e3);
325
- return;
326
- }
327
- const root = join(dir, "node_modules/dsh-multi-router");
328
- try {
329
- const patch = await readFile(join(root, "cordis.patch.yml"), "utf8");
330
- if (!patch.includes(`id: llm-multi-router-v2-${installed.version}
331
- `) || !patch.includes(`name: ./lib/host-v${installed.version}.js`)) throw Error("legacy entry");
332
- await readFile(join(root, `lib/host-v${installed.version}.js`));
333
- } catch {
334
- this.phase = "restart-required";
335
- this.message = "\u5B89\u88C5\u7248\u672C\u7F3A\u5C11\u7248\u672C\u5316\u5165\u53E3\uFF0C\u91CD\u65B0\u5B89\u88C5\u6700\u65B0\u7248\u540E\u518D\u8BD5\u3002";
336
- return;
337
- }
338
- if (this.now() < this.retryAt) return;
339
- this.phase = "reloading";
340
- this.message = "\u6B63\u5728\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u4E0E\u914D\u7F6E\u9762\u677F\u2026";
341
- this.retryAt = this.now() + 6e4;
342
- try {
343
- const result = await outsideActivation(this.hmr(), () => this.service().setBundleEnabled("dsh-multi-router", true));
344
- if (this.closed) return;
345
- this.phase = "restart-required";
346
- this.message = result.application === "applied" ? "DSH \u5C1A\u672A\u5207\u6362\u5230\u65B0\u7248\u672C\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u6216\u67E5\u770B\u7EC4\u4EF6\u9519\u8BEF\u3002" : "DSH \u672A\u80FD\u91CD\u8F7D\u63D2\u4EF6\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
347
- } catch {
348
- if (!this.closed) {
349
- this.phase = "failed";
350
- this.message = "\u63D2\u4EF6\u91CD\u8F7D\u672A\u5B8C\u6210\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002";
351
- }
352
- }
353
- }
354
- };
355
-
356
253
  // src/index.mjs
357
254
  var name = "dsh-multi-router";
358
255
  var inject = ["llm"];
359
- var defaultConfigFile = () => join2(resolveDshHome(), "multi-router", "router.json");
256
+ var defaultConfigFile = () => join(resolveDshHome(), "multi-router", "router.json");
360
257
  var Config = z.object({ configFile: z.string().description("Absolute path to multi-account router.json") });
361
258
  async function apply(ctx, options = {}) {
362
259
  let disposed = false, manager, stopWatch, monitorTimer, startup;
@@ -371,7 +268,7 @@ async function apply(ctx, options = {}) {
371
268
  manager?.checkins.abort.abort();
372
269
  manager?.workbuddyLogin.abort.abort();
373
270
  for (const release of releases) release();
374
- return Promise.all([...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
271
+ return Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close()), ...manager?.refreshes.values() ?? [], ...manager?.monitorFlights.values() ?? [], startup, manager?.localScan, manager?.checkins.flight, manager?.plugins.flight, manager?.plugins.saveQueue.catch(() => {
375
272
  }), manager?.telemetry.queue.catch(() => {
376
273
  })]);
377
274
  });
@@ -387,7 +284,7 @@ async function apply(ctx, options = {}) {
387
284
  manager = new Manager(file, router, () => {
388
285
  for (const wrapper of wrappers) wrapper.invalidate();
389
286
  ctx.emit("llm/adapters-updated");
390
- }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext") });
287
+ }, { service: () => ctx.get("pluginManager"), profile: () => ctx.get("profileContext"), hmr: () => ctx.get("hmr") });
391
288
  await manager.load();
392
289
  if (disposed) return;
393
290
  manager.startup = { phase: "discovering" };
@@ -397,9 +294,10 @@ async function apply(ctx, options = {}) {
397
294
  profile: () => ctx.get("profileContext"),
398
295
  canReload: () => !!ctx.get("hmr"),
399
296
  hmr: () => ctx.get("hmr"),
400
- busy: () => !!(manager.localScan || manager.requests || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
297
+ busy: () => !!(manager.localScan || manager.requests || manager.api.controllers.size || manager.plugins.flight || manager.checkins.flight || manager.workbuddyLogin.flight || manager.workbuddyLogin.pending || manager.refreshes.size || manager.monitorFlights.size || shims.some((s) => s.active()))
401
298
  });
402
299
  ctx.inject?.(["webServer"], (webCtx) => {
300
+ webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: SUMMARY_PATH, handler: managementHandler(manager, { summaryOnly: true }) }));
403
301
  webCtx.effect(() => webCtx.webServer.register({ kind: "exact", path: MANAGEMENT_PATH, handler: managementHandler(manager) }));
404
302
  });
405
303
  const metrics = new SessionMetrics(manager.telemetry);
@@ -469,6 +367,7 @@ async function apply(ctx, options = {}) {
469
367
  releases.push(ctx.llm.registerAdapter([provider], wrapper.adapter));
470
368
  }
471
369
  stopWatch = manager.watch(() => ctx.logger.warn("multi-router: invalid config change; keeping last valid configuration"));
370
+ await manager.api.reconcile();
472
371
  startup = (async () => {
473
372
  await manager.syncLocalAccounts().catch(() => ctx.logger.warn("multi-router: local login discovery failed; manual import remains available"));
474
373
  if (disposed) return;
@@ -499,7 +398,7 @@ async function apply(ctx, options = {}) {
499
398
  stopWatch?.();
500
399
  manager?.runtime?.stop();
501
400
  for (const release of releases) release();
502
- await Promise.all(shims.map((shim) => shim.close()));
401
+ await Promise.all([manager?.api.close(), ...shims.map((shim) => shim.close())]);
503
402
  ctx.logger.error("multi-router: initialization failed", error.code ?? error.message);
504
403
  throw error;
505
404
  }