@openlimiter/core 1.2.0 → 1.3.1

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 (57) hide show
  1. package/dist/acquire/antigravity-probe.d.ts +139 -0
  2. package/dist/acquire/antigravity-probe.d.ts.map +1 -0
  3. package/dist/acquire/antigravity-probe.js +599 -0
  4. package/dist/acquire/antigravity-probe.js.map +1 -0
  5. package/dist/acquire/cadence.d.ts +74 -0
  6. package/dist/acquire/cadence.d.ts.map +1 -0
  7. package/dist/acquire/cadence.js +171 -0
  8. package/dist/acquire/cadence.js.map +1 -0
  9. package/dist/acquire/coordination.d.ts +157 -0
  10. package/dist/acquire/coordination.d.ts.map +1 -0
  11. package/dist/acquire/coordination.js +292 -0
  12. package/dist/acquire/coordination.js.map +1 -0
  13. package/dist/acquire/credentials.d.ts +113 -0
  14. package/dist/acquire/credentials.d.ts.map +1 -0
  15. package/dist/acquire/credentials.js +316 -0
  16. package/dist/acquire/credentials.js.map +1 -0
  17. package/dist/acquire/identity.d.ts +20 -0
  18. package/dist/acquire/identity.d.ts.map +1 -0
  19. package/dist/acquire/identity.js +20 -0
  20. package/dist/acquire/identity.js.map +1 -0
  21. package/dist/acquire/index.d.ts +10 -0
  22. package/dist/acquire/index.d.ts.map +1 -0
  23. package/dist/acquire/index.js +10 -0
  24. package/dist/acquire/index.js.map +1 -0
  25. package/dist/acquire/providers.d.ts +86 -0
  26. package/dist/acquire/providers.d.ts.map +1 -0
  27. package/dist/acquire/providers.js +175 -0
  28. package/dist/acquire/providers.js.map +1 -0
  29. package/dist/acquire/runner.d.ts +146 -0
  30. package/dist/acquire/runner.d.ts.map +1 -0
  31. package/dist/acquire/runner.js +345 -0
  32. package/dist/acquire/runner.js.map +1 -0
  33. package/dist/acquire/transport.d.ts +194 -0
  34. package/dist/acquire/transport.d.ts.map +1 -0
  35. package/dist/acquire/transport.js +434 -0
  36. package/dist/acquire/transport.js.map +1 -0
  37. package/dist/acquire/windows-credential.d.ts +74 -0
  38. package/dist/acquire/windows-credential.d.ts.map +1 -0
  39. package/dist/acquire/windows-credential.js +124 -0
  40. package/dist/acquire/windows-credential.js.map +1 -0
  41. package/dist/cache.d.ts +28 -0
  42. package/dist/cache.d.ts.map +1 -1
  43. package/dist/cache.js +81 -2
  44. package/dist/cache.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/normalizer.d.ts +2 -0
  50. package/dist/normalizer.d.ts.map +1 -1
  51. package/dist/normalizer.js +39 -2
  52. package/dist/normalizer.js.map +1 -1
  53. package/dist/types.d.ts +36 -0
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/types.js +15 -0
  56. package/dist/types.js.map +1 -1
  57. package/package.json +1 -1
