@oxagen/cli 2.1.4-955 → 2.1.4-965

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/oxagen.mjs +1698 -845
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -13816,8 +13816,8 @@ var init_backfill = __esm({
13816
13816
  this.parentMs = ms;
13817
13817
  this.clock.ms = ms;
13818
13818
  }
13819
- const spawn7 = [];
13820
- return { events: this.mappedLine(parsed2, text5, offset, spawn7), spawn: spawn7 };
13819
+ const spawn9 = [];
13820
+ return { events: this.mappedLine(parsed2, text5, offset, spawn9), spawn: spawn9 };
13821
13821
  }
13822
13822
  /**
13823
13823
  * Begin a session still waiting for its facts once its last line is read:
@@ -13843,28 +13843,28 @@ var init_backfill = __esm({
13843
13843
  this.parentMs = timestampOf(first) ?? 0;
13844
13844
  this.clock.ms = this.parentMs;
13845
13845
  const events = this.collect(this.start(first, facts, permissionMode));
13846
- const spawn7 = [];
13846
+ const spawn9 = [];
13847
13847
  for (const item of early) {
13848
13848
  const ms = timestampOf(item.record);
13849
13849
  if (ms !== void 0) this.parentMs = Math.max(this.parentMs, ms);
13850
13850
  this.clock.ms = ms ?? this.parentMs;
13851
13851
  events.push(
13852
- ...this.mappedLine(item.record, item.text, item.offset, spawn7)
13852
+ ...this.mappedLine(item.record, item.text, item.offset, spawn9)
13853
13853
  );
13854
13854
  }
13855
13855
  this.clock.ms = this.parentMs;
13856
- return { events, spawn: spawn7 };
13856
+ return { events, spawn: spawn9 };
13857
13857
  }
13858
13858
  /** One parent line through the mapping, guarded. */
