dsh-multi-router 0.5.1 → 0.5.3
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 +11 -0
- package/README.md +10 -8
- package/lib/accounts.js +2 -2
- package/lib/{chunk-OMG4FZWV.js → chunk-4H6ZXK75.js} +2 -2
- package/lib/{chunk-C6JBDATB.js → chunk-EJWYFWIT.js} +31 -191
- package/lib/{chunk-C6JBDATB.js.map → chunk-EJWYFWIT.js.map} +4 -4
- package/lib/{chunk-U2YTBWLW.js → chunk-JYVJJ7OX.js} +3 -3
- package/lib/{chunk-YUP77Q3A.js → chunk-RPW6DI6O.js} +410 -56
- package/lib/chunk-RPW6DI6O.js.map +7 -0
- package/lib/{chunk-GAQ77CH4.js → chunk-U7J4ABAM.js} +2 -2
- package/lib/chunk-U7J4ABAM.js.map +7 -0
- package/lib/{chunk-XV7O2LRF.js → chunk-XA3V2DGT.js} +2 -2
- package/lib/cli.js +4 -4
- package/lib/client.js +23 -10
- package/lib/client.js.map +2 -2
- package/lib/clients.js +11 -5
- package/lib/host.js +6 -6
- package/lib/index.js +6 -6
- package/lib/manager.js +5 -5
- package/lib/router.js +2 -2
- package/lib/shim.js +3 -3
- package/package.json +1 -1
- package/lib/chunk-GAQ77CH4.js.map +0 -7
- package/lib/chunk-YUP77Q3A.js.map +0 -7
- /package/lib/{chunk-OMG4FZWV.js.map → chunk-4H6ZXK75.js.map} +0 -0
- /package/lib/{chunk-U2YTBWLW.js.map → chunk-JYVJJ7OX.js.map} +0 -0
- /package/lib/{chunk-XV7O2LRF.js.map → chunk-XA3V2DGT.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。版本号从 `0.1` 起以功能迭代为主,接口在 `1.0` 前可能变化。
|
|
4
4
|
|
|
5
|
+
## 0.5.3
|
|
6
|
+
|
|
7
|
+
- 签到面板的手机号打码兼容国际区号及以 0 开头的号码,账号名称和自动签到控件标签都隐藏中间数字。
|
|
8
|
+
|
|
9
|
+
## 0.5.2
|
|
10
|
+
|
|
11
|
+
- Qoder 签到复用 PAT → Job Token 认证及桌面活动请求头,仅领取有效期内的 `CLAIM_BENEFIT / CLAIMABLE` 活动;宣传和锁定活动不领取,「无活动」不误报认证失败,奖励不使用猜测值。
|
|
12
|
+
- 北京时间 10:05 首次检查,20:05 只补查尚未提交领取的失败,最多补查一次;已完成、无活动、提交后结果不明的账号跳过。按账号身份持久化,支持重启、账号调整顺序和重复身份去重。
|
|
13
|
+
- 单独展示令牌失效、平台限制、查询失败与结果待核对;显式更新凭证仅清除认证失败记录,不清除领取意图或设备限制。TRAE 使用原客户端凭证续期,查询失败不再一律误报令牌失效。
|
|
14
|
+
- 签到面板增加最近一轮汇总和补查时间,全部跳过时不重复汇报,手机号打码。接入 WorkBuddy 国内版签到与 AT / RT 续期,查询已签或活动结束时跳过,面板显示连签天数、活动累计积分与本次奖励;只读状态查询可有限重试,领取结果不明不重复提交。
|
|
15
|
+
|
|
5
16
|
## 0.5.1
|
|
6
17
|
|
|
7
18
|
- GitHub 插件更新检查只请求提交 SHA,不再下载完整提交差异,修复构建产物较大时「更新元数据过大」导致无法检查的问题。
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
一个插件内置 WorkBuddy、Trae、Qoder 的认证、模型发现和聊天传输,提供三家的多账号池及集中路由。运行不依赖原来三个 DSH 插件;三家代码随安装包一起打包。基于 MIT 上游,固定版本见 `upstream-lock.json`,许可见 `THIRD_PARTY_NOTICES.md`。
|
|
8
8
|
|
|
9
|
-
版本:0.5.
|
|
9
|
+
版本:0.5.3。适配 DSH Desktop / Web 0.2.0-rc.2,Node.js ^22.19.0 或 >=24。
|
|
10
10
|
|
|
11
11
|
## 在 DSH 里管理
|
|
12
12
|
|
|
@@ -95,13 +95,15 @@ Qoder 的 `dfmodel` 在真实目录里显示为 DeepSeek-Flash,未声明具体
|
|
|
95
95
|
|
|
96
96
|
## 每日积分签到
|
|
97
97
|
|
|
98
|
-
在配置面板打开 **积分签到**。当前接入 Trae CN 与 Qoder CN / Global
|
|
98
|
+
在配置面板打开 **积分签到**。当前接入 WorkBuddy 国内版、Trae CN 与 Qoder CN / Global 的签到能力:先查询平台是否开放活动、账号与设备是否已领取,再提交领取;活动是否开放和奖励额度以平台响应为准。WorkBuddy AI 与 Trae Global 暂无已核验的签到接口,面板会显示需到官方客户端查看,不猜测接口或奖励。
|
|
99
99
|
|
|
100
|
-
- 每个账号可选择「立即签到」,或「签到全部可用账号」。**每日自动签到默认关闭**,按账号单独开启;已停用账号不参与。每天北京时间 10:05
|
|
101
|
-
-
|
|
100
|
+
- 每个账号可选择「立即签到」,或「签到全部可用账号」。**每日自动签到默认关闭**,按账号单独开启;已停用账号不参与。每天北京时间 10:05 后首次检查,20:05 后仅补查确认未提交领取的查询失败或尚未开放窗口,每个账号最多补查一次。DSH 关闭期间不执行,下次启动按当前时间补查;10:05 前不会发起签到请求。
|
|
101
|
+
- Qoder 先用 PAT 交换临时 Job Token,活动请求使用 `Cosy-ClientType: 10`;仅领取有效期内 `CLAIM_BENEFIT` 且 `CLAIMABLE` 的活动。宣传、锁定或未开放给此账号的活动显示「无活动」,不会误报令牌失效;奖励仅显示领取响应实际返回的金额。TRAE 沿用客户端登录的 Refresh Token 和真实设备参数续期,不依赖短信登录,也不按账号数组位置保存状态。
|
|
102
|
+
- WorkBuddy 国内版沿用已保存的客户端 AT / RT,按 JWT 中的账号身份校验请求;续期后保存新 AT 和轮换的 RT。先查今日签到状态及活动结束时间,再提交一次领取,已签或活动结束时跳过;面板展示返回的连签天数、活动累计积分和本次奖励。网络或 5xx 仅对领取前的只读状态查询最多重试两次(2 秒、4 秒),领取请求不做快速重试。接口契约参考 [WorkBuddy-Daily](https://github.com/L0NE-6/WorkBuddy-Daily/blob/main/workbuddy_daily.py),未采用成长任务、抽奖或客户端活跃模拟。
|
|
103
|
+
- 请求串行,账号间至少间隔 10 秒。先持久化当天意图再发请求,重复账号副本按平台身份去重,调整账号顺序不影响记录。每天最多提交一次领取;提交后网络超时或结果不明确时不重复提交,请到官方客户端核对。成功、今日已领、无活动和结果不明的账号不会加入晚间重试队列。
|
|
102
104
|
- 保留 Trae 原有设备身份;同一设备当天的领取已使用或结果不明确时,不切到同设备的其他账号继续领取。Qoder 优先复用官方 CLI 的设备身份,没有时使用连接器持久化的单一设备身份,所有账号一致,不随签到轮换。
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
+
- 验证、403 拒绝或限流会暂停该平台签到;在官方客户端处理后,可以点击「恢复后续签到」。恢复不会清除今日尝试记录与设备限制。401 或明确过期且无法续期的凭证单独显示「令牌失效」,请在「账号与登录」更新凭证;替换凭证只解除认证失败记录,已提交或结果不明的领取记录仍保留。普通网络查询失败不会要求重交令牌。不提供更换设备身份、代理或绕过验证码的能力;不能保证不触发平台风控。
|
|
106
|
+
- 面板显示最近一轮汇总、账号结果、补查时间、可核验奖励与自动签到开关;全部已完成或没有待补查账号时不重复汇报。签到页的手机号打码,不输出令牌或上游原始错误;成功后刷新平台余额。记录保存在配置旁的 `checkins.json`,0600 权限,仅保存身份哈希、日期和结果。保留最近 30 天,记录损坏时停止签到,不自动清空后重试。
|
|
105
107
|
- 自动签到设置可随配置导入导出,领取记录与平台暂停状态不迁移。多台设备的本地记录不会互相同步,建议只在一台设备开启自动签到;平台领取状态仍会在提交前查询。
|
|
106
108
|
|
|
107
109
|
## 所有已安装插件的更新与安装
|
|
@@ -135,7 +137,7 @@ dsh plugin --profile desktop add dsh-multi-router
|
|
|
135
137
|
dsh plugin --profile web add dsh-multi-router
|
|
136
138
|
```
|
|
137
139
|
|
|
138
|
-
包地址:[dsh-multi-router · npm](https://www.npmjs.com/package/dsh-multi-router)。正式版本由 GitHub Release 自动测试、打包并发布到 npm `latest`,包中已包含编译产物,用户无需自行构建。需要固定版本时可安装 `dsh-multi-router@0.5.
|
|
140
|
+
包地址:[dsh-multi-router · npm](https://www.npmjs.com/package/dsh-multi-router)。正式版本由 GitHub Release 自动测试、打包并发布到 npm `latest`,包中已包含编译产物,用户无需自行构建。需要固定版本时可安装 `dsh-multi-router@0.5.3`。
|
|
139
141
|
|
|
140
142
|
已使用 npm 来源的用户更新:
|
|
141
143
|
|
|
@@ -192,7 +194,7 @@ cd dsh-multi-router
|
|
|
192
194
|
npm ci
|
|
193
195
|
npm run check
|
|
194
196
|
npm run pack:release
|
|
195
|
-
dsh plugin --profile desktop add ./dsh-multi-router-0.5.
|
|
197
|
+
dsh plugin --profile desktop add ./dsh-multi-router-0.5.3.tgz
|
|
196
198
|
```
|
|
197
199
|
|
|
198
200
|
### 配置账号
|
package/lib/accounts.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
importAccount,
|
|
4
4
|
localAccountKey,
|
|
5
5
|
readPortableCredential
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4H6ZXK75.js";
|
|
7
7
|
import "./chunk-FTM7RHQV.js";
|
|
8
8
|
import "./chunk-S5KMFAQM.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-U7J4ABAM.js";
|
|
10
10
|
import "./chunk-TEZ2FMEO.js";
|
|
11
11
|
export {
|
|
12
12
|
discoverLocalAccounts,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
PROVIDERS,
|
|
14
14
|
writePrivateJson
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-U7J4ABAM.js";
|
|
16
16
|
|
|
17
17
|
// src/accounts.mjs
|
|
18
18
|
import { readFile } from "node:fs/promises";
|
|
@@ -161,4 +161,4 @@ export {
|
|
|
161
161
|
readPortableCredential,
|
|
162
162
|
importAccount
|
|
163
163
|
};
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-4H6ZXK75.js.map
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Checkins,
|
|
2
3
|
jsonCredential,
|
|
3
|
-
qoderPluginDataDir
|
|
4
|
-
|
|
4
|
+
qoderPluginDataDir,
|
|
5
|
+
supportsCheckin
|
|
6
|
+
} from "./chunk-RPW6DI6O.js";
|
|
5
7
|
import {
|
|
6
8
|
discoverLocalAccounts,
|
|
7
9
|
importAccount,
|
|
8
10
|
localAccountKey,
|
|
9
11
|
readPortableCredential
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4H6ZXK75.js";
|
|
11
13
|
import {
|
|
12
14
|
hostIsLoopback
|
|
13
15
|
} from "./chunk-DNQPZMPF.js";
|
|
14
16
|
import {
|
|
15
17
|
Telemetry,
|
|
16
18
|
matchModel
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-XA3V2DGT.js";
|
|
18
20
|
import {
|
|
19
21
|
PROVIDERS,
|
|
20
22
|
PROVIDER_NAMES,
|
|
@@ -25,22 +27,22 @@ import {
|
|
|
25
27
|
routeReasoning,
|
|
26
28
|
validateConfig,
|
|
27
29
|
writePrivateJson
|
|
28
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-U7J4ABAM.js";
|
|
29
31
|
import {
|
|
30
32
|
modelWithCurrentPromotion
|
|
31
33
|
} from "./chunk-TEZ2FMEO.js";
|
|
32
34
|
|
|
33
35
|
// src/manager.mjs
|
|
34
|
-
import { createHash
|
|
36
|
+
import { createHash, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
35
37
|
|
|
36
38
|
// package.json
|
|
37
|
-
var version = "0.5.
|
|
39
|
+
var version = "0.5.3";
|
|
38
40
|
|
|
39
41
|
// src/manager.mjs
|
|
40
|
-
import { readFile as
|
|
42
|
+
import { readFile as readFile2, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
41
43
|
import { watch } from "node:fs";
|
|
42
44
|
import { dirname as dirname3, join as join4, relative as relative2, resolve } from "node:path";
|
|
43
|
-
import { withFileLock as
|
|
45
|
+
import { withFileLock as withFileLock2 } from "@deepseek-ai/dsh-atomic-write";
|
|
44
46
|
|
|
45
47
|
// upstream/qoder/src/status-paths.ts
|
|
46
48
|
var QODER_STATUS_PATH = "/plugins/dsh-qoder-connect/status";
|
|
@@ -629,172 +631,9 @@ var PluginUpdates = class {
|
|
|
629
631
|
}
|
|
630
632
|
};
|
|
631
633
|
|
|
632
|
-
// src/checkins.mjs
|
|
633
|
-
import { createHash } from "node:crypto";
|
|
634
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
635
|
-
import { setTimeout as delay } from "node:timers/promises";
|
|
636
|
-
import { withFileLock as withFileLock2 } from "@deepseek-ai/dsh-atomic-write";
|
|
637
|
-
var supportsCheckin = (provider) => ["trae", "qoder", "qoder-global"].includes(provider);
|
|
638
|
-
var hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
639
|
-
var nextDay = (now) => Date.parse(`${beijingDay(now)}T00:00:00+08:00`) + 864e5;
|
|
640
|
-
var checkinMessages = {
|
|
641
|
-
claimed: "\u7B7E\u5230\u6210\u529F",
|
|
642
|
-
"already-claimed": "\u4ECA\u65E5\u5DF2\u9886\u53D6",
|
|
643
|
-
"device-used": "\u6B64\u8BBE\u5907\u4ECA\u65E5\u5DF2\u53C2\u4E0E\u7B7E\u5230\uFF0C\u4E0D\u518D\u5207\u6362\u5176\u4ED6\u8D26\u53F7\u9886\u53D6",
|
|
644
|
-
"no-campaign": "\u5E73\u53F0\u672A\u5F00\u653E\u53EF\u9886\u53D6\u6D3B\u52A8",
|
|
645
|
-
"before-window": "\u7B7E\u5230\u7A97\u53E3\u5C1A\u672A\u5F00\u542F",
|
|
646
|
-
paused: "\u5E73\u53F0\u9650\u5236\u6216\u9700\u8981\u9A8C\u8BC1\uFF0C\u5DF2\u6682\u505C\u6B64\u5E73\u53F0\u7B7E\u5230\uFF0C\u8BF7\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u5904\u7406",
|
|
647
|
-
uncertain: "\u8BF7\u6C42\u7ED3\u679C\u5F85\u786E\u8BA4\uFF0C\u4ECA\u65E5\u4E0D\u518D\u81EA\u52A8\u63D0\u4EA4\uFF0C\u8BF7\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u6838\u5BF9",
|
|
648
|
-
error: "\u7B7E\u5230\u672A\u5B8C\u6210\uFF0C\u4ECA\u65E5\u4E0D\u518D\u91CD\u590D\u5C1D\u8BD5\uFF0C\u8BF7\u5728\u5B98\u65B9\u5BA2\u6237\u7AEF\u67E5\u770B",
|
|
649
|
-
unsupported: "\u6682\u672A\u63A5\u5165\u53EF\u6838\u9A8C\u7684\u7B7E\u5230\u63A5\u53E3\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF"
|
|
650
|
-
};
|
|
651
|
-
var Checkins = class {
|
|
652
|
-
constructor(file, manager, { now = Date.now, spacingMs = 1e4 } = {}) {
|
|
653
|
-
this.file = file;
|
|
654
|
-
this.manager = manager;
|
|
655
|
-
this.now = now;
|
|
656
|
-
this.spacingMs = spacingMs;
|
|
657
|
-
this.data = { records: {}, devices: {}, pauses: {} };
|
|
658
|
-
this.abort = new AbortController();
|
|
659
|
-
}
|
|
660
|
-
async read() {
|
|
661
|
-
try {
|
|
662
|
-
const raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
663
|
-
const object = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
664
|
-
if (!object(raw) || !["records", "devices", "pauses"].every((k) => object(raw[k])) || Object.values(raw.records).some((r) => !object(r) || !Array.isArray(r.ids) || !Number.isFinite(r.at) || typeof r.day !== "string")) throw Error("Invalid check-in journal");
|
|
665
|
-
this.data = raw;
|
|
666
|
-
} catch (e) {
|
|
667
|
-
if (e.code !== "ENOENT") throw e;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
async load() {
|
|
671
|
-
try {
|
|
672
|
-
await this.read();
|
|
673
|
-
} catch {
|
|
674
|
-
this.failed = true;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
state() {
|
|
678
|
-
return {
|
|
679
|
-
running: !!this.flight,
|
|
680
|
-
current: this.current,
|
|
681
|
-
error: this.failed ? "\u7B7E\u5230\u8BB0\u5F55\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u5DF2\u505C\u6B62\u7B7E\u5230\uFF1B\u8BF7\u4FEE\u590D\u8BB0\u5F55\u6587\u4EF6\u540E\u91CD\u542F\u63D2\u4EF6\u3002" : void 0,
|
|
682
|
-
schedule: "\u5317\u4EAC\u65F6\u95F4\u6BCF\u5929 10:05 \u540E\uFF0CDSH \u8FD0\u884C\u65F6\u6267\u884C\uFF1B\u9519\u8FC7\u540E\u4E0B\u6B21\u542F\u52A8\u8865\u67E5",
|
|
683
|
-
accounts: this.manager.config.accounts.map((a) => {
|
|
684
|
-
const rows = Object.values(this.data.records).filter((r) => r.ids?.includes(a.id));
|
|
685
|
-
const last = rows.sort((a2, b) => b.at - a2.at)[0];
|
|
686
|
-
const pause = this.data.pauses[a.provider];
|
|
687
|
-
return {
|
|
688
|
-
id: a.id,
|
|
689
|
-
supported: supportsCheckin(a.provider),
|
|
690
|
-
auto: a.autoCheckin === true,
|
|
691
|
-
paused: !!pause,
|
|
692
|
-
last: last ? {
|
|
693
|
-
day: last.day,
|
|
694
|
-
at: last.at,
|
|
695
|
-
status: last.status,
|
|
696
|
-
amount: last.amount,
|
|
697
|
-
message: checkinMessages[last.status] || checkinMessages.error
|
|
698
|
-
} : void 0
|
|
699
|
-
};
|
|
700
|
-
})
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
start(ids, automatic = false) {
|
|
704
|
-
if (this.failed || this.abort.signal.aborted || this.flight) return;
|
|
705
|
-
if (!automatic && (!Array.isArray(ids) || !ids.length || ids.length > 200 || ids.some((id) => !this.manager.config.accounts.some((a) => a.id === id)))) throw Error("\u8BF7\u9009\u62E9\u6709\u6548\u7684\u7B7E\u5230\u8D26\u53F7");
|
|
706
|
-
const selected = this.manager.config.accounts.filter((a) => supportsCheckin(a.provider) && a.enabled !== false && (automatic ? a.autoCheckin === true : ids.includes(a.id))).map((a) => a.id);
|
|
707
|
-
if (!selected.length) return;
|
|
708
|
-
this.flight = (async () => {
|
|
709
|
-
let requested = false;
|
|
710
|
-
for (const id of selected) {
|
|
711
|
-
if (this.abort.signal.aborted) break;
|
|
712
|
-
const account = this.manager.config.accounts.find((a) => a.id === id && a.enabled !== false && (!automatic || a.autoCheckin === true));
|
|
713
|
-
if (!account) continue;
|
|
714
|
-
if (requested) {
|
|
715
|
-
await delay(this.spacingMs, void 0, { signal: this.abort.signal });
|
|
716
|
-
requested = false;
|
|
717
|
-
}
|
|
718
|
-
this.current = id;
|
|
719
|
-
requested = await this.run(account, automatic);
|
|
720
|
-
}
|
|
721
|
-
})().catch(() => {
|
|
722
|
-
if (!this.abort.signal.aborted) this.failed = true;
|
|
723
|
-
}).finally(() => {
|
|
724
|
-
this.flight = void 0;
|
|
725
|
-
this.current = void 0;
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
async resume(provider) {
|
|
729
|
-
if (!supportsCheckin(provider) || this.failed || this.flight) throw Error("\u8BF7\u7B49\u5F85\u7B7E\u5230\u5B8C\u6210\uFF0C\u6216\u5148\u4FEE\u590D\u7B7E\u5230\u8BB0\u5F55");
|
|
730
|
-
await withFileLock2(this.file, async () => {
|
|
731
|
-
await this.read();
|
|
732
|
-
delete this.data.pauses[provider];
|
|
733
|
-
await writePrivateJson(this.file, this.data);
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
async run(account, automatic = false) {
|
|
737
|
-
const now = this.now(), day = beijingDay(now);
|
|
738
|
-
if (now < Date.parse(`${day}T10:05:00+08:00`)) return false;
|
|
739
|
-
return withFileLock2(this.file, async () => {
|
|
740
|
-
await this.read();
|
|
741
|
-
if (this.abort.signal.aborted || this.data.pauses[account.provider]) return false;
|
|
742
|
-
if (Object.values(this.data.records).some((r) => r.day === day && r.ids?.includes(account.id))) return false;
|
|
743
|
-
if (this.data.lastRequestAt && now - this.data.lastRequestAt < this.spacingMs) return false;
|
|
744
|
-
this.data.lastRequestAt = now;
|
|
745
|
-
await writePrivateJson(this.file, this.data);
|
|
746
|
-
const client = await this.manager.router.client(account);
|
|
747
|
-
if (!client.checkin) return false;
|
|
748
|
-
let identity;
|
|
749
|
-
try {
|
|
750
|
-
identity = await client.checkin.identity(AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]));
|
|
751
|
-
} catch {
|
|
752
|
-
this.data.pauses[account.provider] = { at: now };
|
|
753
|
-
await writePrivateJson(this.file, this.data);
|
|
754
|
-
return true;
|
|
755
|
-
}
|
|
756
|
-
if (!identity?.account || account.provider === "trae" && !identity.device) throw Error("Missing check-in identity");
|
|
757
|
-
if (this.abort.signal.aborted || !this.manager.config.accounts.some((a) => a.id === account.id && a.enabled !== false && a.credentialFile === account.credentialFile && (!automatic || a.autoCheckin === true))) return true;
|
|
758
|
-
const key = hash(`${account.provider}:${identity.account}`), device = identity.device ? hash(`${account.provider}:${identity.device}`) : void 0;
|
|
759
|
-
const old = this.data.records[key];
|
|
760
|
-
if (old?.day === day) {
|
|
761
|
-
if (!old.ids.includes(account.id)) {
|
|
762
|
-
old.ids.push(account.id);
|
|
763
|
-
await writePrivateJson(this.file, this.data);
|
|
764
|
-
}
|
|
765
|
-
return true;
|
|
766
|
-
}
|
|
767
|
-
const record = { day, at: now, status: "uncertain", ids: [account.id] };
|
|
768
|
-
this.data.records[key] = record;
|
|
769
|
-
for (const [k, row] of Object.entries(this.data.records)) if (row.at < now - 30 * 864e5) delete this.data.records[k];
|
|
770
|
-
for (const [k, row] of Object.entries(this.data.devices)) if (row.until <= now) delete this.data.devices[k];
|
|
771
|
-
if (device && this.data.devices[device]?.until > now) {
|
|
772
|
-
record.status = "device-used";
|
|
773
|
-
await writePrivateJson(this.file, this.data);
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
if (device) this.data.devices[device] = { until: nextDay(now) };
|
|
777
|
-
await writePrivateJson(this.file, this.data);
|
|
778
|
-
let result;
|
|
779
|
-
try {
|
|
780
|
-
result = await client.checkin.claim(AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]));
|
|
781
|
-
} catch {
|
|
782
|
-
result = { status: "uncertain" };
|
|
783
|
-
}
|
|
784
|
-
record.status = Object.hasOwn(checkinMessages, result?.status) ? result.status : "uncertain";
|
|
785
|
-
if (device && ["already-claimed", "no-campaign"].includes(record.status)) delete this.data.devices[device];
|
|
786
|
-
if (["claimed", "already-claimed"].includes(record.status) && Number.isFinite(result.amount) && result.amount >= 0) record.amount = result.amount;
|
|
787
|
-
if (record.status === "paused") this.data.pauses[account.provider] = { at: now };
|
|
788
|
-
await writePrivateJson(this.file, this.data);
|
|
789
|
-
if (record.status === "claimed") await this.manager.monitorAccount(account);
|
|
790
|
-
return true;
|
|
791
|
-
}, { waitMs: 1e3 });
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
|
|
795
634
|
// src/manager.mjs
|
|
796
635
|
var MANAGEMENT_PATH = "/.well-known/dsh-multi-router";
|
|
797
|
-
var revisionOf = (raw) =>
|
|
636
|
+
var revisionOf = (raw) => createHash("sha256").update(JSON.stringify(raw)).digest("hex");
|
|
798
637
|
var ManagementError = class extends Error {
|
|
799
638
|
constructor(status, message) {
|
|
800
639
|
super(message);
|
|
@@ -852,16 +691,16 @@ var Manager = class {
|
|
|
852
691
|
router.freeModels = (account) => this.freeModels(account);
|
|
853
692
|
}
|
|
854
693
|
async load() {
|
|
855
|
-
this.raw = JSON.parse(await
|
|
694
|
+
this.raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
856
695
|
try {
|
|
857
|
-
this.setup = JSON.parse(await
|
|
696
|
+
this.setup = JSON.parse(await readFile2(this.setupFile, "utf8"));
|
|
858
697
|
} catch {
|
|
859
698
|
this.setup = void 0;
|
|
860
699
|
}
|
|
861
700
|
this.config = validateConfig(this.raw, this.file);
|
|
862
701
|
await this.checkins.load();
|
|
863
702
|
try {
|
|
864
|
-
const saved = JSON.parse(await
|
|
703
|
+
const saved = JSON.parse(await readFile2(this.catalogFile, "utf8"));
|
|
865
704
|
for (const account of this.config.accounts) {
|
|
866
705
|
const item = saved[account.id];
|
|
867
706
|
if (item?.credentialFile === account.credentialFile && Array.isArray(item.models)) {
|
|
@@ -901,9 +740,9 @@ var Manager = class {
|
|
|
901
740
|
...discovered.status.map((s) => ({ ...s, checkedAt: Date.now() }))
|
|
902
741
|
];
|
|
903
742
|
if (this.closed || !discovered.accounts.length) return;
|
|
904
|
-
await
|
|
743
|
+
await withFileLock2(this.file, async () => {
|
|
905
744
|
if (this.closed) return;
|
|
906
|
-
let raw = JSON.parse(await
|
|
745
|
+
let raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
907
746
|
let changed = false;
|
|
908
747
|
const saved = /* @__PURE__ */ new Map();
|
|
909
748
|
for (const item of discovered.accounts) {
|
|
@@ -922,7 +761,7 @@ var Manager = class {
|
|
|
922
761
|
} catch {
|
|
923
762
|
}
|
|
924
763
|
}
|
|
925
|
-
const id = existing?.id ?? `local_${
|
|
764
|
+
const id = existing?.id ?? `local_${createHash("sha256").update(key).digest("hex").slice(0, 20)}`;
|
|
926
765
|
const recordSaved = () => {
|
|
927
766
|
if (!saved.has(item.provider)) saved.set(item.provider, /* @__PURE__ */ new Set());
|
|
928
767
|
saved.get(item.provider).add(id);
|
|
@@ -987,9 +826,9 @@ var Manager = class {
|
|
|
987
826
|
}
|
|
988
827
|
async finishInitialSetup() {
|
|
989
828
|
if (this.setup?.completedAt) return;
|
|
990
|
-
await
|
|
829
|
+
await withFileLock2(this.setupFile, async () => {
|
|
991
830
|
try {
|
|
992
|
-
this.setup = JSON.parse(await
|
|
831
|
+
this.setup = JSON.parse(await readFile2(this.setupFile, "utf8"));
|
|
993
832
|
} catch {
|
|
994
833
|
}
|
|
995
834
|
if (this.setup?.completedAt) return;
|
|
@@ -1056,7 +895,7 @@ var Manager = class {
|
|
|
1056
895
|
for (const account of this.config.accounts.filter((a) => a.enabled !== false)) {
|
|
1057
896
|
for (const model of this.freeModels(account)) {
|
|
1058
897
|
if (!row.targets.some((t) => t.provider === account.provider && (!t.account || t.account === account.id) && (!t.model || t.model === model.id) && (!t.models || t.models.includes(model.id)))) continue;
|
|
1059
|
-
const id = `free_${
|
|
898
|
+
const id = `free_${createHash("sha256").update(JSON.stringify([parentId, account.provider, model.id])).digest("hex").slice(0, 24)}`;
|
|
1060
899
|
if (!groups.has(id)) groups.set(id, { id, models: [], targets: [], accountNames: [], modelName: model.name, provider: account.provider, modelId: model.id });
|
|
1061
900
|
const group = groups.get(id);
|
|
1062
901
|
group.models.push({ model, provider: account.provider });
|
|
@@ -1171,7 +1010,7 @@ var Manager = class {
|
|
|
1171
1010
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id && a.credentialFile === account.credentialFile)) return;
|
|
1172
1011
|
this.catalogs.set(account.id, { credentialFile: account.credentialFile, fetchedAt: Date.now(), pricingVersion: 1, models });
|
|
1173
1012
|
this.catalogErrors.delete(account.id);
|
|
1174
|
-
await
|
|
1013
|
+
await withFileLock2(this.catalogFile, () => writePrivateJson(this.catalogFile, Object.fromEntries(this.catalogs)));
|
|
1175
1014
|
} catch (error) {
|
|
1176
1015
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id)) return;
|
|
1177
1016
|
this.catalogErrors.set(account.id, { status: Number(error.status ?? error.failure?.status) || 502, code: "catalog_fetch_failed", at: Date.now() });
|
|
@@ -1249,14 +1088,14 @@ var Manager = class {
|
|
|
1249
1088
|
return this.state();
|
|
1250
1089
|
}
|
|
1251
1090
|
if (input.action === "setup.dismiss") {
|
|
1252
|
-
await
|
|
1091
|
+
await withFileLock2(this.setupFile, async () => {
|
|
1253
1092
|
this.setup = { ...this.setup, completedAt: this.setup?.completedAt ?? Date.now(), dismissed: true };
|
|
1254
1093
|
await writePrivateJson(this.setupFile, this.setup);
|
|
1255
1094
|
});
|
|
1256
1095
|
return this.state();
|
|
1257
1096
|
}
|
|
1258
1097
|
if (input.action === "account.export") {
|
|
1259
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1098
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile2(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u63D0\u53D6\u51ED\u8BC1");
|
|
1260
1099
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1261
1100
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1262
1101
|
let credential;
|
|
@@ -1278,7 +1117,7 @@ var Manager = class {
|
|
|
1278
1117
|
} };
|
|
1279
1118
|
}
|
|
1280
1119
|
if (input.action === "config.export") {
|
|
1281
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1120
|
+
if (input.revision !== revisionOf(JSON.parse(await readFile2(this.file, "utf8")))) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u5BFC\u51FA");
|
|
1282
1121
|
try {
|
|
1283
1122
|
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) } };
|
|
1284
1123
|
} catch (e) {
|
|
@@ -1325,8 +1164,8 @@ var Manager = class {
|
|
|
1325
1164
|
await this.checkFreeModels(true);
|
|
1326
1165
|
return this.state();
|
|
1327
1166
|
}
|
|
1328
|
-
return
|
|
1329
|
-
let raw = JSON.parse(await
|
|
1167
|
+
return withFileLock2(this.file, async () => {
|
|
1168
|
+
let raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
1330
1169
|
if (input.revision !== revisionOf(raw)) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u518D\u4FDD\u5B58");
|
|
1331
1170
|
raw.routes ??= structuredClone(this.config.routes);
|
|
1332
1171
|
let staging;
|
|
@@ -1530,6 +1369,7 @@ var Manager = class {
|
|
|
1530
1369
|
}
|
|
1531
1370
|
if (input.action === "account.add" || input.action === "account.credentials") {
|
|
1532
1371
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1372
|
+
if (input.action === "account.credentials" && !this.checkins.failed) await this.checkins.credentialsUpdated(input.id);
|
|
1533
1373
|
if (account) {
|
|
1534
1374
|
await Promise.all([this.refreshAccount(account, true), this.monitorAccount(account)]);
|
|
1535
1375
|
return this.state();
|
|
@@ -1544,8 +1384,8 @@ var Manager = class {
|
|
|
1544
1384
|
if (filename && String(filename) !== this.file.split(/[\\/]/).pop()) return;
|
|
1545
1385
|
clearTimeout(timer);
|
|
1546
1386
|
timer = setTimeout(() => {
|
|
1547
|
-
void
|
|
1548
|
-
const raw = JSON.parse(await
|
|
1387
|
+
void withFileLock2(this.file, async () => {
|
|
1388
|
+
const raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
1549
1389
|
if (this.closed || revisionOf(raw) === this.revision) return;
|
|
1550
1390
|
const config = validateConfig(raw, this.file);
|
|
1551
1391
|
this.raw = raw;
|
|
@@ -1612,4 +1452,4 @@ export {
|
|
|
1612
1452
|
Manager,
|
|
1613
1453
|
managementHandler
|
|
1614
1454
|
};
|
|
1615
|
-
//# sourceMappingURL=chunk-
|
|
1455
|
+
//# sourceMappingURL=chunk-EJWYFWIT.js.map
|