kdczyz-dsh-model 1.2.3-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`、
|
|
@@ -287,7 +286,7 @@ trae 页改为本包自写(`console.mjs` 的 `TraeConfigTab`),与 workbudd
|
|
|
287
286
|
`lastServedId` 退回「当前账号」、Qoder 用 `serving.accountId`),与卡片是两个口径。
|
|
288
287
|
|
|
289
288
|
每一行只有一个控件(原先还有一个「停用 / 启用」开关,已移除 —— 完全手动语义下
|
|
290
|
-
|
|
289
|
+
它只对「已选中的那个」有效,停用别的账号对请求毫无影响;账号管理只剩「移出池子」):
|
|
291
290
|
|
|
292
291
|
| 控件 | 干什么 | 打到哪 |
|
|
293
292
|
|---|---|---|
|
|
@@ -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,7 +325,6 @@ 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}
|
|
@@ -689,7 +688,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
689
688
|
const ignored = asArray(status?.ignored);
|
|
690
689
|
const selection = status?.selection ?? {};
|
|
691
690
|
const automation = status?.automation;
|
|
692
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
693
691
|
const automationOn = automation?.enabled === true;
|
|
694
692
|
|
|
695
693
|
const draft = draftByRegion[region];
|
|
@@ -740,17 +738,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
740
738
|
}
|
|
741
739
|
};
|
|
742
740
|
|
|
743
|
-
// —— 账号启停 ——
|
|
744
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
745
|
-
if (!writable) return;
|
|
746
|
-
return run(accountId, () =>
|
|
747
|
-
fetchJson(`${POOL_API}/accounts/disabled`, {
|
|
748
|
-
method: 'POST',
|
|
749
|
-
headers: { 'Content-Type': 'application/json' },
|
|
750
|
-
body: JSON.stringify({ accountId, disabled }),
|
|
751
|
-
}), disabled ? '已停用该账号' : '已重新启用该账号');
|
|
752
|
-
};
|
|
753
|
-
|
|
754
741
|
// —— 移出池子 / 恢复 ——
|
|
755
742
|
const onIgnore = (accountId, ignoredNext) => {
|
|
756
743
|
return run(`ignore-${accountId}`, () =>
|
|
@@ -1086,7 +1073,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1086
1073
|
: jsx(StatsRow, {
|
|
1087
1074
|
items: [
|
|
1088
1075
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
1089
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
1090
1076
|
{
|
|
1091
1077
|
label: '当前使用',
|
|
1092
1078
|
value: currentName,
|
|
@@ -1126,12 +1112,11 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1126
1112
|
AccountCard,
|
|
1127
1113
|
{
|
|
1128
1114
|
name: a.nickname || a.label || a.id,
|
|
1129
|
-
dim:
|
|
1115
|
+
dim: (currentId !== '' && isCurrent !== true),
|
|
1130
1116
|
tags: [
|
|
1131
1117
|
isCurrent
|
|
1132
1118
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
1133
1119
|
: { text: '备用', cls: 'wbc-tag' },
|
|
1134
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
1135
1120
|
],
|
|
1136
1121
|
rows: [
|
|
1137
1122
|
/**
|
|
@@ -1160,9 +1145,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1160
1145
|
CardActions,
|
|
1161
1146
|
{
|
|
1162
1147
|
locked: !writable,
|
|
1163
|
-
disabled: a.disabled === true,
|
|
1164
|
-
saving: busy === a.id,
|
|
1165
|
-
onToggleDisabled: (next) => onToggleDisabled(a.id, next),
|
|
1166
1148
|
onRemove: () => onIgnore(a.id, true),
|
|
1167
1149
|
removeBusy: busy === `ignore-${a.id}`,
|
|
1168
1150
|
},
|
|
@@ -1757,19 +1739,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1757
1739
|
}
|
|
1758
1740
|
};
|
|
1759
1741
|
|
|
1760
|
-
const [savingPool, setSavingPool] = useState(null);
|
|
1761
|
-
|
|
1762
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
1763
|
-
setSavingPool(accountId);
|
|
1764
|
-
const currentIds = asArray(traeRegionSlotOf(scopeObj?.getSnapshot?.().value, region).disabledAccountIds);
|
|
1765
|
-
const next = disabled
|
|
1766
|
-
? [...new Set([...currentIds, accountId])]
|
|
1767
|
-
: currentIds.filter((id) => id !== accountId);
|
|
1768
|
-
writePoolField({ disabledAccountIds: next }, (readBack) =>
|
|
1769
|
-
JSON.stringify(traeRegionSlotOf(readBack, region).disabledAccountIds ?? []) === JSON.stringify(next),
|
|
1770
|
-
disabled ? '已停用该账号' : '已重新启用该账号')
|
|
1771
|
-
.finally(() => setSavingPool(null));
|
|
1772
|
-
};
|
|
1773
1742
|
|
|
1774
1743
|
/** 供应商开关:只改 enabled,其余字段原样保留(关掉不丢勾选) */
|
|
1775
1744
|
const [togglingRegion, setTogglingRegion] = useState(null);
|
|
@@ -1874,10 +1843,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
1874
1843
|
.filter((pool) => !accounts.some((account) => String(account.id) === String(pool.id)))
|
|
1875
1844
|
.map((pool) => ({ account: null, pool })),
|
|
1876
1845
|
];
|
|
1877
|
-
const traeUsable =
|
|
1878
|
-
poolAccounts.length === 0
|
|
1879
|
-
? accounts.length
|
|
1880
|
-
: poolAccounts.filter((a) => a.disabled !== true).length;
|
|
1881
1846
|
const checkinText =
|
|
1882
1847
|
usage?.checkin?.checkedIn === true
|
|
1883
1848
|
? '今日已签到'
|
|
@@ -2002,7 +1967,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2002
1967
|
? jsx(StatsRow, {
|
|
2003
1968
|
items: [
|
|
2004
1969
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
2005
|
-
{ label: '可用', value: fmtNum(traeUsable), sub: '未被停用' },
|
|
2006
1970
|
{
|
|
2007
1971
|
label: '当前使用',
|
|
2008
1972
|
value: usage.accountName ?? current?.accountName ?? '–',
|
|
@@ -2047,7 +2011,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2047
2011
|
isCurrent
|
|
2048
2012
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
2049
2013
|
: { text: '备用', cls: 'wbc-tag' },
|
|
2050
|
-
pool?.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
2051
2014
|
],
|
|
2052
2015
|
// 余额来自 /usage 的逐账号 credits,号池状态来自 pool 段,
|
|
2053
2016
|
// 两张表按 id 对齐后拼成同一张卡。
|
|
@@ -2062,16 +2025,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2062
2025
|
: null,
|
|
2063
2026
|
],
|
|
2064
2027
|
extra: [
|
|
2065
|
-
jsx(
|
|
2066
|
-
CardActions,
|
|
2067
|
-
{
|
|
2068
|
-
locked: !writable || pool === null || pool === undefined,
|
|
2069
|
-
disabled: pool?.disabled === true,
|
|
2070
|
-
saving: savingPool === id,
|
|
2071
|
-
onToggleDisabled: (next) => onToggleDisabled(id, next),
|
|
2072
|
-
},
|
|
2073
|
-
'actions',
|
|
2074
|
-
),
|
|
2075
2028
|
/* 卡片最后一行:与 XD Pool 页同款的两按钮排 —— 左边「切换
|
|
2076
2029
|
* 到此账号」(**当前那张不给**:它已经是当前了),右边
|
|
2077
2030
|
* 「签到」。签到这个按钮是**每张卡都有**的:它签的是卡片
|
|
@@ -2519,26 +2472,18 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
2519
2472
|
* 账号卡里的控件行:三个配置页同一套。
|
|
2520
2473
|
*
|
|
2521
2474
|
* 完全手动语义(本 fork):账号之间没有分配策略 / 轮换 / 冷却,卡上的操作
|
|
2522
|
-
*
|
|
2475
|
+
* 只剩一件**手动管理**的事 ——
|
|
2523
2476
|
*
|
|
2524
|
-
* 「停用 / 启用」:写 `disabledAccountIds`,停用的账号不会被取号;
|
|
2525
2477
|
* 「移出池子」:只在传了 `onRemove` 时渲染(Trae 半边没有「移出」概念,
|
|
2526
2478
|
* 不给它硬塞一个按不动的按钮)。
|
|
2527
2479
|
*
|
|
2528
|
-
*
|
|
2480
|
+
* 早先这里的「参与轮换」勾选 / 「保留额度」输入 / 「停用」按钮都已按完全手动
|
|
2481
|
+
* 语义删除 —— 停用不影响任何请求(取号只认手动选中的那一个)。
|
|
2529
2482
|
*/
|
|
2530
|
-
function CardActions({ locked,
|
|
2483
|
+
function CardActions({ locked, onRemove, removeBusy }) {
|
|
2531
2484
|
return jsxs('div', {
|
|
2532
2485
|
className: 'wbc-bar',
|
|
2533
2486
|
children: [
|
|
2534
|
-
jsx('button', {
|
|
2535
|
-
type: 'button',
|
|
2536
|
-
className: 'wbc-btn',
|
|
2537
|
-
title: disabled === true ? '恢复这个账号参与取号' : '停用后取号不再选中它(请求失败时请手动切换)',
|
|
2538
|
-
disabled: locked === true || saving === true,
|
|
2539
|
-
onClick: () => onToggleDisabled(disabled !== true),
|
|
2540
|
-
children: disabled === true ? '启用' : '停用',
|
|
2541
|
-
}),
|
|
2542
2487
|
jsx('div', { className: 'wbc-spacer' }),
|
|
2543
2488
|
typeof onRemove !== 'function'
|
|
2544
2489
|
? null
|
|
@@ -3013,7 +2958,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3013
2958
|
creditsAtMs,
|
|
3014
2959
|
onRefresh,
|
|
3015
2960
|
onAddAccount,
|
|
3016
|
-
onToggle,
|
|
3017
2961
|
onRemove,
|
|
3018
2962
|
onSelect,
|
|
3019
2963
|
/**
|
|
@@ -3029,7 +2973,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3029
2973
|
checkinNote,
|
|
3030
2974
|
}) {
|
|
3031
2975
|
const accounts = asArray(status?.accounts);
|
|
3032
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
3033
2976
|
/**
|
|
3034
2977
|
* 「当前使用」的那一个 —— 请求就固定用它。
|
|
3035
2978
|
*
|
|
@@ -3212,7 +3155,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3212
3155
|
? jsx(StatsRow, {
|
|
3213
3156
|
items: [
|
|
3214
3157
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已入池' },
|
|
3215
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
3216
3158
|
{
|
|
3217
3159
|
// 「当前使用」是谁:这是本页最该一眼看到的一件事 —— 请求就打在
|
|
3218
3160
|
// 它身上。它这一轮的余额(真正在扣的那份)与全池合计退到 sub,
|
|
@@ -3252,13 +3194,12 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3252
3194
|
name: a.label ?? a.id,
|
|
3253
3195
|
// 完全手动:只有「当前使用」那张是亮的,其余卡片压暗。
|
|
3254
3196
|
// 不可用(停用)也压暗 —— 具体原因由标签说明。
|
|
3255
|
-
dim:
|
|
3197
|
+
dim: isSelected !== true,
|
|
3256
3198
|
tags: [
|
|
3257
3199
|
isServing ? { text: '本次调用', cls: 'wbc-tag wbc-tag-on' } : null,
|
|
3258
3200
|
isSelected
|
|
3259
3201
|
? { text: '当前使用', cls: isServing ? 'wbc-tag' : 'wbc-tag wbc-tag-on' }
|
|
3260
3202
|
: { text: '备用', cls: 'wbc-tag' },
|
|
3261
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
3262
3203
|
expired ? { text: '令牌已过期', cls: 'wbc-tag wbc-tag-warn' } : null,
|
|
3263
3204
|
],
|
|
3264
3205
|
rows: [
|
|
@@ -3289,9 +3230,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3289
3230
|
CardActions,
|
|
3290
3231
|
{
|
|
3291
3232
|
locked,
|
|
3292
|
-
disabled: a.disabled === true,
|
|
3293
|
-
onToggleDisabled: (next) => onToggle(a.id, next),
|
|
3294
|
-
saving: busy === true,
|
|
3295
3233
|
onRemove: () => onRemove(a.id),
|
|
3296
3234
|
removeBusy: busy === true,
|
|
3297
3235
|
},
|
|
@@ -3911,7 +3849,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3911
3849
|
fullName: name || id,
|
|
3912
3850
|
credits:
|
|
3913
3851
|
typeof raw?.credits === 'number' && Number.isFinite(raw.credits) ? raw.credits : undefined,
|
|
3914
|
-
disabled: raw?.disabled === true,
|
|
3915
3852
|
expired: raw?.expired === true,
|
|
3916
3853
|
pinned: raw?.pinned === true,
|
|
3917
3854
|
serving: raw?.serving === true,
|
|
@@ -3930,7 +3867,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
3930
3867
|
id: a.id,
|
|
3931
3868
|
name: a.nickname ?? a.label ?? '',
|
|
3932
3869
|
credits: accountCreditsOf(a),
|
|
3933
|
-
disabled: a.disabled,
|
|
3934
3870
|
pinned: pinned !== '' && a.id === pinned,
|
|
3935
3871
|
serving: serving !== '' && a.id === serving,
|
|
3936
3872
|
error: a.creditsError,
|
|
@@ -4004,7 +3940,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4004
3940
|
credits: isServing
|
|
4005
3941
|
? (accountCreditsOf(serving, 'qoder') ?? accountCreditsOf(a, 'qoder'))
|
|
4006
3942
|
: accountCreditsOf(a, 'qoder'),
|
|
4007
|
-
disabled: a.disabled,
|
|
4008
3943
|
expired: a.expired,
|
|
4009
3944
|
pinned: typeof a.id === 'string' && a.id !== '' && a.id === pinned,
|
|
4010
3945
|
serving: isServing,
|
|
@@ -4558,7 +4493,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4558
4493
|
// 但选中账号不可用时上一轮可能根本没扣到它。
|
|
4559
4494
|
if (row.pinned === true) push('on', row.family === 'wb' ? '已固定' : '当前使用');
|
|
4560
4495
|
if (row.serving === true) push(row.pinned === true ? 'muted' : 'on', '本次调用');
|
|
4561
|
-
if (row.disabled === true) push('warn', '已停用');
|
|
4562
4496
|
if (row.expired === true) push('warn', '凭据已过期');
|
|
4563
4497
|
if (row.error !== '') push('warn', `额度读不到:${row.error}`);
|
|
4564
4498
|
return tags.map((tag, i) =>
|
|
@@ -4592,14 +4526,11 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
4592
4526
|
const hideSelect = unpin === false && row.pinned === true;
|
|
4593
4527
|
const selectTitle = unpin
|
|
4594
4528
|
? '取消固定后请求会失败,请手动切换'
|
|
4595
|
-
: row.disabled === true
|
|
4596
|
-
? '这个账号已停用:选中它请求会失败,请先启用或换一个'
|
|
4597
4529
|
: `让 ${row.fullName} 成为「当前账号」,请求只用它`;
|
|
4598
4530
|
return jsxs(
|
|
4599
4531
|
'div',
|
|
4600
4532
|
{
|
|
4601
4533
|
className: 'wotr-credit-row',
|
|
4602
|
-
'data-off': row.disabled === true ? '1' : '0',
|
|
4603
4534
|
'data-serving': row.serving === true ? '1' : '0',
|
|
4604
4535
|
children: [
|
|
4605
4536
|
jsxs('div', {
|
|
@@ -5188,29 +5119,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
5188
5119
|
[loadQoder],
|
|
5189
5120
|
);
|
|
5190
5121
|
|
|
5191
|
-
/**
|
|
5192
|
-
* 停用 / 启用:往 `disabledAccountIds` 里加减这个账号。
|
|
5193
|
-
*
|
|
5194
|
-
* 必须**读出当前列表再合并** —— 整段覆盖写会把别的账号的停用状态清掉,
|
|
5195
|
-
* 表现为「停用了 A,B 自己被启用了」。
|
|
5196
|
-
*/
|
|
5197
|
-
const onQoderToggle = useCallback(
|
|
5198
|
-
(accountId, disabled) => {
|
|
5199
|
-
const current = asArray(qoderConfigSnapshot().disabledAccountIds).filter(
|
|
5200
|
-
(id) => typeof id === 'string',
|
|
5201
|
-
);
|
|
5202
|
-
const next = disabled === true
|
|
5203
|
-
? [...new Set([...current, accountId])]
|
|
5204
|
-
: current.filter((id) => id !== accountId);
|
|
5205
|
-
const want = disabled === true;
|
|
5206
|
-
return writeQoderPoolField(
|
|
5207
|
-
'disabledAccountIds',
|
|
5208
|
-
next,
|
|
5209
|
-
(doc) => asArray(doc?.disabledAccountIds).includes(accountId) === want,
|
|
5210
|
-
);
|
|
5211
|
-
},
|
|
5212
|
-
[qoderConfigSnapshot, writeQoderPoolField],
|
|
5213
|
-
);
|
|
5214
5122
|
|
|
5215
5123
|
const onQoderRemove = useCallback(
|
|
5216
5124
|
(accountId) => qoderAction(`${SELF_API}/qoder/pool/remove`, { accountId }),
|
|
@@ -5465,7 +5373,6 @@ const __WBC_CONSOLE_FACTORY__ = (react, jsxRuntime, embedded) => {
|
|
|
5465
5373
|
creditsAtMs: qoderCreditsAtMs,
|
|
5466
5374
|
onRefresh: loadQoder,
|
|
5467
5375
|
onAddAccount: onQoderAddAccount,
|
|
5468
|
-
onToggle: onQoderToggle,
|
|
5469
5376
|
onRemove: onQoderRemove,
|
|
5470
5377
|
onSelect: onQoderSelect,
|
|
5471
5378
|
// 每日签到(按账号,不切号):正在签的 id / 本页各账号的答复 / 顶部结论。
|
|
@@ -273,7 +273,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
273
273
|
.wotr-credit-region-title{font-size:11px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
274
274
|
.wotr-credit-region-sub{font-size:11px;color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
275
275
|
.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}
|
|
276
|
-
.wotr-credit-row[data-off="1"]{opacity:.55}
|
|
277
276
|
.wotr-credit-row[data-serving="1"]{border-color:var(--dsw-alias-label-primary)}
|
|
278
277
|
.wotr-credit-row-top{display:flex;align-items:baseline;gap:8px;justify-content:space-between}
|
|
279
278
|
.wotr-credit-row-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-weight:600}
|
|
@@ -637,7 +636,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
637
636
|
const ignored = asArray(status?.ignored);
|
|
638
637
|
const selection = status?.selection ?? {};
|
|
639
638
|
const automation = status?.automation;
|
|
640
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
641
639
|
const automationOn = automation?.enabled === true;
|
|
642
640
|
|
|
643
641
|
const draft = draftByRegion[region];
|
|
@@ -688,17 +686,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
688
686
|
}
|
|
689
687
|
};
|
|
690
688
|
|
|
691
|
-
// —— 账号启停 ——
|
|
692
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
693
|
-
if (!writable) return;
|
|
694
|
-
return run(accountId, () =>
|
|
695
|
-
fetchJson(`${POOL_API}/accounts/disabled`, {
|
|
696
|
-
method: 'POST',
|
|
697
|
-
headers: { 'Content-Type': 'application/json' },
|
|
698
|
-
body: JSON.stringify({ accountId, disabled }),
|
|
699
|
-
}), disabled ? '已停用该账号' : '已重新启用该账号');
|
|
700
|
-
};
|
|
701
|
-
|
|
702
689
|
// —— 移出池子 / 恢复 ——
|
|
703
690
|
const onIgnore = (accountId, ignoredNext) => {
|
|
704
691
|
return run(`ignore-${accountId}`, () =>
|
|
@@ -1034,7 +1021,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1034
1021
|
: jsx(StatsRow, {
|
|
1035
1022
|
items: [
|
|
1036
1023
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
1037
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
1038
1024
|
{
|
|
1039
1025
|
label: '当前使用',
|
|
1040
1026
|
value: currentName,
|
|
@@ -1074,12 +1060,11 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1074
1060
|
AccountCard,
|
|
1075
1061
|
{
|
|
1076
1062
|
name: a.nickname || a.label || a.id,
|
|
1077
|
-
dim:
|
|
1063
|
+
dim: (currentId !== '' && isCurrent !== true),
|
|
1078
1064
|
tags: [
|
|
1079
1065
|
isCurrent
|
|
1080
1066
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
1081
1067
|
: { text: '备用', cls: 'wbc-tag' },
|
|
1082
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
1083
1068
|
],
|
|
1084
1069
|
rows: [
|
|
1085
1070
|
/**
|
|
@@ -1108,9 +1093,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1108
1093
|
CardActions,
|
|
1109
1094
|
{
|
|
1110
1095
|
locked: !writable,
|
|
1111
|
-
disabled: a.disabled === true,
|
|
1112
|
-
saving: busy === a.id,
|
|
1113
|
-
onToggleDisabled: (next) => onToggleDisabled(a.id, next),
|
|
1114
1096
|
onRemove: () => onIgnore(a.id, true),
|
|
1115
1097
|
removeBusy: busy === `ignore-${a.id}`,
|
|
1116
1098
|
},
|
|
@@ -1705,19 +1687,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1705
1687
|
}
|
|
1706
1688
|
};
|
|
1707
1689
|
|
|
1708
|
-
const [savingPool, setSavingPool] = useState(null);
|
|
1709
|
-
|
|
1710
|
-
const onToggleDisabled = (accountId, disabled) => {
|
|
1711
|
-
setSavingPool(accountId);
|
|
1712
|
-
const currentIds = asArray(traeRegionSlotOf(scopeObj?.getSnapshot?.().value, region).disabledAccountIds);
|
|
1713
|
-
const next = disabled
|
|
1714
|
-
? [...new Set([...currentIds, accountId])]
|
|
1715
|
-
: currentIds.filter((id) => id !== accountId);
|
|
1716
|
-
writePoolField({ disabledAccountIds: next }, (readBack) =>
|
|
1717
|
-
JSON.stringify(traeRegionSlotOf(readBack, region).disabledAccountIds ?? []) === JSON.stringify(next),
|
|
1718
|
-
disabled ? '已停用该账号' : '已重新启用该账号')
|
|
1719
|
-
.finally(() => setSavingPool(null));
|
|
1720
|
-
};
|
|
1721
1690
|
|
|
1722
1691
|
/** 供应商开关:只改 enabled,其余字段原样保留(关掉不丢勾选) */
|
|
1723
1692
|
const [togglingRegion, setTogglingRegion] = useState(null);
|
|
@@ -1822,10 +1791,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1822
1791
|
.filter((pool) => !accounts.some((account) => String(account.id) === String(pool.id)))
|
|
1823
1792
|
.map((pool) => ({ account: null, pool })),
|
|
1824
1793
|
];
|
|
1825
|
-
const traeUsable =
|
|
1826
|
-
poolAccounts.length === 0
|
|
1827
|
-
? accounts.length
|
|
1828
|
-
: poolAccounts.filter((a) => a.disabled !== true).length;
|
|
1829
1794
|
const checkinText =
|
|
1830
1795
|
usage?.checkin?.checkedIn === true
|
|
1831
1796
|
? '今日已签到'
|
|
@@ -1950,7 +1915,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1950
1915
|
? jsx(StatsRow, {
|
|
1951
1916
|
items: [
|
|
1952
1917
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已登录' },
|
|
1953
|
-
{ label: '可用', value: fmtNum(traeUsable), sub: '未被停用' },
|
|
1954
1918
|
{
|
|
1955
1919
|
label: '当前使用',
|
|
1956
1920
|
value: usage.accountName ?? current?.accountName ?? '–',
|
|
@@ -1995,7 +1959,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
1995
1959
|
isCurrent
|
|
1996
1960
|
? { text: '当前使用', cls: 'wbc-tag wbc-tag-on' }
|
|
1997
1961
|
: { text: '备用', cls: 'wbc-tag' },
|
|
1998
|
-
pool?.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
1999
1962
|
],
|
|
2000
1963
|
// 余额来自 /usage 的逐账号 credits,号池状态来自 pool 段,
|
|
2001
1964
|
// 两张表按 id 对齐后拼成同一张卡。
|
|
@@ -2010,16 +1973,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
2010
1973
|
: null,
|
|
2011
1974
|
],
|
|
2012
1975
|
extra: [
|
|
2013
|
-
jsx(
|
|
2014
|
-
CardActions,
|
|
2015
|
-
{
|
|
2016
|
-
locked: !writable || pool === null || pool === undefined,
|
|
2017
|
-
disabled: pool?.disabled === true,
|
|
2018
|
-
saving: savingPool === id,
|
|
2019
|
-
onToggleDisabled: (next) => onToggleDisabled(id, next),
|
|
2020
|
-
},
|
|
2021
|
-
'actions',
|
|
2022
|
-
),
|
|
2023
1976
|
/* 卡片最后一行:与 XD Pool 页同款的两按钮排 —— 左边「切换
|
|
2024
1977
|
* 到此账号」(**当前那张不给**:它已经是当前了),右边
|
|
2025
1978
|
* 「签到」。签到这个按钮是**每张卡都有**的:它签的是卡片
|
|
@@ -2467,26 +2420,18 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
2467
2420
|
* 账号卡里的控件行:三个配置页同一套。
|
|
2468
2421
|
*
|
|
2469
2422
|
* 完全手动语义(本 fork):账号之间没有分配策略 / 轮换 / 冷却,卡上的操作
|
|
2470
|
-
*
|
|
2423
|
+
* 只剩一件**手动管理**的事 ——
|
|
2471
2424
|
*
|
|
2472
|
-
* 「停用 / 启用」:写 `disabledAccountIds`,停用的账号不会被取号;
|
|
2473
2425
|
* 「移出池子」:只在传了 `onRemove` 时渲染(Trae 半边没有「移出」概念,
|
|
2474
2426
|
* 不给它硬塞一个按不动的按钮)。
|
|
2475
2427
|
*
|
|
2476
|
-
*
|
|
2428
|
+
* 早先这里的「参与轮换」勾选 / 「保留额度」输入 / 「停用」按钮都已按完全手动
|
|
2429
|
+
* 语义删除 —— 停用不影响任何请求(取号只认手动选中的那一个)。
|
|
2477
2430
|
*/
|
|
2478
|
-
function CardActions({ locked,
|
|
2431
|
+
function CardActions({ locked, onRemove, removeBusy }) {
|
|
2479
2432
|
return jsxs('div', {
|
|
2480
2433
|
className: 'wbc-bar',
|
|
2481
2434
|
children: [
|
|
2482
|
-
jsx('button', {
|
|
2483
|
-
type: 'button',
|
|
2484
|
-
className: 'wbc-btn',
|
|
2485
|
-
title: disabled === true ? '恢复这个账号参与取号' : '停用后取号不再选中它(请求失败时请手动切换)',
|
|
2486
|
-
disabled: locked === true || saving === true,
|
|
2487
|
-
onClick: () => onToggleDisabled(disabled !== true),
|
|
2488
|
-
children: disabled === true ? '启用' : '停用',
|
|
2489
|
-
}),
|
|
2490
2435
|
jsx('div', { className: 'wbc-spacer' }),
|
|
2491
2436
|
typeof onRemove !== 'function'
|
|
2492
2437
|
? null
|
|
@@ -2961,7 +2906,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
2961
2906
|
creditsAtMs,
|
|
2962
2907
|
onRefresh,
|
|
2963
2908
|
onAddAccount,
|
|
2964
|
-
onToggle,
|
|
2965
2909
|
onRemove,
|
|
2966
2910
|
onSelect,
|
|
2967
2911
|
/**
|
|
@@ -2977,7 +2921,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
2977
2921
|
checkinNote,
|
|
2978
2922
|
}) {
|
|
2979
2923
|
const accounts = asArray(status?.accounts);
|
|
2980
|
-
const usable = accounts.filter((a) => a.disabled !== true).length;
|
|
2981
2924
|
/**
|
|
2982
2925
|
* 「当前使用」的那一个 —— 请求就固定用它。
|
|
2983
2926
|
*
|
|
@@ -3160,7 +3103,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3160
3103
|
? jsx(StatsRow, {
|
|
3161
3104
|
items: [
|
|
3162
3105
|
{ label: '账号', value: fmtNum(accounts.length), sub: '个已入池' },
|
|
3163
|
-
{ label: '可用', value: fmtNum(usable), sub: '未被停用' },
|
|
3164
3106
|
{
|
|
3165
3107
|
// 「当前使用」是谁:这是本页最该一眼看到的一件事 —— 请求就打在
|
|
3166
3108
|
// 它身上。它这一轮的余额(真正在扣的那份)与全池合计退到 sub,
|
|
@@ -3200,13 +3142,12 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3200
3142
|
name: a.label ?? a.id,
|
|
3201
3143
|
// 完全手动:只有「当前使用」那张是亮的,其余卡片压暗。
|
|
3202
3144
|
// 不可用(停用)也压暗 —— 具体原因由标签说明。
|
|
3203
|
-
dim:
|
|
3145
|
+
dim: isSelected !== true,
|
|
3204
3146
|
tags: [
|
|
3205
3147
|
isServing ? { text: '本次调用', cls: 'wbc-tag wbc-tag-on' } : null,
|
|
3206
3148
|
isSelected
|
|
3207
3149
|
? { text: '当前使用', cls: isServing ? 'wbc-tag' : 'wbc-tag wbc-tag-on' }
|
|
3208
3150
|
: { text: '备用', cls: 'wbc-tag' },
|
|
3209
|
-
a.disabled === true ? { text: '已停用', cls: 'wbc-tag' } : null,
|
|
3210
3151
|
expired ? { text: '令牌已过期', cls: 'wbc-tag wbc-tag-warn' } : null,
|
|
3211
3152
|
],
|
|
3212
3153
|
rows: [
|
|
@@ -3237,9 +3178,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3237
3178
|
CardActions,
|
|
3238
3179
|
{
|
|
3239
3180
|
locked,
|
|
3240
|
-
disabled: a.disabled === true,
|
|
3241
|
-
onToggleDisabled: (next) => onToggle(a.id, next),
|
|
3242
|
-
saving: busy === true,
|
|
3243
3181
|
onRemove: () => onRemove(a.id),
|
|
3244
3182
|
removeBusy: busy === true,
|
|
3245
3183
|
},
|
|
@@ -3859,7 +3797,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3859
3797
|
fullName: name || id,
|
|
3860
3798
|
credits:
|
|
3861
3799
|
typeof raw?.credits === 'number' && Number.isFinite(raw.credits) ? raw.credits : undefined,
|
|
3862
|
-
disabled: raw?.disabled === true,
|
|
3863
3800
|
expired: raw?.expired === true,
|
|
3864
3801
|
pinned: raw?.pinned === true,
|
|
3865
3802
|
serving: raw?.serving === true,
|
|
@@ -3878,7 +3815,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3878
3815
|
id: a.id,
|
|
3879
3816
|
name: a.nickname ?? a.label ?? '',
|
|
3880
3817
|
credits: accountCreditsOf(a),
|
|
3881
|
-
disabled: a.disabled,
|
|
3882
3818
|
pinned: pinned !== '' && a.id === pinned,
|
|
3883
3819
|
serving: serving !== '' && a.id === serving,
|
|
3884
3820
|
error: a.creditsError,
|
|
@@ -3952,7 +3888,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
3952
3888
|
credits: isServing
|
|
3953
3889
|
? (accountCreditsOf(serving, 'qoder') ?? accountCreditsOf(a, 'qoder'))
|
|
3954
3890
|
: accountCreditsOf(a, 'qoder'),
|
|
3955
|
-
disabled: a.disabled,
|
|
3956
3891
|
expired: a.expired,
|
|
3957
3892
|
pinned: typeof a.id === 'string' && a.id !== '' && a.id === pinned,
|
|
3958
3893
|
serving: isServing,
|
|
@@ -4506,7 +4441,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
4506
4441
|
// 但选中账号不可用时上一轮可能根本没扣到它。
|
|
4507
4442
|
if (row.pinned === true) push('on', row.family === 'wb' ? '已固定' : '当前使用');
|
|
4508
4443
|
if (row.serving === true) push(row.pinned === true ? 'muted' : 'on', '本次调用');
|
|
4509
|
-
if (row.disabled === true) push('warn', '已停用');
|
|
4510
4444
|
if (row.expired === true) push('warn', '凭据已过期');
|
|
4511
4445
|
if (row.error !== '') push('warn', `额度读不到:${row.error}`);
|
|
4512
4446
|
return tags.map((tag, i) =>
|
|
@@ -4540,14 +4474,11 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
4540
4474
|
const hideSelect = unpin === false && row.pinned === true;
|
|
4541
4475
|
const selectTitle = unpin
|
|
4542
4476
|
? '取消固定后请求会失败,请手动切换'
|
|
4543
|
-
: row.disabled === true
|
|
4544
|
-
? '这个账号已停用:选中它请求会失败,请先启用或换一个'
|
|
4545
4477
|
: `让 ${row.fullName} 成为「当前账号」,请求只用它`;
|
|
4546
4478
|
return jsxs(
|
|
4547
4479
|
'div',
|
|
4548
4480
|
{
|
|
4549
4481
|
className: 'wotr-credit-row',
|
|
4550
|
-
'data-off': row.disabled === true ? '1' : '0',
|
|
4551
4482
|
'data-serving': row.serving === true ? '1' : '0',
|
|
4552
4483
|
children: [
|
|
4553
4484
|
jsxs('div', {
|
|
@@ -5136,29 +5067,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
5136
5067
|
[loadQoder],
|
|
5137
5068
|
);
|
|
5138
5069
|
|
|
5139
|
-
/**
|
|
5140
|
-
* 停用 / 启用:往 `disabledAccountIds` 里加减这个账号。
|
|
5141
|
-
*
|
|
5142
|
-
* 必须**读出当前列表再合并** —— 整段覆盖写会把别的账号的停用状态清掉,
|
|
5143
|
-
* 表现为「停用了 A,B 自己被启用了」。
|
|
5144
|
-
*/
|
|
5145
|
-
const onQoderToggle = useCallback(
|
|
5146
|
-
(accountId, disabled) => {
|
|
5147
|
-
const current = asArray(qoderConfigSnapshot().disabledAccountIds).filter(
|
|
5148
|
-
(id) => typeof id === 'string',
|
|
5149
|
-
);
|
|
5150
|
-
const next = disabled === true
|
|
5151
|
-
? [...new Set([...current, accountId])]
|
|
5152
|
-
: current.filter((id) => id !== accountId);
|
|
5153
|
-
const want = disabled === true;
|
|
5154
|
-
return writeQoderPoolField(
|
|
5155
|
-
'disabledAccountIds',
|
|
5156
|
-
next,
|
|
5157
|
-
(doc) => asArray(doc?.disabledAccountIds).includes(accountId) === want,
|
|
5158
|
-
);
|
|
5159
|
-
},
|
|
5160
|
-
[qoderConfigSnapshot, writeQoderPoolField],
|
|
5161
|
-
);
|
|
5162
5070
|
|
|
5163
5071
|
const onQoderRemove = useCallback(
|
|
5164
5072
|
(accountId) => qoderAction(`${SELF_API}/qoder/pool/remove`, { accountId }),
|
|
@@ -5413,7 +5321,6 @@ export function createConsoleSection({ react, jsxRuntime, embedded }) {
|
|
|
5413
5321
|
creditsAtMs: qoderCreditsAtMs,
|
|
5414
5322
|
onRefresh: loadQoder,
|
|
5415
5323
|
onAddAccount: onQoderAddAccount,
|
|
5416
|
-
onToggle: onQoderToggle,
|
|
5417
5324
|
onRemove: onQoderRemove,
|
|
5418
5325
|
onSelect: onQoderSelect,
|
|
5419
5326
|
// 每日签到(按账号,不切号):正在签的 id / 本页各账号的答复 / 顶部结论。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-kdczyz-mode",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4-beta",
|
|
4
4
|
"description": "WorkBuddy + Trae + Qoder merged into one DSH plugin: the WorkBuddy desktop account/model pool, the Trae connector and the Qoder account pool as three independent providers, plus one settings page with six tabs — two configuration tabs (XD Pool / Trae config: provider switch, account binding, model selection and its own multi-account pool) and four management/read-only tabs (accounts [WorkBuddy + Trae + Qoder, three columns] / Qoder pool / credits / usage history).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -744,7 +744,6 @@ for (const [tabId, label, mustHave] of [
|
|
|
744
744
|
'当前使用',
|
|
745
745
|
'备用',
|
|
746
746
|
'切换到此账号',
|
|
747
|
-
'停用',
|
|
748
747
|
'移出池子',
|
|
749
748
|
// 分配策略三按钮(优先/轮换/均衡)在 Qoder 侧已删除:轮换与均衡会在每次
|
|
750
749
|
// 请求上换账号,正是「固定了账号请求还是打到别号上」的来源。剩下的是一句
|
|
@@ -1562,18 +1561,10 @@ await (async () => {
|
|
|
1562
1561
|
JSON.stringify(found.map((b) => b.label)),
|
|
1563
1562
|
);
|
|
1564
1563
|
ok(
|
|
1565
|
-
'XD Pool
|
|
1566
|
-
buttonOf(found, '
|
|
1564
|
+
'XD Pool 账号区仍有手动控件(切换到此账号)',
|
|
1565
|
+
buttonOf(found, '切换到此账号') !== undefined,
|
|
1567
1566
|
JSON.stringify(found.map((b) => b.label)),
|
|
1568
1567
|
);
|
|
1569
|
-
// 写入类操作仍会刷新池状态:点「停用」走 /accounts/disabled 后重读
|
|
1570
|
-
// ⚠️ 不 await onClick 的返回值:写入后的重读依赖桩接口逐拍落地,
|
|
1571
|
-
// 等一个宏任务就够(与 Qoder 块同款约定)。
|
|
1572
|
-
const beforeStatus = statusCalls;
|
|
1573
|
-
const disableBtn = buttonOf(found, '停用');
|
|
1574
|
-
void disableBtn.onClick();
|
|
1575
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
1576
|
-
ok('XD Pool 点「停用」写入后刷新了池状态', statusCalls > beforeStatus, `${beforeStatus} → ${statusCalls}`);
|
|
1577
1568
|
}
|
|
1578
1569
|
|
|
1579
1570
|
// —— Trae:同样没有分配策略按钮(号池只剩停用这一项设置) ——
|
|
@@ -1585,8 +1576,8 @@ await (async () => {
|
|
|
1585
1576
|
JSON.stringify(found.map((b) => b.label)),
|
|
1586
1577
|
);
|
|
1587
1578
|
ok(
|
|
1588
|
-
'Trae
|
|
1589
|
-
buttonOf(found, '
|
|
1579
|
+
'Trae 账号区仍有手动控件(切换到此账号)',
|
|
1580
|
+
buttonOf(found, '切换到此账号') !== undefined,
|
|
1590
1581
|
JSON.stringify(found.map((b) => b.label)),
|
|
1591
1582
|
);
|
|
1592
1583
|
}
|
|
@@ -1608,7 +1599,7 @@ await (async () => {
|
|
|
1608
1599
|
*
|
|
1609
1600
|
* 这一段原先打在「账号」页右列(Trae)上;那一页删掉后,同一条读回确认链
|
|
1610
1601
|
* 由 trae 配置页的「账号」区走(`writePoolField` → `writeTraeField`),
|
|
1611
|
-
* 所以这里改成 `0: 'trae-config'
|
|
1602
|
+
* 所以这里改成 `0: 'trae-config'`、在那一页里点「切换到此账号」。
|
|
1612
1603
|
*/
|
|
1613
1604
|
await (async () => {
|
|
1614
1605
|
const prevFetch = globalThis.fetch;
|
|
@@ -1641,8 +1632,8 @@ await (async () => {
|
|
|
1641
1632
|
* (表现为「按钮明明在,第二次一个都找不到」)。
|
|
1642
1633
|
*/
|
|
1643
1634
|
const found = collectButtons(Page({}));
|
|
1644
|
-
const button = found.find((b) => b.label === '
|
|
1645
|
-
ok('Trae
|
|
1635
|
+
const button = found.find((b) => b.label === '切换到此账号');
|
|
1636
|
+
ok('Trae 页找得到「切换到此账号」按钮', button !== undefined, JSON.stringify(found.map((b) => b.label)));
|
|
1646
1637
|
// ⚠️ 不 await onClick:写入后的重读打的是没桩的接口(永远 pending),
|
|
1647
1638
|
// 写入本身同步发生,等一个宏任务就够。
|
|
1648
1639
|
void button.onClick();
|
|
@@ -1985,42 +1976,6 @@ await (async () => {
|
|
|
1985
1976
|
&& buttonIn(cardWith(afterCol, 'qoder-user@example.com'), '切换到此账号') !== null,
|
|
1986
1977
|
);
|
|
1987
1978
|
|
|
1988
|
-
// ② 停用 / 启用:写 disabledAccountIds(合并式,不能整段覆盖别人的状态)。
|
|
1989
|
-
// 控件是卡里的「停用 / 启用」按钮(CardActions,三个配置页同款)——
|
|
1990
|
-
// 完全手动语义下「参与轮换」勾选已随分配策略删除,启停就这一颗按钮。
|
|
1991
|
-
writes.length = 0;
|
|
1992
|
-
doc = { ...doc, disabledAccountIds: ['q3'] };
|
|
1993
|
-
react.reset();
|
|
1994
|
-
tree = Page({});
|
|
1995
|
-
const q1Card = cardWith(qoderPanelOf(tree), 'qoder-user@example.com');
|
|
1996
|
-
const disableBtn = buttonIn(q1Card, '停用');
|
|
1997
|
-
ok('账号卡里有「停用」按钮(未停用状态)', disableBtn !== null, JSON.stringify(disableBtn));
|
|
1998
|
-
// ⚠️ 不 await onClick 的返回值:写入之后的「重读号池」打的是本块没桩的接口
|
|
1999
|
-
// (永远 pending),await 它会挂死。写入本身是同步发生的,等一个宏任务就够。
|
|
2000
|
-
void disableBtn.onClick();
|
|
2001
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
2002
|
-
const disabledWrite = writes.find(([f]) => f === 'disabledAccountIds');
|
|
2003
|
-
ok(
|
|
2004
|
-
'点「停用」写 disabledAccountIds 且保留已有条目',
|
|
2005
|
-
Array.isArray(disabledWrite?.[1]) && disabledWrite[1].includes('q1') && disabledWrite[1].includes('q3'),
|
|
2006
|
-
JSON.stringify(disabledWrite ?? null),
|
|
2007
|
-
);
|
|
2008
|
-
|
|
2009
|
-
writes.length = 0;
|
|
2010
|
-
doc = { ...doc, disabledAccountIds: ['q1', 'q3'] };
|
|
2011
|
-
react.reset();
|
|
2012
|
-
tree = Page({});
|
|
2013
|
-
const enableBtn = buttonIn(cardWith(qoderPanelOf(tree), 'qoder-user@example.com'), '启用');
|
|
2014
|
-
ok('已停用的账号卡按钮是「启用」', enableBtn !== null, JSON.stringify(enableBtn));
|
|
2015
|
-
void enableBtn.onClick();
|
|
2016
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
2017
|
-
const enableWrite = writes.find(([f]) => f === 'disabledAccountIds');
|
|
2018
|
-
ok(
|
|
2019
|
-
'点「启用」从 disabledAccountIds 里摘掉它',
|
|
2020
|
-
Array.isArray(enableWrite?.[1]) && !enableWrite[1].includes('q1') && enableWrite[1].includes('q3'),
|
|
2021
|
-
JSON.stringify(enableWrite ?? null),
|
|
2022
|
-
);
|
|
2023
|
-
|
|
2024
1979
|
// ③ 保留额度已随分配策略删除:卡片上不应再有「保留额度」输入框
|
|
2025
1980
|
writes.length = 0;
|
|
2026
1981
|
doc = { ...doc, disabledAccountIds: [], creditReserves: {} };
|
|
@@ -2202,7 +2157,6 @@ await (async () => {
|
|
|
2202
2157
|
'trae-backup@example.com', // 号池账号卡
|
|
2203
2158
|
'当前使用', // 首标签:选中的那张亮着
|
|
2204
2159
|
'备用',
|
|
2205
|
-
'停用', // 完全手动语义下号池只剩启停这一项设置
|
|
2206
2160
|
'切换到此账号',
|
|
2207
2161
|
]) {
|
|
2208
2162
|
ok(`Trae 配置页渲染出「${must}」`, text.includes(must), text.slice(0, 200));
|
|
@@ -2289,11 +2243,6 @@ await (async () => {
|
|
|
2289
2243
|
globalThis.fetch = prevFetch;
|
|
2290
2244
|
}
|
|
2291
2245
|
ok('号池快照为空时 Trae 页仍渲染', err === null && text.includes('trae-user@example.com'), err?.message);
|
|
2292
|
-
ok(
|
|
2293
|
-
'号池快照为空时 Trae 账号区仍有「停用」按钮(启停不因快照为空而消失)',
|
|
2294
|
-
text.includes('停用'),
|
|
2295
|
-
text.slice(0, 240),
|
|
2296
|
-
);
|
|
2297
2246
|
ok(
|
|
2298
2247
|
'号池快照为空时 Trae 账号区没有分配策略按钮',
|
|
2299
2248
|
['优先模式', '轮换模式', '均衡模式'].every((l) => text.includes(l) === false),
|
|
@@ -2368,7 +2317,6 @@ await (async () => {
|
|
|
2368
2317
|
'冷却备用号',
|
|
2369
2318
|
'备用', // 首标签与另两页同款
|
|
2370
2319
|
'当前使用',
|
|
2371
|
-
'停用', // 启停按钮(完全手动语义下号池只剩这一项设置)
|
|
2372
2320
|
'移出池子',
|
|
2373
2321
|
'已移出账号池', // ignored 区
|
|
2374
2322
|
'已移出号',
|
|
@@ -2564,99 +2512,6 @@ await (async () => {
|
|
|
2564
2512
|
}
|
|
2565
2513
|
})();
|
|
2566
2514
|
|
|
2567
|
-
/**
|
|
2568
|
-
* —— 2g-3) XD Pool 写入:点「停用」要走 /accounts/disabled 路由并刷新状态 ——
|
|
2569
|
-
*
|
|
2570
|
-
* 只断言渲染不够 —— 控件画出来了但 onClick 写错地方,测试照样全绿。
|
|
2571
|
-
* 完全手动语义下 WorkBuddy 号池只剩启停这一项设置,且启停**必须走 HTTP 路由**
|
|
2572
|
-
* (`/accounts/disabled` 在服务端做跨区域合并,不能直接写 settings —— 那会把
|
|
2573
|
-
* 另一区域已停用的账号静默重新启用)。这里抓出按钮的 onClick 真点一次,
|
|
2574
|
-
* 断言请求打到了路由上,并且之后真的刷新了状态。
|
|
2575
|
-
*/
|
|
2576
|
-
await (async () => {
|
|
2577
|
-
const writes = [];
|
|
2578
|
-
let statusCalls = 0;
|
|
2579
|
-
let disabledCalls = 0;
|
|
2580
|
-
const prevFetch = globalThis.fetch;
|
|
2581
|
-
globalThis.fetch = (url) => {
|
|
2582
|
-
if (typeof url === 'string' && url.includes('/plugins/dsh-workbuddy-xdpool/status')) {
|
|
2583
|
-
statusCalls += 1;
|
|
2584
|
-
return Promise.resolve({ ok: true, json: () => Promise.resolve(poolStatusFixture) });
|
|
2585
|
-
}
|
|
2586
|
-
if (typeof url === 'string' && url.includes('/plugins/dsh-workbuddy-xdpool/accounts/disabled')) {
|
|
2587
|
-
disabledCalls += 1;
|
|
2588
|
-
return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
|
|
2589
|
-
}
|
|
2590
|
-
return new Promise(() => {});
|
|
2591
|
-
};
|
|
2592
|
-
try {
|
|
2593
|
-
const react = makeReact({ 0: 'pool-config' }, true);
|
|
2594
|
-
const jsxRuntime = makeJsxRuntime();
|
|
2595
|
-
const wbScope = makeScope({});
|
|
2596
|
-
wbScope.set = (field, value) => {
|
|
2597
|
-
writes.push([field, value]);
|
|
2598
|
-
return Promise.resolve(true);
|
|
2599
|
-
};
|
|
2600
|
-
const Page = createConsoleSection({
|
|
2601
|
-
react,
|
|
2602
|
-
jsxRuntime,
|
|
2603
|
-
embedded: { wbSettings: makeHandle(wbScope), traeSettings: makeHandle(makeTraeScope({})) },
|
|
2604
|
-
});
|
|
2605
|
-
const collectAll = (node) => {
|
|
2606
|
-
const out = [];
|
|
2607
|
-
collectText(node, out);
|
|
2608
|
-
return out.join(' | ');
|
|
2609
|
-
};
|
|
2610
|
-
collectAll(Page({}));
|
|
2611
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
2612
|
-
react.reset();
|
|
2613
|
-
const tree = Page({});
|
|
2614
|
-
|
|
2615
|
-
/** 在元素树里找文字匹配的按钮并取 onClick */
|
|
2616
|
-
const findButton = (label) => {
|
|
2617
|
-
let handler = null;
|
|
2618
|
-
(function walk(n, d = 0) {
|
|
2619
|
-
if (handler !== null || d > 40 || n === null || typeof n !== 'object') return;
|
|
2620
|
-
if (Array.isArray(n)) {
|
|
2621
|
-
for (const c of n) walk(c, d);
|
|
2622
|
-
return;
|
|
2623
|
-
}
|
|
2624
|
-
const { type, props } = n;
|
|
2625
|
-
if (typeof type === 'string' && type === 'button' && props?.children === label) {
|
|
2626
|
-
handler = props.onClick;
|
|
2627
|
-
return;
|
|
2628
|
-
}
|
|
2629
|
-
if (typeof type === 'function') {
|
|
2630
|
-
try {
|
|
2631
|
-
walk(type(props ?? {}), d + 1);
|
|
2632
|
-
} catch {
|
|
2633
|
-
/* 忽略渲染失败的子树 */
|
|
2634
|
-
}
|
|
2635
|
-
return;
|
|
2636
|
-
}
|
|
2637
|
-
if (typeof type === 'string') walk(props?.children, d + 1);
|
|
2638
|
-
})(tree);
|
|
2639
|
-
return handler;
|
|
2640
|
-
};
|
|
2641
|
-
|
|
2642
|
-
const before = statusCalls;
|
|
2643
|
-
const onClick = findButton('停用');
|
|
2644
|
-
ok('找得到「停用」按钮', typeof onClick === 'function');
|
|
2645
|
-
// ⚠️ 不 await onClick:写入后的重读走的是桩接口(已 stub,但链是异步的),
|
|
2646
|
-
// 等一个宏任务再断言计数。
|
|
2647
|
-
void onClick();
|
|
2648
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
2649
|
-
ok(
|
|
2650
|
-
'点击「停用」打到 /accounts/disabled 路由(而非 settings 字段)',
|
|
2651
|
-
disabledCalls === 1 && writes.length === 0,
|
|
2652
|
-
`disabledCalls=${disabledCalls} writes=${JSON.stringify(writes)}`,
|
|
2653
|
-
);
|
|
2654
|
-
ok('写入后刷新了池状态', statusCalls > before, `${before} → ${statusCalls}`);
|
|
2655
|
-
} finally {
|
|
2656
|
-
globalThis.fetch = prevFetch;
|
|
2657
|
-
}
|
|
2658
|
-
})();
|
|
2659
|
-
|
|
2660
2515
|
/**
|
|
2661
2516
|
* —— 2g-3c) XD Pool「切换到此账号 / 取消固定」:真点一次,断言请求打对了地方 ——
|
|
2662
2517
|
*
|
|
@@ -3260,101 +3115,6 @@ await (async () => {
|
|
|
3260
3115
|
}
|
|
3261
3116
|
})();
|
|
3262
3117
|
|
|
3263
|
-
/**
|
|
3264
|
-
* —— 2g-3b) XD Pool 的账号启停必须走路由,不能整段覆盖设置 ——
|
|
3265
|
-
*
|
|
3266
|
-
* `disabledAccountIds` 是国内版与国际版**共用**的一份扁平数组(两个 provider 注册
|
|
3267
|
-
* 的是同一个 `settingsNs` + `settingsPath: []`),而配置页一次只拿得到一个区域的
|
|
3268
|
-
* 账号列表。照本区域的名单拼一份完整数组写回设置,另一个区域里已停用的账号会被
|
|
3269
|
-
* **静默重新启用** —— 那份名单只存在于设置文档里,这一页上根本看不见。
|
|
3270
|
-
*
|
|
3271
|
-
* 所以断言两件事,缺一不可:
|
|
3272
|
-
* 1. `POST /plugins/dsh-workbuddy-xdpool/accounts/disabled` 发出去了;
|
|
3273
|
-
* 2. **没有**顺手写 `disabledAccountIds` 设置 —— 后者才是这条回归的真正形态
|
|
3274
|
-
* (只断言 ① 的话,将来「路由 + 顺手再写一次设置」也能全绿)。
|
|
3275
|
-
*/
|
|
3276
|
-
await (async () => {
|
|
3277
|
-
const writes = [];
|
|
3278
|
-
const calls = [];
|
|
3279
|
-
const prevFetch = globalThis.fetch;
|
|
3280
|
-
globalThis.fetch = (url, init) => {
|
|
3281
|
-
const u = String(url);
|
|
3282
|
-
if (u.includes('/plugins/dsh-workbuddy-xdpool/status')) {
|
|
3283
|
-
return Promise.resolve({ ok: true, json: () => Promise.resolve(poolStatusFixture) });
|
|
3284
|
-
}
|
|
3285
|
-
if (u.includes('/accounts/disabled')) {
|
|
3286
|
-
calls.push({ url: u, method: init?.method, body: init?.body });
|
|
3287
|
-
return Promise.resolve({
|
|
3288
|
-
ok: true,
|
|
3289
|
-
json: () => Promise.resolve({ ok: true, accountId: 'u1', disabled: true }),
|
|
3290
|
-
});
|
|
3291
|
-
}
|
|
3292
|
-
return new Promise(() => {});
|
|
3293
|
-
};
|
|
3294
|
-
try {
|
|
3295
|
-
const react = makeReact({ 0: 'pool-config' }, true);
|
|
3296
|
-
const wbScope = makeScope({ disabledAccountIds: ['另一个区域的账号'] });
|
|
3297
|
-
wbScope.set = (field, value) => {
|
|
3298
|
-
writes.push([field, value]);
|
|
3299
|
-
return Promise.resolve(true);
|
|
3300
|
-
};
|
|
3301
|
-
const Page = createConsoleSection({
|
|
3302
|
-
react,
|
|
3303
|
-
jsxRuntime: makeJsxRuntime(),
|
|
3304
|
-
embedded: { wbSettings: makeHandle(wbScope), traeSettings: makeHandle(makeTraeScope({})) },
|
|
3305
|
-
});
|
|
3306
|
-
collectText(Page({}), []);
|
|
3307
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
3308
|
-
react.reset();
|
|
3309
|
-
const tree = Page({});
|
|
3310
|
-
|
|
3311
|
-
const card = collectByClass(tree, 'wbc-card').find((c) => textOfNode(c).includes('池内主号'));
|
|
3312
|
-
ok('找得到「池内主号」那张账号卡', card !== undefined);
|
|
3313
|
-
/** 卡里那个「停用」按钮(启停控件,CardActions 三个配置页同款) */
|
|
3314
|
-
const buttons = [];
|
|
3315
|
-
(function walk(n, d = 0) {
|
|
3316
|
-
if (n === null || n === undefined || typeof n !== 'object' || d > 40) return;
|
|
3317
|
-
if (Array.isArray(n)) {
|
|
3318
|
-
for (const c of n) walk(c, d);
|
|
3319
|
-
return;
|
|
3320
|
-
}
|
|
3321
|
-
if (typeof n.type === 'function') {
|
|
3322
|
-
try {
|
|
3323
|
-
walk(n.type(n.props ?? {}), d + 1);
|
|
3324
|
-
} catch {
|
|
3325
|
-
/* 渲染失败的子树跳过 */
|
|
3326
|
-
}
|
|
3327
|
-
return;
|
|
3328
|
-
}
|
|
3329
|
-
if (n.type === 'button' && n.props?.children === '停用') buttons.push(n);
|
|
3330
|
-
walk(n.props?.children, d + 1);
|
|
3331
|
-
})(card);
|
|
3332
|
-
ok('账号卡里有「停用」按钮(完全手动语义下启停就这一颗)', buttons.length === 1, `n=${buttons.length}`);
|
|
3333
|
-
// ⚠️ 不 await onClick:写入后的重读依赖桩接口逐拍落地,等一个宏任务就够。
|
|
3334
|
-
void buttons[0]?.props?.onClick?.();
|
|
3335
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
3336
|
-
|
|
3337
|
-
ok(
|
|
3338
|
-
'点「停用」打的是 /accounts/disabled 路由',
|
|
3339
|
-
calls.length === 1 && calls[0].method === 'POST' && calls[0].url.includes('/accounts/disabled'),
|
|
3340
|
-
calls.map((c) => `${c.method} ${c.url}`).join(' | '),
|
|
3341
|
-
);
|
|
3342
|
-
ok(
|
|
3343
|
-
'路由请求体只带这一个账号',
|
|
3344
|
-
JSON.parse(String(calls[0]?.body ?? '{}')).accountId === 'u1' &&
|
|
3345
|
-
JSON.parse(String(calls[0]?.body ?? '{}')).disabled === true,
|
|
3346
|
-
String(calls[0]?.body),
|
|
3347
|
-
);
|
|
3348
|
-
ok(
|
|
3349
|
-
'没有顺手整段覆盖 disabledAccountIds 设置(另一个区域的名单会被抹掉)',
|
|
3350
|
-
!writes.some(([f]) => f === 'disabledAccountIds'),
|
|
3351
|
-
JSON.stringify(writes),
|
|
3352
|
-
);
|
|
3353
|
-
} finally {
|
|
3354
|
-
globalThis.fetch = prevFetch;
|
|
3355
|
-
}
|
|
3356
|
-
})();
|
|
3357
|
-
|
|
3358
3118
|
/**
|
|
3359
3119
|
* —— 2g-4) XD Pool 模型保存:整段覆盖 + 拒绝空启用列表 ——
|
|
3360
3120
|
*
|
|
@@ -4713,7 +4473,6 @@ console.log('\n胶囊展开的账号卡片\n');
|
|
|
4713
4473
|
findByClass(findRow(node, '国内号'), 'wotr-credit-mini') !== null,
|
|
4714
4474
|
text,
|
|
4715
4475
|
);
|
|
4716
|
-
ok('国际版那一行标着「已停用」', (() => { const o = []; collectText(findRow(node, '国际号'), o); return o.join('').includes('已停用'); })(), text);
|
|
4717
4476
|
globalThis.fetch = prevFetch;
|
|
4718
4477
|
}
|
|
4719
4478
|
|
|
@@ -5221,7 +4980,7 @@ const parityTrae = {
|
|
|
5221
4980
|
return new Promise(() => {});
|
|
5222
4981
|
};
|
|
5223
4982
|
try {
|
|
5224
|
-
for (const [tabId, label] of [['pool-config', 'XD Pool']
|
|
4983
|
+
for (const [tabId, label] of [['pool-config', 'XD Pool']]) {
|
|
5225
4984
|
const react = makeReact({ 0: tabId }, true);
|
|
5226
4985
|
const Page = createConsoleSection({ react, jsxRuntime: makeJsxRuntime(), embedded: makeEmbedded() });
|
|
5227
4986
|
// 同 moduleSequenceOf:要遍历才会执行子组件、发出请求
|
|
@@ -5253,7 +5012,7 @@ const parityTrae = {
|
|
|
5253
5012
|
const texts = [];
|
|
5254
5013
|
collectText(props?.children, texts);
|
|
5255
5014
|
const joined = texts.join('');
|
|
5256
|
-
return joined.includes('
|
|
5015
|
+
return joined.includes('移出池子');
|
|
5257
5016
|
}
|
|
5258
5017
|
return cardHasDisable(props?.children, depth + 1);
|
|
5259
5018
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kdczyz-dsh-model",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4-beta",
|
|
4
4
|
"description": "Installable wrapper for the DSH plugin in dsh-kdczyz-mode/: WorkBuddy model pool + Trae connector + Qoder account pool as three independent providers, plus one settings page with six tabs (XD Pool / Trae config [provider switch, account binding, model selection, account pool] / accounts [WorkBuddy + Trae + Qoder, three columns] / Qoder pool [device-flow login, per-account credits, cooldown] / credits / usage history). This root manifest exists so the DSH plugin manager can install the package: it declares dsh.bundle.patch at its root and re-exports the inner package's entry points. It deliberately declares NO dsh.client and NO ./client export - the client half belongs to the inner dsh-kdczyz-mode package. Re-exporting ./client here would make two manifests claim the same client.js; DSH emits one client bundle per claiming package, and the duplicate makes the renderer throw 'Identifier XDPOOL_CLIENT_SRC has already been declared', which fails web boot and prevents the app from starting.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|