mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
Keep the plugin usage API limited to stable report and presentation helpers. Source-local parsers and timeout handling preserve credential and response parity while the cases move alongside each source.
120 lines
3.4 KiB
TypeScript
120 lines
3.4 KiB
TypeScript
import type { ZodType } from "zod";
|
|
import type { JsonValue } from "@getpaseo/protocol/agent-types";
|
|
|
|
export interface UsageWindow {
|
|
id: string;
|
|
label: string;
|
|
usedPct?: number | null;
|
|
remainingPct?: number | null;
|
|
resetsAt?: string | null;
|
|
runsOutAt?: string | null;
|
|
shortfallPct?: number | null;
|
|
tone?: "default" | "ok" | "warning" | "danger";
|
|
headline?: boolean;
|
|
}
|
|
|
|
export interface UsageBalance {
|
|
id: string;
|
|
label: string;
|
|
used?: number | null;
|
|
remaining?: number | null;
|
|
limit?: number | null;
|
|
unit: "usd" | "credits" | "requests" | "tokens";
|
|
resetsAt?: string | null;
|
|
tone?: UsageWindow["tone"];
|
|
}
|
|
|
|
export interface UsageDetail {
|
|
id: string;
|
|
label: string;
|
|
value: string;
|
|
tone?: UsageWindow["tone"];
|
|
}
|
|
|
|
export interface UsageReport {
|
|
account: { key: string; label?: string };
|
|
status: "available" | "unavailable" | "error";
|
|
planLabel?: string;
|
|
windows: UsageWindow[];
|
|
balances?: UsageBalance[];
|
|
details?: UsageDetail[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface UsageSourceRegistration {
|
|
id: string;
|
|
label: string;
|
|
icon?: string;
|
|
input: ZodType;
|
|
fetch(input: unknown): Promise<UsageReport>;
|
|
discover?(): Promise<JsonValue[]>;
|
|
}
|
|
|
|
export function windowFromUsedPct(input: {
|
|
id: string;
|
|
label: string;
|
|
utilizationPct: number | null | undefined;
|
|
resetsAt?: string | null;
|
|
tone?: UsageWindow["tone"];
|
|
headline?: boolean;
|
|
}): UsageWindow {
|
|
const usedPct = typeof input.utilizationPct === "number" ? input.utilizationPct : null;
|
|
const window: UsageWindow = {
|
|
id: input.id,
|
|
label: input.label,
|
|
usedPct,
|
|
remainingPct: usedPct === null ? null : Math.max(0, 100 - usedPct),
|
|
resetsAt: input.resetsAt ?? null,
|
|
};
|
|
if (input.tone) window.tone = input.tone;
|
|
if (input.headline) window.headline = true;
|
|
return window;
|
|
}
|
|
|
|
/**
|
|
* The tone scale for anything measured against a known limit, windows and balances alike.
|
|
*
|
|
* Thresholds match `deriveTone` in the app's provider-usage/tone.ts, which is what the
|
|
* client falls back to when a window arrives without a tone. Healthy is "ok" rather than
|
|
* "default" because that is what every provider setting a tone has always sent, and it is
|
|
* what the bars render today below their thresholds.
|
|
*/
|
|
export function toneFromUsedPct(usedPct: number | null | undefined): UsageWindow["tone"] {
|
|
if (typeof usedPct !== "number") return "default";
|
|
if (usedPct > 90) return "danger";
|
|
if (usedPct >= 70) return "warning";
|
|
return "ok";
|
|
}
|
|
|
|
/**
|
|
* Tone for a balance with no known limit, where a percentage cannot be computed and the
|
|
* only signal is whether anything is left. Prefer `toneFromUsedPct` when a limit exists:
|
|
* this one stays "ok" until the balance is completely spent.
|
|
*/
|
|
export function balanceToneFromRemaining(
|
|
remaining: number | null | undefined,
|
|
): UsageBalance["tone"] {
|
|
if (typeof remaining !== "number") return "default";
|
|
if (remaining <= 0) return "danger";
|
|
return "ok";
|
|
}
|
|
|
|
/** Percentage of a limit consumed, or null when either side is unknown. */
|
|
export function usedPctOf(
|
|
used: number | null | undefined,
|
|
limit: number | null | undefined,
|
|
): number | null {
|
|
if (typeof used !== "number" || typeof limit !== "number" || limit <= 0) return null;
|
|
return (used / limit) * 100;
|
|
}
|
|
|
|
export function unavailableUsage(accountKey = "default"): UsageReport {
|
|
return {
|
|
account: { key: accountKey },
|
|
status: "unavailable",
|
|
windows: [],
|
|
balances: [],
|
|
details: [],
|
|
};
|
|
}
|