dsh-multi-router 0.5.5 → 0.5.6
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 +7 -0
- package/README.md +12 -2
- package/THIRD_PARTY_NOTICES.md +31 -0
- package/lib/{chunk-EGLF4CWD.js → chunk-U2MU2M5C.js} +10 -3
- package/lib/chunk-U2MU2M5C.js.map +7 -0
- package/lib/{chunk-PQETKUGT.js → chunk-XKDXPB5T.js} +222 -26
- package/lib/chunk-XKDXPB5T.js.map +7 -0
- package/lib/cli.js +1 -1
- package/lib/client.js +62 -7
- package/lib/client.js.map +2 -2
- package/lib/clients.js +5 -3
- package/lib/host.js +3 -2
- package/lib/index.js +3 -2
- package/lib/index.js.map +2 -2
- package/lib/manager.js +2 -2
- package/package.json +1 -1
- package/lib/chunk-EGLF4CWD.js.map +0 -7
- package/lib/chunk-PQETKUGT.js.map +0 -7
|
@@ -2,8 +2,11 @@ import {
|
|
|
2
2
|
Checkins,
|
|
3
3
|
jsonCredential,
|
|
4
4
|
qoderPluginDataDir,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
readLimitedText,
|
|
6
|
+
refreshWorkBuddyCheckinCredential,
|
|
7
|
+
supportsCheckin,
|
|
8
|
+
workbuddyToken
|
|
9
|
+
} from "./chunk-U2MU2M5C.js";
|
|
7
10
|
import {
|
|
8
11
|
discoverLocalAccounts,
|
|
9
12
|
importAccount,
|
|
@@ -33,16 +36,16 @@ import {
|
|
|
33
36
|
} from "./chunk-TEZ2FMEO.js";
|
|
34
37
|
|
|
35
38
|
// src/manager.mjs
|
|
36
|
-
import { createHash, randomBytes as
|
|
39
|
+
import { createHash, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
37
40
|
|
|
38
41
|
// package.json
|
|
39
|
-
var version = "0.5.
|
|
42
|
+
var version = "0.5.6";
|
|
40
43
|
|
|
41
44
|
// src/manager.mjs
|
|
42
|
-
import { readFile as
|
|
45
|
+
import { readFile as readFile3, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
43
46
|
import { watch } from "node:fs";
|
|
44
47
|
import { dirname as dirname3, join as join4, relative as relative2, resolve } from "node:path";
|
|
45
|
-
import { withFileLock as
|
|
48
|
+
import { withFileLock as withFileLock3 } from "@deepseek-ai/dsh-atomic-write";
|
|
46
49
|
|
|
47
50
|
// upstream/qoder/src/status-paths.ts
|
|
48
51
|
var QODER_STATUS_PATH = "/plugins/dsh-qoder-connect/status";
|
|
@@ -680,6 +683,141 @@ var PluginUpdates = class {
|
|
|
680
683
|
}
|
|
681
684
|
};
|
|
682
685
|
|
|
686
|
+
// src/workbuddy-login.mjs
|
|
687
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
688
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
689
|
+
import { withFileLock as withFileLock2 } from "@deepseek-ai/dsh-atomic-write";
|
|
690
|
+
var WorkBuddyLogin = class {
|
|
691
|
+
constructor(file, { fetchImpl = (...args) => fetch(...args), now = Date.now } = {}) {
|
|
692
|
+
this.file = file;
|
|
693
|
+
this.fetchImpl = fetchImpl;
|
|
694
|
+
this.now = now;
|
|
695
|
+
this.abort = new AbortController();
|
|
696
|
+
this.abort.signal.addEventListener("abort", () => this.finish(), { once: true });
|
|
697
|
+
}
|
|
698
|
+
async reserve(field, ms) {
|
|
699
|
+
await withFileLock2(this.file, async () => {
|
|
700
|
+
let state = {};
|
|
701
|
+
try {
|
|
702
|
+
state = JSON.parse(await readFile2(this.file, "utf8"));
|
|
703
|
+
} catch (e) {
|
|
704
|
+
if (e.code !== "ENOENT") throw Error("\u767B\u5F55\u51B7\u5374\u8BB0\u5F55\u4E0D\u53EF\u8BFB\uFF0C\u8BF7\u68C0\u67E5\u672C\u673A\u6587\u4EF6");
|
|
705
|
+
}
|
|
706
|
+
if (state.pausedUntil > this.now() || state[field] > this.now()) throw Error("\u8BF7\u6C42\u51B7\u5374\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5E73\u53F0\u9650\u5236\u8BF7\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u5904\u7406");
|
|
707
|
+
state[field] = this.now() + ms;
|
|
708
|
+
await writePrivateJson(this.file, state);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
async pause() {
|
|
712
|
+
this.pending = void 0;
|
|
713
|
+
await withFileLock2(this.file, async () => {
|
|
714
|
+
const state = JSON.parse(await readFile2(this.file, "utf8"));
|
|
715
|
+
state.pausedUntil = this.now() + 15 * 6e4;
|
|
716
|
+
await writePrivateJson(this.file, state);
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
async post(path, body) {
|
|
720
|
+
let response;
|
|
721
|
+
try {
|
|
722
|
+
response = await this.fetchImpl(`https://www.workbuddy.cn/v2/plugin/login/${path}`, {
|
|
723
|
+
method: "POST",
|
|
724
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", Origin: "https://www.workbuddy.cn", Referer: "https://www.workbuddy.cn/" },
|
|
725
|
+
body: JSON.stringify(body),
|
|
726
|
+
signal: AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]),
|
|
727
|
+
redirect: "error"
|
|
728
|
+
});
|
|
729
|
+
} catch {
|
|
730
|
+
throw Error("\u767B\u5F55\u8BF7\u6C42\u672A\u5B8C\u6210\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8\u91CD\u8BD5\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u91CD\u53D1\u77ED\u4FE1");
|
|
731
|
+
}
|
|
732
|
+
if ([403, 429].includes(response.status)) {
|
|
733
|
+
await this.pause();
|
|
734
|
+
throw Error("\u5E73\u53F0\u9650\u5236\u6216\u9700\u8981\u9A8C\u8BC1\uFF0C\u5DF2\u6682\u505C\u767B\u5F55\u8BF7\u6C42\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF");
|
|
735
|
+
}
|
|
736
|
+
if (!response.ok) throw Error("\u5B98\u65B9\u767B\u5F55\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
737
|
+
let result;
|
|
738
|
+
try {
|
|
739
|
+
result = JSON.parse(await readLimitedText(response, 256 * 1024, "Login"));
|
|
740
|
+
} catch {
|
|
741
|
+
throw Error("\u767B\u5F55\u54CD\u5E94\u65E0\u6CD5\u8BC6\u522B\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF");
|
|
742
|
+
}
|
|
743
|
+
if (result?.code !== 0) {
|
|
744
|
+
if ([403, 429].includes(result?.code) || /频繁|图形验证码|滑块|安全验证|captcha|risk|too many|frequent|验证失败次数/i.test(result?.msg || "")) {
|
|
745
|
+
await this.pause();
|
|
746
|
+
throw Error("\u5E73\u53F0\u9650\u5236\u6216\u9700\u8981\u9A8C\u8BC1\uFF0C\u5DF2\u6682\u505C\u767B\u5F55\u8BF7\u6C42\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF");
|
|
747
|
+
}
|
|
748
|
+
throw Error(path === "token" ? "\u767B\u5F55\u672A\u6210\u529F\uFF0C\u8BF7\u68C0\u67E5\u9A8C\u8BC1\u7801\u662F\u5426\u6B63\u786E\u3001\u8FC7\u671F\uFF1B\u4EC5\u652F\u6301\u5DF2\u6709\u8D26\u53F7" : "\u77ED\u4FE1\u672A\u53D1\u9001\u6210\u529F\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u6709\u8D26\u53F7\u53CA\u624B\u673A\u53F7\uFF0C\u6216\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF");
|
|
749
|
+
}
|
|
750
|
+
return result.data ?? {};
|
|
751
|
+
}
|
|
752
|
+
async send(phone) {
|
|
753
|
+
if (typeof phone !== "string" || !/^1[3-9]\d{9}$/.test(phone)) throw Error("\u8BF7\u8F93\u5165 11 \u4F4D\u4E2D\u56FD\u5927\u9646\u624B\u673A\u53F7");
|
|
754
|
+
if (this.flight) throw Error("\u767B\u5F55\u8BF7\u6C42\u6B63\u5728\u5904\u7406\u4E2D");
|
|
755
|
+
this.flight = (async () => {
|
|
756
|
+
await this.reserve("smsAt", 6e4);
|
|
757
|
+
this.pending = void 0;
|
|
758
|
+
const data = await this.post("send-sms", { phone });
|
|
759
|
+
const expires = Math.min(300, Math.max(1, Number(data.expires_in) || 300)) * 1e3;
|
|
760
|
+
this.pending = { phone, challenge: randomBytes2(24).toString("hex"), expiresAt: this.now() + expires, attempts: 0 };
|
|
761
|
+
clearTimeout(this.expiryTimer);
|
|
762
|
+
this.expiryTimer = setTimeout(() => this.finish(), expires);
|
|
763
|
+
this.expiryTimer.unref();
|
|
764
|
+
return { challenge: this.pending.challenge, expiresAt: this.pending.expiresAt, resendAt: this.now() + 6e4 };
|
|
765
|
+
})();
|
|
766
|
+
try {
|
|
767
|
+
return await this.flight;
|
|
768
|
+
} finally {
|
|
769
|
+
this.flight = void 0;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
async complete(challenge, code, verify = true) {
|
|
773
|
+
if (typeof challenge !== "string" || !this.pending || challenge !== this.pending.challenge || this.pending.expiresAt <= this.now()) throw Error("\u672C\u6B21\u767B\u5F55\u5DF2\u8FC7\u671F\u6216\u63D2\u4EF6\u5DF2\u91CD\u542F\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u9A8C\u8BC1\u7801");
|
|
774
|
+
if (this.flight) throw Error("\u767B\u5F55\u8BF7\u6C42\u6B63\u5728\u5904\u7406\u4E2D");
|
|
775
|
+
if (this.pending.document) return this.pending;
|
|
776
|
+
if (typeof code !== "string" || !/^\d{4,8}$/.test(code) || typeof verify !== "boolean") throw Error("\u8BF7\u8F93\u5165\u6536\u5230\u7684\u77ED\u4FE1\u9A8C\u8BC1\u7801");
|
|
777
|
+
if (this.pending.attempts >= 5) throw Error("\u672C\u6B21\u9A8C\u8BC1\u7801\u5C1D\u8BD5\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u83B7\u53D6");
|
|
778
|
+
this.flight = (async () => {
|
|
779
|
+
await this.reserve("loginAt", 5e3);
|
|
780
|
+
const pending = this.pending;
|
|
781
|
+
pending.attempts++;
|
|
782
|
+
const data = await this.post("token", { login_method: "phone", phone: pending.phone, sms_code: code });
|
|
783
|
+
const accessToken = data.accessToken || data.access_token, refreshToken = data.refreshToken || data.refresh_token;
|
|
784
|
+
let token;
|
|
785
|
+
try {
|
|
786
|
+
token = workbuddyToken(accessToken);
|
|
787
|
+
} catch {
|
|
788
|
+
throw Error("\u767B\u5F55\u8FD4\u56DE\u7684\u4EE4\u724C\u4E0D\u5C5E\u4E8E WorkBuddy \u4E2D\u56FD\u7248\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF");
|
|
789
|
+
}
|
|
790
|
+
if (typeof refreshToken !== "string" || !refreshToken || !Number.isFinite(token.exp) || token.exp * 1e3 <= this.now()) throw Error("\u767B\u5F55\u54CD\u5E94\u7F3A\u5C11\u6709\u6548\u51ED\u8BC1\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
|
|
791
|
+
let credential = { accessToken, refreshToken, uid: token.sub, expiresAtMs: token.exp * 1e3, domain: "www.workbuddy.cn", source: "dsh" };
|
|
792
|
+
if (verify) {
|
|
793
|
+
try {
|
|
794
|
+
const rotated = await refreshWorkBuddyCheckinCredential(credential, { fetchImpl: this.fetchImpl, signal: AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]) });
|
|
795
|
+
credential = {
|
|
796
|
+
...credential,
|
|
797
|
+
accessToken: rotated.accessToken,
|
|
798
|
+
refreshToken: rotated.refreshToken || credential.refreshToken,
|
|
799
|
+
expiresAtMs: Date.now() + rotated.expiresInSec * 1e3
|
|
800
|
+
};
|
|
801
|
+
} catch {
|
|
802
|
+
pending.warning = "\u767B\u5F55\u51ED\u8BC1\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u5237\u65B0\u4EE4\u724C\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF1B\u8BF7\u7A0D\u540E\u5728\u8D26\u53F7\u8BE6\u60C5\u68C0\u67E5\u767B\u5F55\u6709\u6548\u671F\u3002";
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
delete pending.phone;
|
|
806
|
+
pending.document = { version: 1, credential };
|
|
807
|
+
return pending;
|
|
808
|
+
})();
|
|
809
|
+
try {
|
|
810
|
+
return await this.flight;
|
|
811
|
+
} finally {
|
|
812
|
+
this.flight = void 0;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
finish() {
|
|
816
|
+
clearTimeout(this.expiryTimer);
|
|
817
|
+
this.pending = void 0;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
|
|
683
821
|
// src/manager.mjs
|
|
684
822
|
var MANAGEMENT_PATH = "/.well-known/dsh-multi-router";
|
|
685
823
|
var revisionOf = (raw) => createHash("sha256").update(JSON.stringify(raw)).digest("hex");
|
|
@@ -733,6 +871,7 @@ var Manager = class {
|
|
|
733
871
|
this.telemetry = new Telemetry(join4(dirname3(file), "usage.json"));
|
|
734
872
|
this.plugins = new PluginUpdates({ ...pluginAccess, stateFile: join4(dirname3(file), "plugin-updates.json") });
|
|
735
873
|
this.checkins = new Checkins(join4(dirname3(file), "checkins.json"), this);
|
|
874
|
+
this.workbuddyLogin = new WorkBuddyLogin(join4(dirname3(file), "workbuddy-login.json"), { fetchImpl: pluginAccess.fetchImpl });
|
|
736
875
|
this.setupFile = join4(dirname3(file), "initial-setup.json");
|
|
737
876
|
router.telemetry = this.telemetry;
|
|
738
877
|
router.monitor = (a) => this.monitorAccount(a);
|
|
@@ -740,9 +879,9 @@ var Manager = class {
|
|
|
740
879
|
router.freeModels = (account) => this.freeModels(account);
|
|
741
880
|
}
|
|
742
881
|
async load() {
|
|
743
|
-
this.raw = JSON.parse(await
|
|
882
|
+
this.raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
744
883
|
try {
|
|
745
|
-
this.setup = JSON.parse(await
|
|
884
|
+
this.setup = JSON.parse(await readFile3(this.setupFile, "utf8"));
|
|
746
885
|
} catch {
|
|
747
886
|
this.setup = void 0;
|
|
748
887
|
}
|
|
@@ -753,7 +892,7 @@ var Manager = class {
|
|
|
753
892
|
this.plugins.rows = [];
|
|
754
893
|
});
|
|
755
894
|
try {
|
|
756
|
-
const saved = JSON.parse(await
|
|
895
|
+
const saved = JSON.parse(await readFile3(this.catalogFile, "utf8"));
|
|
757
896
|
for (const account of this.config.accounts) {
|
|
758
897
|
const item = saved[account.id];
|
|
759
898
|
if (item?.credentialFile === account.credentialFile && Array.isArray(item.models)) {
|
|
@@ -793,9 +932,9 @@ var Manager = class {
|
|
|
793
932
|
...discovered.status.map((s) => ({ ...s, checkedAt: Date.now() }))
|
|
794
933
|
];
|
|
795
934
|
if (this.closed || !discovered.accounts.length) return;
|
|
796
|
-
await
|
|
935
|
+
await withFileLock3(this.file, async () => {
|
|
797
936
|
if (this.closed) return;
|
|
798
|
-
let raw = JSON.parse(await
|
|
937
|
+
let raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
799
938
|
let changed = false;
|
|
800
939
|
const saved = /* @__PURE__ */ new Map();
|
|
801
940
|
for (const item of discovered.accounts) {
|
|
@@ -879,9 +1018,9 @@ var Manager = class {
|
|
|
879
1018
|
}
|
|
880
1019
|
async finishInitialSetup() {
|
|
881
1020
|
if (this.setup?.completedAt) return;
|
|
882
|
-
await
|
|
1021
|
+
await withFileLock3(this.setupFile, async () => {
|
|
883
1022
|
try {
|
|
884
|
-
this.setup = JSON.parse(await
|
|
1023
|
+
this.setup = JSON.parse(await readFile3(this.setupFile, "utf8"));
|
|
885
1024
|
} catch {
|
|
886
1025
|
}
|
|
887
1026
|
if (this.setup?.completedAt) return;
|
|
@@ -1063,7 +1202,7 @@ var Manager = class {
|
|
|
1063
1202
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id && a.credentialFile === account.credentialFile)) return;
|
|
1064
1203
|
this.catalogs.set(account.id, { credentialFile: account.credentialFile, fetchedAt: Date.now(), pricingVersion: 1, models });
|
|
1065
1204
|
this.catalogErrors.delete(account.id);
|
|
1066
|
-
await
|
|
1205
|
+
await withFileLock3(this.catalogFile, () => writePrivateJson(this.catalogFile, Object.fromEntries(this.catalogs)));
|
|
1067
1206
|
} catch (error) {
|
|
1068
1207
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id)) return;
|
|
1069
1208
|
this.catalogErrors.set(account.id, { status: Number(error.status ?? error.failure?.status) || 502, code: "catalog_fetch_failed", at: Date.now() });
|
|
@@ -1116,6 +1255,63 @@ var Manager = class {
|
|
|
1116
1255
|
}
|
|
1117
1256
|
async action(input) {
|
|
1118
1257
|
if (!input || typeof input !== "object" || Array.isArray(input)) bad("\u8BF7\u6C42\u683C\u5F0F\u65E0\u6548");
|
|
1258
|
+
if (input.action === "workbuddy.sms") {
|
|
1259
|
+
try {
|
|
1260
|
+
const workbuddyLogin = await this.workbuddyLogin.send(input.phone);
|
|
1261
|
+
return { ...this.state(), workbuddyLogin };
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
bad(e.message);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (input.action === "workbuddy.login") {
|
|
1267
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile3(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u63D0\u4EA4\u767B\u5F55");
|
|
1268
|
+
let login;
|
|
1269
|
+
try {
|
|
1270
|
+
login = await this.workbuddyLogin.complete(input.challenge, input.code, input.verify ?? true);
|
|
1271
|
+
} catch (e) {
|
|
1272
|
+
bad(e.message);
|
|
1273
|
+
}
|
|
1274
|
+
let existing;
|
|
1275
|
+
for (const a of this.config.accounts.filter((a2) => a2.provider === "workbuddy")) {
|
|
1276
|
+
try {
|
|
1277
|
+
if ((await jsonCredential(a.credentialFile)).credential.uid === login.document.credential.uid) {
|
|
1278
|
+
existing = a;
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
} catch {
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (input.id && input.id !== existing?.id) bad("\u767B\u5F55\u8EAB\u4EFD\u4E0E\u6B63\u5728\u7F16\u8F91\u7684\u8D26\u53F7\u4E0D\u540C\uFF0C\u8BF7\u4ECE\u6DFB\u52A0\u8D26\u53F7\u5165\u53E3\u4FDD\u5B58");
|
|
1285
|
+
const state = await this.action({
|
|
1286
|
+
action: existing ? "account.credentials" : "account.add",
|
|
1287
|
+
revision: input.revision,
|
|
1288
|
+
id: existing?.id ?? `workbuddy_${randomBytes3(8).toString("hex")}`,
|
|
1289
|
+
provider: "workbuddy",
|
|
1290
|
+
name: input.name,
|
|
1291
|
+
document: login.document
|
|
1292
|
+
});
|
|
1293
|
+
this.workbuddyLogin.finish();
|
|
1294
|
+
return { ...state, loginResult: { warning: login.warning, saved: true } };
|
|
1295
|
+
}
|
|
1296
|
+
if (input.action === "checkins.verify") {
|
|
1297
|
+
const account = this.config.accounts.find((a) => a.id === input.id && a.provider === "workbuddy");
|
|
1298
|
+
if (!account) bad("\u8BF7\u9009\u62E9 WorkBuddy \u4E2D\u56FD\u7248\u8D26\u53F7");
|
|
1299
|
+
if (this.checkins.flight || this.checkins.failed) bad("\u8BF7\u7B49\u5F85\u7B7E\u5230\u5B8C\u6210\uFF0C\u6216\u5148\u4FEE\u590D\u7B7E\u5230\u8BB0\u5F55");
|
|
1300
|
+
try {
|
|
1301
|
+
await this.workbuddyLogin.reserve("verifyAt", 3e4);
|
|
1302
|
+
} catch (e) {
|
|
1303
|
+
bad(e.message);
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
const client = await this.router.client(account);
|
|
1307
|
+
await client.checkin.validate(AbortSignal.timeout(3e4));
|
|
1308
|
+
await this.checkins.credentialsUpdated(account.id);
|
|
1309
|
+
await this.readLogin(account);
|
|
1310
|
+
} catch {
|
|
1311
|
+
bad("\u767B\u5F55\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF1A\u8BF7\u5728\u8D26\u53F7\u4E0E\u767B\u5F55\u4E2D\u66F4\u65B0\u51ED\u8BC1\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u91CD\u8BD5\u6216\u9886\u53D6");
|
|
1312
|
+
}
|
|
1313
|
+
return this.state();
|
|
1314
|
+
}
|
|
1119
1315
|
if (input.action === "checkins.run") {
|
|
1120
1316
|
try {
|
|
1121
1317
|
this.checkins.start(input.ids);
|
|
@@ -1141,14 +1337,14 @@ var Manager = class {
|
|
|
1141
1337
|
return this.state();
|
|
1142
1338
|
}
|
|
1143
1339
|
if (input.action === "setup.dismiss") {
|
|
1144
|
-
await
|
|
1340
|
+
await withFileLock3(this.setupFile, async () => {
|
|
1145
1341
|
this.setup = { ...this.setup, completedAt: this.setup?.completedAt ?? Date.now(), dismissed: true };
|
|
1146
1342
|
await writePrivateJson(this.setupFile, this.setup);
|
|
1147
1343
|
});
|
|
1148
1344
|
return this.state();
|
|
1149
1345
|
}
|
|
1150
1346
|
if (input.action === "account.export") {
|
|
1151
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1347
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile3(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u63D0\u53D6\u51ED\u8BC1");
|
|
1152
1348
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1153
1349
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1154
1350
|
let credential;
|
|
@@ -1170,7 +1366,7 @@ var Manager = class {
|
|
|
1170
1366
|
} };
|
|
1171
1367
|
}
|
|
1172
1368
|
if (input.action === "config.export") {
|
|
1173
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1369
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile3(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u5BFC\u51FA");
|
|
1174
1370
|
try {
|
|
1175
1371
|
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) } };
|
|
1176
1372
|
} catch (e) {
|
|
@@ -1206,7 +1402,7 @@ var Manager = class {
|
|
|
1206
1402
|
}
|
|
1207
1403
|
if (input.action === "account.capture") {
|
|
1208
1404
|
if (!PROVIDERS.includes(input.provider)) bad("\u672A\u77E5\u4F9B\u5E94\u5546");
|
|
1209
|
-
input = { ...input, action: "account.add", id: `${input.provider.replace("-", "_")}_${
|
|
1405
|
+
input = { ...input, action: "account.add", id: `${input.provider.replace("-", "_")}_${randomBytes3(4).toString("hex")}`, capture: true };
|
|
1210
1406
|
}
|
|
1211
1407
|
if (input.action === "refresh") {
|
|
1212
1408
|
if (input.provider && !PROVIDERS.includes(input.provider)) bad("\u672A\u77E5\u4F9B\u5E94\u5546");
|
|
@@ -1217,8 +1413,8 @@ var Manager = class {
|
|
|
1217
1413
|
await this.checkFreeModels(true);
|
|
1218
1414
|
return this.state();
|
|
1219
1415
|
}
|
|
1220
|
-
return
|
|
1221
|
-
let raw = JSON.parse(await
|
|
1416
|
+
return withFileLock3(this.file, async () => {
|
|
1417
|
+
let raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
1222
1418
|
if (input.revision !== revisionOf(raw)) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u518D\u4FDD\u5B58");
|
|
1223
1419
|
raw.routes ??= structuredClone(this.config.routes);
|
|
1224
1420
|
let staging;
|
|
@@ -1228,7 +1424,7 @@ var Manager = class {
|
|
|
1228
1424
|
try {
|
|
1229
1425
|
const prepared = await prepareMigration(this, raw, await decodeConfiguration(input), input.mode);
|
|
1230
1426
|
staging = prepared.staging;
|
|
1231
|
-
await writePrivateJson(`${this.file}.before-import-${Date.now()}-${
|
|
1427
|
+
await writePrivateJson(`${this.file}.before-import-${Date.now()}-${randomBytes3(3).toString("hex")}.bak`, raw);
|
|
1232
1428
|
raw = prepared.raw;
|
|
1233
1429
|
} catch (e) {
|
|
1234
1430
|
bad(e.message);
|
|
@@ -1367,7 +1563,7 @@ var Manager = class {
|
|
|
1367
1563
|
} };
|
|
1368
1564
|
} else if (input.action === "route.save") {
|
|
1369
1565
|
if (input.id === void 0 || input.id === "") {
|
|
1370
|
-
input.id = input.previousId || `route_${
|
|
1566
|
+
input.id = input.previousId || `route_${randomBytes3(8).toString("hex")}`;
|
|
1371
1567
|
}
|
|
1372
1568
|
if (!/^[a-zA-Z0-9_.-]{1,128}$/.test(input.id ?? "")) bad("\u8DEF\u7531 ID \u4EC5\u652F\u6301\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u548C\u77ED\u6A2A\u7EBF");
|
|
1373
1569
|
if (input.previousId && !Object.hasOwn(raw.routes, input.previousId)) bad("\u539F\u8DEF\u7531\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5237\u65B0\u9875\u9762");
|
|
@@ -1444,8 +1640,8 @@ var Manager = class {
|
|
|
1444
1640
|
if (filename && String(filename) !== this.file.split(/[\\/]/).pop()) return;
|
|
1445
1641
|
clearTimeout(timer);
|
|
1446
1642
|
timer = setTimeout(() => {
|
|
1447
|
-
void
|
|
1448
|
-
const raw = JSON.parse(await
|
|
1643
|
+
void withFileLock3(this.file, async () => {
|
|
1644
|
+
const raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
1449
1645
|
if (this.closed || revisionOf(raw) === this.revision) return;
|
|
1450
1646
|
const config = validateConfig(raw, this.file);
|
|
1451
1647
|
this.raw = raw;
|
|
@@ -1463,7 +1659,7 @@ var Manager = class {
|
|
|
1463
1659
|
}
|
|
1464
1660
|
};
|
|
1465
1661
|
function managementHandler(manager) {
|
|
1466
|
-
const key =
|
|
1662
|
+
const key = randomBytes3(32).toString("base64url");
|
|
1467
1663
|
const json = (res, status, value) => {
|
|
1468
1664
|
res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
1469
1665
|
res.end(JSON.stringify(value));
|
|
@@ -1512,4 +1708,4 @@ export {
|
|
|
1512
1708
|
Manager,
|
|
1513
1709
|
managementHandler
|
|
1514
1710
|
};
|
|
1515
|
-
//# sourceMappingURL=chunk-
|
|
1711
|
+
//# sourceMappingURL=chunk-XKDXPB5T.js.map
|