dsh-multi-router 0.3.7 → 0.3.8
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 +71 -0
- package/README.md +16 -6
- package/lib/accounts.js +7 -3
- package/lib/{chunk-IEN3ZQFS.js → chunk-3VGKH7MF.js} +75 -6
- package/lib/chunk-3VGKH7MF.js.map +7 -0
- package/lib/{chunk-WNRHV24A.js → chunk-FTM7RHQV.js} +1 -6
- package/lib/{chunk-WNRHV24A.js.map → chunk-FTM7RHQV.js.map} +2 -2
- package/lib/{chunk-3VVQXFPV.js → chunk-GGRCX2NR.js} +2 -2
- package/lib/{chunk-VESY2B5O.js → chunk-UFS7ZJKY.js} +3 -3
- package/lib/{chunk-BIEDP6NW.js → chunk-WIDXMBQ2.js} +138 -36
- package/lib/chunk-WIDXMBQ2.js.map +7 -0
- package/lib/{chunk-HZVBEBOD.js → chunk-XMXFIV5G.js} +2 -1
- package/lib/{chunk-HZVBEBOD.js.map → chunk-XMXFIV5G.js.map} +2 -2
- package/lib/{chunk-PD2DTHG2.js → chunk-XY2UB22K.js} +3 -3
- package/lib/cli.js +5 -5
- package/lib/client.js +43 -12
- package/lib/client.js.map +3 -3
- package/lib/clients.js +4 -4
- package/lib/host.js +11 -9
- package/lib/index.js +11 -9
- package/lib/index.js.map +2 -2
- package/lib/manager.js +6 -6
- package/lib/router.js +2 -2
- package/lib/shim.js +3 -3
- package/package.json +7 -3
- package/lib/chunk-BIEDP6NW.js.map +0 -7
- package/lib/chunk-IEN3ZQFS.js.map +0 -7
- /package/lib/{chunk-3VVQXFPV.js.map → chunk-GGRCX2NR.js.map} +0 -0
- /package/lib/{chunk-VESY2B5O.js.map → chunk-UFS7ZJKY.js.map} +0 -0
- /package/lib/{chunk-PD2DTHG2.js.map → chunk-XY2UB22K.js.map} +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。版本号从 `0.1` 起以功能迭代为主,接口在 `1.0` 前可能变化。
|
|
4
|
+
|
|
5
|
+
## 0.3.8
|
|
6
|
+
|
|
7
|
+
- 恢复 WorkBuddy / Trae 本机登录自动获取:启动、每分钟检测,以及手动刷新均会扫描;沿用原插件的 WorkBuddy 多路径和加密格式读取器,Trae 自动识别 CN / Global / SOLO。
|
|
8
|
+
- 重复检测去重,保留账号顺序、名称、禁用状态及更长有效期的刷新副本;已移出账号不会被后台扫描重新加入。
|
|
9
|
+
- 账号页显示发现 / 未发现 / 无法读取的检测结果,并增加「重新检测本机登录」;添加账号默认自动识别 Trae 客户端版本。
|
|
10
|
+
- 面板检测前后台版本不一致时展示完整退出并重新打开的步骤,暂停配置写入,避免新版界面调用旧后台。
|
|
11
|
+
- 新增 GitHub Release 自动测试、打包、npm Trusted Publishing 发布流程,支持手动按已有标签重试。
|
|
12
|
+
|
|
13
|
+
## 0.3.7
|
|
14
|
+
|
|
15
|
+
- 首次启用自动创建私有配置和预制路由,修复新设备 router.json 不存在导致的启用失败;不覆盖已有或损坏配置。
|
|
16
|
+
- 改进后台不可用时的面板提示,文档首选 GitHub 链接安装与更新。
|
|
17
|
+
|
|
18
|
+
## 0.3.6
|
|
19
|
+
|
|
20
|
+
- **Free 动态免费模型重构**:免费模型按「渠道 + 精确模型 ID」自动生成独立路由(稳定内部 ID),DSH 模型列表分别展示「Free · 模型名 · 渠道」;同一模型的多个免费账号归为一个池,只在同模型账号间切换,绝不混用其他模型或回退付费模型。
|
|
21
|
+
- 免费目录每天按北京时间检查、启动补查、跨日检测;调用前强制刷新目录并逐候选复核零倍率与限免时间窗;恢复收费或检查失败自动移出,免费恢复后保留展示勾选与档位设置。
|
|
22
|
+
- 面板在「路由规则 → Free」集中管理免费模型:逐个勾选展示、设置默认推理等级、查看参与账号与检查状态。
|
|
23
|
+
- **推理模式修复**:预制路由的可用档位改为各目标声明能力的**并集**(此前误用交集导致档位丢失);用户选档后只向支持该档位的模型/账号发送,不静默降级;显式 Off、平台默认与并发各自选档在真实 DSH 适配器路径验证(每次请求独立的本机 shim 头传递,不发送到上游)。
|
|
24
|
+
|
|
25
|
+
## 0.3.5
|
|
26
|
+
|
|
27
|
+
- 新增「Free · 免费模型自动路由」:默认覆盖三家六个区域,按账号真实目录筛选零倍率模型;判断不依赖赠送积分、免费标签、套餐或占位成本。
|
|
28
|
+
- 免费路由在预配置、路由列表与统一路由模型列表中置顶;收费 / 未知费率 / 过期限免 / 目录失败的目标不参与;无免费池返回明确不可用。
|
|
29
|
+
- 配置导出 / 导入保留 `freeOnly`;动态目录在新设备重新发现。
|
|
30
|
+
|
|
31
|
+
## 0.3.4
|
|
32
|
+
|
|
33
|
+
- 新增「Token 明细」页签:逐次上游调用的时间、渠道 / 账号、路由 / 实际模型、输入 / 输出 / 总 Token 与结果,支持筛选与翻页;缓存输入、推理输出分项展示不重复相加。
|
|
34
|
+
- 明细默认滚动保留 3 × 24 小时,可设 1–365 天;取消 80 条硬截断;缩短期限立即删除超期明细,累计统计与轮换基线不受影响。
|
|
35
|
+
- 路由编辑简化:只填名称,内部 ID 自动生成并保持会话引用兼容;从预配置移除 GLM-5.2。
|
|
36
|
+
|
|
37
|
+
## 0.3.3
|
|
38
|
+
|
|
39
|
+
- 共有模型预配置扩展为八种(DeepSeek V4.1 Flash / V4 Pro、GLM-5.3 / 5.3-Flash / 5.2、Kimi-K3 / K2.8-Preview、MiniMax-M2.7),精确绑定各渠道实际模型 ID;支持单条添加或一键补齐,不覆盖已有路由。
|
|
40
|
+
- 模型管理新增「全部展示 / 全部隐藏(仅路由)」渠道级开关,作用于现有与后续发现的模型;单个模型可覆盖渠道默认。
|
|
41
|
+
|
|
42
|
+
## 0.3.2
|
|
43
|
+
|
|
44
|
+
- 单账号凭证「复制 / 导出 JSON」:含供应商 / 区域 / 名称 / Token / PAT / 有效期 / Trae 设备身份,不含本机路径、其他账号与私钥;新设备粘贴或选择文件即可导入。
|
|
45
|
+
- 凭证提取走带管理密钥的本机 POST,响应禁止缓存。
|
|
46
|
+
|
|
47
|
+
## 0.3.1
|
|
48
|
+
|
|
49
|
+
- 临期积分优先默认开启(范围默认 7 天);全部临期账号完成本轮后暂停 72 小时,重启保留,支持「强制开启并开始新一轮」。
|
|
50
|
+
- 新增「配置迁移」页签:导出 / 导入配置,可选 scrypt + AES-256-GCM 密码加密迁移登录凭证;导入提供预览、合并 / 替换与自动备份,失败不改变原配置。
|
|
51
|
+
- 账号添加支持一键读取本机官方客户端登录、手动字段、凭证 JSON 与 Qoder PAT;详情可补充 / 更新凭证。
|
|
52
|
+
|
|
53
|
+
## 0.3.0
|
|
54
|
+
|
|
55
|
+
- 集中管理面板六页签上线:总览与用量、账号与登录、模型管理、路由规则等;平台余额每分钟查询、面板 15 秒刷新。
|
|
56
|
+
- 权重(`weighted`)、Token 额度(`tokens`)、积分额度(`credits`)三种轮换策略;额度只决定下一次请求选哪个账号,不截断正在生成的回答。
|
|
57
|
+
- 用量统计 `usage.json`(0600):计数、平台额度与保留期限内的调用明细,不保存提示词、回答或凭证。
|
|
58
|
+
|
|
59
|
+
## 0.2.x
|
|
60
|
+
|
|
61
|
+
- 六个内置渠道(workbuddy / workbuddy-ai / trae / trae-global / qoder / qoder-global)+ `multi-router` 统一路由;三家认证、模型发现与聊天传输内置打包,运行不再依赖原三个独立连接器。
|
|
62
|
+
- 4xx 自动切换与冷却(401 / 402 / 429 冷却账号全部路由,其他 4xx 冷却当前路由);5xx / 网络错误默认结束,可开启 `retryServerErrors`;开始输出后不重放,取消不换号。
|
|
63
|
+
- 每账号独立凭证 / 刷新副本 / Trae 设备身份 / Qoder transport 缓存;无效配置保留最后一份有效运行配置,写入冲突返回 409。
|
|
64
|
+
|
|
65
|
+
## 0.1.x
|
|
66
|
+
|
|
67
|
+
- 初版:多账号池、failover 路由、loopback shim(随机端口 + 随机密钥)、CLI 账号管理、离线测试框架。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
> 注:各版本在本机的实际安装与真实账号验证记录属于作者环境,不随仓库分发;公开仓库的验证以 `npm test`(含自动登录获取回归)为准。
|
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.3.
|
|
9
|
+
版本:0.3.8。适配 DSH Desktop / Web 0.2.0-rc.2,Node.js ^22.19.0 或 >=24。
|
|
10
10
|
|
|
11
11
|
## 在 DSH 里管理
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
- **总览与用量**:今日/累计请求与 Token、近 7 日趋势、最近请求、三家平台余额与刷新时间。平台每分钟查询,面板每 15 秒刷新,可暂停面板自动刷新。
|
|
16
16
|
- **Token 明细**:每次上游调用的时间、渠道、账号、路由、实际模型、输入/输出/总 Token 和结果;按账号、路由、结果筛选,每页 50 条。缓存输入和推理输出在平台返回时展示,不重复计入总量。默认滚动保留最近 3 × 24 小时,可设置 1–365 天,并随配置迁移。旧记录缺少的分项显示「—」。
|
|
17
|
-
-
|
|
17
|
+
- **账号与登录**:启动时自动读取本机 WorkBuddy / Trae 已登录账号,每分钟重新检测;Trae 自动识别 CN / Global / SOLO,面板展示检测状态,并提供「重新检测本机登录」。添加账号弹窗也可一键获取;账号 ID 自动生成、显示名自动识别,重复账号会提示。Qoder 使用 PAT,可粘贴或获取本机旧连接器已保存的 PAT。新账号自动获取模型与平台用量。详情弹窗查看身份、认证类型、有效期、刷新能力、套餐与积分包;账号可命名、启停、排序和移出。
|
|
18
18
|
- **模型管理**:原始模型默认隐藏,只展示统一路由;提供所有渠道「全部展示」「全部隐藏(仅路由)」,作用于现有和以后发现的模型。可搜索、获取真实模型目录,修改显示名与单模型展示开关,每家独立保存。
|
|
19
19
|
- **路由规则**:Free 免费路由排在首位,显示各账号当前免费模型与目录检查时间,支持立即检查。列表显示路由名称、切换策略和目标;新建/编辑打开二级弹窗。「添加模型预配置」可逐个添加或补齐全部三家预配置,不覆盖已有同 ID 路由。模型可从下拉列表选择,候选 ID 和上下文长度放在高级选项中。
|
|
20
20
|
|
|
@@ -109,14 +109,16 @@ dsh plugin --profile desktop add github:AndroidSix/dsh-multi-router
|
|
|
109
109
|
dsh plugin --profile web add github:AndroidSix/dsh-multi-router
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
默认获取 main 分支;仓库已包含编译好的 `lib/`,安装端无需 clone、构建或运行初始化命令,也没有安装时构建脚本。首次启用自动在当前设备的 DSH
|
|
112
|
+
默认获取 main 分支;仓库已包含编译好的 `lib/`,安装端无需 clone、构建或运行初始化命令,也没有安装时构建脚本。首次启用自动在当前设备的 DSH 数据目录创建配置及预制路由,并读取本机已登录的 WorkBuddy / Trae 账号,无需先手动添加。Qoder 使用 PAT,或读取旧连接器保存的 PAT。凭证只保存独立副本;再次检测不会重复添加,也不会启用已停用账号。移出的本机账号不会被定时扫描重新加入,可点击「重新检测本机登录」主动恢复。已有配置不会被覆盖;损坏的配置也不会自动重置。安装 / 更新 / 卸载后需**完全退出并重启**对应的 DSH 进程生效。
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
已安装用户更新(也适用于旧版从本地 `.tgz` 安装的用户):
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
|
-
dsh plugin --profile desktop
|
|
117
|
+
dsh plugin --profile desktop add github:AndroidSix/dsh-multi-router
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
这会将同名插件的安装源切换为 GitHub 并获取当前版本,账号、路由和统计保存在包目录之外,不会随包替换。旧的 `file:...tgz` 依赖绑定一个固定本地文件,普通更新不会自动改为 GitHub 来源。已经使用 GitHub 来源时,也可以执行 `dsh plugin --profile desktop update dsh-multi-router`。安装后的运行进程仍需完全退出并重启;「刷新面板」只刷新账号和用量,不会更新插件代码。GitHub 提交、GitHub Releases 和 npm 版本是不同发布来源,第三方商店是否提示更新取决于其检测方式。
|
|
121
|
+
|
|
120
122
|
Desktop 若提示 CLI 仅安装依赖、未自动启用 bundle:完全退出 Desktop 后,在 desktop profile 的 `package.json` 的 `dsh.profile.bundles` 数组里追加 `"dsh-multi-router"`(保留其他 bundle),再启动并打开配置页。
|
|
121
123
|
|
|
122
124
|
> 首次安装请先在 DSH 中**关闭**原 WorkBuddy / Trae / Qoder 三个独立连接器(仅关闭,不必卸载,不影响其凭证),避免同名 provider 冲突。
|
|
@@ -129,7 +131,7 @@ cd dsh-multi-router
|
|
|
129
131
|
npm ci
|
|
130
132
|
npm run check
|
|
131
133
|
npm run pack:release
|
|
132
|
-
dsh plugin --profile desktop add ./dsh-multi-router-0.3.
|
|
134
|
+
dsh plugin --profile desktop add ./dsh-multi-router-0.3.8.tgz
|
|
133
135
|
```
|
|
134
136
|
|
|
135
137
|
### 配置账号
|
|
@@ -248,3 +250,11 @@ npm pack --cache /private/tmp/dsh-multi-router-npm-cache
|
|
|
248
250
|
|
|
249
251
|
- 代码许可:[MIT](./LICENSE)
|
|
250
252
|
- **使用条款与免责:见 [DISCLAIMER.md](./DISCLAIMER.md)——仅供个人学习研究,与官方无关,按「现状」提供,使用者自担风险。**
|
|
253
|
+
|
|
254
|
+
## 自动发版到 npm
|
|
255
|
+
|
|
256
|
+
仓库的 `.github/workflows/publish.yml` 在 GitHub Release 发布时运行:校验 `v版本号` 与 `package.json` / `package-lock.json` 一致 → 安装依赖 → 构建并运行全部回归 → 打包并附加到 GitHub Release → 通过 npm Trusted Publishing 发布。普通提交不触发发布;草稿 Release 也不发布。稳定版本使用 npm `latest`,预发布版本使用 `next`。失败后修复原因,再在 Actions 手动输入已有 tag 重试;已经成功发布的 npm 版本不能重复发布。
|
|
257
|
+
|
|
258
|
+
日常更新:修改版本号和更新日志,运行 `npm run check`,提交源码与编译产物,推送 main,然后创建对应 `v0.x.y` 标签的 GitHub Release。CI 的打包白名单不包含本机账号、配置、统计、npm 登录文件。
|
|
259
|
+
|
|
260
|
+
首次发布需 npm 账号完成双重验证。包的 npm Settings → Trusted Publisher 使用 GitHub Actions,填写组织 / 用户 `AndroidSix`、仓库 `dsh-multi-router`、工作流文件 `publish.yml`,环境名称留空,并允许 `npm publish`。不需要添加 `NPM_TOKEN` GitHub Secret。
|
package/lib/accounts.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
discoverLocalAccounts,
|
|
2
3
|
importAccount,
|
|
4
|
+
localAccountKey,
|
|
3
5
|
readPortableCredential
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-3VGKH7MF.js";
|
|
7
|
+
import "./chunk-FTM7RHQV.js";
|
|
6
8
|
import "./chunk-S5KMFAQM.js";
|
|
7
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-XMXFIV5G.js";
|
|
8
10
|
import "./chunk-TEZ2FMEO.js";
|
|
9
11
|
export {
|
|
12
|
+
discoverLocalAccounts,
|
|
10
13
|
importAccount,
|
|
14
|
+
localAccountKey,
|
|
11
15
|
readPortableCredential
|
|
12
16
|
};
|
|
13
17
|
//# sourceMappingURL=accounts.js.map
|
|
@@ -3,15 +3,82 @@ import {
|
|
|
3
3
|
WORKBUDDY_VARIANTS,
|
|
4
4
|
WorkBuddyCredentialStore,
|
|
5
5
|
atRestKeyProviderFor,
|
|
6
|
-
readTraeIdentity
|
|
7
|
-
|
|
6
|
+
readTraeIdentity,
|
|
7
|
+
traeStorageCandidates
|
|
8
|
+
} from "./chunk-FTM7RHQV.js";
|
|
9
|
+
import {
|
|
10
|
+
regionOfCredential
|
|
11
|
+
} from "./chunk-S5KMFAQM.js";
|
|
8
12
|
import {
|
|
9
13
|
PROVIDERS,
|
|
10
14
|
writePrivateJson
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XMXFIV5G.js";
|
|
12
16
|
|
|
13
17
|
// src/accounts.mjs
|
|
14
18
|
import { readFile } from "node:fs/promises";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { randomBytes } from "node:crypto";
|
|
22
|
+
var localKeyProviders = /* @__PURE__ */ new Map();
|
|
23
|
+
var localAccountKey = (provider, document) => JSON.stringify([
|
|
24
|
+
provider,
|
|
25
|
+
document.credential?.uid || document.credential?.userId,
|
|
26
|
+
document.credential?.enterpriseId || "",
|
|
27
|
+
document.credential?.edition || ""
|
|
28
|
+
]);
|
|
29
|
+
async function discoverLocalAccounts(provider) {
|
|
30
|
+
const accounts = [], status = [];
|
|
31
|
+
for (const variant of WORKBUDDY_VARIANTS.filter((v) => !provider || v.id === provider)) {
|
|
32
|
+
try {
|
|
33
|
+
if (!localKeyProviders.has(variant.id)) localKeyProviders.set(variant.id, atRestKeyProviderFor(variant));
|
|
34
|
+
const store = new WorkBuddyCredentialStore({
|
|
35
|
+
variant,
|
|
36
|
+
ownPath: join(tmpdir(), `dsh-router-scan-${randomBytes(16).toString("hex")}.json`),
|
|
37
|
+
keyProvider: localKeyProviders.get(variant.id),
|
|
38
|
+
refresh: async () => {
|
|
39
|
+
throw new Error("Discovery cannot refresh");
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const credential = await store.current();
|
|
43
|
+
if (credential) accounts.push({ provider: variant.id, document: { version: 1, credential } });
|
|
44
|
+
status.push({ provider: variant.id, state: credential ? "found" : "not-found" });
|
|
45
|
+
} catch (e) {
|
|
46
|
+
status.push({ provider: variant.id, state: "unreadable", code: e.code || "credential_unreadable" });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const id of ["trae", "trae-global"].filter((id2) => !provider || id2 === provider)) {
|
|
50
|
+
let found = false, unreadable = false;
|
|
51
|
+
for (const candidate of traeStorageCandidates().filter((c) => c.source === "desktop" && (id === "trae" ? ["cn", "solo"] : ["sg", "solo-sg"]).includes(c.edition))) {
|
|
52
|
+
try {
|
|
53
|
+
await readFile(candidate.path, "utf8");
|
|
54
|
+
const store = new TraeCredentialStore({
|
|
55
|
+
storagePath: candidate.path,
|
|
56
|
+
edition: candidate.edition,
|
|
57
|
+
region: id === "trae" ? "cn" : "ai",
|
|
58
|
+
ownPath: `${candidate.path}.dsh-router-no-owned-credential`,
|
|
59
|
+
legacyOwnPath: `${candidate.path}.dsh-router-no-legacy`,
|
|
60
|
+
refresh: async () => {
|
|
61
|
+
throw new Error("Discovery cannot refresh");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const credential = await store.current();
|
|
65
|
+
if (!credential) {
|
|
66
|
+
const diagnostic = await store.diagnose();
|
|
67
|
+
unreadable ||= diagnostic.failures.some((f) => f.reason !== "missing");
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (regionOfCredential(credential) === "ai" !== (id === "trae-global")) continue;
|
|
71
|
+
const identityDocument = await readTraeIdentity(candidate);
|
|
72
|
+
accounts.push({ provider: id, document: { version: 1, credential }, identityDocument });
|
|
73
|
+
found = true;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (e.code !== "ENOENT") unreadable = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
status.push({ provider: id, state: found ? "found" : unreadable ? "unreadable" : "not-found" });
|
|
79
|
+
}
|
|
80
|
+
return { accounts, status };
|
|
81
|
+
}
|
|
15
82
|
var pick = (value, keys) => Object.fromEntries(keys.filter((k) => Object.hasOwn(value ?? {}, k)).map((k) => [k, value[k]]));
|
|
16
83
|
async function readPortableCredential(account) {
|
|
17
84
|
const raw = JSON.parse(await readFile(account.credentialFile, "utf8"));
|
|
@@ -72,9 +139,9 @@ async function importAccount({ provider, source, credentialFile, identityFile, e
|
|
|
72
139
|
credential = await store.current();
|
|
73
140
|
}
|
|
74
141
|
if (!text(credential?.accessToken) || !text(credential.userId) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || !["cn", "sg", "solo", "solo-sg"].includes(credential.edition)) throw new Error("No valid Trae credential in source");
|
|
75
|
-
const { regionOfCredential, regionOfHost } = await import("./region-DO3KAV7V.js");
|
|
142
|
+
const { regionOfCredential: regionOfCredential2, regionOfHost } = await import("./region-DO3KAV7V.js");
|
|
76
143
|
if (credential.host && !regionOfHost(credential.host)) throw new Error("Invalid Trae credential host");
|
|
77
|
-
if (
|
|
144
|
+
if (regionOfCredential2(credential) === "ai" !== (provider === "trae-global")) throw new Error("Trae credential region does not match provider");
|
|
78
145
|
const identity = identityDocument ?? document?.identity ?? (identitySource ? JSON.parse(await readFile(identitySource, "utf8")) : source ? await readTraeIdentity({ edition: credential.edition, path: source, source: "desktop" }) : void 0);
|
|
79
146
|
if (!text(identity?.machineId) || !text(identity.deviceId) || identity.edition !== credential.edition) throw new Error("Identity must belong to this Trae edition");
|
|
80
147
|
await writePrivateJson(identityFile, identity);
|
|
@@ -89,7 +156,9 @@ async function importAccount({ provider, source, credentialFile, identityFile, e
|
|
|
89
156
|
}
|
|
90
157
|
|
|
91
158
|
export {
|
|
159
|
+
localAccountKey,
|
|
160
|
+
discoverLocalAccounts,
|
|
92
161
|
readPortableCredential,
|
|
93
162
|
importAccount
|
|
94
163
|
};
|
|
95
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-3VGKH7MF.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/accounts.mjs"],
|
|
4
|
+
"sourcesContent": ["import { readFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { randomBytes } from 'node:crypto';\nimport { WorkBuddyCredentialStore } from '../upstream/workbuddy/src/auth.ts';\nimport { WORKBUDDY_VARIANTS } from '../upstream/workbuddy/src/variants.ts';\nimport { atRestKeyProviderFor } from '../upstream/workbuddy/src/desktop-credential-protection.ts';\nimport { TraeCredentialStore } from '../upstream/trae/src/auth.ts';\nimport { readTraeIdentity } from '../upstream/trae/src/identity.ts';\nimport { PROVIDERS, writePrivateJson } from './config.mjs';\nimport { traeStorageCandidates } from '../upstream/trae/src/paths.ts';\nimport { regionOfCredential } from '../upstream/trae/src/region.ts';\n\nconst localKeyProviders = new Map();\nexport const localAccountKey = (provider, document) => JSON.stringify([provider,\n document.credential?.uid || document.credential?.userId,\n document.credential?.enterpriseId || '', document.credential?.edition || '']);\n\n// Reuse the upstream readers, including WorkBuddy's environment overrides,\n// candidate fallback and encrypted desktop format. Never refresh/write upstream files.\nexport async function discoverLocalAccounts(provider) {\n const accounts = [], status = [];\n for (const variant of WORKBUDDY_VARIANTS.filter(v => !provider || v.id === provider)) {\n try {\n if (!localKeyProviders.has(variant.id)) localKeyProviders.set(variant.id, atRestKeyProviderFor(variant));\n const store = new WorkBuddyCredentialStore({ variant, ownPath: join(tmpdir(), `dsh-router-scan-${randomBytes(16).toString('hex')}.json`),\n keyProvider: localKeyProviders.get(variant.id), refresh: async () => { throw new Error('Discovery cannot refresh'); } });\n const credential = await store.current();\n if (credential) accounts.push({ provider: variant.id, document: { version: 1, credential } });\n status.push({ provider: variant.id, state: credential ? 'found' : 'not-found' });\n } catch (e) { status.push({ provider: variant.id, state: 'unreadable', code: e.code || 'credential_unreadable' }); }\n }\n for (const id of ['trae', 'trae-global'].filter(id => !provider || id === provider)) {\n let found = false, unreadable = false;\n for (const candidate of traeStorageCandidates().filter(c => c.source === 'desktop'\n && (id === 'trae' ? ['cn', 'solo'] : ['sg', 'solo-sg']).includes(c.edition))) {\n try {\n await readFile(candidate.path, 'utf8');\n const store = new TraeCredentialStore({ storagePath: candidate.path, edition: candidate.edition,\n region: id === 'trae' ? 'cn' : 'ai', ownPath: `${candidate.path}.dsh-router-no-owned-credential`,\n legacyOwnPath: `${candidate.path}.dsh-router-no-legacy`, refresh: async () => { throw new Error('Discovery cannot refresh'); } });\n const credential = await store.current();\n if (!credential) { const diagnostic = await store.diagnose(); unreadable ||= diagnostic.failures.some(f => f.reason !== 'missing'); continue; }\n if ((regionOfCredential(credential) === 'ai') !== (id === 'trae-global')) continue;\n const identityDocument = await readTraeIdentity(candidate);\n accounts.push({ provider: id, document: { version: 1, credential }, identityDocument }); found = true;\n } catch (e) { if (e.code !== 'ENOENT') unreadable = true; }\n }\n status.push({ provider: id, state: found ? 'found' : unreadable ? 'unreadable' : 'not-found' });\n }\n return { accounts, status };\n}\n\nconst pick = (value, keys) => Object.fromEntries(keys.filter(k => Object.hasOwn(value ?? {}, k)).map(k => [k, value[k]]));\n// Share the same portable fields with full encrypted configuration migration.\nexport async function readPortableCredential(account) {\n const raw = JSON.parse(await readFile(account.credentialFile, 'utf8'));\n if (account.provider.startsWith('qoder')) return raw.pat ? { document: pick(raw, ['version', 'pat', 'region']) } : null;\n if (!raw.credential?.accessToken) return null;\n const document = { version: 1, credential: pick(raw.credential, ['accessToken', 'refreshToken', 'expiresAtMs', 'refreshExpiresAtMs', 'uid', 'nickname', 'enterpriseId', 'domain', 'userId', 'accountName', 'edition', 'userRegion', 'host']) };\n const identity = account.provider.startsWith('trae') ? pick(JSON.parse(await readFile(account.identityFile, 'utf8')), ['machineId', 'deviceId', 'edition']) : undefined;\n if (identity && (!identity.machineId || !identity.deviceId || identity.edition !== raw.credential.edition)) throw new Error('Trae identity is incomplete');\n return { document, identity };\n}\n\n// Imports never refresh tokens or write to the provider's own files.\nexport async function importAccount({ provider, source, credentialFile, identityFile, edition, identitySource, document, identityDocument }) {\n if (!PROVIDERS.includes(provider)) throw new Error('Unknown provider');\n const inline = document !== undefined;\n const raw = inline ? JSON.stringify(document) : await readFile(source, 'utf8');\n if (!inline) try { document = JSON.parse(raw); } catch { /* A Qoder PAT can be a plain text file. */ }\n if (document?.format === 'dsh-multi-router-account' && document.provider !== provider) throw new Error('Portable credential provider does not match selected provider');\n const text = value => typeof value === 'string' && value.trim().length > 0 && value.length <= 32768;\n if (provider.startsWith('workbuddy')) {\n let credential;\n if (document?.version === 1 && document.credential) credential = document.credential;\n else if (inline) credential = document;\n else {\n const variant = WORKBUDDY_VARIANTS.find(v => v.id === provider);\n const store = new WorkBuddyCredentialStore({ desktopPath: source, ownPath: `${credentialFile}.import-empty`,\n variant, keyProvider: atRestKeyProviderFor(variant), refresh: async () => { throw new Error('Import cannot refresh'); } });\n credential = await store.current();\n }\n if (!text(credential?.accessToken) || !text(credential.uid) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || typeof credential.domain !== 'string' || /[\\r\\n]/.test(credential.domain)) throw new Error('No valid WorkBuddy credential in source');\n // Use the upstream region resolver rather than guessing regional domains.\n const { regionOf } = await import('../upstream/workbuddy/src/upstream.ts');\n if ((regionOf(credential.domain) === 'global') !== (provider === 'workbuddy-ai')) throw new Error('WorkBuddy credential region does not match provider');\n await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: 'dsh' } });\n } else if (provider.startsWith('trae')) {\n let credential;\n if (document?.version === 1 && document.credential) credential = document.credential;\n else if (inline) credential = document;\n else {\n if (!['cn', 'sg', 'solo', 'solo-sg'].includes(edition)) throw new Error('Trae desktop import needs --edition cn|sg|solo|solo-sg');\n const store = new TraeCredentialStore({ storagePath: source, edition, ownPath: `${credentialFile}.import-empty`,\n legacyOwnPath: `${credentialFile}.no-legacy`, region: provider === 'trae' ? 'cn' : 'ai',\n refresh: async () => { throw new Error('Import cannot refresh'); } });\n credential = await store.current();\n }\n if (!text(credential?.accessToken) || !text(credential.userId) || !Number.isFinite(credential.expiresAtMs) || credential.expiresAtMs <= 0 || !['cn', 'sg', 'solo', 'solo-sg'].includes(credential.edition)) throw new Error('No valid Trae credential in source');\n const { regionOfCredential, regionOfHost } = await import('../upstream/trae/src/region.ts');\n if (credential.host && !regionOfHost(credential.host)) throw new Error('Invalid Trae credential host');\n if ((regionOfCredential(credential) === 'ai') !== (provider === 'trae-global')) throw new Error('Trae credential region does not match provider');\n const identity = identityDocument ?? document?.identity ?? (identitySource ? JSON.parse(await readFile(identitySource, 'utf8'))\n : source ? await readTraeIdentity({ edition: credential.edition, path: source, source: 'desktop' }) : undefined);\n if (!text(identity?.machineId) || !text(identity.deviceId) || identity.edition !== credential.edition) throw new Error('Identity must belong to this Trae edition');\n await writePrivateJson(identityFile, identity);\n await writePrivateJson(credentialFile, { version: 1, credential: { ...credential, source: 'dsh' } });\n } else {\n const region = provider === 'qoder' ? 'china' : 'global';\n const pat = typeof document?.pat === 'string' ? document.pat.trim() : raw.trim();\n if (!pat || (document && typeof document.pat !== 'string') || /\\s/.test(pat)) throw new Error('Source must be a Qoder PAT text file or credential JSON');\n if (document?.region && document.region !== region) throw new Error('Qoder PAT region does not match provider');\n await writePrivateJson(credentialFile, { version: 2, pat, region, savedAt: new Date().toISOString() });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["id", "regionOfCredential"]
|
|
7
|
+
}
|
|
@@ -978,10 +978,6 @@ function dedupeCandidates(candidates) {
|
|
|
978
978
|
function desktopAuthCandidatesFor(variant) {
|
|
979
979
|
return dedupeCandidates(defaultDesktopAuthCandidates().map((path) => join2(dirname2(path), variant.desktopFilename)));
|
|
980
980
|
}
|
|
981
|
-
function defaultDesktopAuthPath(variant) {
|
|
982
|
-
const candidates = variant === void 0 ? defaultDesktopAuthCandidates() : desktopAuthCandidatesFor(variant);
|
|
983
|
-
return candidates[0];
|
|
984
|
-
}
|
|
985
981
|
function expiryToMs(value) {
|
|
986
982
|
if (value <= 0) return 0;
|
|
987
983
|
return value > 1e12 ? value : value * 1e3;
|
|
@@ -2011,7 +2007,6 @@ function identityHeaders(identity) {
|
|
|
2011
2007
|
export {
|
|
2012
2008
|
WORKBUDDY_VARIANTS,
|
|
2013
2009
|
atRestKeyProviderFor,
|
|
2014
|
-
defaultDesktopAuthPath,
|
|
2015
2010
|
WorkBuddyCredentialStore,
|
|
2016
2011
|
traeStorageCandidates,
|
|
2017
2012
|
traeAccountId,
|
|
@@ -2019,4 +2014,4 @@ export {
|
|
|
2019
2014
|
readTraeIdentity,
|
|
2020
2015
|
identityHeaders
|
|
2021
2016
|
};
|
|
2022
|
-
//# sourceMappingURL=chunk-
|
|
2017
|
+
//# sourceMappingURL=chunk-FTM7RHQV.js.map
|