@oxagen/cli 2.1.4-957 → 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 +1527 -774
  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-957";
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
  }
@@ -29031,15 +29570,15 @@ var init_github = __esm({
29031
29570
 
29032
29571
  // ../../packages/tacho/src/host/harness-path.ts
29033
29572
  import { spawnSync as spawnSync5 } from "node:child_process";
29034
- import { delimiter, join as join18 } from "node:path";
29573
+ import { delimiter as delimiter2, join as join19 } from "node:path";
29035
29574
  function knownDirs(home) {
29036
29575
  return [
29037
- join18(home, ".local", "bin"),
29038
- join18(home, ".bun", "bin"),
29039
- join18(home, ".npm-global", "bin"),
29040
- join18(home, ".volta", "bin"),
29041
- join18(home, ".cargo", "bin"),
29042
- join18(home, "bin"),
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"),
29043
29582
  "/opt/homebrew/bin",
29044
29583
  "/usr/local/bin",
29045
29584
  "/usr/bin",
@@ -29079,10 +29618,10 @@ function harnessPath(env, home, loginPath) {
29079
29618
  seen.add(dir);
29080
29619
  dirs.push(dir);
29081
29620
  };
29082
- for (const dir of (loginPath ?? "").split(delimiter)) add2(dir);
29083
- for (const dir of (env["PATH"] ?? "").split(delimiter)) add2(dir);
29621
+ for (const dir of (loginPath ?? "").split(delimiter2)) add2(dir);
29622
+ for (const dir of (env["PATH"] ?? "").split(delimiter2)) add2(dir);
29084
29623
  for (const dir of knownDirs(home)) add2(dir);
29085
- return dirs.join(delimiter);
29624
+ return dirs.join(delimiter2);
29086
29625
  }
29087
29626
  var MARK, LOGIN_SHELL_TIMEOUT_MS;
29088
29627
  var init_harness_path = __esm({
@@ -29159,7 +29698,7 @@ var init_policy_mode = __esm({
29159
29698
 
29160
29699
  // ../../packages/tacho/src/host/wal-ceiling.ts
29161
29700
  import { statfsSync } from "node:fs";
29162
- import { join as join19 } from "node:path";
29701
+ import { join as join20 } from "node:path";
29163
29702
  function freeBytesOn(dir) {
29164
29703
  try {
29165
29704
  const stats2 = statfsSync(dir);
@@ -29206,7 +29745,7 @@ function isDrop(value) {
29206
29745
  function readWalCeilingState(walDir) {
29207
29746
  let raw;
29208
29747
  try {
29209
- raw = readJsonFileIfExists(join19(walDir, STATE_FILE));
29748
+ raw = readJsonFileIfExists(join20(walDir, STATE_FILE));
29210
29749
  } catch (error) {
29211
29750
  if (error instanceof SyntaxError) return void 0;
29212
29751
  throw error;
@@ -29406,7 +29945,7 @@ var init_wal_ceiling = __esm({
29406
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);
29407
29946
  this.state = next;
29408
29947
  if (!changed) return;
29409
- const path2 = join19(this.walDir, STATE_FILE);
29948
+ const path2 = join20(this.walDir, STATE_FILE);
29410
29949
  try {
29411
29950
  writeSensitiveFileAtomic(path2, JSON.stringify(next));
29412
29951
  this.unsaved = false;
@@ -29910,7 +30449,7 @@ import {
29910
30449
  rmdirSync as rmdirSync5,
29911
30450
  rmSync as rmSync2
29912
30451
  } from "node:fs";
29913
- import { dirname as dirname12, join as join20 } from "node:path";
30452
+ import { dirname as dirname12, join as join21 } from "node:path";
29914
30453
  async function revokeOnControlPlane(host, options, deps, warnings) {
29915
30454
  const resolved = resolveCredentials(
29916
30455
  {
@@ -30521,7 +31060,7 @@ async function unenrollLocked(options, deps) {
30521
31060
  ];
30522
31061
  const purged = parts.filter(([, path2, recursive]) => remove2(path2, recursive)).map(([name]) => name);
30523
31062
  let failed3 = purged.length < parts.length;
30524
- if (!existsSync15(join20(deps.paths.dir, HARNESS_RECEIPTS)) && !remove2(join20(deps.paths.dir, HARNESS_BACKUPS), true))
31063
+ if (!existsSync15(join21(deps.paths.dir, HARNESS_RECEIPTS)) && !remove2(join21(deps.paths.dir, HARNESS_BACKUPS), true))
30525
31064
  failed3 = true;
30526
31065
  const logShared = otherLiveAgents(deps.paths, deps.paths.dir).length > 0;
30527
31066
  if (!logShared) {
@@ -30579,7 +31118,7 @@ var init_unenroll = __esm({
30579
31118
 
30580
31119
  // ../../packages/tacho/src/cli/enroll.ts
30581
31120
  import { existsSync as existsSync16, lstatSync as lstatSync4, rmSync as rmSync3 } from "node:fs";
30582
- import { dirname as dirname13, isAbsolute, join as join21 } from "node:path";
31121
+ import { dirname as dirname13, isAbsolute, join as join22 } from "node:path";
30583
31122
  function rootRefusal(command2, deps, allowRoot) {
30584
31123
  if (allowRoot === true || deps.getuid?.() !== 0) return void 0;
30585
31124
  const user = deps.env["SUDO_USER"] ?? "<user>";
@@ -30605,7 +31144,7 @@ function runtimeOf(host) {
30605
31144
  if (first === void 0 || !files2.every((file) => existsSync16(file)))
30606
31145
  return void 0;
30607
31146
  const binDir = dirname13(first);
30608
- const tacho = host.daemon_command[0] === first ? shellQuote(first) : `${host.daemon_command[0]} ${shellQuote(join21(binDir, "tacho.mjs"))}`;
31147
+ const tacho = host.daemon_command[0] === first ? shellQuote(first) : `${host.daemon_command[0]} ${shellQuote(join22(binDir, "tacho.mjs"))}`;
30609
31148
  return {
30610
31149
  hookCommand: host.hook_command,
30611
31150
  credentialHelperCommand: helperCommandFor(tacho),
@@ -31699,6 +32238,7 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
31699
32238
  const mode = options.credentials ?? "brokered";
31700
32239
  try {
31701
32240
  if (mode === "brokered") {
32241
+ await signInIfNoCredential(reaching, deps);
31702
32242
  const outcome = await brokerCredentials(host, reaching, deps);
31703
32243
  for (const entry2 of outcome.harnesses)
31704
32244
  deps.out(` ${describeHarness(entry2)}`);
@@ -32861,21 +33401,21 @@ import {
32861
33401
  statSync as statSync7,
32862
33402
  unlinkSync as unlinkSync10
32863
33403
  } from "node:fs";
32864
- import { dirname as dirname14, isAbsolute as isAbsolute2, join as join22, 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";
32865
33405
  function dataLocalDir(home, env, platform) {
32866
33406
  if (platform === "darwin")
32867
- return join22(home, "Library", "Application Support");
33407
+ return join23(home, "Library", "Application Support");
32868
33408
  if (platform === "win32")
32869
- return env["LOCALAPPDATA"] ?? join22(home, "AppData", "Local");
33409
+ return env["LOCALAPPDATA"] ?? join23(home, "AppData", "Local");
32870
33410
  const xdg = env["XDG_DATA_HOME"];
32871
- return xdg !== void 0 && isAbsolute2(xdg) ? xdg : join22(home, ".local", "share");
33411
+ return xdg !== void 0 && isAbsolute2(xdg) ? xdg : join23(home, ".local", "share");
32872
33412
  }
32873
33413
  function desktopPlaces(deps) {
32874
- const oxagenDir = join22(deps.home, ".config", "oxagen");
33414
+ const oxagenDir = join23(deps.home, ".config", "oxagen");
32875
33415
  return {
32876
33416
  oxagenDir,
32877
- desktopConfig: join22(oxagenDir, "desktop.json"),
32878
- copies: join22(
33417
+ desktopConfig: join23(oxagenDir, "desktop.json"),
33418
+ copies: join23(
32879
33419
  dataLocalDir(deps.home, deps.env, deps.platform),
32880
33420
  "oxagen",
32881
33421
  "bin"
@@ -33120,7 +33660,7 @@ function undoEntry(entry2, deps, tally2) {
33120
33660
  }
33121
33661
  function versionDirs(copies) {
33122
33662
  try {
33123
- return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) => join22(copies, entry2.name)).sort();
33663
+ return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) => join23(copies, entry2.name)).sort();
33124
33664
  } catch {
33125
33665
  return [];
33126
33666
  }
@@ -33156,7 +33696,7 @@ function journaledCopies(journal, copies, tally2) {
33156
33696
  function removeSidecars(dir, platform, tally2) {
33157
33697
  const note = platform === "win32" ? `. Windows keeps a running program's file, so delete ${dir} after this command exits` : "";
33158
33698
  for (const name of ["oxagen", "tacho"]) {
33159
- const file = join22(dir, platform === "win32" ? `${name}.exe` : name);
33699
+ const file = join23(dir, platform === "win32" ? `${name}.exe` : name);
33160
33700
  removeFile(file, tally2, note);
33161
33701
  }
33162
33702
  }
@@ -33244,7 +33784,7 @@ async function uninstall(options, deps) {
33244
33784
  tally2.left.push(`cannot remove ${dir}: ${reasonOf3(error)}`);
33245
33785
  }
33246
33786
  }
33247
- const configDir = join22(deps.home, ".config");
33787
+ const configDir = join23(deps.home, ".config");
33248
33788
  if (record.configDirCreated && removeDirIfEmpty(configDir))
33249
33789
  tally2.removed.push(configDir);
33250
33790
  for (const path2 of tally2.removed) deps.out(` removed ${path2}`);
@@ -33658,9 +34198,9 @@ var init_run = __esm({
33658
34198
  });
33659
34199
 
33660
34200
  // ../../packages/tacho/src/cli/agent-run.ts
33661
- import { spawn as spawn2 } from "node:child_process";
34201
+ import { spawn as spawn4 } from "node:child_process";
33662
34202
  import { randomUUID as randomUUID2 } from "node:crypto";
33663
- import { constants as constants2 } from "node:os";
34203
+ import { constants as constants3 } from "node:os";
33664
34204
  import { posix as posix3, win32 as win324 } from "node:path";
33665
34205
  function programName(program2) {
33666
34206
  const base2 = (program2.includes("\\") ? win324 : posix3).basename(program2);
@@ -33686,7 +34226,7 @@ function sessionRefusal(stdout) {
33686
34226
  function exitCodeOf(exit) {
33687
34227
  if (exit.error !== void 0) return 1;
33688
34228
  if (exit.code !== null) return exit.code;
33689
- const number2 = exit.signal !== null ? constants2.signals[exit.signal] : void 0;
34229
+ const number2 = exit.signal !== null ? constants3.signals[exit.signal] : void 0;
33690
34230
  return number2 !== void 0 ? 128 + number2 : 1;
33691
34231
  }
33692
34232
  function spawnAgent(command2, args, options) {
@@ -33697,7 +34237,7 @@ function spawnAgent(command2, args, options) {
33697
34237
  options.env
33698
34238
  );
33699
34239
  return new Promise((resolve25) => {
33700
- const child = spawn2(invocation.command, invocation.args, {
34240
+ const child = spawn4(invocation.command, invocation.args, {
33701
34241
  cwd: options.cwd,
33702
34242
  env: options.env,
33703
34243
  stdio: "inherit",
@@ -33922,11 +34462,11 @@ var init_work_order_start = __esm({
33922
34462
 
33923
34463
  // ../../packages/tacho/src/host/work-orders.ts
33924
34464
  import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
33925
- import { join as join23 } from "node:path";
34465
+ import { join as join24 } from "node:path";
33926
34466
  function orderFile(paths, id2) {
33927
34467
  if (!WORK_ORDER_ID_PATTERN.test(id2))
33928
34468
  throw new Error(`${JSON.stringify(id2)} is not a work order id`);
33929
- return join23(paths.workOrders, `${id2}.json`);
34469
+ return join24(paths.workOrders, `${id2}.json`);
33930
34470
  }
33931
34471
  function readKept(path2) {
33932
34472
  try {
@@ -33976,7 +34516,7 @@ function removeWorkOrder(paths, id2) {
33976
34516
  function runningFile(paths, id2) {
33977
34517
  if (!WORK_ORDER_ID_PATTERN.test(id2))
33978
34518
  throw new Error(`${JSON.stringify(id2)} is not a work order id`);
33979
- return join23(paths.workOrders, `${id2}.running`);
34519
+ return join24(paths.workOrders, `${id2}.running`);
33980
34520
  }
33981
34521
  function processIsAlive(pid) {
33982
34522
  try {
@@ -34378,10 +34918,12 @@ var init_work = __esm({
34378
34918
  var cli_exports = {};
34379
34919
  __export(cli_exports, {
34380
34920
  BACKFILL_EXIT: () => BACKFILL_EXIT,
34921
+ CLAUDE_OAUTH_TOKEN_ENV: () => CLAUDE_OAUTH_TOKEN_ENV,
34381
34922
  CONTAINED_AGENTS: () => CONTAINED_AGENTS,
34382
34923
  CURSOR_COVERAGE_NOTE: () => CURSOR_COVERAGE_NOTE,
34383
34924
  MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
34384
34925
  SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
34926
+ acquireSubscriptionToken: () => acquireSubscriptionToken,
34385
34927
  addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
34386
34928
  agentNameFromCommand: () => agentNameFromCommand,
34387
34929
  backfillCommand: () => backfillCommand,
@@ -34389,6 +34931,7 @@ __export(cli_exports, {
34389
34931
  canServeOxagenTools: () => canServeOxagenTools,
34390
34932
  claudeFacts: () => claudeFacts,
34391
34933
  credentialIssue: () => credentialIssue,
34934
+ credentialLogin: () => credentialLogin,
34392
34935
  credentialStatus: () => credentialStatus,
34393
34936
  cursorAppFacts: () => cursorAppFacts,
34394
34937
  cursorFacts: () => cursorFacts,
@@ -34439,6 +34982,7 @@ __export(cli_exports, {
34439
34982
  shellQuote: () => shellQuote,
34440
34983
  shimError: () => shimError,
34441
34984
  shippingHealth: () => shippingHealth,
34985
+ signInIfNoCredential: () => signInIfNoCredential,
34442
34986
  spawnAgent: () => spawnAgent,
34443
34987
  stableExecutablePath: () => stableExecutablePath,
34444
34988
  status: () => status,
@@ -38173,6 +38717,7 @@ function containedBridgeHandler(options) {
38173
38717
  const modelPaths = options.harness === "claude-code" ? ["/model/v1/messages", "/model/v1/messages/count_tokens"] : ["/model/v1/responses", "/model/v1/chat/completions"];
38174
38718
  if (request3.method === "POST" && modelPaths.includes(path2)) {
38175
38719
  const credential = options.issueCredential();
38720
+ const chatgpt = options.harness === "codex" && options.chatgptLogin?.() === true;
38176
38721
  request3.headers = {
38177
38722
  "content-type": "application/json",
38178
38723
  ...request3.headers["content-length"] ? { "content-length": request3.headers["content-length"] } : {},
@@ -38180,9 +38725,12 @@ function containedBridgeHandler(options) {
38180
38725
  ...request3.headers["anthropic-beta"] ? { "anthropic-beta": request3.headers["anthropic-beta"] } : {},
38181
38726
  host: `127.0.0.1:${options.modelPort}`,
38182
38727
  "x-oxagen-session": options.sessionId,
38183
- ...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" } : {}
38184
38732
  };
38185
- 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");
38186
38734
  options.model(request3, response);
38187
38735
  return;
38188
38736
  }
@@ -38334,7 +38882,7 @@ var init_bridge = __esm({
38334
38882
 
38335
38883
  // ../../packages/tacho/src/contained/workspace.ts
38336
38884
  import { lstatSync as lstatSync6, readFileSync as readFileSync14, readdirSync as readdirSync11, realpathSync as realpathSync3 } from "node:fs";
38337
- import { isAbsolute as isAbsolute3, join as join24, 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";
38338
38886
  function within(root, path2) {
38339
38887
  const rel = relative2(root, path2);
38340
38888
  return rel === "" || !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
@@ -38364,7 +38912,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
38364
38912
  }
38365
38913
  const visit2 = (directory) => {
38366
38914
  for (const entry2 of readdirSync11(directory)) {
38367
- const path2 = join24(directory, entry2);
38915
+ const path2 = join25(directory, entry2);
38368
38916
  if (CREDENTIAL_PATHS.has(entry2) || isEnvFile(entry2))
38369
38917
  throw new Error(
38370
38918
  `Remove local credential files from the contained checkout before launch: ${named(path2)}`
@@ -38398,7 +38946,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
38398
38946
  }
38399
38947
  function gitDirectoryMounts(workspace) {
38400
38948
  const root = realpathSync3(workspace);
38401
- const dotGit = join24(root, ".git");
38949
+ const dotGit = join25(root, ".git");
38402
38950
  let stat7;
38403
38951
  try {
38404
38952
  stat7 = lstatSync6(dotGit);
@@ -38436,7 +38984,7 @@ function gitDirectoryMounts(workspace) {
38436
38984
  if (gitDir !== void 0) {
38437
38985
  let common;
38438
38986
  try {
38439
- common = readFileSync14(join24(gitDir, "commondir"), "utf8").trim();
38987
+ common = readFileSync14(join25(gitDir, "commondir"), "utf8").trim();
38440
38988
  } catch {
38441
38989
  common = void 0;
38442
38990
  }
@@ -38463,7 +39011,7 @@ var init_workspace = __esm({
38463
39011
  // ../../packages/tacho/src/collector/hardened-git.ts
38464
39012
  import { execFile as execFile2 } from "node:child_process";
38465
39013
  import { lstatSync as lstatSync7, readFileSync as readFileSync15, statSync as statSync8 } from "node:fs";
38466
- import { dirname as dirname15, join as join25, resolve as resolve10 } from "node:path";
39014
+ import { dirname as dirname15, join as join26, resolve as resolve10 } from "node:path";
38467
39015
  function hardenedGitArgs(cwd, args, globals = []) {
38468
39016
  const subcommand = args[0];
38469
39017
  const end = args.indexOf("--");
@@ -38502,7 +39050,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
38502
39050
  };
38503
39051
  }
38504
39052
  for (let at2 = directory; ; at2 = dirname15(at2)) {
38505
- const marker = join25(at2, ".git");
39053
+ const marker = join26(at2, ".git");
38506
39054
  const link = tryStat(deps.lstat, marker);
38507
39055
  if (link !== void 0) {
38508
39056
  owners.push(link);
@@ -38510,7 +39058,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
38510
39058
  if (target2 !== void 0) {
38511
39059
  owners.push(target2);
38512
39060
  if (target2.isDirectory()) {
38513
- const config = tryStat(deps.stat, join25(marker, "config"));
39061
+ const config = tryStat(deps.stat, join26(marker, "config"));
38514
39062
  if (config !== void 0) owners.push(config);
38515
39063
  }
38516
39064
  }
@@ -38662,7 +39210,7 @@ var init_hardened_git = __esm({
38662
39210
 
38663
39211
  // ../../packages/tacho/src/contained/profile.ts
38664
39212
  import { createHash as createHash6 } from "node:crypto";
38665
- import { isAbsolute as isAbsolute4, join as join26, 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";
38666
39214
  function containerPath(workspace, path2) {
38667
39215
  const inside2 = relative3(workspace, path2);
38668
39216
  if (inside2 === "" || isAbsolute4(inside2) || inside2 === ".." || inside2.startsWith(`..${sep3}`))
@@ -38672,7 +39220,7 @@ function containerPath(workspace, path2) {
38672
39220
  return `/workspace/${inside2.split(sep3).join("/")}`;
38673
39221
  }
38674
39222
  function gitMountPairs(spec) {
38675
- if (!spec.gitMounts.includes(join26(spec.workspace, ".git")))
39223
+ if (!spec.gitMounts.includes(join27(spec.workspace, ".git")))
38676
39224
  throw new Error(
38677
39225
  "Contained execution must mount the repository's .git read-only"
38678
39226
  );
@@ -38829,7 +39377,7 @@ var init_profile = __esm({
38829
39377
  });
38830
39378
 
38831
39379
  // ../../packages/tacho/src/contained/launcher.ts
38832
- import { execFile as execFile3, spawn as spawn3 } from "node:child_process";
39380
+ import { execFile as execFile3, spawn as spawn5 } from "node:child_process";
38833
39381
  import { randomBytes as randomBytes8 } from "node:crypto";
38834
39382
  import {
38835
39383
  chmodSync as chmodSync7,
@@ -38839,7 +39387,7 @@ import {
38839
39387
  writeFileSync as writeFileSync2
38840
39388
  } from "node:fs";
38841
39389
  import { tmpdir } from "node:os";
38842
- import { isAbsolute as isAbsolute5, join as join27 } from "node:path";
39390
+ import { isAbsolute as isAbsolute5, join as join28 } from "node:path";
38843
39391
  import { promisify } from "node:util";
38844
39392
  function reason(error) {
38845
39393
  return error instanceof Error ? error.message : String(error);
@@ -38878,7 +39426,7 @@ async function launchContainedAgent(options) {
38878
39426
  if (digest6 === void 0)
38879
39427
  throw new Error("The operator's contained image is not installed");
38880
39428
  const sessionId = `contained-${randomBytes8(16).toString("hex")}`;
38881
- const directory = mkdtempSync(join27(tmpdir(), "oxagen-contained-"));
39429
+ const directory = mkdtempSync(join28(tmpdir(), "oxagen-contained-"));
38882
39430
  chmodSync7(directory, 448);
38883
39431
  let prepared;
38884
39432
  let containerCreated = false;
@@ -38899,7 +39447,7 @@ async function launchContainedAgent(options) {
38899
39447
  for (const [name2, contents] of entries2) {
38900
39448
  if (!/^[a-z0-9.-]+$/.test(name2))
38901
39449
  throw new Error("Invalid contained configuration filename");
38902
- writeFileSync2(join27(directory, name2), contents, {
39450
+ writeFileSync2(join28(directory, name2), contents, {
38903
39451
  mode: 256,
38904
39452
  flag: "wx"
38905
39453
  });
@@ -38930,7 +39478,7 @@ async function launchContainedAgent(options) {
38930
39478
  if (options.signal?.aborted)
38931
39479
  throw new Error("Contained run cancelled before start");
38932
39480
  exitCode = await new Promise((resolve25, reject) => {
38933
- const child = spawn3("docker", ["start", "--attach", name], {
39481
+ const child = spawn5("docker", ["start", "--attach", name], {
38934
39482
  stdio: ["ignore", "pipe", "pipe"]
38935
39483
  });
38936
39484
  const stop = () => {
@@ -39298,7 +39846,7 @@ var init_privilege = __esm({
39298
39846
 
39299
39847
  // ../../packages/tacho/src/contained/sandbox/launcher.ts
39300
39848
  import { randomBytes as randomBytes9 } from "node:crypto";
39301
- import { join as join28 } from "node:path";
39849
+ import { join as join29 } from "node:path";
39302
39850
  function shellQuote3(value) {
39303
39851
  return `'${value.replace(/'/g, `'\\''`)}'`;
39304
39852
  }
@@ -39388,7 +39936,7 @@ async function launchSandboxAgent(options) {
39388
39936
  { ...asAgent, cwd: SANDBOX_AGENT_HOME, timeoutMs: 15 * 6e4 }
39389
39937
  );
39390
39938
  for (const [name, contents] of Object.entries(claudeCodeUserState())) {
39391
- const path2 = join28(SANDBOX_AGENT_HOME, name);
39939
+ const path2 = join29(SANDBOX_AGENT_HOME, name);
39392
39940
  system.writeFile(path2, contents, 384);
39393
39941
  system.chown(path2, SANDBOX_AGENT_UID, SANDBOX_AGENT_GID);
39394
39942
  }
@@ -39512,7 +40060,7 @@ var init_request = __esm({
39512
40060
  });
39513
40061
 
39514
40062
  // ../../packages/tacho/src/contained/sandbox/system.ts
39515
- import { execFile as execFile4, spawn as spawn4 } from "node:child_process";
40063
+ import { execFile as execFile4, spawn as spawn6 } from "node:child_process";
39516
40064
  import {
39517
40065
  chmodSync as chmodSync8,
39518
40066
  chownSync as chownSync2,
@@ -39543,7 +40091,7 @@ var init_system = __esm({
39543
40091
  return { stdout: String(answer.stdout), stderr: String(answer.stderr) };
39544
40092
  },
39545
40093
  spawn: (file, args, options) => {
39546
- const child = spawn4(file, [...args], {
40094
+ const child = spawn6(file, [...args], {
39547
40095
  ...options.uid !== void 0 ? { uid: options.uid } : {},
39548
40096
  ...options.gid !== void 0 ? { gid: options.gid } : {},
39549
40097
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
@@ -39594,10 +40142,10 @@ var init_system = __esm({
39594
40142
  });
39595
40143
 
39596
40144
  // ../../packages/tacho/src/contained/runner.ts
39597
- import { join as join29 } from "node:path";
40145
+ import { join as join30 } from "node:path";
39598
40146
  function containedHookEnv(harness, workspace) {
39599
- const home = join29(workspace, ".oxagen-contained", "home");
39600
- return harness === "codex" ? { CODEX_HOME: join29(home, ".codex") } : { CLAUDE_CONFIG_DIR: join29(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") };
39601
40149
  }
39602
40150
  function containedLaunchEndpoint(host) {
39603
40151
  const ingest = new URL(host.endpoints.ingest);
@@ -39693,6 +40241,7 @@ function createContainedRunner(options) {
39693
40241
  throw new Error("Credential custody unavailable");
39694
40242
  return answer.body.token;
39695
40243
  },
40244
+ ...options.chatgptLogin !== void 0 ? { chatgptLogin: options.chatgptLogin } : {},
39696
40245
  model: (request3, response) => options.model.handle(request3, response),
39697
40246
  hook: options.hook,
39698
40247
  opening: route.opening,
@@ -39795,7 +40344,7 @@ function createContainedRunner(options) {
39795
40344
  sessionUuid2 = started.recorder.sessionUuid;
39796
40345
  active2.set(sessionUuid2, controller);
39797
40346
  const bridge = await startContainedBridge({
39798
- socketPath: join29(directory, "bridge.sock"),
40347
+ socketPath: join30(directory, "bridge.sock"),
39799
40348
  ...bridgeRoutes({
39800
40349
  sessionId,
39801
40350
  workspace,
@@ -39879,7 +40428,7 @@ function createContainedRunner(options) {
39879
40428
  const abort = () => controller.abort();
39880
40429
  signal?.addEventListener("abort", abort, { once: true });
39881
40430
  if (signal?.aborted) abort();
39882
- const env = { CLAUDE_CONFIG_DIR: join29(SANDBOX_AGENT_HOME, ".claude") };
40431
+ const env = { CLAUDE_CONFIG_DIR: join30(SANDBOX_AGENT_HOME, ".claude") };
39883
40432
  let sessionUuid2;
39884
40433
  let launchedSessionId;
39885
40434
  let opened = false;
@@ -40012,14 +40561,96 @@ var init_runner = __esm({
40012
40561
  }
40013
40562
  });
40014
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
+
40015
40646
  // ../../packages/tacho/src/host/repositories.ts
40016
40647
  import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
40017
- import { dirname as dirname17, join as join30, resolve as resolve11, sep as sep4 } from "node:path";
40648
+ import { dirname as dirname17, join as join31, resolve as resolve11, sep as sep4 } from "node:path";
40018
40649
  function repositoriesFile(home) {
40019
- return join30(home.tachoDir, "repositories.json");
40650
+ return join31(home.tachoDir, "repositories.json");
40020
40651
  }
40021
40652
  function workTreesDir(home) {
40022
- return join30(home.tachoDir, "work");
40653
+ return join31(home.tachoDir, "work");
40023
40654
  }
40024
40655
  function readIndex(home) {
40025
40656
  let raw;
@@ -40071,7 +40702,7 @@ function recordCheckout(home, canonical3, root, seenAt) {
40071
40702
  return true;
40072
40703
  }
40073
40704
  function mainCheckoutOf(root) {
40074
- const dotGit = join30(root, ".git");
40705
+ const dotGit = join31(root, ".git");
40075
40706
  try {
40076
40707
  if (!statSync10(dotGit).isFile()) return root;
40077
40708
  const line = readFileSync17(dotGit, "utf8").trim();
@@ -40081,13 +40712,13 @@ function mainCheckoutOf(root) {
40081
40712
  const worktrees = dirname17(gitdir);
40082
40713
  if (!worktrees.endsWith(`${sep4}.git${sep4}worktrees`)) return root;
40083
40714
  const clone4 = dirname17(dirname17(worktrees));
40084
- return existsSync18(join30(clone4, ".git")) ? clone4 : root;
40715
+ return existsSync18(join31(clone4, ".git")) ? clone4 : root;
40085
40716
  } catch {
40086
40717
  return root;
40087
40718
  }
40088
40719
  }
40089
40720
  function defaultIsCheckout(root) {
40090
- return existsSync18(join30(root, ".git"));
40721
+ return existsSync18(join31(root, ".git"));
40091
40722
  }
40092
40723
  function findCheckout(home, repository, host, deps = {}) {
40093
40724
  const isCheckout = deps.isCheckout ?? defaultIsCheckout;
@@ -40135,7 +40766,7 @@ var init_repositories = __esm({
40135
40766
 
40136
40767
  // ../../packages/tacho/src/collector/detector.ts
40137
40768
  import { promises as fs3 } from "node:fs";
40138
- import { basename as basename5, join as join31 } from "node:path";
40769
+ import { basename as basename5, join as join32 } from "node:path";
40139
40770
  async function eachLimited(items, limit, task) {
40140
40771
  let next = 0;
40141
40772
  const workers = Array.from(
@@ -40161,7 +40792,7 @@ async function scanProject(dir) {
40161
40792
  await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
40162
40793
  const match = SESSION_FILE.exec(name);
40163
40794
  if (match === null) return;
40164
- const path2 = join31(dir, name);
40795
+ const path2 = join32(dir, name);
40165
40796
  try {
40166
40797
  const st = await fs3.stat(path2);
40167
40798
  out.push({ sessionId: match[1], path: path2, mtimeMs: st.mtimeMs });
@@ -40202,7 +40833,7 @@ var init_detector = __esm({
40202
40833
  continue;
40203
40834
  }
40204
40835
  await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
40205
- const dir = join31(root, name);
40836
+ const dir = join32(root, name);
40206
40837
  try {
40207
40838
  const st = await fs3.stat(dir);
40208
40839
  if (st.isDirectory()) dirs.push({ dir, mtimeMs: st.mtimeMs });
@@ -40442,7 +41073,7 @@ var init_detector = __esm({
40442
41073
  transcript_path: transcript.path,
40443
41074
  transcript_mtime: new Date(transcript.mtimeMs).toISOString(),
40444
41075
  claude_pids: processes.map((p) => p.pid),
40445
- project: basename5(join31(transcript.path, ".."))
41076
+ project: basename5(join32(transcript.path, ".."))
40446
41077
  }
40447
41078
  })
40448
41079
  );
@@ -40812,9 +41443,9 @@ var init_git_facts = __esm({
40812
41443
  });
40813
41444
 
40814
41445
  // ../../packages/tacho/src/collector/work-order-autostart.ts
40815
- import { spawn as spawn5 } from "node:child_process";
41446
+ import { spawn as spawn7 } from "node:child_process";
40816
41447
  import { closeSync as closeSync7, existsSync as existsSync19, openSync as openSync7 } from "node:fs";
40817
- import { dirname as dirname18, join as join32 } from "node:path";
41448
+ import { dirname as dirname18, join as join33 } from "node:path";
40818
41449
  function asError(error) {
40819
41450
  return error instanceof Error ? error : new Error(String(error));
40820
41451
  }
@@ -40867,7 +41498,7 @@ function createWorkOrderAutostart(deps) {
40867
41498
  }
40868
41499
  async function abandon(id2) {
40869
41500
  forget(id2);
40870
- const worktree = join32(workTreesDir(paths), id2);
41501
+ const worktree = join33(workTreesDir(paths), id2);
40871
41502
  if (!existsSync19(worktree)) return;
40872
41503
  const [common, branch, head2] = await Promise.all([
40873
41504
  git6(worktree, ["rev-parse", "--path-format=absolute", "--git-common-dir"]),
@@ -40905,7 +41536,7 @@ function createWorkOrderAutostart(deps) {
40905
41536
  return waitMs;
40906
41537
  }
40907
41538
  async function prepareWorktree(root, id2, itemNumber) {
40908
- const worktree = join32(workTreesDir(paths), id2);
41539
+ const worktree = join33(workTreesDir(paths), id2);
40909
41540
  const logPath = `${worktree}.log`;
40910
41541
  if (existsSync19(worktree)) {
40911
41542
  const [branch2, head2] = await Promise.all([
@@ -41188,7 +41819,7 @@ var init_work_order_autostart = __esm({
41188
41819
  };
41189
41820
  let settled2 = false;
41190
41821
  try {
41191
- const child = spawn5(command2, args, {
41822
+ const child = spawn7(command2, args, {
41192
41823
  cwd: options.cwd,
41193
41824
  env: options.env,
41194
41825
  // Its own process group, so a daemon restart does not stop the run.
@@ -41275,7 +41906,7 @@ import {
41275
41906
  rm,
41276
41907
  unlink
41277
41908
  } from "node:fs/promises";
41278
- import { dirname as dirname19, join as join33 } from "node:path";
41909
+ import { dirname as dirname19, join as join34 } from "node:path";
41279
41910
  function byPath(a, b3) {
41280
41911
  return a.path < b3.path ? -1 : a.path > b3.path ? 1 : 0;
41281
41912
  }
@@ -41318,7 +41949,7 @@ async function contentHash(absolutePath, kind, keep) {
41318
41949
  if (keep !== void 0) {
41319
41950
  try {
41320
41951
  await mkdir(keep.dir, { recursive: true, mode: 448 });
41321
- const temp2 = join33(keep.dir, `.partial-${randomUUID3()}`);
41952
+ const temp2 = join34(keep.dir, `.partial-${randomUUID3()}`);
41322
41953
  copy = {
41323
41954
  temp: temp2,
41324
41955
  handle: await open3(temp2, "wx", keep.executable ? 448 : 384)
@@ -41362,7 +41993,7 @@ async function settleCopy(temp, name) {
41362
41993
  if (temp === void 0) return;
41363
41994
  if (name !== void 0)
41364
41995
  try {
41365
- await rename(temp, join33(dirname19(temp), name));
41996
+ await rename(temp, join34(dirname19(temp), name));
41366
41997
  return;
41367
41998
  } catch {
41368
41999
  }
@@ -41378,7 +42009,7 @@ async function copiedBytes(dir) {
41378
42009
  let total2 = 0;
41379
42010
  for (const name of names2) {
41380
42011
  try {
41381
- total2 += (await lstat(join33(dir, name))).size;
42012
+ total2 += (await lstat(join34(dir, name))).size;
41382
42013
  } catch {
41383
42014
  }
41384
42015
  }
@@ -41394,7 +42025,7 @@ async function removeCopiesOutside(root, live) {
41394
42025
  const keep = new Set(live());
41395
42026
  await Promise.all(
41396
42027
  names2.filter((name) => !keep.has(name)).map(
41397
- (name) => rm(join33(root, name), { recursive: true, force: true }).catch(
42028
+ (name) => rm(join34(root, name), { recursive: true, force: true }).catch(
41398
42029
  () => void 0
41399
42030
  )
41400
42031
  )
@@ -41501,7 +42132,7 @@ async function contributionOf(exec4, cwd, root, repoRelative, entry2, copies) {
41501
42132
  let copy = null;
41502
42133
  if (entry2 !== null) {
41503
42134
  if (copies === void 0 || !COPY_NAME.test(entry2[0])) return void 0;
41504
- copy = join33(copies, entry2[0]);
42135
+ copy = join34(copies, entry2[0]);
41505
42136
  const kept = await statOf(copy);
41506
42137
  if (kept === void 0 || kept === "absent" || kept.kind !== "file")
41507
42138
  return void 0;
@@ -41903,7 +42534,7 @@ var init_session_changes = __esm({
41903
42534
  });
41904
42535
 
41905
42536
  // ../../packages/tacho/src/collector/session-commits.ts
41906
- import { join as join34 } from "node:path";
42537
+ import { join as join35 } from "node:path";
41907
42538
  function parseMeta(stdout) {
41908
42539
  const out = [];
41909
42540
  for (const record of stdout.split("\0")) {
@@ -42028,7 +42659,7 @@ async function readSessionCommits(exec4, cwd, input) {
42028
42659
  const test = input.tests[commit.sha];
42029
42660
  const toolUseId = callAt(times.get(commit.sha), calls);
42030
42661
  const written2 = input.written;
42031
- const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(join34(root, file.path)));
42662
+ const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(join35(root, file.path)));
42032
42663
  return {
42033
42664
  sha: commit.sha,
42034
42665
  parent_shas: commit.parents.slice(0, MAX_COMMIT_PARENTS),
@@ -42076,7 +42707,7 @@ var init_session_commits = __esm({
42076
42707
 
42077
42708
  // ../../packages/tacho/src/collector/worktree-snapshot.ts
42078
42709
  import { lstat as lstat2 } from "node:fs/promises";
42079
- import { join as join35 } from "node:path";
42710
+ import { join as join36 } from "node:path";
42080
42711
  function headerPath(prefix, path2) {
42081
42712
  const name = `${prefix}${path2}`;
42082
42713
  let quoted3 = false;
@@ -42160,7 +42791,7 @@ async function worktreeFingerprint(status2, root) {
42160
42791
  const path2 = statusPath(record);
42161
42792
  if (path2 === null) return "";
42162
42793
  try {
42163
- const info = await lstat2(join35(root, path2));
42794
+ const info = await lstat2(join36(root, path2));
42164
42795
  return `${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
42165
42796
  } catch {
42166
42797
  return "absent";
@@ -42268,7 +42899,7 @@ async function readWorktreeSnapshot(exec4, cwd, baseline, measured) {
42268
42899
  }
42269
42900
  let present;
42270
42901
  try {
42271
- present = (await lstat2(join35(root, path2))).isFile();
42902
+ present = (await lstat2(join36(root, path2))).isFile();
42272
42903
  } catch {
42273
42904
  present = false;
42274
42905
  }
@@ -42371,10 +43002,10 @@ var init_worktree_snapshot = __esm({
42371
43002
  });
42372
43003
 
42373
43004
  // ../../packages/tacho/src/collector/git-lane.ts
42374
- import { join as join36 } from "node:path";
43005
+ import { join as join37 } from "node:path";
42375
43006
  function copiesDirOf(root, session) {
42376
43007
  const uuid = session.recorder.sessionUuid;
42377
- return /^[0-9a-f-]{36}$/i.test(uuid) ? join36(root, uuid) : void 0;
43008
+ return /^[0-9a-f-]{36}$/i.test(uuid) ? join37(root, uuid) : void 0;
42378
43009
  }
42379
43010
  function holdsBaseline(session) {
42380
43011
  return session.baselineCommit !== void 0 || Object.keys(session.baselines ?? {}).length > 0;
@@ -45742,7 +46373,7 @@ var init_hook_queues = __esm({
45742
46373
 
45743
46374
  // ../../packages/tacho/src/collector/memory-capture/memory-reader.ts
45744
46375
  import { createRequire as createRequire2 } from "node:module";
45745
- import { isAbsolute as isAbsolute8, join as join37, sep as sep6 } from "node:path";
46376
+ import { isAbsolute as isAbsolute8, join as join38, sep as sep6 } from "node:path";
45746
46377
  function claudeCodeLocation(projectsDir, memoryDir) {
45747
46378
  return {
45748
46379
  harness: "claude-code",
@@ -45754,13 +46385,13 @@ function claudeCodeLocation(projectsDir, memoryDir) {
45754
46385
  }
45755
46386
  function claudeCodeMemoryLocations(configDir, projectDirs = []) {
45756
46387
  const locations = [
45757
- claudeCodeLocation(() => join37(configDir, "projects"), "memory"),
45758
- claudeCodeLocation(() => join37(configDir, "agent-memory"), "")
46388
+ claudeCodeLocation(() => join38(configDir, "projects"), "memory"),
46389
+ claudeCodeLocation(() => join38(configDir, "agent-memory"), "")
45759
46390
  ];
45760
46391
  for (const dir of projectDirs.slice(0, MEMORY_PROJECT_DIRS_MAX))
45761
46392
  locations.push(
45762
- claudeCodeLocation(() => join37(dir, ".claude", "agent-memory"), ""),
45763
- claudeCodeLocation(() => join37(dir, ".claude", "agent-memory-local"), "")
46393
+ claudeCodeLocation(() => join38(dir, ".claude", "agent-memory"), ""),
46394
+ claudeCodeLocation(() => join38(dir, ".claude", "agent-memory-local"), "")
45764
46395
  );
45765
46396
  return locations;
45766
46397
  }
@@ -45945,13 +46576,13 @@ function createMemoryReader(deps) {
45945
46576
  const projects = await list2(top);
45946
46577
  let complete = projects.complete;
45947
46578
  for (const project2 of projects.names) {
45948
- const dir = location.memoryDir === "" ? join37(top, project2) : join37(top, project2, location.memoryDir);
46579
+ const dir = location.memoryDir === "" ? join38(top, project2) : join38(top, project2, location.memoryDir);
45949
46580
  const files2 = await list2(dir);
45950
46581
  if (!files2.complete) complete = false;
45951
46582
  for (const name of files2.names) {
45952
46583
  if (!name.endsWith(location.extension)) continue;
45953
46584
  if (location.skip.includes(name)) continue;
45954
- const path2 = join37(dir, name);
46585
+ const path2 = join38(dir, name);
45955
46586
  const file = await readAt2(location.harness, path2);
45956
46587
  if (file.kind === "absent" || file.kind === "empty") continue;
45957
46588
  paths.push(path2);
@@ -46037,8 +46668,8 @@ var init_memory_reader = __esm({
46037
46668
  MEMORY_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
46038
46669
  MEMORY_PROJECT_DIRS_MAX = 200;
46039
46670
  HARNESS_MEMORY_LOCATIONS = [
46040
- claudeCodeLocation((home) => join37(home, ".claude", "projects"), "memory"),
46041
- claudeCodeLocation((home) => join37(home, ".claude", "agent-memory"), "")
46671
+ claudeCodeLocation((home) => join38(home, ".claude", "projects"), "memory"),
46672
+ claudeCodeLocation((home) => join38(home, ".claude", "agent-memory"), "")
46042
46673
  ];
46043
46674
  FRONTMATTER = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
46044
46675
  FRONTMATTER_LINE = /^([ \t]*)([A-Za-z_][\w-]*)[ \t]*:[ \t]*(.*)$/;
@@ -46530,7 +47161,7 @@ var init_memory_recall = __esm({
46530
47161
  });
46531
47162
 
46532
47163
  // ../../packages/tacho/src/collector/memory-capture/memory-uses.ts
46533
- import { basename as basename6, dirname as dirname21, isAbsolute as isAbsolute9, join as join38, 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";
46534
47165
  function memoryFileOf(path2, locations, home) {
46535
47166
  if (!isAbsolute9(path2)) return void 0;
46536
47167
  const file = resolve14(path2);
@@ -46548,7 +47179,7 @@ function memoryFileOf(path2, locations, home) {
46548
47179
  return void 0;
46549
47180
  }
46550
47181
  function pathOf(word, cwd, home) {
46551
- const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join38(home, word.slice(word.indexOf("/") + 1)) : word;
47182
+ const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join39(home, word.slice(word.indexOf("/") + 1)) : word;
46552
47183
  if (isAbsolute9(expanded)) return expanded;
46553
47184
  if (cwd !== void 0 && isAbsolute9(cwd)) return resolve14(cwd, expanded);
46554
47185
  return void 0;
@@ -46804,7 +47435,7 @@ ${use.path}`;
46804
47435
  });
46805
47436
 
46806
47437
  // ../../packages/tacho/src/collector/memory-capture/stella-store.ts
46807
- import { dirname as dirname22, isAbsolute as isAbsolute10, join as join39, resolve as resolve15 } from "node:path";
47438
+ import { dirname as dirname22, isAbsolute as isAbsolute10, join as join40, resolve as resolve15 } from "node:path";
46808
47439
  function queryStellaStore(path2, afterSeq, limit) {
46809
47440
  const db = new (nodeSqlite()).DatabaseSync(path2, { readOnly: true });
46810
47441
  try {
@@ -46894,7 +47525,7 @@ async function stellaStoreOf(dir, exists) {
46894
47525
  if (!isAbsolute10(dir)) return void 0;
46895
47526
  let at2 = resolve15(dir);
46896
47527
  for (let depth = 0; depth < WORKSPACE_SEARCH_DEPTH; depth += 1) {
46897
- const candidate = join39(at2, STELLA_CONTEXT_DB);
47528
+ const candidate = join40(at2, STELLA_CONTEXT_DB);
46898
47529
  if (await exists(candidate)) return candidate;
46899
47530
  const up = dirname22(at2);
46900
47531
  if (up === at2) return void 0;
@@ -46907,7 +47538,7 @@ var init_stella_store = __esm({
46907
47538
  "../../packages/tacho/src/collector/memory-capture/stella-store.ts"() {
46908
47539
  "use strict";
46909
47540
  init_memory_reader();
46910
- STELLA_CONTEXT_DB = join39(".stella", "private", "context.db");
47541
+ STELLA_CONTEXT_DB = join40(".stella", "private", "context.db");
46911
47542
  STELLA_MEMORIES_VIEW = "export_memories_v1";
46912
47543
  STELLA_USES_VIEW = "export_memory_uses_v1";
46913
47544
  STELLA_USES_PER_READ = 5e3;
@@ -47569,7 +48200,7 @@ var init_cloud_link = __esm({
47569
48200
 
47570
48201
  // ../../packages/tacho/src/collector/local-servers/digest.ts
47571
48202
  import { createHash as createHash10 } from "node:crypto";
47572
- import { join as join40 } from "node:path";
48203
+ import { join as join41 } from "node:path";
47573
48204
  function rememberBounded(map, key, value) {
47574
48205
  if (!map.has(key) && map.size >= MAX_REMEMBERED_DIGESTS) {
47575
48206
  const oldest = map.keys().next();
@@ -47606,7 +48237,7 @@ function npmNameSegment(name) {
47606
48237
  return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : encodeURIComponent(name);
47607
48238
  }
47608
48239
  async function whichOnPath(command2, pathValue, search) {
47609
- const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join40(dir, command2));
48240
+ const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join41(dir, command2));
47610
48241
  for (const candidate of candidates) {
47611
48242
  for (const extension of search.extensions) {
47612
48243
  if (await search.isExecutable(candidate + extension))
@@ -48609,7 +49240,7 @@ var init_local_servers = __esm({
48609
49240
 
48610
49241
  // ../../packages/tacho/src/collector/local-servers/machine.ts
48611
49242
  import { spawn as nodeSpawn } from "node:child_process";
48612
- 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";
48613
49244
  function machineParked(host) {
48614
49245
  if (host.revoked_at !== null || host.host_status === "revoked")
48615
49246
  return "the enrollment is revoked";
@@ -48634,7 +49265,7 @@ function createMachineLink(host, fetch2, env) {
48634
49265
  async function isExecutable(path2) {
48635
49266
  try {
48636
49267
  if (!(await stat(path2)).isFile()) return false;
48637
- await access(path2, constants3.X_OK);
49268
+ await access(path2, constants4.X_OK);
48638
49269
  return true;
48639
49270
  } catch {
48640
49271
  return false;
@@ -48902,9 +49533,9 @@ import {
48902
49533
  writeFile
48903
49534
  } from "node:fs/promises";
48904
49535
  import { homedir as homedir5 } from "node:os";
48905
- import { dirname as dirname23, join as join41 } from "node:path";
49536
+ import { dirname as dirname23, join as join42 } from "node:path";
48906
49537
  function stagingFolder(root, name) {
48907
- return join41(root, `.${name}${STAGING_SUFFIX}`);
49538
+ return join42(root, `.${name}${STAGING_SUFFIX}`);
48908
49539
  }
48909
49540
  function isStagingName(name) {
48910
49541
  return name.startsWith(".") && name.endsWith(STAGING_SUFFIX);
@@ -48947,17 +49578,17 @@ function envDir(env, key) {
48947
49578
  function skillsRoot(harness, env = process.env, home = homedir5()) {
48948
49579
  switch (harness) {
48949
49580
  case "claude-code":
48950
- return join41(
48951
- envDir(env, "CLAUDE_CONFIG_DIR") ?? join41(home, ".claude"),
49581
+ return join42(
49582
+ envDir(env, "CLAUDE_CONFIG_DIR") ?? join42(home, ".claude"),
48952
49583
  "skills"
48953
49584
  );
48954
49585
  case "codex": {
48955
49586
  const codexHome = envDir(env, "CODEX_HOME");
48956
- return codexHome === null ? join41(home, ".agents", "skills") : join41(codexHome, "skills");
49587
+ return codexHome === null ? join42(home, ".agents", "skills") : join42(codexHome, "skills");
48957
49588
  }
48958
49589
  case "stella":
48959
- return join41(
48960
- envDir(env, "STELLA_HOME") ?? join41(home, ".stella"),
49590
+ return join42(
49591
+ envDir(env, "STELLA_HOME") ?? join42(home, ".stella"),
48961
49592
  "skills"
48962
49593
  );
48963
49594
  case "cursor":
@@ -49018,7 +49649,7 @@ async function readMarker(folder) {
49018
49649
  if (!entries2.includes(SESSION_MARKER)) return { state: "foreign" };
49019
49650
  try {
49020
49651
  const parsed2 = JSON.parse(
49021
- await readFile2(join41(folder, SESSION_MARKER), "utf8")
49652
+ await readFile2(join42(folder, SESSION_MARKER), "utf8")
49022
49653
  );
49023
49654
  return isMarker(parsed2) ? { state: "ours", marker: parsed2 } : { state: "foreign" };
49024
49655
  } catch {
@@ -49035,22 +49666,22 @@ function liveSessions(marker, now) {
49035
49666
  return live;
49036
49667
  }
49037
49668
  async function writeMarker(folder, marker) {
49038
- const temp = join41(folder, MARKER_TEMP);
49669
+ const temp = join42(folder, MARKER_TEMP);
49039
49670
  await writeFile(temp, `${JSON.stringify(marker, null, 2)}
49040
49671
  `);
49041
- await rename2(temp, join41(folder, SESSION_MARKER));
49672
+ await rename2(temp, join42(folder, SESSION_MARKER));
49042
49673
  }
49043
49674
  async function writeSkill(root, skill, marker) {
49044
- const folder = join41(root, skill.name);
49675
+ const folder = join42(root, skill.name);
49045
49676
  const staging = stagingFolder(root, skill.name);
49046
49677
  await rm2(folder, { recursive: true, force: true });
49047
49678
  await rm2(staging, { recursive: true, force: true });
49048
49679
  await mkdir2(staging);
49049
49680
  await writeMarker(staging, { digest: UNFINISHED_DIGEST, sessions: {} });
49050
49681
  await rename2(staging, folder);
49051
- await writeFile(join41(folder, "SKILL.md"), skillMarkdown(skill));
49682
+ await writeFile(join42(folder, "SKILL.md"), skillMarkdown(skill));
49052
49683
  for (const file of skill.files) {
49053
- const target2 = join41(folder, file.path);
49684
+ const target2 = join42(folder, file.path);
49054
49685
  await mkdir2(dirname23(target2), { recursive: true });
49055
49686
  await writeFile(target2, file.content);
49056
49687
  }
@@ -49079,7 +49710,7 @@ async function placeSkills(root, sessionId, skills, now = /* @__PURE__ */ new Da
49079
49710
  result2.warnings.push(refusal);
49080
49711
  continue;
49081
49712
  }
49082
- const folder = join41(root, skill.name);
49713
+ const folder = join42(root, skill.name);
49083
49714
  if (taken.has(skill.name)) {
49084
49715
  result2.skipped.push(skill.name);
49085
49716
  result2.warnings.push(
@@ -49144,7 +49775,7 @@ async function removeSkills(root, sessionId, now = /* @__PURE__ */ new Date()) {
49144
49775
  throw error;
49145
49776
  }
49146
49777
  for (const name of names2.sort(compareText)) {
49147
- const folder = join41(root, name);
49778
+ const folder = join42(root, name);
49148
49779
  if (isStagingName(name)) {
49149
49780
  await rm2(folder, { recursive: true, force: true });
49150
49781
  continue;
@@ -50066,13 +50697,27 @@ function connectionTokens(raw) {
50066
50697
  }
50067
50698
  return named;
50068
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
+ }
50069
50705
  function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, attach) {
50070
50706
  const named = connectionTokens(raw);
50071
50707
  const out = ["Host", host];
50708
+ const oauth = attach?.kind === "oauth";
50709
+ let beta;
50072
50710
  for (let i2 = 0; i2 + 1 < raw.length; i2 += 2) {
50073
50711
  const name = raw[i2];
50074
50712
  const lower = name.toLowerCase();
50075
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
+ }
50076
50721
  if (lower === "host" || lower === "content-length") continue;
50077
50722
  if (lower === "accept-encoding" || lower === "expect") continue;
50078
50723
  if (lower === TACHO_MODEL_SESSION_HEADER) continue;
@@ -50084,6 +50729,9 @@ function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, atta
50084
50729
  if (attach !== void 0) {
50085
50730
  if (attach.kind === "api_key") out.push("X-Api-Key", attach.secret);
50086
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);
50087
50735
  }
50088
50736
  out.push("Accept-Encoding", "identity");
50089
50737
  if (bodyLength > 0 || raw.some((h, i2) => i2 % 2 === 0 && /^content-length$/i.test(h)))
@@ -50117,7 +50765,7 @@ function providerError(provider, status2, code, message) {
50117
50765
  };
50118
50766
  return { status: status2, body: JSON.stringify(body2) };
50119
50767
  }
50120
- 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;
50121
50769
  var init_model_routes = __esm({
50122
50770
  "../../packages/tacho/src/collector/model-routes.ts"() {
50123
50771
  "use strict";
@@ -50146,6 +50794,8 @@ var init_model_routes = __esm({
50146
50794
  "upgrade"
50147
50795
  ]);
50148
50796
  CREDENTIAL_HEADERS = ["authorization", "x-api-key"];
50797
+ CHATGPT_ACCOUNT_HEADER = "chatgpt-account-id";
50798
+ ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
50149
50799
  }
50150
50800
  });
50151
50801
 
@@ -50877,7 +51527,7 @@ function createModelProxy(deps) {
50877
51527
  function resolveCredential(req, route) {
50878
51528
  const broker = deps.credentials;
50879
51529
  const presented = presentedCredential(req);
50880
- 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");
50881
51531
  if (broker === void 0 || !brokered) {
50882
51532
  if (presented !== void 0 && looksLikeRunToken(presented.value)) {
50883
51533
  const claims = peekRunTokenClaims(presented.value);
@@ -50932,6 +51582,27 @@ function createModelProxy(deps) {
50932
51582
  }
50933
51583
  };
50934
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
+ }
50935
51606
  return {
50936
51607
  basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
50937
51608
  claims: verdict.claims,
@@ -51622,6 +52293,15 @@ function createModelProxy(deps) {
51622
52293
  if (result2.path !== path2 && result2.path.startsWith("/"))
51623
52294
  path2 = result2.path;
51624
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
+ }
51625
52305
  const requestModel = askedModel;
51626
52306
  const requestEffort = requestEffortOf(injected ? injectedJson : json2());
51627
52307
  const sent = injected ? body2 : readable();
@@ -52186,6 +52866,7 @@ var init_model_proxy = __esm({
52186
52866
  init_frame_body();
52187
52867
  init_replay_grade();
52188
52868
  init_bundle();
52869
+ init_model_credential();
52189
52870
  init_run_token();
52190
52871
  init_wire();
52191
52872
  init_cache_keep_alive();
@@ -52921,12 +53602,12 @@ var init_server = __esm({
52921
53602
 
52922
53603
  // ../../packages/tacho/src/collector/refused.ts
52923
53604
  import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync12 } from "node:fs";
52924
- import { join as join42 } from "node:path";
53605
+ import { join as join43 } from "node:path";
52925
53606
  function refusalRetryable(refusal) {
52926
53607
  return refusal === "malformed" || refusal === "parked";
52927
53608
  }
52928
53609
  function refusedRecordPath(dir, event) {
52929
- return join42(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`);
52930
53611
  }
52931
53612
  function newRefusedRecord(event, reason3, refusal, at2, body2) {
52932
53613
  const retryable = refusalRetryable(refusal);
@@ -53002,7 +53683,7 @@ function scanRefused(dir, now, limit = MAX_REFUSED_RETRIES_PER_PASS) {
53002
53683
  };
53003
53684
  const due = [];
53004
53685
  for (const name of names2) {
53005
- const entry2 = readRefusedEntry(join42(dir, name));
53686
+ const entry2 = readRefusedEntry(join43(dir, name));
53006
53687
  if (entry2 === void 0) continue;
53007
53688
  summary.total += 1;
53008
53689
  if (summary.lastRefusedAt === void 0 || entry2.refusedAt > summary.lastRefusedAt)
@@ -53039,7 +53720,7 @@ var init_refused = __esm({
53039
53720
 
53040
53721
  // ../../packages/tacho/src/collector/spool.ts
53041
53722
  import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync12 } from "node:fs";
53042
- import { join as join43 } from "node:path";
53723
+ import { join as join44 } from "node:path";
53043
53724
  function sessionOwnedElsewhere(error) {
53044
53725
  return error.status === 403 && error.body.includes(SESSION_OWNED_ELSEWHERE);
53045
53726
  }
@@ -53272,7 +53953,7 @@ var init_spool = __esm({
53272
53953
  }
53273
53954
  /** Add a body to a quarantine record that was written without one. */
53274
53955
  stashBodyBeside(event, body2) {
53275
- const path2 = join43(
53956
+ const path2 = join44(
53276
53957
  this.options.quarantineDir,
53277
53958
  `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`
53278
53959
  );
@@ -53928,7 +54609,7 @@ import {
53928
54609
  } from "node:fs";
53929
54610
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
53930
54611
  import { homedir as homedir7, hostname as osHostname2 } from "node:os";
53931
- import { dirname as dirname24, join as join44 } from "node:path";
54612
+ import { dirname as dirname24, join as join45 } from "node:path";
53932
54613
  import { spawnSync as spawnSync7 } from "node:child_process";
53933
54614
  function cloudGatewayUpstreams(origin = CLOUD_GATEWAY_ORIGIN) {
53934
54615
  const base2 = new URL(origin).origin;
@@ -54672,7 +55353,7 @@ async function initializeDaemon(options, server, port, ready) {
54672
55353
  controlReachable: lastControlAt !== void 0 && now() - lastControlAt < 2 * timers.bundleRefreshMs
54673
55354
  };
54674
55355
  }
54675
- const bodyPurgeOwedPath = join44(paths.wal, "body-purge-owed");
55356
+ const bodyPurgeOwedPath = join45(paths.wal, "body-purge-owed");
54676
55357
  function markBodyPurgeOwed(retention, dropped) {
54677
55358
  try {
54678
55359
  ensureDir(paths.wal);
@@ -54949,7 +55630,7 @@ async function initializeDaemon(options, server, port, ready) {
54949
55630
  }
54950
55631
  void workOrderAutostart.reconcile();
54951
55632
  }
54952
- const lastIngestFile = join44(paths.dir, "last-ingest.json");
55633
+ const lastIngestFile = join45(paths.dir, "last-ingest.json");
54953
55634
  const shipper = new Shipper({
54954
55635
  wal,
54955
55636
  client,
@@ -55471,7 +56152,7 @@ async function initializeDaemon(options, server, port, ready) {
55471
56152
  async function readSpoolFile(name) {
55472
56153
  try {
55473
56154
  const file = parseSpoolFile(
55474
- await readFile3(join44(paths.spool, name), "utf8")
56155
+ await readFile3(join45(paths.spool, name), "utf8")
55475
56156
  );
55476
56157
  spoolFacts.set(name, spoolFactsOf(file));
55477
56158
  return file;
@@ -55660,7 +56341,7 @@ async function initializeDaemon(options, server, port, ready) {
55660
56341
  function setAsideConflictingTerminal(uuid, pending, conflict) {
55661
56342
  const name = `${uuid}-terminal-conflict-${String(conflict.seq).padStart(8, "0")}.json`;
55662
56343
  writeSensitiveFileAtomic(
55663
- join44(paths.quarantine, name),
56344
+ join45(paths.quarantine, name),
55664
56345
  JSON.stringify(
55665
56346
  {
55666
56347
  reason: conflict.message,
@@ -55740,8 +56421,8 @@ async function initializeDaemon(options, server, port, ready) {
55740
56421
  function moveToFailedSpool(path2, name, reason3) {
55741
56422
  log(`spool file ${name} moved to spool/failed/: ${reason3}`);
55742
56423
  try {
55743
- ensureDir(join44(paths.spool, "failed"));
55744
- renameSync4(path2, join44(paths.spool, "failed", name));
56424
+ ensureDir(join45(paths.spool, "failed"));
56425
+ renameSync4(path2, join45(paths.spool, "failed", name));
55745
56426
  } catch (error) {
55746
56427
  log(
55747
56428
  `spool file ${name} could not be moved to spool/failed/: ${error instanceof Error ? error.message : String(error)}`
@@ -55794,7 +56475,7 @@ async function initializeDaemon(options, server, port, ready) {
55794
56475
  continue;
55795
56476
  }
55796
56477
  reads += 1;
55797
- const path2 = join44(paths.spool, name);
56478
+ const path2 = join45(paths.spool, name);
55798
56479
  let file;
55799
56480
  try {
55800
56481
  file = await readSpoolFile(name);
@@ -56183,6 +56864,13 @@ async function initializeDaemon(options, server, port, ready) {
56183
56864
  ...options.pauseHold,
56184
56865
  credentials: {
56185
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
+ },
56186
56874
  custody: (provider) => {
56187
56875
  try {
56188
56876
  return credentialStore.read(provider);
@@ -56247,6 +56935,13 @@ async function initializeDaemon(options, server, port, ready) {
56247
56935
  record,
56248
56936
  model: modelProxy,
56249
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
+ },
56250
56945
  issueCredential: (harness) => issueRunToken(
56251
56946
  { harness },
56252
56947
  {
@@ -56621,7 +57316,7 @@ async function initializeDaemon(options, server, port, ready) {
56621
57316
  let removed = 0;
56622
57317
  for (const name of readdirSync13(paths.quarantine)) {
56623
57318
  if (!name.endsWith(".json")) continue;
56624
- const file = join44(paths.quarantine, name);
57319
+ const file = join45(paths.quarantine, name);
56625
57320
  try {
56626
57321
  const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync13(file).mtimeMs;
56627
57322
  if (at2 - refusedAt < retainMs) continue;
@@ -56877,8 +57572,29 @@ async function initializeDaemon(options, server, port, ready) {
56877
57572
  });
56878
57573
  }
56879
57574
  let lastStaticRenewalAt = 0;
57575
+ let lastLoginRefreshAt = 0;
56880
57576
  async function renewStaticTokens() {
56881
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
+ }
56882
57598
  if (now() - lastStaticRenewalAt < STATIC_TOKEN_RENEWAL_CHECK_MS) return;
56883
57599
  lastStaticRenewalAt = now();
56884
57600
  if (host.host_status !== "active" || !inCustody("openai")) return;
@@ -56951,7 +57667,7 @@ async function initializeDaemon(options, server, port, ready) {
56951
57667
  let memoryScan;
56952
57668
  if (options.listen ?? true) {
56953
57669
  const codexMemories = createCodexMemoryStore({
56954
- path: join44(dirname24(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
57670
+ path: join45(dirname24(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
56955
57671
  stat: (path2) => stat2(path2),
56956
57672
  log
56957
57673
  });
@@ -57096,7 +57812,7 @@ async function initializeDaemon(options, server, port, ready) {
57096
57812
  }
57097
57813
  };
57098
57814
  }
57099
- 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;
57100
57816
  var init_daemon = __esm({
57101
57817
  "../../packages/tacho/src/collector/daemon.ts"() {
57102
57818
  "use strict";
@@ -57118,6 +57834,7 @@ var init_daemon = __esm({
57118
57834
  init_claude_code_mcp_writer();
57119
57835
  init_model_base_url();
57120
57836
  init_model_credential();
57837
+ init_chatgpt_refresh();
57121
57838
  init_run_token();
57122
57839
  init_process_scan();
57123
57840
  init_retention();
@@ -57190,6 +57907,7 @@ var init_daemon = __esm({
57190
57907
  hookBundleWaitMs: 2e3
57191
57908
  };
57192
57909
  STATIC_TOKEN_RENEWAL_CHECK_MS = 60 * 6e4;
57910
+ CHATGPT_LOGIN_CHECK_MS = 10 * 6e4;
57193
57911
  MEMORY_SCAN_MS = 5 * 6e4;
57194
57912
  CLOUD_GATEWAY_ORIGIN = "https://api.oxagen.sh";
57195
57913
  CLOUD_GATEWAY_BASE_PATH = "/v1/gateway";
@@ -57454,7 +58172,7 @@ var init_run2 = __esm({
57454
58172
  // ../../packages/tacho/src/arp/bundle.ts
57455
58173
  import {
57456
58174
  closeSync as closeSync8,
57457
- constants as constants4,
58175
+ constants as constants5,
57458
58176
  fstatSync as fstatSync2,
57459
58177
  lstatSync as lstatSync8,
57460
58178
  mkdirSync as mkdirSync6,
@@ -57463,7 +58181,7 @@ import {
57463
58181
  realpathSync as realpathSync5,
57464
58182
  writeFileSync as writeFileSync4
57465
58183
  } from "node:fs";
57466
- import { dirname as dirname25, join as join45, resolve as resolve17 } from "node:path";
58184
+ import { dirname as dirname25, join as join46, resolve as resolve17 } from "node:path";
57467
58185
  function bundlePath(root, relative11, create2 = false) {
57468
58186
  const absolute = resolve17(root);
57469
58187
  if (create2) mkdirSync6(absolute, { recursive: true, mode: 448 });
@@ -57474,7 +58192,7 @@ function bundlePath(root, relative11, create2 = false) {
57474
58192
  let current = base2;
57475
58193
  const parts = relative11.split("/");
57476
58194
  for (let i2 = 0; i2 < parts.length; i2++) {
57477
- current = join45(current, parts[i2]);
58195
+ current = join46(current, parts[i2]);
57478
58196
  try {
57479
58197
  const stat7 = lstatSync8(current);
57480
58198
  if (stat7.isSymbolicLink() || i2 < parts.length - 1 && !stat7.isDirectory()) {
@@ -57495,7 +58213,7 @@ function bundlePath(root, relative11, create2 = false) {
57495
58213
  }
57496
58214
  function readBounded(root, relative11, limit = ARP_MAX_BLOB_BYTES) {
57497
58215
  const path2 = bundlePath(root, relative11);
57498
- const fd2 = openSync8(path2, constants4.O_RDONLY | constants4.O_NOFOLLOW);
58216
+ const fd2 = openSync8(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
57499
58217
  try {
57500
58218
  const stat7 = fstatSync2(fd2);
57501
58219
  if (!stat7.isFile() || stat7.size > limit)
@@ -57520,7 +58238,7 @@ function writeNew(root, relative11, bytes) {
57520
58238
  const path2 = bundlePath(root, relative11, true);
57521
58239
  const fd2 = openSync8(
57522
58240
  path2,
57523
- 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,
57524
58242
  384
57525
58243
  );
57526
58244
  try {
@@ -58425,7 +59143,7 @@ var init_oracles = __esm({
58425
59143
  import { randomUUID as randomUUID4 } from "node:crypto";
58426
59144
  import {
58427
59145
  closeSync as closeSync9,
58428
- constants as constants5,
59146
+ constants as constants6,
58429
59147
  fstatSync as fstatSync3,
58430
59148
  lstatSync as lstatSync9,
58431
59149
  mkdirSync as mkdirSync7,
@@ -58435,7 +59153,7 @@ import {
58435
59153
  rmSync as rmSync8,
58436
59154
  writeFileSync as writeFileSync5
58437
59155
  } from "node:fs";
58438
- import { dirname as dirname26, isAbsolute as isAbsolute11, join as join46, 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";
58439
59157
  function outsideProfile(path2) {
58440
59158
  return HARNESS_CONFIG_PATH.test(path2) || CREDENTIAL_PATH.test(path2);
58441
59159
  }
@@ -58446,7 +59164,7 @@ function sameJson(bytes, expected) {
58446
59164
  return jcs(JSON.parse(bytes.toString("utf8"))) === jcs(expected);
58447
59165
  }
58448
59166
  function readRegular(label2, path2, limit) {
58449
- const fd2 = openSync9(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
59167
+ const fd2 = openSync9(path2, constants6.O_RDONLY | constants6.O_NOFOLLOW);
58450
59168
  try {
58451
59169
  const before = fstatSync3(fd2);
58452
59170
  if (!before.isFile() || before.size > limit)
@@ -58491,7 +59209,7 @@ function within2(root, path2) {
58491
59209
  }
58492
59210
  function newPath(path2) {
58493
59211
  const full = resolve18(path2);
58494
- return join46(
59212
+ return join47(
58495
59213
  realpathSync6(dirname26(full)),
58496
59214
  full.slice(dirname26(full).length + 1)
58497
59215
  );
@@ -58505,7 +59223,7 @@ function sourcePath(root, path2) {
58505
59223
  }
58506
59224
  let current = root;
58507
59225
  for (const part2 of path2.split("/")) {
58508
- current = join46(current, part2);
59226
+ current = join47(current, part2);
58509
59227
  if (lstatSync9(current).isSymbolicLink())
58510
59228
  arpFail(
58511
59229
  `${path2} is a symlink or passes through one. Symlinks require a resource adapter.`
@@ -58772,10 +59490,10 @@ function prepareCheckpoint(options) {
58772
59490
  });
58773
59491
  mkdirSync7(destination, { mode: 448 });
58774
59492
  try {
58775
- const restored = join46(destination, "workspace");
59493
+ const restored = join47(destination, "workspace");
58776
59494
  mkdirSync7(restored, { mode: 448 });
58777
59495
  for (const item of restore) {
58778
- const path2 = join46(restored, item.path);
59496
+ const path2 = join47(restored, item.path);
58779
59497
  if (item.kind === "directory")
58780
59498
  mkdirSync7(path2, { recursive: true, mode: 448 });
58781
59499
  else {
@@ -58786,9 +59504,9 @@ function prepareCheckpoint(options) {
58786
59504
  });
58787
59505
  }
58788
59506
  }
58789
- const promptDir = join46(restored, ".arp");
59507
+ const promptDir = join47(restored, ".arp");
58790
59508
  mkdirSync7(promptDir, { mode: 448 });
58791
- const promptFile = join46(promptDir, "handoff.json");
59509
+ const promptFile = join47(promptDir, "handoff.json");
58792
59510
  const prompt = {
58793
59511
  checkpoint_digest: checkpointDigest,
58794
59512
  task: task.data.request,
@@ -58805,7 +59523,7 @@ function prepareCheckpoint(options) {
58805
59523
  flag: "wx",
58806
59524
  mode: 384
58807
59525
  });
58808
- const reportPath = join46(destination, "report.json");
59526
+ const reportPath = join47(destination, "report.json");
58809
59527
  writeFileSync5(
58810
59528
  reportPath,
58811
59529
  jcs(
@@ -59182,6 +59900,41 @@ function buildTachoProgram(options = {}) {
59182
59900
  deps.err(`${name} credential: ${result2.detail}`);
59183
59901
  process.exitCode = 1;
59184
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
+ );
59185
59938
  credential.command("status").description(
59186
59939
  "Which providers the gateway holds, and how each harness gets its credential"
59187
59940
  ).option("--json", "Machine-readable output").action(async (opts) => {
@@ -59446,7 +60199,7 @@ var init_package = __esm({
59446
60199
  "package.json"() {
59447
60200
  package_default = {
59448
60201
  name: "@oxagen/cli",
59449
- version: "2.1.4-957",
60202
+ version: "2.1.4-965",
59450
60203
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
59451
60204
  type: "module",
59452
60205
  main: "./dist/index.js",
@@ -59513,10 +60266,10 @@ var init_package = __esm({
59513
60266
 
59514
60267
  // src/lib/atomic-write.ts
59515
60268
  import { writeFileSync as writeFileSync6, renameSync as renameSync5, unlinkSync as unlinkSync15, existsSync as existsSync24 } from "node:fs";
59516
- import { dirname as dirname27, join as join47 } from "node:path";
60269
+ import { dirname as dirname27, join as join48 } from "node:path";
59517
60270
  function atomicWriteFileSync(path2, data, mode) {
59518
60271
  const dir = dirname27(path2);
59519
- const tmp = join47(
60272
+ const tmp = join48(
59520
60273
  dir,
59521
60274
  `.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`
59522
60275
  );
@@ -59541,7 +60294,7 @@ var init_atomic_write = __esm({
59541
60294
 
59542
60295
  // src/lib/config.ts
59543
60296
  import { homedir as homedir8 } from "os";
59544
- import { join as join48 } from "path";
60297
+ import { join as join49 } from "path";
59545
60298
  import { readFileSync as readFileSync24, mkdirSync as mkdirSync8, existsSync as existsSync25, statSync as statSync14 } from "fs";
59546
60299
  function getConfigDir() {
59547
60300
  return CONFIG_DIR;
@@ -59628,8 +60381,8 @@ var CONFIG_DIR, CONFIG_FILE, STRING_FIELDS, TELEMETRY_FIELDS, cached2;
59628
60381
  var init_config = __esm({
59629
60382
  "src/lib/config.ts"() {
59630
60383
  init_atomic_write();
59631
- CONFIG_DIR = join48(homedir8(), ".config", "oxagen");
59632
- CONFIG_FILE = join48(CONFIG_DIR, "config.json");
60384
+ CONFIG_DIR = join49(homedir8(), ".config", "oxagen");
60385
+ CONFIG_FILE = join49(CONFIG_DIR, "config.json");
59633
60386
  STRING_FIELDS = [
59634
60387
  "token",
59635
60388
  "orgSlug",
@@ -59674,16 +60427,16 @@ import {
59674
60427
  writeFile as writeFile2
59675
60428
  } from "node:fs/promises";
59676
60429
  import { homedir as homedir9 } from "node:os";
59677
- import { join as join49 } from "node:path";
60430
+ import { join as join50 } from "node:path";
59678
60431
  function isDebugEnabled() {
59679
60432
  const v = process.env[DEBUG_ENV];
59680
60433
  return v === "1" || v === "true";
59681
60434
  }
59682
60435
  function logsDir() {
59683
- return join49(homedir9(), ".oxagen", "logs");
60436
+ return join50(homedir9(), ".oxagen", "logs");
59684
60437
  }
59685
60438
  function debugLogFile() {
59686
- return join49(logsDir(), "cli.output");
60439
+ return join50(logsDir(), "cli.output");
59687
60440
  }
59688
60441
  function sanitize(value, depth = 0) {
59689
60442
  if (value === null || value === void 0) return value;
@@ -62978,9 +63731,9 @@ var init_git = __esm({
62978
63731
 
62979
63732
  // ../../packages/steering-freshness/src/cache.ts
62980
63733
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
62981
- import { dirname as dirname28, join as join50 } from "node:path";
63734
+ import { dirname as dirname28, join as join51 } from "node:path";
62982
63735
  function stampPath(gitCommonDir) {
62983
- return join50(gitCommonDir, "oxagen", "steering-freshness.json");
63736
+ return join51(gitCommonDir, "oxagen", "steering-freshness.json");
62984
63737
  }
62985
63738
  async function readFetchStamp(gitCommonDir, io = defaultIo) {
62986
63739
  try {
@@ -63032,24 +63785,24 @@ var init_cache = __esm({
63032
63785
  // ../../packages/steering-freshness/src/hooks.ts
63033
63786
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
63034
63787
  import { homedir as homedir10 } from "node:os";
63035
- import { dirname as dirname29, join as join51 } from "node:path";
63788
+ import { dirname as dirname29, join as join52 } from "node:path";
63036
63789
  function hookCommand(harness) {
63037
63790
  return `oxagen steering gate --harness ${harness}`;
63038
63791
  }
63039
63792
  function hookConfigPath(projectRoot, harness, env = process.env, home = homedir10()) {
63040
63793
  switch (harness) {
63041
63794
  case "claude-code":
63042
- return join51(projectRoot, ".claude", "settings.json");
63795
+ return join52(projectRoot, ".claude", "settings.json");
63043
63796
  case "codex":
63044
- return join51(env["CODEX_HOME"] ?? join51(home, ".codex"), "hooks.json");
63797
+ return join52(env["CODEX_HOME"] ?? join52(home, ".codex"), "hooks.json");
63045
63798
  case "cursor":
63046
- return join51(home, ".cursor", "hooks.json");
63799
+ return join52(home, ".cursor", "hooks.json");
63047
63800
  case "stella":
63048
- return join51(env["STELLA_HOME"] ?? join51(home, ".stella"), "stella.toml");
63801
+ return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "stella.toml");
63049
63802
  }
63050
63803
  }
63051
63804
  function stellaJsonPath(env, home) {
63052
- return join51(env["STELLA_HOME"] ?? join51(home, ".stella"), "settings.json");
63805
+ return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "settings.json");
63053
63806
  }
63054
63807
  function isRecord7(v) {
63055
63808
  return v !== null && typeof v === "object" && !Array.isArray(v);
@@ -63709,7 +64462,7 @@ var init_policy = __esm({
63709
64462
  // ../../packages/steering-freshness/src/settings.ts
63710
64463
  import { readFile as readFile8 } from "node:fs/promises";
63711
64464
  import { homedir as homedir11 } from "node:os";
63712
- import { join as join52 } from "node:path";
64465
+ import { join as join53 } from "node:path";
63713
64466
  async function readLayer(scope, path2, read2) {
63714
64467
  let text5;
63715
64468
  try {
@@ -63761,14 +64514,14 @@ function parseLayer(scope, path2, text5) {
63761
64514
  }
63762
64515
  async function loadSteeringSettings({
63763
64516
  projectRoot,
63764
- userSettingsPath = join52(homedir11(), USER_SETTINGS_RELATIVE),
64517
+ userSettingsPath = join53(homedir11(), USER_SETTINGS_RELATIVE),
63765
64518
  read: read2 = readFile8,
63766
64519
  workspacePolicy = null
63767
64520
  }) {
63768
64521
  const targets = [
63769
64522
  ["user", userSettingsPath],
63770
- ["project", join52(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
63771
- ["local", join52(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)]
63772
64525
  ];
63773
64526
  const layers = [];
63774
64527
  const warnings = [];
@@ -63831,7 +64584,7 @@ var init_settings = __esm({
63831
64584
  "use strict";
63832
64585
  init_git();
63833
64586
  init_policy();
63834
- USER_SETTINGS_RELATIVE = join52(
64587
+ USER_SETTINGS_RELATIVE = join53(
63835
64588
  ".config",
63836
64589
  "oxagen",
63837
64590
  "settings.json"
@@ -63844,7 +64597,7 @@ var init_settings = __esm({
63844
64597
 
63845
64598
  // ../../packages/steering-freshness/src/check.ts
63846
64599
  import { lstat as lstat3 } from "node:fs/promises";
63847
- import { isAbsolute as isAbsolute12, join as join53, resolve as resolve19 } from "node:path";
64600
+ import { isAbsolute as isAbsolute12, join as join54, resolve as resolve19 } from "node:path";
63848
64601
  function publishedCommits(platform) {
63849
64602
  if (platform.headCommits && platform.headCommits.length > 0)
63850
64603
  return platform.headCommits;
@@ -64181,7 +64934,7 @@ async function absentFromDisk(root, paths) {
64181
64934
  const flags = await Promise.all(
64182
64935
  paths.map(async (path2) => {
64183
64936
  try {
64184
- await lstat3(join53(root, path2));
64937
+ await lstat3(join54(root, path2));
64185
64938
  return false;
64186
64939
  } catch {
64187
64940
  return true;
@@ -64240,7 +64993,7 @@ var init_check = __esm({
64240
64993
  // ../../packages/steering-freshness/src/sync.ts
64241
64994
  import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
64242
64995
  import { tmpdir as tmpdir2 } from "node:os";
64243
- import { join as join54 } from "node:path";
64996
+ import { join as join55 } from "node:path";
64244
64997
  function refuse3(refusal, message) {
64245
64998
  return {
64246
64999
  applied: false,
@@ -64421,9 +65174,9 @@ async function syncSteering(opts) {
64421
65174
  };
64422
65175
  }
64423
65176
  async function commitPaths(ctx, message, paths) {
64424
- const dir = await mkdtemp(join54(tmpdir2(), "oxagen-steering-sync-"));
65177
+ const dir = await mkdtemp(join55(tmpdir2(), "oxagen-steering-sync-"));
64425
65178
  try {
64426
- const list2 = join54(dir, "pathspec");
65179
+ const list2 = join55(dir, "pathspec");
64427
65180
  await writeFile6(list2, `${paths.join("\0")}\0`, "utf8");
64428
65181
  await git3(
64429
65182
  ctx,
@@ -64951,9 +65704,9 @@ var init_paths2 = __esm({
64951
65704
 
64952
65705
  // src/commands/workspace-link.ts
64953
65706
  import { existsSync as existsSync26, mkdirSync as mkdirSync9, readFileSync as readFileSync25 } from "node:fs";
64954
- import { dirname as dirname30, join as join55 } from "node:path";
65707
+ import { dirname as dirname30, join as join56 } from "node:path";
64955
65708
  function workspaceLinkPath(cwd) {
64956
- return join55(cwd, WORKSPACE_LINK_PATH);
65709
+ return join56(cwd, WORKSPACE_LINK_PATH);
64957
65710
  }
64958
65711
  function isRecord8(value) {
64959
65712
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -65043,7 +65796,7 @@ __export(steering_exports, {
65043
65796
  });
65044
65797
  import { execFileSync } from "node:child_process";
65045
65798
  import { existsSync as existsSync27, realpathSync as realpathSync7 } from "node:fs";
65046
- import { dirname as dirname31, join as join56, normalize, parse as parsePath } from "node:path";
65799
+ import { dirname as dirname31, join as join57, normalize, parse as parsePath } from "node:path";
65047
65800
  function findProjectRoot(start) {
65048
65801
  const { root } = parsePath(start);
65049
65802
  const rawGitRoot = gitRootOr(start);
@@ -65052,7 +65805,7 @@ function findProjectRoot(start) {
65052
65805
  if (gitRoot !== null) return gitRoot;
65053
65806
  let dir = from;
65054
65807
  for (; ; ) {
65055
- if (existsSync27(join56(dir, PROJECT_DIR_NAME))) return dir;
65808
+ if (existsSync27(join57(dir, PROJECT_DIR_NAME))) return dir;
65056
65809
  if (dir === root) return from;
65057
65810
  const parent = dirname31(dir);
65058
65811
  if (parent === dir) return from;
@@ -81417,7 +82170,7 @@ function findSecretsAndPii(text5) {
81417
82170
  const findings = [];
81418
82171
  if (/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(text5))
81419
82172
  findings.push("private key block");
81420
- for (const token of text5.match(TOKEN) ?? []) {
82173
+ for (const token of text5.match(TOKEN2) ?? []) {
81421
82174
  if (isSecretToken(token)) {
81422
82175
  findings.push("credential token");
81423
82176
  break;
@@ -81444,7 +82197,7 @@ function findSecretsAndPii(text5) {
81444
82197
  }
81445
82198
  return findings;
81446
82199
  }
81447
- var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS, TOKEN;
82200
+ var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS, TOKEN2;
81448
82201
  var init_secrets = __esm({
81449
82202
  "../../packages/steering-check/src/secrets.ts"() {
81450
82203
  "use strict";
@@ -81495,7 +82248,7 @@ var init_secrets = __esm({
81495
82248
  "session_id",
81496
82249
  "client_secret"
81497
82250
  ];
81498
- TOKEN = /[A-Za-z0-9_\-./+~]+/g;
82251
+ TOKEN2 = /[A-Za-z0-9_\-./+~]+/g;
81499
82252
  }
81500
82253
  });
81501
82254
 
@@ -82443,7 +83196,7 @@ __export(steering_import_exports, {
82443
83196
  });
82444
83197
  import { readdir as readdir4, realpath as realpath2, stat as stat4 } from "node:fs/promises";
82445
83198
  import { homedir as homedir12 } from "node:os";
82446
- import { basename as basename8, extname, join as join57, 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";
82447
83200
  function envDir2(env, key) {
82448
83201
  const value = env[key];
82449
83202
  return value === void 0 || value.trim() === "" ? null : value;
@@ -82451,34 +83204,34 @@ function envDir2(env, key) {
82451
83204
  function harnessHome(harness, env = process.env, home = homedir12()) {
82452
83205
  switch (harness) {
82453
83206
  case "claude-code": {
82454
- const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join57(home, ".claude");
83207
+ const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join58(home, ".claude");
82455
83208
  return {
82456
83209
  harness,
82457
83210
  folders: [
82458
83211
  {
82459
- dir: skillsRoot("claude-code", env, home) ?? join57(config, "skills"),
83212
+ dir: skillsRoot("claude-code", env, home) ?? join58(config, "skills"),
82460
83213
  mount: ".claude/skills"
82461
83214
  },
82462
- { dir: join57(config, "commands"), mount: ".claude/commands" },
82463
- { dir: join57(config, "agents"), mount: ".claude/agents" }
83215
+ { dir: join58(config, "commands"), mount: ".claude/commands" },
83216
+ { dir: join58(config, "agents"), mount: ".claude/agents" }
82464
83217
  ],
82465
83218
  instructions: [
82466
- { file: join57(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
83219
+ { file: join58(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
82467
83220
  ]
82468
83221
  };
82469
83222
  }
82470
83223
  case "codex": {
82471
- const codexHome = envDir2(env, "CODEX_HOME") ?? join57(home, ".codex");
83224
+ const codexHome = envDir2(env, "CODEX_HOME") ?? join58(home, ".codex");
82472
83225
  return {
82473
83226
  harness,
82474
83227
  folders: [
82475
83228
  {
82476
- dir: skillsRoot("codex", env, home) ?? join57(home, ".agents", "skills"),
83229
+ dir: skillsRoot("codex", env, home) ?? join58(home, ".agents", "skills"),
82477
83230
  mount: ".agents/skills"
82478
83231
  }
82479
83232
  ],
82480
83233
  instructions: ["AGENTS.override.md", "AGENTS.md"].map((name) => ({
82481
- file: join57(codexHome, name),
83234
+ file: join58(codexHome, name),
82482
83235
  filename: `.codex/${name}`
82483
83236
  }))
82484
83237
  };
@@ -82487,8 +83240,8 @@ function harnessHome(harness, env = process.env, home = homedir12()) {
82487
83240
  return {
82488
83241
  harness,
82489
83242
  folders: [
82490
- { dir: join57(home, ".cursor", "skills"), mount: ".cursor/skills" },
82491
- { dir: join57(home, ".cursor", "commands"), mount: ".cursor/commands" }
83243
+ { dir: join58(home, ".cursor", "skills"), mount: ".cursor/skills" },
83244
+ { dir: join58(home, ".cursor", "commands"), mount: ".cursor/commands" }
82492
83245
  ],
82493
83246
  instructions: []
82494
83247
  };
@@ -82534,7 +83287,7 @@ async function findMarkdownFiles(paths, harness) {
82534
83287
  }
82535
83288
  const found = [];
82536
83289
  for (const entry2 of entries2.sort(byName)) {
82537
- const path2 = join57(dir, entry2.name);
83290
+ const path2 = join58(dir, entry2.name);
82538
83291
  if (entry2.isDirectory()) {
82539
83292
  if (!entry2.name.startsWith(".") && entry2.name !== "node_modules")
82540
83293
  found.push(...await filesUnder(path2, from));
@@ -82556,7 +83309,7 @@ async function findMarkdownFiles(paths, harness) {
82556
83309
  const { folder } = frame;
82557
83310
  const named = (rel) => frame.fixedNames ? { filename: rel } : {};
82558
83311
  for (const entry2 of entries2) {
82559
- const path2 = join57(dir, entry2.name);
83312
+ const path2 = join58(dir, entry2.name);
82560
83313
  const rel = frame.relOf(path2);
82561
83314
  if (entry2.isDirectory()) {
82562
83315
  if (entry2.name === "node_modules") continue;
@@ -82615,7 +83368,7 @@ async function findMarkdownFiles(paths, harness) {
82615
83368
  }
82616
83369
  if (folderGiven) {
82617
83370
  const catalogue = CATALOGUE_FOLDERS.has(basename8(path2));
82618
- const root = catalogue ? join57(path2, "..") : path2;
83371
+ const root = catalogue ? join58(path2, "..") : path2;
82619
83372
  await walk2(path2, {
82620
83373
  relOf: (p) => under(root, p),
82621
83374
  folder: path2,
@@ -84275,10 +85028,10 @@ __export(verify_exports, {
84275
85028
  verifyBundle: () => verifyBundle3
84276
85029
  });
84277
85030
  import { existsSync as existsSync28, readFileSync as readFileSync26, statSync as statSync15 } from "node:fs";
84278
- import { join as join58 } from "node:path";
85031
+ import { join as join59 } from "node:path";
84279
85032
  function readDirectory(dir) {
84280
85033
  const read2 = (name) => {
84281
- const path2 = join58(dir, name);
85034
+ const path2 = join59(dir, name);
84282
85035
  return existsSync28(path2) ? readFileSync26(path2, "utf8") : void 0;
84283
85036
  };
84284
85037
  return assemble(read2, dir);
@@ -85233,7 +85986,7 @@ var init_conversation = __esm({
85233
85986
  });
85234
85987
 
85235
85988
  // src/auth/open-browser.ts
85236
- import { spawn as spawn6 } from "node:child_process";
85989
+ import { spawn as spawn8 } from "node:child_process";
85237
85990
  function openBrowser(url) {
85238
85991
  try {
85239
85992
  let cmd;
@@ -85251,7 +86004,7 @@ function openBrowser(url) {
85251
86004
  cmd = "xdg-open";
85252
86005
  args = [url];
85253
86006
  }
85254
- const child = spawn6(cmd, args, { detached: true, stdio: "ignore" });
86007
+ const child = spawn8(cmd, args, { detached: true, stdio: "ignore" });
85255
86008
  child.on("error", () => {
85256
86009
  });
85257
86010
  child.unref();
@@ -85407,7 +86160,7 @@ import {
85407
86160
  writeFileSync as writeFileSync7
85408
86161
  } from "node:fs";
85409
86162
  import { homedir as homedir13, hostname as osHostname3 } from "node:os";
85410
- import { join as join59, 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";
85411
86164
  function git4(cwd, args) {
85412
86165
  return new Promise((done) => {
85413
86166
  try {
@@ -85454,7 +86207,7 @@ function parseRemoteRepository(url) {
85454
86207
  }
85455
86208
  function oxagenPresent(root) {
85456
86209
  try {
85457
- return readdirSync14(join59(root, ".oxagen")).some(
86210
+ return readdirSync14(join60(root, ".oxagen")).some(
85458
86211
  (name) => name !== "workspace.json"
85459
86212
  );
85460
86213
  } catch {
@@ -85463,18 +86216,18 @@ function oxagenPresent(root) {
85463
86216
  }
85464
86217
  function symlinkState(root) {
85465
86218
  try {
85466
- if (!lstatSync10(join59(root, ".stella")).isDirectory()) return "none";
86219
+ if (!lstatSync10(join60(root, ".stella")).isDirectory()) return "none";
85467
86220
  } catch {
85468
86221
  return "none";
85469
86222
  }
85470
86223
  let oxagenReal;
85471
86224
  try {
85472
- oxagenReal = realpathSync8(join59(root, ".oxagen"));
86225
+ oxagenReal = realpathSync8(join60(root, ".oxagen"));
85473
86226
  } catch {
85474
86227
  return "missing";
85475
86228
  }
85476
86229
  for (const link of STELLA_LINKS) {
85477
- const at2 = join59(root, link);
86230
+ const at2 = join60(root, link);
85478
86231
  try {
85479
86232
  if (!lstatSync10(at2).isSymbolicLink()) return "missing";
85480
86233
  const target2 = realpathSync8(at2);
@@ -85488,7 +86241,7 @@ function symlinkState(root) {
85488
86241
  return "linked";
85489
86242
  }
85490
86243
  function machineId(configDir = getConfigDir()) {
85491
- const file = join59(configDir, MACHINE_ID_FILE);
86244
+ const file = join60(configDir, MACHINE_ID_FILE);
85492
86245
  let secret = null;
85493
86246
  try {
85494
86247
  if (existsSync29(file)) {
@@ -85570,12 +86323,12 @@ async function ensureWorkspaceLinkIgnored(root) {
85570
86323
  const topDir = resolve22(top);
85571
86324
  const entry2 = relative8(
85572
86325
  topDir,
85573
- join59(realpathOr2(root), ".oxagen", "workspace.json")
86326
+ join60(realpathOr2(root), ".oxagen", "workspace.json")
85574
86327
  ).split(sep10).join("/");
85575
86328
  if (entry2.startsWith("../")) return null;
85576
86329
  const rule = await git4(topDir, ["check-ignore", "--no-index", "-v", entry2]);
85577
86330
  if (rule !== null) return null;
85578
- const gitignore = join59(resolve22(top), ".gitignore");
86331
+ const gitignore = join60(resolve22(top), ".gitignore");
85579
86332
  let existing = "";
85580
86333
  try {
85581
86334
  existing = readFileSync27(gitignore, "utf8");
@@ -86100,7 +86853,7 @@ import {
86100
86853
  import {
86101
86854
  dirname as dirname33,
86102
86855
  isAbsolute as isAbsolute14,
86103
- join as join60,
86856
+ join as join61,
86104
86857
  parse as parsePath2,
86105
86858
  posix as posix4,
86106
86859
  sep as sep11
@@ -86220,7 +86973,7 @@ function findLinkedRoot(start) {
86220
86973
  }
86221
86974
  function fsReader(root) {
86222
86975
  return (path2) => {
86223
- const abs = join60(root, ...path2.split("/"));
86976
+ const abs = join61(root, ...path2.split("/"));
86224
86977
  try {
86225
86978
  const st = lstatSync11(abs);
86226
86979
  if (!st.isFile()) return Buffer.from(`\0not-a-file:${abs}`);
@@ -86231,14 +86984,14 @@ function fsReader(root) {
86231
86984
  };
86232
86985
  }
86233
86986
  function assertInsideOxagen(root, path2) {
86234
- const oxagenAbs = join60(root, OXAGEN_DIR);
86987
+ const oxagenAbs = join61(root, OXAGEN_DIR);
86235
86988
  let oxagenReal;
86236
86989
  try {
86237
86990
  oxagenReal = realpathSync9(oxagenAbs);
86238
86991
  } catch {
86239
86992
  return;
86240
86993
  }
86241
- const abs = join60(root, ...path2.split("/"));
86994
+ const abs = join61(root, ...path2.split("/"));
86242
86995
  try {
86243
86996
  if (lstatSync11(abs).isSymbolicLink()) {
86244
86997
  throw new Error(
@@ -86268,8 +87021,8 @@ function assertInsideOxagen(root, path2) {
86268
87021
  }
86269
87022
  }
86270
87023
  function pruneEmptyDirs(root, path2) {
86271
- const stop = join60(root, OXAGEN_DIR);
86272
- let dir = dirname33(join60(root, ...path2.split("/")));
87024
+ const stop = join61(root, OXAGEN_DIR);
87025
+ let dir = dirname33(join61(root, ...path2.split("/")));
86273
87026
  while (dir.startsWith(stop + sep11)) {
86274
87027
  try {
86275
87028
  if (readdirSync15(dir).length > 0) return;
@@ -86284,12 +87037,12 @@ function applyPlan(root, plan) {
86284
87037
  for (const w of plan.writes) assertInsideOxagen(root, w.path);
86285
87038
  for (const d of plan.deletes) assertInsideOxagen(root, d);
86286
87039
  for (const w of plan.writes) {
86287
- const abs = join60(root, ...w.path.split("/"));
87040
+ const abs = join61(root, ...w.path.split("/"));
86288
87041
  mkdirSync11(dirname33(abs), { recursive: true });
86289
87042
  atomicWriteFileSync(abs, w.content);
86290
87043
  }
86291
87044
  for (const d of plan.deletes) {
86292
- rmSync10(join60(root, ...d.split("/")), { force: true });
87045
+ rmSync10(join61(root, ...d.split("/")), { force: true });
86293
87046
  pruneEmptyDirs(root, d);
86294
87047
  }
86295
87048
  }
@@ -108252,7 +109005,7 @@ function invalidArguments(message) {
108252
109005
  return new BuildError("Invalid arguments", message);
108253
109006
  }
108254
109007
  function checkedName(parameter) {
108255
- if (!TOKEN2.test(parameter.name)) {
109008
+ if (!TOKEN3.test(parameter.name)) {
108256
109009
  throw new BuildError(
108257
109010
  "Invalid request",
108258
109011
  `The ${parameter.in} parameter name ${parameter.name} is not an HTTP token.`
@@ -108334,7 +109087,7 @@ function cookiePairs(parameter, value) {
108334
109087
  ]
108335
109088
  ] : shape.kind === "list" ? shape.items.map((item) => [name, item]) : shape.entries;
108336
109089
  for (const [cookie, text5] of pairs) {
108337
- if (!TOKEN2.test(cookie)) {
109090
+ if (!TOKEN3.test(cookie)) {
108338
109091
  throw invalidArguments(
108339
109092
  `The cookie ${parameter.name} holds a key that is not an HTTP token, so no cookie can take it as a name.`
108340
109093
  );
@@ -108377,12 +109130,12 @@ function queryEntries(parameter, value) {
108377
109130
  }
108378
109131
  if (shape.items.length === 0) return [];
108379
109132
  if (explode) return shape.items.map((item) => pair(name, item));
108380
- const [raw, delimiter2] = style === "form" ? [",", ","] : style === "spaceDelimited" ? [" ", "%20"] : ["|", "|"];
109133
+ const [raw, delimiter3] = style === "form" ? [",", ","] : style === "spaceDelimited" ? [" ", "%20"] : ["|", "|"];
108381
109134
  return [
108382
109135
  entry(
108383
109136
  name,
108384
109137
  shape.items.join(raw),
108385
- `${enc(name)}=${shape.items.map(val).join(delimiter2)}`
109138
+ `${enc(name)}=${shape.items.map(val).join(delimiter3)}`
108386
109139
  )
108387
109140
  ];
108388
109141
  }
@@ -108599,7 +109352,7 @@ function createHttpSender(options = {}) {
108599
109352
  }
108600
109353
  };
108601
109354
  }
108602
- 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;
108603
109356
  var init_http = __esm({
108604
109357
  "../../packages/mcp-studio/src/execute/http.ts"() {
108605
109358
  "use strict";
@@ -108627,7 +109380,7 @@ var init_http = __esm({
108627
109380
  "proxy-authorization",
108628
109381
  "cookie"
108629
109382
  ]);
108630
- TOKEN2 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
109383
+ TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
108631
109384
  UNSAFE_HEADER_VALUE = /[\r\n\0]/;
108632
109385
  UNSAFE_COOKIE_VALUE = /[;\r\n\0]/;
108633
109386
  }
@@ -130164,7 +130917,7 @@ import {
130164
130917
  readlinkSync as readlinkSync4,
130165
130918
  statSync as statSync16
130166
130919
  } from "node:fs";
130167
- import { dirname as dirname34, isAbsolute as isAbsolute15, join as join61, 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";
130168
130921
  function git5(cwd, args, input) {
130169
130922
  try {
130170
130923
  return execFileSync2("git", args, {
@@ -130196,7 +130949,7 @@ function commitOf2(root, ref2) {
130196
130949
  return sha2.length > 0 ? sha2 : null;
130197
130950
  }
130198
130951
  function readWorkingFile(root, path2) {
130199
- const abs = join61(root, ...path2.split("/"));
130952
+ const abs = join62(root, ...path2.split("/"));
130200
130953
  try {
130201
130954
  const stat7 = lstatSync12(abs);
130202
130955
  if (stat7.isSymbolicLink()) return readlinkSync4(abs, "utf8");
@@ -130290,7 +131043,7 @@ function isDir(path2) {
130290
131043
  function findSteeringRoot(start) {
130291
131044
  let dir = resolve24(start);
130292
131045
  for (; ; ) {
130293
- if (isFile(join61(dir, WORKSPACE_TOML_PATH)) || isFile(join61(dir, AGENTS_MD_PATH)) && isDir(join61(dir, STEERING_DIR))) {
131046
+ if (isFile(join62(dir, WORKSPACE_TOML_PATH)) || isFile(join62(dir, AGENTS_MD_PATH)) && isDir(join62(dir, STEERING_DIR))) {
130294
131047
  return dir;
130295
131048
  }
130296
131049
  const parent = dirname34(dir);
@@ -130370,7 +131123,7 @@ function cacheFile(cacheDir, root) {
130370
131123
  key: token === void 0 ? null : createHash20("sha256").update(token).digest("hex").slice(0, 16)
130371
131124
  };
130372
131125
  const key = createHash20("sha256").update(JSON.stringify([root, identity])).digest("hex").slice(0, 32);
130373
- return join61(cacheDir, `${key}.json`);
131126
+ return join62(cacheDir, `${key}.json`);
130374
131127
  }
130375
131128
  function readCache(file, now) {
130376
131129
  let entry2;
@@ -130484,7 +131237,7 @@ function defaultDeps2() {
130484
131237
  return {
130485
131238
  fetchPublished: fetchSteeringIndex,
130486
131239
  cedar: cedarHooks,
130487
- cacheDir: join61(getConfigDir(), "cache", "steering-check"),
131240
+ cacheDir: join62(getConfigDir(), "cache", "steering-check"),
130488
131241
  now: () => Date.now()
130489
131242
  };
130490
131243
  }
@@ -134367,7 +135120,7 @@ var init_src9 = __esm({
134367
135120
 
134368
135121
  // src/commands/codegraph-upload/files.ts
134369
135122
  import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
134370
- import { join as join62, relative as relative10, sep as sep13 } from "node:path";
135123
+ import { join as join63, relative as relative10, sep as sep13 } from "node:path";
134371
135124
  function hasGlobChars(pattern) {
134372
135125
  return /[*?[\]{}]/.test(pattern);
134373
135126
  }
@@ -134379,7 +135132,7 @@ async function walk(root, dir, out) {
134379
135132
  const entries2 = await readdir5(dir, { withFileTypes: true });
134380
135133
  for (const entry2 of entries2) {
134381
135134
  if (entry2.isSymbolicLink()) continue;
134382
- const full = join62(dir, entry2.name);
135135
+ const full = join63(dir, entry2.name);
134383
135136
  if (entry2.isDirectory()) {
134384
135137
  if (!SKIP_DIRS.has(entry2.name)) await walk(root, full, out);
134385
135138
  } else if (entry2.isFile()) {
@@ -134391,7 +135144,7 @@ async function expandPattern(root, pattern) {
134391
135144
  const wanted = normalize2(pattern.trim());
134392
135145
  if (!hasGlobChars(wanted)) {
134393
135146
  try {
134394
- const info = await stat5(join62(root, wanted));
135147
+ const info = await stat5(join63(root, wanted));
134395
135148
  return info.isFile() ? [wanted] : [];
134396
135149
  } catch {
134397
135150
  return [];
@@ -134542,7 +135295,7 @@ var init_token = __esm({
134542
135295
  });
134543
135296
 
134544
135297
  // src/commands/codegraph-upload/run.ts
134545
- import { join as join63 } from "node:path";
135298
+ import { join as join64 } from "node:path";
134546
135299
  function addDropped(into, more) {
134547
135300
  into.sensitiveValues += more.sensitiveValues;
134548
135301
  into.secretData += more.secretData;
@@ -134560,7 +135313,7 @@ async function uploadOne(deps, config, token, group, path2, totals2) {
134560
135313
  const label2 = `${group.kind}/${group.source} ${path2}`;
134561
135314
  let bytes;
134562
135315
  try {
134563
- bytes = await deps.readFile(join63(deps.cwd, path2));
135316
+ bytes = await deps.readFile(join64(deps.cwd, path2));
134564
135317
  } catch (error) {
134565
135318
  totals2.failed += 1;
134566
135319
  deps.log(`error: ${label2}: cannot read the file: ${error.message}`);