@oxagen/cli 2.1.4-957 → 2.1.4-975
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/oxagen.mjs +1636 -796
- package/package.json +1 -1
package/oxagen.mjs
CHANGED
|
@@ -5307,7 +5307,22 @@ var init_envelope = __esm({
|
|
|
5307
5307
|
// like `proof.observed`. Its strict schema is
|
|
5308
5308
|
// `hostEgressRefusedMessageSchema` in `@oxagen/oxagen/sandbox-host/protocol`,
|
|
5309
5309
|
// which this leaf package cannot import.
|
|
5310
|
-
"oxagen:egress_refused": external_exports.object({}).catchall(json)
|
|
5310
|
+
"oxagen:egress_refused": external_exports.object({}).catchall(json),
|
|
5311
|
+
// The in-app assistant's frames (#5943, ADR-5969). The control plane writes
|
|
5312
|
+
// them on the assistant's own session (`assistant-session.ts` in
|
|
5313
|
+
// @oxagen/agent), never a host. A model call and a tool call each take two
|
|
5314
|
+
// frames: the write-ahead request, durable before the provider or the tool
|
|
5315
|
+
// is reached, and the completion. The history summary is the context a
|
|
5316
|
+
// turn carried in place of its older messages, and the goal verdict is one
|
|
5317
|
+
// verifier round. Each body is opaque on the wire, like `proof.observed`:
|
|
5318
|
+
// its members are the receipt the assistant recorded, and the readers in
|
|
5319
|
+
// @oxagen/run-ledger read them by name.
|
|
5320
|
+
"model.engine_call_started": external_exports.object({}).catchall(json),
|
|
5321
|
+
"model.engine_call_completed": external_exports.object({}).catchall(json),
|
|
5322
|
+
"tool.engine_call_started": external_exports.object({}).catchall(json),
|
|
5323
|
+
"tool.engine_call_completed": external_exports.object({}).catchall(json),
|
|
5324
|
+
"context.history_summarized": external_exports.object({}).catchall(json),
|
|
5325
|
+
"verification.goal_verdict": external_exports.object({}).catchall(json)
|
|
5311
5326
|
};
|
|
5312
5327
|
TACHO_KINDS = Object.keys(KIND_BODIES);
|
|
5313
5328
|
agentIdentitySchema = external_exports.object({
|
|
@@ -13816,8 +13831,8 @@ var init_backfill = __esm({
|
|
|
13816
13831
|
this.parentMs = ms;
|
|
13817
13832
|
this.clock.ms = ms;
|
|
13818
13833
|
}
|
|
13819
|
-
const
|
|
13820
|
-
return { events: this.mappedLine(parsed2, text5, offset,
|
|
13834
|
+
const spawn9 = [];
|
|
13835
|
+
return { events: this.mappedLine(parsed2, text5, offset, spawn9), spawn: spawn9 };
|
|
13821
13836
|
}
|
|
13822
13837
|
/**
|
|
13823
13838
|
* Begin a session still waiting for its facts once its last line is read:
|
|
@@ -13843,28 +13858,28 @@ var init_backfill = __esm({
|
|
|
13843
13858
|
this.parentMs = timestampOf(first) ?? 0;
|
|
13844
13859
|
this.clock.ms = this.parentMs;
|
|
13845
13860
|
const events = this.collect(this.start(first, facts, permissionMode));
|
|
13846
|
-
const
|
|
13861
|
+
const spawn9 = [];
|
|
13847
13862
|
for (const item of early) {
|
|
13848
13863
|
const ms = timestampOf(item.record);
|
|
13849
13864
|
if (ms !== void 0) this.parentMs = Math.max(this.parentMs, ms);
|
|
13850
13865
|
this.clock.ms = ms ?? this.parentMs;
|
|
13851
13866
|
events.push(
|
|
13852
|
-
...this.mappedLine(item.record, item.text, item.offset,
|
|
13867
|
+
...this.mappedLine(item.record, item.text, item.offset, spawn9)
|
|
13853
13868
|
);
|
|
13854
13869
|
}
|
|
13855
13870
|
this.clock.ms = this.parentMs;
|
|
13856
|
-
return { events, spawn:
|
|
13871
|
+
return { events, spawn: spawn9 };
|
|
13857
13872
|
}
|
|
13858
13873
|
/** One parent line through the mapping, guarded. */
|
|
13859
|
-
mappedLine(record, text5, offset,
|
|
13874
|
+
mappedLine(record, text5, offset, spawn9) {
|
|
13860
13875
|
const spawnedBefore = this.spawned.size;
|
|
13861
|
-
const spawnLength =
|
|
13876
|
+
const spawnLength = spawn9.length;
|
|
13862
13877
|
return this.guarded(
|
|
13863
13878
|
offset,
|
|
13864
13879
|
void 0,
|
|
13865
|
-
() => this.mapped(record, text5,
|
|
13880
|
+
() => this.mapped(record, text5, spawn9),
|
|
13866
13881
|
() => {
|
|
13867
|
-
|
|
13882
|
+
spawn9.length = spawnLength;
|
|
13868
13883
|
for (const toolUseId of [...this.spawned.keys()].slice(spawnedBefore))
|
|
13869
13884
|
this.spawned.delete(toolUseId);
|
|
13870
13885
|
}
|
|
@@ -14080,7 +14095,7 @@ var init_backfill = __esm({
|
|
|
14080
14095
|
})
|
|
14081
14096
|
);
|
|
14082
14097
|
}
|
|
14083
|
-
mapped(record, text5,
|
|
14098
|
+
mapped(record, text5, spawn9) {
|
|
14084
14099
|
const type = nonEmptyString(record["type"]);
|
|
14085
14100
|
const ms = timestampOf(record);
|
|
14086
14101
|
const events = [];
|
|
@@ -14213,7 +14228,7 @@ var init_backfill = __esm({
|
|
|
14213
14228
|
ms
|
|
14214
14229
|
)
|
|
14215
14230
|
);
|
|
14216
|
-
|
|
14231
|
+
spawn9.push(subagent.agentId);
|
|
14217
14232
|
}
|
|
14218
14233
|
return events;
|
|
14219
14234
|
}
|
|
@@ -17571,14 +17586,14 @@ var init_recorder = __esm({
|
|
|
17571
17586
|
const parent = this.options.parent;
|
|
17572
17587
|
if (parent === void 0) return;
|
|
17573
17588
|
const type = parent.subagentType === void 0 ? subagentType : void 0;
|
|
17574
|
-
const
|
|
17575
|
-
if (type === void 0 &&
|
|
17589
|
+
const spawn9 = parent.spawnToolUseId === void 0 ? spawnToolUseId : void 0;
|
|
17590
|
+
if (type === void 0 && spawn9 === void 0) return;
|
|
17576
17591
|
this.options = {
|
|
17577
17592
|
...this.options,
|
|
17578
17593
|
parent: {
|
|
17579
17594
|
...parent,
|
|
17580
17595
|
...type !== void 0 ? { subagentType: type } : {},
|
|
17581
|
-
...
|
|
17596
|
+
...spawn9 !== void 0 ? { spawnToolUseId: spawn9 } : {}
|
|
17582
17597
|
}
|
|
17583
17598
|
};
|
|
17584
17599
|
}
|
|
@@ -21029,12 +21044,12 @@ async function sealTranscript(candidate, deps, pass2) {
|
|
|
21029
21044
|
const read2 = await readSlices(candidate.path, sliceBytes, async (lines) => {
|
|
21030
21045
|
let index = 0;
|
|
21031
21046
|
while (index < lines.length) {
|
|
21032
|
-
const
|
|
21047
|
+
const spawn9 = [];
|
|
21033
21048
|
await deps.exclusive(candidate.sessionId, () => {
|
|
21034
21049
|
if (deps.heldLocally(candidate.sessionId)) throw new TakenLive();
|
|
21035
21050
|
const out = [];
|
|
21036
21051
|
let next = readTo;
|
|
21037
|
-
while (
|
|
21052
|
+
while (spawn9.length === 0) {
|
|
21038
21053
|
const line = lines[index];
|
|
21039
21054
|
if (line === void 0) break;
|
|
21040
21055
|
index += 1;
|
|
@@ -21045,7 +21060,7 @@ async function sealTranscript(candidate, deps, pass2) {
|
|
|
21045
21060
|
}
|
|
21046
21061
|
const sealed = driver.line(line.text, line.offset);
|
|
21047
21062
|
out.push(...sealed.events);
|
|
21048
|
-
|
|
21063
|
+
spawn9.push(...sealed.spawn);
|
|
21049
21064
|
}
|
|
21050
21065
|
emit(out);
|
|
21051
21066
|
if (driver.hasChain) {
|
|
@@ -21053,7 +21068,7 @@ async function sealTranscript(candidate, deps, pass2) {
|
|
|
21053
21068
|
partial(readTo, { status: "partial" });
|
|
21054
21069
|
}
|
|
21055
21070
|
});
|
|
21056
|
-
for (const agentId of
|
|
21071
|
+
for (const agentId of spawn9) await feedSubagent(agentId);
|
|
21057
21072
|
}
|
|
21058
21073
|
await between();
|
|
21059
21074
|
});
|
|
@@ -22422,6 +22437,626 @@ var init_claude_code_mcp = __esm({
|
|
|
22422
22437
|
}
|
|
22423
22438
|
});
|
|
22424
22439
|
|
|
22440
|
+
// ../../packages/tacho/src/host/run-token.ts
|
|
22441
|
+
import { createHmac, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
22442
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
22443
|
+
function keyFromBytes(bytes) {
|
|
22444
|
+
if (bytes.length !== KEY_BYTES)
|
|
22445
|
+
throw new Error(
|
|
22446
|
+
`run token key must be ${KEY_BYTES} bytes, got ${bytes.length}`
|
|
22447
|
+
);
|
|
22448
|
+
const id2 = createHmac("sha256", "tacho.run-token-key-id").update(bytes).digest("hex").slice(0, 16);
|
|
22449
|
+
return { bytes, id: id2 };
|
|
22450
|
+
}
|
|
22451
|
+
function generateRunTokenKey() {
|
|
22452
|
+
return keyFromBytes(randomBytes4(KEY_BYTES));
|
|
22453
|
+
}
|
|
22454
|
+
function runTokenKeyFromHex(hex) {
|
|
22455
|
+
const trimmed = hex.trim();
|
|
22456
|
+
if (!/^[0-9a-f]{64}$/i.test(trimmed))
|
|
22457
|
+
throw new Error("run token key file does not hold 64 hex characters");
|
|
22458
|
+
return keyFromBytes(Buffer.from(trimmed, "hex"));
|
|
22459
|
+
}
|
|
22460
|
+
function loadOrCreateRunTokenKey(path2) {
|
|
22461
|
+
try {
|
|
22462
|
+
return {
|
|
22463
|
+
key: runTokenKeyFromHex(readFileSync6(path2, "utf8")),
|
|
22464
|
+
created: false
|
|
22465
|
+
};
|
|
22466
|
+
} catch (error) {
|
|
22467
|
+
if (error.code !== "ENOENT") throw error;
|
|
22468
|
+
}
|
|
22469
|
+
const key = generateRunTokenKey();
|
|
22470
|
+
writeSensitiveFileAtomic(path2, `${key.bytes.toString("hex")}
|
|
22471
|
+
`);
|
|
22472
|
+
return { key, created: true };
|
|
22473
|
+
}
|
|
22474
|
+
function readRunTokenKey(path2) {
|
|
22475
|
+
const version2 = fileVersion(path2);
|
|
22476
|
+
if (version2 === void 0) {
|
|
22477
|
+
keysSeen.delete(path2);
|
|
22478
|
+
return void 0;
|
|
22479
|
+
}
|
|
22480
|
+
const seen = keysSeen.get(path2);
|
|
22481
|
+
if (seen?.version === version2) return seen.key;
|
|
22482
|
+
let key;
|
|
22483
|
+
try {
|
|
22484
|
+
key = runTokenKeyFromHex(readFileSync6(path2, "utf8"));
|
|
22485
|
+
} catch (error) {
|
|
22486
|
+
if (error.code === "ENOENT") return void 0;
|
|
22487
|
+
throw error;
|
|
22488
|
+
}
|
|
22489
|
+
keysSeen.set(path2, { version: version2, key });
|
|
22490
|
+
return key;
|
|
22491
|
+
}
|
|
22492
|
+
function b64url(bytes) {
|
|
22493
|
+
return bytes.toString("base64url");
|
|
22494
|
+
}
|
|
22495
|
+
function sign(key, claimsText) {
|
|
22496
|
+
return createHmac("sha256", key.bytes).update(claimsText, "utf8").digest();
|
|
22497
|
+
}
|
|
22498
|
+
function mintRunToken(options) {
|
|
22499
|
+
const ceiling = options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_MAX_TTL_MS;
|
|
22500
|
+
const asked = options.ttlMs === void 0 ? options.placement === "static" ? RUN_TOKEN_STATIC_MAX_TTL_MS : RUN_TOKEN_DEFAULT_TTL_MS : options.ttlMs;
|
|
22501
|
+
if (!Number.isFinite(asked) || asked <= 0)
|
|
22502
|
+
throw new Error(`run token ttl must be positive, got ${String(asked)}`);
|
|
22503
|
+
let exp = options.now + Math.min(asked, ceiling);
|
|
22504
|
+
if (options.notAfter !== void 0) exp = Math.min(exp, options.notAfter);
|
|
22505
|
+
if (exp <= options.now)
|
|
22506
|
+
throw new Error("run token would already be expired when minted");
|
|
22507
|
+
const claims = {
|
|
22508
|
+
v: 1,
|
|
22509
|
+
tid: `rt_${randomBytes4(10).toString("hex")}`,
|
|
22510
|
+
host: options.host,
|
|
22511
|
+
harness: options.harness,
|
|
22512
|
+
provider: options.provider,
|
|
22513
|
+
placement: options.placement,
|
|
22514
|
+
iat: options.now,
|
|
22515
|
+
exp
|
|
22516
|
+
};
|
|
22517
|
+
const text5 = jcs(claims);
|
|
22518
|
+
const token = `${RUN_TOKEN_PREFIX}${b64url(Buffer.from(text5, "utf8"))}.${b64url(sign(options.key, text5))}`;
|
|
22519
|
+
return { token, claims };
|
|
22520
|
+
}
|
|
22521
|
+
function looksLikeRunToken(value) {
|
|
22522
|
+
return typeof value === "string" && value.startsWith(RUN_TOKEN_PREFIX) && value.length < 2048;
|
|
22523
|
+
}
|
|
22524
|
+
function peekRunTokenClaims(token) {
|
|
22525
|
+
if (!looksLikeRunToken(token)) return void 0;
|
|
22526
|
+
const body2 = token.slice(RUN_TOKEN_PREFIX.length);
|
|
22527
|
+
const dot = body2.indexOf(".");
|
|
22528
|
+
if (dot <= 0) return void 0;
|
|
22529
|
+
try {
|
|
22530
|
+
const parsed2 = JSON.parse(
|
|
22531
|
+
Buffer.from(body2.slice(0, dot), "base64url").toString("utf8")
|
|
22532
|
+
);
|
|
22533
|
+
const claims = runTokenClaimsSchema.safeParse(parsed2);
|
|
22534
|
+
return claims.success ? claims.data : void 0;
|
|
22535
|
+
} catch {
|
|
22536
|
+
return void 0;
|
|
22537
|
+
}
|
|
22538
|
+
}
|
|
22539
|
+
function verifyRunToken(token, options) {
|
|
22540
|
+
if (!looksLikeRunToken(token))
|
|
22541
|
+
return { ok: false, code: "run_token_malformed" };
|
|
22542
|
+
const body2 = token.slice(RUN_TOKEN_PREFIX.length);
|
|
22543
|
+
const dot = body2.indexOf(".");
|
|
22544
|
+
if (dot <= 0 || dot === body2.length - 1)
|
|
22545
|
+
return { ok: false, code: "run_token_malformed" };
|
|
22546
|
+
const bodyText = body2.slice(0, dot);
|
|
22547
|
+
let text5;
|
|
22548
|
+
try {
|
|
22549
|
+
text5 = Buffer.from(bodyText, "base64url").toString("utf8");
|
|
22550
|
+
} catch {
|
|
22551
|
+
return { ok: false, code: "run_token_malformed" };
|
|
22552
|
+
}
|
|
22553
|
+
const presented = Buffer.from(body2.slice(dot + 1), "utf8");
|
|
22554
|
+
const expected = Buffer.from(
|
|
22555
|
+
sign(options.key, text5).toString("base64url"),
|
|
22556
|
+
"utf8"
|
|
22557
|
+
);
|
|
22558
|
+
if (presented.length !== expected.length || !timingSafeEqual(presented, expected) || Buffer.from(text5, "utf8").toString("base64url") !== bodyText) {
|
|
22559
|
+
return { ok: false, code: "run_token_invalid", ...peeked(text5) };
|
|
22560
|
+
}
|
|
22561
|
+
let parsed2;
|
|
22562
|
+
try {
|
|
22563
|
+
parsed2 = JSON.parse(text5);
|
|
22564
|
+
} catch {
|
|
22565
|
+
return { ok: false, code: "run_token_malformed" };
|
|
22566
|
+
}
|
|
22567
|
+
const claims = runTokenClaimsSchema.safeParse(parsed2);
|
|
22568
|
+
if (!claims.success) return { ok: false, code: "run_token_malformed" };
|
|
22569
|
+
if (jcs(claims.data) !== text5)
|
|
22570
|
+
return { ok: false, code: "run_token_invalid" };
|
|
22571
|
+
if (claims.data.exp <= options.now)
|
|
22572
|
+
return { ok: false, code: "run_token_expired", claims: claims.data };
|
|
22573
|
+
if (claims.data.host !== options.host || claims.data.provider !== options.provider)
|
|
22574
|
+
return { ok: false, code: "run_token_mismatch", claims: claims.data };
|
|
22575
|
+
return { ok: true, claims: claims.data };
|
|
22576
|
+
}
|
|
22577
|
+
function peeked(text5) {
|
|
22578
|
+
try {
|
|
22579
|
+
const claims = runTokenClaimsSchema.safeParse(JSON.parse(text5));
|
|
22580
|
+
return claims.success ? { claims: claims.data } : {};
|
|
22581
|
+
} catch {
|
|
22582
|
+
return {};
|
|
22583
|
+
}
|
|
22584
|
+
}
|
|
22585
|
+
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;
|
|
22586
|
+
var init_run_token = __esm({
|
|
22587
|
+
"../../packages/tacho/src/host/run-token.ts"() {
|
|
22588
|
+
"use strict";
|
|
22589
|
+
init_zod();
|
|
22590
|
+
init_digest();
|
|
22591
|
+
init_fs();
|
|
22592
|
+
RUN_TOKEN_PREFIX = "oxrt_";
|
|
22593
|
+
RUN_TOKEN_MAX_TTL_MS = 15 * 6e4;
|
|
22594
|
+
RUN_TOKEN_DEFAULT_TTL_MS = RUN_TOKEN_MAX_TTL_MS;
|
|
22595
|
+
RUN_TOKEN_STATIC_MAX_TTL_MS = 30 * 24 * 60 * 6e4;
|
|
22596
|
+
RUN_TOKEN_PROVIDERS = ["anthropic", "openai"];
|
|
22597
|
+
RUN_TOKEN_PLACEMENTS = ["helper", "static"];
|
|
22598
|
+
runTokenClaimsSchema = external_exports.object({
|
|
22599
|
+
v: external_exports.literal(1),
|
|
22600
|
+
/** `rt_` + 20 hex: cited on frames in place of the token. */
|
|
22601
|
+
tid: external_exports.string().regex(/^rt_[0-9a-f]{20}$/),
|
|
22602
|
+
/** The host enrollment the token was minted for. */
|
|
22603
|
+
host: external_exports.string().min(1).max(64),
|
|
22604
|
+
/** The harness it was handed to. */
|
|
22605
|
+
harness: external_exports.string().min(1).max(32),
|
|
22606
|
+
provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
|
|
22607
|
+
placement: external_exports.enum(RUN_TOKEN_PLACEMENTS),
|
|
22608
|
+
/** Epoch milliseconds. */
|
|
22609
|
+
iat: external_exports.number().int().nonnegative(),
|
|
22610
|
+
exp: external_exports.number().int().nonnegative()
|
|
22611
|
+
}).strict();
|
|
22612
|
+
KEY_BYTES = 32;
|
|
22613
|
+
keysSeen = /* @__PURE__ */ new Map();
|
|
22614
|
+
}
|
|
22615
|
+
});
|
|
22616
|
+
|
|
22617
|
+
// ../../packages/tacho/src/host/credential-store.ts
|
|
22618
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
|
|
22619
|
+
import {
|
|
22620
|
+
closeSync as closeSync4,
|
|
22621
|
+
existsSync as existsSync5,
|
|
22622
|
+
fsyncSync as fsyncSync3,
|
|
22623
|
+
openSync as openSync4,
|
|
22624
|
+
readFileSync as readFileSync7,
|
|
22625
|
+
statSync as statSync3,
|
|
22626
|
+
unlinkSync as unlinkSync4,
|
|
22627
|
+
writeSync as writeSync3
|
|
22628
|
+
} from "node:fs";
|
|
22629
|
+
function anthropicCredentialKind(secret, fallback) {
|
|
22630
|
+
return secret.trim().startsWith(CLAUDE_OAUTH_TOKEN_PREFIX) ? "oauth" : fallback;
|
|
22631
|
+
}
|
|
22632
|
+
function readKey(path2) {
|
|
22633
|
+
try {
|
|
22634
|
+
const hex = readFileSync7(path2, "utf8").trim();
|
|
22635
|
+
if (!/^[0-9a-f]{64}$/i.test(hex))
|
|
22636
|
+
throw new Error(`${path2} does not hold a 32-byte key`);
|
|
22637
|
+
return Buffer.from(hex, "hex");
|
|
22638
|
+
} catch (error) {
|
|
22639
|
+
if (error.code === "ENOENT") return void 0;
|
|
22640
|
+
throw error;
|
|
22641
|
+
}
|
|
22642
|
+
}
|
|
22643
|
+
function loadOrCreateKey(path2) {
|
|
22644
|
+
const existing = readKey(path2);
|
|
22645
|
+
if (existing !== void 0) return existing;
|
|
22646
|
+
const key = randomBytes5(KEY_BYTES2);
|
|
22647
|
+
writeSensitiveFileAtomic(path2, `${key.toString("hex")}
|
|
22648
|
+
`);
|
|
22649
|
+
return key;
|
|
22650
|
+
}
|
|
22651
|
+
function requireKey(path2) {
|
|
22652
|
+
const key = readKey(path2);
|
|
22653
|
+
if (key === void 0)
|
|
22654
|
+
throw new Error(
|
|
22655
|
+
`${path2} is missing, so the credentials in custody cannot be opened`
|
|
22656
|
+
);
|
|
22657
|
+
return key;
|
|
22658
|
+
}
|
|
22659
|
+
function overwriteInPlace(path2) {
|
|
22660
|
+
let fd2;
|
|
22661
|
+
try {
|
|
22662
|
+
const size = statSync3(path2).size;
|
|
22663
|
+
fd2 = openSync4(path2, "r+");
|
|
22664
|
+
writeSync3(fd2, randomBytes5(Math.max(size, KEY_BYTES2 * 2)), 0, void 0, 0);
|
|
22665
|
+
fsyncSync3(fd2);
|
|
22666
|
+
} catch (error) {
|
|
22667
|
+
if (error.code !== "ENOENT") throw error;
|
|
22668
|
+
} finally {
|
|
22669
|
+
if (fd2 !== void 0) closeSync4(fd2);
|
|
22670
|
+
}
|
|
22671
|
+
}
|
|
22672
|
+
function seal(key, secret, aad) {
|
|
22673
|
+
const nonce = randomBytes5(12);
|
|
22674
|
+
const cipher = createCipheriv("aes-256-gcm", key, nonce);
|
|
22675
|
+
cipher.setAAD(Buffer.from(aad, "utf8"));
|
|
22676
|
+
const ciphertext = Buffer.concat([
|
|
22677
|
+
cipher.update(secret, "utf8"),
|
|
22678
|
+
cipher.final()
|
|
22679
|
+
]);
|
|
22680
|
+
return {
|
|
22681
|
+
nonce: nonce.toString("base64"),
|
|
22682
|
+
ciphertext: ciphertext.toString("base64"),
|
|
22683
|
+
tag: cipher.getAuthTag().toString("base64")
|
|
22684
|
+
};
|
|
22685
|
+
}
|
|
22686
|
+
function open(key, sealed, aad) {
|
|
22687
|
+
const decipher = createDecipheriv(
|
|
22688
|
+
"aes-256-gcm",
|
|
22689
|
+
key,
|
|
22690
|
+
Buffer.from(sealed.nonce, "base64")
|
|
22691
|
+
);
|
|
22692
|
+
decipher.setAAD(Buffer.from(aad, "utf8"));
|
|
22693
|
+
decipher.setAuthTag(Buffer.from(sealed.tag, "base64"));
|
|
22694
|
+
return Buffer.concat([
|
|
22695
|
+
decipher.update(Buffer.from(sealed.ciphertext, "base64")),
|
|
22696
|
+
decipher.final()
|
|
22697
|
+
]).toString("utf8");
|
|
22698
|
+
}
|
|
22699
|
+
function credentialPrefix(secret) {
|
|
22700
|
+
const head2 = secret.slice(0, Math.min(8, Math.max(0, secret.length - 4)));
|
|
22701
|
+
return head2.length > 0 ? `${head2}\u2026` : "";
|
|
22702
|
+
}
|
|
22703
|
+
function custodyOf(entry2) {
|
|
22704
|
+
return {
|
|
22705
|
+
provider: entry2.provider,
|
|
22706
|
+
kind: entry2.kind,
|
|
22707
|
+
source: entry2.source,
|
|
22708
|
+
taken_at: entry2.taken_at,
|
|
22709
|
+
prefix: entry2.prefix
|
|
22710
|
+
};
|
|
22711
|
+
}
|
|
22712
|
+
function openCredentialStore(paths) {
|
|
22713
|
+
let entriesSeen;
|
|
22714
|
+
let keySeen;
|
|
22715
|
+
const keyForWrite = () => loadOrCreateKey(paths.key);
|
|
22716
|
+
function forgetKey() {
|
|
22717
|
+
keySeen?.key.fill(0);
|
|
22718
|
+
keySeen = void 0;
|
|
22719
|
+
}
|
|
22720
|
+
function keyForRead() {
|
|
22721
|
+
const version2 = fileVersion(paths.key);
|
|
22722
|
+
if (version2 !== void 0 && keySeen?.version === version2)
|
|
22723
|
+
return keySeen.key;
|
|
22724
|
+
forgetKey();
|
|
22725
|
+
const key = requireKey(paths.key);
|
|
22726
|
+
if (version2 !== void 0) keySeen = { version: version2, key };
|
|
22727
|
+
return key;
|
|
22728
|
+
}
|
|
22729
|
+
function readAll() {
|
|
22730
|
+
const version2 = fileVersion(paths.file);
|
|
22731
|
+
if (version2 === void 0) {
|
|
22732
|
+
entriesSeen = void 0;
|
|
22733
|
+
forgetKey();
|
|
22734
|
+
return [];
|
|
22735
|
+
}
|
|
22736
|
+
if (entriesSeen?.version === version2) return entriesSeen.entries;
|
|
22737
|
+
let text5;
|
|
22738
|
+
try {
|
|
22739
|
+
text5 = readFileSync7(paths.file, "utf8");
|
|
22740
|
+
} catch (error) {
|
|
22741
|
+
if (error.code === "ENOENT") return [];
|
|
22742
|
+
throw error;
|
|
22743
|
+
}
|
|
22744
|
+
let raw;
|
|
22745
|
+
try {
|
|
22746
|
+
raw = JSON.parse(text5);
|
|
22747
|
+
} catch (error) {
|
|
22748
|
+
throw new Error(
|
|
22749
|
+
`${paths.file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
22750
|
+
);
|
|
22751
|
+
}
|
|
22752
|
+
const parsed2 = fileSchema.safeParse(raw);
|
|
22753
|
+
if (!parsed2.success)
|
|
22754
|
+
throw new Error(
|
|
22755
|
+
`${paths.file} is not a credential store this build can read: ${parsed2.error.issues[0]?.message ?? "invalid"}`
|
|
22756
|
+
);
|
|
22757
|
+
entriesSeen = { version: version2, entries: parsed2.data.entries };
|
|
22758
|
+
return parsed2.data.entries;
|
|
22759
|
+
}
|
|
22760
|
+
function writeAll2(entries2) {
|
|
22761
|
+
if (entries2.length === 0) {
|
|
22762
|
+
if (existsSync5(paths.file)) unlinkSync4(paths.file);
|
|
22763
|
+
return;
|
|
22764
|
+
}
|
|
22765
|
+
writeSensitiveFileAtomic(
|
|
22766
|
+
paths.file,
|
|
22767
|
+
`${JSON.stringify({ schema: CREDENTIAL_STORE_SCHEMA, entries: entries2 }, null, 2)}
|
|
22768
|
+
`
|
|
22769
|
+
);
|
|
22770
|
+
}
|
|
22771
|
+
return {
|
|
22772
|
+
has: (provider) => readAll().some((e) => e.provider === provider),
|
|
22773
|
+
read: (provider) => {
|
|
22774
|
+
const entry2 = readAll().find((e) => e.provider === provider);
|
|
22775
|
+
if (entry2 === void 0) return void 0;
|
|
22776
|
+
return {
|
|
22777
|
+
kind: entry2.kind,
|
|
22778
|
+
secret: open(
|
|
22779
|
+
keyForRead(),
|
|
22780
|
+
entry2.sealed,
|
|
22781
|
+
`${CREDENTIAL_STORE_SCHEMA}:${provider}`
|
|
22782
|
+
)
|
|
22783
|
+
};
|
|
22784
|
+
},
|
|
22785
|
+
take: (provider, { kind, secret }, source, now) => {
|
|
22786
|
+
if (secret.trim().length === 0)
|
|
22787
|
+
throw new Error("refusing to take an empty credential into custody");
|
|
22788
|
+
const entry2 = {
|
|
22789
|
+
provider,
|
|
22790
|
+
kind,
|
|
22791
|
+
source,
|
|
22792
|
+
taken_at: new Date(now).toISOString(),
|
|
22793
|
+
// A ChatGPT login is JSON, whose head says nothing; it is named.
|
|
22794
|
+
prefix: kind === "chatgpt" ? "login" : credentialPrefix(secret),
|
|
22795
|
+
sealed: seal(
|
|
22796
|
+
keyForWrite(),
|
|
22797
|
+
secret,
|
|
22798
|
+
`${CREDENTIAL_STORE_SCHEMA}:${provider}`
|
|
22799
|
+
)
|
|
22800
|
+
};
|
|
22801
|
+
writeAll2([...readAll().filter((e) => e.provider !== provider), entry2]);
|
|
22802
|
+
return custodyOf(entry2);
|
|
22803
|
+
},
|
|
22804
|
+
release: (provider) => {
|
|
22805
|
+
const entries2 = readAll();
|
|
22806
|
+
const entry2 = entries2.find((e) => e.provider === provider);
|
|
22807
|
+
if (entry2 === void 0) return void 0;
|
|
22808
|
+
const secret = open(
|
|
22809
|
+
keyForRead(),
|
|
22810
|
+
entry2.sealed,
|
|
22811
|
+
`${CREDENTIAL_STORE_SCHEMA}:${provider}`
|
|
22812
|
+
);
|
|
22813
|
+
writeAll2(entries2.filter((e) => e.provider !== provider));
|
|
22814
|
+
return { kind: entry2.kind, secret };
|
|
22815
|
+
},
|
|
22816
|
+
status: () => readAll().map(custodyOf),
|
|
22817
|
+
shred: () => {
|
|
22818
|
+
let overwrite;
|
|
22819
|
+
try {
|
|
22820
|
+
overwriteInPlace(paths.key);
|
|
22821
|
+
} catch (error) {
|
|
22822
|
+
overwrite = error;
|
|
22823
|
+
}
|
|
22824
|
+
for (const path2 of [paths.file, paths.key])
|
|
22825
|
+
if (existsSync5(path2)) unlinkSync4(path2);
|
|
22826
|
+
entriesSeen = void 0;
|
|
22827
|
+
forgetKey();
|
|
22828
|
+
if (overwrite !== void 0)
|
|
22829
|
+
throw new Error(
|
|
22830
|
+
`${paths.key} was deleted but could not be overwritten first: ${overwrite instanceof Error ? overwrite.message : String(overwrite)}`
|
|
22831
|
+
);
|
|
22832
|
+
}
|
|
22833
|
+
};
|
|
22834
|
+
}
|
|
22835
|
+
var CREDENTIAL_STORE_SCHEMA, CREDENTIAL_SOURCES, CREDENTIAL_KINDS, CLAUDE_OAUTH_TOKEN_PREFIX, sealedSchema, entrySchema, fileSchema, KEY_BYTES2;
|
|
22836
|
+
var init_credential_store = __esm({
|
|
22837
|
+
"../../packages/tacho/src/host/credential-store.ts"() {
|
|
22838
|
+
"use strict";
|
|
22839
|
+
init_zod();
|
|
22840
|
+
init_fs();
|
|
22841
|
+
init_run_token();
|
|
22842
|
+
CREDENTIAL_STORE_SCHEMA = "tacho.credentials.v1";
|
|
22843
|
+
CREDENTIAL_SOURCES = [
|
|
22844
|
+
/** `env.ANTHROPIC_API_KEY` in Claude Code's user settings. */
|
|
22845
|
+
"claude-code:settings.env",
|
|
22846
|
+
/** `OPENAI_API_KEY`, or the ChatGPT login's `tokens`, in Codex's `auth.json`. */
|
|
22847
|
+
"codex:auth.json",
|
|
22848
|
+
/** A ChatGPT login the gateway refreshed, replacing the one it took. */
|
|
22849
|
+
"chatgpt:refresh",
|
|
22850
|
+
/**
|
|
22851
|
+
* `TACHO_BROKER_<PROVIDER>_API_KEY`, or `CLAUDE_CODE_OAUTH_TOKEN`, in the
|
|
22852
|
+
* enrolling shell.
|
|
22853
|
+
*/
|
|
22854
|
+
"enroll:env",
|
|
22855
|
+
/**
|
|
22856
|
+
* The subscription token `claude setup-token` printed, which
|
|
22857
|
+
* `oxagen credential login` took straight into custody.
|
|
22858
|
+
*/
|
|
22859
|
+
"claude:setup-token",
|
|
22860
|
+
/**
|
|
22861
|
+
* The run token in an Oxagen Agent Runners sandbox's `launch` message. It
|
|
22862
|
+
* is spent at the cloud gateway, which attaches the operator's model
|
|
22863
|
+
* credential. It is not a model credential itself.
|
|
22864
|
+
*/
|
|
22865
|
+
"sandbox:launch"
|
|
22866
|
+
];
|
|
22867
|
+
CREDENTIAL_KINDS = [
|
|
22868
|
+
"api_key",
|
|
22869
|
+
"bearer",
|
|
22870
|
+
"oauth",
|
|
22871
|
+
"chatgpt"
|
|
22872
|
+
];
|
|
22873
|
+
CLAUDE_OAUTH_TOKEN_PREFIX = "sk-ant-oat";
|
|
22874
|
+
sealedSchema = external_exports.object({
|
|
22875
|
+
/** AES-256-GCM: 12-byte nonce, ciphertext, 16-byte tag, all base64. */
|
|
22876
|
+
nonce: external_exports.string().min(1),
|
|
22877
|
+
ciphertext: external_exports.string().min(1),
|
|
22878
|
+
tag: external_exports.string().min(1)
|
|
22879
|
+
}).strict();
|
|
22880
|
+
entrySchema = external_exports.object({
|
|
22881
|
+
provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
|
|
22882
|
+
kind: external_exports.enum(CREDENTIAL_KINDS),
|
|
22883
|
+
source: external_exports.enum(CREDENTIAL_SOURCES),
|
|
22884
|
+
taken_at: external_exports.string(),
|
|
22885
|
+
/** The first few characters, the way a vendor console shows a key. */
|
|
22886
|
+
prefix: external_exports.string().max(12),
|
|
22887
|
+
sealed: sealedSchema
|
|
22888
|
+
}).strict();
|
|
22889
|
+
fileSchema = external_exports.object({
|
|
22890
|
+
schema: external_exports.literal(CREDENTIAL_STORE_SCHEMA),
|
|
22891
|
+
entries: external_exports.array(entrySchema)
|
|
22892
|
+
}).strict();
|
|
22893
|
+
KEY_BYTES2 = 32;
|
|
22894
|
+
}
|
|
22895
|
+
});
|
|
22896
|
+
|
|
22897
|
+
// ../../packages/tacho/src/host/claude-setup-token.ts
|
|
22898
|
+
import { spawn } from "node:child_process";
|
|
22899
|
+
import { accessSync as accessSync2, constants as constants2 } from "node:fs";
|
|
22900
|
+
import { delimiter, join as join13 } from "node:path";
|
|
22901
|
+
function isSubscriptionToken(value) {
|
|
22902
|
+
return TOKEN.test(value.trim());
|
|
22903
|
+
}
|
|
22904
|
+
function extractSetupToken(output) {
|
|
22905
|
+
const lines = output.replace(ANSI, "").split(/\r?\n|\r/).map((line) => line.trim());
|
|
22906
|
+
let found;
|
|
22907
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
22908
|
+
const line = lines[i2];
|
|
22909
|
+
const start = line.lastIndexOf(CLAUDE_OAUTH_TOKEN_PREFIX);
|
|
22910
|
+
if (start < 0) continue;
|
|
22911
|
+
let token = "";
|
|
22912
|
+
for (const char of line.slice(start)) {
|
|
22913
|
+
if (!TOKEN_CHAR.test(char)) break;
|
|
22914
|
+
token += char;
|
|
22915
|
+
}
|
|
22916
|
+
if (token.length === line.length - start)
|
|
22917
|
+
for (let j = i2 + 1; j < lines.length; j++) {
|
|
22918
|
+
const next = lines[j];
|
|
22919
|
+
if (next.length === 0 || ![...next].every((c) => TOKEN_CHAR.test(c)))
|
|
22920
|
+
break;
|
|
22921
|
+
token += next;
|
|
22922
|
+
}
|
|
22923
|
+
if (isSubscriptionToken(token)) found = token;
|
|
22924
|
+
}
|
|
22925
|
+
return found;
|
|
22926
|
+
}
|
|
22927
|
+
function findClaudeBinary(env, home) {
|
|
22928
|
+
return findHarnessBinary("claude", env, home);
|
|
22929
|
+
}
|
|
22930
|
+
function findHarnessBinary(name, env, home) {
|
|
22931
|
+
const file = process.platform === "win32" ? `${name}.exe` : name;
|
|
22932
|
+
const candidates = [
|
|
22933
|
+
...(env["PATH"] ?? "").split(delimiter).filter((d) => d.length > 0),
|
|
22934
|
+
join13(home, ".local", "bin"),
|
|
22935
|
+
join13(home, ".claude", "local"),
|
|
22936
|
+
join13(home, ".npm-global", "bin"),
|
|
22937
|
+
"/opt/homebrew/bin",
|
|
22938
|
+
"/usr/local/bin"
|
|
22939
|
+
].map((dir) => join13(dir, file));
|
|
22940
|
+
for (const path2 of candidates) {
|
|
22941
|
+
try {
|
|
22942
|
+
accessSync2(path2, constants2.X_OK);
|
|
22943
|
+
return path2;
|
|
22944
|
+
} catch {
|
|
22945
|
+
}
|
|
22946
|
+
}
|
|
22947
|
+
return void 0;
|
|
22948
|
+
}
|
|
22949
|
+
function setupTokenCommand(claude, platform) {
|
|
22950
|
+
const quoted3 = `'${claude.replace(/'/g, `'\\''`)}'`;
|
|
22951
|
+
const inner = `stty cols 1000 2>/dev/null; exec ${quoted3} setup-token`;
|
|
22952
|
+
if (platform === "darwin")
|
|
22953
|
+
return { file: "script", args: ["-q", "/dev/null", "sh", "-c", inner] };
|
|
22954
|
+
if (platform === "linux")
|
|
22955
|
+
return { file: "script", args: ["-q", "-e", "-c", inner, "/dev/null"] };
|
|
22956
|
+
return void 0;
|
|
22957
|
+
}
|
|
22958
|
+
function shellQuote(value) {
|
|
22959
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
22960
|
+
}
|
|
22961
|
+
function setupTokenShellLine(claude, platform) {
|
|
22962
|
+
const command2 = setupTokenCommand(claude, platform);
|
|
22963
|
+
if (command2 === void 0) return void 0;
|
|
22964
|
+
const script = [command2.file, ...command2.args].map(shellQuote).join(" ");
|
|
22965
|
+
return `sleep 2147483647 | { ${script}; echo "${SETUP_TOKEN_EXIT_MARK}$?"; }`;
|
|
22966
|
+
}
|
|
22967
|
+
function runClaudeSetupToken(options) {
|
|
22968
|
+
const command2 = setupTokenCommand(
|
|
22969
|
+
options.claude,
|
|
22970
|
+
options.platform ?? process.platform
|
|
22971
|
+
);
|
|
22972
|
+
return new Promise((resolve25, reject) => {
|
|
22973
|
+
if (command2 === void 0) {
|
|
22974
|
+
const child2 = spawn(options.claude, ["setup-token"], {
|
|
22975
|
+
stdio: "inherit",
|
|
22976
|
+
env: options.env ?? process.env
|
|
22977
|
+
});
|
|
22978
|
+
child2.on("error", reject);
|
|
22979
|
+
child2.on(
|
|
22980
|
+
"close",
|
|
22981
|
+
(code) => resolve25({ token: void 0, code: code ?? 1, captured: false })
|
|
22982
|
+
);
|
|
22983
|
+
return;
|
|
22984
|
+
}
|
|
22985
|
+
const env = options.env ?? process.env;
|
|
22986
|
+
if (options.interactive) {
|
|
22987
|
+
const child2 = spawn(command2.file, command2.args, {
|
|
22988
|
+
stdio: ["inherit", "pipe", "inherit"],
|
|
22989
|
+
env
|
|
22990
|
+
});
|
|
22991
|
+
let output2 = "";
|
|
22992
|
+
child2.stdout?.setEncoding("utf8");
|
|
22993
|
+
child2.stdout?.on("data", (chunk2) => {
|
|
22994
|
+
output2 += chunk2;
|
|
22995
|
+
options.write(chunk2);
|
|
22996
|
+
});
|
|
22997
|
+
child2.on("error", reject);
|
|
22998
|
+
child2.on(
|
|
22999
|
+
"close",
|
|
23000
|
+
(code) => resolve25({
|
|
23001
|
+
token: extractSetupToken(output2),
|
|
23002
|
+
code: code ?? 1,
|
|
23003
|
+
captured: true
|
|
23004
|
+
})
|
|
23005
|
+
);
|
|
23006
|
+
return;
|
|
23007
|
+
}
|
|
23008
|
+
const line = setupTokenShellLine(
|
|
23009
|
+
options.claude,
|
|
23010
|
+
options.platform ?? process.platform
|
|
23011
|
+
);
|
|
23012
|
+
const child = spawn("sh", ["-c", line], {
|
|
23013
|
+
detached: true,
|
|
23014
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
23015
|
+
env
|
|
23016
|
+
});
|
|
23017
|
+
let output = "";
|
|
23018
|
+
let exited;
|
|
23019
|
+
const mark = new RegExp(`${SETUP_TOKEN_EXIT_MARK}(\\d+)`);
|
|
23020
|
+
const stopGroup = () => {
|
|
23021
|
+
try {
|
|
23022
|
+
if (child.pid !== void 0) process.kill(-child.pid, "SIGTERM");
|
|
23023
|
+
} catch {
|
|
23024
|
+
}
|
|
23025
|
+
};
|
|
23026
|
+
child.stdout?.setEncoding("utf8");
|
|
23027
|
+
child.stdout?.on("data", (chunk2) => {
|
|
23028
|
+
output += chunk2;
|
|
23029
|
+
const found = mark.exec(output);
|
|
23030
|
+
options.write(chunk2.replace(mark, "").replace(SETUP_TOKEN_EXIT_MARK, ""));
|
|
23031
|
+
if (found !== null && exited === void 0) {
|
|
23032
|
+
exited = Number(found[1]);
|
|
23033
|
+
stopGroup();
|
|
23034
|
+
}
|
|
23035
|
+
});
|
|
23036
|
+
child.on("error", reject);
|
|
23037
|
+
child.on(
|
|
23038
|
+
"close",
|
|
23039
|
+
(code) => resolve25({
|
|
23040
|
+
token: extractSetupToken(output.replace(mark, "")),
|
|
23041
|
+
code: exited ?? code ?? 1,
|
|
23042
|
+
captured: true
|
|
23043
|
+
})
|
|
23044
|
+
);
|
|
23045
|
+
});
|
|
23046
|
+
}
|
|
23047
|
+
var TOKEN, TOKEN_CHAR, ANSI, SETUP_TOKEN_EXIT_MARK;
|
|
23048
|
+
var init_claude_setup_token = __esm({
|
|
23049
|
+
"../../packages/tacho/src/host/claude-setup-token.ts"() {
|
|
23050
|
+
"use strict";
|
|
23051
|
+
init_credential_store();
|
|
23052
|
+
TOKEN = /^sk-ant-oat\d{2}-[A-Za-z0-9_-]{20,}$/;
|
|
23053
|
+
TOKEN_CHAR = /[A-Za-z0-9_-]/;
|
|
23054
|
+
ANSI = // biome-ignore lint/suspicious/noControlCharactersInRegex: terminal escapes
|
|
23055
|
+
/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|\u001b\[[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_]/g;
|
|
23056
|
+
SETUP_TOKEN_EXIT_MARK = "__oxagen_setup_token_exit__=";
|
|
23057
|
+
}
|
|
23058
|
+
});
|
|
23059
|
+
|
|
22425
23060
|
// ../../packages/tacho/src/host/model-base-url-toml.ts
|
|
22426
23061
|
function splitLines(text5) {
|
|
22427
23062
|
const lines = [];
|
|
@@ -22539,8 +23174,8 @@ var init_model_base_url_toml = __esm({
|
|
|
22539
23174
|
});
|
|
22540
23175
|
|
|
22541
23176
|
// ../../packages/tacho/src/host/model-base-url.ts
|
|
22542
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
22543
|
-
import { basename as basename4, dirname as dirname7, join as
|
|
23177
|
+
import { chmodSync as chmodSync3, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync as unlinkSync5 } from "node:fs";
|
|
23178
|
+
import { basename as basename4, dirname as dirname7, join as join14 } from "node:path";
|
|
22544
23179
|
function claudeToolSearchEnabled(value) {
|
|
22545
23180
|
return typeof value === "string" && /^(true|auto(:\d+)?)$/i.test(value);
|
|
22546
23181
|
}
|
|
@@ -22554,19 +23189,19 @@ function isModelProxyBaseUrl(harness, value) {
|
|
|
22554
23189
|
}
|
|
22555
23190
|
function modelBaseUrlFile(harness, homes) {
|
|
22556
23191
|
if (harness === "claude-code")
|
|
22557
|
-
return
|
|
23192
|
+
return join14(
|
|
22558
23193
|
homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
|
|
22559
23194
|
"settings.json"
|
|
22560
23195
|
);
|
|
22561
23196
|
if (harness === "codex")
|
|
22562
|
-
return
|
|
23197
|
+
return join14(
|
|
22563
23198
|
homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
|
|
22564
23199
|
"config.toml"
|
|
22565
23200
|
);
|
|
22566
|
-
const stellaHome = homes.stellaHome ??
|
|
22567
|
-
const toml =
|
|
22568
|
-
const json2 =
|
|
22569
|
-
return !
|
|
23201
|
+
const stellaHome = homes.stellaHome ?? join14(homes.home, ".stella");
|
|
23202
|
+
const toml = join14(stellaHome, "stella.toml");
|
|
23203
|
+
const json2 = join14(stellaHome, "settings.json");
|
|
23204
|
+
return !existsSync6(toml) && existsSync6(json2) ? json2 : toml;
|
|
22570
23205
|
}
|
|
22571
23206
|
function keyLabel(harness) {
|
|
22572
23207
|
if (harness === "claude-code") return `env.${CLAUDE_KEY}`;
|
|
@@ -22576,7 +23211,7 @@ function isJsonFile(file) {
|
|
|
22576
23211
|
return file.endsWith(".json");
|
|
22577
23212
|
}
|
|
22578
23213
|
function sidecarBeside(file, contract) {
|
|
22579
|
-
return
|
|
23214
|
+
return join14(dirname7(file), `.${basename4(file)}.${contract}.json`);
|
|
22580
23215
|
}
|
|
22581
23216
|
function sidecarFor(file) {
|
|
22582
23217
|
return sidecarBeside(file, "oxagen-model-base-url");
|
|
@@ -22707,9 +23342,9 @@ function stellaManagedSettingsPath(platform = process.platform, env = process.en
|
|
|
22707
23342
|
const named = env["STELLA_MANAGED_SETTINGS"];
|
|
22708
23343
|
if (named !== void 0 && named.length > 0) return named;
|
|
22709
23344
|
const dir = platform === "darwin" ? "/Library/Application Support/stella" : "/etc/stella";
|
|
22710
|
-
const toml =
|
|
22711
|
-
const json2 =
|
|
22712
|
-
return !
|
|
23345
|
+
const toml = join14(dir, "stella.toml");
|
|
23346
|
+
const json2 = join14(dir, "settings.json");
|
|
23347
|
+
return !existsSync6(toml) && existsSync6(json2) ? json2 : toml;
|
|
22713
23348
|
}
|
|
22714
23349
|
function managedShadow(harness, managedFile, expected) {
|
|
22715
23350
|
try {
|
|
@@ -22917,7 +23552,7 @@ function describe3(harness, options, { changed, text: text5, sidecar, unreadable
|
|
|
22917
23552
|
}
|
|
22918
23553
|
function readBytesIfExists(path2) {
|
|
22919
23554
|
try {
|
|
22920
|
-
return
|
|
23555
|
+
return readFileSync8(path2);
|
|
22921
23556
|
} catch (error) {
|
|
22922
23557
|
if (error.code === "ENOENT") return void 0;
|
|
22923
23558
|
throw error;
|
|
@@ -23031,7 +23666,7 @@ function restoreOne(harness, options) {
|
|
|
23031
23666
|
const { sidecar, unreadable } = sidecarOrWhy(backup, harness);
|
|
23032
23667
|
const text5 = readTextIfExists(file);
|
|
23033
23668
|
const dropSidecar = () => {
|
|
23034
|
-
if (
|
|
23669
|
+
if (existsSync6(backup)) unlinkSync5(backup);
|
|
23035
23670
|
};
|
|
23036
23671
|
const after = (changed, left) => ({
|
|
23037
23672
|
changed,
|
|
@@ -23050,7 +23685,7 @@ function restoreOne(harness, options) {
|
|
|
23050
23685
|
writeFileAtomicPreserving(file, original);
|
|
23051
23686
|
left = original.toString("utf8");
|
|
23052
23687
|
} else {
|
|
23053
|
-
|
|
23688
|
+
unlinkSync5(file);
|
|
23054
23689
|
}
|
|
23055
23690
|
dropSidecar();
|
|
23056
23691
|
return after(true, left);
|
|
@@ -23163,186 +23798,66 @@ var init_model_base_url = __esm({
|
|
|
23163
23798
|
}
|
|
23164
23799
|
});
|
|
23165
23800
|
|
|
23166
|
-
// ../../packages/tacho/src/host/
|
|
23167
|
-
import {
|
|
23168
|
-
import {
|
|
23169
|
-
function
|
|
23170
|
-
|
|
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"));
|
|
23801
|
+
// ../../packages/tacho/src/host/model-credential.ts
|
|
23802
|
+
import { chmodSync as chmodSync4, existsSync as existsSync7, lstatSync as lstatSync2, unlinkSync as unlinkSync6 } from "node:fs";
|
|
23803
|
+
import { join as join15 } from "node:path";
|
|
23804
|
+
function claudeMemberFor(kind) {
|
|
23805
|
+
return kind === "oauth" ? CLAUDE_OAUTH_TOKEN : kind === "bearer" ? CLAUDE_AUTH_TOKEN : CLAUDE_API_KEY;
|
|
23185
23806
|
}
|
|
23186
|
-
function
|
|
23187
|
-
|
|
23188
|
-
|
|
23189
|
-
|
|
23190
|
-
|
|
23191
|
-
|
|
23192
|
-
|
|
23193
|
-
|
|
23807
|
+
function claudeEnvCredentials(env) {
|
|
23808
|
+
const found = [];
|
|
23809
|
+
for (const [member3, kind] of CLAUDE_MEMBERS) {
|
|
23810
|
+
const value = env[member3];
|
|
23811
|
+
if (isSecret(value))
|
|
23812
|
+
found.push({
|
|
23813
|
+
member: member3,
|
|
23814
|
+
credential: {
|
|
23815
|
+
kind: anthropicCredentialKind(value, kind),
|
|
23816
|
+
secret: value
|
|
23817
|
+
}
|
|
23818
|
+
});
|
|
23194
23819
|
}
|
|
23195
|
-
|
|
23196
|
-
writeSensitiveFileAtomic(path2, `${key.bytes.toString("hex")}
|
|
23197
|
-
`);
|
|
23198
|
-
return { key, created: true };
|
|
23820
|
+
return found;
|
|
23199
23821
|
}
|
|
23200
|
-
function
|
|
23201
|
-
const
|
|
23202
|
-
if (
|
|
23203
|
-
keysSeen.delete(path2);
|
|
23822
|
+
function chatGptTokensOf(auth) {
|
|
23823
|
+
const tokens = auth[CODEX_TOKENS];
|
|
23824
|
+
if (typeof tokens !== "object" || tokens === null || Array.isArray(tokens))
|
|
23204
23825
|
return void 0;
|
|
23205
|
-
|
|
23206
|
-
|
|
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();
|
|
23826
|
+
const record = tokens;
|
|
23827
|
+
return isSecret(record["access_token"]) ? record : void 0;
|
|
23223
23828
|
}
|
|
23224
|
-
function
|
|
23225
|
-
const
|
|
23226
|
-
|
|
23227
|
-
|
|
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
|
|
23829
|
+
function encodeChatGptLogin(tokens) {
|
|
23830
|
+
const login = {
|
|
23831
|
+
access_token: tokens["access_token"],
|
|
23832
|
+
refresh_token: typeof tokens["refresh_token"] === "string" ? tokens["refresh_token"] : ""
|
|
23242
23833
|
};
|
|
23243
|
-
|
|
23244
|
-
|
|
23245
|
-
|
|
23246
|
-
|
|
23247
|
-
|
|
23248
|
-
return typeof value === "string" && value.startsWith(RUN_TOKEN_PREFIX) && value.length < 2048;
|
|
23834
|
+
if (typeof tokens["id_token"] === "string")
|
|
23835
|
+
login.id_token = tokens["id_token"];
|
|
23836
|
+
if (typeof tokens["account_id"] === "string")
|
|
23837
|
+
login.account_id = tokens["account_id"];
|
|
23838
|
+
return JSON.stringify(login);
|
|
23249
23839
|
}
|
|
23250
|
-
function
|
|
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;
|
|
23840
|
+
function decodeChatGptLogin(secret) {
|
|
23255
23841
|
try {
|
|
23256
|
-
const
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23842
|
+
const raw = JSON.parse(secret);
|
|
23843
|
+
if (typeof raw.access_token !== "string" || raw.access_token.length === 0)
|
|
23844
|
+
return void 0;
|
|
23845
|
+
return {
|
|
23846
|
+
access_token: raw.access_token,
|
|
23847
|
+
refresh_token: typeof raw.refresh_token === "string" ? raw.refresh_token : "",
|
|
23848
|
+
...typeof raw.id_token === "string" ? { id_token: raw.id_token } : {},
|
|
23849
|
+
...typeof raw.account_id === "string" ? { account_id: raw.account_id } : {}
|
|
23850
|
+
};
|
|
23261
23851
|
} catch {
|
|
23262
23852
|
return void 0;
|
|
23263
23853
|
}
|
|
23264
23854
|
}
|
|
23265
|
-
function
|
|
23266
|
-
|
|
23267
|
-
|
|
23268
|
-
const
|
|
23269
|
-
|
|
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
|
-
}
|
|
23855
|
+
function codexTokenMember(auth) {
|
|
23856
|
+
const tokens = chatGptTokensOf(auth);
|
|
23857
|
+
if (tokens !== void 0) return tokens["access_token"];
|
|
23858
|
+
const key = auth[CODEX_KEY2];
|
|
23859
|
+
return typeof key === "string" ? key : void 0;
|
|
23310
23860
|
}
|
|
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
23861
|
function isTachoHelper(value) {
|
|
23347
23862
|
return typeof value === "string" && HELPER_MARK.test(value);
|
|
23348
23863
|
}
|
|
@@ -23350,10 +23865,10 @@ function helperCommandFor(tachoCommand) {
|
|
|
23350
23865
|
return `${tachoCommand} credential issue --harness claude-code`;
|
|
23351
23866
|
}
|
|
23352
23867
|
function fileFor2(harness, homes) {
|
|
23353
|
-
return harness === "claude-code" ?
|
|
23868
|
+
return harness === "claude-code" ? join15(
|
|
23354
23869
|
homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
|
|
23355
23870
|
"settings.json"
|
|
23356
|
-
) :
|
|
23871
|
+
) : join15(
|
|
23357
23872
|
homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
|
|
23358
23873
|
"auth.json"
|
|
23359
23874
|
);
|
|
@@ -23396,7 +23911,9 @@ function readSidecar2(path2, harness) {
|
|
|
23396
23911
|
existed: parsed2["existed"] === true,
|
|
23397
23912
|
written_sha256: written2,
|
|
23398
23913
|
taken: Array.isArray(taken) ? taken.filter(
|
|
23399
|
-
(t) => typeof t === "object" && t !== null && typeof t.member === "string" && (
|
|
23914
|
+
(t) => typeof t === "object" && t !== null && typeof t.member === "string" && CREDENTIAL_KINDS.includes(
|
|
23915
|
+
t.kind
|
|
23916
|
+
)
|
|
23400
23917
|
) : []
|
|
23401
23918
|
};
|
|
23402
23919
|
if (harness === "codex") return { ...common, harness };
|
|
@@ -23419,7 +23936,7 @@ function isSecret(value) {
|
|
|
23419
23936
|
return typeof value === "string" && value.length > 0;
|
|
23420
23937
|
}
|
|
23421
23938
|
function holdsBothClaudeMembers(env) {
|
|
23422
|
-
return
|
|
23939
|
+
return CLAUDE_MEMBERS.filter(([member3]) => isSecret(env[member3])).length > 1;
|
|
23423
23940
|
}
|
|
23424
23941
|
function managedHelperShadow(managedFile, expected) {
|
|
23425
23942
|
try {
|
|
@@ -23469,7 +23986,7 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
|
|
|
23469
23986
|
const current = settings[HELPER_KEY];
|
|
23470
23987
|
helper = typeof current === "string" ? current : null;
|
|
23471
23988
|
const env = envOf(settings) ?? {};
|
|
23472
|
-
brokered = isTachoHelper(current) &&
|
|
23989
|
+
brokered = isTachoHelper(current) && CLAUDE_MEMBERS.every(([member3]) => typeof env[member3] !== "string");
|
|
23473
23990
|
if (!brokered && holdsBothClaudeMembers(env)) why ??= "two_credentials";
|
|
23474
23991
|
const managed = managedHelperShadow(
|
|
23475
23992
|
internals.managedSettingsFile ?? claudeManagedSettingsPath(),
|
|
@@ -23479,14 +23996,14 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
|
|
|
23479
23996
|
if (managed.unreadable !== void 0) unreadable.push(managed.unreadable);
|
|
23480
23997
|
} else {
|
|
23481
23998
|
const auth = parseForReport(text5, file, unreadable);
|
|
23482
|
-
const key = auth
|
|
23483
|
-
brokered = looksLikeRunToken(
|
|
23999
|
+
const key = codexTokenMember(auth);
|
|
24000
|
+
brokered = looksLikeRunToken(key);
|
|
23484
24001
|
if (brokered) {
|
|
23485
24002
|
const claims = peekRunTokenClaims(key);
|
|
23486
24003
|
if (claims !== void 0)
|
|
23487
24004
|
tokenExpiresAt = new Date(claims.exp).toISOString();
|
|
23488
24005
|
}
|
|
23489
|
-
if (!brokered && why === void 0 && auth[
|
|
24006
|
+
if (!brokered && why === void 0 && auth[CODEX_TOKENS] !== void 0)
|
|
23490
24007
|
why = "subscription_login";
|
|
23491
24008
|
}
|
|
23492
24009
|
return {
|
|
@@ -23519,21 +24036,15 @@ function applyClaude(options, taken) {
|
|
|
23519
24036
|
return { changed: false, text: text5, reason: "two_credentials" };
|
|
23520
24037
|
const rest = { ...env ?? {} };
|
|
23521
24038
|
const took = [];
|
|
23522
|
-
for (const
|
|
23523
|
-
|
|
23524
|
-
|
|
23525
|
-
|
|
23526
|
-
|
|
23527
|
-
|
|
23528
|
-
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
credential: { kind, secret: value },
|
|
23532
|
-
member: `env.${member3}`
|
|
23533
|
-
});
|
|
23534
|
-
took.push({ member: member3, kind });
|
|
23535
|
-
delete rest[member3];
|
|
23536
|
-
}
|
|
24039
|
+
for (const { member: member3, credential } of claudeEnvCredentials(rest)) {
|
|
24040
|
+
taken.push({
|
|
24041
|
+
harness: "claude-code",
|
|
24042
|
+
provider: "anthropic",
|
|
24043
|
+
credential,
|
|
24044
|
+
member: `env.${member3}`
|
|
24045
|
+
});
|
|
24046
|
+
took.push({ member: member3, kind: credential.kind });
|
|
24047
|
+
delete rest[member3];
|
|
23537
24048
|
}
|
|
23538
24049
|
const helperCommand = options.helperCommand;
|
|
23539
24050
|
if (helperCommand === void 0)
|
|
@@ -23581,7 +24092,49 @@ function applyCodex(options, taken) {
|
|
|
23581
24092
|
const existing = readSidecar2(backup, "codex");
|
|
23582
24093
|
const current = auth[CODEX_KEY2];
|
|
23583
24094
|
const hasKey = typeof current === "string" && current.length > 0;
|
|
23584
|
-
|
|
24095
|
+
const tokens = chatGptTokensOf(auth);
|
|
24096
|
+
if (tokens !== void 0) {
|
|
24097
|
+
const access2 = tokens["access_token"];
|
|
24098
|
+
const ours = looksLikeRunToken(access2);
|
|
24099
|
+
if (!ours)
|
|
24100
|
+
taken.push({
|
|
24101
|
+
harness: "codex",
|
|
24102
|
+
provider: "openai",
|
|
24103
|
+
credential: { kind: "chatgpt", secret: encodeChatGptLogin(tokens) },
|
|
24104
|
+
member: CODEX_TOKENS
|
|
24105
|
+
});
|
|
24106
|
+
if (token === void 0) {
|
|
24107
|
+
if (!ours) taken.pop();
|
|
24108
|
+
return { changed: false, text: text5, reason: "no_token" };
|
|
24109
|
+
}
|
|
24110
|
+
if (access2 === token && existing !== void 0)
|
|
24111
|
+
return { changed: false, text: text5 };
|
|
24112
|
+
auth[CODEX_TOKENS] = {
|
|
24113
|
+
...tokens,
|
|
24114
|
+
access_token: token,
|
|
24115
|
+
refresh_token: CHATGPT_REFRESH_HELD
|
|
24116
|
+
};
|
|
24117
|
+
auth[CODEX_LAST_REFRESH] = (/* @__PURE__ */ new Date()).toISOString();
|
|
24118
|
+
const next2 = serializeJsonLike(text5, auth);
|
|
24119
|
+
const sidecar2 = existing ? {
|
|
24120
|
+
...existing,
|
|
24121
|
+
written_sha256: sha256Hex(next2),
|
|
24122
|
+
taken: [
|
|
24123
|
+
...existing.taken.filter((t) => t.member !== CODEX_TOKENS),
|
|
24124
|
+
{ member: CODEX_TOKENS, kind: "chatgpt" }
|
|
24125
|
+
]
|
|
24126
|
+
} : {
|
|
24127
|
+
schema: SIDECAR_SCHEMA2,
|
|
24128
|
+
harness: "codex",
|
|
24129
|
+
existed: true,
|
|
24130
|
+
written_sha256: sha256Hex(next2),
|
|
24131
|
+
taken: [{ member: CODEX_TOKENS, kind: "chatgpt" }]
|
|
24132
|
+
};
|
|
24133
|
+
writeSidecar(backup, sidecar2);
|
|
24134
|
+
writeFileAtomicPreserving(file, next2);
|
|
24135
|
+
return { changed: true, text: next2 };
|
|
24136
|
+
}
|
|
24137
|
+
if (auth[CODEX_TOKENS] !== void 0 && !looksLikeRunToken(typeof current === "string" ? current : void 0)) {
|
|
23585
24138
|
return { changed: false, text: text5, reason: "subscription_login" };
|
|
23586
24139
|
}
|
|
23587
24140
|
if (hasKey && !looksLikeRunToken(current)) {
|
|
@@ -23616,11 +24169,11 @@ function restoreClaude(options, released, sidecar) {
|
|
|
23616
24169
|
const backup = sidecarFor2(file);
|
|
23617
24170
|
const text5 = readTextIfExists(file);
|
|
23618
24171
|
const dropSidecar = () => {
|
|
23619
|
-
if (
|
|
24172
|
+
if (existsSync7(backup)) unlinkSync6(backup);
|
|
23620
24173
|
};
|
|
23621
24174
|
if (text5 === void 0) {
|
|
23622
24175
|
if (released !== void 0) {
|
|
23623
|
-
const member3 = released.kind
|
|
24176
|
+
const member3 = claudeMemberFor(released.kind);
|
|
23624
24177
|
const written2 = serializeJsonLike(void 0, {
|
|
23625
24178
|
env: { [member3]: released.secret }
|
|
23626
24179
|
});
|
|
@@ -23640,11 +24193,11 @@ function restoreClaude(options, released, sidecar) {
|
|
|
23640
24193
|
touched = true;
|
|
23641
24194
|
}
|
|
23642
24195
|
const current = envOf(settings);
|
|
23643
|
-
const foreignKey = current !== void 0 && (
|
|
24196
|
+
const foreignKey = current !== void 0 && CLAUDE_MEMBERS.some(([member3]) => isSecret(current[member3]));
|
|
23644
24197
|
const why = foreignKey ? { reason: "foreign_key_present" } : {};
|
|
23645
24198
|
let secretRestored = false;
|
|
23646
24199
|
if (released !== void 0 && !foreignKey) {
|
|
23647
|
-
const member3 = sidecar?.taken.find((t) => t.kind === released.kind)?.member ?? (released.kind
|
|
24200
|
+
const member3 = sidecar?.taken.find((t) => t.kind === released.kind)?.member ?? claudeMemberFor(released.kind);
|
|
23648
24201
|
const env = { ...current ?? {} };
|
|
23649
24202
|
env[member3] = released.secret;
|
|
23650
24203
|
settings["env"] = env;
|
|
@@ -23674,7 +24227,7 @@ function restoreCodex(options, released, sidecar) {
|
|
|
23674
24227
|
const backup = sidecarFor2(file);
|
|
23675
24228
|
const text5 = readTextIfExists(file);
|
|
23676
24229
|
const dropSidecar = () => {
|
|
23677
|
-
if (
|
|
24230
|
+
if (existsSync7(backup)) unlinkSync6(backup);
|
|
23678
24231
|
};
|
|
23679
24232
|
if (text5 === void 0) {
|
|
23680
24233
|
if (released !== void 0) {
|
|
@@ -23689,6 +24242,18 @@ function restoreCodex(options, released, sidecar) {
|
|
|
23689
24242
|
return { changed: false, text: text5, secretRestored: false };
|
|
23690
24243
|
}
|
|
23691
24244
|
const auth = parseJsonObject(text5, file);
|
|
24245
|
+
const tokens = chatGptTokensOf(auth);
|
|
24246
|
+
if (tokens !== void 0 && looksLikeRunToken(tokens["access_token"])) {
|
|
24247
|
+
const login = released?.kind === "chatgpt" ? decodeChatGptLogin(released.secret) : void 0;
|
|
24248
|
+
if (login !== void 0) {
|
|
24249
|
+
auth[CODEX_TOKENS] = { ...tokens, ...login };
|
|
24250
|
+
auth[CODEX_LAST_REFRESH] = (/* @__PURE__ */ new Date()).toISOString();
|
|
24251
|
+
} else delete auth[CODEX_TOKENS];
|
|
24252
|
+
const next2 = serializeJsonLike(text5, auth);
|
|
24253
|
+
writeFileAtomicPreserving(file, next2);
|
|
24254
|
+
dropSidecar();
|
|
24255
|
+
return { changed: true, text: next2, secretRestored: login !== void 0 };
|
|
24256
|
+
}
|
|
23692
24257
|
const current = auth[CODEX_KEY2];
|
|
23693
24258
|
if (!looksLikeRunToken(typeof current === "string" ? current : void 0)) {
|
|
23694
24259
|
dropSidecar();
|
|
@@ -23702,7 +24267,7 @@ function restoreCodex(options, released, sidecar) {
|
|
|
23702
24267
|
if (released !== void 0) auth[CODEX_KEY2] = released.secret;
|
|
23703
24268
|
else delete auth[CODEX_KEY2];
|
|
23704
24269
|
if (sidecar !== void 0 && !sidecar.existed && Object.keys(auth).length === 0) {
|
|
23705
|
-
|
|
24270
|
+
unlinkSync6(file);
|
|
23706
24271
|
dropSidecar();
|
|
23707
24272
|
return {
|
|
23708
24273
|
changed: true,
|
|
@@ -23765,22 +24330,30 @@ async function peekModelCredentials(options) {
|
|
|
23765
24330
|
if (harness === "claude-code") {
|
|
23766
24331
|
const env = envOf(document) ?? {};
|
|
23767
24332
|
if (holdsBothClaudeMembers(env)) continue;
|
|
23768
|
-
for (const
|
|
23769
|
-
|
|
23770
|
-
|
|
23771
|
-
|
|
23772
|
-
|
|
23773
|
-
|
|
24333
|
+
for (const { member: member3, credential } of claudeEnvCredentials(env))
|
|
24334
|
+
taken.push({
|
|
24335
|
+
harness,
|
|
24336
|
+
provider: "anthropic",
|
|
24337
|
+
credential,
|
|
24338
|
+
member: `env.${member3}`
|
|
24339
|
+
});
|
|
24340
|
+
} else {
|
|
24341
|
+
const tokens = chatGptTokensOf(document);
|
|
24342
|
+
if (tokens !== void 0) {
|
|
24343
|
+
if (!looksLikeRunToken(tokens["access_token"]))
|
|
23774
24344
|
taken.push({
|
|
23775
24345
|
harness,
|
|
23776
|
-
provider: "
|
|
23777
|
-
credential: {
|
|
23778
|
-
|
|
24346
|
+
provider: "openai",
|
|
24347
|
+
credential: {
|
|
24348
|
+
kind: "chatgpt",
|
|
24349
|
+
secret: encodeChatGptLogin(tokens)
|
|
24350
|
+
},
|
|
24351
|
+
member: CODEX_TOKENS
|
|
23779
24352
|
});
|
|
24353
|
+
continue;
|
|
23780
24354
|
}
|
|
23781
|
-
} else {
|
|
23782
24355
|
const current = document[CODEX_KEY2];
|
|
23783
|
-
if (document[
|
|
24356
|
+
if (document[CODEX_TOKENS] === void 0 && typeof current === "string" && current.length > 0 && !looksLikeRunToken(current))
|
|
23784
24357
|
taken.push({
|
|
23785
24358
|
harness,
|
|
23786
24359
|
provider: "openai",
|
|
@@ -23806,8 +24379,8 @@ function hasOrphanedModelCredential(harness, home, dirs = {}) {
|
|
|
23806
24379
|
try {
|
|
23807
24380
|
const document = parseJsonObject(text5, file);
|
|
23808
24381
|
if (harness === "claude-code") return isTachoHelper(document[HELPER_KEY]);
|
|
23809
|
-
const key = document
|
|
23810
|
-
return looksLikeRunToken(
|
|
24382
|
+
const key = codexTokenMember(document);
|
|
24383
|
+
return looksLikeRunToken(key);
|
|
23811
24384
|
} catch {
|
|
23812
24385
|
return text5.includes("credential issue --harness") || text5.includes("oxrt_");
|
|
23813
24386
|
}
|
|
@@ -23829,24 +24402,46 @@ function staticTokenStillGood(token, host, keyPath2, now) {
|
|
|
23829
24402
|
});
|
|
23830
24403
|
return verdict.ok && verdict.claims.exp - now > staticTokenRenewWindowMs(host, now);
|
|
23831
24404
|
}
|
|
24405
|
+
function restateCodexLastRefresh(home, now, dirs = {}) {
|
|
24406
|
+
const file = fileFor2("codex", { ...dirs, home });
|
|
24407
|
+
const text5 = readTextIfExists(file);
|
|
24408
|
+
if (text5 === void 0 || isSymlink(file)) return false;
|
|
24409
|
+
const auth = parseJsonObject(text5, file);
|
|
24410
|
+
const tokens = chatGptTokensOf(auth);
|
|
24411
|
+
if (tokens === void 0 || !looksLikeRunToken(tokens["access_token"]))
|
|
24412
|
+
return false;
|
|
24413
|
+
const last = Date.parse(
|
|
24414
|
+
typeof auth[CODEX_LAST_REFRESH] === "string" ? auth[CODEX_LAST_REFRESH] : ""
|
|
24415
|
+
);
|
|
24416
|
+
if (Number.isFinite(last) && now - last < CODEX_LAST_REFRESH_RESTATE_MS)
|
|
24417
|
+
return false;
|
|
24418
|
+
auth[CODEX_LAST_REFRESH] = new Date(now).toISOString();
|
|
24419
|
+
const next = serializeJsonLike(text5, auth);
|
|
24420
|
+
const backup = sidecarFor2(file);
|
|
24421
|
+
const sidecar = readSidecar2(backup, "codex");
|
|
24422
|
+
if (sidecar !== void 0)
|
|
24423
|
+
writeSidecar(backup, { ...sidecar, written_sha256: sha256Hex(next) });
|
|
24424
|
+
writeFileAtomicPreserving(file, next);
|
|
24425
|
+
return true;
|
|
24426
|
+
}
|
|
23832
24427
|
function readCodexApiKeyMember(home, dirs = {}) {
|
|
23833
24428
|
const file = fileFor2("codex", { ...dirs, home });
|
|
23834
24429
|
try {
|
|
23835
24430
|
const text5 = readTextIfExists(file);
|
|
23836
24431
|
if (text5 === void 0) return void 0;
|
|
23837
|
-
|
|
23838
|
-
return typeof value === "string" ? value : void 0;
|
|
24432
|
+
return codexTokenMember(parseJsonObject(text5, file));
|
|
23839
24433
|
} catch {
|
|
23840
24434
|
return void 0;
|
|
23841
24435
|
}
|
|
23842
24436
|
}
|
|
23843
|
-
var HARNESS_PROVIDER, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CODEX_KEY2, HELPER_MARK, STATIC_TOKEN_RENEW_WINDOW_MS;
|
|
24437
|
+
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
24438
|
var init_model_credential = __esm({
|
|
23845
24439
|
"../../packages/tacho/src/host/model-credential.ts"() {
|
|
23846
24440
|
"use strict";
|
|
23847
24441
|
init_digest();
|
|
23848
24442
|
init_fs();
|
|
23849
24443
|
init_wire();
|
|
24444
|
+
init_credential_store();
|
|
23850
24445
|
init_model_base_url();
|
|
23851
24446
|
init_paths();
|
|
23852
24447
|
init_run_token();
|
|
@@ -23855,14 +24450,24 @@ var init_model_credential = __esm({
|
|
|
23855
24450
|
HELPER_KEY = "apiKeyHelper";
|
|
23856
24451
|
CLAUDE_API_KEY = "ANTHROPIC_API_KEY";
|
|
23857
24452
|
CLAUDE_AUTH_TOKEN = "ANTHROPIC_AUTH_TOKEN";
|
|
24453
|
+
CLAUDE_OAUTH_TOKEN = "CLAUDE_CODE_OAUTH_TOKEN";
|
|
24454
|
+
CLAUDE_MEMBERS = [
|
|
24455
|
+
[CLAUDE_API_KEY, "api_key"],
|
|
24456
|
+
[CLAUDE_AUTH_TOKEN, "bearer"],
|
|
24457
|
+
[CLAUDE_OAUTH_TOKEN, "oauth"]
|
|
24458
|
+
];
|
|
23858
24459
|
CODEX_KEY2 = "OPENAI_API_KEY";
|
|
24460
|
+
CODEX_TOKENS = "tokens";
|
|
24461
|
+
CODEX_LAST_REFRESH = "last_refresh";
|
|
24462
|
+
CHATGPT_REFRESH_HELD = "held-by-oxagen-gateway";
|
|
23859
24463
|
HELPER_MARK = /\bcredential issue --harness claude-code\s*$/;
|
|
23860
24464
|
STATIC_TOKEN_RENEW_WINDOW_MS = 7 * 24 * 60 * 6e4;
|
|
24465
|
+
CODEX_LAST_REFRESH_RESTATE_MS = 5 * 24 * 60 * 6e4;
|
|
23861
24466
|
}
|
|
23862
24467
|
});
|
|
23863
24468
|
|
|
23864
24469
|
// ../../packages/tacho/src/cli/credential.ts
|
|
23865
|
-
import { existsSync as
|
|
24470
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
23866
24471
|
import { dirname as dirname8 } from "node:path";
|
|
23867
24472
|
function harnessDirsOf(deps) {
|
|
23868
24473
|
return {
|
|
@@ -23982,15 +24587,15 @@ function describeHarness(entry2) {
|
|
|
23982
24587
|
}
|
|
23983
24588
|
switch (entry2.reason) {
|
|
23984
24589
|
case "subscription_login":
|
|
23985
|
-
return `${label2}: signed in with a ChatGPT login
|
|
24590
|
+
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
24591
|
case "symlink":
|
|
23987
24592
|
return `${label2}: ${entry2.file} is a symbolic link, so it was left alone; its own credential crosses the proxy`;
|
|
23988
24593
|
case "no_file":
|
|
23989
24594
|
return `${label2}: no ${entry2.file} yet; its own credential crosses the proxy`;
|
|
23990
24595
|
case "two_credentials":
|
|
23991
|
-
return `${label2}: ${entry2.file} sets
|
|
24596
|
+
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
24597
|
default:
|
|
23993
|
-
return `${label2}: holds its own credential, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
|
|
24598
|
+
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
24599
|
}
|
|
23995
24600
|
}
|
|
23996
24601
|
async function brokerCredentials(host, harnesses, deps) {
|
|
@@ -24018,11 +24623,17 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24018
24623
|
if (!offered.has(provider) && typeof fromEnv === "string" && fromEnv.trim().length > 0)
|
|
24019
24624
|
offered.set(provider, {
|
|
24020
24625
|
credential: {
|
|
24021
|
-
kind: provider === "anthropic" ? "api_key" : "bearer",
|
|
24626
|
+
kind: provider === "anthropic" ? anthropicCredentialKind(fromEnv, "api_key") : "bearer",
|
|
24022
24627
|
secret: fromEnv.trim()
|
|
24023
24628
|
},
|
|
24024
24629
|
source: "enroll:env"
|
|
24025
24630
|
});
|
|
24631
|
+
const oauth = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
|
|
24632
|
+
if (provider === "anthropic" && !offered.has(provider) && oauth !== void 0 && isSubscriptionToken(oauth))
|
|
24633
|
+
offered.set(provider, {
|
|
24634
|
+
credential: { kind: "oauth", secret: oauth },
|
|
24635
|
+
source: "enroll:env"
|
|
24636
|
+
});
|
|
24026
24637
|
}
|
|
24027
24638
|
const takenNow = [];
|
|
24028
24639
|
const staticTokens = {};
|
|
@@ -24033,8 +24644,7 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24033
24644
|
takenNow.push(provider);
|
|
24034
24645
|
}
|
|
24035
24646
|
if (targets.includes("codex") && store.has("openai")) {
|
|
24036
|
-
|
|
24037
|
-
if (before?.reason !== "subscription_login") {
|
|
24647
|
+
{
|
|
24038
24648
|
const current = readCodexApiKeyMember(deps.home, harnessDirsOf(deps));
|
|
24039
24649
|
if (!staticTokenStillGood(
|
|
24040
24650
|
current,
|
|
@@ -24085,7 +24695,7 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24085
24695
|
reason: "no_token"
|
|
24086
24696
|
};
|
|
24087
24697
|
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
|
|
24698
|
+
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
24699
|
);
|
|
24090
24700
|
}
|
|
24091
24701
|
continue;
|
|
@@ -24131,7 +24741,7 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
|
|
|
24131
24741
|
try {
|
|
24132
24742
|
if (host !== void 0 && !host.harnesses.includes(harness)) {
|
|
24133
24743
|
const dirs = harnessDirsOf(deps);
|
|
24134
|
-
const hasReceipt =
|
|
24744
|
+
const hasReceipt = existsSync8(
|
|
24135
24745
|
modelCredentialBackupPath(harness, deps.home, dirs)
|
|
24136
24746
|
);
|
|
24137
24747
|
if (!hasReceipt && !hasOrphanedModelCredential(harness, deps.home, dirs))
|
|
@@ -24141,8 +24751,13 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
|
|
|
24141
24751
|
const label2 = TACHO_HARNESS_LABELS[harness];
|
|
24142
24752
|
let released;
|
|
24143
24753
|
let unreadable;
|
|
24754
|
+
let fromLogin = false;
|
|
24144
24755
|
try {
|
|
24145
|
-
|
|
24756
|
+
fromLogin = store?.status().some(
|
|
24757
|
+
(c) => c.provider === provider && c.source === "claude:setup-token"
|
|
24758
|
+
) === true;
|
|
24759
|
+
if (fromLogin) store?.release(provider);
|
|
24760
|
+
else released = store?.read(provider);
|
|
24146
24761
|
} catch (error) {
|
|
24147
24762
|
unreadable = error instanceof Error ? error.message : String(error);
|
|
24148
24763
|
}
|
|
@@ -24186,10 +24801,200 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
|
|
|
24186
24801
|
}
|
|
24187
24802
|
return { restored, failed: failed3, warnings, custodyUnreadable };
|
|
24188
24803
|
}
|
|
24189
|
-
|
|
24804
|
+
async function acquireSubscriptionToken(options, deps) {
|
|
24805
|
+
const io = deps.subscriptionLogin;
|
|
24806
|
+
if (options.tokenStdin === true) {
|
|
24807
|
+
const text5 = (await io?.readStdin())?.trim() ?? "";
|
|
24808
|
+
return isSubscriptionToken(text5) ? { token: text5, source: "claude:setup-token" } : {
|
|
24809
|
+
detail: "stdin did not hold a Claude subscription token (sk-ant-oat\u2026). Pipe in what `claude setup-token` printed"
|
|
24810
|
+
};
|
|
24811
|
+
}
|
|
24812
|
+
const fromEnv = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
|
|
24813
|
+
if (fromEnv !== void 0 && isSubscriptionToken(fromEnv))
|
|
24814
|
+
return { token: fromEnv, source: "enroll:env" };
|
|
24815
|
+
if (io === void 0)
|
|
24816
|
+
return {
|
|
24817
|
+
detail: `no Claude subscription token: set ${CLAUDE_OAUTH_TOKEN_ENV} or pipe one in with --token-stdin`
|
|
24818
|
+
};
|
|
24819
|
+
const claude = io.findClaude();
|
|
24820
|
+
if (claude === void 0)
|
|
24821
|
+
return {
|
|
24822
|
+
detail: "Claude Code is not installed, so `claude setup-token` cannot run. Install it, or pipe a token in with --token-stdin"
|
|
24823
|
+
};
|
|
24824
|
+
deps.out(
|
|
24825
|
+
"Signing in to your Claude subscription with `claude setup-token`. Finish the sign-in in your browser."
|
|
24826
|
+
);
|
|
24827
|
+
const run3 = await io.runSetupToken(
|
|
24828
|
+
claude,
|
|
24829
|
+
(chunk2) => process.stdout.write(
|
|
24830
|
+
chunk2.replace(/sk-ant-oat[A-Za-z0-9_-]*/g, "sk-ant-oat\u2026 (sent to Oxagen)")
|
|
24831
|
+
)
|
|
24832
|
+
);
|
|
24833
|
+
if (run3.token !== void 0)
|
|
24834
|
+
return { token: run3.token, source: "claude:setup-token" };
|
|
24835
|
+
if (io.interactive) {
|
|
24836
|
+
const pasted = await io.promptSecret(
|
|
24837
|
+
"Paste the token `claude setup-token` printed (sk-ant-oat\u2026): "
|
|
24838
|
+
);
|
|
24839
|
+
if (pasted !== void 0 && isSubscriptionToken(pasted))
|
|
24840
|
+
return { token: pasted, source: "claude:setup-token" };
|
|
24841
|
+
return {
|
|
24842
|
+
detail: pasted === void 0 || pasted.length === 0 ? "no token was pasted" : "that is not a Claude subscription token (sk-ant-oat\u2026)"
|
|
24843
|
+
};
|
|
24844
|
+
}
|
|
24845
|
+
return {
|
|
24846
|
+
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`
|
|
24847
|
+
};
|
|
24848
|
+
}
|
|
24849
|
+
async function signInIfNoCredential(harnesses, deps) {
|
|
24850
|
+
const store = deps.credentialStore;
|
|
24851
|
+
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({
|
|
24852
|
+
home: deps.home,
|
|
24853
|
+
harnesses: ["codex"]
|
|
24854
|
+
}) ?? []).length === 0) {
|
|
24855
|
+
const why = await ensureCodexLogin(deps);
|
|
24856
|
+
if (why !== void 0)
|
|
24857
|
+
deps.err(
|
|
24858
|
+
`warning: ChatGPT sign-in for Codex did not finish (${why}). Run \`oxagen credential login --harness codex\` to try again`
|
|
24859
|
+
);
|
|
24860
|
+
}
|
|
24861
|
+
if (!harnesses.includes("claude-code") || store === void 0 || deps.subscriptionLogin?.interactive !== true || store.has("anthropic"))
|
|
24862
|
+
return;
|
|
24863
|
+
if ([brokerEnvVar("anthropic"), CLAUDE_OAUTH_TOKEN_ENV].some(
|
|
24864
|
+
(name) => (deps.env[name]?.trim() ?? "").length > 0
|
|
24865
|
+
))
|
|
24866
|
+
return;
|
|
24867
|
+
const inFile = await deps.modelCredentials?.peek({
|
|
24868
|
+
home: deps.home,
|
|
24869
|
+
harnesses: ["claude-code"],
|
|
24870
|
+
helperCommand: deps.runtime.credentialHelperCommand
|
|
24871
|
+
});
|
|
24872
|
+
if ((inFile ?? []).some((t) => t.provider === "anthropic")) return;
|
|
24873
|
+
const acquired = await acquireSubscriptionToken({}, deps);
|
|
24874
|
+
if (acquired.token === void 0) {
|
|
24875
|
+
deps.err(
|
|
24876
|
+
`warning: Claude subscription sign-in did not finish (${acquired.detail}). Run \`oxagen credential login\` to try again`
|
|
24877
|
+
);
|
|
24878
|
+
return;
|
|
24879
|
+
}
|
|
24880
|
+
const custody = store.take(
|
|
24881
|
+
"anthropic",
|
|
24882
|
+
{ kind: "oauth", secret: acquired.token },
|
|
24883
|
+
acquired.source,
|
|
24884
|
+
deps.now()
|
|
24885
|
+
);
|
|
24886
|
+
deps.out(
|
|
24887
|
+
` Claude subscription ${custody.prefix} taken into the gateway's custody`
|
|
24888
|
+
);
|
|
24889
|
+
}
|
|
24890
|
+
async function routedToGateway(host, harness, deps) {
|
|
24891
|
+
if (deps.modelBaseUrls === void 0) return false;
|
|
24892
|
+
try {
|
|
24893
|
+
const state = await deps.modelBaseUrls.read({
|
|
24894
|
+
home: deps.home,
|
|
24895
|
+
stellaHome: dirname8(deps.paths.stellaToml),
|
|
24896
|
+
port: modelProxyPortFor(host),
|
|
24897
|
+
harnesses: [harness]
|
|
24898
|
+
});
|
|
24899
|
+
const entry2 = state.harnesses.find((h) => h.harness === harness);
|
|
24900
|
+
return entry2?.ours === true && entry2.shadowedBy === void 0;
|
|
24901
|
+
} catch {
|
|
24902
|
+
return false;
|
|
24903
|
+
}
|
|
24904
|
+
}
|
|
24905
|
+
async function codexHasChatGptLogin(deps) {
|
|
24906
|
+
const offered = await deps.modelCredentials?.peek({
|
|
24907
|
+
home: deps.home,
|
|
24908
|
+
harnesses: ["codex"]
|
|
24909
|
+
});
|
|
24910
|
+
return (offered ?? []).some((t) => t.credential.kind === "chatgpt");
|
|
24911
|
+
}
|
|
24912
|
+
async function ensureCodexLogin(deps) {
|
|
24913
|
+
if (await codexHasChatGptLogin(deps)) return void 0;
|
|
24914
|
+
const io = deps.subscriptionLogin;
|
|
24915
|
+
const codex = io?.findCodex();
|
|
24916
|
+
if (io === void 0 || codex === void 0)
|
|
24917
|
+
return "Codex is not installed, so `codex login` cannot run. Install it and try again";
|
|
24918
|
+
deps.out(
|
|
24919
|
+
"Signing in to ChatGPT with `codex login`. Finish the sign-in in your browser."
|
|
24920
|
+
);
|
|
24921
|
+
const code = await io.runCodexLogin(codex);
|
|
24922
|
+
if (code !== 0) return `\`codex login\` exited with ${code}`;
|
|
24923
|
+
return await codexHasChatGptLogin(deps) ? void 0 : "`codex login` finished, but ~/.codex/auth.json holds no ChatGPT login";
|
|
24924
|
+
}
|
|
24925
|
+
async function codexLogin(deps) {
|
|
24926
|
+
const store = deps.credentialStore;
|
|
24927
|
+
const held = store?.status().some(
|
|
24928
|
+
(c) => c.provider === "openai" && c.kind === "chatgpt"
|
|
24929
|
+
);
|
|
24930
|
+
if (held !== true) {
|
|
24931
|
+
const why = await ensureCodexLogin(deps);
|
|
24932
|
+
if (why !== void 0)
|
|
24933
|
+
return { ok: false, harnesses: [], warnings: [], detail: why };
|
|
24934
|
+
}
|
|
24935
|
+
const host = readHostFileLenient(deps.paths.hostFile).host;
|
|
24936
|
+
if (host === void 0 || !host.harnesses.includes("codex") || !await routedToGateway(host, "codex", deps))
|
|
24937
|
+
return {
|
|
24938
|
+
ok: true,
|
|
24939
|
+
harnesses: [],
|
|
24940
|
+
warnings: [],
|
|
24941
|
+
detail: "Codex is signed in to ChatGPT. Run `oxagen agent enroll --harness codex` to route it through the gateway, which then holds the login"
|
|
24942
|
+
};
|
|
24943
|
+
const outcome = await brokerCredentials(host, ["codex"], deps);
|
|
24944
|
+
return {
|
|
24945
|
+
ok: true,
|
|
24946
|
+
source: "codex:auth.json",
|
|
24947
|
+
harnesses: outcome.harnesses,
|
|
24948
|
+
warnings: outcome.warnings,
|
|
24949
|
+
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"
|
|
24950
|
+
};
|
|
24951
|
+
}
|
|
24952
|
+
async function credentialLogin(options, deps) {
|
|
24953
|
+
const store = deps.credentialStore;
|
|
24954
|
+
if (store === void 0)
|
|
24955
|
+
return {
|
|
24956
|
+
ok: false,
|
|
24957
|
+
harnesses: [],
|
|
24958
|
+
warnings: [],
|
|
24959
|
+
detail: "this build holds no credential store"
|
|
24960
|
+
};
|
|
24961
|
+
if (options.harness === "codex") return codexLogin(deps);
|
|
24962
|
+
const acquired = await acquireSubscriptionToken(options, deps);
|
|
24963
|
+
if (acquired.token === void 0)
|
|
24964
|
+
return { ok: false, harnesses: [], warnings: [], detail: acquired.detail };
|
|
24965
|
+
const custody = store.take(
|
|
24966
|
+
"anthropic",
|
|
24967
|
+
{ kind: "oauth", secret: acquired.token },
|
|
24968
|
+
acquired.source,
|
|
24969
|
+
deps.now()
|
|
24970
|
+
);
|
|
24971
|
+
deps.out(
|
|
24972
|
+
`Claude subscription ${custody.prefix} taken into the gateway's custody (${deps.paths.credentials})`
|
|
24973
|
+
);
|
|
24974
|
+
const host = readHostFileLenient(deps.paths.hostFile).host;
|
|
24975
|
+
if (host === void 0 || !host.harnesses.includes("claude-code") || !await routedToGateway(host, "claude-code", deps))
|
|
24976
|
+
return {
|
|
24977
|
+
ok: true,
|
|
24978
|
+
source: acquired.source,
|
|
24979
|
+
harnesses: [],
|
|
24980
|
+
warnings: [],
|
|
24981
|
+
detail: "Run `oxagen agent enroll` to route Claude Code through the gateway. It will use this subscription"
|
|
24982
|
+
};
|
|
24983
|
+
const outcome = await brokerCredentials(host, ["claude-code"], deps);
|
|
24984
|
+
return {
|
|
24985
|
+
ok: true,
|
|
24986
|
+
source: acquired.source,
|
|
24987
|
+
harnesses: outcome.harnesses,
|
|
24988
|
+
warnings: outcome.warnings,
|
|
24989
|
+
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"
|
|
24990
|
+
};
|
|
24991
|
+
}
|
|
24992
|
+
var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, CLAUDE_OAUTH_TOKEN_ENV;
|
|
24190
24993
|
var init_credential = __esm({
|
|
24191
24994
|
"../../packages/tacho/src/cli/credential.ts"() {
|
|
24192
24995
|
"use strict";
|
|
24996
|
+
init_claude_setup_token();
|
|
24997
|
+
init_credential_store();
|
|
24193
24998
|
init_host_file();
|
|
24194
24999
|
init_model_credential();
|
|
24195
25000
|
init_agents();
|
|
@@ -24202,11 +25007,12 @@ var init_credential = __esm({
|
|
|
24202
25007
|
"brokered",
|
|
24203
25008
|
"passthrough"
|
|
24204
25009
|
];
|
|
25010
|
+
CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
|
|
24205
25011
|
}
|
|
24206
25012
|
});
|
|
24207
25013
|
|
|
24208
25014
|
// ../../packages/tacho/src/host/codex-app-server.ts
|
|
24209
|
-
import { spawn } from "node:child_process";
|
|
25015
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
24210
25016
|
function cmdArgument(value) {
|
|
24211
25017
|
let quoted3 = "";
|
|
24212
25018
|
let slashes = 0;
|
|
@@ -24244,7 +25050,7 @@ function codexAppServerClient(options) {
|
|
|
24244
25050
|
let child;
|
|
24245
25051
|
try {
|
|
24246
25052
|
const invocation = spawnInvocation(options.binary, ["app-server"]);
|
|
24247
|
-
child =
|
|
25053
|
+
child = spawn2(invocation.command, invocation.args, {
|
|
24248
25054
|
cwd: options.cwd,
|
|
24249
25055
|
env: options.env,
|
|
24250
25056
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -24377,266 +25183,6 @@ var init_codex_app_server = __esm({
|
|
|
24377
25183
|
}
|
|
24378
25184
|
});
|
|
24379
25185
|
|
|
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
25186
|
// ../../packages/tacho/src/host/claude-config-lock.ts
|
|
24641
25187
|
import { mkdirSync as mkdirSync4, rmdirSync as rmdirSync3, statSync as statSync4 } from "node:fs";
|
|
24642
25188
|
function sleepSync(ms) {
|
|
@@ -24709,7 +25255,7 @@ var init_claude_config_lock = __esm({
|
|
|
24709
25255
|
});
|
|
24710
25256
|
|
|
24711
25257
|
// ../../packages/tacho/src/host/service.ts
|
|
24712
|
-
import { join as
|
|
25258
|
+
import { join as join16 } from "node:path";
|
|
24713
25259
|
import {
|
|
24714
25260
|
existsSync as existsSync9,
|
|
24715
25261
|
readdirSync as readdirSync6,
|
|
@@ -24794,8 +25340,8 @@ function blockingSleep(ms) {
|
|
|
24794
25340
|
}
|
|
24795
25341
|
function launchdManager(options) {
|
|
24796
25342
|
const sleep2 = options.sleep ?? blockingSleep;
|
|
24797
|
-
const dir =
|
|
24798
|
-
const unitPath =
|
|
25343
|
+
const dir = join16(options.home, "Library", "LaunchAgents");
|
|
25344
|
+
const unitPath = join16(dir, `${SERVICE_LABEL}.plist`);
|
|
24799
25345
|
const domain = `gui/${options.uid ?? process.getuid?.() ?? 501}`;
|
|
24800
25346
|
const target2 = `${domain}/${SERVICE_LABEL}`;
|
|
24801
25347
|
const loaded = () => options.exec("launchctl", ["print", target2]).status === 0;
|
|
@@ -24900,9 +25446,9 @@ function stopByPidFile(options) {
|
|
|
24900
25446
|
);
|
|
24901
25447
|
}
|
|
24902
25448
|
function systemdManager(options) {
|
|
24903
|
-
const dir =
|
|
24904
|
-
const unitPath =
|
|
24905
|
-
const wants =
|
|
25449
|
+
const dir = join16(options.home, ".config", "systemd", "user");
|
|
25450
|
+
const unitPath = join16(dir, "tachod.service");
|
|
25451
|
+
const wants = join16(dir, "default.target.wants");
|
|
24906
25452
|
return {
|
|
24907
25453
|
kind: "systemd",
|
|
24908
25454
|
unitPath,
|
|
@@ -25053,8 +25599,8 @@ function windowsCreatedMs(exec4, pid) {
|
|
|
25053
25599
|
return result2.status === 0 && /^\d+$/.test(text5) ? Number(text5) : void 0;
|
|
25054
25600
|
}
|
|
25055
25601
|
function schtasksManager(options) {
|
|
25056
|
-
const launcher = options.launcherPath ??
|
|
25057
|
-
const pidPath = options.pidPath ??
|
|
25602
|
+
const launcher = options.launcherPath ?? join16(options.home, "tachod.cmd");
|
|
25603
|
+
const pidPath = options.pidPath ?? join16(launcher, "..", "tachod.pid");
|
|
25058
25604
|
const liveRecord = () => {
|
|
25059
25605
|
if (!existsSync9(pidPath)) return void 0;
|
|
25060
25606
|
const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
|
|
@@ -25099,7 +25645,7 @@ function schtasksManager(options) {
|
|
|
25099
25645
|
unitPath: launcher,
|
|
25100
25646
|
install: (spec) => {
|
|
25101
25647
|
stopDaemon();
|
|
25102
|
-
ensureDir(
|
|
25648
|
+
ensureDir(join16(launcher, ".."), 493);
|
|
25103
25649
|
writeSensitiveFileAtomic(
|
|
25104
25650
|
launcher,
|
|
25105
25651
|
renderWindowsLauncher(
|
|
@@ -25231,7 +25777,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
|
|
|
25231
25777
|
import { dirname as dirname9, resolve as resolve3 } from "node:path";
|
|
25232
25778
|
import { fileURLToPath } from "node:url";
|
|
25233
25779
|
function readVersion() {
|
|
25234
|
-
if (true) return "2.1.4-
|
|
25780
|
+
if (true) return "2.1.4-975";
|
|
25235
25781
|
try {
|
|
25236
25782
|
const here = dirname9(fileURLToPath(import.meta.url));
|
|
25237
25783
|
const pkg = JSON.parse(
|
|
@@ -25251,7 +25797,7 @@ var init_version = __esm({
|
|
|
25251
25797
|
});
|
|
25252
25798
|
|
|
25253
25799
|
// ../../packages/tacho/src/cli/deps.ts
|
|
25254
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
25800
|
+
import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
|
|
25255
25801
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
25256
25802
|
import { existsSync as existsSync10 } from "node:fs";
|
|
25257
25803
|
import { get as httpGet, request as httpRequest } from "node:http";
|
|
@@ -25316,7 +25862,7 @@ function transientBinDir(binDir, env = process.env) {
|
|
|
25316
25862
|
return "an AppImage mount";
|
|
25317
25863
|
return void 0;
|
|
25318
25864
|
}
|
|
25319
|
-
function
|
|
25865
|
+
function shellQuote2(value, platform = process.platform) {
|
|
25320
25866
|
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(value)) return value;
|
|
25321
25867
|
if (platform === "win32") return `"${value}"`;
|
|
25322
25868
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -25367,8 +25913,8 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
|
|
|
25367
25913
|
const dir = P.dirname(tacho);
|
|
25368
25914
|
const transient2 = transientBinDir(dir, env);
|
|
25369
25915
|
return {
|
|
25370
|
-
hookCommand: `${
|
|
25371
|
-
credentialHelperCommand: helperCommandFor(
|
|
25916
|
+
hookCommand: `${shellQuote2(tacho, platform)} hook`,
|
|
25917
|
+
credentialHelperCommand: helperCommandFor(shellQuote2(tacho, platform)),
|
|
25372
25918
|
daemonCommand: [tacho, "daemon"],
|
|
25373
25919
|
mcpStdioCommand: [tacho, "mcp-stdio"],
|
|
25374
25920
|
binDir: dir,
|
|
@@ -25382,9 +25928,9 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
|
|
|
25382
25928
|
const flagged = transient !== void 0 ? { transient } : {};
|
|
25383
25929
|
const node2 = stableExecutablePath(nodePath, platform, exists);
|
|
25384
25930
|
return {
|
|
25385
|
-
hookCommand: `${
|
|
25931
|
+
hookCommand: `${shellQuote2(node2, platform)} ${shellQuote2(P.join(binDir, "tacho-hook.mjs"), platform)}`,
|
|
25386
25932
|
credentialHelperCommand: helperCommandFor(
|
|
25387
|
-
`${
|
|
25933
|
+
`${shellQuote2(node2, platform)} ${shellQuote2(P.join(binDir, "tacho.mjs"), platform)}`
|
|
25388
25934
|
),
|
|
25389
25935
|
daemonCommand: [node2, P.join(binDir, "tachod.mjs")],
|
|
25390
25936
|
mcpStdioCommand: [node2, P.join(binDir, "tacho.mjs"), "mcp-stdio"],
|
|
@@ -25412,7 +25958,7 @@ function oxagenRuntimeCommands(entry2 = process.argv[1], env = process.env, node
|
|
|
25412
25958
|
} else {
|
|
25413
25959
|
return runtimeCommands(void 0, env, nodePath, platform, false, exists);
|
|
25414
25960
|
}
|
|
25415
|
-
const line = prefix.map((word) =>
|
|
25961
|
+
const line = prefix.map((word) => shellQuote2(word, platform)).join(" ");
|
|
25416
25962
|
const binDir = P.dirname(prefix[prefix.length - 1]);
|
|
25417
25963
|
const transient = transientBinDir(binDir, env);
|
|
25418
25964
|
return {
|
|
@@ -25550,6 +26096,63 @@ function settleOrThrow(harnessFiles) {
|
|
|
25550
26096
|
);
|
|
25551
26097
|
return outcomes;
|
|
25552
26098
|
}
|
|
26099
|
+
function promptHidden(question) {
|
|
26100
|
+
const stdin = process.stdin;
|
|
26101
|
+
if (!stdin.isTTY) return Promise.resolve(void 0);
|
|
26102
|
+
return new Promise((resolve25) => {
|
|
26103
|
+
process.stderr.write(question);
|
|
26104
|
+
let value = "";
|
|
26105
|
+
stdin.setRawMode(true);
|
|
26106
|
+
stdin.resume();
|
|
26107
|
+
stdin.setEncoding("utf8");
|
|
26108
|
+
const done = (answer) => {
|
|
26109
|
+
stdin.setRawMode(false);
|
|
26110
|
+
stdin.pause();
|
|
26111
|
+
stdin.off("data", onData);
|
|
26112
|
+
process.stderr.write("\n");
|
|
26113
|
+
resolve25(answer);
|
|
26114
|
+
};
|
|
26115
|
+
const onData = (chunk2) => {
|
|
26116
|
+
for (const char of chunk2) {
|
|
26117
|
+
if (char === "\r" || char === "\n") return done(value.trim());
|
|
26118
|
+
if (char === "" || char === "") return done(void 0);
|
|
26119
|
+
if (char === "\x7F" || char === "\b") value = value.slice(0, -1);
|
|
26120
|
+
else value += char;
|
|
26121
|
+
}
|
|
26122
|
+
};
|
|
26123
|
+
stdin.on("data", onData);
|
|
26124
|
+
});
|
|
26125
|
+
}
|
|
26126
|
+
function realSubscriptionLogin(env, home) {
|
|
26127
|
+
return {
|
|
26128
|
+
interactive: process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
26129
|
+
findClaude: () => findClaudeBinary(env, home),
|
|
26130
|
+
runSetupToken: (claude, write) => runClaudeSetupToken({
|
|
26131
|
+
claude,
|
|
26132
|
+
interactive: process.stdin.isTTY === true,
|
|
26133
|
+
write
|
|
26134
|
+
}),
|
|
26135
|
+
promptSecret: promptHidden,
|
|
26136
|
+
findCodex: () => findHarnessBinary("codex", env, home),
|
|
26137
|
+
runCodexLogin: (codex) => new Promise((resolve25, reject) => {
|
|
26138
|
+
const child = spawn3(codex, ["login"], {
|
|
26139
|
+
stdio: [process.stdin.isTTY ? "inherit" : "pipe", "inherit", "inherit"],
|
|
26140
|
+
env: process.env
|
|
26141
|
+
});
|
|
26142
|
+
child.on("error", reject);
|
|
26143
|
+
child.on("close", (code) => {
|
|
26144
|
+
child.stdin?.destroy();
|
|
26145
|
+
resolve25(code ?? 1);
|
|
26146
|
+
});
|
|
26147
|
+
}),
|
|
26148
|
+
readStdin: async () => {
|
|
26149
|
+
let text5 = "";
|
|
26150
|
+
process.stdin.setEncoding("utf8");
|
|
26151
|
+
for await (const chunk2 of process.stdin) text5 += chunk2;
|
|
26152
|
+
return text5;
|
|
26153
|
+
}
|
|
26154
|
+
};
|
|
26155
|
+
}
|
|
25553
26156
|
function defaultCliDeps(overrides = {}, managed = {}) {
|
|
25554
26157
|
const env = overrides.env ?? process.env;
|
|
25555
26158
|
const home = overrides.home ?? homedir3();
|
|
@@ -25739,6 +26342,7 @@ function defaultCliDeps(overrides = {}, managed = {}) {
|
|
|
25739
26342
|
file: paths.credentials,
|
|
25740
26343
|
key: paths.credentialsKey
|
|
25741
26344
|
}),
|
|
26345
|
+
subscriptionLogin: realSubscriptionLogin(env, home),
|
|
25742
26346
|
daemonPost: async (path2, body2) => {
|
|
25743
26347
|
const host = readHostFile(paths.hostFile);
|
|
25744
26348
|
if (host === void 0) return void 0;
|
|
@@ -25832,6 +26436,7 @@ var init_deps = __esm({
|
|
|
25832
26436
|
"../../packages/tacho/src/cli/deps.ts"() {
|
|
25833
26437
|
"use strict";
|
|
25834
26438
|
init_hook_client();
|
|
26439
|
+
init_claude_setup_token();
|
|
25835
26440
|
init_codex_app_server();
|
|
25836
26441
|
init_credential_store();
|
|
25837
26442
|
init_claude_config_lock();
|
|
@@ -26657,7 +27262,7 @@ import {
|
|
|
26657
27262
|
unlinkSync as unlinkSync8
|
|
26658
27263
|
} from "node:fs";
|
|
26659
27264
|
import { open as open2 } from "node:fs/promises";
|
|
26660
|
-
import { join as
|
|
27265
|
+
import { join as join17 } from "node:path";
|
|
26661
27266
|
function parseStoredBody(text5) {
|
|
26662
27267
|
let value;
|
|
26663
27268
|
try {
|
|
@@ -26858,7 +27463,7 @@ var init_wal_index = __esm({
|
|
|
26858
27463
|
/** The scan `ensureAsync` has running for a session, shared by callers. */
|
|
26859
27464
|
building = /* @__PURE__ */ new Map();
|
|
26860
27465
|
sidecarFor(sessionUuid2) {
|
|
26861
|
-
return
|
|
27466
|
+
return join17(this.dir, `${sessionUuid2}${SIDECAR_SUFFIX}`);
|
|
26862
27467
|
}
|
|
26863
27468
|
/** Whether a name in the WAL directory is one of these sidecars. */
|
|
26864
27469
|
static isSidecar(name) {
|
|
@@ -27090,7 +27695,7 @@ import {
|
|
|
27090
27695
|
writeSync as writeSync4
|
|
27091
27696
|
} from "node:fs";
|
|
27092
27697
|
import { randomUUID } from "node:crypto";
|
|
27093
|
-
import { join as
|
|
27698
|
+
import { join as join18 } from "node:path";
|
|
27094
27699
|
function activityAt(event) {
|
|
27095
27700
|
const at2 = Date.parse(event.ts);
|
|
27096
27701
|
return Number.isFinite(at2) ? at2 : Number.NEGATIVE_INFINITY;
|
|
@@ -27154,7 +27759,7 @@ var init_wal = __esm({
|
|
|
27154
27759
|
this.dir = dir;
|
|
27155
27760
|
ensureDir(dir);
|
|
27156
27761
|
this.bodyIndexes = new BodyIndexStore(dir);
|
|
27157
|
-
this.cursorPath =
|
|
27762
|
+
this.cursorPath = join18(dir, "cursor.json");
|
|
27158
27763
|
let raw;
|
|
27159
27764
|
if (onCursorSetAside !== void 0)
|
|
27160
27765
|
raw = readJsonStateFile(this.cursorPath, onCursorSetAside);
|
|
@@ -27234,10 +27839,10 @@ var init_wal = __esm({
|
|
|
27234
27839
|
*/
|
|
27235
27840
|
dirtyPaths = /* @__PURE__ */ new Set();
|
|
27236
27841
|
fileFor(sessionUuid2) {
|
|
27237
|
-
return
|
|
27842
|
+
return join18(this.dir, `${sessionUuid2}.ndjson`);
|
|
27238
27843
|
}
|
|
27239
27844
|
bodyFileFor(sessionUuid2) {
|
|
27240
|
-
return
|
|
27845
|
+
return join18(this.dir, `${sessionUuid2}.bodies.jsonl`);
|
|
27241
27846
|
}
|
|
27242
27847
|
persistCursor() {
|
|
27243
27848
|
this.flush();
|
|
@@ -28666,7 +29271,7 @@ ${lines.join("\n")}
|
|
|
28666
29271
|
for (const name of readdirSync7(this.dir)) {
|
|
28667
29272
|
if (abandonedRewrite.test(name)) {
|
|
28668
29273
|
try {
|
|
28669
|
-
unlinkSync9(
|
|
29274
|
+
unlinkSync9(join18(this.dir, name));
|
|
28670
29275
|
} catch (error) {
|
|
28671
29276
|
this.bodyFailure(name.slice(0, 36), "cleanup", error);
|
|
28672
29277
|
}
|
|
@@ -28813,7 +29418,7 @@ function githubConfigure(options, rootDeps) {
|
|
|
28813
29418
|
const helperBase = deps.runtime.credentialHelperCommand;
|
|
28814
29419
|
if (!helperBase)
|
|
28815
29420
|
throw new Error("This installation has no credential helper command");
|
|
28816
|
-
const helper = `!${helperBase.replace(/credential issue --harness claude-code$/, "github credential")} --harness ${
|
|
29421
|
+
const helper = `!${helperBase.replace(/credential issue --harness claude-code$/, "github credential")} --harness ${shellQuote2(options.harness, deps.platform)} --cwd ${shellQuote2(cwd, deps.platform)}`;
|
|
28817
29422
|
const previous = (host.github_repositories ?? []).find(
|
|
28818
29423
|
(entry2) => sameReceipt(entry2, cwd, repository)
|
|
28819
29424
|
);
|
|
@@ -29031,15 +29636,15 @@ var init_github = __esm({
|
|
|
29031
29636
|
|
|
29032
29637
|
// ../../packages/tacho/src/host/harness-path.ts
|
|
29033
29638
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
29034
|
-
import { delimiter, join as
|
|
29639
|
+
import { delimiter as delimiter2, join as join19 } from "node:path";
|
|
29035
29640
|
function knownDirs(home) {
|
|
29036
29641
|
return [
|
|
29037
|
-
|
|
29038
|
-
|
|
29039
|
-
|
|
29040
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29642
|
+
join19(home, ".local", "bin"),
|
|
29643
|
+
join19(home, ".bun", "bin"),
|
|
29644
|
+
join19(home, ".npm-global", "bin"),
|
|
29645
|
+
join19(home, ".volta", "bin"),
|
|
29646
|
+
join19(home, ".cargo", "bin"),
|
|
29647
|
+
join19(home, "bin"),
|
|
29043
29648
|
"/opt/homebrew/bin",
|
|
29044
29649
|
"/usr/local/bin",
|
|
29045
29650
|
"/usr/bin",
|
|
@@ -29079,10 +29684,10 @@ function harnessPath(env, home, loginPath) {
|
|
|
29079
29684
|
seen.add(dir);
|
|
29080
29685
|
dirs.push(dir);
|
|
29081
29686
|
};
|
|
29082
|
-
for (const dir of (loginPath ?? "").split(
|
|
29083
|
-
for (const dir of (env["PATH"] ?? "").split(
|
|
29687
|
+
for (const dir of (loginPath ?? "").split(delimiter2)) add2(dir);
|
|
29688
|
+
for (const dir of (env["PATH"] ?? "").split(delimiter2)) add2(dir);
|
|
29084
29689
|
for (const dir of knownDirs(home)) add2(dir);
|
|
29085
|
-
return dirs.join(
|
|
29690
|
+
return dirs.join(delimiter2);
|
|
29086
29691
|
}
|
|
29087
29692
|
var MARK, LOGIN_SHELL_TIMEOUT_MS;
|
|
29088
29693
|
var init_harness_path = __esm({
|
|
@@ -29159,7 +29764,7 @@ var init_policy_mode = __esm({
|
|
|
29159
29764
|
|
|
29160
29765
|
// ../../packages/tacho/src/host/wal-ceiling.ts
|
|
29161
29766
|
import { statfsSync } from "node:fs";
|
|
29162
|
-
import { join as
|
|
29767
|
+
import { join as join20 } from "node:path";
|
|
29163
29768
|
function freeBytesOn(dir) {
|
|
29164
29769
|
try {
|
|
29165
29770
|
const stats2 = statfsSync(dir);
|
|
@@ -29206,7 +29811,7 @@ function isDrop(value) {
|
|
|
29206
29811
|
function readWalCeilingState(walDir) {
|
|
29207
29812
|
let raw;
|
|
29208
29813
|
try {
|
|
29209
|
-
raw = readJsonFileIfExists(
|
|
29814
|
+
raw = readJsonFileIfExists(join20(walDir, STATE_FILE));
|
|
29210
29815
|
} catch (error) {
|
|
29211
29816
|
if (error instanceof SyntaxError) return void 0;
|
|
29212
29817
|
throw error;
|
|
@@ -29406,7 +30011,7 @@ var init_wal_ceiling = __esm({
|
|
|
29406
30011
|
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
30012
|
this.state = next;
|
|
29408
30013
|
if (!changed) return;
|
|
29409
|
-
const path2 =
|
|
30014
|
+
const path2 = join20(this.walDir, STATE_FILE);
|
|
29410
30015
|
try {
|
|
29411
30016
|
writeSensitiveFileAtomic(path2, JSON.stringify(next));
|
|
29412
30017
|
this.unsaved = false;
|
|
@@ -29910,7 +30515,7 @@ import {
|
|
|
29910
30515
|
rmdirSync as rmdirSync5,
|
|
29911
30516
|
rmSync as rmSync2
|
|
29912
30517
|
} from "node:fs";
|
|
29913
|
-
import { dirname as dirname12, join as
|
|
30518
|
+
import { dirname as dirname12, join as join21 } from "node:path";
|
|
29914
30519
|
async function revokeOnControlPlane(host, options, deps, warnings) {
|
|
29915
30520
|
const resolved = resolveCredentials(
|
|
29916
30521
|
{
|
|
@@ -30521,7 +31126,7 @@ async function unenrollLocked(options, deps) {
|
|
|
30521
31126
|
];
|
|
30522
31127
|
const purged = parts.filter(([, path2, recursive]) => remove2(path2, recursive)).map(([name]) => name);
|
|
30523
31128
|
let failed3 = purged.length < parts.length;
|
|
30524
|
-
if (!existsSync15(
|
|
31129
|
+
if (!existsSync15(join21(deps.paths.dir, HARNESS_RECEIPTS)) && !remove2(join21(deps.paths.dir, HARNESS_BACKUPS), true))
|
|
30525
31130
|
failed3 = true;
|
|
30526
31131
|
const logShared = otherLiveAgents(deps.paths, deps.paths.dir).length > 0;
|
|
30527
31132
|
if (!logShared) {
|
|
@@ -30579,7 +31184,7 @@ var init_unenroll = __esm({
|
|
|
30579
31184
|
|
|
30580
31185
|
// ../../packages/tacho/src/cli/enroll.ts
|
|
30581
31186
|
import { existsSync as existsSync16, lstatSync as lstatSync4, rmSync as rmSync3 } from "node:fs";
|
|
30582
|
-
import { dirname as dirname13, isAbsolute, join as
|
|
31187
|
+
import { dirname as dirname13, isAbsolute, join as join22 } from "node:path";
|
|
30583
31188
|
function rootRefusal(command2, deps, allowRoot) {
|
|
30584
31189
|
if (allowRoot === true || deps.getuid?.() !== 0) return void 0;
|
|
30585
31190
|
const user = deps.env["SUDO_USER"] ?? "<user>";
|
|
@@ -30605,7 +31210,7 @@ function runtimeOf(host) {
|
|
|
30605
31210
|
if (first === void 0 || !files2.every((file) => existsSync16(file)))
|
|
30606
31211
|
return void 0;
|
|
30607
31212
|
const binDir = dirname13(first);
|
|
30608
|
-
const tacho = host.daemon_command[0] === first ?
|
|
31213
|
+
const tacho = host.daemon_command[0] === first ? shellQuote2(first) : `${host.daemon_command[0]} ${shellQuote2(join22(binDir, "tacho.mjs"))}`;
|
|
30609
31214
|
return {
|
|
30610
31215
|
hookCommand: host.hook_command,
|
|
30611
31216
|
credentialHelperCommand: helperCommandFor(tacho),
|
|
@@ -31699,6 +32304,7 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
|
|
|
31699
32304
|
const mode = options.credentials ?? "brokered";
|
|
31700
32305
|
try {
|
|
31701
32306
|
if (mode === "brokered") {
|
|
32307
|
+
await signInIfNoCredential(reaching, deps);
|
|
31702
32308
|
const outcome = await brokerCredentials(host, reaching, deps);
|
|
31703
32309
|
for (const entry2 of outcome.harnesses)
|
|
31704
32310
|
deps.out(` ${describeHarness(entry2)}`);
|
|
@@ -32861,21 +33467,21 @@ import {
|
|
|
32861
33467
|
statSync as statSync7,
|
|
32862
33468
|
unlinkSync as unlinkSync10
|
|
32863
33469
|
} from "node:fs";
|
|
32864
|
-
import { dirname as dirname14, isAbsolute as isAbsolute2, join as
|
|
33470
|
+
import { dirname as dirname14, isAbsolute as isAbsolute2, join as join23, relative, resolve as resolve7, sep } from "node:path";
|
|
32865
33471
|
function dataLocalDir(home, env, platform) {
|
|
32866
33472
|
if (platform === "darwin")
|
|
32867
|
-
return
|
|
33473
|
+
return join23(home, "Library", "Application Support");
|
|
32868
33474
|
if (platform === "win32")
|
|
32869
|
-
return env["LOCALAPPDATA"] ??
|
|
33475
|
+
return env["LOCALAPPDATA"] ?? join23(home, "AppData", "Local");
|
|
32870
33476
|
const xdg = env["XDG_DATA_HOME"];
|
|
32871
|
-
return xdg !== void 0 && isAbsolute2(xdg) ? xdg :
|
|
33477
|
+
return xdg !== void 0 && isAbsolute2(xdg) ? xdg : join23(home, ".local", "share");
|
|
32872
33478
|
}
|
|
32873
33479
|
function desktopPlaces(deps) {
|
|
32874
|
-
const oxagenDir =
|
|
33480
|
+
const oxagenDir = join23(deps.home, ".config", "oxagen");
|
|
32875
33481
|
return {
|
|
32876
33482
|
oxagenDir,
|
|
32877
|
-
desktopConfig:
|
|
32878
|
-
copies:
|
|
33483
|
+
desktopConfig: join23(oxagenDir, "desktop.json"),
|
|
33484
|
+
copies: join23(
|
|
32879
33485
|
dataLocalDir(deps.home, deps.env, deps.platform),
|
|
32880
33486
|
"oxagen",
|
|
32881
33487
|
"bin"
|
|
@@ -33120,7 +33726,7 @@ function undoEntry(entry2, deps, tally2) {
|
|
|
33120
33726
|
}
|
|
33121
33727
|
function versionDirs(copies) {
|
|
33122
33728
|
try {
|
|
33123
|
-
return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) =>
|
|
33729
|
+
return readdirSync9(copies, { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && !entry2.name.startsWith(".")).map((entry2) => join23(copies, entry2.name)).sort();
|
|
33124
33730
|
} catch {
|
|
33125
33731
|
return [];
|
|
33126
33732
|
}
|
|
@@ -33156,7 +33762,7 @@ function journaledCopies(journal, copies, tally2) {
|
|
|
33156
33762
|
function removeSidecars(dir, platform, tally2) {
|
|
33157
33763
|
const note = platform === "win32" ? `. Windows keeps a running program's file, so delete ${dir} after this command exits` : "";
|
|
33158
33764
|
for (const name of ["oxagen", "tacho"]) {
|
|
33159
|
-
const file =
|
|
33765
|
+
const file = join23(dir, platform === "win32" ? `${name}.exe` : name);
|
|
33160
33766
|
removeFile(file, tally2, note);
|
|
33161
33767
|
}
|
|
33162
33768
|
}
|
|
@@ -33244,7 +33850,7 @@ async function uninstall(options, deps) {
|
|
|
33244
33850
|
tally2.left.push(`cannot remove ${dir}: ${reasonOf3(error)}`);
|
|
33245
33851
|
}
|
|
33246
33852
|
}
|
|
33247
|
-
const configDir =
|
|
33853
|
+
const configDir = join23(deps.home, ".config");
|
|
33248
33854
|
if (record.configDirCreated && removeDirIfEmpty(configDir))
|
|
33249
33855
|
tally2.removed.push(configDir);
|
|
33250
33856
|
for (const path2 of tally2.removed) deps.out(` removed ${path2}`);
|
|
@@ -33658,9 +34264,9 @@ var init_run = __esm({
|
|
|
33658
34264
|
});
|
|
33659
34265
|
|
|
33660
34266
|
// ../../packages/tacho/src/cli/agent-run.ts
|
|
33661
|
-
import { spawn as
|
|
34267
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
33662
34268
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
33663
|
-
import { constants as
|
|
34269
|
+
import { constants as constants3 } from "node:os";
|
|
33664
34270
|
import { posix as posix3, win32 as win324 } from "node:path";
|
|
33665
34271
|
function programName(program2) {
|
|
33666
34272
|
const base2 = (program2.includes("\\") ? win324 : posix3).basename(program2);
|
|
@@ -33686,7 +34292,7 @@ function sessionRefusal(stdout) {
|
|
|
33686
34292
|
function exitCodeOf(exit) {
|
|
33687
34293
|
if (exit.error !== void 0) return 1;
|
|
33688
34294
|
if (exit.code !== null) return exit.code;
|
|
33689
|
-
const number2 = exit.signal !== null ?
|
|
34295
|
+
const number2 = exit.signal !== null ? constants3.signals[exit.signal] : void 0;
|
|
33690
34296
|
return number2 !== void 0 ? 128 + number2 : 1;
|
|
33691
34297
|
}
|
|
33692
34298
|
function spawnAgent(command2, args, options) {
|
|
@@ -33697,7 +34303,7 @@ function spawnAgent(command2, args, options) {
|
|
|
33697
34303
|
options.env
|
|
33698
34304
|
);
|
|
33699
34305
|
return new Promise((resolve25) => {
|
|
33700
|
-
const child =
|
|
34306
|
+
const child = spawn4(invocation.command, invocation.args, {
|
|
33701
34307
|
cwd: options.cwd,
|
|
33702
34308
|
env: options.env,
|
|
33703
34309
|
stdio: "inherit",
|
|
@@ -33922,11 +34528,11 @@ var init_work_order_start = __esm({
|
|
|
33922
34528
|
|
|
33923
34529
|
// ../../packages/tacho/src/host/work-orders.ts
|
|
33924
34530
|
import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
|
|
33925
|
-
import { join as
|
|
34531
|
+
import { join as join24 } from "node:path";
|
|
33926
34532
|
function orderFile(paths, id2) {
|
|
33927
34533
|
if (!WORK_ORDER_ID_PATTERN.test(id2))
|
|
33928
34534
|
throw new Error(`${JSON.stringify(id2)} is not a work order id`);
|
|
33929
|
-
return
|
|
34535
|
+
return join24(paths.workOrders, `${id2}.json`);
|
|
33930
34536
|
}
|
|
33931
34537
|
function readKept(path2) {
|
|
33932
34538
|
try {
|
|
@@ -33976,7 +34582,7 @@ function removeWorkOrder(paths, id2) {
|
|
|
33976
34582
|
function runningFile(paths, id2) {
|
|
33977
34583
|
if (!WORK_ORDER_ID_PATTERN.test(id2))
|
|
33978
34584
|
throw new Error(`${JSON.stringify(id2)} is not a work order id`);
|
|
33979
|
-
return
|
|
34585
|
+
return join24(paths.workOrders, `${id2}.running`);
|
|
33980
34586
|
}
|
|
33981
34587
|
function processIsAlive(pid) {
|
|
33982
34588
|
try {
|
|
@@ -34378,10 +34984,12 @@ var init_work = __esm({
|
|
|
34378
34984
|
var cli_exports = {};
|
|
34379
34985
|
__export(cli_exports, {
|
|
34380
34986
|
BACKFILL_EXIT: () => BACKFILL_EXIT,
|
|
34987
|
+
CLAUDE_OAUTH_TOKEN_ENV: () => CLAUDE_OAUTH_TOKEN_ENV,
|
|
34381
34988
|
CONTAINED_AGENTS: () => CONTAINED_AGENTS,
|
|
34382
34989
|
CURSOR_COVERAGE_NOTE: () => CURSOR_COVERAGE_NOTE,
|
|
34383
34990
|
MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
|
|
34384
34991
|
SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
|
|
34992
|
+
acquireSubscriptionToken: () => acquireSubscriptionToken,
|
|
34385
34993
|
addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
|
|
34386
34994
|
agentNameFromCommand: () => agentNameFromCommand,
|
|
34387
34995
|
backfillCommand: () => backfillCommand,
|
|
@@ -34389,6 +34997,7 @@ __export(cli_exports, {
|
|
|
34389
34997
|
canServeOxagenTools: () => canServeOxagenTools,
|
|
34390
34998
|
claudeFacts: () => claudeFacts,
|
|
34391
34999
|
credentialIssue: () => credentialIssue,
|
|
35000
|
+
credentialLogin: () => credentialLogin,
|
|
34392
35001
|
credentialStatus: () => credentialStatus,
|
|
34393
35002
|
cursorAppFacts: () => cursorAppFacts,
|
|
34394
35003
|
cursorFacts: () => cursorFacts,
|
|
@@ -34436,9 +35045,10 @@ __export(cli_exports, {
|
|
|
34436
35045
|
runtimeCommands: () => runtimeCommands,
|
|
34437
35046
|
serviceInstalled: () => serviceInstalled,
|
|
34438
35047
|
sessionRefusal: () => sessionRefusal,
|
|
34439
|
-
shellQuote: () =>
|
|
35048
|
+
shellQuote: () => shellQuote2,
|
|
34440
35049
|
shimError: () => shimError,
|
|
34441
35050
|
shippingHealth: () => shippingHealth,
|
|
35051
|
+
signInIfNoCredential: () => signInIfNoCredential,
|
|
34442
35052
|
spawnAgent: () => spawnAgent,
|
|
34443
35053
|
stableExecutablePath: () => stableExecutablePath,
|
|
34444
35054
|
status: () => status,
|
|
@@ -38173,6 +38783,7 @@ function containedBridgeHandler(options) {
|
|
|
38173
38783
|
const modelPaths = options.harness === "claude-code" ? ["/model/v1/messages", "/model/v1/messages/count_tokens"] : ["/model/v1/responses", "/model/v1/chat/completions"];
|
|
38174
38784
|
if (request3.method === "POST" && modelPaths.includes(path2)) {
|
|
38175
38785
|
const credential = options.issueCredential();
|
|
38786
|
+
const chatgpt = options.harness === "codex" && options.chatgptLogin?.() === true;
|
|
38176
38787
|
request3.headers = {
|
|
38177
38788
|
"content-type": "application/json",
|
|
38178
38789
|
...request3.headers["content-length"] ? { "content-length": request3.headers["content-length"] } : {},
|
|
@@ -38180,9 +38791,12 @@ function containedBridgeHandler(options) {
|
|
|
38180
38791
|
...request3.headers["anthropic-beta"] ? { "anthropic-beta": request3.headers["anthropic-beta"] } : {},
|
|
38181
38792
|
host: `127.0.0.1:${options.modelPort}`,
|
|
38182
38793
|
"x-oxagen-session": options.sessionId,
|
|
38183
|
-
...options.harness === "claude-code" ? { "x-api-key": credential } : { authorization: `Bearer ${credential}` }
|
|
38794
|
+
...options.harness === "claude-code" ? { "x-api-key": credential } : { authorization: `Bearer ${credential}` },
|
|
38795
|
+
// Marks the call as a ChatGPT login's; the gateway puts the
|
|
38796
|
+
// login's own account in its place.
|
|
38797
|
+
...chatgpt ? { "chatgpt-account-id": "custody" } : {}
|
|
38184
38798
|
};
|
|
38185
|
-
request3.url = options.harness === "claude-code" ? path2.replace("/model", "/anthropic") : path2.replace("/model", "/openai");
|
|
38799
|
+
request3.url = options.harness === "claude-code" ? path2.replace("/model", "/anthropic") : chatgpt ? path2.replace("/model/v1", "/backend-api/codex") : path2.replace("/model", "/openai");
|
|
38186
38800
|
options.model(request3, response);
|
|
38187
38801
|
return;
|
|
38188
38802
|
}
|
|
@@ -38334,7 +38948,7 @@ var init_bridge = __esm({
|
|
|
38334
38948
|
|
|
38335
38949
|
// ../../packages/tacho/src/contained/workspace.ts
|
|
38336
38950
|
import { lstatSync as lstatSync6, readFileSync as readFileSync14, readdirSync as readdirSync11, realpathSync as realpathSync3 } from "node:fs";
|
|
38337
|
-
import { isAbsolute as isAbsolute3, join as
|
|
38951
|
+
import { isAbsolute as isAbsolute3, join as join25, relative as relative2, resolve as resolve9, sep as sep2 } from "node:path";
|
|
38338
38952
|
function within(root, path2) {
|
|
38339
38953
|
const rel = relative2(root, path2);
|
|
38340
38954
|
return rel === "" || !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
|
|
@@ -38364,7 +38978,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
|
|
|
38364
38978
|
}
|
|
38365
38979
|
const visit2 = (directory) => {
|
|
38366
38980
|
for (const entry2 of readdirSync11(directory)) {
|
|
38367
|
-
const path2 =
|
|
38981
|
+
const path2 = join25(directory, entry2);
|
|
38368
38982
|
if (CREDENTIAL_PATHS.has(entry2) || isEnvFile(entry2))
|
|
38369
38983
|
throw new Error(
|
|
38370
38984
|
`Remove local credential files from the contained checkout before launch: ${named(path2)}`
|
|
@@ -38398,7 +39012,7 @@ function validateContainedWorkspace(workspace, sessionDirectory, mountInfo = rea
|
|
|
38398
39012
|
}
|
|
38399
39013
|
function gitDirectoryMounts(workspace) {
|
|
38400
39014
|
const root = realpathSync3(workspace);
|
|
38401
|
-
const dotGit =
|
|
39015
|
+
const dotGit = join25(root, ".git");
|
|
38402
39016
|
let stat7;
|
|
38403
39017
|
try {
|
|
38404
39018
|
stat7 = lstatSync6(dotGit);
|
|
@@ -38436,7 +39050,7 @@ function gitDirectoryMounts(workspace) {
|
|
|
38436
39050
|
if (gitDir !== void 0) {
|
|
38437
39051
|
let common;
|
|
38438
39052
|
try {
|
|
38439
|
-
common = readFileSync14(
|
|
39053
|
+
common = readFileSync14(join25(gitDir, "commondir"), "utf8").trim();
|
|
38440
39054
|
} catch {
|
|
38441
39055
|
common = void 0;
|
|
38442
39056
|
}
|
|
@@ -38463,7 +39077,7 @@ var init_workspace = __esm({
|
|
|
38463
39077
|
// ../../packages/tacho/src/collector/hardened-git.ts
|
|
38464
39078
|
import { execFile as execFile2 } from "node:child_process";
|
|
38465
39079
|
import { lstatSync as lstatSync7, readFileSync as readFileSync15, statSync as statSync8 } from "node:fs";
|
|
38466
|
-
import { dirname as dirname15, join as
|
|
39080
|
+
import { dirname as dirname15, join as join26, resolve as resolve10 } from "node:path";
|
|
38467
39081
|
function hardenedGitArgs(cwd, args, globals = []) {
|
|
38468
39082
|
const subcommand = args[0];
|
|
38469
39083
|
const end = args.indexOf("--");
|
|
@@ -38480,9 +39094,9 @@ function hardenedGitArgs(cwd, args, globals = []) {
|
|
|
38480
39094
|
];
|
|
38481
39095
|
}
|
|
38482
39096
|
function hardenedGitShellPrefix() {
|
|
38483
|
-
return ["git", ...HARDENED_GIT_CONFIG.map(
|
|
39097
|
+
return ["git", ...HARDENED_GIT_CONFIG.map(shellQuote3)].join(" ");
|
|
38484
39098
|
}
|
|
38485
|
-
function
|
|
39099
|
+
function shellQuote3(value) {
|
|
38486
39100
|
return /^[A-Za-z0-9_./=:-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
38487
39101
|
}
|
|
38488
39102
|
function hardenedGitExecOptions(repository) {
|
|
@@ -38502,7 +39116,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
|
|
|
38502
39116
|
};
|
|
38503
39117
|
}
|
|
38504
39118
|
for (let at2 = directory; ; at2 = dirname15(at2)) {
|
|
38505
|
-
const marker =
|
|
39119
|
+
const marker = join26(at2, ".git");
|
|
38506
39120
|
const link = tryStat(deps.lstat, marker);
|
|
38507
39121
|
if (link !== void 0) {
|
|
38508
39122
|
owners.push(link);
|
|
@@ -38510,7 +39124,7 @@ function gitIdentity(repository, deps = DEFAULT_IDENTITY_DEPS) {
|
|
|
38510
39124
|
if (target2 !== void 0) {
|
|
38511
39125
|
owners.push(target2);
|
|
38512
39126
|
if (target2.isDirectory()) {
|
|
38513
|
-
const config = tryStat(deps.stat,
|
|
39127
|
+
const config = tryStat(deps.stat, join26(marker, "config"));
|
|
38514
39128
|
if (config !== void 0) owners.push(config);
|
|
38515
39129
|
}
|
|
38516
39130
|
}
|
|
@@ -38662,7 +39276,7 @@ var init_hardened_git = __esm({
|
|
|
38662
39276
|
|
|
38663
39277
|
// ../../packages/tacho/src/contained/profile.ts
|
|
38664
39278
|
import { createHash as createHash6 } from "node:crypto";
|
|
38665
|
-
import { isAbsolute as isAbsolute4, join as
|
|
39279
|
+
import { isAbsolute as isAbsolute4, join as join27, relative as relative3, sep as sep3 } from "node:path";
|
|
38666
39280
|
function containerPath(workspace, path2) {
|
|
38667
39281
|
const inside2 = relative3(workspace, path2);
|
|
38668
39282
|
if (inside2 === "" || isAbsolute4(inside2) || inside2 === ".." || inside2.startsWith(`..${sep3}`))
|
|
@@ -38672,7 +39286,7 @@ function containerPath(workspace, path2) {
|
|
|
38672
39286
|
return `/workspace/${inside2.split(sep3).join("/")}`;
|
|
38673
39287
|
}
|
|
38674
39288
|
function gitMountPairs(spec) {
|
|
38675
|
-
if (!spec.gitMounts.includes(
|
|
39289
|
+
if (!spec.gitMounts.includes(join27(spec.workspace, ".git")))
|
|
38676
39290
|
throw new Error(
|
|
38677
39291
|
"Contained execution must mount the repository's .git read-only"
|
|
38678
39292
|
);
|
|
@@ -38829,7 +39443,7 @@ var init_profile = __esm({
|
|
|
38829
39443
|
});
|
|
38830
39444
|
|
|
38831
39445
|
// ../../packages/tacho/src/contained/launcher.ts
|
|
38832
|
-
import { execFile as execFile3, spawn as
|
|
39446
|
+
import { execFile as execFile3, spawn as spawn5 } from "node:child_process";
|
|
38833
39447
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
38834
39448
|
import {
|
|
38835
39449
|
chmodSync as chmodSync7,
|
|
@@ -38839,7 +39453,7 @@ import {
|
|
|
38839
39453
|
writeFileSync as writeFileSync2
|
|
38840
39454
|
} from "node:fs";
|
|
38841
39455
|
import { tmpdir } from "node:os";
|
|
38842
|
-
import { isAbsolute as isAbsolute5, join as
|
|
39456
|
+
import { isAbsolute as isAbsolute5, join as join28 } from "node:path";
|
|
38843
39457
|
import { promisify } from "node:util";
|
|
38844
39458
|
function reason(error) {
|
|
38845
39459
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -38878,7 +39492,7 @@ async function launchContainedAgent(options) {
|
|
|
38878
39492
|
if (digest6 === void 0)
|
|
38879
39493
|
throw new Error("The operator's contained image is not installed");
|
|
38880
39494
|
const sessionId = `contained-${randomBytes8(16).toString("hex")}`;
|
|
38881
|
-
const directory = mkdtempSync(
|
|
39495
|
+
const directory = mkdtempSync(join28(tmpdir(), "oxagen-contained-"));
|
|
38882
39496
|
chmodSync7(directory, 448);
|
|
38883
39497
|
let prepared;
|
|
38884
39498
|
let containerCreated = false;
|
|
@@ -38899,7 +39513,7 @@ async function launchContainedAgent(options) {
|
|
|
38899
39513
|
for (const [name2, contents] of entries2) {
|
|
38900
39514
|
if (!/^[a-z0-9.-]+$/.test(name2))
|
|
38901
39515
|
throw new Error("Invalid contained configuration filename");
|
|
38902
|
-
writeFileSync2(
|
|
39516
|
+
writeFileSync2(join28(directory, name2), contents, {
|
|
38903
39517
|
mode: 256,
|
|
38904
39518
|
flag: "wx"
|
|
38905
39519
|
});
|
|
@@ -38930,7 +39544,7 @@ async function launchContainedAgent(options) {
|
|
|
38930
39544
|
if (options.signal?.aborted)
|
|
38931
39545
|
throw new Error("Contained run cancelled before start");
|
|
38932
39546
|
exitCode = await new Promise((resolve25, reject) => {
|
|
38933
|
-
const child =
|
|
39547
|
+
const child = spawn5("docker", ["start", "--attach", name], {
|
|
38934
39548
|
stdio: ["ignore", "pipe", "pipe"]
|
|
38935
39549
|
});
|
|
38936
39550
|
const stop = () => {
|
|
@@ -39298,8 +39912,8 @@ var init_privilege = __esm({
|
|
|
39298
39912
|
|
|
39299
39913
|
// ../../packages/tacho/src/contained/sandbox/launcher.ts
|
|
39300
39914
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
39301
|
-
import { join as
|
|
39302
|
-
function
|
|
39915
|
+
import { join as join29 } from "node:path";
|
|
39916
|
+
function shellQuote4(value) {
|
|
39303
39917
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
39304
39918
|
}
|
|
39305
39919
|
function sandboxHarnessEnvironment(request3, gatewayPort) {
|
|
@@ -39325,7 +39939,7 @@ function ptyCommand(argv) {
|
|
|
39325
39939
|
"-f",
|
|
39326
39940
|
"-e",
|
|
39327
39941
|
"-c",
|
|
39328
|
-
`exec ${argv.map(
|
|
39942
|
+
`exec ${argv.map(shellQuote4).join(" ")}`,
|
|
39329
39943
|
"/dev/null"
|
|
39330
39944
|
]
|
|
39331
39945
|
};
|
|
@@ -39388,7 +40002,7 @@ async function launchSandboxAgent(options) {
|
|
|
39388
40002
|
{ ...asAgent, cwd: SANDBOX_AGENT_HOME, timeoutMs: 15 * 6e4 }
|
|
39389
40003
|
);
|
|
39390
40004
|
for (const [name, contents] of Object.entries(claudeCodeUserState())) {
|
|
39391
|
-
const path2 =
|
|
40005
|
+
const path2 = join29(SANDBOX_AGENT_HOME, name);
|
|
39392
40006
|
system.writeFile(path2, contents, 384);
|
|
39393
40007
|
system.chown(path2, SANDBOX_AGENT_UID, SANDBOX_AGENT_GID);
|
|
39394
40008
|
}
|
|
@@ -39512,7 +40126,7 @@ var init_request = __esm({
|
|
|
39512
40126
|
});
|
|
39513
40127
|
|
|
39514
40128
|
// ../../packages/tacho/src/contained/sandbox/system.ts
|
|
39515
|
-
import { execFile as execFile4, spawn as
|
|
40129
|
+
import { execFile as execFile4, spawn as spawn6 } from "node:child_process";
|
|
39516
40130
|
import {
|
|
39517
40131
|
chmodSync as chmodSync8,
|
|
39518
40132
|
chownSync as chownSync2,
|
|
@@ -39543,7 +40157,7 @@ var init_system = __esm({
|
|
|
39543
40157
|
return { stdout: String(answer.stdout), stderr: String(answer.stderr) };
|
|
39544
40158
|
},
|
|
39545
40159
|
spawn: (file, args, options) => {
|
|
39546
|
-
const child =
|
|
40160
|
+
const child = spawn6(file, [...args], {
|
|
39547
40161
|
...options.uid !== void 0 ? { uid: options.uid } : {},
|
|
39548
40162
|
...options.gid !== void 0 ? { gid: options.gid } : {},
|
|
39549
40163
|
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
@@ -39594,10 +40208,10 @@ var init_system = __esm({
|
|
|
39594
40208
|
});
|
|
39595
40209
|
|
|
39596
40210
|
// ../../packages/tacho/src/contained/runner.ts
|
|
39597
|
-
import { join as
|
|
40211
|
+
import { join as join30 } from "node:path";
|
|
39598
40212
|
function containedHookEnv(harness, workspace) {
|
|
39599
|
-
const home =
|
|
39600
|
-
return harness === "codex" ? { CODEX_HOME:
|
|
40213
|
+
const home = join30(workspace, ".oxagen-contained", "home");
|
|
40214
|
+
return harness === "codex" ? { CODEX_HOME: join30(home, ".codex") } : { CLAUDE_CONFIG_DIR: join30(home, ".claude") };
|
|
39601
40215
|
}
|
|
39602
40216
|
function containedLaunchEndpoint(host) {
|
|
39603
40217
|
const ingest = new URL(host.endpoints.ingest);
|
|
@@ -39693,6 +40307,7 @@ function createContainedRunner(options) {
|
|
|
39693
40307
|
throw new Error("Credential custody unavailable");
|
|
39694
40308
|
return answer.body.token;
|
|
39695
40309
|
},
|
|
40310
|
+
...options.chatgptLogin !== void 0 ? { chatgptLogin: options.chatgptLogin } : {},
|
|
39696
40311
|
model: (request3, response) => options.model.handle(request3, response),
|
|
39697
40312
|
hook: options.hook,
|
|
39698
40313
|
opening: route.opening,
|
|
@@ -39795,7 +40410,7 @@ function createContainedRunner(options) {
|
|
|
39795
40410
|
sessionUuid2 = started.recorder.sessionUuid;
|
|
39796
40411
|
active2.set(sessionUuid2, controller);
|
|
39797
40412
|
const bridge = await startContainedBridge({
|
|
39798
|
-
socketPath:
|
|
40413
|
+
socketPath: join30(directory, "bridge.sock"),
|
|
39799
40414
|
...bridgeRoutes({
|
|
39800
40415
|
sessionId,
|
|
39801
40416
|
workspace,
|
|
@@ -39879,7 +40494,7 @@ function createContainedRunner(options) {
|
|
|
39879
40494
|
const abort = () => controller.abort();
|
|
39880
40495
|
signal?.addEventListener("abort", abort, { once: true });
|
|
39881
40496
|
if (signal?.aborted) abort();
|
|
39882
|
-
const env = { CLAUDE_CONFIG_DIR:
|
|
40497
|
+
const env = { CLAUDE_CONFIG_DIR: join30(SANDBOX_AGENT_HOME, ".claude") };
|
|
39883
40498
|
let sessionUuid2;
|
|
39884
40499
|
let launchedSessionId;
|
|
39885
40500
|
let opened = false;
|
|
@@ -40012,14 +40627,96 @@ var init_runner = __esm({
|
|
|
40012
40627
|
}
|
|
40013
40628
|
});
|
|
40014
40629
|
|
|
40630
|
+
// ../../packages/tacho/src/host/chatgpt-refresh.ts
|
|
40631
|
+
function jwtExpiryMs(token) {
|
|
40632
|
+
const payload = token.split(".")[1];
|
|
40633
|
+
if (payload === void 0) return void 0;
|
|
40634
|
+
try {
|
|
40635
|
+
const claims = JSON.parse(
|
|
40636
|
+
Buffer.from(payload, "base64url").toString("utf8")
|
|
40637
|
+
);
|
|
40638
|
+
return typeof claims.exp === "number" ? claims.exp * 1e3 : void 0;
|
|
40639
|
+
} catch {
|
|
40640
|
+
return void 0;
|
|
40641
|
+
}
|
|
40642
|
+
}
|
|
40643
|
+
function chatGptLoginDue(login, takenAt, now) {
|
|
40644
|
+
const exp = jwtExpiryMs(login.access_token);
|
|
40645
|
+
if (exp !== void 0) return exp - now < CHATGPT_REFRESH_MARGIN_MS;
|
|
40646
|
+
return now - takenAt > CHATGPT_REFRESH_MAX_AGE_MS;
|
|
40647
|
+
}
|
|
40648
|
+
async function refreshChatGptLogin(login, fetchImpl) {
|
|
40649
|
+
if (login.refresh_token.length === 0)
|
|
40650
|
+
throw new Error("the ChatGPT login in custody has no refresh token");
|
|
40651
|
+
const response = await fetchImpl(CHATGPT_TOKEN_URL, {
|
|
40652
|
+
method: "POST",
|
|
40653
|
+
headers: { "Content-Type": "application/json" },
|
|
40654
|
+
body: JSON.stringify({
|
|
40655
|
+
client_id: CODEX_OAUTH_CLIENT_ID,
|
|
40656
|
+
grant_type: "refresh_token",
|
|
40657
|
+
refresh_token: login.refresh_token,
|
|
40658
|
+
scope: "openid profile email"
|
|
40659
|
+
})
|
|
40660
|
+
});
|
|
40661
|
+
if (!response.ok)
|
|
40662
|
+
throw new Error(
|
|
40663
|
+
`auth.openai.com refused to refresh the ChatGPT login (${response.status}); run \`codex login\`, then \`oxagen agent enroll\``
|
|
40664
|
+
);
|
|
40665
|
+
let body2;
|
|
40666
|
+
try {
|
|
40667
|
+
body2 = JSON.parse(await response.text());
|
|
40668
|
+
} catch {
|
|
40669
|
+
throw new Error("auth.openai.com answered with something other than JSON");
|
|
40670
|
+
}
|
|
40671
|
+
if (typeof body2.access_token !== "string" || body2.access_token.length === 0)
|
|
40672
|
+
throw new Error("auth.openai.com answered without an access token");
|
|
40673
|
+
return {
|
|
40674
|
+
...login,
|
|
40675
|
+
access_token: body2.access_token,
|
|
40676
|
+
refresh_token: typeof body2.refresh_token === "string" ? body2.refresh_token : login.refresh_token,
|
|
40677
|
+
...typeof body2.id_token === "string" ? { id_token: body2.id_token } : {}
|
|
40678
|
+
};
|
|
40679
|
+
}
|
|
40680
|
+
async function refreshChatGptCustody(store, fetchImpl, now) {
|
|
40681
|
+
const entry2 = store.status().find((c) => c.provider === "openai" && c.kind === "chatgpt");
|
|
40682
|
+
if (entry2 === void 0) return "none";
|
|
40683
|
+
const held = store.read("openai");
|
|
40684
|
+
const login = held?.kind === "chatgpt" ? decodeChatGptLogin(held.secret) : void 0;
|
|
40685
|
+
if (login === void 0) return { failed: "custody holds no ChatGPT login" };
|
|
40686
|
+
if (!chatGptLoginDue(login, Date.parse(entry2.taken_at), now)) return "fresh";
|
|
40687
|
+
try {
|
|
40688
|
+
const next = await refreshChatGptLogin(login, fetchImpl);
|
|
40689
|
+
store.take(
|
|
40690
|
+
"openai",
|
|
40691
|
+
{ kind: "chatgpt", secret: JSON.stringify(next) },
|
|
40692
|
+
"chatgpt:refresh",
|
|
40693
|
+
now
|
|
40694
|
+
);
|
|
40695
|
+
return "refreshed";
|
|
40696
|
+
} catch (error) {
|
|
40697
|
+
return { failed: error instanceof Error ? error.message : String(error) };
|
|
40698
|
+
}
|
|
40699
|
+
}
|
|
40700
|
+
var CODEX_OAUTH_CLIENT_ID, CHATGPT_TOKEN_URL, CHATGPT_REFRESH_MARGIN_MS, CHATGPT_REFRESH_MAX_AGE_MS;
|
|
40701
|
+
var init_chatgpt_refresh = __esm({
|
|
40702
|
+
"../../packages/tacho/src/host/chatgpt-refresh.ts"() {
|
|
40703
|
+
"use strict";
|
|
40704
|
+
init_model_credential();
|
|
40705
|
+
CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
40706
|
+
CHATGPT_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
40707
|
+
CHATGPT_REFRESH_MARGIN_MS = 60 * 6e4;
|
|
40708
|
+
CHATGPT_REFRESH_MAX_AGE_MS = 7 * 24 * 60 * 6e4;
|
|
40709
|
+
}
|
|
40710
|
+
});
|
|
40711
|
+
|
|
40015
40712
|
// ../../packages/tacho/src/host/repositories.ts
|
|
40016
40713
|
import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
|
|
40017
|
-
import { dirname as dirname17, join as
|
|
40714
|
+
import { dirname as dirname17, join as join31, resolve as resolve11, sep as sep4 } from "node:path";
|
|
40018
40715
|
function repositoriesFile(home) {
|
|
40019
|
-
return
|
|
40716
|
+
return join31(home.tachoDir, "repositories.json");
|
|
40020
40717
|
}
|
|
40021
40718
|
function workTreesDir(home) {
|
|
40022
|
-
return
|
|
40719
|
+
return join31(home.tachoDir, "work");
|
|
40023
40720
|
}
|
|
40024
40721
|
function readIndex(home) {
|
|
40025
40722
|
let raw;
|
|
@@ -40071,7 +40768,7 @@ function recordCheckout(home, canonical3, root, seenAt) {
|
|
|
40071
40768
|
return true;
|
|
40072
40769
|
}
|
|
40073
40770
|
function mainCheckoutOf(root) {
|
|
40074
|
-
const dotGit =
|
|
40771
|
+
const dotGit = join31(root, ".git");
|
|
40075
40772
|
try {
|
|
40076
40773
|
if (!statSync10(dotGit).isFile()) return root;
|
|
40077
40774
|
const line = readFileSync17(dotGit, "utf8").trim();
|
|
@@ -40081,13 +40778,13 @@ function mainCheckoutOf(root) {
|
|
|
40081
40778
|
const worktrees = dirname17(gitdir);
|
|
40082
40779
|
if (!worktrees.endsWith(`${sep4}.git${sep4}worktrees`)) return root;
|
|
40083
40780
|
const clone4 = dirname17(dirname17(worktrees));
|
|
40084
|
-
return existsSync18(
|
|
40781
|
+
return existsSync18(join31(clone4, ".git")) ? clone4 : root;
|
|
40085
40782
|
} catch {
|
|
40086
40783
|
return root;
|
|
40087
40784
|
}
|
|
40088
40785
|
}
|
|
40089
40786
|
function defaultIsCheckout(root) {
|
|
40090
|
-
return existsSync18(
|
|
40787
|
+
return existsSync18(join31(root, ".git"));
|
|
40091
40788
|
}
|
|
40092
40789
|
function findCheckout(home, repository, host, deps = {}) {
|
|
40093
40790
|
const isCheckout = deps.isCheckout ?? defaultIsCheckout;
|
|
@@ -40135,7 +40832,7 @@ var init_repositories = __esm({
|
|
|
40135
40832
|
|
|
40136
40833
|
// ../../packages/tacho/src/collector/detector.ts
|
|
40137
40834
|
import { promises as fs3 } from "node:fs";
|
|
40138
|
-
import { basename as basename5, join as
|
|
40835
|
+
import { basename as basename5, join as join32 } from "node:path";
|
|
40139
40836
|
async function eachLimited(items, limit, task) {
|
|
40140
40837
|
let next = 0;
|
|
40141
40838
|
const workers = Array.from(
|
|
@@ -40161,7 +40858,7 @@ async function scanProject(dir) {
|
|
|
40161
40858
|
await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
|
|
40162
40859
|
const match = SESSION_FILE.exec(name);
|
|
40163
40860
|
if (match === null) return;
|
|
40164
|
-
const path2 =
|
|
40861
|
+
const path2 = join32(dir, name);
|
|
40165
40862
|
try {
|
|
40166
40863
|
const st = await fs3.stat(path2);
|
|
40167
40864
|
out.push({ sessionId: match[1], path: path2, mtimeMs: st.mtimeMs });
|
|
@@ -40202,7 +40899,7 @@ var init_detector = __esm({
|
|
|
40202
40899
|
continue;
|
|
40203
40900
|
}
|
|
40204
40901
|
await eachLimited(names2, SCAN_CONCURRENCY, async (name) => {
|
|
40205
|
-
const dir =
|
|
40902
|
+
const dir = join32(root, name);
|
|
40206
40903
|
try {
|
|
40207
40904
|
const st = await fs3.stat(dir);
|
|
40208
40905
|
if (st.isDirectory()) dirs.push({ dir, mtimeMs: st.mtimeMs });
|
|
@@ -40442,7 +41139,7 @@ var init_detector = __esm({
|
|
|
40442
41139
|
transcript_path: transcript.path,
|
|
40443
41140
|
transcript_mtime: new Date(transcript.mtimeMs).toISOString(),
|
|
40444
41141
|
claude_pids: processes.map((p) => p.pid),
|
|
40445
|
-
project: basename5(
|
|
41142
|
+
project: basename5(join32(transcript.path, ".."))
|
|
40446
41143
|
}
|
|
40447
41144
|
})
|
|
40448
41145
|
);
|
|
@@ -40812,9 +41509,9 @@ var init_git_facts = __esm({
|
|
|
40812
41509
|
});
|
|
40813
41510
|
|
|
40814
41511
|
// ../../packages/tacho/src/collector/work-order-autostart.ts
|
|
40815
|
-
import { spawn as
|
|
41512
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
40816
41513
|
import { closeSync as closeSync7, existsSync as existsSync19, openSync as openSync7 } from "node:fs";
|
|
40817
|
-
import { dirname as dirname18, join as
|
|
41514
|
+
import { dirname as dirname18, join as join33 } from "node:path";
|
|
40818
41515
|
function asError(error) {
|
|
40819
41516
|
return error instanceof Error ? error : new Error(String(error));
|
|
40820
41517
|
}
|
|
@@ -40867,7 +41564,7 @@ function createWorkOrderAutostart(deps) {
|
|
|
40867
41564
|
}
|
|
40868
41565
|
async function abandon(id2) {
|
|
40869
41566
|
forget(id2);
|
|
40870
|
-
const worktree =
|
|
41567
|
+
const worktree = join33(workTreesDir(paths), id2);
|
|
40871
41568
|
if (!existsSync19(worktree)) return;
|
|
40872
41569
|
const [common, branch, head2] = await Promise.all([
|
|
40873
41570
|
git6(worktree, ["rev-parse", "--path-format=absolute", "--git-common-dir"]),
|
|
@@ -40905,7 +41602,7 @@ function createWorkOrderAutostart(deps) {
|
|
|
40905
41602
|
return waitMs;
|
|
40906
41603
|
}
|
|
40907
41604
|
async function prepareWorktree(root, id2, itemNumber) {
|
|
40908
|
-
const worktree =
|
|
41605
|
+
const worktree = join33(workTreesDir(paths), id2);
|
|
40909
41606
|
const logPath = `${worktree}.log`;
|
|
40910
41607
|
if (existsSync19(worktree)) {
|
|
40911
41608
|
const [branch2, head2] = await Promise.all([
|
|
@@ -40966,9 +41663,17 @@ function createWorkOrderAutostart(deps) {
|
|
|
40966
41663
|
log(`work order ${id2}: the harness ${ended}`);
|
|
40967
41664
|
return;
|
|
40968
41665
|
}
|
|
40969
|
-
const [status2, ahead] = await Promise.all([
|
|
41666
|
+
const [status2, ahead, current] = await Promise.all([
|
|
40970
41667
|
git6(worktree, ["status", "--porcelain"]),
|
|
40971
|
-
base2 === void 0 ? Promise.resolve({ ok: false, out: "", error: "no base commit" }) : git6(worktree, [
|
|
41668
|
+
base2 === void 0 ? Promise.resolve({ ok: false, out: "", error: "no base commit" }) : git6(worktree, [
|
|
41669
|
+
"rev-list",
|
|
41670
|
+
"--count",
|
|
41671
|
+
"HEAD",
|
|
41672
|
+
"--not",
|
|
41673
|
+
base2,
|
|
41674
|
+
"--remotes"
|
|
41675
|
+
]),
|
|
41676
|
+
git6(worktree, ["branch", "--show-current"])
|
|
40972
41677
|
]);
|
|
40973
41678
|
const clean = status2.ok && status2.out.length === 0 && ahead.ok && ahead.out === "0";
|
|
40974
41679
|
if (!clean || root === void 0) {
|
|
@@ -40984,13 +41689,26 @@ function createWorkOrderAutostart(deps) {
|
|
|
40984
41689
|
);
|
|
40985
41690
|
if (!removed.ok) {
|
|
40986
41691
|
log(
|
|
40987
|
-
`work order ${id2}: the harness ${ended} and left
|
|
41692
|
+
`work order ${id2}: the harness ${ended} and left nothing that is not on a remote, but Git could not remove ${worktree}: ${removed.error}`
|
|
40988
41693
|
);
|
|
40989
41694
|
return;
|
|
40990
41695
|
}
|
|
40991
|
-
|
|
41696
|
+
const used = /* @__PURE__ */ new Set();
|
|
41697
|
+
if (branch !== void 0) used.add(branch);
|
|
41698
|
+
if (current.ok && current.out.length > 0) used.add(current.out);
|
|
41699
|
+
for (const name of used) {
|
|
41700
|
+
const unpushed = await git6(root, [
|
|
41701
|
+
"rev-list",
|
|
41702
|
+
"--count",
|
|
41703
|
+
name,
|
|
41704
|
+
"--not",
|
|
41705
|
+
"--remotes"
|
|
41706
|
+
]);
|
|
41707
|
+
if (unpushed.ok && unpushed.out === "0")
|
|
41708
|
+
await git6(root, ["branch", "-D", name]);
|
|
41709
|
+
}
|
|
40992
41710
|
log(
|
|
40993
|
-
`work order ${id2}: the harness ${ended} and left
|
|
41711
|
+
`work order ${id2}: the harness ${ended} and left nothing that is not on a remote, so its worktree is removed${mark.log !== void 0 ? `. Its output is in ${mark.log}` : ""}.`
|
|
40994
41712
|
);
|
|
40995
41713
|
}
|
|
40996
41714
|
function watch2(id2, workspace, exited) {
|
|
@@ -41188,7 +41906,7 @@ var init_work_order_autostart = __esm({
|
|
|
41188
41906
|
};
|
|
41189
41907
|
let settled2 = false;
|
|
41190
41908
|
try {
|
|
41191
|
-
const child =
|
|
41909
|
+
const child = spawn7(command2, args, {
|
|
41192
41910
|
cwd: options.cwd,
|
|
41193
41911
|
env: options.env,
|
|
41194
41912
|
// Its own process group, so a daemon restart does not stop the run.
|
|
@@ -41275,7 +41993,7 @@ import {
|
|
|
41275
41993
|
rm,
|
|
41276
41994
|
unlink
|
|
41277
41995
|
} from "node:fs/promises";
|
|
41278
|
-
import { dirname as dirname19, join as
|
|
41996
|
+
import { dirname as dirname19, join as join34 } from "node:path";
|
|
41279
41997
|
function byPath(a, b3) {
|
|
41280
41998
|
return a.path < b3.path ? -1 : a.path > b3.path ? 1 : 0;
|
|
41281
41999
|
}
|
|
@@ -41318,7 +42036,7 @@ async function contentHash(absolutePath, kind, keep) {
|
|
|
41318
42036
|
if (keep !== void 0) {
|
|
41319
42037
|
try {
|
|
41320
42038
|
await mkdir(keep.dir, { recursive: true, mode: 448 });
|
|
41321
|
-
const temp2 =
|
|
42039
|
+
const temp2 = join34(keep.dir, `.partial-${randomUUID3()}`);
|
|
41322
42040
|
copy = {
|
|
41323
42041
|
temp: temp2,
|
|
41324
42042
|
handle: await open3(temp2, "wx", keep.executable ? 448 : 384)
|
|
@@ -41362,7 +42080,7 @@ async function settleCopy(temp, name) {
|
|
|
41362
42080
|
if (temp === void 0) return;
|
|
41363
42081
|
if (name !== void 0)
|
|
41364
42082
|
try {
|
|
41365
|
-
await rename(temp,
|
|
42083
|
+
await rename(temp, join34(dirname19(temp), name));
|
|
41366
42084
|
return;
|
|
41367
42085
|
} catch {
|
|
41368
42086
|
}
|
|
@@ -41378,7 +42096,7 @@ async function copiedBytes(dir) {
|
|
|
41378
42096
|
let total2 = 0;
|
|
41379
42097
|
for (const name of names2) {
|
|
41380
42098
|
try {
|
|
41381
|
-
total2 += (await lstat(
|
|
42099
|
+
total2 += (await lstat(join34(dir, name))).size;
|
|
41382
42100
|
} catch {
|
|
41383
42101
|
}
|
|
41384
42102
|
}
|
|
@@ -41394,7 +42112,7 @@ async function removeCopiesOutside(root, live) {
|
|
|
41394
42112
|
const keep = new Set(live());
|
|
41395
42113
|
await Promise.all(
|
|
41396
42114
|
names2.filter((name) => !keep.has(name)).map(
|
|
41397
|
-
(name) => rm(
|
|
42115
|
+
(name) => rm(join34(root, name), { recursive: true, force: true }).catch(
|
|
41398
42116
|
() => void 0
|
|
41399
42117
|
)
|
|
41400
42118
|
)
|
|
@@ -41501,7 +42219,7 @@ async function contributionOf(exec4, cwd, root, repoRelative, entry2, copies) {
|
|
|
41501
42219
|
let copy = null;
|
|
41502
42220
|
if (entry2 !== null) {
|
|
41503
42221
|
if (copies === void 0 || !COPY_NAME.test(entry2[0])) return void 0;
|
|
41504
|
-
copy =
|
|
42222
|
+
copy = join34(copies, entry2[0]);
|
|
41505
42223
|
const kept = await statOf(copy);
|
|
41506
42224
|
if (kept === void 0 || kept === "absent" || kept.kind !== "file")
|
|
41507
42225
|
return void 0;
|
|
@@ -41903,7 +42621,7 @@ var init_session_changes = __esm({
|
|
|
41903
42621
|
});
|
|
41904
42622
|
|
|
41905
42623
|
// ../../packages/tacho/src/collector/session-commits.ts
|
|
41906
|
-
import { join as
|
|
42624
|
+
import { join as join35 } from "node:path";
|
|
41907
42625
|
function parseMeta(stdout) {
|
|
41908
42626
|
const out = [];
|
|
41909
42627
|
for (const record of stdout.split("\0")) {
|
|
@@ -42028,7 +42746,7 @@ async function readSessionCommits(exec4, cwd, input) {
|
|
|
42028
42746
|
const test = input.tests[commit.sha];
|
|
42029
42747
|
const toolUseId = callAt(times.get(commit.sha), calls);
|
|
42030
42748
|
const written2 = input.written;
|
|
42031
|
-
const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(
|
|
42749
|
+
const outside = merge || written2 === void 0 || root === void 0 ? void 0 : files2.some((file) => !written2.has(join35(root, file.path)));
|
|
42032
42750
|
return {
|
|
42033
42751
|
sha: commit.sha,
|
|
42034
42752
|
parent_shas: commit.parents.slice(0, MAX_COMMIT_PARENTS),
|
|
@@ -42076,7 +42794,7 @@ var init_session_commits = __esm({
|
|
|
42076
42794
|
|
|
42077
42795
|
// ../../packages/tacho/src/collector/worktree-snapshot.ts
|
|
42078
42796
|
import { lstat as lstat2 } from "node:fs/promises";
|
|
42079
|
-
import { join as
|
|
42797
|
+
import { join as join36 } from "node:path";
|
|
42080
42798
|
function headerPath(prefix, path2) {
|
|
42081
42799
|
const name = `${prefix}${path2}`;
|
|
42082
42800
|
let quoted3 = false;
|
|
@@ -42160,7 +42878,7 @@ async function worktreeFingerprint(status2, root) {
|
|
|
42160
42878
|
const path2 = statusPath(record);
|
|
42161
42879
|
if (path2 === null) return "";
|
|
42162
42880
|
try {
|
|
42163
|
-
const info = await lstat2(
|
|
42881
|
+
const info = await lstat2(join36(root, path2));
|
|
42164
42882
|
return `${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
|
|
42165
42883
|
} catch {
|
|
42166
42884
|
return "absent";
|
|
@@ -42268,7 +42986,7 @@ async function readWorktreeSnapshot(exec4, cwd, baseline, measured) {
|
|
|
42268
42986
|
}
|
|
42269
42987
|
let present;
|
|
42270
42988
|
try {
|
|
42271
|
-
present = (await lstat2(
|
|
42989
|
+
present = (await lstat2(join36(root, path2))).isFile();
|
|
42272
42990
|
} catch {
|
|
42273
42991
|
present = false;
|
|
42274
42992
|
}
|
|
@@ -42371,10 +43089,10 @@ var init_worktree_snapshot = __esm({
|
|
|
42371
43089
|
});
|
|
42372
43090
|
|
|
42373
43091
|
// ../../packages/tacho/src/collector/git-lane.ts
|
|
42374
|
-
import { join as
|
|
43092
|
+
import { join as join37 } from "node:path";
|
|
42375
43093
|
function copiesDirOf(root, session) {
|
|
42376
43094
|
const uuid = session.recorder.sessionUuid;
|
|
42377
|
-
return /^[0-9a-f-]{36}$/i.test(uuid) ?
|
|
43095
|
+
return /^[0-9a-f-]{36}$/i.test(uuid) ? join37(root, uuid) : void 0;
|
|
42378
43096
|
}
|
|
42379
43097
|
function holdsBaseline(session) {
|
|
42380
43098
|
return session.baselineCommit !== void 0 || Object.keys(session.baselines ?? {}).length > 0;
|
|
@@ -45742,7 +46460,7 @@ var init_hook_queues = __esm({
|
|
|
45742
46460
|
|
|
45743
46461
|
// ../../packages/tacho/src/collector/memory-capture/memory-reader.ts
|
|
45744
46462
|
import { createRequire as createRequire2 } from "node:module";
|
|
45745
|
-
import { isAbsolute as isAbsolute8, join as
|
|
46463
|
+
import { isAbsolute as isAbsolute8, join as join38, sep as sep6 } from "node:path";
|
|
45746
46464
|
function claudeCodeLocation(projectsDir, memoryDir) {
|
|
45747
46465
|
return {
|
|
45748
46466
|
harness: "claude-code",
|
|
@@ -45754,13 +46472,13 @@ function claudeCodeLocation(projectsDir, memoryDir) {
|
|
|
45754
46472
|
}
|
|
45755
46473
|
function claudeCodeMemoryLocations(configDir, projectDirs = []) {
|
|
45756
46474
|
const locations = [
|
|
45757
|
-
claudeCodeLocation(() =>
|
|
45758
|
-
claudeCodeLocation(() =>
|
|
46475
|
+
claudeCodeLocation(() => join38(configDir, "projects"), "memory"),
|
|
46476
|
+
claudeCodeLocation(() => join38(configDir, "agent-memory"), "")
|
|
45759
46477
|
];
|
|
45760
46478
|
for (const dir of projectDirs.slice(0, MEMORY_PROJECT_DIRS_MAX))
|
|
45761
46479
|
locations.push(
|
|
45762
|
-
claudeCodeLocation(() =>
|
|
45763
|
-
claudeCodeLocation(() =>
|
|
46480
|
+
claudeCodeLocation(() => join38(dir, ".claude", "agent-memory"), ""),
|
|
46481
|
+
claudeCodeLocation(() => join38(dir, ".claude", "agent-memory-local"), "")
|
|
45764
46482
|
);
|
|
45765
46483
|
return locations;
|
|
45766
46484
|
}
|
|
@@ -45945,13 +46663,13 @@ function createMemoryReader(deps) {
|
|
|
45945
46663
|
const projects = await list2(top);
|
|
45946
46664
|
let complete = projects.complete;
|
|
45947
46665
|
for (const project2 of projects.names) {
|
|
45948
|
-
const dir = location.memoryDir === "" ?
|
|
46666
|
+
const dir = location.memoryDir === "" ? join38(top, project2) : join38(top, project2, location.memoryDir);
|
|
45949
46667
|
const files2 = await list2(dir);
|
|
45950
46668
|
if (!files2.complete) complete = false;
|
|
45951
46669
|
for (const name of files2.names) {
|
|
45952
46670
|
if (!name.endsWith(location.extension)) continue;
|
|
45953
46671
|
if (location.skip.includes(name)) continue;
|
|
45954
|
-
const path2 =
|
|
46672
|
+
const path2 = join38(dir, name);
|
|
45955
46673
|
const file = await readAt2(location.harness, path2);
|
|
45956
46674
|
if (file.kind === "absent" || file.kind === "empty") continue;
|
|
45957
46675
|
paths.push(path2);
|
|
@@ -46037,8 +46755,8 @@ var init_memory_reader = __esm({
|
|
|
46037
46755
|
MEMORY_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
46038
46756
|
MEMORY_PROJECT_DIRS_MAX = 200;
|
|
46039
46757
|
HARNESS_MEMORY_LOCATIONS = [
|
|
46040
|
-
claudeCodeLocation((home) =>
|
|
46041
|
-
claudeCodeLocation((home) =>
|
|
46758
|
+
claudeCodeLocation((home) => join38(home, ".claude", "projects"), "memory"),
|
|
46759
|
+
claudeCodeLocation((home) => join38(home, ".claude", "agent-memory"), "")
|
|
46042
46760
|
];
|
|
46043
46761
|
FRONTMATTER = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
|
|
46044
46762
|
FRONTMATTER_LINE = /^([ \t]*)([A-Za-z_][\w-]*)[ \t]*:[ \t]*(.*)$/;
|
|
@@ -46530,7 +47248,7 @@ var init_memory_recall = __esm({
|
|
|
46530
47248
|
});
|
|
46531
47249
|
|
|
46532
47250
|
// ../../packages/tacho/src/collector/memory-capture/memory-uses.ts
|
|
46533
|
-
import { basename as basename6, dirname as dirname21, isAbsolute as isAbsolute9, join as
|
|
47251
|
+
import { basename as basename6, dirname as dirname21, isAbsolute as isAbsolute9, join as join39, resolve as resolve14 } from "node:path";
|
|
46534
47252
|
function memoryFileOf(path2, locations, home) {
|
|
46535
47253
|
if (!isAbsolute9(path2)) return void 0;
|
|
46536
47254
|
const file = resolve14(path2);
|
|
@@ -46548,7 +47266,7 @@ function memoryFileOf(path2, locations, home) {
|
|
|
46548
47266
|
return void 0;
|
|
46549
47267
|
}
|
|
46550
47268
|
function pathOf(word, cwd, home) {
|
|
46551
|
-
const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ?
|
|
47269
|
+
const expanded = /^(?:~|\$HOME|\$\{HOME\})\//.test(word) ? join39(home, word.slice(word.indexOf("/") + 1)) : word;
|
|
46552
47270
|
if (isAbsolute9(expanded)) return expanded;
|
|
46553
47271
|
if (cwd !== void 0 && isAbsolute9(cwd)) return resolve14(cwd, expanded);
|
|
46554
47272
|
return void 0;
|
|
@@ -46804,7 +47522,7 @@ ${use.path}`;
|
|
|
46804
47522
|
});
|
|
46805
47523
|
|
|
46806
47524
|
// ../../packages/tacho/src/collector/memory-capture/stella-store.ts
|
|
46807
|
-
import { dirname as dirname22, isAbsolute as isAbsolute10, join as
|
|
47525
|
+
import { dirname as dirname22, isAbsolute as isAbsolute10, join as join40, resolve as resolve15 } from "node:path";
|
|
46808
47526
|
function queryStellaStore(path2, afterSeq, limit) {
|
|
46809
47527
|
const db = new (nodeSqlite()).DatabaseSync(path2, { readOnly: true });
|
|
46810
47528
|
try {
|
|
@@ -46894,7 +47612,7 @@ async function stellaStoreOf(dir, exists) {
|
|
|
46894
47612
|
if (!isAbsolute10(dir)) return void 0;
|
|
46895
47613
|
let at2 = resolve15(dir);
|
|
46896
47614
|
for (let depth = 0; depth < WORKSPACE_SEARCH_DEPTH; depth += 1) {
|
|
46897
|
-
const candidate =
|
|
47615
|
+
const candidate = join40(at2, STELLA_CONTEXT_DB);
|
|
46898
47616
|
if (await exists(candidate)) return candidate;
|
|
46899
47617
|
const up = dirname22(at2);
|
|
46900
47618
|
if (up === at2) return void 0;
|
|
@@ -46907,7 +47625,7 @@ var init_stella_store = __esm({
|
|
|
46907
47625
|
"../../packages/tacho/src/collector/memory-capture/stella-store.ts"() {
|
|
46908
47626
|
"use strict";
|
|
46909
47627
|
init_memory_reader();
|
|
46910
|
-
STELLA_CONTEXT_DB =
|
|
47628
|
+
STELLA_CONTEXT_DB = join40(".stella", "private", "context.db");
|
|
46911
47629
|
STELLA_MEMORIES_VIEW = "export_memories_v1";
|
|
46912
47630
|
STELLA_USES_VIEW = "export_memory_uses_v1";
|
|
46913
47631
|
STELLA_USES_PER_READ = 5e3;
|
|
@@ -47569,7 +48287,7 @@ var init_cloud_link = __esm({
|
|
|
47569
48287
|
|
|
47570
48288
|
// ../../packages/tacho/src/collector/local-servers/digest.ts
|
|
47571
48289
|
import { createHash as createHash10 } from "node:crypto";
|
|
47572
|
-
import { join as
|
|
48290
|
+
import { join as join41 } from "node:path";
|
|
47573
48291
|
function rememberBounded(map, key, value) {
|
|
47574
48292
|
if (!map.has(key) && map.size >= MAX_REMEMBERED_DIGESTS) {
|
|
47575
48293
|
const oldest = map.keys().next();
|
|
@@ -47606,7 +48324,7 @@ function npmNameSegment(name) {
|
|
|
47606
48324
|
return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : encodeURIComponent(name);
|
|
47607
48325
|
}
|
|
47608
48326
|
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) =>
|
|
48327
|
+
const candidates = command2.includes("/") || command2.includes("\\") ? [command2] : (pathValue ?? "").split(search.delimiter).filter((dir) => dir.length > 0).map((dir) => join41(dir, command2));
|
|
47610
48328
|
for (const candidate of candidates) {
|
|
47611
48329
|
for (const extension of search.extensions) {
|
|
47612
48330
|
if (await search.isExecutable(candidate + extension))
|
|
@@ -48609,7 +49327,7 @@ var init_local_servers = __esm({
|
|
|
48609
49327
|
|
|
48610
49328
|
// ../../packages/tacho/src/collector/local-servers/machine.ts
|
|
48611
49329
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
48612
|
-
import { access, constants as
|
|
49330
|
+
import { access, constants as constants4, readFile, realpath, stat } from "node:fs/promises";
|
|
48613
49331
|
function machineParked(host) {
|
|
48614
49332
|
if (host.revoked_at !== null || host.host_status === "revoked")
|
|
48615
49333
|
return "the enrollment is revoked";
|
|
@@ -48634,7 +49352,7 @@ function createMachineLink(host, fetch2, env) {
|
|
|
48634
49352
|
async function isExecutable(path2) {
|
|
48635
49353
|
try {
|
|
48636
49354
|
if (!(await stat(path2)).isFile()) return false;
|
|
48637
|
-
await access(path2,
|
|
49355
|
+
await access(path2, constants4.X_OK);
|
|
48638
49356
|
return true;
|
|
48639
49357
|
} catch {
|
|
48640
49358
|
return false;
|
|
@@ -48902,9 +49620,9 @@ import {
|
|
|
48902
49620
|
writeFile
|
|
48903
49621
|
} from "node:fs/promises";
|
|
48904
49622
|
import { homedir as homedir5 } from "node:os";
|
|
48905
|
-
import { dirname as dirname23, join as
|
|
49623
|
+
import { dirname as dirname23, join as join42 } from "node:path";
|
|
48906
49624
|
function stagingFolder(root, name) {
|
|
48907
|
-
return
|
|
49625
|
+
return join42(root, `.${name}${STAGING_SUFFIX}`);
|
|
48908
49626
|
}
|
|
48909
49627
|
function isStagingName(name) {
|
|
48910
49628
|
return name.startsWith(".") && name.endsWith(STAGING_SUFFIX);
|
|
@@ -48947,17 +49665,17 @@ function envDir(env, key) {
|
|
|
48947
49665
|
function skillsRoot(harness, env = process.env, home = homedir5()) {
|
|
48948
49666
|
switch (harness) {
|
|
48949
49667
|
case "claude-code":
|
|
48950
|
-
return
|
|
48951
|
-
envDir(env, "CLAUDE_CONFIG_DIR") ??
|
|
49668
|
+
return join42(
|
|
49669
|
+
envDir(env, "CLAUDE_CONFIG_DIR") ?? join42(home, ".claude"),
|
|
48952
49670
|
"skills"
|
|
48953
49671
|
);
|
|
48954
49672
|
case "codex": {
|
|
48955
49673
|
const codexHome = envDir(env, "CODEX_HOME");
|
|
48956
|
-
return codexHome === null ?
|
|
49674
|
+
return codexHome === null ? join42(home, ".agents", "skills") : join42(codexHome, "skills");
|
|
48957
49675
|
}
|
|
48958
49676
|
case "stella":
|
|
48959
|
-
return
|
|
48960
|
-
envDir(env, "STELLA_HOME") ??
|
|
49677
|
+
return join42(
|
|
49678
|
+
envDir(env, "STELLA_HOME") ?? join42(home, ".stella"),
|
|
48961
49679
|
"skills"
|
|
48962
49680
|
);
|
|
48963
49681
|
case "cursor":
|
|
@@ -49018,7 +49736,7 @@ async function readMarker(folder) {
|
|
|
49018
49736
|
if (!entries2.includes(SESSION_MARKER)) return { state: "foreign" };
|
|
49019
49737
|
try {
|
|
49020
49738
|
const parsed2 = JSON.parse(
|
|
49021
|
-
await readFile2(
|
|
49739
|
+
await readFile2(join42(folder, SESSION_MARKER), "utf8")
|
|
49022
49740
|
);
|
|
49023
49741
|
return isMarker(parsed2) ? { state: "ours", marker: parsed2 } : { state: "foreign" };
|
|
49024
49742
|
} catch {
|
|
@@ -49035,22 +49753,22 @@ function liveSessions(marker, now) {
|
|
|
49035
49753
|
return live;
|
|
49036
49754
|
}
|
|
49037
49755
|
async function writeMarker(folder, marker) {
|
|
49038
|
-
const temp =
|
|
49756
|
+
const temp = join42(folder, MARKER_TEMP);
|
|
49039
49757
|
await writeFile(temp, `${JSON.stringify(marker, null, 2)}
|
|
49040
49758
|
`);
|
|
49041
|
-
await rename2(temp,
|
|
49759
|
+
await rename2(temp, join42(folder, SESSION_MARKER));
|
|
49042
49760
|
}
|
|
49043
49761
|
async function writeSkill(root, skill, marker) {
|
|
49044
|
-
const folder =
|
|
49762
|
+
const folder = join42(root, skill.name);
|
|
49045
49763
|
const staging = stagingFolder(root, skill.name);
|
|
49046
49764
|
await rm2(folder, { recursive: true, force: true });
|
|
49047
49765
|
await rm2(staging, { recursive: true, force: true });
|
|
49048
49766
|
await mkdir2(staging);
|
|
49049
49767
|
await writeMarker(staging, { digest: UNFINISHED_DIGEST, sessions: {} });
|
|
49050
49768
|
await rename2(staging, folder);
|
|
49051
|
-
await writeFile(
|
|
49769
|
+
await writeFile(join42(folder, "SKILL.md"), skillMarkdown(skill));
|
|
49052
49770
|
for (const file of skill.files) {
|
|
49053
|
-
const target2 =
|
|
49771
|
+
const target2 = join42(folder, file.path);
|
|
49054
49772
|
await mkdir2(dirname23(target2), { recursive: true });
|
|
49055
49773
|
await writeFile(target2, file.content);
|
|
49056
49774
|
}
|
|
@@ -49079,7 +49797,7 @@ async function placeSkills(root, sessionId, skills, now = /* @__PURE__ */ new Da
|
|
|
49079
49797
|
result2.warnings.push(refusal);
|
|
49080
49798
|
continue;
|
|
49081
49799
|
}
|
|
49082
|
-
const folder =
|
|
49800
|
+
const folder = join42(root, skill.name);
|
|
49083
49801
|
if (taken.has(skill.name)) {
|
|
49084
49802
|
result2.skipped.push(skill.name);
|
|
49085
49803
|
result2.warnings.push(
|
|
@@ -49144,7 +49862,7 @@ async function removeSkills(root, sessionId, now = /* @__PURE__ */ new Date()) {
|
|
|
49144
49862
|
throw error;
|
|
49145
49863
|
}
|
|
49146
49864
|
for (const name of names2.sort(compareText)) {
|
|
49147
|
-
const folder =
|
|
49865
|
+
const folder = join42(root, name);
|
|
49148
49866
|
if (isStagingName(name)) {
|
|
49149
49867
|
await rm2(folder, { recursive: true, force: true });
|
|
49150
49868
|
continue;
|
|
@@ -50066,13 +50784,27 @@ function connectionTokens(raw) {
|
|
|
50066
50784
|
}
|
|
50067
50785
|
return named;
|
|
50068
50786
|
}
|
|
50787
|
+
function withOAuthBeta(value) {
|
|
50788
|
+
const flags = (value ?? "").split(",").map((flag2) => flag2.trim()).filter((flag2) => flag2.length > 0);
|
|
50789
|
+
if (!flags.includes(ANTHROPIC_OAUTH_BETA)) flags.push(ANTHROPIC_OAUTH_BETA);
|
|
50790
|
+
return flags.join(",");
|
|
50791
|
+
}
|
|
50069
50792
|
function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, attach) {
|
|
50070
50793
|
const named = connectionTokens(raw);
|
|
50071
50794
|
const out = ["Host", host];
|
|
50795
|
+
const oauth = attach?.kind === "oauth";
|
|
50796
|
+
let beta;
|
|
50072
50797
|
for (let i2 = 0; i2 + 1 < raw.length; i2 += 2) {
|
|
50073
50798
|
const name = raw[i2];
|
|
50074
50799
|
const lower = name.toLowerCase();
|
|
50075
50800
|
if (HOP_BY_HOP.has(lower) || named.has(lower)) continue;
|
|
50801
|
+
if (attach?.kind === "chatgpt" && attach.accountId !== void 0 && lower === CHATGPT_ACCOUNT_HEADER)
|
|
50802
|
+
continue;
|
|
50803
|
+
if (oauth && lower === "anthropic-beta") {
|
|
50804
|
+
const value = raw[i2 + 1];
|
|
50805
|
+
beta = beta === void 0 ? value : `${beta},${value}`;
|
|
50806
|
+
continue;
|
|
50807
|
+
}
|
|
50076
50808
|
if (lower === "host" || lower === "content-length") continue;
|
|
50077
50809
|
if (lower === "accept-encoding" || lower === "expect") continue;
|
|
50078
50810
|
if (lower === TACHO_MODEL_SESSION_HEADER) continue;
|
|
@@ -50084,6 +50816,9 @@ function upstreamRequestHeaders(raw, host, bodyLength, dropContentEncoding, atta
|
|
|
50084
50816
|
if (attach !== void 0) {
|
|
50085
50817
|
if (attach.kind === "api_key") out.push("X-Api-Key", attach.secret);
|
|
50086
50818
|
else out.push("Authorization", `Bearer ${attach.secret}`);
|
|
50819
|
+
if (oauth) out.push("anthropic-beta", withOAuthBeta(beta));
|
|
50820
|
+
if (attach.kind === "chatgpt" && attach.accountId !== void 0)
|
|
50821
|
+
out.push("ChatGPT-Account-ID", attach.accountId);
|
|
50087
50822
|
}
|
|
50088
50823
|
out.push("Accept-Encoding", "identity");
|
|
50089
50824
|
if (bodyLength > 0 || raw.some((h, i2) => i2 % 2 === 0 && /^content-length$/i.test(h)))
|
|
@@ -50117,7 +50852,7 @@ function providerError(provider, status2, code, message) {
|
|
|
50117
50852
|
};
|
|
50118
50853
|
return { status: status2, body: JSON.stringify(body2) };
|
|
50119
50854
|
}
|
|
50120
|
-
var DEFAULT_MODEL_UPSTREAMS, MODEL_PROXY_ROUTES, HOP_BY_HOP, CREDENTIAL_HEADERS;
|
|
50855
|
+
var DEFAULT_MODEL_UPSTREAMS, MODEL_PROXY_ROUTES, HOP_BY_HOP, CREDENTIAL_HEADERS, CHATGPT_ACCOUNT_HEADER, ANTHROPIC_OAUTH_BETA;
|
|
50121
50856
|
var init_model_routes = __esm({
|
|
50122
50857
|
"../../packages/tacho/src/collector/model-routes.ts"() {
|
|
50123
50858
|
"use strict";
|
|
@@ -50146,6 +50881,8 @@ var init_model_routes = __esm({
|
|
|
50146
50881
|
"upgrade"
|
|
50147
50882
|
]);
|
|
50148
50883
|
CREDENTIAL_HEADERS = ["authorization", "x-api-key"];
|
|
50884
|
+
CHATGPT_ACCOUNT_HEADER = "chatgpt-account-id";
|
|
50885
|
+
ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
50149
50886
|
}
|
|
50150
50887
|
});
|
|
50151
50888
|
|
|
@@ -50877,7 +51614,7 @@ function createModelProxy(deps) {
|
|
|
50877
51614
|
function resolveCredential(req, route) {
|
|
50878
51615
|
const broker = deps.credentials;
|
|
50879
51616
|
const presented = presentedCredential(req);
|
|
50880
|
-
const brokered = broker !== void 0 && route.
|
|
51617
|
+
const brokered = broker !== void 0 && route.harness !== "stella" && broker.brokered(route.provider) && route.upstream === "chatgpt" === (broker.kind?.(route.provider) === "chatgpt");
|
|
50881
51618
|
if (broker === void 0 || !brokered) {
|
|
50882
51619
|
if (presented !== void 0 && looksLikeRunToken(presented.value)) {
|
|
50883
51620
|
const claims = peekRunTokenClaims(presented.value);
|
|
@@ -50932,6 +51669,27 @@ function createModelProxy(deps) {
|
|
|
50932
51669
|
}
|
|
50933
51670
|
};
|
|
50934
51671
|
}
|
|
51672
|
+
if (held2.kind === "chatgpt") {
|
|
51673
|
+
const login = decodeChatGptLogin(held2.secret);
|
|
51674
|
+
if (login === void 0)
|
|
51675
|
+
return {
|
|
51676
|
+
basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
|
|
51677
|
+
claims: verdict.claims,
|
|
51678
|
+
refusal: {
|
|
51679
|
+
code: "credential_unavailable",
|
|
51680
|
+
message: CREDENTIAL_MESSAGES.credential_unavailable
|
|
51681
|
+
}
|
|
51682
|
+
};
|
|
51683
|
+
return {
|
|
51684
|
+
basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
|
|
51685
|
+
claims: verdict.claims,
|
|
51686
|
+
attach: {
|
|
51687
|
+
kind: "chatgpt",
|
|
51688
|
+
secret: login.access_token,
|
|
51689
|
+
...login.account_id !== void 0 ? { accountId: login.account_id } : {}
|
|
51690
|
+
}
|
|
51691
|
+
};
|
|
51692
|
+
}
|
|
50935
51693
|
return {
|
|
50936
51694
|
basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
|
|
50937
51695
|
claims: verdict.claims,
|
|
@@ -51622,6 +52380,15 @@ function createModelProxy(deps) {
|
|
|
51622
52380
|
if (result2.path !== path2 && result2.path.startsWith("/"))
|
|
51623
52381
|
path2 = result2.path;
|
|
51624
52382
|
}
|
|
52383
|
+
if (credential.attach?.kind === "chatgpt") {
|
|
52384
|
+
const sent2 = injected ? injectedJson : json2();
|
|
52385
|
+
if (sent2 !== void 0 && sent2["store"] !== false) {
|
|
52386
|
+
const unstored = { ...sent2, store: false };
|
|
52387
|
+
if (injected) injectedJson = unstored;
|
|
52388
|
+
body2 = Buffer.from(JSON.stringify(unstored), "utf8");
|
|
52389
|
+
dropContentEncoding = true;
|
|
52390
|
+
}
|
|
52391
|
+
}
|
|
51625
52392
|
const requestModel = askedModel;
|
|
51626
52393
|
const requestEffort = requestEffortOf(injected ? injectedJson : json2());
|
|
51627
52394
|
const sent = injected ? body2 : readable();
|
|
@@ -52186,6 +52953,7 @@ var init_model_proxy = __esm({
|
|
|
52186
52953
|
init_frame_body();
|
|
52187
52954
|
init_replay_grade();
|
|
52188
52955
|
init_bundle();
|
|
52956
|
+
init_model_credential();
|
|
52189
52957
|
init_run_token();
|
|
52190
52958
|
init_wire();
|
|
52191
52959
|
init_cache_keep_alive();
|
|
@@ -52921,12 +53689,12 @@ var init_server = __esm({
|
|
|
52921
53689
|
|
|
52922
53690
|
// ../../packages/tacho/src/collector/refused.ts
|
|
52923
53691
|
import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync12 } from "node:fs";
|
|
52924
|
-
import { join as
|
|
53692
|
+
import { join as join43 } from "node:path";
|
|
52925
53693
|
function refusalRetryable(refusal) {
|
|
52926
53694
|
return refusal === "malformed" || refusal === "parked";
|
|
52927
53695
|
}
|
|
52928
53696
|
function refusedRecordPath(dir, event) {
|
|
52929
|
-
return
|
|
53697
|
+
return join43(dir, `${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`);
|
|
52930
53698
|
}
|
|
52931
53699
|
function newRefusedRecord(event, reason3, refusal, at2, body2) {
|
|
52932
53700
|
const retryable = refusalRetryable(refusal);
|
|
@@ -53002,7 +53770,7 @@ function scanRefused(dir, now, limit = MAX_REFUSED_RETRIES_PER_PASS) {
|
|
|
53002
53770
|
};
|
|
53003
53771
|
const due = [];
|
|
53004
53772
|
for (const name of names2) {
|
|
53005
|
-
const entry2 = readRefusedEntry(
|
|
53773
|
+
const entry2 = readRefusedEntry(join43(dir, name));
|
|
53006
53774
|
if (entry2 === void 0) continue;
|
|
53007
53775
|
summary.total += 1;
|
|
53008
53776
|
if (summary.lastRefusedAt === void 0 || entry2.refusedAt > summary.lastRefusedAt)
|
|
@@ -53039,7 +53807,7 @@ var init_refused = __esm({
|
|
|
53039
53807
|
|
|
53040
53808
|
// ../../packages/tacho/src/collector/spool.ts
|
|
53041
53809
|
import { existsSync as existsSync22, readFileSync as readFileSync20, unlinkSync as unlinkSync12 } from "node:fs";
|
|
53042
|
-
import { join as
|
|
53810
|
+
import { join as join44 } from "node:path";
|
|
53043
53811
|
function sessionOwnedElsewhere(error) {
|
|
53044
53812
|
return error.status === 403 && error.body.includes(SESSION_OWNED_ELSEWHERE);
|
|
53045
53813
|
}
|
|
@@ -53272,7 +54040,7 @@ var init_spool = __esm({
|
|
|
53272
54040
|
}
|
|
53273
54041
|
/** Add a body to a quarantine record that was written without one. */
|
|
53274
54042
|
stashBodyBeside(event, body2) {
|
|
53275
|
-
const path2 =
|
|
54043
|
+
const path2 = join44(
|
|
53276
54044
|
this.options.quarantineDir,
|
|
53277
54045
|
`${event.session_uuid}-${String(event.seq).padStart(8, "0")}.json`
|
|
53278
54046
|
);
|
|
@@ -53928,7 +54696,7 @@ import {
|
|
|
53928
54696
|
} from "node:fs";
|
|
53929
54697
|
import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
|
|
53930
54698
|
import { homedir as homedir7, hostname as osHostname2 } from "node:os";
|
|
53931
|
-
import { dirname as dirname24, join as
|
|
54699
|
+
import { dirname as dirname24, join as join45 } from "node:path";
|
|
53932
54700
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
53933
54701
|
function cloudGatewayUpstreams(origin = CLOUD_GATEWAY_ORIGIN) {
|
|
53934
54702
|
const base2 = new URL(origin).origin;
|
|
@@ -54672,7 +55440,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
54672
55440
|
controlReachable: lastControlAt !== void 0 && now() - lastControlAt < 2 * timers.bundleRefreshMs
|
|
54673
55441
|
};
|
|
54674
55442
|
}
|
|
54675
|
-
const bodyPurgeOwedPath =
|
|
55443
|
+
const bodyPurgeOwedPath = join45(paths.wal, "body-purge-owed");
|
|
54676
55444
|
function markBodyPurgeOwed(retention, dropped) {
|
|
54677
55445
|
try {
|
|
54678
55446
|
ensureDir(paths.wal);
|
|
@@ -54949,7 +55717,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
54949
55717
|
}
|
|
54950
55718
|
void workOrderAutostart.reconcile();
|
|
54951
55719
|
}
|
|
54952
|
-
const lastIngestFile =
|
|
55720
|
+
const lastIngestFile = join45(paths.dir, "last-ingest.json");
|
|
54953
55721
|
const shipper = new Shipper({
|
|
54954
55722
|
wal,
|
|
54955
55723
|
client,
|
|
@@ -55471,7 +56239,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55471
56239
|
async function readSpoolFile(name) {
|
|
55472
56240
|
try {
|
|
55473
56241
|
const file = parseSpoolFile(
|
|
55474
|
-
await readFile3(
|
|
56242
|
+
await readFile3(join45(paths.spool, name), "utf8")
|
|
55475
56243
|
);
|
|
55476
56244
|
spoolFacts.set(name, spoolFactsOf(file));
|
|
55477
56245
|
return file;
|
|
@@ -55660,7 +56428,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55660
56428
|
function setAsideConflictingTerminal(uuid, pending, conflict) {
|
|
55661
56429
|
const name = `${uuid}-terminal-conflict-${String(conflict.seq).padStart(8, "0")}.json`;
|
|
55662
56430
|
writeSensitiveFileAtomic(
|
|
55663
|
-
|
|
56431
|
+
join45(paths.quarantine, name),
|
|
55664
56432
|
JSON.stringify(
|
|
55665
56433
|
{
|
|
55666
56434
|
reason: conflict.message,
|
|
@@ -55740,8 +56508,8 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55740
56508
|
function moveToFailedSpool(path2, name, reason3) {
|
|
55741
56509
|
log(`spool file ${name} moved to spool/failed/: ${reason3}`);
|
|
55742
56510
|
try {
|
|
55743
|
-
ensureDir(
|
|
55744
|
-
renameSync4(path2,
|
|
56511
|
+
ensureDir(join45(paths.spool, "failed"));
|
|
56512
|
+
renameSync4(path2, join45(paths.spool, "failed", name));
|
|
55745
56513
|
} catch (error) {
|
|
55746
56514
|
log(
|
|
55747
56515
|
`spool file ${name} could not be moved to spool/failed/: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -55794,7 +56562,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55794
56562
|
continue;
|
|
55795
56563
|
}
|
|
55796
56564
|
reads += 1;
|
|
55797
|
-
const path2 =
|
|
56565
|
+
const path2 = join45(paths.spool, name);
|
|
55798
56566
|
let file;
|
|
55799
56567
|
try {
|
|
55800
56568
|
file = await readSpoolFile(name);
|
|
@@ -56183,6 +56951,13 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56183
56951
|
...options.pauseHold,
|
|
56184
56952
|
credentials: {
|
|
56185
56953
|
brokered: (provider) => inCustody(provider),
|
|
56954
|
+
kind: (provider) => {
|
|
56955
|
+
try {
|
|
56956
|
+
return credentialStore.status().find((entry2) => entry2.provider === provider)?.kind;
|
|
56957
|
+
} catch {
|
|
56958
|
+
return void 0;
|
|
56959
|
+
}
|
|
56960
|
+
},
|
|
56186
56961
|
custody: (provider) => {
|
|
56187
56962
|
try {
|
|
56188
56963
|
return credentialStore.read(provider);
|
|
@@ -56247,6 +57022,13 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56247
57022
|
record,
|
|
56248
57023
|
model: modelProxy,
|
|
56249
57024
|
modelPort: () => modelProxyListener.port(),
|
|
57025
|
+
chatgptLogin: () => {
|
|
57026
|
+
try {
|
|
57027
|
+
return credentialStore.status().some((c) => c.provider === "openai" && c.kind === "chatgpt");
|
|
57028
|
+
} catch {
|
|
57029
|
+
return false;
|
|
57030
|
+
}
|
|
57031
|
+
},
|
|
56250
57032
|
issueCredential: (harness) => issueRunToken(
|
|
56251
57033
|
{ harness },
|
|
56252
57034
|
{
|
|
@@ -56621,7 +57403,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56621
57403
|
let removed = 0;
|
|
56622
57404
|
for (const name of readdirSync13(paths.quarantine)) {
|
|
56623
57405
|
if (!name.endsWith(".json")) continue;
|
|
56624
|
-
const file =
|
|
57406
|
+
const file = join45(paths.quarantine, name);
|
|
56625
57407
|
try {
|
|
56626
57408
|
const refusedAt = readRefusedEntry(file)?.refusedAt ?? statSync13(file).mtimeMs;
|
|
56627
57409
|
if (at2 - refusedAt < retainMs) continue;
|
|
@@ -56877,8 +57659,29 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56877
57659
|
});
|
|
56878
57660
|
}
|
|
56879
57661
|
let lastStaticRenewalAt = 0;
|
|
57662
|
+
let lastLoginRefreshAt = 0;
|
|
56880
57663
|
async function renewStaticTokens() {
|
|
56881
57664
|
if (!host.harnesses.includes("codex")) return;
|
|
57665
|
+
if (now() - lastLoginRefreshAt >= CHATGPT_LOGIN_CHECK_MS) {
|
|
57666
|
+
lastLoginRefreshAt = now();
|
|
57667
|
+
try {
|
|
57668
|
+
const refreshed = await refreshChatGptCustody(
|
|
57669
|
+
credentialStore,
|
|
57670
|
+
options.fetch ?? fetch,
|
|
57671
|
+
now()
|
|
57672
|
+
);
|
|
57673
|
+
if (refreshed === "refreshed")
|
|
57674
|
+
log("refreshed the ChatGPT login in custody");
|
|
57675
|
+
else if (typeof refreshed === "object")
|
|
57676
|
+
log(`ChatGPT login refresh: ${refreshed.failed}`);
|
|
57677
|
+
if (refreshed !== "none")
|
|
57678
|
+
restateCodexLastRefresh(options.home ?? homedir7(), now());
|
|
57679
|
+
} catch (error) {
|
|
57680
|
+
log(
|
|
57681
|
+
`ChatGPT login refresh: ${error instanceof Error ? error.message : String(error)}`
|
|
57682
|
+
);
|
|
57683
|
+
}
|
|
57684
|
+
}
|
|
56882
57685
|
if (now() - lastStaticRenewalAt < STATIC_TOKEN_RENEWAL_CHECK_MS) return;
|
|
56883
57686
|
lastStaticRenewalAt = now();
|
|
56884
57687
|
if (host.host_status !== "active" || !inCustody("openai")) return;
|
|
@@ -56951,7 +57754,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56951
57754
|
let memoryScan;
|
|
56952
57755
|
if (options.listen ?? true) {
|
|
56953
57756
|
const codexMemories = createCodexMemoryStore({
|
|
56954
|
-
path:
|
|
57757
|
+
path: join45(dirname24(paths.codexHooks), CODEX_MEMORY_STORE_FILE),
|
|
56955
57758
|
stat: (path2) => stat2(path2),
|
|
56956
57759
|
log
|
|
56957
57760
|
});
|
|
@@ -57096,7 +57899,7 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
57096
57899
|
}
|
|
57097
57900
|
};
|
|
57098
57901
|
}
|
|
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;
|
|
57902
|
+
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
57903
|
var init_daemon = __esm({
|
|
57101
57904
|
"../../packages/tacho/src/collector/daemon.ts"() {
|
|
57102
57905
|
"use strict";
|
|
@@ -57118,6 +57921,7 @@ var init_daemon = __esm({
|
|
|
57118
57921
|
init_claude_code_mcp_writer();
|
|
57119
57922
|
init_model_base_url();
|
|
57120
57923
|
init_model_credential();
|
|
57924
|
+
init_chatgpt_refresh();
|
|
57121
57925
|
init_run_token();
|
|
57122
57926
|
init_process_scan();
|
|
57123
57927
|
init_retention();
|
|
@@ -57190,6 +57994,7 @@ var init_daemon = __esm({
|
|
|
57190
57994
|
hookBundleWaitMs: 2e3
|
|
57191
57995
|
};
|
|
57192
57996
|
STATIC_TOKEN_RENEWAL_CHECK_MS = 60 * 6e4;
|
|
57997
|
+
CHATGPT_LOGIN_CHECK_MS = 10 * 6e4;
|
|
57193
57998
|
MEMORY_SCAN_MS = 5 * 6e4;
|
|
57194
57999
|
CLOUD_GATEWAY_ORIGIN = "https://api.oxagen.sh";
|
|
57195
58000
|
CLOUD_GATEWAY_BASE_PATH = "/v1/gateway";
|
|
@@ -57454,7 +58259,7 @@ var init_run2 = __esm({
|
|
|
57454
58259
|
// ../../packages/tacho/src/arp/bundle.ts
|
|
57455
58260
|
import {
|
|
57456
58261
|
closeSync as closeSync8,
|
|
57457
|
-
constants as
|
|
58262
|
+
constants as constants5,
|
|
57458
58263
|
fstatSync as fstatSync2,
|
|
57459
58264
|
lstatSync as lstatSync8,
|
|
57460
58265
|
mkdirSync as mkdirSync6,
|
|
@@ -57463,7 +58268,7 @@ import {
|
|
|
57463
58268
|
realpathSync as realpathSync5,
|
|
57464
58269
|
writeFileSync as writeFileSync4
|
|
57465
58270
|
} from "node:fs";
|
|
57466
|
-
import { dirname as dirname25, join as
|
|
58271
|
+
import { dirname as dirname25, join as join46, resolve as resolve17 } from "node:path";
|
|
57467
58272
|
function bundlePath(root, relative11, create2 = false) {
|
|
57468
58273
|
const absolute = resolve17(root);
|
|
57469
58274
|
if (create2) mkdirSync6(absolute, { recursive: true, mode: 448 });
|
|
@@ -57474,7 +58279,7 @@ function bundlePath(root, relative11, create2 = false) {
|
|
|
57474
58279
|
let current = base2;
|
|
57475
58280
|
const parts = relative11.split("/");
|
|
57476
58281
|
for (let i2 = 0; i2 < parts.length; i2++) {
|
|
57477
|
-
current =
|
|
58282
|
+
current = join46(current, parts[i2]);
|
|
57478
58283
|
try {
|
|
57479
58284
|
const stat7 = lstatSync8(current);
|
|
57480
58285
|
if (stat7.isSymbolicLink() || i2 < parts.length - 1 && !stat7.isDirectory()) {
|
|
@@ -57495,7 +58300,7 @@ function bundlePath(root, relative11, create2 = false) {
|
|
|
57495
58300
|
}
|
|
57496
58301
|
function readBounded(root, relative11, limit = ARP_MAX_BLOB_BYTES) {
|
|
57497
58302
|
const path2 = bundlePath(root, relative11);
|
|
57498
|
-
const fd2 = openSync8(path2,
|
|
58303
|
+
const fd2 = openSync8(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
57499
58304
|
try {
|
|
57500
58305
|
const stat7 = fstatSync2(fd2);
|
|
57501
58306
|
if (!stat7.isFile() || stat7.size > limit)
|
|
@@ -57520,7 +58325,7 @@ function writeNew(root, relative11, bytes) {
|
|
|
57520
58325
|
const path2 = bundlePath(root, relative11, true);
|
|
57521
58326
|
const fd2 = openSync8(
|
|
57522
58327
|
path2,
|
|
57523
|
-
|
|
58328
|
+
constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW,
|
|
57524
58329
|
384
|
|
57525
58330
|
);
|
|
57526
58331
|
try {
|
|
@@ -58425,7 +59230,7 @@ var init_oracles = __esm({
|
|
|
58425
59230
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
58426
59231
|
import {
|
|
58427
59232
|
closeSync as closeSync9,
|
|
58428
|
-
constants as
|
|
59233
|
+
constants as constants6,
|
|
58429
59234
|
fstatSync as fstatSync3,
|
|
58430
59235
|
lstatSync as lstatSync9,
|
|
58431
59236
|
mkdirSync as mkdirSync7,
|
|
@@ -58435,7 +59240,7 @@ import {
|
|
|
58435
59240
|
rmSync as rmSync8,
|
|
58436
59241
|
writeFileSync as writeFileSync5
|
|
58437
59242
|
} from "node:fs";
|
|
58438
|
-
import { dirname as dirname26, isAbsolute as isAbsolute11, join as
|
|
59243
|
+
import { dirname as dirname26, isAbsolute as isAbsolute11, join as join47, relative as relative5, resolve as resolve18, sep as sep7 } from "node:path";
|
|
58439
59244
|
function outsideProfile(path2) {
|
|
58440
59245
|
return HARNESS_CONFIG_PATH.test(path2) || CREDENTIAL_PATH.test(path2);
|
|
58441
59246
|
}
|
|
@@ -58446,7 +59251,7 @@ function sameJson(bytes, expected) {
|
|
|
58446
59251
|
return jcs(JSON.parse(bytes.toString("utf8"))) === jcs(expected);
|
|
58447
59252
|
}
|
|
58448
59253
|
function readRegular(label2, path2, limit) {
|
|
58449
|
-
const fd2 = openSync9(path2,
|
|
59254
|
+
const fd2 = openSync9(path2, constants6.O_RDONLY | constants6.O_NOFOLLOW);
|
|
58450
59255
|
try {
|
|
58451
59256
|
const before = fstatSync3(fd2);
|
|
58452
59257
|
if (!before.isFile() || before.size > limit)
|
|
@@ -58491,7 +59296,7 @@ function within2(root, path2) {
|
|
|
58491
59296
|
}
|
|
58492
59297
|
function newPath(path2) {
|
|
58493
59298
|
const full = resolve18(path2);
|
|
58494
|
-
return
|
|
59299
|
+
return join47(
|
|
58495
59300
|
realpathSync6(dirname26(full)),
|
|
58496
59301
|
full.slice(dirname26(full).length + 1)
|
|
58497
59302
|
);
|
|
@@ -58505,7 +59310,7 @@ function sourcePath(root, path2) {
|
|
|
58505
59310
|
}
|
|
58506
59311
|
let current = root;
|
|
58507
59312
|
for (const part2 of path2.split("/")) {
|
|
58508
|
-
current =
|
|
59313
|
+
current = join47(current, part2);
|
|
58509
59314
|
if (lstatSync9(current).isSymbolicLink())
|
|
58510
59315
|
arpFail(
|
|
58511
59316
|
`${path2} is a symlink or passes through one. Symlinks require a resource adapter.`
|
|
@@ -58772,10 +59577,10 @@ function prepareCheckpoint(options) {
|
|
|
58772
59577
|
});
|
|
58773
59578
|
mkdirSync7(destination, { mode: 448 });
|
|
58774
59579
|
try {
|
|
58775
|
-
const restored =
|
|
59580
|
+
const restored = join47(destination, "workspace");
|
|
58776
59581
|
mkdirSync7(restored, { mode: 448 });
|
|
58777
59582
|
for (const item of restore) {
|
|
58778
|
-
const path2 =
|
|
59583
|
+
const path2 = join47(restored, item.path);
|
|
58779
59584
|
if (item.kind === "directory")
|
|
58780
59585
|
mkdirSync7(path2, { recursive: true, mode: 448 });
|
|
58781
59586
|
else {
|
|
@@ -58786,9 +59591,9 @@ function prepareCheckpoint(options) {
|
|
|
58786
59591
|
});
|
|
58787
59592
|
}
|
|
58788
59593
|
}
|
|
58789
|
-
const promptDir =
|
|
59594
|
+
const promptDir = join47(restored, ".arp");
|
|
58790
59595
|
mkdirSync7(promptDir, { mode: 448 });
|
|
58791
|
-
const promptFile =
|
|
59596
|
+
const promptFile = join47(promptDir, "handoff.json");
|
|
58792
59597
|
const prompt = {
|
|
58793
59598
|
checkpoint_digest: checkpointDigest,
|
|
58794
59599
|
task: task.data.request,
|
|
@@ -58805,7 +59610,7 @@ function prepareCheckpoint(options) {
|
|
|
58805
59610
|
flag: "wx",
|
|
58806
59611
|
mode: 384
|
|
58807
59612
|
});
|
|
58808
|
-
const reportPath =
|
|
59613
|
+
const reportPath = join47(destination, "report.json");
|
|
58809
59614
|
writeFileSync5(
|
|
58810
59615
|
reportPath,
|
|
58811
59616
|
jcs(
|
|
@@ -59182,6 +59987,41 @@ function buildTachoProgram(options = {}) {
|
|
|
59182
59987
|
deps.err(`${name} credential: ${result2.detail}`);
|
|
59183
59988
|
process.exitCode = 1;
|
|
59184
59989
|
});
|
|
59990
|
+
credential.command("login").description(
|
|
59991
|
+
"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`)"
|
|
59992
|
+
).option("--harness <name>", "claude-code (default) | codex").option(
|
|
59993
|
+
"--token-stdin",
|
|
59994
|
+
"Claude Code only: read a token `claude setup-token` printed from stdin instead of signing in"
|
|
59995
|
+
).option("--json", "Print the outcome as JSON").action(
|
|
59996
|
+
async (opts) => {
|
|
59997
|
+
const harness = opts.harness ?? "claude-code";
|
|
59998
|
+
if (harness !== "claude-code" && harness !== "codex") {
|
|
59999
|
+
deps.err(
|
|
60000
|
+
`${name} credential login: --harness takes claude-code or codex; got "${harness}"`
|
|
60001
|
+
);
|
|
60002
|
+
process.exitCode = 1;
|
|
60003
|
+
return;
|
|
60004
|
+
}
|
|
60005
|
+
const outcome = await credentialLogin(
|
|
60006
|
+
{
|
|
60007
|
+
harness,
|
|
60008
|
+
...opts.tokenStdin === true ? { tokenStdin: true } : {}
|
|
60009
|
+
},
|
|
60010
|
+
deps
|
|
60011
|
+
);
|
|
60012
|
+
if (opts.json === true) deps.out(JSON.stringify(outcome, null, 2));
|
|
60013
|
+
else {
|
|
60014
|
+
for (const entry2 of outcome.harnesses)
|
|
60015
|
+
deps.out(` ${describeHarness(entry2)}`);
|
|
60016
|
+
for (const warning of outcome.warnings)
|
|
60017
|
+
deps.err(`warning: ${warning}`);
|
|
60018
|
+
(outcome.ok ? deps.out : deps.err)(
|
|
60019
|
+
`${outcome.ok ? "" : `${name} credential login: `}${outcome.detail}`
|
|
60020
|
+
);
|
|
60021
|
+
}
|
|
60022
|
+
if (!outcome.ok) process.exitCode = 1;
|
|
60023
|
+
}
|
|
60024
|
+
);
|
|
59185
60025
|
credential.command("status").description(
|
|
59186
60026
|
"Which providers the gateway holds, and how each harness gets its credential"
|
|
59187
60027
|
).option("--json", "Machine-readable output").action(async (opts) => {
|
|
@@ -59446,7 +60286,7 @@ var init_package = __esm({
|
|
|
59446
60286
|
"package.json"() {
|
|
59447
60287
|
package_default = {
|
|
59448
60288
|
name: "@oxagen/cli",
|
|
59449
|
-
version: "2.1.4-
|
|
60289
|
+
version: "2.1.4-975",
|
|
59450
60290
|
description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
|
|
59451
60291
|
type: "module",
|
|
59452
60292
|
main: "./dist/index.js",
|
|
@@ -59513,10 +60353,10 @@ var init_package = __esm({
|
|
|
59513
60353
|
|
|
59514
60354
|
// src/lib/atomic-write.ts
|
|
59515
60355
|
import { writeFileSync as writeFileSync6, renameSync as renameSync5, unlinkSync as unlinkSync15, existsSync as existsSync24 } from "node:fs";
|
|
59516
|
-
import { dirname as dirname27, join as
|
|
60356
|
+
import { dirname as dirname27, join as join48 } from "node:path";
|
|
59517
60357
|
function atomicWriteFileSync(path2, data, mode) {
|
|
59518
60358
|
const dir = dirname27(path2);
|
|
59519
|
-
const tmp =
|
|
60359
|
+
const tmp = join48(
|
|
59520
60360
|
dir,
|
|
59521
60361
|
`.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`
|
|
59522
60362
|
);
|
|
@@ -59541,7 +60381,7 @@ var init_atomic_write = __esm({
|
|
|
59541
60381
|
|
|
59542
60382
|
// src/lib/config.ts
|
|
59543
60383
|
import { homedir as homedir8 } from "os";
|
|
59544
|
-
import { join as
|
|
60384
|
+
import { join as join49 } from "path";
|
|
59545
60385
|
import { readFileSync as readFileSync24, mkdirSync as mkdirSync8, existsSync as existsSync25, statSync as statSync14 } from "fs";
|
|
59546
60386
|
function getConfigDir() {
|
|
59547
60387
|
return CONFIG_DIR;
|
|
@@ -59628,8 +60468,8 @@ var CONFIG_DIR, CONFIG_FILE, STRING_FIELDS, TELEMETRY_FIELDS, cached2;
|
|
|
59628
60468
|
var init_config = __esm({
|
|
59629
60469
|
"src/lib/config.ts"() {
|
|
59630
60470
|
init_atomic_write();
|
|
59631
|
-
CONFIG_DIR =
|
|
59632
|
-
CONFIG_FILE =
|
|
60471
|
+
CONFIG_DIR = join49(homedir8(), ".config", "oxagen");
|
|
60472
|
+
CONFIG_FILE = join49(CONFIG_DIR, "config.json");
|
|
59633
60473
|
STRING_FIELDS = [
|
|
59634
60474
|
"token",
|
|
59635
60475
|
"orgSlug",
|
|
@@ -59674,16 +60514,16 @@ import {
|
|
|
59674
60514
|
writeFile as writeFile2
|
|
59675
60515
|
} from "node:fs/promises";
|
|
59676
60516
|
import { homedir as homedir9 } from "node:os";
|
|
59677
|
-
import { join as
|
|
60517
|
+
import { join as join50 } from "node:path";
|
|
59678
60518
|
function isDebugEnabled() {
|
|
59679
60519
|
const v = process.env[DEBUG_ENV];
|
|
59680
60520
|
return v === "1" || v === "true";
|
|
59681
60521
|
}
|
|
59682
60522
|
function logsDir() {
|
|
59683
|
-
return
|
|
60523
|
+
return join50(homedir9(), ".oxagen", "logs");
|
|
59684
60524
|
}
|
|
59685
60525
|
function debugLogFile() {
|
|
59686
|
-
return
|
|
60526
|
+
return join50(logsDir(), "cli.output");
|
|
59687
60527
|
}
|
|
59688
60528
|
function sanitize(value, depth = 0) {
|
|
59689
60529
|
if (value === null || value === void 0) return value;
|
|
@@ -62978,9 +63818,9 @@ var init_git = __esm({
|
|
|
62978
63818
|
|
|
62979
63819
|
// ../../packages/steering-freshness/src/cache.ts
|
|
62980
63820
|
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
62981
|
-
import { dirname as dirname28, join as
|
|
63821
|
+
import { dirname as dirname28, join as join51 } from "node:path";
|
|
62982
63822
|
function stampPath(gitCommonDir) {
|
|
62983
|
-
return
|
|
63823
|
+
return join51(gitCommonDir, "oxagen", "steering-freshness.json");
|
|
62984
63824
|
}
|
|
62985
63825
|
async function readFetchStamp(gitCommonDir, io = defaultIo) {
|
|
62986
63826
|
try {
|
|
@@ -63032,24 +63872,24 @@ var init_cache = __esm({
|
|
|
63032
63872
|
// ../../packages/steering-freshness/src/hooks.ts
|
|
63033
63873
|
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
|
|
63034
63874
|
import { homedir as homedir10 } from "node:os";
|
|
63035
|
-
import { dirname as dirname29, join as
|
|
63875
|
+
import { dirname as dirname29, join as join52 } from "node:path";
|
|
63036
63876
|
function hookCommand(harness) {
|
|
63037
63877
|
return `oxagen steering gate --harness ${harness}`;
|
|
63038
63878
|
}
|
|
63039
63879
|
function hookConfigPath(projectRoot, harness, env = process.env, home = homedir10()) {
|
|
63040
63880
|
switch (harness) {
|
|
63041
63881
|
case "claude-code":
|
|
63042
|
-
return
|
|
63882
|
+
return join52(projectRoot, ".claude", "settings.json");
|
|
63043
63883
|
case "codex":
|
|
63044
|
-
return
|
|
63884
|
+
return join52(env["CODEX_HOME"] ?? join52(home, ".codex"), "hooks.json");
|
|
63045
63885
|
case "cursor":
|
|
63046
|
-
return
|
|
63886
|
+
return join52(home, ".cursor", "hooks.json");
|
|
63047
63887
|
case "stella":
|
|
63048
|
-
return
|
|
63888
|
+
return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "stella.toml");
|
|
63049
63889
|
}
|
|
63050
63890
|
}
|
|
63051
63891
|
function stellaJsonPath(env, home) {
|
|
63052
|
-
return
|
|
63892
|
+
return join52(env["STELLA_HOME"] ?? join52(home, ".stella"), "settings.json");
|
|
63053
63893
|
}
|
|
63054
63894
|
function isRecord7(v) {
|
|
63055
63895
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
@@ -63709,7 +64549,7 @@ var init_policy = __esm({
|
|
|
63709
64549
|
// ../../packages/steering-freshness/src/settings.ts
|
|
63710
64550
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
63711
64551
|
import { homedir as homedir11 } from "node:os";
|
|
63712
|
-
import { join as
|
|
64552
|
+
import { join as join53 } from "node:path";
|
|
63713
64553
|
async function readLayer(scope, path2, read2) {
|
|
63714
64554
|
let text5;
|
|
63715
64555
|
try {
|
|
@@ -63761,14 +64601,14 @@ function parseLayer(scope, path2, text5) {
|
|
|
63761
64601
|
}
|
|
63762
64602
|
async function loadSteeringSettings({
|
|
63763
64603
|
projectRoot,
|
|
63764
|
-
userSettingsPath =
|
|
64604
|
+
userSettingsPath = join53(homedir11(), USER_SETTINGS_RELATIVE),
|
|
63765
64605
|
read: read2 = readFile8,
|
|
63766
64606
|
workspacePolicy = null
|
|
63767
64607
|
}) {
|
|
63768
64608
|
const targets = [
|
|
63769
64609
|
["user", userSettingsPath],
|
|
63770
|
-
["project",
|
|
63771
|
-
["local",
|
|
64610
|
+
["project", join53(projectRoot, PROJECT_DIR_NAME, PROJECT_SETTINGS_FILE)],
|
|
64611
|
+
["local", join53(projectRoot, PROJECT_DIR_NAME, LOCAL_SETTINGS_FILE)]
|
|
63772
64612
|
];
|
|
63773
64613
|
const layers = [];
|
|
63774
64614
|
const warnings = [];
|
|
@@ -63831,7 +64671,7 @@ var init_settings = __esm({
|
|
|
63831
64671
|
"use strict";
|
|
63832
64672
|
init_git();
|
|
63833
64673
|
init_policy();
|
|
63834
|
-
USER_SETTINGS_RELATIVE =
|
|
64674
|
+
USER_SETTINGS_RELATIVE = join53(
|
|
63835
64675
|
".config",
|
|
63836
64676
|
"oxagen",
|
|
63837
64677
|
"settings.json"
|
|
@@ -63844,7 +64684,7 @@ var init_settings = __esm({
|
|
|
63844
64684
|
|
|
63845
64685
|
// ../../packages/steering-freshness/src/check.ts
|
|
63846
64686
|
import { lstat as lstat3 } from "node:fs/promises";
|
|
63847
|
-
import { isAbsolute as isAbsolute12, join as
|
|
64687
|
+
import { isAbsolute as isAbsolute12, join as join54, resolve as resolve19 } from "node:path";
|
|
63848
64688
|
function publishedCommits(platform) {
|
|
63849
64689
|
if (platform.headCommits && platform.headCommits.length > 0)
|
|
63850
64690
|
return platform.headCommits;
|
|
@@ -64181,7 +65021,7 @@ async function absentFromDisk(root, paths) {
|
|
|
64181
65021
|
const flags = await Promise.all(
|
|
64182
65022
|
paths.map(async (path2) => {
|
|
64183
65023
|
try {
|
|
64184
|
-
await lstat3(
|
|
65024
|
+
await lstat3(join54(root, path2));
|
|
64185
65025
|
return false;
|
|
64186
65026
|
} catch {
|
|
64187
65027
|
return true;
|
|
@@ -64240,7 +65080,7 @@ var init_check = __esm({
|
|
|
64240
65080
|
// ../../packages/steering-freshness/src/sync.ts
|
|
64241
65081
|
import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
|
|
64242
65082
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
64243
|
-
import { join as
|
|
65083
|
+
import { join as join55 } from "node:path";
|
|
64244
65084
|
function refuse3(refusal, message) {
|
|
64245
65085
|
return {
|
|
64246
65086
|
applied: false,
|
|
@@ -64421,9 +65261,9 @@ async function syncSteering(opts) {
|
|
|
64421
65261
|
};
|
|
64422
65262
|
}
|
|
64423
65263
|
async function commitPaths(ctx, message, paths) {
|
|
64424
|
-
const dir = await mkdtemp(
|
|
65264
|
+
const dir = await mkdtemp(join55(tmpdir2(), "oxagen-steering-sync-"));
|
|
64425
65265
|
try {
|
|
64426
|
-
const list2 =
|
|
65266
|
+
const list2 = join55(dir, "pathspec");
|
|
64427
65267
|
await writeFile6(list2, `${paths.join("\0")}\0`, "utf8");
|
|
64428
65268
|
await git3(
|
|
64429
65269
|
ctx,
|
|
@@ -64951,9 +65791,9 @@ var init_paths2 = __esm({
|
|
|
64951
65791
|
|
|
64952
65792
|
// src/commands/workspace-link.ts
|
|
64953
65793
|
import { existsSync as existsSync26, mkdirSync as mkdirSync9, readFileSync as readFileSync25 } from "node:fs";
|
|
64954
|
-
import { dirname as dirname30, join as
|
|
65794
|
+
import { dirname as dirname30, join as join56 } from "node:path";
|
|
64955
65795
|
function workspaceLinkPath(cwd) {
|
|
64956
|
-
return
|
|
65796
|
+
return join56(cwd, WORKSPACE_LINK_PATH);
|
|
64957
65797
|
}
|
|
64958
65798
|
function isRecord8(value) {
|
|
64959
65799
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -65043,7 +65883,7 @@ __export(steering_exports, {
|
|
|
65043
65883
|
});
|
|
65044
65884
|
import { execFileSync } from "node:child_process";
|
|
65045
65885
|
import { existsSync as existsSync27, realpathSync as realpathSync7 } from "node:fs";
|
|
65046
|
-
import { dirname as dirname31, join as
|
|
65886
|
+
import { dirname as dirname31, join as join57, normalize, parse as parsePath } from "node:path";
|
|
65047
65887
|
function findProjectRoot(start) {
|
|
65048
65888
|
const { root } = parsePath(start);
|
|
65049
65889
|
const rawGitRoot = gitRootOr(start);
|
|
@@ -65052,7 +65892,7 @@ function findProjectRoot(start) {
|
|
|
65052
65892
|
if (gitRoot !== null) return gitRoot;
|
|
65053
65893
|
let dir = from;
|
|
65054
65894
|
for (; ; ) {
|
|
65055
|
-
if (existsSync27(
|
|
65895
|
+
if (existsSync27(join57(dir, PROJECT_DIR_NAME))) return dir;
|
|
65056
65896
|
if (dir === root) return from;
|
|
65057
65897
|
const parent = dirname31(dir);
|
|
65058
65898
|
if (parent === dir) return from;
|
|
@@ -78530,10 +79370,10 @@ var init_run_list = __esm({
|
|
|
78530
79370
|
/** `owner/name` as the frame recorded it; null when it recorded none. */
|
|
78531
79371
|
repository: external_exports.string().max(512).nullable(),
|
|
78532
79372
|
/**
|
|
78533
|
-
* The state
|
|
78534
|
-
*
|
|
78535
|
-
*
|
|
78536
|
-
* renders "status unknown", never a guessed "open".
|
|
79373
|
+
* The state `forge.pull_requests` holds for the pull request, which the
|
|
79374
|
+
* forge sync keeps current from each delivery and each recorded link
|
|
79375
|
+
* (ADR-288, #5949). Null when the store holds no row for it, and a caller
|
|
79376
|
+
* then renders "status unknown", never a guessed "open".
|
|
78537
79377
|
*/
|
|
78538
79378
|
state: external_exports.enum(["open", "draft", "merged", "closed"]).nullable(),
|
|
78539
79379
|
/**
|
|
@@ -81417,7 +82257,7 @@ function findSecretsAndPii(text5) {
|
|
|
81417
82257
|
const findings = [];
|
|
81418
82258
|
if (/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(text5))
|
|
81419
82259
|
findings.push("private key block");
|
|
81420
|
-
for (const token of text5.match(
|
|
82260
|
+
for (const token of text5.match(TOKEN2) ?? []) {
|
|
81421
82261
|
if (isSecretToken(token)) {
|
|
81422
82262
|
findings.push("credential token");
|
|
81423
82263
|
break;
|
|
@@ -81444,7 +82284,7 @@ function findSecretsAndPii(text5) {
|
|
|
81444
82284
|
}
|
|
81445
82285
|
return findings;
|
|
81446
82286
|
}
|
|
81447
|
-
var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS,
|
|
82287
|
+
var SECRET_PREFIXES, SENSITIVE_KEY_MARKERS, TOKEN2;
|
|
81448
82288
|
var init_secrets = __esm({
|
|
81449
82289
|
"../../packages/steering-check/src/secrets.ts"() {
|
|
81450
82290
|
"use strict";
|
|
@@ -81495,7 +82335,7 @@ var init_secrets = __esm({
|
|
|
81495
82335
|
"session_id",
|
|
81496
82336
|
"client_secret"
|
|
81497
82337
|
];
|
|
81498
|
-
|
|
82338
|
+
TOKEN2 = /[A-Za-z0-9_\-./+~]+/g;
|
|
81499
82339
|
}
|
|
81500
82340
|
});
|
|
81501
82341
|
|
|
@@ -82443,7 +83283,7 @@ __export(steering_import_exports, {
|
|
|
82443
83283
|
});
|
|
82444
83284
|
import { readdir as readdir4, realpath as realpath2, stat as stat4 } from "node:fs/promises";
|
|
82445
83285
|
import { homedir as homedir12 } from "node:os";
|
|
82446
|
-
import { basename as basename8, extname, join as
|
|
83286
|
+
import { basename as basename8, extname, join as join58, relative as relative7, sep as sep9 } from "node:path";
|
|
82447
83287
|
function envDir2(env, key) {
|
|
82448
83288
|
const value = env[key];
|
|
82449
83289
|
return value === void 0 || value.trim() === "" ? null : value;
|
|
@@ -82451,34 +83291,34 @@ function envDir2(env, key) {
|
|
|
82451
83291
|
function harnessHome(harness, env = process.env, home = homedir12()) {
|
|
82452
83292
|
switch (harness) {
|
|
82453
83293
|
case "claude-code": {
|
|
82454
|
-
const config = envDir2(env, "CLAUDE_CONFIG_DIR") ??
|
|
83294
|
+
const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join58(home, ".claude");
|
|
82455
83295
|
return {
|
|
82456
83296
|
harness,
|
|
82457
83297
|
folders: [
|
|
82458
83298
|
{
|
|
82459
|
-
dir: skillsRoot("claude-code", env, home) ??
|
|
83299
|
+
dir: skillsRoot("claude-code", env, home) ?? join58(config, "skills"),
|
|
82460
83300
|
mount: ".claude/skills"
|
|
82461
83301
|
},
|
|
82462
|
-
{ dir:
|
|
82463
|
-
{ dir:
|
|
83302
|
+
{ dir: join58(config, "commands"), mount: ".claude/commands" },
|
|
83303
|
+
{ dir: join58(config, "agents"), mount: ".claude/agents" }
|
|
82464
83304
|
],
|
|
82465
83305
|
instructions: [
|
|
82466
|
-
{ file:
|
|
83306
|
+
{ file: join58(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
|
|
82467
83307
|
]
|
|
82468
83308
|
};
|
|
82469
83309
|
}
|
|
82470
83310
|
case "codex": {
|
|
82471
|
-
const codexHome = envDir2(env, "CODEX_HOME") ??
|
|
83311
|
+
const codexHome = envDir2(env, "CODEX_HOME") ?? join58(home, ".codex");
|
|
82472
83312
|
return {
|
|
82473
83313
|
harness,
|
|
82474
83314
|
folders: [
|
|
82475
83315
|
{
|
|
82476
|
-
dir: skillsRoot("codex", env, home) ??
|
|
83316
|
+
dir: skillsRoot("codex", env, home) ?? join58(home, ".agents", "skills"),
|
|
82477
83317
|
mount: ".agents/skills"
|
|
82478
83318
|
}
|
|
82479
83319
|
],
|
|
82480
83320
|
instructions: ["AGENTS.override.md", "AGENTS.md"].map((name) => ({
|
|
82481
|
-
file:
|
|
83321
|
+
file: join58(codexHome, name),
|
|
82482
83322
|
filename: `.codex/${name}`
|
|
82483
83323
|
}))
|
|
82484
83324
|
};
|
|
@@ -82487,8 +83327,8 @@ function harnessHome(harness, env = process.env, home = homedir12()) {
|
|
|
82487
83327
|
return {
|
|
82488
83328
|
harness,
|
|
82489
83329
|
folders: [
|
|
82490
|
-
{ dir:
|
|
82491
|
-
{ dir:
|
|
83330
|
+
{ dir: join58(home, ".cursor", "skills"), mount: ".cursor/skills" },
|
|
83331
|
+
{ dir: join58(home, ".cursor", "commands"), mount: ".cursor/commands" }
|
|
82492
83332
|
],
|
|
82493
83333
|
instructions: []
|
|
82494
83334
|
};
|
|
@@ -82534,7 +83374,7 @@ async function findMarkdownFiles(paths, harness) {
|
|
|
82534
83374
|
}
|
|
82535
83375
|
const found = [];
|
|
82536
83376
|
for (const entry2 of entries2.sort(byName)) {
|
|
82537
|
-
const path2 =
|
|
83377
|
+
const path2 = join58(dir, entry2.name);
|
|
82538
83378
|
if (entry2.isDirectory()) {
|
|
82539
83379
|
if (!entry2.name.startsWith(".") && entry2.name !== "node_modules")
|
|
82540
83380
|
found.push(...await filesUnder(path2, from));
|
|
@@ -82556,7 +83396,7 @@ async function findMarkdownFiles(paths, harness) {
|
|
|
82556
83396
|
const { folder } = frame;
|
|
82557
83397
|
const named = (rel) => frame.fixedNames ? { filename: rel } : {};
|
|
82558
83398
|
for (const entry2 of entries2) {
|
|
82559
|
-
const path2 =
|
|
83399
|
+
const path2 = join58(dir, entry2.name);
|
|
82560
83400
|
const rel = frame.relOf(path2);
|
|
82561
83401
|
if (entry2.isDirectory()) {
|
|
82562
83402
|
if (entry2.name === "node_modules") continue;
|
|
@@ -82615,7 +83455,7 @@ async function findMarkdownFiles(paths, harness) {
|
|
|
82615
83455
|
}
|
|
82616
83456
|
if (folderGiven) {
|
|
82617
83457
|
const catalogue = CATALOGUE_FOLDERS.has(basename8(path2));
|
|
82618
|
-
const root = catalogue ?
|
|
83458
|
+
const root = catalogue ? join58(path2, "..") : path2;
|
|
82619
83459
|
await walk2(path2, {
|
|
82620
83460
|
relOf: (p) => under(root, p),
|
|
82621
83461
|
folder: path2,
|
|
@@ -84275,10 +85115,10 @@ __export(verify_exports, {
|
|
|
84275
85115
|
verifyBundle: () => verifyBundle3
|
|
84276
85116
|
});
|
|
84277
85117
|
import { existsSync as existsSync28, readFileSync as readFileSync26, statSync as statSync15 } from "node:fs";
|
|
84278
|
-
import { join as
|
|
85118
|
+
import { join as join59 } from "node:path";
|
|
84279
85119
|
function readDirectory(dir) {
|
|
84280
85120
|
const read2 = (name) => {
|
|
84281
|
-
const path2 =
|
|
85121
|
+
const path2 = join59(dir, name);
|
|
84282
85122
|
return existsSync28(path2) ? readFileSync26(path2, "utf8") : void 0;
|
|
84283
85123
|
};
|
|
84284
85124
|
return assemble(read2, dir);
|
|
@@ -85233,7 +86073,7 @@ var init_conversation = __esm({
|
|
|
85233
86073
|
});
|
|
85234
86074
|
|
|
85235
86075
|
// src/auth/open-browser.ts
|
|
85236
|
-
import { spawn as
|
|
86076
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
85237
86077
|
function openBrowser(url) {
|
|
85238
86078
|
try {
|
|
85239
86079
|
let cmd;
|
|
@@ -85251,7 +86091,7 @@ function openBrowser(url) {
|
|
|
85251
86091
|
cmd = "xdg-open";
|
|
85252
86092
|
args = [url];
|
|
85253
86093
|
}
|
|
85254
|
-
const child =
|
|
86094
|
+
const child = spawn8(cmd, args, { detached: true, stdio: "ignore" });
|
|
85255
86095
|
child.on("error", () => {
|
|
85256
86096
|
});
|
|
85257
86097
|
child.unref();
|
|
@@ -85407,7 +86247,7 @@ import {
|
|
|
85407
86247
|
writeFileSync as writeFileSync7
|
|
85408
86248
|
} from "node:fs";
|
|
85409
86249
|
import { homedir as homedir13, hostname as osHostname3 } from "node:os";
|
|
85410
|
-
import { join as
|
|
86250
|
+
import { join as join60, relative as relative8, resolve as resolve22, sep as sep10 } from "node:path";
|
|
85411
86251
|
function git4(cwd, args) {
|
|
85412
86252
|
return new Promise((done) => {
|
|
85413
86253
|
try {
|
|
@@ -85454,7 +86294,7 @@ function parseRemoteRepository(url) {
|
|
|
85454
86294
|
}
|
|
85455
86295
|
function oxagenPresent(root) {
|
|
85456
86296
|
try {
|
|
85457
|
-
return readdirSync14(
|
|
86297
|
+
return readdirSync14(join60(root, ".oxagen")).some(
|
|
85458
86298
|
(name) => name !== "workspace.json"
|
|
85459
86299
|
);
|
|
85460
86300
|
} catch {
|
|
@@ -85463,18 +86303,18 @@ function oxagenPresent(root) {
|
|
|
85463
86303
|
}
|
|
85464
86304
|
function symlinkState(root) {
|
|
85465
86305
|
try {
|
|
85466
|
-
if (!lstatSync10(
|
|
86306
|
+
if (!lstatSync10(join60(root, ".stella")).isDirectory()) return "none";
|
|
85467
86307
|
} catch {
|
|
85468
86308
|
return "none";
|
|
85469
86309
|
}
|
|
85470
86310
|
let oxagenReal;
|
|
85471
86311
|
try {
|
|
85472
|
-
oxagenReal = realpathSync8(
|
|
86312
|
+
oxagenReal = realpathSync8(join60(root, ".oxagen"));
|
|
85473
86313
|
} catch {
|
|
85474
86314
|
return "missing";
|
|
85475
86315
|
}
|
|
85476
86316
|
for (const link of STELLA_LINKS) {
|
|
85477
|
-
const at2 =
|
|
86317
|
+
const at2 = join60(root, link);
|
|
85478
86318
|
try {
|
|
85479
86319
|
if (!lstatSync10(at2).isSymbolicLink()) return "missing";
|
|
85480
86320
|
const target2 = realpathSync8(at2);
|
|
@@ -85488,7 +86328,7 @@ function symlinkState(root) {
|
|
|
85488
86328
|
return "linked";
|
|
85489
86329
|
}
|
|
85490
86330
|
function machineId(configDir = getConfigDir()) {
|
|
85491
|
-
const file =
|
|
86331
|
+
const file = join60(configDir, MACHINE_ID_FILE);
|
|
85492
86332
|
let secret = null;
|
|
85493
86333
|
try {
|
|
85494
86334
|
if (existsSync29(file)) {
|
|
@@ -85570,12 +86410,12 @@ async function ensureWorkspaceLinkIgnored(root) {
|
|
|
85570
86410
|
const topDir = resolve22(top);
|
|
85571
86411
|
const entry2 = relative8(
|
|
85572
86412
|
topDir,
|
|
85573
|
-
|
|
86413
|
+
join60(realpathOr2(root), ".oxagen", "workspace.json")
|
|
85574
86414
|
).split(sep10).join("/");
|
|
85575
86415
|
if (entry2.startsWith("../")) return null;
|
|
85576
86416
|
const rule = await git4(topDir, ["check-ignore", "--no-index", "-v", entry2]);
|
|
85577
86417
|
if (rule !== null) return null;
|
|
85578
|
-
const gitignore =
|
|
86418
|
+
const gitignore = join60(resolve22(top), ".gitignore");
|
|
85579
86419
|
let existing = "";
|
|
85580
86420
|
try {
|
|
85581
86421
|
existing = readFileSync27(gitignore, "utf8");
|
|
@@ -86100,7 +86940,7 @@ import {
|
|
|
86100
86940
|
import {
|
|
86101
86941
|
dirname as dirname33,
|
|
86102
86942
|
isAbsolute as isAbsolute14,
|
|
86103
|
-
join as
|
|
86943
|
+
join as join61,
|
|
86104
86944
|
parse as parsePath2,
|
|
86105
86945
|
posix as posix4,
|
|
86106
86946
|
sep as sep11
|
|
@@ -86220,7 +87060,7 @@ function findLinkedRoot(start) {
|
|
|
86220
87060
|
}
|
|
86221
87061
|
function fsReader(root) {
|
|
86222
87062
|
return (path2) => {
|
|
86223
|
-
const abs =
|
|
87063
|
+
const abs = join61(root, ...path2.split("/"));
|
|
86224
87064
|
try {
|
|
86225
87065
|
const st = lstatSync11(abs);
|
|
86226
87066
|
if (!st.isFile()) return Buffer.from(`\0not-a-file:${abs}`);
|
|
@@ -86231,14 +87071,14 @@ function fsReader(root) {
|
|
|
86231
87071
|
};
|
|
86232
87072
|
}
|
|
86233
87073
|
function assertInsideOxagen(root, path2) {
|
|
86234
|
-
const oxagenAbs =
|
|
87074
|
+
const oxagenAbs = join61(root, OXAGEN_DIR);
|
|
86235
87075
|
let oxagenReal;
|
|
86236
87076
|
try {
|
|
86237
87077
|
oxagenReal = realpathSync9(oxagenAbs);
|
|
86238
87078
|
} catch {
|
|
86239
87079
|
return;
|
|
86240
87080
|
}
|
|
86241
|
-
const abs =
|
|
87081
|
+
const abs = join61(root, ...path2.split("/"));
|
|
86242
87082
|
try {
|
|
86243
87083
|
if (lstatSync11(abs).isSymbolicLink()) {
|
|
86244
87084
|
throw new Error(
|
|
@@ -86268,8 +87108,8 @@ function assertInsideOxagen(root, path2) {
|
|
|
86268
87108
|
}
|
|
86269
87109
|
}
|
|
86270
87110
|
function pruneEmptyDirs(root, path2) {
|
|
86271
|
-
const stop =
|
|
86272
|
-
let dir = dirname33(
|
|
87111
|
+
const stop = join61(root, OXAGEN_DIR);
|
|
87112
|
+
let dir = dirname33(join61(root, ...path2.split("/")));
|
|
86273
87113
|
while (dir.startsWith(stop + sep11)) {
|
|
86274
87114
|
try {
|
|
86275
87115
|
if (readdirSync15(dir).length > 0) return;
|
|
@@ -86284,12 +87124,12 @@ function applyPlan(root, plan) {
|
|
|
86284
87124
|
for (const w of plan.writes) assertInsideOxagen(root, w.path);
|
|
86285
87125
|
for (const d of plan.deletes) assertInsideOxagen(root, d);
|
|
86286
87126
|
for (const w of plan.writes) {
|
|
86287
|
-
const abs =
|
|
87127
|
+
const abs = join61(root, ...w.path.split("/"));
|
|
86288
87128
|
mkdirSync11(dirname33(abs), { recursive: true });
|
|
86289
87129
|
atomicWriteFileSync(abs, w.content);
|
|
86290
87130
|
}
|
|
86291
87131
|
for (const d of plan.deletes) {
|
|
86292
|
-
rmSync10(
|
|
87132
|
+
rmSync10(join61(root, ...d.split("/")), { force: true });
|
|
86293
87133
|
pruneEmptyDirs(root, d);
|
|
86294
87134
|
}
|
|
86295
87135
|
}
|
|
@@ -108252,7 +109092,7 @@ function invalidArguments(message) {
|
|
|
108252
109092
|
return new BuildError("Invalid arguments", message);
|
|
108253
109093
|
}
|
|
108254
109094
|
function checkedName(parameter) {
|
|
108255
|
-
if (!
|
|
109095
|
+
if (!TOKEN3.test(parameter.name)) {
|
|
108256
109096
|
throw new BuildError(
|
|
108257
109097
|
"Invalid request",
|
|
108258
109098
|
`The ${parameter.in} parameter name ${parameter.name} is not an HTTP token.`
|
|
@@ -108334,7 +109174,7 @@ function cookiePairs(parameter, value) {
|
|
|
108334
109174
|
]
|
|
108335
109175
|
] : shape.kind === "list" ? shape.items.map((item) => [name, item]) : shape.entries;
|
|
108336
109176
|
for (const [cookie, text5] of pairs) {
|
|
108337
|
-
if (!
|
|
109177
|
+
if (!TOKEN3.test(cookie)) {
|
|
108338
109178
|
throw invalidArguments(
|
|
108339
109179
|
`The cookie ${parameter.name} holds a key that is not an HTTP token, so no cookie can take it as a name.`
|
|
108340
109180
|
);
|
|
@@ -108377,12 +109217,12 @@ function queryEntries(parameter, value) {
|
|
|
108377
109217
|
}
|
|
108378
109218
|
if (shape.items.length === 0) return [];
|
|
108379
109219
|
if (explode) return shape.items.map((item) => pair(name, item));
|
|
108380
|
-
const [raw,
|
|
109220
|
+
const [raw, delimiter3] = style === "form" ? [",", ","] : style === "spaceDelimited" ? [" ", "%20"] : ["|", "|"];
|
|
108381
109221
|
return [
|
|
108382
109222
|
entry(
|
|
108383
109223
|
name,
|
|
108384
109224
|
shape.items.join(raw),
|
|
108385
|
-
`${enc(name)}=${shape.items.map(val).join(
|
|
109225
|
+
`${enc(name)}=${shape.items.map(val).join(delimiter3)}`
|
|
108386
109226
|
)
|
|
108387
109227
|
];
|
|
108388
109228
|
}
|
|
@@ -108599,7 +109439,7 @@ function createHttpSender(options = {}) {
|
|
|
108599
109439
|
}
|
|
108600
109440
|
};
|
|
108601
109441
|
}
|
|
108602
|
-
var IDEMPOTENT_METHODS, RESERVED_HEADERS,
|
|
109442
|
+
var IDEMPOTENT_METHODS, RESERVED_HEADERS, TOKEN3, UNSAFE_HEADER_VALUE, UNSAFE_COOKIE_VALUE;
|
|
108603
109443
|
var init_http = __esm({
|
|
108604
109444
|
"../../packages/mcp-studio/src/execute/http.ts"() {
|
|
108605
109445
|
"use strict";
|
|
@@ -108627,7 +109467,7 @@ var init_http = __esm({
|
|
|
108627
109467
|
"proxy-authorization",
|
|
108628
109468
|
"cookie"
|
|
108629
109469
|
]);
|
|
108630
|
-
|
|
109470
|
+
TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
108631
109471
|
UNSAFE_HEADER_VALUE = /[\r\n\0]/;
|
|
108632
109472
|
UNSAFE_COOKIE_VALUE = /[;\r\n\0]/;
|
|
108633
109473
|
}
|
|
@@ -130164,7 +131004,7 @@ import {
|
|
|
130164
131004
|
readlinkSync as readlinkSync4,
|
|
130165
131005
|
statSync as statSync16
|
|
130166
131006
|
} from "node:fs";
|
|
130167
|
-
import { dirname as dirname34, isAbsolute as isAbsolute15, join as
|
|
131007
|
+
import { dirname as dirname34, isAbsolute as isAbsolute15, join as join62, relative as relative9, resolve as resolve24, sep as sep12 } from "node:path";
|
|
130168
131008
|
function git5(cwd, args, input) {
|
|
130169
131009
|
try {
|
|
130170
131010
|
return execFileSync2("git", args, {
|
|
@@ -130196,7 +131036,7 @@ function commitOf2(root, ref2) {
|
|
|
130196
131036
|
return sha2.length > 0 ? sha2 : null;
|
|
130197
131037
|
}
|
|
130198
131038
|
function readWorkingFile(root, path2) {
|
|
130199
|
-
const abs =
|
|
131039
|
+
const abs = join62(root, ...path2.split("/"));
|
|
130200
131040
|
try {
|
|
130201
131041
|
const stat7 = lstatSync12(abs);
|
|
130202
131042
|
if (stat7.isSymbolicLink()) return readlinkSync4(abs, "utf8");
|
|
@@ -130290,7 +131130,7 @@ function isDir(path2) {
|
|
|
130290
131130
|
function findSteeringRoot(start) {
|
|
130291
131131
|
let dir = resolve24(start);
|
|
130292
131132
|
for (; ; ) {
|
|
130293
|
-
if (isFile(
|
|
131133
|
+
if (isFile(join62(dir, WORKSPACE_TOML_PATH)) || isFile(join62(dir, AGENTS_MD_PATH)) && isDir(join62(dir, STEERING_DIR))) {
|
|
130294
131134
|
return dir;
|
|
130295
131135
|
}
|
|
130296
131136
|
const parent = dirname34(dir);
|
|
@@ -130370,7 +131210,7 @@ function cacheFile(cacheDir, root) {
|
|
|
130370
131210
|
key: token === void 0 ? null : createHash20("sha256").update(token).digest("hex").slice(0, 16)
|
|
130371
131211
|
};
|
|
130372
131212
|
const key = createHash20("sha256").update(JSON.stringify([root, identity])).digest("hex").slice(0, 32);
|
|
130373
|
-
return
|
|
131213
|
+
return join62(cacheDir, `${key}.json`);
|
|
130374
131214
|
}
|
|
130375
131215
|
function readCache(file, now) {
|
|
130376
131216
|
let entry2;
|
|
@@ -130484,7 +131324,7 @@ function defaultDeps2() {
|
|
|
130484
131324
|
return {
|
|
130485
131325
|
fetchPublished: fetchSteeringIndex,
|
|
130486
131326
|
cedar: cedarHooks,
|
|
130487
|
-
cacheDir:
|
|
131327
|
+
cacheDir: join62(getConfigDir(), "cache", "steering-check"),
|
|
130488
131328
|
now: () => Date.now()
|
|
130489
131329
|
};
|
|
130490
131330
|
}
|
|
@@ -134367,7 +135207,7 @@ var init_src9 = __esm({
|
|
|
134367
135207
|
|
|
134368
135208
|
// src/commands/codegraph-upload/files.ts
|
|
134369
135209
|
import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
|
|
134370
|
-
import { join as
|
|
135210
|
+
import { join as join63, relative as relative10, sep as sep13 } from "node:path";
|
|
134371
135211
|
function hasGlobChars(pattern) {
|
|
134372
135212
|
return /[*?[\]{}]/.test(pattern);
|
|
134373
135213
|
}
|
|
@@ -134379,7 +135219,7 @@ async function walk(root, dir, out) {
|
|
|
134379
135219
|
const entries2 = await readdir5(dir, { withFileTypes: true });
|
|
134380
135220
|
for (const entry2 of entries2) {
|
|
134381
135221
|
if (entry2.isSymbolicLink()) continue;
|
|
134382
|
-
const full =
|
|
135222
|
+
const full = join63(dir, entry2.name);
|
|
134383
135223
|
if (entry2.isDirectory()) {
|
|
134384
135224
|
if (!SKIP_DIRS.has(entry2.name)) await walk(root, full, out);
|
|
134385
135225
|
} else if (entry2.isFile()) {
|
|
@@ -134391,7 +135231,7 @@ async function expandPattern(root, pattern) {
|
|
|
134391
135231
|
const wanted = normalize2(pattern.trim());
|
|
134392
135232
|
if (!hasGlobChars(wanted)) {
|
|
134393
135233
|
try {
|
|
134394
|
-
const info = await stat5(
|
|
135234
|
+
const info = await stat5(join63(root, wanted));
|
|
134395
135235
|
return info.isFile() ? [wanted] : [];
|
|
134396
135236
|
} catch {
|
|
134397
135237
|
return [];
|
|
@@ -134542,7 +135382,7 @@ var init_token = __esm({
|
|
|
134542
135382
|
});
|
|
134543
135383
|
|
|
134544
135384
|
// src/commands/codegraph-upload/run.ts
|
|
134545
|
-
import { join as
|
|
135385
|
+
import { join as join64 } from "node:path";
|
|
134546
135386
|
function addDropped(into, more) {
|
|
134547
135387
|
into.sensitiveValues += more.sensitiveValues;
|
|
134548
135388
|
into.secretData += more.secretData;
|
|
@@ -134560,7 +135400,7 @@ async function uploadOne(deps, config, token, group, path2, totals2) {
|
|
|
134560
135400
|
const label2 = `${group.kind}/${group.source} ${path2}`;
|
|
134561
135401
|
let bytes;
|
|
134562
135402
|
try {
|
|
134563
|
-
bytes = await deps.readFile(
|
|
135403
|
+
bytes = await deps.readFile(join64(deps.cwd, path2));
|
|
134564
135404
|
} catch (error) {
|
|
134565
135405
|
totals2.failed += 1;
|
|
134566
135406
|
deps.log(`error: ${label2}: cannot read the file: ${error.message}`);
|