zcode-acp-server 0.69.4 → 0.71.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.
- package/README.md +1 -1
- package/dist/bin/quota.d.ts +1 -1
- package/dist/bin/quota.d.ts.map +1 -1
- package/dist/bin/quota.js +23 -5
- package/dist/bin/quota.js.map +1 -1
- package/dist/code-fingerprint.json +2 -2
- package/dist/config/user-config.d.ts +8 -0
- package/dist/config/user-config.d.ts.map +1 -1
- package/dist/config/user-config.js +2 -0
- package/dist/config/user-config.js.map +1 -1
- package/dist/handlers/account.d.ts +43 -4
- package/dist/handlers/account.d.ts.map +1 -1
- package/dist/handlers/account.js +79 -6
- package/dist/handlers/account.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/quota/cliproxy/aggregate.d.ts +48 -0
- package/dist/quota/cliproxy/aggregate.d.ts.map +1 -0
- package/dist/quota/cliproxy/aggregate.js +118 -0
- package/dist/quota/cliproxy/aggregate.js.map +1 -0
- package/dist/quota/cliproxy/client.d.ts +45 -0
- package/dist/quota/cliproxy/client.d.ts.map +1 -0
- package/dist/quota/cliproxy/client.js +134 -0
- package/dist/quota/cliproxy/client.js.map +1 -0
- package/dist/quota/cliproxy/config.d.ts +24 -0
- package/dist/quota/cliproxy/config.d.ts.map +1 -0
- package/dist/quota/cliproxy/config.js +27 -0
- package/dist/quota/cliproxy/config.js.map +1 -0
- package/dist/quota/cliproxy/format.d.ts +18 -0
- package/dist/quota/cliproxy/format.d.ts.map +1 -0
- package/dist/quota/cliproxy/format.js +71 -0
- package/dist/quota/cliproxy/format.js.map +1 -0
- package/dist/quota/cliproxy/index.d.ts +34 -0
- package/dist/quota/cliproxy/index.d.ts.map +1 -0
- package/dist/quota/cliproxy/index.js +110 -0
- package/dist/quota/cliproxy/index.js.map +1 -0
- package/dist/quota/cliproxy/parse.d.ts +46 -0
- package/dist/quota/cliproxy/parse.d.ts.map +1 -0
- package/dist/quota/cliproxy/parse.js +176 -0
- package/dist/quota/cliproxy/parse.js.map +1 -0
- package/dist/quota/cliproxy/types.d.ts +82 -0
- package/dist/quota/cliproxy/types.d.ts.map +1 -0
- package/dist/quota/cliproxy/types.js +33 -0
- package/dist/quota/cliproxy/types.js.map +1 -0
- package/dist/quota/combined.d.ts +5 -1
- package/dist/quota/combined.d.ts.map +1 -1
- package/dist/quota/combined.js +24 -6
- package/dist/quota/combined.js.map +1 -1
- package/dist/quota/format.d.ts +9 -0
- package/dist/quota/format.d.ts.map +1 -1
- package/dist/quota/format.js +19 -0
- package/dist/quota/format.js.map +1 -1
- package/dist/quota/live.d.ts.map +1 -1
- package/dist/quota/live.js +12 -5
- package/dist/quota/live.js.map +1 -1
- package/dist/remote/hub-server.d.ts.map +1 -1
- package/dist/remote/hub-server.js +7 -3
- package/dist/remote/hub-server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-account aggregation for the CLIProxy card.
|
|
3
|
+
*
|
|
4
|
+
* Each antigravity subscription has its own quota pools; CLIProxy rotates
|
|
5
|
+
* requests across them, so the actionable numbers are POOL-WIDE: total used
|
|
6
|
+
* over total capacity (the average of the per-account used fractions — the
|
|
7
|
+
* sum divided by the number of subscriptions reporting that family+window),
|
|
8
|
+
* plus the earliest reset among the contributing buckets (when capacity
|
|
9
|
+
* starts coming back). The per-account detail stays in
|
|
10
|
+
* {@link ./types.js CpAccountQuota} for ACP consumers.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Aggregate every account's families into pool-wide rows. Families are keyed
|
|
14
|
+
* by name (accounts report the same family set); a window reported by only
|
|
15
|
+
* some accounts still divides by the reporting count — a subscription that
|
|
16
|
+
* reports nothing for a family contributes neither usage nor capacity.
|
|
17
|
+
*/
|
|
18
|
+
export function aggregateCpFamilies(accounts) {
|
|
19
|
+
// family name → window → {usedSum, count, resetAt?}
|
|
20
|
+
const families = new Map();
|
|
21
|
+
for (const account of accounts) {
|
|
22
|
+
for (const model of account.models) {
|
|
23
|
+
let windows = families.get(model.name);
|
|
24
|
+
if (!windows) {
|
|
25
|
+
windows = new Map();
|
|
26
|
+
families.set(model.name, windows);
|
|
27
|
+
}
|
|
28
|
+
for (const w of model.windows) {
|
|
29
|
+
const agg = windows.get(w.window) ?? { usedSum: 0, count: 0 };
|
|
30
|
+
agg.usedSum += w.used;
|
|
31
|
+
agg.count += 1;
|
|
32
|
+
if (w.resetAt !== undefined && (agg.resetAt === undefined || w.resetAt < agg.resetAt)) {
|
|
33
|
+
agg.resetAt = w.resetAt;
|
|
34
|
+
}
|
|
35
|
+
windows.set(w.window, agg);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const [name, windows] of families) {
|
|
41
|
+
const rows = [];
|
|
42
|
+
for (const [window, agg] of windows) {
|
|
43
|
+
rows.push({
|
|
44
|
+
window,
|
|
45
|
+
used: Math.min(1, agg.usedSum / agg.count),
|
|
46
|
+
...(agg.resetAt !== undefined && { resetAt: agg.resetAt }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Tightest window first; ties keep the 5h-then-week display order.
|
|
50
|
+
rows.sort((a, b) => b.used - a.used || windowOrder(a.window) - windowOrder(b.window));
|
|
51
|
+
out.push({ name, windows: rows });
|
|
52
|
+
}
|
|
53
|
+
// Most-used family leads the card.
|
|
54
|
+
return out.sort((a, b) => maxUsed(b) - maxUsed(a));
|
|
55
|
+
}
|
|
56
|
+
function windowOrder(window) {
|
|
57
|
+
return window === "5h" ? 0 : 1;
|
|
58
|
+
}
|
|
59
|
+
function maxUsed(family) {
|
|
60
|
+
return family.windows.length > 0 ? family.windows[0].used : 0;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The pool-wide binding row across ALL families: the single (window, used,
|
|
64
|
+
* earliest reset) the dock and the ACP `combined` field report. The window is
|
|
65
|
+
* whichever is fullest (max aggregated used over every family×window); the
|
|
66
|
+
* reset is the earliest among that window's family rows — capacity starts
|
|
67
|
+
* returning then even though the pool refills gradually.
|
|
68
|
+
*/
|
|
69
|
+
export function bindingCpWindow(families) {
|
|
70
|
+
let binding = null;
|
|
71
|
+
for (const family of families) {
|
|
72
|
+
for (const w of family.windows) {
|
|
73
|
+
if (!binding || w.used > binding.used)
|
|
74
|
+
binding = { ...w };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!binding)
|
|
78
|
+
return null;
|
|
79
|
+
// Earliest reset across every family reporting the SAME window.
|
|
80
|
+
let earliest;
|
|
81
|
+
for (const family of families) {
|
|
82
|
+
for (const w of family.windows) {
|
|
83
|
+
if (w.window === binding.window && w.resetAt !== undefined) {
|
|
84
|
+
earliest = earliest === undefined ? w.resetAt : Math.min(earliest, w.resetAt);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { ...binding, ...(earliest !== undefined && { resetAt: earliest }) };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Per-window pool-wide rows for compact consumers (ACP `combined`): for each
|
|
92
|
+
* window, the fullest family's aggregated used plus that window's earliest
|
|
93
|
+
* reset. Families are separate pools, so a per-window sum would double-count
|
|
94
|
+
* capacity; the fullest family is the binding constraint for that window.
|
|
95
|
+
*/
|
|
96
|
+
export function combinedCpWindows(families) {
|
|
97
|
+
const rows = new Map();
|
|
98
|
+
const earliest = {};
|
|
99
|
+
for (const family of families) {
|
|
100
|
+
for (const w of family.windows) {
|
|
101
|
+
if (w.resetAt !== undefined) {
|
|
102
|
+
earliest[w.window] =
|
|
103
|
+
earliest[w.window] === undefined ? w.resetAt : Math.min(earliest[w.window], w.resetAt);
|
|
104
|
+
}
|
|
105
|
+
const prev = rows.get(w.window);
|
|
106
|
+
if (!prev || w.used > prev.used)
|
|
107
|
+
rows.set(w.window, { ...w });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const out = [...rows.values()].map((w) => ({
|
|
111
|
+
window: w.window,
|
|
112
|
+
used: w.used,
|
|
113
|
+
...(earliest[w.window] !== undefined && { resetAt: earliest[w.window] }),
|
|
114
|
+
}));
|
|
115
|
+
out.sort((a, b) => b.used - a.used || windowOrder(a.window) - windowOrder(b.window));
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../../src/quota/cliproxy/aggregate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 type { CpResponse } from "./types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Upstream quota endpoints CPAMC itself tries, in order
|
|
17
|
+
* (Cli-Proxy-API-Management-Center `constants.ts`): the primary internal
|
|
18
|
+
* quota API first, sandbox and bare variants as fallbacks.
|
|
19
|
+
*/
|
|
20
|
+
export declare const QUOTA_SUMMARY_URLS: readonly ["https://daily-cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary", "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:retrieveUserQuotaSummary", "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary"];
|
|
21
|
+
/** Hook: forget the cached route spellings (tests, and watch-mode ticks so a
|
|
22
|
+
* server upgrade mid-watch re-probes instead of sticking to a stale route). */
|
|
23
|
+
export declare function resetRouteCache(): void;
|
|
24
|
+
/**
|
|
25
|
+
* List the proxy's credential entries. 401/403 surfaces to the caller as
|
|
26
|
+
* `auth_error`; other statuses as `unavailable`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function fetchCredentials(base: string, key: string, fetchImpl?: typeof globalThis.fetch): Promise<CpResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Relay `retrieveUserQuotaSummary` for one account. `$TOKEN$` is substituted
|
|
31
|
+
* by the proxy with that credential's OAuth token — the key never sees the
|
|
32
|
+
* token, and we never see it either (it stays server-side).
|
|
33
|
+
*
|
|
34
|
+
* The upstream URL ladder walks on the UPSTREAM status carried inside the
|
|
35
|
+
* relay envelope (CPAMC's semantics): the relay itself answers HTTP 200 with
|
|
36
|
+
* the upstream result embedded, so a 403/404 upstream means this URL variant
|
|
37
|
+
* is not the one this account's client is recognized on — the next variant
|
|
38
|
+
* may still answer. Only an upstream 2xx satisfies the ladder.
|
|
39
|
+
*
|
|
40
|
+
* @returns the relay response (its `body` is the upstream JSON as a STRING),
|
|
41
|
+
* or null when every URL candidate failed (relay/network error, or
|
|
42
|
+
* no variant produced an upstream 2xx).
|
|
43
|
+
*/
|
|
44
|
+
export declare function fetchQuotaSummary(base: string, key: string, authIndex: string, projectId: string | undefined, fetchImpl?: typeof globalThis.fetch): Promise<CpResponse | null>;
|
|
45
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/quota/cliproxy/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAkB7C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,gQAIrB,CAAC;AAMX;gFACgF;AAChF,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AA4CD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,GACpD,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,GACpD,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoC5B"}
|
|
@@ -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 — pool-wide rows: every Gemini subscription's
|
|
3
|
+
* usage aggregated per model family and window (`5h` / `Week`), each row
|
|
4
|
+
* stamped with that window's EARLIEST reset across accounts (when capacity
|
|
5
|
+
* starts coming back). Same bar style as the other sections so the combined
|
|
6
|
+
* card reads as one piece. Non-Gemini providers the proxy routes are
|
|
7
|
+
* summarised in a single trailing count line.
|
|
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;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAahD,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,CA2CpC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLIProxyAPI quota rendering — pool-wide rows: every Gemini subscription's
|
|
3
|
+
* usage aggregated per model family and window (`5h` / `Week`), each row
|
|
4
|
+
* stamped with that window's EARLIEST reset across accounts (when capacity
|
|
5
|
+
* starts coming back). Same bar style as the other sections so the combined
|
|
6
|
+
* card reads as one piece. Non-Gemini providers the proxy routes are
|
|
7
|
+
* summarised in a single trailing count line.
|
|
8
|
+
*/
|
|
9
|
+
import { aggregateCpFamilies } from "./aggregate.js";
|
|
10
|
+
import { pickOverlay, renderColorBar } from "../color.js";
|
|
11
|
+
import { formatResetTime, renderBar } from "../format.js";
|
|
12
|
+
import { roundTenth } from "../rounding.js";
|
|
13
|
+
/** `3m`-style age for the cached-stale header stamp. */
|
|
14
|
+
function formatAge(ms) {
|
|
15
|
+
const minutes = Math.floor(ms / 60_000);
|
|
16
|
+
if (minutes >= 60)
|
|
17
|
+
return `${Math.floor(minutes / 60)}h${minutes % 60 || ""}`;
|
|
18
|
+
return `${minutes}m`;
|
|
19
|
+
}
|
|
20
|
+
function windowLabel(window) {
|
|
21
|
+
return window === "5h" ? "5h" : "Week";
|
|
22
|
+
}
|
|
23
|
+
export function formatCpSection(result, color = false,
|
|
24
|
+
/** Count of non-Gemini accounts routed by the proxy, for the tail line. */
|
|
25
|
+
otherAccounts = 0,
|
|
26
|
+
/** Clock for the cached-age stamp (tests inject; default real time). */
|
|
27
|
+
now = () => Date.now()) {
|
|
28
|
+
// The section's 5-minute server-side-of-the-card TTL means values can lag
|
|
29
|
+
// the rest of the card by minutes (see ../index.js TTL_MS) — stamp the age
|
|
30
|
+
// so a motionless number reads as cached, not broken.
|
|
31
|
+
const ageMs = result.kind === "success" ? now() - result.fetchedAt : 0;
|
|
32
|
+
const header = ageMs > 90_000 ? `CLIProxy · ${formatAge(ageMs)} old` : "CLIProxy";
|
|
33
|
+
if (result.kind !== "success") {
|
|
34
|
+
const msg = result.kind === "not_configured"
|
|
35
|
+
? "not configured (set quota.cliproxyUrl + quota.cliproxyManagementKey)"
|
|
36
|
+
: result.kind === "auth_error"
|
|
37
|
+
? "auth failed — check the management key"
|
|
38
|
+
: "unavailable";
|
|
39
|
+
return { header, body: [msg] };
|
|
40
|
+
}
|
|
41
|
+
if (result.accounts.length === 0) {
|
|
42
|
+
return { header, body: ["no Gemini accounts on this proxy"] };
|
|
43
|
+
}
|
|
44
|
+
const body = [];
|
|
45
|
+
for (const family of aggregateCpFamilies(result.accounts)) {
|
|
46
|
+
const label = family.name.slice(0, 22);
|
|
47
|
+
for (const [index, w] of family.windows.entries()) {
|
|
48
|
+
const pct = roundTenth(w.used * 100);
|
|
49
|
+
// Continuation rows (2nd window, 2nd family…) indent under the label.
|
|
50
|
+
const name = index === 0 ? label.padEnd(22) : " ".repeat(22);
|
|
51
|
+
const window = windowLabel(w.window).padEnd(5);
|
|
52
|
+
const reset = formatResetTime(w.resetAt);
|
|
53
|
+
const trailing = reset ? ` · ${reset}` : "";
|
|
54
|
+
if (color) {
|
|
55
|
+
const bar = renderColorBar(pct, { overlay: pickOverlay({ usedPercent: pct }) });
|
|
56
|
+
body.push(`${name}${window}${bar}${trailing}`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
body.push(`${name}${window}${renderBar(pct)} ${String(pct).padStart(2)}%${trailing}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (body.length === 0) {
|
|
64
|
+
return { header, body: ["no quota buckets reported"] };
|
|
65
|
+
}
|
|
66
|
+
if (otherAccounts > 0) {
|
|
67
|
+
body.push(` + ${otherAccounts} non-Gemini account${otherAccounts > 1 ? "s" : ""}`);
|
|
68
|
+
}
|
|
69
|
+
return { header, body };
|
|
70
|
+
}
|
|
71
|
+
//# 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, CpWindow, CpWindowQuota } 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,QAAQ,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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"}
|