@openlimiter/core 0.4.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.
- package/dist/acquire/antigravity-probe.d.ts +139 -0
- package/dist/acquire/antigravity-probe.d.ts.map +1 -0
- package/dist/acquire/antigravity-probe.js +599 -0
- package/dist/acquire/antigravity-probe.js.map +1 -0
- package/dist/acquire/cadence.d.ts +74 -0
- package/dist/acquire/cadence.d.ts.map +1 -0
- package/dist/acquire/cadence.js +171 -0
- package/dist/acquire/cadence.js.map +1 -0
- package/dist/acquire/coordination.d.ts +157 -0
- package/dist/acquire/coordination.d.ts.map +1 -0
- package/dist/acquire/coordination.js +292 -0
- package/dist/acquire/coordination.js.map +1 -0
- package/dist/acquire/credentials.d.ts +113 -0
- package/dist/acquire/credentials.d.ts.map +1 -0
- package/dist/acquire/credentials.js +316 -0
- package/dist/acquire/credentials.js.map +1 -0
- package/dist/acquire/identity.d.ts +20 -0
- package/dist/acquire/identity.d.ts.map +1 -0
- package/dist/acquire/identity.js +20 -0
- package/dist/acquire/identity.js.map +1 -0
- package/dist/acquire/index.d.ts +10 -0
- package/dist/acquire/index.d.ts.map +1 -0
- package/dist/acquire/index.js +10 -0
- package/dist/acquire/index.js.map +1 -0
- package/dist/acquire/providers.d.ts +86 -0
- package/dist/acquire/providers.d.ts.map +1 -0
- package/dist/acquire/providers.js +175 -0
- package/dist/acquire/providers.js.map +1 -0
- package/dist/acquire/runner.d.ts +146 -0
- package/dist/acquire/runner.d.ts.map +1 -0
- package/dist/acquire/runner.js +345 -0
- package/dist/acquire/runner.js.map +1 -0
- package/dist/acquire/transport.d.ts +194 -0
- package/dist/acquire/transport.d.ts.map +1 -0
- package/dist/acquire/transport.js +434 -0
- package/dist/acquire/transport.js.map +1 -0
- package/dist/acquire/windows-credential.d.ts +74 -0
- package/dist/acquire/windows-credential.d.ts.map +1 -0
- package/dist/acquire/windows-credential.js +124 -0
- package/dist/acquire/windows-credential.js.map +1 -0
- package/dist/cache.d.ts +28 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +179 -25
- package/dist/cache.js.map +1 -1
- package/dist/connection-state.d.ts +53 -2
- package/dist/connection-state.d.ts.map +1 -1
- package/dist/connection-state.js +98 -9
- package/dist/connection-state.js.map +1 -1
- package/dist/failures.d.ts +2 -1
- package/dist/failures.d.ts.map +1 -1
- package/dist/failures.js +7 -3
- package/dist/failures.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +17 -1
- package/dist/merge.js.map +1 -1
- package/dist/normalizer.d.ts +2 -0
- package/dist/normalizer.d.ts.map +1 -1
- package/dist/normalizer.js +39 -4
- package/dist/normalizer.js.map +1 -1
- package/dist/types.d.ts +63 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who refreshes the cache, and how a terminal gets fresh bars with no daemon.
|
|
3
|
+
*
|
|
4
|
+
* Two questions live here, and they are different questions.
|
|
5
|
+
*
|
|
6
|
+
* The first is ownership. A machine running the desktop tray already has a
|
|
7
|
+
* refresher, and a status line that polls anyway doubles the traffic the
|
|
8
|
+
* provider sees for no new information. So a cache carrying a desktop write
|
|
9
|
+
* inside the last interval is a cache the command line tool leaves alone.
|
|
10
|
+
*
|
|
11
|
+
* The second is freshness without a background service. A status line render
|
|
12
|
+
* reads the cache and nothing else, and when the cache is older than a minute
|
|
13
|
+
* it starts a detached refresh under a lock and returns immediately. The render
|
|
14
|
+
* never waits for the network; the next render shows what the refresh found.
|
|
15
|
+
*/
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
import { lstat, open, readFile, unlink, utimes } from "node:fs/promises";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { readJsonFileSafely, resolveStateDirectory, writeFileAtomically } from "../cache.js";
|
|
20
|
+
import { canonicalJson } from "../normalizer.js";
|
|
21
|
+
import { DESKTOP_OWNERSHIP_SECONDS } from "./cadence.js";
|
|
22
|
+
/**
|
|
23
|
+
* How old the cache may be before a render starts a refresh behind itself.
|
|
24
|
+
*
|
|
25
|
+
* A minute, which is the number Lucas's own status line has used for months. It
|
|
26
|
+
* is short enough that a person who opens a terminal sees current bars and long
|
|
27
|
+
* enough that a status line redrawing on every keystroke starts nothing.
|
|
28
|
+
*/
|
|
29
|
+
export const CACHE_REFRESH_STALE_SECONDS = 60;
|
|
30
|
+
/** The lock one detached refresh holds, beside the cache lock and separate. */
|
|
31
|
+
export const REFRESH_LOCK_NAME = "openlimiter-refresh.lock";
|
|
32
|
+
/**
|
|
33
|
+
* How often a running refresh says it is still alive.
|
|
34
|
+
*
|
|
35
|
+
* The lock's modification time is the signal, so the heartbeat only touches the
|
|
36
|
+
* file and never rewrites it: the token inside has to stay byte for byte the
|
|
37
|
+
* same, because that token is what proves ownership at every write below.
|
|
38
|
+
*/
|
|
39
|
+
export const REFRESH_LOCK_HEARTBEAT_MILLISECONDS = 10_000;
|
|
40
|
+
/**
|
|
41
|
+
* How long a silent lock is honoured before it is treated as abandoned.
|
|
42
|
+
*
|
|
43
|
+
* Six missed heartbeats. The bound used to be a flat two minutes measured from
|
|
44
|
+
* when the lock was TAKEN, which quietly assumed a whole round fits in two
|
|
45
|
+
* minutes; seven providers, two of them two hop, each with a fifteen second
|
|
46
|
+
* budget, does not fit, so a legitimate refresh could be declared dead and
|
|
47
|
+
* raced by the next status line render. Measuring from the last heartbeat
|
|
48
|
+
* instead means the bound is about liveness rather than about duration, and a
|
|
49
|
+
* round may take as long as it takes.
|
|
50
|
+
*/
|
|
51
|
+
export const REFRESH_LOCK_STALE_MILLISECONDS = 60_000;
|
|
52
|
+
function errorCode(error) {
|
|
53
|
+
return error.code;
|
|
54
|
+
}
|
|
55
|
+
/** The newest observation in a set of rows, in epoch milliseconds, or null. */
|
|
56
|
+
export function newestObservedMilliseconds(snapshots) {
|
|
57
|
+
let newest = null;
|
|
58
|
+
for (const snapshot of snapshots) {
|
|
59
|
+
const observed = Date.parse(snapshot.observedAt);
|
|
60
|
+
if (!Number.isFinite(observed))
|
|
61
|
+
continue;
|
|
62
|
+
if (newest === null || observed > newest)
|
|
63
|
+
newest = observed;
|
|
64
|
+
}
|
|
65
|
+
return newest;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* How old the freshest row is, in seconds.
|
|
69
|
+
*
|
|
70
|
+
* An empty cache has no age, which the caller reads as "refresh": there is
|
|
71
|
+
* nothing to be stale, and nothing to show either.
|
|
72
|
+
*/
|
|
73
|
+
export function cacheAgeSeconds(snapshots, now) {
|
|
74
|
+
const current = Date.parse(now);
|
|
75
|
+
const newest = newestObservedMilliseconds(snapshots);
|
|
76
|
+
if (!Number.isFinite(current) || newest === null)
|
|
77
|
+
return null;
|
|
78
|
+
return (current - newest) / 1_000;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether a writer has kept this cache fresh inside the ownership window.
|
|
82
|
+
*
|
|
83
|
+
* Absent markers answer false, which is the backward compatible reading: a
|
|
84
|
+
* cache written before this field existed says nothing about who wrote it, and
|
|
85
|
+
* a reader that assumed a desktop was running would stop refreshing on a
|
|
86
|
+
* machine that has none.
|
|
87
|
+
*/
|
|
88
|
+
export function writerHoldsCache(snapshots, writer, now, withinSeconds = DESKTOP_OWNERSHIP_SECONDS) {
|
|
89
|
+
const current = Date.parse(now);
|
|
90
|
+
if (!Number.isFinite(current))
|
|
91
|
+
return false;
|
|
92
|
+
return snapshots.some((snapshot) => {
|
|
93
|
+
if (snapshot.writer !== writer)
|
|
94
|
+
return false;
|
|
95
|
+
const observed = Date.parse(snapshot.observedAt);
|
|
96
|
+
if (!Number.isFinite(observed))
|
|
97
|
+
return false;
|
|
98
|
+
/* A row stamped in the future is a clock disagreement, not a fresh write,
|
|
99
|
+
and treating it as ownership would silence this machine indefinitely. */
|
|
100
|
+
if (observed > current)
|
|
101
|
+
return false;
|
|
102
|
+
return current - observed <= withinSeconds * 1_000;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Whether a running desktop is already keeping this cache fresh. */
|
|
106
|
+
export function desktopHoldsCache(snapshots, now, withinSeconds = DESKTOP_OWNERSHIP_SECONDS) {
|
|
107
|
+
return writerHoldsCache(snapshots, "desktop", now, withinSeconds);
|
|
108
|
+
}
|
|
109
|
+
/** Stamp rows with the process that is about to write them. */
|
|
110
|
+
export function withWriter(snapshots, writer) {
|
|
111
|
+
return snapshots.map((snapshot) => ({ ...snapshot, writer }));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Whether this render should start a refresh behind itself.
|
|
115
|
+
*
|
|
116
|
+
* Three inputs and no side effects, so the whole policy is one testable
|
|
117
|
+
* function rather than a condition spread across two commands.
|
|
118
|
+
*/
|
|
119
|
+
export function shouldStartRefresh(snapshots, now, staleSeconds = CACHE_REFRESH_STALE_SECONDS) {
|
|
120
|
+
if (desktopHoldsCache(snapshots, now)) {
|
|
121
|
+
return { refresh: false, reason: "desktop_running" };
|
|
122
|
+
}
|
|
123
|
+
const age = cacheAgeSeconds(snapshots, now);
|
|
124
|
+
if (age !== null && age <= staleSeconds)
|
|
125
|
+
return { refresh: false, reason: "fresh" };
|
|
126
|
+
return { refresh: true };
|
|
127
|
+
}
|
|
128
|
+
/* ------------------------------------------------------------------ lock */
|
|
129
|
+
function lockPath(directory) {
|
|
130
|
+
return path.join(directory, REFRESH_LOCK_NAME);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Whether a refresh is running right now.
|
|
134
|
+
*
|
|
135
|
+
* A lock file older than the stale bound is not a running refresh, it is a
|
|
136
|
+
* process that died, and it is reported as free so the next render can take
|
|
137
|
+
* over. Nothing is deleted here: reclaiming belongs to the acquisition below,
|
|
138
|
+
* which does it under the same create attempt that would have failed.
|
|
139
|
+
*/
|
|
140
|
+
export async function refreshLockHeld(directory = resolveStateDirectory(), nowMilliseconds = Date.now()) {
|
|
141
|
+
try {
|
|
142
|
+
const observed = await lstat(lockPath(directory));
|
|
143
|
+
if (!Number.isFinite(observed.mtimeMs))
|
|
144
|
+
return true;
|
|
145
|
+
return nowMilliseconds - observed.mtimeMs < REFRESH_LOCK_STALE_MILLISECONDS;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (errorCode(error) === "ENOENT")
|
|
149
|
+
return false;
|
|
150
|
+
/* A lock we cannot even inspect is treated as held. Refusing to refresh is
|
|
151
|
+
recoverable; two refreshes racing on one credential is not. */
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Take the refresh lock, or say why not.
|
|
157
|
+
*
|
|
158
|
+
* Exclusive creation is the whole mechanism: the operating system decides the
|
|
159
|
+
* winner, so two terminals starting a refresh in the same millisecond produce
|
|
160
|
+
* exactly one round of requests. The token inside the file is checked before
|
|
161
|
+
* the release, so a process that lost its lock to the stale reclaim cannot
|
|
162
|
+
* delete the lock its successor is holding.
|
|
163
|
+
*/
|
|
164
|
+
export async function acquireRefreshLock(directory = resolveStateDirectory(), nowMilliseconds = Date.now()) {
|
|
165
|
+
const target = lockPath(directory);
|
|
166
|
+
const token = JSON.stringify({ at: nowMilliseconds, id: randomUUID(), pid: process.pid });
|
|
167
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
168
|
+
try {
|
|
169
|
+
const handle = await open(target, "wx", 0o600);
|
|
170
|
+
try {
|
|
171
|
+
await handle.writeFile(token, "utf8");
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
await handle.close().catch(() => undefined);
|
|
175
|
+
}
|
|
176
|
+
const owns = async () => {
|
|
177
|
+
try {
|
|
178
|
+
return (await readFile(target, "utf8")) === token;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
/*
|
|
185
|
+
* The heartbeat is unreferenced, so it can never hold the process open
|
|
186
|
+
* past the work it was watching, and it stops touching the file the
|
|
187
|
+
* moment this holder stops owning it.
|
|
188
|
+
*/
|
|
189
|
+
const beat = setInterval(() => {
|
|
190
|
+
void (async () => {
|
|
191
|
+
if (!(await owns()))
|
|
192
|
+
return;
|
|
193
|
+
const stamp = new Date();
|
|
194
|
+
await utimes(target, stamp, stamp).catch(() => undefined);
|
|
195
|
+
})();
|
|
196
|
+
}, REFRESH_LOCK_HEARTBEAT_MILLISECONDS);
|
|
197
|
+
beat.unref();
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
stillOwned: owns,
|
|
201
|
+
release: async () => {
|
|
202
|
+
clearInterval(beat);
|
|
203
|
+
if (!(await owns()))
|
|
204
|
+
return;
|
|
205
|
+
await unlink(target).catch(() => undefined);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const code = errorCode(error);
|
|
211
|
+
if (code !== "EEXIST" && code !== "EPERM" && code !== "EACCES") {
|
|
212
|
+
return { ok: false, reason: "unavailable" };
|
|
213
|
+
}
|
|
214
|
+
if (attempt > 0)
|
|
215
|
+
return { ok: false, reason: "held" };
|
|
216
|
+
if (await refreshLockHeld(directory, nowMilliseconds)) {
|
|
217
|
+
return { ok: false, reason: "held" };
|
|
218
|
+
}
|
|
219
|
+
await unlink(target).catch(() => undefined);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { ok: false, reason: "held" };
|
|
223
|
+
}
|
|
224
|
+
/* ------------------------------------------------------------ detachment */
|
|
225
|
+
/** The file that remembers a refresh this machine could not start. */
|
|
226
|
+
export const REFRESH_SPAWN_FAILURE_NAME = "openlimiter-refresh-spawn.json";
|
|
227
|
+
/**
|
|
228
|
+
* Remember that a background refresh could not be started.
|
|
229
|
+
*
|
|
230
|
+
* A detached spawn fails asynchronously: the executable is missing, or the
|
|
231
|
+
* platform refuses, and the error arrives after the status line has already
|
|
232
|
+
* drawn and returned. There is nobody left to tell at that point, so the fact
|
|
233
|
+
* is written down instead and doctor reads it back. Best effort throughout,
|
|
234
|
+
* because a machine that cannot start a refresh may well not be able to write
|
|
235
|
+
* this either, and neither failure may break a render.
|
|
236
|
+
*/
|
|
237
|
+
export async function recordRefreshSpawnFailure(directory, now) {
|
|
238
|
+
await writeFileAtomically(path.join(directory, REFRESH_SPAWN_FAILURE_NAME), canonicalJson({ at: now, version: 1 })).catch(() => undefined);
|
|
239
|
+
}
|
|
240
|
+
/** When the last background refresh failed to start, if one did. */
|
|
241
|
+
export async function readRefreshSpawnFailure(directory = resolveStateDirectory()) {
|
|
242
|
+
const document = await readJsonFileSafely(path.join(directory, REFRESH_SPAWN_FAILURE_NAME), 4_096);
|
|
243
|
+
if (!document.ok)
|
|
244
|
+
return null;
|
|
245
|
+
const value = document.value;
|
|
246
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
247
|
+
return null;
|
|
248
|
+
const at = value["at"];
|
|
249
|
+
return typeof at === "string" && Number.isFinite(Date.parse(at)) ? at : null;
|
|
250
|
+
}
|
|
251
|
+
/** Forget it, which a refresh that actually ran is the proof of. */
|
|
252
|
+
export async function clearRefreshSpawnFailure(directory = resolveStateDirectory()) {
|
|
253
|
+
await unlink(path.join(directory, REFRESH_SPAWN_FAILURE_NAME)).catch(() => undefined);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Start a refresh behind the caller, and never wait for it.
|
|
257
|
+
*
|
|
258
|
+
* Everything expensive happens in the child. This function creates one lock
|
|
259
|
+
* file and hands the operating system a detached process, so the render that
|
|
260
|
+
* called it returns in the time a file create takes. A failure to spawn is
|
|
261
|
+
* reported and never thrown: a status line that cannot start a refresh still
|
|
262
|
+
* has to draw the bars it already has.
|
|
263
|
+
*/
|
|
264
|
+
export async function spawnDetachedRefresh(options) {
|
|
265
|
+
const decision = shouldStartRefresh(options.snapshots, options.now, options.staleSeconds ?? CACHE_REFRESH_STALE_SECONDS);
|
|
266
|
+
if (!decision.refresh)
|
|
267
|
+
return { spawned: false, reason: decision.reason };
|
|
268
|
+
if (options.openLimiterScript === "")
|
|
269
|
+
return { spawned: false, reason: "no_script" };
|
|
270
|
+
const directory = options.stateDirectory ?? resolveStateDirectory();
|
|
271
|
+
/*
|
|
272
|
+
* The parent only checks the lock; the child takes it. Taking it here would
|
|
273
|
+
* mean releasing it here, and the parent exits long before the child has
|
|
274
|
+
* finished, which would leave every render starting one more refresh.
|
|
275
|
+
*/
|
|
276
|
+
if (await refreshLockHeld(directory)) {
|
|
277
|
+
return { spawned: false, reason: "already_running" };
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
options.spawn(options.nodeExecutable, [options.openLimiterScript, "refresh", "--detached"], {
|
|
281
|
+
onError: () => {
|
|
282
|
+
void recordRefreshSpawnFailure(directory, options.now);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return { spawned: true };
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
await recordRefreshSpawnFailure(directory, options.now);
|
|
289
|
+
return { spawned: false, reason: "failed" };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=coordination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination.js","sourceRoot":"","sources":["../../src/acquire/coordination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** The providers this path can read a credential for. */
|
|
2
|
+
export declare const ACQUISITION_PROVIDERS: readonly ["CLAUDE", "CODEX", "GEMINI_CLI", "ANTIGRAVITY", "GROK", "KIMI", "OPENROUTER"];
|
|
3
|
+
export type AcquisitionProvider = (typeof ACQUISITION_PROVIDERS)[number];
|
|
4
|
+
/** Largest credential document this path will read into memory. */
|
|
5
|
+
export declare const MAX_CREDENTIAL_FILE_BYTES = 65536;
|
|
6
|
+
/**
|
|
7
|
+
* Why no usable credential came back.
|
|
8
|
+
*
|
|
9
|
+
* `absent` is the ordinary answer on a machine that does not run that client.
|
|
10
|
+
* `expired` is separated from `invalid` because only one of them has an action
|
|
11
|
+
* a person can take, and the row says so.
|
|
12
|
+
*/
|
|
13
|
+
export type CredentialFailureReason = "absent" | "unreadable" | "invalid" | "expired" | "keychain_not_read";
|
|
14
|
+
/**
|
|
15
|
+
* Whose login this actually is.
|
|
16
|
+
*
|
|
17
|
+
* The one that matters is `shared_code_assist`. Antigravity keeps its token in
|
|
18
|
+
* the Windows credential store, and when that store holds nothing this reader
|
|
19
|
+
* falls back to the Gemini CLI's file, which is what the Antigravity client's
|
|
20
|
+
* own quota shares a backend with. That fallback is legitimate and it is what
|
|
21
|
+
* every other reader does, but the resulting row is NOT Antigravity's own
|
|
22
|
+
* login, and presenting it as one would tell a person they had connected
|
|
23
|
+
* something they never connected. So the origin travels with the credential and
|
|
24
|
+
* the row says which it is.
|
|
25
|
+
*/
|
|
26
|
+
export declare const CREDENTIAL_ORIGINS: readonly ["vendor_store", "vendor_file", "shared_code_assist", "user_key"];
|
|
27
|
+
export type CredentialOrigin = (typeof CREDENTIAL_ORIGINS)[number];
|
|
28
|
+
export interface AcquiredCredential {
|
|
29
|
+
/** The bearer secret. Never printed, never logged, never persisted by us. */
|
|
30
|
+
readonly secret: string;
|
|
31
|
+
/** The account the provider knows this credential by, when it states one. */
|
|
32
|
+
readonly accountId: string | null;
|
|
33
|
+
/** Expiry in epoch milliseconds, when the document states one. */
|
|
34
|
+
readonly expiresAtMilliseconds: number | null;
|
|
35
|
+
/** Where this credential came from, so a row can say whose login it is. */
|
|
36
|
+
readonly origin: CredentialOrigin;
|
|
37
|
+
}
|
|
38
|
+
export type CredentialResult = {
|
|
39
|
+
ok: true;
|
|
40
|
+
credential: AcquiredCredential;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
reason: CredentialFailureReason;
|
|
44
|
+
};
|
|
45
|
+
export interface CredentialLookupOptions {
|
|
46
|
+
readonly platform?: NodeJS.Platform;
|
|
47
|
+
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
48
|
+
readonly homeDirectory?: string;
|
|
49
|
+
/** Injected clock, so an expiry test does not depend on the wall clock. */
|
|
50
|
+
readonly now?: string;
|
|
51
|
+
/**
|
|
52
|
+
* How a Windows Credential Manager entry is read.
|
|
53
|
+
*
|
|
54
|
+
* Injected so the Antigravity path can be proved without a real credential
|
|
55
|
+
* on the machine, and so no test ever touches the live credential store.
|
|
56
|
+
*/
|
|
57
|
+
readonly readWindowsCredential?: WindowsCredentialReader;
|
|
58
|
+
}
|
|
59
|
+
export type WindowsCredentialReader = (target: string) => Promise<{
|
|
60
|
+
ok: true;
|
|
61
|
+
value: string;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
reason: CredentialFailureReason;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Every place a provider's own client is known to keep its credential, in the
|
|
68
|
+
* order the vendor's own environment variable would win.
|
|
69
|
+
*
|
|
70
|
+
* The first readable document that yields a usable secret wins. Order matters
|
|
71
|
+
* only for a machine carrying two of them, and in that case the explicit
|
|
72
|
+
* environment variable is the one the person set on purpose.
|
|
73
|
+
*/
|
|
74
|
+
export declare function credentialCandidatePaths(provider: AcquisitionProvider, options?: CredentialLookupOptions): string[];
|
|
75
|
+
/** The Windows Credential Manager target the Antigravity client writes to. */
|
|
76
|
+
export declare const ANTIGRAVITY_CREDENTIAL_TARGET = "gemini:antigravity";
|
|
77
|
+
/** The macOS keychain service Claude Code writes to, which this path skips. */
|
|
78
|
+
export declare const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
79
|
+
/** The issuer a Grok credential file should be read under when it names one. */
|
|
80
|
+
export declare const GROK_PREFERRED_ISSUER = "https://auth.x.ai";
|
|
81
|
+
/**
|
|
82
|
+
* An expiry, whatever unit the document happened to state it in.
|
|
83
|
+
*
|
|
84
|
+
* Seconds, milliseconds and RFC3339 all appear across these six clients. A
|
|
85
|
+
* value below the seconds ceiling is read as seconds, which is the same rule
|
|
86
|
+
* every reset parser in this product already uses.
|
|
87
|
+
*/
|
|
88
|
+
export declare function expiryMilliseconds(value: unknown): number | null;
|
|
89
|
+
/**
|
|
90
|
+
* Read one credential document that has already been parsed.
|
|
91
|
+
*
|
|
92
|
+
* Exported so a test can prove every shape without writing a file, and so the
|
|
93
|
+
* Windows Credential Manager path can reuse exactly the same reader for the
|
|
94
|
+
* envelope it gets back from the credential store.
|
|
95
|
+
*/
|
|
96
|
+
export declare function readCredentialDocument(provider: AcquisitionProvider, document: unknown, nowMilliseconds: number, origin?: CredentialOrigin): CredentialResult;
|
|
97
|
+
/**
|
|
98
|
+
* Read the credential a provider's own client stored, from disk.
|
|
99
|
+
*
|
|
100
|
+
* The result is one of four things and never a mixture: a usable credential, a
|
|
101
|
+
* provider that is simply not installed here, a document that could not be
|
|
102
|
+
* believed, or a login that has run out. Nothing is refreshed and nothing is
|
|
103
|
+
* written back.
|
|
104
|
+
*/
|
|
105
|
+
export declare function readAcquisitionCredential(provider: AcquisitionProvider, options?: CredentialLookupOptions): Promise<CredentialResult>;
|
|
106
|
+
/**
|
|
107
|
+
* One sentence per failure, for the row a person actually reads.
|
|
108
|
+
*
|
|
109
|
+
* No dashes anywhere, and every sentence names the next step rather than the
|
|
110
|
+
* internal reason, because a person cannot act on the word "invalid".
|
|
111
|
+
*/
|
|
112
|
+
export declare const CREDENTIAL_FAILURE_SENTENCE: Readonly<Record<CredentialFailureReason, string>>;
|
|
113
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/acquire/credentials.ts"],"names":[],"mappings":"AAuBA,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,yFAQxB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,mEAAmE;AACnE,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,SAAS,GACT,mBAAmB,CAAC;AAExB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,4EAKrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,kBAAkB,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,uBAAuB,CAAC;CAC1D;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,MAAM,KACX,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC,CAAC;AAoC3F;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,MAAM,EAAE,CAqDV;AAED,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAElE,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AA6DjE,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWhE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,OAAO,EACjB,eAAe,EAAE,MAAM,EACvB,MAAM,GAAE,gBAAgC,GACvC,gBAAgB,CA4ClB;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,CAsD3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,EACtC,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAOjD,CAAC"}
|