dsh-multi-router 0.5.3 → 0.5.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.
@@ -3,20 +3,20 @@ import {
3
3
  jsonCredential,
4
4
  qoderPluginDataDir,
5
5
  supportsCheckin
6
- } from "./chunk-RPW6DI6O.js";
6
+ } from "./chunk-EGLF4CWD.js";
7
7
  import {
8
8
  discoverLocalAccounts,
9
9
  importAccount,
10
10
  localAccountKey,
11
11
  readPortableCredential
12
- } from "./chunk-4H6ZXK75.js";
12
+ } from "./chunk-OFFVP6YN.js";
13
13
  import {
14
14
  hostIsLoopback
15
15
  } from "./chunk-DNQPZMPF.js";
16
16
  import {
17
17
  Telemetry,
18
18
  matchModel
19
- } from "./chunk-XA3V2DGT.js";
19
+ } from "./chunk-J3XO7RTF.js";
20
20
  import {
21
21
  PROVIDERS,
22
22
  PROVIDER_NAMES,
@@ -27,7 +27,7 @@ import {
27
27
  routeReasoning,
28
28
  validateConfig,
29
29
  writePrivateJson
30
- } from "./chunk-U7J4ABAM.js";
30
+ } from "./chunk-MCHV2R25.js";
31
31
  import {
32
32
  modelWithCurrentPromotion
33
33
  } from "./chunk-TEZ2FMEO.js";
