dsh-workbuddy-xdpool 1.6.1 → 1.7.1

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/lib/index.d.ts CHANGED
@@ -221,6 +221,21 @@ interface WorkBuddyUpstreamModel {
221
221
  descriptionZh?: string;
222
222
  descriptionEn?: string;
223
223
  supportsToolCall?: boolean;
224
+ /**
225
+ * Promo tags the upstream attaches to a model.
226
+ *
227
+ * Both gateways send a `tags` array, but the vocabularies are NOT the same
228
+ * and neither uses the words this plugin used to expect:
229
+ *
230
+ * - Global sends `["craft"]`, `["text-to-image"]`, `["text-to-video"]`, `[]`
231
+ * — capability/grouping labels, never `free`.
232
+ * - Zero cost is expressed as `credits: "x0.00"` instead.
233
+ *
234
+ * So this field is a passthrough of what the upstream really said, and
235
+ * `free` is DERIVED from the multiplier rather than awaited as a tag (see
236
+ * {@link isFreeModel}).
237
+ */
238
+ tags?: readonly string[];
224
239
  }
225
240
  /** One billing package, already normalised. */
226
241
  interface WorkBuddyCreditPackage {
@@ -830,6 +845,21 @@ export declare class WorkBuddyAccountPool {
830
845
  * lives on the pool and is re-applied from settings after each scan.
831
846
  */
832
847
  private disabledIds;
848
+ /**
849
+ * Account ids the user threw out of the pool for good.
850
+ *
851
+ * Enforced BEFORE the credential is parsed: `scan()` skips a file whose
852
+ * identity is already ignored, so an ignored account costs no at-rest key
853
+ * lookup (which spawns the desktop app on 5.6.0+) and cannot re-enter the pool
854
+ * when the app writes a fresh sign-in for it. That is the difference from
855
+ * {@link disabledIds}, which only filters at pick time and leaves the account
856
+ * listed, readable and re-discoverable.
857
+ *
858
+ * The set is supplied by the host from the plugin's own ignore file, and is
859
+ * replaced wholesale on every {@link applyIgnored} so removing an entry takes
860
+ * effect on the next scan without a restart.
861
+ */
862
+ private ignoredIds;
833
863
  /**
834
864
  * Per-account credit floor, keyed by account id. 0 (or absent) means "spend
835
865
  * it all".
@@ -875,6 +905,19 @@ export declare class WorkBuddyAccountPool {
875
905
  /** Per-account credit floor, keyed by account id. Absent keeps the current map. */
876
906
  creditReserves?: Readonly<Record<string, number>>;
877
907
  }): void;
908
+ /**
909
+ * Replace the permanent ignore list.
910
+ *
911
+ * Also drops any already-discovered account that is now ignored, so the change
912
+ * is visible without waiting for the next scan: the card refreshes its status
913
+ * document right after the write, and an account still sitting in `accounts`
914
+ * would keep showing up there.
915
+ */
916
+ applyIgnored(ids: Iterable<string>): void;
917
+ /** Whether this account has been thrown out of the pool for good. */
918
+ isIgnored(accountId: string): boolean;
919
+ /** Every ignored id currently in force, in insertion order. */
920
+ ignoredIdsInOrder(): string[];
878
921
  /** Rescan the auth directories and merge newly discovered accounts. */
879
922
  scan(): Promise<WorkBuddyAccount[]>;
880
923
  /** All accounts, cooldown state included. */
@@ -1036,7 +1079,20 @@ interface WorkBuddyModelInfo {
1036
1079
  /** Upstream tags: free / limited-free / night-discount. */
1037
1080
  tags?: readonly string[];
1038
1081
  }
1039
- /** Static fallback used before the first live catalog fetch. */
1082
+ /**
1083
+ * Static fallback used before the first live catalog fetch, and whenever the
1084
+ * upstream cannot be reached.
1085
+ *
1086
+ * The multipliers are carried on purpose. Without them the provider's model
1087
+ * picker silently loses every rate and every free badge the moment the live
1088
+ * fetch fails — which reads to the user as "the plugin broke my model list"
1089
+ * rather than "the upstream is unreachable". The values are the ones the two
1090
+ * gateways actually advertise for these ids (`credits: "x0.79 credits"` and so
1091
+ * on), so a fallback row looks the same as a live one.
1092
+ *
1093
+ * `multiplier: 0` is the gateways' own spelling of "free" (`credits: "x0.00"`),
1094
+ * which is what turns on the free badge.
1095
+ */
1040
1096
  export declare const FALLBACK_WORKBUDDY_MODELS: readonly WorkBuddyModelInfo[];
