zcode-acp-server 0.69.4 → 0.70.0

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.
Files changed (56) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/quota.d.ts +1 -1
  3. package/dist/bin/quota.d.ts.map +1 -1
  4. package/dist/bin/quota.js +23 -5
  5. package/dist/bin/quota.js.map +1 -1
  6. package/dist/code-fingerprint.json +2 -2
  7. package/dist/config/user-config.d.ts +8 -0
  8. package/dist/config/user-config.d.ts.map +1 -1
  9. package/dist/config/user-config.js +2 -0
  10. package/dist/config/user-config.js.map +1 -1
  11. package/dist/handlers/account.d.ts +35 -4
  12. package/dist/handlers/account.d.ts.map +1 -1
  13. package/dist/handlers/account.js +69 -6
  14. package/dist/handlers/account.js.map +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/quota/cliproxy/client.d.ts +45 -0
  19. package/dist/quota/cliproxy/client.d.ts.map +1 -0
  20. package/dist/quota/cliproxy/client.js +134 -0
  21. package/dist/quota/cliproxy/client.js.map +1 -0
  22. package/dist/quota/cliproxy/config.d.ts +24 -0
  23. package/dist/quota/cliproxy/config.d.ts.map +1 -0
  24. package/dist/quota/cliproxy/config.js +27 -0
  25. package/dist/quota/cliproxy/config.js.map +1 -0
  26. package/dist/quota/cliproxy/format.d.ts +18 -0
  27. package/dist/quota/cliproxy/format.d.ts.map +1 -0
  28. package/dist/quota/cliproxy/format.js +64 -0
  29. package/dist/quota/cliproxy/format.js.map +1 -0
  30. package/dist/quota/cliproxy/index.d.ts +34 -0
  31. package/dist/quota/cliproxy/index.d.ts.map +1 -0
  32. package/dist/quota/cliproxy/index.js +110 -0
  33. package/dist/quota/cliproxy/index.js.map +1 -0
  34. package/dist/quota/cliproxy/parse.d.ts +48 -0
  35. package/dist/quota/cliproxy/parse.d.ts.map +1 -0
  36. package/dist/quota/cliproxy/parse.js +193 -0
  37. package/dist/quota/cliproxy/parse.js.map +1 -0
  38. package/dist/quota/cliproxy/types.d.ts +74 -0
  39. package/dist/quota/cliproxy/types.d.ts.map +1 -0
  40. package/dist/quota/cliproxy/types.js +33 -0
  41. package/dist/quota/cliproxy/types.js.map +1 -0
  42. package/dist/quota/combined.d.ts +5 -1
  43. package/dist/quota/combined.d.ts.map +1 -1
  44. package/dist/quota/combined.js +24 -6
  45. package/dist/quota/combined.js.map +1 -1
  46. package/dist/quota/format.d.ts +10 -0
  47. package/dist/quota/format.d.ts.map +1 -1
  48. package/dist/quota/format.js +26 -0
  49. package/dist/quota/format.js.map +1 -1
  50. package/dist/quota/live.d.ts.map +1 -1
  51. package/dist/quota/live.js +12 -5
  52. package/dist/quota/live.js.map +1 -1
  53. package/dist/remote/hub-server.d.ts.map +1 -1
  54. package/dist/remote/hub-server.js +7 -3
  55. package/dist/remote/hub-server.js.map +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * CLIProxyAPI management HTTP client.
