@phnx-labs/agents-cli 1.20.89 → 1.20.90
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 +240 -0
- package/README.md +6 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +7 -1
- package/dist/commands/harness.d.ts +27 -0
- package/dist/commands/harness.js +120 -13
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/routines.d.ts +19 -0
- package/dist/commands/routines.js +28 -6
- package/dist/commands/secrets.d.ts +10 -1
- package/dist/commands/secrets.js +18 -6
- package/dist/commands/sessions-browser.d.ts +4 -0
- package/dist/commands/sessions-browser.js +51 -9
- package/dist/commands/sessions-favorite.d.ts +20 -0
- package/dist/commands/sessions-favorite.js +120 -0
- package/dist/commands/sessions.d.ts +103 -20
- package/dist/commands/sessions.js +356 -62
- package/dist/commands/setup-secrets.d.ts +7 -0
- package/dist/commands/setup-secrets.js +12 -9
- package/dist/commands/versions.js +12 -4
- package/dist/commands/view.d.ts +14 -1
- package/dist/commands/view.js +103 -128
- package/dist/lib/agents.d.ts +4 -2
- package/dist/lib/agents.js +21 -6
- package/dist/lib/hosts/dispatch.js +19 -1
- package/dist/lib/hq/floor.js +12 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/picker.d.ts +27 -2
- package/dist/lib/picker.js +71 -7
- package/dist/lib/profiles.d.ts +48 -0
- package/dist/lib/profiles.js +67 -0
- package/dist/lib/rotate.d.ts +24 -2
- package/dist/lib/rotate.js +63 -6
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/session/active.d.ts +109 -3
- package/dist/lib/session/active.js +269 -13
- package/dist/lib/session/db.d.ts +14 -0
- package/dist/lib/session/db.js +35 -0
- package/dist/lib/session/favorites.d.ts +39 -0
- package/dist/lib/session/favorites.js +101 -0
- package/dist/lib/session/host-link.d.ts +68 -0
- package/dist/lib/session/host-link.js +64 -0
- package/dist/lib/session/presence.d.ts +85 -0
- package/dist/lib/session/presence.js +150 -0
- package/dist/lib/session/remote-list.d.ts +10 -0
- package/dist/lib/session/remote-list.js +47 -9
- package/dist/lib/tmux/binary.d.ts +7 -0
- package/dist/lib/tmux/binary.js +11 -1
- package/dist/lib/types.d.ts +4 -3
- package/dist/lib/usage-backoff.d.ts +29 -0
- package/dist/lib/usage-backoff.js +165 -0
- package/dist/lib/usage.d.ts +112 -5
- package/dist/lib/usage.js +464 -46
- package/dist/lib/watchdog/runner.d.ts +13 -0
- package/dist/lib/watchdog/runner.js +16 -1
- package/package.json +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentId } from './types.js';
|
|
2
|
+
export declare function setUsageBackoffDirForTest(dir: string | null): string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Parse a `Retry-After` header. HTTP allows either delta-seconds or an HTTP
|
|
5
|
+
* date; both appear in the wild, so handle both and ignore anything else.
|
|
6
|
+
* Returns milliseconds from `now`, or null when there is nothing usable.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseRetryAfterMs(header: string | null | undefined, now?: number): number | null;
|
|
9
|
+
/**
|
|
10
|
+
* Record that `agent`'s usage endpoint threw a 429. `retryAfter` is the raw
|
|
11
|
+
* header; when it is absent or unparseable we still back off for `fallbackMs`,
|
|
12
|
+
* because continuing to poll an endpoint that just said no is what created the
|
|
13
|
+
* loop in the first place.
|
|
14
|
+
*/
|
|
15
|
+
export declare function noteUsageRateLimited(agent: AgentId, retryAfter: string | null | undefined, opts?: {
|
|
16
|
+
now?: number;
|
|
17
|
+
fallbackMs?: number;
|
|
18
|
+
}): void;
|
|
19
|
+
/**
|
|
20
|
+
* Epoch ms until which `agent`'s usage endpoint should not be called, or null
|
|
21
|
+
* when it is free — the furthest recorded deadline still in the future, so a
|
|
22
|
+
* concurrently-written shorter one can never pull it in.
|
|
23
|
+
*
|
|
24
|
+
* Sweeps elapsed files while it is here: they can only accumulate at the rate
|
|
25
|
+
* penalties are issued, and this is the one place that already lists them.
|
|
26
|
+
*/
|
|
27
|
+
export declare function usageRateLimitedUntil(agent: AgentId, now?: number): number | null;
|
|
28
|
+
/** Human-readable remaining backoff, for the error a skipped read returns. */
|
|
29
|
+
export declare function formatBackoffRemaining(untilMs: number, now?: number): string;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Respect a usage endpoint's `Retry-After` instead of hammering through it.
|
|
3
|
+
*
|
|
4
|
+
* The failure this exists to stop, measured on `yosemite-s1` (2026-08-03): every
|
|
5
|
+
* Claude account there returned `429 rate_limit_error` with `retry-after: 2678`
|
|
6
|
+
* — about 45 minutes — while the credentials themselves read healthy
|
|
7
|
+
* (`probeClaudeStatus` → `status=429 token=present`). The daemon warms
|
|
8
|
+
* auth-health every 3 minutes (`daemon.ts`, `setInterval(..., 3 * 60_000)`) and
|
|
9
|
+
* `probeLocalFleetAuth` fans out over every installed version home in one
|
|
10
|
+
* `Promise.all`, so five Claude homes meant five concurrent requests to one
|
|
11
|
+
* endpoint every three minutes — ~100/hour from a single machine. Nothing read
|
|
12
|
+
* `Retry-After`, so each tick fired deep inside the penalty window and re-armed
|
|
13
|
+
* the throttle. Measured 75 minutes apart, the box never got out: `retry-after`
|
|
14
|
+
* 2678s at 08:42Z, still 429 at 09:57Z on a freshly-issued 1208s penalty. Its
|
|
15
|
+
* usage cache froze — exactly the permanently-stale state the routing freshness
|
|
16
|
+
* rule (`rotate.ts`, `USAGE_DECISION_MAX_AGE_MS`) was written to defend against.
|
|
17
|
+
*
|
|
18
|
+
* So a 429 is recorded here with its deadline, and every usage read and health
|
|
19
|
+
* probe for that provider short-circuits until the deadline passes — no request,
|
|
20
|
+
* no renewed penalty. State is on disk rather than in memory because the
|
|
21
|
+
* offenders are separate processes: the long-lived daemon, and every one-shot
|
|
22
|
+
* `agents view` / `agents run` invocation.
|
|
23
|
+
*
|
|
24
|
+
* ## The deadline lives in the FILENAME, and that is the whole design
|
|
25
|
+
*
|
|
26
|
+
* The obvious shape — one JSON document holding `{agent: deadline}` — is
|
|
27
|
+
* read-modify-write, and without a lock two processes recording the same
|
|
28
|
+
* provider can both read the old value and let the SHORTER deadline write last,
|
|
29
|
+
* silently undoing the longer penalty. That is not a theoretical race here: the
|
|
30
|
+
* triggering condition is a batch of concurrent same-provider 429s, which is
|
|
31
|
+
* precisely what the daemon issues, and it can recur on every batch. Reading
|
|
32
|
+
* back and retrying does not fix it either — that only detects a clobber which
|
|
33
|
+
* already landed, and a stale writer can still write after the check.
|
|
34
|
+
*
|
|
35
|
+
* So no shared document. Each penalty is its own file named `<agent>.<deadline>`
|
|
36
|
+
* with empty contents, and a read takes the MAXIMUM deadline across that
|
|
37
|
+
* provider's files. Two concurrent writers create two different files and
|
|
38
|
+
* neither can erase the other, so a shorter deadline cannot displace a longer
|
|
39
|
+
* one — monotonicity is structural rather than argued, and there is no lock to
|
|
40
|
+
* go stale on a path every usage read touches. Elapsed files are swept on read.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately per-provider, not per-account: the endpoint throttles the caller,
|
|
43
|
+
* and the observed 429 hit all five accounts on the box at once. Backing off one
|
|
44
|
+
* account while the others keep firing would not clear the penalty.
|
|
45
|
+
*/
|
|
46
|
+
import * as fs from 'fs';
|
|
47
|
+
import * as path from 'path';
|
|
48
|
+
import { getCacheDir } from './state.js';
|
|
49
|
+
/** Cap a server-supplied delay so a bad header cannot park a provider forever. */
|
|
50
|
+
const MAX_BACKOFF_MS = 60 * 60 * 1000;
|
|
51
|
+
/**
|
|
52
|
+
* Test seam, mirroring `setKeychainBackendForTest`. The cache dir is resolved
|
|
53
|
+
* from a module-level constant at import time, so overriding `HOME` in a test
|
|
54
|
+
* does NOT redirect this state — it silently writes into the developer's real
|
|
55
|
+
* `~/.agents/.cache/` and parks their own usage reads behind a 45-minute
|
|
56
|
+
* penalty. (It did exactly that once while this was being written.) Returns the
|
|
57
|
+
* previous value so a test can restore it.
|
|
58
|
+
*/
|
|
59
|
+
let backoffDirOverride = null;
|
|
60
|
+
export function setUsageBackoffDirForTest(dir) {
|
|
61
|
+
const prev = backoffDirOverride;
|
|
62
|
+
backoffDirOverride = dir;
|
|
63
|
+
return prev;
|
|
64
|
+
}
|
|
65
|
+
function backoffDir() {
|
|
66
|
+
return backoffDirOverride ?? path.join(getCacheDir(), 'usage-backoff');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse a `Retry-After` header. HTTP allows either delta-seconds or an HTTP
|
|
70
|
+
* date; both appear in the wild, so handle both and ignore anything else.
|
|
71
|
+
* Returns milliseconds from `now`, or null when there is nothing usable.
|
|
72
|
+
*/
|
|
73
|
+
export function parseRetryAfterMs(header, now = Date.now()) {
|
|
74
|
+
const raw = (header ?? '').trim();
|
|
75
|
+
if (!raw)
|
|
76
|
+
return null;
|
|
77
|
+
if (/^\d+$/.test(raw)) {
|
|
78
|
+
const ms = Number(raw) * 1000;
|
|
79
|
+
return ms > 0 ? Math.min(ms, MAX_BACKOFF_MS) : null;
|
|
80
|
+
}
|
|
81
|
+
const at = Date.parse(raw);
|
|
82
|
+
if (Number.isNaN(at))
|
|
83
|
+
return null;
|
|
84
|
+
const ms = at - now;
|
|
85
|
+
return ms > 0 ? Math.min(ms, MAX_BACKOFF_MS) : null;
|
|
86
|
+
}
|
|
87
|
+
/** Every recorded deadline for `agent`, newest-first. Never throws. */
|
|
88
|
+
function deadlinesFor(agent) {
|
|
89
|
+
let names;
|
|
90
|
+
try {
|
|
91
|
+
names = fs.readdirSync(backoffDir());
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// No directory yet: nothing is throttled.
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const prefix = `${agent}.`;
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const name of names) {
|
|
100
|
+
if (!name.startsWith(prefix))
|
|
101
|
+
continue;
|
|
102
|
+
const at = Number(name.slice(prefix.length));
|
|
103
|
+
if (Number.isFinite(at))
|
|
104
|
+
out.push(at);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Record that `agent`'s usage endpoint threw a 429. `retryAfter` is the raw
|
|
110
|
+
* header; when it is absent or unparseable we still back off for `fallbackMs`,
|
|
111
|
+
* because continuing to poll an endpoint that just said no is what created the
|
|
112
|
+
* loop in the first place.
|
|
113
|
+
*/
|
|
114
|
+
export function noteUsageRateLimited(agent, retryAfter, opts) {
|
|
115
|
+
const now = opts?.now ?? Date.now();
|
|
116
|
+
const fallbackMs = opts?.fallbackMs ?? 15 * 60 * 1000;
|
|
117
|
+
const ms = parseRetryAfterMs(retryAfter, now) ?? fallbackMs;
|
|
118
|
+
const deadline = now + Math.min(ms, MAX_BACKOFF_MS);
|
|
119
|
+
try {
|
|
120
|
+
fs.mkdirSync(backoffDir(), { recursive: true });
|
|
121
|
+
// Empty file: the name carries the whole value, so there is no content a
|
|
122
|
+
// concurrent reader could catch half-written, and no document to merge.
|
|
123
|
+
fs.writeFileSync(path.join(backoffDir(), `${agent}.${deadline}`), '');
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Best-effort. An unwritable cache dir costs the cross-process backoff, not
|
|
127
|
+
// the correctness of this read.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Epoch ms until which `agent`'s usage endpoint should not be called, or null
|
|
132
|
+
* when it is free — the furthest recorded deadline still in the future, so a
|
|
133
|
+
* concurrently-written shorter one can never pull it in.
|
|
134
|
+
*
|
|
135
|
+
* Sweeps elapsed files while it is here: they can only accumulate at the rate
|
|
136
|
+
* penalties are issued, and this is the one place that already lists them.
|
|
137
|
+
*/
|
|
138
|
+
export function usageRateLimitedUntil(agent, now = Date.now()) {
|
|
139
|
+
let latest = null;
|
|
140
|
+
for (const at of deadlinesFor(agent)) {
|
|
141
|
+
if (at > now) {
|
|
142
|
+
if (latest === null || at > latest)
|
|
143
|
+
latest = at;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
try {
|
|
147
|
+
fs.rmSync(path.join(backoffDir(), `${agent}.${at}`), { force: true });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
/* another process may have swept it already */
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return latest;
|
|
155
|
+
}
|
|
156
|
+
/** Human-readable remaining backoff, for the error a skipped read returns. */
|
|
157
|
+
export function formatBackoffRemaining(untilMs, now = Date.now()) {
|
|
158
|
+
const mins = Math.ceil((untilMs - now) / 60_000);
|
|
159
|
+
if (mins <= 1)
|
|
160
|
+
return 'under a minute';
|
|
161
|
+
if (mins < 60)
|
|
162
|
+
return `${mins} minutes`;
|
|
163
|
+
const hours = Math.round(mins / 60);
|
|
164
|
+
return hours === 1 ? 'about an hour' : `about ${hours} hours`;
|
|
165
|
+
}
|
package/dist/lib/usage.d.ts
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
import { type AccountInfo } from './agents.js';
|
|
2
2
|
import type { AgentId } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Why a usage read produced no snapshot, when the cause is the credential or the
|
|
5
|
+
* server rather than the payload. Every provider used to return `error: null`
|
|
6
|
+
* for all three, which made an account nobody can read indistinguishable from a
|
|
7
|
+
* healthy one: the caller fell back to whatever was in the SWR cache and
|
|
8
|
+
* rendered its bars as fact. On `yosemite-s1` that hid five Claude accounts
|
|
9
|
+
* whose stored access token had expired — one of them eleven days earlier —
|
|
10
|
+
* behind a cache frozen for 26h, and balanced routing launched into an account
|
|
11
|
+
* that was actually at its weekly cap.
|
|
12
|
+
*
|
|
13
|
+
* No usage read ever refreshes a token (RUSH-1822 for Claude; the same rule for
|
|
14
|
+
* Kimi/Droid/Cursor, whose own CLIs rotate on their next launch), so an expired
|
|
15
|
+
* credential cannot heal on its own — the account stays unreadable until that
|
|
16
|
+
* agent actually runs, or a long-lived token is provisioned for it.
|
|
17
|
+
*
|
|
18
|
+
* Shared across all four networked providers on purpose: the failure shape is
|
|
19
|
+
* identical, and wiring only Claude would leave `agents view --refresh`
|
|
20
|
+
* reporting Claude accounts while silently presenting stale Kimi, Droid, and
|
|
21
|
+
* Cursor readings as confirmed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function usageNoCredentialError(agent: string): string;
|
|
24
|
+
export declare function usageExpiredCredentialError(agent: string): string;
|
|
25
|
+
export declare function usageRejectedError(agent: string, status: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* The read threw rather than answering — a timeout, DNS/TLS failure, a payload
|
|
28
|
+
* that would not parse, a credential that would not decrypt. Every provider
|
|
29
|
+
* swallowed these into `error: null`, which is the same silence as an expired
|
|
30
|
+
* token: the caller renders a stale snapshot as confirmed. The cause is carried
|
|
31
|
+
* verbatim because these are the failures a user cannot otherwise see.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* The provider told us to back off and we are still inside that window, so this
|
|
35
|
+
* read made no request at all. Distinct from `usageRejectedError(agent, 429)`,
|
|
36
|
+
* which is the 429 itself: this one says we are *honouring* it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function usageThrottledError(agent: string, untilMs: number): string;
|
|
39
|
+
export declare function usageUnreachableError(agent: string, cause?: unknown): string;
|
|
3
40
|
/**
|
|
4
41
|
* True when a Claude OAuth access token is within the refresh leeway of expiry
|
|
5
42
|
* (or already expired) — i.e. it "would need a refresh" before the next use.
|
|
@@ -92,6 +129,10 @@ declare const USAGE_SOURCES: {
|
|
|
92
129
|
readonly fetch: typeof getCursorUsageInfo;
|
|
93
130
|
readonly network: true;
|
|
94
131
|
};
|
|
132
|
+
readonly antigravity: {
|
|
133
|
+
readonly fetch: typeof getAntigravityUsageInfo;
|
|
134
|
+
readonly network: true;
|
|
135
|
+
};
|
|
95
136
|
};
|
|
96
137
|
export declare const USAGE_SOURCE_AGENT_IDS: (keyof typeof USAGE_SOURCES)[];
|
|
97
138
|
/** Fetch usage info for a given agent through the canonical source registry. */
|
|
@@ -107,20 +148,29 @@ export declare function buildCanonicalUsageContext(inputs: UsageIdentityInput[])
|
|
|
107
148
|
usageFetchInputs: Map<string, UsageFetchInput>;
|
|
108
149
|
};
|
|
109
150
|
/**
|
|
110
|
-
* Whether an agent exposes usage/limit data we can render — Claude/Kimi/Droid/
|
|
111
|
-
* via a live API, Codex/Grok via local session logs.
|
|
112
|
-
* concept, so callers use this to decide whether
|
|
113
|
-
* flagging as "usage unavailable" (a signed-in
|
|
114
|
-
* versus simply not applicable (
|
|
151
|
+
* Whether an agent exposes usage/limit data we can render — Claude/Kimi/Droid/
|
|
152
|
+
* Cursor/Antigravity via a live API, Codex/Grok via local session logs.
|
|
153
|
+
* Everything else has no usage concept, so callers use this to decide whether
|
|
154
|
+
* a missing snapshot is worth flagging as "usage unavailable" (a signed-in
|
|
155
|
+
* Claude account with no data) versus simply not applicable (OpenCode).
|
|
115
156
|
*/
|
|
116
157
|
export declare function agentReportsUsage(agentId: AgentId): boolean;
|
|
117
158
|
/** Fetch usage info for all unique accounts in parallel, keyed by usage key. */
|
|
118
159
|
export declare function getUsageInfoByIdentity(inputs: UsageIdentityInput[], opts?: {
|
|
119
160
|
forceRefresh?: boolean;
|
|
161
|
+
maxAgeMs?: number;
|
|
120
162
|
}): Promise<{
|
|
121
163
|
canonicalByUsageKey: Map<string, AccountInfo>;
|
|
122
164
|
usageByKey: Map<string, UsageInfo>;
|
|
123
165
|
}>;
|
|
166
|
+
/**
|
|
167
|
+
* How stale a cached snapshot may be before the read stops serving it and blocks
|
|
168
|
+
* on the network. Defaults to the full 24h stale-while-revalidate window; a
|
|
169
|
+
* caller that is about to ROUTE on the number passes a shorter `maxAgeMs` and
|
|
170
|
+
* gets a live read instead of a day-old one. Never widens past 24h — a caller
|
|
171
|
+
* cannot opt into more staleness than the cache policy allows.
|
|
172
|
+
*/
|
|
173
|
+
export declare function swrWindowMsFor(maxAgeMs?: number): number;
|
|
124
174
|
/**
|
|
125
175
|
* Fetch usage for a single identity using stale-while-revalidate.
|
|
126
176
|
*
|
|
@@ -135,10 +185,12 @@ export declare function getUsageInfoByIdentity(inputs: UsageIdentityInput[], opt
|
|
|
135
185
|
*/
|
|
136
186
|
export declare function getUsageInfoForIdentity(input: UsageIdentityInput, opts?: {
|
|
137
187
|
forceRefresh?: boolean;
|
|
188
|
+
maxAgeMs?: number;
|
|
138
189
|
}): Promise<UsageInfo>;
|
|
139
190
|
/** Format a one-line usage summary with compact bars for inline display. */
|
|
140
191
|
export declare function formatUsageSummary(plan: string | null, snapshot: UsageSnapshot | null, planWidth?: number, opts?: {
|
|
141
192
|
unavailable?: boolean;
|
|
193
|
+
unverified?: boolean;
|
|
142
194
|
}): string;
|
|
143
195
|
/**
|
|
144
196
|
* Derive an account's real throttle state from its live usage windows — the
|
|
@@ -390,4 +442,59 @@ export declare function normalizeCursorUsage(data: CursorUsageResponse): UsageWi
|
|
|
390
442
|
* row still renders, without a misleading empty gauge).
|
|
391
443
|
*/
|
|
392
444
|
declare function getCursorUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
|
|
445
|
+
/** The OAuth token `agy` stores (inside `{ token: … }`) in the OS keyring or file. */
|
|
446
|
+
interface AntigravityOauthToken {
|
|
447
|
+
access_token?: string | null;
|
|
448
|
+
refresh_token?: string | null;
|
|
449
|
+
/** RFC3339 expiry timestamp for the access token. */
|
|
450
|
+
expiry?: string | null;
|
|
451
|
+
}
|
|
452
|
+
/** One per-model quota bucket from the :retrieveUserQuota response. */
|
|
453
|
+
export interface AntigravityQuotaBucket {
|
|
454
|
+
modelId?: string | null;
|
|
455
|
+
tokenType?: string | null;
|
|
456
|
+
remainingFraction?: number | null;
|
|
457
|
+
resetTime?: string | null;
|
|
458
|
+
}
|
|
459
|
+
/** Response shape from the Code Assist :retrieveUserQuota endpoint. */
|
|
460
|
+
export interface AntigravityQuotaResponse {
|
|
461
|
+
buckets?: AntigravityQuotaBucket[] | null;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Parse a stored `agy` OAuth payload into its token. Handles both on-disk
|
|
465
|
+
* shapes: the raw `{ token: {…} }` JSON (Linux file fallback) and the
|
|
466
|
+
* `go-keyring-base64:<base64>` wrapper zalando/go-keyring writes into the
|
|
467
|
+
* macOS Keychain item (service `gemini`, account `antigravity`). Never throws
|
|
468
|
+
* (malformed input => null).
|
|
469
|
+
*/
|
|
470
|
+
export declare function parseAntigravityOauthPayload(raw: string): AntigravityOauthToken | null;
|
|
471
|
+
/**
|
|
472
|
+
* True when the stored access token is expired (or inside the refresh leeway).
|
|
473
|
+
* A missing/unparseable expiry is treated as still-fresh — the quota call
|
|
474
|
+
* below is the source of truth if the token is actually dead (401 => render
|
|
475
|
+
* nothing), and we never want to force a refresh without evidence.
|
|
476
|
+
*/
|
|
477
|
+
export declare function antigravityTokenNeedsRefresh(expiry: string | null | undefined, nowMs?: number): boolean;
|
|
478
|
+
/** Compact model tag for the inline bar — 'gemini-2.5-flash-lite' => '2.5FL'. */
|
|
479
|
+
export declare function antigravityModelShortLabel(modelId: string): string;
|
|
480
|
+
/**
|
|
481
|
+
* Normalize the per-model quota buckets into the common UsageWindow shape —
|
|
482
|
+
* one window per model (`gemini-3.1-pro`, `gemini-2.5-flash`, …), keyed
|
|
483
|
+
* `session` since each bucket is a short-cycle quota with its own reset time.
|
|
484
|
+
* Duplicate buckets for one model keep the LOWEST remaining fraction (the
|
|
485
|
+
* most conservative read). Sorted most-used first so the bar closest to
|
|
486
|
+
* throttling leads the row. `windowMinutes` stays null: the API reports only
|
|
487
|
+
* the reset timestamp, not the window length, and an inferred 5h session
|
|
488
|
+
* length would wrongly zero the SWR cache between resets.
|
|
489
|
+
*/
|
|
490
|
+
export declare function normalizeAntigravityWindows(buckets: AntigravityQuotaBucket[]): UsageWindow[];
|
|
491
|
+
/**
|
|
492
|
+
* Fetch Antigravity usage via Google Code Assist's :retrieveUserQuota — the
|
|
493
|
+
* quota API `agy` itself talks to (its log shows the sibling :loadCodeAssist
|
|
494
|
+
* and :fetchAvailableModels calls on the same host). Auth is the stored `agy`
|
|
495
|
+
* OAuth token (OS keyring on macOS, file fallback on Linux), refreshed
|
|
496
|
+
* in-memory when expired — safe because Google's refresh tokens are
|
|
497
|
+
* non-rotating (see refreshAntigravityAccessToken).
|
|
498
|
+
*/
|
|
499
|
+
declare function getAntigravityUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
|
|
393
500
|
export {};
|