UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

131 lines (130 loc) • 5.74 kB
import { type NotLinkedComputer } from "./hand-off.js"; import { type HookPeer } from "./peers.js"; import { type Json } from "./protocol.js"; import { NEAR_LIMIT_LEFT, type AccountUsage, type Subscription, type UsageCredits, type UsageWindow } from "./usage.js"; export { NEAR_LIMIT_LEFT }; /** * Every computer's agent usage merged by account, for a conductor choosing * where to send work: `account_usage` and `phren dispatch usage`. Read like * `live_sessions`: this Hook, then each linked Hook over its pinned pipe, with * unreachable and unlinked computers listed apart. * * The merge follows the phone's cards: one account is one allowance whichever * computers report it, so each window comes from the newest report that has * it (`mergedWindows`). Claude and Codex * rows merge by their account key; a Claude login with no identity * (`claude:home:<id>`) stays per computer, since two unknown logins are not * one account. A window whose reset time has passed says `reset` and has no * percent, and a report older than STALE_AFTER_MS is flagged stale. * * `nearLimit` is information: the owner often wants quota used before it * resets. Only `exhausted` (a window at 100% or refusing requests) rules an * account out. */ /** A report older than this is flagged: the Hook reads each account at most once a minute. */ export declare const STALE_AFTER_MS: number; export interface AccountWindow { id: string; name: string; usedPercent?: number; leftPercent?: number; resetsAt?: string; resetsIn?: string; /** Reset since its report: the allowance is back, but no new percent is known. */ reset?: true; /** The service is refusing requests on this window now. */ limited?: true; /** At 100% or refused: no quota on this window until it resets. */ exhausted?: true; usedUSD?: number; limitUSD?: number; } export interface AccountUsageRow { /** `source` or `source|key`, as the phone keys its cards. */ id: string; harness: string; name: string; /** The login's email, else the account's label, when the Hook names one. */ account?: string; windows: AccountWindow[]; /** Least room on any window with a percent; `limited` counts as 0. */ leftPercent?: number; /** Under 20% left on some window. Information, not a reason to avoid the account. */ nearLimit: boolean; /** A window is at 100% or refusing requests: never dispatch to it until `availableIn`. */ exhausted: boolean; /** When the last exhausted window resets, as "3h 10m". */ availableIn?: string; spend?: { amountUSD: number; period: string; }; subscription?: Subscription; /** Codex's credit balance from the newest report that has one. */ credits?: UsageCredits; updatedAt?: string; /** How old the standing report is, as "4m" or "2d 3h". */ age?: string; stale: boolean; /** The computer whose report stands. */ from?: string; /** Where it is signed in, with the Claude account id dispatch takes there. */ computers: Array<{ name: string; account?: string; }>; message?: string; } export interface AccountUsageView { accounts: AccountUsageRow[]; /** Harnesses no computer reported numbers for, with what each Hook said. */ noData: Array<{ harness: string; name: string; computers: string[]; message?: string; }>; /** The computers that answered, this one first. */ computers: string[]; unreachable: Array<{ computer: string; error: string; code?: string; }>; /** Registered in the store but not linked in hooks.yaml: their usage is unknown, not zero. */ notLinked: NotLinkedComputer[]; enrolled: number; peerError?: string; } export interface ComputerUsage { computer: string; accounts: AccountUsage[]; } export declare function ageText(ms: number): string; /** The phone's card key, except that a Claude login with no identity stays on its own computer. */ export declare function accountIdentity(usage: AccountUsage, computer: string): string; /** Percent used and left, with a passed reset reported as reset rather than as its old percent. */ export declare function settleWindow(window: UsageWindow, now: number): AccountWindow; /** Least room across an account's windows, undefined when none has a percent. */ export declare function roomLeft(windows: readonly AccountWindow[]): number | undefined; /** One row per account across every computer's report. */ export declare function mergeAccountUsage(reports: readonly ComputerUsage[], now?: number): Pick<AccountUsageView, "accounts" | "noData">; export interface ReadAccountUsageOptions { /** This computer's Hook; tests pass the fixture's socket. */ hook?: (route: string) => Promise<Json>; peers?: () => Promise<{ peers: HookPeer[]; peerError?: string; }>; peer?: (peer: HookPeer, route: string) => Promise<Json>; store?: string | null; now?: number; } /** This Hook's usage and every linked Hook's, merged by account. */ export declare function readAccountUsage(options?: ReadAccountUsageOptions): Promise<AccountUsageView>; /** The one-line answer a conductor reads first: how many, and which accounts are tight or stale. */ export declare function usageSummary(view: AccountUsageView): string; /** "5-hour 82% left (resets in 2h 10m), weekly 64% left (resets in 3d 4h)": what launch and failover rank Claude accounts by. */ export declare function claudeRoom(row: Pick<AccountUsageRow, "windows">): string; /** `phren dispatch usage` without --json. */ export declare function formatAccountUsage(view: AccountUsageView): string;