@claudexor/harness-agy 3.6.0 → 3.8.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/README.md +19 -5
- package/dist/index.d.ts +24 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +123 -44
- package/dist/index.js.map +1 -1
- package/dist/keychain.d.ts +41 -0
- package/dist/keychain.d.ts.map +1 -0
- package/dist/keychain.js +354 -0
- package/dist/keychain.js.map +1 -0
- package/dist/print-command.d.ts +64 -0
- package/dist/print-command.d.ts.map +1 -0
- package/dist/print-command.js +384 -0
- package/dist/print-command.js.map +1 -0
- package/dist/profile.d.ts +11 -18
- package/dist/profile.d.ts.map +1 -1
- package/dist/profile.js +53 -49
- package/dist/profile.js.map +1 -1
- package/dist/vendor-cli-version.d.ts +6 -3
- package/dist/vendor-cli-version.d.ts.map +1 -1
- package/dist/vendor-cli-version.js +6 -3
- package/dist/vendor-cli-version.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { extname, isAbsolute } from "node:path";
|
|
3
|
+
import { composeBaseEnv, defaultProcessGroupService, labelStreams, reapProcessTree, registerChildProcess, resolveHarnessBinary, unregisterChildProcess, } from "@claudexor/core";
|
|
4
|
+
import { redactSecrets } from "@claudexor/util";
|
|
5
|
+
export const AGY_PRINT_TIMEOUT_MS = 30_000;
|
|
6
|
+
export const AGY_PRINT_STREAM_LIMIT_BYTES = 256 * 1024;
|
|
7
|
+
const AGY_PRINT_DRAIN_MS = 200;
|
|
8
|
+
const AGY_PRINT_CANCEL_DEADLINE_MS = 5_000;
|
|
9
|
+
/** Spawn policy is exported so Windows fixtures can assert no inherited console/stdio. */
|
|
10
|
+
export function agyPrintSpawnOptions(platform, env) {
|
|
11
|
+
return {
|
|
12
|
+
env,
|
|
13
|
+
shell: false,
|
|
14
|
+
detached: true,
|
|
15
|
+
windowsHide: platform === "win32",
|
|
16
|
+
// On Windows CREATE_NO_WINDOW still exposes a windowless CONIN$. A real
|
|
17
|
+
// DETACHED_PROCESS prevents console inheritance; ignored stdin gives print
|
|
18
|
+
// mode immediate EOF without exposing an input descriptor.
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function composedEnv(patch) {
|
|
22
|
+
const env = composeBaseEnv("mirror_native");
|
|
23
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
24
|
+
if (value === null || value === undefined)
|
|
25
|
+
delete env[key];
|
|
26
|
+
else
|
|
27
|
+
env[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return env;
|
|
30
|
+
}
|
|
31
|
+
function exactBinary(bin, env, platform, resolver) {
|
|
32
|
+
const resolved = resolver
|
|
33
|
+
? resolver(bin, env, platform)
|
|
34
|
+
: resolveHarnessBinary(bin, env, process.execPath, platform);
|
|
35
|
+
if (!resolved || !isAbsolute(resolved))
|
|
36
|
+
return null;
|
|
37
|
+
if (platform === "win32" && ![".exe", ".com"].includes(extname(resolved).toLowerCase())) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
function boundedBuffer(maxBytes, overflow) {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
let bytes = 0;
|
|
45
|
+
let overflowed = false;
|
|
46
|
+
const markOverflow = () => {
|
|
47
|
+
if (overflowed)
|
|
48
|
+
return;
|
|
49
|
+
overflowed = true;
|
|
50
|
+
overflow();
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
push(chunk) {
|
|
54
|
+
if (chunk.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
// A prior chunk may have filled the cap exactly. The NEXT byte is still
|
|
57
|
+
// overflow and must cancel; silently returning here used to let an
|
|
58
|
+
// unbounded producer run forever. This also makes a zero-byte cap real.
|
|
59
|
+
if (bytes >= maxBytes) {
|
|
60
|
+
markOverflow();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const remaining = maxBytes - bytes;
|
|
64
|
+
if (chunk.length > remaining) {
|
|
65
|
+
if (remaining > 0)
|
|
66
|
+
chunks.push(chunk.subarray(0, remaining));
|
|
67
|
+
bytes = maxBytes;
|
|
68
|
+
markOverflow();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
chunks.push(chunk);
|
|
72
|
+
bytes += chunk.length;
|
|
73
|
+
},
|
|
74
|
+
text() {
|
|
75
|
+
return Buffer.concat(chunks, bytes).toString("utf8");
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function delay(ms) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
const timer = setTimeout(resolve, ms);
|
|
82
|
+
timer.unref();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Bounded print-mode owner for `/model` and `/quota`. The child has no stdin
|
|
87
|
+
* and no controlling terminal: POSIX starts a new session; Windows uses
|
|
88
|
+
* DETACHED_PROCESS because CREATE_NO_WINDOW still exposes a windowless
|
|
89
|
+
* CONIN$. Child exit is completion authority; descendant-held pipes receive
|
|
90
|
+
* only a bounded drain.
|
|
91
|
+
*/
|
|
92
|
+
export async function runAgyPrintCommand(bin, command, envPatch, options = {}) {
|
|
93
|
+
const platform = options.platform ?? process.platform;
|
|
94
|
+
const env = composedEnv(envPatch);
|
|
95
|
+
const resolved = exactBinary(bin, env, platform, options.resolveBinary);
|
|
96
|
+
if (!resolved) {
|
|
97
|
+
return {
|
|
98
|
+
kind: "failed",
|
|
99
|
+
reason: "spawn_failed",
|
|
100
|
+
detail: `agy executable could not be resolved to an exact ${platform === "win32" ? "Windows image" : "regular executable"}`,
|
|
101
|
+
stdout: "",
|
|
102
|
+
stderr: "",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (options.abortSignal?.aborted) {
|
|
106
|
+
return {
|
|
107
|
+
kind: "failed",
|
|
108
|
+
reason: "aborted",
|
|
109
|
+
detail: "agy print probe was aborted",
|
|
110
|
+
stdout: "",
|
|
111
|
+
stderr: "",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
let child;
|
|
115
|
+
try {
|
|
116
|
+
child = (options.spawnProcess ?? spawn)(resolved, ["-p", command, "--output-format", "json"], {
|
|
117
|
+
...agyPrintSpawnOptions(platform, env),
|
|
118
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
kind: "failed",
|
|
124
|
+
reason: "spawn_failed",
|
|
125
|
+
detail: redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300),
|
|
126
|
+
stdout: "",
|
|
127
|
+
stderr: "",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const pid = child.pid;
|
|
131
|
+
const processRegistry = options.processRegistry ?? {
|
|
132
|
+
register: registerChildProcess,
|
|
133
|
+
unregister: unregisterChildProcess,
|
|
134
|
+
};
|
|
135
|
+
let registered = false;
|
|
136
|
+
if (typeof pid === "number") {
|
|
137
|
+
processRegistry.register(pid, resolved);
|
|
138
|
+
registered = true;
|
|
139
|
+
}
|
|
140
|
+
const unregisterExitedChild = () => {
|
|
141
|
+
if (!registered || typeof pid !== "number")
|
|
142
|
+
return;
|
|
143
|
+
registered = false;
|
|
144
|
+
processRegistry.unregister(pid);
|
|
145
|
+
};
|
|
146
|
+
let group;
|
|
147
|
+
if (platform !== "win32" && typeof pid === "number") {
|
|
148
|
+
const captured = defaultProcessGroupService.captureLeader(pid);
|
|
149
|
+
if (captured.status === "known")
|
|
150
|
+
group = captured.handle;
|
|
151
|
+
}
|
|
152
|
+
let requested = null;
|
|
153
|
+
let reap = null;
|
|
154
|
+
let cancellationDeadlineTimer;
|
|
155
|
+
let resolveCancellationDeadline;
|
|
156
|
+
const cancellationDeadline = new Promise((resolve) => {
|
|
157
|
+
resolveCancellationDeadline = resolve;
|
|
158
|
+
});
|
|
159
|
+
const requestCancel = (why) => {
|
|
160
|
+
requested ??= why;
|
|
161
|
+
if (!cancellationDeadlineTimer) {
|
|
162
|
+
// Relative to the FIRST cancellation trigger, not the original process
|
|
163
|
+
// timeout. Abort/overflow at t=0 must settle after one cancel deadline,
|
|
164
|
+
// never after timeout+deadline.
|
|
165
|
+
cancellationDeadlineTimer = setTimeout(() => resolveCancellationDeadline({ kind: "unconfirmed_wait" }), options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS);
|
|
166
|
+
}
|
|
167
|
+
if (reap || typeof pid !== "number") {
|
|
168
|
+
try {
|
|
169
|
+
child.kill("SIGKILL");
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
/* already gone */
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
reap = (options.reap ?? reapProcessTree)({
|
|
177
|
+
rootPid: pid,
|
|
178
|
+
cooperativeSignal: "SIGKILL",
|
|
179
|
+
graceMs: 0,
|
|
180
|
+
deadlineMs: options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS,
|
|
181
|
+
...(group ? { seedHandles: [group], rootIdentity: group.leader } : {}),
|
|
182
|
+
platform,
|
|
183
|
+
});
|
|
184
|
+
try {
|
|
185
|
+
child.kill("SIGKILL");
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* the tree reaper owns the bounded proof */
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const stdout = boundedBuffer(options.maxStdoutBytes ?? AGY_PRINT_STREAM_LIMIT_BYTES, () => requestCancel("overflow"));
|
|
192
|
+
const stderr = boundedBuffer(options.maxStderrBytes ?? AGY_PRINT_STREAM_LIMIT_BYTES, () => requestCancel("overflow"));
|
|
193
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
194
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
195
|
+
let stdoutEnded = false;
|
|
196
|
+
let stderrEnded = false;
|
|
197
|
+
const stdoutEnd = new Promise((resolve) => child.stdout.once("end", () => {
|
|
198
|
+
stdoutEnded = true;
|
|
199
|
+
resolve();
|
|
200
|
+
}));
|
|
201
|
+
const stderrEnd = new Promise((resolve) => child.stderr.once("end", () => {
|
|
202
|
+
stderrEnded = true;
|
|
203
|
+
resolve();
|
|
204
|
+
}));
|
|
205
|
+
const terminal = new Promise((resolve) => {
|
|
206
|
+
let settled = false;
|
|
207
|
+
const settle = (value) => {
|
|
208
|
+
if (settled)
|
|
209
|
+
return;
|
|
210
|
+
settled = true;
|
|
211
|
+
resolve(value);
|
|
212
|
+
};
|
|
213
|
+
child.once("error", (error) => {
|
|
214
|
+
unregisterExitedChild();
|
|
215
|
+
settle({ kind: "error", error });
|
|
216
|
+
});
|
|
217
|
+
child.once("exit", (code, signal) => {
|
|
218
|
+
unregisterExitedChild();
|
|
219
|
+
settle({ kind: "exit", code, signal });
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
const timeout = setTimeout(() => requestCancel("timeout"), options.timeoutMs ?? AGY_PRINT_TIMEOUT_MS);
|
|
223
|
+
timeout.unref();
|
|
224
|
+
const onAbort = () => requestCancel("abort");
|
|
225
|
+
options.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
226
|
+
// Abort can race the synchronous spawn/setup window after the pre-spawn
|
|
227
|
+
// check but before listener registration. AbortSignal does not replay that
|
|
228
|
+
// event, so sample it again after the listener is armed.
|
|
229
|
+
if (options.abortSignal?.aborted)
|
|
230
|
+
requestCancel("abort");
|
|
231
|
+
const outcome = await Promise.race([terminal, cancellationDeadline]);
|
|
232
|
+
clearTimeout(timeout);
|
|
233
|
+
if (cancellationDeadlineTimer)
|
|
234
|
+
clearTimeout(cancellationDeadlineTimer);
|
|
235
|
+
options.abortSignal?.removeEventListener("abort", onAbort);
|
|
236
|
+
// Do not await descendant-owned EOF indefinitely. Give bytes already in the
|
|
237
|
+
// pipes a small drain window, then detach the readers.
|
|
238
|
+
await Promise.race([
|
|
239
|
+
Promise.all([stdoutEnd, stderrEnd]).then(() => undefined),
|
|
240
|
+
delay(options.drainMs ?? AGY_PRINT_DRAIN_MS),
|
|
241
|
+
]);
|
|
242
|
+
if (!stdoutEnded)
|
|
243
|
+
child.stdout.destroy();
|
|
244
|
+
if (!stderrEnded)
|
|
245
|
+
child.stderr.destroy();
|
|
246
|
+
let termination = null;
|
|
247
|
+
if (reap) {
|
|
248
|
+
try {
|
|
249
|
+
termination = await Promise.race([
|
|
250
|
+
reap,
|
|
251
|
+
delay(options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS).then(() => ({
|
|
252
|
+
state: "unconfirmed",
|
|
253
|
+
survivors: typeof pid === "number" ? [pid] : [],
|
|
254
|
+
unresolved: [],
|
|
255
|
+
})),
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
termination = {
|
|
260
|
+
state: "unconfirmed",
|
|
261
|
+
survivors: typeof pid === "number" ? [pid] : [],
|
|
262
|
+
unresolved: [],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const capturedOut = stdout.text();
|
|
267
|
+
const capturedErr = stderr.text();
|
|
268
|
+
const streamDetail = labelStreams(capturedErr, capturedOut, {
|
|
269
|
+
maxLen: 300,
|
|
270
|
+
transform: redactSecrets,
|
|
271
|
+
});
|
|
272
|
+
if (outcome.kind === "error") {
|
|
273
|
+
return {
|
|
274
|
+
kind: "failed",
|
|
275
|
+
reason: "spawn_failed",
|
|
276
|
+
detail: redactSecrets(outcome.error.message).slice(0, 300),
|
|
277
|
+
stdout: capturedOut,
|
|
278
|
+
stderr: capturedErr,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (outcome.kind === "unconfirmed_wait" || termination?.state === "unconfirmed") {
|
|
282
|
+
return {
|
|
283
|
+
kind: "failed",
|
|
284
|
+
reason: "termination_unconfirmed",
|
|
285
|
+
detail: streamDetail ?? "agy print probe termination could not be confirmed",
|
|
286
|
+
stdout: capturedOut,
|
|
287
|
+
stderr: capturedErr,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (requested) {
|
|
291
|
+
const reason = requested === "timeout" ? "timed_out" : requested === "abort" ? "aborted" : "output_overflow";
|
|
292
|
+
return {
|
|
293
|
+
kind: "failed",
|
|
294
|
+
reason,
|
|
295
|
+
detail: streamDetail ??
|
|
296
|
+
(reason === "timed_out"
|
|
297
|
+
? `agy did not answer ${command} within ${(options.timeoutMs ?? AGY_PRINT_TIMEOUT_MS) / 1000}s`
|
|
298
|
+
: reason === "aborted"
|
|
299
|
+
? "agy print probe was aborted"
|
|
300
|
+
: "agy print output exceeded its bounded stream limit"),
|
|
301
|
+
stdout: capturedOut,
|
|
302
|
+
stderr: capturedErr,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
kind: "completed",
|
|
307
|
+
code: outcome.code,
|
|
308
|
+
signal: outcome.signal,
|
|
309
|
+
stdout: capturedOut,
|
|
310
|
+
stderr: capturedErr,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const RECOGNIZED_AUTH_REJECTIONS = [
|
|
314
|
+
"authentication required",
|
|
315
|
+
"authentication failed or timed out",
|
|
316
|
+
"not authenticated",
|
|
317
|
+
"login required",
|
|
318
|
+
"credential revoked",
|
|
319
|
+
"credentials revoked",
|
|
320
|
+
"token revoked",
|
|
321
|
+
"token expired",
|
|
322
|
+
];
|
|
323
|
+
function vendorError(envelope) {
|
|
324
|
+
const error = envelope["error"];
|
|
325
|
+
if (typeof error === "string" && error.trim())
|
|
326
|
+
return error.trim();
|
|
327
|
+
if (error && typeof error === "object" && !Array.isArray(error)) {
|
|
328
|
+
const message = error["message"];
|
|
329
|
+
if (typeof message === "string" && message.trim())
|
|
330
|
+
return message.trim();
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
function recognizedAuthRejection(detail) {
|
|
335
|
+
const normalized = detail.trim().toLowerCase().replaceAll(/\s+/g, " ");
|
|
336
|
+
return RECOGNIZED_AUTH_REJECTIONS.some((prefix) => normalized === prefix ||
|
|
337
|
+
normalized.startsWith(`${prefix}.`) ||
|
|
338
|
+
normalized.startsWith(`${prefix}:`) ||
|
|
339
|
+
normalized.startsWith(`${prefix} `));
|
|
340
|
+
}
|
|
341
|
+
/** Shared exit/envelope/auth classifier for doctor and quota. */
|
|
342
|
+
export function classifyAgyPrintResult(result) {
|
|
343
|
+
if (result.kind === "failed") {
|
|
344
|
+
return { kind: "probe_failed", detail: redactSecrets(result.detail).slice(0, 300) };
|
|
345
|
+
}
|
|
346
|
+
const streams = labelStreams(result.stderr, result.stdout, {
|
|
347
|
+
maxLen: 300,
|
|
348
|
+
transform: redactSecrets,
|
|
349
|
+
});
|
|
350
|
+
if (result.signal !== null) {
|
|
351
|
+
return {
|
|
352
|
+
kind: "probe_failed",
|
|
353
|
+
detail: `agy exited by signal ${result.signal}${streams ? ` (${streams})` : ""}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (!result.stdout.trim()) {
|
|
357
|
+
return { kind: "probe_failed", detail: streams ?? "agy returned empty stdout" };
|
|
358
|
+
}
|
|
359
|
+
let envelope;
|
|
360
|
+
try {
|
|
361
|
+
envelope = JSON.parse(result.stdout);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return { kind: "probe_failed", detail: streams ?? "agy output was not valid JSON" };
|
|
365
|
+
}
|
|
366
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
367
|
+
return { kind: "probe_failed", detail: "agy output was not a JSON object" };
|
|
368
|
+
}
|
|
369
|
+
const record = envelope;
|
|
370
|
+
const error = vendorError(record);
|
|
371
|
+
if (record["status"] === "ERROR" && error && recognizedAuthRejection(error)) {
|
|
372
|
+
return { kind: "unauthenticated", detail: redactSecrets(error).slice(0, 300) };
|
|
373
|
+
}
|
|
374
|
+
if (result.code !== 0 || record["status"] !== "SUCCESS") {
|
|
375
|
+
return {
|
|
376
|
+
kind: "probe_failed",
|
|
377
|
+
detail: (error ? redactSecrets(error).slice(0, 300) : null) ??
|
|
378
|
+
streams ??
|
|
379
|
+
`agy returned unsupported status/exit combination (status ${String(record["status"])}; exit ${String(result.code)})`,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return { kind: "success", envelope: record };
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=print-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print-command.js","sourceRoot":"","sources":["../src/print-command.ts"],"names":[],"mappings":"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"}
|
package/dist/profile.d.ts
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import type { CredentialProfile, CredentialProfileStatus } from "@claudexor/schema";
|
|
2
2
|
type EnvMap = Record<string, string | null | undefined>;
|
|
3
3
|
export declare const AGY_BIN: () => string;
|
|
4
|
-
/** Canonical, Claudexor-owned HOME for one named Antigravity
|
|
4
|
+
/** Canonical, Claudexor-owned HOME for one named Antigravity binding. */
|
|
5
5
|
export declare function canonicalAgyProfileHome(locator: string): string;
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* login keychain in `$HOME/Library/Keychains` → `falling back to file`); the
|
|
10
|
-
* profile mechanism therefore REQUIRES that no keychain ever be created
|
|
11
|
-
* inside the profile HOME. Live-proven on three separate Google accounts
|
|
12
|
-
* (PLAN §1.2a-1.2c); re-proven per AGY_VENDOR_CLI_VERSION bump because the
|
|
13
|
-
* fallback is a vendor error path, not a documented mode (R-2').
|
|
14
|
-
*/
|
|
6
|
+
/** Diagnostic-only path for stale-file precedence fixtures. Credential
|
|
7
|
+
* readiness and routing never infer auth from this file: the effective
|
|
8
|
+
* transport is platform/version dependent and only the vendor probe proves it. */
|
|
15
9
|
export declare function agyTokenPath(profileHome: string): string;
|
|
16
|
-
/** The token must be a regular FILE: a directory at that path is a malformed
|
|
17
|
-
* profile, not a login, and must refuse rather than route (Ф0 review #11). */
|
|
18
|
-
export declare function agyTokenFilePresent(profileHome: string): boolean;
|
|
19
10
|
/**
|
|
20
11
|
* Exact strict-profile env: the profile HOME selects the vendor's whole
|
|
21
12
|
* config root (`$HOME/.gemini/...`) — agy has no config-dir env var, HOME is
|
|
@@ -46,15 +37,17 @@ export type AgyResolvedProfileRoute = {
|
|
|
46
37
|
refusal: string;
|
|
47
38
|
};
|
|
48
39
|
/**
|
|
49
|
-
* INV-135
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
40
|
+
* INV-135 route construction only: validate the named binding and build the
|
|
41
|
+
* exact scoped environment. Credential evidence belongs exclusively to the
|
|
42
|
+
* vendor probe; this resolver never treats a fallback token file (or its
|
|
43
|
+
* absence) as an auth oracle.
|
|
53
44
|
*/
|
|
54
45
|
export declare function resolveAgyProfileRoute(profile: Pick<CredentialProfile, "profile_id" | "credential_kind" | "isolation_locator">, specEnv?: EnvMap): AgyResolvedProfileRoute;
|
|
55
46
|
export interface AgyProfileProbeDeps {
|
|
56
47
|
/** Injectable live probe (tests): print-mode `/model` under the profile env. */
|
|
57
48
|
runModelProbe: (env: EnvMap, abortSignal?: AbortSignal) => Promise<AgyModelProbe>;
|
|
49
|
+
/** Production adapter seam: prepare the profile before the vendor probe. */
|
|
50
|
+
prepareProfileKeychain?: (home: string) => void;
|
|
58
51
|
}
|
|
59
52
|
export type AgyModelProbe = {
|
|
60
53
|
kind: "authenticated";
|
|
@@ -69,7 +62,7 @@ export type AgyModelProbe = {
|
|
|
69
62
|
/**
|
|
70
63
|
* Quota-free liveness probe: `agy -p "/model" --output-format json` answers
|
|
71
64
|
* from the CLI without spending a turn (vendor 1.1.11+ print-mode
|
|
72
|
-
* slash-commands). SUCCESS proves the
|
|
65
|
+
* slash-commands). SUCCESS proves the named binding authenticates in the
|
|
73
66
|
* exact env its runs will spawn with (INV-067); an auth error is an honest
|
|
74
67
|
* logged-out/revoked verdict, and a spawn/parse failure stays `unknown`.
|
|
75
68
|
*/
|
package/dist/profile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAMpF,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExD,eAAO,MAAM,OAAO,cAA+C,CAAC;AAEpE,yEAAyE;AACzE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;kFAEkF;AAClF,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,EACxF,OAAO,GAAE,MAAW,GACnB,uBAAuB,CAWzB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAClF,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,iBAAiB,EAC1B,IAAI,GAAE,mBAA6D,EACnE,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAyDlC"}
|
package/dist/profile.js
CHANGED
|
@@ -1,35 +1,20 @@
|
|
|
1
|
-
import { statSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
|
-
import { canonicalIsolationLocator, providerScrubEnv
|
|
2
|
+
import { canonicalIsolationLocator, providerScrubEnv } from "@claudexor/core";
|
|
4
3
|
import { CredentialProfileStatus as CredentialProfileStatusSchema } from "@claudexor/schema";
|
|
5
4
|
import { nowIso, redactSecrets } from "@claudexor/util";
|
|
5
|
+
import { isAgyProfileKeychainUnsafe } from "./keychain.js";
|
|
6
|
+
import { classifyAgyPrintResult, runAgyPrintCommand } from "./print-command.js";
|
|
6
7
|
export const AGY_BIN = () => process.env.CLAUDEXOR_AGY_BIN || "agy";
|
|
7
|
-
/** Canonical, Claudexor-owned HOME for one named Antigravity
|
|
8
|
+
/** Canonical, Claudexor-owned HOME for one named Antigravity binding. */
|
|
8
9
|
export function canonicalAgyProfileHome(locator) {
|
|
9
10
|
return canonicalIsolationLocator(locator, "credential profile Antigravity HOME");
|
|
10
11
|
}
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* login keychain in `$HOME/Library/Keychains` → `falling back to file`); the
|
|
15
|
-
* profile mechanism therefore REQUIRES that no keychain ever be created
|
|
16
|
-
* inside the profile HOME. Live-proven on three separate Google accounts
|
|
17
|
-
* (PLAN §1.2a-1.2c); re-proven per AGY_VENDOR_CLI_VERSION bump because the
|
|
18
|
-
* fallback is a vendor error path, not a documented mode (R-2').
|
|
19
|
-
*/
|
|
12
|
+
/** Diagnostic-only path for stale-file precedence fixtures. Credential
|
|
13
|
+
* readiness and routing never infer auth from this file: the effective
|
|
14
|
+
* transport is platform/version dependent and only the vendor probe proves it. */
|
|
20
15
|
export function agyTokenPath(profileHome) {
|
|
21
16
|
return join(profileHome, ".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
22
17
|
}
|
|
23
|
-
/** The token must be a regular FILE: a directory at that path is a malformed
|
|
24
|
-
* profile, not a login, and must refuse rather than route (Ф0 review #11). */
|
|
25
|
-
export function agyTokenFilePresent(profileHome) {
|
|
26
|
-
try {
|
|
27
|
-
return statSync(agyTokenPath(profileHome)).isFile();
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
18
|
/**
|
|
34
19
|
* Exact strict-profile env: the profile HOME selects the vendor's whole
|
|
35
20
|
* config root (`$HOME/.gemini/...`) — agy has no config-dir env var, HOME is
|
|
@@ -63,10 +48,10 @@ export function agyProfileRunEnv(profileHome, specEnv = {}) {
|
|
|
63
48
|
};
|
|
64
49
|
}
|
|
65
50
|
/**
|
|
66
|
-
* INV-135
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
51
|
+
* INV-135 route construction only: validate the named binding and build the
|
|
52
|
+
* exact scoped environment. Credential evidence belongs exclusively to the
|
|
53
|
+
* vendor probe; this resolver never treats a fallback token file (or its
|
|
54
|
+
* absence) as an auth oracle.
|
|
70
55
|
*/
|
|
71
56
|
export function resolveAgyProfileRoute(profile, specEnv = {}) {
|
|
72
57
|
if (profile.credential_kind !== "config_dir_login")
|
|
@@ -75,10 +60,6 @@ export function resolveAgyProfileRoute(profile, specEnv = {}) {
|
|
|
75
60
|
};
|
|
76
61
|
try {
|
|
77
62
|
const home = canonicalAgyProfileHome(profile.isolation_locator ?? "");
|
|
78
|
-
if (!agyTokenFilePresent(home))
|
|
79
|
-
return {
|
|
80
|
-
refusal: `credential profile "${profile.profile_id}" has no Antigravity login in its profile HOME (run \`claudexor profiles login agy ${profile.profile_id}\` first)`,
|
|
81
|
-
};
|
|
82
63
|
return { home, env: agyProfileRunEnv(home, specEnv) };
|
|
83
64
|
}
|
|
84
65
|
catch (err) {
|
|
@@ -88,28 +69,28 @@ export function resolveAgyProfileRoute(profile, specEnv = {}) {
|
|
|
88
69
|
/**
|
|
89
70
|
* Quota-free liveness probe: `agy -p "/model" --output-format json` answers
|
|
90
71
|
* from the CLI without spending a turn (vendor 1.1.11+ print-mode
|
|
91
|
-
* slash-commands). SUCCESS proves the
|
|
72
|
+
* slash-commands). SUCCESS proves the named binding authenticates in the
|
|
92
73
|
* exact env its runs will spawn with (INV-067); an auth error is an honest
|
|
93
74
|
* logged-out/revoked verdict, and a spawn/parse failure stays `unknown`.
|
|
94
75
|
*/
|
|
95
76
|
export async function defaultAgyModelProbe(env, abortSignal) {
|
|
96
77
|
try {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
kind: "
|
|
111
|
-
|
|
112
|
-
};
|
|
78
|
+
const classified = classifyAgyPrintResult(await runAgyPrintCommand(AGY_BIN(), "/model", env, { abortSignal }));
|
|
79
|
+
if (classified.kind === "unauthenticated")
|
|
80
|
+
return classified;
|
|
81
|
+
if (classified.kind === "probe_failed")
|
|
82
|
+
return classified;
|
|
83
|
+
const command = classified.envelope["command"];
|
|
84
|
+
const data = command && typeof command === "object" && !Array.isArray(command)
|
|
85
|
+
? command["data"]
|
|
86
|
+
: null;
|
|
87
|
+
const id = data && typeof data === "object" && !Array.isArray(data)
|
|
88
|
+
? data["id"]
|
|
89
|
+
: null;
|
|
90
|
+
if (typeof id !== "string" || !id.trim()) {
|
|
91
|
+
return { kind: "probe_failed", detail: "agy model envelope carried no model id" };
|
|
92
|
+
}
|
|
93
|
+
return { kind: "authenticated", modelId: id };
|
|
113
94
|
}
|
|
114
95
|
catch (err) {
|
|
115
96
|
return {
|
|
@@ -134,13 +115,35 @@ export async function probeAgyCredentialProfile(profile, deps = { runModelProbe:
|
|
|
134
115
|
verification: "not_run",
|
|
135
116
|
detail: route.refusal,
|
|
136
117
|
});
|
|
118
|
+
let keychainSetupWarning = null;
|
|
119
|
+
try {
|
|
120
|
+
deps.prepareProfileKeychain?.(route.home);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (isAgyProfileKeychainUnsafe(error)) {
|
|
124
|
+
return CredentialProfileStatusSchema.parse({
|
|
125
|
+
...base,
|
|
126
|
+
availability: "unavailable",
|
|
127
|
+
verification: "not_run",
|
|
128
|
+
detail: redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// A custom operational seam may model a recoverable security-tool miss.
|
|
132
|
+
// Keep the vendor file fallback and disclose the degraded container on
|
|
133
|
+
// the profile status rather than presenting a completely clean proof.
|
|
134
|
+
keychainSetupWarning = redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300);
|
|
135
|
+
}
|
|
137
136
|
const probe = await deps.runModelProbe(route.env, abortSignal);
|
|
138
137
|
if (probe.kind === "authenticated")
|
|
139
138
|
return CredentialProfileStatusSchema.parse({
|
|
140
139
|
...base,
|
|
141
140
|
availability: "available",
|
|
142
141
|
verification: "passed",
|
|
143
|
-
|
|
142
|
+
verification_source: "vendor",
|
|
143
|
+
detail: `Antigravity accepted the named binding${probe.modelId ? ` (model ${probe.modelId})` : ""}; whether the credential is backed by a keyring or file is not inferred` +
|
|
144
|
+
(keychainSetupWarning
|
|
145
|
+
? `; private profile keychain setup degraded: ${keychainSetupWarning}`
|
|
146
|
+
: ""),
|
|
144
147
|
last_verified_at: nowIso(),
|
|
145
148
|
});
|
|
146
149
|
if (probe.kind === "unauthenticated")
|
|
@@ -148,7 +151,8 @@ export async function probeAgyCredentialProfile(profile, deps = { runModelProbe:
|
|
|
148
151
|
...base,
|
|
149
152
|
availability: "unavailable",
|
|
150
153
|
verification: "failed",
|
|
151
|
-
|
|
154
|
+
verification_source: "vendor",
|
|
155
|
+
detail: `Antigravity rejected the named binding credential: ${probe.detail}`,
|
|
152
156
|
});
|
|
153
157
|
return CredentialProfileStatusSchema.parse({
|
|
154
158
|
...base,
|