dsh-workbuddy-xdpool 1.7.2 → 1.7.3
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 +37 -0
- package/lib/bin.js +234 -43
- package/lib/client.js +89 -15
- package/lib/index.d.ts +1033 -926
- package/lib/index.js +332 -58
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -288,6 +288,13 @@ interface UpstreamClientOptions {
|
|
|
288
288
|
fetchImpl?: typeof fetch;
|
|
289
289
|
/** Client version string sent to the upstream. */
|
|
290
290
|
clientVersion?: string;
|
|
291
|
+
/**
|
|
292
|
+
* Override the catalog-fetch backoff, in milliseconds.
|
|
293
|
+
*
|
|
294
|
+
* Tests set this to `[]` (or tiny values) so a retry case does not spend real
|
|
295
|
+
* seconds sleeping. Production uses {@link CATALOG_RETRY_BACKOFF_MS}.
|
|
296
|
+
*/
|
|
297
|
+
catalogRetryBackoffMs?: readonly number[];
|
|
291
298
|
}
|
|
292
299
|
/** Region for a login domain; an empty domain means CN (matching upstream tooling). */
|
|
293
300
|
/** The two gateways WorkBuddy serves: the domestic one and the international one. */
|
|
@@ -421,6 +428,19 @@ export declare function buddyAppEvents(buddyId: string, buddyName: string): Reco
|
|
|
421
428
|
export declare class WorkBuddyUpstreamClient {
|
|
422
429
|
private readonly fetchImpl;
|
|
423
430
|
private readonly clientVersion;
|
|
431
|
+
/** Backoff ladder for `fetchModels`; empty means a single attempt. */
|
|
432
|
+
private readonly catalogRetryBackoffMs;
|
|
433
|
+
/**
|
|
434
|
+
* Optional logger for retry notices.
|
|
435
|
+
*
|
|
436
|
+
* Set by the host so a retry is visible in the log with its attempt count —
|
|
437
|
+
* without it, a retry that eventually succeeds is invisible, and an operator
|
|
438
|
+
* debugging "why was the catalog slow" has nothing to look at.
|
|
439
|
+
*/
|
|
440
|
+
logger: {
|
|
441
|
+
warn?(...args: unknown[]): void;
|
|
442
|
+
info?(...args: unknown[]): void;
|
|
443
|
+
} | undefined;
|
|
424
444
|
constructor(options?: UpstreamClientOptions);
|
|
425
445
|
/**
|
|
426
446
|
* Normalize an OpenAI chat-completions body for the WorkBuddy upstream:
|
|
@@ -467,6 +487,16 @@ export declare class WorkBuddyUpstreamClient {
|
|
|
467
487
|
* below is common to the two branches.
|
|
468
488
|
*/
|
|
469
489
|
fetchModels(credential: WorkBuddyCredential, signal?: AbortSignal): Promise<readonly WorkBuddyUpstreamModel[]>;
|
|
490
|
+
/**
|
|
491
|
+
* One catalog attempt, without retries.
|
|
492
|
+
*
|
|
493
|
+
* Split out so {@link fetchModels} can retry it: the failure this guards
|
|
494
|
+
* against is a startup network hiccup, where several independent components
|
|
495
|
+
* see `fetch failed` inside the same second and the very next request
|
|
496
|
+
* succeeds — exactly the case a single attempt turns into "the user's model
|
|
497
|
+
* list is missing half its entries for the rest of the session".
|
|
498
|
+
*/
|
|
499
|
+
private fetchModelsOnce;
|
|
470
500
|
/** Read-only credits query, aggregated by package. Does not consume credits. */
|
|
471
501
|
fetchCredits(credential: WorkBuddyCredential): Promise<WorkBuddyCredits>;
|
|
472
502
|
/** Query today's check-in status without changing account state. */
|
|
@@ -1063,241 +1093,283 @@ export declare class WorkBuddyAccountPool {
|
|
|
1063
1093
|
};
|
|
1064
1094
|
}
|
|
1065
1095
|
//#endregion
|
|
1066
|
-
//#region src/
|
|
1067
|
-
/**
|
|
1068
|
-
|
|
1096
|
+
//#region src/status-paths.d.ts
|
|
1097
|
+
/**
|
|
1098
|
+
* Node-free constants and types shared by the Host and browser halves of the
|
|
1099
|
+
* WorkBuddy XD Pool settings card.
|
|
1100
|
+
*
|
|
1101
|
+
* Pool's runtime state already lives in `src/status.ts` (`buildStatus` /
|
|
1102
|
+
* `WorkBuddyStatus`); this module only carves the cross-domain (Host→browser)
|
|
1103
|
+
* JSON document into a shape that stays token-free and matches what the
|
|
1104
|
+
* browser card renders. Route paths are plugin-owned and mounted on the Host's
|
|
1105
|
+
* same-origin web server (see `src/web-status.ts`).
|
|
1106
|
+
*
|
|
1107
|
+
* @module dsh-workbuddy-xdpool/status-paths
|
|
1108
|
+
*/
|
|
1109
|
+
/** Plugin-owned read-only pool status endpoint (account rows + models + shim). */
|
|
1110
|
+
export declare const POOL_STATUS_PATH = "/plugins/dsh-workbuddy-xdpool/status";
|
|
1111
|
+
/** Plugin-owned local account rescan endpoint (re-read desktop snapshots). */
|
|
1112
|
+
export declare const POOL_RESCAN_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/rescan";
|
|
1113
|
+
/** Plugin-owned cooldown reset endpoint (clear all 429 cooldowns). */
|
|
1114
|
+
export declare const POOL_RESET_COOLDOWN_PATH = "/plugins/dsh-workbuddy-xdpool/cooldowns/reset";
|
|
1115
|
+
/** Plugin-owned daily check-in action endpoint (claim today's reward). */
|
|
1116
|
+
export declare const POOL_CHECKIN_PATH = "/plugins/dsh-workbuddy-xdpool/checkin";
|
|
1117
|
+
/** Plugin-owned model-selection save endpoint (writes the settings section). */
|
|
1118
|
+
export declare const POOL_MODELS_SAVE_PATH = "/plugins/dsh-workbuddy-xdpool/models/save";
|
|
1119
|
+
/**
|
|
1120
|
+
* Throw one account out of the pool for good, or take it back.
|
|
1121
|
+
*
|
|
1122
|
+
* Separate from the disable route because the semantics differ: disabling is a
|
|
1123
|
+
* rotation preference the account survives, ignoring survives the account.
|
|
1124
|
+
*/
|
|
1125
|
+
export declare const POOL_ACCOUNT_IGNORE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/ignored";
|
|
1126
|
+
/** Run one automation job immediately, so the card can verify it on demand. */
|
|
1127
|
+
export declare const POOL_AUTOMATION_RUN_PATH = "/plugins/dsh-workbuddy-xdpool/automation/run";
|
|
1128
|
+
/** Set or clear one account's reserved-credit floor. */
|
|
1129
|
+
export declare const POOL_CREDIT_RESERVE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/credit-reserve";
|
|
1130
|
+
/** One account's row, token-free. */
|
|
1131
|
+
interface PoolWebAccount {
|
|
1132
|
+
id: string;
|
|
1133
|
+
label: string;
|
|
1134
|
+
nickname?: string;
|
|
1135
|
+
domain: string;
|
|
1136
|
+
/** ISO timestamp; absent when the credential carries no expiry. */
|
|
1137
|
+
expiresAt?: string;
|
|
1138
|
+
/** Account-wide cooldown (every model blocked); only after a no-model penalize. */
|
|
1139
|
+
cooling: boolean;
|
|
1140
|
+
/** ISO timestamp when the account-wide 429 cooldown lifts; only while cooling. */
|
|
1141
|
+
cooldownUntil?: string;
|
|
1142
|
+
/**
|
|
1143
|
+
* Per-model cooldowns currently active. The account is NOT `cooling` while a
|
|
1144
|
+
* model is limited — its other models still serve — but each entry tells the
|
|
1145
|
+
* card which model is out until when (e.g. `hy4-preview` cooling to 10:14,
|
|
1146
|
+
* `hy3` normal).
|
|
1147
|
+
*/
|
|
1148
|
+
modelCooldowns?: ReadonlyArray<{
|
|
1149
|
+
modelId: string;
|
|
1150
|
+
until: string;
|
|
1151
|
+
}>;
|
|
1152
|
+
/**
|
|
1153
|
+
* Whether the user switched this account off. A disabled account never
|
|
1154
|
+
* serves a request, but it stays listed so the card can switch it back on.
|
|
1155
|
+
*/
|
|
1156
|
+
disabled: boolean;
|
|
1157
|
+
rateLimitHits: number;
|
|
1158
|
+
/**
|
|
1159
|
+
* Credits the user asked to keep for this account. The pool stops picking the
|
|
1160
|
+
* account once its balance reaches the reserve, so this many credits survive.
|
|
1161
|
+
* 0 means the account may be spent down as before.
|
|
1162
|
+
*/
|
|
1163
|
+
creditReserve: number;
|
|
1164
|
+
/**
|
|
1165
|
+
* Whether the account is held back purely by its reserve right now. Kept
|
|
1166
|
+
* distinct from `cooling`: a reserved account is healthy and simply
|
|
1167
|
+
* protected, which is a different thing to tell the user than rate-limited.
|
|
1168
|
+
*/
|
|
1169
|
+
reserved: boolean;
|
|
1170
|
+
/**
|
|
1171
|
+
* What the automation earned for this account today. Absent when it earned
|
|
1172
|
+
* nothing (or the automation never ran for it), so the card can stay quiet
|
|
1173
|
+
* instead of printing a row of zeroes.
|
|
1174
|
+
*/
|
|
1175
|
+
automationToday?: PoolWebAutomationEarnings;
|
|
1176
|
+
/** ISO timestamp of the last successful use (best-effort pool bookkeeping). */
|
|
1177
|
+
lastUsedAt?: string;
|
|
1178
|
+
/** Aggregated credit summary for the account, read-only. */
|
|
1179
|
+
credits?: PoolWebCredits;
|
|
1180
|
+
creditsError?: string;
|
|
1181
|
+
/**
|
|
1182
|
+
* Today's check-in state for this account, read-only. Present only when the
|
|
1183
|
+
* per-account check-in probe succeeded and the program is active. The card
|
|
1184
|
+
* renders one claim button per account, so a multi-account pool can collect
|
|
1185
|
+
* every account's daily reward without switching accounts by hand.
|
|
1186
|
+
*/
|
|
1187
|
+
checkin?: PoolWebCheckin;
|
|
1188
|
+
checkinError?: string;
|
|
1189
|
+
}
|
|
1190
|
+
/** One credit package (as surfaced by the pool's upstream client), node-free. */
|
|
1191
|
+
interface PoolWebCreditPackage {
|
|
1192
|
+
packageName: string;
|
|
1193
|
+
remain?: number;
|
|
1194
|
+
size?: number;
|
|
1195
|
+
/** CapacityType 4 — refreshed each cycle and never expires. */
|
|
1196
|
+
monthly?: boolean;
|
|
1197
|
+
/** Next cycle refresh point, ms. */
|
|
1198
|
+
cycleRefreshMs?: number;
|
|
1199
|
+
/** One-off expiry, ms. */
|
|
1200
|
+
expiresAtMs?: number;
|
|
1201
|
+
}
|
|
1202
|
+
/** Aggregated credit answer the card renders under one account. */
|
|
1203
|
+
interface PoolWebCredits {
|
|
1204
|
+
total?: number;
|
|
1205
|
+
packages: readonly PoolWebCreditPackage[];
|
|
1206
|
+
/** Credits expiring within 3 days. */
|
|
1207
|
+
expiringSoon?: number;
|
|
1208
|
+
/** When the nearest package expires, ms. */
|
|
1209
|
+
nearestExpiryMs?: number;
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Daily check-in state the card renders under one account's credits. Mirrors
|
|
1213
|
+
* the upstream activity endpoint, minus anything the browser does not need.
|
|
1214
|
+
*/
|
|
1215
|
+
interface PoolWebCheckin {
|
|
1216
|
+
/** The activity is running; a claim button is offered only while true. */
|
|
1217
|
+
active: boolean;
|
|
1218
|
+
/** Already collected today — the button renders as a done state. */
|
|
1219
|
+
todayCheckedIn: boolean;
|
|
1220
|
+
/** Consecutive days checked in. */
|
|
1221
|
+
streakDays: number;
|
|
1222
|
+
/** Credits a single day grants. */
|
|
1223
|
+
dailyCredit: number;
|
|
1224
|
+
/** Credits collected today (0 before claiming). */
|
|
1225
|
+
todayCredit: number;
|
|
1226
|
+
/** Today is a streak milestone day. */
|
|
1227
|
+
isStreakDay: boolean;
|
|
1228
|
+
/** The day count the next milestone lands on. */
|
|
1229
|
+
nextStreakDay: number;
|
|
1230
|
+
/** Bonus credits granted on a milestone day. */
|
|
1231
|
+
streakBonusCredit: number;
|
|
1232
|
+
}
|
|
1233
|
+
/** Result of one claim, so the card can confirm what was collected. */
|
|
1234
|
+
interface PoolWebCheckinClaim {
|
|
1235
|
+
credit: number;
|
|
1236
|
+
streakDays: number;
|
|
1237
|
+
isStreakDay: boolean;
|
|
1238
|
+
}
|
|
1239
|
+
/** One model the pool exposes to DSH, with cost / free tags. */
|
|
1240
|
+
interface PoolWebModel {
|
|
1069
1241
|
id: string;
|
|
1070
|
-
/** Display name; the multiplier is appended for the picker. */
|
|
1071
1242
|
name: string;
|
|
1072
|
-
|
|
1073
|
-
maxOutputTokens: number;
|
|
1074
|
-
/** Relative credit cost, e.g. 0.79 for `x0.79`. */
|
|
1243
|
+
/** Relative credit cost, e.g. 0.79 for x0.79. */
|
|
1075
1244
|
multiplier?: number;
|
|
1076
|
-
/** Upstream-declared thinking levels. */
|
|
1077
|
-
supportedEfforts?: readonly string[];
|
|
1078
|
-
supportsImages: boolean;
|
|
1079
1245
|
/** Upstream tags: free / limited-free / night-discount. */
|
|
1080
1246
|
tags?: readonly string[];
|
|
1247
|
+
/** Effective image support after the user's per-model toggle. */
|
|
1248
|
+
supportsImages: boolean;
|
|
1249
|
+
/** Effective context window after the user's budget cap. */
|
|
1250
|
+
contextWindow: number;
|
|
1251
|
+
/** The window the upstream advertises, before any cap. */
|
|
1252
|
+
nativeContextWindow: number;
|
|
1253
|
+
/** Upstream output ceiling, so the card can show both limits. */
|
|
1254
|
+
maxOutputTokens: number;
|
|
1255
|
+
/** Thinking levels the upstream declares, when it declares any. */
|
|
1256
|
+
supportedEfforts?: readonly string[];
|
|
1257
|
+
/** Whether this model is currently enabled in the picker. */
|
|
1258
|
+
enabled: boolean;
|
|
1081
1259
|
}
|
|
1082
1260
|
/**
|
|
1083
|
-
*
|
|
1084
|
-
* upstream cannot be reached.
|
|
1261
|
+
* Body of the account ignore/unignore route: exactly one account per request.
|
|
1085
1262
|
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1263
|
+
* `ignored: true` throws the account out of the pool for good (its credential is
|
|
1264
|
+
* not even read on the next scan, and a fresh desktop sign-in will not bring it
|
|
1265
|
+
* back). `false` restores it, at which point the next scan discovers it again.
|
|
1266
|
+
*/
|
|
1267
|
+
interface PoolWebAccountIgnore {
|
|
1268
|
+
/** Pool account id, as reported in `PoolWebAccount.id`. */
|
|
1269
|
+
accountId: string;
|
|
1270
|
+
/** `true` ignores the account permanently; `false` takes it back. */
|
|
1271
|
+
ignored: boolean;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* One account the user has thrown out of the pool.
|
|
1092
1275
|
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1276
|
+
* Kept on the status document so the card can list what was ignored and offer a
|
|
1277
|
+
* way back: without that, "ignored" is a one-way door the user cannot inspect or
|
|
1278
|
+
* undo from the UI, which is how a hidden list becomes a support burden.
|
|
1095
1279
|
*/
|
|
1096
|
-
|
|
1097
|
-
/**
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
/**
|
|
1102
|
-
|
|
1103
|
-
current(): readonly WorkBuddyModelInfo[];
|
|
1104
|
-
/**
|
|
1105
|
-
* The models DSH should actually offer, after applying the user's selection:
|
|
1106
|
-
* disabled models are dropped, an explicit image list overrides the upstream
|
|
1107
|
-
* capability flag, and a per-model budget caps the advertised window.
|
|
1108
|
-
*
|
|
1109
|
-
* An absent `enabledModelIds` means "everything" — a fresh install with no
|
|
1110
|
-
* saved selection must not present an empty picker.
|
|
1111
|
-
*/
|
|
1112
|
-
visible(): readonly WorkBuddyModelInfo[];
|
|
1113
|
-
/** Replace the catalog and notify the adapter to rebuild its model list. */
|
|
1114
|
-
update(models: readonly WorkBuddyModelInfo[]): void;
|
|
1115
|
-
/** Restore the static fallback, e.g. when the upstream stops answering. */
|
|
1116
|
-
reset(): void;
|
|
1117
|
-
/** Replace the user's selection; the adapter rebuilds from `visible()`. */
|
|
1118
|
-
applySelection(selection: ModelSelection): void;
|
|
1119
|
-
/** The selection currently in force, for the card's save round-trip. */
|
|
1120
|
-
currentSelection(): ModelSelection;
|
|
1121
|
-
onChange(listener: () => void): () => void;
|
|
1122
|
-
find(id: string): WorkBuddyModelInfo | undefined;
|
|
1123
|
-
/** Replace the catalog from the live upstream list; keeps the fallback if empty. */
|
|
1124
|
-
updateFromUpstream(models: readonly WorkBuddyUpstreamModel[]): void;
|
|
1125
|
-
private notify;
|
|
1280
|
+
interface PoolWebIgnoredAccount {
|
|
1281
|
+
/** Pool account id, the same key `PoolWebAccount.id` uses. */
|
|
1282
|
+
id: string;
|
|
1283
|
+
/** Human label captured at ignore time, so the row reads without a rescan. */
|
|
1284
|
+
label: string;
|
|
1285
|
+
/** ISO timestamp of when it was ignored. */
|
|
1286
|
+
ignoredAt: string;
|
|
1126
1287
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
/** Absent = every model in the catalog is offered. */
|
|
1288
|
+
interface PoolWebModelSelection {
|
|
1289
|
+
/** Absent = every model is enabled. */
|
|
1130
1290
|
enabledModelIds?: readonly string[];
|
|
1131
1291
|
/** Absent = each model follows its upstream image capability. */
|
|
1132
1292
|
imageModelIds?: readonly string[];
|
|
1133
1293
|
/** Per-model context-window cap, keyed by model id. */
|
|
1134
1294
|
contextBudgets?: Readonly<Record<string, number | undefined>>;
|
|
1135
1295
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
token(): string;
|
|
1147
|
-
close(): Promise<void>;
|
|
1148
|
-
}
|
|
1149
|
-
interface WorkBuddyShimOptions {
|
|
1150
|
-
pool: WorkBuddyAccountPool;
|
|
1151
|
-
client: WorkBuddyUpstreamClient;
|
|
1152
|
-
catalog: WorkBuddyCatalog;
|
|
1153
|
-
logger?: ShimLogger;
|
|
1296
|
+
/** The JSON document the pool card renders. */
|
|
1297
|
+
interface PoolWebStatus {
|
|
1298
|
+
ok: boolean;
|
|
1299
|
+
accounts: readonly PoolWebAccount[];
|
|
1300
|
+
/** The next account the pool would use (rotation cursor). */
|
|
1301
|
+
activeAccountId?: string;
|
|
1302
|
+
cooling: number;
|
|
1303
|
+
models: readonly PoolWebModel[];
|
|
1304
|
+
/** The saved selection the card diffs its draft against. */
|
|
1305
|
+
selection: PoolWebModelSelection;
|
|
1154
1306
|
/**
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
* own gateway. Absent means "every account" (a single-region deployment).
|
|
1307
|
+
* How the pool spreads requests: `priority` drains one account before
|
|
1308
|
+
* moving on, `round-robin` splits the spend evenly.
|
|
1158
1309
|
*/
|
|
1159
|
-
|
|
1160
|
-
/**
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
/** Plugin context; the pi-ai adapter reads `attachments`/`fs` from it. */
|
|
1173
|
-
ctx: Context$1;
|
|
1174
|
-
providerId?: string;
|
|
1175
|
-
displayName?: string;
|
|
1176
|
-
}
|
|
1177
|
-
/** What {@link createWorkBuddyAdapter} hands back. */
|
|
1178
|
-
interface WorkBuddyAdapter {
|
|
1179
|
-
providerId: string;
|
|
1180
|
-
displayName: string;
|
|
1181
|
-
adapter: PiAiAdapter;
|
|
1182
|
-
/** Rebuild the pi-ai model list from the current catalog. */
|
|
1183
|
-
buildModels: () => Model<Api>[];
|
|
1184
|
-
/** Rebuild the adapter's provider snapshot; call after a catalog update. */
|
|
1185
|
-
invalidate: () => void;
|
|
1186
|
-
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Assemble the adapter. `getModels` re-reads the live catalog, and every
|
|
1189
|
-
* model's `baseUrl` is re-resolved per read so the shim's ephemeral port
|
|
1190
|
-
* applies from the first snapshot after startup. Call only after `shim.ready`.
|
|
1191
|
-
*/
|
|
1192
|
-
export declare function createWorkBuddyAdapter(options: WorkBuddyAdapterOptions): WorkBuddyAdapter;
|
|
1193
|
-
//#endregion
|
|
1194
|
-
//#region src/scheduler.d.ts
|
|
1195
|
-
/** How often the loop wakes to look for a due job. */
|
|
1196
|
-
/**
|
|
1197
|
-
* How long to wait for event scoring to land before re-reading the task list.
|
|
1198
|
-
*
|
|
1199
|
-
* Scoring is asynchronous on the upstream side, so an immediate re-read still
|
|
1200
|
-
* shows the old progress and the claim pass would skip a task that is in fact
|
|
1201
|
-
* now claimable. Measured: the chain is reflected by about eight seconds.
|
|
1202
|
-
*/
|
|
1203
|
-
export declare const EVENT_SCORE_WAIT_MS = 9000;
|
|
1204
|
-
export declare const AUTOMATION_TICK_MS = 60000;
|
|
1205
|
-
/** Which jobs the automation runs, and when. */
|
|
1206
|
-
/**
|
|
1207
|
-
* The persisted daily earnings ledger.
|
|
1208
|
-
*
|
|
1209
|
-
* `date` is the local day the counters belong to: a ledger from a previous day
|
|
1210
|
-
* is discarded on load rather than carried forward as "today".
|
|
1211
|
-
*/
|
|
1212
|
-
interface AutomationLedger {
|
|
1213
|
-
date: string;
|
|
1214
|
-
accounts: Record<string, AutomationAccountEarnings>;
|
|
1215
|
-
}
|
|
1216
|
-
interface AutomationOptions {
|
|
1217
|
-
/** Master switch; false stops every job. */
|
|
1218
|
-
enabled?: boolean;
|
|
1219
|
-
/** Hour list for the daily check-in job. */
|
|
1220
|
-
checkinHours?: readonly number[];
|
|
1221
|
-
/** Hour list for the task-centre job (accept + claim). */
|
|
1222
|
-
taskHours?: readonly number[];
|
|
1223
|
-
/** Hour list for the activity-report job. */
|
|
1224
|
-
reportHours?: readonly number[];
|
|
1225
|
-
/** Hour list for the streak-redemption job. */
|
|
1226
|
-
streakHours?: readonly number[];
|
|
1227
|
-
/** Hour list for the buddy travel job. */
|
|
1228
|
-
travelHours?: readonly number[];
|
|
1229
|
-
/** Per-account serial delay, in milliseconds. */
|
|
1230
|
-
accountDelayMs?: number;
|
|
1231
|
-
/** Override the gap between expert chains, in milliseconds (tests use 0). */
|
|
1232
|
-
expertGapMs?: number;
|
|
1233
|
-
/** Override the event-scoring wait, in milliseconds (tests use 0). */
|
|
1234
|
-
eventScoreWaitMs?: number;
|
|
1235
|
-
/** Override the clock, for tests. */
|
|
1236
|
-
now?: () => Date;
|
|
1237
|
-
/** Logger; defaults to a no-op so tests stay quiet. */
|
|
1238
|
-
logger?: SchedulerLogger;
|
|
1310
|
+
distribution: PoolDistribution;
|
|
1311
|
+
/** Which region this document describes. */
|
|
1312
|
+
region: PoolRegion;
|
|
1313
|
+
/** Every region holding at least one account, in display order. */
|
|
1314
|
+
regions: readonly PoolRegion[];
|
|
1315
|
+
shim: {
|
|
1316
|
+
running: boolean;
|
|
1317
|
+
baseUrl?: string;
|
|
1318
|
+
};
|
|
1319
|
+
/** Daily-points automation state, so the card can show what ran and when. */
|
|
1320
|
+
automation: PoolWebAutomation;
|
|
1321
|
+
/** Per-account credit floors currently in force, keyed by account id. */
|
|
1322
|
+
creditReserves: Readonly<Record<string, number>>;
|
|
1239
1323
|
/**
|
|
1240
|
-
*
|
|
1324
|
+
* Accounts thrown out of the pool, in the order they were ignored.
|
|
1241
1325
|
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1326
|
+
* Reported so the card can show the list and offer a way back. These accounts
|
|
1327
|
+
* are NOT in `accounts`: they are filtered out before their credentials are
|
|
1328
|
+
* read, which is the whole point of the feature.
|
|
1245
1329
|
*/
|
|
1246
|
-
|
|
1247
|
-
saveEarnings?: (ledger: AutomationLedger) => void;
|
|
1248
|
-
}
|
|
1249
|
-
/** Logger surface, kept structural so any host logger fits. */
|
|
1250
|
-
interface SchedulerLogger {
|
|
1251
|
-
info?(...args: unknown[]): void;
|
|
1252
|
-
warn?(...args: unknown[]): void;
|
|
1253
|
-
}
|
|
1254
|
-
/** One job's last-run record, surfaced on the status document. */
|
|
1255
|
-
interface AutomationJobState {
|
|
1256
|
-
/** `YYYY-MM-DD` of the last completed run, or undefined if it never ran. */
|
|
1257
|
-
lastRunDate?: string;
|
|
1330
|
+
ignored: readonly PoolWebIgnoredAccount[];
|
|
1258
1331
|
/**
|
|
1259
|
-
*
|
|
1332
|
+
* Where THIS region's model list came from.
|
|
1260
1333
|
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1334
|
+
* `live` = fetched from the gateway (or a cached fetch survived a restart).
|
|
1335
|
+
* `fallback` = the built-in static table, which means the startup fetch
|
|
1336
|
+
* failed and the user is looking at a SHORTER, possibly stale roster — models
|
|
1337
|
+
* they had enabled can be missing from it entirely.
|
|
1338
|
+
*
|
|
1339
|
+
* Surfaced because the failure used to be log-only: the picker quietly lost
|
|
1340
|
+
* half its entries and nothing on screen said why, so it read as "the plugin
|
|
1341
|
+
* deleted my models".
|
|
1266
1342
|
*/
|
|
1267
|
-
|
|
1343
|
+
catalogSource?: PoolWebCatalogSource;
|
|
1344
|
+
/** When the live catalog was last successfully fetched, ISO. */
|
|
1345
|
+
catalogUpdatedAt?: string;
|
|
1346
|
+
/** Why the last fetch failed, when it did. Redacted and length-capped. */
|
|
1347
|
+
catalogError?: string;
|
|
1348
|
+
}
|
|
1349
|
+
/** How a region's model list was obtained. */
|
|
1350
|
+
type PoolWebCatalogSource = 'live' | 'fallback';
|
|
1351
|
+
/** One automation job's last run, as shown on the card. */
|
|
1352
|
+
interface PoolWebAutomationJob {
|
|
1353
|
+
/** `YYYY-MM-DD` of the last run in this process, if it has run. */
|
|
1354
|
+
lastRunDate?: string;
|
|
1268
1355
|
/**
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
* This is what actually gates a re-run, and it is a SET rather than a single
|
|
1272
|
-
* slot because one field could not express the contract: a job configured for
|
|
1273
|
-
* `[9, 21]` runs twice, and a single "last slot" value can only remember one
|
|
1274
|
-
* of them — the second run erased the first, so the 9 o'clock candidate looked
|
|
1275
|
-
* unconsumed again and the job re-fired every hour after 21:00.
|
|
1276
|
-
*
|
|
1277
|
-
* The values are the CONFIGURED hours that were spent, never the clock time
|
|
1278
|
-
* the run happened to finish at. Storing the finish time was the original
|
|
1279
|
-
* defect: the reader compared it against a configured candidate, so the two
|
|
1280
|
-
* almost never matched and the gate stayed open all day.
|
|
1356
|
+
* Epoch ms of the last run, so the card can show the TIME.
|
|
1281
1357
|
*
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1358
|
+
* Carried because a date-only stamp cannot tell one run from eight: every
|
|
1359
|
+
* repeat inside the same day rendered as the identical `2026-09-28 · 2`,
|
|
1360
|
+
* which is what kept a "re-runs every hour" defect invisible on the card.
|
|
1285
1361
|
*/
|
|
1286
|
-
|
|
1362
|
+
lastRunAtMs?: number;
|
|
1287
1363
|
/**
|
|
1288
|
-
*
|
|
1364
|
+
* Configured slots consumed today, as `YYYY-MM-DDTHH`.
|
|
1289
1365
|
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
* ledger. Conflating the two is what let a catch-up run at 13:00 erase the
|
|
1293
|
-
* record of the 9 o'clock slot.
|
|
1366
|
+
* Shown so "which of today's hours already ran" is answerable at a glance
|
|
1367
|
+
* rather than inferred from a counter.
|
|
1294
1368
|
*/
|
|
1295
|
-
|
|
1296
|
-
/**
|
|
1297
|
-
lastRunAtMs?: number;
|
|
1298
|
-
/** Accounts that completed without throwing. */
|
|
1369
|
+
firedSlots?: readonly string[];
|
|
1370
|
+
/** Accounts that finished without error on the last run. */
|
|
1299
1371
|
ok: number;
|
|
1300
|
-
/** Accounts that
|
|
1372
|
+
/** Accounts that failed on the last run (each one skipped, the run continued). */
|
|
1301
1373
|
failed: number;
|
|
1302
1374
|
/** Credits claimed by the task job on the last run. */
|
|
1303
1375
|
credit: number;
|
|
@@ -1305,843 +1377,865 @@ interface AutomationJobState {
|
|
|
1305
1377
|
energy: number;
|
|
1306
1378
|
/** Tasks claimed by the task job on the last run. */
|
|
1307
1379
|
claimed: number;
|
|
1308
|
-
/**
|
|
1380
|
+
/** One-line summary of the last run. */
|
|
1381
|
+
message?: string;
|
|
1309
1382
|
/**
|
|
1310
|
-
* What
|
|
1383
|
+
* What the last run actually did, in the words of the task board.
|
|
1311
1384
|
*
|
|
1312
|
-
* `message` is a count
|
|
1313
|
-
*
|
|
1314
|
-
* only a bare number cannot answer "did it do the thing I care about", which
|
|
1315
|
-
* is the question the panel exists to answer.
|
|
1385
|
+
* `message` is a count; this is the list a person can check off, which is
|
|
1386
|
+
* what turns a row from "it ran" into "it did the things I care about".
|
|
1316
1387
|
*/
|
|
1317
1388
|
detail?: readonly string[];
|
|
1318
|
-
/**
|
|
1319
|
-
* A pending milestone worth naming, when there is one.
|
|
1320
|
-
*
|
|
1321
|
-
* Streak tiers are why this exists: every tier reads `locked` until enough
|
|
1322
|
-
* consecutive days accumulate, and "locked" on its own reads as "broken"
|
|
1323
|
-
* rather than "come back in four days".
|
|
1324
|
-
*/
|
|
1389
|
+
/** A pending milestone worth naming, e.g. the next streak tier countdown. */
|
|
1325
1390
|
progress?: string;
|
|
1326
|
-
message?: string;
|
|
1327
1391
|
}
|
|
1328
1392
|
/**
|
|
1329
|
-
*
|
|
1393
|
+
* Automation block on the status document.
|
|
1330
1394
|
*
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1395
|
+
* Carries the schedule and each job's last outcome so the card can answer
|
|
1396
|
+
* "is it on, when does it run, and what did it last do" without reaching into
|
|
1397
|
+
* the scheduler itself.
|
|
1334
1398
|
*/
|
|
1335
|
-
interface
|
|
1336
|
-
/**
|
|
1399
|
+
interface PoolWebAutomation {
|
|
1400
|
+
/** Master switch, mirrored from the saved config. */
|
|
1401
|
+
enabled: boolean;
|
|
1402
|
+
/** Whether the loop is currently running. */
|
|
1403
|
+
running: boolean;
|
|
1404
|
+
/** Configured hours per job, so the card can show the schedule. */
|
|
1405
|
+
checkinHours: readonly number[];
|
|
1406
|
+
reportHours: readonly number[];
|
|
1407
|
+
taskHours: readonly number[];
|
|
1408
|
+
streakHours: readonly number[];
|
|
1409
|
+
travelHours: readonly number[];
|
|
1410
|
+
jobs: {
|
|
1411
|
+
checkin: PoolWebAutomationJob;
|
|
1412
|
+
report: PoolWebAutomationJob;
|
|
1413
|
+
tasks: PoolWebAutomationJob;
|
|
1414
|
+
streak: PoolWebAutomationJob;
|
|
1415
|
+
travel: PoolWebAutomationJob;
|
|
1416
|
+
};
|
|
1417
|
+
/** Claimable tasks seen on the most recent task pass, across accounts. */
|
|
1418
|
+
claimableSeen: number;
|
|
1419
|
+
/**
|
|
1420
|
+
* Whether a manual run is in flight. The card polls this to know when to
|
|
1421
|
+
* stop showing progress and report the result.
|
|
1422
|
+
*/
|
|
1423
|
+
runInProgress: boolean;
|
|
1424
|
+
/**
|
|
1425
|
+
* Per-account credits/energy/tasks the automation earned TODAY, keyed by
|
|
1426
|
+
* account id. An account that earned nothing is simply absent, so the card
|
|
1427
|
+
* can say "nothing yet" instead of showing a bare zero.
|
|
1428
|
+
*/
|
|
1429
|
+
earningsToday: Readonly<Record<string, PoolWebAutomationEarnings>>;
|
|
1430
|
+
}
|
|
1431
|
+
/** Today's automation take for one account. */
|
|
1432
|
+
interface PoolWebAutomationEarnings {
|
|
1433
|
+
/** Credits claimed from the task centre today. */
|
|
1337
1434
|
credit: number;
|
|
1338
1435
|
/** Energy claimed from the task centre today. */
|
|
1339
1436
|
energy: number;
|
|
1340
1437
|
/** Tasks claimed today. */
|
|
1341
1438
|
claimed: number;
|
|
1342
|
-
/**
|
|
1343
|
-
* Credits the automation collected from check-in today.
|
|
1344
|
-
*
|
|
1345
|
-
* Kept separate from `credit` because they are different achievements and the
|
|
1346
|
-
* card shows them on their own lines: "the automation claimed 3 tasks" and
|
|
1347
|
-
* "the automation checked in" are not the same claim to the user.
|
|
1348
|
-
*/
|
|
1439
|
+
/** Credits collected from check-in today. */
|
|
1349
1440
|
checkinCredit: number;
|
|
1350
|
-
/** Credits from streak redemption
|
|
1441
|
+
/** Credits from streak redemption and the lottery today. */
|
|
1351
1442
|
bonusCredit: number;
|
|
1352
|
-
/** Credits from
|
|
1443
|
+
/** Credits from buddy adoption and the travel loop today. */
|
|
1353
1444
|
travelCredit: number;
|
|
1354
|
-
/** Local date the counters belong to. */
|
|
1445
|
+
/** Local date the counters belong to (YYYY-MM-DD). */
|
|
1355
1446
|
date: string;
|
|
1356
1447
|
}
|
|
1357
|
-
/** What ONE account gained during a single run. */
|
|
1358
|
-
interface AutomationAccountGain {
|
|
1359
|
-
credit: number;
|
|
1360
|
-
energy: number;
|
|
1361
|
-
claimed: number;
|
|
1362
|
-
checkinCredit: number;
|
|
1363
|
-
bonusCredit: number;
|
|
1364
|
-
travelCredit: number;
|
|
1365
|
-
}
|
|
1366
|
-
/** Totals from running the whole ordered pass at once. */
|
|
1367
|
-
interface AutomationRunSummary {
|
|
1368
|
-
/** How many jobs actually ran (a job with no hour is skipped). */
|
|
1369
|
-
jobsRun: number;
|
|
1370
|
-
/** Accounts that finished without error, summed across jobs. */
|
|
1371
|
-
okCount: number;
|
|
1372
|
-
/** Accounts that failed, summed across jobs. */
|
|
1373
|
-
failed: number;
|
|
1374
|
-
credit: number;
|
|
1375
|
-
energy: number;
|
|
1376
|
-
claimed: number;
|
|
1377
|
-
/**
|
|
1378
|
-
* What each account gained during THIS run, keyed by account id. Only
|
|
1379
|
-
* accounts that gained something appear.
|
|
1380
|
-
*/
|
|
1381
|
-
accounts: Readonly<Record<string, AutomationAccountGain>>;
|
|
1382
|
-
}
|
|
1383
|
-
/** Automation snapshot for the status document and the card. */
|
|
1384
|
-
interface AutomationStatus {
|
|
1385
|
-
enabled: boolean;
|
|
1386
|
-
/** Whether the loop is running. */
|
|
1387
|
-
running: boolean;
|
|
1388
|
-
checkinHours: readonly number[];
|
|
1389
|
-
taskHours: readonly number[];
|
|
1390
|
-
reportHours: readonly number[];
|
|
1391
|
-
streakHours: readonly number[];
|
|
1392
|
-
travelHours: readonly number[];
|
|
1393
|
-
jobs: {
|
|
1394
|
-
checkin: AutomationJobState;
|
|
1395
|
-
report: AutomationJobState;
|
|
1396
|
-
tasks: AutomationJobState;
|
|
1397
|
-
streak: AutomationJobState;
|
|
1398
|
-
travel: AutomationJobState;
|
|
1399
|
-
};
|
|
1400
|
-
/** Claimable tasks seen on the most recent task pass, across accounts. */
|
|
1401
|
-
claimableSeen: number;
|
|
1402
|
-
/**
|
|
1403
|
-
* Per-account totals for today, keyed by account id. Only accounts that
|
|
1404
|
-
* actually earned something appear, so the card can render "no earnings"
|
|
1405
|
-
* as an absence rather than a zero it has to explain.
|
|
1406
|
-
*/
|
|
1407
|
-
earningsToday: Readonly<Record<string, AutomationAccountEarnings>>;
|
|
1408
|
-
/**
|
|
1409
|
-
* Whether a manual run is in flight right now.
|
|
1410
|
-
*
|
|
1411
|
-
* A run takes tens of seconds (one upstream call per account per job, plus
|
|
1412
|
-
* the scoring wait), which is far too long for the card to hold a request
|
|
1413
|
-
* open. The button starts a run and the panel polls this flag instead.
|
|
1414
|
-
*/
|
|
1415
|
-
runInProgress: boolean;
|
|
1416
|
-
}
|
|
1417
|
-
/** The three plus one job kinds, in a stable order. */
|
|
1418
|
-
type AutomationJobKind = 'checkin' | 'tasks' | 'report' | 'streak' | 'travel';
|
|
1419
|
-
/** The four jobs in the order a tick runs them: report before tasks, always. */
|
|
1420
|
-
export declare const AUTOMATION_JOB_KINDS: readonly AutomationJobKind[];
|
|
1421
|
-
/** Reject anything that is not a job kind, so a route cannot name an unknown job. */
|
|
1422
|
-
export declare function isAutomationJobKind(value: unknown): value is AutomationJobKind;
|
|
1423
|
-
export declare function dayKey(date: Date, timeZone?: string): string;
|
|
1424
1448
|
/**
|
|
1425
|
-
*
|
|
1449
|
+
* The two gateways, matching the provider ids the host registers. `cn` is the
|
|
1450
|
+
* domestic gateway (`copilot.tencent.com` / `codebuddy.cn`); `global` is the
|
|
1451
|
+
* international one (`workbuddy.ai`).
|
|
1452
|
+
*/
|
|
1453
|
+
type PoolRegion = 'cn' | 'global';
|
|
1454
|
+
/** How the pool spreads requests across its accounts. */
|
|
1455
|
+
type PoolDistribution = 'priority' | 'round-robin' | 'balanced';
|
|
1456
|
+
/**
|
|
1457
|
+
* The schedule every automation job falls back to.
|
|
1426
1458
|
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1429
|
-
* the
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1459
|
+
* Shared by both halves on purpose. The host uses it when a configured hour
|
|
1460
|
+
* list arrives empty (the settings schema materializes "never configured" into
|
|
1461
|
+
* `[]`), and the card uses it when it writes the `automation` block back, so a
|
|
1462
|
+
* document that already holds an empty list is healed instead of being saved
|
|
1463
|
+
* back as an unrunnable schedule.
|
|
1464
|
+
*
|
|
1465
|
+
* This lives here rather than in `scheduler.ts` because the browser half cannot
|
|
1466
|
+
* import the host module: `scheduler.ts` pulls in `node:crypto` and the whole
|
|
1467
|
+
* upstream client, none of which exists in the browser bundle. Two hand-written
|
|
1468
|
+
* copies would drift, and the drift is invisible — the card would write a
|
|
1469
|
+
* schedule the scheduler does not run.
|
|
1432
1470
|
*/
|
|
1433
|
-
export declare
|
|
1471
|
+
export declare const DEFAULT_AUTOMATION_HOURS: {
|
|
1472
|
+
readonly checkin: readonly [9];
|
|
1473
|
+
readonly report: readonly [10];
|
|
1474
|
+
readonly tasks: readonly [11];
|
|
1475
|
+
readonly streak: readonly [12];
|
|
1476
|
+
readonly travel: readonly [9, 21];
|
|
1477
|
+
};
|
|
1478
|
+
//#endregion
|
|
1479
|
+
//#region src/catalog.d.ts
|
|
1480
|
+
/** One model the provider exposes. */
|
|
1481
|
+
interface WorkBuddyModelInfo {
|
|
1482
|
+
id: string;
|
|
1483
|
+
/** Display name; the multiplier is appended for the picker. */
|
|
1484
|
+
name: string;
|
|
1485
|
+
contextWindow: number;
|
|
1486
|
+
maxOutputTokens: number;
|
|
1487
|
+
/** Relative credit cost, e.g. 0.79 for `x0.79`. */
|
|
1488
|
+
multiplier?: number;
|
|
1489
|
+
/** Upstream-declared thinking levels. */
|
|
1490
|
+
supportedEfforts?: readonly string[];
|
|
1491
|
+
supportsImages: boolean;
|
|
1492
|
+
/** Upstream tags: free / limited-free / night-discount. */
|
|
1493
|
+
tags?: readonly string[];
|
|
1494
|
+
}
|
|
1434
1495
|
/**
|
|
1435
|
-
*
|
|
1496
|
+
* Static fallback used before the first live catalog fetch, and whenever the
|
|
1497
|
+
* upstream cannot be reached.
|
|
1436
1498
|
*
|
|
1437
|
-
*
|
|
1438
|
-
*
|
|
1439
|
-
*
|
|
1499
|
+
* This table is the DOMESTIC (CN) roster, and it is kept in step with what
|
|
1500
|
+
* `copilot.tencent.com` actually advertises. It matters more than a "just in
|
|
1501
|
+
* case" list usually would: a failed startup fetch falls back to it, and the
|
|
1502
|
+
* user sees that as "half my models were deleted" — `deepseek-v4.1-flash`
|
|
1503
|
+
* and friends simply vanishing from the picker, with no visible explanation.
|
|
1504
|
+
*
|
|
1505
|
+
* Every row below was captured from the live endpoint, INCLUDING the window
|
|
1506
|
+
* sizes: the previous version carried a 32K window for `hy3` when the gateway
|
|
1507
|
+
* says 192K, and listed `kimi-k3` under an id the gateway no longer uses
|
|
1508
|
+
* (`kimi-k3-1`). A stale fallback is worse than a short one — it looks
|
|
1509
|
+
* authoritative while being wrong.
|
|
1510
|
+
*
|
|
1511
|
+
* `multiplier: 0` is the gateways' own spelling of "free" (`credits: "x0.00"`),
|
|
1512
|
+
* which is what turns on the free badge.
|
|
1440
1513
|
*/
|
|
1441
|
-
export declare
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
private
|
|
1445
|
-
private
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
* How long to wait for event scoring before re-reading the task list.
|
|
1449
|
-
* Tests set 0 so a pass does not spend nine real seconds per account.
|
|
1450
|
-
*/
|
|
1451
|
-
private readonly eventScoreWaitMs;
|
|
1452
|
-
/**
|
|
1453
|
-
* Gap between two expert summon chains.
|
|
1454
|
-
* Tests set 0 so a pass does not spend six real seconds per expert.
|
|
1455
|
-
*/
|
|
1456
|
-
private readonly expertGapMs;
|
|
1457
|
-
private enabled;
|
|
1458
|
-
private checkinHours;
|
|
1459
|
-
private taskHours;
|
|
1460
|
-
private reportHours;
|
|
1461
|
-
private streakHours;
|
|
1462
|
-
private travelHours;
|
|
1463
|
-
private timer;
|
|
1464
|
-
private running;
|
|
1465
|
-
/** Guards against a slow run overlapping the next tick. */
|
|
1466
|
-
private busy;
|
|
1467
|
-
/** True while a manual run is in flight, so the card can poll it. */
|
|
1468
|
-
private runInFlight;
|
|
1469
|
-
/**
|
|
1470
|
-
* Set once {@link stop} is called.
|
|
1471
|
-
*
|
|
1472
|
-
* Deliberately false before `start`: the loop is not running yet, but a
|
|
1473
|
-
* manual `tick` must still work. `stop` is what makes a run abandon the
|
|
1474
|
-
* accounts it has not reached yet.
|
|
1475
|
-
*/
|
|
1476
|
-
private stopped;
|
|
1477
|
-
private readonly states;
|
|
1478
|
-
private claimableSeen;
|
|
1479
|
-
/**
|
|
1480
|
-
* Credits/energy/tasks earned per account TODAY, keyed by account id.
|
|
1481
|
-
*
|
|
1482
|
-
* Cleared whenever the day key rolls over, so the card always answers
|
|
1483
|
-
* "what did the automation get for THIS account today".
|
|
1484
|
-
*/
|
|
1485
|
-
private earnings;
|
|
1486
|
-
/** Day key the counters above belong to. */
|
|
1487
|
-
private earningsDate;
|
|
1488
|
-
/** Host hooks that persist the ledger across restarts. */
|
|
1489
|
-
private readonly loadEarnings;
|
|
1490
|
-
private saveEarningsFn;
|
|
1491
|
-
constructor(pool: WorkBuddyAccountPool, client: WorkBuddyUpstreamClient, options?: AutomationOptions);
|
|
1492
|
-
/** Apply a new configuration; safe to call while running. */
|
|
1514
|
+
export declare const FALLBACK_WORKBUDDY_MODELS: readonly WorkBuddyModelInfo[];
|
|
1515
|
+
/** Live catalog with a static fallback behind it. */
|
|
1516
|
+
export declare class WorkBuddyCatalog {
|
|
1517
|
+
private models;
|
|
1518
|
+
private listeners;
|
|
1519
|
+
/** User's model selection. Empty object = follow the catalog unfiltered. */
|
|
1520
|
+
private selection;
|
|
1493
1521
|
/**
|
|
1494
|
-
*
|
|
1522
|
+
* Whether `models` came from the gateway or from the static table.
|
|
1495
1523
|
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1524
|
+
* Tracked so the card can SAY which it is showing. A failed fetch used to be
|
|
1525
|
+
* invisible on screen: the picker simply held fewer models than before, which
|
|
1526
|
+
* the user reasonably read as "the plugin deleted my models" rather than "the
|
|
1527
|
+
* network hiccuped at startup".
|
|
1499
1528
|
*/
|
|
1500
|
-
|
|
1529
|
+
private source;
|
|
1530
|
+
/** When the live list last landed. */
|
|
1531
|
+
private lastUpdatedAt;
|
|
1532
|
+
/** Why the last fetch failed, for display. */
|
|
1533
|
+
private lastError;
|
|
1534
|
+
current(): readonly WorkBuddyModelInfo[];
|
|
1501
1535
|
/**
|
|
1502
|
-
*
|
|
1536
|
+
* The models DSH should actually offer, after applying the user's selection:
|
|
1537
|
+
* disabled models are dropped, an explicit image list overrides the upstream
|
|
1538
|
+
* capability flag, and a per-model budget caps the advertised window.
|
|
1503
1539
|
*
|
|
1504
|
-
*
|
|
1505
|
-
*
|
|
1506
|
-
* claim yesterday as today.
|
|
1540
|
+
* An absent `enabledModelIds` means "everything" — a fresh install with no
|
|
1541
|
+
* saved selection must not present an empty picker.
|
|
1507
1542
|
*/
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1543
|
+
visible(): readonly WorkBuddyModelInfo[];
|
|
1544
|
+
/** Replace the catalog and notify the adapter to rebuild its model list. */
|
|
1545
|
+
update(models: readonly WorkBuddyModelInfo[]): void;
|
|
1546
|
+
/** Restore the static fallback, e.g. when the upstream stops answering. */
|
|
1547
|
+
reset(): void;
|
|
1548
|
+
/** Replace the user's selection; the adapter rebuilds from `visible()`. */
|
|
1549
|
+
applySelection(selection: ModelSelection): void;
|
|
1550
|
+
/** The selection currently in force, for the card's save round-trip. */
|
|
1551
|
+
currentSelection(): ModelSelection;
|
|
1517
1552
|
/**
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
* Exists so the automation can be verified from the card without waiting for
|
|
1521
|
-
* its hour. A manual run is recorded exactly like a scheduled one, so the
|
|
1522
|
-
* timer will not repeat it later the same day: every job is idempotent, but a
|
|
1523
|
-
* second pass would still be wasted upstream calls.
|
|
1553
|
+
* Whether this catalog is serving live data or the built-in table.
|
|
1524
1554
|
*
|
|
1525
|
-
* `
|
|
1526
|
-
*
|
|
1555
|
+
* `fallback` is not an error state, but it IS a degraded one: the user is
|
|
1556
|
+
* looking at a shorter roster than the gateway offers, so the card says so
|
|
1557
|
+
* and offers a retry instead of letting them wonder where the models went.
|
|
1527
1558
|
*/
|
|
1528
|
-
|
|
1559
|
+
currentSource(): PoolWebCatalogSource;
|
|
1529
1560
|
/**
|
|
1530
|
-
*
|
|
1561
|
+
* When the live list last landed, if it ever did.
|
|
1531
1562
|
*
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1563
|
+
* Named `catalogUpdatedAt()` rather than `updatedAt()` because the class
|
|
1564
|
+
* already had an `updatedAt` member; two members of the same name is a
|
|
1565
|
+
* compile error, and the awkwardness is a useful signal that the concept is
|
|
1566
|
+
* "when THIS catalog was refreshed", not a generic timestamp.
|
|
1535
1567
|
*/
|
|
1568
|
+
catalogUpdatedAt(): string | undefined;
|
|
1569
|
+
/** Why the last fetch failed, if it did. */
|
|
1570
|
+
lastFetchError(): string | undefined;
|
|
1571
|
+
onChange(listener: () => void): () => void;
|
|
1572
|
+
find(id: string): WorkBuddyModelInfo | undefined;
|
|
1573
|
+
/** Replace the catalog from the live upstream list; keeps the fallback if empty. */
|
|
1574
|
+
updateFromUpstream(models: readonly WorkBuddyUpstreamModel[]): void;
|
|
1536
1575
|
/**
|
|
1537
|
-
*
|
|
1538
|
-
*
|
|
1539
|
-
* A pass takes tens of seconds - one upstream round trip per account per job,
|
|
1540
|
-
* plus the scoring wait - which is far too long to hold the card request open:
|
|
1541
|
-
* the browser or the host web server would time out, and the user would see
|
|
1542
|
-
* a hung button for a run that is actually working.
|
|
1576
|
+
* Record that a fetch attempt failed, leaving the current list in place.
|
|
1543
1577
|
*
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
* the
|
|
1547
|
-
*/
|
|
1548
|
-
startRunAll(): boolean;
|
|
1549
|
-
runAll(): Promise<AutomationRunSummary>;
|
|
1550
|
-
status(): AutomationStatus;
|
|
1551
|
-
/**
|
|
1552
|
-
* One poll: run every due job, serially.
|
|
1553
|
-
*
|
|
1554
|
-
* Serial by design — the jobs share the same accounts and the upstream
|
|
1555
|
-
* rate-limits per account, so overlapping passes would only trip that limit.
|
|
1556
|
-
* A job that throws is recorded and the loop continues.
|
|
1578
|
+
* The list is deliberately NOT reset here: a refresh that fails should keep
|
|
1579
|
+
* whatever working catalog is already loaded, rather than demoting a healthy
|
|
1580
|
+
* session to the static table because one retry ran out.
|
|
1557
1581
|
*/
|
|
1582
|
+
noteFetchFailure(message: string): void;
|
|
1583
|
+
private notify;
|
|
1584
|
+
}
|
|
1585
|
+
/** The user's model selection, as stored in the settings section. */
|
|
1586
|
+
interface ModelSelection {
|
|
1587
|
+
/** Absent = every model in the catalog is offered. */
|
|
1588
|
+
enabledModelIds?: readonly string[];
|
|
1589
|
+
/** Absent = each model follows its upstream image capability. */
|
|
1590
|
+
imageModelIds?: readonly string[];
|
|
1591
|
+
/** Per-model context-window cap, keyed by model id. */
|
|
1592
|
+
contextBudgets?: Readonly<Record<string, number | undefined>>;
|
|
1593
|
+
}
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region src/shim.d.ts
|
|
1596
|
+
interface ShimLogger {
|
|
1597
|
+
info?(...args: unknown[]): void;
|
|
1598
|
+
warn(...args: unknown[]): void;
|
|
1599
|
+
error(...args: unknown[]): void;
|
|
1600
|
+
}
|
|
1601
|
+
interface WorkBuddyShim {
|
|
1602
|
+
ready: Promise<void>;
|
|
1603
|
+
baseUrl(): string;
|
|
1604
|
+
token(): string;
|
|
1605
|
+
close(): Promise<void>;
|
|
1606
|
+
}
|
|
1607
|
+
interface WorkBuddyShimOptions {
|
|
1608
|
+
pool: WorkBuddyAccountPool;
|
|
1609
|
+
client: WorkBuddyUpstreamClient;
|
|
1610
|
+
catalog: WorkBuddyCatalog;
|
|
1611
|
+
logger?: ShimLogger;
|
|
1558
1612
|
/**
|
|
1559
|
-
*
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
* Hours are consumed per SLOT (one entry per configured hour), so a job with
|
|
1563
|
-
* two hours still runs twice a day — but a job whose hour passed while DSH was
|
|
1564
|
-
* closed runs immediately on the next tick instead of waiting for tomorrow.
|
|
1613
|
+
* Restrict this shim to one gateway. Two shims run side by side — one
|
|
1614
|
+
* per region — and each must only ever draw accounts that belong to its
|
|
1615
|
+
* own gateway. Absent means "every account" (a single-region deployment).
|
|
1565
1616
|
*/
|
|
1617
|
+
region?: WorkBuddyRegion;
|
|
1618
|
+
/** Max accounts to try per request before giving up. */
|
|
1619
|
+
maxAttempts?: number;
|
|
1620
|
+
}
|
|
1621
|
+
export declare function createWorkBuddyShim(options: WorkBuddyShimOptions): WorkBuddyShim;
|
|
1622
|
+
//#endregion
|
|
1623
|
+
//#region src/adapter.d.ts
|
|
1624
|
+
/** Provider route this bundle owns. */
|
|
1625
|
+
/** Provider route this bundle owns for the domestic (CN) gateway. */
|
|
1626
|
+
export declare const WORKBUDDY_POOL_PROVIDER = "workbuddy-xdpool";
|
|
1627
|
+
interface WorkBuddyAdapterOptions {
|
|
1628
|
+
shim: WorkBuddyShim;
|
|
1629
|
+
catalog: WorkBuddyCatalog;
|
|
1630
|
+
/** Plugin context; the pi-ai adapter reads `attachments`/`fs` from it. */
|
|
1631
|
+
ctx: Context$1;
|
|
1632
|
+
providerId?: string;
|
|
1633
|
+
displayName?: string;
|
|
1634
|
+
}
|
|
1635
|
+
/** What {@link createWorkBuddyAdapter} hands back. */
|
|
1636
|
+
interface WorkBuddyAdapter {
|
|
1637
|
+
providerId: string;
|
|
1638
|
+
displayName: string;
|
|
1639
|
+
adapter: PiAiAdapter;
|
|
1640
|
+
/** Rebuild the pi-ai model list from the current catalog. */
|
|
1641
|
+
buildModels: () => Model<Api>[];
|
|
1642
|
+
/** Rebuild the adapter's provider snapshot; call after a catalog update. */
|
|
1643
|
+
invalidate: () => void;
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Assemble the adapter. `getModels` re-reads the live catalog, and every
|
|
1647
|
+
* model's `baseUrl` is re-resolved per read so the shim's ephemeral port
|
|
1648
|
+
* applies from the first snapshot after startup. Call only after `shim.ready`.
|
|
1649
|
+
*/
|
|
1650
|
+
export declare function createWorkBuddyAdapter(options: WorkBuddyAdapterOptions): WorkBuddyAdapter;
|
|
1651
|
+
//#endregion
|
|
1652
|
+
//#region src/scheduler.d.ts
|
|
1653
|
+
/** How often the loop wakes to look for a due job. */
|
|
1654
|
+
/**
|
|
1655
|
+
* How long to wait for event scoring to land before re-reading the task list.
|
|
1656
|
+
*
|
|
1657
|
+
* Scoring is asynchronous on the upstream side, so an immediate re-read still
|
|
1658
|
+
* shows the old progress and the claim pass would skip a task that is in fact
|
|
1659
|
+
* now claimable. Measured: the chain is reflected by about eight seconds.
|
|
1660
|
+
*/
|
|
1661
|
+
export declare const EVENT_SCORE_WAIT_MS = 9000;
|
|
1662
|
+
export declare const AUTOMATION_TICK_MS = 60000;
|
|
1663
|
+
/** Which jobs the automation runs, and when. */
|
|
1664
|
+
/**
|
|
1665
|
+
* The persisted daily earnings ledger.
|
|
1666
|
+
*
|
|
1667
|
+
* `date` is the local day the counters belong to: a ledger from a previous day
|
|
1668
|
+
* is discarded on load rather than carried forward as "today".
|
|
1669
|
+
*/
|
|
1670
|
+
interface AutomationLedger {
|
|
1671
|
+
date: string;
|
|
1672
|
+
accounts: Record<string, AutomationAccountEarnings>;
|
|
1673
|
+
}
|
|
1674
|
+
interface AutomationOptions {
|
|
1675
|
+
/** Master switch; false stops every job. */
|
|
1676
|
+
enabled?: boolean;
|
|
1677
|
+
/** Hour list for the daily check-in job. */
|
|
1678
|
+
checkinHours?: readonly number[];
|
|
1679
|
+
/** Hour list for the task-centre job (accept + claim). */
|
|
1680
|
+
taskHours?: readonly number[];
|
|
1681
|
+
/** Hour list for the activity-report job. */
|
|
1682
|
+
reportHours?: readonly number[];
|
|
1683
|
+
/** Hour list for the streak-redemption job. */
|
|
1684
|
+
streakHours?: readonly number[];
|
|
1685
|
+
/** Hour list for the buddy travel job. */
|
|
1686
|
+
travelHours?: readonly number[];
|
|
1687
|
+
/** Per-account serial delay, in milliseconds. */
|
|
1688
|
+
accountDelayMs?: number;
|
|
1689
|
+
/** Override the gap between expert chains, in milliseconds (tests use 0). */
|
|
1690
|
+
expertGapMs?: number;
|
|
1691
|
+
/** Override the event-scoring wait, in milliseconds (tests use 0). */
|
|
1692
|
+
eventScoreWaitMs?: number;
|
|
1693
|
+
/** Override the clock, for tests. */
|
|
1694
|
+
now?: () => Date;
|
|
1695
|
+
/** Logger; defaults to a no-op so tests stay quiet. */
|
|
1696
|
+
logger?: SchedulerLogger;
|
|
1566
1697
|
/**
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
* Returns the SLOT STRING (not a boolean) because the caller must record the
|
|
1570
|
-
* same value it acted on. Returning a boolean was the original bug's enabler:
|
|
1571
|
-
* the tick asked "is it due", then `runJob` independently wrote "what time is
|
|
1572
|
-
* it now" — and those two answers were almost never equal.
|
|
1573
|
-
*
|
|
1574
|
-
* CATCH-UP semantics are deliberate: a candidate fires once its hour has
|
|
1575
|
-
* PASSED and its slot is still unconsumed, so a laptop that slept through
|
|
1576
|
-
* 10:00 still runs the job when it wakes, on the same day.
|
|
1698
|
+
* Persist the daily earnings ledger, and restore it on construction.
|
|
1577
1699
|
*
|
|
1578
|
-
* The
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1581
|
-
*/
|
|
1582
|
-
private dueSlot;
|
|
1583
|
-
/** Today's consumed slots for one job, as a set. */
|
|
1584
|
-
private firedSlotsOf;
|
|
1585
|
-
/** Record one consumed slot on a job's state. */
|
|
1586
|
-
private consumeSlot;
|
|
1587
|
-
private tick;
|
|
1588
|
-
/** Run one job against every eligible account and record the outcome. */
|
|
1589
|
-
/**
|
|
1590
|
-
* Add one account's take to today's counters, resetting first if the day
|
|
1591
|
-
* rolled over. Called from the task pass, which is the only job that earns.
|
|
1700
|
+
* The ledger cannot live only in memory: a host restart mid-day would wipe
|
|
1701
|
+
* what the automation already earned, and the card would show nothing for
|
|
1702
|
+
* rewards that really were collected.
|
|
1592
1703
|
*/
|
|
1704
|
+
loadEarnings?: () => AutomationLedger | undefined;
|
|
1705
|
+
saveEarnings?: (ledger: AutomationLedger) => void;
|
|
1706
|
+
}
|
|
1707
|
+
/** Logger surface, kept structural so any host logger fits. */
|
|
1708
|
+
interface SchedulerLogger {
|
|
1709
|
+
info?(...args: unknown[]): void;
|
|
1710
|
+
warn?(...args: unknown[]): void;
|
|
1711
|
+
}
|
|
1712
|
+
/** One job's last-run record, surfaced on the status document. */
|
|
1713
|
+
interface AutomationJobState {
|
|
1714
|
+
/** `YYYY-MM-DD` of the last completed run, or undefined if it never ran. */
|
|
1715
|
+
lastRunDate?: string;
|
|
1593
1716
|
/**
|
|
1594
|
-
*
|
|
1717
|
+
* The scheduled SLOT of the last run, as `YYYY-MM-DDTHH`.
|
|
1595
1718
|
*
|
|
1596
|
-
*
|
|
1597
|
-
*
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
* Add one account take to today counters, resetting first if the day rolled
|
|
1602
|
-
* over. Every source is tracked separately so the card can show what earned
|
|
1603
|
-
* what, rather than one opaque total.
|
|
1719
|
+
* The tick de-duplicates on this rather than on the date: keying on the date
|
|
1720
|
+
* alone caps every job at one run a day, which is wrong for a job with two
|
|
1721
|
+
* time points — blocking the cat loop's second pass would leave the cat out
|
|
1722
|
+
* until tomorrow. Per slot a job runs once in each configured hour, while a
|
|
1723
|
+
* repeat tick inside the same hour is still refused.
|
|
1604
1724
|
*/
|
|
1605
|
-
|
|
1606
|
-
/** Clear the per-account counters when the local day changes. */
|
|
1607
|
-
private rollEarnings;
|
|
1725
|
+
lastRunSlot?: string;
|
|
1608
1726
|
/**
|
|
1609
|
-
*
|
|
1727
|
+
* Every configured slot consumed TODAY, as `YYYY-MM-DDTHH`.
|
|
1610
1728
|
*
|
|
1611
|
-
*
|
|
1612
|
-
*
|
|
1613
|
-
*
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
/**
|
|
1617
|
-
* Run one job against every eligible account and record the outcome.
|
|
1729
|
+
* This is what actually gates a re-run, and it is a SET rather than a single
|
|
1730
|
+
* slot because one field could not express the contract: a job configured for
|
|
1731
|
+
* `[9, 21]` runs twice, and a single "last slot" value can only remember one
|
|
1732
|
+
* of them — the second run erased the first, so the 9 o'clock candidate looked
|
|
1733
|
+
* unconsumed again and the job re-fired every hour after 21:00.
|
|
1618
1734
|
*
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1621
|
-
* the
|
|
1622
|
-
*
|
|
1623
|
-
* almost never equals the configured candidate the gate had compared against.
|
|
1735
|
+
* The values are the CONFIGURED hours that were spent, never the clock time
|
|
1736
|
+
* the run happened to finish at. Storing the finish time was the original
|
|
1737
|
+
* defect: the reader compared it against a configured candidate, so the two
|
|
1738
|
+
* almost never matched and the gate stayed open all day.
|
|
1624
1739
|
*
|
|
1625
|
-
*
|
|
1626
|
-
* up
|
|
1627
|
-
*
|
|
1628
|
-
* same breath finds the task still un-scored — and because sending is fast
|
|
1629
|
-
* while claiming wants the whole pool to have been lit up first. Splitting
|
|
1630
|
-
* them costs one shared wait instead of one wait per account.
|
|
1740
|
+
* In-memory like the rest of `states`: this is a per-process record, and the
|
|
1741
|
+
* catch-up design deliberately re-runs an hour that passed while DSH was
|
|
1742
|
+
* closed. See `automationEarnings` for the ledger that DOES persist.
|
|
1631
1743
|
*/
|
|
1632
|
-
|
|
1633
|
-
/** Compose the one-line summary shown on the card. */
|
|
1634
|
-
private summarise;
|
|
1744
|
+
firedSlots?: readonly string[];
|
|
1635
1745
|
/**
|
|
1636
|
-
*
|
|
1746
|
+
* The clock slot the last run STARTED in (`YYYY-MM-DDTHH`).
|
|
1637
1747
|
*
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1748
|
+
* Separate from {@link firedSlots} on purpose: this is a throttle ("do not
|
|
1749
|
+
* start twice inside the same hour"), while `firedSlots` is the schedule
|
|
1750
|
+
* ledger. Conflating the two is what let a catch-up run at 13:00 erase the
|
|
1751
|
+
* record of the 9 o'clock slot.
|
|
1640
1752
|
*/
|
|
1641
|
-
|
|
1753
|
+
lastFiredHour?: string;
|
|
1754
|
+
/** Epoch ms of the last completed run. */
|
|
1755
|
+
lastRunAtMs?: number;
|
|
1756
|
+
/** Accounts that completed without throwing. */
|
|
1757
|
+
ok: number;
|
|
1758
|
+
/** Accounts that threw (each one skipped, the run continued). */
|
|
1759
|
+
failed: number;
|
|
1760
|
+
/** Credits claimed by the task job on the last run. */
|
|
1761
|
+
credit: number;
|
|
1762
|
+
/** Energy claimed by the task job on the last run. */
|
|
1763
|
+
energy: number;
|
|
1764
|
+
/** Tasks claimed by the task job on the last run. */
|
|
1765
|
+
claimed: number;
|
|
1766
|
+
/** Human-readable summary of the last run. */
|
|
1642
1767
|
/**
|
|
1643
|
-
*
|
|
1768
|
+
* What this run actually did, in the words of the task board.
|
|
1644
1769
|
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
* exists to avoid.
|
|
1770
|
+
* `message` is a count ("3 accounts, 5 tasks claimed"); this is the list a
|
|
1771
|
+
* person can check off — the reward titles the pass collected. A row showing
|
|
1772
|
+
* only a bare number cannot answer "did it do the thing I care about", which
|
|
1773
|
+
* is the question the panel exists to answer.
|
|
1650
1774
|
*/
|
|
1651
|
-
|
|
1652
|
-
private sendEventChains;
|
|
1775
|
+
detail?: readonly string[];
|
|
1653
1776
|
/**
|
|
1654
|
-
*
|
|
1777
|
+
* A pending milestone worth naming, when there is one.
|
|
1655
1778
|
*
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1779
|
+
* Streak tiers are why this exists: every tier reads `locked` until enough
|
|
1780
|
+
* consecutive days accumulate, and "locked" on its own reads as "broken"
|
|
1781
|
+
* rather than "come back in four days".
|
|
1659
1782
|
*/
|
|
1660
|
-
|
|
1783
|
+
progress?: string;
|
|
1784
|
+
message?: string;
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* What the automation earned for ONE account today.
|
|
1788
|
+
*
|
|
1789
|
+
* Reset at the local day boundary alongside the per-job "already ran today"
|
|
1790
|
+
* guard, so the card shows today rather than a running total that never
|
|
1791
|
+
* answers "did it do anything for this account recently".
|
|
1792
|
+
*/
|
|
1793
|
+
interface AutomationAccountEarnings {
|
|
1794
|
+
/** Credits the automation claimed from the task centre today. */
|
|
1795
|
+
credit: number;
|
|
1796
|
+
/** Energy claimed from the task centre today. */
|
|
1797
|
+
energy: number;
|
|
1798
|
+
/** Tasks claimed today. */
|
|
1799
|
+
claimed: number;
|
|
1661
1800
|
/**
|
|
1662
|
-
*
|
|
1801
|
+
* Credits the automation collected from check-in today.
|
|
1663
1802
|
*
|
|
1664
|
-
*
|
|
1665
|
-
*
|
|
1666
|
-
*
|
|
1667
|
-
* this is async.
|
|
1803
|
+
* Kept separate from `credit` because they are different achievements and the
|
|
1804
|
+
* card shows them on their own lines: "the automation claimed 3 tasks" and
|
|
1805
|
+
* "the automation checked in" are not the same claim to the user.
|
|
1668
1806
|
*/
|
|
1669
|
-
|
|
1807
|
+
checkinCredit: number;
|
|
1808
|
+
/** Credits from streak redemption + lottery today. */
|
|
1809
|
+
bonusCredit: number;
|
|
1810
|
+
/** Credits from the buddy adoption / travel loop today. */
|
|
1811
|
+
travelCredit: number;
|
|
1812
|
+
/** Local date the counters belong to. */
|
|
1813
|
+
date: string;
|
|
1814
|
+
}
|
|
1815
|
+
/** What ONE account gained during a single run. */
|
|
1816
|
+
interface AutomationAccountGain {
|
|
1817
|
+
credit: number;
|
|
1818
|
+
energy: number;
|
|
1819
|
+
claimed: number;
|
|
1820
|
+
checkinCredit: number;
|
|
1821
|
+
bonusCredit: number;
|
|
1822
|
+
travelCredit: number;
|
|
1823
|
+
}
|
|
1824
|
+
/** Totals from running the whole ordered pass at once. */
|
|
1825
|
+
interface AutomationRunSummary {
|
|
1826
|
+
/** How many jobs actually ran (a job with no hour is skipped). */
|
|
1827
|
+
jobsRun: number;
|
|
1828
|
+
/** Accounts that finished without error, summed across jobs. */
|
|
1829
|
+
okCount: number;
|
|
1830
|
+
/** Accounts that failed, summed across jobs. */
|
|
1831
|
+
failed: number;
|
|
1832
|
+
credit: number;
|
|
1833
|
+
energy: number;
|
|
1834
|
+
claimed: number;
|
|
1670
1835
|
/**
|
|
1671
|
-
*
|
|
1836
|
+
* What each account gained during THIS run, keyed by account id. Only
|
|
1837
|
+
* accounts that gained something appear.
|
|
1838
|
+
*/
|
|
1839
|
+
accounts: Readonly<Record<string, AutomationAccountGain>>;
|
|
1840
|
+
}
|
|
1841
|
+
/** Automation snapshot for the status document and the card. */
|
|
1842
|
+
interface AutomationStatus {
|
|
1843
|
+
enabled: boolean;
|
|
1844
|
+
/** Whether the loop is running. */
|
|
1845
|
+
running: boolean;
|
|
1846
|
+
checkinHours: readonly number[];
|
|
1847
|
+
taskHours: readonly number[];
|
|
1848
|
+
reportHours: readonly number[];
|
|
1849
|
+
streakHours: readonly number[];
|
|
1850
|
+
travelHours: readonly number[];
|
|
1851
|
+
jobs: {
|
|
1852
|
+
checkin: AutomationJobState;
|
|
1853
|
+
report: AutomationJobState;
|
|
1854
|
+
tasks: AutomationJobState;
|
|
1855
|
+
streak: AutomationJobState;
|
|
1856
|
+
travel: AutomationJobState;
|
|
1857
|
+
};
|
|
1858
|
+
/** Claimable tasks seen on the most recent task pass, across accounts. */
|
|
1859
|
+
claimableSeen: number;
|
|
1860
|
+
/**
|
|
1861
|
+
* Per-account totals for today, keyed by account id. Only accounts that
|
|
1862
|
+
* actually earned something appear, so the card can render "no earnings"
|
|
1863
|
+
* as an absence rather than a zero it has to explain.
|
|
1864
|
+
*/
|
|
1865
|
+
earningsToday: Readonly<Record<string, AutomationAccountEarnings>>;
|
|
1866
|
+
/**
|
|
1867
|
+
* Whether a manual run is in flight right now.
|
|
1672
1868
|
*
|
|
1673
|
-
*
|
|
1674
|
-
*
|
|
1675
|
-
*
|
|
1676
|
-
* id it will accept.
|
|
1869
|
+
* A run takes tens of seconds (one upstream call per account per job, plus
|
|
1870
|
+
* the scoring wait), which is far too long for the card to hold a request
|
|
1871
|
+
* open. The button starts a run and the panel polls this flag instead.
|
|
1677
1872
|
*/
|
|
1678
|
-
|
|
1873
|
+
runInProgress: boolean;
|
|
1874
|
+
}
|
|
1875
|
+
/** The three plus one job kinds, in a stable order. */
|
|
1876
|
+
type AutomationJobKind = 'checkin' | 'tasks' | 'report' | 'streak' | 'travel';
|
|
1877
|
+
/** The four jobs in the order a tick runs them: report before tasks, always. */
|
|
1878
|
+
export declare const AUTOMATION_JOB_KINDS: readonly AutomationJobKind[];
|
|
1879
|
+
/** Reject anything that is not a job kind, so a route cannot name an unknown job. */
|
|
1880
|
+
export declare function isAutomationJobKind(value: unknown): value is AutomationJobKind;
|
|
1881
|
+
export declare function dayKey(date: Date, timeZone?: string): string;
|
|
1882
|
+
/**
|
|
1883
|
+
* Whether `now`'s local hour is one of `hours`.
|
|
1884
|
+
*
|
|
1885
|
+
* The reference panel computes a `nextFire` instant and sleeps until it; this
|
|
1886
|
+
* loop instead wakes every minute and asks "is any job due now". Both fire at
|
|
1887
|
+
* the top of a configured hour, but the polling form cannot miss a slot to a
|
|
1888
|
+
* suspended process — a laptop that slept through 10:00 still runs the job the
|
|
1889
|
+
* moment it wakes, on the same day.
|
|
1890
|
+
*/
|
|
1891
|
+
export declare function isFireHour(now: Date, hours: readonly number[]): boolean;
|
|
1892
|
+
/**
|
|
1893
|
+
* The points automation.
|
|
1894
|
+
*
|
|
1895
|
+
* Owns a single timer loop. Construction is inert — nothing runs until
|
|
1896
|
+
* {@link start}, and {@link stop} is idempotent so a plugin teardown that fires
|
|
1897
|
+
* twice is harmless.
|
|
1898
|
+
*/
|
|
1899
|
+
export declare class WorkBuddyScheduler {
|
|
1900
|
+
private readonly pool;
|
|
1901
|
+
private readonly client;
|
|
1902
|
+
private readonly logger;
|
|
1903
|
+
private readonly now;
|
|
1904
|
+
private readonly delayMs;
|
|
1679
1905
|
/**
|
|
1680
|
-
*
|
|
1906
|
+
* How long to wait for event scoring before re-reading the task list.
|
|
1907
|
+
* Tests set 0 so a pass does not spend nine real seconds per account.
|
|
1908
|
+
*/
|
|
1909
|
+
private readonly eventScoreWaitMs;
|
|
1910
|
+
/**
|
|
1911
|
+
* Gap between two expert summon chains.
|
|
1912
|
+
* Tests set 0 so a pass does not spend six real seconds per expert.
|
|
1913
|
+
*/
|
|
1914
|
+
private readonly expertGapMs;
|
|
1915
|
+
private enabled;
|
|
1916
|
+
private checkinHours;
|
|
1917
|
+
private taskHours;
|
|
1918
|
+
private reportHours;
|
|
1919
|
+
private streakHours;
|
|
1920
|
+
private travelHours;
|
|
1921
|
+
private timer;
|
|
1922
|
+
private running;
|
|
1923
|
+
/** Guards against a slow run overlapping the next tick. */
|
|
1924
|
+
private busy;
|
|
1925
|
+
/** True while a manual run is in flight, so the card can poll it. */
|
|
1926
|
+
private runInFlight;
|
|
1927
|
+
/**
|
|
1928
|
+
* Set once {@link stop} is called.
|
|
1681
1929
|
*
|
|
1682
|
-
*
|
|
1683
|
-
*
|
|
1684
|
-
*
|
|
1685
|
-
* the lighthouse criterion looks for.
|
|
1930
|
+
* Deliberately false before `start`: the loop is not running yet, but a
|
|
1931
|
+
* manual `tick` must still work. `stop` is what makes a run abandon the
|
|
1932
|
+
* accounts it has not reached yet.
|
|
1686
1933
|
*/
|
|
1687
|
-
private
|
|
1934
|
+
private stopped;
|
|
1935
|
+
private readonly states;
|
|
1936
|
+
private claimableSeen;
|
|
1688
1937
|
/**
|
|
1689
|
-
*
|
|
1938
|
+
* Credits/energy/tasks earned per account TODAY, keyed by account id.
|
|
1690
1939
|
*
|
|
1691
|
-
*
|
|
1692
|
-
*
|
|
1693
|
-
* repeat claim answers `already_claimed` — so a pass that dies halfway is
|
|
1694
|
-
* safe to replay on the next tick.
|
|
1940
|
+
* Cleared whenever the day key rolls over, so the card always answers
|
|
1941
|
+
* "what did the automation get for THIS account today".
|
|
1695
1942
|
*/
|
|
1696
|
-
private
|
|
1943
|
+
private earnings;
|
|
1944
|
+
/** Day key the counters above belong to. */
|
|
1945
|
+
private earningsDate;
|
|
1946
|
+
/** Host hooks that persist the ledger across restarts. */
|
|
1947
|
+
private readonly loadEarnings;
|
|
1948
|
+
private saveEarningsFn;
|
|
1949
|
+
constructor(pool: WorkBuddyAccountPool, client: WorkBuddyUpstreamClient, options?: AutomationOptions);
|
|
1950
|
+
/** Apply a new configuration; safe to call while running. */
|
|
1697
1951
|
/**
|
|
1698
|
-
*
|
|
1952
|
+
* Install the persistence hook once the host settings service is available.
|
|
1699
1953
|
*
|
|
1700
|
-
*
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
1954
|
+
* Separate from the constructor because the scheduler is built with the pool,
|
|
1955
|
+
* long before the settings section exists; a ledger written before that point
|
|
1956
|
+
* would have nowhere to go.
|
|
1957
|
+
*/
|
|
1958
|
+
setEarningsPersistence(save: (ledger: AutomationLedger) => void | Promise<void>): void;
|
|
1959
|
+
/**
|
|
1960
|
+
* Fold a previously persisted ledger back in, when it belongs to today.
|
|
1703
1961
|
*
|
|
1704
|
-
*
|
|
1705
|
-
*
|
|
1962
|
+
* Used after the settings document becomes readable, which happens after
|
|
1963
|
+
* construction; a ledger from an earlier day is ignored so the counters never
|
|
1964
|
+
* claim yesterday as today.
|
|
1706
1965
|
*/
|
|
1707
|
-
|
|
1966
|
+
applyEarningsLedger(ledger: AutomationLedger): void;
|
|
1967
|
+
applyConfig(options: AutomationOptions): void;
|
|
1968
|
+
/** Hours for one job, used by the loop and the status document. */
|
|
1969
|
+
private hoursOf;
|
|
1970
|
+
/** Start the loop. Idempotent. */
|
|
1971
|
+
start(): void;
|
|
1972
|
+
/** Stop the loop. Idempotent, and safe before `start`. */
|
|
1973
|
+
stop(): void;
|
|
1974
|
+
/** Snapshot for the status document. */
|
|
1708
1975
|
/**
|
|
1709
|
-
*
|
|
1976
|
+
* Run one job immediately, regardless of the clock.
|
|
1710
1977
|
*
|
|
1711
|
-
*
|
|
1712
|
-
*
|
|
1713
|
-
*
|
|
1978
|
+
* Exists so the automation can be verified from the card without waiting for
|
|
1979
|
+
* its hour. A manual run is recorded exactly like a scheduled one, so the
|
|
1980
|
+
* timer will not repeat it later the same day: every job is idempotent, but a
|
|
1981
|
+
* second pass would still be wasted upstream calls.
|
|
1714
1982
|
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
* bookkeeping.
|
|
1983
|
+
* `force` ignores the already-ran-today guard, which is what pressing the
|
|
1984
|
+
* button a second time means.
|
|
1718
1985
|
*/
|
|
1719
|
-
|
|
1720
|
-
}
|
|
1721
|
-
//#endregion
|
|
1722
|
-
//#region src/status.d.ts
|
|
1723
|
-
/** One account's status row. */
|
|
1724
|
-
interface AccountStatus {
|
|
1725
|
-
id: string;
|
|
1726
|
-
label: string;
|
|
1727
|
-
nickname?: string;
|
|
1728
|
-
domain: string;
|
|
1729
|
-
/** ISO timestamp when the access token expires. */
|
|
1730
|
-
expiresAt?: string;
|
|
1731
|
-
/** Account-wide cooldown (every model blocked). */
|
|
1732
|
-
cooling: boolean;
|
|
1733
|
-
cooldownUntil?: string;
|
|
1734
|
-
/** Per-model cooldowns active right now (modelId → ISO until); the account
|
|
1735
|
-
* itself is not `cooling` while only some models are limited. */
|
|
1736
|
-
modelCooldowns?: readonly {
|
|
1737
|
-
modelId: string;
|
|
1738
|
-
until: string;
|
|
1739
|
-
}[];
|
|
1740
|
-
rateLimitHits: number;
|
|
1741
|
-
/** Read-only aggregated credit summary for the account. */
|
|
1742
|
-
credits?: WorkBuddyCredits;
|
|
1743
|
-
creditsError?: string;
|
|
1744
|
-
sourcePath: string;
|
|
1745
|
-
}
|
|
1746
|
-
/** Whole-plugin status document. */
|
|
1747
|
-
interface WorkBuddyStatus {
|
|
1748
|
-
ok: boolean;
|
|
1749
|
-
accounts: AccountStatus[];
|
|
1750
|
-
activeAccountId?: string;
|
|
1751
|
-
cooling: number;
|
|
1752
|
-
models: {
|
|
1753
|
-
id: string;
|
|
1754
|
-
name: string;
|
|
1755
|
-
multiplier?: number;
|
|
1756
|
-
tags?: readonly string[];
|
|
1757
|
-
}[];
|
|
1758
|
-
shim: {
|
|
1759
|
-
running: boolean;
|
|
1760
|
-
baseUrl?: string;
|
|
1761
|
-
};
|
|
1762
|
-
}
|
|
1763
|
-
interface StatusOptions {
|
|
1764
|
-
pool: WorkBuddyAccountPool;
|
|
1765
|
-
/** The catalog to report. Regional callers pass their own region's. */
|
|
1766
|
-
catalog: WorkBuddyCatalog;
|
|
1767
|
-
client: WorkBuddyUpstreamClient;
|
|
1768
|
-
shim?: {
|
|
1769
|
-
running: boolean;
|
|
1770
|
-
baseUrl?: string;
|
|
1771
|
-
};
|
|
1772
|
-
/** Query credits per account. Off for cheap diagnostics runs. */
|
|
1773
|
-
includeCredits?: boolean;
|
|
1774
|
-
}
|
|
1775
|
-
/** Build the status document. Never throws. */
|
|
1776
|
-
export declare function buildStatus(options: StatusOptions): Promise<WorkBuddyStatus>;
|
|
1777
|
-
/** Format the status document for a terminal. */
|
|
1778
|
-
export declare function formatStatus(status: WorkBuddyStatus): string;
|
|
1779
|
-
/** Format the per-model credit multipliers. */
|
|
1780
|
-
export declare function formatRates(status: WorkBuddyStatus): string;
|
|
1781
|
-
//#endregion
|
|
1782
|
-
//#region src/status-paths.d.ts
|
|
1783
|
-
/**
|
|
1784
|
-
* Node-free constants and types shared by the Host and browser halves of the
|
|
1785
|
-
* WorkBuddy XD Pool settings card.
|
|
1786
|
-
*
|
|
1787
|
-
* Pool's runtime state already lives in `src/status.ts` (`buildStatus` /
|
|
1788
|
-
* `WorkBuddyStatus`); this module only carves the cross-domain (Host→browser)
|
|
1789
|
-
* JSON document into a shape that stays token-free and matches what the
|
|
1790
|
-
* browser card renders. Route paths are plugin-owned and mounted on the Host's
|
|
1791
|
-
* same-origin web server (see `src/web-status.ts`).
|
|
1792
|
-
*
|
|
1793
|
-
* @module dsh-workbuddy-xdpool/status-paths
|
|
1794
|
-
*/
|
|
1795
|
-
/** Plugin-owned read-only pool status endpoint (account rows + models + shim). */
|
|
1796
|
-
export declare const POOL_STATUS_PATH = "/plugins/dsh-workbuddy-xdpool/status";
|
|
1797
|
-
/** Plugin-owned local account rescan endpoint (re-read desktop snapshots). */
|
|
1798
|
-
export declare const POOL_RESCAN_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/rescan";
|
|
1799
|
-
/** Plugin-owned cooldown reset endpoint (clear all 429 cooldowns). */
|
|
1800
|
-
export declare const POOL_RESET_COOLDOWN_PATH = "/plugins/dsh-workbuddy-xdpool/cooldowns/reset";
|
|
1801
|
-
/** Plugin-owned daily check-in action endpoint (claim today's reward). */
|
|
1802
|
-
export declare const POOL_CHECKIN_PATH = "/plugins/dsh-workbuddy-xdpool/checkin";
|
|
1803
|
-
/** Plugin-owned model-selection save endpoint (writes the settings section). */
|
|
1804
|
-
export declare const POOL_MODELS_SAVE_PATH = "/plugins/dsh-workbuddy-xdpool/models/save";
|
|
1805
|
-
/**
|
|
1806
|
-
* Throw one account out of the pool for good, or take it back.
|
|
1807
|
-
*
|
|
1808
|
-
* Separate from the disable route because the semantics differ: disabling is a
|
|
1809
|
-
* rotation preference the account survives, ignoring survives the account.
|
|
1810
|
-
*/
|
|
1811
|
-
export declare const POOL_ACCOUNT_IGNORE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/ignored";
|
|
1812
|
-
/** Run one automation job immediately, so the card can verify it on demand. */
|
|
1813
|
-
export declare const POOL_AUTOMATION_RUN_PATH = "/plugins/dsh-workbuddy-xdpool/automation/run";
|
|
1814
|
-
/** Set or clear one account's reserved-credit floor. */
|
|
1815
|
-
export declare const POOL_CREDIT_RESERVE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/credit-reserve";
|
|
1816
|
-
/** One account's row, token-free. */
|
|
1817
|
-
interface PoolWebAccount {
|
|
1818
|
-
id: string;
|
|
1819
|
-
label: string;
|
|
1820
|
-
nickname?: string;
|
|
1821
|
-
domain: string;
|
|
1822
|
-
/** ISO timestamp; absent when the credential carries no expiry. */
|
|
1823
|
-
expiresAt?: string;
|
|
1824
|
-
/** Account-wide cooldown (every model blocked); only after a no-model penalize. */
|
|
1825
|
-
cooling: boolean;
|
|
1826
|
-
/** ISO timestamp when the account-wide 429 cooldown lifts; only while cooling. */
|
|
1827
|
-
cooldownUntil?: string;
|
|
1986
|
+
runNow(kind: AutomationJobKind, force?: boolean): Promise<AutomationJobState>;
|
|
1828
1987
|
/**
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1988
|
+
* Run every job once, in the scheduled order.
|
|
1989
|
+
*
|
|
1990
|
+
* Order matters and is not configurable: the activity report has to land
|
|
1991
|
+
* before the task pass reads task progress, or the pass sees counters the
|
|
1992
|
+
* report would have moved. This is what the card's single button calls.
|
|
1833
1993
|
*/
|
|
1834
|
-
modelCooldowns?: ReadonlyArray<{
|
|
1835
|
-
modelId: string;
|
|
1836
|
-
until: string;
|
|
1837
|
-
}>;
|
|
1838
1994
|
/**
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1995
|
+
* Start a full pass in the background and return immediately.
|
|
1996
|
+
*
|
|
1997
|
+
* A pass takes tens of seconds - one upstream round trip per account per job,
|
|
1998
|
+
* plus the scoring wait - which is far too long to hold the card request open:
|
|
1999
|
+
* the browser or the host web server would time out, and the user would see
|
|
2000
|
+
* a hung button for a run that is actually working.
|
|
2001
|
+
*
|
|
2002
|
+
* Returns whether a run started. A second call while one is in flight is
|
|
2003
|
+
* ignored rather than queued: pressing the button twice means hurry up, and
|
|
2004
|
+
* the run already under way covers it.
|
|
1841
2005
|
*/
|
|
1842
|
-
|
|
1843
|
-
|
|
2006
|
+
startRunAll(): boolean;
|
|
2007
|
+
runAll(): Promise<AutomationRunSummary>;
|
|
2008
|
+
status(): AutomationStatus;
|
|
1844
2009
|
/**
|
|
1845
|
-
*
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
2010
|
+
* One poll: run every due job, serially.
|
|
2011
|
+
*
|
|
2012
|
+
* Serial by design — the jobs share the same accounts and the upstream
|
|
2013
|
+
* rate-limits per account, so overlapping passes would only trip that limit.
|
|
2014
|
+
* A job that throws is recorded and the loop continues.
|
|
1848
2015
|
*/
|
|
1849
|
-
creditReserve: number;
|
|
1850
2016
|
/**
|
|
1851
|
-
* Whether
|
|
1852
|
-
*
|
|
1853
|
-
*
|
|
2017
|
+
* Whether `kind` is due at `now`: its earliest configured hour has passed in
|
|
2018
|
+
* the scheduling timezone, and no hour of today has been consumed yet.
|
|
2019
|
+
*
|
|
2020
|
+
* Hours are consumed per SLOT (one entry per configured hour), so a job with
|
|
2021
|
+
* two hours still runs twice a day — but a job whose hour passed while DSH was
|
|
2022
|
+
* closed runs immediately on the next tick instead of waiting for tomorrow.
|
|
1854
2023
|
*/
|
|
1855
|
-
reserved: boolean;
|
|
1856
2024
|
/**
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
2025
|
+
* The configured slot `kind` should consume at `now`, or undefined when none.
|
|
2026
|
+
*
|
|
2027
|
+
* Returns the SLOT STRING (not a boolean) because the caller must record the
|
|
2028
|
+
* same value it acted on. Returning a boolean was the original bug's enabler:
|
|
2029
|
+
* the tick asked "is it due", then `runJob` independently wrote "what time is
|
|
2030
|
+
* it now" — and those two answers were almost never equal.
|
|
2031
|
+
*
|
|
2032
|
+
* CATCH-UP semantics are deliberate: a candidate fires once its hour has
|
|
2033
|
+
* PASSED and its slot is still unconsumed, so a laptop that slept through
|
|
2034
|
+
* 10:00 still runs the job when it wakes, on the same day.
|
|
2035
|
+
*
|
|
2036
|
+
* The EARLIEST unconsumed candidate wins, which is what keeps a two-hour job
|
|
2037
|
+
* (`travelHours: [9, 21]`) whole: consuming the earliest due slot leaves the
|
|
2038
|
+
* later one for its own hour.
|
|
1860
2039
|
*/
|
|
1861
|
-
|
|
1862
|
-
/**
|
|
1863
|
-
|
|
1864
|
-
/**
|
|
1865
|
-
|
|
1866
|
-
|
|
2040
|
+
private dueSlot;
|
|
2041
|
+
/** Today's consumed slots for one job, as a set. */
|
|
2042
|
+
private firedSlotsOf;
|
|
2043
|
+
/** Record one consumed slot on a job's state. */
|
|
2044
|
+
private consumeSlot;
|
|
2045
|
+
private tick;
|
|
2046
|
+
/** Run one job against every eligible account and record the outcome. */
|
|
1867
2047
|
/**
|
|
1868
|
-
*
|
|
1869
|
-
*
|
|
1870
|
-
* renders one claim button per account, so a multi-account pool can collect
|
|
1871
|
-
* every account's daily reward without switching accounts by hand.
|
|
2048
|
+
* Add one account's take to today's counters, resetting first if the day
|
|
2049
|
+
* rolled over. Called from the task pass, which is the only job that earns.
|
|
1872
2050
|
*/
|
|
1873
|
-
checkin?: PoolWebCheckin;
|
|
1874
|
-
checkinError?: string;
|
|
1875
|
-
}
|
|
1876
|
-
/** One credit package (as surfaced by the pool's upstream client), node-free. */
|
|
1877
|
-
interface PoolWebCreditPackage {
|
|
1878
|
-
packageName: string;
|
|
1879
|
-
remain?: number;
|
|
1880
|
-
size?: number;
|
|
1881
|
-
/** CapacityType 4 — refreshed each cycle and never expires. */
|
|
1882
|
-
monthly?: boolean;
|
|
1883
|
-
/** Next cycle refresh point, ms. */
|
|
1884
|
-
cycleRefreshMs?: number;
|
|
1885
|
-
/** One-off expiry, ms. */
|
|
1886
|
-
expiresAtMs?: number;
|
|
1887
|
-
}
|
|
1888
|
-
/** Aggregated credit answer the card renders under one account. */
|
|
1889
|
-
interface PoolWebCredits {
|
|
1890
|
-
total?: number;
|
|
1891
|
-
packages: readonly PoolWebCreditPackage[];
|
|
1892
|
-
/** Credits expiring within 3 days. */
|
|
1893
|
-
expiringSoon?: number;
|
|
1894
|
-
/** When the nearest package expires, ms. */
|
|
1895
|
-
nearestExpiryMs?: number;
|
|
1896
|
-
}
|
|
1897
|
-
/**
|
|
1898
|
-
* Daily check-in state the card renders under one account's credits. Mirrors
|
|
1899
|
-
* the upstream activity endpoint, minus anything the browser does not need.
|
|
1900
|
-
*/
|
|
1901
|
-
interface PoolWebCheckin {
|
|
1902
|
-
/** The activity is running; a claim button is offered only while true. */
|
|
1903
|
-
active: boolean;
|
|
1904
|
-
/** Already collected today — the button renders as a done state. */
|
|
1905
|
-
todayCheckedIn: boolean;
|
|
1906
|
-
/** Consecutive days checked in. */
|
|
1907
|
-
streakDays: number;
|
|
1908
|
-
/** Credits a single day grants. */
|
|
1909
|
-
dailyCredit: number;
|
|
1910
|
-
/** Credits collected today (0 before claiming). */
|
|
1911
|
-
todayCredit: number;
|
|
1912
|
-
/** Today is a streak milestone day. */
|
|
1913
|
-
isStreakDay: boolean;
|
|
1914
|
-
/** The day count the next milestone lands on. */
|
|
1915
|
-
nextStreakDay: number;
|
|
1916
|
-
/** Bonus credits granted on a milestone day. */
|
|
1917
|
-
streakBonusCredit: number;
|
|
1918
|
-
}
|
|
1919
|
-
/** Result of one claim, so the card can confirm what was collected. */
|
|
1920
|
-
interface PoolWebCheckinClaim {
|
|
1921
|
-
credit: number;
|
|
1922
|
-
streakDays: number;
|
|
1923
|
-
isStreakDay: boolean;
|
|
1924
|
-
}
|
|
1925
|
-
/** One model the pool exposes to DSH, with cost / free tags. */
|
|
1926
|
-
interface PoolWebModel {
|
|
1927
|
-
id: string;
|
|
1928
|
-
name: string;
|
|
1929
|
-
/** Relative credit cost, e.g. 0.79 for x0.79. */
|
|
1930
|
-
multiplier?: number;
|
|
1931
|
-
/** Upstream tags: free / limited-free / night-discount. */
|
|
1932
|
-
tags?: readonly string[];
|
|
1933
|
-
/** Effective image support after the user's per-model toggle. */
|
|
1934
|
-
supportsImages: boolean;
|
|
1935
|
-
/** Effective context window after the user's budget cap. */
|
|
1936
|
-
contextWindow: number;
|
|
1937
|
-
/** The window the upstream advertises, before any cap. */
|
|
1938
|
-
nativeContextWindow: number;
|
|
1939
|
-
/** Upstream output ceiling, so the card can show both limits. */
|
|
1940
|
-
maxOutputTokens: number;
|
|
1941
|
-
/** Thinking levels the upstream declares, when it declares any. */
|
|
1942
|
-
supportedEfforts?: readonly string[];
|
|
1943
|
-
/** Whether this model is currently enabled in the picker. */
|
|
1944
|
-
enabled: boolean;
|
|
1945
|
-
}
|
|
1946
|
-
/**
|
|
1947
|
-
* Body of the account ignore/unignore route: exactly one account per request.
|
|
1948
|
-
*
|
|
1949
|
-
* `ignored: true` throws the account out of the pool for good (its credential is
|
|
1950
|
-
* not even read on the next scan, and a fresh desktop sign-in will not bring it
|
|
1951
|
-
* back). `false` restores it, at which point the next scan discovers it again.
|
|
1952
|
-
*/
|
|
1953
|
-
interface PoolWebAccountIgnore {
|
|
1954
|
-
/** Pool account id, as reported in `PoolWebAccount.id`. */
|
|
1955
|
-
accountId: string;
|
|
1956
|
-
/** `true` ignores the account permanently; `false` takes it back. */
|
|
1957
|
-
ignored: boolean;
|
|
1958
|
-
}
|
|
1959
|
-
/**
|
|
1960
|
-
* One account the user has thrown out of the pool.
|
|
1961
|
-
*
|
|
1962
|
-
* Kept on the status document so the card can list what was ignored and offer a
|
|
1963
|
-
* way back: without that, "ignored" is a one-way door the user cannot inspect or
|
|
1964
|
-
* undo from the UI, which is how a hidden list becomes a support burden.
|
|
1965
|
-
*/
|
|
1966
|
-
interface PoolWebIgnoredAccount {
|
|
1967
|
-
/** Pool account id, the same key `PoolWebAccount.id` uses. */
|
|
1968
|
-
id: string;
|
|
1969
|
-
/** Human label captured at ignore time, so the row reads without a rescan. */
|
|
1970
|
-
label: string;
|
|
1971
|
-
/** ISO timestamp of when it was ignored. */
|
|
1972
|
-
ignoredAt: string;
|
|
1973
|
-
}
|
|
1974
|
-
interface PoolWebModelSelection {
|
|
1975
|
-
/** Absent = every model is enabled. */
|
|
1976
|
-
enabledModelIds?: readonly string[];
|
|
1977
|
-
/** Absent = each model follows its upstream image capability. */
|
|
1978
|
-
imageModelIds?: readonly string[];
|
|
1979
|
-
/** Per-model context-window cap, keyed by model id. */
|
|
1980
|
-
contextBudgets?: Readonly<Record<string, number | undefined>>;
|
|
1981
|
-
}
|
|
1982
|
-
/** The JSON document the pool card renders. */
|
|
1983
|
-
interface PoolWebStatus {
|
|
1984
|
-
ok: boolean;
|
|
1985
|
-
accounts: readonly PoolWebAccount[];
|
|
1986
|
-
/** The next account the pool would use (rotation cursor). */
|
|
1987
|
-
activeAccountId?: string;
|
|
1988
|
-
cooling: number;
|
|
1989
|
-
models: readonly PoolWebModel[];
|
|
1990
|
-
/** The saved selection the card diffs its draft against. */
|
|
1991
|
-
selection: PoolWebModelSelection;
|
|
1992
2051
|
/**
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
2052
|
+
* Today's per-account earnings, as a plain object for the status document.
|
|
2053
|
+
*
|
|
2054
|
+
* Rolls the day first so a status read just after midnight does not report
|
|
2055
|
+
* yesterday's totals under today's date.
|
|
2056
|
+
*/
|
|
2057
|
+
private earningsSnapshot;
|
|
2058
|
+
/**
|
|
2059
|
+
* Add one account take to today counters, resetting first if the day rolled
|
|
2060
|
+
* over. Every source is tracked separately so the card can show what earned
|
|
2061
|
+
* what, rather than one opaque total.
|
|
2062
|
+
*/
|
|
2063
|
+
private recordEarnings;
|
|
2064
|
+
/** Clear the per-account counters when the local day changes. */
|
|
2065
|
+
private rollEarnings;
|
|
2066
|
+
/**
|
|
2067
|
+
* Write the ledger through the host hook, when one was supplied.
|
|
2068
|
+
*
|
|
2069
|
+
* Best effort on purpose: a failed save must never abort a run that has
|
|
2070
|
+
* already collected rewards, and the in-memory ledger keeps serving the card
|
|
2071
|
+
* for the rest of the session either way.
|
|
2072
|
+
*/
|
|
2073
|
+
private persistEarnings;
|
|
2074
|
+
/**
|
|
2075
|
+
* Run one job against every eligible account and record the outcome.
|
|
2076
|
+
*
|
|
2077
|
+
* `consumedSlot` is the configured slot this run spends (`dueSlot`'s answer).
|
|
2078
|
+
* It is passed IN rather than recomputed here so the value recorded is exactly
|
|
2079
|
+
* the value the schedule decided on — the defect this replaces wrote
|
|
2080
|
+
* `slotKey(now)` instead, i.e. the clock time the run finished at, which
|
|
2081
|
+
* almost never equals the configured candidate the gate had compared against.
|
|
2082
|
+
*
|
|
2083
|
+
* The task job runs in TWO passes. The first sends the event chains that light
|
|
2084
|
+
* up client-scored tasks; the second collects rewards. They are separate
|
|
2085
|
+
* because scoring lands asynchronously — a chain sent and claimed within the
|
|
2086
|
+
* same breath finds the task still un-scored — and because sending is fast
|
|
2087
|
+
* while claiming wants the whole pool to have been lit up first. Splitting
|
|
2088
|
+
* them costs one shared wait instead of one wait per account.
|
|
2089
|
+
*/
|
|
2090
|
+
private runJob;
|
|
2091
|
+
/** Compose the one-line summary shown on the card. */
|
|
2092
|
+
private summarise;
|
|
2093
|
+
/**
|
|
2094
|
+
* Accounts to run against, in pool order.
|
|
2095
|
+
*
|
|
2096
|
+
* Disabled accounts are excluded here rather than filtered by the caller so a
|
|
2097
|
+
* card switch takes effect on the next pass without any event plumbing.
|
|
2098
|
+
*/
|
|
2099
|
+
private accountsInOrder;
|
|
2100
|
+
/**
|
|
2101
|
+
* Send one activity report, then verify it landed.
|
|
2102
|
+
*
|
|
2103
|
+
* The upstream answers 200 even when it drops the event, so the streak is
|
|
2104
|
+
* read back as the oracle: `days > 0` means it counted. A failed read-back is
|
|
2105
|
+
* logged and treated as a suspicious result, never as a retry — the report is
|
|
2106
|
+
* idempotent per day, and hammering it is exactly what the one-a-day quota
|
|
2107
|
+
* exists to avoid.
|
|
1995
2108
|
*/
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
region: PoolRegion;
|
|
1999
|
-
/** Every region holding at least one account, in display order. */
|
|
2000
|
-
regions: readonly PoolRegion[];
|
|
2001
|
-
shim: {
|
|
2002
|
-
running: boolean;
|
|
2003
|
-
baseUrl?: string;
|
|
2004
|
-
};
|
|
2005
|
-
/** Daily-points automation state, so the card can show what ran and when. */
|
|
2006
|
-
automation: PoolWebAutomation;
|
|
2007
|
-
/** Per-account credit floors currently in force, keyed by account id. */
|
|
2008
|
-
creditReserves: Readonly<Record<string, number>>;
|
|
2109
|
+
private reportOne;
|
|
2110
|
+
private sendEventChains;
|
|
2009
2111
|
/**
|
|
2010
|
-
*
|
|
2112
|
+
* Send one chain on the channel it was built for.
|
|
2011
2113
|
*
|
|
2012
|
-
*
|
|
2013
|
-
*
|
|
2014
|
-
*
|
|
2114
|
+
* The transport is not a detail of the sender: the scorer keys different
|
|
2115
|
+
* tasks to different fingerprint families, so a web-scored event posted as a
|
|
2116
|
+
* desktop event is accepted and then ignored.
|
|
2015
2117
|
*/
|
|
2016
|
-
|
|
2017
|
-
}
|
|
2018
|
-
/** One automation job's last run, as shown on the card. */
|
|
2019
|
-
interface PoolWebAutomationJob {
|
|
2020
|
-
/** `YYYY-MM-DD` of the last run in this process, if it has run. */
|
|
2021
|
-
lastRunDate?: string;
|
|
2118
|
+
private sendChain;
|
|
2022
2119
|
/**
|
|
2023
|
-
*
|
|
2120
|
+
* Build every chain that scores one task.
|
|
2024
2121
|
*
|
|
2025
|
-
*
|
|
2026
|
-
*
|
|
2027
|
-
*
|
|
2122
|
+
* Most tasks need a single chain; `template_5` needs five, because the scorer
|
|
2123
|
+
* counts distinct `template_used` events rather than a boolean. The two tasks
|
|
2124
|
+
* that join a conversation (skill, expert) open a real one first, which is why
|
|
2125
|
+
* this is async.
|
|
2028
2126
|
*/
|
|
2029
|
-
|
|
2127
|
+
private chainsFor;
|
|
2030
2128
|
/**
|
|
2031
|
-
*
|
|
2129
|
+
* The summon-and-use chains for the expert tasks.
|
|
2032
2130
|
*
|
|
2033
|
-
*
|
|
2034
|
-
*
|
|
2131
|
+
* Two steps per expert, and both are load-bearing: the summon events alone are
|
|
2132
|
+
* impressions, and a use event on its own scores nothing because the scorer
|
|
2133
|
+
* looks the conversation up. Only a real chat with `X-Expert-Id` produces an
|
|
2134
|
+
* id it will accept.
|
|
2035
2135
|
*/
|
|
2036
|
-
|
|
2037
|
-
/** Accounts that finished without error on the last run. */
|
|
2038
|
-
ok: number;
|
|
2039
|
-
/** Accounts that failed on the last run (each one skipped, the run continued). */
|
|
2040
|
-
failed: number;
|
|
2041
|
-
/** Credits claimed by the task job on the last run. */
|
|
2042
|
-
credit: number;
|
|
2043
|
-
/** Energy claimed by the task job on the last run. */
|
|
2044
|
-
energy: number;
|
|
2045
|
-
/** Tasks claimed by the task job on the last run. */
|
|
2046
|
-
claimed: number;
|
|
2047
|
-
/** One-line summary of the last run. */
|
|
2048
|
-
message?: string;
|
|
2136
|
+
private expertChains;
|
|
2049
2137
|
/**
|
|
2050
|
-
*
|
|
2138
|
+
* The 腾讯轻量云 expert chain.
|
|
2051
2139
|
*
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2140
|
+
* Structurally the same as the expert task, with two differences the scorer
|
|
2141
|
+
* checks: `agent_task_created` has to name the expert, and the use event has
|
|
2142
|
+
* to report `mode: 'LOCAL'` with an empty type and zero cost — that is what
|
|
2143
|
+
* the lighthouse criterion looks for.
|
|
2054
2144
|
*/
|
|
2055
|
-
|
|
2056
|
-
/** A pending milestone worth naming, e.g. the next streak tier countdown. */
|
|
2057
|
-
progress?: string;
|
|
2058
|
-
}
|
|
2059
|
-
/**
|
|
2060
|
-
* Automation block on the status document.
|
|
2061
|
-
*
|
|
2062
|
-
* Carries the schedule and each job's last outcome so the card can answer
|
|
2063
|
-
* "is it on, when does it run, and what did it last do" without reaching into
|
|
2064
|
-
* the scheduler itself.
|
|
2065
|
-
*/
|
|
2066
|
-
interface PoolWebAutomation {
|
|
2067
|
-
/** Master switch, mirrored from the saved config. */
|
|
2068
|
-
enabled: boolean;
|
|
2069
|
-
/** Whether the loop is currently running. */
|
|
2070
|
-
running: boolean;
|
|
2071
|
-
/** Configured hours per job, so the card can show the schedule. */
|
|
2072
|
-
checkinHours: readonly number[];
|
|
2073
|
-
reportHours: readonly number[];
|
|
2074
|
-
taskHours: readonly number[];
|
|
2075
|
-
streakHours: readonly number[];
|
|
2076
|
-
travelHours: readonly number[];
|
|
2077
|
-
jobs: {
|
|
2078
|
-
checkin: PoolWebAutomationJob;
|
|
2079
|
-
report: PoolWebAutomationJob;
|
|
2080
|
-
tasks: PoolWebAutomationJob;
|
|
2081
|
-
streak: PoolWebAutomationJob;
|
|
2082
|
-
travel: PoolWebAutomationJob;
|
|
2083
|
-
};
|
|
2084
|
-
/** Claimable tasks seen on the most recent task pass, across accounts. */
|
|
2085
|
-
claimableSeen: number;
|
|
2145
|
+
private lighthouseChains;
|
|
2086
2146
|
/**
|
|
2087
|
-
*
|
|
2088
|
-
*
|
|
2147
|
+
* The task-centre pass for one account.
|
|
2148
|
+
*
|
|
2149
|
+
* Order matters: enrich first (enrol in everything open), then claim. Both
|
|
2150
|
+
* halves are idempotent — accepting an already-accepted task succeeds, and a
|
|
2151
|
+
* repeat claim answers `already_claimed` — so a pass that dies halfway is
|
|
2152
|
+
* safe to replay on the next tick.
|
|
2089
2153
|
*/
|
|
2090
|
-
|
|
2154
|
+
private runTasks;
|
|
2091
2155
|
/**
|
|
2092
|
-
*
|
|
2093
|
-
*
|
|
2094
|
-
*
|
|
2156
|
+
* Streak redemption plus the lottery it unlocks.
|
|
2157
|
+
*
|
|
2158
|
+
* Tiers unlock on consecutive active days (7/14/28). Redeeming one pays
|
|
2159
|
+
* credits, energy, a makeup card and — the part nothing else grants — lottery
|
|
2160
|
+
* draws, so the draw runs straight after and only for the chances in hand.
|
|
2161
|
+
*
|
|
2162
|
+
* Everything here is idempotent: a tier already claimed is skipped by its
|
|
2163
|
+
* status, and a draw consumes one chance, so a replay cannot double-spend.
|
|
2095
2164
|
*/
|
|
2096
|
-
|
|
2165
|
+
private redeemStreak;
|
|
2166
|
+
/**
|
|
2167
|
+
* One trip through the buddy travel loop for an account.
|
|
2168
|
+
*
|
|
2169
|
+
* A single pass advances the state machine by at most one step: a trip
|
|
2170
|
+
* that has arrived is collected, and an idle buddy is sent out. A buddy
|
|
2171
|
+
* already travelling is left alone — there is nothing to do until it lands.
|
|
2172
|
+
*
|
|
2173
|
+
* Measured against the live upstream: the departed trip reports
|
|
2174
|
+
* `dailyLimitReached` immediately, so the once-a-day limit needs no local
|
|
2175
|
+
* bookkeeping.
|
|
2176
|
+
*/
|
|
2177
|
+
private runTravel;
|
|
2097
2178
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
/**
|
|
2107
|
-
|
|
2108
|
-
/**
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2179
|
+
//#endregion
|
|
2180
|
+
//#region src/status.d.ts
|
|
2181
|
+
/** One account's status row. */
|
|
2182
|
+
interface AccountStatus {
|
|
2183
|
+
id: string;
|
|
2184
|
+
label: string;
|
|
2185
|
+
nickname?: string;
|
|
2186
|
+
domain: string;
|
|
2187
|
+
/** ISO timestamp when the access token expires. */
|
|
2188
|
+
expiresAt?: string;
|
|
2189
|
+
/** Account-wide cooldown (every model blocked). */
|
|
2190
|
+
cooling: boolean;
|
|
2191
|
+
cooldownUntil?: string;
|
|
2192
|
+
/** Per-model cooldowns active right now (modelId → ISO until); the account
|
|
2193
|
+
* itself is not `cooling` while only some models are limited. */
|
|
2194
|
+
modelCooldowns?: readonly {
|
|
2195
|
+
modelId: string;
|
|
2196
|
+
until: string;
|
|
2197
|
+
}[];
|
|
2198
|
+
rateLimitHits: number;
|
|
2199
|
+
/** Read-only aggregated credit summary for the account. */
|
|
2200
|
+
credits?: WorkBuddyCredits;
|
|
2201
|
+
creditsError?: string;
|
|
2202
|
+
sourcePath: string;
|
|
2114
2203
|
}
|
|
2115
|
-
/**
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2204
|
+
/** Whole-plugin status document. */
|
|
2205
|
+
interface WorkBuddyStatus {
|
|
2206
|
+
ok: boolean;
|
|
2207
|
+
accounts: AccountStatus[];
|
|
2208
|
+
activeAccountId?: string;
|
|
2209
|
+
cooling: number;
|
|
2210
|
+
models: {
|
|
2211
|
+
id: string;
|
|
2212
|
+
name: string;
|
|
2213
|
+
multiplier?: number;
|
|
2214
|
+
tags?: readonly string[];
|
|
2215
|
+
}[];
|
|
2216
|
+
shim: {
|
|
2217
|
+
running: boolean;
|
|
2218
|
+
baseUrl?: string;
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
interface StatusOptions {
|
|
2222
|
+
pool: WorkBuddyAccountPool;
|
|
2223
|
+
/** The catalog to report. Regional callers pass their own region's. */
|
|
2224
|
+
catalog: WorkBuddyCatalog;
|
|
2225
|
+
client: WorkBuddyUpstreamClient;
|
|
2226
|
+
shim?: {
|
|
2227
|
+
running: boolean;
|
|
2228
|
+
baseUrl?: string;
|
|
2229
|
+
};
|
|
2230
|
+
/** Query credits per account. Off for cheap diagnostics runs. */
|
|
2231
|
+
includeCredits?: boolean;
|
|
2232
|
+
}
|
|
2233
|
+
/** Build the status document. Never throws. */
|
|
2234
|
+
export declare function buildStatus(options: StatusOptions): Promise<WorkBuddyStatus>;
|
|
2235
|
+
/** Format the status document for a terminal. */
|
|
2236
|
+
export declare function formatStatus(status: WorkBuddyStatus): string;
|
|
2237
|
+
/** Format the per-model credit multipliers. */
|
|
2238
|
+
export declare function formatRates(status: WorkBuddyStatus): string;
|
|
2145
2239
|
//#endregion
|
|
2146
2240
|
//#region src/ignored.d.ts
|
|
2147
2241
|
/** Directory holding this plugin's own state (imported snapshots, ignore list). */
|
|
@@ -2271,6 +2365,19 @@ interface PoolStatusRouteOptions {
|
|
|
2271
2365
|
* disk, including edits made by the CLI while the card is open.
|
|
2272
2366
|
*/
|
|
2273
2367
|
ignoredAccounts?: () => readonly PoolWebIgnoredAccount[];
|
|
2368
|
+
/**
|
|
2369
|
+
* Re-fetch the model catalog for BOTH regions and report what landed.
|
|
2370
|
+
*
|
|
2371
|
+
* Absent when the host did not wire it: the route then answers 503 rather
|
|
2372
|
+
* than pretending the refresh happened.
|
|
2373
|
+
*/
|
|
2374
|
+
refreshCatalog?: () => Promise<{
|
|
2375
|
+
regions: Readonly<Record<PoolRegion, {
|
|
2376
|
+
source: PoolWebCatalogSource;
|
|
2377
|
+
models: number;
|
|
2378
|
+
error?: string;
|
|
2379
|
+
}>>;
|
|
2380
|
+
}>;
|
|
2274
2381
|
}
|
|
2275
2382
|
/**
|
|
2276
2383
|
* Assemble the card's status document. Per-account credits and check-in state
|