@cortexkit/pi-antigravity-auth 3.0.5 → 3.0.7
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/commands.d.ts.map +1 -1
- package/dist/index.js +223 -145
- package/dist/index.js.map +3 -3
- package/dist/paths.d.ts +1 -13
- package/dist/paths.d.ts.map +1 -1
- package/dist/stream.d.ts +3 -3
- package/dist/stream.d.ts.map +1 -1
- package/dist/vault-setup.d.ts +24 -2
- package/dist/vault-setup.d.ts.map +1 -1
- package/dist/vault-source.d.ts +1 -1
- package/dist/vault-source.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { join
|
|
2
|
+
import { join } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
ANTIGRAVITY_PROVIDER_ID as ANTIGRAVITY_PROVIDER_ID2,
|
|
5
5
|
authorizeAntigravity,
|
|
@@ -26,7 +26,13 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
26
26
|
import { readFileSync } from "node:fs";
|
|
27
27
|
import {
|
|
28
28
|
ANTIGRAVITY_PROVIDER_ID,
|
|
29
|
-
|
|
29
|
+
emptyVaultRosterCause,
|
|
30
|
+
enrollmentAuthority,
|
|
31
|
+
sharedVaultTokenFolder,
|
|
32
|
+
sharedVaultTokenFolderOutcome,
|
|
33
|
+
sharedVaultTokenFolderText,
|
|
34
|
+
VAULT_RECLASSIFY_COMMAND,
|
|
35
|
+
VAULT_STATUS_COMMAND
|
|
30
36
|
} from "@cortexkit/antigravity-auth-core";
|
|
31
37
|
import { writeJsonAtomic } from "@cortexkit/antigravity-auth-core/atomic-write";
|
|
32
38
|
var PI_ANTIGRAVITY_PROVIDER_ID = ANTIGRAVITY_PROVIDER_ID;
|
|
@@ -62,16 +68,19 @@ function readPiVaultMode(path) {
|
|
|
62
68
|
} catch (error) {
|
|
63
69
|
if (error?.code === "ENOENT")
|
|
64
70
|
return { ok: true, record: LOCAL_MODE };
|
|
65
|
-
return { ok: false, reason: "
|
|
71
|
+
return { ok: false, reason: "its vault settings file could not be read" };
|
|
66
72
|
}
|
|
67
73
|
let raw;
|
|
68
74
|
try {
|
|
69
75
|
raw = JSON.parse(text);
|
|
70
76
|
} catch {
|
|
71
|
-
return { ok: false, reason: "
|
|
77
|
+
return { ok: false, reason: "its vault settings file is damaged" };
|
|
72
78
|
}
|
|
73
79
|
const record = parseModeRecord(raw);
|
|
74
|
-
return record ? { ok: true, record } : {
|
|
80
|
+
return record ? { ok: true, record } : {
|
|
81
|
+
ok: false,
|
|
82
|
+
reason: "its vault settings file holds settings it does not understand"
|
|
83
|
+
};
|
|
75
84
|
}
|
|
76
85
|
async function writePiVaultMode(path, record) {
|
|
77
86
|
const value = record.routeId === void 0 ? { version: 1, mode: record.mode } : { version: 1, mode: record.mode, routeId: record.routeId };
|
|
@@ -177,7 +186,7 @@ var PiVaultSetup = class {
|
|
|
177
186
|
);
|
|
178
187
|
if (enrollment.state !== "approved")
|
|
179
188
|
throw new PiVaultSetupError(
|
|
180
|
-
"Pi
|
|
189
|
+
"Pi doesn't have vault access yet. Choose Request vault access and wait for the vault operator to approve it."
|
|
181
190
|
);
|
|
182
191
|
if (routeId !== void 0 && routeId.trim() === "")
|
|
183
192
|
throw new PiVaultSetupError("Choose a vault account to use.");
|
|
@@ -194,7 +203,7 @@ var PiVaultSetup = class {
|
|
|
194
203
|
this.#options.claustrum
|
|
195
204
|
) !== "local") {
|
|
196
205
|
throw new PiVaultSetupError(
|
|
197
|
-
"Disconnect vault
|
|
206
|
+
"Disconnect from the vault before switching to Pi's own login."
|
|
198
207
|
);
|
|
199
208
|
}
|
|
200
209
|
await writePiVaultMode(this.#options.modeFile, LOCAL_MODE);
|
|
@@ -213,7 +222,7 @@ var PiVaultSetup = class {
|
|
|
213
222
|
);
|
|
214
223
|
if (result === "busy")
|
|
215
224
|
throw new PiVaultSetupError(
|
|
216
|
-
"
|
|
225
|
+
"Another vault setup step is running, so Pi is still connected to the vault. Try again in a moment."
|
|
217
226
|
);
|
|
218
227
|
await writePiVaultMode(this.#options.modeFile, LOCAL_MODE);
|
|
219
228
|
await this.#options.onModeChanged?.(LOCAL_MODE);
|
|
@@ -222,43 +231,69 @@ var PiVaultSetup = class {
|
|
|
222
231
|
function describeEnrollment(status) {
|
|
223
232
|
switch (status.state) {
|
|
224
233
|
case "idle":
|
|
225
|
-
return "not requested";
|
|
234
|
+
return "Vault access: not requested yet.";
|
|
226
235
|
case "pending":
|
|
227
|
-
return status.retryCode ? `
|
|
236
|
+
return status.retryCode ? `Vault access: the vault can't take Pi's request right now; Pi asks again the next time it checks (reference: ${status.retryCode}).` : "Vault access: waiting for the vault operator to approve this request.";
|
|
228
237
|
case "approved":
|
|
229
|
-
return
|
|
238
|
+
return "Vault access: approved by the vault operator.";
|
|
230
239
|
case "denied":
|
|
231
|
-
return "
|
|
240
|
+
return "Vault access: the vault operator denied the request. Choose Request vault access to ask again.";
|
|
232
241
|
case "blocked":
|
|
233
|
-
|
|
242
|
+
if (status.code === "pending_exists")
|
|
243
|
+
return "Vault access: the vault turned down a new request because an earlier request from Pi is still waiting.";
|
|
244
|
+
if (status.code === "missing_token")
|
|
245
|
+
return "Vault access: approved, but Pi's vault key is missing from this computer. Disconnect from the vault, then request vault access again.";
|
|
246
|
+
if (status.code === "unreadable-state")
|
|
247
|
+
return "Vault access: Pi can't read its record of the vault request. Disconnect from the vault, then request vault access again.";
|
|
248
|
+
return `Vault access: the vault ended this request (reference: ${status.code}). Choose Request vault access to start a new one.`;
|
|
234
249
|
case "unavailable":
|
|
235
|
-
return `vault
|
|
250
|
+
return `Vault access: Pi can't reach the vault right now. Try again later (reference: ${status.code}).`;
|
|
236
251
|
case "busy":
|
|
237
|
-
return "another setup step is running";
|
|
252
|
+
return "Vault access: another vault setup step is running. Try again in a moment.";
|
|
238
253
|
}
|
|
239
254
|
}
|
|
240
255
|
var PI_VAULT_GRANT_COMMAND = `ck auth grant --principal enrolled:${PI_ANTIGRAVITY_ENROLLMENT_NAME} --selector-kind category --selector antigravity-native --operation read`;
|
|
241
256
|
function piPendingEnrollmentGuidance(requestId) {
|
|
242
257
|
return [
|
|
243
258
|
"Next, the vault operator runs these outside Pi:",
|
|
244
|
-
requestId === void 0 ?
|
|
259
|
+
requestId === void 0 ? `1. Find the request from ${PI_ANTIGRAVITY_ENROLLMENT_NAME} with ck auth enroll list, then approve it: ck auth enroll approve --request-id <id>` : `1. Approve the request: ck auth enroll approve --request-id ${requestId}`,
|
|
245
260
|
`2. Grant read access: ${PI_VAULT_GRANT_COMMAND}`,
|
|
246
|
-
"Both are needed:
|
|
247
|
-
"3. Pi switches to the vault once it sees the approval
|
|
261
|
+
"Both are needed: without the grant, Pi can see no accounts in the vault.",
|
|
262
|
+
"3. Pi switches to the vault once it sees the approval. Choose Request vault access to check again."
|
|
248
263
|
];
|
|
249
264
|
}
|
|
250
265
|
function piPendingExistsGuidance() {
|
|
251
266
|
return [
|
|
252
|
-
|
|
267
|
+
"The earlier request may come from a vault setup that was disconnected here before the operator answered it.",
|
|
253
268
|
"It expires 15 minutes after it was made; request vault access again after that.",
|
|
254
|
-
|
|
269
|
+
`Or the vault operator clears it now: find the request from ${PI_ANTIGRAVITY_ENROLLMENT_NAME} with ck auth enroll list, run ck auth enroll deny --request-id <id>, then request vault access again here.`
|
|
255
270
|
];
|
|
256
271
|
}
|
|
257
|
-
function piNoVaultAccountsGuidance() {
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
272
|
+
function piNoVaultAccountsGuidance(reach) {
|
|
273
|
+
return piEmptyRosterGuidance(emptyVaultRosterCause(reach));
|
|
274
|
+
}
|
|
275
|
+
function piEmptyRosterGuidance(cause) {
|
|
276
|
+
switch (cause) {
|
|
277
|
+
case "no-grant":
|
|
278
|
+
return [
|
|
279
|
+
"The vault operator approved Pi. The vault has not granted Pi read access.",
|
|
280
|
+
`The vault operator grants it with: ${PI_VAULT_GRANT_COMMAND}`
|
|
281
|
+
];
|
|
282
|
+
case "grant-reaches-nothing":
|
|
283
|
+
return [
|
|
284
|
+
"The vault operator approved Pi. Pi has read access, but it reaches no Antigravity login in the vault.",
|
|
285
|
+
`The vault operator updates the vault's logins with: ${VAULT_RECLASSIFY_COMMAND}`,
|
|
286
|
+
`Then they check them with: ${VAULT_STATUS_COMMAND}`
|
|
287
|
+
];
|
|
288
|
+
case "unknown":
|
|
289
|
+
return [
|
|
290
|
+
"The vault operator approved Pi, but Pi can't see any Antigravity accounts in the vault yet.",
|
|
291
|
+
`If the operator has not granted Pi read access yet, they run: ${PI_VAULT_GRANT_COMMAND}`
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function piEmptyVaultRosterRefusal(cause) {
|
|
296
|
+
return cause === "unknown" ? void 0 : piEmptyRosterGuidance(cause).join("\n");
|
|
262
297
|
}
|
|
263
298
|
function enrollmentGuidance(status) {
|
|
264
299
|
if (status.state === "pending")
|
|
@@ -267,28 +302,20 @@ function enrollmentGuidance(status) {
|
|
|
267
302
|
return piPendingExistsGuidance();
|
|
268
303
|
return [];
|
|
269
304
|
}
|
|
270
|
-
function
|
|
271
|
-
switch (
|
|
272
|
-
case "
|
|
273
|
-
return "
|
|
274
|
-
case "
|
|
275
|
-
return "
|
|
276
|
-
case "
|
|
277
|
-
return "
|
|
305
|
+
function describeSource(status) {
|
|
306
|
+
switch (status.mode) {
|
|
307
|
+
case "custody":
|
|
308
|
+
return "Using the vault for Antigravity accounts; Pi's own login is set aside.";
|
|
309
|
+
case "local":
|
|
310
|
+
return status.hostSlot === "login" ? "Using Pi's own login (the account you signed in with /login), not the vault." : status.hostSlot === "dormant" ? "Using Pi's own login, not the vault." : "Using Pi's own login, not the vault, but you are not signed in. Run /login to sign in.";
|
|
311
|
+
case "unreadable":
|
|
312
|
+
return status.modeProblem === void 0 ? "Pi can't tell whether to use the vault or its own login, so it sends no Antigravity requests." : `Pi can't tell whether to use the vault or its own login (${status.modeProblem}), so it sends no Antigravity requests.`;
|
|
278
313
|
}
|
|
279
314
|
}
|
|
280
315
|
function formatPiVaultSetupStatus(status) {
|
|
281
|
-
const lines = [
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
status.hostSlot === "dormant" ? "Pi auth slot: dormant (not read)" : `Pi auth slot: ${describeHostSlot(status.hostSlot)}`
|
|
285
|
-
];
|
|
286
|
-
if (status.selectedRouteId !== void 0)
|
|
287
|
-
lines.push("Vault account: a specific account is chosen (marked below)");
|
|
288
|
-
if (status.mode === "local" && status.hostSlot === "placeholder")
|
|
289
|
-
lines.push(
|
|
290
|
-
"Conflict: local mode has no login to serve. Run /login or switch to the vault."
|
|
291
|
-
);
|
|
316
|
+
const lines = [describeSource(status), describeEnrollment(status.enrollment)];
|
|
317
|
+
if (status.mode === "custody" && status.selectedRouteId !== void 0)
|
|
318
|
+
lines.push('Pi uses the vault account marked "in use by Pi" below.');
|
|
292
319
|
lines.push(...enrollmentGuidance(status.enrollment));
|
|
293
320
|
return lines;
|
|
294
321
|
}
|
|
@@ -296,6 +323,9 @@ var ITEM_ID_SALT = randomBytes(16);
|
|
|
296
323
|
function vaultAccountName(listed, account) {
|
|
297
324
|
return `Vault account ${listed.indexOf(account) + 1}`;
|
|
298
325
|
}
|
|
326
|
+
var VAULT_ACCOUNT_IN_USE = "in use by Pi";
|
|
327
|
+
var VAULT_ACCOUNT_AVAILABLE = "available";
|
|
328
|
+
var VAULT_ACCOUNT_TURNED_OFF = "turned off for Pi on this computer";
|
|
299
329
|
function opaqueItemId(routeId) {
|
|
300
330
|
return `vault-${createHash("sha256").update(ITEM_ID_SALT).update(routeId).digest("hex").slice(0, 16)}`;
|
|
301
331
|
}
|
|
@@ -307,6 +337,11 @@ function refused(error) {
|
|
|
307
337
|
return { ok: false, text: error.message, code: "vault-refused" };
|
|
308
338
|
throw error;
|
|
309
339
|
}
|
|
340
|
+
function sharedFolderRefusal(error) {
|
|
341
|
+
const outcome = sharedVaultTokenFolderOutcome(error);
|
|
342
|
+
if (outcome) return outcome;
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
310
345
|
async function waitForApproval(setup, wait = {}) {
|
|
311
346
|
const sleep = wait.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
312
347
|
const interval = wait.pollIntervalMs ?? 2e3;
|
|
@@ -327,13 +362,13 @@ function createPiVaultSection(options) {
|
|
|
327
362
|
const port = options.accounts?.();
|
|
328
363
|
if (!port) return [];
|
|
329
364
|
try {
|
|
330
|
-
return (await port.list()).length === 0 ? piNoVaultAccountsGuidance() : [];
|
|
365
|
+
return (await port.list()).length === 0 ? piNoVaultAccountsGuidance(port.grantReach()) : [];
|
|
331
366
|
} catch {
|
|
332
367
|
return [];
|
|
333
368
|
}
|
|
334
369
|
};
|
|
335
370
|
const stepText = async (step, suffix = "") => [
|
|
336
|
-
|
|
371
|
+
`${describeEnrollment(step)}${suffix}`,
|
|
337
372
|
...enrollmentGuidance(step),
|
|
338
373
|
...step.state === "approved" ? await approvedRosterGuidance() : []
|
|
339
374
|
].join("\n");
|
|
@@ -356,48 +391,51 @@ function createPiVaultSection(options) {
|
|
|
356
391
|
items.push({
|
|
357
392
|
id: opaqueItemId(account.routeId),
|
|
358
393
|
label: name,
|
|
359
|
-
detail: account.declined ?
|
|
394
|
+
detail: account.declined ? VAULT_ACCOUNT_TURNED_OFF : serving ? VAULT_ACCOUNT_IN_USE : VAULT_ACCOUNT_AVAILABLE,
|
|
360
395
|
actions: account.declined ? [
|
|
361
396
|
{
|
|
362
397
|
id: "accept",
|
|
363
|
-
label: "
|
|
398
|
+
label: "Turn back on for Pi",
|
|
364
399
|
run: async () => {
|
|
365
400
|
await port.accept(account.routeId);
|
|
366
|
-
return
|
|
401
|
+
return `Pi can use ${name} again.`;
|
|
367
402
|
}
|
|
368
403
|
}
|
|
369
404
|
] : [
|
|
370
405
|
{
|
|
371
406
|
id: "use",
|
|
372
|
-
label: "
|
|
407
|
+
label: "Use this account",
|
|
373
408
|
run: async () => {
|
|
374
409
|
try {
|
|
375
410
|
await setup.useVault(account.routeId);
|
|
376
411
|
} catch (error) {
|
|
377
412
|
return refused(error);
|
|
378
413
|
}
|
|
379
|
-
return `Pi now
|
|
414
|
+
return `Pi now uses ${name} for Antigravity.`;
|
|
380
415
|
}
|
|
381
416
|
},
|
|
382
417
|
{
|
|
383
418
|
id: "decline",
|
|
384
|
-
label: "
|
|
385
|
-
description: "
|
|
419
|
+
label: "Turn off for Pi",
|
|
420
|
+
description: "The account stays in the vault and in this list, but Pi on this computer never uses it until you turn it back on.",
|
|
386
421
|
run: async () => {
|
|
387
422
|
await port.decline(account.routeId);
|
|
388
|
-
return
|
|
423
|
+
return `Pi on this computer won't use ${name} until you turn it back on.`;
|
|
389
424
|
}
|
|
390
425
|
}
|
|
391
426
|
]
|
|
392
427
|
});
|
|
393
428
|
}
|
|
394
|
-
if (listed.length === 0)
|
|
429
|
+
if (listed.length === 0)
|
|
430
|
+
lines.push(...piNoVaultAccountsGuidance(port.grantReach()));
|
|
395
431
|
else if (status.mode !== "custody")
|
|
396
432
|
lines.push(
|
|
397
|
-
"Next: choose
|
|
433
|
+
"Next: choose Use the vault to have Pi use these accounts."
|
|
398
434
|
);
|
|
399
435
|
} catch (error) {
|
|
400
|
-
lines.push(
|
|
436
|
+
lines.push(
|
|
437
|
+
`Pi can't load the vault accounts right now: ${errorText(error)}`
|
|
438
|
+
);
|
|
401
439
|
}
|
|
402
440
|
}
|
|
403
441
|
const actions = [];
|
|
@@ -405,9 +443,14 @@ function createPiVaultSection(options) {
|
|
|
405
443
|
actions.push({
|
|
406
444
|
id: "request-access",
|
|
407
445
|
label: "Request vault access",
|
|
408
|
-
description:
|
|
446
|
+
description: "Asks the vault operator to let Pi use the vault's Antigravity accounts.",
|
|
409
447
|
run: async ({ invocation }) => {
|
|
410
|
-
|
|
448
|
+
let step;
|
|
449
|
+
try {
|
|
450
|
+
step = await setup.connectStep();
|
|
451
|
+
} catch (error) {
|
|
452
|
+
return sharedFolderRefusal(error);
|
|
453
|
+
}
|
|
411
454
|
if (step.state !== "pending" && step.state !== "busy")
|
|
412
455
|
return {
|
|
413
456
|
ok: step.state === "approved",
|
|
@@ -422,8 +465,9 @@ function createPiVaultSection(options) {
|
|
|
422
465
|
final.state === "approved" ? "info" : "warning"
|
|
423
466
|
);
|
|
424
467
|
}).catch((error) => {
|
|
468
|
+
const folder = sharedVaultTokenFolder(error);
|
|
425
469
|
invocation.notify(
|
|
426
|
-
`Requesting vault access failed: ${errorText(error)}
|
|
470
|
+
folder === void 0 ? `Requesting vault access failed: ${errorText(error)}` : sharedVaultTokenFolderText(folder),
|
|
427
471
|
"error"
|
|
428
472
|
);
|
|
429
473
|
}).finally(() => {
|
|
@@ -434,7 +478,7 @@ function createPiVaultSection(options) {
|
|
|
434
478
|
ok: true,
|
|
435
479
|
text: await stepText(
|
|
436
480
|
step,
|
|
437
|
-
"
|
|
481
|
+
" Pi tells you here when the operator decides."
|
|
438
482
|
)
|
|
439
483
|
};
|
|
440
484
|
}
|
|
@@ -442,39 +486,39 @@ function createPiVaultSection(options) {
|
|
|
442
486
|
if (approved && status.mode !== "custody")
|
|
443
487
|
actions.push({
|
|
444
488
|
id: "use-vault",
|
|
445
|
-
label: "
|
|
489
|
+
label: "Use the vault",
|
|
446
490
|
run: async () => {
|
|
447
491
|
try {
|
|
448
492
|
await setup.useVault(status.selectedRouteId);
|
|
449
493
|
} catch (error) {
|
|
450
494
|
return refused(error);
|
|
451
495
|
}
|
|
452
|
-
return "Pi now
|
|
496
|
+
return "Pi now uses the vault for Antigravity.";
|
|
453
497
|
}
|
|
454
498
|
});
|
|
455
499
|
if (status.mode !== "local" && (status.enrollment.state === "idle" || status.enrollment.state === "pending" || status.enrollment.state === "denied"))
|
|
456
500
|
actions.push({
|
|
457
501
|
id: "use-local",
|
|
458
|
-
label: "
|
|
502
|
+
label: "Use Pi's own login",
|
|
459
503
|
run: async () => {
|
|
460
504
|
await setup.useLocal();
|
|
461
|
-
return "Pi now
|
|
505
|
+
return "Pi now uses its own login for Antigravity. Run /login if you haven't signed in.";
|
|
462
506
|
}
|
|
463
507
|
});
|
|
464
508
|
if (status.enrollment.state === "pending")
|
|
465
509
|
lines.push(
|
|
466
|
-
`
|
|
510
|
+
`You can't disconnect while this request waits for the vault operator. To withdraw it, ask the vault operator to run ck auth enroll deny --request-id ${status.enrollment.requestId ?? "<id>"}, then choose Request vault access here so Pi sees the answer.`
|
|
467
511
|
);
|
|
468
512
|
else if (status.enrollment.state !== "idle")
|
|
469
513
|
actions.push({
|
|
470
514
|
id: "forget",
|
|
471
|
-
label: "
|
|
472
|
-
description: "
|
|
515
|
+
label: "Disconnect from the vault",
|
|
516
|
+
description: "Removes Pi's vault key from this computer and goes back to Pi's own login. Using the vault here again needs a new request and a new approval. The earlier approval stays in the vault until the vault operator revokes it.",
|
|
473
517
|
irreversible: true,
|
|
474
|
-
confirm:
|
|
518
|
+
confirm: "Disconnect this computer from the vault? Pi goes back to its own login, and using the vault here again needs a new approval from the vault operator.",
|
|
475
519
|
run: async () => {
|
|
476
520
|
await setup.disconnect();
|
|
477
|
-
return "
|
|
521
|
+
return "Disconnected from the vault. Pi now uses its own login for Antigravity.";
|
|
478
522
|
}
|
|
479
523
|
});
|
|
480
524
|
return { lines, items, actions };
|
|
@@ -513,22 +557,25 @@ async function custodyAccounts(status, options) {
|
|
|
513
557
|
if (status.mode !== "custody" || status.enrollment.state !== "approved")
|
|
514
558
|
return { list: [] };
|
|
515
559
|
try {
|
|
516
|
-
|
|
560
|
+
const port = options.accounts();
|
|
561
|
+
const list = await port.list();
|
|
562
|
+
const reach = port.grantReach();
|
|
563
|
+
return { list, ...reach && { reach } };
|
|
517
564
|
} catch (error) {
|
|
518
565
|
return {
|
|
519
566
|
list: [],
|
|
520
|
-
problem: `
|
|
567
|
+
problem: `Pi can't load the vault accounts right now: ${error instanceof Error ? error.message : String(error)}`
|
|
521
568
|
};
|
|
522
569
|
}
|
|
523
570
|
}
|
|
524
571
|
function localLoginLine(status) {
|
|
525
572
|
switch (status.hostSlot) {
|
|
526
573
|
case "dormant":
|
|
527
|
-
return "
|
|
574
|
+
return "Pi's own login is set aside while Pi uses the vault.";
|
|
528
575
|
case "login":
|
|
529
576
|
return "Signed in with Pi's own login. Pi's /login replaces it and /logout removes it.";
|
|
530
577
|
case "placeholder":
|
|
531
|
-
return "
|
|
578
|
+
return "Not signed in with Pi's own login. Run /login, or choose Use the vault in the Vault section.";
|
|
532
579
|
case "empty":
|
|
533
580
|
return "Not signed in. Run Pi's /login to sign in.";
|
|
534
581
|
}
|
|
@@ -542,7 +589,7 @@ function createPiAntigravityAccountSections(options) {
|
|
|
542
589
|
if (status.mode === "unreadable")
|
|
543
590
|
return {
|
|
544
591
|
lines: [
|
|
545
|
-
`
|
|
592
|
+
status.modeProblem === void 0 ? "Pi can't tell whether to use the vault or its own login, so it sends no Antigravity requests. See the Vault section." : `Pi can't tell whether to use the vault or its own login (${status.modeProblem}), so it sends no Antigravity requests. Choose one in the Vault section.`
|
|
546
593
|
]
|
|
547
594
|
};
|
|
548
595
|
if (status.mode === "local")
|
|
@@ -552,23 +599,23 @@ function createPiAntigravityAccountSections(options) {
|
|
|
552
599
|
localLoginLine(status)
|
|
553
600
|
]
|
|
554
601
|
};
|
|
555
|
-
const { list, problem } = await custodyAccounts(status, options);
|
|
602
|
+
const { list, reach, problem } = await custodyAccounts(status, options);
|
|
556
603
|
const serving = servingAccount(status, list);
|
|
557
604
|
const items = list.map((account) => ({
|
|
558
605
|
id: opaqueItemId(account.routeId),
|
|
559
606
|
label: vaultAccountName(list, account),
|
|
560
|
-
detail: account.declined ?
|
|
607
|
+
detail: account.declined ? VAULT_ACCOUNT_TURNED_OFF : account === serving ? VAULT_ACCOUNT_IN_USE : VAULT_ACCOUNT_AVAILABLE
|
|
561
608
|
}));
|
|
562
609
|
return {
|
|
563
610
|
lines: [
|
|
564
|
-
"Pi
|
|
611
|
+
"Pi uses the vault for Antigravity, one account at a time. Choose an account, or turn accounts off and on, in the Vault section.",
|
|
565
612
|
...status.hostSlot === "login" ? [
|
|
566
|
-
"
|
|
613
|
+
"Pi is also signed in with its own login, so requests are refused. Run /logout, or switch back to Pi's own login in the Vault section."
|
|
567
614
|
] : [],
|
|
568
615
|
...problem ? [problem] : [],
|
|
569
616
|
// An approved enrollment without a read grant lists nothing;
|
|
570
617
|
// say so rather than show an empty account list.
|
|
571
|
-
...!problem && list.length === 0 && status.enrollment.state === "approved" ? piNoVaultAccountsGuidance() : []
|
|
618
|
+
...!problem && list.length === 0 && status.enrollment.state === "approved" ? piNoVaultAccountsGuidance(reach) : []
|
|
572
619
|
],
|
|
573
620
|
items
|
|
574
621
|
};
|
|
@@ -589,7 +636,7 @@ function createPiAntigravityAccountSections(options) {
|
|
|
589
636
|
if (status.mode === "unreadable")
|
|
590
637
|
return {
|
|
591
638
|
lines: [
|
|
592
|
-
"
|
|
639
|
+
"Pi can't read its vault setup, so requests are refused. See the Vault section."
|
|
593
640
|
]
|
|
594
641
|
};
|
|
595
642
|
if (status.mode !== "custody")
|
|
@@ -598,13 +645,13 @@ function createPiAntigravityAccountSections(options) {
|
|
|
598
645
|
"Every request uses Pi's own login. There is no account rotation."
|
|
599
646
|
]
|
|
600
647
|
};
|
|
601
|
-
const { list, problem } = await custodyAccounts(status, options);
|
|
648
|
+
const { list, reach, problem } = await custodyAccounts(status, options);
|
|
602
649
|
const serving = servingAccount(status, list);
|
|
603
650
|
if (!problem && list.length === 0 && status.enrollment.state === "approved")
|
|
604
651
|
return {
|
|
605
652
|
lines: [
|
|
606
|
-
"The vault
|
|
607
|
-
...piNoVaultAccountsGuidance()
|
|
653
|
+
"The vault gives Pi no accounts, so requests are refused. There is no account rotation.",
|
|
654
|
+
...piNoVaultAccountsGuidance(reach)
|
|
608
655
|
]
|
|
609
656
|
};
|
|
610
657
|
return {
|
|
@@ -643,23 +690,13 @@ function getPackedRefresh(accessToken) {
|
|
|
643
690
|
}
|
|
644
691
|
|
|
645
692
|
// src/paths.ts
|
|
646
|
-
import {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
function getPiAntigravityVaultDir() {
|
|
655
|
-
return join(dirname(getPiAntigravityAuthFile()), "antigravity-auth-vault");
|
|
656
|
-
}
|
|
657
|
-
function getPiAntigravityVaultModeFile() {
|
|
658
|
-
return join(getPiAntigravityVaultDir(), "pi-mode.json");
|
|
659
|
-
}
|
|
660
|
-
function getPiAntigravityVaultRosterFile() {
|
|
661
|
-
return join(getPiAntigravityVaultDir(), "pi-roster.json");
|
|
662
|
-
}
|
|
693
|
+
import {
|
|
694
|
+
getPiAntigravityAuthFile,
|
|
695
|
+
getPiAntigravityVaultDir,
|
|
696
|
+
getPiAntigravityVaultModeFile,
|
|
697
|
+
getPiAntigravityVaultRosterFile,
|
|
698
|
+
getPiConfigDir
|
|
699
|
+
} from "@cortexkit/antigravity-auth-core";
|
|
663
700
|
|
|
664
701
|
// src/stream.ts
|
|
665
702
|
import {
|
|
@@ -674,10 +711,12 @@ import {
|
|
|
674
711
|
isGeminiArgonModel,
|
|
675
712
|
isRetiredAgyModel,
|
|
676
713
|
orderAgyRequestPayloadInPlace,
|
|
714
|
+
PreResponseDeadline,
|
|
715
|
+
parseRefreshParts,
|
|
677
716
|
RETIRED_AGY_MODEL_MESSAGE,
|
|
678
717
|
redactCredentialError,
|
|
679
718
|
redactCredentialText,
|
|
680
|
-
|
|
719
|
+
resolveAntigravityModel
|
|
681
720
|
} from "@cortexkit/antigravity-auth-core";
|
|
682
721
|
import {
|
|
683
722
|
calculateCost,
|
|
@@ -1021,11 +1060,11 @@ function resolvePiAntigravityModel(model, reasoning) {
|
|
|
1021
1060
|
const lower = model.id.toLowerCase();
|
|
1022
1061
|
const supportsAgyTiers = model.reasoning && (isGeminiArgonModel(lower) || lower.includes("gemini-3") || lower.includes("claude") && (lower.includes("thinking") || /claude-(opus|sonnet)-5-5/.test(lower)));
|
|
1023
1062
|
if (!supportsAgyTiers || !reasoning) {
|
|
1024
|
-
return
|
|
1063
|
+
return resolveAntigravityModel(model.id);
|
|
1025
1064
|
}
|
|
1026
1065
|
const tier = reasoning === "minimal" ? "low" : reasoning === "xhigh" ? "high" : reasoning;
|
|
1027
1066
|
const baseModel = model.id.replace(/-(minimal|low|medium|high|xhigh)$/i, "");
|
|
1028
|
-
return
|
|
1067
|
+
return resolveAntigravityModel(`${baseModel}-${tier}`);
|
|
1029
1068
|
}
|
|
1030
1069
|
function prepareAntigravityRequest(options) {
|
|
1031
1070
|
const resolved = resolvePiAntigravityModel(
|
|
@@ -1100,14 +1139,25 @@ async function sendAntigravityRequest(options) {
|
|
|
1100
1139
|
throw new Error(RETIRED_AGY_MODEL_MESSAGE);
|
|
1101
1140
|
}
|
|
1102
1141
|
const packedRefresh = getPackedRefresh(options.accessToken) ?? "";
|
|
1103
|
-
const
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1142
|
+
const packedProject = parseRefreshParts(packedRefresh);
|
|
1143
|
+
for (const projectId of [
|
|
1144
|
+
packedProject.projectId,
|
|
1145
|
+
packedProject.managedProjectId
|
|
1146
|
+
]) {
|
|
1147
|
+
if (projectId) options.selectedProjectIds.add(projectId);
|
|
1148
|
+
}
|
|
1149
|
+
const projectContext = await options.deadline.wait(
|
|
1150
|
+
() => ensureProjectContext({
|
|
1151
|
+
type: "oauth",
|
|
1152
|
+
refresh: packedRefresh,
|
|
1153
|
+
access: options.accessToken,
|
|
1154
|
+
expires: Date.now() + 6e4
|
|
1155
|
+
})
|
|
1156
|
+
);
|
|
1157
|
+
if (projectContext.effectiveProjectId)
|
|
1158
|
+
options.selectedProjectIds.add(projectContext.effectiveProjectId);
|
|
1109
1159
|
const prepared = prepareAntigravityRequest(options);
|
|
1110
|
-
await options.assertAdmission?.();
|
|
1160
|
+
await options.deadline.wait(() => options.assertAdmission?.());
|
|
1111
1161
|
return dispatchAntigravityRequest(
|
|
1112
1162
|
prepared,
|
|
1113
1163
|
{
|
|
@@ -1117,13 +1167,13 @@ async function sendAntigravityRequest(options) {
|
|
|
1117
1167
|
options.signal ?? options.streamOptions?.signal ?? null
|
|
1118
1168
|
);
|
|
1119
1169
|
}
|
|
1120
|
-
function streamCortexKitAntigravity(model, context, options, assertAdmission) {
|
|
1170
|
+
function streamCortexKitAntigravity(model, context, options, assertAdmission, preResponseClock) {
|
|
1121
1171
|
return runAntigravityStream(
|
|
1122
1172
|
model,
|
|
1123
1173
|
context,
|
|
1124
1174
|
options,
|
|
1125
|
-
async (sessionKey, signal) => {
|
|
1126
|
-
await assertAdmission?.();
|
|
1175
|
+
async (sessionKey, signal, _selectedCredentials, selectedProjectIds, deadline) => {
|
|
1176
|
+
await deadline.wait(() => assertAdmission?.());
|
|
1127
1177
|
const accessToken = options?.apiKey ?? "";
|
|
1128
1178
|
if (!accessToken)
|
|
1129
1179
|
throw new Error("Missing Antigravity OAuth access token");
|
|
@@ -1134,17 +1184,20 @@ function streamCortexKitAntigravity(model, context, options, assertAdmission) {
|
|
|
1134
1184
|
accessToken,
|
|
1135
1185
|
sessionKey,
|
|
1136
1186
|
signal,
|
|
1137
|
-
assertAdmission
|
|
1187
|
+
assertAdmission,
|
|
1188
|
+
selectedProjectIds,
|
|
1189
|
+
deadline
|
|
1138
1190
|
});
|
|
1139
|
-
}
|
|
1191
|
+
},
|
|
1192
|
+
preResponseClock
|
|
1140
1193
|
);
|
|
1141
1194
|
}
|
|
1142
|
-
function createCortexKitAntigravityVaultStream(send, placeholderKey) {
|
|
1195
|
+
function createCortexKitAntigravityVaultStream(send, placeholderKey, preResponseClock) {
|
|
1143
1196
|
return (model, context, options) => runAntigravityStream(
|
|
1144
1197
|
model,
|
|
1145
1198
|
context,
|
|
1146
1199
|
options,
|
|
1147
|
-
(sessionKey, signal, selectedCredentials) => {
|
|
1200
|
+
(sessionKey, signal, selectedCredentials, selectedProjectIds, deadline) => {
|
|
1148
1201
|
if (options?.apiKey !== placeholderKey)
|
|
1149
1202
|
throw new Error(
|
|
1150
1203
|
"The native Google credential slot is not synchronized for vault mode. No request was sent."
|
|
@@ -1155,28 +1208,37 @@ function createCortexKitAntigravityVaultStream(send, placeholderKey) {
|
|
|
1155
1208
|
streamOptions: options,
|
|
1156
1209
|
sessionKey
|
|
1157
1210
|
});
|
|
1158
|
-
return
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1211
|
+
return deadline.open(
|
|
1212
|
+
() => send(async (credential, sendSignal) => {
|
|
1213
|
+
selectedCredentials.add(credential.accessToken);
|
|
1214
|
+
selectedProjectIds.add(credential.projectId);
|
|
1215
|
+
try {
|
|
1216
|
+
deadline.follow(sendSignal);
|
|
1217
|
+
return await deadline.open(
|
|
1218
|
+
() => dispatchAntigravityRequest(prepared, credential, signal)
|
|
1219
|
+
);
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
throw redactCredentialError(
|
|
1222
|
+
error,
|
|
1223
|
+
[...selectedCredentials],
|
|
1224
|
+
[...selectedProjectIds]
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
}, signal)
|
|
1228
|
+
);
|
|
1229
|
+
},
|
|
1230
|
+
preResponseClock
|
|
1171
1231
|
);
|
|
1172
1232
|
}
|
|
1173
|
-
function runAntigravityStream(model, context, options, open) {
|
|
1233
|
+
function runAntigravityStream(model, context, options, open, preResponseClock) {
|
|
1174
1234
|
const stream = createAssistantMessageEventStream();
|
|
1235
|
+
const deadline = new PreResponseDeadline(options?.signal, preResponseClock);
|
|
1175
1236
|
void (async () => {
|
|
1176
1237
|
const output = createOutput(model);
|
|
1177
1238
|
const selectedCredentials = new Set(
|
|
1178
1239
|
options?.apiKey ? [options.apiKey] : []
|
|
1179
1240
|
);
|
|
1241
|
+
const selectedProjectIds = /* @__PURE__ */ new Set();
|
|
1180
1242
|
stream.push({ type: "start", partial: output });
|
|
1181
1243
|
let response;
|
|
1182
1244
|
let requestAbort;
|
|
@@ -1185,16 +1247,22 @@ function runAntigravityStream(model, context, options, open) {
|
|
|
1185
1247
|
};
|
|
1186
1248
|
try {
|
|
1187
1249
|
const sessionKey = getRequestSessionKey(context, options);
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1250
|
+
const openingAbort = new AbortController();
|
|
1251
|
+
requestAbort = openingAbort;
|
|
1252
|
+
detachCaller = forwardAbort(deadline.signal, openingAbort);
|
|
1253
|
+
response = await deadline.open(
|
|
1254
|
+
() => open(
|
|
1255
|
+
sessionKey,
|
|
1256
|
+
openingAbort.signal,
|
|
1257
|
+
selectedCredentials,
|
|
1258
|
+
selectedProjectIds,
|
|
1259
|
+
deadline
|
|
1260
|
+
)
|
|
1194
1261
|
);
|
|
1262
|
+
if (response.ok) deadline.acceptHeaders();
|
|
1195
1263
|
if (!response.ok) {
|
|
1196
1264
|
throw new Error(
|
|
1197
|
-
`Antigravity request failed: HTTP ${response.status} ${await response.text()}`
|
|
1265
|
+
`Antigravity request failed: HTTP ${response.status} ${await deadline.wait(() => response.text())}`
|
|
1198
1266
|
);
|
|
1199
1267
|
}
|
|
1200
1268
|
const content = output.content;
|
|
@@ -1390,11 +1458,13 @@ function runAntigravityStream(model, context, options, open) {
|
|
|
1390
1458
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
1391
1459
|
output.errorMessage = redactCredentialText(
|
|
1392
1460
|
error instanceof Error ? error.message : String(error),
|
|
1393
|
-
[...selectedCredentials]
|
|
1461
|
+
[...selectedCredentials],
|
|
1462
|
+
[...selectedProjectIds]
|
|
1394
1463
|
);
|
|
1395
1464
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
1396
1465
|
stream.end();
|
|
1397
1466
|
} finally {
|
|
1467
|
+
deadline.dispose();
|
|
1398
1468
|
detachCaller();
|
|
1399
1469
|
}
|
|
1400
1470
|
})();
|
|
@@ -1425,7 +1495,7 @@ function requireCustody(read) {
|
|
|
1425
1495
|
);
|
|
1426
1496
|
return read.record;
|
|
1427
1497
|
}
|
|
1428
|
-
function selectRoute(routes, routeId) {
|
|
1498
|
+
function selectRoute(routes, routeId, emptyRosterCause) {
|
|
1429
1499
|
if (routeId !== void 0) {
|
|
1430
1500
|
const selected = routes.find((route) => route.routeId === routeId);
|
|
1431
1501
|
if (!selected)
|
|
@@ -1437,7 +1507,7 @@ function selectRoute(routes, routeId) {
|
|
|
1437
1507
|
const [only, ...rest] = routes;
|
|
1438
1508
|
if (!only)
|
|
1439
1509
|
throw new PiVaultSendError(
|
|
1440
|
-
"The vault serves Pi no Antigravity accounts yet."
|
|
1510
|
+
piEmptyVaultRosterRefusal(emptyRosterCause()) ?? "The vault serves Pi no Antigravity accounts yet."
|
|
1441
1511
|
);
|
|
1442
1512
|
if (rest.length > 0)
|
|
1443
1513
|
throw new PiVaultSendError(
|
|
@@ -1460,12 +1530,17 @@ function createPiVaultSend(options) {
|
|
|
1460
1530
|
signal.throwIfAborted();
|
|
1461
1531
|
routes = source.routes();
|
|
1462
1532
|
}
|
|
1463
|
-
const ref = selectRoute(
|
|
1533
|
+
const ref = selectRoute(
|
|
1534
|
+
routes,
|
|
1535
|
+
record.routeId,
|
|
1536
|
+
() => source.emptyRosterCause()
|
|
1537
|
+
);
|
|
1464
1538
|
return source.send(ref, dispatch, { site: "model", signal });
|
|
1465
1539
|
};
|
|
1466
1540
|
}
|
|
1467
1541
|
function createPiVaultAccountsPort(options) {
|
|
1468
1542
|
let lastRows = /* @__PURE__ */ new Map();
|
|
1543
|
+
let lastReach;
|
|
1469
1544
|
const refFor = (routeId) => {
|
|
1470
1545
|
const ref = lastRows.get(routeId);
|
|
1471
1546
|
if (!ref)
|
|
@@ -1477,7 +1552,9 @@ function createPiVaultAccountsPort(options) {
|
|
|
1477
1552
|
return {
|
|
1478
1553
|
list: async () => {
|
|
1479
1554
|
const { source } = await openCustody(options);
|
|
1555
|
+
lastReach = void 0;
|
|
1480
1556
|
const roster = await source.refresh();
|
|
1557
|
+
lastReach = source.grantReach();
|
|
1481
1558
|
const rows = /* @__PURE__ */ new Map();
|
|
1482
1559
|
const choices = [];
|
|
1483
1560
|
for (const row of roster?.rows ?? []) {
|
|
@@ -1503,6 +1580,7 @@ function createPiVaultAccountsPort(options) {
|
|
|
1503
1580
|
lastRows = rows;
|
|
1504
1581
|
return choices;
|
|
1505
1582
|
},
|
|
1583
|
+
grantReach: () => lastReach,
|
|
1506
1584
|
decline: async (routeId) => {
|
|
1507
1585
|
const { source } = await openCustody(options);
|
|
1508
1586
|
await source.decline(refFor(routeId));
|
|
@@ -1783,8 +1861,8 @@ async function registerCortexKitPiAntigravity(pi, seams = {}) {
|
|
|
1783
1861
|
const mode = await currentMode();
|
|
1784
1862
|
const { openPiSlot } = await (seams.loadPiSlot ?? loadCommonAuthPiSlot)();
|
|
1785
1863
|
const slot = openPiSlot({
|
|
1786
|
-
authPath: seams.nativeAuthPath ??
|
|
1787
|
-
stashPath: seams.nativeStashPath ??
|
|
1864
|
+
authPath: seams.nativeAuthPath ?? join(getAgentDir(), "auth.json"),
|
|
1865
|
+
stashPath: seams.nativeStashPath ?? join(
|
|
1788
1866
|
seams.vaultDir ?? getPiAntigravityVaultDir(),
|
|
1789
1867
|
"google-login-stash.json"
|
|
1790
1868
|
),
|