@@ -36,7 +36,7 @@ import {
36
36
  import { createHash, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
37
37
 
38
38
  // package.json
39
- var version = "0.5.3";
39
+ var version = "0.5.5";
40
40
 
41
41
  // src/manager.mjs
42
42
  import { readFile as readFile2, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
@@ -98,7 +98,7 @@ var derive = promisify(scrypt);
98
98
  var FORMAT = "dsh-multi-router";
99
99
  var MAX_BYTES = 512 * 1024;
100
100
  var pick = (value, keys) => Object.fromEntries(keys.filter((k) => Object.hasOwn(value ?? {}, k)).map((k) => [k, value[k]]));
101
- var options = ["cooldownMs", "requestTimeoutMs", "catalogTtlMs", "retryServerErrors", "modelVisibility", "usageRetentionDays"];
101
+ var options = ["cooldownMs", "requestTimeoutMs", "catalogTtlMs", "retryServerErrors", "modelVisibility", "usageRetentionDays", "checkinSchedule"];
102
102
  function settings(config) {
103
103
  return {
104
104
  ...pick(config, options),
@@ -414,7 +414,12 @@ async function refreshGitPlugin(service, profile, row, options2, signal) {
414
414
  }, { waitMs: 12e4 });
415
415
  }
416
416
  var PluginUpdates = class {
417
- constructor({ service = () => void 0, profile = () => void 0, fetchImpl = (...args) => fetch(...args), lookup = registryVersion, refreshGit = refreshGitPlugin } = {}) {
417
+ constructor({ service = () => void 0, profile = () => void 0, fetchImpl = (...args) => fetch(...args), lookup = registryVersion, refreshGit = refreshGitPlugin, stateFile, now = Date.now } = {}) {
418
+ this.stateFile = stateFile;
419
+ this.now = now;
420
+ this.dismissed = {};
421
+ this.lastAttempt = 0;
422
+ this.saveQueue = Promise.resolve();
418
423
  this.service = service;
419
424
  this.profile = profile;
420
425
  this.fetchImpl = fetchImpl;
@@ -423,10 +428,47 @@ var PluginUpdates = class {
423
428
  this.lookup = lookup;
424
429
  this.refreshGit = refreshGit;
425
430
  }
431
+ async load() {
432
+ const saved = this.stateFile && await jsonFile(this.stateFile);
433
+ if (saved?.version === 1) {
434
+ this.lastAttempt = Number.isFinite(saved.lastAttempt) ? saved.lastAttempt : 0;
435
+ this.checkedAt = saved.checkedAt;
436
+ this.dismissed = saved.dismissed && typeof saved.dismissed === "object" && !Array.isArray(saved.dismissed) ? saved.dismissed : {};
437
+ this.rows = Array.isArray(saved.rows) ? saved.rows.filter((r) => ["github", "npm"].includes(r.source?.kind)) : [];
438
+ }
439
+ }
440
+ persist() {
441
+ if (!this.stateFile) return Promise.resolve();
442
+ const saved = structuredClone({ version: 1, lastAttempt: this.lastAttempt, checkedAt: this.checkedAt, dismissed: this.dismissed, rows: this.rows.filter((r) => ["github", "npm"].includes(r.source.kind)) });
443
+ this.saveQueue = this.saveQueue.catch(() => {
444
+ }).then(() => writePrivateJson(this.stateFile, saved));
445
+ return this.saveQueue;
446
+ }
447
+ reminderKey(row) {
448
+ return JSON.stringify([row.name, row.source, row.version, row.latestVersion ?? row.latestCommit]);
449
+ }
450
+ reminders() {
451
+ return this.rows.filter((r) => r.status === "available" && (!Object.hasOwn(this.dismissed, r.name) || this.dismissed[r.name] !== this.reminderKey(r))).map((r) => ({ key: this.reminderKey(r), name: r.name, version: r.version, latestVersion: r.latestVersion, latestCommit: r.latestCommit }));
452
+ }
453
+ async dismiss(keys) {
454
+ if (!Array.isArray(keys) || keys.length > 200 || keys.some((k) => typeof k !== "string")) throw Error("\u65E0\u6548\u7684\u63D0\u9192\u5217\u8868");
455
+ for (const row of this.reminders()) if (keys.includes(row.key)) Object.defineProperty(this.dismissed, row.name, { value: row.key, enumerable: true, configurable: true, writable: true });
456
+ await this.persist();
457
+ }
458
+ async tick() {
459
+ if (this.flight || !this.state().available) return;
460
+ await this.list();
461
+ if (this.flight || this.now() >= this.lastAttempt && this.now() - this.lastAttempt < 864e5) return;
462
+ this.lastAttempt = this.now();
463
+ await this.persist();
464
+ if (!this.flight && !this.abort.signal.aborted) this.check();
465
+ }
426
466
  state() {
427
467
  return {
428
468
  available: !!this.service()?.listBundles && !!this.profile()?.dir,
429
469
  checkedAt: this.checkedAt,
470
+ reminders: this.reminders(),
471
+ autoCheckHours: 24,
430
472
  rows: this.rows.map(({ spec, ...row }) => ({ ...row, source: row.source.kind === "unsupported" ? { kind: "unsupported" } : row.source })),
431
473
  job: this.job,
432
474
  error: this.error
@@ -440,9 +482,9 @@ var PluginUpdates = class {
440
482
  }
441
483
  const [manifest, bundles, lock] = await Promise.all([jsonFile(join3(profile.dir, "package.json")), service.listBundles(), locks(profile.dir)]);
442
484
  if (!manifest) throw Error("\u5F53\u524D DSH \u914D\u7F6E\u7684\u63D2\u4EF6\u6E05\u5355\u65E0\u6CD5\u8BFB\u53D6");
443
- this.rows = bundles.filter((b) => b.installed || b.version).map((b) => {
485
+ this.rows = bundles.filter((b) => (b.installed || b.version) && manifest.dependencies?.[b.name] && b.removable !== false).map((b) => {
444
486
  const spec = manifest.dependencies?.[b.name];
445
- const source = !spec || b.removable === false && !b.readOnlyReason ? { kind: "builtin" } : parsePluginSource(b.name, spec);
487
+ const source = parsePluginSource(b.name, spec);
446
488
  const npm = lock.npm?.packages?.[`node_modules/${b.name}`]?.resolved;
447
489
  const pnpm = lock.pnpm?.importers?.["."]?.dependencies?.[b.name]?.version;
448
490
  const installed = lock.preferNpm ? npm ?? pnpm : pnpm ?? npm;
@@ -455,8 +497,7 @@ var PluginUpdates = class {
455
497
  spec,
456
498
  source,
457
499
  commit: githubCommit(installed),
458
- status: previous?.status ?? (source.kind === "builtin" ? "builtin" : source.kind === "unsupported" ? "unsupported" : "unchecked"),
459
- ...source.kind === "builtin" ? { message: "DSH \u81EA\u5E26 / \u53D7\u4FDD\u62A4\u7EC4\u4EF6\uFF0C\u8BF7\u901A\u8FC7\u66F4\u65B0 DeepSeek Harness \u5347\u7EA7" } : {},
500
+ status: previous?.status ?? (source.kind === "unsupported" ? "unsupported" : "unchecked"),
460
501
  ...source.kind === "unsupported" ? { message: "\u672C\u5730\u6587\u4EF6 / \u94FE\u63A5\u6765\u6E90\uFF0C\u8BF7\u901A\u8FC7\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0" } : {}
461
502
  };
462
503
  });
@@ -503,13 +544,20 @@ var PluginUpdates = class {
503
544
  }, (e) => {
504
545
  this.job.state = this.job.cancelRequested ? "cancelled" : "failed";
505
546
  this.job.message = e.message;
506
- }).finally(() => {
547
+ }).finally(async () => {
507
548
  this.job.finishedAt = Date.now();
549
+ try {
550
+ await this.persist();
551
+ } catch {
552
+ this.error = "\u66F4\u65B0\u68C0\u67E5\u8BB0\u5F55\u672A\u80FD\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u672C\u5730\u76EE\u5F55\u6743\u9650";
553
+ }
508
554
  this.flight = void 0;
509
555
  });
510
556
  }
511
557
  check() {
512
558
  this.start("check", async (job) => {
559
+ this.lastAttempt = this.now();
560
+ await this.persist();
513
561
  await this.list();
514
562
  const rows = this.rows.filter((r) => ["npm", "github"].includes(r.source.kind));
515
563
  job.total = rows.length;
@@ -618,7 +666,8 @@ var PluginUpdates = class {
618
666
  if (input.registry && !["https://registry.npmjs.org/", "https://registry.npmmirror.com/"].includes(input.registry)) throw Error("\u65E0\u6548\u7684 npm \u6765\u6E90");
619
667
  if (input.action === "plugins.list") {
620
668
  if (!this.flight) await this.list();
621
- } else if (input.action === "plugins.check") this.check();
669
+ } else if (input.action === "plugins.dismiss") await this.dismiss(input.keys);
670
+ else if (input.action === "plugins.check") this.check();
622
671
  else if (input.action === "plugins.update") this.update(input.names, input.registry);
623
672
  else if (input.action === "plugins.install") this.install(input.spec, input.registry);
624
673
  else if (input.action === "plugins.cancel") {
@@ -682,7 +731,7 @@ var Manager = class {
682
731
  this.monitorFlights = /* @__PURE__ */ new Map();
683
732
  this.loginInfo = /* @__PURE__ */ new Map();
684
733
  this.telemetry = new Telemetry(join4(dirname3(file), "usage.json"));
685
- this.plugins = new PluginUpdates(pluginAccess);
734
+ this.plugins = new PluginUpdates({ ...pluginAccess, stateFile: join4(dirname3(file), "plugin-updates.json") });
686
735
  this.checkins = new Checkins(join4(dirname3(file), "checkins.json"), this);
687
736
  this.setupFile = join4(dirname3(file), "initial-setup.json");
688
737
  router.telemetry = this.telemetry;
@@ -699,6 +748,10 @@ var Manager = class {
699
748
  }
700
749
  this.config = validateConfig(this.raw, this.file);
701
750
  await this.checkins.load();
751
+ await this.plugins.load();
752
+ if (this.plugins.state().available) await this.plugins.list().catch(() => {
753
+ this.plugins.rows = [];
754
+ });
702
755
  try {
703
756
  const saved = JSON.parse(await readFile2(this.catalogFile, "utf8"));
704
757
  for (const account of this.config.accounts) {
@@ -1264,6 +1317,9 @@ var Manager = class {
1264
1317
  }
1265
1318
  if (input.name !== void 0) account.name = input.name;
1266
1319
  if (input.enabled !== void 0) account.enabled = input.enabled;
1320
+ } else if (input.action === "accounts.enabled") {
1321
+ if (input.provider !== void 0 && !PROVIDERS.includes(input.provider) || typeof input.enabled !== "boolean") bad("\u4F9B\u5E94\u5546\u6216\u8DEF\u7531\u53C2\u4E0E\u5F00\u5173\u65E0\u6548");
1322
+ for (const a of raw.accounts.filter((a2) => !input.provider || a2.provider === input.provider)) a.enabled = input.enabled;
1267
1323
  } else if (input.action === "account.move") {
1268
1324
  if (!account || ![-1, 1].includes(input.direction)) bad("\u8D26\u53F7\u6216\u987A\u5E8F\u65E0\u6548");
1269
1325
  const group = raw.accounts.filter((a) => a.provider === account.provider), position = group.indexOf(account) + input.direction;
@@ -1334,6 +1390,10 @@ var Manager = class {
1334
1390
  raw.routes[id] = structuredClone(ROUTE_PRESETS[id]);
1335
1391
  } else if (input.action === "preset.add-all") {
1336
1392
  for (const [id, route] of Object.entries(ROUTE_PRESETS)) if (!Object.hasOwn(raw.routes, id)) raw.routes[id] = structuredClone(route);
1393
+ } else if (input.action === "checkins.settings") {
1394
+ if (this.checkins.flight) bad("\u8BF7\u7B49\u5F85\u5F53\u524D\u7B7E\u5230\u5B8C\u6210\u540E\u4FEE\u6539\u8BA1\u5212");
1395
+ if (!input.schedule) bad("\u8BF7\u586B\u5199\u7B7E\u5230\u8BA1\u5212");
1396
+ raw.checkinSchedule = input.schedule;
1337
1397
  } else if (input.action === "options.save") {
1338
1398
  for (const key of ["cooldownMs", "requestTimeoutMs", "retryServerErrors", "usageRetentionDays"]) if (input[key] !== void 0) raw[key] = input[key];
1339
1399
  } else bad("\u672A\u77E5\u64CD\u4F5C");
@@ -1452,4 +1512,4 @@ export {
1452
1512
  Manager,
1453
1513
  managementHandler
1454
1514
  };
1455
- //# sourceMappingURL=chunk-EJWYFWIT.js.map
1515
+ //# sourceMappingURL=chunk-PQETKUGT.js.map