/** * dsh-widgets — the bridge snapshot contract. * * Every React entry in this plugin reads ONE immutable object per emit (see * `emit` in `client/index.ts`), because `useSyncExternalStore` compares * references: a rebuilt object is what tells every subscriber that something * moved. * * The type lives here rather than in `client/index.ts` so that modules the * composition root renders — starting with `data/collector.tsx` — can name it * without importing back into their own caller (which would be a cycle). */ import type { CommandCodeData, DeepSeekBalance, GitHubData, HostOverview, PriceTable, ScheduleSnapshot, SysInfo, UsageData, UsageMulti } from '../lib/contract/types'; import type { Stats } from '../data/session-stats'; import type { Prefs } from './prefs'; /** * Everything a React entry reads from the plugin bridge. One immutable object * per emit (see `emit`), because `useSyncExternalStore` compares references. */ export interface BridgeSnapshot { open: boolean; hasSession: boolean; stats: Stats | null; usageData: UsageData | null; usageMulti: UsageMulti | null; commandCode: CommandCodeData | null; commandCodeError: string | null; usageDaily: Record | null; /** The same daily log RESTRICTED to the `commandcode` route — 「额度管理」's * token side. Separate from `usageDaily` because that one is machine-wide. */ commandCodeDaily: Record | null; sysinfo: SysInfo | null; /** The machine's operational picture (throughput / power / processes / local * services / proxy egress) from the host `/api/host/overview` route. Polled by * the same effect as `sysinfo` — both are the machine's own data, and the * route is cheap once each section's cache is warm. */ host: HostOverview | null; /** `'unloaded'` when the host route is missing (dsh web not restarted), * otherwise a transport code; `null` once a payload has arrived. */ hostError: string | null; /** The price table usage-center keeps in storage (see host/pricing.ts). Read * only when a card that prices this session is installed; `null` = not read * yet, `available: false` = no table (cards then print tokens, not money). */ pricing: PriceTable | null; /** GitHub family payload (contribution calendar + repo pulses) from the * host `/api/github` route. */ github: GitHubData | null; /** `'unloaded'` when the host route is missing (dsh web not restarted), * otherwise a transport code; `null` once a payload has arrived. */ githubError: string | null; /** DeepSeek platform balance + the day's observed spend, from the host * `/api/deepseek-balance` route (see DeepSeekBalance). */ deepseekBalance: DeepSeekBalance | null; /** `'unconfigured'` (no key) / `'unauthorized'` (key rejected) / `'unloaded'` * (host route missing → dsh web not restarted) / a transport code; `null` * once a payload has arrived. */ deepseekBalanceError: string | null; /** * Per-source load status, keyed by `manifest.source` (v1.11 adapter contract). * * An ABSENT key means the source's adapter has never answered — the loading * skeleton the unit's manifest declares is still up. `ready` means it produced * something renderable at least once, so the card shows its own state from then * on (a later failure keeps the last good payload; it never re-enters loading). * * The shell reads this to decide the skeleton; the SOURCE decides when it turns * ready (`SourceEffect.resolved`), because only the adapter knows whether its * route's error answer is the card's real state or a transient miss. */ sources: Record; /** * The DSH scheduled-task subsystem, folded (see data/schedule.ts). * * `null` = never read (no installed card declares `reads: ['schedule']`), which * is a different statement from `status: 'unavailable'` (a deployment without the * schedule bundle) — cards show `—` for the first and degrade to their plain * layout for the second. */ schedule: ScheduleSnapshot | null; prefs: Prefs; railBudget: number; } /** The mutable half of the bridge: everything in {@link BridgeSnapshot} that the * collector WRITES through `setState`. Spelled as an Omit so the two can never * drift apart. */ export type BridgeState = Omit;