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.
- package/CHANGELOG.md +20 -0
- package/README.md +49 -8
- package/cordis.patch.yml +2 -2
- package/lib/accounts.js +6 -4
- package/lib/api-service.js +11 -0
- package/lib/api-service.js.map +7 -0
- package/lib/chunk-4V52V5GO.js +105 -0
- package/lib/chunk-4V52V5GO.js.map +7 -0
- package/lib/{chunk-OFFVP6YN.js → chunk-JUOESKAK.js} +22 -5
- package/lib/chunk-JUOESKAK.js.map +7 -0
- package/lib/{chunk-P43AWJ4Q.js → chunk-LLDJVBTQ.js} +3 -3
- package/lib/{chunk-U2MU2M5C.js → chunk-NSKCFVHP.js} +13 -111
- package/lib/chunk-NSKCFVHP.js.map +7 -0
- package/lib/{chunk-ILRW3ELD.js → chunk-OCMOMSB4.js} +293 -77
- package/lib/chunk-OCMOMSB4.js.map +7 -0
- package/lib/{chunk-FTM7RHQV.js → chunk-OK2T2UAZ.js} +4 -4
- package/lib/{chunk-J3XO7RTF.js → chunk-ROZINFTV.js} +3 -2
- package/lib/chunk-ROZINFTV.js.map +7 -0
- package/lib/chunk-SNEZMYNU.js +332 -0
- package/lib/chunk-SNEZMYNU.js.map +7 -0
- package/lib/{chunk-MCHV2R25.js → chunk-WVNY4LSL.js} +3 -1
- package/lib/{chunk-MCHV2R25.js.map → chunk-WVNY4LSL.js.map} +2 -2
- package/lib/cli.js +7 -6
- package/lib/cli.js.map +1 -1
- package/lib/client.js +743 -315
- package/lib/client.js.map +2 -2
- package/lib/clients.js +6 -5
- package/lib/{host-v0.5.8.js → host-v0.6.0.js} +22 -123
- package/lib/host.js +22 -123
- package/lib/index.js +22 -123
- package/lib/index.js.map +4 -4
- package/lib/manager.js +11 -7
- package/lib/router.js +2 -2
- package/lib/shim.js +3 -3
- package/package.json +1 -1
- package/lib/chunk-ILRW3ELD.js.map +0 -7
- package/lib/chunk-J3XO7RTF.js.map +0 -7
- package/lib/chunk-OFFVP6YN.js.map +0 -7
- package/lib/chunk-U2MU2M5C.js.map +0 -7
- /package/lib/{chunk-P43AWJ4Q.js.map → chunk-LLDJVBTQ.js.map} +0 -0
- /package/lib/{chunk-FTM7RHQV.js.map → chunk-OK2T2UAZ.js.map} +0 -0
|
@@ -6,20 +6,27 @@ import {
|
|
|
6
6
|
refreshWorkBuddyCheckinCredential,
|
|
7
7
|
supportsCheckin,
|
|
8
8
|
workbuddyToken
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-NSKCFVHP.js";
|
|
10
10
|
import {
|
|
11
11
|
discoverLocalAccounts,
|
|
12
12
|
importAccount,
|
|
13
13
|
localAccountKey,
|
|
14
|
+
normalizeLocalAccountKey,
|
|
14
15
|
readPortableCredential
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-JUOESKAK.js";
|
|
17
|
+
import {
|
|
18
|
+
modelWithCurrentPromotion
|
|
19
|
+
} from "./chunk-TEZ2FMEO.js";
|
|
20
|
+
import {
|
|
21
|
+
ApiService
|
|
22
|
+
} from "./chunk-SNEZMYNU.js";
|
|
16
23
|
import {
|
|
17
24
|
hostIsLoopback
|
|
18
25
|
} from "./chunk-DNQPZMPF.js";
|
|
19
26
|
import {
|
|
20
27
|
Telemetry,
|
|
21
28
|
matchModel
|
|
22
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-ROZINFTV.js";
|
|
23
30
|
import {
|
|
24
31
|
PROVIDERS,
|
|
25
32
|
PROVIDER_NAMES,
|
|
@@ -30,21 +37,18 @@ import {
|
|
|
30
37
|
routeReasoning,
|
|
31
38
|
validateConfig,
|
|
32
39
|
writePrivateJson
|
|
33
|
-
} from "./chunk-
|
|
34
|
-
import {
|
|
35
|
-
modelWithCurrentPromotion
|
|
36
|
-
} from "./chunk-TEZ2FMEO.js";
|
|
40
|
+
} from "./chunk-WVNY4LSL.js";
|
|
37
41
|
|
|
38
42
|
// src/manager.mjs
|
|
39
43
|
import { createHash, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
40
44
|
|
|
41
45
|
// package.json
|
|
42
|
-
var version = "0.
|
|
46
|
+
var version = "0.6.0";
|
|
43
47
|
|
|
44
48
|
// src/manager.mjs
|
|
45
|
-
import { readFile as
|
|
46
|
-
import { watch } from "node:fs";
|
|
47
|
-
import { dirname as dirname3, join as
|
|
49
|
+
import { readFile as readFile4, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
50
|
+
import { watch as watch2 } from "node:fs";
|
|
51
|
+
import { dirname as dirname3, join as join5, relative as relative2, resolve } from "node:path";
|
|
48
52
|
import { withFileLock as withFileLock3 } from "@deepseek-ai/dsh-atomic-write";
|
|
49
53
|
|
|
50
54
|
// upstream/qoder/src/status-paths.ts
|
|
@@ -101,7 +105,7 @@ var derive = promisify(scrypt);
|
|
|
101
105
|
var FORMAT = "dsh-multi-router";
|
|
102
106
|
var MAX_BYTES = 512 * 1024;
|
|
103
107
|
var pick = (value, keys) => Object.fromEntries(keys.filter((k) => Object.hasOwn(value ?? {}, k)).map((k) => [k, value[k]]));
|
|
104
|
-
var options = ["cooldownMs", "requestTimeoutMs", "catalogTtlMs", "retryServerErrors", "modelVisibility", "usageRetentionDays", "checkinSchedule"];
|
|
108
|
+
var options = ["cooldownMs", "requestTimeoutMs", "catalogTtlMs", "retryServerErrors", "modelVisibility", "usageRetentionDays", "checkinSchedule", "sidebarUsage"];
|
|
105
109
|
function settings(config) {
|
|
106
110
|
return {
|
|
107
111
|
...pick(config, options),
|
|
@@ -239,14 +243,140 @@ async function prepareMigration(manager, raw, portable, mode) {
|
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
// src/plugin-updates.mjs
|
|
242
|
-
import { readFile, stat, rm as rm2 } from "node:fs/promises";
|
|
243
|
-
import { join as
|
|
246
|
+
import { readFile as readFile2, stat, rm as rm2 } from "node:fs/promises";
|
|
247
|
+
import { join as join4 } from "node:path";
|
|
244
248
|
import { randomUUID } from "node:crypto";
|
|
245
249
|
import { createRequire } from "node:module";
|
|
246
250
|
import { pathToFileURL } from "node:url";
|
|
247
251
|
import { parse as parseYaml } from "yaml";
|
|
248
252
|
import { withFileLock, writeFileAtomic as writeFileAtomic2 } from "@deepseek-ai/dsh-atomic-write";
|
|
249
253
|
|
|
254
|
+
// src/plugin-reload.mjs
|
|
255
|
+
import { readFile } from "node:fs/promises";
|
|
256
|
+
import { watch } from "node:fs";
|
|
257
|
+
import { join as join3 } from "node:path";
|
|
258
|
+
import { parse } from "yaml";
|
|
259
|
+
function outsideActivation(hmr, operation) {
|
|
260
|
+
const scope = hmr?.executing;
|
|
261
|
+
return typeof scope?.exit === "function" ? scope.exit(operation) : operation();
|
|
262
|
+
}
|
|
263
|
+
var PluginReload = class {
|
|
264
|
+
constructor({ service, profile, canReload, hmr = () => void 0, busy = () => false, now = Date.now, runningVersion }) {
|
|
265
|
+
Object.assign(this, { service, profile, canReload, hmr, busy, now, runningVersion });
|
|
266
|
+
this.phase = "current";
|
|
267
|
+
this.installedVersion = runningVersion;
|
|
268
|
+
this.retryAt = 0;
|
|
269
|
+
}
|
|
270
|
+
state() {
|
|
271
|
+
return { runningVersion: this.runningVersion, installedVersion: this.installedVersion, phase: this.phase, canReload: !!this.canReload(), message: this.message };
|
|
272
|
+
}
|
|
273
|
+
start() {
|
|
274
|
+
const dir = this.profile()?.dir;
|
|
275
|
+
this.watchers = [];
|
|
276
|
+
if (dir) for (const target of [dir, join3(dir, "node_modules")]) try {
|
|
277
|
+
const watcher = watch(target, () => this.schedule());
|
|
278
|
+
watcher.unref();
|
|
279
|
+
this.watchers.push(watcher);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
this.schedule();
|
|
283
|
+
}
|
|
284
|
+
schedule(delay = 500) {
|
|
285
|
+
if (this.closed) return;
|
|
286
|
+
clearTimeout(this.timer);
|
|
287
|
+
this.timer = setTimeout(() => {
|
|
288
|
+
void this.check().catch(() => {
|
|
289
|
+
});
|
|
290
|
+
}, delay);
|
|
291
|
+
this.timer.unref();
|
|
292
|
+
}
|
|
293
|
+
stop() {
|
|
294
|
+
this.closed = true;
|
|
295
|
+
clearTimeout(this.timer);
|
|
296
|
+
for (const watcher of this.watchers ?? []) watcher.close();
|
|
297
|
+
}
|
|
298
|
+
check() {
|
|
299
|
+
if (this.closed) return Promise.resolve();
|
|
300
|
+
if (this.flight) return this.flight;
|
|
301
|
+
this.flight = this.inspect().finally(() => {
|
|
302
|
+
this.flight = void 0;
|
|
303
|
+
});
|
|
304
|
+
return this.flight;
|
|
305
|
+
}
|
|
306
|
+
async inspect() {
|
|
307
|
+
const dir = this.profile()?.dir;
|
|
308
|
+
if (!dir) return;
|
|
309
|
+
let manifest, installed;
|
|
310
|
+
try {
|
|
311
|
+
manifest = JSON.parse(await readFile(join3(dir, "package.json"), "utf8"));
|
|
312
|
+
installed = JSON.parse(await readFile(join3(dir, "node_modules/dsh-multi-router/package.json"), "utf8"));
|
|
313
|
+
} catch {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (this.closed || !manifest.dsh?.profile?.bundles?.includes("dsh-multi-router")) return;
|
|
317
|
+
if (installed.name !== "dsh-multi-router" || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(installed.version)) return;
|
|
318
|
+
this.installedVersion = installed.version;
|
|
319
|
+
if (installed.version === this.runningVersion) {
|
|
320
|
+
this.phase = "current";
|
|
321
|
+
this.message = void 0;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const declared = manifest.dependencies?.["dsh-multi-router"];
|
|
325
|
+
const exact = typeof declared === "string" && /^(?:npm:dsh-multi-router@)?(\d+\.\d+\.\d+(?:-[\w.-]+)?)$/.exec(declared)?.[1];
|
|
326
|
+
let installing = false;
|
|
327
|
+
const pid = Number(await readFile(join3(dir, "package.json.lock"), "utf8").catch(() => ""));
|
|
328
|
+
if (Number.isInteger(pid) && pid > 0) try {
|
|
329
|
+
process.kill(pid, 0);
|
|
330
|
+
installing = true;
|
|
331
|
+
} catch (e) {
|
|
332
|
+
installing = e.code !== "ESRCH";
|
|
333
|
+
}
|
|
334
|
+
if (installing || exact && exact !== installed.version) {
|
|
335
|
+
this.phase = "waiting";
|
|
336
|
+
this.message = "\u5B89\u88C5\u4E8B\u52A1\u5C1A\u672A\u5B8C\u6210\uFF0C\u6216\u5B89\u88C5\u5931\u8D25\u5DF2\u56DE\u6EDA\uFF1B\u7B49\u5F85\u914D\u7F6E\u4E0E\u6587\u4EF6\u7248\u672C\u4E00\u81F4\u540E\u518D\u52A0\u8F7D\u3002";
|
|
337
|
+
if (installing) this.schedule(1e3);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (!this.canReload() || !this.service()?.setBundleEnabled) {
|
|
341
|
+
this.phase = "restart-required";
|
|
342
|
+
this.message = "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u8FD0\u884C\u4E2D\u91CD\u8F7D\uFF0C\u8BF7\u5347\u7EA7 DSH \u6216\u91CD\u542F\u5E94\u7528\u3002";
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (this.busy()) {
|
|
346
|
+
this.phase = "waiting";
|
|
347
|
+
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";
|
|
348
|
+
this.schedule(1e3);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const root = join3(dir, "node_modules/dsh-multi-router");
|
|
352
|
+
try {
|
|
353
|
+
const patch = parse(await readFile(join3(root, "cordis.patch.yml"), "utf8"));
|
|
354
|
+
const row = Array.isArray(patch) && patch.flatMap((p) => p?.insert ?? []).find((e) => e?.id === `llm-multi-router-v2-${installed.version}`);
|
|
355
|
+
if (row?.name !== `./lib/host-v${installed.version}.js` || row.disabled) throw Error("legacy entry");
|
|
356
|
+
await readFile(join3(root, `lib/host-v${installed.version}.js`));
|
|
357
|
+
} catch {
|
|
358
|
+
this.phase = "restart-required";
|
|
359
|
+
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";
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (this.now() < this.retryAt) return;
|
|
363
|
+
this.phase = "reloading";
|
|
364
|
+
this.message = "\u6B63\u5728\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u4E0E\u914D\u7F6E\u9762\u677F\u2026";
|
|
365
|
+
this.retryAt = this.now() + 6e4;
|
|
366
|
+
try {
|
|
367
|
+
const result = await outsideActivation(this.hmr(), () => this.service().setBundleEnabled("dsh-multi-router", true));
|
|
368
|
+
if (this.closed) return;
|
|
369
|
+
this.phase = "restart-required";
|
|
370
|
+
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";
|
|
371
|
+
} catch {
|
|
372
|
+
if (!this.closed) {
|
|
373
|
+
this.phase = "failed";
|
|
374
|
+
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";
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
250
380
|
// upstream/workbuddy/src/update.ts
|
|
251
381
|
var WORKBUDDY_REPOSITORY_URL = "https://github.com/corrinehu/dsh-workbuddy-connect";
|
|
252
382
|
var WORKBUDDY_UPDATE_PACKAGE_NAME = "dsh-workbuddy-connect";
|
|
@@ -322,7 +452,7 @@ function githubCommit(value) {
|
|
|
322
452
|
}
|
|
323
453
|
async function jsonFile(file) {
|
|
324
454
|
try {
|
|
325
|
-
return JSON.parse(await
|
|
455
|
+
return JSON.parse(await readFile2(file, "utf8"));
|
|
326
456
|
} catch {
|
|
327
457
|
return void 0;
|
|
328
458
|
}
|
|
@@ -330,11 +460,11 @@ async function jsonFile(file) {
|
|
|
330
460
|
async function locks(dir) {
|
|
331
461
|
let pnpm;
|
|
332
462
|
try {
|
|
333
|
-
pnpm = parseYaml(await
|
|
463
|
+
pnpm = parseYaml(await readFile2(join4(dir, "pnpm-lock.yaml"), "utf8"), { maxAliasCount: 20 });
|
|
334
464
|
} catch {
|
|
335
465
|
}
|
|
336
|
-
const npm = await jsonFile(
|
|
337
|
-
const times = await Promise.all(["pnpm-lock.yaml", "package-lock.json"].map((name) => stat(
|
|
466
|
+
const npm = await jsonFile(join4(dir, "package-lock.json"));
|
|
467
|
+
const times = await Promise.all(["pnpm-lock.yaml", "package-lock.json"].map((name) => stat(join4(dir, name)).then((s) => s.mtimeMs).catch(() => 0)));
|
|
338
468
|
return { pnpm, npm, preferNpm: times[1] > times[0] };
|
|
339
469
|
}
|
|
340
470
|
async function publicJson(url, fetchImpl, signal) {
|
|
@@ -355,11 +485,46 @@ async function publicJson(url, fetchImpl, signal) {
|
|
|
355
485
|
return /^[a-f0-9]{40}$/i.test(text) ? { sha: text } : JSON.parse(text);
|
|
356
486
|
}
|
|
357
487
|
var short = (sha) => sha?.slice(0, 8);
|
|
358
|
-
|
|
488
|
+
var installFailed = (r) => !!(r.error || ["failed", "cancelled"].includes(r.application) || r.packageResult && r.packageResult.exitCode !== 0);
|
|
489
|
+
function installFailure(result) {
|
|
490
|
+
const text = [result.error?.diagnostic, result.packageResult?.output, result.packageResult?.kind].filter((v) => typeof v === "string").join("\n");
|
|
491
|
+
let reason = "unknown", message = "\u5B89\u88C5\u672A\u5B8C\u6210\uFF1B\u8BF7\u5728 DSH \u539F\u751F\u63D2\u4EF6\u9875\u67E5\u770B\u5B89\u88C5\u65E5\u5FD7\uFF0C\u6216\u590D\u5236\u4E0B\u65B9\u8BCA\u65AD\u4FE1\u606F\u3002";
|
|
492
|
+
if (result.application === "cancelled") {
|
|
493
|
+
reason = "cancelled";
|
|
494
|
+
message = "\u5B89\u88C5\u5DF2\u53D6\u6D88\uFF0C\u53EF\u91CD\u65B0\u68C0\u67E5\u540E\u91CD\u8BD5\u3002";
|
|
495
|
+
} else if (/ERR_PNPM_(?:MINIMUM_RELEASE_AGE_VIOLATION|NO_MATURE_MATCHING_VERSION)|minimumReleaseAge/i.test(text)) {
|
|
496
|
+
reason = "release-age";
|
|
497
|
+
message = "\u65B0\u7248\u672C\u5C1A\u5728\u5B89\u88C5\u5668\u7684\u65B0\u5305\u7B49\u5F85\u671F\u5185\u3002\u8F93\u5165\u5305\u540D\u53EF\u80FD\u9009\u4E2D\u65E7\u7248\uFF0C\u6307\u5B9A\u65B0\u7248\u672C\u53EF\u80FD\u88AB\u62D2\u7EDD\uFF1B\u8BF7\u7B49\u5F85\u7B56\u7565\u89C4\u5B9A\u7684\u65F6\u95F4\u540E\u91CD\u8BD5\uFF0C\u6216\u7531\u7528\u6237\u660E\u786E\u4E3A\u8BE5\u7248\u672C\u8BBE\u7F6E\u4F8B\u5916\u3002\u6362\u955C\u50CF\u6216\u91CD\u542F\u4E0D\u4F1A\u89E3\u9664\u6B64\u9650\u5236\u3002";
|
|
498
|
+
} else if (result.pendingBuilds?.length) {
|
|
499
|
+
reason = "build";
|
|
500
|
+
message = "\u4F9D\u8D56\u6784\u5EFA\u811A\u672C\u9700\u8981\u6388\u6743\uFF0C\u8BF7\u5230 DSH \u63D2\u4EF6\u5B89\u88C5\u9875\u5141\u8BB8\u8FD9\u4E9B\u811A\u672C\u540E\u91CD\u8BD5\u3002";
|
|
501
|
+
} else if (result.error?.code === "incompatible-version") {
|
|
502
|
+
reason = "compatibility";
|
|
503
|
+
message = "\u63D2\u4EF6\u4E0E\u5F53\u524D DSH \u4E0D\u517C\u5BB9\uFF0C\u8BF7\u5148\u5347\u7EA7 DSH \u6216\u9009\u62E9\u517C\u5BB9\u7248\u672C\u3002";
|
|
504
|
+
} else if (/\b(?:EPERM|EBUSY|EACCES)\b|resource busy|file.*(?:locked|in use)/i.test(text)) {
|
|
505
|
+
reason = "files";
|
|
506
|
+
message = "\u63D2\u4EF6\u6587\u4EF6\u88AB\u5360\u7528\u6216\u76EE\u5F55\u6743\u9650\u4E0D\u8DB3\uFF0C\u5B89\u88C5\u672A\u5B8C\u6210\u3002\u8BF7\u5148\u5728 DSH \u505C\u7528\u8BE5\u63D2\u4EF6\u540E\u91CD\u8BD5\uFF0C\u5E76\u68C0\u67E5\u5B89\u88C5\u76EE\u5F55\u6743\u9650\uFF1B\u4ECD\u88AB\u5360\u7528\u65F6\u9700\u9000\u51FA\u6258\u76D8\u5E94\u7528\u3002";
|
|
507
|
+
} else if (/HMR transactions cannot be nested|activation|did not activate|entry.*activate/i.test(text)) {
|
|
508
|
+
reason = "activation";
|
|
509
|
+
message = "\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25\u3002\u8BF7\u68C0\u67E5 DSH \u63D2\u4EF6\u9875\u7684\u7EC4\u4EF6\u9519\u8BEF\uFF0C\u518D\u5173\u95ED\u5E76\u91CD\u65B0\u542F\u7528\uFF1B\u5B89\u88C5\u4E0E\u52A0\u8F7D\u662F\u4E24\u4E2A\u72EC\u7ACB\u6B65\u9AA4\u3002";
|
|
510
|
+
} else if (result.packageResult?.timedOut || /\b(?:ETIMEDOUT|ECONNRESET|ENOTFOUND|EAI_AGAIN|ECONNREFUSED)\b|network|fetch|download/i.test(text)) {
|
|
511
|
+
reason = "network";
|
|
512
|
+
message = "\u63D2\u4EF6\u4E0B\u8F7D\u6216\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF0C\u5B89\u88C5\u672A\u5B8C\u6210\u3002npm \u6765\u6E90\u53EF\u5207\u6362\u5230\u300Cnpm \u5B98\u65B9\u300D\u540E\u91CD\u8BD5\uFF1BGitHub \u6765\u6E90\u8BF7\u68C0\u67E5 GitHub \u7F51\u7EDC\u8FDE\u63A5\u3002";
|
|
513
|
+
}
|
|
514
|
+
const code = /\b(?:ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION|ERR_PNPM_NO_MATURE_MATCHING_VERSION|EPERM|EBUSY|EACCES|ETIMEDOUT|ECONNRESET|ENOTFOUND|EAI_AGAIN|ECONNREFUSED)\b/.exec(text)?.[0] ?? (["operation-error", "incompatible-version"].includes(result.error?.code) ? result.error.code : "unknown");
|
|
515
|
+
return { status: "failed", application: result.application, message, diagnostic: {
|
|
516
|
+
reason,
|
|
517
|
+
code,
|
|
518
|
+
platform: process.platform,
|
|
519
|
+
stage: reason === "activation" ? "activation" : "install",
|
|
520
|
+
...Number.isInteger(result.packageResult?.exitCode) ? { exitCode: result.packageResult.exitCode } : {}
|
|
521
|
+
} };
|
|
522
|
+
}
|
|
523
|
+
async function registryVersion(service, profile, spec, signal, requestedRegistry) {
|
|
359
524
|
const require2 = createRequire(profile.installAnchor);
|
|
360
525
|
const { viewProfilePackage } = await import(pathToFileURL(require2.resolve("@deepseek-ai/dsh-plugin-manager/operations")).href);
|
|
361
526
|
const { registryPlan } = await import(pathToFileURL(require2.resolve("@deepseek-ai/dsh-plugin-manager/registry")).href);
|
|
362
|
-
for (const registry of registryPlan(
|
|
527
|
+
for (const registry of registryPlan(requestedRegistry, await service.registries())) {
|
|
363
528
|
const answer = await viewProfilePackage(profile.dir, spec, { ...profile.packageManager, registry, timeoutMs: 12e3, signal });
|
|
364
529
|
if (signal.aborted) throw Error("\u68C0\u67E5\u5DF2\u53D6\u6D88");
|
|
365
530
|
if (answer.exitCode !== 0) continue;
|
|
@@ -374,11 +539,11 @@ async function refreshGitPlugin(service, profile, row, options2, signal) {
|
|
|
374
539
|
const { registryPlan, attributeFailure } = await import(pathToFileURL(require2.resolve("@deepseek-ai/dsh-plugin-manager/registry")).href);
|
|
375
540
|
const { classifyInstallFailure } = await import(pathToFileURL(require2.resolve("@deepseek-ai/dsh-plugin-manager")).href);
|
|
376
541
|
const plan = registryPlan(options2.registry, await service.registries());
|
|
377
|
-
return withFileLock(
|
|
542
|
+
return withFileLock(join4(profile.dir, "package.json"), async () => {
|
|
378
543
|
const files = await Promise.all(["package.json", "pnpm-lock.yaml"].map(async (name) => {
|
|
379
|
-
const path =
|
|
544
|
+
const path = join4(profile.dir, name);
|
|
380
545
|
try {
|
|
381
|
-
return { path, content: await
|
|
546
|
+
return { path, content: await readFile2(path, "utf8") };
|
|
382
547
|
} catch (e) {
|
|
383
548
|
if (e.code !== "ENOENT") throw e;
|
|
384
549
|
return { path };
|
|
@@ -417,7 +582,7 @@ async function refreshGitPlugin(service, profile, row, options2, signal) {
|
|
|
417
582
|
}, { waitMs: 12e4 });
|
|
418
583
|
}
|
|
419
584
|
var PluginUpdates = class {
|
|
420
|
-
constructor({ service = () => void 0, profile = () => void 0, fetchImpl = (...args) => fetch(...args), lookup = registryVersion, refreshGit = refreshGitPlugin, onInstalled, stateFile, now = Date.now } = {}) {
|
|
585
|
+
constructor({ service = () => void 0, profile = () => void 0, hmr = () => void 0, fetchImpl = (...args) => fetch(...args), lookup = registryVersion, refreshGit = refreshGitPlugin, onInstalled, stateFile, now = Date.now } = {}) {
|
|
421
586
|
this.stateFile = stateFile;
|
|
422
587
|
this.now = now;
|
|
423
588
|
this.dismissed = {};
|
|
@@ -425,6 +590,7 @@ var PluginUpdates = class {
|
|
|
425
590
|
this.saveQueue = Promise.resolve();
|
|
426
591
|
this.service = service;
|
|
427
592
|
this.profile = profile;
|
|
593
|
+
this.hmr = hmr;
|
|
428
594
|
this.fetchImpl = fetchImpl;
|
|
429
595
|
this.rows = [];
|
|
430
596
|
this.abort = new AbortController();
|
|
@@ -485,7 +651,7 @@ var PluginUpdates = class {
|
|
|
485
651
|
this.rows = [];
|
|
486
652
|
return;
|
|
487
653
|
}
|
|
488
|
-
const [manifest, bundles, lock] = await Promise.all([jsonFile(
|
|
654
|
+
const [manifest, bundles, lock] = await Promise.all([jsonFile(join4(profile.dir, "package.json")), service.listBundles(), locks(profile.dir)]);
|
|
489
655
|
if (!manifest) throw Error("\u5F53\u524D DSH \u914D\u7F6E\u7684\u63D2\u4EF6\u6E05\u5355\u65E0\u6CD5\u8BFB\u53D6");
|
|
490
656
|
this.rows = bundles.filter((b) => (b.installed || b.version) && manifest.dependencies?.[b.name] && b.removable !== false).map((b) => {
|
|
491
657
|
const spec = manifest.dependencies?.[b.name];
|
|
@@ -578,7 +744,7 @@ var PluginUpdates = class {
|
|
|
578
744
|
});
|
|
579
745
|
}
|
|
580
746
|
async installRow(row, registry) {
|
|
581
|
-
const original = await jsonFile(
|
|
747
|
+
const original = await jsonFile(join4(this.profile().dir, "package.json"));
|
|
582
748
|
if (original?.dependencies?.[row.name] !== row.spec) throw Error("\u5B89\u88C5\u6765\u6E90\u5DF2\u6539\u53D8\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5");
|
|
583
749
|
const current = (await this.service().listBundles()).find((b) => b.name === row.name);
|
|
584
750
|
if (!current || current.enabled !== row.checkedEnabled || current.version !== row.version) throw Error("\u63D2\u4EF6\u7248\u672C\u6216\u542F\u7528\u72B6\u6001\u5DF2\u6539\u53D8\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5");
|
|
@@ -591,17 +757,8 @@ var PluginUpdates = class {
|
|
|
591
757
|
} finally {
|
|
592
758
|
this.gitAbort = void 0;
|
|
593
759
|
}
|
|
594
|
-
} else result = await this.service().installBundle(row.target, options2);
|
|
595
|
-
if (result
|
|
596
|
-
return {
|
|
597
|
-
name: row.name,
|
|
598
|
-
status: "failed",
|
|
599
|
-
application: result.application,
|
|
600
|
-
message: result.pendingBuilds?.length ? "\u4F9D\u8D56\u6784\u5EFA\u811A\u672C\u9700\u8981\u6388\u6743\uFF0C\u8BF7\u5230 DSH \u63D2\u4EF6\u5B89\u88C5\u9875\u5141\u8BB8\u8FD9\u4E9B\u811A\u672C\u540E\u91CD\u8BD5" : result.error?.code === "incompatible-version" ? "\u63D2\u4EF6\u4E0E\u5F53\u524D DSH \u4E0D\u517C\u5BB9\uFF0C\u8BF7\u5148\u5347\u7EA7 DSH \u6216\u9009\u62E9\u517C\u5BB9\u7248\u672C" : `\u5B89\u88C5\u672A\u5B8C\u6210\uFF08${result.error?.code ?? result.packageResult?.kind ?? "unknown"}\uFF09\uFF1B\u8BF7\u5230 DSH \u63D2\u4EF6\u9875\u67E5\u770B\u5B89\u88C5\u65E5\u5FD7`,
|
|
601
|
-
pendingBuilds: result.pendingBuilds ?? [],
|
|
602
|
-
logPath: result.packageResult?.logPath
|
|
603
|
-
};
|
|
604
|
-
}
|
|
760
|
+
} else result = await outsideActivation(this.hmr(), () => this.service().installBundle(row.target, options2));
|
|
761
|
+
if (installFailed(result)) return { name: row.name, ...installFailure(result), expectedVersion: row.latestVersion };
|
|
605
762
|
const installed = (await this.service().listBundles()).find((b) => b.name === row.name);
|
|
606
763
|
if (row.source.kind === "npm" && installed?.version !== row.latestVersion) return {
|
|
607
764
|
name: row.name,
|
|
@@ -654,7 +811,32 @@ var PluginUpdates = class {
|
|
|
654
811
|
this.start("install", async (job) => {
|
|
655
812
|
job.total = 1;
|
|
656
813
|
job.current = spec;
|
|
657
|
-
|
|
814
|
+
let expected;
|
|
815
|
+
if (npm?.kind === "npm" && github.kind !== "github") {
|
|
816
|
+
const found = await this.lookup(this.service(), this.profile(), `${npm.package}@${named[2] || "latest"}`, this.abort.signal, registry);
|
|
817
|
+
if (!parseWorkBuddyVersion(found.version)) throw Error("npm \u672A\u8FD4\u56DE\u6709\u6548\u7248\u672C\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u6E90\u540E\u91CD\u8BD5");
|
|
818
|
+
expected = found.version;
|
|
819
|
+
}
|
|
820
|
+
const result = await outsideActivation(this.hmr(), () => this.service().installBundle(spec, { requestId: job.id, ...registry ? { registry } : {} }));
|
|
821
|
+
if (installFailed(result)) {
|
|
822
|
+
job.results.push({ name: result.bundle ?? spec, ...installFailure(result), expectedVersion: expected });
|
|
823
|
+
delete job.current;
|
|
824
|
+
await this.list();
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
const installed = (await this.service().listBundles()).find((b) => b.name === (result.bundle ?? named?.[1]));
|
|
828
|
+
if (expected && installed?.version !== expected) {
|
|
829
|
+
job.results.push({
|
|
830
|
+
name: result.bundle ?? named[1],
|
|
831
|
+
status: "failed",
|
|
832
|
+
expectedVersion: expected,
|
|
833
|
+
installedVersion: installed?.version,
|
|
834
|
+
message: `\u5B9E\u9645\u5B89\u88C5\u7248\u672C ${installed?.version ?? "\u672A\u77E5"} \u4E0E\u68C0\u67E5\u7248\u672C ${expected} \u4E0D\u540C\uFF1B\u8BF7\u5207\u6362\u300Cnpm \u5B98\u65B9\u300D\u540E\u91CD\u65B0\u68C0\u67E5\u5E76\u5B89\u88C5\u6307\u5B9A\u7248\u672C\u3002`
|
|
835
|
+
});
|
|
836
|
+
delete job.current;
|
|
837
|
+
await this.list();
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
658
840
|
const reloadPending = !result.error && result.packageResult?.exitCode === 0 && result.application === "restart-required" && this.onInstalled?.(result.bundle) === true;
|
|
659
841
|
job.results.push({
|
|
660
842
|
name: result.bundle ?? spec,
|
|
@@ -689,7 +871,7 @@ var PluginUpdates = class {
|
|
|
689
871
|
|
|
690
872
|
// src/workbuddy-login.mjs
|
|
691
873
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
692
|
-
import { readFile as
|
|
874
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
693
875
|
import { withFileLock as withFileLock2 } from "@deepseek-ai/dsh-atomic-write";
|
|
694
876
|
var WorkBuddyLogin = class {
|
|
695
877
|
constructor(file, { fetchImpl = (...args) => fetch(...args), now = Date.now } = {}) {
|
|
@@ -703,7 +885,7 @@ var WorkBuddyLogin = class {
|
|
|
703
885
|
await withFileLock2(this.file, async () => {
|
|
704
886
|
let state = {};
|
|
705
887
|
try {
|
|
706
|
-
state = JSON.parse(await
|
|
888
|
+
state = JSON.parse(await readFile3(this.file, "utf8"));
|
|
707
889
|
} catch (e) {
|
|
708
890
|
if (e.code !== "ENOENT") throw Error("\u767B\u5F55\u51B7\u5374\u8BB0\u5F55\u4E0D\u53EF\u8BFB\uFF0C\u8BF7\u68C0\u67E5\u672C\u673A\u6587\u4EF6");
|
|
709
891
|
}
|
|
@@ -715,7 +897,7 @@ var WorkBuddyLogin = class {
|
|
|
715
897
|
async pause() {
|
|
716
898
|
this.pending = void 0;
|
|
717
899
|
await withFileLock2(this.file, async () => {
|
|
718
|
-
const state = JSON.parse(await
|
|
900
|
+
const state = JSON.parse(await readFile3(this.file, "utf8"));
|
|
719
901
|
state.pausedUntil = this.now() + 15 * 6e4;
|
|
720
902
|
await writePrivateJson(this.file, state);
|
|
721
903
|
});
|
|
@@ -824,6 +1006,7 @@ var WorkBuddyLogin = class {
|
|
|
824
1006
|
|
|
825
1007
|
// src/manager.mjs
|
|
826
1008
|
var MANAGEMENT_PATH = "/.well-known/dsh-multi-router";
|
|
1009
|
+
var SUMMARY_PATH = `${MANAGEMENT_PATH}/summary`;
|
|
827
1010
|
var revisionOf = (raw) => createHash("sha256").update(JSON.stringify(raw)).digest("hex");
|
|
828
1011
|
var ManagementError = class extends Error {
|
|
829
1012
|
constructor(status, message) {
|
|
@@ -865,31 +1048,32 @@ var Manager = class {
|
|
|
865
1048
|
this.file = file;
|
|
866
1049
|
this.router = router;
|
|
867
1050
|
this.onChange = onChange;
|
|
868
|
-
this.catalogFile =
|
|
1051
|
+
this.catalogFile = join5(dirname3(file), "catalogs.json");
|
|
869
1052
|
this.catalogs = /* @__PURE__ */ new Map();
|
|
870
1053
|
this.catalogErrors = /* @__PURE__ */ new Map();
|
|
871
1054
|
this.refreshes = /* @__PURE__ */ new Map();
|
|
872
1055
|
this.closed = false;
|
|
873
1056
|
this.monitorFlights = /* @__PURE__ */ new Map();
|
|
874
1057
|
this.loginInfo = /* @__PURE__ */ new Map();
|
|
875
|
-
this.telemetry = new Telemetry(
|
|
876
|
-
this.plugins = new PluginUpdates({ ...pluginAccess, stateFile:
|
|
1058
|
+
this.telemetry = new Telemetry(join5(dirname3(file), "usage.json"));
|
|
1059
|
+
this.plugins = new PluginUpdates({ ...pluginAccess, stateFile: join5(dirname3(file), "plugin-updates.json"), onInstalled: (name) => {
|
|
877
1060
|
if (name !== "dsh-multi-router" || !this.runtime?.canReload()) return false;
|
|
878
1061
|
this.runtime.schedule();
|
|
879
1062
|
return true;
|
|
880
1063
|
} });
|
|
881
|
-
this.checkins = new Checkins(
|
|
882
|
-
this.workbuddyLogin = new WorkBuddyLogin(
|
|
883
|
-
this.setupFile =
|
|
1064
|
+
this.checkins = new Checkins(join5(dirname3(file), "checkins.json"), this);
|
|
1065
|
+
this.workbuddyLogin = new WorkBuddyLogin(join5(dirname3(file), "workbuddy-login.json"), { fetchImpl: pluginAccess.fetchImpl });
|
|
1066
|
+
this.setupFile = join5(dirname3(file), "initial-setup.json");
|
|
1067
|
+
this.api = new ApiService(join5(dirname3(file), "api-service.json"), this);
|
|
884
1068
|
router.telemetry = this.telemetry;
|
|
885
|
-
router.monitor = (a) => this.monitorAccount(a);
|
|
1069
|
+
router.monitor = (a) => this.monitorAccount(a, { maxAgeMs: 6e4 });
|
|
886
1070
|
router.prepareFree = () => this.checkFreeModels(true);
|
|
887
1071
|
router.freeModels = (account) => this.freeModels(account);
|
|
888
1072
|
}
|
|
889
1073
|
async load() {
|
|
890
|
-
this.raw = JSON.parse(await
|
|
1074
|
+
this.raw = JSON.parse(await readFile4(this.file, "utf8"));
|
|
891
1075
|
try {
|
|
892
|
-
this.setup = JSON.parse(await
|
|
1076
|
+
this.setup = JSON.parse(await readFile4(this.setupFile, "utf8"));
|
|
893
1077
|
} catch {
|
|
894
1078
|
this.setup = void 0;
|
|
895
1079
|
}
|
|
@@ -897,7 +1081,7 @@ var Manager = class {
|
|
|
897
1081
|
await this.checkins.load();
|
|
898
1082
|
await this.plugins.load();
|
|
899
1083
|
try {
|
|
900
|
-
const saved = JSON.parse(await
|
|
1084
|
+
const saved = JSON.parse(await readFile4(this.catalogFile, "utf8"));
|
|
901
1085
|
for (const account of this.config.accounts) {
|
|
902
1086
|
const item = saved[account.id];
|
|
903
1087
|
if (item?.credentialFile === account.credentialFile && Array.isArray(item.models)) {
|
|
@@ -908,6 +1092,7 @@ var Manager = class {
|
|
|
908
1092
|
}
|
|
909
1093
|
this.telemetry.configure(this.config.usageRetentionDays);
|
|
910
1094
|
await this.telemetry.load();
|
|
1095
|
+
await this.api.load();
|
|
911
1096
|
await Promise.all(this.config.accounts.map((a) => this.readLogin(a)));
|
|
912
1097
|
this.publish();
|
|
913
1098
|
}
|
|
@@ -939,12 +1124,12 @@ var Manager = class {
|
|
|
939
1124
|
if (this.closed || !discovered.accounts.length) return;
|
|
940
1125
|
await withFileLock3(this.file, async () => {
|
|
941
1126
|
if (this.closed) return;
|
|
942
|
-
let raw = JSON.parse(await
|
|
1127
|
+
let raw = JSON.parse(await readFile4(this.file, "utf8"));
|
|
943
1128
|
let changed = false;
|
|
944
1129
|
const saved = /* @__PURE__ */ new Map();
|
|
945
1130
|
for (const item of discovered.accounts) {
|
|
946
1131
|
const key = localAccountKey(item.provider, item.document);
|
|
947
|
-
if (!force && raw.ignoredLocalAccounts?.
|
|
1132
|
+
if (!force && raw.ignoredLocalAccounts?.some((k) => normalizeLocalAccountKey(k) === key)) continue;
|
|
948
1133
|
const config = validateConfig(raw, this.file);
|
|
949
1134
|
let existing, current;
|
|
950
1135
|
for (const a of config.accounts.filter((a2) => a2.provider === item.provider)) {
|
|
@@ -976,11 +1161,11 @@ var Manager = class {
|
|
|
976
1161
|
recordSaved();
|
|
977
1162
|
continue;
|
|
978
1163
|
}
|
|
979
|
-
const base =
|
|
1164
|
+
const base = join5(dirname3(this.file), "accounts");
|
|
980
1165
|
await mkdir2(base, { recursive: true, mode: 448 });
|
|
981
|
-
const staging = await mkdtemp2(
|
|
1166
|
+
const staging = await mkdtemp2(join5(base, `${id}-`));
|
|
982
1167
|
try {
|
|
983
|
-
const credentialFile =
|
|
1168
|
+
const credentialFile = join5(staging, "credential.json"), identityFile = join5(staging, "identity.json");
|
|
984
1169
|
await importAccount({ ...item, credentialFile, identityFile });
|
|
985
1170
|
const paths = {
|
|
986
1171
|
credentialFile: relative2(dirname3(this.file), credentialFile),
|
|
@@ -994,7 +1179,7 @@ var Manager = class {
|
|
|
994
1179
|
name: item.document.credential.nickname || item.document.credential.accountName || item.document.credential.uid || item.document.credential.userId,
|
|
995
1180
|
...paths
|
|
996
1181
|
});
|
|
997
|
-
next.ignoredLocalAccounts = (raw.ignoredLocalAccounts ?? []).filter((k) => k !== key);
|
|
1182
|
+
next.ignoredLocalAccounts = (raw.ignoredLocalAccounts ?? []).filter((k) => normalizeLocalAccountKey(k) !== key);
|
|
998
1183
|
validateConfig(next, this.file);
|
|
999
1184
|
await writePrivateJson(this.file, next);
|
|
1000
1185
|
raw = next;
|
|
@@ -1025,7 +1210,7 @@ var Manager = class {
|
|
|
1025
1210
|
if (this.setup?.completedAt) return;
|
|
1026
1211
|
await withFileLock3(this.setupFile, async () => {
|
|
1027
1212
|
try {
|
|
1028
|
-
this.setup = JSON.parse(await
|
|
1213
|
+
this.setup = JSON.parse(await readFile4(this.setupFile, "utf8"));
|
|
1029
1214
|
} catch {
|
|
1030
1215
|
}
|
|
1031
1216
|
if (this.setup?.completedAt) return;
|
|
@@ -1033,8 +1218,12 @@ var Manager = class {
|
|
|
1033
1218
|
await writePrivateJson(this.setupFile, this.setup);
|
|
1034
1219
|
});
|
|
1035
1220
|
}
|
|
1036
|
-
async monitorAccount(account) {
|
|
1221
|
+
async monitorAccount(account, { force = false, maxAgeMs = 3e5 } = {}) {
|
|
1037
1222
|
if (this.monitorFlights.has(account.id)) return this.monitorFlights.get(account.id);
|
|
1223
|
+
const cached = this.telemetry.row("accounts", account.id).balance;
|
|
1224
|
+
const last = cached?.error ? cached.attemptedAt : cached?.fetchedAt;
|
|
1225
|
+
const ttl = cached?.error ? 3e5 : maxAgeMs;
|
|
1226
|
+
if (!force && Number.isFinite(last) && Date.now() >= last && Date.now() - last < ttl) return;
|
|
1038
1227
|
const promise = (async () => {
|
|
1039
1228
|
try {
|
|
1040
1229
|
const client = await this.router.client(account);
|
|
@@ -1053,8 +1242,8 @@ var Manager = class {
|
|
|
1053
1242
|
this.monitorFlights.set(account.id, promise);
|
|
1054
1243
|
return promise;
|
|
1055
1244
|
}
|
|
1056
|
-
async monitorAll(provider) {
|
|
1057
|
-
await Promise.all(this.config.accounts.filter((a) => !provider || a.provider === provider).map((a) => this.monitorAccount(a)));
|
|
1245
|
+
async monitorAll(provider, options2) {
|
|
1246
|
+
await Promise.all(this.config.accounts.filter((a) => (!provider || a.provider === provider) && (options2?.force || a.enabled !== false)).map((a) => this.monitorAccount(a, options2)));
|
|
1058
1247
|
}
|
|
1059
1248
|
freeAccounts() {
|
|
1060
1249
|
const targets = Object.values(this.config.routes).filter((r) => r.freeOnly).flatMap((r) => r.targets);
|
|
@@ -1217,6 +1406,19 @@ var Manager = class {
|
|
|
1217
1406
|
this.refreshes.set(account.id, promise);
|
|
1218
1407
|
return promise;
|
|
1219
1408
|
}
|
|
1409
|
+
summaryState() {
|
|
1410
|
+
const today = new Date(this.telemetry.now()).toLocaleDateString("en-CA", { timeZone: "Asia/Shanghai" });
|
|
1411
|
+
const daily = this.telemetry.data.days[today] ?? {};
|
|
1412
|
+
return {
|
|
1413
|
+
pluginVersion: version,
|
|
1414
|
+
enabled: this.config.sidebarUsage,
|
|
1415
|
+
today,
|
|
1416
|
+
tokens: daily.tokens ?? 0,
|
|
1417
|
+
requests: daily.requests ?? 0,
|
|
1418
|
+
unmetered: daily.unmetered ?? 0,
|
|
1419
|
+
totalTokens: Object.values(this.telemetry.data.accounts).reduce((sum, row) => sum + (row.tokens ?? 0), 0)
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1220
1422
|
state() {
|
|
1221
1423
|
const status = this.router.status();
|
|
1222
1424
|
return {
|
|
@@ -1229,6 +1431,7 @@ var Manager = class {
|
|
|
1229
1431
|
pluginUpdates: this.plugins.state(),
|
|
1230
1432
|
checkins: this.checkins.state(),
|
|
1231
1433
|
telemetry: this.telemetry.state(),
|
|
1434
|
+
apiService: this.api.state(),
|
|
1232
1435
|
accounts: this.config.accounts.map((a) => ({
|
|
1233
1436
|
id: a.id,
|
|
1234
1437
|
provider: a.provider,
|
|
@@ -1256,12 +1459,22 @@ var Manager = class {
|
|
|
1256
1459
|
cooldownMs: this.config.cooldownMs,
|
|
1257
1460
|
requestTimeoutMs: this.config.requestTimeoutMs,
|
|
1258
1461
|
retryServerErrors: this.config.retryServerErrors,
|
|
1259
|
-
usageRetentionDays: this.config.usageRetentionDays
|
|
1462
|
+
usageRetentionDays: this.config.usageRetentionDays,
|
|
1463
|
+
sidebarUsage: this.config.sidebarUsage
|
|
1260
1464
|
}
|
|
1261
1465
|
};
|
|
1262
1466
|
}
|
|
1263
1467
|
async action(input) {
|
|
1264
1468
|
if (!input || typeof input !== "object" || Array.isArray(input)) bad("\u8BF7\u6C42\u683C\u5F0F\u65E0\u6548");
|
|
1469
|
+
if (input.action?.startsWith("api.")) {
|
|
1470
|
+
let result;
|
|
1471
|
+
try {
|
|
1472
|
+
result = await this.api.action(input);
|
|
1473
|
+
} catch (e) {
|
|
1474
|
+
bad(e.message);
|
|
1475
|
+
}
|
|
1476
|
+
return { ...this.state(), ...result };
|
|
1477
|
+
}
|
|
1265
1478
|
if (input.action === "plugin.reload") {
|
|
1266
1479
|
if (!this.runtime) bad("\u5F53\u524D\u540E\u53F0\u4E0D\u652F\u6301\u91CD\u65B0\u52A0\u8F7D");
|
|
1267
1480
|
this.runtime.retryAt = 0;
|
|
@@ -1277,7 +1490,7 @@ var Manager = class {
|
|
|
1277
1490
|
}
|
|
1278
1491
|
}
|
|
1279
1492
|
if (input.action === "workbuddy.login") {
|
|
1280
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1493
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile4(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u63D0\u4EA4\u767B\u5F55");
|
|
1281
1494
|
let login;
|
|
1282
1495
|
try {
|
|
1283
1496
|
login = await this.workbuddyLogin.complete(input.challenge, input.code, input.verify ?? true);
|
|
@@ -1357,7 +1570,7 @@ var Manager = class {
|
|
|
1357
1570
|
return this.state();
|
|
1358
1571
|
}
|
|
1359
1572
|
if (input.action === "account.export") {
|
|
1360
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1573
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile4(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u63D0\u53D6\u51ED\u8BC1");
|
|
1361
1574
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1362
1575
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1363
1576
|
let credential;
|
|
@@ -1379,7 +1592,7 @@ var Manager = class {
|
|
|
1379
1592
|
} };
|
|
1380
1593
|
}
|
|
1381
1594
|
if (input.action === "config.export") {
|
|
1382
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1595
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile4(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u5BFC\u51FA");
|
|
1383
1596
|
try {
|
|
1384
1597
|
return { ...this.state(), exported: { filename: `dsh-multi-router-${input.includeCredentials ? "encrypted" : "settings"}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.json`, document: await exportConfiguration(this, input) } };
|
|
1385
1598
|
} catch (e) {
|
|
@@ -1400,10 +1613,10 @@ var Manager = class {
|
|
|
1400
1613
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1401
1614
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1402
1615
|
this.router.clients.delete(account.id);
|
|
1403
|
-
await this.monitorAccount(account);
|
|
1616
|
+
await this.monitorAccount(account, { force: true });
|
|
1404
1617
|
} else {
|
|
1405
1618
|
await this.syncLocalAccounts(input.provider);
|
|
1406
|
-
await this.monitorAll(input.provider);
|
|
1619
|
+
await this.monitorAll(input.provider, { force: true });
|
|
1407
1620
|
}
|
|
1408
1621
|
return this.state();
|
|
1409
1622
|
}
|
|
@@ -1427,7 +1640,7 @@ var Manager = class {
|
|
|
1427
1640
|
return this.state();
|
|
1428
1641
|
}
|
|
1429
1642
|
return withFileLock3(this.file, async () => {
|
|
1430
|
-
let raw = JSON.parse(await
|
|
1643
|
+
let raw = JSON.parse(await readFile4(this.file, "utf8"));
|
|
1431
1644
|
if (input.revision !== revisionOf(raw)) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u518D\u4FDD\u5B58");
|
|
1432
1645
|
raw.routes ??= structuredClone(this.config.routes);
|
|
1433
1646
|
let staging;
|
|
@@ -1450,10 +1663,10 @@ var Manager = class {
|
|
|
1450
1663
|
}
|
|
1451
1664
|
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(input.id ?? "") || !PROVIDERS.includes(input.provider)) bad("\u8D26\u53F7 ID \u6216\u4F9B\u5E94\u5546\u65E0\u6548");
|
|
1452
1665
|
if (account && !replacing) bad("\u8D26\u53F7 ID \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528\u4E0D\u540C ID");
|
|
1453
|
-
const base =
|
|
1666
|
+
const base = join5(dirname3(this.file), "accounts");
|
|
1454
1667
|
await mkdir2(base, { recursive: true, mode: 448 });
|
|
1455
|
-
staging = await mkdtemp2(
|
|
1456
|
-
const credentialFile =
|
|
1668
|
+
staging = await mkdtemp2(join5(base, `${input.id}-`));
|
|
1669
|
+
const credentialFile = join5(staging, "credential.json"), identityFile = join5(staging, "identity.json");
|
|
1457
1670
|
const added = {
|
|
1458
1671
|
id: input.id,
|
|
1459
1672
|
name: input.name || account?.name || input.id,
|
|
@@ -1604,7 +1817,7 @@ var Manager = class {
|
|
|
1604
1817
|
if (!input.schedule) bad("\u8BF7\u586B\u5199\u7B7E\u5230\u8BA1\u5212");
|
|
1605
1818
|
raw.checkinSchedule = input.schedule;
|
|
1606
1819
|
} else if (input.action === "options.save") {
|
|
1607
|
-
for (const key of ["cooldownMs", "requestTimeoutMs", "retryServerErrors", "usageRetentionDays"]) if (input[key] !== void 0) raw[key] = input[key];
|
|
1820
|
+
for (const key of ["cooldownMs", "requestTimeoutMs", "retryServerErrors", "usageRetentionDays", "sidebarUsage"]) if (input[key] !== void 0) raw[key] = input[key];
|
|
1608
1821
|
} else bad("\u672A\u77E5\u64CD\u4F5C");
|
|
1609
1822
|
let config;
|
|
1610
1823
|
try {
|
|
@@ -1649,12 +1862,12 @@ var Manager = class {
|
|
|
1649
1862
|
}
|
|
1650
1863
|
watch(onError) {
|
|
1651
1864
|
let timer;
|
|
1652
|
-
const watcher =
|
|
1865
|
+
const watcher = watch2(dirname3(this.file), (_, filename) => {
|
|
1653
1866
|
if (filename && String(filename) !== this.file.split(/[\\/]/).pop()) return;
|
|
1654
1867
|
clearTimeout(timer);
|
|
1655
1868
|
timer = setTimeout(() => {
|
|
1656
1869
|
void withFileLock3(this.file, async () => {
|
|
1657
|
-
const raw = JSON.parse(await
|
|
1870
|
+
const raw = JSON.parse(await readFile4(this.file, "utf8"));
|
|
1658
1871
|
if (this.closed || revisionOf(raw) === this.revision) return;
|
|
1659
1872
|
const config = validateConfig(raw, this.file);
|
|
1660
1873
|
this.raw = raw;
|
|
@@ -1671,7 +1884,7 @@ var Manager = class {
|
|
|
1671
1884
|
};
|
|
1672
1885
|
}
|
|
1673
1886
|
};
|
|
1674
|
-
function managementHandler(manager) {
|
|
1887
|
+
function managementHandler(manager, { summaryOnly = false } = {}) {
|
|
1675
1888
|
const key = randomBytes3(32).toString("base64url");
|
|
1676
1889
|
const json = (res, status, value) => {
|
|
1677
1890
|
res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
@@ -1685,9 +1898,10 @@ function managementHandler(manager) {
|
|
|
1685
1898
|
if (req.headers.origin && req.headers.origin !== `http://${req.headers.host}` && req.headers.origin !== `https://${req.headers.host}`) throw new ManagementError(403, "\u6765\u6E90\u65E0\u6548");
|
|
1686
1899
|
if (req.headers["sec-fetch-site"] === "cross-site") throw new ManagementError(403, "\u6765\u6E90\u65E0\u6548");
|
|
1687
1900
|
if (req.method === "GET") {
|
|
1688
|
-
json(res, 200, { ...manager.state(), key });
|
|
1901
|
+
json(res, 200, summaryOnly ? manager.summaryState() : { ...manager.state(), key });
|
|
1689
1902
|
return;
|
|
1690
1903
|
}
|
|
1904
|
+
if (summaryOnly) throw new ManagementError(405, "\u65B9\u6CD5\u65E0\u6548");
|
|
1691
1905
|
if (req.method !== "POST") throw new ManagementError(405, "\u65B9\u6CD5\u65E0\u6548");
|
|
1692
1906
|
const actual = Buffer.from(req.headers["x-multi-router-key"] ?? ""), expected = Buffer.from(key);
|
|
1693
1907
|
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) throw new ManagementError(401, "\u8BF7\u5237\u65B0\u914D\u7F6E\u9875\u9762");
|
|
@@ -1719,10 +1933,12 @@ function managementHandler(manager) {
|
|
|
1719
1933
|
|
|
1720
1934
|
export {
|
|
1721
1935
|
version,
|
|
1936
|
+
PluginReload,
|
|
1722
1937
|
MANAGEMENT_PATH,
|
|
1938
|
+
SUMMARY_PATH,
|
|
1723
1939
|
ManagementError,
|
|
1724
1940
|
modelInfo,
|
|
1725
1941
|
Manager,
|
|
1726
1942
|
managementHandler
|
|
1727
1943
|
};
|
|
1728
|
-
//# sourceMappingURL=chunk-
|
|
1944
|
+
//# sourceMappingURL=chunk-OCMOMSB4.js.map
|