13859
- mappedLine(record, text5, offset, spawn7) {
13859
+ mappedLine(record, text5, offset, spawn9) {
13860
13860
  const spawnedBefore = this.spawned.size;
13861
- const spawnLength = spawn7.length;
13861
+ const spawnLength = spawn9.length;
13862
13862
  return this.guarded(
13863
13863
  offset,
13864
13864
  void 0,
13865
- () => this.mapped(record, text5, spawn7),
13865
+ () => this.mapped(record, text5, spawn9),
13866
13866
  () => {
13867
- spawn7.length = spawnLength;
13867
+ spawn9.length = spawnLength;
13868
13868
  for (const toolUseId of [...this.spawned.keys()].slice(spawnedBefore))
13869
13869
  this.spawned.delete(toolUseId);
13870
13870
  }
@@ -14080,7 +14080,7 @@ var init_backfill = __esm({
14080
14080
  })
14081
14081
  );
14082
14082
  }
14083
- mapped(record, text5, spawn7) {
14083
+ mapped(record, text5, spawn9) {
14084
14084
  const type = nonEmptyString(record["type"]);
14085
14085
  const ms = timestampOf(record);
14086
14086
  const events = [];
@@ -14213,7 +14213,7 @@ var init_backfill = __esm({
14213
14213
  ms
14214
14214
  )
14215
14215
  );
14216
- spawn7.push(subagent.agentId);
14216
+ spawn9.push(subagent.agentId);
14217
14217
  }
14218
14218
  return events;
14219
14219
  }
@@ -17571,14 +17571,14 @@ var init_recorder = __esm({
17571
17571
  const parent = this.options.parent;
17572
17572
  if (parent === void 0) return;
17573
17573
  const type = parent.subagentType === void 0 ? subagentType : void 0;
17574
- const spawn7 = parent.spawnToolUseId === void 0 ? spawnToolUseId : void 0;
17575
- if (type === void 0 && spawn7 === void 0) return;
17574
+ const spawn9 = parent.spawnToolUseId === void 0 ? spawnToolUseId : void 0;
17575
+ if (type === void 0 && spawn9 === void 0) return;
17576
17576
  this.options = {
17577
17577
  ...this.options,
17578
17578
  parent: {
17579
17579
  ...parent,
17580
17580
  ...type !== void 0 ? { subagentType: type } : {},
17581
- ...spawn7 !== void 0 ? { spawnToolUseId: spawn7 } : {}
17581
+ ...spawn9 !== void 0 ? { spawnToolUseId: spawn9 } : {}
17582
17582
  }
17583
17583
  };
17584
17584
  }
@@ -21029,12 +21029,12 @@ async function sealTranscript(candidate, deps, pass2) {
21029
21029
  const read2 = await readSlices(candidate.path, sliceBytes, async (lines) => {
21030
21030
  let index = 0;
21031
21031
  while (index < lines.length) {
21032
- const spawn7 = [];
21032
+ const spawn9 = [];
21033
21033
  await deps.exclusive(candidate.sessionId, () => {
21034
21034
  if (deps.heldLocally(candidate.sessionId)) throw new TakenLive();
21035
21035
  const out = [];
21036
21036
  let next = readTo;
21037
- while (spawn7.length === 0) {
21037
+ while (spawn9.length === 0) {
21038
21038
  const line = lines[index];
21039
21039
  if (line === void 0) break;
21040
21040
  index += 1;
@@ -21045,7 +21045,7 @@ async function sealTranscript(candidate, deps, pass2) {
21045
21045
  }
21046
21046
  const sealed = driver.line(line.text, line.offset);
21047
21047
  out.push(...sealed.events);
21048
- spawn7.push(...sealed.spawn);
21048
+ spawn9.push(...sealed.spawn);
21049
21049
  }
21050
21050
  emit(out);
21051
21051
  if (driver.hasChain) {
@@ -21053,7 +21053,7 @@ async function sealTranscript(candidate, deps, pass2) {
21053
21053
  partial(readTo, { status: "partial" });
21054
21054
  }
21055
21055
  });
21056
- for (const agentId of spawn7) await feedSubagent(agentId);
21056
+ for (const agentId of spawn9) await feedSubagent(agentId);
21057
21057
  }
21058
21058
  await between();
21059
21059
  });
@@ -22422,6 +22422,575 @@ var init_claude_code_mcp = __esm({
22422
22422
  }
22423
22423
  });
22424
22424
 
22425
+ // ../../packages/tacho/src/host/run-token.ts
22426
+ import { createHmac, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
22427
+ import { readFileSync as readFileSync6 } from "node:fs";
22428
+ function keyFromBytes(bytes) {
22429
+ if (bytes.length !== KEY_BYTES)
22430
+ throw new Error(
22431
+ `run token key must be ${KEY_BYTES} bytes, got ${bytes.length}`
22432
+ );
22433
+ const id2 = createHmac("sha256", "tacho.run-token-key-id").update(bytes).digest("hex").slice(0, 16);
22434
+ return { bytes, id: id2 };
22435
+ }
22436
+ function generateRunTokenKey() {
22437
+ return keyFromBytes(randomBytes4(KEY_BYTES));
22438
+ }
22439
+ function runTokenKeyFromHex(hex) {
22440
+ const trimmed = hex.trim();
22441
+ if (!/^[0-9a-f]{64}$/i.test(trimmed))
22442
+ throw new Error("run token key file does not hold 64 hex characters");
22443
+ return keyFromBytes(Buffer.from(trimmed, "hex"));
22444
+ }
22445
+ function loadOrCreateRunTokenKey(path2) {
22446
+ try {
22447
+ return {
22448
+ key: runTokenKeyFromHex(readFileSync6(path2, "utf8")),
22449
+ created: false
22450
+ };
22451
+ } catch (error) {
22452
+ if (error.code !== "ENOENT") throw error;
22453
+ }
22454
+ const key = generateRunTokenKey();
22455
+ writeSensitiveFileAtomic(path2, `${key.bytes.toString("hex")}
22456
+ `);
22457
+ return { key, created: true };
22458
+ }
22459
+ function readRunTokenKey(path2) {
22460
+ const version2 = fileVersion(path2);
22461
+ if (version2 === void 0) {
22462
+ keysSeen.delete(path2);
22463
+ return void 0;
22464
+ }
22465
+ const seen = keysSeen.get(path2);
22466
+ if (seen?.version === version2) return seen.key;
22467
+ let key;
22468
+ try {
22469
+ key = runTokenKeyFromHex(readFileSync6(path2, "utf8"));
22470
+ } catch (error) {
22471
+ if (error.code === "ENOENT") return void 0;
22472
+ throw error;
22473
+ }
22474
+ keysSeen.set(path2, { version: version2, key });
22475
+ return key;
22476
+ }
22477
+ function b64url(bytes) {
22478
+ return bytes.toString("base64url");
22479
+ }
22480
+ function sign(key, claimsText) {
22481
+ return createHmac("sha256", key.bytes).update(claimsText, "utf8").digest();
22482
+ }
22483
+ function mintRunToken(options) {
22484
+ const ceiling = options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_MAX_TTL_MS;
22485
+ const asked = options.ttlMs === void 0 ? options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_DEFAULT_TTL_MS : options.ttlMs;
22486
+ if (!Number.isFinite(asked) || asked <= 0)
22487
+ throw new Error(`run token ttl must be positive, got ${String(asked)}`);
22488
+ let exp = options.now + Math.min(asked, ceiling);
22489
+ if (options.notAfter !== void 0) exp = Math.min(exp, options.notAfter);
22490
+ if (exp <= options.now)
22491
+ throw new Error("run token would already be expired when minted");
22492
+ const claims = {
22493
+ v: 1,
22494
+ tid: `rt_${randomBytes4(10).toString("hex")}`,
22495
+ host: options.host,
22496
+ harness: options.harness,
22497
+ provider: options.provider,
22498
+ placement: options.placement,
22499
+ iat: options.now,
22500
+ exp
22501
+ };
22502
+ const text5 = jcs(claims);
22503
+ const token = `${RUN_TOKEN_PREFIX}${b64url(Buffer.from(text5, "utf8"))}.${b64url(sign(options.key, text5))}`;
22504
+ return { token, claims };
22505
+ }
22506
+ function looksLikeRunToken(value) {
22507
+ return typeof value === "string" && value.startsWith(RUN_TOKEN_PREFIX) && value.length < 2048;
22508
+ }
22509
+ function peekRunTokenClaims(token) {
22510
+ if (!looksLikeRunToken(token)) return void 0;
22511
+ const body2 = token.slice(RUN_TOKEN_PREFIX.length);
22512
+ const dot = body2.indexOf(".");
22513
+ if (dot <= 0) return void 0;
22514
+ try {
22515
+ const parsed2 = JSON.parse(
22516
+ Buffer.from(body2.slice(0, dot), "base64url").toString("utf8")
22517
+ );
22518
+ const claims = runTokenClaimsSchema.safeParse(parsed2);
22519
+ return claims.success ? claims.data : void 0;
22520
+ } catch {
22521
+ return void 0;
22522
+ }
22523
+ }
22524
+ function verifyRunToken(token, options) {
22525
+ if (!looksLikeRunToken(token))
22526
+ return { ok: false, code: "run_token_malformed" };
22527
+ const body2 = token.slice(RUN_TOKEN_PREFIX.length);
22528
+ const dot = body2.indexOf(".");
22529
+ if (dot <= 0 || dot === body2.length - 1)
22530
+ return { ok: false, code: "run_token_malformed" };
22531
+ const bodyText = body2.slice(0, dot);
22532
+ let text5;
22533
+ try {
22534
+ text5 = Buffer.from(bodyText, "base64url").toString("utf8");
22535
+ } catch {
22536
+ return { ok: false, code: "run_token_malformed" };
22537
+ }
22538
+ const presented = Buffer.from(body2.slice(dot + 1), "utf8");
22539
+ const expected = Buffer.from(
22540
+ sign(options.key, text5).toString("base64url"),
22541
+ "utf8"
22542
+ );
22543
+ if (presented.length !== expected.length || !timingSafeEqual(presented, expected) || Buffer.from(text5, "utf8").toString("base64url") !== bodyText) {
22544
+ return { ok: false, code: "run_token_invalid", ...peeked(text5) };
22545
+ }
22546
+ let parsed2;
22547
+ try {
22548
+ parsed2 = JSON.parse(text5);
22549
+ } catch {
22550
+ return { ok: false, code: "run_token_malformed" };
22551
+ }
22552
+ const claims = runTokenClaimsSchema.safeParse(parsed2);
22553
+ if (!claims.success) return { ok: false, code: "run_token_malformed" };
22554
+ if (jcs(claims.data) !== text5)
22555
+ return { ok: false, code: "run_token_invalid" };
22556
+ if (claims.data.exp <= options.now)
22557
+ return { ok: false, code: "run_token_expired", claims: claims.data };
22558
+ if (claims.data.host !== options.host || claims.data.provider !== options.provider)
22559
+ return { ok: false, code: "run_token_mismatch", claims: claims.data };
22560
+ return { ok: true, claims: claims.data };
22561
+ }
22562
+ function peeked(text5) {
22563
+ try {
22564
+ const claims = runTokenClaimsSchema.safeParse(JSON.parse(text5));
22565
+ return claims.success ? { claims: claims.data } : {};
22566
+ } catch {
22567
+ return {};
22568
+ }
22569
+ }
22570
+ var RUN_TOKEN_PREFIX, RUN_TOKEN_MAX_TTL_MS, RUN_TOKEN_DEFAULT_TTL_MS, RUN_TOKEN_STATIC_MAX_TTL_MS, RUN_TOKEN_PROVIDERS, RUN_TOKEN_PLACEMENTS, runTokenClaimsSchema, KEY_BYTES, keysSeen;
22571
+ var init_run_token = __esm({
22572
+ "../../packages/tacho/src/host/run-token.ts"() {
22573
+ "use strict";
22574
+ init_zod();
22575
+ init_digest();
22576
+ init_fs();
22577
+ RUN_TOKEN_PREFIX = "oxrt_";
22578
+ RUN_TOKEN_MAX_TTL_MS = 15 * 6e4;
22579
+ RUN_TOKEN_DEFAULT_TTL_MS = RUN_TOKEN_MAX_TTL_MS;
22580
+ RUN_TOKEN_STATIC_MAX_TTL_MS = 30 * 24 * 60 * 6e4;
22581
+ RUN_TOKEN_PROVIDERS = ["anthropic", "openai"];
22582
+ RUN_TOKEN_PLACEMENTS = ["helper", "static"];
22583
+ runTokenClaimsSchema = external_exports.object({
22584
+ v: external_exports.literal(1),
22585
+ /** `rt_` + 20 hex: cited on frames in place of the token. */
22586
+ tid: external_exports.string().regex(/^rt_[0-9a-f]{20}$/),
22587
+ /** The host enrollment the token was minted for. */
22588
+ host: external_exports.string().min(1).max(64),
22589
+ /** The harness it was handed to. */
22590
+ harness: external_exports.string().min(1).max(32),
22591
+ provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
22592
+ placement: external_exports.enum(RUN_TOKEN_PLACEMENTS),
22593
+ /** Epoch milliseconds. */
22594
+ iat: external_exports.number().int().nonnegative(),
22595
+ exp: external_exports.number().int().nonnegative()
22596
+ }).strict();
22597
+ KEY_BYTES = 32;
22598
+ keysSeen = /* @__PURE__ */ new Map();
22599
+ }
22600
+ });
22601
+
22602
+ // ../../packages/tacho/src/host/credential-store.ts
22603
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
22604
+ import {
22605
+ closeSync as closeSync4,
22606
+ existsSync as existsSync5,
22607
+ fsyncSync as fsyncSync3,
22608
+ openSync as openSync4,
22609
+ readFileSync as readFileSync7,
22610
+ statSync as statSync3,
22611
+ unlinkSync as unlinkSync4,
22612
+ writeSync as writeSync3
22613
+ } from "node:fs";
22614
+ function anthropicCredentialKind(secret, fallback) {
22615
+ return secret.trim().startsWith(CLAUDE_OAUTH_TOKEN_PREFIX) ? "oauth" : fallback;
22616
+ }
22617
+ function readKey(path2) {
22618
+ try {
22619
+ const hex = readFileSync7(path2, "utf8").trim();
22620
+ if (!/^[0-9a-f]{64}$/i.test(hex))
22621
+ throw new Error(`${path2} does not hold a 32-byte key`);
22622
+ return Buffer.from(hex, "hex");
22623
+ } catch (error) {
22624
+ if (error.code === "ENOENT") return void 0;
22625
+ throw error;
22626
+ }
22627
+ }
22628
+ function loadOrCreateKey(path2) {
22629
+ const existing = readKey(path2);
22630
+ if (existing !== void 0) return existing;
22631
+ const key = randomBytes5(KEY_BYTES2);
22632
+ writeSensitiveFileAtomic(path2, `${key.toString("hex")}
22633
+ `);
22634
+ return key;
22635
+ }
22636
+ function requireKey(path2) {
22637
+ const key = readKey(path2);
22638
+ if (key === void 0)
22639
+ throw new Error(
22640
+ `${path2} is missing, so the credentials in custody cannot be opened`
22641
+ );
22642
+ return key;
22643
+ }
22644
+ function overwriteInPlace(path2) {
22645
+ let fd2;
22646
+ try {
22647
+ const size = statSync3(path2).size;
22648
+ fd2 = openSync4(path2, "r+");
22649
+ writeSync3(fd2, randomBytes5(Math.max(size, KEY_BYTES2 * 2)), 0, void 0, 0);
22650
+ fsyncSync3(fd2);
22651
+ } catch (error) {
22652
+ if (error.code !== "ENOENT") throw error;
22653
+ } finally {
22654
+ if (fd2 !== void 0) closeSync4(fd2);
22655
+ }
22656
+ }
22657
+ function seal(key, secret, aad) {
22658
+ const nonce = randomBytes5(12);
22659
+ const cipher = createCipheriv("aes-256-gcm", key, nonce);
22660
+ cipher.setAAD(Buffer.from(aad, "utf8"));
22661
+ const ciphertext = Buffer.concat([
22662
+ cipher.update(secret, "utf8"),
22663
+ cipher.final()
22664
+ ]);
22665
+ return {
22666
+ nonce: nonce.toString("base64"),
22667
+ ciphertext: ciphertext.toString("base64"),
22668
+ tag: cipher.getAuthTag().toString("base64")
22669
+ };
22670
+ }
22671
+ function open(key, sealed, aad) {
22672
+ const decipher = createDecipheriv(
22673
+ "aes-256-gcm",
22674
+ key,
22675
+ Buffer.from(sealed.nonce, "base64")
22676
+ );
22677
+ decipher.setAAD(Buffer.from(aad, "utf8"));
22678
+ decipher.setAuthTag(Buffer.from(sealed.tag, "base64"));
22679
+ return Buffer.concat([
22680
+ decipher.update(Buffer.from(sealed.ciphertext, "base64")),
22681
+ decipher.final()
22682
+ ]).toString("utf8");
22683
+ }
22684
+ function credentialPrefix(secret) {
22685
+ const head2 = secret.slice(0, Math.min(8, Math.max(0, secret.length - 4)));
22686
+ return head2.length > 0 ? `${head2}\u2026` : "";
22687
+ }
22688
+ function custodyOf(entry2) {
22689
+ return {
22690
+ provider: entry2.provider,
22691
+ kind: entry2.kind,
22692
+ source: entry2.source,
22693
+ taken_at: entry2.taken_at,
22694
+ prefix: entry2.prefix
22695
+ };
22696
+ }
22697
+ function openCredentialStore(paths) {
22698
+ let entriesSeen;
22699
+ let keySeen;
22700
+ const keyForWrite = () => loadOrCreateKey(paths.key);
22701
+ function forgetKey() {
22702
+ keySeen?.key.fill(0);
22703
+ keySeen = void 0;
22704
+ }
22705
+ function keyForRead() {
22706
+ const version2 = fileVersion(paths.key);
22707
+ if (version2 !== void 0 && keySeen?.version === version2)
22708
+ return keySeen.key;
22709
+ forgetKey();
22710
+ const key = requireKey(paths.key);
22711
+ if (version2 !== void 0) keySeen = { version: version2, key };
22712
+ return key;
22713
+ }
22714
+ function readAll() {
22715
+ const version2 = fileVersion(paths.file);
22716
+ if (version2 === void 0) {
22717
+ entriesSeen = void 0;
22718
+ forgetKey();
22719
+ return [];
22720
+ }
22721
+ if (entriesSeen?.version === version2) return entriesSeen.entries;
22722
+ let text5;
22723
+ try {
22724
+ text5 = readFileSync7(paths.file, "utf8");
22725
+ } catch (error) {
22726
+ if (error.code === "ENOENT") return [];
22727
+ throw error;
22728
+ }
22729
+ let raw;
22730
+ try {
22731
+ raw = JSON.parse(text5);
22732
+ } catch (error) {
22733
+ throw new Error(
22734
+ `${paths.file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
22735
+ );
22736
+ }
22737
+ const parsed2 = fileSchema.safeParse(raw);
22738
+ if (!parsed2.success)
22739
+ throw new Error(
22740
+ `${paths.file} is not a credential store this build can read: ${parsed2.error.issues[0]?.message ?? "invalid"}`
22741
+ );
22742
+ entriesSeen = { version: version2, entries: parsed2.data.entries };
22743
+ return parsed2.data.entries;
22744
+ }
22745
+ function writeAll2(entries2) {
22746
+ if (entries2.length === 0) {
22747
+ if (existsSync5(paths.file)) unlinkSync4(paths.file);
22748
+ return;
22749
+ }
22750
+ writeSensitiveFileAtomic(
22751
+ paths.file,
22752
+ `${JSON.stringify({ schema: CREDENTIAL_STORE_SCHEMA, entries: entries2 }, null, 2)}
22753
+ `
22754
+ );
22755
+ }
22756
+ return {
22757
+ has: (provider) => readAll().some((e) => e.provider === provider),
22758
+ read: (provider) => {
22759
+ const entry2 = readAll().find((e) => e.provider === provider);
22760
+ if (entry2 === void 0) return void 0;
22761
+ return {
22762
+ kind: entry2.kind,
22763
+ secret: open(
22764
+ keyForRead(),
22765
+ entry2.sealed,
22766
+ `${CREDENTIAL_STORE_SCHEMA}:${provider}`
22767
+ )
22768
+ };
22769
+ },
22770
+ take: (provider, { kind, secret }, source, now) => {
22771
+ if (secret.trim().length === 0)
22772
+ throw new Error("refusing to take an empty credential into custody");
22773
+ const entry2 = {
22774
+ provider,
22775
+ kind,
22776
+ source,
22777
+ taken_at: new Date(now).toISOString(),
22778
+ // A ChatGPT login is JSON, whose head says nothing; it is named.
22779
+ prefix: kind === "chatgpt" ? "login" : credentialPrefix(secret),
22780
+ sealed: seal(
22781
+ keyForWrite(),
22782
+ secret,
22783
+ `${CREDENTIAL_STORE_SCHEMA}:${provider}`
22784
+ )
22785
+ };
22786
+ writeAll2([...readAll().filter((e) => e.provider !== provider), entry2]);
22787
+ return custodyOf(entry2);
22788
+ },
22789
+ release: (provider) => {
22790
+ const entries2 = readAll();
22791
+ const entry2 = entries2.find((e) => e.provider === provider);
22792
+ if (entry2 === void 0) return void 0;
22793
+ const secret = open(
22794
+ keyForRead(),
22795
+ entry2.sealed,
22796
+ `${CREDENTIAL_STORE_SCHEMA}:${provider}`
22797
+ );
22798
+ writeAll2(entries2.filter((e) => e.provider !== provider));
22799
+ return { kind: entry2.kind, secret };
22800
+ },
22801
+ status: () => readAll().map(custodyOf),
22802
+ shred: () => {
22803
+ let overwrite;
22804
+ try {
22805
+ overwriteInPlace(paths.key);
22806
+ } catch (error) {
22807
+ overwrite = error;
22808
+ }
22809
+ for (const path2 of [paths.file, paths.key])
22810
+ if (existsSync5(path2)) unlinkSync4(path2);
22811
+ entriesSeen = void 0;
22812
+ forgetKey();
22813
+ if (overwrite !== void 0)
22814
+ throw new Error(
22815
+ `${paths.key} was deleted but could not be overwritten first: ${overwrite instanceof Error ? overwrite.message : String(overwrite)}`
22816
+ );
22817
+ }
22818
+ };
22819
+ }
22820
+ var CREDENTIAL_STORE_SCHEMA, CREDENTIAL_SOURCES, CREDENTIAL_KINDS, CLAUDE_OAUTH_TOKEN_PREFIX, sealedSchema, entrySchema, fileSchema, KEY_BYTES2;
22821
+ var init_credential_store = __esm({
22822
+ "../../packages/tacho/src/host/credential-store.ts"() {
22823
+ "use strict";
22824
+ init_zod();
22825
+ init_fs();
22826
+ init_run_token();
22827
+ CREDENTIAL_STORE_SCHEMA = "tacho.credentials.v1";
22828
+ CREDENTIAL_SOURCES = [
22829
+ /** `env.ANTHROPIC_API_KEY` in Claude Code's user settings. */
22830
+ "claude-code:settings.env",
22831
+ /** `OPENAI_API_KEY`, or the ChatGPT login's `tokens`, in Codex's `auth.json`. */
22832
+ "codex:auth.json",
22833
+ /** A ChatGPT login the gateway refreshed, replacing the one it took. */
22834
+ "chatgpt:refresh",
22835
+ /**
22836
+ * `TACHO_BROKER_<PROVIDER>_API_KEY`, or `CLAUDE_CODE_OAUTH_TOKEN`, in the
22837
+ * enrolling shell.
22838
+ */
22839
+ "enroll:env",
22840
+ /**
22841
+ * The subscription token `claude setup-token` printed, which
22842
+ * `oxagen credential login` took straight into custody.
22843
+ */
22844
+ "claude:setup-token",
22845
+ /**
22846
+ * The run token in an Oxagen Agent Runners sandbox's `launch` message. It
22847
+ * is spent at the cloud gateway, which attaches the operator's model
22848
+ * credential. It is not a model credential itself.
22849
+ */
22850
+ "sandbox:launch"
22851
+ ];
22852
+ CREDENTIAL_KINDS = [
22853
+ "api_key",
22854
+ "bearer",
22855
+ "oauth",
22856
+ "chatgpt"
22857
+ ];
22858
+ CLAUDE_OAUTH_TOKEN_PREFIX = "sk-ant-oat";
22859
+ sealedSchema = external_exports.object({
22860
+ /** AES-256-GCM: 12-byte nonce, ciphertext, 16-byte tag, all base64. */
22861
+ nonce: external_exports.string().min(1),
22862
+ ciphertext: external_exports.string().min(1),
22863
+ tag: external_exports.string().min(1)
22864
+ }).strict();
22865
+ entrySchema = external_exports.object({
22866
+ provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
22867
+ kind: external_exports.enum(CREDENTIAL_KINDS),
22868
+ source: external_exports.enum(CREDENTIAL_SOURCES),
22869
+ taken_at: external_exports.string(),
22870
+ /** The first few characters, the way a vendor console shows a key. */
22871
+ prefix: external_exports.string().max(12),
22872
+ sealed: sealedSchema
22873
+ }).strict();
22874
+ fileSchema = external_exports.object({
22875
+ schema: external_exports.literal(CREDENTIAL_STORE_SCHEMA),
22876
+ entries: external_exports.array(entrySchema)
22877
+ }).strict();
22878
+ KEY_BYTES2 = 32;
22879
+ }
22880
+ });
22881
+
22882
+ // ../../packages/tacho/src/host/claude-setup-token.ts
22883
+ import { spawn } from "node:child_process";
22884
+ import { accessSync as accessSync2, constants as constants2 } from "node:fs";
22885
+ import { delimiter, join as join13 } from "node:path";
22886
+ function isSubscriptionToken(value) {
22887
+ return TOKEN.test(value.trim());
22888
+ }
22889
+ function extractSetupToken(output) {
22890
+ const lines = output.replace(ANSI, "").split(/\r?\n|\r/).map((line) => line.trim());
22891
+ let found;
22892
+ for (let i2 = 0; i2 < lines.length; i2++) {
22893
+ const line = lines[i2];
22894
+ const start = line.lastIndexOf(CLAUDE_OAUTH_TOKEN_PREFIX);
22895
+ if (start < 0) continue;
22896
+ let token = "";
22897
+ for (const char of line.slice(start)) {
22898
+ if (!TOKEN_CHAR.test(char)) break;
22899
+ token += char;
22900
+ }
22901
+ if (token.length === line.length - start)
22902
+ for (let j = i2 + 1; j < lines.length; j++) {
22903
+ const next = lines[j];
22904
+ if (next.length === 0 || ![...next].every((c) => TOKEN_CHAR.test(c)))
22905
+ break;
22906
+ token += next;
22907
+ }
22908
+ if (isSubscriptionToken(token)) found = token;
22909
+ }
22910
+ return found;
22911
+ }
22912
+ function findClaudeBinary(env, home) {
22913
+ return findHarnessBinary("claude", env, home);
22914
+ }
22915
+ function findHarnessBinary(name, env, home) {
22916
+ const file = process.platform === "win32" ? `${name}.exe` : name;
22917
+ const candidates = [
22918
+ ...(env["PATH"] ?? "").split(delimiter).filter((d) => d.length > 0),
22919
+ join13(home, ".local", "bin"),
22920
+ join13(home, ".claude", "local"),
22921
+ join13(home, ".npm-global", "bin"),
22922
+ "/opt/homebrew/bin",
22923
+ "/usr/local/bin"
22924
+ ].map((dir) => join13(dir, file));
22925
+ for (const path2 of candidates) {
22926
+ try {
22927
+ accessSync2(path2, constants2.X_OK);
22928
+ return path2;
22929
+ } catch {
22930
+ }
22931
+ }
22932
+ return void 0;
22933
+ }
22934
+ function setupTokenCommand(claude, platform) {
22935
+ const quoted3 = `'${claude.replace(/'/g, `'\\''`)}'`;
22936
+ const inner = `stty cols 1000 2>/dev/null; exec ${quoted3} setup-token`;
22937
+ if (platform === "darwin")
22938
+ return { file: "script", args: ["-q", "/dev/null", "sh", "-c", inner] };
22939
+ if (platform === "linux")
22940
+ return { file: "script", args: ["-q", "-e", "-c", inner, "/dev/null"] };
22941
+ return void 0;
22942
+ }
22943
+ function runClaudeSetupToken(options) {
22944
+ const command2 = setupTokenCommand(
22945
+ options.claude,
22946
+ options.platform ?? process.platform
22947
+ );
22948
+ return new Promise((resolve25, reject) => {
22949
+ if (command2 === void 0) {
22950
+ const child2 = spawn(options.claude, ["setup-token"], {
22951
+ stdio: "inherit",
22952
+ env: options.env ?? process.env
22953
+ });
22954
+ child2.on("error", reject);
22955
+ child2.on(
22956
+ "close",
22957
+ (code) => resolve25({ token: void 0, code: code ?? 1, captured: false })
22958
+ );
22959
+ return;
22960
+ }
22961
+ const child = spawn(command2.file, command2.args, {
22962
+ stdio: [options.interactive ? "inherit" : "pipe", "pipe", "inherit"],
22963
+ env: options.env ?? process.env
22964
+ });
22965
+ let output = "";
22966
+ child.stdout?.setEncoding("utf8");
22967
+ child.stdout?.on("data", (chunk2) => {
22968
+ output += chunk2;
22969
+ options.write(chunk2);
22970
+ });
22971
+ child.on("error", reject);
22972
+ child.on("close", (code) => {
22973
+ child.stdin?.destroy();
22974
+ resolve25({
22975
+ token: extractSetupToken(output),
22976
+ code: code ?? 1,
22977
+ captured: true
22978
+ });
22979
+ });
22980
+ });
22981
+ }
22982
+ var TOKEN, TOKEN_CHAR, ANSI;
22983
+ var init_claude_setup_token = __esm({
22984
+ "../../packages/tacho/src/host/claude-setup-token.ts"() {
22985
+ "use strict";
22986
+ init_credential_store();
22987
+ TOKEN = /^sk-ant-oat\d{2}-[A-Za-z0-9_-]{20,}$/;
22988
+ TOKEN_CHAR = /[A-Za-z0-9_-]/;
22989
+ ANSI = // biome-ignore lint/suspicious/noControlCharactersInRegex: terminal escapes
22990
+ /\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|\u001b\[[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_]/g;
22991
+ }
22992
+ });
22993
+
22425
22994
  // ../../packages/tacho/src/host/model-base-url-toml.ts
22426
22995
  function splitLines(text5) {
22427
22996
  const lines = [];
@@ -22539,8 +23108,8 @@ var init_model_base_url_toml = __esm({
22539
23108
  });
22540
23109
 
22541
23110
  // ../../packages/tacho/src/host/model-base-url.ts
22542
- import { chmodSync as chmodSync3, existsSync as existsSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync4 } from "node:fs";
22543
- import { basename as basename4, dirname as dirname7, join as join13 } from "node:path";
23111
+ import { chmodSync as chmodSync3, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync as unlinkSync5 } from "node:fs";
23112
+ import { basename as basename4, dirname as dirname7, join as join14 } from "node:path";
22544
23113
  function claudeToolSearchEnabled(value) {
22545
23114
  return typeof value === "string" && /^(true|auto(:\d+)?)$/i.test(value);
22546
23115
  }
@@ -22554,19 +23123,19 @@ function isModelProxyBaseUrl(harness, value) {
22554
23123
  }
22555
23124
  function modelBaseUrlFile(harness, homes) {
22556
23125
  if (harness === "claude-code")
22557
- return join13(
23126
+ return join14(
22558
23127
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
22559
23128
  "settings.json"
22560
23129
  );
22561
23130
  if (harness === "codex")
22562
- return join13(
23131
+ return join14(
22563
23132
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
22564
23133
  "config.toml"
22565
23134
  );
22566
- const stellaHome = homes.stellaHome ?? join13(homes.home, ".stella");
22567
- const toml = join13(stellaHome, "stella.toml");
22568
- const json2 = join13(stellaHome, "settings.json");
22569
- return !existsSync5(toml) && existsSync5(json2) ? json2 : toml;
23135
+ const stellaHome = homes.stellaHome ?? join14(homes.home, ".stella");
23136
+ const toml = join14(stellaHome, "stella.toml");
23137
+ const json2 = join14(stellaHome, "settings.json");
23138
+ return !existsSync6(toml) && existsSync6(json2) ? json2 : toml;
22570
23139
  }
22571
23140
  function keyLabel(harness) {
22572
23141
  if (harness === "claude-code") return `env.${CLAUDE_KEY}`;
@@ -22576,7 +23145,7 @@ function isJsonFile(file) {
22576
23145
  return file.endsWith(".json");
22577
23146
  }
22578
23147
  function sidecarBeside(file, contract) {
22579
- return join13(dirname7(file), `.${basename4(file)}.${contract}.json`);
23148
+ return join14(dirname7(file), `.${basename4(file)}.${contract}.json`);
22580
23149
  }
22581
23150
  function sidecarFor(file) {
22582
23151
  return sidecarBeside(file, "oxagen-model-base-url");
@@ -22707,9 +23276,9 @@ function stellaManagedSettingsPath(platform = process.platform, env = process.en
22707
23276
  const named = env["STELLA_MANAGED_SETTINGS"];
22708
23277
  if (named !== void 0 && named.length > 0) return named;
22709
23278
  const dir = platform === "darwin" ? "/Library/Application Support/stella" : "/etc/stella";
22710
- const toml = join13(dir, "stella.toml");
22711
- const json2 = join13(dir, "settings.json");
22712
- return !existsSync5(toml) && existsSync5(json2) ? json2 : toml;
23279
+ const toml = join14(dir, "stella.toml");
23280
+ const json2 = join14(dir, "settings.json");
23281
+ return !existsSync6(toml) && existsSync6(json2) ? json2 : toml;
22713
23282
  }
22714
23283
  function managedShadow(harness, managedFile, expected) {
22715
23284
  try {
@@ -22917,7 +23486,7 @@ function describe3(harness, options, { changed, text: text5, sidecar, unreadable
22917
23486
  }
22918
23487
  function readBytesIfExists(path2) {
22919
23488
  try {
22920
- return readFileSync6(path2);
23489
+ return readFileSync8(path2);
22921
23490
  } catch (error) {
22922
23491
  if (error.code === "ENOENT") return void 0;
22923
23492
  throw error;
@@ -23031,7 +23600,7 @@ function restoreOne(harness, options) {
23031
23600
  const { sidecar, unreadable } = sidecarOrWhy(backup, harness);
23032
23601
  const text5 = readTextIfExists(file);
23033
23602
  const dropSidecar = () => {
23034
- if (existsSync5(backup)) unlinkSync4(backup);
23603
+ if (existsSync6(backup)) unlinkSync5(backup);
23035
23604
  };
23036
23605
  const after = (changed, left) => ({
23037
23606
  changed,
@@ -23050,7 +23619,7 @@ function restoreOne(harness, options) {
23050
23619
  writeFileAtomicPreserving(file, original);
23051
23620
  left = original.toString("utf8");
23052
23621
  } else {
23053
- unlinkSync4(file);
23622
+ unlinkSync5(file);
23054
23623
  }
23055
23624
  dropSidecar();
23056
23625
  return after(true, left);
@@ -23163,186 +23732,66 @@ var init_model_base_url = __esm({
23163
23732
  }
23164
23733
  });
23165
23734
 
23166
- // ../../packages/tacho/src/host/run-token.ts
23167
- import { createHmac, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
23168
- import { readFileSync as readFileSync7 } from "node:fs";
23169
- function keyFromBytes(bytes) {
23170
- if (bytes.length !== KEY_BYTES)
23171
- throw new Error(
23172
- `run token key must be ${KEY_BYTES} bytes, got ${bytes.length}`
23173
- );
23174
- const id2 = createHmac("sha256", "tacho.run-token-key-id").update(bytes).digest("hex").slice(0, 16);
23175
- return { bytes, id: id2 };
23176
- }
23177
- function generateRunTokenKey() {
23178
- return keyFromBytes(randomBytes4(KEY_BYTES));
23179
- }
23180
- function runTokenKeyFromHex(hex) {
23181
- const trimmed = hex.trim();
23182
- if (!/^[0-9a-f]{64}$/i.test(trimmed))
23183
- throw new Error("run token key file does not hold 64 hex characters");
23184
- return keyFromBytes(Buffer.from(trimmed, "hex"));
23735
+ // ../../packages/tacho/src/host/model-credential.ts
23736
+ import { chmodSync as chmodSync4, existsSync as existsSync7, lstatSync as lstatSync2, unlinkSync as unlinkSync6 } from "node:fs";
23737
+ import { join as join15 } from "node:path";
23738
+ function claudeMemberFor(kind) {
23739
+ return kind === "oauth" ? CLAUDE_OAUTH_TOKEN : kind === "bearer" ? CLAUDE_AUTH_TOKEN : CLAUDE_API_KEY;
23185
23740
  }
23186
- function loadOrCreateRunTokenKey(path2) {
23187
- try {
23188
- return {
23189
- key: runTokenKeyFromHex(readFileSync7(path2, "utf8")),
23190
- created: false
23191
- };
23192
- } catch (error) {
23193
- if (error.code !== "ENOENT") throw error;
23741
+ function claudeEnvCredentials(env) {
23742
+ const found = [];
23743
+ for (const [member3, kind] of CLAUDE_MEMBERS) {
23744
+ const value = env[member3];
23745
+ if (isSecret(value))
23746
+ found.push({
23747
+ member: member3,
23748
+ credential: {
23749
+ kind: anthropicCredentialKind(value, kind),
23750
+ secret: value
23751
+ }
23752
+ });
23194
23753
  }
23195
- const key = generateRunTokenKey();
23196
- writeSensitiveFileAtomic(path2, `${key.bytes.toString("hex")}
23197
- `);
23198
- return { key, created: true };
23754
+ return found;
23199
23755
  }
23200
- function readRunTokenKey(path2) {
23201
- const version2 = fileVersion(path2);
23202
- if (version2 === void 0) {
23203
- keysSeen.delete(path2);
23756
+ function chatGptTokensOf(auth) {
23757
+ const tokens = auth[CODEX_TOKENS];
23758
+ if (typeof tokens !== "object" || tokens === null || Array.isArray(tokens))
23204
23759
  return void 0;
23205
- }
23206
- const seen = keysSeen.get(path2);
23207
- if (seen?.version === version2) return seen.key;
23208
- let key;
23209
- try {
23210
- key = runTokenKeyFromHex(readFileSync7(path2, "utf8"));
23211
- } catch (error) {
23212
- if (error.code === "ENOENT") return void 0;
23213
- throw error;
23214
- }
23215
- keysSeen.set(path2, { version: version2, key });
23216
- return key;
23217
- }
23218
- function b64url(bytes) {
23219
- return bytes.toString("base64url");
23220
- }
23221
- function sign(key, claimsText) {
23222
- return createHmac("sha256", key.bytes).update(claimsText, "utf8").digest();
23760
+ const record = tokens;
23761
+ return isSecret(record["access_token"]) ? record : void 0;
23223
23762
  }
23224
- function mintRunToken(options) {
23225
- const ceiling = options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_MAX_TTL_MS;
23226
- const asked = options.ttlMs === void 0 ? options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_DEFAULT_TTL_MS : options.ttlMs;
23227
- if (!Number.isFinite(asked) || asked <= 0)
23228
- throw new Error(`run token ttl must be positive, got ${String(asked)}`);
23229
- let exp = options.now + Math.min(asked, ceiling);
23230
- if (options.notAfter !== void 0) exp = Math.min(exp, options.notAfter);
23231
- if (exp <= options.now)
23232
- throw new Error("run token would already be expired when minted");
23233
- const claims = {
23234
- v: 1,
23235
- tid: `rt_${randomBytes4(10).toString("hex")}`,
23236
- host: options.host,
23237
- harness: options.harness,
23238
- provider: options.provider,
23239
- placement: options.placement,
23240
- iat: options.now,
23241
- exp
23763
+ function encodeChatGptLogin(tokens) {
23764
+ const login = {
23765
+ access_token: tokens["access_token"],
23766
+ refresh_token: typeof tokens["refresh_token"] === "string" ? tokens["refresh_token"] : ""
23242
23767
  };
23243
- const text5 = jcs(claims);
23244
- const token = `${RUN_TOKEN_PREFIX}${b64url(Buffer.from(text5, "utf8"))}.${b64url(sign(options.key, text5))}`;
23245
- return { token, claims };
23246
- }
23247
- function looksLikeRunToken(value) {
23248
- return typeof value === "string" && value.startsWith(RUN_TOKEN_PREFIX) && value.length < 2048;
23768
+ if (typeof tokens["id_token"] === "string")
23769
+ login.id_token = tokens["id_token"];
23770
+ if (typeof tokens["account_id"] === "string")
23771
+ login.account_id = tokens["account_id"];
23772
+ return JSON.stringify(login);
23249
23773
  }
23250
- function peekRunTokenClaims(token) {
23251
- if (!looksLikeRunToken(token)) return void 0;
23252
- const body2 = token.slice(RUN_TOKEN_PREFIX.length);
23253
- const dot = body2.indexOf(".");
23254
- if (dot <= 0) return void 0;
23774
+ function decodeChatGptLogin(secret) {
23255
23775
  try {
23256
- const parsed2 = JSON.parse(
23257
- Buffer.from(body2.slice(0, dot), "base64url").toString("utf8")
23258
- );
23259
- const claims = runTokenClaimsSchema.safeParse(parsed2);
23260
- return claims.success ? claims.data : void 0;
23776
+ const raw = JSON.parse(secret);
23777
+ if (typeof raw.access_token !== "string" || raw.access_token.length === 0)
23778
+ return void 0;
23779
+ return {
23780
+ access_token: raw.access_token,
23781
+ refresh_token: typeof raw.refresh_token === "string" ? raw.refresh_token : "",
23782
+ ...typeof raw.id_token === "string" ? { id_token: raw.id_token } : {},
23783
+ ...typeof raw.account_id === "string" ? { account_id: raw.account_id } : {}
23784
+ };
23261
23785
  } catch {
23262
23786
  return void 0;
23263
23787
  }
23264
23788
  }
23265
- function verifyRunToken(token, options) {
23266
- if (!looksLikeRunToken(token))
23267
- return { ok: false, code: "run_token_malformed" };
23268
- const body2 = token.slice(RUN_TOKEN_PREFIX.length);
23269
- const dot = body2.indexOf(".");
23270
- if (dot <= 0 || dot === body2.length - 1)
23271
- return { ok: false, code: "run_token_malformed" };
23272
- const bodyText = body2.slice(0, dot);
23273
- let text5;
23274
- try {
23275
- text5 = Buffer.from(bodyText, "base64url").toString("utf8");
23276
- } catch {
23277
- return { ok: false, code: "run_token_malformed" };
23278
- }
23279
- const presented = Buffer.from(body2.slice(dot + 1), "utf8");
23280
- const expected = Buffer.from(
23281
- sign(options.key, text5).toString("base64url"),
23282
- "utf8"
23283
- );
23284
- if (presented.length !== expected.length || !timingSafeEqual(presented, expected) || Buffer.from(text5, "utf8").toString("base64url") !== bodyText) {
23285
- return { ok: false, code: "run_token_invalid", ...peeked(text5) };
23286
- }
23287
- let parsed2;
23288
- try {
23289
- parsed2 = JSON.parse(text5);
23290
- } catch {
23291
- return { ok: false, code: "run_token_malformed" };
23292
- }
23293
- const claims = runTokenClaimsSchema.safeParse(parsed2);
23294
- if (!claims.success) return { ok: false, code: "run_token_malformed" };
23295
- if (jcs(claims.data) !== text5)
23296
- return { ok: false, code: "run_token_invalid" };
23297
- if (claims.data.exp <= options.now)
23298
- return { ok: false, code: "run_token_expired", claims: claims.data };
23299
- if (claims.data.host !== options.host || claims.data.provider !== options.provider)
23300
- return { ok: false, code: "run_token_mismatch", claims: claims.data };
23301
- return { ok: true, claims: claims.data };
23302
- }
23303
- function peeked(text5) {
23304
- try {
23305
- const claims = runTokenClaimsSchema.safeParse(JSON.parse(text5));
23306
- return claims.success ? { claims: claims.data } : {};
23307
- } catch {
23308
- return {};
23309
- }
23789
+ function codexTokenMember(auth) {
23790
+ const tokens = chatGptTokensOf(auth);
23791
+ if (tokens !== void 0) return tokens["access_token"];
23792
+ const key = auth[CODEX_KEY2];
23793
+ return typeof key === "string" ? key : void 0;
23310
23794
  }
23311
- var RUN_TOKEN_PREFIX, RUN_TOKEN_MAX_TTL_MS, RUN_TOKEN_DEFAULT_TTL_MS, RUN_TOKEN_STATIC_MAX_TTL_MS, RUN_TOKEN_PROVIDERS, RUN_TOKEN_PLACEMENTS, runTokenClaimsSchema, KEY_BYTES, keysSeen;
23312
- var init_run_token = __esm({
23313
- "../../packages/tacho/src/host/run-token.ts"() {
23314
- "use strict";
23315
- init_zod();
23316
- init_digest();
23317
- init_fs();
23318
- RUN_TOKEN_PREFIX = "oxrt_";
23319
- RUN_TOKEN_MAX_TTL_MS = 15 * 6e4;
23320
- RUN_TOKEN_DEFAULT_TTL_MS = RUN_TOKEN_MAX_TTL_MS;
23321
- RUN_TOKEN_STATIC_MAX_TTL_MS = 30 * 24 * 60 * 6e4;
23322
- RUN_TOKEN_PROVIDERS = ["anthropic", "openai"];
23323
- RUN_TOKEN_PLACEMENTS = ["helper", "static"];
23324
- runTokenClaimsSchema = external_exports.object({
23325
- v: external_exports.literal(1),
23326
- /** `rt_` + 20 hex: cited on frames in place of the token. */
23327
- tid: external_exports.string().regex(/^rt_[0-9a-f]{20}$/),
23328
- /** The host enrollment the token was minted for. */
23329
- host: external_exports.string().min(1).max(64),
23330
- /** The harness it was handed to. */
23331
- harness: external_exports.string().min(1).max(32),
23332
- provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
23333
- placement: external_exports.enum(RUN_TOKEN_PLACEMENTS),
23334
- /** Epoch milliseconds. */
23335
- iat: external_exports.number().int().nonnegative(),
23336
- exp: external_exports.number().int().nonnegative()
23337
- }).strict();
23338
- KEY_BYTES = 32;
23339
- keysSeen = /* @__PURE__ */ new Map();
23340
- }
23341
- });
23342
-
23343
- // ../../packages/tacho/src/host/model-credential.ts
23344
- import { chmodSync as chmodSync4, existsSync as existsSync6, lstatSync as lstatSync2, unlinkSync as unlinkSync5 } from "node:fs";
23345
- import { join as join14 } from "node:path";
23346
23795
  function isTachoHelper(value) {
23347
23796
  return typeof value === "string" && HELPER_MARK.test(value);
23348
23797
  }
@@ -23350,10 +23799,10 @@ function helperCommandFor(tachoCommand) {
23350
23799
  return `${tachoCommand} credential issue --harness claude-code`;
23351
23800
  }
23352
23801
  function fileFor2(harness, homes) {
23353
- return harness === "claude-code" ? join14(
23802
+ return harness === "claude-code" ? join15(
23354
23803
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
23355
23804
  "settings.json"
23356
- ) : join14(
23805
+ ) : join15(
23357
23806
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
23358
23807
  "auth.json"
23359
23808
  );
@@ -23396,7 +23845,9 @@ function readSidecar2(path2, harness) {
23396
23845
  existed: parsed2["existed"] === true,
23397
23846
  written_sha256: written2,
23398
23847
  taken: Array.isArray(taken) ? taken.filter(
23399
- (t) => typeof t === "object" && t !== null && typeof t.member === "string" && (t.kind === "api_key" || t.kind === "bearer")
23848
+ (t) => typeof t === "object" && t !== null && typeof t.member === "string" && CREDENTIAL_KINDS.includes(
23849
+ t.kind
23850
+ )
23400
23851
  ) : []
23401
23852
  };
23402
23853
  if (harness === "codex") return { ...common, harness };
@@ -23419,7 +23870,7 @@ function isSecret(value) {
23419
23870
  return typeof value === "string" && value.length > 0;
23420
23871
  }
23421
23872
  function holdsBothClaudeMembers(env) {
23422
- return isSecret(env[CLAUDE_API_KEY]) && isSecret(env[CLAUDE_AUTH_TOKEN]);
23873
+ return CLAUDE_MEMBERS.filter(([member3]) => isSecret(env[member3])).length > 1;
23423
23874
  }
23424
23875
  function managedHelperShadow(managedFile, expected) {
23425
23876
  try {
@@ -23469,7 +23920,7 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
23469
23920
  const current = settings[HELPER_KEY];
23470
23921
  helper = typeof current === "string" ? current : null;
23471
23922
  const env = envOf(settings) ?? {};
23472
- brokered = isTachoHelper(current) && typeof env[CLAUDE_API_KEY] !== "string" && typeof env[CLAUDE_AUTH_TOKEN] !== "string";
23923
+ brokered = isTachoHelper(current) && CLAUDE_MEMBERS.every(([member3]) => typeof env[member3] !== "string");
23473
23924
  if (!brokered && holdsBothClaudeMembers(env)) why ??= "two_credentials";
23474
23925
  const managed = managedHelperShadow(
23475
23926
  internals.managedSettingsFile ?? claudeManagedSettingsPath(),
@@ -23479,14 +23930,14 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
23479
23930
  if (managed.unreadable !== void 0) unreadable.push(managed.unreadable);
23480
23931
  } else {
23481
23932
  const auth = parseForReport(text5, file, unreadable);
23482
- const key = auth[CODEX_KEY2];
23483
- brokered = looksLikeRunToken(typeof key === "string" ? key : void 0);
23933
+ const key = codexTokenMember(auth);
23934
+ brokered = looksLikeRunToken(key);
23484
23935
  if (brokered) {
23485
23936
  const claims = peekRunTokenClaims(key);
23486
23937
  if (claims !== void 0)
23487
23938
  tokenExpiresAt = new Date(claims.exp).toISOString();
23488
23939
  }
23489
- if (!brokered && why === void 0 && auth["tokens"] !== void 0)
23940
+ if (!brokered && why === void 0 && auth[CODEX_TOKENS] !== void 0)
23490
23941
  why = "subscription_login";
23491
23942
  }
23492
23943
  return {
@@ -23519,21 +23970,15 @@ function applyClaude(options, taken) {
23519
23970
  return { changed: false, text: text5, reason: "two_credentials" };
23520
23971
  const rest = { ...env ?? {} };
23521
23972
  const took = [];
23522
- for (const [member3, kind] of [
23523
- [CLAUDE_API_KEY, "api_key"],
23524
- [CLAUDE_AUTH_TOKEN, "bearer"]
23525
- ]) {
23526
- const value = rest[member3];
23527
- if (isSecret(value)) {
23528
- taken.push({
23529
- harness: "claude-code",
23530
- provider: "anthropic",
23531
- credential: { kind, secret: value },
23532
- member: `env.${member3}`
23533
- });
23534
- took.push({ member: member3, kind });
23535
- delete rest[member3];
23536
- }
23973
+ for (const { member: member3, credential } of claudeEnvCredentials(rest)) {
23974
+ taken.push({
23975
+ harness: "claude-code",
23976
+ provider: "anthropic",
23977
+ credential,
23978
+ member: `env.${member3}`
23979
+ });
23980
+ took.push({ member: member3, kind: credential.kind });
23981
+ delete rest[member3];
23537
23982
  }
23538
23983
  const helperCommand = options.helperCommand;
23539
23984
  if (helperCommand === void 0)
@@ -23581,7 +24026,49 @@ function applyCodex(options, taken) {
23581
24026
  const existing = readSidecar2(backup, "codex");
23582
24027
  const current = auth[CODEX_KEY2];
23583
24028
  const hasKey = typeof current === "string" && current.length > 0;
23584
- if (auth["tokens"] !== void 0 && !looksLikeRunToken(typeof current === "string" ? current : void 0)) {
24029
+ const tokens = chatGptTokensOf(auth);
24030
+ if (tokens !== void 0) {
24031
+ const access2 = tokens["access_token"];
24032
+ const ours = looksLikeRunToken(access2);
24033
+ if (!ours)
24034
+ taken.push({
24035
+ harness: "codex",
24036
+ provider: "openai",
24037
+ credential: { kind: "chatgpt", secret: encodeChatGptLogin(tokens) },
24038
+ member: CODEX_TOKENS
24039
+ });
24040
+ if (token === void 0) {
24041
+ if (!ours) taken.pop();
24042
+ return { changed: false, text: text5, reason: "no_token" };
24043
+ }
24044
+ if (access2 === token && existing !== void 0)
24045
+ return { changed: false, text: text5 };
24046
+ auth[CODEX_TOKENS] = {
24047
+ ...tokens,
24048
+ access_token: token,
24049
+ refresh_token: CHATGPT_REFRESH_HELD
24050
+ };
24051
+ auth[CODEX_LAST_REFRESH] = (/* @__PURE__ */ new Date()).toISOString();
24052
+ const next2 = serializeJsonLike(text5, auth);
24053
+ const sidecar2 = existing ? {
24054
+ ...existing,
24055
+ written_sha256: sha256Hex(next2),
24056
+ taken: [
24057
+ ...existing.taken.filter((t) => t.member !== CODEX_TOKENS),
24058
+ { member: CODEX_TOKENS, kind: "chatgpt" }
24059
+ ]
24060
+ } : {
24061
+ schema: SIDECAR_SCHEMA2,
24062
+ harness: "codex",
24063
+ existed: true,
24064
+ written_sha256: sha256Hex(next2),
24065
+ taken: [{ member: CODEX_TOKENS, kind: "chatgpt" }]
24066
+ };
24067
+ writeSidecar(backup, sidecar2);
24068
+ writeFileAtomicPreserving(file, next2);
24069
+ return { changed: true, text: next2 };
24070
+ }
24071
+ if (auth[CODEX_TOKENS] !== void 0 && !looksLikeRunToken(typeof current === "string" ? current : void 0)) {
23585
24072
  return { changed: false, text: text5, reason: "subscription_login" };
23586
24073
  }
23587
24074
  if (hasKey && !looksLikeRunToken(current)) {
@@ -23616,11 +24103,11 @@ function restoreClaude(options, released, sidecar) {
23616
24103
  const backup = sidecarFor2(file);
23617
24104
  const text5 = readTextIfExists(file);
23618
24105
  const dropSidecar = () => {
23619
- if (existsSync6(backup)) unlinkSync5(backup);
24106
+ if (existsSync7(backup)) unlinkSync6(backup);
23620
24107
  };
23621
24108
  if (text5 === void 0) {
23622
24109
  if (released !== void 0) {
23623
- const member3 = released.kind === "bearer" ? CLAUDE_AUTH_TOKEN : CLAUDE_API_KEY;
24110
+ const member3 = claudeMemberFor(released.kind);
23624
24111
  const written2 = serializeJsonLike(void 0, {
23625
24112
  env: { [member3]: released.secret }
23626
24113
  });
@@ -23640,11 +24127,11 @@ function restoreClaude(options, released, sidecar) {
23640
24127
  touched = true;
23641
24128
  }
23642
24129
  const current = envOf(settings);
23643
- const foreignKey = current !== void 0 && (isSecret(current[CLAUDE_API_KEY]) || isSecret(current[CLAUDE_AUTH_TOKEN]));
24130
+ const foreignKey = current !== void 0 && CLAUDE_MEMBERS.some(([member3]) => isSecret(current[member3]));
23644
24131
  const why = foreignKey ? { reason: "foreign_key_present" } : {};
23645
24132
  let secretRestored = false;
23646
24133
  if (released !== void 0 && !foreignKey) {
23647
- const member3 = sidecar?.taken.find((t) => t.kind === released.kind)?.member ?? (released.kind === "bearer" ? CLAUDE_AUTH_TOKEN : CLAUDE_API_KEY);
24134
+ const member3 = sidecar?.taken.find((t) => t.kind === released.kind)?.member ?? claudeMemberFor(released.kind);
23648
24135
  const env = { ...current ?? {} };
23649
24136
  env[member3] = released.secret;
23650
24137
  settings["env"] = env;
@@ -23674,7 +24161,7 @@ function restoreCodex(options, released, sidecar) {
23674
24161
  const backup = sidecarFor2(file);
23675
24162
  const text5 = readTextIfExists(file);
23676
24163
  const dropSidecar = () => {
23677
- if (existsSync6(backup)) unlinkSync5(backup);
24164
+ if (existsSync7(backup)) unlinkSync6(backup);
23678
24165
  };
23679
24166
  if (text5 === void 0) {
23680
24167
  if (released !== void 0) {
@@ -23689,6 +24176,18 @@ function restoreCodex(options, released, sidecar) {
23689
24176
  return { changed: false, text: text5, secretRestored: false };
23690
24177
  }
23691
24178
  const auth = parseJsonObject(text5, file);
24179
+ const tokens = chatGptTokensOf(auth);
24180
+ if (tokens !== void 0 && looksLikeRunToken(tokens["access_token"])) {
24181
+ const login = released?.kind === "chatgpt" ? decodeChatGptLogin(released.secret) : void 0;
24182
+ if (login !== void 0) {
24183
+ auth[CODEX_TOKENS] = { ...tokens, ...login };
24184
+ auth[CODEX_LAST_REFRESH] = (/* @__PURE__ */ new Date()).toISOString();
24185
+ } else delete auth[CODEX_TOKENS];
24186
+ const next2 = serializeJsonLike(text5, auth);
24187
+ writeFileAtomicPreserving(file, next2);
24188
+ dropSidecar();
24189
+ return { changed: true, text: next2, secretRestored: login !== void 0 };
24190
+ }
23692
24191
  const current = auth[CODEX_KEY2];
23693
24192
  if (!looksLikeRunToken(typeof current === "string" ? current : void 0)) {
23694
24193
  dropSidecar();
@@ -23702,7 +24201,7 @@ function restoreCodex(options, released, sidecar) {
23702
24201
  if (released !== void 0) auth[CODEX_KEY2] = released.secret;
23703
24202
  else delete auth[CODEX_KEY2];
23704
24203
  if (sidecar !== void 0 && !sidecar.existed && Object.keys(auth).length === 0) {
23705
- unlinkSync5(file);
24204
+ unlinkSync6(file);
23706
24205
  dropSidecar();
23707
24206
  return {
23708
24207
  changed: true,
@@ -23765,22 +24264,30 @@ async function peekModelCredentials(options) {
23765
24264
  if (harness === "claude-code") {
23766
24265
  const env = envOf(document) ?? {};
23767
24266
  if (holdsBothClaudeMembers(env)) continue;
23768
- for (const [member3, kind] of [
23769
- [CLAUDE_API_KEY, "api_key"],
23770
- [CLAUDE_AUTH_TOKEN, "bearer"]
23771
- ]) {
23772
- const value = env[member3];
23773
- if (isSecret(value))
24267
+ for (const { member: member3, credential } of claudeEnvCredentials(env))
24268
+ taken.push({
24269
+ harness,
24270
+ provider: "anthropic",
24271
+ credential,
24272
+ member: `env.${member3}`
24273
+ });
24274
+ } else {
24275
+ const tokens = chatGptTokensOf(document);
24276
+ if (tokens !== void 0) {
24277
+ if (!looksLikeRunToken(tokens["access_token"]))
23774
24278
  taken.push({
23775
24279
  harness,
23776
- provider: "anthropic",
23777
- credential: { kind, secret: value },
23778
- member: `env.${member3}`
24280
+ provider: "openai",
24281
+ credential: {
24282
+ kind: "chatgpt",
24283
+ secret: encodeChatGptLogin(tokens)
24284
+ },
24285
+ member: CODEX_TOKENS
23779
24286
  });
24287
+ continue;
23780
24288
  }
23781
- } else {
23782
24289
  const current = document[CODEX_KEY2];
23783
- if (document["tokens"] === void 0 && typeof current === "string" && current.length > 0 && !looksLikeRunToken(current))
24290
+ if (document[CODEX_TOKENS] === void 0 && typeof current === "string" && current.length > 0 && !looksLikeRunToken(current))
23784
24291
  taken.push({
23785
24292
  harness,
23786
24293
  provider: "openai",
@@ -23806,8 +24313,8 @@ function hasOrphanedModelCredential(harness, home, dirs = {}) {
23806
24313
  try {
23807
24314
  const document = parseJsonObject(text5, file);
23808
24315
  if (harness === "claude-code") return isTachoHelper(document[HELPER_KEY]);
23809
- const key = document[CODEX_KEY2];
23810
- return looksLikeRunToken(typeof key === "string" ? key : void 0);
24316
+ const key = codexTokenMember(document);
24317
+ return looksLikeRunToken(key);
23811
24318
  } catch {
23812
24319
  return text5.includes("credential issue --harness") || text5.includes("oxrt_");
23813
24320
  }
@@ -23829,24 +24336,46 @@ function staticTokenStillGood(token, host, keyPath2, now) {
23829
24336
  });
23830
24337
  return verdict.ok && verdict.claims.exp - now > staticTokenRenewWindowMs(host, now);
23831
24338
  }
24339
+ function restateCodexLastRefresh(home, now, dirs = {}) {
24340
+ const file = fileFor2("codex", { ...dirs, home });
24341
+ const text5 = readTextIfExists(file);
24342
+ if (text5 === void 0 || isSymlink(file)) return false;
24343
+ const auth = parseJsonObject(text5, file);
24344
+ const tokens = chatGptTokensOf(auth);
24345
+ if (tokens === void 0 || !looksLikeRunToken(tokens["access_token"]))
24346
+ return false;
24347
+ const last = Date.parse(
24348
+ typeof auth[CODEX_LAST_REFRESH] === "string" ? auth[CODEX_LAST_REFRESH] : ""
24349
+ );
24350
+ if (Number.isFinite(last) && now - last < CODEX_LAST_REFRESH_RESTATE_MS)
24351
+ return false;
24352
+ auth[CODEX_LAST_REFRESH] = new Date(now).toISOString();
24353
+ const next = serializeJsonLike(text5, auth);
24354
+ const backup = sidecarFor2(file);
24355
+ const sidecar = readSidecar2(backup, "codex");
24356
+ if (sidecar !== void 0)
24357
+ writeSidecar(backup, { ...sidecar, written_sha256: sha256Hex(next) });
24358
+ writeFileAtomicPreserving(file, next);
24359
+ return true;
24360
+ }
23832
24361
  function readCodexApiKeyMember(home, dirs = {}) {
23833
24362
  const file = fileFor2("codex", { ...dirs, home });
23834
24363
  try {
23835
24364
  const text5 = readTextIfExists(file);
23836
24365
  if (text5 === void 0) return void 0;
23837
- const value = parseJsonObject(text5, file)[CODEX_KEY2];
23838
- return typeof value === "string" ? value : void 0;
24366
+ return codexTokenMember(parseJsonObject(text5, file));
23839
24367
  } catch {
23840
24368
  return void 0;
23841
24369
  }
23842
24370
  }
23843
- var HARNESS_PROVIDER, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CODEX_KEY2, HELPER_MARK, STATIC_TOKEN_RENEW_WINDOW_MS;
24371
+ var HARNESS_PROVIDER, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CLAUDE_OAUTH_TOKEN, CLAUDE_MEMBERS, CODEX_KEY2, CODEX_TOKENS, CODEX_LAST_REFRESH, CHATGPT_REFRESH_HELD, HELPER_MARK, STATIC_TOKEN_RENEW_WINDOW_MS, CODEX_LAST_REFRESH_RESTATE_MS;
23844
24372
  var init_model_credential = __esm({
23845
24373
  "../../packages/tacho/src/host/model-credential.ts"() {
23846
24374
  "use strict";
23847
24375
  init_digest();
23848
24376
  init_fs();
23849
24377
  init_wire();
24378
+ init_credential_store();
23850
24379
  init_model_base_url();
23851
24380
  init_paths();
23852
24381
  init_run_token();
@@ -23855,14 +24384,24 @@ var init_model_credential = __esm({
23855
24384
  HELPER_KEY = "apiKeyHelper";
23856
24385
  CLAUDE_API_KEY = "ANTHROPIC_API_KEY";
23857
24386
  CLAUDE_AUTH_TOKEN = "ANTHROPIC_AUTH_TOKEN";
24387
+ CLAUDE_OAUTH_TOKEN = "CLAUDE_CODE_OAUTH_TOKEN";
24388
+ CLAUDE_MEMBERS = [
24389
+ [CLAUDE_API_KEY, "api_key"],
24390
+ [CLAUDE_AUTH_TOKEN, "bearer"],
24391
+ [CLAUDE_OAUTH_TOKEN, "oauth"]
24392
+ ];
23858
24393
  CODEX_KEY2 = "OPENAI_API_KEY";
24394
+ CODEX_TOKENS = "tokens";
24395
+ CODEX_LAST_REFRESH = "last_refresh";
24396
+ CHATGPT_REFRESH_HELD = "held-by-oxagen-gateway";
23859
24397
  HELPER_MARK = /\bcredential issue --harness claude-code\s*$/;
23860
24398
  STATIC_TOKEN_RENEW_WINDOW_MS = 7 * 24 * 60 * 6e4;
24399
+ CODEX_LAST_REFRESH_RESTATE_MS = 5 * 24 * 60 * 6e4;
23861
24400
  }
23862
24401
  });
23863
24402
 
23864
24403
  // ../../packages/tacho/src/cli/credential.ts
23865
- import { existsSync as existsSync7 } from "node:fs";
24404
+ import { existsSync as existsSync8 } from "node:fs";
23866
24405
  import { dirname as dirname8 } from "node:path";
23867
24406
  function harnessDirsOf(deps) {
23868
24407
  return {
@@ -23982,15 +24521,15 @@ function describeHarness(entry2) {
23982
24521
  }
23983
24522
  switch (entry2.reason) {
23984
24523
  case "subscription_login":
23985
- return `${label2}: signed in with a ChatGPT login, which the gateway cannot take into custody; its own login crosses the proxy`;
24524
+ return `${label2}: signed in with a ChatGPT login that crosses the proxy as its own (run \`oxagen credential login --harness codex\` to put it in the gateway's custody)`;
23986
24525
  case "symlink":
23987
24526
  return `${label2}: ${entry2.file} is a symbolic link, so it was left alone; its own credential crosses the proxy`;
23988
24527
  case "no_file":
23989
24528
  return `${label2}: no ${entry2.file} yet; its own credential crosses the proxy`;
23990
24529
  case "two_credentials":
23991
- return `${label2}: ${entry2.file} sets both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN in env, and the gateway holds one ${HARNESS_PROVIDER[entry2.harness]} credential; remove one and run \`oxagen agent enroll\` again. Its own credential crosses the proxy`;
24530
+ return `${label2}: ${entry2.file} sets more than one of ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_OAUTH_TOKEN in env, and the gateway holds one ${HARNESS_PROVIDER[entry2.harness]} credential; keep one and run \`oxagen agent enroll\` again. Its own credential crosses the proxy`;
23992
24531
  default:
23993
- return `${label2}: holds its own credential, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
24532
+ return entry2.harness === "claude-code" ? `${label2}: holds its own credential, which crosses the proxy (run \`oxagen credential login\` to put your Claude subscription in the gateway's custody)` : `${label2}: holds its own credential, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
23994
24533
  }
23995
24534
  }
23996
24535
  async function brokerCredentials(host, harnesses, deps) {
@@ -24018,11 +24557,17 @@ async function brokerCredentials(host, harnesses, deps) {
24018
24557
  if (!offered.has(provider) && typeof fromEnv === "string" && fromEnv.trim().length > 0)
24019
24558
  offered.set(provider, {
24020
24559
  credential: {
24021
- kind: provider === "anthropic" ? "api_key" : "bearer",
24560
+ kind: provider === "anthropic" ? anthropicCredentialKind(fromEnv, "api_key") : "bearer",
24022
24561
  secret: fromEnv.trim()
24023
24562
  },
24024
24563
  source: "enroll:env"
24025
24564
  });
24565
+ const oauth = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
24566
+ if (provider === "anthropic" && !offered.has(provider) && oauth !== void 0 && isSubscriptionToken(oauth))
24567
+ offered.set(provider, {
24568
+ credential: { kind: "oauth", secret: oauth },
24569
+ source: "enroll:env"
24570
+ });
24026
24571
  }
24027
24572
  const takenNow = [];
24028
24573
  const staticTokens = {};
@@ -24033,8 +24578,7 @@ async function brokerCredentials(host, harnesses, deps) {
24033
24578
  takenNow.push(provider);
24034
24579
  }
24035
24580
  if (targets.includes("codex") && store.has("openai")) {
24036
- const before = (await contract.read({ home: deps.home, harnesses: ["codex"] })).harnesses[0];
24037
- if (before?.reason !== "subscription_login") {
24581
+ {
24038
24582
  const current = readCodexApiKeyMember(deps.home, harnessDirsOf(deps));
24039
24583
  if (!staticTokenStillGood(
24040
24584
  current,
@@ -24085,7 +24629,7 @@ async function brokerCredentials(host, harnesses, deps) {
24085
24629
  reason: "no_token"
24086
24630
  };
24087
24631
  warnings.push(
24088
- `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own login: no ${provider} key was found in ${entry2.file} or ${brokerEnvVar(provider)}, so there is nothing for the gateway to take into custody. A subscription login crosses the proxy as it is`
24632
+ provider === "anthropic" ? `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own login: the gateway holds no Anthropic credential yet. Run \`oxagen credential login\` to sign in with your Claude subscription, or put a key in ${entry2.file} and enroll again` : `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own login: no ${provider} key was found in ${entry2.file} or ${brokerEnvVar(provider)}, so there is nothing for the gateway to take into custody`
24089
24633
  );
24090
24634
  }
24091
24635
  continue;
@@ -24131,7 +24675,7 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
24131
24675
  try {
24132
24676
  if (host !== void 0 && !host.harnesses.includes(harness)) {
24133
24677
  const dirs = harnessDirsOf(deps);
24134
- const hasReceipt = existsSync7(
24678
+ const hasReceipt = existsSync8(
24135
24679
  modelCredentialBackupPath(harness, deps.home, dirs)
24136
24680
  );
24137
24681
  if (!hasReceipt && !hasOrphanedModelCredential(harness, deps.home, dirs))
@@ -24141,8 +24685,13 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
24141
24685
  const label2 = TACHO_HARNESS_LABELS[harness];
24142
24686
  let released;
24143
24687
  let unreadable;
24688
+ let fromLogin = false;
24144
24689
  try {
24145
- released = store?.read(provider);
24690
+ fromLogin = store?.status().some(
24691
+ (c) => c.provider === provider && c.source === "claude:setup-token"
24692
+ ) === true;
24693
+ if (fromLogin) store?.release(provider);
24694
+ else released = store?.read(provider);
24146
24695
  } catch (error) {
24147
24696
  unreadable = error instanceof Error ? error.message : String(error);
24148
24697
  }
@@ -24186,10 +24735,200 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
24186
24735
  }
24187
24736
  return { restored, failed: failed3, warnings, custodyUnreadable };
24188
24737
  }
24189
- var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES;
24738
+ async function acquireSubscriptionToken(options, deps) {
24739
+ const io = deps.subscriptionLogin;
24740
+ if (options.tokenStdin === true) {
24741
+ const text5 = (await io?.readStdin())?.trim() ?? "";
24742
+ return isSubscriptionToken(text5) ? { token: text5, source: "claude:setup-token" } : {
24743
+ detail: "stdin did not hold a Claude subscription token (sk-ant-oat\u2026). Pipe in what `claude setup-token` printed"
24744
+ };
24745
+ }
24746
+ const fromEnv = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
24747
+ if (fromEnv !== void 0 && isSubscriptionToken(fromEnv))
24748
+ return { token: fromEnv, source: "enroll:env" };
24749
+ if (io === void 0)
24750
+ return {
24751
+ detail: `no Claude subscription token: set ${CLAUDE_OAUTH_TOKEN_ENV} or pipe one in with --token-stdin`
24752
+ };
24753
+ const claude = io.findClaude();
24754
+ if (claude === void 0)
24755
+ return {
24756
+ detail: "Claude Code is not installed, so `claude setup-token` cannot run. Install it, or pipe a token in with --token-stdin"
24757
+ };
24758
+ deps.out(
24759
+ "Signing in to your Claude subscription with `claude setup-token`. Finish the sign-in in your browser."
24760
+ );
24761
+ const run3 = await io.runSetupToken(
24762
+ claude,
24763
+ (chunk2) => process.stdout.write(
24764
+ chunk2.replace(/sk-ant-oat[A-Za-z0-9_-]*/g, "sk-ant-oat\u2026 (sent to Oxagen)")
24765
+ )
24766
+ );
24767
+ if (run3.token !== void 0)
24768
+ return { token: run3.token, source: "claude:setup-token" };
24769
+ if (io.interactive) {
24770
+ const pasted = await io.promptSecret(
24771
+ "Paste the token `claude setup-token` printed (sk-ant-oat\u2026): "
24772
+ );
24773
+ if (pasted !== void 0 && isSubscriptionToken(pasted))
24774
+ return { token: pasted, source: "claude:setup-token" };
24775
+ return {
24776
+ detail: pasted === void 0 || pasted.length === 0 ? "no token was pasted" : "that is not a Claude subscription token (sk-ant-oat\u2026)"
24777
+ };
24778
+ }
24779
+ return {
24780
+ detail: run3.code === 0 ? "`claude setup-token` finished, but no token could be read from its output. Run `oxagen credential login` in a terminal" : `\`claude setup-token\` exited with ${run3.code}. Run \`oxagen credential login\` in a terminal`
24781
+ };
24782
+ }
24783
+ async function signInIfNoCredential(harnesses, deps) {
24784
+ const store = deps.credentialStore;
24785
+ if (harnesses.includes("codex") && store !== void 0 && deps.subscriptionLogin?.interactive === true && !store.has("openai") && (deps.env[brokerEnvVar("openai")]?.trim() ?? "").length === 0 && (await deps.modelCredentials?.peek({
24786
+ home: deps.home,
24787
+ harnesses: ["codex"]
24788
+ }) ?? []).length === 0) {
24789
+ const why = await ensureCodexLogin(deps);
24790
+ if (why !== void 0)
24791
+ deps.err(
24792
+ `warning: ChatGPT sign-in for Codex did not finish (${why}). Run \`oxagen credential login --harness codex\` to try again`
24793
+ );
24794
+ }
24795
+ if (!harnesses.includes("claude-code") || store === void 0 || deps.subscriptionLogin?.interactive !== true || store.has("anthropic"))
24796
+ return;
24797
+ if ([brokerEnvVar("anthropic"), CLAUDE_OAUTH_TOKEN_ENV].some(
24798
+ (name) => (deps.env[name]?.trim() ?? "").length > 0
24799
+ ))
24800
+ return;
24801
+ const inFile = await deps.modelCredentials?.peek({
24802
+ home: deps.home,
24803
+ harnesses: ["claude-code"],
24804
+ helperCommand: deps.runtime.credentialHelperCommand
24805
+ });
24806
+ if ((inFile ?? []).some((t) => t.provider === "anthropic")) return;
24807
+ const acquired = await acquireSubscriptionToken({}, deps);
24808
+ if (acquired.token === void 0) {
24809
+ deps.err(
24810
+ `warning: Claude subscription sign-in did not finish (${acquired.detail}). Run \`oxagen credential login\` to try again`
24811
+ );
24812
+ return;
24813
+ }
24814
+ const custody = store.take(
24815
+ "anthropic",
24816
+ { kind: "oauth", secret: acquired.token },
24817
+ acquired.source,
24818
+ deps.now()
24819
+ );
24820
+ deps.out(
24821
+ ` Claude subscription ${custody.prefix} taken into the gateway's custody`
24822
+ );
24823
+ }
24824
+ async function routedToGateway(host, harness, deps) {
24825
+ if (deps.modelBaseUrls === void 0) return false;
24826
+ try {
24827
+ const state = await deps.modelBaseUrls.read({
24828
+ home: deps.home,
24829
+ stellaHome: dirname8(deps.paths.stellaToml),
24830
+ port: modelProxyPortFor(host),
24831
+ harnesses: [harness]
24832
+ });
24833
+ const entry2 = state.harnesses.find((h) => h.harness === harness);
24834
+ return entry2?.ours === true && entry2.shadowedBy === void 0;
24835
+ } catch {
24836
+ return false;
24837
+ }
24838
+ }
24839
+ async function codexHasChatGptLogin(deps) {
24840
+ const offered = await deps.modelCredentials?.peek({
24841
+ home: deps.home,
24842
+ harnesses: ["codex"]
24843
+ });
24844
+ return (offered ?? []).some((t) => t.credential.kind === "chatgpt");
24845
+ }
24846
+ async function ensureCodexLogin(deps) {
24847
+ if (await codexHasChatGptLogin(deps)) return void 0;
24848
+ const io = deps.subscriptionLogin;
24849
+ const codex = io?.findCodex();
24850
+ if (io === void 0 || codex === void 0)
24851
+ return "Codex is not installed, so `codex login` cannot run. Install it and try again";
24852
+ deps.out(
24853
+ "Signing in to ChatGPT with `codex login`. Finish the sign-in in your browser."
24854
+ );
24855
+ const code = await io.runCodexLogin(codex);
24856
+ if (code !== 0) return `\`codex login\` exited with ${code}`;
24857
+ return await codexHasChatGptLogin(deps) ? void 0 : "`codex login` finished, but ~/.codex/auth.json holds no ChatGPT login";
24858
+ }
24859
+ async function codexLogin(deps) {
24860
+ const store = deps.credentialStore;
24861
+ const held = store?.status().some(
24862
+ (c) => c.provider === "openai" && c.kind === "chatgpt"
24863
+ );
24864
+ if (held !== true) {
24865
+ const why = await ensureCodexLogin(deps);
24866
+ if (why !== void 0)
24867
+ return { ok: false, harnesses: [], warnings: [], detail: why };
24868
+ }
24869
+ const host = readHostFileLenient(deps.paths.hostFile).host;
24870
+ if (host === void 0 || !host.harnesses.includes("codex") || !await routedToGateway(host, "codex", deps))
24871
+ return {
24872
+ ok: true,
24873
+ harnesses: [],
24874
+ warnings: [],
24875
+ detail: "Codex is signed in to ChatGPT. Run `oxagen agent enroll --harness codex` to route it through the gateway, which then holds the login"
24876
+ };
24877
+ const outcome = await brokerCredentials(host, ["codex"], deps);
24878
+ return {
24879
+ ok: true,
24880
+ source: "codex:auth.json",
24881
+ harnesses: outcome.harnesses,
24882
+ warnings: outcome.warnings,
24883
+ detail: outcome.harnesses.some((h) => h.brokered) ? "Codex now holds a run token, and the gateway sends your ChatGPT login with every call" : "Codex is signed in, but its login stays in its own file. See the warnings above"
24884
+ };
24885
+ }
24886
+ async function credentialLogin(options, deps) {
24887
+ const store = deps.credentialStore;
24888
+ if (store === void 0)
24889
+ return {
24890
+ ok: false,
24891
+ harnesses: [],
24892
+ warnings: [],
24893
+ detail: "this build holds no credential store"
24894
+ };
24895
+ if (options.harness === "codex") return codexLogin(deps);
24896
+ const acquired = await acquireSubscriptionToken(options, deps);
24897
+ if (acquired.token === void 0)
24898
+ return { ok: false, harnesses: [], warnings: [], detail: acquired.detail };
24899
+ const custody = store.take(
24900
+ "anthropic",
24901
+ { kind: "oauth", secret: acquired.token },
24902
+ acquired.source,
24903
+ deps.now()
24904
+ );
24905
+ deps.out(
24906
+ `Claude subscription ${custody.prefix} taken into the gateway's custody (${deps.paths.credentials})`
24907
+ );
24908
+ const host = readHostFileLenient(deps.paths.hostFile).host;
24909
+ if (host === void 0 || !host.harnesses.includes("claude-code") || !await routedToGateway(host, "claude-code", deps))
24910
+ return {
24911
+ ok: true,
24912
+ source: acquired.source,
24913
+ harnesses: [],
24914
+ warnings: [],
24915
+ detail: "Run `oxagen agent enroll` to route Claude Code through the gateway. It will use this subscription"
24916
+ };
24917
+ const outcome = await brokerCredentials(host, ["claude-code"], deps);
24918
+ return {
24919
+ ok: true,
24920
+ source: acquired.source,
24921
+ harnesses: outcome.harnesses,
24922
+ warnings: outcome.warnings,
24923
+ detail: outcome.harnesses.some((h) => h.brokered) ? "Claude Code now holds a run token, and the gateway sends your subscription with every call" : "The subscription is in custody. Run `oxagen agent enroll` to route Claude Code through the gateway"
24924
+ };
24925
+ }
24926
+ var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, CLAUDE_OAUTH_TOKEN_ENV;
24190
24927
  var init_credential = __esm({
24191
24928
  "../../packages/tacho/src/cli/credential.ts"() {
24192
24929
  "use strict";
24930
+ init_claude_setup_token();
24931
+ init_credential_store();
24193
24932
  init_host_file();
24194
24933
  init_model_credential();
24195
24934
  init_agents();
@@ -24202,11 +24941,12 @@ var init_credential = __esm({
24202
24941
  "brokered",
24203
24942
  "passthrough"
24204
24943
  ];
24944
+ CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
24205
24945
  }
24206
24946
  });
24207
24947
 
24208
24948
  // ../../packages/tacho/src/host/codex-app-server.ts
24209
- import { spawn } from "node:child_process";
24949
+ import { spawn as spawn2 } from "node:child_process";
24210
24950
  function cmdArgument(value) {
24211
24951
  let quoted3 = "";
24212
24952
  let slashes = 0;
@@ -24244,7 +24984,7 @@ function codexAppServerClient(options) {
24244
24984
  let child;
24245
24985
  try {
24246
24986
  const invocation = spawnInvocation(options.binary, ["app-server"]);
24247
- child = spawn(invocation.command, invocation.args, {
24987
+ child = spawn2(invocation.command, invocation.args, {
24248
24988
  cwd: options.cwd,
24249
24989
  env: options.env,
24250
24990
  stdio: ["pipe", "pipe", "pipe"],
@@ -24377,266 +25117,6 @@ var init_codex_app_server = __esm({
24377
25117
  }
24378
25118
  });
24379
25119
 
24380
- // ../../packages/tacho/src/host/credential-store.ts
24381
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
24382
- import {
24383
- closeSync as closeSync4,
24384
- existsSync as existsSync8,
24385
- fsyncSync as fsyncSync3,
24386
- openSync as openSync4,
24387
- readFileSync as readFileSync8,
24388
- statSync as statSync3,
24389
- unlinkSync as unlinkSync6,
24390
- writeSync as writeSync3
24391
- } from "node:fs";
24392
- function readKey(path2) {
24393
- try {
24394
- const hex = readFileSync8(path2, "utf8").trim();
24395
- if (!/^[0-9a-f]{64}$/i.test(hex))
24396
- throw new Error(`${path2} does not hold a 32-byte key`);
24397
- return Buffer.from(hex, "hex");
24398
- } catch (error) {
24399
- if (error.code === "ENOENT") return void 0;
24400
- throw error;
24401
- }
24402
- }
24403
- function loadOrCreateKey(path2) {
24404
- const existing = readKey(path2);
24405
- if (existing !== void 0) return existing;
24406
- const key = randomBytes5(KEY_BYTES2);
24407
- writeSensitiveFileAtomic(path2, `${key.toString("hex")}
24408
- `);
24409
- return key;
24410
- }
24411
- function requireKey(path2) {
24412
- const key = readKey(path2);
24413
- if (key === void 0)
24414
- throw new Error(
24415
- `${path2} is missing, so the credentials in custody cannot be opened`
24416
- );
24417
- return key;
24418
- }
24419
- function overwriteInPlace(path2) {
24420
- let fd2;
24421
- try {
24422
- const size = statSync3(path2).size;
24423
- fd2 = openSync4(path2, "r+");
24424
- writeSync3(fd2, randomBytes5(Math.max(size, KEY_BYTES2 * 2)), 0, void 0, 0);
24425
- fsyncSync3(fd2);
24426
- } catch (error) {
24427
- if (error.code !== "ENOENT") throw error;
24428
- } finally {
24429
- if (fd2 !== void 0) closeSync4(fd2);
24430
- }
24431
- }
24432
- function seal(key, secret, aad) {
24433
- const nonce = randomBytes5(12);
24434
- const cipher = createCipheriv("aes-256-gcm", key, nonce);
24435
- cipher.setAAD(Buffer.from(aad, "utf8"));
24436
- const ciphertext = Buffer.concat([
24437
- cipher.update(secret, "utf8"),
24438
- cipher.final()
24439
- ]);
24440
- return {
24441
- nonce: nonce.toString("base64"),
24442
- ciphertext: ciphertext.toString("base64"),
24443
- tag: cipher.getAuthTag().toString("base64")
24444
- };
24445
- }
24446
- function open(key, sealed, aad) {
24447
- const decipher = createDecipheriv(
24448
- "aes-256-gcm",
24449
- key,
24450
- Buffer.from(sealed.nonce, "base64")
24451
- );
24452
- decipher.setAAD(Buffer.from(aad, "utf8"));
24453
- decipher.setAuthTag(Buffer.from(sealed.tag, "base64"));
24454
- return Buffer.concat([
24455
- decipher.update(Buffer.from(sealed.ciphertext, "base64")),
24456
- decipher.final()
24457
- ]).toString("utf8");
24458
- }
24459
- function credentialPrefix(secret) {
24460
- const head2 = secret.slice(0, Math.min(8, Math.max(0, secret.length - 4)));
24461
- return head2.length > 0 ? `${head2}\u2026` : "";
24462
- }
24463
- function custodyOf(entry2) {
24464
- return {
24465
- provider: entry2.provider,
24466
- kind: entry2.kind,
24467
- source: entry2.source,
24468
- taken_at: entry2.taken_at,
24469
- prefix: entry2.prefix
24470
- };
24471
- }
24472
- function openCredentialStore(paths) {
24473
- let entriesSeen;
24474
- let keySeen;
24475
- const keyForWrite = () => loadOrCreateKey(paths.key);
24476
- function forgetKey() {
24477
- keySeen?.key.fill(0);
24478
- keySeen = void 0;
24479
- }
24480
- function keyForRead() {
24481
- const version2 = fileVersion(paths.key);
24482
- if (version2 !== void 0 && keySeen?.version === version2)
24483
- return keySeen.key;
24484
- forgetKey();
24485
- const key = requireKey(paths.key);
24486
- if (version2 !== void 0) keySeen = { version: version2, key };
24487
- return key;
24488
- }
24489
- function readAll() {
24490
- const version2 = fileVersion(paths.file);
24491
- if (version2 === void 0) {
24492
- entriesSeen = void 0;
24493
- forgetKey();
24494
- return [];
24495
- }
24496
- if (entriesSeen?.version === version2) return entriesSeen.entries;
24497
- let text5;
24498
- try {
24499
- text5 = readFileSync8(paths.file, "utf8");
24500
- } catch (error) {
24501
- if (error.code === "ENOENT") return [];
24502
- throw error;
24503
- }
24504
- let raw;
24505
- try {
24506
- raw = JSON.parse(text5);
24507
- } catch (error) {
24508
- throw new Error(
24509
- `${paths.file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
24510
- );
24511
- }
24512
- const parsed2 = fileSchema.safeParse(raw);
24513
- if (!parsed2.success)
24514
- throw new Error(
24515
- `${paths.file} is not a credential store this build can read: ${parsed2.error.issues[0]?.message ?? "invalid"}`
24516
- );
24517
- entriesSeen = { version: version2, entries: parsed2.data.entries };
24518
- return parsed2.data.entries;
24519
- }
24520
- function writeAll2(entries2) {
24521
- if (entries2.length === 0) {
24522
- if (existsSync8(paths.file)) unlinkSync6(paths.file);
24523
- return;
24524
- }
24525
- writeSensitiveFileAtomic(
24526
- paths.file,
24527
- `${JSON.stringify({ schema: CREDENTIAL_STORE_SCHEMA, entries: entries2 }, null, 2)}
24528
- `
24529
- );
24530
- }
24531
- return {
24532
- has: (provider) => readAll().some((e) => e.provider === provider),
24533
- read: (provider) => {
24534
- const entry2 = readAll().find((e) => e.provider === provider);
24535
- if (entry2 === void 0) return void 0;
24536
- return {
24537
- kind: entry2.kind,
24538
- secret: open(
24539
- keyForRead(),
24540
- entry2.sealed,
24541
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
24542
- )
24543
- };
24544
- },
24545
- take: (provider, { kind, secret }, source, now) => {
24546
- if (secret.trim().length === 0)
24547
- throw new Error("refusing to take an empty credential into custody");
24548
- const entry2 = {
24549
- provider,
24550
- kind,
24551
- source,
24552
- taken_at: new Date(now).toISOString(),
24553
- prefix: credentialPrefix(secret),
24554
- sealed: seal(
24555
- keyForWrite(),
24556
- secret,
24557
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
24558
- )
24559
- };
24560
- writeAll2([...readAll().filter((e) => e.provider !== provider), entry2]);
24561
- return custodyOf(entry2);
24562
- },
24563
- release: (provider) => {
24564
- const entries2 = readAll();
24565
- const entry2 = entries2.find((e) => e.provider === provider);
24566
- if (entry2 === void 0) return void 0;
24567
- const secret = open(
24568
- keyForRead(),
24569
- entry2.sealed,
24570
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
24571
- );
24572
- writeAll2(entries2.filter((e) => e.provider !== provider));
24573
- return { kind: entry2.kind, secret };
24574
- },
24575
- status: () => readAll().map(custodyOf),
24576
- shred: () => {
24577
- let overwrite;
24578
- try {
24579
- overwriteInPlace(paths.key);
24580
- } catch (error) {
24581
- overwrite = error;
24582
- }
24583
- for (const path2 of [paths.file, paths.key])
24584
- if (existsSync8(path2)) unlinkSync6(path2);
24585
- entriesSeen = void 0;
24586
- forgetKey();
24587
- if (overwrite !== void 0)
24588
- throw new Error(
24589
- `${paths.key} was deleted but could not be overwritten first: ${overwrite instanceof Error ? overwrite.message : String(overwrite)}`
24590
- );
24591
- }
24592
- };
24593
- }
24594
- var CREDENTIAL_STORE_SCHEMA, CREDENTIAL_SOURCES, CREDENTIAL_KINDS, sealedSchema, entrySchema, fileSchema, KEY_BYTES2;
24595
- var init_credential_store = __esm({
24596
- "../../packages/tacho/src/host/credential-store.ts"() {
24597
- "use strict";
24598
- init_zod();
24599
- init_fs();
24600
- init_run_token();
24601
- CREDENTIAL_STORE_SCHEMA = "tacho.credentials.v1";
24602
- CREDENTIAL_SOURCES = [
24603
- /** `env.ANTHROPIC_API_KEY` in Claude Code's user settings. */
24604
- "claude-code:settings.env",
24605
- /** `OPENAI_API_KEY` in Codex's `auth.json`. */
24606
- "codex:auth.json",
24607
- /** `TACHO_BROKER_<PROVIDER>_API_KEY` in the enrolling shell. */
24608
- "enroll:env",
24609
- /**
24610
- * The run token in an Oxagen Agent Runners sandbox's `launch` message. It
24611
- * is spent at the cloud gateway, which attaches the operator's model
24612
- * credential. It is not a model credential itself.
24613
- */
24614
- "sandbox:launch"
24615
- ];
24616
- CREDENTIAL_KINDS = ["api_key", "bearer"];
24617
- sealedSchema = external_exports.object({
24618
- /** AES-256-GCM: 12-byte nonce, ciphertext, 16-byte tag, all base64. */
24619
- nonce: external_exports.string().min(1),
24620
- ciphertext: external_exports.string().min(1),
24621
- tag: external_exports.string().min(1)
24622
- }).strict();
24623
- entrySchema = external_exports.object({
24624
- provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
24625
- kind: external_exports.enum(CREDENTIAL_KINDS),
24626
- source: external_exports.enum(CREDENTIAL_SOURCES),
24627
- taken_at: external_exports.string(),
24628
- /** The first few characters, the way a vendor console shows a key. */
24629
- prefix: external_exports.string().max(12),
24630
- sealed: sealedSchema
24631
- }).strict();
24632
- fileSchema = external_exports.object({
24633
- schema: external_exports.literal(CREDENTIAL_STORE_SCHEMA),
24634
- entries: external_exports.array(entrySchema)
24635
- }).strict();
24636
- KEY_BYTES2 = 32;
24637
- }
24638
- });
24639
-
24640
25120
  // ../../packages/tacho/src/host/claude-config-lock.ts
24641
25121
  import { mkdirSync as mkdirSync4, rmdirSync as rmdirSync3, statSync as statSync4 } from "node:fs";
24642
25122
  function sleepSync(ms) {
@@ -24709,7 +25189,7 @@ var init_claude_config_lock = __esm({
24709
25189
  });
24710
25190
 
24711
25191
  // ../../packages/tacho/src/host/service.ts
24712
- import { join as join15 } from "node:path";
25192
+ import { join as join16 } from "node:path";
24713
25193
  import {
24714
25194
  existsSync as existsSync9,
24715
25195
  readdirSync as readdirSync6,
@@ -24794,8 +25274,8 @@ function blockingSleep(ms) {
24794
25274
  }
24795
25275
  function launchdManager(options) {
24796
25276
  const sleep2 = options.sleep ?? blockingSleep;
24797
- const dir = join15(options.home, "Library", "LaunchAgents");
24798
- const unitPath = join15(dir, `${SERVICE_LABEL}.plist`);
25277
+ const dir = join16(options.home, "Library", "LaunchAgents");
25278
+ const unitPath = join16(dir, `${SERVICE_LABEL}.plist`);
24799
25279
  const domain = `gui/${options.uid ?? process.getuid?.() ?? 501}`;
24800
25280
  const target2 = `${domain}/${SERVICE_LABEL}`;
24801
25281
  const loaded = () => options.exec("launchctl", ["print", target2]).status === 0;
@@ -24900,9 +25380,9 @@ function stopByPidFile(options) {
24900
25380
  );
24901
25381
  }
24902
25382
  function systemdManager(options) {
24903
- const dir = join15(options.home, ".config", "systemd", "user");
24904
- const unitPath = join15(dir, "tachod.service");
24905
- const wants = join15(dir, "default.target.wants");
25383
+ const dir = join16(options.home, ".config", "systemd", "user");
25384
+ const unitPath = join16(dir, "tachod.service");
25385
+ const wants = join16(dir, "default.target.wants");
24906
25386
  return {
24907
25387
  kind: "systemd",
24908
25388
  unitPath,
@@ -25053,8 +25533,8 @@ function windowsCreatedMs(exec4, pid) {
25053
25533
  return result2.status === 0 && /^\d+$/.test(text5) ? Number(text5) : void 0;
25054
25534
  }
25055
25535
  function schtasksManager(options) {
25056
- const launcher = options.launcherPath ?? join15(options.home, "tachod.cmd");
25057
- const pidPath = options.pidPath ?? join15(launcher, "..", "tachod.pid");
25536
+ const launcher = options.launcherPath ?? join16(options.home, "tachod.cmd");
25537
+ const pidPath = options.pidPath ?? join16(launcher, "..", "tachod.pid");
25058
25538
  const liveRecord = () => {
25059
25539
  if (!existsSync9(pidPath)) return void 0;
25060
25540
  const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
@@ -25099,7 +25579,7 @@ function schtasksManager(options) {
25099
25579
  unitPath: launcher,
25100
25580
  install: (spec) => {
25101
25581
  stopDaemon();
25102
- ensureDir(join15(launcher, ".."), 493);
25582
+ ensureDir(join16(launcher, ".."), 493);
25103
25583
  writeSensitiveFileAtomic(
25104
25584
  launcher,
25105
25585
  renderWindowsLauncher(
@@ -25231,7 +25711,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
25231
25711
  import { dirname as dirname9, resolve as resolve3 } from "node:path";
25232
25712
  import { fileURLToPath } from "node:url";
25233
25713
  function readVersion() {
25234
- if (true) return "2.1.4-955";
25714
+ if (true) return "2.1.4-965";
25235
25715
  try {
25236
25716
  const here = dirname9(fileURLToPath(import.meta.url));
25237
25717
  const pkg = JSON.parse(
@@ -25251,7 +25731,7 @@ var init_version = __esm({
25251
25731
  });
25252
25732
 
25253
25733
  // ../../packages/tacho/src/cli/deps.ts
25254
- import { spawnSync as spawnSync4 } from "node:child_process";
25734
+ import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
25255
25735
  import { randomBytes as randomBytes7 } from "node:crypto";
25256
25736
  import { existsSync as existsSync10 } from "node:fs";
25257
25737
  import { get as httpGet, request as httpRequest } from "node:http";
@@ -25550,6 +26030,63 @@ function settleOrThrow(harnessFiles) {
25550
26030
  );
25551
26031
  return outcomes;
25552
26032
  }
26033
+ function promptHidden(question) {
26034
+ const stdin = process.stdin;
26035
+ if (!stdin.isTTY) return Promise.resolve(void 0);
26036
+ return new Promise((resolve25) => {
26037
+ process.stderr.write(question);
26038
+ let value = "";
26039
+ stdin.setRawMode(true);
26040
+ stdin.resume();
26041
+ stdin.setEncoding("utf8");
26042
+ const done = (answer) => {
26043
+ stdin.setRawMode(false);
26044
+ stdin.pause();
26045
+ stdin.off("data", onData);
26046
+ process.stderr.write("\n");
26047
+ resolve25(answer);
26048
+ };
26049
+ const onData = (chunk2) => {
26050
+ for (const char of chunk2) {
26051
+ if (char === "\r" || char === "\n") return done(value.trim());
26052
+ if (char === "" || char === "") return done(void 0);
26053
+ if (char === "\x7F" || char === "\b") value = value.slice(0, -1);
26054
+ else value += char;
26055
+ }
26056
+ };
26057
+ stdin.on("data", onData);
26058
+ });
26059
+ }
26060
+ function realSubscriptionLogin(env, home) {
26061
+ return {
26062
+ interactive: process.stdin.isTTY === true && process.stderr.isTTY === true,
26063
+ findClaude: () => findClaudeBinary(env, home),
26064
+ runSetupToken: (claude, write) => runClaudeSetupToken({
26065
+ claude,
26066
+ interactive: process.stdin.isTTY === true,
26067
+ write
26068
+ }),
26069
+ promptSecret: promptHidden,
26070
+ findCodex: () => findHarnessBinary("codex", env, home),
26071
+ runCodexLogin: (codex) => new Promise((resolve25, reject) => {
26072
+ const child = spawn3(codex, ["login"], {
26073
+ stdio: [process.stdin.isTTY ? "inherit" : "pipe", "inherit", "inherit"],
26074
+ env: process.env
26075
+ });
26076
+ child.on("error", reject);
26077
+ child.on("close", (code) => {
26078
+ child.stdin?.destroy();
26079
+ resolve25(code ?? 1);
26080
+ });
26081
+ }),
26082
+ readStdin: async () => {
26083
+ let text5 = "";
26084
+ process.stdin.setEncoding("utf8");
26085
+ for await (const chunk2 of process.stdin) text5 += chunk2;
26086
+ return text5;
26087
+ }
26088
+ };
26089
+ }
25553
26090
  function defaultCliDeps(overrides = {}, managed = {}) {
25554
26091
  const env = overrides.env ?? process.env;
25555
26092
  const home = overrides.home ?? homedir3();
@@ -25739,6 +26276,7 @@ function defaultCliDeps(overrides = {}, managed = {}) {
25739
26276
  file: paths.credentials,
25740
26277
  key: paths.credentialsKey
25741
26278
  }),
26279
+ subscriptionLogin: realSubscriptionLogin(env, home),
25742
26280
  daemonPost: async (path2, body2) => {
25743
26281
  const host = readHostFile(paths.hostFile);
25744
26282
  if (host === void 0) return void 0;
@@ -25832,6 +26370,7 @@ var init_deps = __esm({
25832
26370
  "../../packages/tacho/src/cli/deps.ts"() {
25833
26371
  "use strict";
25834
26372
  init_hook_client();
26373
+ init_claude_setup_token();
25835
26374
  init_codex_app_server();
25836
26375
  init_credential_store();
25837
26376
  init_claude_config_lock();
@@ -26657,7 +27196,7 @@ import {
26657
27196
  unlinkSync as unlinkSync8
26658
27197
  } from "node:fs";
26659
27198
  import { open as open2 } from "node:fs/promises";
26660
- import { join as join16 } from "node:path";
27199
+ import { join as join17 } from "node:path";
26661
27200
  function parseStoredBody(text5) {
26662
27201
  let value;
26663
27202
  try {
@@ -26858,7 +27397,7 @@ var init_wal_index = __esm({
26858
27397
  /** The scan `ensureAsync` has running for a session, shared by callers. */
26859
27398
  building = /* @__PURE__ */ new Map();
26860
27399
  sidecarFor(sessionUuid2) {
26861
- return join16(this.dir, `${sessionUuid2}${SIDECAR_SUFFIX}`);
27400
+ return join17(this.dir, `${sessionUuid2}${SIDECAR_SUFFIX}`);
26862
27401
  }
26863
27402
  /** Whether a name in the WAL directory is one of these sidecars. */
26864
27403
  static isSidecar(name) {
@@ -27090,7 +27629,7 @@ import {
27090
27629
  writeSync as writeSync4
27091
27630
  } from "node:fs";
27092
27631
  import { randomUUID } from "node:crypto";
27093
- import { join as join17 } from "node:path";
27632
+ import { join as join18 } from "node:path";
27094
27633
  function activityAt(event) {
27095
27634
  const at2 = Date.parse(event.ts);
27096
27635
  return Number.isFinite(at2) ? at2 : Number.NEGATIVE_INFINITY;
@@ -27154,7 +27693,7 @@ var init_wal = __esm({
27154
27693
  this.dir = dir;
27155
27694
  ensureDir(dir);
27156
27695
  this.bodyIndexes = new BodyIndexStore(dir);
27157
- this.cursorPath = join17(dir, "cursor.json");
27696
+ this.cursorPath = join18(dir, "cursor.json");
27158
27697
  let raw;
27159
27698
  if (onCursorSetAside !== void 0)
27160
27699
  raw = readJsonStateFile(this.cursorPath, onCursorSetAside);
@@ -27234,10 +27773,10 @@ var init_wal = __esm({
27234
27773
  */
27235
27774
  dirtyPaths = /* @__PURE__ */ new Set();
27236
27775
  fileFor(sessionUuid2) {
27237
- return join17(this.dir, `${sessionUuid2}.ndjson`);
27776
+ return join18(this.dir, `${sessionUuid2}.ndjson`);
27238
27777
  }
27239
27778
  bodyFileFor(sessionUuid2) {
27240
- return join17(this.dir, `${sessionUuid2}.bodies.jsonl`);
27779
+ return join18(this.dir, `${sessionUuid2}.bodies.jsonl`);
27241
27780
  }
27242
27781
  persistCursor() {
27243
27782
  this.flush();
@@ -28666,7 +29205,7 @@ ${lines.join("\n")}
28666
29205
  for (const name of readdirSync7(this.dir)) {
28667
29206
  if (abandonedRewrite.test(name)) {
28668
29207
  try {
28669
- unlinkSync9(join17(this.dir, name));
29208
+ unlinkSync9(join18(this.dir, name));
28670
29209
  } catch (error) {
28671
29210
  this.bodyFailure(name.slice(0, 36), "cleanup", error);
28672
29211
  }
@@ -29029,6 +29568,70 @@ var init_github = __esm({
29029
29568
  }
29030
29569
  });
29031
29570
 
29571
+ // ../../packages/tacho/src/host/harness-path.ts
29572
+ import { spawnSync as spawnSync5 } from "node:child_process";
29573
+ import { delimiter as delimiter2, join as join19 } from "node:path";
29574
+ function knownDirs(home) {
29575
+ return [
29576
+ join19(home, ".local", "bin"),
29577
+ join19(home, ".bun", "bin"),
29578
+ join19(home, ".npm-global", "bin"),
29579
+ join19(home, ".volta", "bin"),
29580
+ join19(home, ".cargo", "bin"),
29581
+ join19(home, "bin"),
29582
+ "/opt/homebrew/bin",
29583
+ "/usr/local/bin",
29584
+ "/usr/bin",
29585
+ "/bin",
29586
+ "/usr/sbin",
29587
+ "/sbin"
29588
+ ];
29589
+ }
29590
+ function loginShellPath(env, run3 = spawnSync5) {
29591
+ const shell = env["SHELL"] ?? "/bin/zsh";
29592
+ try {
29593
+ const result2 = run3(
29594
+ shell,
29595
+ ["-ilc", `printf '%s%s%s' '${MARK}' "$PATH" '${MARK}'`],
29596
+ {
29597
+ encoding: "utf8",
29598
+ timeout: LOGIN_SHELL_TIMEOUT_MS,
29599
+ stdio: ["ignore", "pipe", "ignore"],
29600
+ env: { ...env, TERM: env["TERM"] ?? "dumb" }
29601
+ }
29602
+ );
29603
+ const out = typeof result2.stdout === "string" ? result2.stdout : "";
29604
+ const start = out.indexOf(MARK);
29605
+ const end = out.indexOf(MARK, start + MARK.length);
29606
+ if (start < 0 || end < 0) return void 0;
29607
+ const path2 = out.slice(start + MARK.length, end).trim();
29608
+ return path2.length > 0 ? path2 : void 0;
29609
+ } catch {
29610
+ return void 0;
29611
+ }
29612
+ }
29613
+ function harnessPath(env, home, loginPath) {
29614
+ const seen = /* @__PURE__ */ new Set();
29615
+ const dirs = [];
29616
+ const add2 = (dir) => {
29617
+ if (dir.length === 0 || seen.has(dir)) return;
29618
+ seen.add(dir);
29619
+ dirs.push(dir);
29620
+ };
29621
+ for (const dir of (loginPath ?? "").split(delimiter2)) add2(dir);
29622
+ for (const dir of (env["PATH"] ?? "").split(delimiter2)) add2(dir);
29623
+ for (const dir of knownDirs(home)) add2(dir);
29624
+ return dirs.join(delimiter2);
29625
+ }
29626
+ var MARK, LOGIN_SHELL_TIMEOUT_MS;
29627
+ var init_harness_path = __esm({
29628
+ "../../packages/tacho/src/host/harness-path.ts"() {
29629
+ "use strict";
29630
+ MARK = "__OXAGEN_PATH__";
29631
+ LOGIN_SHELL_TIMEOUT_MS = 5e3;
29632
+ }
29633
+ });
29634
+
29032
29635
  // ../../packages/tacho/src/cli/daemon-service.ts
29033
29636
  function daemonServiceSpec(daemonCommand, deps) {
29034
29637
  return {
@@ -29043,7 +29646,11 @@ function daemonServiceSpec(daemonCommand, deps) {
29043
29646
  return value !== void 0 ? [[key, value]] : [];
29044
29647
  })
29045
29648
  ),
29046
- PATH: deps.env["PATH"] ?? "/usr/local/bin:/usr/bin:/bin",
29649
+ // The login shell's PATH, so the daemon's git calls find their
29650
+ // credential helpers and a work order's harness is found. A desktop app
29651
+ // installs the service from a GUI process whose PATH is the bare system
29652
+ // path (#5936).
29653
+ PATH: harnessPath(deps.env, deps.home, loginShellPath(deps.env)),
29047
29654
  HOME: deps.home
29048
29655
  },
29049
29656
  // One tachod serves every agent, so its log and working directory are
@@ -29056,6 +29663,7 @@ var HARNESS_HOME_VARS;
29056
29663
  var init_daemon_service = __esm({
29057
29664
  "../../packages/tacho/src/cli/daemon-service.ts"() {
29058
29665
  "use strict";
29666
+ init_harness_path();
29059
29667
  HARNESS_HOME_VARS = [
29060
29668
  "CLAUDE_CONFIG_DIR",
29061
29669
  "CODEX_HOME",
@@ -29090,7 +29698,7 @@ var init_policy_mode = __esm({
29090
29698
 
29091
29699
  // ../../packages/tacho/src/host/wal-ceiling.ts
29092
29700
  import { statfsSync } from "node:fs";
29093
- import { join as join18 } from "node:path";
29701
+ import { join as join20 } from "node:path";
29094
29702
  function freeBytesOn(dir) {
29095
29703
  try {
29096
29704
  const stats2 = statfsSync(dir);
@@ -29137,7 +29745,7 @@ function isDrop(value) {
29137
29745
  function readWalCeilingState(walDir) {
29138
29746
  let raw;
29139
29747
  try {
29140
- raw = readJsonFileIfExists(join18(walDir, STATE_FILE));
29748
+ raw = readJsonFileIfExists(join20(walDir, STATE_FILE));
29141
29749
  } catch (error) {
29142
29750
  if (error instanceof SyntaxError) return void 0;
29143
29751
  throw error;
@@ -29337,7 +29945,7 @@ var init_wal_ceiling = __esm({
29337
29945
  const changed = this.unsaved || prior === void 0 || prior.stalled_sessions !== next.stalled_sessions || prior.drops.length !== next.drops.length || prior.drops.at(-1)?.dropped_at !== next.drops.at(-1)?.dropped_at || !sameClocks(prior.stall_clocks, next.stall_clocks) || next.stalled_sessions > 0 && (prior.stalled_bytes !== next.stalled_bytes || prior.ceiling_bytes !== next.ceiling_bytes);
29338
29946
  this.state = next;
29339
29947
  if (!changed) return;
29340
- const path2 = join18(this.walDir, STATE_FILE);
29948
+ const path2 = join20(this.walDir, STATE_FILE);
29341
29949
  try {
29342
29950
  writeSensitiveFileAtomic(path2, JSON.stringify(next));
29343
29951
  this.unsaved = false;
@@ -29841,7 +30449,7 @@ import {
29841
30449
  rmdirSync as rmdirSync5,
29842
30450
  rmSync as rmSync2
29843
30451
  } from "node:fs";
29844
- import { dirname as dirname12, join as join19 } from "node:path";
30452
+ import { dirname as dirname12, join as join21 } from "node:path";
29845
30453
  async function revokeOnControlPlane(host, options, deps, warnings) {
29846
30454
  const resolved = resolveCredentials(
29847
30455
  {
@@ -30452,7 +31060,7 @@ async function unenrollLocked(options, deps) {
30452
31060
  ];
30453
31061
  const purged = parts.filter(([, path2, recursive]) => remove2(path2, recursive)).map(([name]) => name);
30454
31062
  let failed3 = purged.length < parts.length;
30455
- if (!existsSync15(join19(deps.paths.dir, HARNESS_RECEIPTS)) && !remove2(join19(deps.paths.dir, HARNESS_BACKUPS), true))
31063
+ if (!existsSync15(join21(deps.paths.dir, HARNESS_RECEIPTS)) && !remove2(join21(deps.paths.dir, HARNESS_BACKUPS), true))
30456
31064
  failed3 = true;
30457
31065
  const logShared = otherLiveAgents(deps.paths, deps.paths.dir).length > 0;
30458
31066
  if (!logShared) {
@@ -30510,7 +31118,7 @@ var init_unenroll = __esm({
30510
31118
 
30511
31119
  // ../../packages/tacho/src/cli/enroll.ts
30512
31120
  import { existsSync as existsSync16, lstatSync as lstatSync4, rmSync as rmSync3 } from "node:fs";
30513
- import { dirname as dirname13, isAbsolute, join as join20 } from "node:path";
31121
+ import { dirname as dirname13, isAbsolute, join as join22 } from "node:path";
30514
31122
  function rootRefusal(command2, deps, allowRoot) {
30515
31123
  if (allowRoot === true || deps.getuid?.() !== 0) return void 0;
30516
31124
  const user = deps.env["SUDO_USER"] ?? "<user>";
@@ -30536,7 +31144,7 @@ function runtimeOf(host) {
30536
31144
  if (first === void 0 || !files2.every((file) => existsSync16(file)))
30537
31145
  return void 0;
30538
31146
  const binDir = dirname13(first);
30539
- const tacho = host.daemon_command[0] === first ? shellQuote(first) : `${host.daemon_command[0]} ${shellQuote(join20(binDir, "tacho.mjs"))}`;
31147
+ const tacho = host.daemon_command[0] === first ? shellQuote(first) : `${host.daemon_command[0]} ${shellQuote(join22(binDir, "tacho.mjs"))}`;
30540
31148
  return {
30541
31149
  hookCommand: host.hook_command,
30542
31150
  credentialHelperCommand: helperCommandFor(tacho),
@@ -31630,6 +32238,7 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
31630
32238
  const mode = options.credentials ?? "brokered";
31631
32239
  try {
31632
32240
  if (mode === "brokered") {
32241
+ await signInIfNoCredential(reaching, deps);
31633
32242
  const outcome = await brokerCredentials(host, reaching, deps);
31634
32243
  for (const entry2 of outcome.harnesses)
31635
32244
  deps.out(` ${describeHarness(entry2)}`);
@@ -32792,21 +33401,21 @@ import {
32792
33401
  statSync as statSync7,
32793
33402
  unlinkSync as unlinkSync10
32794
33403
  } from "node:fs";
32795
- import { dirname as dirname14, isAbsolute as isAbsolute2, join as join21, relative, resolve as resolve7, sep } from "node:path";
33404
+ import { dirname as dirname14, isAbsolute as isAbsolute2, join as join23, relative, resolve as resolve7, sep } from "node:path";
32796
33405
  function dataLocalDir(home, env, platform) {
32797
33406
  if (platform === "darwin")
32798
- return join21(home, "Library", "Application Support");
33407
+ return join23(home, "Library", "Application Support");
32799
33408
  if (platform === "win32")
32800
- return env["LOCALAPPDATA"] ?? join21(home, "AppData", "Local");
33409
+ return env["LOCALAPPDATA"] ?? join23(home, "AppData", "Local");
32801
33410
  const xdg = env["XDG_DATA_HOME"];
32802
- return xdg !== void 0 && isAbsolute2(xdg) ? xdg : join21(home, ".local", "share");
33411
+ return xdg !== void 0 && isAbsolute2(xdg) ? xdg : join23(home, ".local", "share");
32803
33412
  }
32804
33413
  function desktopPlaces(deps) {
32805
- const oxagenDir = join21(deps.home, ".config", "oxagen");
33414
+ const oxagenDir = join23(deps.home, ".config", "oxagen");
32806
33415
  return {
32807
33416
  oxagenDir,
32808
- desktopConfig: join21(oxagenDir, "desktop.json"),
32809
- copies: join21(
33417
+ desktopConfig: join23(oxagenDir, "desktop.json"),
33418
+ copies: join23(
32810
33419
  dataLocalDir(deps.home, deps.env, deps.platform),
32811
33420
  "oxagen",
32812
33421
  "bin"
@@ -33051,7 +33660,7 @@ function undoEntry(entry2, deps, tally2) {
33051
33660
  }
33052
33661
  function versionDirs(copies) {
33053
33662
  try {
33054
- return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) => join21(copies, entry2.name)).sort();
33663
+ return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) => join23(copies, entry2.name)).sort();
33055
33664
  } catch {
33056
33665
  return [];
33057
33666
  }
@@ -33087,7 +33696,7 @@ function journaledCopies(journal, copies, tally2) {
33087
33696
  function removeSidecars(dir, platform, tally2) {
33088
33697
  const note = platform === "win32" ? `. Windows keeps a running program's file, so delete ${dir} after this command exits` : "";
33089
33698
  for (const name of ["oxagen", "tacho"]) {
33090
- const file = join21(dir, platform === "win32" ? `${name}.exe` : name);
33699
+ const file = join23(dir, platform === "win32" ? `${name}.exe` : name);
33091
33700
  removeFile(file, tally2, note);
33092
33701
  }
33093
33702
  }
@@ -33175,7 +33784,7 @@ async function uninstall(options, deps) {
33175
33784
  tally2.left.push(`cannot remove ${dir}: ${reasonOf3(error)}`);
33176
33785
  }
33177
33786
  }
33178
- const configDir = join21(deps.home, ".config");
33787
+ const configDir = join23(deps.home, ".config");
33179
33788
  if (record.configDirCreated && removeDirIfEmpty(configDir))
33180
33789
  tally2.removed.push(configDir);
33181
33790
  for (const path2 of tally2.removed) deps.out(` removed ${path2}`);
@@ -33589,9 +34198,9 @@ var init_run = __esm({
33589
34198
  });
33590
34199
 
33591
34200
  // ../../packages/tacho/src/cli/agent-run.ts
33592
- import { spawn as spawn2 } from "node:child_process";
34201
+ import { spawn as spawn4 } from "node:child_process";
33593
34202
  import { randomUUID as randomUUID2 } from "node:crypto";
33594
- import { constants as constants2 } from "node:os";
34203
+ import { constants as constants3 } from "node:os";
33595
34204
  import { posix as posix3, win32 as win324 } from "node:path";
33596
34205
  function programName(program2) {
33597
34206
  const base2 = (program2.includes("\\") ? win324 : posix3).basename(program2);
@@ -33617,7 +34226,7 @@ function sessionRefusal(stdout) {
33617
34226
  function exitCodeOf(exit) {
33618
34227
  if (exit.error !== void 0) return 1;
33619
34228
  if (exit.code !== null) return exit.code;
33620
- const number2 = exit.signal !== null ? constants2.signals[exit.signal] : void 0;
34229
+ const number2 = exit.signal !== null ? constants3.signals[exit.signal] : void 0;
33621
34230
  return number2 !== void 0 ? 128 + number2 : 1;
33622
34231
  }
33623
34232
  function spawnAgent(command2, args, options) {
@@ -33628,7 +34237,7 @@ function spawnAgent(command2, args, options) {
33628
34237
  options.env
33629
34238
  );
33630
34239
  return new Promise((resolve25) => {
33631
- const child = spawn2(invocation.command, invocation.args, {
34240
+ const child = spawn4(invocation.command, invocation.args, {
33632
34241
  cwd: options.cwd,
33633
34242
  env: options.env,
33634
34243
  stdio: "inherit",
@@ -33853,11 +34462,11 @@ var init_work_order_start = __esm({
33853
34462
 
33854
34463
  // ../../packages/tacho/src/host/work-orders.ts
33855
34464
  import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
33856
- import { join as join22 } from "node:path";
34465
+ import { join as join24 } from "node:path";
33857
34466
  function orderFile(paths, id2) {
33858
34467
  if (!WORK_ORDER_ID_PATTERN.test(id2))
33859
34468
  throw new Error(`${JSON.stringify(id2)} is not a work order id`);
33860
- return join22(paths.workOrders, `${id2}.json`);
34469
+ return join24(paths.workOrders, `${id2}.json`);
33861
34470
  }
33862
34471
  function readKept(path2) {
33863
34472
  try {
@@ -33907,7 +34516,7 @@ function removeWorkOrder(paths, id2) {
33907
34516
  function runningFile(paths, id2) {
33908
34517
  if (!WORK_ORDER_ID_PATTERN.test(id2))
33909
34518
  throw new Error(`${JSON.stringify(id2)} is not a work order id`);
33910
- return join22(paths.workOrders, `${id2}.running`);
34519
+ return join24(paths.workOrders, `${id2}.running`);
33911
34520
  }
33912
34521
  function processIsAlive(pid) {
33913
34522
  try {
@@ -34000,7 +34609,7 @@ var init_work_orders = __esm({
34000
34609
  });
34001
34610
 
34002
34611
  // ../../packages/tacho/src/cli/work.ts
34003
- import { spawnSync as spawnSync5 } from "node:child_process";
34612
+ import { spawnSync as spawnSync6 } from "node:child_process";
34004
34613
  function liveAgents(deps) {
34005
34614
  return listAgents(homeOf(deps.paths)).filter(agentIsLive);
34006
34615
  }
@@ -34188,7 +34797,7 @@ async function workStart(id2, deps) {
34188
34797
  return exitCodeOf(exit);
34189
34798
  }
34190
34799
  function gitHeadIn(cwd) {
34191
- const result2 = spawnSync5("git", ["-C", cwd, "rev-parse", "HEAD"], {
34800
+ const result2 = spawnSync6("git", ["-C", cwd, "rev-parse", "HEAD"], {
34192
34801
  encoding: "utf8",
34193
34802
  stdio: ["ignore", "pipe", "pipe"],
34194
34803
  timeout: 1e4
@@ -34309,10 +34918,12 @@ var init_work = __esm({
34309
34918
  var cli_exports = {};
34310
34919
  __export(cli_exports, {
34311
34920
  BACKFILL_EXIT: () => BACKFILL_EXIT,
34921
+ CLAUDE_OAUTH_TOKEN_ENV: () => CLAUDE_OAUTH_TOKEN_ENV,
34312
34922
  CONTAINED_AGENTS: () => CONTAINED_AGENTS,
34313
34923
  CURSOR_COVERAGE_NOTE: () => CURSOR_COVERAGE_NOTE,
34314
34924
  MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
34315
34925
  SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
34926
+ acquireSubscriptionToken: () => acquireSubscriptionToken,
34316
34927
  addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
34317
34928
  agentNameFromCommand: () => agentNameFromCommand,
34318
34929
  backfillCommand: () => backfillCommand,
@@ -34320,6 +34931,7 @@ __export(cli_exports, {
34320
34931
  canServeOxagenTools: () => canServeOxagenTools,
34321
34932
  claudeFacts: () => claudeFacts,
34322
34933
  credentialIssue: () => credentialIssue,
34934
+ credentialLogin: () => credentialLogin,
34323
34935
  credentialStatus: () => credentialStatus,
34324
34936
  cursorAppFacts: () => cursorAppFacts,
34325
34937
  cursorFacts: () => cursorFacts,
@@ -34370,6 +34982,7 @@ __export(cli_exports, {
34370
34982
  shellQuote: () => shellQuote,
34371
34983
  shimError: () => shimError,
34372
34984
  shippingHealth: () => shippingHealth,
34985
+ signInIfNoCredential: () => signInIfNoCredential,
34373
34986
  spawnAgent: () => spawnAgent,
34374
34987
  stableExecutablePath: () => stableExecutablePath,
34375
34988
  status: () => status,
@@ -38104,6 +38717,7 @@ function containedBridgeHandler(options) {
38104
38717
  const modelPaths = options.harness === "claude-code" ? ["/model/v1/messages", "/model/v1/messages/count_tokens"] : ["/model/v1/responses", "/model/v1/chat/completions"];
38105
38718
  if (request3.method === "POST" && modelPaths.includes(path2)) {
38106
38719
  const credential = options.issueCredential();
38720
+ const chatgpt = options.harness === "codex" && options.chatgptLogin?.() === true;
38107
38721
  request3.headers = {
38108
38722
  "content-type": "application/json",
38109
38723
  ...request3.headers["content-length"] ? { "content-length": request3.headers["content-length"] } : {},
@@ -38111,9 +38725,12 @@ function containedBridgeHandler(options) {
38111
38725
  ...request3.headers["anthropic-beta"] ? { "anthropic-beta": request3.headers["anthropic-beta"] } : {},
38112
38726
  host: `127.0.0.1:${options.modelPort}`,
38113
38727
  "x-oxagen-session": options.sessionId,
38114
- ...options.harness === "claude-code" ? { "x-api-key": credential } : { authorization: `Bearer ${credential}` }
38728
+ ...options.harness === "claude-code" ? { "x-api-key": credential } : { authorization: `Bearer ${credential}` },
38729
+ // Marks the call as a ChatGPT login's; the gateway puts the
38730
+ // login's own account in its place.
38731
+ ...chatgpt ? { "chatgpt-account-id": "custody" } : {}
38115
38732
  };
38116
- request3.url = options.harness === "claude-code" ? path2.replace("/model", "/anthropic") : path2.replace("/model", "/openai");
38733
+ request3.url = options.harness === "claude-code" ? path2.replace("/model", "/anthropic") : chatgpt ? path2.replace("/model/v1", "/backend-api/codex") : path2.replace("/model", "/openai");
38117
38734
  options.model(request3, response);
38118
38735
  return;
38119
38736
  }
@@ -38265,7 +38882,7 @@ var init_bridge = __esm({
38265
38882
 
38266
38883
  // ../../packages/tacho/src/contained/workspace.ts
38267
38884
  import { lstatSync as lstatSync6, readFileSync as readFileSync14, readdirSync as readdirSync11, realpathSync as realpathSync3 } from "node:fs";
38268
- import { isAbsolute as isAbsolute3, join as join23, relative as relative2, resolve as resolve9, sep as sep2 } from "node:path";
38885
+ import { isAbsolute as isAbsolute3, join as join25, relative as relative2, resolve as resolve9, sep as sep2 } from "node:path";
38269
38886
  function within(root, path2) {
38270
38887
  const rel = relative2(root, path2);
38271
38888
  return rel === "" || !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
@@ -38295,7 +38912,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
38295
38912
  }
38296
38913
  const visit2 = (directory) => {
38297
38914
  for (const entry2 of readdirSync11(directory)) {
38298
- const path2 = join23(directory, entry2);
38915
+ const path2 = join25(directory, entry2);
38299
38916
  if (CREDENTIAL_PATHS.has(entry2) || isEnvFile(entry2))
38300
38917
  throw new Error(
38301
38918
  `Remove local credential files from the contained checkout before launch: ${named(path2)}`
@@ -38329,7 +38946,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
38329
38946
  }
38330
38947
  function gitDirectoryMounts(workspace) {
38331
38948
  const root = realpathSync3(workspace);
38332
- const dotGit = join23(root, ".git");
38949
+ const dotGit = join25(root, ".git");
38333
38950
  let stat7;
38334
38951
  try {
38335
38952
  stat7 = lstatSync6(dotGit);
@@ -38367,7 +38984,7 @@ function gitDirectoryMounts(workspace) {
38367
38984
  if (gitDir !== void 0) {
38368
38985
  let common;
38369
38986
  try {
38370
- common = readFileSync14(join23(gitDir, "commondir"), "utf8").trim();
38987
+ common = readFileSync14(join25(gitDir, "commondir"), "utf8").trim();
38371
38988
  } catch {
38372
38989
  common = void 0;
38373
38990
  }
@@ -38394,7 +39011,7 @@ var init_workspace = __esm({
38394
39011
  // ../../packages/tacho/src/collector/hardened-git.ts
38395
39012
  import { execFile as execFile2 } from "node:child_process";
38396
39013
  import { lstatSync as lstatSync7, readFileSync as readFileSync15, statSync as statSync8 } from "node:fs";
38397
- import { dirname as dirname15, join as join24, resolve as resolve10 } from "node:path";
39014
+ import { dirname as dirname15, join as join26, resolve as resolve10 } from "node:path";
38398
39015
  function hardenedGitArgs(cwd, args, globals = []) {
38399
39016
  const subcommand = args[0];
38400
39017
  const end = args.indexOf("--");
@@ -38433,7 +39050,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
38433
39050
  };
38434
39051
  }
38435
39052
  for (let at2 = directory; ; at2 = dirname15(at2)) {
38436
- const marker = join24(at2, ".git");
39053
+ const marker = join26(at2, ".git");
38437
39054
  const link = tryStat(deps.lstat, marker);
38438
39055
  if (link !== void 0) {
38439
39056
  owners.push(link);
@@ -38441,7 +39058,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
38441
39058
  if (target2 !== void 0) {
38442
39059
  owners.push(target2);
38443
39060
  if (target2.isDirectory()) {
38444
- const config = tryStat(deps.stat, join24(marker, "config"));
39061
+ const config = tryStat(deps.stat, join26(marker, "config"));
38445
39062
  if (config !== void 0) owners.push(config);
38446
39063
  }
38447
39064
  }
@@ -38593,7 +39210,7 @@ var init_hardened_git = __esm({
38593
39210
 
38594
39211
  // ../../packages/tacho/src/contained/profile.ts
38595
39212
  import { createHash as createHash6 } from "node:crypto";
38596
- import { isAbsolute as isAbsolute4, join as join25, relative as relative3, sep as sep3 } from "node:path";
39213
+ import { isAbsolute as isAbsolute4, join as join27, relative as relative3, sep as sep3 } from "node:path";
38597
39214
  function containerPath(workspace, path2) {
38598
39215
  const inside2 = relative3(workspace, path2);
38599
39216
  if (inside2 === "" || isAbsolute4(inside2) || inside2 === ".." || inside2.startsWith(`..${sep3}`))
@@ -38603,7 +39220,7 @@ function containerPath(workspace, path2) {
38603
39220
  return `/workspace/${inside2.split(sep3).join("/")}`;
38604
39221
  }
38605
39222
  function gitMountPairs(spec) {
38606
- if (!spec.gitMounts.includes(join25(spec.workspace, ".git")))
39223
+ if (!spec.gitMounts.includes(join27(spec.workspace, ".git")))
38607
39224
  throw new Error(
38608
39225
  "Contained execution must mount the repository's .git read-only"
38609
39226
  );
@@ -38760,7 +39377,7 @@ var init_profile = __esm({
38760
39377
  });
38761
39378
 
38762
39379
  // ../../packages/tacho/src/contained/launcher.ts
38763
- import { execFile as execFile3, spawn as spawn3 } from "node:child_process";
39380
+ import { execFile as execFile3, spawn as spawn5 } from "node:child_process";
38764
39381
  import { randomBytes as randomBytes8 } from "node:crypto";
38765
39382
  import {
38766
39383
  chmodSync as chmodSync7,
@@ -38770,7 +39387,7 @@ import {
38770
39387
  writeFileSync as writeFileSync2
38771
39388
  } from "node:fs";
38772
39389
  import { tmpdir } from "node:os";
38773
- import { isAbsolute as isAbsolute5, join as join26 } from "node:path";
39390
+ import { isAbsolute as isAbsolute5, join as join28 } from "node:path";
38774
39391
  import { promisify } from "node:util";
38775
39392
  function reason(error) {
38776
39393
  return error instanceof Error ? error.message : String(error);
@@ -38809,7 +39426,7 @@ async function launchContainedAgent(options) {
38809
39426
  if (digest6 === void 0)
38810
39427
  throw new Error("The operator's contained image is not installed");
38811
39428
  const sessionId = `contained-${randomBytes8(16).toString("hex")}`;
38812
- const directory = mkdtempSync(join26(tmpdir(), "oxagen-contained-"));
39429
+ const directory = mkdtempSync(join28(tmpdir(), "oxagen-contained-"));
38813
39430
  chmodSync7(directory, 448);
38814
39431
  let prepared;
38815
39432
  let containerCreated = false;
@@ -38830,7 +39447,7 @@ async function launchContainedAgent(options) {
38830
39447
  for (const [name2, contents] of entries2) {
38831
39448
  if (!/^[a-z0-9.-]+$/.test(name2))
38832
39449
  throw new Error("Invalid contained configuration filename");
38833
- writeFileSync2(join26(directory, name2), contents, {
39450
+ writeFileSync2(join28(directory, name2), contents, {
38834
39451
  mode: 256,
38835
39452
  flag: "wx"
38836
39453
  });
@@ -38861,7 +39478,7 @@ async function launchContainedAgent(options) {
38861
39478
  if (options.signal?.aborted)
38862
39479
  throw new Error("Contained run cancelled before start");
38863
39480
  exitCode = await new Promise((resolve25, reject) => {
38864
- const child = spawn3("docker", ["start", "--attach", name], {
39481
+ const child = spawn5("docker", ["start", "--attach", name], {
38865
39482
  stdio: ["ignore", "pipe", "pipe"]
38866
39483
  });
38867
39484
  const stop = () => {
@@ -39229,7 +39846,7 @@ var init_privilege = __esm({
39229
39846
 
39230
39847
  // ../../packages/tacho/src/contained/sandbox/launcher.ts
39231
39848
  import { randomBytes as randomBytes9 } from "node:crypto";
39232
- import { join as join27 } from "node:path";
39849
+ import { join as join29 } from "node:path";
39233
39850
  function shellQuote3(value) {
39234
39851
  return `'${value.replace(/'/g, `'\\''`)}'`;
39235
39852
  }
@@ -39319,7 +39936,7 @@ async function launchSandboxAgent(options) {
39319
39936
  { ...asAgent, cwd: SANDBOX_AGENT_HOME, timeoutMs: 15 * 6e4 }
39320
39937
  );
39321
39938
  for (const [name, contents] of Object.entries(claudeCodeUserState())) {
39322
- const path2 = join27(SANDBOX_AGENT_HOME, name);
39939
+ const path2 = join29(SANDBOX_AGENT_HOME, name);
39323
39940
  system.writeFile(path2, contents, 384);
39324
39941
  system.chown(path2, SANDBOX_AGENT_UID, SANDBOX_AGENT_GID);
39325
39942
  }
@@ -39443,7 +40060,7 @@ var init_request = __esm({
39443
40060
  });
39444
40061
 
39445
40062
  // ../../packages/tacho/src/contained/sandbox/system.ts
39446
- import { execFile as execFile4, spawn as spawn4 } from "node:child_process";
40063
+ import { execFile as execFile4, spawn as spawn6 } from "node:child_process";
39447
40064
  import {
39448
40065
  chmodSync as chmodSync8,
39449
40066
  chownSync as chownSync2,
@@ -39474,7 +40091,7 @@ var init_system = __esm({
39474
40091
  return { stdout: String(answer.stdout), stderr: String(answer.stderr) };
39475
40092
  },
39476
40093
  spawn: (file, args, options) => {
39477
- const child = spawn4(file, [...args], {
40094
+ const child = spawn6(file, [...args], {
39478
40095
  ...options.uid !== void 0 ? { uid: options.uid } : {},
39479
40096
  ...options.gid !== void 0 ? { gid: options.gid } : {},
39480
40097
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
@@ -39525,10 +40142,10 @@ var init_system = __esm({
39525
40142
  });
39526
40143
 
39527
40144
  // ../../packages/tacho/src/contained/runner.ts
39528
- import { join as join28 } from "node:path";
40145
+ import { join as join30 } from "node:path";
39529
40146
  function containedHookEnv(harness, workspace) {
39530
- const home = join28(workspace, ".oxagen-contained", "home");
39531
- return harness === "codex" ? { CODEX_HOME: join28(home, ".codex") } : { CLAUDE_CONFIG_DIR: join28(home, ".claude") };
40147
+ const home = join30(workspace, ".oxagen-contained", "home");
40148
+ return harness === "codex" ? { CODEX_HOME: join30(home, ".codex") } : { CLAUDE_CONFIG_DIR: join30(home, ".claude") };
39532
40149
  }
39533
40150
  function containedLaunchEndpoint(host) {
39534
40151
  const ingest = new URL(host.endpoints.ingest);
@@ -39624,6 +40241,7 @@ function createContainedRunner(options) {
39624
40241
  throw new Error("Credential custody unavailable");
39625
40242
  return answer.body.token;
39626
40243
  },
40244
+ ...options.chatgptLogin !== void 0 ? { chatgptLogin: options.chatgptLogin } : {},
39627
40245
  model: (request3, response) => options.model.handle(request3, response),
39628
40246
  hook: options.hook,
39629
40247
  opening: route.opening,
@@ -39726,7 +40344,7 @@ function createContainedRunner(options) {
39726
40344
  sessionUuid2 = started.recorder.sessionUuid;
39727
40345
  active2.set(sessionUuid2, controller);
39728
40346
  const bridge = await startContainedBridge({
39729
- socketPath: join28(directory, "bridge.sock"),
40347
+ socketPath: join30(directory, "bridge.sock"),
39730
40348
  ...bridgeRoutes({
39731
40349
  sessionId,
39732
40350
  workspace,
@@ -39810,7 +40428,7 @@ function createContainedRunner(options) {
39810
40428
  const abort = () => controller.abort();
39811
40429
  signal?.addEventListener("abort", abort, { once: true });
39812
40430
  if (signal?.aborted) abort();
39813
- const env = { CLAUDE_CONFIG_DIR: join28(SANDBOX_AGENT_HOME, ".claude") };
40431
+ const env = { CLAUDE_CONFIG_DIR: join30(SANDBOX_AGENT_HOME, ".claude") };
39814
40432
  let sessionUuid2;
39815
40433
  let launchedSessionId;
39816
40434
  let opened = false;
@@ -39943,14 +40561,96 @@ var init_runner = __esm({
39943
40561
  }
39944
40562
  });
39945
40563
 
40564
+ // ../../packages/tacho/src/host/chatgpt-refresh.ts
40565
+ function jwtExpiryMs(token) {
40566
+ const payload = token.split(".")[1];
40567
+ if (payload === void 0) return void 0;
40568
+ try {
40569
+ const claims = JSON.parse(
40570
+ Buffer.from(payload, "base64url").toString("utf8")
40571
+ );
40572
+ return typeof claims.exp === "number" ? claims.exp * 1e3 : void 0;
40573
+ } catch {
40574
+ return void 0;
40575
+ }
40576
+ }
40577
+ function chatGptLoginDue(login, takenAt, now) {
40578
+ const exp = jwtExpiryMs(login.access_token);
40579
+ if (exp !== void 0) return exp - now < CHATGPT_REFRESH_MARGIN_MS;
40580
+ return now - takenAt > CHATGPT_REFRESH_MAX_AGE_MS;
40581
+ }
40582
+ async function refreshChatGptLogin(login, fetchImpl) {
40583
+ if (login.refresh_token.length === 0)
40584
+ throw new Error("the ChatGPT login in custody has no refresh token");
40585
+ const response = await fetchImpl(CHATGPT_TOKEN_URL, {
40586
+ method: "POST",
40587
+ headers: { "Content-Type": "application/json" },
40588
+ body: JSON.stringify({
40589
+ client_id: CODEX_OAUTH_CLIENT_ID,
40590
+ grant_type: "refresh_token",
40591
+ refresh_token: login.refresh_token,
40592
+ scope: "openid profile email"
40593
+ })
40594
+ });
40595
+ if (!response.ok)
40596
+ throw new Error(
40597
+ `auth.openai.com refused to refresh the ChatGPT login (${response.status}); run \`codex login\`, then \`oxagen agent enroll\``
40598
+ );
40599
+ let body2;
40600
+ try {
40601
+ body2 = JSON.parse(await response.text());
40602
+ } catch {
40603
+ throw new Error("auth.openai.com answered with something other than JSON");
40604
+ }
40605
+ if (typeof body2.access_token !== "string" || body2.access_token.length === 0)
40606
+ throw new Error("auth.openai.com answered without an access token");
40607
+ return {
40608
+ ...login,
40609
+ access_token: body2.access_token,
40610
+ refresh_token: typeof body2.refresh_token === "string" ? body2.refresh_token : login.refresh_token,
40611
+ ...typeof body2.id_token === "string" ? { id_token: body2.id_token } : {}
40612
+ };
40613
+ }
40614
+ async function refreshChatGptCustody(store, fetchImpl, now) {
40615
+ const entry2 = store.status().find((c) => c.provider === "openai" && c.kind === "chatgpt");
40616
+ if (entry2 === void 0) return "none";
40617
+ const held = store.read("openai");
40618
+ const login = held?.kind === "chatgpt" ? decodeChatGptLogin(held.secret) : void 0;
40619
+ if (login === void 0) return { failed: "custody holds no ChatGPT login" };
40620
+ if (!chatGptLoginDue(login, Date.parse(entry2.taken_at), now)) return "fresh";
40621
+ try {
40622
+ const next = await refreshChatGptLogin(login, fetchImpl);
40623
+ store.take(
40624
+ "openai",
40625
+ { kind: "chatgpt", secret: JSON.stringify(next) },
40626
+ "chatgpt:refresh",
40627
+ now
40628
+ );
40629
+ return "refreshed";
40630
+ } catch (error) {
40631
+ return { failed: error instanceof Error ? error.message : String(error) };
40632
+ }
40633
+ }
40634
+ var CODEX_OAUTH_CLIENT_ID, CHATGPT_TOKEN_URL, CHATGPT_REFRESH_MARGIN_MS, CHATGPT_REFRESH_MAX_AGE_MS;
40635
+ var init_chatgpt_refresh = __esm({
40636
+ "../../packages/tacho/src/host/chatgpt-refresh.ts"() {
40637
+ "use strict";
40638
+ init_model_credential();
40639
+ CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
40640
+ CHATGPT_TOKEN_URL = "https://auth.openai.com/oauth/token";
40641
+ CHATGPT_REFRESH_MARGIN_MS = 60 * 6e4;
40642
+ CHATGPT_REFRESH_MAX_AGE_MS = 7 * 24 * 60 * 6e4;
40643
+ }
40644
+ });
40645
+
39946
40646
  // ../../packages/tacho/src/host/repositories.ts
39947
- import { existsSync as existsSync18 } from "node:fs";
39948
- import { join as join29, resolve as resolve11, sep as sep4 } from "node:path";
40647
+ import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
40648
+ import { dirname as dirname17, join as join31, resolve as resolve11, sep as sep4 } from "node:path";
39949
40649
  function repositoriesFile(home) {
39950
- return join29(home.tachoDir, "repositories.json");
40650
+ return join31(home.tachoDir, "repositories.json");
39951
40651
  }
39952
40652
  function workTreesDir(home) {
39953
- return join29(home.tachoDir, "work");
40653
+ return join31(home.tachoDir, "work");
39954
40654
  }
39955
40655
  function readIndex(home) {
39956
40656
  let raw;
@@ -39974,6 +40674,7 @@ function latest(checkouts) {
39974
40674
  function recordCheckout(home, canonical3, root, seenAt) {
39975
40675
  if (canonical3.length === 0 || root.length === 0) return false;
39976
40676
  if (isUnder(root, workTreesDir(home))) return false;
40677
+ root = mainCheckoutOf(root);
39977
40678
  const key = canonical3.toLowerCase();
39978
40679
  const index = readIndex(home);
39979
40680
  const kept = index.repositories[key] ?? [];
@@ -40000,8 +40701,24 @@ function recordCheckout(home, canonical3, root, seenAt) {
40000
40701
  );
40001
40702
  return true;
40002
40703
  }
40704
+ function mainCheckoutOf(root) {
40705
+ const dotGit = join31(root, ".git");
40706
+ try {
40707
+ if (!statSync10(dotGit).isFile()) return root;
40708
+ const line = readFileSync17(dotGit, "utf8").trim();
40709
+ const match = /^gitdir:\s*(.+)$/m.exec(line);
40710
+ if (match === null) return root;
40711
+ const gitdir = resolve11(root, match[1].trim());
40712
+ const worktrees = dirname17(gitdir);
40713
+ if (!worktrees.endsWith(`${sep4}.git${sep4}worktrees`)) return root;
40714
+ const clone4 = dirname17(dirname17(worktrees));
40715
+ return existsSync18(join31(clone4, ".git")) ? clone4 : root;
40716
+ } catch {
40717
+ return root;
40718
+ }
40719
+ }
40003
40720
  function defaultIsCheckout(root) {
40004
- return existsSync18(join29(root, ".git"));
40721
+ return existsSync18(join31(root, ".git"));
40005
40722
  }
40006
40723
  function findCheckout(home, repository, host, deps = {}) {
40007
40724
  const isCheckout = deps.isCheckout ?? defaultIsCheckout;
@@ -40049,7 +40766,7 @@ var init_repositories = __esm({
40049
40766
 
40050
40767
  // ../../packages/tacho/src/collector/detector.ts
40051
40768
  import { promises as fs3 } from "node:fs";
40052
- import { basename as basename5, join as join30 } from "node:path";
40769
+ import { basename as basename5, join as join32 } from "node:path";
40053
40770
  async function eachLimited(items, limit, task) {
40054
40771
  let next = 0;
40055
40772
  const workers = Array.from(
@@ -40075,7 +40792,7 @@ async function scanProject(dir) {
40075
40792
  await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
40076
40793
  const match = SESSION_FILE.exec(name);
40077
40794
  if (match === null) return;
40078
- const path2 = join30(dir, name);
40795
+ const path2 = join32(dir, name);
40079
40796
  try {
40080
40797
  const st = await fs3.stat(path2);
40081
40798
  out.push({ sessionId: match[1], path: path2, mtimeMs: st.mtimeMs });
@@ -40116,7 +40833,7 @@ var init_detector = __esm({
40116
40833
  continue;
40117
40834
  }
40118
40835
  await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
40119
- const dir = join30(root, name);
40836
+ const dir = join32(root, name);
40120
40837
  try {
40121
40838
  const st = await fs3.stat(dir);
40122
40839
  if (st.isDirectory()) dirs.push({ dir, mtimeMs: st.mtimeMs });
@@ -40356,7 +41073,7 @@ var init_detector = __esm({
40356
41073
  transcript_path: transcript.path,
40357
41074
  transcript_mtime: new Date(transcript.mtimeMs).toISOString(),
40358
41075
  claude_pids: processes.map((p) => p.pid),
40359
- project: basename5(join30(transcript.path, ".."))
41076
+ project: basename5(join32(transcript.path, ".."))
40360
41077
  }
40361
41078
  })
40362
41079
  );
@@ -40726,9 +41443,9 @@ var init_git_facts = __esm({
40726
41443
  });
40727
41444
 
40728
41445
  // ../../packages/tacho/src/collector/work-order-autostart.ts
40729
- import { spawn as spawn5 } from "node:child_process";
41446
+ import { spawn as spawn7 } from "node:child_process";
40730
41447
  import { closeSync as closeSync7, existsSync as existsSync19, openSync as openSync7 } from "node:fs";
40731
- import { dirname as dirname17, join as join31 } from "node:path";
41448
+ import { dirname as dirname18, join as join33 } from "node:path";
40732
41449
  function asError(error) {
40733
41450
  return error instanceof Error ? error : new Error(String(error));
40734
41451
  }
@@ -40741,6 +41458,14 @@ function createWorkOrderAutostart(deps) {
40741
41458
  const { paths, log } = deps;
40742
41459
  const isAlive = deps.isAlive ?? processIsAlive;
40743
41460
  const spawnHarness = deps.spawn ?? spawnDetached;
41461
+ let searchPath;
41462
+ const pathForHarness = () => {
41463
+ if (searchPath === void 0) {
41464
+ const login = (deps.loginPath ?? (() => loginShellPath(deps.env)))();
41465
+ searchPath = harnessPath(deps.env, deps.env["HOME"] ?? "", login);
41466
+ }
41467
+ return searchPath;
41468
+ };
40744
41469
  const lookup = deps.findCheckout ?? ((repository) => findCheckout(paths, repository, deps.host()));
40745
41470
  const retry = /* @__PURE__ */ new Map();
40746
41471
  const cleanups = /* @__PURE__ */ new Set();
@@ -40773,7 +41498,7 @@ function createWorkOrderAutostart(deps) {
40773
41498
  }
40774
41499
  async function abandon(id2) {
40775
41500
  forget(id2);
40776
- const worktree = join31(workTreesDir(paths), id2);
41501
+ const worktree = join33(workTreesDir(paths), id2);
40777
41502
  if (!existsSync19(worktree)) return;
40778
41503
  const [common, branch, head2] = await Promise.all([
40779
41504
  git6(worktree, ["rev-parse", "--path-format=absolute", "--git-common-dir"]),
@@ -40789,7 +41514,7 @@ function createWorkOrderAutostart(deps) {
40789
41514
  await cleanUp(
40790
41515
  {
40791
41516
  work_order: id2,
40792
- root: dirname17(common.out),
41517
+ root: dirname18(common.out),
40793
41518
  worktree,
40794
41519
  branch: branch.out,
40795
41520
  base_sha: head2.out
@@ -40811,7 +41536,7 @@ function createWorkOrderAutostart(deps) {
40811
41536
  return waitMs;
40812
41537
  }
40813
41538
  async function prepareWorktree(root, id2, itemNumber) {
40814
- const worktree = join31(workTreesDir(paths), id2);
41539
+ const worktree = join33(workTreesDir(paths), id2);
40815
41540
  const logPath = `${worktree}.log`;
40816
41541
  if (existsSync19(worktree)) {
40817
41542
  const [branch2, head2] = await Promise.all([
@@ -40963,7 +41688,12 @@ function createWorkOrderAutostart(deps) {
40963
41688
  headlessArgs(harness, claim.prompt),
40964
41689
  {
40965
41690
  cwd: workspace.worktree,
40966
- env: { ...deps.env, ...headlessEnv(harness), [WORK_ORDER_ENV]: id2 },
41691
+ env: {
41692
+ ...deps.env,
41693
+ PATH: pathForHarness(),
41694
+ ...headlessEnv(harness),
41695
+ [WORK_ORDER_ENV]: id2
41696
+ },
40967
41697
  logPath: workspace.log
40968
41698
  }
40969
41699
  );
@@ -41060,6 +41790,7 @@ var init_work_order_autostart = __esm({
41060
41790
  "../../packages/tacho/src/collector/work-order-autostart.ts"() {
41061
41791
  "use strict";
41062
41792
  init_fs();
41793
+ init_harness_path();
41063
41794
  init_repositories();
41064
41795
  init_work_order_start();
41065
41796
  init_work_orders();
@@ -41071,7 +41802,7 @@ var init_work_order_autostart = __esm({
41071
41802
  spawnDetached = (command2, args, options) => new Promise((resolve25) => {
41072
41803
  let fd2;
41073
41804
  try {
41074
- ensureDir(dirname17(options.logPath));
41805
+ ensureDir(dirname18(options.logPath));
41075
41806
  fd2 = openSync7(options.logPath, "a", 384);
41076
41807
  } catch (error) {
41077
41808
  resolve25({ ok: false, error: asError(error) });
@@ -41088,7 +41819,7 @@ var init_work_order_autostart = __esm({
41088
41819
  };
41089
41820
  let settled2 = false;
41090
41821
  try {
41091
- const child = spawn5(command2, args, {
41822
+ const child = spawn7(command2, args, {
41092
41823
  cwd: options.cwd,
41093
41824
  env: options.env,
41094
41825
  // Its own process group, so a daemon restart does not stop the run.
@@ -41175,7 +41906,7 @@ import {
41175
41906
  rm,
41176
41907
  unlink
41177
41908
  } from "node:fs/promises";
41178
- import { dirname as dirname18, join as join32 } from "node:path";
41909
+ import { dirname as dirname19, join as join34 } from "node:path";
41179
41910
  function byPath(a, b3) {
41180
41911
  return a.path < b3.path ? -1 : a.path > b3.path ? 1 : 0;
41181
41912
  }
@@ -41218,7 +41949,7 @@ async function contentHash(absolutePath, kind, keep) {
41218
41949
  if (keep !== void 0) {
41219
41950
  try {
41220
41951
  await mkdir(keep.dir, { recursive: true, mode: 448 });
41221
- const temp2 = join32(keep.dir, `.partial-${randomUUID3()}`);
41952
+ const temp2 = join34(keep.dir, `.partial-${randomUUID3()}`);
41222
41953
  copy = {
41223
41954
  temp: temp2,
41224
41955
  handle: await open3(temp2, "wx", keep.executable ? 448 : 384)
@@ -41262,7 +41993,7 @@ async function settleCopy(temp, name) {
41262
41993
  if (temp === void 0) return;
41263
41994
  if (name !== void 0)
41264
41995
  try {
41265
- await rename(temp, join32(dirname18(temp), name));
41996
+ await rename(temp, join34(dirname19(temp), name));
41266
41997
  return;
41267
41998
  } catch {
41268
41999
  }
@@ -41278,7 +42009,7 @@ async function copiedBytes(dir) {
41278
42009
  let total2 = 0;
41279
42010
  for (const name of names2) {
41280
42011
  try {
41281
- total2 += (await lstat(join32(dir, name))).size;
42012
+ total2 += (await lstat(join34(dir, name))).size;
41282
42013
  } catch {
41283
42014
  }
41284
42015
  }
@@ -41294,7 +42025,7 @@ async function removeCopiesOutside(root, live) {
41294
42025
  const keep = new Set(live());
41295
42026
  await Promise.all(
41296
42027
  names2.filter((name) => !keep.has(name)).map(
41297
- (name) => rm(join32(root, name), { recursive: true, force: true }).catch(
42028
+ (name) => rm(join34(root, name), { recursive: true, force: true }).catch(
41298
42029
  () => void 0
41299
42030
  )
41300
42031
  )
@@ -41316,7 +42047,7 @@ async function readPreexistingPaths(exec4, root, startedAtMs, copies) {
41316
42047
  if (found === void 0) return;
41317
42048
  if (found === "absent") {
41318
42049
  if (!deletedIn(entry2.code)) return;
41319
- const parent = await directoryChangedAt(dirname18(absolute));
42050
+ const parent = await directoryChangedAt(dirname19(absolute));
41320
42051
  if (parent === void 0 || parent >= startedAtMs) return;
41321
42052
  paths[entry2.path] = null;
41322
42053
  return;
@@ -41401,7 +42132,7 @@ async function contributionOf(exec4, cwd, root, repoRelative, entry2, copies) {
41401
42132
  let copy = null;
41402
42133
  if (entry2 !== null) {
41403
42134
  if (copies === void 0 || !COPY_NAME.test(entry2[0])) return void 0;
41404
- copy = join32(copies, entry2[0]);
42135
+ copy = join34(copies, entry2[0]);
41405
42136
  const kept = await statOf(copy);
41406
42137
  if (kept === void 0 || kept === "absent" || kept.kind !== "file")
41407
42138
  return void 0;
@@ -41803,7 +42534,7 @@ var init_session_changes = __esm({
41803
42534
  });
41804
42535
 
41805
42536
  // ../../packages/tacho/src/collector/session-commits.ts
41806
- import { join as join33 } from "node:path";
42537
+ import { join as join35 } from "node:path";
41807
42538
  function parseMeta(stdout) {
41808
42539
  const out = [];
41809
42540
  for (const record of stdout.split("\0")) {
@@ -41928,7 +42659,7 @@ async function readSessionCommits(exec4, cwd, input) {
41928
42659
  const test = input.tests[commit.sha];
41929
42660
  const toolUseId = callAt(times.get(commit.sha), calls);
41930
42661
  const written2 = input.written;
41931
- const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(join33(root, file.path)));
42662
+ const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(join35(root, file.path)));
41932
42663
  return {
41933
42664
  sha: commit.sha,
41934
42665
  parent_shas: commit.parents.slice(0, MAX_COMMIT_PARENTS),
@@ -41976,7 +42707,7 @@ var init_session_commits = __esm({
41976
42707
 
41977
42708
  // ../../packages/tacho/src/collector/worktree-snapshot.ts
41978
42709
  import { lstat as lstat2 } from "node:fs/promises";
41979
- import { join as join34 } from "node:path";
42710
+ import { join as join36 } from "node:path";
41980
42711
  function headerPath(prefix, path2) {
41981
42712
  const name = `${prefix}${path2}`;
41982
42713
  let quoted3 = false;
@@ -42060,7 +42791,7 @@ async function worktreeFingerprint(status2, root) {
42060
42791
  const path2 = statusPath(record);
42061
42792
  if (path2 === null) return "";
42062
42793
  try {
42063
- const info = await lstat2(join34(root, path2));
42794
+ const info = await lstat2(join36(root, path2));
42064
42795
  return `${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
42065
42796
  } catch {
42066
42797
  return "absent";
@@ -42168,7 +42899,7 @@ async function readWorktreeSnapshot(exec4, cwd, baseline, measured) {
42168
42899
  }
42169
42900
  let present;
42170
42901
  try {
42171
- present = (await lstat2(join34(root, path2))).isFile();
42902
+ present = (await lstat2(join36(root, path2))).isFile();
42172
42903
  } catch {
42173
42904
  present = false;
42174
42905
  }
@@ -42271,10 +43002,10 @@ var init_worktree_snapshot = __esm({
42271
43002
  });
42272
43003
 
42273
43004
  // ../../packages/tacho/src/collector/git-lane.ts
42274
- import { join as join35 } from "node:path";
43005
+ import { join as join37 } from "node:path";
42275
43006
  function copiesDirOf(root, session) {
42276
43007
  const uuid = session.recorder.sessionUuid;
42277
- return /^[0-9a-f-]{36}$/i.test(uuid) ? join35(root, uuid) : void 0;
43008
+ return /^[0-9a-f-]{36}$/i.test(uuid) ? join37(root, uuid) : void 0;
42278
43009
  }
42279
43010
  function holdsBaseline(session) {
42280
43011
  return session.baselineCommit !== void 0 || Object.keys(session.baselines ?? {}).length > 0;
@@ -43308,7 +44039,7 @@ var init_policy_run = __esm({
43308
44039
 
43309
44040
  // ../../packages/tacho/src/collector/hook-handler.ts
43310
44041
  import { homedir as homedir4 } from "node:os";
43311
- import { dirname as dirname19, isAbsolute as isAbsolute7 } from "node:path";
44042
+ import { dirname as dirname20, isAbsolute as isAbsolute7 } from "node:path";
43312
44043
  function containmentUnmet(view, record) {
43313
44044
  return view.verified && view.bundle.mode === "enforce" && view.bundle.containment?.required === true && view.launchedContained?.(record.harnessSessionId) !== true;
43314
44045
  }
@@ -44264,7 +44995,7 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
44264
44995
  }
44265
44996
  function writtenDir(input) {
44266
44997
  const path2 = writtenPath(input);
44267
- return path2 === void 0 ? void 0 : dirname19(path2);
44998
+ return path2 === void 0 ? void 0 : dirname20(path2);
44268
44999
  }
44269
45000
  function writtenPath(input) {
44270
45001
  if (input.hook_event_name !== "PreToolUse" && input.hook_event_name !== "PostToolUse")
@@ -44490,7 +45221,7 @@ var init_steer_waiters = __esm({
44490
45221
  });
44491
45222
 
44492
45223
  // ../../packages/tacho/src/collector/hook-id-journal.ts
44493
- import { appendFileSync as appendFileSync3, existsSync as existsSync20, readFileSync as readFileSync17, rmSync as rmSync7 } from "node:fs";
45224
+ import { appendFileSync as appendFileSync3, existsSync as existsSync20, readFileSync as readFileSync18, rmSync as rmSync7 } from "node:fs";
44494
45225
  function hookIdJournalEntry(session, key, at2, events) {
44495
45226
  const highest = /* @__PURE__ */ new Map();
44496
45227
  for (const event of events) {
@@ -44517,7 +45248,7 @@ function isEntry(value) {
44517
45248
  function readHookIdJournal(path2) {
44518
45249
  if (!existsSync20(path2)) return [];
44519
45250
  const entries2 = [];
44520
- for (const line of readFileSync17(path2, "utf8").split("\n")) {
45251
+ for (const line of readFileSync18(path2, "utf8").split("\n")) {
44521
45252
  if (line.trim().length === 0) continue;
44522
45253
  try {
44523
45254
  const value = JSON.parse(line);
@@ -45642,7 +46373,7 @@ var init_hook_queues = __esm({
45642
46373
 
45643
46374
  // ../../packages/tacho/src/collector/memory-capture/memory-reader.ts
45644
46375
  import { createRequire as createRequire2 } from "node:module";
45645
- import { isAbsolute as isAbsolute8, join as join36, sep as sep6 } from "node:path";
46376
+ import { isAbsolute as isAbsolute8, join as join38, sep as sep6 } from "node:path";
45646
46377
  function claudeCodeLocation(projectsDir, memoryDir) {
45647
46378
  return {
45648
46379
  harness: "claude-code",
@@ -45654,13 +46385,13 @@ function claudeCodeLocation(projectsDir, memoryDir) {
45654
46385
  }
45655
46386
  function claudeCodeMemoryLocations(configDir, projectDirs = []) {
45656
46387
  const locations = [
45657
- claudeCodeLocation(() => join36(configDir, "projects"), "memory"),
45658
- claudeCodeLocation(() => join36(configDir, "agent-memory"), "")
46388
+ claudeCodeLocation(() => join38(configDir, "projects"), "memory"),
46389
+ claudeCodeLocation(() => join38(configDir, "agent-memory"), "")
45659
46390
  ];
45660
46391
  for (const dir of projectDirs.slice(0, MEMORY_PROJECT_DIRS_MAX))
45661
46392
  locations.push(
45662
- claudeCodeLocation(() => join36(dir, ".claude", "agent-memory"), ""),
45663
- claudeCodeLocation(() => join36(dir, ".claude", "agent-memory-local"), "")
46393
+ claudeCodeLocation(() => join38(dir, ".claude", "agent-memory"), ""),
46394
+ claudeCodeLocation(() => join38(dir, ".claude", "agent-memory-local"), "")
45664
46395
  );
45665
46396
  return locations;
45666
46397
  }
@@ -45845,13 +46576,13 @@ function createMemoryReader(deps) {
45845
46576
  const projects = await list2(top);
45846
46577
  let complete = projects.complete;
45847
46578
  for (const project2 of projects.names) {
45848
- const dir = location.memoryDir === "" ? join36(top, project2) : join36(top, project2, location.memoryDir);
46579
+ const dir = location.memoryDir === "" ? join38(top, project2) : join38(top, project2, location.memoryDir);
45849
46580
  const files2 = await list2(dir);
45850
46581
  if (!files2.complete) complete = false;
45851
46582
  for (const name of files2.names) {
45852
46583
  if (!name.endsWith(location.extension)) continue;
45853
46584
  if (location.skip.includes(name)) continue;
45854
- const path2 = join36(dir, name);
46585
+ const path2 = join38(dir, name);
45855
46586
  const file = await readAt2(location.harness, path2);
45856
46587
  if (file.kind === "absent" || file.kind === "empty") continue;
45857
46588
  paths.push(path2);
@@ -45937,8 +46668,8 @@ var init_memory_reader = __esm({
45937
46668
  MEMORY_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
45938
46669
  MEMORY_PROJECT_DIRS_MAX = 200;
45939
46670
  HARNESS_MEMORY_LOCATIONS = [
45940
- claudeCodeLocation((home) => join36(home, ".claude", "projects"), "memory"),
45941
- claudeCodeLocation((home) => join36(home, ".claude", "agent-memory"), "")
46671
+ claudeCodeLocation((home) => join38(home, ".claude", "projects"), "memory"),
46672
+ claudeCodeLocation((home) => join38(home, ".claude", "agent-memory"), "")
45942
46673
  ];
45943
46674
  FRONTMATTER = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
45944
46675
  FRONTMATTER_LINE = /^([ \t]*)([A-Za-z_][\w-]*)[ \t]*:[ \t]*(.*)$/;
@@ -46430,25 +47161,25 @@ var init_memory_recall = __esm({
46430
47161
  });
46431
47162
 
46432
47163
  // ../../packages/tacho/src/collector/memory-capture/memory-uses.ts
46433
- import { basename as basename6, dirname as dirname20, isAbsolute as isAbsolute9, join as join37, resolve as resolve14 } from "node:path";
47164
+ import { basename as basename6, dirname as dirname21, isAbsolute as isAbsolute9, join as join39, resolve as resolve14 } from "node:path";
46434
47165
  function memoryFileOf(path2, locations, home) {
46435
47166
  if (!isAbsolute9(path2)) return void 0;
46436
47167
  const file = resolve14(path2);
46437
47168
  const name = basename6(file);
46438
- const folder = dirname20(file);
47169
+ const folder = dirname21(file);
46439
47170
  for (const location of locations) {
46440
47171
  if (!name.endsWith(location.extension)) continue;
46441
47172
  if (location.skip.includes(name)) continue;
46442
- const projectDir = location.memoryDir === "" ? folder : dirname20(folder);
47173
+ const projectDir = location.memoryDir === "" ? folder : dirname21(folder);
46443
47174
  if (location.memoryDir !== "" && basename6(folder) !== location.memoryDir)
46444
47175
  continue;
46445
- if (dirname20(projectDir) !== resolve14(location.projectsDir(home))) continue;
47176
+ if (dirname21(projectDir) !== resolve14(location.projectsDir(home))) continue;
46446
47177
  return { harness: location.harness, path: file };
46447
47178
  }
46448
47179
  return void 0;
46449
47180
  }
46450
47181
  function pathOf(word, cwd, home) {
46451
- const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join37(home, word.slice(word.indexOf("/") + 1)) : word;
47182
+ const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join39(home, word.slice(word.indexOf("/") + 1)) : word;
46452
47183
  if (isAbsolute9(expanded)) return expanded;
46453
47184
  if (cwd !== void 0 && isAbsolute9(cwd)) return resolve14(cwd, expanded);
46454
47185
  return void 0;
@@ -46704,7 +47435,7 @@ ${use.path}`;
46704
47435
  });
46705
47436
 
46706
47437
  // ../../packages/tacho/src/collector/memory-capture/stella-store.ts
46707
- import { dirname as dirname21, isAbsolute as isAbsolute10, join as join38, resolve as resolve15 } from "node:path";
47438
+ import { dirname as dirname22, isAbsolute as isAbsolute10, join as join40, resolve as resolve15 } from "node:path";
46708
47439
  function queryStellaStore(path2, afterSeq, limit) {
46709
47440
  const db = new (nodeSqlite()).DatabaseSync(path2, { readOnly: true });
46710
47441
  try {
@@ -46794,9 +47525,9 @@ async function stellaStoreOf(dir, exists) {
46794
47525
  if (!isAbsolute10(dir)) return void 0;
46795
47526
  let at2 = resolve15(dir);
46796
47527
  for (let depth = 0; depth < WORKSPACE_SEARCH_DEPTH; depth += 1) {
46797
- const candidate = join38(at2, STELLA_CONTEXT_DB);
47528
+ const candidate = join40(at2, STELLA_CONTEXT_DB);
46798
47529
  if (await exists(candidate)) return candidate;
46799
- const up = dirname21(at2);
47530
+ const up = dirname22(at2);
46800
47531
  if (up === at2) return void 0;
46801
47532
  at2 = up;
46802
47533
  }
@@ -46807,7 +47538,7 @@ var init_stella_store = __esm({
46807
47538
  "../../packages/tacho/src/collector/memory-capture/stella-store.ts"() {
46808
47539
  "use strict";
46809
47540
  init_memory_reader();
46810
- STELLA_CONTEXT_DB = join38(".stella", "private", "context.db");
47541
+ STELLA_CONTEXT_DB = join40(".stella", "private", "context.db");
46811
47542
  STELLA_MEMORIES_VIEW = "export_memories_v1";
46812
47543
  STELLA_USES_VIEW = "export_memory_uses_v1";
46813
47544
  STELLA_USES_PER_READ = 5e3;
@@ -47469,7 +48200,7 @@ var init_cloud_link = __esm({
47469
48200
 
47470
48201
  // ../../packages/tacho/src/collector/local-servers/digest.ts
47471
48202
  import { createHash as createHash10 } from "node:crypto";
47472
- import { join as join39 } from "node:path";
48203
+ import { join as join41 } from "node:path";
47473
48204
  function rememberBounded(map, key, value) {
47474
48205
  if (!map.has(key) && map.size >= MAX_REMEMBERED_DIGESTS) {
47475
48206
  const oldest = map.keys().next();
@@ -47506,7 +48237,7 @@ function npmNameSegment(name) {
47506
48237
  return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : encodeURIComponent(name);
47507
48238
  }
47508
48239
  async function whichOnPath(command2, pathValue, search) {
47509
- const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join39(dir, command2));
48240
+ const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join41(dir, command2));
47510
48241
  for (const candidate of candidates) {
47511
48242
  for (const extension of search.extensions) {
47512
48243
  if (await search.isExecutable(candidate + extension))
@@ -48509,7 +49240,7 @@ var init_local_servers = __esm({
48509
49240
 
48510
49241
  // ../../packages/tacho/src/collector/local-servers/machine.ts
48511
49242
  import { spawn as nodeSpawn } from "node:child_process";
48512
- import { access, constants as constants3, readFile, realpath, stat } from "node:fs/promises";
49243
+ import { access, constants as constants4, readFile, realpath, stat } from "node:fs/promises";
48513
49244
  function machineParked(host) {
48514
49245
  if (host.revoked_at !== null || host.host_status === "revoked")
48515
49246
  return "the enrollment is revoked";
@@ -48534,7 +49265,7 @@ function createMachineLink(host, fetch2, env) {
48534
49265
  async function isExecutable(path2) {
48535
49266
  try {
48536
49267
  if (!(await stat(path2)).isFile()) return false;
48537
- await access(path2, constants3.X_OK);
49268
+ await access(path2, constants4.X_OK);
48538
49269
  return true;
48539
49270
  } catch {
48540
49271
  return false;
@@ -48802,9 +49533,9 @@ import {
48802
49533
  writeFile
48803
49534
  } from "node:fs/promises";
48804
49535
  import { homedir as homedir5 } from "node:os";
48805
- import { dirname as dirname22, join as join40 } from "node:path";
49536
+ import { dirname as dirname23, join as join42 } from "node:path";
48806
49537
  function stagingFolder(root, name) {
48807
- return join40(root, `.${name}${STAGING_SUFFIX}`);
49538
+ return join42(root, `.${name}${STAGING_SUFFIX}`);
48808
49539
  }
48809
49540
  function isStagingName(name) {
48810
49541
  return name.startsWith(".") && name.endsWith(STAGING_SUFFIX);
@@ -48847,17 +49578,17 @@ function envDir(env, key) {
48847
49578
  function skillsRoot(harness, env = process.env, home = homedir5()) {
48848
49579
  switch (harness) {
48849
49580
  case "claude-code":
48850
- return join40(
48851
- envDir(env, "CLAUDE_CONFIG_DIR") ?? join40(home, ".claude"),
49581
+ return join42(
49582
+ envDir(env, "CLAUDE_CONFIG_DIR") ?? join42(home, ".claude"),
48852
49583
  "skills"
48853
49584
  );
48854
49585
  case "codex": {
48855
49586
  const codexHome = envDir(env, "CODEX_HOME");
48856
- return codexHome === null ? join40(home, ".agents", "skills") : join40(codexHome, "skills");
49587
+ return codexHome === null ? join42(home, ".agents", "skills") : join42(codexHome, "skills");
48857
49588
  }
48858
49589
  case "stella":
48859
- return join40(
48860
- envDir(env, "STELLA_HOME") ?? join40(home, ".stella"),
49590
+ return join42(
49591
+ envDir(env, "STELLA_HOME") ?? join42(home, ".stella"),
48861
49592
  "skills"
48862
49593
  );
48863
49594
  case "cursor":
@@ -48918,7 +49649,7 @@ async function readMarker(folder) {
48918
49649
  if (!entries2.includes(SESSION_MARKER)) return { state: "foreign" };
48919
49650
  try {
48920
49651
  const parsed2 = JSON.parse(
48921
- await readFile2(join40(folder, SESSION_MARKER), "utf8")
49652
+ await readFile2(join42(folder, SESSION_MARKER), "utf8")
48922
49653
  );
48923
49654
  return isMarker(parsed2) ? { state: "ours", marker: parsed2 } : { state: "foreign" };
48924
49655
  } catch {
@@ -48935,23 +49666,23 @@ function liveSessions(marker, now) {
48935
49666
  return live;
48936
49667
  }
48937
49668
  async function writeMarker(folder, marker) {
48938
- const temp = join40(folder, MARKER_TEMP);
49669
+ const temp = join42(folder, MARKER_TEMP);
48939
49670
  await writeFile(temp, `${JSON.stringify(marker, null, 2)}
48940
49671
  `);
48941
- await rename2(temp, join40(folder, SESSION_MARKER));
49672
+ await rename2(temp, join42(folder, SESSION_MARKER));
48942
49673
  }
48943
49674
  async function writeSkill(root, skill, marker) {
48944
- const folder = join40(root, skill.name);
49675
+ const folder = join42(root, skill.name);
48945
49676
  const staging = stagingFolder(root, skill.name);
48946
49677
  await rm2(folder, { recursive: true, force: true });
48947
49678
  await rm2(staging, { recursive: true, force: true });
48948
49679
  await mkdir2(staging);
48949
49680
  await writeMarker(staging, { digest: UNFINISHED_DIGEST, sessions: {} });
48950
49681
  await rename2(staging, folder);
48951
- await writeFile(join40(folder, "SKILL.md"), skillMarkdown(skill));
49682
+ await writeFile(join42(folder, "SKILL.md"), skillMarkdown(skill));
48952
49683
  for (const file of skill.files) {
48953
- const target2 = join40(folder, file.path);
48954
- await mkdir2(dirname22(target2), { recursive: true });
49684
+ const target2 = join42(folder, file.path);
49685
+ await mkdir2(dirname23(target2), { recursive: true });
48955
49686
  await writeFile(target2, file.content);
48956
49687
  }
48957
49688
  await writeMarker(folder, marker);
@@ -48979,7 +49710,7 @@ async function placeSkills(root, sessionId, skills, now = /* @__PURE__ */ new Da
48979
49710
  result2.warnings.push(refusal);
48980
49711
  continue;
48981
49712
  }
48982
- const folder = join40(root, skill.name);
49713
+ const folder = join42(root, skill.name);
48983
49714
  if (taken.has(skill.name)) {
48984
49715
  result2.skipped.push(skill.name);
48985
49716
  result2.warnings.push(
@@ -49044,7 +49775,7 @@ async function removeSkills(root, sessionId, now = /* @__PURE__ */ new Date()) {
49044
49775
  throw error;
49045
49776
  }
49046
49777
  for (const name of names2.sort(compareText)) {
49047
- const folder = join40(root, name);
49778
+ const folder = join42(root, name);
49048
49779
  if (isStagingName(name)) {
49049
49780
  await rm2(folder, { recursive: true, force: true });
49050
49781
  continue;
@@ -49966,13 +50697,27 @@ function connectionTokens(raw) {
49966
50697
  }
49967
50698
  return named;
49968
50699
  }
50700
+ function withOAuthBeta(value) {
50701
+ const flags = (value ?? "").split(",").map((flag2) => flag2.trim()).filter((flag2) => flag2.length > 0);
50702
+ if (!flags.includes(ANTHROPIC_OAUTH_BETA)) flags.push(ANTHROPIC_OAUTH_BETA);
50703
+ return flags.join(",");
50704
+ }
49969
50705
  function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, attach) {
49970
50706
  const named = connectionTokens(raw);
49971
50707
  const out = ["Host", host];
50708
+ const oauth = attach?.kind === "oauth";
50709
+ let beta;
49972
50710
  for (let i2 = 0; i2 + 1 < raw.length; i2 += 2) {
49973
50711
  const name = raw[i2];
49974
50712
  const lower = name.toLowerCase();
49975
50713
  if (HOP_BY_HOP.has(lower) || named.has(lower)) continue;
50714
+ if (attach?.kind === "chatgpt" && attach.accountId !== void 0 && lower === CHATGPT_ACCOUNT_HEADER)
50715
+ continue;
50716
+ if (oauth && lower === "anthropic-beta") {
50717
+ const value = raw[i2 + 1];
50718
+ beta = beta === void 0 ? value : `${beta},${value}`;
50719
+ continue;
50720
+ }
49976
50721
  if (lower === "host" || lower === "content-length") continue;
49977
50722
  if (lower === "accept-encoding" || lower === "expect") continue;
49978
50723
  if (lower === TACHO_MODEL_SESSION_HEADER) continue;
@@ -49984,6 +50729,9 @@ function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, atta
49984
50729
  if (attach !== void 0) {
49985
50730
  if (attach.kind === "api_key") out.push("X-Api-Key", attach.secret);
49986
50731
  else out.push("Authorization", `Bearer ${attach.secret}`);
50732
+ if (oauth) out.push("anthropic-beta", withOAuthBeta(beta));
50733
+ if (attach.kind === "chatgpt" && attach.accountId !== void 0)
50734
+ out.push("ChatGPT-Account-ID", attach.accountId);
49987
50735
  }
49988
50736
  out.push("Accept-Encoding", "identity");
49989
50737
  if (bodyLength > 0 || raw.some((h, i2) => i2 % 2 === 0 && /^content-length$/i.test(h)))
@@ -50017,7 +50765,7 @@ function providerError(provider, status2, code, message) {
50017
50765
  };
50018
50766
  return { status: status2, body: JSON.stringify(body2) };
50019
50767
  }
50020
- var DEFAULT_MODEL_UPSTREAMS, MODEL_PROXY_ROUTES, HOP_BY_HOP, CREDENTIAL_HEADERS;
50768
+ var DEFAULT_MODEL_UPSTREAMS, MODEL_PROXY_ROUTES, HOP_BY_HOP, CREDENTIAL_HEADERS, CHATGPT_ACCOUNT_HEADER, ANTHROPIC_OAUTH_BETA;
50021
50769
  var init_model_routes = __esm({
50022
50770
  "../../packages/tacho/src/collector/model-routes.ts"() {
50023
50771
  "use strict";
@@ -50046,6 +50794,8 @@ var init_model_routes = __esm({
50046
50794
  "upgrade"
50047
50795
  ]);
50048
50796
  CREDENTIAL_HEADERS = ["authorization", "x-api-key"];
50797
+ CHATGPT_ACCOUNT_HEADER = "chatgpt-account-id";
50798
+ ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
50049
50799
  }
50050
50800
  });
50051
50801
 
@@ -50777,7 +51527,7 @@ function createModelProxy(deps) {
50777
51527
  function resolveCredential(req, route) {
50778
51528
  const broker = deps.credentials;
50779
51529
  const presented = presentedCredential(req);
50780
- const brokered = broker !== void 0 && route.upstream !== "chatgpt" && route.harness !== "stella" && broker.brokered(route.provider);
51530
+ const brokered = broker !== void 0 && route.harness !== "stella" && broker.brokered(route.provider) && route.upstream === "chatgpt" === (broker.kind?.(route.provider) === "chatgpt");
50781
51531
  if (broker === void 0 || !brokered) {
50782
51532
  if (presented !== void 0 && looksLikeRunToken(presented.value)) {
50783
51533
  const claims = peekRunTokenClaims(presented.value);
@@ -50832,6 +51582,27 @@ function createModelProxy(deps) {
50832
51582
  }
50833
51583
  };
50834
51584
  }
51585
+ if (held2.kind === "chatgpt") {
51586
+ const login = decodeChatGptLogin(held2.secret);
51587
+ if (login === void 0)
51588
+ return {
51589
+ basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
51590
+ claims: verdict.claims,
51591
+ refusal: {
51592
+ code: "credential_unavailable",
51593
+ message: CREDENTIAL_MESSAGES.credential_unavailable
51594
+ }
51595
+ };
51596
+ return {
51597
+ basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
51598
+ claims: verdict.claims,
51599
+ attach: {
51600
+ kind: "chatgpt",
51601
+ secret: login.access_token,
51602
+ ...login.account_id !== void 0 ? { accountId: login.account_id } : {}
51603
+ }
51604
+ };
51605
+ }
50835
51606
  return {
50836
51607
  basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
50837
51608
  claims: verdict.claims,
@@ -51522,6 +52293,15 @@ function createModelProxy(deps) {
51522
52293
  if (result2.path !== path2 && result2.path.startsWith("/"))
51523
52294
  path2 = result2.path;
51524
52295
  }
52296
+ if (credential.attach?.kind === "chatgpt") {
52297
+ const sent2 = injected ? injectedJson : json2();
52298
+ if (sent2 !== void 0 && sent2["store"] !== false) {
52299
+ const unstored = { ...sent2, store: false };
52300
+ if (injected) injectedJson = unstored;
52301
+ body2 = Buffer.from(JSON.stringify(unstored), "utf8");
52302
+ dropContentEncoding = true;
52303
+ }
52304
+ }
51525
52305
  const requestModel = askedModel;
51526
52306
  const requestEffort = requestEffortOf(injected ? injectedJson : json2());
51527
52307
  const sent = injected ? body2 : readable();
@@ -52086,6 +52866,7 @@ var init_model_proxy = __esm({
52086
52866
  init_frame_body();
52087
52867
  init_replay_grade();
52088
52868
  init_bundle();
52869
+ init_model_credential();
52089
52870
  init_run_token();
52090
52871
  init_wire();
52091
52872
  init_cache_keep_alive();
@@ -52287,7 +53068,7 @@ var init_model_proxy_listener = __esm({
52287
53068
  import {
52288
53069
  createServer as createServer5
52289
53070
  } from "node:http";
52290
- import { chmodSync as chmodSync9, existsSync as existsSync21, statSync as statSync10, unlinkSync as unlinkSync11 } from "node:fs";
53071
+ import { chmodSync as chmodSync9, existsSync as existsSync21, statSync as statSync11, unlinkSync as unlinkSync11 } from "node:fs";
52291
53072
  import { randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
52292
53073
  import { createConnection } from "node:net";
52293
53074
  function isExportFormat(value) {
@@ -52652,7 +53433,7 @@ function createRequestHandler(api, log, options = {}) {
52652
53433
  }
52653
53434
  async function removeStaleSocket(socketPath) {
52654
53435
  if (!existsSync21(socketPath)) return;
52655
- if (!statSync10(socketPath).isSocket()) {
53436
+ if (!statSync11(socketPath).isSocket()) {
52656
53437
  throw new Error(`collector socket path is not a socket: ${socketPath}`);
52657
53438
  }
52658
53439
  const stale = await new Promise((resolve25, reject) => {
@@ -52820,13 +53601,13 @@ var init_server = __esm({
52820
53601
  });
52821
53602
 
52822
53603
  // ../../packages/tacho/src/collector/refused.ts
52823
- import { readdirSync as readdirSync12, readFileSync as readFileSync18, statSync as statSync11 } from "node:fs";
52824
- import { join as join41 } from "node:path";
53604
+ import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync12 } from "node:fs";
53605
+ import { join as join43 } from "node:path";
52825
53606
  function refusalRetryable(refusal) {
52826
53607
  return refusal === "malformed" || refusal === "parked";
52827
53608
  }
52828
53609
  function refusedRecordPath(dir, event) {
52829
- return join41(dir, `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`);
53610
+ return join43(dir, `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`);
52830
53611
  }
52831
53612
  function newRefusedRecord(event, reason3, refusal, at2, body2) {
52832
53613
  const retryable = refusalRetryable(refusal);
@@ -52869,8 +53650,8 @@ function readRefusedEntry(file) {
52869
53650
  let record;
52870
53651
  let mtimeMs;
52871
53652
  try {
52872
- record = JSON.parse(readFileSync18(file, "utf8"));
52873
- mtimeMs = statSync11(file).mtimeMs;
53653
+ record = JSON.parse(readFileSync19(file, "utf8"));
53654
+ mtimeMs = statSync12(file).mtimeMs;
52874
53655
  } catch {
52875
53656
  return void 0;
52876
53657
  }
@@ -52902,7 +53683,7 @@ function scanRefused(dir, now, limit = MAX_REFUSED_RETRIES_PER_PASS) {
52902
53683
  };
52903
53684
  const due = [];
52904
53685
  for (const name of names2) {
52905
- const entry2 = readRefusedEntry(join41(dir, name));
53686
+ const entry2 = readRefusedEntry(join43(dir, name));
52906
53687
  if (entry2 === void 0) continue;
52907
53688
  summary.total += 1;
52908
53689
  if (summary.lastRefusedAt === void 0 || entry2.refusedAt > summary.lastRefusedAt)
@@ -52938,8 +53719,8 @@ var init_refused = __esm({
52938
53719
  });
52939
53720
 
52940
53721
  // ../../packages/tacho/src/collector/spool.ts
52941
- import { existsSync as existsSync22, readFileSync as readFileSync19, unlinkSync as unlinkSync12 } from "node:fs";
52942
- import { join as join42 } from "node:path";
53722
+ import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync12 } from "node:fs";
53723
+ import { join as join44 } from "node:path";
52943
53724
  function sessionOwnedElsewhere(error) {
52944
53725
  return error.status === 403 && error.body.includes(SESSION_OWNED_ELSEWHERE);
52945
53726
  }
@@ -53172,12 +53953,12 @@ var init_spool = __esm({
53172
53953
  }
53173
53954
  /** Add a body to a quarantine record that was written without one. */
53174
53955
  stashBodyBeside(event, body2) {
53175
- const path2 = join42(
53956
+ const path2 = join44(
53176
53957
  this.options.quarantineDir,
53177
53958
  `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`
53178
53959
  );
53179
53960
  try {
53180
- const record = JSON.parse(readFileSync19(path2, "utf8"));
53961
+ const record = JSON.parse(readFileSync20(path2, "utf8"));
53181
53962
  if (record["body"] !== void 0) return;
53182
53963
  writeSensitiveFileAtomic(
53183
53964
  path2,
@@ -53821,15 +54602,15 @@ var init_session_heads = __esm({
53821
54602
  import {
53822
54603
  existsSync as existsSync23,
53823
54604
  readdirSync as readdirSync13,
53824
- readFileSync as readFileSync20,
54605
+ readFileSync as readFileSync21,
53825
54606
  renameSync as renameSync4,
53826
- statSync as statSync12,
54607
+ statSync as statSync13,
53827
54608
  unlinkSync as unlinkSync13
53828
54609
  } from "node:fs";
53829
54610
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
53830
54611
  import { homedir as homedir7, hostname as osHostname2 } from "node:os";
53831
- import { dirname as dirname23, join as join43 } from "node:path";
53832
- import { spawnSync as spawnSync6 } from "node:child_process";
54612
+ import { dirname as dirname24, join as join45 } from "node:path";
54613
+ import { spawnSync as spawnSync7 } from "node:child_process";
53833
54614
  function cloudGatewayUpstreams(origin = CLOUD_GATEWAY_ORIGIN) {
53834
54615
  const base2 = new URL(origin).origin;
53835
54616
  const openai = `${base2}${CLOUD_GATEWAY_BASE_PATH}/openai/v1`;
@@ -53859,7 +54640,7 @@ function cloudGatewayCredentialStore(disk, runToken) {
53859
54640
  }
53860
54641
  function readTextOrUndefined(path2) {
53861
54642
  try {
53862
- return readFileSync20(path2, "utf8");
54643
+ return readFileSync21(path2, "utf8");
53863
54644
  } catch {
53864
54645
  return void 0;
53865
54646
  }
@@ -53880,7 +54661,7 @@ function parseOtlpPayload(payload) {
53880
54661
  return otlpPayloadSchema.parse(payload);
53881
54662
  }
53882
54663
  function defaultExec(command2, args) {
53883
- const result2 = spawnSync6(command2, args, {
54664
+ const result2 = spawnSync7(command2, args, {
53884
54665
  encoding: "utf8",
53885
54666
  maxBuffer: 4 * 1024 * 1024,
53886
54667
  timeout: 1e4
@@ -54060,7 +54841,7 @@ async function initializeDaemon(options, server, port, ready) {
54060
54841
  }) : void 0;
54061
54842
  const memoryHome = options.home ?? homedir7();
54062
54843
  const memoryLocations = () => claudeCodeMemoryLocations(
54063
- dirname23(paths.claudeProjects),
54844
+ dirname24(paths.claudeProjects),
54064
54845
  projectDirsOf(registry2.list())
54065
54846
  );
54066
54847
  const memoryUses = options.listen ?? true ? createMemoryUses({
@@ -54171,7 +54952,7 @@ async function initializeDaemon(options, server, port, ready) {
54171
54952
  processStarts: hookProcessStarts,
54172
54953
  adoptedChain: (sessionUuid2) => backfillLedger.chainHead(sessionUuid2)
54173
54954
  });
54174
- const priorStateWrittenAt = existsSync23(paths.daemonState) ? statSync12(paths.daemonState).mtimeMs : void 0;
54955
+ const priorStateWrittenAt = existsSync23(paths.daemonState) ? statSync13(paths.daemonState).mtimeMs : void 0;
54175
54956
  const sealedState = parseSealedState(
54176
54957
  readJsonStateFile(
54177
54958
  paths.daemonSealedState,
@@ -54572,7 +55353,7 @@ async function initializeDaemon(options, server, port, ready) {
54572
55353
  controlReachable: lastControlAt !== void 0 && now() - lastControlAt < 2 * timers.bundleRefreshMs
54573
55354
  };
54574
55355
  }
54575
- const bodyPurgeOwedPath = join43(paths.wal, "body-purge-owed");
55356
+ const bodyPurgeOwedPath = join45(paths.wal, "body-purge-owed");
54576
55357
  function markBodyPurgeOwed(retention, dropped) {
54577
55358
  try {
54578
55359
  ensureDir(paths.wal);
@@ -54594,7 +55375,7 @@ async function initializeDaemon(options, server, port, ready) {
54594
55375
  if (!existsSync23(bodyPurgeOwedPath)) return void 0;
54595
55376
  let text5;
54596
55377
  try {
54597
- text5 = readFileSync20(bodyPurgeOwedPath, "utf8");
55378
+ text5 = readFileSync21(bodyPurgeOwedPath, "utf8");
54598
55379
  } catch (error) {
54599
55380
  log(
54600
55381
  `cannot read the owed body purge at ${bodyPurgeOwedPath}: ${error instanceof Error ? error.message : String(error)}; the retry sweeps against the clause in force. Check the file's permissions`
@@ -54849,7 +55630,7 @@ async function initializeDaemon(options, server, port, ready) {
54849
55630
  }
54850
55631
  void workOrderAutostart.reconcile();
54851
55632
  }
54852
- const lastIngestFile = join43(paths.dir, "last-ingest.json");
55633
+ const lastIngestFile = join45(paths.dir, "last-ingest.json");
54853
55634
  const shipper = new Shipper({
54854
55635
  wal,
54855
55636
  client,
@@ -54890,7 +55671,7 @@ async function initializeDaemon(options, server, port, ready) {
54890
55671
  read: () => {
54891
55672
  try {
54892
55673
  const at2 = Date.parse(
54893
- JSON.parse(readFileSync20(lastIngestFile, "utf8")).at ?? ""
55674
+ JSON.parse(readFileSync21(lastIngestFile, "utf8")).at ?? ""
54894
55675
  );
54895
55676
  return Number.isFinite(at2) ? at2 : void 0;
54896
55677
  } catch {
@@ -55371,7 +56152,7 @@ async function initializeDaemon(options, server, port, ready) {
55371
56152
  async function readSpoolFile(name) {
55372
56153
  try {
55373
56154
  const file = parseSpoolFile(
55374
- await readFile3(join43(paths.spool, name), "utf8")
56155
+ await readFile3(join45(paths.spool, name), "utf8")
55375
56156
  );
55376
56157
  spoolFacts.set(name, spoolFactsOf(file));
55377
56158
  return file;
@@ -55560,7 +56341,7 @@ async function initializeDaemon(options, server, port, ready) {
55560
56341
  function setAsideConflictingTerminal(uuid, pending, conflict) {
55561
56342
  const name = `${uuid}-terminal-conflict-${String(conflict.seq).padStart(8, "0")}.json`;
55562
56343
  writeSensitiveFileAtomic(
55563
- join43(paths.quarantine, name),
56344
+ join45(paths.quarantine, name),
55564
56345
  JSON.stringify(
55565
56346
  {
55566
56347
  reason: conflict.message,
@@ -55640,8 +56421,8 @@ async function initializeDaemon(options, server, port, ready) {
55640
56421
  function moveToFailedSpool(path2, name, reason3) {
55641
56422
  log(`spool file ${name} moved to spool/failed/: ${reason3}`);
55642
56423
  try {
55643
- ensureDir(join43(paths.spool, "failed"));
55644
- renameSync4(path2, join43(paths.spool, "failed", name));
56424
+ ensureDir(join45(paths.spool, "failed"));
56425
+ renameSync4(path2, join45(paths.spool, "failed", name));
55645
56426
  } catch (error) {
55646
56427
  log(
55647
56428
  `spool file ${name} could not be moved to spool/failed/: ${error instanceof Error ? error.message : String(error)}`
@@ -55694,7 +56475,7 @@ async function initializeDaemon(options, server, port, ready) {
55694
56475
  continue;
55695
56476
  }
55696
56477
  reads += 1;
55697
- const path2 = join43(paths.spool, name);
56478
+ const path2 = join45(paths.spool, name);
55698
56479
  let file;
55699
56480
  try {
55700
56481
  file = await readSpoolFile(name);
@@ -55979,7 +56760,7 @@ async function initializeDaemon(options, server, port, ready) {
55979
56760
  const state = await readModelBaseUrlState({
55980
56761
  home: options.home ?? homedir7(),
55981
56762
  port: modelProxyPortFor(host),
55982
- stellaHome: dirname23(options.paths.stellaToml),
56763
+ stellaHome: dirname24(options.paths.stellaToml),
55983
56764
  harnesses: ["claude-code", "codex", "stella"]
55984
56765
  });
55985
56766
  const next = {};
@@ -56083,6 +56864,13 @@ async function initializeDaemon(options, server, port, ready) {
56083
56864
  ...options.pauseHold,
56084
56865
  credentials: {
56085
56866
  brokered: (provider) => inCustody(provider),
56867
+ kind: (provider) => {
56868
+ try {
56869
+ return credentialStore.status().find((entry2) => entry2.provider === provider)?.kind;
56870
+ } catch {
56871
+ return void 0;
56872
+ }
56873
+ },
56086
56874
  custody: (provider) => {
56087
56875
  try {
56088
56876
  return credentialStore.read(provider);
@@ -56147,6 +56935,13 @@ async function initializeDaemon(options, server, port, ready) {
56147
56935
  record,
56148
56936
  model: modelProxy,
56149
56937
  modelPort: () => modelProxyListener.port(),
56938
+ chatgptLogin: () => {
56939
+ try {
56940
+ return credentialStore.status().some((c) => c.provider === "openai" && c.kind === "chatgpt");
56941
+ } catch {
56942
+ return false;
56943
+ }
56944
+ },
56150
56945
  issueCredential: (harness) => issueRunToken(
56151
56946
  { harness },
56152
56947
  {
@@ -56521,9 +57316,9 @@ async function initializeDaemon(options, server, port, ready) {
56521
57316
  let removed = 0;
56522
57317
  for (const name of readdirSync13(paths.quarantine)) {
56523
57318
  if (!name.endsWith(".json")) continue;
56524
- const file = join43(paths.quarantine, name);
57319
+ const file = join45(paths.quarantine, name);
56525
57320
  try {
56526
- const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync12(file).mtimeMs;
57321
+ const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync13(file).mtimeMs;
56527
57322
  if (at2 - refusedAt < retainMs) continue;
56528
57323
  unlinkSync13(file);
56529
57324
  removed += 1;
@@ -56777,8 +57572,29 @@ async function initializeDaemon(options, server, port, ready) {
56777
57572
  });
56778
57573
  }
56779
57574
  let lastStaticRenewalAt = 0;
57575
+ let lastLoginRefreshAt = 0;
56780
57576
  async function renewStaticTokens() {
56781
57577
  if (!host.harnesses.includes("codex")) return;
57578
+ if (now() - lastLoginRefreshAt >= CHATGPT_LOGIN_CHECK_MS) {
57579
+ lastLoginRefreshAt = now();
57580
+ try {
57581
+ const refreshed = await refreshChatGptCustody(
57582
+ credentialStore,
57583
+ options.fetch ?? fetch,
57584
+ now()
57585
+ );
57586
+ if (refreshed === "refreshed")
57587
+ log("refreshed the ChatGPT login in custody");
57588
+ else if (typeof refreshed === "object")
57589
+ log(`ChatGPT login refresh: ${refreshed.failed}`);
57590
+ if (refreshed !== "none")
57591
+ restateCodexLastRefresh(options.home ?? homedir7(), now());
57592
+ } catch (error) {
57593
+ log(
57594
+ `ChatGPT login refresh: ${error instanceof Error ? error.message : String(error)}`
57595
+ );
57596
+ }
57597
+ }
56782
57598
  if (now() - lastStaticRenewalAt < STATIC_TOKEN_RENEWAL_CHECK_MS) return;
56783
57599
  lastStaticRenewalAt = now();
56784
57600
  if (host.host_status !== "active" || !inCustody("openai")) return;
@@ -56851,7 +57667,7 @@ async function initializeDaemon(options, server, port, ready) {
56851
57667
  let memoryScan;
56852
57668
  if (options.listen ?? true) {
56853
57669
  const codexMemories = createCodexMemoryStore({
56854
- path: join43(dirname23(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
57670
+ path: join45(dirname24(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
56855
57671
  stat: (path2) => stat2(path2),
56856
57672
  log
56857
57673
  });
@@ -56996,7 +57812,7 @@ async function initializeDaemon(options, server, port, ready) {
56996
57812
  }
56997
57813
  };
56998
57814
  }
56999
- var DEFAULT_TIMERS, STATIC_TOKEN_RENEWAL_CHECK_MS, MEMORY_SCAN_MS, CLOUD_GATEWAY_ORIGIN, CLOUD_GATEWAY_BASE_PATH, QUEUED_PROMPT_CLASS, RECORDER_EVENTS_KEPT, owedBodyPurgeSchema, objectSchema, spoolFileSchema, otlpResourceListSchema, otlpPayloadSchema, defaultExecAsync, STOPPING, STOPPED_EXEC, STATE_WRITE_DEFER_MS;
57815
+ var DEFAULT_TIMERS, STATIC_TOKEN_RENEWAL_CHECK_MS, CHATGPT_LOGIN_CHECK_MS, MEMORY_SCAN_MS, CLOUD_GATEWAY_ORIGIN, CLOUD_GATEWAY_BASE_PATH, QUEUED_PROMPT_CLASS, RECORDER_EVENTS_KEPT, owedBodyPurgeSchema, objectSchema, spoolFileSchema, otlpResourceListSchema, otlpPayloadSchema, defaultExecAsync, STOPPING, STOPPED_EXEC, STATE_WRITE_DEFER_MS;
57000
57816
  var init_daemon = __esm({
57001
57817
  "../../packages/tacho/src/collector/daemon.ts"() {
57002
57818
  "use strict";
@@ -57018,6 +57834,7 @@ var init_daemon = __esm({
57018
57834
  init_claude_code_mcp_writer();
57019
57835
  init_model_base_url();
57020
57836
  init_model_credential();
57837
+ init_chatgpt_refresh();
57021
57838
  init_run_token();
57022
57839
  init_process_scan();
57023
57840
  init_retention();
@@ -57090,6 +57907,7 @@ var init_daemon = __esm({
57090
57907
  hookBundleWaitMs: 2e3
57091
57908
  };
57092
57909
  STATIC_TOKEN_RENEWAL_CHECK_MS = 60 * 6e4;
57910
+ CHATGPT_LOGIN_CHECK_MS = 10 * 6e4;
57093
57911
  MEMORY_SCAN_MS = 5 * 6e4;
57094
57912
  CLOUD_GATEWAY_ORIGIN = "https://api.oxagen.sh";
57095
57913
  CLOUD_GATEWAY_BASE_PATH = "/v1/gateway";
@@ -57139,7 +57957,7 @@ var init_daemon = __esm({
57139
57957
  });
57140
57958
 
57141
57959
  // ../../packages/tacho/src/collector/run.ts
57142
- import { readFileSync as readFileSync21, unlinkSync as unlinkSync14 } from "node:fs";
57960
+ import { readFileSync as readFileSync22, unlinkSync as unlinkSync14 } from "node:fs";
57143
57961
  function stopWithin(stop, graceMs, exit, log) {
57144
57962
  const timer = setTimeout(() => {
57145
57963
  log(`tachod: stop did not finish within ${graceMs} ms, exiting
@@ -57207,14 +58025,14 @@ function ownProcStart() {
57207
58025
  }
57208
58026
  function readPidFile(path2) {
57209
58027
  try {
57210
- return readFileSync21(path2, "utf8");
58028
+ return readFileSync22(path2, "utf8");
57211
58029
  } catch {
57212
58030
  return void 0;
57213
58031
  }
57214
58032
  }
57215
58033
  function releaseDaemonPid(path2, previous) {
57216
58034
  try {
57217
- if (parseDaemonPid(readFileSync21(path2, "utf8"))?.pid !== process.pid) return;
58035
+ if (parseDaemonPid(readFileSync22(path2, "utf8"))?.pid !== process.pid) return;
57218
58036
  if (previous === void 0) unlinkSync14(path2);
57219
58037
  else writeSensitiveFileAtomic(path2, previous, 420);
57220
58038
  } catch {
@@ -57354,7 +58172,7 @@ var init_run2 = __esm({
57354
58172
  // ../../packages/tacho/src/arp/bundle.ts
57355
58173
  import {
57356
58174
  closeSync as closeSync8,
57357
- constants as constants4,
58175
+ constants as constants5,
57358
58176
  fstatSync as fstatSync2,
57359
58177
  lstatSync as lstatSync8,
57360
58178
  mkdirSync as mkdirSync6,
@@ -57363,7 +58181,7 @@ import {
57363
58181
  realpathSync as realpathSync5,
57364
58182
  writeFileSync as writeFileSync4
57365
58183
  } from "node:fs";
57366
- import { dirname as dirname24, join as join44, resolve as resolve17 } from "node:path";
58184
+ import { dirname as dirname25, join as join46, resolve as resolve17 } from "node:path";
57367
58185
  function bundlePath(root, relative11, create2 = false) {
57368
58186
  const absolute = resolve17(root);
57369
58187
  if (create2) mkdirSync6(absolute, { recursive: true, mode: 448 });
@@ -57374,7 +58192,7 @@ function bundlePath(root, relative11, create2 = false) {
57374
58192
  let current = base2;
57375
58193
  const parts = relative11.split("/");
57376
58194
  for (let i2 = 0; i2 < parts.length; i2++) {
57377
- current = join44(current, parts[i2]);
58195
+ current = join46(current, parts[i2]);
57378
58196
  try {
57379
58197
  const stat7 = lstatSync8(current);
57380
58198
  if (stat7.isSymbolicLink() || i2 < parts.length - 1 && !stat7.isDirectory()) {
@@ -57388,14 +58206,14 @@ function bundlePath(root, relative11, create2 = false) {
57388
58206
  else if (i2 < parts.length - 1) throw error;
57389
58207
  }
57390
58208
  }
57391
- if (realpathSync5(dirname24(current)) !== dirname24(current)) {
58209
+ if (realpathSync5(dirname25(current)) !== dirname25(current)) {
57392
58210
  arpFail(`The bundle directory changed while ${relative11} was read.`);
57393
58211
  }
57394
58212
  return current;
57395
58213
  }
57396
58214
  function readBounded(root, relative11, limit = ARP_MAX_BLOB_BYTES) {
57397
58215
  const path2 = bundlePath(root, relative11);
57398
- const fd2 = openSync8(path2, constants4.O_RDONLY | constants4.O_NOFOLLOW);
58216
+ const fd2 = openSync8(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
57399
58217
  try {
57400
58218
  const stat7 = fstatSync2(fd2);
57401
58219
  if (!stat7.isFile() || stat7.size > limit)
@@ -57420,7 +58238,7 @@ function writeNew(root, relative11, bytes) {
57420
58238
  const path2 = bundlePath(root, relative11, true);
57421
58239
  const fd2 = openSync8(
57422
58240
  path2,
57423
- constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW,
58241
+ constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW,
57424
58242
  384
57425
58243
  );
57426
58244
  try {
@@ -58325,7 +59143,7 @@ var init_oracles = __esm({
58325
59143
  import { randomUUID as randomUUID4 } from "node:crypto";
58326
59144
  import {
58327
59145
  closeSync as closeSync9,
58328
- constants as constants5,
59146
+ constants as constants6,
58329
59147
  fstatSync as fstatSync3,
58330
59148
  lstatSync as lstatSync9,
58331
59149
  mkdirSync as mkdirSync7,
@@ -58335,7 +59153,7 @@ import {
58335
59153
  rmSync as rmSync8,
58336
59154
  writeFileSync as writeFileSync5
58337
59155
  } from "node:fs";
58338
- import { dirname as dirname25, isAbsolute as isAbsolute11, join as join45, relative as relative5, resolve as resolve18, sep as sep7 } from "node:path";
59156
+ import { dirname as dirname26, isAbsolute as isAbsolute11, join as join47, relative as relative5, resolve as resolve18, sep as sep7 } from "node:path";
58339
59157
  function outsideProfile(path2) {
58340
59158
  return HARNESS_CONFIG_PATH.test(path2) || CREDENTIAL_PATH.test(path2);
58341
59159
  }
@@ -58346,7 +59164,7 @@ function sameJson(bytes, expected) {
58346
59164
  return jcs(JSON.parse(bytes.toString("utf8"))) === jcs(expected);
58347
59165
  }
58348
59166
  function readRegular(label2, path2, limit) {
58349
- const fd2 = openSync9(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
59167
+ const fd2 = openSync9(path2, constants6.O_RDONLY | constants6.O_NOFOLLOW);
58350
59168
  try {
58351
59169
  const before = fstatSync3(fd2);
58352
59170
  if (!before.isFile() || before.size > limit)
@@ -58391,9 +59209,9 @@ function within2(root, path2) {
58391
59209
  }
58392
59210
  function newPath(path2) {
58393
59211
  const full = resolve18(path2);
58394
- return join45(
58395
- realpathSync6(dirname25(full)),
58396
- full.slice(dirname25(full).length + 1)
59212
+ return join47(
59213
+ realpathSync6(dirname26(full)),
59214
+ full.slice(dirname26(full).length + 1)
58397
59215
  );
58398
59216
  }
58399
59217
  function sourcePath(root, path2) {
@@ -58405,7 +59223,7 @@ function sourcePath(root, path2) {
58405
59223
  }
58406
59224
  let current = root;
58407
59225
  for (const part2 of path2.split("/")) {
58408
- current = join45(current, part2);
59226
+ current = join47(current, part2);
58409
59227
  if (lstatSync9(current).isSymbolicLink())
58410
59228
  arpFail(
58411
59229
  `${path2} is a symlink or passes through one. Symlinks require a resource adapter.`
@@ -58672,23 +59490,23 @@ function prepareCheckpoint(options) {
58672
59490
  });
58673
59491
  mkdirSync7(destination, { mode: 448 });
58674
59492
  try {
58675
- const restored = join45(destination, "workspace");
59493
+ const restored = join47(destination, "workspace");
58676
59494
  mkdirSync7(restored, { mode: 448 });
58677
59495
  for (const item of restore) {
58678
- const path2 = join45(restored, item.path);
59496
+ const path2 = join47(restored, item.path);
58679
59497
  if (item.kind === "directory")
58680
59498
  mkdirSync7(path2, { recursive: true, mode: 448 });
58681
59499
  else {
58682
- mkdirSync7(dirname25(path2), { recursive: true, mode: 448 });
59500
+ mkdirSync7(dirname26(path2), { recursive: true, mode: 448 });
58683
59501
  writeFileSync5(path2, item.bytes, {
58684
59502
  flag: "wx",
58685
59503
  mode: item.executable ? 448 : 384
58686
59504
  });
58687
59505
  }
58688
59506
  }
58689
- const promptDir = join45(restored, ".arp");
59507
+ const promptDir = join47(restored, ".arp");
58690
59508
  mkdirSync7(promptDir, { mode: 448 });
58691
- const promptFile = join45(promptDir, "handoff.json");
59509
+ const promptFile = join47(promptDir, "handoff.json");
58692
59510
  const prompt = {
58693
59511
  checkpoint_digest: checkpointDigest,
58694
59512
  task: task.data.request,
@@ -58705,7 +59523,7 @@ function prepareCheckpoint(options) {
58705
59523
  flag: "wx",
58706
59524
  mode: 384
58707
59525
  });
58708
- const reportPath = join45(destination, "report.json");
59526
+ const reportPath = join47(destination, "report.json");
58709
59527
  writeFileSync5(
58710
59528
  reportPath,
58711
59529
  jcs(
@@ -58890,7 +59708,7 @@ __export(main_exports, {
58890
59708
  recordedCliDeps: () => recordedCliDeps,
58891
59709
  tokenOption: () => tokenOption
58892
59710
  });
58893
- import { readFileSync as readFileSync22 } from "node:fs";
59711
+ import { readFileSync as readFileSync23 } from "node:fs";
58894
59712
  function parsePort(value) {
58895
59713
  const port = Number(value);
58896
59714
  if (!/^\d+$/.test(value) || port < 1024 || port > 65535)
@@ -58927,7 +59745,7 @@ function parseOneHarness(value) {
58927
59745
  throw new InvalidArgumentError("name one harness");
58928
59746
  return harness;
58929
59747
  }
58930
- function tokenOption(opts, err2, readStdin3 = () => readFileSync22(0, "utf8")) {
59748
+ function tokenOption(opts, err2, readStdin3 = () => readFileSync23(0, "utf8")) {
58931
59749
  const flag2 = opts.token;
58932
59750
  if (opts.tokenStdin !== true) {
58933
59751
  if (flag2 !== void 0)
@@ -59082,6 +59900,41 @@ function buildTachoProgram(options = {}) {
59082
59900
  deps.err(`${name} credential: ${result2.detail}`);
59083
59901
  process.exitCode = 1;
59084
59902
  });
59903
+ credential.command("login").description(
59904
+ "Sign in with your subscription and put it in the gateway's custody, so gateway and contained runs use it: Claude (`claude setup-token`) or ChatGPT for Codex (`codex login`)"
59905
+ ).option("--harness <name>", "claude-code (default) | codex").option(
59906
+ "--token-stdin",
59907
+ "Claude Code only: read a token `claude setup-token` printed from stdin instead of signing in"
59908
+ ).option("--json", "Print the outcome as JSON").action(
59909
+ async (opts) => {
59910
+ const harness = opts.harness ?? "claude-code";
59911
+ if (harness !== "claude-code" && harness !== "codex") {
59912
+ deps.err(
59913
+ `${name} credential login: --harness takes claude-code or codex; got "${harness}"`
59914
+ );
59915
+ process.exitCode = 1;
59916
+ return;
59917
+ }
59918
+ const outcome = await credentialLogin(
59919
+ {
59920
+ harness,
59921
+ ...opts.tokenStdin === true ? { tokenStdin: true } : {}
59922
+ },
59923
+ deps
59924
+ );
59925
+ if (opts.json === true) deps.out(JSON.stringify(outcome, null, 2));
59926
+ else {
59927
+ for (const entry2 of outcome.harnesses)
59928
+ deps.out(` ${describeHarness(entry2)}`);
59929
+ for (const warning of outcome.warnings)
59930
+ deps.err(`warning: ${warning}`);
59931
+ (outcome.ok ? deps.out : deps.err)(
59932
+ `${outcome.ok ? "" : `${name} credential login: `}${outcome.detail}`
59933
+ );
59934
+ }
59935
+ if (!outcome.ok) process.exitCode = 1;
59936
+ }
59937
+ );
59085
59938
  credential.command("status").description(
59086
59939
  "Which providers the gateway holds, and how each harness gets its credential"
59087
59940
  ).option("--json", "Machine-readable output").action(async (opts) => {
@@ -59346,7 +60199,7 @@ var init_package = __esm({
59346
60199
  "package.json"() {
59347
60200
  package_default = {
59348
60201
  name: "@oxagen/cli",
59349
- version: "2.1.4-955",
60202
+ version: "2.1.4-965",
59350
60203
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
59351
60204
  type: "module",
59352
60205
  main: "./dist/index.js",
@@ -59413,10 +60266,10 @@ var init_package = __esm({
59413
60266
 
59414
60267
  // src/lib/atomic-write.ts
59415
60268
  import { writeFileSync as writeFileSync6, renameSync as renameSync5, unlinkSync as unlinkSync15, existsSync as existsSync24 } from "node:fs";
59416
- import { dirname as dirname26, join as join46 } from "node:path";
60269
+ import { dirname as dirname27, join as join48 } from "node:path";
59417
60270
  function atomicWriteFileSync(path2, data, mode) {
59418
- const dir = dirname26(path2);
59419
- const tmp = join46(
60271
+ const dir = dirname27(path2);
60272
+ const tmp = join48(
59420
60273
  dir,
59421
60274
  `.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`
59422
60275
  );
@@ -59441,8 +60294,8 @@ var init_atomic_write = __esm({
59441
60294
 
59442
60295
  // src/lib/config.ts
59443
60296
  import { homedir as homedir8 } from "os";
59444
- import { join as join47 } from "path";
59445
- import { readFileSync as readFileSync23, mkdirSync as mkdirSync8, existsSync as existsSync25, statSync as statSync13 } from "fs";
60297
+ import { join as join49 } from "path";
60298
+ import { readFileSync as readFileSync24, mkdirSync as mkdirSync8, existsSync as existsSync25, statSync as statSync14 } from "fs";
59446
60299
  function getConfigDir() {
59447
60300
  return CONFIG_DIR;
59448
60301
  }
@@ -59472,7 +60325,7 @@ function parseConfig(raw) {
59472
60325
  function readConfig() {
59473
60326
  let identity;
59474
60327
  try {
59475
- const info = statSync13(CONFIG_FILE);
60328
+ const info = statSync14(CONFIG_FILE);
59476
60329
  identity = `${info.ino}:${info.size}:${info.mtimeMs}`;
59477
60330
  } catch {
59478
60331
  cached2 = void 0;
@@ -59480,7 +60333,7 @@ function readConfig() {
59480
60333
  }
59481
60334
  if (cached2?.identity === identity) return structuredClone(cached2.config);
59482
60335
  try {
59483
- const config = parseConfig(JSON.parse(readFileSync23(CONFIG_FILE, "utf8")));
60336
+ const config = parseConfig(JSON.parse(readFileSync24(CONFIG_FILE, "utf8")));
59484
60337
  cached2 = { identity, config };
59485
60338
  return structuredClone(config);
59486
60339
  } catch (err2) {
@@ -59528,8 +60381,8 @@ var CONFIG_DIR, CONFIG_FILE, STRING_FIELDS, TELEMETRY_FIELDS, cached2;
59528
60381
  var init_config = __esm({
59529
60382
  "src/lib/config.ts"() {
59530
60383
  init_atomic_write();
59531
- CONFIG_DIR = join47(homedir8(), ".config", "oxagen");
59532
- CONFIG_FILE = join47(CONFIG_DIR, "config.json");
60384
+ CONFIG_DIR = join49(homedir8(), ".config", "oxagen");
60385
+ CONFIG_FILE = join49(CONFIG_DIR, "config.json");
59533
60386
  STRING_FIELDS = [
59534
60387
  "token",
59535
60388
  "orgSlug",
@@ -59574,16 +60427,16 @@ import {
59574
60427
  writeFile as writeFile2
59575
60428
  } from "node:fs/promises";
59576
60429
  import { homedir as homedir9 } from "node:os";
59577
- import { join as join48 } from "node:path";
60430
+ import { join as join50 } from "node:path";
59578
60431
  function isDebugEnabled() {
59579
60432
  const v = process.env[DEBUG_ENV];
59580
60433
  return v === "1" || v === "true";
59581
60434
  }
59582
60435
  function logsDir() {
59583
- return join48(homedir9(), ".oxagen", "logs");
60436
+ return join50(homedir9(), ".oxagen", "logs");
59584
60437
  }
59585
60438
  function debugLogFile() {
59586
- return join48(logsDir(), "cli.output");
60439
+ return join50(logsDir(), "cli.output");
59587
60440
  }
59588
60441
  function sanitize(value, depth = 0) {
59589
60442
  if (value === null || value === void 0) return value;
@@ -62878,9 +63731,9 @@ var init_git = __esm({
62878
63731
 
62879
63732
  // ../../packages/steering-freshness/src/cache.ts
62880
63733
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
62881
- import { dirname as dirname27, join as join49 } from "node:path";
63734
+ import { dirname as dirname28, join as join51 } from "node:path";
62882
63735
  function stampPath(gitCommonDir) {
62883
- return join49(gitCommonDir, "oxagen", "steering-freshness.json");
63736
+ return join51(gitCommonDir, "oxagen", "steering-freshness.json");
62884
63737
  }
62885
63738
  async function readFetchStamp(gitCommonDir, io = defaultIo) {
62886
63739
  try {
@@ -62901,7 +63754,7 @@ async function readFetchStamp(gitCommonDir, io = defaultIo) {
62901
63754
  async function writeFetchStamp(gitCommonDir, stamp, io = defaultIo) {
62902
63755
  const path2 = stampPath(gitCommonDir);
62903
63756
  try {
62904
- await io.mkdirp(dirname27(path2));
63757
+ await io.mkdirp(dirname28(path2));
62905
63758
  await io.write(path2, `${JSON.stringify(stamp)}
62906
63759
  `, "utf8");
62907
63760
  } catch {
@@ -62932,24 +63785,24 @@ var init_cache = __esm({
62932
63785
  // ../../packages/steering-freshness/src/hooks.ts
62933
63786
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
62934
63787
  import { homedir as homedir10 } from "node:os";
62935
- import { dirname as dirname28, join as join50 } from "node:path";
63788
+ import { dirname as dirname29, join as join52 } from "node:path";
62936
63789
  function hookCommand(harness) {
62937
63790
  return `oxagen steering gate --harness ${harness}`;
62938
63791
  }
62939
63792
  function hookConfigPath(projectRoot, harness, env = process.env, home = homedir10()) {
62940
63793
  switch (harness) {
62941
63794
  case "claude-code":
62942
- return join50(projectRoot, ".claude", "settings.json");
63795
+ return join52(projectRoot, ".claude", "settings.json");
62943
63796
  case "codex":
62944
- return join50(env["CODEX_HOME"] ?? join50(home, ".codex"), "hooks.json");
63797
+ return join52(env["CODEX_HOME"] ?? join52(home, ".codex"), "hooks.json");
62945
63798
  case "cursor":
62946
- return join50(home, ".cursor", "hooks.json");
63799
+ return join52(home, ".cursor", "hooks.json");
62947
63800
  case "stella":
62948
- return join50(env["STELLA_HOME"] ?? join50(home, ".stella"), "stella.toml");
63801
+ return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "stella.toml");
62949
63802
  }
62950
63803
  }
62951
63804
  function stellaJsonPath(env, home) {
62952
- return join50(env["STELLA_HOME"] ?? join50(home, ".stella"), "settings.json");
63805
+ return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "settings.json");
62953
63806
  }
62954
63807
  function isRecord7(v) {
62955
63808
  return v !== null && typeof v === "object" && !Array.isArray(v);
@@ -63036,7 +63889,7 @@ async function installJsonGroupHook(path2, harness, entry2, io) {
63036
63889
  };
63037
63890
  }
63038
63891
  const had = hasGroupHook(loaded.config);
63039
- await io.mkdirp(dirname28(path2));
63892
+ await io.mkdirp(dirname29(path2));
63040
63893
  await io.write(
63041
63894
  path2,
63042
63895
  `${JSON.stringify(withGroupHook(loaded.config, entry2), null, 2)}
@@ -63138,7 +63991,7 @@ async function installCursorHook(path2, io) {
63138
63991
  ];
63139
63992
  const next = { ...config, hooks };
63140
63993
  if (next.version === void 0) next.version = 1;
63141
- await io.mkdirp(dirname28(path2));
63994
+ await io.mkdirp(dirname29(path2));
63142
63995
  await io.write(path2, `${JSON.stringify(next, null, 2)}
63143
63996
  `, "utf8");
63144
63997
  return {
@@ -63321,7 +64174,7 @@ async function installStellaHook(projectRoot, io, env, home) {
63321
64174
  }
63322
64175
  const separator = base2.length === 0 ? "" : eol;
63323
64176
  const next = `${base2}${separator}${renderStellaBlock(eol)}`;
63324
- await io.mkdirp(dirname28(path2));
64177
+ await io.mkdirp(dirname29(path2));
63325
64178
  await io.write(path2, next, "utf8");
63326
64179
  return {
63327
64180
  harness: "stella",
@@ -63609,7 +64462,7 @@ var init_policy = __esm({
63609
64462
  // ../../packages/steering-freshness/src/settings.ts
63610
64463
  import { readFile as readFile8 } from "node:fs/promises";
63611
64464
  import { homedir as homedir11 } from "node:os";
63612
- import { join as join51 } from "node:path";
64465
+ import { join as join53 } from "node:path";
63613
64466
  async function readLayer(scope, path2, read2) {
63614
64467
  let text5;
63615
64468
  try {
@@ -63661,14 +64514,14 @@ function parseLayer(scope, path2, text5) {
63661
64514
  }
63662
64515
  async function loadSteeringSettings({
63663
64516
  projectRoot,
63664
- userSettingsPath = join51(homedir11(), USER_SETTINGS_RELATIVE),
64517
+ userSettingsPath = join53(homedir11(), USER_SETTINGS_RELATIVE),
63665
64518
  read: read2 = readFile8,
63666
64519
  workspacePolicy = null
63667
64520
  }) {
63668
64521
  const targets = [
63669
64522
  ["user", userSettingsPath],
63670
- ["project", join51(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
63671
- ["local", join51(projectRoot, PROJECT_DIR_NAME, LOCAL_SETTINGS_FILE)]
64523
+ ["project", join53(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
64524
+ ["local", join53(projectRoot, PROJECT_DIR_NAME, LOCAL_SETTINGS_FILE)]
63672
64525
  ];
63673
64526
  const layers = [];
63674
64527
  const warnings = [];
@@ -63731,7 +64584,7 @@ var init_settings = __esm({
63731
64584
  "use strict";
63732
64585
  init_git();
63733
64586
  init_policy();
63734
- USER_SETTINGS_RELATIVE = join51(
64587
+ USER_SETTINGS_RELATIVE = join53(
63735
64588
  ".config",
63736
64589
  "oxagen",
63737
64590
  "settings.json"
@@ -63744,7 +64597,7 @@ var init_settings = __esm({
63744
64597
 
63745
64598
  // ../../packages/steering-freshness/src/check.ts
63746
64599
  import { lstat as lstat3 } from "node:fs/promises";
63747
- import { isAbsolute as isAbsolute12, join as join52, resolve as resolve19 } from "node:path";
64600
+ import { isAbsolute as isAbsolute12, join as join54, resolve as resolve19 } from "node:path";
63748
64601
  function publishedCommits(platform) {
63749
64602
  if (platform.headCommits && platform.headCommits.length > 0)
63750
64603
  return platform.headCommits;
@@ -64081,7 +64934,7 @@ async function absentFromDisk(root, paths) {
64081
64934
  const flags = await Promise.all(
64082
64935
  paths.map(async (path2) => {
64083
64936
  try {
64084
- await lstat3(join52(root, path2));
64937
+ await lstat3(join54(root, path2));
64085
64938
  return false;
64086
64939
  } catch {
64087
64940
  return true;
@@ -64140,7 +64993,7 @@ var init_check = __esm({
64140
64993
  // ../../packages/steering-freshness/src/sync.ts
64141
64994
  import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
64142
64995
  import { tmpdir as tmpdir2 } from "node:os";
64143
- import { join as join53 } from "node:path";
64996
+ import { join as join55 } from "node:path";
64144
64997
  function refuse3(refusal, message) {
64145
64998
  return {
64146
64999
  applied: false,
@@ -64321,9 +65174,9 @@ async function syncSteering(opts) {
64321
65174
  };
64322
65175
  }
64323
65176
  async function commitPaths(ctx, message, paths) {
64324
- const dir = await mkdtemp(join53(tmpdir2(), "oxagen-steering-sync-"));
65177
+ const dir = await mkdtemp(join55(tmpdir2(), "oxagen-steering-sync-"));
64325
65178
  try {
64326
- const list2 = join53(dir, "pathspec");
65179
+ const list2 = join55(dir, "pathspec");
64327
65180
  await writeFile6(list2, `${paths.join("\0")}\0`, "utf8");
64328
65181
  await git3(
64329
65182
  ctx,
@@ -64850,10 +65703,10 @@ var init_paths2 = __esm({
64850
65703
  });
64851
65704
 
64852
65705
  // src/commands/workspace-link.ts
64853
- import { existsSync as existsSync26, mkdirSync as mkdirSync9, readFileSync as readFileSync24 } from "node:fs";
64854
- import { dirname as dirname29, join as join54 } from "node:path";
65706
+ import { existsSync as existsSync26, mkdirSync as mkdirSync9, readFileSync as readFileSync25 } from "node:fs";
65707
+ import { dirname as dirname30, join as join56 } from "node:path";
64855
65708
  function workspaceLinkPath(cwd) {
64856
- return join54(cwd, WORKSPACE_LINK_PATH);
65709
+ return join56(cwd, WORKSPACE_LINK_PATH);
64857
65710
  }
64858
65711
  function isRecord8(value) {
64859
65712
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -64886,7 +65739,7 @@ function readWorkspaceLink(cwd) {
64886
65739
  if (!existsSync26(filePath)) return null;
64887
65740
  let parsed2;
64888
65741
  try {
64889
- parsed2 = JSON.parse(readFileSync24(filePath, "utf8"));
65742
+ parsed2 = JSON.parse(readFileSync25(filePath, "utf8"));
64890
65743
  } catch (err2) {
64891
65744
  const detail = err2 instanceof Error ? err2.message : String(err2);
64892
65745
  process.stderr.write(
@@ -64907,7 +65760,7 @@ function readWorkspaceLink(cwd) {
64907
65760
  }
64908
65761
  function writeWorkspaceLink(cwd, link) {
64909
65762
  const filePath = workspaceLinkPath(cwd);
64910
- mkdirSync9(dirname29(filePath), { recursive: true });
65763
+ mkdirSync9(dirname30(filePath), { recursive: true });
64911
65764
  atomicWriteFileSync(filePath, JSON.stringify(link, null, 2));
64912
65765
  }
64913
65766
  var LINK_STRING_FIELDS;
@@ -64943,7 +65796,7 @@ __export(steering_exports, {
64943
65796
  });
64944
65797
  import { execFileSync } from "node:child_process";
64945
65798
  import { existsSync as existsSync27, realpathSync as realpathSync7 } from "node:fs";
64946
- import { dirname as dirname30, join as join55, normalize, parse as parsePath } from "node:path";
65799
+ import { dirname as dirname31, join as join57, normalize, parse as parsePath } from "node:path";
64947
65800
  function findProjectRoot(start) {
64948
65801
  const { root } = parsePath(start);
64949
65802
  const rawGitRoot = gitRootOr(start);
@@ -64952,9 +65805,9 @@ function findProjectRoot(start) {
64952
65805
  if (gitRoot !== null) return gitRoot;
64953
65806
  let dir = from;
64954
65807
  for (; ; ) {
64955
- if (existsSync27(join55(dir, PROJECT_DIR_NAME))) return dir;
65808
+ if (existsSync27(join57(dir, PROJECT_DIR_NAME))) return dir;
64956
65809
  if (dir === root) return from;
64957
- const parent = dirname30(dir);
65810
+ const parent = dirname31(dir);
64958
65811
  if (parent === dir) return from;
64959
65812
  dir = parent;
64960
65813
  }
@@ -81317,7 +82170,7 @@ function findSecretsAndPii(text5) {
81317
82170
  const findings = [];
81318
82171
  if (/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(text5))
81319
82172
  findings.push("private key block");
81320
- for (const token of text5.match(TOKEN) ?? []) {
82173
+ for (const token of text5.match(TOKEN2) ?? []) {
81321
82174
  if (isSecretToken(token)) {
81322
82175
  findings.push("credential token");
81323
82176
  break;
@@ -81344,7 +82197,7 @@ function findSecretsAndPii(text5) {
81344
82197
  }
81345
82198
  return findings;
81346
82199
  }
81347
- var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS, TOKEN;
82200
+ var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS, TOKEN2;
81348
82201
  var init_secrets = __esm({
81349
82202
  "../../packages/steering-check/src/secrets.ts"() {
81350
82203
  "use strict";
@@ -81395,7 +82248,7 @@ var init_secrets = __esm({
81395
82248
  "session_id",
81396
82249
  "client_secret"
81397
82250
  ];
81398
- TOKEN = /[A-Za-z0-9_\-./+~]+/g;
82251
+ TOKEN2 = /[A-Za-z0-9_\-./+~]+/g;
81399
82252
  }
81400
82253
  });
81401
82254
 
@@ -82044,7 +82897,7 @@ var init_memory_client = __esm({
82044
82897
  import { readFile as readFile9 } from "node:fs/promises";
82045
82898
  import {
82046
82899
  basename as basename7,
82047
- dirname as dirname31,
82900
+ dirname as dirname32,
82048
82901
  isAbsolute as isAbsolute13,
82049
82902
  relative as relative6,
82050
82903
  resolve as resolve20,
@@ -82056,7 +82909,7 @@ function inside(rel) {
82056
82909
  function importFilename(path2, folder) {
82057
82910
  let rel = relative6(process.cwd(), path2);
82058
82911
  if (!inside(rel) && folder !== void 0) {
82059
- rel = relative6(dirname31(resolve20(folder)), resolve20(path2));
82912
+ rel = relative6(dirname32(resolve20(folder)), resolve20(path2));
82060
82913
  }
82061
82914
  const name = inside(rel) ? rel.split(sep8).join("/") : basename7(path2);
82062
82915
  return name.length > 256 ? basename7(path2).slice(-256) : name;
@@ -82343,7 +83196,7 @@ __export(steering_import_exports, {
82343
83196
  });
82344
83197
  import { readdir as readdir4, realpath as realpath2, stat as stat4 } from "node:fs/promises";
82345
83198
  import { homedir as homedir12 } from "node:os";
82346
- import { basename as basename8, extname, join as join56, relative as relative7, sep as sep9 } from "node:path";
83199
+ import { basename as basename8, extname, join as join58, relative as relative7, sep as sep9 } from "node:path";
82347
83200
  function envDir2(env, key) {
82348
83201
  const value = env[key];
82349
83202
  return value === void 0 || value.trim() === "" ? null : value;
@@ -82351,34 +83204,34 @@ function envDir2(env, key) {
82351
83204
  function harnessHome(harness, env = process.env, home = homedir12()) {
82352
83205
  switch (harness) {
82353
83206
  case "claude-code": {
82354
- const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join56(home, ".claude");
83207
+ const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join58(home, ".claude");
82355
83208
  return {
82356
83209
  harness,
82357
83210
  folders: [
82358
83211
  {
82359
- dir: skillsRoot("claude-code", env, home) ?? join56(config, "skills"),
83212
+ dir: skillsRoot("claude-code", env, home) ?? join58(config, "skills"),
82360
83213
  mount: ".claude/skills"
82361
83214
  },
82362
- { dir: join56(config, "commands"), mount: ".claude/commands" },
82363
- { dir: join56(config, "agents"), mount: ".claude/agents" }
83215
+ { dir: join58(config, "commands"), mount: ".claude/commands" },
83216
+ { dir: join58(config, "agents"), mount: ".claude/agents" }
82364
83217
  ],
82365
83218
  instructions: [
82366
- { file: join56(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
83219
+ { file: join58(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
82367
83220
  ]
82368
83221
  };
82369
83222
  }
82370
83223
  case "codex": {
82371
- const codexHome = envDir2(env, "CODEX_HOME") ?? join56(home, ".codex");
83224
+ const codexHome = envDir2(env, "CODEX_HOME") ?? join58(home, ".codex");
82372
83225
  return {
82373
83226
  harness,
82374
83227
  folders: [
82375
83228
  {
82376
- dir: skillsRoot("codex", env, home) ?? join56(home, ".agents", "skills"),
83229
+ dir: skillsRoot("codex", env, home) ?? join58(home, ".agents", "skills"),
82377
83230
  mount: ".agents/skills"
82378
83231
  }
82379
83232
  ],
82380
83233
  instructions: ["AGENTS.override.md", "AGENTS.md"].map((name) => ({
82381
- file: join56(codexHome, name),
83234
+ file: join58(codexHome, name),
82382
83235
  filename: `.codex/${name}`
82383
83236
  }))
82384
83237
  };
@@ -82387,8 +83240,8 @@ function harnessHome(harness, env = process.env, home = homedir12()) {
82387
83240
  return {
82388
83241
  harness,
82389
83242
  folders: [
82390
- { dir: join56(home, ".cursor", "skills"), mount: ".cursor/skills" },
82391
- { dir: join56(home, ".cursor", "commands"), mount: ".cursor/commands" }
83243
+ { dir: join58(home, ".cursor", "skills"), mount: ".cursor/skills" },
83244
+ { dir: join58(home, ".cursor", "commands"), mount: ".cursor/commands" }
82392
83245
  ],
82393
83246
  instructions: []
82394
83247
  };
@@ -82434,7 +83287,7 @@ async function findMarkdownFiles(paths, harness) {
82434
83287
  }
82435
83288
  const found = [];
82436
83289
  for (const entry2 of entries2.sort(byName)) {
82437
- const path2 = join56(dir, entry2.name);
83290
+ const path2 = join58(dir, entry2.name);
82438
83291
  if (entry2.isDirectory()) {
82439
83292
  if (!entry2.name.startsWith(".") && entry2.name !== "node_modules")
82440
83293
  found.push(...await filesUnder(path2, from));
@@ -82456,7 +83309,7 @@ async function findMarkdownFiles(paths, harness) {
82456
83309
  const { folder } = frame;
82457
83310
  const named = (rel) => frame.fixedNames ? { filename: rel } : {};
82458
83311
  for (const entry2 of entries2) {
82459
- const path2 = join56(dir, entry2.name);
83312
+ const path2 = join58(dir, entry2.name);
82460
83313
  const rel = frame.relOf(path2);
82461
83314
  if (entry2.isDirectory()) {
82462
83315
  if (entry2.name === "node_modules") continue;
@@ -82515,7 +83368,7 @@ async function findMarkdownFiles(paths, harness) {
82515
83368
  }
82516
83369
  if (folderGiven) {
82517
83370
  const catalogue = CATALOGUE_FOLDERS.has(basename8(path2));
82518
- const root = catalogue ? join56(path2, "..") : path2;
83371
+ const root = catalogue ? join58(path2, "..") : path2;
82519
83372
  await walk2(path2, {
82520
83373
  relOf: (p) => under(root, p),
82521
83374
  folder: path2,
@@ -84174,12 +85027,12 @@ __export(verify_exports, {
84174
85027
  readBundle: () => readBundle,
84175
85028
  verifyBundle: () => verifyBundle3
84176
85029
  });
84177
- import { existsSync as existsSync28, readFileSync as readFileSync25, statSync as statSync14 } from "node:fs";
84178
- import { join as join57 } from "node:path";
85030
+ import { existsSync as existsSync28, readFileSync as readFileSync26, statSync as statSync15 } from "node:fs";
85031
+ import { join as join59 } from "node:path";
84179
85032
  function readDirectory(dir) {
84180
85033
  const read2 = (name) => {
84181
- const path2 = join57(dir, name);
84182
- return existsSync28(path2) ? readFileSync25(path2, "utf8") : void 0;
85034
+ const path2 = join59(dir, name);
85035
+ return existsSync28(path2) ? readFileSync26(path2, "utf8") : void 0;
84183
85036
  };
84184
85037
  return assemble(read2, dir);
84185
85038
  }
@@ -84189,7 +85042,7 @@ function isBundleEntry(name) {
84189
85042
  function readZip(path2) {
84190
85043
  let entries2;
84191
85044
  try {
84192
- entries2 = unzipSync(new Uint8Array(readFileSync25(path2)), {
85045
+ entries2 = unzipSync(new Uint8Array(readFileSync26(path2)), {
84193
85046
  filter: (file) => isBundleEntry(file.name)
84194
85047
  });
84195
85048
  } catch (err2) {
@@ -84225,7 +85078,7 @@ function readBundle(path2) {
84225
85078
  if (!existsSync28(path2)) {
84226
85079
  throw new BundleReadError(`${path2} does not exist.`);
84227
85080
  }
84228
- return statSync14(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
85081
+ return statSync15(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
84229
85082
  }
84230
85083
  function plural3(count12, one, many) {
84231
85084
  return `${count12} ${count12 === 1 ? one : many}`;
@@ -85133,7 +85986,7 @@ var init_conversation = __esm({
85133
85986
  });
85134
85987
 
85135
85988
  // src/auth/open-browser.ts
85136
- import { spawn as spawn6 } from "node:child_process";
85989
+ import { spawn as spawn8 } from "node:child_process";
85137
85990
  function openBrowser(url) {
85138
85991
  try {
85139
85992
  let cmd;
@@ -85151,7 +86004,7 @@ function openBrowser(url) {
85151
86004
  cmd = "xdg-open";
85152
86005
  args = [url];
85153
86006
  }
85154
- const child = spawn6(cmd, args, { detached: true, stdio: "ignore" });
86007
+ const child = spawn8(cmd, args, { detached: true, stdio: "ignore" });
85155
86008
  child.on("error", () => {
85156
86009
  });
85157
86010
  child.unref();
@@ -85301,13 +86154,13 @@ import {
85301
86154
  existsSync as existsSync29,
85302
86155
  lstatSync as lstatSync10,
85303
86156
  mkdirSync as mkdirSync10,
85304
- readFileSync as readFileSync26,
86157
+ readFileSync as readFileSync27,
85305
86158
  readdirSync as readdirSync14,
85306
86159
  realpathSync as realpathSync8,
85307
86160
  writeFileSync as writeFileSync7
85308
86161
  } from "node:fs";
85309
86162
  import { homedir as homedir13, hostname as osHostname3 } from "node:os";
85310
- import { join as join58, relative as relative8, resolve as resolve22, sep as sep10 } from "node:path";
86163
+ import { join as join60, relative as relative8, resolve as resolve22, sep as sep10 } from "node:path";
85311
86164
  function git4(cwd, args) {
85312
86165
  return new Promise((done) => {
85313
86166
  try {
@@ -85354,7 +86207,7 @@ function parseRemoteRepository(url) {
85354
86207
  }
85355
86208
  function oxagenPresent(root) {
85356
86209
  try {
85357
- return readdirSync14(join58(root, ".oxagen")).some(
86210
+ return readdirSync14(join60(root, ".oxagen")).some(
85358
86211
  (name) => name !== "workspace.json"
85359
86212
  );
85360
86213
  } catch {
@@ -85363,18 +86216,18 @@ function oxagenPresent(root) {
85363
86216
  }
85364
86217
  function symlinkState(root) {
85365
86218
  try {
85366
- if (!lstatSync10(join58(root, ".stella")).isDirectory()) return "none";
86219
+ if (!lstatSync10(join60(root, ".stella")).isDirectory()) return "none";
85367
86220
  } catch {
85368
86221
  return "none";
85369
86222
  }
85370
86223
  let oxagenReal;
85371
86224
  try {
85372
- oxagenReal = realpathSync8(join58(root, ".oxagen"));
86225
+ oxagenReal = realpathSync8(join60(root, ".oxagen"));
85373
86226
  } catch {
85374
86227
  return "missing";
85375
86228
  }
85376
86229
  for (const link of STELLA_LINKS) {
85377
- const at2 = join58(root, link);
86230
+ const at2 = join60(root, link);
85378
86231
  try {
85379
86232
  if (!lstatSync10(at2).isSymbolicLink()) return "missing";
85380
86233
  const target2 = realpathSync8(at2);
@@ -85388,11 +86241,11 @@ function symlinkState(root) {
85388
86241
  return "linked";
85389
86242
  }
85390
86243
  function machineId(configDir = getConfigDir()) {
85391
- const file = join58(configDir, MACHINE_ID_FILE);
86244
+ const file = join60(configDir, MACHINE_ID_FILE);
85392
86245
  let secret = null;
85393
86246
  try {
85394
86247
  if (existsSync29(file)) {
85395
- const stored = readFileSync26(file, "utf8").trim();
86248
+ const stored = readFileSync27(file, "utf8").trim();
85396
86249
  if (/^[0-9a-f]{64}$/.test(stored)) secret = stored;
85397
86250
  }
85398
86251
  if (secret === null) {
@@ -85470,15 +86323,15 @@ async function ensureWorkspaceLinkIgnored(root) {
85470
86323
  const topDir = resolve22(top);
85471
86324
  const entry2 = relative8(
85472
86325
  topDir,
85473
- join58(realpathOr2(root), ".oxagen", "workspace.json")
86326
+ join60(realpathOr2(root), ".oxagen", "workspace.json")
85474
86327
  ).split(sep10).join("/");
85475
86328
  if (entry2.startsWith("../")) return null;
85476
86329
  const rule = await git4(topDir, ["check-ignore", "--no-index", "-v", entry2]);
85477
86330
  if (rule !== null) return null;
85478
- const gitignore = join58(resolve22(top), ".gitignore");
86331
+ const gitignore = join60(resolve22(top), ".gitignore");
85479
86332
  let existing = "";
85480
86333
  try {
85481
- existing = readFileSync26(gitignore, "utf8");
86334
+ existing = readFileSync27(gitignore, "utf8");
85482
86335
  } catch {
85483
86336
  }
85484
86337
  const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
@@ -85991,16 +86844,16 @@ import { createHash as createHash18 } from "node:crypto";
85991
86844
  import {
85992
86845
  lstatSync as lstatSync11,
85993
86846
  mkdirSync as mkdirSync11,
85994
- readFileSync as readFileSync27,
86847
+ readFileSync as readFileSync28,
85995
86848
  readdirSync as readdirSync15,
85996
86849
  realpathSync as realpathSync9,
85997
86850
  rmSync as rmSync10,
85998
86851
  rmdirSync as rmdirSync7
85999
86852
  } from "node:fs";
86000
86853
  import {
86001
- dirname as dirname32,
86854
+ dirname as dirname33,
86002
86855
  isAbsolute as isAbsolute14,
86003
- join as join59,
86856
+ join as join61,
86004
86857
  parse as parsePath2,
86005
86858
  posix as posix4,
86006
86859
  sep as sep11
@@ -86113,32 +86966,32 @@ function findLinkedRoot(start) {
86113
86966
  } catch {
86114
86967
  }
86115
86968
  if (dir === root) return null;
86116
- const parent = dirname32(dir);
86969
+ const parent = dirname33(dir);
86117
86970
  if (parent === dir) return null;
86118
86971
  dir = parent;
86119
86972
  }
86120
86973
  }
86121
86974
  function fsReader(root) {
86122
86975
  return (path2) => {
86123
- const abs = join59(root, ...path2.split("/"));
86976
+ const abs = join61(root, ...path2.split("/"));
86124
86977
  try {
86125
86978
  const st = lstatSync11(abs);
86126
86979
  if (!st.isFile()) return Buffer.from(`\0not-a-file:${abs}`);
86127
- return readFileSync27(abs);
86980
+ return readFileSync28(abs);
86128
86981
  } catch {
86129
86982
  return null;
86130
86983
  }
86131
86984
  };
86132
86985
  }
86133
86986
  function assertInsideOxagen(root, path2) {
86134
- const oxagenAbs = join59(root, OXAGEN_DIR);
86987
+ const oxagenAbs = join61(root, OXAGEN_DIR);
86135
86988
  let oxagenReal;
86136
86989
  try {
86137
86990
  oxagenReal = realpathSync9(oxagenAbs);
86138
86991
  } catch {
86139
86992
  return;
86140
86993
  }
86141
- const abs = join59(root, ...path2.split("/"));
86994
+ const abs = join61(root, ...path2.split("/"));
86142
86995
  try {
86143
86996
  if (lstatSync11(abs).isSymbolicLink()) {
86144
86997
  throw new Error(
@@ -86148,7 +87001,7 @@ function assertInsideOxagen(root, path2) {
86148
87001
  } catch (err2) {
86149
87002
  if (err2.code !== "ENOENT") throw err2;
86150
87003
  }
86151
- let dir = dirname32(abs);
87004
+ let dir = dirname33(abs);
86152
87005
  for (; ; ) {
86153
87006
  let real = null;
86154
87007
  try {
@@ -86162,14 +87015,14 @@ function assertInsideOxagen(root, path2) {
86162
87015
  }
86163
87016
  return;
86164
87017
  }
86165
- const parent = dirname32(dir);
87018
+ const parent = dirname33(dir);
86166
87019
  if (parent === dir) return;
86167
87020
  dir = parent;
86168
87021
  }
86169
87022
  }
86170
87023
  function pruneEmptyDirs(root, path2) {
86171
- const stop = join59(root, OXAGEN_DIR);
86172
- let dir = dirname32(join59(root, ...path2.split("/")));
87024
+ const stop = join61(root, OXAGEN_DIR);
87025
+ let dir = dirname33(join61(root, ...path2.split("/")));
86173
87026
  while (dir.startsWith(stop + sep11)) {
86174
87027
  try {
86175
87028
  if (readdirSync15(dir).length > 0) return;
@@ -86177,19 +87030,19 @@ function pruneEmptyDirs(root, path2) {
86177
87030
  } catch {
86178
87031
  return;
86179
87032
  }
86180
- dir = dirname32(dir);
87033
+ dir = dirname33(dir);
86181
87034
  }
86182
87035
  }
86183
87036
  function applyPlan(root, plan) {
86184
87037
  for (const w of plan.writes) assertInsideOxagen(root, w.path);
86185
87038
  for (const d of plan.deletes) assertInsideOxagen(root, d);
86186
87039
  for (const w of plan.writes) {
86187
- const abs = join59(root, ...w.path.split("/"));
86188
- mkdirSync11(dirname32(abs), { recursive: true });
87040
+ const abs = join61(root, ...w.path.split("/"));
87041
+ mkdirSync11(dirname33(abs), { recursive: true });
86189
87042
  atomicWriteFileSync(abs, w.content);
86190
87043
  }
86191
87044
  for (const d of plan.deletes) {
86192
- rmSync10(join59(root, ...d.split("/")), { force: true });
87045
+ rmSync10(join61(root, ...d.split("/")), { force: true });
86193
87046
  pruneEmptyDirs(root, d);
86194
87047
  }
86195
87048
  }
@@ -108152,7 +109005,7 @@ function invalidArguments(message) {
108152
109005
  return new BuildError("Invalid arguments", message);
108153
109006
  }
108154
109007
  function checkedName(parameter) {
108155
- if (!TOKEN2.test(parameter.name)) {
109008
+ if (!TOKEN3.test(parameter.name)) {
108156
109009
  throw new BuildError(
108157
109010
  "Invalid request",
108158
109011
  `The ${parameter.in} parameter name ${parameter.name} is not an HTTP token.`
@@ -108234,7 +109087,7 @@ function cookiePairs(parameter, value) {
108234
109087
  ]
108235
109088
  ] : shape.kind === "list" ? shape.items.map((item) => [name, item]) : shape.entries;
108236
109089
  for (const [cookie, text5] of pairs) {
108237
- if (!TOKEN2.test(cookie)) {
109090
+ if (!TOKEN3.test(cookie)) {
108238
109091
  throw invalidArguments(
108239
109092
  `The cookie ${parameter.name} holds a key that is not an HTTP token, so no cookie can take it as a name.`
108240
109093
  );
@@ -108277,12 +109130,12 @@ function queryEntries(parameter, value) {
108277
109130
  }
108278
109131
  if (shape.items.length === 0) return [];
108279
109132
  if (explode) return shape.items.map((item) => pair(name, item));
108280
- const [raw, delimiter] = style === "form" ? [",", ","] : style === "spaceDelimited" ? [" ", "%20"] : ["|", "|"];
109133
+ const [raw, delimiter3] = style === "form" ? [",", ","] : style === "spaceDelimited" ? [" ", "%20"] : ["|", "|"];
108281
109134
  return [
108282
109135
  entry(
108283
109136
  name,
108284
109137
  shape.items.join(raw),
108285
- `${enc(name)}=${shape.items.map(val).join(delimiter)}`
109138
+ `${enc(name)}=${shape.items.map(val).join(delimiter3)}`
108286
109139
  )
108287
109140
  ];
108288
109141
  }
@@ -108499,7 +109352,7 @@ function createHttpSender(options = {}) {
108499
109352
  }
108500
109353
  };
108501
109354
  }
108502
- var IDEMPOTENT_METHODS, RESERVED_HEADERS, TOKEN2, UNSAFE_HEADER_VALUE, UNSAFE_COOKIE_VALUE;
109355
+ var IDEMPOTENT_METHODS, RESERVED_HEADERS, TOKEN3, UNSAFE_HEADER_VALUE, UNSAFE_COOKIE_VALUE;
108503
109356
  var init_http = __esm({
108504
109357
  "../../packages/mcp-studio/src/execute/http.ts"() {
108505
109358
  "use strict";
@@ -108527,7 +109380,7 @@ var init_http = __esm({
108527
109380
  "proxy-authorization",
108528
109381
  "cookie"
108529
109382
  ]);
108530
- TOKEN2 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
109383
+ TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
108531
109384
  UNSAFE_HEADER_VALUE = /[\r\n\0]/;
108532
109385
  UNSAFE_COOKIE_VALUE = /[;\r\n\0]/;
108533
109386
  }
@@ -130060,11 +130913,11 @@ import { createHash as createHash20 } from "node:crypto";
130060
130913
  import {
130061
130914
  lstatSync as lstatSync12,
130062
130915
  mkdirSync as mkdirSync12,
130063
- readFileSync as readFileSync28,
130916
+ readFileSync as readFileSync29,
130064
130917
  readlinkSync as readlinkSync4,
130065
- statSync as statSync15
130918
+ statSync as statSync16
130066
130919
  } from "node:fs";
130067
- import { dirname as dirname33, isAbsolute as isAbsolute15, join as join60, relative as relative9, resolve as resolve24, sep as sep12 } from "node:path";
130920
+ import { dirname as dirname34, isAbsolute as isAbsolute15, join as join62, relative as relative9, resolve as resolve24, sep as sep12 } from "node:path";
130068
130921
  function git5(cwd, args, input) {
130069
130922
  try {
130070
130923
  return execFileSync2("git", args, {
@@ -130096,11 +130949,11 @@ function commitOf2(root, ref2) {
130096
130949
  return sha2.length > 0 ? sha2 : null;
130097
130950
  }
130098
130951
  function readWorkingFile(root, path2) {
130099
- const abs = join60(root, ...path2.split("/"));
130952
+ const abs = join62(root, ...path2.split("/"));
130100
130953
  try {
130101
130954
  const stat7 = lstatSync12(abs);
130102
130955
  if (stat7.isSymbolicLink()) return readlinkSync4(abs, "utf8");
130103
- return stat7.isFile() ? readFileSync28(abs, "utf8") : null;
130956
+ return stat7.isFile() ? readFileSync29(abs, "utf8") : null;
130104
130957
  } catch {
130105
130958
  return null;
130106
130959
  }
@@ -130175,14 +131028,14 @@ function chooseBase(root, ref2) {
130175
131028
  }
130176
131029
  function isFile(path2) {
130177
131030
  try {
130178
- return statSync15(path2).isFile();
131031
+ return statSync16(path2).isFile();
130179
131032
  } catch {
130180
131033
  return false;
130181
131034
  }
130182
131035
  }
130183
131036
  function isDir(path2) {
130184
131037
  try {
130185
- return statSync15(path2).isDirectory();
131038
+ return statSync16(path2).isDirectory();
130186
131039
  } catch {
130187
131040
  return false;
130188
131041
  }
@@ -130190,10 +131043,10 @@ function isDir(path2) {
130190
131043
  function findSteeringRoot(start) {
130191
131044
  let dir = resolve24(start);
130192
131045
  for (; ; ) {
130193
- if (isFile(join60(dir, WORKSPACE_TOML_PATH)) || isFile(join60(dir, AGENTS_MD_PATH)) && isDir(join60(dir, STEERING_DIR))) {
131046
+ if (isFile(join62(dir, WORKSPACE_TOML_PATH)) || isFile(join62(dir, AGENTS_MD_PATH)) && isDir(join62(dir, STEERING_DIR))) {
130194
131047
  return dir;
130195
131048
  }
130196
- const parent = dirname33(dir);
131049
+ const parent = dirname34(dir);
130197
131050
  if (parent === dir) return null;
130198
131051
  dir = parent;
130199
131052
  }
@@ -130270,12 +131123,12 @@ function cacheFile(cacheDir, root) {
130270
131123
  key: token === void 0 ? null : createHash20("sha256").update(token).digest("hex").slice(0, 16)
130271
131124
  };
130272
131125
  const key = createHash20("sha256").update(JSON.stringify([root, identity])).digest("hex").slice(0, 32);
130273
- return join60(cacheDir, `${key}.json`);
131126
+ return join62(cacheDir, `${key}.json`);
130274
131127
  }
130275
131128
  function readCache(file, now) {
130276
131129
  let entry2;
130277
131130
  try {
130278
- entry2 = JSON.parse(readFileSync28(file, "utf8"));
131131
+ entry2 = JSON.parse(readFileSync29(file, "utf8"));
130279
131132
  } catch {
130280
131133
  return null;
130281
131134
  }
@@ -130298,7 +131151,7 @@ async function publishedInputs(root, deps, refresh, out) {
130298
131151
  }
130299
131152
  if (file !== null) {
130300
131153
  try {
130301
- mkdirSync12(dirname33(file), { recursive: true });
131154
+ mkdirSync12(dirname34(file), { recursive: true });
130302
131155
  atomicWriteFileSync(
130303
131156
  file,
130304
131157
  JSON.stringify({
@@ -130384,7 +131237,7 @@ function defaultDeps2() {
130384
131237
  return {
130385
131238
  fetchPublished: fetchSteeringIndex,
130386
131239
  cedar: cedarHooks,
130387
- cacheDir: join60(getConfigDir(), "cache", "steering-check"),
131240
+ cacheDir: join62(getConfigDir(), "cache", "steering-check"),
130388
131241
  now: () => Date.now()
130389
131242
  };
130390
131243
  }
@@ -134267,7 +135120,7 @@ var init_src9 = __esm({
134267
135120
 
134268
135121
  // src/commands/codegraph-upload/files.ts
134269
135122
  import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
134270
- import { join as join61, relative as relative10, sep as sep13 } from "node:path";
135123
+ import { join as join63, relative as relative10, sep as sep13 } from "node:path";
134271
135124
  function hasGlobChars(pattern) {
134272
135125
  return /[*?[\]{}]/.test(pattern);
134273
135126
  }
@@ -134279,7 +135132,7 @@ async function walk(root, dir, out) {
134279
135132
  const entries2 = await readdir5(dir, { withFileTypes: true });
134280
135133
  for (const entry2 of entries2) {
134281
135134
  if (entry2.isSymbolicLink()) continue;
134282
- const full = join61(dir, entry2.name);
135135
+ const full = join63(dir, entry2.name);
134283
135136
  if (entry2.isDirectory()) {
134284
135137
  if (!SKIP_DIRS.has(entry2.name)) await walk(root, full, out);
134285
135138
  } else if (entry2.isFile()) {
@@ -134291,7 +135144,7 @@ async function expandPattern(root, pattern) {
134291
135144
  const wanted = normalize2(pattern.trim());
134292
135145
  if (!hasGlobChars(wanted)) {
134293
135146
  try {
134294
- const info = await stat5(join61(root, wanted));
135147
+ const info = await stat5(join63(root, wanted));
134295
135148
  return info.isFile() ? [wanted] : [];
134296
135149
  } catch {
134297
135150
  return [];
@@ -134442,7 +135295,7 @@ var init_token = __esm({
134442
135295
  });
134443
135296
 
134444
135297
  // src/commands/codegraph-upload/run.ts
134445
- import { join as join62 } from "node:path";
135298
+ import { join as join64 } from "node:path";
134446
135299
  function addDropped(into, more) {
134447
135300
  into.sensitiveValues += more.sensitiveValues;
134448
135301
  into.secretData += more.secretData;
@@ -134460,7 +135313,7 @@ async function uploadOne(deps, config, token, group, path2, totals2) {
134460
135313
  const label2 = `${group.kind}/${group.source} ${path2}`;
134461
135314
  let bytes;
134462
135315
  try {
134463
- bytes = await deps.readFile(join62(deps.cwd, path2));
135316
+ bytes = await deps.readFile(join64(deps.cwd, path2));
134464
135317
  } catch (error) {
134465
135318
  totals2.failed += 1;
134466
135319
  deps.log(`error: ${label2}: cannot read the file: ${error.message}`);
@@ -136296,7 +137149,7 @@ __export(secret_exports, {
136296
137149
  handleSecretReveal: () => handleSecretReveal,
136297
137150
  handleSecretSet: () => handleSecretSet
136298
137151
  });
136299
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync8 } from "fs";
137152
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync8 } from "fs";
136300
137153
  async function readStdin2() {
136301
137154
  const chunks = [];
136302
137155
  for await (const chunk2 of process.stdin) chunks.push(chunk2);
@@ -136409,7 +137262,7 @@ async function handleSecretReveal(key, opts, writer = stdoutWriter) {
136409
137262
  async function handleSecretImport(opts, writer = stdoutWriter) {
136410
137263
  const out = createOutput({}, writer);
136411
137264
  try {
136412
- const text5 = opts.file ? readFileSync29(opts.file, "utf8") : await readStdin2();
137265
+ const text5 = opts.file ? readFileSync30(opts.file, "utf8") : await readStdin2();
136413
137266
  const environmentId = opts.env ? await resolveEnvironmentId2(opts.env) : null;
136414
137267
  const { rows, committed } = await apiPostOrThrow("secret/import-env", { text: text5, environmentId, commit: !!opts.yes });
136415
137268
  const newCount = rows.filter((r) => r.isNewKey).length;