@@ -0,0 +1,434 @@
1
+ /**
2
+ * The network layer of the Node acquisition path, closed the same way the
3
+ * desktop's is.
4
+ *
5
+ * There is no function here that fetches a caller supplied URL. There is a
6
+ * closed list of endpoints, each carrying one constant address and one constant
7
+ * method, and a transport that is handed a request built from that list. The
8
+ * whole reachable internet is the table below, and nothing read off disk or out
9
+ * of a provider response can widen it.
10
+ *
11
+ * Failures are a closed vocabulary with no payload, so a header, a body or a
12
+ * token cannot end up inside an error string no matter who formats it. A
13
+ * response outside the success range has its body dropped unread.
14
+ */
15
+ import { OPENLIMITER_USER_AGENT } from "./identity.js";
16
+ /** One request's total budget, connect to last body byte. */
17
+ export const ACQUISITION_TIMEOUT_MILLISECONDS = 15_000;
18
+ /** Largest response body accepted, the bound every state file already uses. */
19
+ export const MAX_ACQUISITION_RESPONSE_BYTES = 1_048_576;
20
+ /* ------------------------------------------------------------- addresses */
21
+ /** The Claude account usage report, read with Claude Code's own OAuth token. */
22
+ export const CLAUDE_OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
23
+ /** The Codex usage report, read with the session the Codex CLI holds. */
24
+ export const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
25
+ /** Gemini CLI's Code Assist account bootstrap, which names the project. */
26
+ export const GEMINI_CLI_LOAD_URL = "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist";
27
+ /** Gemini CLI's per model quota report. */
28
+ export const GEMINI_CLI_QUOTA_URL = "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota";
29
+ /** The Grok Build billing report the official Grok CLI reads. */
30
+ export const GROK_USAGE_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
31
+ /** The Kimi Code usage report the official Kimi CLI reads. */
32
+ export const KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
33
+ /** The OpenRouter inference key report: limit, remaining and usage. */
34
+ export const OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key";
35
+ /**
36
+ * Every address this path may speak to.
37
+ *
38
+ * Adding a provider means adding an entry here, in code, in review. Antigravity
39
+ * has no entry of its own: it reads the same Code Assist pair as Gemini CLI
40
+ * with the credential the Antigravity client stored, which is why this table is
41
+ * shorter than the provider list.
42
+ */
43
+ export const ACQUISITION_ENDPOINTS = {
44
+ claude_usage: { url: CLAUDE_OAUTH_USAGE_URL, method: "GET" },
45
+ codex_usage: { url: CODEX_USAGE_URL, method: "GET" },
46
+ code_assist_load: { url: GEMINI_CLI_LOAD_URL, method: "POST" },
47
+ code_assist_quota: { url: GEMINI_CLI_QUOTA_URL, method: "POST" },
48
+ grok_billing: { url: GROK_USAGE_URL, method: "GET" },
49
+ kimi_usage: { url: KIMI_USAGE_URL, method: "GET" },
50
+ openrouter_key: { url: OPENROUTER_KEY_URL, method: "GET" }
51
+ };
52
+ /* ---------------------------------------------------------------- headers */
53
+ /** The beta contract the OAuth usage route answers. */
54
+ export const CLAUDE_OAUTH_BETA_HEADER = "anthropic-beta";
55
+ export const CLAUDE_OAUTH_BETA_VALUE = "oauth-2025-04-20";
56
+ /** The account header the ChatGPT backend reads beside the bearer token. */
57
+ export const CODEX_ACCOUNT_HEADER = "chatgpt-account-id";
58
+ /** The account identity the Grok billing service reads beside the token. */
59
+ export const GROK_ACCOUNT_HEADER = "x-userid";
60
+ /**
61
+ * The Code Assist bootstrap body, the same metadata the maintained client
62
+ * sends for an account that is already onboarded. Undefined project fields are
63
+ * omitted exactly as that client omits them.
64
+ */
65
+ export const CODE_ASSIST_LOAD_BODY = JSON.stringify({
66
+ metadata: {
67
+ ideType: "IDE_UNSPECIFIED",
68
+ platform: "PLATFORM_UNSPECIFIED",
69
+ pluginType: "GEMINI"
70
+ }
71
+ });
72
+ /**
73
+ * Whether a secret may be written into a header at all.
74
+ *
75
+ * The value is read off disk from a file this process does not own. A control
76
+ * character in it would let a hostile credential file inject a second header,
77
+ * so anything that is not bounded printable ASCII is refused before it can
78
+ * reach a request. Nothing about the refusal names the value.
79
+ */
80
+ export function usableHeaderSecret(value) {
81
+ return value.length > 0 &&
82
+ value.length <= 8_192 &&
83
+ /^[\x20-\x7E]+$/u.test(value);
84
+ }
85
+ /**
86
+ * Whether an account identifier may be written into a header.
87
+ *
88
+ * Narrower than a secret, because this one is an opaque identifier rather than
89
+ * a token: letters, digits and the three separators the observed identifiers
90
+ * use, and nothing else.
91
+ */
92
+ export function usableHeaderAccountId(value) {
93
+ return /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(value);
94
+ }
95
+ function bearer(secret) {
96
+ return {
97
+ authorization: "Bearer " + secret,
98
+ "user-agent": OPENLIMITER_USER_AGENT,
99
+ accept: "application/json"
100
+ };
101
+ }
102
+ /** The Claude usage request. Our identity, and the documented beta contract. */
103
+ export function claudeUsageRequest(secret) {
104
+ if (!usableHeaderSecret(secret))
105
+ return null;
106
+ return {
107
+ endpoint: "claude_usage",
108
+ url: ACQUISITION_ENDPOINTS.claude_usage.url,
109
+ method: "GET",
110
+ headers: { ...bearer(secret), [CLAUDE_OAUTH_BETA_HEADER]: CLAUDE_OAUTH_BETA_VALUE },
111
+ body: null
112
+ };
113
+ }
114
+ /** The Codex usage request, with the account header the backend requires. */
115
+ export function codexUsageRequest(secret, accountId) {
116
+ if (!usableHeaderSecret(secret) || !usableHeaderAccountId(accountId))
117
+ return null;
118
+ return {
119
+ endpoint: "codex_usage",
120
+ url: ACQUISITION_ENDPOINTS.codex_usage.url,
121
+ method: "GET",
122
+ headers: { ...bearer(secret), [CODEX_ACCOUNT_HEADER]: accountId },
123
+ body: null
124
+ };
125
+ }
126
+ /** The Code Assist bootstrap, which answers with the companion project. */
127
+ export function codeAssistLoadRequest(secret) {
128
+ if (!usableHeaderSecret(secret))
129
+ return null;
130
+ return {
131
+ endpoint: "code_assist_load",
132
+ url: ACQUISITION_ENDPOINTS.code_assist_load.url,
133
+ method: "POST",
134
+ headers: { ...bearer(secret), "content-type": "application/json" },
135
+ body: CODE_ASSIST_LOAD_BODY
136
+ };
137
+ }
138
+ /**
139
+ * Shape of a companion project identifier, and the reason there is one.
140
+ *
141
+ * The value arrives in a provider response body and is then written into the
142
+ * body of our next request. That is the one place a provider gets to influence
143
+ * what we send, so it is held to an opaque identifier and nothing else.
144
+ */
145
+ export const CODE_ASSIST_PROJECT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u;
146
+ /** The per model quota request, scoped to a validated companion project. */
147
+ export function codeAssistQuotaRequest(secret, project) {
148
+ if (!usableHeaderSecret(secret))
149
+ return null;
150
+ if (!CODE_ASSIST_PROJECT_PATTERN.test(project))
151
+ return null;
152
+ return {
153
+ endpoint: "code_assist_quota",
154
+ url: ACQUISITION_ENDPOINTS.code_assist_quota.url,
155
+ method: "POST",
156
+ headers: { ...bearer(secret), "content-type": "application/json" },
157
+ body: JSON.stringify({ project, userAgent: OPENLIMITER_USER_AGENT })
158
+ };
159
+ }
160
+ /**
161
+ * The Grok billing request.
162
+ *
163
+ * The account header is an identifier for the person's own account and is sent
164
+ * when the credential names one. Nothing else is: the desktop sends xAI's
165
+ * `x-xai-token-auth: xai-grok-cli` marker beside it, and that value NAMES THE
166
+ * VENDOR'S OWN TOOL, which is the same claim a copied user agent makes. Rule 1
167
+ * says we identify as OpenLimiter and take whatever that costs us, so the
168
+ * marker is not sent here and no client version is invented either.
169
+ *
170
+ * The consequence is honestly unknown rather than assumed: no Grok login exists
171
+ * on the machine this was written on, so whether the billing route answers a
172
+ * request that does not claim to be the Grok CLI is verified on the first
173
+ * install that has one, and the row says so until then.
174
+ */
175
+ export function grokBillingRequest(secret, accountId) {
176
+ if (!usableHeaderSecret(secret))
177
+ return null;
178
+ const account = accountId !== null && usableHeaderAccountId(accountId)
179
+ ? accountId
180
+ : null;
181
+ return {
182
+ endpoint: "grok_billing",
183
+ url: ACQUISITION_ENDPOINTS.grok_billing.url,
184
+ method: "GET",
185
+ headers: {
186
+ ...bearer(secret),
187
+ ...(account === null ? {} : { [GROK_ACCOUNT_HEADER]: account })
188
+ },
189
+ body: null
190
+ };
191
+ }
192
+ /** The Kimi Code usage request. */
193
+ export function kimiUsageRequest(secret) {
194
+ if (!usableHeaderSecret(secret))
195
+ return null;
196
+ return {
197
+ endpoint: "kimi_usage",
198
+ url: ACQUISITION_ENDPOINTS.kimi_usage.url,
199
+ method: "GET",
200
+ headers: bearer(secret),
201
+ body: null
202
+ };
203
+ }
204
+ /** The OpenRouter key report, the one documented interface in this table. */
205
+ export function openrouterKeyRequest(secret) {
206
+ if (!usableHeaderSecret(secret))
207
+ return null;
208
+ return {
209
+ endpoint: "openrouter_key",
210
+ url: ACQUISITION_ENDPOINTS.openrouter_key.url,
211
+ method: "GET",
212
+ headers: bearer(secret),
213
+ body: null
214
+ };
215
+ }
216
+ /* -------------------------------------------------------------- outcomes */
217
+ /**
218
+ * What one request achieved, in words this product owns.
219
+ *
220
+ * Every value here is a decision about what to do next, never a description of
221
+ * a response body. `blocked` and `rate_limited` are separated because they earn
222
+ * different backoffs, and `drift` is separated from `remote_error` because only
223
+ * drift says the meaning of a stored reading is in doubt.
224
+ *
225
+ * `identity_refused` is the one that cost the most to learn. A provider can
226
+ * answer 200 to an honest client and still leave out the field the next request
227
+ * needs, which reads exactly like drift and is not: nothing changed shape, the
228
+ * provider simply serves that field to its own tools. Measured on 2026-09-07
229
+ * against Code Assist, which answered 200 with tier information and no
230
+ * companion project. Calling it drift would have had this build retry every
231
+ * fifteen minutes forever against an answer that will never differ.
232
+ */
233
+ export const ACQUISITION_OUTCOMES = [
234
+ "ok",
235
+ "unauthorized",
236
+ "rate_limited",
237
+ "blocked",
238
+ "remote_error",
239
+ "transport",
240
+ "too_large",
241
+ "drift",
242
+ "identity_refused"
243
+ ];
244
+ /**
245
+ * Turn a status into a decision.
246
+ *
247
+ * 401 and 403 are told apart on purpose. A lapsed token is a person's next
248
+ * step, and a refusal to serve this client at all is a reason to stay away for
249
+ * a day. Everything from 500 up is the provider having a bad minute and is
250
+ * retried on the ordinary interval.
251
+ */
252
+ export function outcomeForStatus(status) {
253
+ if (status >= 200 && status < 300)
254
+ return "ok";
255
+ if (status === 401)
256
+ return "unauthorized";
257
+ if (status === 403)
258
+ return "blocked";
259
+ if (status === 429)
260
+ return "rate_limited";
261
+ return "remote_error";
262
+ }
263
+ /** Retry-After in seconds, when the header is a count of seconds we believe. */
264
+ export function retryAfterSeconds(value) {
265
+ if (value === null)
266
+ return null;
267
+ const trimmed = value.trim();
268
+ if (!/^\d{1,7}$/u.test(trimmed))
269
+ return null;
270
+ const seconds = Number.parseInt(trimmed, 10);
271
+ return Number.isFinite(seconds) && seconds > 0 ? seconds : null;
272
+ }
273
+ /**
274
+ * The header names an acquisition request may carry.
275
+ *
276
+ * Closed, and checked at the boundary below rather than trusted from the
277
+ * builders above. The builders are the only callers today, but the transport is
278
+ * exported and a future caller that assembled a request by hand would otherwise
279
+ * be able to put anything on the wire under this product's name.
280
+ */
281
+ export const ALLOWED_REQUEST_HEADERS = [
282
+ "authorization",
283
+ "accept",
284
+ "user-agent",
285
+ "content-type",
286
+ CLAUDE_OAUTH_BETA_HEADER,
287
+ CODEX_ACCOUNT_HEADER,
288
+ GROK_ACCOUNT_HEADER
289
+ ];
290
+ /**
291
+ * Whether one header's VALUE is the value that header is allowed to hold.
292
+ *
293
+ * Checking names alone was half a check. A name allowlist stops a cookie going
294
+ * out; it does nothing about an authorization header carrying something that is
295
+ * not a bearer token, or a beta header carrying a contract we never agreed to,
296
+ * or an account header carrying a path. Each of these has exactly one shape and
297
+ * this is where each one is held to it.
298
+ */
299
+ function validHeaderValue(name, value, method) {
300
+ if (!/^[\x20-\x7E]+$/u.test(value))
301
+ return false;
302
+ if (name === "user-agent")
303
+ return value === OPENLIMITER_USER_AGENT;
304
+ if (name === "accept")
305
+ return value === "application/json";
306
+ if (name === "content-type") {
307
+ return value === "application/json" && method === "POST";
308
+ }
309
+ if (name === "authorization") {
310
+ return value.startsWith("Bearer ") && usableHeaderSecret(value.slice(7));
311
+ }
312
+ if (name === CLAUDE_OAUTH_BETA_HEADER)
313
+ return value === CLAUDE_OAUTH_BETA_VALUE;
314
+ if (name === CODEX_ACCOUNT_HEADER || name === GROK_ACCOUNT_HEADER) {
315
+ return usableHeaderAccountId(value);
316
+ }
317
+ return false;
318
+ }
319
+ /**
320
+ * Whether a request body is the body that endpoint is allowed to carry.
321
+ *
322
+ * Two endpoints take a body and both bodies have a fixed shape. The bootstrap's
323
+ * is a constant. The quota read's carries the one field a provider response
324
+ * ever influences, which is exactly why it is checked here rather than trusted
325
+ * from the builder: this is the last point before the wire, and the last chance
326
+ * to notice that a project identifier grew into something else.
327
+ */
328
+ function validRequestBody(request) {
329
+ if (request.endpoint === "code_assist_load") {
330
+ return request.body === CODE_ASSIST_LOAD_BODY;
331
+ }
332
+ if (request.endpoint === "code_assist_quota") {
333
+ if (request.body === null)
334
+ return false;
335
+ let parsed;
336
+ try {
337
+ parsed = JSON.parse(request.body);
338
+ }
339
+ catch {
340
+ return false;
341
+ }
342
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
343
+ return false;
344
+ }
345
+ const fields = parsed;
346
+ const keys = Object.keys(fields).sort();
347
+ if (keys.length !== 2 || keys[0] !== "project" || keys[1] !== "userAgent") {
348
+ return false;
349
+ }
350
+ const project = fields["project"];
351
+ return typeof project === "string" &&
352
+ CODE_ASSIST_PROJECT_PATTERN.test(project) &&
353
+ fields["userAgent"] === OPENLIMITER_USER_AGENT;
354
+ }
355
+ /* Every other endpoint is a GET that asks for something and sends nothing. */
356
+ return request.body === null && request.method === "GET";
357
+ }
358
+ /**
359
+ * Whether a request may leave this process.
360
+ *
361
+ * Address, method, every header name, every header VALUE and the body, all
362
+ * answered against constants in this file. A request that fails any of them
363
+ * never reaches the network.
364
+ */
365
+ export function validAcquisitionRequest(request) {
366
+ const endpoint = ACQUISITION_ENDPOINTS[request.endpoint];
367
+ if (endpoint === undefined)
368
+ return false;
369
+ if (request.url !== endpoint.url || request.method !== endpoint.method)
370
+ return false;
371
+ if (request.headers["user-agent"] !== OPENLIMITER_USER_AGENT)
372
+ return false;
373
+ for (const name of Object.keys(request.headers)) {
374
+ if (!ALLOWED_REQUEST_HEADERS.includes(name))
375
+ return false;
376
+ const value = request.headers[name];
377
+ if (value === undefined)
378
+ return false;
379
+ if (!validHeaderValue(name, value, request.method))
380
+ return false;
381
+ }
382
+ return validRequestBody(request);
383
+ }
384
+ /**
385
+ * The transport the product uses, built on the runtime's own fetch.
386
+ *
387
+ * Redirects are refused rather than followed: a redirect on any of these
388
+ * addresses would carry the bearer token somewhere the closed table above never
389
+ * named. The body is bounded before it is decoded, and the whole request lives
390
+ * under one deadline. A transport level failure is an exception, which the
391
+ * runner turns into the `transport` outcome without ever formatting it.
392
+ */
393
+ export function createFetchTransport(fetchImplementation = globalThis.fetch) {
394
+ return async (request) => {
395
+ /* Payload free on purpose. The runner turns this into the `transport`
396
+ outcome, and an error carrying a URL or a header would be the one place
397
+ a credential could reach a log. */
398
+ if (!validAcquisitionRequest(request)) {
399
+ throw new Error("Refused a request outside the acquisition contract");
400
+ }
401
+ const controller = new AbortController();
402
+ const timer = setTimeout(() => controller.abort(), ACQUISITION_TIMEOUT_MILLISECONDS);
403
+ try {
404
+ const response = await fetchImplementation(request.url, {
405
+ method: request.method,
406
+ headers: { ...request.headers },
407
+ redirect: "error",
408
+ signal: controller.signal,
409
+ ...(request.body === null ? {} : { body: request.body })
410
+ });
411
+ const retryAfter = retryAfterSeconds(response.headers.get("retry-after"));
412
+ if (response.status < 200 || response.status >= 300) {
413
+ /* The body of a refusal is never read. It cannot help, and reading it
414
+ is how a provider's prose reaches a log. */
415
+ return { status: response.status, body: "", retryAfterSeconds: retryAfter };
416
+ }
417
+ const buffer = await response.arrayBuffer();
418
+ if (buffer.byteLength > MAX_ACQUISITION_RESPONSE_BYTES) {
419
+ /* Status zero is this layer's way of saying the answer was refused
420
+ before it was read, which the runner reads as too large. */
421
+ return { status: 0, body: "", retryAfterSeconds: retryAfter };
422
+ }
423
+ return {
424
+ status: response.status,
425
+ body: new TextDecoder("utf-8", { fatal: false }).decode(buffer),
426
+ retryAfterSeconds: retryAfter
427
+ };
428
+ }
429
+ finally {
430
+ clearTimeout(timer);
431
+ }
432
+ };
433
+ }
434
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/acquire/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Reading one Windows Credential Manager entry, with no native dependency.
3
+ *
4
+ * The Antigravity client keeps its token in the credential store rather than in
5
+ * a file, so a Windows machine has nothing on disk to read. Every published way
6
+ * to reach the store from Node is a native module with an install script, which
7
+ * is a compiler on a user's machine and a post install hook in our supply chain
8
+ * for the sake of one string. This asks Windows itself instead: PowerShell with
9
+ * no profile, a small block that declares CredRead through the platform
10
+ * invocation layer, and base64 on the way out so a credential containing a
11
+ * quote or a newline cannot change the shape of what comes back.
12
+ *
13
+ * Nothing here writes to the store, and the target is a constant supplied by
14
+ * the caller rather than anything read off disk.
15
+ */
16
+ /** How long the helper may run before it is abandoned. */
17
+ export declare const WINDOWS_CREDENTIAL_TIMEOUT_MILLISECONDS = 5000;
18
+ /** Largest credential blob accepted, matching the desktop's own bound. */
19
+ export declare const MAX_WINDOWS_CREDENTIAL_BYTES = 16384;
20
+ /**
21
+ * The one shape a credential target may take.
22
+ *
23
+ * Bounded printable ASCII with no quote and no newline, so the value can never
24
+ * end a string literal inside the script below. A target outside this shape is
25
+ * refused rather than escaped: there is exactly one target in this product and
26
+ * it is a constant.
27
+ */
28
+ export declare const CREDENTIAL_TARGET_PATTERN: RegExp;
29
+ export type CredentialCommandRunner = (executable: string, argumentsList: readonly string[], timeoutMilliseconds: number) => Promise<{
30
+ ok: true;
31
+ stdout: string;
32
+ } | {
33
+ ok: false;
34
+ }>;
35
+ export type WindowsCredentialOutcome = {
36
+ ok: true;
37
+ value: string;
38
+ } | {
39
+ ok: false;
40
+ reason: "absent" | "unreadable" | "invalid";
41
+ };
42
+ /**
43
+ * The script the helper runs.
44
+ *
45
+ * It declares only CredRead and CredFree, reads the blob as bytes, and writes
46
+ * base64 to standard output. A missing entry prints nothing and exits zero,
47
+ * which is how "this machine has no Antigravity login" reaches us as an absence
48
+ * rather than as an error.
49
+ */
50
+ export declare function credentialReadScript(target: string): string;
51
+ /**
52
+ * Decode what the helper printed.
53
+ *
54
+ * Empty output is an absent entry, which is the ordinary answer on a machine
55
+ * that never ran the Antigravity client. Anything that is not base64 of valid
56
+ * UTF-16 or UTF-8 text is unreadable, and neither branch ever echoes what it
57
+ * saw.
58
+ */
59
+ export declare function decodeCredentialOutput(stdout: string): WindowsCredentialOutcome;
60
+ export interface WindowsCredentialOptions {
61
+ readonly runCommand: CredentialCommandRunner;
62
+ readonly powerShellExecutable?: string;
63
+ readonly timeoutMilliseconds?: number;
64
+ }
65
+ /** The executable the helper is run through, named rather than searched for. */
66
+ export declare const DEFAULT_POWERSHELL_EXECUTABLE = "powershell.exe";
67
+ /**
68
+ * Read one credential target through the helper.
69
+ *
70
+ * The runner is injected so this whole path is provable without a credential on
71
+ * the machine and so a test can never reach the real store.
72
+ */
73
+ export declare function readWindowsCredentialWith(target: string, options: WindowsCredentialOptions): Promise<WindowsCredentialOutcome>;
74
+ //# sourceMappingURL=windows-credential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-credential.d.ts","sourceRoot":"","sources":["../../src/acquire/windows-credential.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,0DAA0D;AAC1D,eAAO,MAAM,uCAAuC,OAAQ,CAAC;AAE7D,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,QAA6C,CAAC;AAEpF,MAAM,MAAM,uBAAuB,GAAG,CACpC,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,mBAAmB,EAAE,MAAM,KACxB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE3D,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/D;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA2B3D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAwB/E;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;IAC7C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED,gFAAgF;AAChF,eAAO,MAAM,6BAA6B,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,wBAAwB,CAAC,CAiBnC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Reading one Windows Credential Manager entry, with no native dependency.
3
+ *
4
+ * The Antigravity client keeps its token in the credential store rather than in
5
+ * a file, so a Windows machine has nothing on disk to read. Every published way
6
+ * to reach the store from Node is a native module with an install script, which
7
+ * is a compiler on a user's machine and a post install hook in our supply chain
8
+ * for the sake of one string. This asks Windows itself instead: PowerShell with
9
+ * no profile, a small block that declares CredRead through the platform
10
+ * invocation layer, and base64 on the way out so a credential containing a
11
+ * quote or a newline cannot change the shape of what comes back.
12
+ *
13
+ * Nothing here writes to the store, and the target is a constant supplied by
14
+ * the caller rather than anything read off disk.
15
+ */
16
+ /** How long the helper may run before it is abandoned. */
17
+ export const WINDOWS_CREDENTIAL_TIMEOUT_MILLISECONDS = 5_000;
18
+ /** Largest credential blob accepted, matching the desktop's own bound. */
19
+ export const MAX_WINDOWS_CREDENTIAL_BYTES = 16_384;
20
+ /**
21
+ * The one shape a credential target may take.
22
+ *
23
+ * Bounded printable ASCII with no quote and no newline, so the value can never
24
+ * end a string literal inside the script below. A target outside this shape is
25
+ * refused rather than escaped: there is exactly one target in this product and
26
+ * it is a constant.
27
+ */
28
+ export const CREDENTIAL_TARGET_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:@\\/-]{0,127}$/u;
29
+ /**
30
+ * The script the helper runs.
31
+ *
32
+ * It declares only CredRead and CredFree, reads the blob as bytes, and writes
33
+ * base64 to standard output. A missing entry prints nothing and exits zero,
34
+ * which is how "this machine has no Antigravity login" reaches us as an absence
35
+ * rather than as an error.
36
+ */
37
+ export function credentialReadScript(target) {
38
+ return [
39
+ "$ErrorActionPreference = 'Stop';",
40
+ "Add-Type -Namespace OpenLimiterCred -Name Native -MemberDefinition @'",
41
+ "[DllImport(\"advapi32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]",
42
+ "public static extern bool CredRead(string target, uint type, uint flags, out IntPtr credential);",
43
+ "[DllImport(\"advapi32.dll\", SetLastError = true)]",
44
+ "public static extern void CredFree(IntPtr buffer);",
45
+ "'@;",
46
+ "$handle = [IntPtr]::Zero;",
47
+ "if (-not [OpenLimiterCred.Native]::CredRead('" + target + "', 1, 0, [ref] $handle)) { exit 0 };",
48
+ "try {",
49
+ /* CREDENTIALW packs its blob size and blob pointer at different offsets on
50
+ a 32 bit and a 64 bit host, so the offsets are derived from the pointer
51
+ width rather than hard coded to whichever one this machine happens to
52
+ be. Reading the wrong offset would hand back another field's bytes. */
53
+ " $wide = [IntPtr]::Size -eq 8;",
54
+ " $sizeOffset = if ($wide) { 32 } else { 24 };",
55
+ " $blobOffset = if ($wide) { 40 } else { 28 };",
56
+ " $size = [Runtime.InteropServices.Marshal]::ReadInt32($handle, $sizeOffset);",
57
+ " $blob = [Runtime.InteropServices.Marshal]::ReadIntPtr($handle, $blobOffset);",
58
+ " if ($size -le 0 -or $size -gt " + String(MAX_WINDOWS_CREDENTIAL_BYTES) + ") { exit 0 };",
59
+ " $bytes = New-Object byte[] $size;",
60
+ " [Runtime.InteropServices.Marshal]::Copy($blob, $bytes, 0, $size);",
61
+ " [Console]::Out.Write([Convert]::ToBase64String($bytes));",
62
+ "} finally { [OpenLimiterCred.Native]::CredFree($handle) }"
63
+ ].join("\n");
64
+ }
65
+ /**
66
+ * Decode what the helper printed.
67
+ *
68
+ * Empty output is an absent entry, which is the ordinary answer on a machine
69
+ * that never ran the Antigravity client. Anything that is not base64 of valid
70
+ * UTF-16 or UTF-8 text is unreadable, and neither branch ever echoes what it
71
+ * saw.
72
+ */
73
+ export function decodeCredentialOutput(stdout) {
74
+ const trimmed = stdout.trim();
75
+ if (trimmed === "")
76
+ return { ok: false, reason: "absent" };
77
+ if (!/^[A-Za-z0-9+/=]+$/u.test(trimmed))
78
+ return { ok: false, reason: "unreadable" };
79
+ let bytes;
80
+ try {
81
+ bytes = Buffer.from(trimmed, "base64");
82
+ }
83
+ catch {
84
+ return { ok: false, reason: "unreadable" };
85
+ }
86
+ if (bytes.byteLength === 0 || bytes.byteLength > MAX_WINDOWS_CREDENTIAL_BYTES) {
87
+ return { ok: false, reason: "unreadable" };
88
+ }
89
+ /* The store holds the blob the writer chose, and this one is UTF-8 JSON. A
90
+ zero byte in an even position is the signature of UTF-16, which the same
91
+ writer uses on some releases, so both are read and neither is guessed. */
92
+ const utf16 = bytes.byteLength % 2 === 0 && bytes[1] === 0;
93
+ const text = utf16
94
+ ? bytes.toString("utf16le")
95
+ : bytes.toString("utf8");
96
+ const value = text.replace(/\0+$/u, "");
97
+ return value.length === 0
98
+ ? { ok: false, reason: "unreadable" }
99
+ : { ok: true, value };
100
+ }
101
+ /** The executable the helper is run through, named rather than searched for. */
102
+ export const DEFAULT_POWERSHELL_EXECUTABLE = "powershell.exe";
103
+ /**
104
+ * Read one credential target through the helper.
105
+ *
106
+ * The runner is injected so this whole path is provable without a credential on
107
+ * the machine and so a test can never reach the real store.
108
+ */
109
+ export async function readWindowsCredentialWith(target, options) {
110
+ if (!CREDENTIAL_TARGET_PATTERN.test(target))
111
+ return { ok: false, reason: "invalid" };
112
+ const result = await options.runCommand(options.powerShellExecutable ?? DEFAULT_POWERSHELL_EXECUTABLE, [
113
+ "-NoProfile",
114
+ "-NonInteractive",
115
+ "-ExecutionPolicy",
116
+ "Bypass",
117
+ "-Command",
118
+ credentialReadScript(target)
119
+ ], options.timeoutMilliseconds ?? WINDOWS_CREDENTIAL_TIMEOUT_MILLISECONDS);
120
+ return result.ok
121
+ ? decodeCredentialOutput(result.stdout)
122
+ : { ok: false, reason: "unreadable" };
123
+ }
124
+ //# sourceMappingURL=windows-credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-credential.js","sourceRoot":"","sources":["../../src/acquire/windows-credential.ts"],"names":[],"mappings":"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"}