@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.
@@ -0,0 +1,354 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { closeSync, constants, fchmodSync, fstatSync, linkSync, lstatSync, openSync, realpathSync, unlinkSync, } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { canonicalIsolationLocator } from "@claudexor/core";
5
+ import { ensureCanonicalPrivateDirectory, redactSecrets } from "@claudexor/util";
6
+ const SECURITY_BINARY = "/usr/bin/security";
7
+ const SECURITY_TIMEOUT_MS = 5_000;
8
+ const configuredKeychains = new Set();
9
+ /**
10
+ * Typed, bounded setup result. `unsafe` means the profile path or target
11
+ * identity is not proven and callers must refuse the vendor child; a false
12
+ * value is a recoverable security-tool miss where agy's file fallback may
13
+ * still keep authentication working.
14
+ */
15
+ export class AgyProfileKeychainError extends Error {
16
+ code = "agy_profile_keychain_unavailable";
17
+ unsafe;
18
+ constructor(detail, options = {}) {
19
+ super(`agy profile keychain setup failed: ${redactSecrets(detail).slice(0, 300)}`);
20
+ this.name = "AgyProfileKeychainError";
21
+ this.unsafe = options.unsafe === true;
22
+ }
23
+ }
24
+ export function isAgyProfileKeychainUnsafe(error) {
25
+ return error instanceof AgyProfileKeychainError && error.unsafe;
26
+ }
27
+ /** The only keychain path agy may use for one named profile. */
28
+ export function agyProfileKeychainPath(profileHome) {
29
+ const home = canonicalIsolationLocator(profileHome, "credential profile Antigravity HOME");
30
+ return join(home, "Library", "Keychains", "login.keychain-db");
31
+ }
32
+ /**
33
+ * Prepare the private Darwin keychain declared by the agy capability profile.
34
+ * Other platforms deliberately do nothing: Linux and Windows retain their
35
+ * vendor-owned credential behavior. The helper never reads, copies, or
36
+ * removes credential items; agy itself remains the only item owner. Creation
37
+ * uses a neutral bootstrap filename before adopting login.keychain-db because
38
+ * Apple's create operation treats the latter name as a user-list entry on the
39
+ * supported Darwin path.
40
+ */
41
+ export function prepareAgyProfileKeychain(profileHome, options = {}) {
42
+ const platform = options.platform ?? process.platform;
43
+ if (platform !== "darwin")
44
+ return;
45
+ ensureAgyProfileKeychain(profileHome, { ...options, platform });
46
+ }
47
+ /**
48
+ * Low-level Darwin ensure used by the adapter's production seams and tests.
49
+ * The caller has already established that this is the private agy route.
50
+ */
51
+ export function ensureAgyProfileKeychain(profileHome, options = {}) {
52
+ const platform = options.platform ?? process.platform;
53
+ if (platform !== "darwin")
54
+ return;
55
+ let home;
56
+ let keychain;
57
+ try {
58
+ home = canonicalIsolationLocator(profileHome, "credential profile Antigravity HOME");
59
+ keychain = agyProfileKeychainPath(home);
60
+ }
61
+ catch (error) {
62
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
63
+ unsafe: true,
64
+ });
65
+ }
66
+ try {
67
+ try {
68
+ ensureOwnedDirectory(home, platform);
69
+ ensureOwnedDirectory(join(home, "Library"), platform);
70
+ ensureOwnedDirectory(join(home, "Library", "Keychains"), platform);
71
+ }
72
+ catch (error) {
73
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
74
+ unsafe: true,
75
+ });
76
+ }
77
+ let exists = validateKeychainFile(keychain, home, false);
78
+ const runSecurity = options.runSecurity ?? defaultSecurityRunner;
79
+ const env = securityEnv(home);
80
+ if (!exists) {
81
+ const bootstrap = temporaryKeychainPath(join(home, "Library", "Keychains"));
82
+ if (bootstrap.includes("/login.keychain")) {
83
+ throw new AgyProfileKeychainError("profile keychain path would match Apple's login-keychain search-list heuristic", { unsafe: true });
84
+ }
85
+ const bootstrapExists = validateKeychainFile(bootstrap, home, false);
86
+ const created = bootstrapExists
87
+ ? { status: 48, detail: "bootstrap keychain already exists" }
88
+ : runSecurity(["create-keychain", "-p", "", bootstrap], env);
89
+ const canonicalAfterCreate = validateKeychainFile(keychain, home, false);
90
+ if (!canonicalAfterCreate) {
91
+ let bootstrapReady = false;
92
+ if (created.status !== 0) {
93
+ // A concurrent process may have won creation with a
94
+ // platform-specific already-exists status. Revalidation, not a
95
+ // numeric status guess, is the idempotence authority.
96
+ try {
97
+ validateKeychainFile(bootstrap, home, true);
98
+ bootstrapReady = true;
99
+ }
100
+ catch (error) {
101
+ // A peer can adopt and remove the bootstrap file between our
102
+ // status check and this validation. Recheck the canonical winner
103
+ // only for that missing-file race; identity failures remain hard
104
+ // refusals.
105
+ if (pathIsMissing(bootstrap) && validateKeychainFile(keychain, home, false)) {
106
+ exists = true;
107
+ }
108
+ else {
109
+ // A failed create with no usable target is unsafe: spawning agy
110
+ // would return to the missing-keychain prompt. Explicit security
111
+ // errors remain recoverable only when a valid file exists.
112
+ if (error instanceof AgyProfileKeychainError)
113
+ throw error;
114
+ throw new AgyProfileKeychainError(created.detail ?? "create-keychain failed", {
115
+ unsafe: true,
116
+ });
117
+ }
118
+ }
119
+ }
120
+ else {
121
+ validateKeychainFile(bootstrap, home, true);
122
+ bootstrapReady = true;
123
+ }
124
+ if (bootstrapReady) {
125
+ adoptBootstrapKeychain(bootstrap, keychain, home);
126
+ exists = true;
127
+ }
128
+ }
129
+ else {
130
+ // Another process completed the canonical rename while this process
131
+ // was creating its neutral bootstrap file. The neutral file is ours;
132
+ // remove it only after the canonical target is identity-checked.
133
+ removeBootstrapKeychain(bootstrap, home);
134
+ exists = true;
135
+ }
136
+ configuredKeychains.delete(keychain);
137
+ }
138
+ let unlockedBeforeSettings = false;
139
+ if (!configuredKeychains.has(keychain)) {
140
+ const unlocked = runSecurity(["unlock-keychain", "-p", "", keychain], env);
141
+ if (unlocked.status !== 0) {
142
+ throw new AgyProfileKeychainError(unlocked.detail ?? "unlock-keychain failed");
143
+ }
144
+ unlockedBeforeSettings = true;
145
+ const settings = runSecurity(["set-keychain-settings", keychain], env);
146
+ if (settings.status !== 0) {
147
+ throw new AgyProfileKeychainError(settings.detail ?? "set-keychain-settings failed");
148
+ }
149
+ configuredKeychains.add(keychain);
150
+ }
151
+ // A login keychain can still be locked by sleep, logout, or an explicit
152
+ // user action. Re-open it at every vendor-spawn boundary; the first-call
153
+ // unlock above also makes set-keychain-settings safe on a locked file.
154
+ if (!unlockedBeforeSettings) {
155
+ const unlocked = runSecurity(["unlock-keychain", "-p", "", keychain], env);
156
+ if (unlocked.status !== 0) {
157
+ throw new AgyProfileKeychainError(unlocked.detail ?? "unlock-keychain failed");
158
+ }
159
+ }
160
+ validateKeychainFile(keychain, home, true);
161
+ }
162
+ catch (error) {
163
+ configuredKeychains.delete(keychain);
164
+ if (error instanceof AgyProfileKeychainError)
165
+ throw error;
166
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
167
+ unsafe: true,
168
+ });
169
+ }
170
+ }
171
+ function securityEnv(home) {
172
+ return {
173
+ HOME: home,
174
+ USERPROFILE: home,
175
+ PATH: "/usr/bin:/bin",
176
+ ...(process.env.TMPDIR ? { TMPDIR: process.env.TMPDIR } : {}),
177
+ ...(process.env.LANG ? { LANG: process.env.LANG } : {}),
178
+ ...(process.env.LC_ALL ? { LC_ALL: process.env.LC_ALL } : {}),
179
+ };
180
+ }
181
+ function defaultSecurityRunner(args, env) {
182
+ try {
183
+ execFileSync(SECURITY_BINARY, [...args], {
184
+ env,
185
+ stdio: "ignore",
186
+ timeout: SECURITY_TIMEOUT_MS,
187
+ windowsHide: true,
188
+ });
189
+ return { status: 0 };
190
+ }
191
+ catch (error) {
192
+ const status = typeof error === "object" && error !== null && "status" in error
193
+ ? error.status
194
+ : null;
195
+ return {
196
+ status: typeof status === "number" ? status : null,
197
+ detail: error instanceof Error ? error.message : String(error),
198
+ };
199
+ }
200
+ }
201
+ function ensureOwnedDirectory(path, platform) {
202
+ try {
203
+ ensureCanonicalPrivateDirectory(path, platform);
204
+ }
205
+ catch (error) {
206
+ if (!(error instanceof Error) || !/EEXIST|already exists/i.test(error.message))
207
+ throw error;
208
+ // Another process won the mkdir race. Re-run the full identity proof.
209
+ ensureCanonicalPrivateDirectory(path, platform);
210
+ }
211
+ }
212
+ function temporaryKeychainPath(directory) {
213
+ // Keep one deterministic neutral name so a crash between create and adopt
214
+ // is recoverable on the next preparation rather than accumulating orphan
215
+ // keychain databases. It deliberately does not contain "login.keychain",
216
+ // the Apple Security heuristic that updates the user search list.
217
+ return join(directory, ".agy-profile-bootstrap.keychain-db");
218
+ }
219
+ /**
220
+ * Install the neutral-name keychain without calling a user-domain list
221
+ * mutation. A hard link gives us an atomic no-replace destination on the
222
+ * same APFS directory; the source is then removed, so the vendor sees one
223
+ * canonical login.keychain-db path and no bootstrap alias remains.
224
+ */
225
+ function adoptBootstrapKeychain(source, destination, home) {
226
+ try {
227
+ linkSync(source, destination);
228
+ }
229
+ catch (error) {
230
+ if (isAlreadyExists(error)) {
231
+ validateKeychainFile(destination, home, true);
232
+ removeBootstrapKeychain(source, home);
233
+ return;
234
+ }
235
+ if (isMissing(error) && validateKeychainFile(destination, home, false)) {
236
+ // A peer may have adopted and removed the bootstrap between our
237
+ // identity check and this no-replace link. The canonical winner is
238
+ // authoritative; do not turn that benign race into a profile refusal.
239
+ return;
240
+ }
241
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
242
+ unsafe: true,
243
+ });
244
+ }
245
+ try {
246
+ unlinkSync(source);
247
+ }
248
+ catch (error) {
249
+ if (isMissing(error)) {
250
+ validateKeychainFile(destination, home, true);
251
+ return;
252
+ }
253
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
254
+ unsafe: true,
255
+ });
256
+ }
257
+ validateKeychainFile(destination, home, true);
258
+ }
259
+ function removeBootstrapKeychain(path, home) {
260
+ let exists = false;
261
+ try {
262
+ exists = validateKeychainFile(path, home, false);
263
+ }
264
+ catch (error) {
265
+ if (error instanceof AgyProfileKeychainError)
266
+ throw error;
267
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
268
+ unsafe: true,
269
+ });
270
+ }
271
+ if (!exists)
272
+ return;
273
+ try {
274
+ unlinkSync(path);
275
+ }
276
+ catch (error) {
277
+ if (isMissing(error))
278
+ return;
279
+ throw new AgyProfileKeychainError(error instanceof Error ? error.message : String(error), {
280
+ unsafe: true,
281
+ });
282
+ }
283
+ }
284
+ function validateKeychainFile(path, home, required) {
285
+ const absolute = resolve(path);
286
+ let named;
287
+ try {
288
+ named = lstatSync(absolute);
289
+ }
290
+ catch (error) {
291
+ if (!required && isMissing(error))
292
+ return false;
293
+ throw new AgyProfileKeychainError("private keychain path is missing or unreadable", {
294
+ unsafe: true,
295
+ });
296
+ }
297
+ if (named.isSymbolicLink() || !named.isFile()) {
298
+ throw new AgyProfileKeychainError("private keychain path is not a regular file", {
299
+ unsafe: true,
300
+ });
301
+ }
302
+ let fd;
303
+ try {
304
+ fd = openSync(absolute, constants.O_RDWR | constants.O_NOFOLLOW);
305
+ }
306
+ catch {
307
+ throw new AgyProfileKeychainError("private keychain path cannot be opened safely", {
308
+ unsafe: true,
309
+ });
310
+ }
311
+ try {
312
+ const opened = fstatSync(fd);
313
+ const current = lstatSync(absolute);
314
+ if (!opened.isFile() ||
315
+ current.isSymbolicLink() ||
316
+ !current.isFile() ||
317
+ opened.dev !== current.dev ||
318
+ opened.ino !== current.ino ||
319
+ realpathSync.native(absolute) !== absolute ||
320
+ (typeof process.getuid === "function" && opened.uid !== process.getuid()) ||
321
+ !absolute.startsWith(resolve(home) + "/")) {
322
+ throw new AgyProfileKeychainError("private keychain identity is not canonical", {
323
+ unsafe: true,
324
+ });
325
+ }
326
+ fchmodSync(fd, 0o600);
327
+ }
328
+ finally {
329
+ closeSync(fd);
330
+ }
331
+ return true;
332
+ }
333
+ function isMissing(error) {
334
+ return typeof error === "object" && error !== null && "code" in error
335
+ ? error.code === "ENOENT"
336
+ : false;
337
+ }
338
+ function pathIsMissing(path) {
339
+ try {
340
+ lstatSync(path);
341
+ return false;
342
+ }
343
+ catch (error) {
344
+ if (isMissing(error))
345
+ return true;
346
+ throw error;
347
+ }
348
+ }
349
+ function isAlreadyExists(error) {
350
+ return typeof error === "object" && error !== null && "code" in error
351
+ ? error.code === "EEXIST"
352
+ : false;
353
+ }
354
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { spawn, type SpawnOptionsWithoutStdio } from "node:child_process";
2
+ import { reapProcessTree } from "@claudexor/core";
3
+ export declare const AGY_PRINT_TIMEOUT_MS = 30000;
4
+ export declare const AGY_PRINT_STREAM_LIMIT_BYTES: number;
5
+ type EnvMap = Record<string, string | null | undefined>;
6
+ export type AgyPrintFailureReason = "spawn_failed" | "timed_out" | "aborted" | "output_overflow" | "termination_unconfirmed";
7
+ export type AgyPrintCommandResult = {
8
+ kind: "completed";
9
+ code: number | null;
10
+ signal: NodeJS.Signals | null;
11
+ stdout: string;
12
+ stderr: string;
13
+ } | {
14
+ kind: "failed";
15
+ reason: AgyPrintFailureReason;
16
+ detail: string;
17
+ stdout: string;
18
+ stderr: string;
19
+ };
20
+ export interface AgyPrintCommandOptions {
21
+ timeoutMs?: number;
22
+ maxStdoutBytes?: number;
23
+ maxStderrBytes?: number;
24
+ abortSignal?: AbortSignal;
25
+ platform?: NodeJS.Platform;
26
+ /** Bounded cancellation proof seam; production default is 5 seconds. */
27
+ cancelDeadlineMs?: number;
28
+ /** Bounded post-exit pipe drain seam; production default is 200 ms. */
29
+ drainMs?: number;
30
+ /** Deterministic binary-resolution seam; production uses the shared exact resolver. */
31
+ resolveBinary?: (bin: string, env: NodeJS.ProcessEnv, platform: NodeJS.Platform) => string | null;
32
+ /** Deterministic lifecycle seams. */
33
+ spawnProcess?: typeof spawn;
34
+ reap?: typeof reapProcessTree;
35
+ /** Deterministic passive-registry seam; production uses the shared registry. */
36
+ processRegistry?: {
37
+ register(pid: number, command: string): void;
38
+ unregister(pid: number): void;
39
+ };
40
+ }
41
+ /** Spawn policy is exported so Windows fixtures can assert no inherited console/stdio. */
42
+ export declare function agyPrintSpawnOptions(platform: NodeJS.Platform, env: NodeJS.ProcessEnv): SpawnOptionsWithoutStdio;
43
+ /**
44
+ * Bounded print-mode owner for `/model` and `/quota`. The child has no stdin
45
+ * and no controlling terminal: POSIX starts a new session; Windows uses
46
+ * DETACHED_PROCESS because CREATE_NO_WINDOW still exposes a windowless
47
+ * CONIN$. Child exit is completion authority; descendant-held pipes receive
48
+ * only a bounded drain.
49
+ */
50
+ export declare function runAgyPrintCommand(bin: string, command: "/model" | "/quota", envPatch: EnvMap, options?: AgyPrintCommandOptions): Promise<AgyPrintCommandResult>;
51
+ export type AgyPrintClassification = {
52
+ kind: "success";
53
+ envelope: Record<string, unknown>;
54
+ } | {
55
+ kind: "unauthenticated";
56
+ detail: string;
57
+ } | {
58
+ kind: "probe_failed";
59
+ detail: string;
60
+ };
61
+ /** Shared exit/envelope/auth classifier for doctor and quota. */
62
+ export declare function classifyAgyPrintResult(result: AgyPrintCommandResult): AgyPrintClassification;
63
+ export {};
64
+ //# sourceMappingURL=print-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-command.d.ts","sourceRoot":"","sources":["../src/print-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA4B,KAAK,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGpG,OAAO,EAIL,eAAe,EAMhB,MAAM,iBAAiB,CAAC;AAGzB,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,4BAA4B,QAAa,CAAC;AAIvD,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExD,MAAM,MAAM,qBAAqB,GAC/B,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,yBAAyB,CAAC;AAE3F,MAAM,MAAM,qBAAqB,GAC7B;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,MAAM,GAAG,IAAI,CAAC;IAClG,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,eAAe,CAAC;IAC9B,gFAAgF;IAChF,eAAe,CAAC,EAAE;QAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACH;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,wBAAwB,CAU1B;AAqED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAgPhC;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkC7C,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG,sBAAsB,CAyC5F"}