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
- 交回轮换);Trae / Qoder **不给按钮** —— 这两家的池永远有一个当前账号,
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` 是一次实时计费读),卡片开着不动也在持续花钱 | 只在**打开卡片 / 手动刷新 / 写完一次启停或切换之后**取一次;`CREDIT_REFRESH_MS = 30000` 只作用于胶囊那颗数字。单测里钉着「展开卡片不新增任何 interval」 |
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 # 444 项:真正渲染三个 tab(三个配置页的同一套骨架与页头按钮、Qoder 单列)与 **composer 胶囊展开的账号卡片**,空/坏数据与边界值、自动刷新定时器、分配策略读写、Qoder 模型列(图片/上下文跟随上游 + 「从 Qoder 刷新」拉取即落盘)、Trae 与 Qoder 的「签到」(每张卡带自己的 accountId、不写 selectedAccountId、重放不许说成「已领 +100」)、卡片的启停/切换两条写路径与「展开卡片不新增任何定时器」
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: a.disabled === true || (currentId !== '' && isCurrent !== true),
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, disabled, onToggleDisabled, saving, onRemove, removeBusy }) {
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: a.disabled === true || isSelected !== true,
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
- * 计费调用。写路径本身已经做过读回确认(见 setAccountDisabledRemote /
4101
- * selectAccountRemote),所以就地改动是有依据的;下一次打开卡片或点「刷新」
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 / 本页各账号的答复 / 顶部结论。