kdczyz-dsh-model 1.2.2-beta → 1.2.4-beta
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.
|
@@ -204,8 +204,7 @@ props(`opts.inject()` 求值一次,`settingsScope` 是同一对象引用)
|
|
|
204
204
|
`balanced` 与随机权重)、「参与轮换」开关、冷却机制(`cooldownMs` /
|
|
205
205
|
`exhaustCooldownMs` / 冷却重置路由 `/cooldowns/reset`、冷却展示与
|
|
206
206
|
「清除冷却」按钮)、保留额度(`creditReserves` / `/credit-reserve` 路由);
|
|
207
|
-
-
|
|
208
|
-
签到领取。WorkBuddy 额外保留「取消固定」(取消后 = 未选,请求失败直到手动切换);
|
|
207
|
+
- **保留**(手动管理):移出池子 / 恢复、切换到此账号、签到领取。WorkBuddy 额外保留「取消固定」(取消后 = 未选,请求失败直到手动切换);
|
|
209
208
|
Trae / Qoder 选中即服务,没有「清空选择」状态。
|
|
210
209
|
|
|
211
210
|
管理页(`console.mjs`)与号池实现(`vendor/xdpool/lib/index.js`、`lib/trae.js`、
|
|
@@ -286,15 +285,15 @@ trae 页改为本包自写(`console.mjs` 的 `TraeConfigTab`),与 workbudd
|
|
|
286
285
|
那个区域里正在服务的那一个账号**(WorkBuddy 用 `activeAccountId`、Trae 用
|
|
287
286
|
`lastServedId` 退回「当前账号」、Qoder 用 `serving.accountId`),与卡片是两个口径。
|
|
288
287
|
|
|
289
|
-
|
|
288
|
+
每一行只有一个控件(原先还有一个「停用 / 启用」开关,已移除 —— 完全手动语义下
|
|
289
|
+
它只对「已选中的那个」有效,停用别的账号对请求毫无影响;账号管理只剩「移出池子」):
|
|
290
290
|
|
|
291
291
|
| 控件 | 干什么 | 打到哪 |
|
|
292
292
|
|---|---|---|
|
|
293
|
-
| **「参与轮换」开关** | 把这个账号移出 / 放回轮换 | WorkBuddy:`POST /plugins/dsh-workbuddy-xdpool/accounts/disabled`(服务端合并);Trae / Qoder:设置 `regions` / `disabledAccountIds`,**读-改-写合并** |
|
|
294
293
|
| **「切换到此账号」** | 下一次请求优先用它 | WorkBuddy:`POST …/accounts/select?region=cn\|global`;Trae:`POST /pool/account?region=` **且必须补一次 `accounts` 设置写**;Qoder:写 `selectedAccountId` |
|
|
295
294
|
|
|
296
295
|
已经是「当前账号」的那一行:WorkBuddy 把按钮换成「**取消固定**」(`{accountId:null}`,
|
|
297
|
-
|
|
296
|
+
交回手动状态(未选 → 请求失败));Trae / Qoder **不给按钮** —— 这两家的池永远有一个当前账号,
|
|
298
297
|
「切换到这个已经是当前的账号」是纯 no-op(两个配置页也是这么做的)。
|
|
299
298
|
|
|
300
299
|
### 四个「做反了也不会报错」的坑
|
|
@@ -303,7 +302,7 @@ trae 页改为本包自写(`console.mjs` 的 `TraeConfigTab`),与 workbudd
|
|
|
303
302
|
|---|---|---|
|
|
304
303
|
| Trae 的 `credits.total` 是**发放总量**,不是余额 | 真机上把 3,050(发放)当成余额显示,而账号只剩 796.88 | `accountCreditsOf(row, family)` 按平台取字段:Trae 取 `generalAvailable → available → total`,WorkBuddy / Qoder 取 `total → …`(Qoder 就是裸数字) |
|
|
305
304
|
| WorkBuddy 的 `disabledAccountIds` 是**两个区域共用的一份扁平数组**(两个 provider 注册同一个 `settingsNs` + `settingsPath: []`) | 配置页只看得见本区域的账号,照本区域拼一份完整名单写回设置,会把**另一个区域**已停用的账号静默重新启用 | 启停只走 `/accounts/disabled` 路由(服务端对 `current().disabledAccountIds` 合并);单测里除了断言「请求发出去了」,还断言**没有**顺手写那份设置 —— 只断言前者的话,「路由 + 顺手再写一次」也能全绿 |
|
|
306
|
-
| 卡片开着的时候轮询额度 | 上游按账号计费(Trae 的 `/usage` 是一次实时计费读),卡片开着不动也在持续花钱 | 只在**打开卡片 / 手动刷新 /
|
|
305
|
+
| 卡片开着的时候轮询额度 | 上游按账号计费(Trae 的 `/usage` 是一次实时计费读),卡片开着不动也在持续花钱 | 只在**打开卡片 / 手动刷新 / 写完一次切换之后**取一次;`CREDIT_REFRESH_MS = 30000` 只作用于胶囊那颗数字。单测里钉着「展开卡片不新增任何 interval」 |
|
|
307
306
|
| 行里的名字直接用上游昵称 | Qoder / Trae 的 label 是 uuid,卡片被撑破 | `shortAccountName()` 截到 8 个字符 + `…`,完整值放 `title` |
|
|
308
307
|
|
|
309
308
|
### 浮层为什么是 portal + fixed
|
|
@@ -337,7 +336,7 @@ node scripts/gen-xdpool-client.mjs # 改 vendor/xdpool/lib/client.js 后
|
|
|
337
336
|
```sh
|
|
338
337
|
node test/credit-store.test.mjs # 14 项:积分/历史记录读写、去重、坏行容错
|
|
339
338
|
node test/client-apply.test.mjs # 43 项:仿浏览器执行 client.js 并真正调用 apply
|
|
340
|
-
node test/console-render.test.mjs #
|
|
339
|
+
node test/console-render.test.mjs # 402 项:真正渲染三个 tab(三个配置页的同一套骨架与页头按钮、Qoder 单列)与 **composer 胶囊展开的账号卡片**,空/坏数据与边界值、自动刷新定时器、分配策略读写、Qoder 模型列(图片/上下文跟随上游 + 「从 Qoder 刷新」拉取即落盘)、Trae 与 Qoder 的「签到」(每张卡带自己的 accountId、不写 selectedAccountId、重放不许说成「已领 +100」)、卡片的启停/切换两条写路径与「展开卡片不新增任何定时器」
|
|
341
340
|
node test/host-assembly.test.mjs # 10 项:cordis 装配层(exports 白名单、cordis.patch.yml 条目)
|
|
342
341
|
node test/trae-pool.test.mjs # 46 项:Trae 号池(scan/分配策略/冷却换号/停用/保留额度)
|
|
343
342
|
node test/trae-checkin-route.test.mjs # 36 项:Trae 签到路由(指定账号走 clientOfAccount 且绝不碰绑定账号、设备门禁 9095 与「账号已签」分开、未知账号 404、方法/区域/来源/坏 JSON 边界)
|
|
@@ -325,21 +325,12 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
325
325
|
.wotr-credit-region-title{font-size:11px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
326
326
|
.wotr-credit-region-sub{font-size:11px;color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
327
327
|
.wotr-credit-row{margin-top:6px;padding:8px;border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:6px}
|
|
328
|
-
.wotr-credit-row[data-off="1"]{opacity:.55}
|
|
329
328
|
.wotr-credit-row[data-serving="1"]{border-color:var(--dsw-alias-label-primary)}
|
|
330
329
|
.wotr-credit-row-top{display:flex;align-items:baseline;gap:8px;justify-content:space-between}
|
|
331
330
|
.wotr-credit-row-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-weight:600}
|
|
332
331
|
.wotr-credit-row-credits{flex:none;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary)}
|
|
333
332
|
.wotr-credit-row-foot{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
334
333
|
.wotr-credit-tags{display:flex;align-items:center;gap:4px;flex-wrap:wrap;min-width:0}
|
|
335
|
-
/* 启停开关:本页自绘(不引 primitives 的 Switch),配色沿用本文件
|
|
336
|
-
既有的 token 组合 —— 开 = label-primary 底 + bg-layer-1 滑块,
|
|
337
|
-
与 .wbc-btn-primary 同一套对比关系,深浅色主题下都不会糊。 */
|
|
338
|
-
.wotr-credit-switch{position:relative;flex:none;width:30px;height:16px;padding:0;border:none;border-radius:8px;background:var(--dsw-alias-bg-layer-3,rgba(127,127,127,.35));cursor:pointer;transition:background .12s ease-out}
|
|
339
|
-
.wotr-credit-switch::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:var(--dsw-alias-label-secondary,#888);transition:transform .12s ease-out,background .12s ease-out}
|
|
340
|
-
.wotr-credit-switch[data-on="1"]{background:var(--dsw-alias-label-primary,#4c8dff)}
|
|
341
|
-
.wotr-credit-switch[data-on="1"]::after{transform:translateX(14px);background:var(--dsw-alias-bg-layer-1,#fff)}
|
|
342
|
-
.wotr-credit-switch:disabled{opacity:.5;cursor:default}
|
|
343
334
|
.wotr-credit-foot{margin-top:10px;padding-top:8px;border-top:.5px solid var(--dsw-alias-border-l2);font-size:11px;line-height:16px}
|
|
344
335
|
.wotr-credit-note{margin-top:6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-primary)}
|
|
345
336
|
.wotr-credit-err{margin-top:6px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-error,#e5484d)}
|
|
@@ -697,7 +688,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
697
688
|
const ignored = asArray(status?.ignored);
|
|
698
689
|
const selection = status?.selection ?? {};
|
|
699
690
|
const automation = status?.automation;
|
|
700
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
701
691
|
const automationOn = automation?.enabled === true;
|
|
702
692
|
|
|
703
693
|
const draft = draftByRegion[region];
|
|
@@ -748,17 +738,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
748
738
|
}
|
|
749
739
|
};
|
|
750
740
|
|
|
751
|
-
// —— 账号启停 ——
|
|
752
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
753
|
-
if (!writable) return;
|
|
754
|
-
return run(accountId, () =>
|
|
755
|
-
fetchJson(`${POOL_API}/accounts/disabled`, {
|
|
756
|
-
method: 'POST',
|
|
757
|
-
headers: { 'Content-Type': 'application/json' },
|
|
758
|
-
body: JSON.stringify({ accountId, disabled }),
|
|
759
|
-
}), disabled ? '已停用该账号' : '已重新启用该账号');
|
|
760
|
-
};
|
|
761
|
-
|
|
762
741
|
// —— 移出池子 / 恢复 ——
|
|
763
742
|
const onIgnore = (accountId, ignoredNext) => {
|
|
764
743
|
return run(`ignore-${accountId}`, () =>
|
|
@@ -1094,7 +1073,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1094
1073
|
: jsx(StatsRow, {
|
|
1095
1074
|
items: [
|
|
1096
1075
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
1097
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
1098
1076
|
{
|
|
1099
1077
|
label: '当前使用',
|
|
1100
1078
|
value: currentName,
|
|
@@ -1134,12 +1112,11 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1134
1112
|
AccountCard,
|
|
1135
1113
|
{
|
|
1136
1114
|
name: a.nickname || a.label || a.id,
|
|
1137
|
-
dim:
|
|
1115
|
+
dim: (currentId !== '' && isCurrent !== true),
|
|
1138
1116
|
tags: [
|
|
1139
1117
|
isCurrent
|
|
1140
1118
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
1141
1119
|
: { text: '备用', cls: 'wbc-tag' },
|
|
1142
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
1143
1120
|
],
|
|
1144
1121
|
rows: [
|
|
1145
1122
|
/**
|
|
@@ -1168,9 +1145,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1168
1145
|
CardActions,
|
|
1169
1146
|
{
|
|
1170
1147
|
locked: !writable,
|
|
1171
|
-
disabled: a.disabled === true,
|
|
1172
|
-
saving: busy === a.id,
|
|
1173
|
-
onToggleDisabled: (next) => onToggleDisabled(a.id, next),
|
|
1174
1148
|
onRemove: () => onIgnore(a.id, true),
|
|
1175
1149
|
removeBusy: busy === `ignore-${a.id}`,
|
|
1176
1150
|
},
|
|
@@ -1765,19 +1739,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1765
1739
|
}
|
|
1766
1740
|
};
|
|
1767
1741
|
|
|
1768
|
-
const [savingPool, setSavingPool] = useState(null);
|
|
1769
|
-
|
|
1770
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
1771
|
-
setSavingPool(accountId);
|
|
1772
|
-
const currentIds = asArray(traeRegionSlotOf(scopeObj?.getSnapshot?.().value, region).disabledAccountIds);
|
|
1773
|
-
const next = disabled
|
|
1774
|
-
? [...new Set([...currentIds, accountId])]
|
|
1775
|
-
: currentIds.filter((id) => id !== accountId);
|
|
1776
|
-
writePoolField({ disabledAccountIds: next }, (readBack) =>
|
|
1777
|
-
JSON.stringify(traeRegionSlotOf(readBack, region).disabledAccountIds ?? []) === JSON.stringify(next),
|
|
1778
|
-
disabled ? '已停用该账号' : '已重新启用该账号')
|
|
1779
|
-
.finally(() => setSavingPool(null));
|
|
1780
|
-
};
|
|
1781
1742
|
|
|
1782
1743
|
/** 供应商开关:只改 enabled,其余字段原样保留(关掉不丢勾选) */
|
|
1783
1744
|
const [togglingRegion, setTogglingRegion] = useState(null);
|
|
@@ -1882,10 +1843,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1882
1843
|
.filter((pool) => !accounts.some((account) => String(account.id) === String(pool.id)))
|
|
1883
1844
|
.map((pool) => ({ account: null, pool })),
|
|
1884
1845
|
];
|
|
1885
|
-
const traeUsable =
|
|
1886
|
-
poolAccounts.length === 0
|
|
1887
|
-
? accounts.length
|
|
1888
|
-
: poolAccounts.filter((a) => a.disabled !== true).length;
|
|
1889
1846
|
const checkinText =
|
|
1890
1847
|
usage?.checkin?.checkedIn === true
|
|
1891
1848
|
? '今日已签到'
|
|
@@ -2010,7 +1967,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2010
1967
|
? jsx(StatsRow, {
|
|
2011
1968
|
items: [
|
|
2012
1969
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
2013
|
-
{ label: '可用', value: fmtNum(traeUsable), sub: '未被停用' },
|
|
2014
1970
|
{
|
|
2015
1971
|
label: '当前使用',
|
|
2016
1972
|
value: usage.accountName ?? current?.accountName ?? '–',
|
|
@@ -2055,7 +2011,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2055
2011
|
isCurrent
|
|
2056
2012
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
2057
2013
|
: { text: '备用', cls: 'wbc-tag' },
|
|
2058
|
-
pool?.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
2059
2014
|
],
|
|
2060
2015
|
// 余额来自 /usage 的逐账号 credits,号池状态来自 pool 段,
|
|
2061
2016
|
// 两张表按 id 对齐后拼成同一张卡。
|
|
@@ -2070,16 +2025,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2070
2025
|
: null,
|
|
2071
2026
|
],
|
|
2072
2027
|
extra: [
|
|
2073
|
-
jsx(
|
|
2074
|
-
CardActions,
|
|
2075
|
-
{
|
|
2076
|
-
locked: !writable || pool === null || pool === undefined,
|
|
2077
|
-
disabled: pool?.disabled === true,
|
|
2078
|
-
saving: savingPool === id,
|
|
2079
|
-
onToggleDisabled: (next) => onToggleDisabled(id, next),
|
|
2080
|
-
},
|
|
2081
|
-
'actions',
|
|
2082
|
-
),
|
|
2083
2028
|
/* 卡片最后一行:与 XD Pool 页同款的两按钮排 —— 左边「切换
|
|
2084
2029
|
* 到此账号」(**当前那张不给**:它已经是当前了),右边
|
|
2085
2030
|
* 「签到」。签到这个按钮是**每张卡都有**的:它签的是卡片
|
|
@@ -2527,26 +2472,18 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2527
2472
|
* 账号卡里的控件行:三个配置页同一套。
|
|
2528
2473
|
*
|
|
2529
2474
|
* 完全手动语义(本 fork):账号之间没有分配策略 / 轮换 / 冷却,卡上的操作
|
|
2530
|
-
*
|
|
2475
|
+
* 只剩一件**手动管理**的事 ——
|
|
2531
2476
|
*
|
|
2532
|
-
* 「停用 / 启用」:写 `disabledAccountIds`,停用的账号不会被取号;
|
|
2533
2477
|
* 「移出池子」:只在传了 `onRemove` 时渲染(Trae 半边没有「移出」概念,
|
|
2534
2478
|
* 不给它硬塞一个按不动的按钮)。
|
|
2535
2479
|
*
|
|
2536
|
-
*
|
|
2480
|
+
* 早先这里的「参与轮换」勾选 / 「保留额度」输入 / 「停用」按钮都已按完全手动
|
|
2481
|
+
* 语义删除 —— 停用不影响任何请求(取号只认手动选中的那一个)。
|
|
2537
2482
|
*/
|
|
2538
|
-
function CardActions({ locked,
|
|
2483
|
+
function CardActions({ locked, onRemove, removeBusy }) {
|
|
2539
2484
|
return jsxs('div', {
|
|
2540
2485
|
className: 'wbc-bar',
|
|
2541
2486
|
children: [
|
|
2542
|
-
jsx('button', {
|
|
2543
|
-
type: 'button',
|
|
2544
|
-
className: 'wbc-btn',
|
|
2545
|
-
title: disabled === true ? '恢复这个账号参与取号' : '停用后取号不再选中它(请求失败时请手动切换)',
|
|
2546
|
-
disabled: locked === true || saving === true,
|
|
2547
|
-
onClick: () => onToggleDisabled(disabled !== true),
|
|
2548
|
-
children: disabled === true ? '启用' : '停用',
|
|
2549
|
-
}),
|
|
2550
2487
|
jsx('div', { className: 'wbc-spacer' }),
|
|
2551
2488
|
typeof onRemove !== 'function'
|
|
2552
2489
|
? null
|
|
@@ -3021,7 +2958,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3021
2958
|
creditsAtMs,
|
|
3022
2959
|
onRefresh,
|
|
3023
2960
|
onAddAccount,
|
|
3024
|
-
onToggle,
|
|
3025
2961
|
onRemove,
|
|
3026
2962
|
onSelect,
|
|
3027
2963
|
/**
|
|
@@ -3037,7 +2973,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3037
2973
|
checkinNote,
|
|
3038
2974
|
}) {
|
|
3039
2975
|
const accounts = asArray(status?.accounts);
|
|
3040
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
3041
2976
|
/**
|
|
3042
2977
|
* 「当前使用」的那一个 —— 请求就固定用它。
|
|
3043
2978
|
*
|
|
@@ -3220,7 +3155,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3220
3155
|
? jsx(StatsRow, {
|
|
3221
3156
|
items: [
|
|
3222
3157
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已入池' },
|
|
3223
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
3224
3158
|
{
|
|
3225
3159
|
// 「当前使用」是谁:这是本页最该一眼看到的一件事 —— 请求就打在
|
|
3226
3160
|
// 它身上。它这一轮的余额(真正在扣的那份)与全池合计退到 sub,
|
|
@@ -3260,13 +3194,12 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3260
3194
|
name: a.label ?? a.id,
|
|
3261
3195
|
// 完全手动:只有「当前使用」那张是亮的,其余卡片压暗。
|
|
3262
3196
|
// 不可用(停用)也压暗 —— 具体原因由标签说明。
|
|
3263
|
-
dim:
|
|
3197
|
+
dim: isSelected !== true,
|
|
3264
3198
|
tags: [
|
|
3265
3199
|
isServing ? { text: '本次调用', cls: 'wbc-tag wbc-tag-on' } : null,
|
|
3266
3200
|
isSelected
|
|
3267
3201
|
? { text: '当前使用', cls: isServing ? 'wbc-tag' : 'wbc-tag wbc-tag-on' }
|
|
3268
3202
|
: { text: '备用', cls: 'wbc-tag' },
|
|
3269
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
3270
3203
|
expired ? { text: '令牌已过期', cls: 'wbc-tag wbc-tag-warn' } : null,
|
|
3271
3204
|
],
|
|
3272
3205
|
rows: [
|
|
@@ -3297,9 +3230,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3297
3230
|
CardActions,
|
|
3298
3231
|
{
|
|
3299
3232
|
locked,
|
|
3300
|
-
disabled: a.disabled === true,
|
|
3301
|
-
onToggleDisabled: (next) => onToggle(a.id, next),
|
|
3302
|
-
saving: busy === true,
|
|
3303
3233
|
onRemove: () => onRemove(a.id),
|
|
3304
3234
|
removeBusy: busy === true,
|
|
3305
3235
|
},
|
|
@@ -3919,7 +3849,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3919
3849
|
fullName: name || id,
|
|
3920
3850
|
credits:
|
|
3921
3851
|
typeof raw?.credits === 'number' && Number.isFinite(raw.credits) ? raw.credits : undefined,
|
|
3922
|
-
disabled: raw?.disabled === true,
|
|
3923
3852
|
expired: raw?.expired === true,
|
|
3924
3853
|
pinned: raw?.pinned === true,
|
|
3925
3854
|
serving: raw?.serving === true,
|
|
@@ -3938,7 +3867,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3938
3867
|
id: a.id,
|
|
3939
3868
|
name: a.nickname ?? a.label ?? '',
|
|
3940
3869
|
credits: accountCreditsOf(a),
|
|
3941
|
-
disabled: a.disabled,
|
|
3942
3870
|
pinned: pinned !== '' && a.id === pinned,
|
|
3943
3871
|
serving: serving !== '' && a.id === serving,
|
|
3944
3872
|
error: a.creditsError,
|
|
@@ -4012,7 +3940,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4012
3940
|
credits: isServing
|
|
4013
3941
|
? (accountCreditsOf(serving, 'qoder') ?? accountCreditsOf(a, 'qoder'))
|
|
4014
3942
|
: accountCreditsOf(a, 'qoder'),
|
|
4015
|
-
disabled: a.disabled,
|
|
4016
3943
|
expired: a.expired,
|
|
4017
3944
|
pinned: typeof a.id === 'string' && a.id !== '' && a.id === pinned,
|
|
4018
3945
|
serving: isServing,
|
|
@@ -4097,8 +4024,8 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4097
4024
|
* 就地改缓存里的行(写完之后的即时反馈)。
|
|
4098
4025
|
*
|
|
4099
4026
|
* 刻意**不**为了这一次改动再打一遍状态接口 —— 那个接口每个账号都会触发一次
|
|
4100
|
-
* 计费调用。写路径本身已经做过读回确认(见
|
|
4101
|
-
*
|
|
4027
|
+
* 计费调用。写路径本身已经做过读回确认(见 selectAccountRemote),
|
|
4028
|
+
* 所以就地改动是有依据的;下一次打开卡片或点「刷新」
|
|
4102
4029
|
* 会用真实快照把它对齐。
|
|
4103
4030
|
*/
|
|
4104
4031
|
function patchCreditRows(family, match, patch) {
|
|
@@ -4111,81 +4038,11 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4111
4038
|
});
|
|
4112
4039
|
}
|
|
4113
4040
|
|
|
4114
|
-
/** 停用名单的加减:**只动这一个 id**,其余原样带过 */
|
|
4115
|
-
function mergeDisabledIds(ids, accountId, disabled) {
|
|
4116
|
-
const current = asArray(ids).filter((id) => typeof id === 'string' && id !== '');
|
|
4117
|
-
return disabled
|
|
4118
|
-
? [...new Set([...current, accountId])]
|
|
4119
|
-
: current.filter((id) => id !== accountId);
|
|
4120
|
-
}
|
|
4121
|
-
|
|
4122
|
-
/** 两份停用名单是否等价(读回确认用;顺序无关) */
|
|
4123
|
-
function sameIdSet(a, b) {
|
|
4124
|
-
const left = asArray(a).filter((id) => typeof id === 'string' && id !== '');
|
|
4125
|
-
const right = asArray(b).filter((id) => typeof id === 'string' && id !== '');
|
|
4126
|
-
if (left.length !== right.length) return false;
|
|
4127
|
-
const set = new Set(left);
|
|
4128
|
-
return right.every((id) => set.has(id));
|
|
4129
|
-
}
|
|
4130
|
-
|
|
4131
4041
|
/** 该平台设置作用域(拿不到就是 null,调用方按只读处理) */
|
|
4132
4042
|
function creditScopeOf(family) {
|
|
4133
4043
|
return poolScopeOf(family === 'qoder' ? qoderSettings : traeSettings);
|
|
4134
4044
|
}
|
|
4135
4045
|
|
|
4136
|
-
/**
|
|
4137
|
-
* 停用 / 启用一个账号 —— 三家写法不同,语义是同一条。
|
|
4138
|
-
*
|
|
4139
|
-
* WorkBuddy:走路由 `POST /accounts/disabled`。**不要**在这里自己拼
|
|
4140
|
-
* `disabledAccountIds` 写设置:国内版与国际版共用**同一份**设置文档
|
|
4141
|
-
* (两个 provider 用同一个 settingsNs、`settingsPath: []`),只按当前区域的
|
|
4142
|
-
* 行拼出来的列表会把另一个区域的停用项整段抹掉。路由在服务端按整份设置合并
|
|
4143
|
-
* (`setAccountDisabled` 读 `current().disabledAccountIds` 再加减一个 id)。
|
|
4144
|
-
*
|
|
4145
|
-
* Trae / Qoder:没有可用的写路由(Trae 的 `/pool/account` 只改进程内的池,
|
|
4146
|
-
* 下一次设置同步就被覆盖回去),所以写设置字段 —— 但**从当前设置文档里读旧值
|
|
4147
|
-
* 再合并**,而不是从行里拼。
|
|
4148
|
-
*/
|
|
4149
|
-
async function setAccountDisabledRemote(family, region, accountId, disabled) {
|
|
4150
|
-
if (typeof accountId !== 'string' || accountId === '') throw new Error('缺少账号 id');
|
|
4151
|
-
const want = disabled === true;
|
|
4152
|
-
if (family === 'wb') {
|
|
4153
|
-
await fetchJson(`${POOL_API}/accounts/disabled`, {
|
|
4154
|
-
method: 'POST',
|
|
4155
|
-
headers: { 'Content-Type': 'application/json' },
|
|
4156
|
-
body: JSON.stringify({ accountId, disabled: want }),
|
|
4157
|
-
});
|
|
4158
|
-
return;
|
|
4159
|
-
}
|
|
4160
|
-
const scope = creditScopeOf(family);
|
|
4161
|
-
const who = family === 'qoder' ? 'qoder' : 'trae';
|
|
4162
|
-
if (scope === undefined || scope === null) {
|
|
4163
|
-
throw new Error(`${who}: 拿不到设置作用域,无法保存(配置页同一处也只读)`);
|
|
4164
|
-
}
|
|
4165
|
-
if (!poolWritable(scope)) throw new Error(`${who}: 设置当前不可写`);
|
|
4166
|
-
if (family === 'qoder') {
|
|
4167
|
-
const currentIds = asArray(unwrapVolatileDeep(scope.getSnapshot?.().value)?.disabledAccountIds);
|
|
4168
|
-
const next = mergeDisabledIds(currentIds, accountId, want);
|
|
4169
|
-
await writeSettingsField(
|
|
4170
|
-
scope,
|
|
4171
|
-
'disabledAccountIds',
|
|
4172
|
-
next,
|
|
4173
|
-
(doc) => sameIdSet(doc?.disabledAccountIds, next),
|
|
4174
|
-
'qoder',
|
|
4175
|
-
);
|
|
4176
|
-
return;
|
|
4177
|
-
}
|
|
4178
|
-
const value = scope.getSnapshot?.().value;
|
|
4179
|
-
const next = mergeDisabledIds(traeRegionSlotOf(value, region).disabledAccountIds, accountId, want);
|
|
4180
|
-
const slot = { ...traeRegionSlotOf(value, region), disabledAccountIds: next };
|
|
4181
|
-
await writeTraeField(
|
|
4182
|
-
scope,
|
|
4183
|
-
'regions',
|
|
4184
|
-
nextRegionSlots(regionsMapOf(value), region, slot),
|
|
4185
|
-
(readBack) => sameIdSet(traeRegionSlotOf(readBack, region).disabledAccountIds, next),
|
|
4186
|
-
);
|
|
4187
|
-
}
|
|
4188
|
-
|
|
4189
4046
|
/**
|
|
4190
4047
|
* 把某个账号设成「当前账号」。
|
|
4191
4048
|
*
|
|
@@ -4325,8 +4182,7 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4325
4182
|
*
|
|
4326
4183
|
* 展开的卡片里是**当前这个平台的全部账号**(不是「所有平台的账号」):
|
|
4327
4184
|
* WorkBuddy 与 Trae 各自的国内版 + 国际版都列出来,Qoder 只有一个池。
|
|
4328
|
-
*
|
|
4329
|
-
* setAccountDisabledRemote / selectAccountRemote 的语义说明。
|
|
4185
|
+
* 每行带一个控件 —— 「切换到此账号」,见 selectAccountRemote 的语义说明。
|
|
4330
4186
|
*
|
|
4331
4187
|
* 刷新策略分两档,因为两档的成本差一个量级:
|
|
4332
4188
|
* - 胶囊那颗数字:**每 30 秒自动刷一次**(见 CREDIT_REFRESH_MS 的成本说明),
|
|
@@ -4602,28 +4458,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4602
4458
|
refreshHeadline();
|
|
4603
4459
|
};
|
|
4604
4460
|
|
|
4605
|
-
/** 停用 / 启用某一行 */
|
|
4606
|
-
const onToggleRow = async (row) => {
|
|
4607
|
-
const next = row.disabled !== true;
|
|
4608
|
-
setBusyRow(row.key);
|
|
4609
|
-
setWriteError('');
|
|
4610
|
-
setNote('');
|
|
4611
|
-
try {
|
|
4612
|
-
await setAccountDisabledRemote(row.family, row.region, row.id, next);
|
|
4613
|
-
patchCreditRows(row.family, (r) => r.key === row.key, { disabled: next });
|
|
4614
|
-
// 停掉的正好是「这一轮在用的那个」→ 胶囊上的主语马上就会变,补一发
|
|
4615
|
-
if (next === true && row.serving === true && row.region === source.region) {
|
|
4616
|
-
refreshHeadline();
|
|
4617
|
-
}
|
|
4618
|
-
setNote(next ? `已停用 ${row.name},请求失败时请手动切换` : `已重新启用 ${row.name}`);
|
|
4619
|
-
} catch (e) {
|
|
4620
|
-
setWriteError(e?.message ?? String(e));
|
|
4621
|
-
} finally {
|
|
4622
|
-
setBusyRow('');
|
|
4623
|
-
setTick((n) => n + 1);
|
|
4624
|
-
}
|
|
4625
|
-
};
|
|
4626
|
-
|
|
4627
4461
|
/** 切换到此账号 / 取消固定 */
|
|
4628
4462
|
const onSelectRow = async (row) => {
|
|
4629
4463
|
// WorkBuddy 独有的「取消固定」:完全手动语义下它的池把「没有当前账号」
|
|
@@ -4659,7 +4493,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4659
4493
|
// 但选中账号不可用时上一轮可能根本没扣到它。
|
|
4660
4494
|
if (row.pinned === true) push('on', row.family === 'wb' ? '已固定' : '当前使用');
|
|
4661
4495
|
if (row.serving === true) push(row.pinned === true ? 'muted' : 'on', '本次调用');
|
|
4662
|
-
if (row.disabled === true) push('warn', '已停用');
|
|
4663
4496
|
if (row.expired === true) push('warn', '凭据已过期');
|
|
4664
4497
|
if (row.error !== '') push('warn', `额度读不到:${row.error}`);
|
|
4665
4498
|
return tags.map((tag, i) =>
|
|
@@ -4693,14 +4526,11 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4693
4526
|
const hideSelect = unpin === false && row.pinned === true;
|
|
4694
4527
|
const selectTitle = unpin
|
|
4695
4528
|
? '取消固定后请求会失败,请手动切换'
|
|
4696
|
-
: row.disabled === true
|
|
4697
|
-
? '这个账号已停用:选中它请求会失败,请先启用或换一个'
|
|
4698
4529
|
: `让 ${row.fullName} 成为「当前账号」,请求只用它`;
|
|
4699
4530
|
return jsxs(
|
|
4700
4531
|
'div',
|
|
4701
4532
|
{
|
|
4702
4533
|
className: 'wotr-credit-row',
|
|
4703
|
-
'data-off': row.disabled === true ? '1' : '0',
|
|
4704
4534
|
'data-serving': row.serving === true ? '1' : '0',
|
|
4705
4535
|
children: [
|
|
4706
4536
|
jsxs('div', {
|
|
@@ -4730,22 +4560,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4730
4560
|
},
|
|
4731
4561
|
children: label,
|
|
4732
4562
|
}),
|
|
4733
|
-
jsx('button', {
|
|
4734
|
-
type: 'button',
|
|
4735
|
-
role: 'switch',
|
|
4736
|
-
className: 'wotr-credit-switch',
|
|
4737
|
-
'aria-checked': row.disabled === true ? 'false' : 'true',
|
|
4738
|
-
'aria-label': `${row.name} 停用/启用`,
|
|
4739
|
-
'data-on': row.disabled === true ? '0' : '1',
|
|
4740
|
-
disabled: busy,
|
|
4741
|
-
title:
|
|
4742
|
-
row.disabled === true
|
|
4743
|
-
? `启用 ${row.fullName}(恢复参与取号)`
|
|
4744
|
-
: `停用 ${row.fullName}(选中它时请求会失败)`,
|
|
4745
|
-
onClick: () => {
|
|
4746
|
-
void onToggleRow(row);
|
|
4747
|
-
},
|
|
4748
|
-
}),
|
|
4749
4563
|
],
|
|
4750
4564
|
}),
|
|
4751
4565
|
],
|
|
@@ -5305,29 +5119,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
5305
5119
|
[loadQoder],
|
|
5306
5120
|
);
|
|
5307
5121
|
|
|
5308
|
-
/**
|
|
5309
|
-
* 停用 / 启用:往 `disabledAccountIds` 里加减这个账号。
|
|
5310
|
-
*
|
|
5311
|
-
* 必须**读出当前列表再合并** —— 整段覆盖写会把别的账号的停用状态清掉,
|
|
5312
|
-
* 表现为「停用了 A,B 自己被启用了」。
|
|
5313
|
-
*/
|
|
5314
|
-
const onQoderToggle = useCallback(
|
|
5315
|
-
(accountId, disabled) => {
|
|
5316
|
-
const current = asArray(qoderConfigSnapshot().disabledAccountIds).filter(
|
|
5317
|
-
(id) => typeof id === 'string',
|
|
5318
|
-
);
|
|
5319
|
-
const next = disabled === true
|
|
5320
|
-
? [...new Set([...current, accountId])]
|
|
5321
|
-
: current.filter((id) => id !== accountId);
|
|
5322
|
-
const want = disabled === true;
|
|
5323
|
-
return writeQoderPoolField(
|
|
5324
|
-
'disabledAccountIds',
|
|
5325
|
-
next,
|
|
5326
|
-
(doc) => asArray(doc?.disabledAccountIds).includes(accountId) === want,
|
|
5327
|
-
);
|
|
5328
|
-
},
|
|
5329
|
-
[qoderConfigSnapshot, writeQoderPoolField],
|
|
5330
|
-
);
|
|
5331
5122
|
|
|
5332
5123
|
const onQoderRemove = useCallback(
|
|
5333
5124
|
(accountId) => qoderAction(`${SELF_API}/qoder/pool/remove`, { accountId }),
|
|
@@ -5582,7 +5373,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
5582
5373
|
creditsAtMs: qoderCreditsAtMs,
|
|
5583
5374
|
onRefresh: loadQoder,
|
|
5584
5375
|
onAddAccount: onQoderAddAccount,
|
|
5585
|
-
onToggle: onQoderToggle,
|
|
5586
5376
|
onRemove: onQoderRemove,
|
|
5587
5377
|
onSelect: onQoderSelect,
|
|
5588
5378
|
// 每日签到(按账号,不切号):正在签的 id / 本页各账号的答复 / 顶部结论。
|