3
+ *
4
+ * Two calls, both Bearer-authed with the management key:
5
+ * - credentials listing (v8: `/v8/management/credentials`; the v0 servers
6
+ * that still exist answer `/v0/management/auth-files` — probed once, the
7
+ * working spelling cached for the process);
8
+ * - `api-call` — the credential-scoped upstream relay the management web UI
9
+ * itself uses for Gemini quota.
10
+ *
11
+ * No retries beyond the URL-candidate ladder: a remote quota card must fail
12
+ * fast into `unavailable`, not hang the combined view.
13
+ */
14
+ import { relayUpstreamStatus } from "./parse.js";
15
+ const TIMEOUT_MS = 10_000;
16
+ /** Both route spellings, tried in order; the winner is cached per process. */
17
+ const CREDENTIALS_ROUTES = ["/v8/management/credentials", "/v0/management/auth-files"];
18
+ // The v8 relay lives under `/requests/` (official docs/management-api-v8.md);
19
+ // a top-level `/v8/management/api-call` does not exist and answers 404.
20
+ const API_CALL_ROUTES = ["/v8/management/requests/api-call", "/v0/management/api-call"];
21
+ /**
22
+ * Antigravity accounts' client identity (CPAMC `constants.ts`
23
+ * ANTIGRAVITY_USER_AGENT). The quota endpoint gates on it: without the
24
+ * antigravity CLI UA, Google answers 403 "You do not have a valid license of
25
+ * this product" even for a perfectly healthy account (verified live 2026-10).
26
+ */
27
+ const ANTIGRAVITY_USER_AGENT = "antigravity/cli/1.0.13 (aidev_client; os_type=darwin; arch=arm64)";
28
+ /**
29
+ * Upstream quota endpoints CPAMC itself tries, in order
30
+ * (Cli-Proxy-API-Management-Center `constants.ts`): the primary internal
31
+ * quota API first, sandbox and bare variants as fallbacks.
32
+ */
33
+ export const QUOTA_SUMMARY_URLS = [
34
+ "https://daily-cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary",
35
+ "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:retrieveUserQuotaSummary",
36
+ "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary",
37
+ ];
38
+ /** Route-spelling memory (set on first success; 404 keeps probing). */
39
+ let credentialsRoute = null;
40
+ let apiCallRoute = null;
41
+ /** Hook: forget the cached route spellings (tests, and watch-mode ticks so a
42
+ * server upgrade mid-watch re-probes instead of sticking to a stale route). */
43
+ export function resetRouteCache() {
44
+ credentialsRoute = null;
45
+ apiCallRoute = null;
46
+ }
47
+ function authHeaders(key) {
48
+ return {
49
+ Authorization: `Bearer ${key}`,
50
+ "X-Management-Key": key,
51
+ Accept: "application/json",
52
+ };
53
+ }
54
+ async function fetchOnce(url, key, init, fetchImpl) {
55
+ const resp = await fetchImpl(url, {
56
+ ...init,
57
+ headers: { ...authHeaders(key), ...init.headers },
58
+ signal: AbortSignal.timeout(TIMEOUT_MS),
59
+ });
60
+ return { status: resp.status, text: await resp.text() };
61
+ }
62
+ /** Probe the route spellings until one answers non-404 (cached afterwards). */
63
+ async function fetchRouted(base, key, routes, cached, init, fetchImpl) {
64
+ if (cached) {
65
+ return { route: cached, resp: await fetchOnce(`${base}${cached}`, key, init, fetchImpl) };
66
+ }
67
+ let last = { status: 404, text: "" };
68
+ for (const route of routes) {
69
+ last = await fetchOnce(`${base}${route}`, key, init, fetchImpl);
70
+ if (last.status !== 404)
71
+ return { route, resp: last };
72
+ }
73
+ return { route: null, resp: last };
74
+ }
75
+ /**
76
+ * List the proxy's credential entries. 401/403 surfaces to the caller as
77
+ * `auth_error`; other statuses as `unavailable`.
78
+ */
79
+ export async function fetchCredentials(base, key, fetchImpl = globalThis.fetch) {
80
+ const { route, resp } = await fetchRouted(base, key, CREDENTIALS_ROUTES, credentialsRoute, { method: "GET" }, fetchImpl);
81
+ if (route)
82
+ credentialsRoute = route;
83
+ return resp;
84
+ }
85
+ /**
86
+ * Relay `retrieveUserQuotaSummary` for one account. `$TOKEN$` is substituted
87
+ * by the proxy with that credential's OAuth token — the key never sees the
88
+ * token, and we never see it either (it stays server-side).
89
+ *
90
+ * The upstream URL ladder walks on the UPSTREAM status carried inside the
91
+ * relay envelope (CPAMC's semantics): the relay itself answers HTTP 200 with
92
+ * the upstream result embedded, so a 403/404 upstream means this URL variant
93
+ * is not the one this account's client is recognized on — the next variant
94
+ * may still answer. Only an upstream 2xx satisfies the ladder.
95
+ *
96
+ * @returns the relay response (its `body` is the upstream JSON as a STRING),
97
+ * or null when every URL candidate failed (relay/network error, or
98
+ * no variant produced an upstream 2xx).
99
+ */
100
+ export async function fetchQuotaSummary(base, key, authIndex, projectId, fetchImpl = globalThis.fetch) {
101
+ for (const url of QUOTA_SUMMARY_URLS) {
102
+ let resp = null;
103
+ try {
104
+ const routed = await fetchRouted(base, key, API_CALL_ROUTES, apiCallRoute, {
105
+ method: "POST",
106
+ headers: { "Content-Type": "application/json" },
107
+ body: JSON.stringify({
108
+ auth_index: authIndex,
109
+ method: "POST",
110
+ url,
111
+ header: {
112
+ Authorization: "Bearer $TOKEN$",
113
+ "Content-Type": "application/json",
114
+ "User-Agent": ANTIGRAVITY_USER_AGENT,
115
+ },
116
+ data: JSON.stringify(projectId !== undefined ? { project: projectId } : {}),
117
+ }),
118
+ }, fetchImpl);
119
+ if (routed.route)
120
+ apiCallRoute = routed.route;
121
+ resp = routed.resp;
122
+ }
123
+ catch {
124
+ resp = null; // network/timeout on this variant — try the next
125
+ }
126
+ if (resp?.status !== 200)
127
+ continue;
128
+ const upstream = relayUpstreamStatus(resp.text);
129
+ if (upstream >= 200 && upstream < 300)
130
+ return resp;
131
+ }
132
+ return null;
133
+ }
134
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/quota/cliproxy/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * CLIProxyAPI credential discovery — the same convention as the Ollama card:
3
+ * config file first (`~/.config/zcode-acp/config.json` `quota.cliproxyUrl` +
4
+ * `quota.cliproxyManagementKey`), env vars as a full fallback
5
+ * (`CLIPROXY_URL` / `CLIPROXY_MANAGEMENT_KEY`).
6
+ *
7
+ * A missing URL or key yields `not_configured` (the section is silently
8
+ * skipped in `all` mode). An `http://` URL to a non-loopback host is refused
9
+ * at query time — see {@link loadCpConfig}.
10
+ */
11
+ export declare const ENV_URL = "CLIPROXY_URL";
12
+ export declare const ENV_KEY = "CLIPROXY_MANAGEMENT_KEY";
13
+ export interface CpConfig {
14
+ /** Base URL, no trailing slash/path (e.g. `https://cp.example.com:8317`). */
15
+ url: string;
16
+ /** Management key (`remote-management.secret-key`). */
17
+ key: string;
18
+ }
19
+ /**
20
+ * Resolve the endpoint + key. Blank strings count as absent. The URL gets a
21
+ * trailing-slash trim so `https://host/` still works.
22
+ */
23
+ export declare function loadCpConfig(): CpConfig | null;
24
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/quota/cliproxy/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,eAAO,MAAM,OAAO,iBAAiB,CAAC;AACtC,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAEjD,MAAM,WAAW,QAAQ;IACvB,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,QAAQ,GAAG,IAAI,CAM9C"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CLIProxyAPI credential discovery — the same convention as the Ollama card:
3
+ * config file first (`~/.config/zcode-acp/config.json` `quota.cliproxyUrl` +
4
+ * `quota.cliproxyManagementKey`), env vars as a full fallback
5
+ * (`CLIPROXY_URL` / `CLIPROXY_MANAGEMENT_KEY`).
6
+ *
7
+ * A missing URL or key yields `not_configured` (the section is silently
8
+ * skipped in `all` mode). An `http://` URL to a non-loopback host is refused
9
+ * at query time — see {@link loadCpConfig}.
10
+ */
11
+ import process from "node:process";
12
+ import { loadUserConfig } from "../../config/user-config.js";
13
+ export const ENV_URL = "CLIPROXY_URL";
14
+ export const ENV_KEY = "CLIPROXY_MANAGEMENT_KEY";
15
+ /**
16
+ * Resolve the endpoint + key. Blank strings count as absent. The URL gets a
17
+ * trailing-slash trim so `https://host/` still works.
18
+ */
19
+ export function loadCpConfig() {
20
+ const quota = loadUserConfig().quota;
21
+ const url = (quota?.cliproxyUrl ?? process.env[ENV_URL] ?? "").trim().replace(/\/+$/, "");
22
+ const key = (quota?.cliproxyManagementKey ?? process.env[ENV_KEY] ?? "").trim();
23
+ if (!url || !key)
24
+ return null;
25
+ return { url, key };
26
+ }
27
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/quota/cliproxy/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC;AACtC,MAAM,CAAC,MAAM,OAAO,GAAG,yBAAyB,CAAC;AASjD;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC,KAAK,CAAC;IACrC,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChF,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CLIProxyAPI quota rendering — one row per (account, model), tightest model
3
+ * first, in the same bar style as the other sections so the combined card
4
+ * reads as one piece. Only Gemini (`antigravity`) accounts render rows today;
5
+ * other providers the proxy routes are summarised in a single trailing count
6
+ * line (their passive quota fields follow a different, header-observed shape
7
+ * and can join later without changing this layout).
8
+ */
9
+ import type { CpQueryResult } from "./types.js";
10
+ export declare function formatCpSection(result: CpQueryResult, color?: boolean,
11
+ /** Count of non-Gemini accounts routed by the proxy, for the tail line. */
12
+ otherAccounts?: number,
13
+ /** Clock for the cached-age stamp (tests inject; default real time). */
14
+ now?: () => number): {
15
+ header: string;
16
+ body: string[];
17
+ };
18
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/quota/cliproxy/format.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAShD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EACrB,KAAK,UAAQ;AACb,2EAA2E;AAC3E,aAAa,SAAI;AACjB,wEAAwE;AACxE,GAAG,GAAE,MAAM,MAAyB,GACnC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAwCpC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * CLIProxyAPI quota rendering — one row per (account, model), tightest model
3
+ * first, in the same bar style as the other sections so the combined card
4
+ * reads as one piece. Only Gemini (`antigravity`) accounts render rows today;
5
+ * other providers the proxy routes are summarised in a single trailing count
6
+ * line (their passive quota fields follow a different, header-observed shape
7
+ * and can join later without changing this layout).
8
+ */
9
+ import { pickOverlay, renderColorBar } from "../color.js";
10
+ import { formatResetTime, renderBar } from "../format.js";
11
+ import { roundTenth } from "../rounding.js";
12
+ /** `3m`-style age for the cached-stale header stamp. */
13
+ function formatAge(ms) {
14
+ const minutes = Math.floor(ms / 60_000);
15
+ if (minutes >= 60)
16
+ return `${Math.floor(minutes / 60)}h${minutes % 60 || ""}`;
17
+ return `${minutes}m`;
18
+ }
19
+ export function formatCpSection(result, color = false,
20
+ /** Count of non-Gemini accounts routed by the proxy, for the tail line. */
21
+ otherAccounts = 0,
22
+ /** Clock for the cached-age stamp (tests inject; default real time). */
23
+ now = () => Date.now()) {
24
+ // The section's 5-minute server-side-of-the-card TTL means values can lag
25
+ // the rest of the card by minutes (see ../index.js TTL_MS) — stamp the age
26
+ // so a motionless number reads as cached, not broken.
27
+ const ageMs = result.kind === "success" ? now() - result.fetchedAt : 0;
28
+ const header = ageMs > 90_000 ? `CLIProxy · ${formatAge(ageMs)} old` : "CLIProxy";
29
+ if (result.kind !== "success") {
30
+ const msg = result.kind === "not_configured"
31
+ ? "not configured (set quota.cliproxyUrl + quota.cliproxyManagementKey)"
32
+ : result.kind === "auth_error"
33
+ ? "auth failed — check the management key"
34
+ : "unavailable";
35
+ return { header, body: [msg] };
36
+ }
37
+ if (result.accounts.length === 0) {
38
+ return { header, body: ["no Gemini accounts on this proxy"] };
39
+ }
40
+ const body = [];
41
+ for (const account of result.accounts) {
42
+ for (const model of account.models) {
43
+ const pct = roundTenth(model.used * 100);
44
+ const reset = formatResetTime(model.resetAt);
45
+ const trailing = reset ? ` · ${reset}` : "";
46
+ const label = `${account.label} ${model.name}`.slice(0, 26);
47
+ if (color) {
48
+ const bar = renderColorBar(pct, { overlay: pickOverlay({ usedPercent: pct }) });
49
+ body.push(`${label.padEnd(27)} ${bar}${trailing}`);
50
+ }
51
+ else {
52
+ body.push(`${label.padEnd(27)} ${renderBar(pct)} ${String(pct).padStart(2)}%${trailing}`);
53
+ }
54
+ }
55
+ }
56
+ if (body.length === 0) {
57
+ return { header, body: ["no quota buckets reported"] };
58
+ }
59
+ if (otherAccounts > 0) {
60
+ body.push(` + ${otherAccounts} non-Gemini account${otherAccounts > 1 ? "s" : ""}`);
61
+ }
62
+ return { header, body };
63
+ }
64
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/quota/cliproxy/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * CLIProxyAPI quota orchestration — the entry used by `zcode-acp quota cp`
3
+ * and the combined card.
4
+ *
5
+ * Flow: config (file → env) → plain-HTTP guard → cache → credentials listing
6
+ * → per-Gemini-account quota relay (parallel across accounts, three upstream
7
+ * URL variants in order) → parse → cache. Account-level failures degrade to
8
+ * that account's omission; a failed listing degrades the whole section.
9
+ *
10
+ * Never throws — the CLI always produces output.
11
+ */
12
+ import type { CpQueryResult } from "./types.js";
13
+ /** Result carrying the non-Gemini account count for the renderer's tail. */
14
+ export interface CpQueryOutcome {
15
+ result: CpQueryResult;
16
+ /** Credential entries whose provider is not Gemini (codex/claude/…). */
17
+ otherCount: number;
18
+ }
19
+ /**
20
+ * Drop the cached outcome. NOT wired into the CLI watch tick on purpose —
21
+ * clearing it per tick would restore the 30s poll this TTL exists to prevent.
22
+ * Tests use it to force a fresh query.
23
+ */
24
+ export declare function clearCpCache(): void;
25
+ /**
26
+ * Query the remote CLIProxyAPI. Returns `{result, otherCount}` so the caller
27
+ * can render the codex/claude tail without a second listing.
28
+ */
29
+ export declare function queryCpUsage(): Promise<CpQueryOutcome>;
30
+ export { formatCpSection } from "./format.js";
31
+ export { loadCpConfig, ENV_KEY, ENV_URL } from "./config.js";
32
+ export { ANTIGRAVITY_PROVIDER, parseCredentialEntries, parseQuotaSummary } from "./parse.js";
33
+ export type { CpAccountQuota, CpModelQuota, CpQueryResult } from "./types.js";
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/quota/cliproxy/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAYH,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAeD;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAsBD;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC,CAkD5D;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7F,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * CLIProxyAPI quota orchestration — the entry used by `zcode-acp quota cp`
3
+ * and the combined card.
4
+ *
5
+ * Flow: config (file → env) → plain-HTTP guard → cache → credentials listing
6
+ * → per-Gemini-account quota relay (parallel across accounts, three upstream
7
+ * URL variants in order) → parse → cache. Account-level failures degrade to
8
+ * that account's omission; a failed listing degrades the whole section.
9
+ *
10
+ * Never throws — the CLI always produces output.
11
+ */
12
+ import { log } from "../../utils.js";
13
+ import { fetchCredentials, fetchQuotaSummary } from "./client.js";
14
+ import { loadCpConfig } from "./config.js";
15
+ import { ANTIGRAVITY_PROVIDER, parseCredentialEntries, parseQuotaSummary, relayUpstreamBody, relayUpstreamStatus, } from "./parse.js";
16
+ /**
17
+ * Cache TTL — deliberately MUCH longer than the other providers (10s): the
18
+ * Gemini quota summary rides `v1internal:retrieveUserQuotaSummary`, a Google
19
+ * INTERNAL endpoint reached with the account's own OAuth token, so every
20
+ * refresh is charged to that Google account. High-frequency polling risks
21
+ * rate-limiting or account-level risk flags, and quota buckets move at
22
+ * minute-scale anyway. This TTL is the rate guard NO MATTER how often the
23
+ * caller polls (watch mode's 30s ticks included — they must not clear this
24
+ * cache; see `clearCpCache`).
25
+ */
26
+ const TTL_MS = 5 * 60_000;
27
+ let cached = null;
28
+ /**
29
+ * Drop the cached outcome. NOT wired into the CLI watch tick on purpose —
30
+ * clearing it per tick would restore the 30s poll this TTL exists to prevent.
31
+ * Tests use it to force a fresh query.
32
+ */
33
+ export function clearCpCache() {
34
+ cached = null;
35
+ }
36
+ async function queryOneAccount(base, key, entry) {
37
+ try {
38
+ const relay = await fetchQuotaSummary(base, key, entry.authIndex, entry.projectId);
39
+ if (!relay)
40
+ return null;
41
+ if (relayUpstreamStatus(relay.text) !== 200)
42
+ return null;
43
+ const body = relayUpstreamBody(relay.text);
44
+ if (body === undefined)
45
+ return null;
46
+ const models = parseQuotaSummary(body);
47
+ if (models.length === 0)
48
+ return null;
49
+ return { label: entry.label, provider: ANTIGRAVITY_PROVIDER, models };
50
+ }
51
+ catch (e) {
52
+ log(`cliproxy: account ${entry.authIndex} quota relay failed (${String(e)})`);
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Query the remote CLIProxyAPI. Returns `{result, otherCount}` so the caller
58
+ * can render the codex/claude tail without a second listing.
59
+ */
60
+ export async function queryCpUsage() {
61
+ if (cached && Date.now() - cached.at < TTL_MS) {
62
+ log("cliproxy: serving cached result");
63
+ return cached.outcome;
64
+ }
65
+ const cfg = loadCpConfig();
66
+ if (!cfg)
67
+ return { result: { kind: "not_configured" }, otherCount: 0 };
68
+ // The management key relays credential-bearing upstream calls — refuse a
69
+ // plaintext connection to anything but loopback (an SSH tunnel is the
70
+ // recommended way to reach a plain-HTTP instance).
71
+ if (cfg.url.startsWith("http://") && !/^http:\/\/(127\.|localhost|\[::1\])/.test(cfg.url)) {
72
+ log("cliproxy: refusing plain-HTTP management URL to a non-loopback host (use https or an SSH tunnel)");
73
+ return { result: { kind: "unavailable" }, otherCount: 0 };
74
+ }
75
+ let outcome;
76
+ try {
77
+ const resp = await fetchCredentials(cfg.url, cfg.key);
78
+ if (resp.status === 401 || resp.status === 403) {
79
+ outcome = { result: { kind: "auth_error" }, otherCount: 0 };
80
+ }
81
+ else if (resp.status !== 200) {
82
+ log(`cliproxy: unexpected status ${resp.status}`);
83
+ outcome = { result: { kind: "unavailable" }, otherCount: 0 };
84
+ }
85
+ else {
86
+ const entries = parseCredentialEntries(resp.text);
87
+ const gemini = entries.filter((e) => e.provider === ANTIGRAVITY_PROVIDER);
88
+ const accounts = (await Promise.all(gemini.map((e) => queryOneAccount(cfg.url, cfg.key, e)))).filter((a) => a !== null);
89
+ // Every Gemini account failing its relay reads as "unreachable quota",
90
+ // not as an empty success — with accounts present but none answering,
91
+ // the honest row is an error line.
92
+ outcome = {
93
+ result: gemini.length > 0 && accounts.length === 0
94
+ ? { kind: "unavailable" }
95
+ : { kind: "success", accounts, fetchedAt: Date.now() },
96
+ otherCount: entries.length - gemini.length,
97
+ };
98
+ }
99
+ }
100
+ catch (e) {
101
+ log(`cliproxy: fetch failed (${e instanceof Error ? e.message : String(e)})`);
102
+ outcome = { result: { kind: "unavailable" }, otherCount: 0 };
103
+ }
104
+ cached = { outcome, at: Date.now() };
105
+ return outcome;
106
+ }
107
+ export { formatCpSection } from "./format.js";
108
+ export { loadCpConfig, ENV_KEY, ENV_URL } from "./config.js";
109
+ export { ANTIGRAVITY_PROVIDER, parseCredentialEntries, parseQuotaSummary } from "./parse.js";
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/quota/cliproxy/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Parsers for the two CLIProxyAPI response shapes. Both are reconstructed
3
+ * from the v8.0.28 source + CPAMC web-UI readers (undocumented on the wire) —
4
+ * every field is read defensively: missing/odd shapes degrade to empty, never
5
+ * throw. The management API accepts both camelCase and snake_case INPUT, but
6
+ * its OUTPUT spelling is not contractual, so both variants are read.
7
+ */
8
+ import type { CpAccountQuota, CpModelQuota } from "./types.js";
9
+ /** Antigravity accounts' provider id in the credentials listing (CPAMC's own
10
+ * quota support is antigravity-only — there is no separate gemini provider). */
11
+ export declare const ANTIGRAVITY_PROVIDER = "antigravity";
12
+ /** A parsed credential entry — only the fields the quota flow needs. */
13
+ export interface CpCredentialEntry {
14
+ authIndex: string;
15
+ provider: string;
16
+ label: string;
17
+ projectId?: string;
18
+ }
19
+ /**
20
+ * Parse the credentials listing. The response is an array of entries (the
21
+ * field-carrying object may nest one level under `auth_files`/`credentials`/
22
+ * `files` on some builds), each with `auth_index`/`authIndex` and
23
+ * `provider`. The Gemini quota relay needs the account's CloudCode project
24
+ * id: `file.project_id` → `metadata` → `attributes` → the entry itself
25
+ * (CPAMC's resolution ladder).
26
+ */
27
+ export declare function parseCredentialEntries(text: string): CpCredentialEntry[];
28
+ /**
29
+ * Parse one relayed `retrieveUserQuotaSummary` body into per-model rows.
30
+ *
31
+ * Two shapes, both read defensively (missing/odd fields degrade to empty,
32
+ * never throw):
33
+ * - antigravity (the shape live servers answer, CPAMC `builders.ts`):
34
+ * `{groups: [{displayName, buckets: [{window, remainingFraction,
35
+ * resetTime, …}]}]}` — each group is a model family ("Gemini Models",
36
+ * "Claude and GPT models") with one bucket per window.
37
+ * - legacy flat `{models: [{name, buckets […]}]}` kept as a fallback.
38
+ *
39
+ * Each row carries several buckets (windows); we surface only the TIGHTEST
40
+ * (lowest remaining) as `used`, the one the user actually hits first.
41
+ */
42
+ export declare function parseQuotaSummary(bodyText: string): CpModelQuota[];
43
+ /** Convenience: the relay envelope's upstream status (200 unless malformed). */
44
+ export declare function relayUpstreamStatus(text: string): number;
45
+ /** Convenience: the relay envelope's upstream body (a STRING inside). */
46
+ export declare function relayUpstreamBody(text: string): string | undefined;
47
+ export type { CpAccountQuota, CpModelQuota };
48
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/quota/cliproxy/parse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/D;iFACiF;AACjF,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAgBlD,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4CxE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAYlE;AAgED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOlE;AAED,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC"}