dsh-workbuddy-xdpool 0.4.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +220 -153
- package/LICENSE +1 -1
- package/README.en.md +163 -163
- package/README.md +181 -181
- package/lib/bin.js +110 -10
- package/lib/client.js +109 -13
- package/lib/index.d.ts +72 -8
- package/lib/index.js +186 -27
- package/package.json +126 -109
package/lib/client.js
CHANGED
|
@@ -28,6 +28,8 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
const POOL_RESET_COOLDOWN_PATH = "/plugins/dsh-workbuddy-xdpool/cooldowns/reset";
|
|
29
29
|
/** Plugin-owned daily check-in action endpoint (claim today's reward). */
|
|
30
30
|
const POOL_CHECKIN_PATH = "/plugins/dsh-workbuddy-xdpool/checkin";
|
|
31
|
+
/** Switch one account in or out of the pool (card toggle). */
|
|
32
|
+
const POOL_ACCOUNT_DISABLE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/disabled";
|
|
31
33
|
//#endregion
|
|
32
34
|
//#region src/client/icon.ts
|
|
33
35
|
/**
|
|
@@ -133,6 +135,13 @@ window.__ModuleLoader__.load({
|
|
|
133
135
|
.dsm-workbuddy-xdpool-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
|
|
134
136
|
.dsm-workbuddy-xdpool-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
|
|
135
137
|
.dsm-workbuddy-xdpool-account-label{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
138
|
+
.dsm-workbuddy-xdpool-account-head{display:flex;align-items:center;gap:8px;min-width:0}
|
|
139
|
+
/* Account switched off on the card: still listed (so it can be turned back on) but visually muted. */
|
|
140
|
+
.dsm-workbuddy-xdpool-account-off{opacity:.55}
|
|
141
|
+
/* Small pill switch: "in rotation" vs "off". Native checkbox styled by the label. */
|
|
142
|
+
.dsm-workbuddy-xdpool-account-toggle{display:inline-flex;align-items:center;gap:5px;flex:none;cursor:pointer;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px;white-space:nowrap}
|
|
143
|
+
.dsm-workbuddy-xdpool-account-toggle input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
|
|
144
|
+
.dsm-workbuddy-xdpool-account-toggle:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}
|
|
136
145
|
.dsm-workbuddy-xdpool-account-tags{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
137
146
|
.dsm-workbuddy-xdpool-account-tag{padding:1px 8px;border-radius:999px;font-size:11px;line-height:18px;background:var(--dsw-alias-state-success-subtle,rgba(34,160,107,.12));color:var(--dsw-alias-state-success-primary,#22a06b)}
|
|
138
147
|
.dsm-workbuddy-xdpool-account-tag-cooling{background:var(--dsw-alias-state-warning-subtle,rgba(217,119,6,.15));color:var(--dsw-alias-state-warning-primary,#d97706)}
|
|
@@ -374,12 +383,26 @@ window.__ModuleLoader__.load({
|
|
|
374
383
|
const [flash, setFlash] = (0, react.useState)(void 0);
|
|
375
384
|
/** Account id whose daily claim is currently in flight. */
|
|
376
385
|
const [checkinBusyId, setCheckinBusyId] = (0, react.useState)(void 0);
|
|
386
|
+
/** Account id whose enable/disable switch is in flight, if any. */
|
|
387
|
+
const [accountBusyId, setAccountBusyId] = (0, react.useState)(void 0);
|
|
377
388
|
/**
|
|
378
389
|
* Draft model selection. `undefined` means "no local edits"; once a checkbox
|
|
379
390
|
* is touched the draft takes over and is what the Save button posts. Discard
|
|
380
391
|
* drops it back to the copy the server last reported.
|
|
381
392
|
*/
|
|
382
|
-
const [
|
|
393
|
+
const [draftByRegion, setDraftByRegion] = (0, react.useState)({});
|
|
394
|
+
/**
|
|
395
|
+
* The draft for the tab on screen. Keyed by region: the two gateways have
|
|
396
|
+
* different rosters, so edits made on one tab must not leak into the other
|
|
397
|
+
* when the user switches tabs (or saves).
|
|
398
|
+
*/
|
|
399
|
+
const draft = draftByRegion[activeRegion];
|
|
400
|
+
const setDraft = (next) => {
|
|
401
|
+
setDraftByRegion((prev) => ({
|
|
402
|
+
...prev,
|
|
403
|
+
[activeRegion]: next
|
|
404
|
+
}));
|
|
405
|
+
};
|
|
383
406
|
const [savingModels, setSavingModels] = (0, react.useState)(false);
|
|
384
407
|
const mounted = (0, react.useRef)(true);
|
|
385
408
|
(0, react.useEffect)(() => {
|
|
@@ -497,6 +520,41 @@ window.__ModuleLoader__.load({
|
|
|
497
520
|
if (mounted.current) setCheckinBusyId(void 0);
|
|
498
521
|
}
|
|
499
522
|
};
|
|
523
|
+
/**
|
|
524
|
+
* Switch one account in or out of the pool.
|
|
525
|
+
*
|
|
526
|
+
* The id travels in the body and the host validates it against the accounts it
|
|
527
|
+
* really knows, so a stale tab cannot write an orphan id. Disabling only stops
|
|
528
|
+
* the account from being picked — it stays listed so it can be turned back on —
|
|
529
|
+
* and the change is saved through the settings section, so it survives a
|
|
530
|
+
* restart and is re-applied after every re-scan.
|
|
531
|
+
*/
|
|
532
|
+
const toggleAccountDisabled = async (accountId, disabled) => {
|
|
533
|
+
setAccountBusyId(accountId);
|
|
534
|
+
setFlash(void 0);
|
|
535
|
+
try {
|
|
536
|
+
const response = await fetch(POOL_ACCOUNT_DISABLE_PATH, {
|
|
537
|
+
method: "POST",
|
|
538
|
+
headers: {
|
|
539
|
+
accept: "application/json",
|
|
540
|
+
"content-type": "application/json"
|
|
541
|
+
},
|
|
542
|
+
credentials: "same-origin",
|
|
543
|
+
body: JSON.stringify({
|
|
544
|
+
accountId,
|
|
545
|
+
disabled
|
|
546
|
+
})
|
|
547
|
+
});
|
|
548
|
+
const body = await response.json().catch(() => void 0);
|
|
549
|
+
if (!response.ok) throw new Error(body?.error ?? `HTTP ${response.status}`);
|
|
550
|
+
await refresh(activeRegion);
|
|
551
|
+
} catch (cause) {
|
|
552
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
553
|
+
if (mounted.current) setError(t?.("row.accountToggleError", { message }) ?? `Could not switch the account: ${message}`);
|
|
554
|
+
} finally {
|
|
555
|
+
if (mounted.current) setAccountBusyId(void 0);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
500
558
|
/** Keep the draft in step with the server copy while nothing is dirty. */
|
|
501
559
|
const modelDraft = draft ?? (status === void 0 ? {} : draftFromStatus(status));
|
|
502
560
|
/** Model edits need a writable settings scope; otherwise the rows are read-only. */
|
|
@@ -600,9 +658,12 @@ window.__ModuleLoader__.load({
|
|
|
600
658
|
const imageModelIds = Object.entries(draft).filter(([, e]) => e.images).map(([id]) => id);
|
|
601
659
|
const contextBudgets = {};
|
|
602
660
|
for (const [id, entry] of Object.entries(draft)) if (entry.budget !== void 0) contextBudgets[id] = entry.budget;
|
|
603
|
-
|
|
604
|
-
await write.call(settingsScope,
|
|
605
|
-
|
|
661
|
+
const key = activeRegion === "cn" ? "modelSelectionCn" : "modelSelectionGlobal";
|
|
662
|
+
await write.call(settingsScope, key, {
|
|
663
|
+
enabledModelIds,
|
|
664
|
+
imageModelIds,
|
|
665
|
+
contextBudgets
|
|
666
|
+
});
|
|
606
667
|
setDraft(void 0);
|
|
607
668
|
if (mounted.current) setFlash(t?.("row.modelsSaved") ?? "Saved");
|
|
608
669
|
} catch (cause) {
|
|
@@ -707,17 +768,21 @@ window.__ModuleLoader__.load({
|
|
|
707
768
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
708
769
|
className: "dsm-workbuddy-xdpool-dist-title",
|
|
709
770
|
children: t?.("row.distTitle") ?? "Account usage"
|
|
710
|
-
}), [
|
|
771
|
+
}), [
|
|
772
|
+
"priority",
|
|
773
|
+
"balanced",
|
|
774
|
+
"round-robin"
|
|
775
|
+
].map((option) => {
|
|
711
776
|
const active = (status.distribution ?? "priority") === option;
|
|
712
|
-
const label = option === "priority" ? t?.("row.distPriority") ?? "Priority" : t?.("row.distRoundRobin") ?? "Round-robin";
|
|
713
|
-
const hint = option === "priority" ? t?.("row.distPriorityHint") ?? "" : t?.("row.distRoundRobinHint") ?? "";
|
|
777
|
+
const label = option === "priority" ? t?.("row.distPriority") ?? "Priority" : option === "balanced" ? t?.("row.distBalanced") ?? "Balanced" : t?.("row.distRoundRobin") ?? "Round-robin";
|
|
778
|
+
const hint = option === "priority" ? t?.("row.distPriorityHint") ?? "" : option === "balanced" ? t?.("row.distBalancedHint") ?? "" : t?.("row.distRoundRobinHint") ?? "";
|
|
714
779
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
715
780
|
type: "button",
|
|
716
781
|
role: "radio",
|
|
717
782
|
"aria-checked": active,
|
|
718
783
|
title: hint,
|
|
719
784
|
disabled: !modelsEditable,
|
|
720
|
-
className:
|
|
785
|
+
className: "dsm-workbuddy-xdpool-dist-option" + (active ? " dsm-workbuddy-xdpool-dist-option-active" : ""),
|
|
721
786
|
onClick: () => {
|
|
722
787
|
setDistribution(option);
|
|
723
788
|
},
|
|
@@ -805,6 +870,10 @@ window.__ModuleLoader__.load({
|
|
|
805
870
|
onClaimCheckin: (accountId) => {
|
|
806
871
|
claimCheckin(accountId);
|
|
807
872
|
},
|
|
873
|
+
onToggleDisabled: (accountId, disabled) => {
|
|
874
|
+
toggleAccountDisabled(accountId, disabled);
|
|
875
|
+
},
|
|
876
|
+
...accountBusyId === void 0 ? {} : { accountBusyId },
|
|
808
877
|
t
|
|
809
878
|
}, account.id))]
|
|
810
879
|
}) : null,
|
|
@@ -865,8 +934,9 @@ window.__ModuleLoader__.load({
|
|
|
865
934
|
});
|
|
866
935
|
}
|
|
867
936
|
/** One account block: label + status tag + meta + optional credit panels. */
|
|
868
|
-
function AccountBlock({ account, activeAccountId, t, checkinBusyId, onClaimCheckin }) {
|
|
937
|
+
function AccountBlock({ account, activeAccountId, t, checkinBusyId, onClaimCheckin, accountBusyId, onToggleDisabled }) {
|
|
869
938
|
const isActive = account.id === activeAccountId;
|
|
939
|
+
const isDisabled = account.disabled === true;
|
|
870
940
|
const isCooling = account.cooling === true;
|
|
871
941
|
const cooldownUntil = account.cooldownUntil !== void 0 ? Date.parse(account.cooldownUntil) : void 0;
|
|
872
942
|
const modelCooldowns = account.modelCooldowns ?? [];
|
|
@@ -878,13 +948,27 @@ window.__ModuleLoader__.load({
|
|
|
878
948
|
cls: "dsm-workbuddy-xdpool-account-tag dsm-workbuddy-xdpool-account-tag-cooling"
|
|
879
949
|
} : null;
|
|
880
950
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
881
|
-
className: "dsm-workbuddy-xdpool-account",
|
|
951
|
+
className: isDisabled ? "dsm-workbuddy-xdpool-account dsm-workbuddy-xdpool-account-off" : "dsm-workbuddy-xdpool-account",
|
|
882
952
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
883
953
|
className: "dsm-workbuddy-xdpool-account-copy",
|
|
884
954
|
children: [
|
|
885
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
886
|
-
className: "dsm-workbuddy-xdpool-account-
|
|
887
|
-
children:
|
|
955
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
956
|
+
className: "dsm-workbuddy-xdpool-account-head",
|
|
957
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
958
|
+
className: "dsm-workbuddy-xdpool-account-label",
|
|
959
|
+
children: account.label
|
|
960
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
961
|
+
className: "dsm-workbuddy-xdpool-account-toggle",
|
|
962
|
+
title: t?.("row.accountToggleHint") ?? "Include this account in the pool",
|
|
963
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
964
|
+
type: "checkbox",
|
|
965
|
+
checked: !isDisabled,
|
|
966
|
+
disabled: accountBusyId === account.id,
|
|
967
|
+
onChange: (event) => {
|
|
968
|
+
onToggleDisabled(account.id, !event.target.checked);
|
|
969
|
+
}
|
|
970
|
+
}), isDisabled ? t?.("row.accountOff") ?? "Off" : t?.("row.accountInRotation") ?? "In rotation"]
|
|
971
|
+
})]
|
|
888
972
|
}),
|
|
889
973
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
890
974
|
className: "dsm-workbuddy-xdpool-account-tags",
|
|
@@ -1171,6 +1255,10 @@ window.__ModuleLoader__.load({
|
|
|
1171
1255
|
"row.ok": "Healthy — requests auto-rotate across accounts",
|
|
1172
1256
|
"row.allCooling": "Every account is rate-limited right now; requests pause until a cooldown lifts.",
|
|
1173
1257
|
"row.accountNext": "Next up",
|
|
1258
|
+
"row.accountInRotation": "In rotation",
|
|
1259
|
+
"row.accountOff": "Off",
|
|
1260
|
+
"row.accountToggleHint": "Include this account in the pool",
|
|
1261
|
+
"row.accountToggleError": "Could not switch the account: {message}",
|
|
1174
1262
|
"row.cooling": "Cooling (rate-limited)",
|
|
1175
1263
|
"row.cooldownHits": "{hits} hit(s)",
|
|
1176
1264
|
"row.cooldownUntil": "until {time}",
|
|
@@ -1223,6 +1311,8 @@ window.__ModuleLoader__.load({
|
|
|
1223
1311
|
"row.distPriorityHint": "Drain one account before moving to the next",
|
|
1224
1312
|
"row.distRoundRobin": "Round-robin",
|
|
1225
1313
|
"row.distRoundRobinHint": "Spread the spend evenly across accounts",
|
|
1314
|
+
"row.distBalanced": "Balanced",
|
|
1315
|
+
"row.distBalancedHint": "Draw from every account, favouring whichever has been idle longest",
|
|
1226
1316
|
"row.accountsScanning": "Detecting…",
|
|
1227
1317
|
"row.resetCooldowns": "Clear all cooldowns",
|
|
1228
1318
|
"row.resetCooldownsBusy": "Clearing…",
|
|
@@ -1257,6 +1347,10 @@ window.__ModuleLoader__.load({
|
|
|
1257
1347
|
"row.ok": "运行健康 —— 请求会在各账号间自动轮换",
|
|
1258
1348
|
"row.allCooling": "当前所有账号都处于限流冷却,请求会暂停直到某个冷却结束。",
|
|
1259
1349
|
"row.accountNext": "下一个",
|
|
1350
|
+
"row.accountInRotation": "参与轮换",
|
|
1351
|
+
"row.accountOff": "已停用",
|
|
1352
|
+
"row.accountToggleHint": "把这个账号计入池子",
|
|
1353
|
+
"row.accountToggleError": "切换账号失败:{message}",
|
|
1260
1354
|
"row.cooling": "冷却中(被限流)",
|
|
1261
1355
|
"row.cooldownHits": "触发 {hits} 次",
|
|
1262
1356
|
"row.cooldownUntil": "至 {time}",
|
|
@@ -1309,6 +1403,8 @@ window.__ModuleLoader__.load({
|
|
|
1309
1403
|
"row.distPriorityHint": "优先用完一个账号再换下一个",
|
|
1310
1404
|
"row.distRoundRobin": "轮换模式",
|
|
1311
1405
|
"row.distRoundRobinHint": "积分平均分摊到各账号",
|
|
1406
|
+
"row.distBalanced": "均衡",
|
|
1407
|
+
"row.distBalancedHint": "每个账号都参与抽取,闲置越久权重越高",
|
|
1312
1408
|
"row.accountsScanning": "正在检测…",
|
|
1313
1409
|
"row.resetCooldowns": "清除所有冷却",
|
|
1314
1410
|
"row.resetCooldownsBusy": "正在清除…",
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { Api, Model } from "@earendil-works/pi-ai";
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
4
|
+
import { Context, Context as Context$1 } from "@deepseek-ai/cordis";
|
|
5
5
|
import { SettingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
6
6
|
//#region src/upstream.d.ts
|
|
7
7
|
/** Upstream failure classes the shim maps onto distinct HTTP answers. */
|
|
@@ -228,7 +228,7 @@ export declare function workbuddyAccountId(credential: Pick<WorkBuddyCredential,
|
|
|
228
228
|
/** Every directory the pool should scan, in probe order. */
|
|
229
229
|
export declare function candidateAuthDirs(env?: NodeJS.ProcessEnv): string[];
|
|
230
230
|
/** How the pool chooses which account serves the next request. */
|
|
231
|
-
type AccountDistribution = 'priority' | 'round-robin';
|
|
231
|
+
type AccountDistribution = 'priority' | 'round-robin' | 'balanced';
|
|
232
232
|
interface AccountPoolOptions {
|
|
233
233
|
/** Logger for discovery and rotation events. */
|
|
234
234
|
logger?: {
|
|
@@ -256,10 +256,6 @@ interface AccountPoolOptions {
|
|
|
256
256
|
*/
|
|
257
257
|
distribution?: AccountDistribution;
|
|
258
258
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Read-only pool of every discovered WorkBuddy account, with rate-limit
|
|
261
|
-
* cooldown and round-robin failover.
|
|
262
|
-
*/
|
|
263
259
|
export declare class WorkBuddyAccountPool {
|
|
264
260
|
private readonly logger;
|
|
265
261
|
private authDirs;
|
|
@@ -272,6 +268,24 @@ export declare class WorkBuddyAccountPool {
|
|
|
272
268
|
private cursor;
|
|
273
269
|
private lastScanAtMs;
|
|
274
270
|
private preferredId;
|
|
271
|
+
/**
|
|
272
|
+
* Account ids the user switched off on the card.
|
|
273
|
+
*
|
|
274
|
+
* Disabling is a user preference rather than a property of the credential:
|
|
275
|
+
* `scan()` rebuilds every account object from the auth files, so the set
|
|
276
|
+
* lives on the pool and is re-applied from settings after each scan.
|
|
277
|
+
*/
|
|
278
|
+
private disabledIds;
|
|
279
|
+
/**
|
|
280
|
+
* Last time each account served a request, epoch ms. Drives the idle term
|
|
281
|
+
* of the priority-mode weighting below: an account that just served loses to
|
|
282
|
+
* one that has been idle, so a small pool stops hammering a single account.
|
|
283
|
+
*
|
|
284
|
+
* In-memory on purpose: it only biases the next pick, so a cold start that
|
|
285
|
+
* treats every account as idle is the right default. Not keyed by id lookup
|
|
286
|
+
* misses because a removed account simply disappears from the map on re-scan.
|
|
287
|
+
*/
|
|
288
|
+
private lastUsedAt;
|
|
275
289
|
private refreshInflight;
|
|
276
290
|
constructor(options?: AccountPoolOptions);
|
|
277
291
|
/**
|
|
@@ -283,6 +297,7 @@ export declare class WorkBuddyAccountPool {
|
|
|
283
297
|
authDirs?: readonly string[];
|
|
284
298
|
cooldownMs?: number;
|
|
285
299
|
distribution?: AccountDistribution;
|
|
300
|
+
disabledAccountIds?: readonly string[];
|
|
286
301
|
}): void;
|
|
287
302
|
/** Rescan the auth directories and merge newly discovered accounts. */
|
|
288
303
|
scan(): Promise<WorkBuddyAccount[]>;
|
|
@@ -298,6 +313,22 @@ export declare class WorkBuddyAccountPool {
|
|
|
298
313
|
* model, e.g. CLI diagnostics).
|
|
299
314
|
*/
|
|
300
315
|
private available;
|
|
316
|
+
/** Round-robin: the legacy cursor walk, kept for the distribution that asks for it. */
|
|
317
|
+
private pickRoundRobin;
|
|
318
|
+
/**
|
|
319
|
+
* Priority mode: weighted random over the eligible accounts.
|
|
320
|
+
*
|
|
321
|
+
* The weight is an idle bonus — `1 + min(idleHours * perHour, max)` — so an
|
|
322
|
+
* account that has never served (or has been idle for a while) outranks one
|
|
323
|
+
* that just answered. Reference panel logic drops its success-rate term
|
|
324
|
+
* entirely because a lifetime error counter penalises an account forever;
|
|
325
|
+
* instantaneous health is already handled by cooldowns, which is why those
|
|
326
|
+
* accounts never reach this list.
|
|
327
|
+
*
|
|
328
|
+
* A pool with no idle history (fresh process) hashes to equal weights, which
|
|
329
|
+
* spreads the very first picks instead of always returning index 0.
|
|
330
|
+
*/
|
|
331
|
+
private pickByWeight;
|
|
301
332
|
/**
|
|
302
333
|
* Pick the account to serve a request.
|
|
303
334
|
*
|
|
@@ -323,6 +354,10 @@ export declare class WorkBuddyAccountPool {
|
|
|
323
354
|
/** How the pool currently spreads requests. Shown on the card. */
|
|
324
355
|
currentDistribution(): AccountDistribution;
|
|
325
356
|
prefer(accountId: string | undefined): void;
|
|
357
|
+
/** Whether the user switched this account off on the card. */
|
|
358
|
+
isDisabled(accountId: string): boolean;
|
|
359
|
+
/** Every account id the user switched off, in discovery order. */
|
|
360
|
+
disabledIdsInOrder(): string[];
|
|
326
361
|
/** Best-effort refresh of one account after a session-dead upstream answer. */
|
|
327
362
|
refreshAccount(accountId: string): Promise<void>;
|
|
328
363
|
/**
|
|
@@ -445,6 +480,8 @@ export declare const WORKBUDDY_POOL_PROVIDER = "workbuddy-xdpool";
|
|
|
445
480
|
interface WorkBuddyAdapterOptions {
|
|
446
481
|
shim: WorkBuddyShim;
|
|
447
482
|
catalog: WorkBuddyCatalog;
|
|
483
|
+
/** Plugin context; the pi-ai adapter reads `attachments`/`fs` from it. */
|
|
484
|
+
ctx: Context$1;
|
|
448
485
|
providerId?: string;
|
|
449
486
|
displayName?: string;
|
|
450
487
|
}
|
|
@@ -570,6 +607,11 @@ interface PoolWebAccount {
|
|
|
570
607
|
modelId: string;
|
|
571
608
|
until: string;
|
|
572
609
|
}>;
|
|
610
|
+
/**
|
|
611
|
+
* Whether the user switched this account off. A disabled account never
|
|
612
|
+
* serves a request, but it stays listed so the card can switch it back on.
|
|
613
|
+
*/
|
|
614
|
+
disabled: boolean;
|
|
573
615
|
rateLimitHits: number;
|
|
574
616
|
/** ISO timestamp of the last successful use (best-effort pool bookkeeping). */
|
|
575
617
|
lastUsedAt?: string;
|
|
@@ -655,7 +697,6 @@ interface PoolWebModel {
|
|
|
655
697
|
/** Whether this model is currently enabled in the picker. */
|
|
656
698
|
enabled: boolean;
|
|
657
699
|
}
|
|
658
|
-
/** The user's saved model selection, echoed back so the card can diff a draft. */
|
|
659
700
|
interface PoolWebModelSelection {
|
|
660
701
|
/** Absent = every model is enabled. */
|
|
661
702
|
enabledModelIds?: readonly string[];
|
|
@@ -695,7 +736,7 @@ interface PoolWebStatus {
|
|
|
695
736
|
*/
|
|
696
737
|
type PoolRegion = 'cn' | 'global';
|
|
697
738
|
/** How the pool spreads requests across its accounts. */
|
|
698
|
-
type PoolDistribution = 'priority' | 'round-robin';
|
|
739
|
+
type PoolDistribution = 'priority' | 'round-robin' | 'balanced';
|
|
699
740
|
//#endregion
|
|
700
741
|
//#region src/index.d.ts
|
|
701
742
|
/** Stable Cordis plugin name. */
|
|
@@ -723,6 +764,13 @@ export interface Config {
|
|
|
723
764
|
* spend evenly instead. Absent reads as `priority`.
|
|
724
765
|
*/
|
|
725
766
|
distribution?: 'priority' | 'round-robin';
|
|
767
|
+
/**
|
|
768
|
+
* Account ids switched off on the card. A disabled account is never picked
|
|
769
|
+
* to serve a request, but it stays in the pool and on the card so it can be
|
|
770
|
+
* switched back on. Ids are the pool's stable per-credential keys, which
|
|
771
|
+
* survive re-scans (see WorkBuddyAccountPool.disabledIds).
|
|
772
|
+
*/
|
|
773
|
+
disabledAccountIds?: string[];
|
|
726
774
|
/**
|
|
727
775
|
* Model ids enabled in the picker. Absent means "every model the catalog
|
|
728
776
|
* advertises" — an unconfigured install should never present an empty model
|
|
@@ -741,9 +789,25 @@ export interface Config {
|
|
|
741
789
|
* user cap a model without touching the catalog.
|
|
742
790
|
*/
|
|
743
791
|
contextBudgets?: Record<string, number>;
|
|
792
|
+
/**
|
|
793
|
+
* Per-region model selection. The two gateways advertise different rosters, so
|
|
794
|
+
* one shared list would let a save on one tab silently rewrite the other tab's
|
|
795
|
+
* picker. Each region owns its own copy; a region with no entry falls back to
|
|
796
|
+
* the legacy flat keys above, so an upgrade keeps the list already in use.
|
|
797
|
+
*/
|
|
798
|
+
modelSelectionCn?: ModelSelectionConfig;
|
|
799
|
+
modelSelectionGlobal?: ModelSelectionConfig;
|
|
800
|
+
}
|
|
801
|
+
/** One region's saved model selection. */
|
|
802
|
+
export interface ModelSelectionConfig {
|
|
803
|
+
enabledModelIds?: string[];
|
|
804
|
+
imageModelIds?: string[];
|
|
805
|
+
contextBudgets?: Record<string, number>;
|
|
744
806
|
}
|
|
745
807
|
/** Upper bound the card offers as the "default" context window, in tokens. */
|
|
746
808
|
export declare const DEFAULT_CONTEXT_BUDGET = 200000;
|
|
809
|
+
/** Settings key holding one region's saved selection. */
|
|
810
|
+
export declare const modelSelectionKeyFor: (region: 'cn' | 'global') => string;
|
|
747
811
|
/**
|
|
748
812
|
* Plugin configuration schema.
|
|
749
813
|
*
|