@cabane/companion 0.6.19 → 0.6.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +100 -626
- package/dist/runtime.js +82 -608
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -316,14 +316,14 @@ function localAgentConfig(cfg, agent) {
|
|
|
316
316
|
return map[agent.agentId] ?? map[agent.agentUsername] ?? map[`${agent.workspaceSlug}/${agent.agentUsername}`] ?? {};
|
|
317
317
|
}
|
|
318
318
|
function loadConfig() {
|
|
319
|
-
const
|
|
320
|
-
if (!existsSync(
|
|
319
|
+
const path = configPath();
|
|
320
|
+
if (!existsSync(path)) return null;
|
|
321
321
|
let raw;
|
|
322
322
|
try {
|
|
323
|
-
raw = readFileSync(
|
|
323
|
+
raw = readFileSync(path, "utf8");
|
|
324
324
|
} catch (err) {
|
|
325
325
|
throw new ConfigError(
|
|
326
|
-
`couldn't read ${
|
|
326
|
+
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
327
327
|
);
|
|
328
328
|
}
|
|
329
329
|
if (raw.trim().length === 0) return null;
|
|
@@ -332,7 +332,7 @@ function loadConfig() {
|
|
|
332
332
|
parsed = JSON.parse(raw);
|
|
333
333
|
} catch (err) {
|
|
334
334
|
throw new ConfigError(
|
|
335
|
-
`${
|
|
335
|
+
`${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
336
336
|
);
|
|
337
337
|
}
|
|
338
338
|
const result = companionConfigSchema.safeParse(parsed);
|
|
@@ -340,22 +340,22 @@ function loadConfig() {
|
|
|
340
340
|
const agentIssue = result.error.issues.find((i) => i.path[0] === "agents");
|
|
341
341
|
if (agentIssue) {
|
|
342
342
|
throw new ConfigError(
|
|
343
|
-
`${
|
|
343
|
+
`${path}: invalid "agents" config at \`${agentIssue.path.join(".")}\` \u2014 ${agentIssue.message}. Fix the agents block in the config and retry.`
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
throw new ConfigError(
|
|
347
|
-
`${
|
|
347
|
+
`${path} is from an incompatible or older version of the companion, or was hand-edited. Run \`cabane-companion pair\` to re-pair, or \`cabane-companion logout --purge\` to reset.`
|
|
348
348
|
);
|
|
349
349
|
}
|
|
350
350
|
return result.data;
|
|
351
351
|
}
|
|
352
352
|
function loadConfigTolerant() {
|
|
353
|
-
const
|
|
353
|
+
const path = configPath();
|
|
354
354
|
const empty = {};
|
|
355
|
-
if (!existsSync(
|
|
355
|
+
if (!existsSync(path)) return { local: empty, note: null };
|
|
356
356
|
let raw;
|
|
357
357
|
try {
|
|
358
|
-
raw = readFileSync(
|
|
358
|
+
raw = readFileSync(path, "utf8");
|
|
359
359
|
} catch {
|
|
360
360
|
return { local: empty, note: null };
|
|
361
361
|
}
|
|
@@ -364,7 +364,7 @@ function loadConfigTolerant() {
|
|
|
364
364
|
try {
|
|
365
365
|
parsed = JSON.parse(raw);
|
|
366
366
|
} catch {
|
|
367
|
-
return { local: empty, note: `${
|
|
367
|
+
return { local: empty, note: `${path} was unreadable (invalid JSON) and has been reset.` };
|
|
368
368
|
}
|
|
369
369
|
const strict = companionConfigSchema.safeParse(parsed);
|
|
370
370
|
if (strict.success) {
|
|
@@ -390,24 +390,24 @@ function loadConfigTolerant() {
|
|
|
390
390
|
}
|
|
391
391
|
return {
|
|
392
392
|
local,
|
|
393
|
-
note: `the existing ${
|
|
393
|
+
note: `the existing ${path} was from an older or incompatible companion; re-pairing rewrote it.`
|
|
394
394
|
};
|
|
395
395
|
}
|
|
396
396
|
function saveConfig(cfg) {
|
|
397
|
-
const
|
|
398
|
-
mkdirSync(dirname(
|
|
397
|
+
const path = configPath();
|
|
398
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
399
399
|
try {
|
|
400
400
|
chmodSync(cabaneDir(), 448);
|
|
401
401
|
} catch {
|
|
402
402
|
}
|
|
403
|
-
const tmp = `${
|
|
403
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
404
404
|
try {
|
|
405
405
|
writeFileSync(tmp, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
|
|
406
406
|
try {
|
|
407
407
|
chmodSync(tmp, 384);
|
|
408
408
|
} catch {
|
|
409
409
|
}
|
|
410
|
-
renameSync(tmp,
|
|
410
|
+
renameSync(tmp, path);
|
|
411
411
|
} catch (err) {
|
|
412
412
|
try {
|
|
413
413
|
rmSync(tmp, { force: true });
|
|
@@ -426,9 +426,9 @@ function requireConfig() {
|
|
|
426
426
|
return cfg;
|
|
427
427
|
}
|
|
428
428
|
function deleteConfig() {
|
|
429
|
-
const
|
|
430
|
-
if (existsSync(
|
|
431
|
-
writeFileSync(
|
|
429
|
+
const path = configPath();
|
|
430
|
+
if (existsSync(path)) {
|
|
431
|
+
writeFileSync(path, "", { mode: 384 });
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
|
|
@@ -462,8 +462,8 @@ function consoleMessageFormat(log, messageKey) {
|
|
|
462
462
|
var cached = null;
|
|
463
463
|
function getLogger() {
|
|
464
464
|
if (cached) return cached;
|
|
465
|
-
const
|
|
466
|
-
mkdirSync2(dirname2(
|
|
465
|
+
const path = companionLogPath();
|
|
466
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
467
467
|
const streams = [];
|
|
468
468
|
if (process.env.CABANE_COMPANION_DAEMON !== "1") {
|
|
469
469
|
const consoleStream = pretty({
|
|
@@ -473,7 +473,7 @@ function getLogger() {
|
|
|
473
473
|
});
|
|
474
474
|
streams.push({ level: "info", stream: consoleStream });
|
|
475
475
|
}
|
|
476
|
-
streams.push({ level: "debug", stream: createWriteStream(
|
|
476
|
+
streams.push({ level: "debug", stream: createWriteStream(path, { flags: "a" }) });
|
|
477
477
|
cached = pino({ level: "debug" }, pino.multistream(streams));
|
|
478
478
|
return cached;
|
|
479
479
|
}
|
|
@@ -634,9 +634,9 @@ function serialize(state) {
|
|
|
634
634
|
return JSON.stringify(state, null, 2) + "\n";
|
|
635
635
|
}
|
|
636
636
|
function writeRuntimeState(state) {
|
|
637
|
-
const
|
|
637
|
+
const path = runtimePath();
|
|
638
638
|
mkdirSync3(cabaneDir(), { recursive: true });
|
|
639
|
-
writeFileSync2(
|
|
639
|
+
writeFileSync2(path, serialize(state), "utf8");
|
|
640
640
|
}
|
|
641
641
|
function acquireRuntimeState(state) {
|
|
642
642
|
const live = readLiveRuntimeState();
|
|
@@ -656,15 +656,15 @@ function acquireRuntimeState(state) {
|
|
|
656
656
|
return { acquired: true };
|
|
657
657
|
}
|
|
658
658
|
function clearRuntimeState() {
|
|
659
|
-
const
|
|
660
|
-
if (existsSync2(
|
|
659
|
+
const path = runtimePath();
|
|
660
|
+
if (existsSync2(path)) rmSync2(path, { force: true });
|
|
661
661
|
}
|
|
662
662
|
function readLiveRuntimeState() {
|
|
663
|
-
const
|
|
664
|
-
if (!existsSync2(
|
|
663
|
+
const path = runtimePath();
|
|
664
|
+
if (!existsSync2(path)) return null;
|
|
665
665
|
let parsed;
|
|
666
666
|
try {
|
|
667
|
-
parsed = JSON.parse(readFileSync2(
|
|
667
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
668
668
|
} catch {
|
|
669
669
|
return null;
|
|
670
670
|
}
|
|
@@ -795,11 +795,11 @@ function credentialsPath() {
|
|
|
795
795
|
}
|
|
796
796
|
var credentialStoreSchema = z3.record(z3.string(), z3.string());
|
|
797
797
|
function load() {
|
|
798
|
-
const
|
|
799
|
-
if (!existsSync3(
|
|
798
|
+
const path = credentialsPath();
|
|
799
|
+
if (!existsSync3(path)) return {};
|
|
800
800
|
let raw;
|
|
801
801
|
try {
|
|
802
|
-
raw = readFileSync3(
|
|
802
|
+
raw = readFileSync3(path, "utf8");
|
|
803
803
|
} catch {
|
|
804
804
|
return {};
|
|
805
805
|
}
|
|
@@ -812,20 +812,20 @@ function load() {
|
|
|
812
812
|
}
|
|
813
813
|
}
|
|
814
814
|
function save(map) {
|
|
815
|
-
const
|
|
816
|
-
mkdirSync5(dirname3(
|
|
815
|
+
const path = credentialsPath();
|
|
816
|
+
mkdirSync5(dirname3(path), { recursive: true });
|
|
817
817
|
try {
|
|
818
818
|
chmodSync2(cabaneDir(), 448);
|
|
819
819
|
} catch {
|
|
820
820
|
}
|
|
821
|
-
const tmp = `${
|
|
821
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
822
822
|
try {
|
|
823
823
|
writeFileSync3(tmp, JSON.stringify(map, null, 2) + "\n", { mode: 384 });
|
|
824
824
|
try {
|
|
825
825
|
chmodSync2(tmp, 384);
|
|
826
826
|
} catch {
|
|
827
827
|
}
|
|
828
|
-
renameSync2(tmp,
|
|
828
|
+
renameSync2(tmp, path);
|
|
829
829
|
} catch (err) {
|
|
830
830
|
try {
|
|
831
831
|
rmSync3(tmp, { force: true });
|
|
@@ -857,8 +857,8 @@ function pruneCredentials(keepAgentIds) {
|
|
|
857
857
|
return removed;
|
|
858
858
|
}
|
|
859
859
|
function clearCredentials() {
|
|
860
|
-
const
|
|
861
|
-
if (existsSync3(
|
|
860
|
+
const path = credentialsPath();
|
|
861
|
+
if (existsSync3(path)) writeFileSync3(path, "", { mode: 384 });
|
|
862
862
|
}
|
|
863
863
|
|
|
864
864
|
// src/commands/logout.ts
|
|
@@ -897,10 +897,10 @@ async function logout(opts = {}) {
|
|
|
897
897
|
function trimBase(baseUrl) {
|
|
898
898
|
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
899
899
|
}
|
|
900
|
-
async function postJson(baseUrl,
|
|
900
|
+
async function postJson(baseUrl, path, body) {
|
|
901
901
|
let res;
|
|
902
902
|
try {
|
|
903
|
-
res = await fetch(`${trimBase(baseUrl)}${
|
|
903
|
+
res = await fetch(`${trimBase(baseUrl)}${path}`, {
|
|
904
904
|
method: "POST",
|
|
905
905
|
headers: { "content-type": "application/json", accept: "application/json" },
|
|
906
906
|
body: JSON.stringify(body)
|
|
@@ -920,7 +920,7 @@ async function postJson(baseUrl, path3, body) {
|
|
|
920
920
|
}
|
|
921
921
|
}
|
|
922
922
|
if (res.status >= 400) {
|
|
923
|
-
if (res.status === 404 &&
|
|
923
|
+
if (res.status === 404 && path.endsWith("/code")) {
|
|
924
924
|
throw new CompanionError(
|
|
925
925
|
`no device-flow pairing endpoint at ${baseUrl}. Either that server predates \`cabane-companion pair\`, or it isn't a Cabane API origin \u2014 the hosted app is https://app.cabane.ai (pass \`--server <url>\` for your own instance).`
|
|
926
926
|
);
|
|
@@ -1458,12 +1458,12 @@ function clampLimit(raw, fallback, max = 200) {
|
|
|
1458
1458
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
1459
1459
|
return Math.min(Math.floor(n), max);
|
|
1460
1460
|
}
|
|
1461
|
-
function tailFile(
|
|
1462
|
-
if (!existsSync4(
|
|
1461
|
+
function tailFile(path, lines) {
|
|
1462
|
+
if (!existsSync4(path)) return [];
|
|
1463
1463
|
const MAX_BYTES = 256 * 1024;
|
|
1464
1464
|
let fd;
|
|
1465
1465
|
try {
|
|
1466
|
-
fd = openSync3(
|
|
1466
|
+
fd = openSync3(path, "r");
|
|
1467
1467
|
const size = fstatSync(fd).size;
|
|
1468
1468
|
const start2 = Math.max(0, size - MAX_BYTES);
|
|
1469
1469
|
const len = size - start2;
|
|
@@ -1574,8 +1574,8 @@ var CabaneApi = class {
|
|
|
1574
1574
|
// One HTTP attempt — no retry. Throws `ApiError` on a 4xx/5xx response and
|
|
1575
1575
|
// rethrows transport errors (fetch reject) unchanged so the caller's retry
|
|
1576
1576
|
// logic can classify them.
|
|
1577
|
-
async attempt(method,
|
|
1578
|
-
const res = await fetch(`${this.base}${
|
|
1577
|
+
async attempt(method, path, body, signal) {
|
|
1578
|
+
const res = await fetch(`${this.base}${path}`, {
|
|
1579
1579
|
method,
|
|
1580
1580
|
headers: {
|
|
1581
1581
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -1601,12 +1601,12 @@ var CabaneApi = class {
|
|
|
1601
1601
|
}
|
|
1602
1602
|
return parsed;
|
|
1603
1603
|
}
|
|
1604
|
-
async request(method,
|
|
1604
|
+
async request(method, path, body, opts = {}) {
|
|
1605
1605
|
const { signal, retry = false } = opts;
|
|
1606
1606
|
const maxAttempts = retry ? RETRY_BACKOFF_MS.length + 1 : 1;
|
|
1607
1607
|
for (let attempt = 1; ; attempt++) {
|
|
1608
1608
|
try {
|
|
1609
|
-
return await this.attempt(method,
|
|
1609
|
+
return await this.attempt(method, path, body, signal);
|
|
1610
1610
|
} catch (err) {
|
|
1611
1611
|
if (attempt >= maxAttempts || signal?.aborted || !isRetryable(err)) throw err;
|
|
1612
1612
|
await sleep2(RETRY_BACKOFF_MS[attempt - 1], signal);
|
|
@@ -1632,9 +1632,9 @@ var CabaneApi = class {
|
|
|
1632
1632
|
// delivers it once the API returns. `(turnId, seq)` is the server's
|
|
1633
1633
|
// idempotency key, so a replay whose original POST's fate is unknown
|
|
1634
1634
|
// converges instead of duplicating.
|
|
1635
|
-
async durableCommit(kind,
|
|
1635
|
+
async durableCommit(kind, path, body, turnId, seq, signal) {
|
|
1636
1636
|
try {
|
|
1637
|
-
await this.request("POST",
|
|
1637
|
+
await this.request("POST", path, body, {
|
|
1638
1638
|
retry: true,
|
|
1639
1639
|
...signal ? { signal } : {}
|
|
1640
1640
|
});
|
|
@@ -1643,7 +1643,7 @@ var CabaneApi = class {
|
|
|
1643
1643
|
if (!outbox) throw err;
|
|
1644
1644
|
if (signal?.aborted || isAbortError(err)) throw err;
|
|
1645
1645
|
if (!isRetryable(err)) throw err;
|
|
1646
|
-
outbox.persist({ enqueuedAt: Date.now(), turnId, seq, method: "POST", path
|
|
1646
|
+
outbox.persist({ enqueuedAt: Date.now(), turnId, seq, method: "POST", path, body, kind });
|
|
1647
1647
|
this.opts.log?.warn(
|
|
1648
1648
|
{ kind, turnId, seq, err: err instanceof Error ? err.message : String(err) },
|
|
1649
1649
|
"companion: commit queued to outbox after transient failure (will drain when the API returns)"
|
|
@@ -1795,12 +1795,12 @@ var CabaneApi = class {
|
|
|
1795
1795
|
// left best-effort: it's lower-stakes and self-heals on the next turn, so it
|
|
1796
1796
|
// stays a single-shot PATCH and is deliberately out of CT93's scope.
|
|
1797
1797
|
setActiveRun(workspaceId, conversationId, agentId, body) {
|
|
1798
|
-
const
|
|
1798
|
+
const path = `/api/workspaces/${workspaceId}/conversations/${conversationId}/participants/agents/${agentId}/active-run`;
|
|
1799
1799
|
const touchesFlag = Object.prototype.hasOwnProperty.call(body, "activeRunStartedAt");
|
|
1800
1800
|
if (touchesFlag && this.opts.outbox) {
|
|
1801
|
-
return this.durableActiveRunWrite(
|
|
1801
|
+
return this.durableActiveRunWrite(path, conversationId, agentId, body);
|
|
1802
1802
|
}
|
|
1803
|
-
return this.request("PATCH",
|
|
1803
|
+
return this.request("PATCH", path, body);
|
|
1804
1804
|
}
|
|
1805
1805
|
// CT93: send-or-enqueue for an active-run flag write, with last-writer-wins
|
|
1806
1806
|
// across the (conversation, agent) pair. Mirrors `durableCommit`, with two
|
|
@@ -1814,11 +1814,11 @@ var CabaneApi = class {
|
|
|
1814
1814
|
// later and clobber the state we just wrote (the cross-turn race: turn N's
|
|
1815
1815
|
// queued clear vs. turn N+1's live set). Combined with persist-overwrites-
|
|
1816
1816
|
// by-key, this is the full last-writer-wins guarantee.
|
|
1817
|
-
async durableActiveRunWrite(
|
|
1817
|
+
async durableActiveRunWrite(path, conversationId, agentId, body) {
|
|
1818
1818
|
const outbox = this.opts.outbox;
|
|
1819
1819
|
const key = activeRunOutboxKey(conversationId, agentId);
|
|
1820
1820
|
try {
|
|
1821
|
-
await this.request("PATCH",
|
|
1821
|
+
await this.request("PATCH", path, body, { retry: true });
|
|
1822
1822
|
outbox?.remove(key, ACTIVE_RUN_OUTBOX_SEQ);
|
|
1823
1823
|
} catch (err) {
|
|
1824
1824
|
if (!outbox) throw err;
|
|
@@ -1831,7 +1831,7 @@ var CabaneApi = class {
|
|
|
1831
1831
|
turnId: key,
|
|
1832
1832
|
seq: ACTIVE_RUN_OUTBOX_SEQ,
|
|
1833
1833
|
method: "PATCH",
|
|
1834
|
-
path
|
|
1834
|
+
path,
|
|
1835
1835
|
body,
|
|
1836
1836
|
kind: "active-run"
|
|
1837
1837
|
});
|
|
@@ -1919,8 +1919,8 @@ var CabaneApi = class {
|
|
|
1919
1919
|
// shared resolver the in-app path uses. Omitting it (older call sites) returns
|
|
1920
1920
|
// the agent default — graceful degradation, no version coupling.
|
|
1921
1921
|
getAgentSelf(conversationId) {
|
|
1922
|
-
const
|
|
1923
|
-
return this.request("GET",
|
|
1922
|
+
const path = conversationId ? `/api/agent/me?conversationId=${encodeURIComponent(conversationId)}` : `/api/agent/me`;
|
|
1923
|
+
return this.request("GET", path);
|
|
1924
1924
|
}
|
|
1925
1925
|
// The companion fetches the triggering message body by listing the
|
|
1926
1926
|
// conversation's messages and finding the one with `id === messageId`.
|
|
@@ -1975,8 +1975,8 @@ var DeviceApi = class {
|
|
|
1975
1975
|
get base() {
|
|
1976
1976
|
return this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
1977
1977
|
}
|
|
1978
|
-
async request(method,
|
|
1979
|
-
const res = await fetch(`${this.base}${
|
|
1978
|
+
async request(method, path, body) {
|
|
1979
|
+
const res = await fetch(`${this.base}${path}`, {
|
|
1980
1980
|
method,
|
|
1981
1981
|
headers: {
|
|
1982
1982
|
Authorization: `Bearer ${this.opts.deviceToken}`,
|
|
@@ -2030,15 +2030,15 @@ function pathFor(workspaceId) {
|
|
|
2030
2030
|
return join7(cabaneDir(), "cursors", encodeURIComponent(workspaceId));
|
|
2031
2031
|
}
|
|
2032
2032
|
function readCursor(workspaceId) {
|
|
2033
|
-
const
|
|
2034
|
-
if (!existsSync5(
|
|
2035
|
-
const raw = readFileSync4(
|
|
2033
|
+
const path = pathFor(workspaceId);
|
|
2034
|
+
if (!existsSync5(path)) return null;
|
|
2035
|
+
const raw = readFileSync4(path, "utf8").trim();
|
|
2036
2036
|
return raw.length > 0 ? raw : null;
|
|
2037
2037
|
}
|
|
2038
2038
|
function writeCursor(workspaceId, eventId) {
|
|
2039
|
-
const
|
|
2039
|
+
const path = pathFor(workspaceId);
|
|
2040
2040
|
mkdirSync6(join7(cabaneDir(), "cursors"), { recursive: true });
|
|
2041
|
-
writeFileSync4(
|
|
2041
|
+
writeFileSync4(path, eventId + "\n", "utf8");
|
|
2042
2042
|
}
|
|
2043
2043
|
|
|
2044
2044
|
// src/cursor-tracker.ts
|
|
@@ -2091,10 +2091,10 @@ function pathFor2(log, workspaceId) {
|
|
|
2091
2091
|
return join8(dir(log), encodeURIComponent(workspaceId));
|
|
2092
2092
|
}
|
|
2093
2093
|
function readIds(log, workspaceId) {
|
|
2094
|
-
const
|
|
2095
|
-
if (!existsSync6(
|
|
2094
|
+
const path = pathFor2(log, workspaceId);
|
|
2095
|
+
if (!existsSync6(path)) return [];
|
|
2096
2096
|
try {
|
|
2097
|
-
return readFileSync5(
|
|
2097
|
+
return readFileSync5(path, "utf8").split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2098
2098
|
} catch {
|
|
2099
2099
|
return [];
|
|
2100
2100
|
}
|
|
@@ -2131,10 +2131,10 @@ function resumePathFor(workspaceId) {
|
|
|
2131
2131
|
}
|
|
2132
2132
|
function readResumeCounts(workspaceId) {
|
|
2133
2133
|
const out = /* @__PURE__ */ new Map();
|
|
2134
|
-
const
|
|
2135
|
-
if (!existsSync6(
|
|
2134
|
+
const path = resumePathFor(workspaceId);
|
|
2135
|
+
if (!existsSync6(path)) return out;
|
|
2136
2136
|
try {
|
|
2137
|
-
for (const line of readFileSync5(
|
|
2137
|
+
for (const line of readFileSync5(path, "utf8").split("\n")) {
|
|
2138
2138
|
const trimmed = line.trim();
|
|
2139
2139
|
if (!trimmed) continue;
|
|
2140
2140
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -5072,536 +5072,10 @@ function isStringRecord2(v) {
|
|
|
5072
5072
|
return !!v && typeof v === "object" && Object.values(v).every((x) => typeof x === "string");
|
|
5073
5073
|
}
|
|
5074
5074
|
|
|
5075
|
-
// node_modules/.pnpm/@openai+codex-sdk@0.147.0/node_modules/@openai/codex-sdk/dist/index.js
|
|
5076
|
-
import { promises as fs } from "fs";
|
|
5077
|
-
import os from "os";
|
|
5078
|
-
import path from "path";
|
|
5079
|
-
import { spawn as spawn6 } from "child_process";
|
|
5080
|
-
import { statSync } from "fs";
|
|
5081
|
-
import path2 from "path";
|
|
5082
|
-
import readline from "readline";
|
|
5083
|
-
import { createRequire } from "module";
|
|
5084
|
-
async function createOutputSchemaFile(schema) {
|
|
5085
|
-
if (schema === void 0) {
|
|
5086
|
-
return { cleanup: async () => {
|
|
5087
|
-
} };
|
|
5088
|
-
}
|
|
5089
|
-
if (!isJsonObject(schema)) {
|
|
5090
|
-
throw new Error("outputSchema must be a plain JSON object");
|
|
5091
|
-
}
|
|
5092
|
-
const schemaDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-output-schema-"));
|
|
5093
|
-
const schemaPath = path.join(schemaDir, "schema.json");
|
|
5094
|
-
const cleanup = async () => {
|
|
5095
|
-
try {
|
|
5096
|
-
await fs.rm(schemaDir, { recursive: true, force: true });
|
|
5097
|
-
} catch {
|
|
5098
|
-
}
|
|
5099
|
-
};
|
|
5100
|
-
try {
|
|
5101
|
-
await fs.writeFile(schemaPath, JSON.stringify(schema), "utf8");
|
|
5102
|
-
return { schemaPath, cleanup };
|
|
5103
|
-
} catch (error) {
|
|
5104
|
-
await cleanup();
|
|
5105
|
-
throw error;
|
|
5106
|
-
}
|
|
5107
|
-
}
|
|
5108
|
-
function isJsonObject(value) {
|
|
5109
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5110
|
-
}
|
|
5111
|
-
var Thread = class {
|
|
5112
|
-
_exec;
|
|
5113
|
-
_options;
|
|
5114
|
-
_id;
|
|
5115
|
-
_threadOptions;
|
|
5116
|
-
/** Returns the ID of the thread. Populated after the first turn starts. */
|
|
5117
|
-
get id() {
|
|
5118
|
-
return this._id;
|
|
5119
|
-
}
|
|
5120
|
-
/* @internal */
|
|
5121
|
-
constructor(exec, options, threadOptions, id = null) {
|
|
5122
|
-
this._exec = exec;
|
|
5123
|
-
this._options = options;
|
|
5124
|
-
this._id = id;
|
|
5125
|
-
this._threadOptions = threadOptions;
|
|
5126
|
-
}
|
|
5127
|
-
/** Provides the input to the agent and streams events as they are produced during the turn. */
|
|
5128
|
-
async runStreamed(input, turnOptions = {}) {
|
|
5129
|
-
return { events: this.runStreamedInternal(input, turnOptions) };
|
|
5130
|
-
}
|
|
5131
|
-
async *runStreamedInternal(input, turnOptions = {}) {
|
|
5132
|
-
const { schemaPath, cleanup } = await createOutputSchemaFile(turnOptions.outputSchema);
|
|
5133
|
-
const options = this._threadOptions;
|
|
5134
|
-
const { prompt, images } = normalizeInput(input);
|
|
5135
|
-
const generator = this._exec.run({
|
|
5136
|
-
input: prompt,
|
|
5137
|
-
baseUrl: this._options.baseUrl,
|
|
5138
|
-
apiKey: this._options.apiKey,
|
|
5139
|
-
threadId: this._id,
|
|
5140
|
-
images,
|
|
5141
|
-
model: options?.model,
|
|
5142
|
-
sandboxMode: options?.sandboxMode,
|
|
5143
|
-
workingDirectory: options?.workingDirectory,
|
|
5144
|
-
skipGitRepoCheck: options?.skipGitRepoCheck,
|
|
5145
|
-
outputSchemaFile: schemaPath,
|
|
5146
|
-
modelReasoningEffort: options?.modelReasoningEffort,
|
|
5147
|
-
signal: turnOptions.signal,
|
|
5148
|
-
networkAccessEnabled: options?.networkAccessEnabled,
|
|
5149
|
-
webSearchMode: options?.webSearchMode,
|
|
5150
|
-
webSearchEnabled: options?.webSearchEnabled,
|
|
5151
|
-
approvalPolicy: options?.approvalPolicy,
|
|
5152
|
-
additionalDirectories: options?.additionalDirectories
|
|
5153
|
-
});
|
|
5154
|
-
try {
|
|
5155
|
-
for await (const item of generator) {
|
|
5156
|
-
let parsed;
|
|
5157
|
-
try {
|
|
5158
|
-
parsed = JSON.parse(item);
|
|
5159
|
-
} catch (error) {
|
|
5160
|
-
throw new Error(`Failed to parse item: ${item}`, { cause: error });
|
|
5161
|
-
}
|
|
5162
|
-
if (parsed.type === "thread.started") {
|
|
5163
|
-
this._id = parsed.thread_id;
|
|
5164
|
-
} else if (parsed.type === "turn.completed") {
|
|
5165
|
-
parsed.usage.cache_write_input_tokens ??= 0;
|
|
5166
|
-
}
|
|
5167
|
-
yield parsed;
|
|
5168
|
-
}
|
|
5169
|
-
} finally {
|
|
5170
|
-
await cleanup();
|
|
5171
|
-
}
|
|
5172
|
-
}
|
|
5173
|
-
/** Provides the input to the agent and returns the completed turn. */
|
|
5174
|
-
async run(input, turnOptions = {}) {
|
|
5175
|
-
const generator = this.runStreamedInternal(input, turnOptions);
|
|
5176
|
-
const items = [];
|
|
5177
|
-
let finalResponse = "";
|
|
5178
|
-
let usage = null;
|
|
5179
|
-
let turnFailure = null;
|
|
5180
|
-
for await (const event of generator) {
|
|
5181
|
-
if (event.type === "item.completed") {
|
|
5182
|
-
if (event.item.type === "agent_message") {
|
|
5183
|
-
finalResponse = event.item.text;
|
|
5184
|
-
}
|
|
5185
|
-
items.push(event.item);
|
|
5186
|
-
} else if (event.type === "turn.completed") {
|
|
5187
|
-
usage = event.usage;
|
|
5188
|
-
} else if (event.type === "turn.failed") {
|
|
5189
|
-
turnFailure = event.error;
|
|
5190
|
-
break;
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
if (turnFailure) {
|
|
5194
|
-
throw new Error(turnFailure.message);
|
|
5195
|
-
}
|
|
5196
|
-
return { items, finalResponse, usage };
|
|
5197
|
-
}
|
|
5198
|
-
};
|
|
5199
|
-
function normalizeInput(input) {
|
|
5200
|
-
if (typeof input === "string") {
|
|
5201
|
-
return { prompt: input, images: [] };
|
|
5202
|
-
}
|
|
5203
|
-
const promptParts = [];
|
|
5204
|
-
const images = [];
|
|
5205
|
-
for (const item of input) {
|
|
5206
|
-
if (item.type === "text") {
|
|
5207
|
-
promptParts.push(item.text);
|
|
5208
|
-
} else if (item.type === "local_image") {
|
|
5209
|
-
images.push(item.path);
|
|
5210
|
-
}
|
|
5211
|
-
}
|
|
5212
|
-
return { prompt: promptParts.join("\n\n"), images };
|
|
5213
|
-
}
|
|
5214
|
-
var INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
|
5215
|
-
var TYPESCRIPT_SDK_ORIGINATOR = "codex_sdk_ts";
|
|
5216
|
-
var CODEX_NPM_NAME = "@openai/codex";
|
|
5217
|
-
var PLATFORM_PACKAGE_BY_TARGET = {
|
|
5218
|
-
"x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
|
|
5219
|
-
"aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
|
|
5220
|
-
"x86_64-apple-darwin": "@openai/codex-darwin-x64",
|
|
5221
|
-
"aarch64-apple-darwin": "@openai/codex-darwin-arm64",
|
|
5222
|
-
"x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
|
|
5223
|
-
"aarch64-pc-windows-msvc": "@openai/codex-win32-arm64"
|
|
5224
|
-
};
|
|
5225
|
-
var moduleRequire = createRequire(import.meta.url);
|
|
5226
|
-
var CodexExec = class {
|
|
5227
|
-
executablePath;
|
|
5228
|
-
pathDirs;
|
|
5229
|
-
envOverride;
|
|
5230
|
-
configOverrides;
|
|
5231
|
-
constructor(executablePath = null, env, configOverrides) {
|
|
5232
|
-
if (executablePath) {
|
|
5233
|
-
this.executablePath = executablePath;
|
|
5234
|
-
this.pathDirs = [];
|
|
5235
|
-
} else {
|
|
5236
|
-
const resolved = findCodexPath();
|
|
5237
|
-
this.executablePath = resolved.executablePath;
|
|
5238
|
-
this.pathDirs = resolved.pathDirs;
|
|
5239
|
-
}
|
|
5240
|
-
this.envOverride = env;
|
|
5241
|
-
this.configOverrides = configOverrides;
|
|
5242
|
-
}
|
|
5243
|
-
async *run(args) {
|
|
5244
|
-
const commandArgs = ["exec", "--experimental-json"];
|
|
5245
|
-
if (this.configOverrides) {
|
|
5246
|
-
for (const override of serializeConfigOverrides(this.configOverrides)) {
|
|
5247
|
-
commandArgs.push("--config", override);
|
|
5248
|
-
}
|
|
5249
|
-
}
|
|
5250
|
-
if (args.baseUrl) {
|
|
5251
|
-
commandArgs.push(
|
|
5252
|
-
"--config",
|
|
5253
|
-
`openai_base_url=${toTomlValue(args.baseUrl, "openai_base_url")}`
|
|
5254
|
-
);
|
|
5255
|
-
}
|
|
5256
|
-
if (args.model) {
|
|
5257
|
-
commandArgs.push("--model", args.model);
|
|
5258
|
-
}
|
|
5259
|
-
if (args.sandboxMode) {
|
|
5260
|
-
commandArgs.push("--sandbox", args.sandboxMode);
|
|
5261
|
-
}
|
|
5262
|
-
if (args.workingDirectory) {
|
|
5263
|
-
commandArgs.push("--cd", args.workingDirectory);
|
|
5264
|
-
}
|
|
5265
|
-
if (args.additionalDirectories?.length) {
|
|
5266
|
-
for (const dir2 of args.additionalDirectories) {
|
|
5267
|
-
commandArgs.push("--add-dir", dir2);
|
|
5268
|
-
}
|
|
5269
|
-
}
|
|
5270
|
-
if (args.skipGitRepoCheck) {
|
|
5271
|
-
commandArgs.push("--skip-git-repo-check");
|
|
5272
|
-
}
|
|
5273
|
-
if (args.outputSchemaFile) {
|
|
5274
|
-
commandArgs.push("--output-schema", args.outputSchemaFile);
|
|
5275
|
-
}
|
|
5276
|
-
if (args.modelReasoningEffort) {
|
|
5277
|
-
commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
|
|
5278
|
-
}
|
|
5279
|
-
if (args.networkAccessEnabled !== void 0) {
|
|
5280
|
-
commandArgs.push(
|
|
5281
|
-
"--config",
|
|
5282
|
-
`sandbox_workspace_write.network_access=${args.networkAccessEnabled}`
|
|
5283
|
-
);
|
|
5284
|
-
}
|
|
5285
|
-
if (args.webSearchMode) {
|
|
5286
|
-
commandArgs.push("--config", `web_search="${args.webSearchMode}"`);
|
|
5287
|
-
} else if (args.webSearchEnabled === true) {
|
|
5288
|
-
commandArgs.push("--config", `web_search="live"`);
|
|
5289
|
-
} else if (args.webSearchEnabled === false) {
|
|
5290
|
-
commandArgs.push("--config", `web_search="disabled"`);
|
|
5291
|
-
}
|
|
5292
|
-
if (args.approvalPolicy) {
|
|
5293
|
-
commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`);
|
|
5294
|
-
}
|
|
5295
|
-
if (args.threadId) {
|
|
5296
|
-
commandArgs.push("resume", args.threadId);
|
|
5297
|
-
}
|
|
5298
|
-
if (args.images?.length) {
|
|
5299
|
-
for (const image of args.images) {
|
|
5300
|
-
commandArgs.push("--image", image);
|
|
5301
|
-
}
|
|
5302
|
-
}
|
|
5303
|
-
const env = {};
|
|
5304
|
-
if (this.envOverride) {
|
|
5305
|
-
Object.assign(env, this.envOverride);
|
|
5306
|
-
} else {
|
|
5307
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
5308
|
-
if (value !== void 0) {
|
|
5309
|
-
env[key] = value;
|
|
5310
|
-
}
|
|
5311
|
-
}
|
|
5312
|
-
}
|
|
5313
|
-
if (!env[INTERNAL_ORIGINATOR_ENV]) {
|
|
5314
|
-
env[INTERNAL_ORIGINATOR_ENV] = TYPESCRIPT_SDK_ORIGINATOR;
|
|
5315
|
-
}
|
|
5316
|
-
if (args.apiKey) {
|
|
5317
|
-
env.CODEX_API_KEY = args.apiKey;
|
|
5318
|
-
}
|
|
5319
|
-
if (this.pathDirs.length > 0) {
|
|
5320
|
-
prependPathDirs(env, this.pathDirs);
|
|
5321
|
-
}
|
|
5322
|
-
const child = spawn6(this.executablePath, commandArgs, {
|
|
5323
|
-
env,
|
|
5324
|
-
signal: args.signal
|
|
5325
|
-
});
|
|
5326
|
-
let spawnError = null;
|
|
5327
|
-
child.once("error", (err) => spawnError = err);
|
|
5328
|
-
if (!child.stdin) {
|
|
5329
|
-
child.kill();
|
|
5330
|
-
throw new Error("Child process has no stdin");
|
|
5331
|
-
}
|
|
5332
|
-
child.stdin.write(args.input);
|
|
5333
|
-
child.stdin.end();
|
|
5334
|
-
if (!child.stdout) {
|
|
5335
|
-
child.kill();
|
|
5336
|
-
throw new Error("Child process has no stdout");
|
|
5337
|
-
}
|
|
5338
|
-
const stderrChunks = [];
|
|
5339
|
-
if (child.stderr) {
|
|
5340
|
-
child.stderr.on("data", (data) => {
|
|
5341
|
-
stderrChunks.push(data);
|
|
5342
|
-
});
|
|
5343
|
-
}
|
|
5344
|
-
const exitPromise = new Promise(
|
|
5345
|
-
(resolve) => {
|
|
5346
|
-
child.once("exit", (code, signal) => {
|
|
5347
|
-
resolve({ code, signal });
|
|
5348
|
-
});
|
|
5349
|
-
}
|
|
5350
|
-
);
|
|
5351
|
-
const rl = readline.createInterface({
|
|
5352
|
-
input: child.stdout,
|
|
5353
|
-
crlfDelay: Infinity
|
|
5354
|
-
});
|
|
5355
|
-
try {
|
|
5356
|
-
for await (const line of rl) {
|
|
5357
|
-
yield line;
|
|
5358
|
-
}
|
|
5359
|
-
if (spawnError) throw spawnError;
|
|
5360
|
-
const { code, signal } = await exitPromise;
|
|
5361
|
-
if (code !== 0 || signal) {
|
|
5362
|
-
const stderrBuffer = Buffer.concat(stderrChunks);
|
|
5363
|
-
const detail = signal ? `signal ${signal}` : `code ${code ?? 1}`;
|
|
5364
|
-
throw new Error(`Codex Exec exited with ${detail}: ${stderrBuffer.toString("utf8")}`);
|
|
5365
|
-
}
|
|
5366
|
-
} finally {
|
|
5367
|
-
rl.close();
|
|
5368
|
-
child.removeAllListeners();
|
|
5369
|
-
try {
|
|
5370
|
-
if (!child.killed) child.kill();
|
|
5371
|
-
} catch {
|
|
5372
|
-
}
|
|
5373
|
-
}
|
|
5374
|
-
}
|
|
5375
|
-
};
|
|
5376
|
-
function serializeConfigOverrides(configOverrides) {
|
|
5377
|
-
const overrides = [];
|
|
5378
|
-
flattenConfigOverrides(configOverrides, "", overrides);
|
|
5379
|
-
return overrides;
|
|
5380
|
-
}
|
|
5381
|
-
function flattenConfigOverrides(value, prefix, overrides) {
|
|
5382
|
-
if (!isPlainObject(value)) {
|
|
5383
|
-
if (prefix) {
|
|
5384
|
-
overrides.push(`${prefix}=${toTomlValue(value, prefix)}`);
|
|
5385
|
-
return;
|
|
5386
|
-
} else {
|
|
5387
|
-
throw new Error("Codex config overrides must be a plain object");
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
|
-
const entries = Object.entries(value);
|
|
5391
|
-
if (!prefix && entries.length === 0) {
|
|
5392
|
-
return;
|
|
5393
|
-
}
|
|
5394
|
-
if (prefix && entries.length === 0) {
|
|
5395
|
-
overrides.push(`${prefix}={}`);
|
|
5396
|
-
return;
|
|
5397
|
-
}
|
|
5398
|
-
for (const [key, child] of entries) {
|
|
5399
|
-
if (!key) {
|
|
5400
|
-
throw new Error("Codex config override keys must be non-empty strings");
|
|
5401
|
-
}
|
|
5402
|
-
if (child === void 0) {
|
|
5403
|
-
continue;
|
|
5404
|
-
}
|
|
5405
|
-
const path3 = prefix ? `${prefix}.${key}` : key;
|
|
5406
|
-
if (isPlainObject(child)) {
|
|
5407
|
-
flattenConfigOverrides(child, path3, overrides);
|
|
5408
|
-
} else {
|
|
5409
|
-
overrides.push(`${path3}=${toTomlValue(child, path3)}`);
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5412
|
-
}
|
|
5413
|
-
function toTomlValue(value, path3) {
|
|
5414
|
-
if (typeof value === "string") {
|
|
5415
|
-
return JSON.stringify(value);
|
|
5416
|
-
} else if (typeof value === "number") {
|
|
5417
|
-
if (!Number.isFinite(value)) {
|
|
5418
|
-
throw new Error(`Codex config override at ${path3} must be a finite number`);
|
|
5419
|
-
}
|
|
5420
|
-
return `${value}`;
|
|
5421
|
-
} else if (typeof value === "boolean") {
|
|
5422
|
-
return value ? "true" : "false";
|
|
5423
|
-
} else if (Array.isArray(value)) {
|
|
5424
|
-
const rendered = value.map((item, index) => toTomlValue(item, `${path3}[${index}]`));
|
|
5425
|
-
return `[${rendered.join(", ")}]`;
|
|
5426
|
-
} else if (isPlainObject(value)) {
|
|
5427
|
-
const parts = [];
|
|
5428
|
-
for (const [key, child] of Object.entries(value)) {
|
|
5429
|
-
if (!key) {
|
|
5430
|
-
throw new Error("Codex config override keys must be non-empty strings");
|
|
5431
|
-
}
|
|
5432
|
-
if (child === void 0) {
|
|
5433
|
-
continue;
|
|
5434
|
-
}
|
|
5435
|
-
parts.push(`${formatTomlKey(key)} = ${toTomlValue(child, `${path3}.${key}`)}`);
|
|
5436
|
-
}
|
|
5437
|
-
return `{${parts.join(", ")}}`;
|
|
5438
|
-
} else if (value === null) {
|
|
5439
|
-
throw new Error(`Codex config override at ${path3} cannot be null`);
|
|
5440
|
-
} else {
|
|
5441
|
-
const typeName = typeof value;
|
|
5442
|
-
throw new Error(`Unsupported Codex config override value at ${path3}: ${typeName}`);
|
|
5443
|
-
}
|
|
5444
|
-
}
|
|
5445
|
-
var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
|
|
5446
|
-
function formatTomlKey(key) {
|
|
5447
|
-
return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
|
|
5448
|
-
}
|
|
5449
|
-
function isPlainObject(value) {
|
|
5450
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5451
|
-
}
|
|
5452
|
-
function findCodexPath() {
|
|
5453
|
-
const { platform: platform2, arch } = process;
|
|
5454
|
-
let targetTriple = null;
|
|
5455
|
-
switch (platform2) {
|
|
5456
|
-
case "linux":
|
|
5457
|
-
case "android":
|
|
5458
|
-
switch (arch) {
|
|
5459
|
-
case "x64":
|
|
5460
|
-
targetTriple = "x86_64-unknown-linux-musl";
|
|
5461
|
-
break;
|
|
5462
|
-
case "arm64":
|
|
5463
|
-
targetTriple = "aarch64-unknown-linux-musl";
|
|
5464
|
-
break;
|
|
5465
|
-
default:
|
|
5466
|
-
break;
|
|
5467
|
-
}
|
|
5468
|
-
break;
|
|
5469
|
-
case "darwin":
|
|
5470
|
-
switch (arch) {
|
|
5471
|
-
case "x64":
|
|
5472
|
-
targetTriple = "x86_64-apple-darwin";
|
|
5473
|
-
break;
|
|
5474
|
-
case "arm64":
|
|
5475
|
-
targetTriple = "aarch64-apple-darwin";
|
|
5476
|
-
break;
|
|
5477
|
-
default:
|
|
5478
|
-
break;
|
|
5479
|
-
}
|
|
5480
|
-
break;
|
|
5481
|
-
case "win32":
|
|
5482
|
-
switch (arch) {
|
|
5483
|
-
case "x64":
|
|
5484
|
-
targetTriple = "x86_64-pc-windows-msvc";
|
|
5485
|
-
break;
|
|
5486
|
-
case "arm64":
|
|
5487
|
-
targetTriple = "aarch64-pc-windows-msvc";
|
|
5488
|
-
break;
|
|
5489
|
-
default:
|
|
5490
|
-
break;
|
|
5491
|
-
}
|
|
5492
|
-
break;
|
|
5493
|
-
default:
|
|
5494
|
-
break;
|
|
5495
|
-
}
|
|
5496
|
-
if (!targetTriple) {
|
|
5497
|
-
throw new Error(`Unsupported platform: ${platform2} (${arch})`);
|
|
5498
|
-
}
|
|
5499
|
-
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
|
|
5500
|
-
if (!platformPackage) {
|
|
5501
|
-
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
|
5502
|
-
}
|
|
5503
|
-
let vendorRoot;
|
|
5504
|
-
try {
|
|
5505
|
-
const codexPackageJsonPath = moduleRequire.resolve(`${CODEX_NPM_NAME}/package.json`);
|
|
5506
|
-
const codexRequire = createRequire(codexPackageJsonPath);
|
|
5507
|
-
const platformPackageJsonPath = codexRequire.resolve(`${platformPackage}/package.json`);
|
|
5508
|
-
vendorRoot = path2.join(path2.dirname(platformPackageJsonPath), "vendor");
|
|
5509
|
-
} catch {
|
|
5510
|
-
throw new Error(
|
|
5511
|
-
`Unable to locate Codex CLI binaries. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
|
|
5512
|
-
);
|
|
5513
|
-
}
|
|
5514
|
-
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
5515
|
-
const nativePackage = resolveNativePackage(vendorRoot, targetTriple, codexBinaryName);
|
|
5516
|
-
if (!nativePackage) {
|
|
5517
|
-
throw new Error(
|
|
5518
|
-
`Unable to locate Codex CLI binaries for ${targetTriple}. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
|
|
5519
|
-
);
|
|
5520
|
-
}
|
|
5521
|
-
return nativePackage;
|
|
5522
|
-
}
|
|
5523
|
-
function resolveNativePackage(vendorRoot, targetTriple, codexBinaryName) {
|
|
5524
|
-
const packageRoot = path2.join(vendorRoot, targetTriple);
|
|
5525
|
-
const packageBinaryPath = path2.join(packageRoot, "bin", codexBinaryName);
|
|
5526
|
-
if (isFile(packageBinaryPath) && isFile(path2.join(packageRoot, "codex-package.json"))) {
|
|
5527
|
-
return {
|
|
5528
|
-
executablePath: packageBinaryPath,
|
|
5529
|
-
pathDirs: existingDirs(path2.join(packageRoot, "codex-path"))
|
|
5530
|
-
};
|
|
5531
|
-
}
|
|
5532
|
-
const legacyBinaryPath = path2.join(packageRoot, "codex", codexBinaryName);
|
|
5533
|
-
if (isFile(legacyBinaryPath)) {
|
|
5534
|
-
return {
|
|
5535
|
-
executablePath: legacyBinaryPath,
|
|
5536
|
-
pathDirs: existingDirs(path2.join(packageRoot, "path"))
|
|
5537
|
-
};
|
|
5538
|
-
}
|
|
5539
|
-
return null;
|
|
5540
|
-
}
|
|
5541
|
-
function existingDirs(...dirs) {
|
|
5542
|
-
return dirs.filter(isDirectory);
|
|
5543
|
-
}
|
|
5544
|
-
function prependPathDirs(env, pathDirs, platform2 = process.platform) {
|
|
5545
|
-
const pathKey = pathEnvKey(env, platform2);
|
|
5546
|
-
if (platform2 === "win32") {
|
|
5547
|
-
for (const key of Object.keys(env)) {
|
|
5548
|
-
if (key.toLowerCase() === "path" && key !== pathKey) {
|
|
5549
|
-
delete env[key];
|
|
5550
|
-
}
|
|
5551
|
-
}
|
|
5552
|
-
}
|
|
5553
|
-
const existingEntries = (env[pathKey] ?? "").split(path2.delimiter).filter((entry) => entry.length > 0 && !pathDirs.includes(entry));
|
|
5554
|
-
env[pathKey] = [...pathDirs, ...existingEntries].join(path2.delimiter);
|
|
5555
|
-
}
|
|
5556
|
-
function pathEnvKey(env, platform2) {
|
|
5557
|
-
if (platform2 !== "win32") {
|
|
5558
|
-
return "PATH";
|
|
5559
|
-
}
|
|
5560
|
-
const matchingKeys = Object.keys(env).filter((key) => key.toLowerCase() === "path");
|
|
5561
|
-
return matchingKeys.includes("Path") ? "Path" : matchingKeys.at(-1) ?? "PATH";
|
|
5562
|
-
}
|
|
5563
|
-
function isFile(filePath) {
|
|
5564
|
-
try {
|
|
5565
|
-
return statSync(filePath).isFile();
|
|
5566
|
-
} catch {
|
|
5567
|
-
return false;
|
|
5568
|
-
}
|
|
5569
|
-
}
|
|
5570
|
-
function isDirectory(filePath) {
|
|
5571
|
-
try {
|
|
5572
|
-
return statSync(filePath).isDirectory();
|
|
5573
|
-
} catch {
|
|
5574
|
-
return false;
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
var Codex = class {
|
|
5578
|
-
exec;
|
|
5579
|
-
options;
|
|
5580
|
-
constructor(options = {}) {
|
|
5581
|
-
const { codexPathOverride, env, config } = options;
|
|
5582
|
-
this.exec = new CodexExec(codexPathOverride, env, config);
|
|
5583
|
-
this.options = options;
|
|
5584
|
-
}
|
|
5585
|
-
/**
|
|
5586
|
-
* Starts a new conversation with an agent.
|
|
5587
|
-
* @returns A new thread instance.
|
|
5588
|
-
*/
|
|
5589
|
-
startThread(options = {}) {
|
|
5590
|
-
return new Thread(this.exec, this.options, options);
|
|
5591
|
-
}
|
|
5592
|
-
/**
|
|
5593
|
-
* Resumes a conversation with an agent based on the thread id.
|
|
5594
|
-
* Threads are persisted in ~/.codex/sessions.
|
|
5595
|
-
*
|
|
5596
|
-
* @param id The id of the thread to resume.
|
|
5597
|
-
* @returns A new thread instance.
|
|
5598
|
-
*/
|
|
5599
|
-
resumeThread(id, options = {}) {
|
|
5600
|
-
return new Thread(this.exec, this.options, options, id);
|
|
5601
|
-
}
|
|
5602
|
-
};
|
|
5603
|
-
|
|
5604
5075
|
// packages/agent-runtime/src/codex/transport.ts
|
|
5076
|
+
import {
|
|
5077
|
+
Codex
|
|
5078
|
+
} from "@openai/codex-sdk";
|
|
5605
5079
|
function buildSdkThreadOptions(spec) {
|
|
5606
5080
|
return {
|
|
5607
5081
|
...spec.model ? { model: spec.model } : {},
|
|
@@ -6393,7 +5867,7 @@ var ConnectorHealthStore = class {
|
|
|
6393
5867
|
|
|
6394
5868
|
// src/dispatcher.ts
|
|
6395
5869
|
import { randomUUID } from "crypto";
|
|
6396
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync10, statSync
|
|
5870
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync10, statSync } from "fs";
|
|
6397
5871
|
import { join as join13 } from "path";
|
|
6398
5872
|
|
|
6399
5873
|
// src/summon.ts
|
|
@@ -6731,10 +6205,10 @@ import { join as join9 } from "path";
|
|
|
6731
6205
|
var PREFIX = "cabane-codex-instructions-";
|
|
6732
6206
|
async function writeCodexInstructionsFile(contents) {
|
|
6733
6207
|
const dir2 = await mkdtemp(join9(tmpdir(), PREFIX));
|
|
6734
|
-
const
|
|
6735
|
-
await writeFile(
|
|
6208
|
+
const path = join9(dir2, "instructions.md");
|
|
6209
|
+
await writeFile(path, contents, { encoding: "utf8", mode: 384 });
|
|
6736
6210
|
return {
|
|
6737
|
-
path
|
|
6211
|
+
path,
|
|
6738
6212
|
cleanup: async () => {
|
|
6739
6213
|
await rm(dir2, { recursive: true, force: true });
|
|
6740
6214
|
}
|
|
@@ -6754,10 +6228,10 @@ function pathFor3(workspaceId, conversationId, agentId) {
|
|
|
6754
6228
|
return join10(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
6755
6229
|
}
|
|
6756
6230
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
6757
|
-
const
|
|
6758
|
-
if (!existsSync7(
|
|
6231
|
+
const path = pathFor3(workspaceId, conversationId, agentId);
|
|
6232
|
+
if (!existsSync7(path)) return null;
|
|
6759
6233
|
try {
|
|
6760
|
-
const parsed = JSON.parse(readFileSync6(
|
|
6234
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
6761
6235
|
if (parsed && typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
|
|
6762
6236
|
return {
|
|
6763
6237
|
cwd: parsed.cwd,
|
|
@@ -6791,14 +6265,14 @@ function secretsPath() {
|
|
|
6791
6265
|
}
|
|
6792
6266
|
var secretStoreSchema = z13.record(z13.string(), z13.string());
|
|
6793
6267
|
function loadSecretStore() {
|
|
6794
|
-
const
|
|
6795
|
-
if (!existsSync8(
|
|
6268
|
+
const path = secretsPath();
|
|
6269
|
+
if (!existsSync8(path)) return makeStore({});
|
|
6796
6270
|
let raw;
|
|
6797
6271
|
try {
|
|
6798
|
-
raw = readFileSync7(
|
|
6272
|
+
raw = readFileSync7(path, "utf8");
|
|
6799
6273
|
} catch (err) {
|
|
6800
6274
|
throw new ConfigError(
|
|
6801
|
-
`couldn't read ${
|
|
6275
|
+
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
6802
6276
|
);
|
|
6803
6277
|
}
|
|
6804
6278
|
if (raw.trim().length === 0) return makeStore({});
|
|
@@ -6807,13 +6281,13 @@ function loadSecretStore() {
|
|
|
6807
6281
|
parsed = JSON.parse(raw);
|
|
6808
6282
|
} catch (err) {
|
|
6809
6283
|
throw new ConfigError(
|
|
6810
|
-
`${
|
|
6284
|
+
`${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}. It must be a flat object of "NAME": "value" secret pairs.`
|
|
6811
6285
|
);
|
|
6812
6286
|
}
|
|
6813
6287
|
const result = secretStoreSchema.safeParse(parsed);
|
|
6814
6288
|
if (!result.success) {
|
|
6815
6289
|
throw new ConfigError(
|
|
6816
|
-
`${
|
|
6290
|
+
`${path} must be a flat object mapping secret names to string values (e.g. { "GITHUB_TOKEN": "ghp_\u2026" }).`
|
|
6817
6291
|
);
|
|
6818
6292
|
}
|
|
6819
6293
|
return makeStore(result.data);
|
|
@@ -7249,7 +6723,7 @@ function checkoutState(cwd) {
|
|
|
7249
6723
|
if (!existsSync9(gitPath)) return { ok: false, reason: `${cwd} holds no git metadata` };
|
|
7250
6724
|
let stat;
|
|
7251
6725
|
try {
|
|
7252
|
-
stat =
|
|
6726
|
+
stat = statSync(gitPath);
|
|
7253
6727
|
} catch (error) {
|
|
7254
6728
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
7255
6729
|
}
|
|
@@ -8680,8 +8154,8 @@ function sleep3(ms) {
|
|
|
8680
8154
|
}
|
|
8681
8155
|
|
|
8682
8156
|
// src/version.ts
|
|
8683
|
-
import { createRequire
|
|
8684
|
-
var pkg =
|
|
8157
|
+
import { createRequire } from "module";
|
|
8158
|
+
var pkg = createRequire(import.meta.url)("../package.json");
|
|
8685
8159
|
var COMPANION_VERSION = pkg.version;
|
|
8686
8160
|
|
|
8687
8161
|
// src/supervisor.ts
|
|
@@ -9485,9 +8959,9 @@ var CompanionSupervisor = class {
|
|
|
9485
8959
|
};
|
|
9486
8960
|
function defaultReexec() {
|
|
9487
8961
|
clearRuntimeState();
|
|
9488
|
-
void import("child_process").then(({ spawn:
|
|
8962
|
+
void import("child_process").then(({ spawn: spawn6 }) => {
|
|
9489
8963
|
try {
|
|
9490
|
-
const child =
|
|
8964
|
+
const child = spawn6(process.execPath, process.argv.slice(1), {
|
|
9491
8965
|
stdio: "inherit",
|
|
9492
8966
|
detached: false
|
|
9493
8967
|
});
|
|
@@ -9571,8 +9045,8 @@ function recordCrash(rec2) {
|
|
|
9571
9045
|
}
|
|
9572
9046
|
function clearCrash() {
|
|
9573
9047
|
try {
|
|
9574
|
-
const
|
|
9575
|
-
if (existsSync11(
|
|
9048
|
+
const path = crashMarkerPath();
|
|
9049
|
+
if (existsSync11(path)) rmSync7(path, { force: true });
|
|
9576
9050
|
} catch {
|
|
9577
9051
|
}
|
|
9578
9052
|
}
|
|
@@ -10071,11 +9545,11 @@ function printList(dir2) {
|
|
|
10071
9545
|
"render one: cabane-companion transcript <file> (or `--last` for the newest)\n"
|
|
10072
9546
|
);
|
|
10073
9547
|
}
|
|
10074
|
-
function peek(
|
|
9548
|
+
function peek(path) {
|
|
10075
9549
|
let meta;
|
|
10076
9550
|
let outcome;
|
|
10077
9551
|
try {
|
|
10078
|
-
for (const line of readFileSync10(
|
|
9552
|
+
for (const line of readFileSync10(path, "utf8").split("\n")) {
|
|
10079
9553
|
if (!line.trim()) continue;
|
|
10080
9554
|
const o = safeParse(line);
|
|
10081
9555
|
const t = str2(rec(o)?.type);
|
|
@@ -10105,13 +9579,13 @@ function resolveTarget(dir2, target) {
|
|
|
10105
9579
|
` + matches.slice(0, 10).map((m) => ` ${m}`).join("\n")
|
|
10106
9580
|
);
|
|
10107
9581
|
}
|
|
10108
|
-
function renderFile(
|
|
9582
|
+
function renderFile(path) {
|
|
10109
9583
|
let content;
|
|
10110
9584
|
try {
|
|
10111
|
-
content = readFileSync10(
|
|
9585
|
+
content = readFileSync10(path, "utf8");
|
|
10112
9586
|
} catch (err) {
|
|
10113
9587
|
throw new CompanionError(
|
|
10114
|
-
`couldn't read ${
|
|
9588
|
+
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
10115
9589
|
);
|
|
10116
9590
|
}
|
|
10117
9591
|
return renderTranscript(content.split("\n"));
|