dsh-multi-router 0.4.1 → 0.5.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 +8 -0
- package/README.md +15 -4
- package/lib/accounts.js +2 -2
- package/lib/{chunk-XMXFIV5G.js → chunk-GAQ77CH4.js} +2 -1
- package/lib/chunk-GAQ77CH4.js.map +7 -0
- package/lib/{chunk-3VGKH7MF.js → chunk-OMG4FZWV.js} +2 -2
- package/lib/{chunk-XY2UB22K.js → chunk-U2YTBWLW.js} +3 -3
- package/lib/{chunk-DZYRIZW2.js → chunk-W5WP4ATM.js} +214 -28
- package/lib/chunk-W5WP4ATM.js.map +7 -0
- package/lib/{chunk-GGRCX2NR.js → chunk-XV7O2LRF.js} +2 -2
- package/lib/{chunk-UFS7ZJKY.js → chunk-YUP77Q3A.js} +62 -5
- package/lib/chunk-YUP77Q3A.js.map +7 -0
- package/lib/cli.js +4 -4
- package/lib/client.js +81 -5
- package/lib/client.js.map +3 -3
- package/lib/clients.js +5 -3
- package/lib/host.js +10 -7
- package/lib/index.js +10 -7
- package/lib/index.js.map +2 -2
- 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-DZYRIZW2.js.map +0 -7
- package/lib/chunk-UFS7ZJKY.js.map +0 -7
- package/lib/chunk-XMXFIV5G.js.map +0 -7
- /package/lib/{chunk-3VGKH7MF.js.map → chunk-OMG4FZWV.js.map} +0 -0
- /package/lib/{chunk-XY2UB22K.js.map → chunk-U2YTBWLW.js.map} +0 -0
- /package/lib/{chunk-GGRCX2NR.js.map → chunk-XV7O2LRF.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。版本号从 `0.1` 起以功能迭代为主,接口在 `1.0` 前可能变化。
|
|
4
4
|
|
|
5
|
+
## 0.5.0
|
|
6
|
+
|
|
7
|
+
- 新增「积分签到」面板:接入 Trae CN 与 Qoder 签到,显示账号结果与奖励,支持手动领取、批量领取及按账号选择每日自动签到(默认关闭)。
|
|
8
|
+
- 每日北京时间 10:05 后自动补查;持久化每日意图、账号身份去重、串行限频、Trae 同设备限制;结果不明不重复提交。
|
|
9
|
+
- 验证、授权拒绝与限流时暂停平台签到,官方客户端处理后可恢复后续签到;保留设备身份,不绕过风控。
|
|
10
|
+
- Qoder 账号统一复用官方 CLI / 原连接器的持久设备身份。WorkBuddy 与 Trae Global 未核验的签到接口明确标为暂未接入。
|
|
11
|
+
- 自动签到设置支持配置迁移,新增并发、重启、设备、失败及面板回归测试。
|
|
12
|
+
|
|
5
13
|
## 0.4.1
|
|
6
14
|
|
|
7
15
|
- 修复 GitHub 来源再次安装时 pnpm 复用旧锁文件:改走官方包管理更新路径刷新原分支,保留安装来源与启用状态。
|
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.
|
|
9
|
+
版本:0.5.0。适配 DSH Desktop / Web 0.2.0-rc.2,Node.js ^22.19.0 或 >=24。
|
|
10
10
|
|
|
11
11
|
## 在 DSH 里管理
|
|
12
12
|
|
|
@@ -87,12 +87,23 @@ Qoder 的 `dfmodel` 在真实目录里显示为 DeepSeek-Flash,未声明具体
|
|
|
87
87
|
|
|
88
88
|
统计保存于配置旁的 `usage.json`,0600 权限。仅保存账号/路由元数据、计数、平台额度和保留期限内的逐次调用明细,不保存提示词、回答或认证凭证;明细默认保留最近 3 天(滚动 72 小时),由 `usageRetentionDays` 设置 1–365 天,不再限定 80 条;缩短期限立即删除超期明细,延长不会恢复已删除记录。累计用量、额度轮换基线、临期冷却不清零,日趋势独立保留最近 31 天。Token 从本版本启用后开始计量,不回填历史会话。三家积分单位分别展示,不汇总为通用余额;平台余额下降可包含其他应用消费、积分包到期或延迟结算,不宣称是精确的单请求扣费。平台查询失败保留最后数据与错误、时间;没有可用积分数据的账号不参加积分阈值轮换。
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
账号获取采用官方客户端已有登录文件的只读解析,不修改客户端登录、不复制参考项目代码;自动签到需要在独立的「积分签到」页按账号开启,默认关闭。手动凭证支持 WorkBuddy / Trae 字段表单、三家凭证 JSON 和 Qoder PAT;凭证格式、区域、Trae 设备身份会校验。已有账号可在详情中「补充 / 更新凭证」,旧副本保留。Qoder 普通网页 Cookie 无法替代 PAT;官方 PAT 指引见 https://docs.qoder.com/cli/authentication 。
|
|
91
91
|
|
|
92
92
|
## 首次使用与账号自动获取
|
|
93
93
|
|
|
94
94
|
第一次启用插件时会执行 WorkBuddy / WorkBuddy AI 与 Trae CN / Global / SOLO 的本机登录检测,将找到的有效登录保存为独立账号副本。首页会展示「本机账号自动获取」结果,区分已保存、未登录和无法读取,并提供「一键获取并保存」及手动添加入口;无需先逐个点击添加账号。之后每分钟检测新增登录,重复检测不重复添加,已停用和已删除的账号保持用户选择。Qoder 仍需添加 PAT。
|
|
95
95
|
|
|
96
|
+
## 每日积分签到
|
|
97
|
+
|
|
98
|
+
在配置面板打开 **积分签到**。当前接入 Trae CN 与 Qoder CN / Global 的已有签到能力:先查询平台是否开放活动、账号与设备是否已领取,再提交领取;活动是否开放和奖励额度以平台响应为准。WorkBuddy / WorkBuddy AI 与 Trae Global 暂无已核验的签到接口,面板会显示需到官方客户端查看,不猜测接口或奖励。
|
|
99
|
+
|
|
100
|
+
- 每个账号可选择「立即签到」,或「签到全部可用账号」。**每日自动签到默认关闭**,按账号单独开启;已停用账号不参与。每天北京时间 10:05 后、DSH 运行期间执行,错过时在下次启动补查;10:05 前不会发起签到请求。
|
|
101
|
+
- 请求串行,账号间至少间隔 10 秒。先持久化当天尝试记录,再发请求;同一账号当天最多尝试一次,重复账号副本按平台身份去重。网络超时或结果不明确时不重复提交,请到官方客户端核对。
|
|
102
|
+
- 保留 Trae 原有设备身份;同一设备当天的领取已使用或结果不明确时,不切到同设备的其他账号继续领取。Qoder 优先复用官方 CLI 的设备身份,没有时使用连接器持久化的单一设备身份,所有账号一致,不随签到轮换。
|
|
103
|
+
- 验证、授权拒绝或限流会暂停该平台签到;在官方客户端处理后,可以点击「恢复后续签到」。恢复不会清除今日尝试记录与设备限制,不提供更换设备身份、代理或绕过验证码的能力。平台风控行为不可预测,这些措施不能保证不触发风控。
|
|
104
|
+
- 最近结果、可核验的奖励积分与自动签到开关直接展示(Qoder 不展示上游辅助函数猜测的奖励数额);成功后刷新该账号平台余额。记录保存在配置旁的 `checkins.json`,0600 权限,仅保存身份哈希、日期和结果,不保存 Token 或原始服务端错误。保留最近 30 天,记录损坏时停止签到,不自动清空后重试。
|
|
105
|
+
- 自动签到设置可随配置导入导出,领取记录与平台暂停状态不迁移。多台设备的本地记录不会互相同步,建议只在一台设备开启自动签到;平台领取状态仍会在提交前查询。
|
|
106
|
+
|
|
96
107
|
## 所有已安装插件的更新与安装
|
|
97
108
|
|
|
98
109
|
在本插件配置面板打开 **插件更新**,管理当前 DSH 配置中的全部已安装插件,包括其它插件和已停用插件。首次打开自动检查,后续超过 24 小时重新打开时再检查,也可随时点击「检查全部更新」。
|
|
@@ -124,7 +135,7 @@ dsh plugin --profile desktop add dsh-multi-router
|
|
|
124
135
|
dsh plugin --profile web add dsh-multi-router
|
|
125
136
|
```
|
|
126
137
|
|
|
127
|
-
包地址:[dsh-multi-router · npm](https://www.npmjs.com/package/dsh-multi-router)。正式版本由 GitHub Release 自动测试、打包并发布到 npm `latest`,包中已包含编译产物,用户无需自行构建。需要固定版本时可安装 `dsh-multi-router@0.
|
|
138
|
+
包地址:[dsh-multi-router · npm](https://www.npmjs.com/package/dsh-multi-router)。正式版本由 GitHub Release 自动测试、打包并发布到 npm `latest`,包中已包含编译产物,用户无需自行构建。需要固定版本时可安装 `dsh-multi-router@0.5.0`。
|
|
128
139
|
|
|
129
140
|
已使用 npm 来源的用户更新:
|
|
130
141
|
|
|
@@ -181,7 +192,7 @@ cd dsh-multi-router
|
|
|
181
192
|
npm ci
|
|
182
193
|
npm run check
|
|
183
194
|
npm run pack:release
|
|
184
|
-
dsh plugin --profile desktop add ./dsh-multi-router-0.
|
|
195
|
+
dsh plugin --profile desktop add ./dsh-multi-router-0.5.0.tgz
|
|
185
196
|
```
|
|
186
197
|
|
|
187
198
|
### 配置账号
|
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-OMG4FZWV.js";
|
|
7
7
|
import "./chunk-FTM7RHQV.js";
|
|
8
8
|
import "./chunk-S5KMFAQM.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-GAQ77CH4.js";
|
|
10
10
|
import "./chunk-TEZ2FMEO.js";
|
|
11
11
|
export {
|
|
12
12
|
discoverLocalAccounts,
|
|
@@ -99,6 +99,7 @@ function validateConfig(raw, file = resolve("router.json")) {
|
|
|
99
99
|
ids.add(account.id);
|
|
100
100
|
if (!PROVIDERS.includes(account.provider) || !nonempty(account.credentialFile)) throw new Error(`Invalid provider or credentialFile for ${account.id}`);
|
|
101
101
|
if (account.enabled !== void 0 && typeof account.enabled !== "boolean") throw new Error(`Invalid enabled flag for ${account.id}`);
|
|
102
|
+
if (account.autoCheckin !== void 0 && (typeof account.autoCheckin !== "boolean" || account.autoCheckin && !["trae", "qoder", "qoder-global"].includes(account.provider))) throw new Error(`Invalid autoCheckin for ${account.id}`);
|
|
102
103
|
if (account.name !== void 0 && (!nonempty(account.name) || account.name.length > 100)) throw new Error(`Invalid account name for ${account.id}`);
|
|
103
104
|
if (account.identityFile !== void 0 && !nonempty(account.identityFile)) throw new Error(`Invalid identityFile for ${account.id}`);
|
|
104
105
|
return {
|
|
@@ -206,4 +207,4 @@ export {
|
|
|
206
207
|
ensureConfig,
|
|
207
208
|
writePrivateJson
|
|
208
209
|
};
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
210
|
+
//# sourceMappingURL=chunk-GAQ77CH4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/reasoning.mjs", "../src/config.mjs"],
|
|
4
|
+
"sourcesContent": ["export const REASONING_EFFORTS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n\n// Keep declared modes only. A fixed-thinking model without a declared set uses platform default.\nexport function reasoningModes(model, provider) {\n const r = model?.reasoning;\n if (!r || (r.supports !== true && !Array.isArray(r.supported))) return [];\n const declared = r.supportedEfforts ?? r.supported ?? [];\n const disable = r.canDisableThinking === true || declared.includes('off');\n const off = disable && !(provider === 'workbuddy' && r.onlyReasoning === true);\n return REASONING_EFFORTS.filter(e => e === 'off' ? off : declared.includes(e));\n}\n\nexport function routeReasoning(models) {\n const efforts = REASONING_EFFORTS.filter(e => models.some(({ model, provider }) => reasoningModes(model, provider).includes(e)));\n if (!efforts.length) return undefined;\n return { supports: true, supportedEfforts: efforts, canDisableThinking: efforts.includes('off'),\n defaultEffort: efforts.includes('high') ? 'high' : efforts[0] };\n}\n", "import { readFile, mkdir, rename, writeFile, chmod } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { withFileLock } from '@deepseek-ai/dsh-atomic-write';\nimport { REASONING_EFFORTS } from './reasoning.mjs';\n\nexport const PROVIDERS = ['workbuddy', 'workbuddy-ai', 'trae', 'trae-global', 'qoder', 'qoder-global'];\nexport const PROVIDER_NAMES = { workbuddy: 'WorkBuddy', 'workbuddy-ai': 'WorkBuddy AI', trae: 'Trae CN',\n 'trae-global': 'Trae Global', qoder: 'Qoder CN', 'qoder-global': 'Qoder Global' };\nexport const FLASH_PRESET = {\n name: 'DeepSeek V4.1 Flash \u00B7 \u81EA\u52A8\u8DEF\u7531', contextWindow: 200000, maxTokens: 32000, preferExpiringCredits: true, expiringWithinDays: 7,\n targets: ['workbuddy', 'trae', 'qoder'].map(provider => ({ provider, models: provider.startsWith('qoder')\n ? ['dfmodel'] : ['deepseek-v4.1-flash', 'DeepSeek-V4.1-Flash', 'DeepSeek-V4.1-Flash-Official'] })),\n};\n// IDs differ between providers; keep precise model/version bindings, never fuzzy-match variants.\nconst sharedPreset = (name, workbuddy, trae, qoder) => ({\n name: `${name} \u00B7 \u81EA\u52A8\u8DEF\u7531`, contextWindow: 200000, maxTokens: 32000, preferExpiringCredits: true, expiringWithinDays: 7,\n targets: [{ provider: 'workbuddy', models: [workbuddy] }, { provider: 'trae', models: Array.isArray(trae) ? trae : [trae] }, { provider: 'qoder', models: [qoder] }],\n});\nexport const ROUTE_PRESETS = {\n free: { name: 'Free \u00B7 \u52A8\u6001\u514D\u8D39\u6A21\u578B', freeOnly: true, visible: true, contextWindow: 200000, maxTokens: 32000,\n strategy: 'failover', preferExpiringCredits: false, targets: PROVIDERS.map(provider => ({ provider })) },\n 'deepseek-v4.1-flash': FLASH_PRESET,\n 'deepseek-v4-pro': sharedPreset('DeepSeek V4 Pro', 'deepseek-v4-pro', ['DeepSeek-V4-Pro-Official', 'DeepSeek-V4-Pro'], 'dmodel'),\n 'glm-5.3': sharedPreset('GLM-5.3', 'glm-5.3', 'glm-5.3', 'gmodel'),\n 'glm-5.3-flash': sharedPreset('GLM-5.3-Flash', 'glm-5.3-flash', 'glm-5.3-flash', 'gfmodel'),\n 'kimi-k3': sharedPreset('Kimi-K3', 'kimi-k3-1', 'kimi-k3', 'kmodel_latest'),\n 'kimi-k2.8-preview': sharedPreset('Kimi-K2.8-Preview', 'kimi-k2.8-preview', 'kimi-k2.8-preview', 'kmodel'),\n 'minimax-m2.7': sharedPreset('MiniMax-M2.7', 'minimax-m2.7', 'minimax-m2.7', 'mmodel'),\n};\nexport const DEFAULT_ROUTES = {\n free: ROUTE_PRESETS.free,\n 'ds4.1f': {\n name: 'DeepSeek V4.1 Flash \u00B7 Auto', contextWindow: 200000, maxTokens: 32000, preferExpiringCredits: true, expiringWithinDays: 7,\n targets: PROVIDERS.map(provider => ({ provider })),\n },\n ...ROUTE_PRESETS,\n};\nconst object = x => x !== null && typeof x === 'object' && !Array.isArray(x);\nconst nonempty = x => typeof x === 'string' && x.trim().length > 0;\nconst positive = x => Number.isSafeInteger(x) && x > 0;\nexport const beijingDay = at => new Date(at).toLocaleDateString('en-CA', { timeZone: 'Asia/Shanghai' });\nexport function isFreeModel(model, now = Date.now()) {\n if (model.billing?.rateUnknown === true) return false;\n // A badge or a zero adapter cost is not an account-scoped zero billing rate.\n const rate = model.creditMultiplier ?? (/^x?(\\d+(?:\\.\\d+)?)(?:\\s+credits?)?$/i.exec(model.billing?.credits?.trim() ?? '')?.[1]);\n if (rate === undefined || !Number.isFinite(Number(rate)) || Number(rate) !== 0) return false;\n if (model.promotions?.length && !model.promotions.some(p => now >= p.start && now < p.end)) return false;\n return true;\n}\nexport function validateConfig(raw, file = resolve('router.json')) {\n if (!object(raw) || !Array.isArray(raw.accounts)) throw new Error('Config must contain an accounts array');\n const ids = new Set();\n const accounts = raw.accounts.map(account => {\n if (!object(account) || !/^[a-zA-Z0-9_-]{1,64}$/.test(account.id ?? '') || ids.has(account.id)) throw new Error('Account ids must be unique, 1\u201364 letters, digits, _ or -');\n ids.add(account.id);\n if (!PROVIDERS.includes(account.provider) || !nonempty(account.credentialFile)) throw new Error(`Invalid provider or credentialFile for ${account.id}`);\n if (account.enabled !== undefined && typeof account.enabled !== 'boolean') throw new Error(`Invalid enabled flag for ${account.id}`);\n if (account.autoCheckin !== undefined && (typeof account.autoCheckin !== 'boolean' || (account.autoCheckin && !['trae', 'qoder', 'qoder-global'].includes(account.provider)))) throw new Error(`Invalid autoCheckin for ${account.id}`);\n if (account.name !== undefined && (!nonempty(account.name) || account.name.length > 100)) throw new Error(`Invalid account name for ${account.id}`);\n if (account.identityFile !== undefined && !nonempty(account.identityFile)) throw new Error(`Invalid identityFile for ${account.id}`);\n return { ...account, credentialFile: resolve(dirname(file), account.credentialFile),\n ...(account.identityFile ? { identityFile: resolve(dirname(file), account.identityFile) } : {}) };\n });\n if (!object(raw.routes ?? DEFAULT_ROUTES)) throw new Error('Routes must be an object');\n const routes = Object.fromEntries(Object.entries(raw.routes ?? DEFAULT_ROUTES).map(([id, route]) => [id, { ...route, preferExpiringCredits: route?.preferExpiringCredits ?? !route?.freeOnly, expiringWithinDays: route?.expiringWithinDays ?? 7 }]));\n for (const [alias, route] of Object.entries(routes)) {\n if (!/^[a-zA-Z0-9_.-]{1,128}$/.test(alias) || !object(route) || !Array.isArray(route.targets) || !route.targets.length) throw new Error(`Invalid route ${alias}`);\n for (const key of ['contextWindow', 'maxTokens']) if (!positive(route[key])) throw new Error(`Route ${alias} needs a positive ${key}`);\n if (route.maxTokens > route.contextWindow) throw new Error(`Route ${alias}: maxTokens exceeds contextWindow`);\n if (route.supportsImages !== undefined && typeof route.supportsImages !== 'boolean') throw new Error(`Invalid supportsImages for ${alias}`);\n if (route.visible !== undefined && typeof route.visible !== 'boolean') throw new Error(`Invalid visibility for ${alias}`);\n if (route.name !== undefined && (!nonempty(route.name) || route.name.length > 200)) throw new Error(`Invalid route name for ${alias}`);\n if (route.freeOnly !== undefined && typeof route.freeOnly !== 'boolean') throw new Error(`Invalid freeOnly for ${alias}`);\n if (route.freeModels !== undefined && (!route.freeOnly || !object(route.freeModels) || Object.entries(route.freeModels).some(([id, p]) =>\n !/^free_[a-f0-9]{24}$/.test(id) || !object(p) || Object.keys(p).some(k => !['visible', 'reasoningEffort'].includes(k))\n || (p.visible !== undefined && typeof p.visible !== 'boolean')\n || (p.reasoningEffort !== undefined && p.reasoningEffort !== null && !REASONING_EFFORTS.includes(p.reasoningEffort))))) throw new Error('\u514D\u8D39\u6A21\u578B\u5C55\u793A\u6216\u63A8\u7406\u7B49\u7EA7\u8BBE\u7F6E\u65E0\u6548');\n if (route.freeOnly && ((route.strategy && route.strategy !== 'failover') || route.preferExpiringCredits)) throw new Error('Free \u8DEF\u7531\u4EC5\u4F7F\u7528\u514D\u8D39\u6A21\u578B\u6545\u969C\u5207\u6362\uFF0C\u4E0D\u4F7F\u7528\u79EF\u5206\u6216\u989D\u5EA6\u8F6E\u6362');\n if (route.preferExpiringCredits !== undefined && typeof route.preferExpiringCredits !== 'boolean') throw new Error(`Invalid expiring credits flag in ${alias}`);\n if (route.expiringWithinDays !== undefined && (!positive(route.expiringWithinDays) || route.expiringWithinDays > 365)) throw new Error(`Invalid expiry horizon in ${alias}`);\n if (route.strategy !== undefined && !['failover', 'weighted', 'tokens', 'credits'].includes(route.strategy)) throw new Error(`Invalid strategy in ${alias}`);\n for (const target of route.targets) {\n if (!object(target)) throw new Error(`Invalid target in ${alias}`);\n if (target.weight !== undefined && (!positive(target.weight) || target.weight > 1000)) throw new Error(`Invalid target weight in ${alias}`);\n if (target.tokenBudget !== undefined && !positive(target.tokenBudget)) throw new Error(`Invalid token budget in ${alias}`);\n if (target.creditBudget !== undefined && (typeof target.creditBudget !== 'number' || !Number.isFinite(target.creditBudget) || target.creditBudget <= 0)) throw new Error(`Invalid credit budget in ${alias}`);\n if (route.strategy === 'tokens' && !positive(target.tokenBudget)) throw new Error(`Route ${alias} needs token budgets`);\n if (route.strategy === 'credits' && !(target.creditBudget > 0)) throw new Error(`Route ${alias} needs credit budgets`);\n if (!object(target) || !PROVIDERS.includes(target.provider) || (target.model !== undefined && !nonempty(target.model))) throw new Error(`Invalid target in ${alias}`);\n if (target.models !== undefined && (!Array.isArray(target.models) || !target.models.length || target.models.length > 20 || !target.models.every(nonempty))) throw new Error(`Invalid target models in ${alias}`);\n if (target.model !== undefined && target.models !== undefined) throw new Error(`Choose model or models in ${alias}`);\n if (target.account !== undefined && !accounts.some(a => a.id === target.account && a.provider === target.provider)) throw new Error(`Unknown target account in ${alias}`);\n if (!target.model && !target.models && alias !== 'ds4.1f' && !route.freeOnly) throw new Error(`Route ${alias} requires explicit target model ids`);\n }\n }\n const models = raw.models ?? {};\n if (!object(models)) throw new Error('Models must be an object');\n for (const [provider, preferences] of Object.entries(models)) {\n if (!PROVIDERS.includes(provider) || !object(preferences)) throw new Error('Invalid model provider');\n for (const [id, preference] of Object.entries(preferences)) {\n if (!nonempty(id) || id.length > 256 || !object(preference)\n || (preference.name !== undefined && (!nonempty(preference.name) || preference.name.length > 200))\n || (preference.visible !== undefined && typeof preference.visible !== 'boolean')) throw new Error('Invalid model preference');\n }\n }\n const modelVisibility = raw.modelVisibility ?? {};\n if (!object(modelVisibility) || Object.entries(modelVisibility).some(([provider, visible]) => !PROVIDERS.includes(provider) || typeof visible !== 'boolean')) throw new Error('Invalid modelVisibility');\n for (const key of ['cooldownMs', 'requestTimeoutMs', 'catalogTtlMs']) if (raw[key] !== undefined && !positive(raw[key])) throw new Error(`Invalid ${key}`);\n if (raw.retryServerErrors !== undefined && typeof raw.retryServerErrors !== 'boolean') throw new Error('Invalid retryServerErrors');\n if (raw.ignoredLocalAccounts !== undefined && (!Array.isArray(raw.ignoredLocalAccounts) || raw.ignoredLocalAccounts.some(k => typeof k !== 'string' || k.length > 1024))) throw new Error('Invalid ignoredLocalAccounts');\n if (raw.usageRetentionDays !== undefined && (!positive(raw.usageRetentionDays) || raw.usageRetentionDays > 365)) throw new Error('Token \u660E\u7EC6\u4FDD\u7559\u5929\u6570\u5FC5\u987B\u4E3A 1\u2013365 \u7684\u6574\u6570');\n return { ...raw, accounts, routes, models, modelVisibility, cooldownMs: raw.cooldownMs ?? 60000,\n requestTimeoutMs: raw.requestTimeoutMs ?? 300000, catalogTtlMs: raw.catalogTtlMs ?? 300000,\n retryServerErrors: raw.retryServerErrors ?? false, usageRetentionDays: raw.usageRetentionDays ?? 3 };\n}\nexport async function readConfig(file) {\n return validateConfig(JSON.parse(await readFile(file, 'utf8')), resolve(file));\n}\nexport async function ensureConfig(file) {\n try { return await readConfig(file); } catch (error) { if (error.code !== 'ENOENT') throw error; }\n await mkdir(dirname(file), { recursive: true, mode: 0o700 });\n return withFileLock(file, async () => {\n try { return await readConfig(file); } catch (error) { if (error.code !== 'ENOENT') throw error; }\n await writePrivateJson(file, { accounts: [], routes: ROUTE_PRESETS });\n return readConfig(file);\n }, { waitMs: 30000 });\n}\nexport async function writePrivateJson(file, value) {\n await mkdir(dirname(file), { recursive: true, mode: 0o700 });\n const temp = `${file}.${randomUUID()}.tmp`;\n await writeFile(temp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600, flag: 'wx' });\n await rename(temp, file);\n await chmod(file, 0o600);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
PROVIDERS,
|
|
14
14
|
writePrivateJson
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GAQ77CH4.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-OMG4FZWV.js.map
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-DNQPZMPF.js";
|
|
5
5
|
import {
|
|
6
6
|
RouteError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XV7O2LRF.js";
|
|
8
8
|
import {
|
|
9
9
|
REASONING_EFFORTS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GAQ77CH4.js";
|
|
11
11
|
|
|
12
12
|
// src/shim.mjs
|
|
13
13
|
import { createServer } from "node:http";
|
|
@@ -113,4 +113,4 @@ function createShim(router, { modelPrefix = "" } = {}) {
|
|
|
113
113
|
export {
|
|
114
114
|
createShim
|
|
115
115
|
};
|
|
116
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-U2YTBWLW.js.map
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
jsonCredential,
|
|
3
3
|
qoderPluginDataDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YUP77Q3A.js";
|
|
5
5
|
import {
|
|
6
6
|
discoverLocalAccounts,
|
|
7
7
|
importAccount,
|
|
8
8
|
localAccountKey,
|
|
9
9
|
readPortableCredential
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OMG4FZWV.js";
|
|
11
11
|
import {
|
|
12
12
|
hostIsLoopback
|
|
13
13
|
} from "./chunk-DNQPZMPF.js";
|
|
14
14
|
import {
|
|
15
15
|
Telemetry,
|
|
16
16
|
matchModel
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XV7O2LRF.js";
|
|
18
18
|
import {
|
|
19
19
|
PROVIDERS,
|
|
20
20
|
PROVIDER_NAMES,
|
|
@@ -25,22 +25,22 @@ import {
|
|
|
25
25
|
routeReasoning,
|
|
26
26
|
validateConfig,
|
|
27
27
|
writePrivateJson
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-GAQ77CH4.js";
|
|
29
29
|
import {
|
|
30
30
|
modelWithCurrentPromotion
|
|
31
31
|
} from "./chunk-TEZ2FMEO.js";
|
|
32
32
|
|
|
33
33
|
// src/manager.mjs
|
|
34
|
-
import { createHash, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
34
|
+
import { createHash as createHash2, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
35
35
|
|
|
36
36
|
// package.json
|
|
37
|
-
var version = "0.
|
|
37
|
+
var version = "0.5.0";
|
|
38
38
|
|
|
39
39
|
// src/manager.mjs
|
|
40
|
-
import { readFile as
|
|
40
|
+
import { readFile as readFile3, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
41
41
|
import { watch } from "node:fs";
|
|
42
42
|
import { dirname as dirname3, join as join4, relative as relative2, resolve } from "node:path";
|
|
43
|
-
import { withFileLock as
|
|
43
|
+
import { withFileLock as withFileLock3 } from "@deepseek-ai/dsh-atomic-write";
|
|
44
44
|
|
|
45
45
|
// upstream/qoder/src/status-paths.ts
|
|
46
46
|
var QODER_STATUS_PATH = "/plugins/dsh-qoder-connect/status";
|
|
@@ -101,7 +101,7 @@ function settings(config) {
|
|
|
101
101
|
return {
|
|
102
102
|
...pick(config, options),
|
|
103
103
|
models: Object.fromEntries(Object.entries(config.models ?? {}).map(([p, rows]) => [p, Object.fromEntries(Object.entries(rows).map(([id, pref]) => [id, pick(pref, ["name", "visible"])]))])),
|
|
104
|
-
accounts: config.accounts.map((a) => pick(a, ["id", "provider", "name", "enabled"])),
|
|
104
|
+
accounts: config.accounts.map((a) => pick(a, ["id", "provider", "name", "enabled", "autoCheckin"])),
|
|
105
105
|
routes: Object.fromEntries(Object.entries(config.routes).map(([id, r]) => [id, {
|
|
106
106
|
...pick(r, ["name", "visible", "strategy", "contextWindow", "maxTokens", "preferExpiringCredits", "expiringWithinDays", "freeOnly", "freeModels"]),
|
|
107
107
|
targets: r.targets.map((t) => pick(t, ["provider", "account", "model", "models", "weight", "tokenBudget", "creditBudget"]))
|
|
@@ -627,9 +627,172 @@ var PluginUpdates = class {
|
|
|
627
627
|
}
|
|
628
628
|
};
|
|
629
629
|
|
|
630
|
+
// src/checkins.mjs
|
|
631
|
+
import { createHash } from "node:crypto";
|
|
632
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
633
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
634
|
+
import { withFileLock as withFileLock2 } from "@deepseek-ai/dsh-atomic-write";
|
|
635
|
+
var supportsCheckin = (provider) => ["trae", "qoder", "qoder-global"].includes(provider);
|
|
636
|
+
var hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
637
|
+
var nextDay = (now) => Date.parse(`${beijingDay(now)}T00:00:00+08:00`) + 864e5;
|
|
638
|
+
var checkinMessages = {
|
|
639
|
+
claimed: "\u7B7E\u5230\u6210\u529F",
|
|
640
|
+
"already-claimed": "\u4ECA\u65E5\u5DF2\u9886\u53D6",
|
|
641
|
+
"device-used": "\u6B64\u8BBE\u5907\u4ECA\u65E5\u5DF2\u53C2\u4E0E\u7B7E\u5230\uFF0C\u4E0D\u518D\u5207\u6362\u5176\u4ED6\u8D26\u53F7\u9886\u53D6",
|
|
642
|
+
"no-campaign": "\u5E73\u53F0\u672A\u5F00\u653E\u53EF\u9886\u53D6\u6D3B\u52A8",
|
|
643
|
+
"before-window": "\u7B7E\u5230\u7A97\u53E3\u5C1A\u672A\u5F00\u542F",
|
|
644
|
+
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",
|
|
645
|
+
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",
|
|
646
|
+
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",
|
|
647
|
+
unsupported: "\u6682\u672A\u63A5\u5165\u53EF\u6838\u9A8C\u7684\u7B7E\u5230\u63A5\u53E3\uFF0C\u8BF7\u4F7F\u7528\u5B98\u65B9\u5BA2\u6237\u7AEF"
|
|
648
|
+
};
|
|
649
|
+
var Checkins = class {
|
|
650
|
+
constructor(file, manager, { now = Date.now, spacingMs = 1e4 } = {}) {
|
|
651
|
+
this.file = file;
|
|
652
|
+
this.manager = manager;
|
|
653
|
+
this.now = now;
|
|
654
|
+
this.spacingMs = spacingMs;
|
|
655
|
+
this.data = { records: {}, devices: {}, pauses: {} };
|
|
656
|
+
this.abort = new AbortController();
|
|
657
|
+
}
|
|
658
|
+
async read() {
|
|
659
|
+
try {
|
|
660
|
+
const raw = JSON.parse(await readFile2(this.file, "utf8"));
|
|
661
|
+
const object = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
662
|
+
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");
|
|
663
|
+
this.data = raw;
|
|
664
|
+
} catch (e) {
|
|
665
|
+
if (e.code !== "ENOENT") throw e;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async load() {
|
|
669
|
+
try {
|
|
670
|
+
await this.read();
|
|
671
|
+
} catch {
|
|
672
|
+
this.failed = true;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
state() {
|
|
676
|
+
return {
|
|
677
|
+
running: !!this.flight,
|
|
678
|
+
current: this.current,
|
|
679
|
+
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,
|
|
680
|
+
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",
|
|
681
|
+
accounts: this.manager.config.accounts.map((a) => {
|
|
682
|
+
const rows = Object.values(this.data.records).filter((r) => r.ids?.includes(a.id));
|
|
683
|
+
const last = rows.sort((a2, b) => b.at - a2.at)[0];
|
|
684
|
+
const pause = this.data.pauses[a.provider];
|
|
685
|
+
return {
|
|
686
|
+
id: a.id,
|
|
687
|
+
supported: supportsCheckin(a.provider),
|
|
688
|
+
auto: a.autoCheckin === true,
|
|
689
|
+
paused: !!pause,
|
|
690
|
+
last: last ? {
|
|
691
|
+
day: last.day,
|
|
692
|
+
at: last.at,
|
|
693
|
+
status: last.status,
|
|
694
|
+
amount: last.amount,
|
|
695
|
+
message: checkinMessages[last.status] || checkinMessages.error
|
|
696
|
+
} : void 0
|
|
697
|
+
};
|
|
698
|
+
})
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
start(ids, automatic = false) {
|
|
702
|
+
if (this.failed || this.abort.signal.aborted || this.flight) return;
|
|
703
|
+
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");
|
|
704
|
+
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);
|
|
705
|
+
if (!selected.length) return;
|
|
706
|
+
this.flight = (async () => {
|
|
707
|
+
let requested = false;
|
|
708
|
+
for (const id of selected) {
|
|
709
|
+
if (this.abort.signal.aborted) break;
|
|
710
|
+
const account = this.manager.config.accounts.find((a) => a.id === id && a.enabled !== false && (!automatic || a.autoCheckin === true));
|
|
711
|
+
if (!account) continue;
|
|
712
|
+
if (requested) {
|
|
713
|
+
await delay(this.spacingMs, void 0, { signal: this.abort.signal });
|
|
714
|
+
requested = false;
|
|
715
|
+
}
|
|
716
|
+
this.current = id;
|
|
717
|
+
requested = await this.run(account, automatic);
|
|
718
|
+
}
|
|
719
|
+
})().catch(() => {
|
|
720
|
+
if (!this.abort.signal.aborted) this.failed = true;
|
|
721
|
+
}).finally(() => {
|
|
722
|
+
this.flight = void 0;
|
|
723
|
+
this.current = void 0;
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
async resume(provider) {
|
|
727
|
+
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");
|
|
728
|
+
await withFileLock2(this.file, async () => {
|
|
729
|
+
await this.read();
|
|
730
|
+
delete this.data.pauses[provider];
|
|
731
|
+
await writePrivateJson(this.file, this.data);
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
async run(account, automatic = false) {
|
|
735
|
+
const now = this.now(), day = beijingDay(now);
|
|
736
|
+
if (now < Date.parse(`${day}T10:05:00+08:00`)) return false;
|
|
737
|
+
return withFileLock2(this.file, async () => {
|
|
738
|
+
await this.read();
|
|
739
|
+
if (this.abort.signal.aborted || this.data.pauses[account.provider]) return false;
|
|
740
|
+
if (Object.values(this.data.records).some((r) => r.day === day && r.ids?.includes(account.id))) return false;
|
|
741
|
+
if (this.data.lastRequestAt && now - this.data.lastRequestAt < this.spacingMs) return false;
|
|
742
|
+
this.data.lastRequestAt = now;
|
|
743
|
+
await writePrivateJson(this.file, this.data);
|
|
744
|
+
const client = await this.manager.router.client(account);
|
|
745
|
+
if (!client.checkin) return false;
|
|
746
|
+
let identity;
|
|
747
|
+
try {
|
|
748
|
+
identity = await client.checkin.identity(AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]));
|
|
749
|
+
} catch {
|
|
750
|
+
this.data.pauses[account.provider] = { at: now };
|
|
751
|
+
await writePrivateJson(this.file, this.data);
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
if (!identity?.account || account.provider === "trae" && !identity.device) throw Error("Missing check-in identity");
|
|
755
|
+
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;
|
|
756
|
+
const key = hash(`${account.provider}:${identity.account}`), device = identity.device ? hash(`${account.provider}:${identity.device}`) : void 0;
|
|
757
|
+
const old = this.data.records[key];
|
|
758
|
+
if (old?.day === day) {
|
|
759
|
+
if (!old.ids.includes(account.id)) {
|
|
760
|
+
old.ids.push(account.id);
|
|
761
|
+
await writePrivateJson(this.file, this.data);
|
|
762
|
+
}
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
const record = { day, at: now, status: "uncertain", ids: [account.id] };
|
|
766
|
+
this.data.records[key] = record;
|
|
767
|
+
for (const [k, row] of Object.entries(this.data.records)) if (row.at < now - 30 * 864e5) delete this.data.records[k];
|
|
768
|
+
for (const [k, row] of Object.entries(this.data.devices)) if (row.until <= now) delete this.data.devices[k];
|
|
769
|
+
if (device && this.data.devices[device]?.until > now) {
|
|
770
|
+
record.status = "device-used";
|
|
771
|
+
await writePrivateJson(this.file, this.data);
|
|
772
|
+
return true;
|
|
773
|
+
}
|
|
774
|
+
if (device) this.data.devices[device] = { until: nextDay(now) };
|
|
775
|
+
await writePrivateJson(this.file, this.data);
|
|
776
|
+
let result;
|
|
777
|
+
try {
|
|
778
|
+
result = await client.checkin.claim(AbortSignal.any([this.abort.signal, AbortSignal.timeout(3e4)]));
|
|
779
|
+
} catch {
|
|
780
|
+
result = { status: "uncertain" };
|
|
781
|
+
}
|
|
782
|
+
record.status = Object.hasOwn(checkinMessages, result?.status) ? result.status : "uncertain";
|
|
783
|
+
if (device && ["already-claimed", "no-campaign"].includes(record.status)) delete this.data.devices[device];
|
|
784
|
+
if (["claimed", "already-claimed"].includes(record.status) && Number.isFinite(result.amount) && result.amount >= 0) record.amount = result.amount;
|
|
785
|
+
if (record.status === "paused") this.data.pauses[account.provider] = { at: now };
|
|
786
|
+
await writePrivateJson(this.file, this.data);
|
|
787
|
+
if (record.status === "claimed") await this.manager.monitorAccount(account);
|
|
788
|
+
return true;
|
|
789
|
+
}, { waitMs: 1e3 });
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
630
793
|
// src/manager.mjs
|
|
631
794
|
var MANAGEMENT_PATH = "/.well-known/dsh-multi-router";
|
|
632
|
-
var revisionOf = (raw) =>
|
|
795
|
+
var revisionOf = (raw) => createHash2("sha256").update(JSON.stringify(raw)).digest("hex");
|
|
633
796
|
var ManagementError = class extends Error {
|
|
634
797
|
constructor(status, message) {
|
|
635
798
|
super(message);
|
|
@@ -679,6 +842,7 @@ var Manager = class {
|
|
|
679
842
|
this.loginInfo = /* @__PURE__ */ new Map();
|
|
680
843
|
this.telemetry = new Telemetry(join4(dirname3(file), "usage.json"));
|
|
681
844
|
this.plugins = new PluginUpdates(pluginAccess);
|
|
845
|
+
this.checkins = new Checkins(join4(dirname3(file), "checkins.json"), this);
|
|
682
846
|
this.setupFile = join4(dirname3(file), "initial-setup.json");
|
|
683
847
|
router.telemetry = this.telemetry;
|
|
684
848
|
router.monitor = (a) => this.monitorAccount(a);
|
|
@@ -686,15 +850,16 @@ var Manager = class {
|
|
|
686
850
|
router.freeModels = (account) => this.freeModels(account);
|
|
687
851
|
}
|
|
688
852
|
async load() {
|
|
689
|
-
this.raw = JSON.parse(await
|
|
853
|
+
this.raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
690
854
|
try {
|
|
691
|
-
this.setup = JSON.parse(await
|
|
855
|
+
this.setup = JSON.parse(await readFile3(this.setupFile, "utf8"));
|
|
692
856
|
} catch {
|
|
693
857
|
this.setup = void 0;
|
|
694
858
|
}
|
|
695
859
|
this.config = validateConfig(this.raw, this.file);
|
|
860
|
+
await this.checkins.load();
|
|
696
861
|
try {
|
|
697
|
-
const saved = JSON.parse(await
|
|
862
|
+
const saved = JSON.parse(await readFile3(this.catalogFile, "utf8"));
|
|
698
863
|
for (const account of this.config.accounts) {
|
|
699
864
|
const item = saved[account.id];
|
|
700
865
|
if (item?.credentialFile === account.credentialFile && Array.isArray(item.models)) {
|
|
@@ -734,9 +899,9 @@ var Manager = class {
|
|
|
734
899
|
...discovered.status.map((s) => ({ ...s, checkedAt: Date.now() }))
|
|
735
900
|
];
|
|
736
901
|
if (this.closed || !discovered.accounts.length) return;
|
|
737
|
-
await
|
|
902
|
+
await withFileLock3(this.file, async () => {
|
|
738
903
|
if (this.closed) return;
|
|
739
|
-
let raw = JSON.parse(await
|
|
904
|
+
let raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
740
905
|
let changed = false;
|
|
741
906
|
const saved = /* @__PURE__ */ new Map();
|
|
742
907
|
for (const item of discovered.accounts) {
|
|
@@ -755,7 +920,7 @@ var Manager = class {
|
|
|
755
920
|
} catch {
|
|
756
921
|
}
|
|
757
922
|
}
|
|
758
|
-
const id = existing?.id ?? `local_${
|
|
923
|
+
const id = existing?.id ?? `local_${createHash2("sha256").update(key).digest("hex").slice(0, 20)}`;
|
|
759
924
|
const recordSaved = () => {
|
|
760
925
|
if (!saved.has(item.provider)) saved.set(item.provider, /* @__PURE__ */ new Set());
|
|
761
926
|
saved.get(item.provider).add(id);
|
|
@@ -820,9 +985,9 @@ var Manager = class {
|
|
|
820
985
|
}
|
|
821
986
|
async finishInitialSetup() {
|
|
822
987
|
if (this.setup?.completedAt) return;
|
|
823
|
-
await
|
|
988
|
+
await withFileLock3(this.setupFile, async () => {
|
|
824
989
|
try {
|
|
825
|
-
this.setup = JSON.parse(await
|
|
990
|
+
this.setup = JSON.parse(await readFile3(this.setupFile, "utf8"));
|
|
826
991
|
} catch {
|
|
827
992
|
}
|
|
828
993
|
if (this.setup?.completedAt) return;
|
|
@@ -889,7 +1054,7 @@ var Manager = class {
|
|
|
889
1054
|
for (const account of this.config.accounts.filter((a) => a.enabled !== false)) {
|
|
890
1055
|
for (const model of this.freeModels(account)) {
|
|
891
1056
|
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;
|
|
892
|
-
const id = `free_${
|
|
1057
|
+
const id = `free_${createHash2("sha256").update(JSON.stringify([parentId, account.provider, model.id])).digest("hex").slice(0, 24)}`;
|
|
893
1058
|
if (!groups.has(id)) groups.set(id, { id, models: [], targets: [], accountNames: [], modelName: model.name, provider: account.provider, modelId: model.id });
|
|
894
1059
|
const group = groups.get(id);
|
|
895
1060
|
group.models.push({ model, provider: account.provider });
|
|
@@ -1004,7 +1169,7 @@ var Manager = class {
|
|
|
1004
1169
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id && a.credentialFile === account.credentialFile)) return;
|
|
1005
1170
|
this.catalogs.set(account.id, { credentialFile: account.credentialFile, fetchedAt: Date.now(), pricingVersion: 1, models });
|
|
1006
1171
|
this.catalogErrors.delete(account.id);
|
|
1007
|
-
await
|
|
1172
|
+
await withFileLock3(this.catalogFile, () => writePrivateJson(this.catalogFile, Object.fromEntries(this.catalogs)));
|
|
1008
1173
|
} catch (error) {
|
|
1009
1174
|
if (this.closed || !this.config.accounts.some((a) => a.id === account.id)) return;
|
|
1010
1175
|
this.catalogErrors.set(account.id, { status: Number(error.status ?? error.failure?.status) || 502, code: "catalog_fetch_failed", at: Date.now() });
|
|
@@ -1022,6 +1187,7 @@ var Manager = class {
|
|
|
1022
1187
|
localDiscovery: this.localDiscovery ?? [],
|
|
1023
1188
|
setup: this.setup,
|
|
1024
1189
|
pluginUpdates: this.plugins.state(),
|
|
1190
|
+
checkins: this.checkins.state(),
|
|
1025
1191
|
telemetry: this.telemetry.state(),
|
|
1026
1192
|
accounts: this.config.accounts.map((a) => ({
|
|
1027
1193
|
id: a.id,
|
|
@@ -1056,6 +1222,22 @@ var Manager = class {
|
|
|
1056
1222
|
}
|
|
1057
1223
|
async action(input) {
|
|
1058
1224
|
if (!input || typeof input !== "object" || Array.isArray(input)) bad("\u8BF7\u6C42\u683C\u5F0F\u65E0\u6548");
|
|
1225
|
+
if (input.action === "checkins.run") {
|
|
1226
|
+
try {
|
|
1227
|
+
this.checkins.start(input.ids);
|
|
1228
|
+
} catch (e) {
|
|
1229
|
+
bad(e.message);
|
|
1230
|
+
}
|
|
1231
|
+
return this.state();
|
|
1232
|
+
}
|
|
1233
|
+
if (input.action === "checkins.resume") {
|
|
1234
|
+
try {
|
|
1235
|
+
await this.checkins.resume(input.provider);
|
|
1236
|
+
} catch (e) {
|
|
1237
|
+
bad(e.message);
|
|
1238
|
+
}
|
|
1239
|
+
return this.state();
|
|
1240
|
+
}
|
|
1059
1241
|
if (typeof input.action === "string" && input.action.startsWith("plugins.")) {
|
|
1060
1242
|
try {
|
|
1061
1243
|
await this.plugins.action(input);
|
|
@@ -1065,14 +1247,14 @@ var Manager = class {
|
|
|
1065
1247
|
return this.state();
|
|
1066
1248
|
}
|
|
1067
1249
|
if (input.action === "setup.dismiss") {
|
|
1068
|
-
await
|
|
1250
|
+
await withFileLock3(this.setupFile, async () => {
|
|
1069
1251
|
this.setup = { ...this.setup, completedAt: this.setup?.completedAt ?? Date.now(), dismissed: true };
|
|
1070
1252
|
await writePrivateJson(this.setupFile, this.setup);
|
|
1071
1253
|
});
|
|
1072
1254
|
return this.state();
|
|
1073
1255
|
}
|
|
1074
1256
|
if (input.action === "account.export") {
|
|
1075
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1257
|
+
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");
|
|
1076
1258
|
const account = this.config.accounts.find((a) => a.id === input.id);
|
|
1077
1259
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1078
1260
|
let credential;
|
|
@@ -1094,7 +1276,7 @@ var Manager = class {
|
|
|
1094
1276
|
} };
|
|
1095
1277
|
}
|
|
1096
1278
|
if (input.action === "config.export") {
|
|
1097
|
-
if (input.revision !== revisionOf(JSON.parse(await
|
|
1279
|
+
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");
|
|
1098
1280
|
try {
|
|
1099
1281
|
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) } };
|
|
1100
1282
|
} catch (e) {
|
|
@@ -1141,8 +1323,8 @@ var Manager = class {
|
|
|
1141
1323
|
await this.checkFreeModels(true);
|
|
1142
1324
|
return this.state();
|
|
1143
1325
|
}
|
|
1144
|
-
return
|
|
1145
|
-
let raw = JSON.parse(await
|
|
1326
|
+
return withFileLock3(this.file, async () => {
|
|
1327
|
+
let raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
1146
1328
|
if (input.revision !== revisionOf(raw)) throw new ManagementError(409, "\u914D\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u540E\u518D\u4FDD\u5B58");
|
|
1147
1329
|
raw.routes ??= structuredClone(this.config.routes);
|
|
1148
1330
|
let staging;
|
|
@@ -1235,6 +1417,10 @@ var Manager = class {
|
|
|
1235
1417
|
if (!input.name && !replacing) added.name = imported.credential?.nickname || imported.credential?.accountName || input.id;
|
|
1236
1418
|
} else if (input.action === "account.update") {
|
|
1237
1419
|
if (!account) bad("\u8D26\u53F7\u4E0D\u5B58\u5728");
|
|
1420
|
+
if (input.autoCheckin !== void 0) {
|
|
1421
|
+
if (typeof input.autoCheckin !== "boolean" || !supportsCheckin(account.provider)) bad("\u6B64\u5E73\u53F0\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u7B7E\u5230");
|
|
1422
|
+
account.autoCheckin = input.autoCheckin;
|
|
1423
|
+
}
|
|
1238
1424
|
if (input.name !== void 0) account.name = input.name;
|
|
1239
1425
|
if (input.enabled !== void 0) account.enabled = input.enabled;
|
|
1240
1426
|
} else if (input.action === "account.move") {
|
|
@@ -1356,8 +1542,8 @@ var Manager = class {
|
|
|
1356
1542
|
if (filename && String(filename) !== this.file.split(/[\\/]/).pop()) return;
|
|
1357
1543
|
clearTimeout(timer);
|
|
1358
1544
|
timer = setTimeout(() => {
|
|
1359
|
-
void
|
|
1360
|
-
const raw = JSON.parse(await
|
|
1545
|
+
void withFileLock3(this.file, async () => {
|
|
1546
|
+
const raw = JSON.parse(await readFile3(this.file, "utf8"));
|
|
1361
1547
|
if (this.closed || revisionOf(raw) === this.revision) return;
|
|
1362
1548
|
const config = validateConfig(raw, this.file);
|
|
1363
1549
|
this.raw = raw;
|
|
@@ -1424,4 +1610,4 @@ export {
|
|
|
1424
1610
|
Manager,
|
|
1425
1611
|
managementHandler
|
|
1426
1612
|
};
|
|
1427
|
-
//# sourceMappingURL=chunk-
|
|
1613
|
+
//# sourceMappingURL=chunk-W5WP4ATM.js.map
|