1041
1097
  /** Live catalog with a static fallback behind it. */
1042
1098
  export declare class WorkBuddyCatalog {
@@ -1413,7 +1469,7 @@ export declare class WorkBuddyScheduler {
1413
1469
  * long before the settings section exists; a ledger written before that point
1414
1470
  * would have nowhere to go.
1415
1471
  */
1416
- setEarningsPersistence(save: (ledger: AutomationLedger) => void): void;
1472
+ setEarningsPersistence(save: (ledger: AutomationLedger) => void | Promise<void>): void;
1417
1473
  /**
1418
1474
  * Fold a previously persisted ledger back in, when it belongs to today.
1419
1475
  *
@@ -1692,11 +1748,18 @@ export declare const POOL_RESET_COOLDOWN_PATH = "/plugins/dsh-workbuddy-xdpool/c
1692
1748
  export declare const POOL_CHECKIN_PATH = "/plugins/dsh-workbuddy-xdpool/checkin";
1693
1749
  /** Plugin-owned model-selection save endpoint (writes the settings section). */
1694
1750
  export declare const POOL_MODELS_SAVE_PATH = "/plugins/dsh-workbuddy-xdpool/models/save";
1751
+ /**
1752
+ * Throw one account out of the pool for good, or take it back.
1753
+ *
1754
+ * Separate from the disable route because the semantics differ: disabling is a
1755
+ * rotation preference the account survives, ignoring survives the account.
1756
+ */
1757
+ export declare const POOL_ACCOUNT_IGNORE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/ignored";
1695
1758
  /** Run one automation job immediately, so the card can verify it on demand. */
1696
1759
  export declare const POOL_AUTOMATION_RUN_PATH = "/plugins/dsh-workbuddy-xdpool/automation/run";
1697
1760
  /** Set or clear one account's reserved-credit floor. */
1698
1761
  export declare const POOL_CREDIT_RESERVE_PATH = "/plugins/dsh-workbuddy-xdpool/accounts/credit-reserve";
1699
- /** One pool account's row, token-free. */
1762
+ /** One account's row, token-free. */
1700
1763
  interface PoolWebAccount {
1701
1764
  id: string;
1702
1765
  label: string;
@@ -1826,6 +1889,34 @@ interface PoolWebModel {
1826
1889
  /** Whether this model is currently enabled in the picker. */
1827
1890
  enabled: boolean;
1828
1891
  }
1892
+ /**
1893
+ * Body of the account ignore/unignore route: exactly one account per request.
1894
+ *
1895
+ * `ignored: true` throws the account out of the pool for good (its credential is
1896
+ * not even read on the next scan, and a fresh desktop sign-in will not bring it
1897
+ * back). `false` restores it, at which point the next scan discovers it again.
1898
+ */
1899
+ interface PoolWebAccountIgnore {
1900
+ /** Pool account id, as reported in `PoolWebAccount.id`. */
1901
+ accountId: string;
1902
+ /** `true` ignores the account permanently; `false` takes it back. */
1903
+ ignored: boolean;
1904
+ }
1905
+ /**
1906
+ * One account the user has thrown out of the pool.
1907
+ *
1908
+ * Kept on the status document so the card can list what was ignored and offer a
1909
+ * way back: without that, "ignored" is a one-way door the user cannot inspect or
1910
+ * undo from the UI, which is how a hidden list becomes a support burden.
1911
+ */
1912
+ interface PoolWebIgnoredAccount {
1913
+ /** Pool account id, the same key `PoolWebAccount.id` uses. */
1914
+ id: string;
1915
+ /** Human label captured at ignore time, so the row reads without a rescan. */
1916
+ label: string;
1917
+ /** ISO timestamp of when it was ignored. */
1918
+ ignoredAt: string;
1919
+ }
1829
1920
  interface PoolWebModelSelection {
1830
1921
  /** Absent = every model is enabled. */
1831
1922
  enabledModelIds?: readonly string[];
@@ -1861,6 +1952,14 @@ interface PoolWebStatus {
1861
1952
  automation: PoolWebAutomation;
1862
1953
  /** Per-account credit floors currently in force, keyed by account id. */
1863
1954
  creditReserves: Readonly<Record<string, number>>;
1955
+ /**
1956
+ * Accounts thrown out of the pool, in the order they were ignored.
1957
+ *
1958
+ * Reported so the card can show the list and offer a way back. These accounts
1959
+ * are NOT in `accounts`: they are filtered out before their credentials are
1960
+ * read, which is the whole point of the feature.
1961
+ */
1962
+ ignored: readonly PoolWebIgnoredAccount[];
1864
1963
  }
1865
1964
  /** One automation job's last run, as shown on the card. */
1866
1965
  interface PoolWebAutomationJob {
@@ -1952,6 +2051,89 @@ interface PoolWebAutomationEarnings {
1952
2051
  type PoolRegion = 'cn' | 'global';
1953
2052
  /** How the pool spreads requests across its accounts. */
1954
2053
  type PoolDistribution = 'priority' | 'round-robin' | 'balanced';
2054
+ /**
2055
+ * The schedule every automation job falls back to.
2056
+ *
2057
+ * Shared by both halves on purpose. The host uses it when a configured hour
2058
+ * list arrives empty (the settings schema materializes "never configured" into
2059
+ * `[]`), and the card uses it when it writes the `automation` block back, so a
2060
+ * document that already holds an empty list is healed instead of being saved
2061
+ * back as an unrunnable schedule.
2062
+ *
2063
+ * This lives here rather than in `scheduler.ts` because the browser half cannot
2064
+ * import the host module: `scheduler.ts` pulls in `node:crypto` and the whole
2065
+ * upstream client, none of which exists in the browser bundle. Two hand-written
2066
+ * copies would drift, and the drift is invisible — the card would write a
2067
+ * schedule the scheduler does not run.
2068
+ */
2069
+ export declare const DEFAULT_AUTOMATION_HOURS: {
2070
+ readonly checkin: readonly [9];
2071
+ readonly report: readonly [10];
2072
+ readonly tasks: readonly [11];
2073
+ readonly streak: readonly [12];
2074
+ readonly travel: readonly [9, 21];
2075
+ };
2076
+ //#endregion
2077
+ //#region src/ignored.d.ts
2078
+ /** Directory holding this plugin's own state (imported snapshots, ignore list). */
2079
+ export declare const PLUGIN_DATA_DIR_NAME = ".workbuddy-xdpool";
2080
+ /** File holding the permanent ignore list, inside {@link pluginDataDir}. */
2081
+ export declare const IGNORED_FILE_NAME = "ignored.json";
2082
+ /** One ignored account, as stored on disk and shown on the card. */
2083
+ type IgnoredAccount = PoolWebIgnoredAccount;
2084
+ /**
2085
+ * The DSH home directory, honouring the same override the host uses.
2086
+ *
2087
+ * Shared by the CLI and the host so both halves resolve the same file: an
2088
+ * `ignore` written from the terminal has to be visible to the running plugin,
2089
+ * which is only true if they agree on where "home" is.
2090
+ */
2091
+ export declare function dshHome(env?: NodeJS.ProcessEnv): string;
2092
+ /** This plugin's own state directory. */
2093
+ export declare function pluginDataDir(env?: NodeJS.ProcessEnv): string;
2094
+ /** Absolute path of the ignore list. */
2095
+ export declare function ignoredIdsPath(env?: NodeJS.ProcessEnv): string;
2096
+ /**
2097
+ * Read the ignore list, tolerating every "no list yet" shape.
2098
+ *
2099
+ * A missing file, unreadable file, or invalid JSON all mean the same thing to
2100
+ * the caller — nothing is ignored — so none of them throws. The pool must be
2101
+ * able to start on a machine that has never ignored anything.
2102
+ */
2103
+ export declare function readIgnoredAccounts(path?: string): Promise<IgnoredAccount[]>;
2104
+ /**
2105
+ * Synchronous read, for startup.
2106
+ *
2107
+ * The host applies the ignore list from inside `apply()`, which is synchronous,
2108
+ * and doing it there removes a startup race: an async load could resolve AFTER
2109
+ * the first account scan, which would let an ignored account slip into the pool
2110
+ * once per boot. The file is a few hundred bytes, so a blocking read at startup
2111
+ * costs nothing measurable.
2112
+ */
2113
+ export declare function readIgnoredAccountsSync(path?: string): IgnoredAccount[];
2114
+ /**
2115
+ * Replace the ignore list, atomically.
2116
+ *
2117
+ * Written to a sibling temp file and renamed over the target so a crash (or a
2118
+ * concurrent reader) can never observe a half-written document — the ignore
2119
+ * list is the only thing standing between a dead account and the rotation, and
2120
+ * a truncated file reads as "nothing is ignored", which would quietly put every
2121
+ * discarded account back in the pool.
2122
+ */
2123
+ export declare function writeIgnoredAccounts(accounts: readonly IgnoredAccount[], path?: string): Promise<void>;
2124
+ /**
2125
+ * Add one account to the ignore list, preserving the rest.
2126
+ *
2127
+ * A read-modify-write rather than a wholesale replace: the card and the CLI can
2128
+ * both be open, and each request names exactly one account, so re-writing the
2129
+ * whole list from a stale view would drop the other side's edits.
2130
+ */
2131
+ export declare function ignoreAccount(account: {
2132
+ id: string;
2133
+ label?: string;
2134
+ }, path?: string): Promise<IgnoredAccount[]>;
2135
+ /** Drop one account from the ignore list. Returns the resulting list. */
2136
+ export declare function unignoreAccount(accountId: string, path?: string): Promise<IgnoredAccount[]>;
1955
2137
  //#endregion
1956
2138
  //#region src/web-status.d.ts
1957
2139
  /** Constructor dependencies — a narrow slice of the pool runtime. */
@@ -2005,6 +2187,21 @@ interface PoolStatusRouteOptions {
2005
2187
  * Absent without a settings service: the route then answers 503.
2006
2188
  */
2007
2189
  setAccountDisabled?: (accountId: string, disabled: boolean) => Promise<void> | void;
2190
+ /**
2191
+ * Throw one account out of the pool for good, or take it back.
2192
+ *
2193
+ * Backed by the plugin's own ignore file rather than the settings document,
2194
+ * because the CLI writes the same list and has no settings service. Absent
2195
+ * when the host did not wire it: the route then answers 503.
2196
+ */
2197
+ setAccountIgnored?: (accountId: string, ignored: boolean) => Promise<void> | void;
2198
+ /**
2199
+ * The accounts currently ignored, for the card's "ignored" list.
2200
+ *
2201
+ * A thunk rather than a snapshot so the document always reflects the file on
2202
+ * disk, including edits made by the CLI while the card is open.
2203
+ */
2204
+ ignoredAccounts?: () => readonly PoolWebIgnoredAccount[];
2008
2205
  }
2009
2206
  /**
2010
2207
  * Assemble the card's status document. Per-account credits and check-in state
@@ -2158,6 +2355,14 @@ export declare const modelSelectionKeyFor: (region: 'cn' | 'global') => string;
2158
2355
  * also why `contextBudgets` is a real dictionary (`z.dict`) - an open object
2159
2356
  * schema reads as "an object with no fields" and the fold then throws while
2160
2357
  * the provider row is rendered.
2358
+ *
2359
+ * Every field is wrapped in {@link asVolatile}: on the 0.1.7 line the settings
2360
+ * write gate refuses an entry whose schema declares no volatile field at all
2361
+ * ("Plugin entry ... has no volatile fields") and `describe()` skips such an
2362
+ * entry — so an unmarked schema means the card can neither render nor save. On
2363
+ * the 0.1.5 line the wrapper degrades to an identity no-op (see its JSDoc), and
2364
+ * the value the running instance reads is a plain value either way once
2365
+ * unwrapped.
2161
2366
  */
2162
2367
  export declare const Config: z<Config>;
2163
2368
  /** Everything the CLI needs from a live plugin instance. */
@@ -2206,4 +2411,4 @@ export declare function createCore(logger?: {
2206
2411
  */
2207
2412
  export declare function apply(ctx: Context, config?: Config): void;
2208
2413
  //#endregion
2209
- export type { AccountStatus, AutomationLedger, AutomationRunSummary, AutomationStatus, Context, ExpertUseMode, MarketExpert, ModelSelection, PoolStatusRouteOptions, PoolWebCheckin, PoolWebCheckinClaim, PoolWebModel, PoolWebModelSelection, PoolWebStatus, SchedulerLogger, TaskEventChain, TaskEventTransport, UpstreamErrorKind, WorkBuddyAccount, WorkBuddyAdapter, WorkBuddyCredential, WorkBuddyModelInfo, WorkBuddyShim, WorkBuddyStatus };
2414
+ export type { AccountStatus, AutomationLedger, AutomationRunSummary, AutomationStatus, Context, ExpertUseMode, IgnoredAccount, MarketExpert, ModelSelection, PoolStatusRouteOptions, PoolWebAccountIgnore, PoolWebCheckin, PoolWebCheckinClaim, PoolWebIgnoredAccount, PoolWebModel, PoolWebModelSelection, PoolWebStatus, SchedulerLogger, TaskEventChain, TaskEventTransport, UpstreamErrorKind, WorkBuddyAccount, WorkBuddyAdapter, WorkBuddyCredential, WorkBuddyModelInfo, WorkBuddyShim, WorkBuddyStatus };