@cabane/companion 0.6.162 → 0.6.164
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 +370 -239
- package/dist/runtime.js +178 -67
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3048,14 +3048,14 @@ function readSdkUsage(usage) {
|
|
|
3048
3048
|
function readRequestContextTokens(msg) {
|
|
3049
3049
|
return readSdkUsage(msg.message?.usage)?.inputTokens;
|
|
3050
3050
|
}
|
|
3051
|
-
async function readHarnessContext(
|
|
3052
|
-
if (!
|
|
3051
|
+
async function readHarnessContext(read2) {
|
|
3052
|
+
if (!read2) return void 0;
|
|
3053
3053
|
let timer;
|
|
3054
3054
|
const timeout = new Promise((resolve2) => {
|
|
3055
3055
|
timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
|
|
3056
3056
|
});
|
|
3057
3057
|
try {
|
|
3058
|
-
return parseContextUsage(await Promise.race([
|
|
3058
|
+
return parseContextUsage(await Promise.race([read2(), timeout]));
|
|
3059
3059
|
} catch {
|
|
3060
3060
|
return void 0;
|
|
3061
3061
|
} finally {
|
|
@@ -5412,14 +5412,14 @@ function decideResume3(stored, currentCwd) {
|
|
|
5412
5412
|
function readUsageBaseline(raw) {
|
|
5413
5413
|
if (!raw || typeof raw !== "object") return null;
|
|
5414
5414
|
const rec2 = raw;
|
|
5415
|
-
const
|
|
5415
|
+
const read2 = (key) => {
|
|
5416
5416
|
const v = rec2[key];
|
|
5417
5417
|
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
5418
5418
|
};
|
|
5419
|
-
const inputTokens =
|
|
5420
|
-
const outputTokens =
|
|
5421
|
-
const cacheReadTokens =
|
|
5422
|
-
const cacheCreationTokens =
|
|
5419
|
+
const inputTokens = read2("inputTokens");
|
|
5420
|
+
const outputTokens = read2("outputTokens");
|
|
5421
|
+
const cacheReadTokens = read2("cacheReadTokens");
|
|
5422
|
+
const cacheCreationTokens = read2("cacheCreationTokens");
|
|
5423
5423
|
if (inputTokens === null || outputTokens === null || cacheReadTokens === null || cacheCreationTokens === null) {
|
|
5424
5424
|
return null;
|
|
5425
5425
|
}
|
|
@@ -7244,7 +7244,7 @@ var init_opencode_lock = __esm({
|
|
|
7244
7244
|
|
|
7245
7245
|
// src/turn-containment.ts
|
|
7246
7246
|
import { execFile, execFileSync, spawn as spawn6 } from "child_process";
|
|
7247
|
-
import { readFileSync as
|
|
7247
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7248
7248
|
import { promisify } from "util";
|
|
7249
7249
|
function turnScopeUnit(turnId, attempt) {
|
|
7250
7250
|
const safe2 = turnId.replace(/[^A-Za-z0-9_.-]/g, "-");
|
|
@@ -7297,7 +7297,7 @@ function currentSystemdUnit(cgroupText) {
|
|
|
7297
7297
|
}
|
|
7298
7298
|
function readCurrentSystemdUnit() {
|
|
7299
7299
|
try {
|
|
7300
|
-
return currentSystemdUnit(
|
|
7300
|
+
return currentSystemdUnit(readFileSync11("/proc/self/cgroup", "utf8"));
|
|
7301
7301
|
} catch {
|
|
7302
7302
|
return null;
|
|
7303
7303
|
}
|
|
@@ -7527,18 +7527,18 @@ var init_turn_containment = __esm({
|
|
|
7527
7527
|
});
|
|
7528
7528
|
|
|
7529
7529
|
// src/secrets.ts
|
|
7530
|
-
import { existsSync as existsSync12, readFileSync as
|
|
7531
|
-
import { join as
|
|
7530
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
7531
|
+
import { join as join20 } from "path";
|
|
7532
7532
|
import { z as z15 } from "zod";
|
|
7533
7533
|
function secretsPath() {
|
|
7534
|
-
return
|
|
7534
|
+
return join20(cabaneDir(), "secrets.json");
|
|
7535
7535
|
}
|
|
7536
7536
|
function loadSecretStore() {
|
|
7537
7537
|
const path = secretsPath();
|
|
7538
7538
|
if (!existsSync12(path)) return makeStore({});
|
|
7539
7539
|
let raw;
|
|
7540
7540
|
try {
|
|
7541
|
-
raw =
|
|
7541
|
+
raw = readFileSync12(path, "utf8");
|
|
7542
7542
|
} catch (err) {
|
|
7543
7543
|
throw new ConfigError(
|
|
7544
7544
|
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8581,10 +8581,10 @@ var init_build_options = __esm({
|
|
|
8581
8581
|
// src/codex-instructions.ts
|
|
8582
8582
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
8583
8583
|
import { tmpdir } from "os";
|
|
8584
|
-
import { join as
|
|
8584
|
+
import { join as join21 } from "path";
|
|
8585
8585
|
async function writeCodexInstructionsFile(contents) {
|
|
8586
|
-
const dir2 = await mkdtemp(
|
|
8587
|
-
const path =
|
|
8586
|
+
const dir2 = await mkdtemp(join21(tmpdir(), PREFIX));
|
|
8587
|
+
const path = join21(dir2, "instructions.md");
|
|
8588
8588
|
await writeFile(path, contents, { encoding: "utf8", mode: 384 });
|
|
8589
8589
|
return {
|
|
8590
8590
|
path,
|
|
@@ -8604,13 +8604,13 @@ var init_codex_instructions = __esm({
|
|
|
8604
8604
|
// src/codex-images.ts
|
|
8605
8605
|
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
8606
8606
|
import { tmpdir as tmpdir2 } from "os";
|
|
8607
|
-
import { join as
|
|
8607
|
+
import { join as join22 } from "path";
|
|
8608
8608
|
function createCodexImagePreparer(opts) {
|
|
8609
8609
|
const doFetch = opts.fetch ?? fetch;
|
|
8610
8610
|
const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
8611
8611
|
return async (urls, signal) => {
|
|
8612
8612
|
signal.throwIfAborted();
|
|
8613
|
-
const dir2 = await mkdtemp2(
|
|
8613
|
+
const dir2 = await mkdtemp2(join22(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
|
|
8614
8614
|
const cleanup = async () => {
|
|
8615
8615
|
try {
|
|
8616
8616
|
await rm2(dir2, { recursive: true, force: true });
|
|
@@ -8631,7 +8631,7 @@ function createCodexImagePreparer(opts) {
|
|
|
8631
8631
|
const ext = EXTENSIONS[type];
|
|
8632
8632
|
if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
|
|
8633
8633
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
8634
|
-
const path =
|
|
8634
|
+
const path = join22(dir2, `image-${index + 1}.${ext}`);
|
|
8635
8635
|
await writeFile2(path, bytes, { mode: 384 });
|
|
8636
8636
|
return path;
|
|
8637
8637
|
} catch (err) {
|
|
@@ -8682,9 +8682,9 @@ function resolveSeqFloor(sources, ctx2) {
|
|
|
8682
8682
|
);
|
|
8683
8683
|
return floor;
|
|
8684
8684
|
}
|
|
8685
|
-
function readOutboxFloor(
|
|
8685
|
+
function readOutboxFloor(read2, turnId, log2) {
|
|
8686
8686
|
try {
|
|
8687
|
-
return
|
|
8687
|
+
return read2(turnId);
|
|
8688
8688
|
} catch (err) {
|
|
8689
8689
|
log2.debug(
|
|
8690
8690
|
{ turnId, err: err instanceof Error ? err.message : String(err) },
|
|
@@ -8776,22 +8776,22 @@ var init_prepare_failure = __esm({
|
|
|
8776
8776
|
});
|
|
8777
8777
|
|
|
8778
8778
|
// src/prepared.ts
|
|
8779
|
-
import { mkdirSync as mkdirSync13, readFileSync as
|
|
8780
|
-
import { join as
|
|
8779
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync13, rmSync as rmSync9, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
|
|
8780
|
+
import { join as join23 } from "path";
|
|
8781
8781
|
function dirFor(workspaceId) {
|
|
8782
|
-
return
|
|
8782
|
+
return join23(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
8783
8783
|
}
|
|
8784
8784
|
function conversationDir(workspaceId, conversationId) {
|
|
8785
|
-
return
|
|
8785
|
+
return join23(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
8786
8786
|
}
|
|
8787
8787
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
8788
|
-
return
|
|
8788
|
+
return join23(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
8789
8789
|
}
|
|
8790
8790
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
8791
8791
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
8792
8792
|
if (!existsSync13(path)) return null;
|
|
8793
8793
|
try {
|
|
8794
|
-
const parsed = JSON.parse(
|
|
8794
|
+
const parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
8795
8795
|
if (parsed && typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
|
|
8796
8796
|
return {
|
|
8797
8797
|
cwd: parsed.cwd,
|
|
@@ -8823,9 +8823,9 @@ var init_prepared = __esm({
|
|
|
8823
8823
|
|
|
8824
8824
|
// src/transcript-writer.ts
|
|
8825
8825
|
import { appendFileSync, chmodSync as chmodSync5, copyFileSync as copyFileSync2, mkdirSync as mkdirSync14, readdirSync as readdirSync2, rmSync as rmSync10 } from "fs";
|
|
8826
|
-
import { basename as basename3, dirname as
|
|
8826
|
+
import { basename as basename3, dirname as dirname12, join as join24 } from "path";
|
|
8827
8827
|
function transcriptsDir() {
|
|
8828
|
-
return
|
|
8828
|
+
return join24(runnerStateDir(), "transcripts");
|
|
8829
8829
|
}
|
|
8830
8830
|
function fileName(meta) {
|
|
8831
8831
|
const d = new Date(meta.ts);
|
|
@@ -8851,7 +8851,7 @@ function pruneOld(dir2, retain) {
|
|
|
8851
8851
|
const drop = files.sort().slice(0, files.length - retain);
|
|
8852
8852
|
for (const f of drop) {
|
|
8853
8853
|
try {
|
|
8854
|
-
rmSync10(
|
|
8854
|
+
rmSync10(join24(dir2, f), { force: true });
|
|
8855
8855
|
} catch {
|
|
8856
8856
|
}
|
|
8857
8857
|
}
|
|
@@ -8869,7 +8869,7 @@ var init_transcript_writer = __esm({
|
|
|
8869
8869
|
onWarn;
|
|
8870
8870
|
constructor(dir2, meta, onWarn) {
|
|
8871
8871
|
this.onWarn = onWarn;
|
|
8872
|
-
this.path =
|
|
8872
|
+
this.path = join24(dir2, fileName(meta));
|
|
8873
8873
|
try {
|
|
8874
8874
|
mkdirSync14(dir2, { recursive: true });
|
|
8875
8875
|
try {
|
|
@@ -8898,10 +8898,10 @@ var init_transcript_writer = __esm({
|
|
|
8898
8898
|
preserveAnomaly() {
|
|
8899
8899
|
if (this.broken) return;
|
|
8900
8900
|
try {
|
|
8901
|
-
const dir2 =
|
|
8901
|
+
const dir2 = join24(dirname12(this.path), "anomalies");
|
|
8902
8902
|
mkdirSync14(dir2, { recursive: true, mode: 448 });
|
|
8903
8903
|
chmodSync5(dir2, 448);
|
|
8904
|
-
const target =
|
|
8904
|
+
const target = join24(dir2, basename3(this.path));
|
|
8905
8905
|
copyFileSync2(this.path, target);
|
|
8906
8906
|
chmodSync5(target, 384);
|
|
8907
8907
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -10338,7 +10338,7 @@ var init_turn_execution = __esm({
|
|
|
10338
10338
|
// src/dispatcher.ts
|
|
10339
10339
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10340
10340
|
import { existsSync as existsSync15, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
10341
|
-
import { join as
|
|
10341
|
+
import { join as join25 } from "path";
|
|
10342
10342
|
function checkoutState(cwd) {
|
|
10343
10343
|
if (!cwd) return { ok: false, reason: "no working directory was resolved for this turn" };
|
|
10344
10344
|
if (!existsSync15(cwd)) return { ok: false, reason: `the working directory is gone (${cwd})` };
|
|
@@ -10351,7 +10351,7 @@ function checkoutState(cwd) {
|
|
|
10351
10351
|
if (entries.length === 0) {
|
|
10352
10352
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
10353
10353
|
}
|
|
10354
|
-
const gitPath =
|
|
10354
|
+
const gitPath = join25(cwd, ".git");
|
|
10355
10355
|
if (!existsSync15(gitPath)) return { ok: true, reason: "usable" };
|
|
10356
10356
|
let stat;
|
|
10357
10357
|
try {
|
|
@@ -10359,7 +10359,7 @@ function checkoutState(cwd) {
|
|
|
10359
10359
|
} catch (error) {
|
|
10360
10360
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
10361
10361
|
}
|
|
10362
|
-
if (stat.isDirectory() && !existsSync15(
|
|
10362
|
+
if (stat.isDirectory() && !existsSync15(join25(gitPath, "HEAD")))
|
|
10363
10363
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
10364
10364
|
return { ok: true, reason: "usable" };
|
|
10365
10365
|
}
|
|
@@ -10730,7 +10730,7 @@ import {
|
|
|
10730
10730
|
chmodSync as chmodSync7,
|
|
10731
10731
|
mkdirSync as mkdirSync17,
|
|
10732
10732
|
mkdtempSync,
|
|
10733
|
-
readFileSync as
|
|
10733
|
+
readFileSync as readFileSync16,
|
|
10734
10734
|
realpathSync as realpathSync7,
|
|
10735
10735
|
rmSync as rmSync14,
|
|
10736
10736
|
writeFileSync as writeFileSync10
|
|
@@ -10738,7 +10738,7 @@ import {
|
|
|
10738
10738
|
import { createRequire as createRequire3 } from "module";
|
|
10739
10739
|
import { isIP } from "net";
|
|
10740
10740
|
import { homedir as homedir8, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo3 } from "os";
|
|
10741
|
-
import { dirname as
|
|
10741
|
+
import { dirname as dirname14, join as join29, sep as sep5 } from "path";
|
|
10742
10742
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
10743
10743
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
10744
10744
|
function accountHome() {
|
|
@@ -10770,23 +10770,23 @@ async function resolvedAddresses(baseUrl) {
|
|
|
10770
10770
|
}
|
|
10771
10771
|
}
|
|
10772
10772
|
function companionPackageRoot(modulePath) {
|
|
10773
|
-
for (let dir2 =
|
|
10773
|
+
for (let dir2 = dirname14(modulePath); ; dir2 = dirname14(dir2)) {
|
|
10774
10774
|
try {
|
|
10775
|
-
const manifest = JSON.parse(
|
|
10775
|
+
const manifest = JSON.parse(readFileSync16(join29(dir2, "package.json"), "utf8"));
|
|
10776
10776
|
if (manifest.name === "@cabane/companion") return dir2;
|
|
10777
10777
|
} catch {
|
|
10778
10778
|
}
|
|
10779
|
-
if (
|
|
10779
|
+
if (dirname14(dir2) === dir2) return null;
|
|
10780
10780
|
}
|
|
10781
10781
|
}
|
|
10782
10782
|
function harnessRuntimePaths() {
|
|
10783
|
-
const paths = [
|
|
10783
|
+
const paths = [dirname14(dirname14(process.execPath))];
|
|
10784
10784
|
const root = companionPackageRoot(fileURLToPath7(import.meta.url));
|
|
10785
10785
|
if (root) paths.push(root);
|
|
10786
10786
|
try {
|
|
10787
10787
|
const require2 = createRequire3(import.meta.url);
|
|
10788
10788
|
paths.push(
|
|
10789
|
-
|
|
10789
|
+
dirname14(realpathSync7(require2.resolve("@anthropic-ai/sandbox-runtime/package.json")))
|
|
10790
10790
|
);
|
|
10791
10791
|
} catch {
|
|
10792
10792
|
}
|
|
@@ -10794,7 +10794,7 @@ function harnessRuntimePaths() {
|
|
|
10794
10794
|
const bundled = resolveBundledBinary(runtime).path;
|
|
10795
10795
|
if (bundled) {
|
|
10796
10796
|
try {
|
|
10797
|
-
paths.push(
|
|
10797
|
+
paths.push(dirname14(realpathSync7(bundled)));
|
|
10798
10798
|
} catch {
|
|
10799
10799
|
}
|
|
10800
10800
|
}
|
|
@@ -10805,9 +10805,9 @@ function ownAddresses() {
|
|
|
10805
10805
|
return Object.values(networkInterfaces()).flat().flatMap((i) => i ? [i.address] : []);
|
|
10806
10806
|
}
|
|
10807
10807
|
function storeWalkRoots(homes, cabaneDir2) {
|
|
10808
|
-
const companionHome =
|
|
10809
|
-
const parent =
|
|
10810
|
-
const beside = homes.includes(companionHome) ||
|
|
10808
|
+
const companionHome = dirname14(cabaneDir2);
|
|
10809
|
+
const parent = dirname14(companionHome);
|
|
10810
|
+
const beside = homes.includes(companionHome) || dirname14(parent) === parent ? [] : [parent];
|
|
10811
10811
|
const candidates = [.../* @__PURE__ */ new Set([...homes, ...beside])];
|
|
10812
10812
|
return candidates.filter(
|
|
10813
10813
|
(root) => !candidates.some((other) => other !== root && isAtOrUnder(root, other))
|
|
@@ -10845,19 +10845,19 @@ async function walkCompanionStores(roots, opts = {}) {
|
|
|
10845
10845
|
}
|
|
10846
10846
|
function companionStoreDenies(input) {
|
|
10847
10847
|
const { homes, cabaneDir: cabaneDir2, foundStores } = input;
|
|
10848
|
-
const parent =
|
|
10848
|
+
const parent = dirname14(dirname14(cabaneDir2));
|
|
10849
10849
|
const always = [
|
|
10850
10850
|
cabaneDir2,
|
|
10851
10851
|
...homes.flatMap((home) => [
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10852
|
+
join29(home, ".cabane"),
|
|
10853
|
+
join29(home, "*", ".cabane"),
|
|
10854
|
+
join29(home, "*", "*", ".cabane")
|
|
10855
10855
|
]),
|
|
10856
10856
|
// A glob anchored at the root has no directory to start from.
|
|
10857
|
-
...
|
|
10857
|
+
...dirname14(parent) === parent ? [] : [join29(parent, "*", ".cabane")]
|
|
10858
10858
|
];
|
|
10859
|
-
const companionHome =
|
|
10860
|
-
const otherLogins = foundStores.map((store) =>
|
|
10859
|
+
const companionHome = dirname14(cabaneDir2);
|
|
10860
|
+
const otherLogins = foundStores.map((store) => dirname14(store)).filter((home) => home !== companionHome).flatMap((home) => [join29(home, ".claude"), join29(home, ".codex")]).filter((path) => !input.ownState.some((own) => isAtOrUnder(own, path)));
|
|
10861
10861
|
return [.../* @__PURE__ */ new Set([...always, ...foundStores, ...otherLogins])];
|
|
10862
10862
|
}
|
|
10863
10863
|
function companionStoreWatch(log2) {
|
|
@@ -10874,7 +10874,7 @@ function isAtOrUnder(path, dir2) {
|
|
|
10874
10874
|
}
|
|
10875
10875
|
function expandHome(path, home) {
|
|
10876
10876
|
if (path === "~") return home;
|
|
10877
|
-
return path.startsWith("~/") ?
|
|
10877
|
+
return path.startsWith("~/") ? join29(home, path.slice(2)) : path;
|
|
10878
10878
|
}
|
|
10879
10879
|
function stripPort(pattern) {
|
|
10880
10880
|
return pattern.startsWith("[") ? pattern.replace(/^\[([^\]]+)\](:\d+)?$/, "$1") : pattern.replace(/:\d+$/, "");
|
|
@@ -10892,8 +10892,8 @@ function patternsOverlap(a, b) {
|
|
|
10892
10892
|
}
|
|
10893
10893
|
function harnessSandboxConfig(input) {
|
|
10894
10894
|
const { policy } = input;
|
|
10895
|
-
const claudeDir = input.claudeConfigDir ??
|
|
10896
|
-
const codexDir = input.codexHome ??
|
|
10895
|
+
const claudeDir = input.claudeConfigDir ?? join29(input.home, ".claude");
|
|
10896
|
+
const codexDir = input.codexHome ?? join29(input.home, ".codex");
|
|
10897
10897
|
const homes = [.../* @__PURE__ */ new Set([input.home, input.accountHome ?? input.home])];
|
|
10898
10898
|
const expand = (paths) => (paths ?? []).map((p) => expandHome(p, input.home));
|
|
10899
10899
|
const cabane2 = cabaneHostEntries(input.baseUrl, input.resolved, input.ownAddresses);
|
|
@@ -10910,7 +10910,7 @@ function harnessSandboxConfig(input) {
|
|
|
10910
10910
|
foundStores: input.foundStores,
|
|
10911
10911
|
ownState: [claudeDir, codexDir]
|
|
10912
10912
|
}),
|
|
10913
|
-
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) =>
|
|
10913
|
+
...homes.flatMap((home) => SECRET_PATHS_UNDER_HOME.map((p) => join29(home, p))),
|
|
10914
10914
|
...expand(policy.filesystem?.denyRead)
|
|
10915
10915
|
];
|
|
10916
10916
|
const strict = policy.filesystem?.mode === "strict";
|
|
@@ -10918,7 +10918,7 @@ function harnessSandboxConfig(input) {
|
|
|
10918
10918
|
const allowRead = strict ? [
|
|
10919
10919
|
...input.cwd ? [input.cwd] : [],
|
|
10920
10920
|
claudeDir,
|
|
10921
|
-
|
|
10921
|
+
join29(input.home, ".claude.json"),
|
|
10922
10922
|
codexDir,
|
|
10923
10923
|
...input.runtimePaths ?? [],
|
|
10924
10924
|
...expand(policy.filesystem?.allowRead)
|
|
@@ -10958,7 +10958,7 @@ function harnessSandboxConfig(input) {
|
|
|
10958
10958
|
...input.cwd ? [input.cwd] : [],
|
|
10959
10959
|
input.tmp,
|
|
10960
10960
|
claudeDir,
|
|
10961
|
-
|
|
10961
|
+
join29(input.home, ".claude.json"),
|
|
10962
10962
|
codexDir,
|
|
10963
10963
|
...expand(policy.filesystem?.allowWrite)
|
|
10964
10964
|
],
|
|
@@ -11060,9 +11060,9 @@ async function prepareHarnessSandbox(input) {
|
|
|
11060
11060
|
} finally {
|
|
11061
11061
|
for (const name of held) delete process.env[name];
|
|
11062
11062
|
}
|
|
11063
|
-
mkdirSync17(
|
|
11064
|
-
const dir2 = mkdtempSync(
|
|
11065
|
-
const script =
|
|
11063
|
+
mkdirSync17(join29(cabaneDir(), "sandbox"), { recursive: true });
|
|
11064
|
+
const dir2 = mkdtempSync(join29(cabaneDir(), "sandbox", "turn-"));
|
|
11065
|
+
const script = join29(dir2, "exec-sandboxed");
|
|
11066
11066
|
writeFileSync10(
|
|
11067
11067
|
script,
|
|
11068
11068
|
[
|
|
@@ -11078,7 +11078,7 @@ async function prepareHarnessSandbox(input) {
|
|
|
11078
11078
|
return {
|
|
11079
11079
|
wrap: (command, args) => ({ command: script, args: [command, ...args] }),
|
|
11080
11080
|
executableFor: (real) => {
|
|
11081
|
-
const shim =
|
|
11081
|
+
const shim = join29(dir2, `exec-${++shims}`);
|
|
11082
11082
|
writeFileSync10(shim, `#!/bin/bash
|
|
11083
11083
|
exec ${shellQuote(script)} ${shellQuote(real)} "$@"
|
|
11084
11084
|
`);
|
|
@@ -11166,12 +11166,12 @@ import {
|
|
|
11166
11166
|
existsSync as existsSync19,
|
|
11167
11167
|
mkdirSync as mkdirSync18,
|
|
11168
11168
|
readdirSync as readdirSync6,
|
|
11169
|
-
readFileSync as
|
|
11169
|
+
readFileSync as readFileSync17,
|
|
11170
11170
|
renameSync as renameSync12,
|
|
11171
11171
|
rmSync as rmSync15,
|
|
11172
11172
|
writeFileSync as writeFileSync11
|
|
11173
11173
|
} from "fs";
|
|
11174
|
-
import { join as
|
|
11174
|
+
import { join as join30 } from "path";
|
|
11175
11175
|
var MAX_ENTRIES, MAX_AGE_MS, Outbox;
|
|
11176
11176
|
var init_outbox = __esm({
|
|
11177
11177
|
"src/outbox.ts"() {
|
|
@@ -11189,10 +11189,10 @@ var init_outbox = __esm({
|
|
|
11189
11189
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
11190
11190
|
// cases route writes at the right tmpdir.
|
|
11191
11191
|
dir() {
|
|
11192
|
-
return
|
|
11192
|
+
return join30(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
11193
11193
|
}
|
|
11194
11194
|
fileFor(turnId, seq) {
|
|
11195
|
-
return
|
|
11195
|
+
return join30(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
11196
11196
|
}
|
|
11197
11197
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
11198
11198
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -11234,9 +11234,9 @@ var init_outbox = __esm({
|
|
|
11234
11234
|
const entries = [];
|
|
11235
11235
|
for (const name of names) {
|
|
11236
11236
|
if (!name.endsWith(".json")) continue;
|
|
11237
|
-
const full =
|
|
11237
|
+
const full = join30(dir2, name);
|
|
11238
11238
|
try {
|
|
11239
|
-
const parsed = JSON.parse(
|
|
11239
|
+
const parsed = JSON.parse(readFileSync17(full, "utf8"));
|
|
11240
11240
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
11241
11241
|
entries.push(parsed);
|
|
11242
11242
|
} else {
|
|
@@ -11363,7 +11363,7 @@ var init_image_manifest = __esm({
|
|
|
11363
11363
|
|
|
11364
11364
|
// src/runner/exec-shim.ts
|
|
11365
11365
|
import { chmodSync as chmodSync8, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
11366
|
-
import { join as
|
|
11366
|
+
import { join as join34 } from "path";
|
|
11367
11367
|
function shellQuote2(value) {
|
|
11368
11368
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11369
11369
|
}
|
|
@@ -11373,10 +11373,10 @@ function createShimDir() {
|
|
|
11373
11373
|
return {
|
|
11374
11374
|
write(argv) {
|
|
11375
11375
|
if (!dir2) {
|
|
11376
|
-
mkdirSync20(
|
|
11377
|
-
dir2 = mkdtempSync2(
|
|
11376
|
+
mkdirSync20(join34(cabaneDir(), "runner"), { recursive: true });
|
|
11377
|
+
dir2 = mkdtempSync2(join34(cabaneDir(), "runner", "turn-"));
|
|
11378
11378
|
}
|
|
11379
|
-
const path =
|
|
11379
|
+
const path = join34(dir2, `exec-${++count}`);
|
|
11380
11380
|
writeFileSync13(path, `#!/bin/sh
|
|
11381
11381
|
exec ${argv.map(shellQuote2).join(" ")} "$@"
|
|
11382
11382
|
`);
|
|
@@ -11486,7 +11486,7 @@ __export(run_turn_exports, {
|
|
|
11486
11486
|
runnerLogger: () => runnerLogger,
|
|
11487
11487
|
stopWithLauncher: () => stopWithLauncher
|
|
11488
11488
|
});
|
|
11489
|
-
import { spawnSync } from "child_process";
|
|
11489
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
11490
11490
|
import pino2 from "pino";
|
|
11491
11491
|
async function drainBeforeExit(outbox, api, graceMs = OUTBOX_GRACE_MS, log2) {
|
|
11492
11492
|
if (outbox.size() === 0) return;
|
|
@@ -11595,7 +11595,7 @@ async function stopWithLauncher(groups, deps = {}) {
|
|
|
11595
11595
|
process.stdout.on("error", () => {
|
|
11596
11596
|
});
|
|
11597
11597
|
const ownGroup = () => {
|
|
11598
|
-
const ps =
|
|
11598
|
+
const ps = spawnSync2("ps", ["-eo", "pid=,pgid="], { timeout: 5e3 });
|
|
11599
11599
|
if (ps.status !== 0) return null;
|
|
11600
11600
|
return String(ps.stdout).split("\n").map((line) => /^\s*(\d+)\s+(\d+)\s*$/.exec(line)).filter((m) => m !== null).filter((m) => Number(m[2]) === process.pid).map((m) => Number(m[1])).filter((pid) => pid !== process.pid && pid !== ps.pid);
|
|
11601
11601
|
};
|
|
@@ -12437,7 +12437,9 @@ function clearRuntimeStateIfOurs(instanceId) {
|
|
|
12437
12437
|
}
|
|
12438
12438
|
rmSync3(path, { force: true });
|
|
12439
12439
|
}
|
|
12440
|
-
function readLiveRuntimeState(
|
|
12440
|
+
function readLiveRuntimeState({
|
|
12441
|
+
sweep = true
|
|
12442
|
+
} = {}) {
|
|
12441
12443
|
const path = runtimePath();
|
|
12442
12444
|
if (!existsSync5(path)) return null;
|
|
12443
12445
|
let parsed;
|
|
@@ -12450,7 +12452,7 @@ function readLiveRuntimeState() {
|
|
|
12450
12452
|
try {
|
|
12451
12453
|
process.kill(parsed.pid, 0);
|
|
12452
12454
|
} catch {
|
|
12453
|
-
clearRuntimeState();
|
|
12455
|
+
if (sweep) clearRuntimeState();
|
|
12454
12456
|
return null;
|
|
12455
12457
|
}
|
|
12456
12458
|
return parsed;
|
|
@@ -13004,6 +13006,96 @@ function plainUi() {
|
|
|
13004
13006
|
};
|
|
13005
13007
|
}
|
|
13006
13008
|
|
|
13009
|
+
// src/stray-companion.ts
|
|
13010
|
+
init_config();
|
|
13011
|
+
import { spawnSync } from "child_process";
|
|
13012
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
13013
|
+
import { join as join10 } from "path";
|
|
13014
|
+
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
13015
|
+
var START = /\sstart(\s|$)/;
|
|
13016
|
+
function companionStarts(psOutput) {
|
|
13017
|
+
const found = [];
|
|
13018
|
+
for (const line of psOutput.split("\n")) {
|
|
13019
|
+
const match = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
13020
|
+
if (!match) continue;
|
|
13021
|
+
const command = match[2];
|
|
13022
|
+
if (COMPANION_ENTRY.test(command) && START.test(command)) {
|
|
13023
|
+
found.push({ pid: Number(match[1]), command });
|
|
13024
|
+
}
|
|
13025
|
+
}
|
|
13026
|
+
return found;
|
|
13027
|
+
}
|
|
13028
|
+
function stateDirOf(env) {
|
|
13029
|
+
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
13030
|
+
return home ? join10(home, ".cabane") : null;
|
|
13031
|
+
}
|
|
13032
|
+
function envFromPsE(output, command) {
|
|
13033
|
+
const line = output.replace(/\n$/, "");
|
|
13034
|
+
if (!line.startsWith(command)) return {};
|
|
13035
|
+
const env = {};
|
|
13036
|
+
for (const entry of line.slice(command.length).trim().split(/ (?=[A-Za-z_][A-Za-z0-9_]*=)/)) {
|
|
13037
|
+
const eq = entry.indexOf("=");
|
|
13038
|
+
if (eq > 0) env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
13039
|
+
}
|
|
13040
|
+
return env;
|
|
13041
|
+
}
|
|
13042
|
+
function envFromProc(pid) {
|
|
13043
|
+
try {
|
|
13044
|
+
const env = {};
|
|
13045
|
+
for (const entry of readFileSync4(`/proc/${pid}/environ`, "utf8").split("\0")) {
|
|
13046
|
+
const eq = entry.indexOf("=");
|
|
13047
|
+
if (eq > 0) env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
13048
|
+
}
|
|
13049
|
+
return env;
|
|
13050
|
+
} catch {
|
|
13051
|
+
return null;
|
|
13052
|
+
}
|
|
13053
|
+
}
|
|
13054
|
+
function envOf(pid, command) {
|
|
13055
|
+
if (process.platform === "linux") return envFromProc(pid);
|
|
13056
|
+
const result = spawnSync("ps", ["-E", "-ww", "-o", "command=", "-p", String(pid)], {
|
|
13057
|
+
encoding: "utf8",
|
|
13058
|
+
timeout: 5e3
|
|
13059
|
+
});
|
|
13060
|
+
if (result.error || result.status !== 0) return null;
|
|
13061
|
+
return envFromPsE(result.stdout, command);
|
|
13062
|
+
}
|
|
13063
|
+
function findStrayCompanions(exclude = [], deps = {}) {
|
|
13064
|
+
if (!deps.ps && (process.platform === "win32" || typeof process.getuid !== "function")) {
|
|
13065
|
+
return [];
|
|
13066
|
+
}
|
|
13067
|
+
const ps = deps.ps ?? (() => {
|
|
13068
|
+
const result = spawnSync("ps", ["-U", String(process.getuid()), "-o", "pid=,command="], {
|
|
13069
|
+
encoding: "utf8",
|
|
13070
|
+
timeout: 5e3,
|
|
13071
|
+
maxBuffer: 16 * 1024 * 1024
|
|
13072
|
+
});
|
|
13073
|
+
return result.error || typeof result.stdout !== "string" ? null : result.stdout;
|
|
13074
|
+
});
|
|
13075
|
+
const listing = ps();
|
|
13076
|
+
if (!listing) return [];
|
|
13077
|
+
const dir2 = deps.dir ?? cabaneDir();
|
|
13078
|
+
const skip = /* @__PURE__ */ new Set([...deps.self ?? [process.pid, process.ppid], ...exclude]);
|
|
13079
|
+
return companionStarts(listing).filter((c) => {
|
|
13080
|
+
if (skip.has(c.pid)) return false;
|
|
13081
|
+
const env = (deps.env ?? envOf)(c.pid, c.command);
|
|
13082
|
+
return env !== null && stateDirOf(env) === dir2;
|
|
13083
|
+
});
|
|
13084
|
+
}
|
|
13085
|
+
var SERVICE_ENTRY = /\.cabane\/boot-guard/;
|
|
13086
|
+
function strayCompanionLines(strays, retry) {
|
|
13087
|
+
const one = strays.length === 1;
|
|
13088
|
+
return [
|
|
13089
|
+
`Another Cabane companion is running for this computer's ~/.cabane, and ~/.cabane has no record of it:`,
|
|
13090
|
+
...strays.map((s) => ` pid ${s.pid} ${s.command}`),
|
|
13091
|
+
`Two companions for one ~/.cabane sign each other's agents out. Stop ${one ? "it" : "them"} first:`,
|
|
13092
|
+
...strays.map(
|
|
13093
|
+
(s) => SERVICE_ENTRY.test(s.command) ? ` pid ${s.pid} is a service, which restarts if killed: cabane service uninstall` : ` kill ${s.pid}`
|
|
13094
|
+
),
|
|
13095
|
+
`then run ${retry} again.`
|
|
13096
|
+
];
|
|
13097
|
+
}
|
|
13098
|
+
|
|
13007
13099
|
// src/service.ts
|
|
13008
13100
|
init_config();
|
|
13009
13101
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -13012,13 +13104,13 @@ import {
|
|
|
13012
13104
|
cpSync,
|
|
13013
13105
|
existsSync as existsSync8,
|
|
13014
13106
|
mkdirSync as mkdirSync9,
|
|
13015
|
-
readFileSync as
|
|
13107
|
+
readFileSync as readFileSync8,
|
|
13016
13108
|
renameSync as renameSync7,
|
|
13017
13109
|
rmSync as rmSync7,
|
|
13018
13110
|
writeFileSync as writeFileSync5
|
|
13019
13111
|
} from "fs";
|
|
13020
13112
|
import { homedir as homedir7, userInfo as userInfo2 } from "os";
|
|
13021
|
-
import { dirname as dirname10, join as
|
|
13113
|
+
import { dirname as dirname10, join as join15, sep as sep3 } from "path";
|
|
13022
13114
|
|
|
13023
13115
|
// src/install-layout.ts
|
|
13024
13116
|
init_config();
|
|
@@ -13026,39 +13118,39 @@ import {
|
|
|
13026
13118
|
existsSync as existsSync6,
|
|
13027
13119
|
lstatSync,
|
|
13028
13120
|
mkdirSync as mkdirSync5,
|
|
13029
|
-
readFileSync as
|
|
13121
|
+
readFileSync as readFileSync5,
|
|
13030
13122
|
readdirSync,
|
|
13031
13123
|
realpathSync as realpathSync4,
|
|
13032
13124
|
renameSync as renameSync3,
|
|
13033
13125
|
rmSync as rmSync4,
|
|
13034
13126
|
symlinkSync
|
|
13035
13127
|
} from "fs";
|
|
13036
|
-
import { dirname as dirname6, join as
|
|
13128
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
13037
13129
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13038
13130
|
function versionsDir() {
|
|
13039
|
-
return
|
|
13131
|
+
return join11(cabaneDir(), "versions");
|
|
13040
13132
|
}
|
|
13041
13133
|
function currentLink() {
|
|
13042
|
-
return
|
|
13134
|
+
return join11(cabaneDir(), "current");
|
|
13043
13135
|
}
|
|
13044
13136
|
function bootGuardPath() {
|
|
13045
|
-
return
|
|
13137
|
+
return join11(cabaneDir(), "boot-guard.mjs");
|
|
13046
13138
|
}
|
|
13047
13139
|
function pinnedNodeDir() {
|
|
13048
|
-
return
|
|
13140
|
+
return join11(cabaneDir(), "node");
|
|
13049
13141
|
}
|
|
13050
13142
|
function pinnedNodePath() {
|
|
13051
|
-
return
|
|
13143
|
+
return join11(pinnedNodeDir(), "bin", "node");
|
|
13052
13144
|
}
|
|
13053
13145
|
function versionPackageRoot(version) {
|
|
13054
|
-
return
|
|
13146
|
+
return join11(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
13055
13147
|
}
|
|
13056
13148
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
13057
13149
|
return dirname6(dirname6(fileURLToPath4(moduleUrl)));
|
|
13058
13150
|
}
|
|
13059
13151
|
function packageVersion(root) {
|
|
13060
13152
|
try {
|
|
13061
|
-
const pkg2 = JSON.parse(
|
|
13153
|
+
const pkg2 = JSON.parse(readFileSync5(join11(root, "package.json"), "utf8"));
|
|
13062
13154
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
13063
13155
|
} catch {
|
|
13064
13156
|
return null;
|
|
@@ -13067,7 +13159,7 @@ function packageVersion(root) {
|
|
|
13067
13159
|
function currentRoot() {
|
|
13068
13160
|
try {
|
|
13069
13161
|
const root = realpathSync4(currentLink());
|
|
13070
|
-
return existsSync6(
|
|
13162
|
+
return existsSync6(join11(root, "dist", "cli.js")) ? root : null;
|
|
13071
13163
|
} catch {
|
|
13072
13164
|
return null;
|
|
13073
13165
|
}
|
|
@@ -13090,7 +13182,7 @@ function pruneVersions(keep) {
|
|
|
13090
13182
|
const removed = [];
|
|
13091
13183
|
for (const name of readdirSync(dir2)) {
|
|
13092
13184
|
if (keep.includes(name)) continue;
|
|
13093
|
-
rmSync4(
|
|
13185
|
+
rmSync4(join11(dir2, name), { recursive: true, force: true });
|
|
13094
13186
|
removed.push(name);
|
|
13095
13187
|
}
|
|
13096
13188
|
return removed;
|
|
@@ -13110,19 +13202,19 @@ function compareVersions(a, b) {
|
|
|
13110
13202
|
// src/updater.ts
|
|
13111
13203
|
import { spawn as spawn4 } from "child_process";
|
|
13112
13204
|
import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync8, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
|
|
13113
|
-
import { dirname as dirname9, join as
|
|
13205
|
+
import { dirname as dirname9, join as join14 } from "path";
|
|
13114
13206
|
|
|
13115
13207
|
// src/boot-rule.ts
|
|
13116
13208
|
import {
|
|
13117
13209
|
mkdirSync as mkdirSync6,
|
|
13118
|
-
readFileSync as
|
|
13210
|
+
readFileSync as readFileSync6,
|
|
13119
13211
|
realpathSync as realpathSync5,
|
|
13120
13212
|
renameSync as renameSync4,
|
|
13121
13213
|
rmSync as rmSync5,
|
|
13122
13214
|
symlinkSync as symlinkSync2,
|
|
13123
13215
|
writeFileSync as writeFileSync3
|
|
13124
13216
|
} from "fs";
|
|
13125
|
-
import { dirname as dirname7, join as
|
|
13217
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
13126
13218
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
13127
13219
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
13128
13220
|
function superseded(pending, ...running) {
|
|
@@ -13145,8 +13237,8 @@ function rolledBackState(state, pending, reason, now) {
|
|
|
13145
13237
|
|
|
13146
13238
|
// src/update-state.ts
|
|
13147
13239
|
init_config();
|
|
13148
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
13149
|
-
import { dirname as dirname8, join as
|
|
13240
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
13241
|
+
import { dirname as dirname8, join as join13 } from "path";
|
|
13150
13242
|
import { z as z3 } from "zod";
|
|
13151
13243
|
var resultSchema = z3.object({
|
|
13152
13244
|
kind: z3.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -13170,11 +13262,11 @@ var stateSchema = z3.object({
|
|
|
13170
13262
|
failed: z3.object({ version: z3.string(), attempts: z3.number().int(), at: z3.string() }).optional()
|
|
13171
13263
|
});
|
|
13172
13264
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13173
|
-
return
|
|
13265
|
+
return join13(dir2, "update.json");
|
|
13174
13266
|
}
|
|
13175
13267
|
function readUpdateState(path = updateStatePath()) {
|
|
13176
13268
|
try {
|
|
13177
|
-
const parsed = stateSchema.safeParse(JSON.parse(
|
|
13269
|
+
const parsed = stateSchema.safeParse(JSON.parse(readFileSync7(path, "utf8")));
|
|
13178
13270
|
return parsed.success ? parsed.data : {};
|
|
13179
13271
|
} catch {
|
|
13180
13272
|
return {};
|
|
@@ -13219,7 +13311,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
13219
13311
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
13220
13312
|
}
|
|
13221
13313
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
13222
|
-
const built =
|
|
13314
|
+
const built = join14(root, "dist", "boot-guard.js");
|
|
13223
13315
|
if (!existsSync7(built)) return false;
|
|
13224
13316
|
const target = bootGuardPath();
|
|
13225
13317
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
@@ -13259,17 +13351,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
13259
13351
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
13260
13352
|
const run = deps.run ?? runProcess;
|
|
13261
13353
|
const node = deps.execPath ?? process.execPath;
|
|
13262
|
-
const target =
|
|
13354
|
+
const target = join14(versionsDir(), version);
|
|
13263
13355
|
const placed = versionPackageRoot(version);
|
|
13264
13356
|
if (existsSync7(placed)) {
|
|
13265
13357
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
13266
13358
|
return { ok: true, root: placed };
|
|
13267
13359
|
}
|
|
13268
|
-
const npm =
|
|
13360
|
+
const npm = join14(dirname9(node), "npm");
|
|
13269
13361
|
if (!existsSync7(npm))
|
|
13270
13362
|
return { ok: false, reason: `there is no npm beside Node at ${dirname9(node)}` };
|
|
13271
13363
|
mkdirSync8(versionsDir(), { recursive: true });
|
|
13272
|
-
const staging =
|
|
13364
|
+
const staging = join14(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
13273
13365
|
rmSync6(staging, { recursive: true, force: true });
|
|
13274
13366
|
const installed = await run(
|
|
13275
13367
|
npm,
|
|
@@ -13298,7 +13390,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13298
13390
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
13299
13391
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
13300
13392
|
}
|
|
13301
|
-
const root =
|
|
13393
|
+
const root = join14(staging, "node_modules", "@cabane", "companion");
|
|
13302
13394
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
13303
13395
|
if (verdict) {
|
|
13304
13396
|
rmSync6(staging, { recursive: true, force: true });
|
|
@@ -13310,7 +13402,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13310
13402
|
}
|
|
13311
13403
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
13312
13404
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
13313
|
-
const answered = await deps.run(deps.execPath, [
|
|
13405
|
+
const answered = await deps.run(deps.execPath, [join14(root, "dist", "bin.js"), "--version"], {
|
|
13314
13406
|
timeoutMs: 6e4,
|
|
13315
13407
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
13316
13408
|
});
|
|
@@ -13318,7 +13410,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
13318
13410
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
13319
13411
|
}
|
|
13320
13412
|
if (harnesses.length === 0) return null;
|
|
13321
|
-
const probed = await deps.run(deps.execPath, [
|
|
13413
|
+
const probed = await deps.run(deps.execPath, [join14(root, "dist", "postinstall.js"), "--json"], {
|
|
13322
13414
|
timeoutMs: 6e4
|
|
13323
13415
|
});
|
|
13324
13416
|
let resolutions = {};
|
|
@@ -13606,15 +13698,15 @@ function serviceNode(execPath = process.execPath) {
|
|
|
13606
13698
|
return existsSync8(pinnedNodePath()) ? pinnedNodePath() : execPath;
|
|
13607
13699
|
}
|
|
13608
13700
|
function servicePaths(platform, env = process.env, home = homedir7(), node = serviceNode()) {
|
|
13609
|
-
const unitFile = platform === "systemd" ?
|
|
13610
|
-
env.XDG_CONFIG_HOME ||
|
|
13701
|
+
const unitFile = platform === "systemd" ? join15(
|
|
13702
|
+
env.XDG_CONFIG_HOME || join15(home, ".config"),
|
|
13611
13703
|
"systemd",
|
|
13612
13704
|
"user",
|
|
13613
13705
|
`${serviceName(env)}.service`
|
|
13614
|
-
) :
|
|
13706
|
+
) : join15(home, "Library", "LaunchAgents", `${launchdLabel(env)}.plist`);
|
|
13615
13707
|
return {
|
|
13616
13708
|
unitFile,
|
|
13617
|
-
envFile:
|
|
13709
|
+
envFile: join15(cabaneDir(), "service.env"),
|
|
13618
13710
|
node,
|
|
13619
13711
|
entry: bootGuardPath()
|
|
13620
13712
|
};
|
|
@@ -13681,7 +13773,7 @@ function renderLaunchdPlist(paths, env = process.env, servicePath) {
|
|
|
13681
13773
|
" <key>ThrottleInterval</key><integer>5</integer>",
|
|
13682
13774
|
// The companion writes its own log; the console copy would only grow a second one.
|
|
13683
13775
|
" <key>StandardOutPath</key><string>/dev/null</string>",
|
|
13684
|
-
` <key>StandardErrorPath</key><string>${xml(
|
|
13776
|
+
` <key>StandardErrorPath</key><string>${xml(join15(cabaneDir(), "service-error.log"))}</string>`,
|
|
13685
13777
|
"</dict>",
|
|
13686
13778
|
"</plist>",
|
|
13687
13779
|
""
|
|
@@ -13737,7 +13829,7 @@ async function installService(deps = {}) {
|
|
|
13737
13829
|
const paths = servicePaths(platform, env, deps.home, node);
|
|
13738
13830
|
const servicePath = await loginShellPath(run, env);
|
|
13739
13831
|
mkdirSync9(dirname10(paths.envFile), { recursive: true });
|
|
13740
|
-
const existing = existsSync8(paths.envFile) ?
|
|
13832
|
+
const existing = existsSync8(paths.envFile) ? readFileSync8(paths.envFile, "utf8") : "";
|
|
13741
13833
|
writeFileSync5(paths.envFile, withServicePath(existing, servicePath), { mode: 384 });
|
|
13742
13834
|
chmodSync3(paths.envFile, 384);
|
|
13743
13835
|
mkdirSync9(dirname10(paths.unitFile), { recursive: true });
|
|
@@ -13821,15 +13913,15 @@ async function seedManagedInstall(deps) {
|
|
|
13821
13913
|
}
|
|
13822
13914
|
const target = versionPackageRoot(version);
|
|
13823
13915
|
if (!existsSync8(target)) {
|
|
13824
|
-
const staging =
|
|
13916
|
+
const staging = join15(versionsDir(), `.${version}.seed-${process.pid}`);
|
|
13825
13917
|
rmSync7(staging, { recursive: true, force: true });
|
|
13826
13918
|
try {
|
|
13827
|
-
cpSync(own,
|
|
13919
|
+
cpSync(own, join15(staging, "node_modules", "@cabane", "companion"), {
|
|
13828
13920
|
recursive: true,
|
|
13829
13921
|
verbatimSymlinks: true
|
|
13830
13922
|
});
|
|
13831
|
-
rmSync7(
|
|
13832
|
-
renameSync7(staging,
|
|
13923
|
+
rmSync7(join15(versionsDir(), version), { recursive: true, force: true });
|
|
13924
|
+
renameSync7(staging, join15(versionsDir(), version));
|
|
13833
13925
|
} catch (err) {
|
|
13834
13926
|
rmSync7(staging, { recursive: true, force: true });
|
|
13835
13927
|
return {
|
|
@@ -13840,7 +13932,7 @@ async function seedManagedInstall(deps) {
|
|
|
13840
13932
|
}
|
|
13841
13933
|
const unrunnable = await verifyInstall(target, version, [], deps);
|
|
13842
13934
|
if (unrunnable) {
|
|
13843
|
-
rmSync7(
|
|
13935
|
+
rmSync7(join15(versionsDir(), version), { recursive: true, force: true });
|
|
13844
13936
|
return { ok: false, reason: `The copy of Cabane for the service can't run: ${unrunnable}.` };
|
|
13845
13937
|
}
|
|
13846
13938
|
pointCurrentAt(target);
|
|
@@ -13852,7 +13944,7 @@ function tildeDir() {
|
|
|
13852
13944
|
function unitNode(platform, unitFile) {
|
|
13853
13945
|
let text;
|
|
13854
13946
|
try {
|
|
13855
|
-
text =
|
|
13947
|
+
text = readFileSync8(unitFile, "utf8");
|
|
13856
13948
|
} catch {
|
|
13857
13949
|
return null;
|
|
13858
13950
|
}
|
|
@@ -13922,12 +14014,12 @@ function userName() {
|
|
|
13922
14014
|
return process.env.USER ?? "";
|
|
13923
14015
|
}
|
|
13924
14016
|
}
|
|
13925
|
-
function rewriteScriptWrappers(dir2 =
|
|
14017
|
+
function rewriteScriptWrappers(dir2 = join15(cabaneDir(), "bin")) {
|
|
13926
14018
|
for (const name of ["cabane", "cabane-companion"]) {
|
|
13927
|
-
const path =
|
|
14019
|
+
const path = join15(dir2, name);
|
|
13928
14020
|
let text;
|
|
13929
14021
|
try {
|
|
13930
|
-
text =
|
|
14022
|
+
text = readFileSync8(path, "utf8");
|
|
13931
14023
|
} catch {
|
|
13932
14024
|
continue;
|
|
13933
14025
|
}
|
|
@@ -14339,6 +14431,12 @@ async function setupScript(opts, deps) {
|
|
|
14339
14431
|
ui.outro(["Nothing left to set up."]);
|
|
14340
14432
|
return;
|
|
14341
14433
|
}
|
|
14434
|
+
const strays = (deps.strays ?? findStrayCompanions)(running ? [running.pid] : []);
|
|
14435
|
+
if (strays.length > 0) {
|
|
14436
|
+
ui.cancel(strayCompanionLines(strays, cabane("setup")).join("\n"));
|
|
14437
|
+
process.exitCode = 1;
|
|
14438
|
+
return;
|
|
14439
|
+
}
|
|
14342
14440
|
let justPaired = false;
|
|
14343
14441
|
if (!paired) {
|
|
14344
14442
|
const device = await (deps.pair ?? pairDevice)({
|
|
@@ -14497,41 +14595,52 @@ import {
|
|
|
14497
14595
|
chmodSync as chmodSync4,
|
|
14498
14596
|
existsSync as existsSync9,
|
|
14499
14597
|
mkdirSync as mkdirSync11,
|
|
14500
|
-
readFileSync as
|
|
14598
|
+
readFileSync as readFileSync9,
|
|
14501
14599
|
renameSync as renameSync8,
|
|
14502
14600
|
rmSync as rmSync8,
|
|
14503
14601
|
writeFileSync as writeFileSync6
|
|
14504
14602
|
} from "fs";
|
|
14505
|
-
import {
|
|
14603
|
+
import { join as join16 } from "path";
|
|
14506
14604
|
import { z as z4 } from "zod";
|
|
14507
|
-
|
|
14508
|
-
|
|
14605
|
+
var credentialMapSchema = z4.record(z4.string(), z4.string());
|
|
14606
|
+
function credentialsDir() {
|
|
14607
|
+
return join16(cabaneDir(), "credentials");
|
|
14509
14608
|
}
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14609
|
+
function legacyPath() {
|
|
14610
|
+
return join16(cabaneDir(), "credentials.json");
|
|
14611
|
+
}
|
|
14612
|
+
function devicePath(deviceId) {
|
|
14613
|
+
return join16(credentialsDir(), `${deviceId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
14614
|
+
}
|
|
14615
|
+
function read(path) {
|
|
14616
|
+
if (!existsSync9(path)) return null;
|
|
14514
14617
|
let raw;
|
|
14515
14618
|
try {
|
|
14516
|
-
raw =
|
|
14619
|
+
raw = readFileSync9(path, "utf8");
|
|
14517
14620
|
} catch {
|
|
14518
14621
|
return {};
|
|
14519
14622
|
}
|
|
14520
14623
|
if (raw.trim().length === 0) return {};
|
|
14521
14624
|
try {
|
|
14522
|
-
const result =
|
|
14625
|
+
const result = credentialMapSchema.safeParse(JSON.parse(raw));
|
|
14523
14626
|
return result.success ? result.data : {};
|
|
14524
14627
|
} catch {
|
|
14525
14628
|
return {};
|
|
14526
14629
|
}
|
|
14527
14630
|
}
|
|
14528
|
-
function
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14533
|
-
|
|
14631
|
+
function load(deviceId) {
|
|
14632
|
+
return read(devicePath(deviceId)) ?? read(legacyPath()) ?? {};
|
|
14633
|
+
}
|
|
14634
|
+
function save(deviceId, map) {
|
|
14635
|
+
const dir2 = credentialsDir();
|
|
14636
|
+
mkdirSync11(dir2, { recursive: true });
|
|
14637
|
+
for (const d of [cabaneDir(), dir2]) {
|
|
14638
|
+
try {
|
|
14639
|
+
chmodSync4(d, 448);
|
|
14640
|
+
} catch {
|
|
14641
|
+
}
|
|
14534
14642
|
}
|
|
14643
|
+
const path = devicePath(deviceId);
|
|
14535
14644
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
14536
14645
|
try {
|
|
14537
14646
|
writeFileSync6(tmp, JSON.stringify(map, null, 2) + "\n", { mode: 384 });
|
|
@@ -14548,31 +14657,28 @@ function save(map) {
|
|
|
14548
14657
|
throw err;
|
|
14549
14658
|
}
|
|
14550
14659
|
}
|
|
14551
|
-
function getCredential(agentId) {
|
|
14552
|
-
return load()[agentId] ?? null;
|
|
14660
|
+
function getCredential(deviceId, agentId) {
|
|
14661
|
+
return load(deviceId)[agentId] ?? null;
|
|
14553
14662
|
}
|
|
14554
|
-
function setCredential(agentId, pat) {
|
|
14555
|
-
|
|
14556
|
-
|
|
14663
|
+
function setCredential(deviceId, agentId, pat) {
|
|
14664
|
+
if (read(devicePath(deviceId))?.[agentId] === pat) return;
|
|
14665
|
+
const map = load(deviceId);
|
|
14557
14666
|
map[agentId] = pat;
|
|
14558
|
-
save(map);
|
|
14667
|
+
save(deviceId, map);
|
|
14559
14668
|
}
|
|
14560
|
-
function pruneCredentials(keepAgentIds) {
|
|
14561
|
-
const map = load();
|
|
14669
|
+
function pruneCredentials(deviceId, keepAgentIds) {
|
|
14562
14670
|
const keep = new Set(keepAgentIds);
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
}
|
|
14570
|
-
if (removed > 0) save(map);
|
|
14671
|
+
if (keep.size === 0) return [];
|
|
14672
|
+
const own = read(devicePath(deviceId));
|
|
14673
|
+
const map = own ?? load(deviceId);
|
|
14674
|
+
const removed = Object.keys(map).filter((id) => !keep.has(id));
|
|
14675
|
+
for (const id of removed) delete map[id];
|
|
14676
|
+
if (removed.length > 0 || own === null) save(deviceId, map);
|
|
14571
14677
|
return removed;
|
|
14572
14678
|
}
|
|
14573
14679
|
function clearCredentials() {
|
|
14574
|
-
|
|
14575
|
-
if (existsSync9(
|
|
14680
|
+
rmSync8(credentialsDir(), { recursive: true, force: true });
|
|
14681
|
+
if (existsSync9(legacyPath())) writeFileSync6(legacyPath(), "", { mode: 384 });
|
|
14576
14682
|
}
|
|
14577
14683
|
|
|
14578
14684
|
// src/commands/logout.ts
|
|
@@ -14628,7 +14734,7 @@ function writeCompletedPairing(raw) {
|
|
|
14628
14734
|
}
|
|
14629
14735
|
|
|
14630
14736
|
// src/cli.ts
|
|
14631
|
-
import { readFileSync as
|
|
14737
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
14632
14738
|
|
|
14633
14739
|
// src/commands/logs.ts
|
|
14634
14740
|
init_cabane_command();
|
|
@@ -14720,7 +14826,7 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
14720
14826
|
|
|
14721
14827
|
// src/dashboard/server.ts
|
|
14722
14828
|
init_errors();
|
|
14723
|
-
import { dirname as
|
|
14829
|
+
import { dirname as dirname11, join as join18 } from "path";
|
|
14724
14830
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
14725
14831
|
import { serve } from "@hono/node-server";
|
|
14726
14832
|
import { Hono } from "hono";
|
|
@@ -14728,7 +14834,7 @@ import { Hono } from "hono";
|
|
|
14728
14834
|
// src/dashboard/routes.ts
|
|
14729
14835
|
import { openSync as openSync4, readSync, closeSync as closeSync3, fstatSync, existsSync as existsSync10 } from "fs";
|
|
14730
14836
|
import { readFile } from "fs/promises";
|
|
14731
|
-
import { extname, join as
|
|
14837
|
+
import { extname, join as join17, normalize } from "path";
|
|
14732
14838
|
import { streamSSE } from "hono/streaming";
|
|
14733
14839
|
|
|
14734
14840
|
// src/state.ts
|
|
@@ -14977,13 +15083,13 @@ var CONTENT_TYPES = {
|
|
|
14977
15083
|
function registerRoutes(app, deps) {
|
|
14978
15084
|
const { supervisor, hub, staticDir } = deps;
|
|
14979
15085
|
app.get("/", async (c) => {
|
|
14980
|
-
const html = await readFile(
|
|
15086
|
+
const html = await readFile(join17(staticDir, "index.html"), "utf8");
|
|
14981
15087
|
return c.html(html);
|
|
14982
15088
|
});
|
|
14983
15089
|
app.get("/static/:file", async (c) => {
|
|
14984
15090
|
const file = c.req.param("file");
|
|
14985
15091
|
const safe2 = normalize(file).replace(/^(\.\.[/\\])+/, "");
|
|
14986
|
-
const full =
|
|
15092
|
+
const full = join17(staticDir, safe2);
|
|
14987
15093
|
if (!full.startsWith(staticDir) || !existsSync10(full)) return c.notFound();
|
|
14988
15094
|
const body = await readFile(full);
|
|
14989
15095
|
const type = CONTENT_TYPES[extname(full).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -15174,7 +15280,7 @@ function isAddrInUse(err) {
|
|
|
15174
15280
|
return Boolean(err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE");
|
|
15175
15281
|
}
|
|
15176
15282
|
function resolveStaticDir() {
|
|
15177
|
-
return
|
|
15283
|
+
return join18(dirname11(fileURLToPath5(import.meta.url)), "static");
|
|
15178
15284
|
}
|
|
15179
15285
|
|
|
15180
15286
|
// src/supervisor.ts
|
|
@@ -15341,20 +15447,20 @@ init_config();
|
|
|
15341
15447
|
|
|
15342
15448
|
// src/dispatch-dedupe.ts
|
|
15343
15449
|
init_config();
|
|
15344
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
15345
|
-
import { join as
|
|
15450
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync9, existsSync as existsSync11 } from "fs";
|
|
15451
|
+
import { join as join19 } from "path";
|
|
15346
15452
|
var MAX_IDS = 256;
|
|
15347
15453
|
function dir(log2) {
|
|
15348
|
-
return
|
|
15454
|
+
return join19(cabaneDir(), log2);
|
|
15349
15455
|
}
|
|
15350
15456
|
function pathFor(log2, workspaceId) {
|
|
15351
|
-
return
|
|
15457
|
+
return join19(dir(log2), encodeURIComponent(workspaceId));
|
|
15352
15458
|
}
|
|
15353
15459
|
function readIds(log2, workspaceId) {
|
|
15354
15460
|
const path = pathFor(log2, workspaceId);
|
|
15355
15461
|
if (!existsSync11(path)) return [];
|
|
15356
15462
|
try {
|
|
15357
|
-
return
|
|
15463
|
+
return readFileSync10(path, "utf8").split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
15358
15464
|
} catch {
|
|
15359
15465
|
return [];
|
|
15360
15466
|
}
|
|
@@ -15385,17 +15491,17 @@ function markCompleted(workspaceId, eventId) {
|
|
|
15385
15491
|
}
|
|
15386
15492
|
var MAX_RESUME_ATTEMPTS = 3;
|
|
15387
15493
|
function resumeDir() {
|
|
15388
|
-
return
|
|
15494
|
+
return join19(cabaneDir(), "resume-attempts");
|
|
15389
15495
|
}
|
|
15390
15496
|
function resumePathFor(workspaceId) {
|
|
15391
|
-
return
|
|
15497
|
+
return join19(resumeDir(), encodeURIComponent(workspaceId));
|
|
15392
15498
|
}
|
|
15393
15499
|
function readResumeCounts(workspaceId) {
|
|
15394
15500
|
const out = /* @__PURE__ */ new Map();
|
|
15395
15501
|
const path = resumePathFor(workspaceId);
|
|
15396
15502
|
if (!existsSync11(path)) return out;
|
|
15397
15503
|
try {
|
|
15398
|
-
for (const line of
|
|
15504
|
+
for (const line of readFileSync10(path, "utf8").split("\n")) {
|
|
15399
15505
|
const trimmed = line.trim();
|
|
15400
15506
|
if (!trimmed) continue;
|
|
15401
15507
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -15424,10 +15530,10 @@ function bumpResumeAttempt(workspaceId, eventId) {
|
|
|
15424
15530
|
return next;
|
|
15425
15531
|
}
|
|
15426
15532
|
function turnDir() {
|
|
15427
|
-
return
|
|
15533
|
+
return join19(cabaneDir(), "turns");
|
|
15428
15534
|
}
|
|
15429
15535
|
function turnPathFor(workspaceId) {
|
|
15430
|
-
return
|
|
15536
|
+
return join19(turnDir(), encodeURIComponent(workspaceId));
|
|
15431
15537
|
}
|
|
15432
15538
|
var TURN_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
15433
15539
|
function readTurnIds(workspaceId) {
|
|
@@ -15435,7 +15541,7 @@ function readTurnIds(workspaceId) {
|
|
|
15435
15541
|
const path = turnPathFor(workspaceId);
|
|
15436
15542
|
if (!existsSync11(path)) return out;
|
|
15437
15543
|
try {
|
|
15438
|
-
for (const line of
|
|
15544
|
+
for (const line of readFileSync10(path, "utf8").split("\n")) {
|
|
15439
15545
|
const trimmed = line.trim();
|
|
15440
15546
|
if (!trimmed) continue;
|
|
15441
15547
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -15655,13 +15761,13 @@ import {
|
|
|
15655
15761
|
existsSync as existsSync18,
|
|
15656
15762
|
mkdirSync as mkdirSync16,
|
|
15657
15763
|
readdirSync as readdirSync5,
|
|
15658
|
-
readFileSync as
|
|
15764
|
+
readFileSync as readFileSync15,
|
|
15659
15765
|
renameSync as renameSync11,
|
|
15660
15766
|
rmSync as rmSync13,
|
|
15661
15767
|
statSync as statSync4,
|
|
15662
15768
|
writeFileSync as writeFileSync9
|
|
15663
15769
|
} from "fs";
|
|
15664
|
-
import { join as
|
|
15770
|
+
import { join as join28 } from "path";
|
|
15665
15771
|
import { Writable } from "stream";
|
|
15666
15772
|
|
|
15667
15773
|
// src/providers/docker-cli.ts
|
|
@@ -15730,11 +15836,11 @@ import {
|
|
|
15730
15836
|
existsSync as existsSync16,
|
|
15731
15837
|
mkdirSync as mkdirSync15,
|
|
15732
15838
|
readdirSync as readdirSync4,
|
|
15733
|
-
readFileSync as
|
|
15839
|
+
readFileSync as readFileSync14,
|
|
15734
15840
|
renameSync as renameSync10,
|
|
15735
15841
|
rmSync as rmSync12
|
|
15736
15842
|
} from "fs";
|
|
15737
|
-
import { join as
|
|
15843
|
+
import { join as join26 } from "path";
|
|
15738
15844
|
function routes(t) {
|
|
15739
15845
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
15740
15846
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -15780,7 +15886,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15780
15886
|
const staged = readdirSync4(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
15781
15887
|
let entry = {};
|
|
15782
15888
|
try {
|
|
15783
|
-
entry = JSON.parse(
|
|
15889
|
+
entry = JSON.parse(readFileSync14(join26(from, name), "utf8"));
|
|
15784
15890
|
} catch {
|
|
15785
15891
|
}
|
|
15786
15892
|
return { name, entry };
|
|
@@ -15793,7 +15899,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15793
15899
|
let published = 0;
|
|
15794
15900
|
for (const { name } of staged) {
|
|
15795
15901
|
try {
|
|
15796
|
-
move(
|
|
15902
|
+
move(join26(from, name), join26(to, name));
|
|
15797
15903
|
} catch {
|
|
15798
15904
|
return { published, complete: false };
|
|
15799
15905
|
}
|
|
@@ -15806,7 +15912,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15806
15912
|
// src/providers/runner-base.ts
|
|
15807
15913
|
import { createHash as createHash4 } from "crypto";
|
|
15808
15914
|
import { existsSync as existsSync17, realpathSync as realpathSync6 } from "fs";
|
|
15809
|
-
import { dirname as
|
|
15915
|
+
import { dirname as dirname13, join as join27, sep as sep4 } from "path";
|
|
15810
15916
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15811
15917
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
15812
15918
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -15852,13 +15958,13 @@ async function ensureBaseImage(docker, image, log2) {
|
|
|
15852
15958
|
return true;
|
|
15853
15959
|
}
|
|
15854
15960
|
function ownInstall(moduleFile = fileURLToPath6(import.meta.url)) {
|
|
15855
|
-
let dir2 =
|
|
15961
|
+
let dir2 = dirname13(moduleFile);
|
|
15856
15962
|
for (let i = 0; i < 4; i++) {
|
|
15857
|
-
if (existsSync17(
|
|
15858
|
-
dir2 =
|
|
15963
|
+
if (existsSync17(join27(dir2, "dist", "cli.js")) && existsSync17(join27(dir2, "package.json"))) break;
|
|
15964
|
+
dir2 = dirname13(dir2);
|
|
15859
15965
|
}
|
|
15860
|
-
if (!existsSync17(
|
|
15861
|
-
const sdk =
|
|
15966
|
+
if (!existsSync17(join27(dir2, "dist", "cli.js"))) return null;
|
|
15967
|
+
const sdk = join27(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
15862
15968
|
if (!existsSync17(sdk)) return null;
|
|
15863
15969
|
const root = realpathSync6(dir2);
|
|
15864
15970
|
return realpathSync6(sdk).startsWith(root + sep4) ? root : null;
|
|
@@ -15997,39 +16103,39 @@ function networkCreateError(stderr) {
|
|
|
15997
16103
|
}
|
|
15998
16104
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
15999
16105
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
16000
|
-
return
|
|
16106
|
+
return join28(deviceDir, "attempts", attemptId, "session.tar");
|
|
16001
16107
|
}
|
|
16002
16108
|
function pruneOtherAttempts(deviceDir, pair2, keep) {
|
|
16003
|
-
const root =
|
|
16109
|
+
const root = join28(deviceDir, "attempts");
|
|
16004
16110
|
if (!existsSync18(root)) return;
|
|
16005
16111
|
for (const id of readdirSync5(root)) {
|
|
16006
16112
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
16007
16113
|
let pairOf = "";
|
|
16008
16114
|
try {
|
|
16009
|
-
pairOf =
|
|
16115
|
+
pairOf = readFileSync15(join28(root, id, "pair"), "utf8");
|
|
16010
16116
|
} catch {
|
|
16011
16117
|
continue;
|
|
16012
16118
|
}
|
|
16013
16119
|
if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
|
|
16014
|
-
rmSync13(
|
|
16120
|
+
rmSync13(join28(root, id), { recursive: true, force: true });
|
|
16015
16121
|
}
|
|
16016
16122
|
}
|
|
16017
16123
|
}
|
|
16018
16124
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
16019
16125
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
16020
|
-
const root =
|
|
16126
|
+
const root = join28(deviceDir, "attempts");
|
|
16021
16127
|
if (!existsSync18(root)) return [];
|
|
16022
16128
|
const removed = [];
|
|
16023
16129
|
for (const id of readdirSync5(root)) {
|
|
16024
16130
|
if (!UUID_RE.test(id)) continue;
|
|
16025
16131
|
let kept;
|
|
16026
16132
|
try {
|
|
16027
|
-
kept = statSync4(
|
|
16133
|
+
kept = statSync4(join28(root, id)).mtimeMs;
|
|
16028
16134
|
} catch {
|
|
16029
16135
|
continue;
|
|
16030
16136
|
}
|
|
16031
16137
|
if (now - kept < maxAgeMs) continue;
|
|
16032
|
-
rmSync13(
|
|
16138
|
+
rmSync13(join28(root, id), { recursive: true, force: true });
|
|
16033
16139
|
removed.push(id);
|
|
16034
16140
|
}
|
|
16035
16141
|
return removed;
|
|
@@ -16230,11 +16336,11 @@ function createDockerProvider(opts) {
|
|
|
16230
16336
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
16231
16337
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
16232
16338
|
};
|
|
16233
|
-
const socketRoot = opts.socketRoot ??
|
|
16339
|
+
const socketRoot = opts.socketRoot ?? join28(
|
|
16234
16340
|
process.env.XDG_RUNTIME_DIR && existsSync18(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
16235
16341
|
`cabane-runners-${opts.deviceKey}`
|
|
16236
16342
|
);
|
|
16237
|
-
const socketDir = (name) =>
|
|
16343
|
+
const socketDir = (name) => join28(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
|
|
16238
16344
|
const create = async (name, attempt) => {
|
|
16239
16345
|
mkdirSync16(socketRoot, { recursive: true, mode: 448 });
|
|
16240
16346
|
mkdirSync16(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -16376,10 +16482,10 @@ function createDockerProvider(opts) {
|
|
|
16376
16482
|
}
|
|
16377
16483
|
};
|
|
16378
16484
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
16379
|
-
const pendingPath = (name) =>
|
|
16485
|
+
const pendingPath = (name) => join28(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
16380
16486
|
const readPending = (name) => {
|
|
16381
16487
|
try {
|
|
16382
|
-
const ids = JSON.parse(
|
|
16488
|
+
const ids = JSON.parse(readFileSync15(pendingPath(name), "utf8"));
|
|
16383
16489
|
return Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : [];
|
|
16384
16490
|
} catch {
|
|
16385
16491
|
return [];
|
|
@@ -16390,12 +16496,12 @@ function createDockerProvider(opts) {
|
|
|
16390
16496
|
rmSync13(pendingPath(name), { force: true });
|
|
16391
16497
|
return;
|
|
16392
16498
|
}
|
|
16393
|
-
mkdirSync16(
|
|
16499
|
+
mkdirSync16(join28(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
16394
16500
|
writeFileSync9(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
16395
16501
|
};
|
|
16396
16502
|
const keepSession = async (name, attempt, attemptId) => {
|
|
16397
16503
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16398
|
-
mkdirSync16(
|
|
16504
|
+
mkdirSync16(join28(archive, ".."), { recursive: true });
|
|
16399
16505
|
const partial = `${archive}.${process.pid}.partial`;
|
|
16400
16506
|
const file = new CappedFile(partial, sessionCap);
|
|
16401
16507
|
const r = await execIn(
|
|
@@ -16412,7 +16518,7 @@ function createDockerProvider(opts) {
|
|
|
16412
16518
|
);
|
|
16413
16519
|
if (r.code === 0 && !file.over) {
|
|
16414
16520
|
renameSync11(partial, archive);
|
|
16415
|
-
writeFileSync9(
|
|
16521
|
+
writeFileSync9(join28(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
16416
16522
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
16417
16523
|
await writeMarker(name, attemptId);
|
|
16418
16524
|
log2.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -16426,7 +16532,7 @@ function createDockerProvider(opts) {
|
|
|
16426
16532
|
file.over ? "runners: the session files are over the size cap and weren't kept; the next new container starts the session over" : "runners: couldn't keep the session files; the next new container starts the session over"
|
|
16427
16533
|
);
|
|
16428
16534
|
};
|
|
16429
|
-
const stagingDir = (name) =>
|
|
16535
|
+
const stagingDir = (name) => join28(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
16430
16536
|
const ensureRunning = async (name) => {
|
|
16431
16537
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
16432
16538
|
if (seen.code !== 0) return false;
|
|
@@ -16434,7 +16540,7 @@ function createDockerProvider(opts) {
|
|
|
16434
16540
|
return (await docker.run(["start", name])).code === 0;
|
|
16435
16541
|
};
|
|
16436
16542
|
const dropArchive = (attemptId) => {
|
|
16437
|
-
rmSync13(
|
|
16543
|
+
rmSync13(join28(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
16438
16544
|
recursive: true,
|
|
16439
16545
|
force: true
|
|
16440
16546
|
});
|
|
@@ -16503,7 +16609,7 @@ function createDockerProvider(opts) {
|
|
|
16503
16609
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
16504
16610
|
await must(["start", name], "starting the container");
|
|
16505
16611
|
const git = await serveGitCredentials({
|
|
16506
|
-
socketPath:
|
|
16612
|
+
socketPath: join28(socketDir(name), "git.sock"),
|
|
16507
16613
|
hosts: opts.gitHosts,
|
|
16508
16614
|
fill: opts.gitFill,
|
|
16509
16615
|
log: log2
|
|
@@ -16652,15 +16758,15 @@ function createDockerProvider(opts) {
|
|
|
16652
16758
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
16653
16759
|
if (transcripts) {
|
|
16654
16760
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
16655
|
-
if (kept.length) mkdirSync16(
|
|
16656
|
-
for (const f of kept) writeFileSync9(
|
|
16761
|
+
if (kept.length) mkdirSync16(join28(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
16762
|
+
for (const f of kept) writeFileSync9(join28(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
16657
16763
|
await removeIn(
|
|
16658
16764
|
name,
|
|
16659
16765
|
`${CONTAINER_STATE}/transcripts`,
|
|
16660
16766
|
kept.map((f) => f.name)
|
|
16661
16767
|
);
|
|
16662
16768
|
}
|
|
16663
|
-
const outboxRel =
|
|
16769
|
+
const outboxRel = join28("outbox", encodeURIComponent(attempt.agentId));
|
|
16664
16770
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
16665
16771
|
let unpublished = false;
|
|
16666
16772
|
if (outbox) {
|
|
@@ -16686,7 +16792,7 @@ function createDockerProvider(opts) {
|
|
|
16686
16792
|
const staged = stagingDir(name);
|
|
16687
16793
|
mkdirSync16(staged, { recursive: true });
|
|
16688
16794
|
writeFileSync9(
|
|
16689
|
-
|
|
16795
|
+
join28(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
16690
16796
|
`${JSON.stringify(entry)}
|
|
16691
16797
|
`
|
|
16692
16798
|
);
|
|
@@ -16699,7 +16805,7 @@ function createDockerProvider(opts) {
|
|
|
16699
16805
|
if (outbox.complete) {
|
|
16700
16806
|
const published = publishStaged(
|
|
16701
16807
|
stagingDir(name),
|
|
16702
|
-
|
|
16808
|
+
join28(opts.hostStateDir, outboxRel),
|
|
16703
16809
|
opts.moveFile
|
|
16704
16810
|
);
|
|
16705
16811
|
queued = published.published;
|
|
@@ -17500,7 +17606,7 @@ var CompanionSupervisor = class {
|
|
|
17500
17606
|
saveConfig(this.config);
|
|
17501
17607
|
}
|
|
17502
17608
|
for (const it of items) {
|
|
17503
|
-
if (it.credential) setCredential(it.agentId, it.credential);
|
|
17609
|
+
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
17504
17610
|
}
|
|
17505
17611
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
17506
17612
|
for (const it of items) {
|
|
@@ -17511,8 +17617,17 @@ var CompanionSupervisor = class {
|
|
|
17511
17617
|
for (const wsId of [...this.workspaces.keys()]) {
|
|
17512
17618
|
if (!byWorkspace.has(wsId)) await this.removeWorkspace(wsId);
|
|
17513
17619
|
}
|
|
17514
|
-
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(wsId, list);
|
|
17515
|
-
pruneCredentials(
|
|
17620
|
+
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(device.id, wsId, list);
|
|
17621
|
+
const pruned = pruneCredentials(
|
|
17622
|
+
device.id,
|
|
17623
|
+
items.map((i) => i.agentId)
|
|
17624
|
+
);
|
|
17625
|
+
if (pruned.length > 0) {
|
|
17626
|
+
this.log.info(
|
|
17627
|
+
{ deviceId: device.id, agentIds: pruned },
|
|
17628
|
+
`Removed the saved sign-in of ${pruned.length === 1 ? "agent" : "agents"} ${pruned.join(", ")}: no longer assigned to this device (${device.id}).`
|
|
17629
|
+
);
|
|
17630
|
+
}
|
|
17516
17631
|
void this.recoverRuns();
|
|
17517
17632
|
} finally {
|
|
17518
17633
|
this.refreshing = false;
|
|
@@ -17526,7 +17641,7 @@ var CompanionSupervisor = class {
|
|
|
17526
17641
|
}
|
|
17527
17642
|
}
|
|
17528
17643
|
// ---- reconciliation ----
|
|
17529
|
-
reconcileWorkspace(workspaceId, list) {
|
|
17644
|
+
reconcileWorkspace(deviceId, workspaceId, list) {
|
|
17530
17645
|
const first = list[0];
|
|
17531
17646
|
let wr = this.workspaces.get(workspaceId);
|
|
17532
17647
|
if (!wr) {
|
|
@@ -17552,7 +17667,7 @@ var CompanionSupervisor = class {
|
|
|
17552
17667
|
if (!desired.has(agentId)) this.removeAgent(wr, agentId);
|
|
17553
17668
|
}
|
|
17554
17669
|
for (const it of list) {
|
|
17555
|
-
const credential = it.credential ?? getCredential(it.agentId);
|
|
17670
|
+
const credential = it.credential ?? getCredential(deviceId, it.agentId);
|
|
17556
17671
|
const runConfig = parseRunConfig(
|
|
17557
17672
|
it.runConfig,
|
|
17558
17673
|
(m) => this.log.warn(
|
|
@@ -18619,10 +18734,10 @@ function handleUncaught(log2, err, origin) {
|
|
|
18619
18734
|
|
|
18620
18735
|
// src/crash-marker.ts
|
|
18621
18736
|
init_config();
|
|
18622
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as
|
|
18623
|
-
import { join as
|
|
18737
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as readFileSync18, rmSync as rmSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
18738
|
+
import { join as join31 } from "path";
|
|
18624
18739
|
function crashMarkerPath() {
|
|
18625
|
-
return
|
|
18740
|
+
return join31(cabaneDir(), "last-error.json");
|
|
18626
18741
|
}
|
|
18627
18742
|
function recordCrash(rec2) {
|
|
18628
18743
|
try {
|
|
@@ -18665,6 +18780,8 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
18665
18780
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18666
18781
|
const pre = readLiveRuntimeState();
|
|
18667
18782
|
if (pre && await verifyRuntime(pre) === "stale") clearRuntimeState();
|
|
18783
|
+
const strays = findStrayCompanions(pre ? [pre.pid] : []);
|
|
18784
|
+
if (strays.length > 0) return { ok: false, reason: "stray-companion", strays };
|
|
18668
18785
|
const claim = acquireRuntimeState({
|
|
18669
18786
|
pid: process.pid,
|
|
18670
18787
|
startedAt,
|
|
@@ -18844,6 +18961,11 @@ async function startSupervised(opts, interactive, daemon) {
|
|
|
18844
18961
|
if (daemon) write(`Running in the background (pid ${process.pid}). Stop with ${cabane("stop")}.`);
|
|
18845
18962
|
else blank();
|
|
18846
18963
|
if (!isDevicePaired()) {
|
|
18964
|
+
const strays = findStrayCompanions();
|
|
18965
|
+
if (strays.length > 0) {
|
|
18966
|
+
refuse(strayCompanionLines(strays, cabane("start")));
|
|
18967
|
+
return;
|
|
18968
|
+
}
|
|
18847
18969
|
const paired = await pairDevice({
|
|
18848
18970
|
baseUrl: resolvePairBaseUrl(opts.server),
|
|
18849
18971
|
interactive,
|
|
@@ -18879,6 +19001,10 @@ async function bringUp2() {
|
|
|
18879
19001
|
// the middle of it would be the same news, worse.
|
|
18880
19002
|
onReadinessWarning: (message2) => log2.warn(message2)
|
|
18881
19003
|
});
|
|
19004
|
+
if (!result.ok && result.reason === "stray-companion") {
|
|
19005
|
+
refuse(strayCompanionLines(result.strays, cabane("start")));
|
|
19006
|
+
return null;
|
|
19007
|
+
}
|
|
18882
19008
|
if (!result.ok) {
|
|
18883
19009
|
for (const line of alreadyRunningLines(result.existing?.pid)) write(`${INDENT}${line}`);
|
|
18884
19010
|
return null;
|
|
@@ -18925,7 +19051,7 @@ init_install_kind();
|
|
|
18925
19051
|
init_cabane_command();
|
|
18926
19052
|
import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync21 } from "fs";
|
|
18927
19053
|
import { homedir as homedir9 } from "os";
|
|
18928
|
-
import { dirname as
|
|
19054
|
+
import { dirname as dirname15, join as join32 } from "path";
|
|
18929
19055
|
init_config();
|
|
18930
19056
|
init_sandbox_policy();
|
|
18931
19057
|
init_secrets();
|
|
@@ -18936,11 +19062,13 @@ var LABEL_WIDTH2 = 13;
|
|
|
18936
19062
|
function row2(label, value) {
|
|
18937
19063
|
return ` ${label.padEnd(LABEL_WIDTH2)}${value}`;
|
|
18938
19064
|
}
|
|
18939
|
-
async function status() {
|
|
19065
|
+
async function status(deps = {}) {
|
|
18940
19066
|
const out = (line) => void process.stdout.write(`${line}
|
|
18941
19067
|
`);
|
|
19068
|
+
const running = await liveState();
|
|
19069
|
+
const strays = strayStatusLines(running, deps.strays);
|
|
18942
19070
|
out(` ${versionLine()}`);
|
|
18943
|
-
for (const line of otherInstallLines(
|
|
19071
|
+
for (const line of otherInstallLines(running)) out(line);
|
|
18944
19072
|
let cfg = null;
|
|
18945
19073
|
try {
|
|
18946
19074
|
cfg = loadConfig();
|
|
@@ -18949,11 +19077,11 @@ async function status() {
|
|
|
18949
19077
|
}
|
|
18950
19078
|
if (!cfg || !cfg.deviceToken) {
|
|
18951
19079
|
out(` Not paired yet. Run ${cabane("setup")} to set up this device.`);
|
|
19080
|
+
for (const line of strays) out(line);
|
|
18952
19081
|
process.exitCode = 1;
|
|
18953
19082
|
return;
|
|
18954
19083
|
}
|
|
18955
19084
|
out(row2("Paired with", `${cfg.baseUrl}${cfg.deviceLabel ? ` as ${cfg.deviceLabel}` : ""}`));
|
|
18956
|
-
const running = await liveState();
|
|
18957
19085
|
let detection = null;
|
|
18958
19086
|
if (running) {
|
|
18959
19087
|
const mode = running.daemon ? "in the background" : "in a terminal";
|
|
@@ -18963,6 +19091,7 @@ async function status() {
|
|
|
18963
19091
|
} else {
|
|
18964
19092
|
out(row2("Running", `no \u2014 start it with ${cabane("start")}`));
|
|
18965
19093
|
}
|
|
19094
|
+
for (const line of strays) out(line);
|
|
18966
19095
|
const service2 = await serviceState();
|
|
18967
19096
|
for (const line of serviceRows(service2)) out(line);
|
|
18968
19097
|
for (const line of updateRows(readUpdateState(), cfg.update?.auto, {
|
|
@@ -18991,10 +19120,10 @@ function versionLine() {
|
|
|
18991
19120
|
const install = companionInstall();
|
|
18992
19121
|
const own = installedCommandPath({ install });
|
|
18993
19122
|
if (install === "script") {
|
|
18994
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(
|
|
19123
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join32(homedir9(), ".cabane"))})`;
|
|
18995
19124
|
}
|
|
18996
19125
|
if (own)
|
|
18997
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(
|
|
19126
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname15(dirname15(own)))})`;
|
|
18998
19127
|
return `Cabane ${COMPANION_VERSION}`;
|
|
18999
19128
|
}
|
|
19000
19129
|
function otherInstallLines(running) {
|
|
@@ -19017,7 +19146,7 @@ function otherInstall(install, own) {
|
|
|
19017
19146
|
}
|
|
19018
19147
|
};
|
|
19019
19148
|
if (install === "npm") {
|
|
19020
|
-
const script =
|
|
19149
|
+
const script = join32(
|
|
19021
19150
|
homedir9(),
|
|
19022
19151
|
".cabane",
|
|
19023
19152
|
"node",
|
|
@@ -19026,19 +19155,16 @@ function otherInstall(install, own) {
|
|
|
19026
19155
|
"@cabane",
|
|
19027
19156
|
"companion"
|
|
19028
19157
|
);
|
|
19029
|
-
return existsSync21(script) ?
|
|
19158
|
+
return existsSync21(script) ? join32(homedir9(), ".cabane") : null;
|
|
19030
19159
|
}
|
|
19031
19160
|
const found = firstOnPath("cabane-companion", process.env.PATH);
|
|
19032
19161
|
if (!found) return null;
|
|
19033
|
-
const ownAlias = own ? real(
|
|
19034
|
-
return real(found) === ownAlias ? null :
|
|
19162
|
+
const ownAlias = own ? real(join32(dirname15(own), "cabane-companion")) : null;
|
|
19163
|
+
return real(found) === ownAlias ? null : dirname15(found);
|
|
19035
19164
|
}
|
|
19036
19165
|
async function liveState() {
|
|
19037
|
-
const running = readLiveRuntimeState();
|
|
19038
|
-
if (running && await verifyRuntime(running) === "stale")
|
|
19039
|
-
clearRuntimeState();
|
|
19040
|
-
return null;
|
|
19041
|
-
}
|
|
19166
|
+
const running = readLiveRuntimeState({ sweep: false });
|
|
19167
|
+
if (running && await verifyRuntime(running) === "stale") return null;
|
|
19042
19168
|
return running;
|
|
19043
19169
|
}
|
|
19044
19170
|
async function runningDetection(running) {
|
|
@@ -19054,6 +19180,11 @@ async function runningDetection(running) {
|
|
|
19054
19180
|
return null;
|
|
19055
19181
|
}
|
|
19056
19182
|
}
|
|
19183
|
+
function strayStatusLines(running, find = findStrayCompanions) {
|
|
19184
|
+
const strays = find(running ? [running.pid] : []);
|
|
19185
|
+
if (strays.length === 0) return [];
|
|
19186
|
+
return strayCompanionLines(strays, cabane("status")).map((line) => ` ${line}`);
|
|
19187
|
+
}
|
|
19057
19188
|
async function agentLines2(cfg, running) {
|
|
19058
19189
|
const detection = running ?? await detectHarnesses(cfg);
|
|
19059
19190
|
return agentRows(detection, { connected: false }).map((agent) => row2(agent.label, agent.text));
|
|
@@ -19254,8 +19385,8 @@ init_cabane_command();
|
|
|
19254
19385
|
init_errors();
|
|
19255
19386
|
init_src();
|
|
19256
19387
|
init_transcript_writer();
|
|
19257
|
-
import { existsSync as existsSync22, readFileSync as
|
|
19258
|
-
import { isAbsolute, join as
|
|
19388
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "fs";
|
|
19389
|
+
import { isAbsolute, join as join33 } from "path";
|
|
19259
19390
|
async function transcript(opts = {}) {
|
|
19260
19391
|
const dir2 = transcriptsDir();
|
|
19261
19392
|
if (opts.follow) {
|
|
@@ -19272,7 +19403,7 @@ async function transcript(opts = {}) {
|
|
|
19272
19403
|
process.stdout.write(emptyMessage(dir2));
|
|
19273
19404
|
return;
|
|
19274
19405
|
}
|
|
19275
|
-
process.stdout.write(renderFile(
|
|
19406
|
+
process.stdout.write(renderFile(join33(dir2, newest)) + "\n");
|
|
19276
19407
|
return;
|
|
19277
19408
|
}
|
|
19278
19409
|
printList(dir2);
|
|
@@ -19355,7 +19486,7 @@ function isComplete(content) {
|
|
|
19355
19486
|
async function followTranscripts(dir2) {
|
|
19356
19487
|
const follower = new TranscriptFollower({
|
|
19357
19488
|
listFiles: () => listFiles(dir2),
|
|
19358
|
-
read: (f) =>
|
|
19489
|
+
read: (f) => readFileSync19(join33(dir2, f), "utf8"),
|
|
19359
19490
|
write: (s) => process.stdout.write(s),
|
|
19360
19491
|
// CSI: cursor up `n` lines, then erase from cursor to end of screen.
|
|
19361
19492
|
clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
|
|
@@ -19395,7 +19526,7 @@ function printList(dir2) {
|
|
|
19395
19526
|
|
|
19396
19527
|
`);
|
|
19397
19528
|
for (const f of files.slice(0, 20)) {
|
|
19398
|
-
const { meta, outcome } = peek(
|
|
19529
|
+
const { meta, outcome } = peek(join33(dir2, f));
|
|
19399
19530
|
const when = fmtTime(rec(meta)?.ts);
|
|
19400
19531
|
const ws = str2(rec(meta)?.workspaceSlug);
|
|
19401
19532
|
const o = rec(outcome);
|
|
@@ -19417,7 +19548,7 @@ function peek(path) {
|
|
|
19417
19548
|
let meta;
|
|
19418
19549
|
let outcome;
|
|
19419
19550
|
try {
|
|
19420
|
-
for (const line of
|
|
19551
|
+
for (const line of readFileSync19(path, "utf8").split("\n")) {
|
|
19421
19552
|
if (!line.trim()) continue;
|
|
19422
19553
|
const o = safeParse(line);
|
|
19423
19554
|
const t = str2(rec(o)?.type);
|
|
@@ -19433,10 +19564,10 @@ function resolveTarget(dir2, target) {
|
|
|
19433
19564
|
if (existsSync22(target)) return target;
|
|
19434
19565
|
throw new CompanionError(`no transcript at ${target}.`);
|
|
19435
19566
|
}
|
|
19436
|
-
const exact =
|
|
19567
|
+
const exact = join33(dir2, target);
|
|
19437
19568
|
if (existsSync22(exact)) return exact;
|
|
19438
19569
|
const matches = listFiles(dir2).filter((f) => f.includes(target));
|
|
19439
|
-
if (matches.length === 1) return
|
|
19570
|
+
if (matches.length === 1) return join33(dir2, matches[0]);
|
|
19440
19571
|
if (matches.length === 0) {
|
|
19441
19572
|
throw new CompanionError(
|
|
19442
19573
|
`no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`
|
|
@@ -19450,7 +19581,7 @@ function resolveTarget(dir2, target) {
|
|
|
19450
19581
|
function renderFile(path) {
|
|
19451
19582
|
let content;
|
|
19452
19583
|
try {
|
|
19453
|
-
content =
|
|
19584
|
+
content = readFileSync19(path, "utf8");
|
|
19454
19585
|
} catch (err) {
|
|
19455
19586
|
throw new CompanionError(
|
|
19456
19587
|
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -19628,7 +19759,7 @@ program.command("pair", { hidden: true }).allowExcessArguments(false).option("--
|
|
|
19628
19759
|
});
|
|
19629
19760
|
});
|
|
19630
19761
|
program.command("write-paired-config", { hidden: true }).description("persist an already-completed device enrollment payload from stdin.").action(() => {
|
|
19631
|
-
writeCompletedPairing(
|
|
19762
|
+
writeCompletedPairing(readFileSync20(0, "utf8"));
|
|
19632
19763
|
});
|
|
19633
19764
|
program.command("start").description("run Cabane on this device in the background.").option("--foreground", "run attached in this terminal (for terminals and supervisors)").option("--daemon", "run in the background (the default; kept for compatibility)").addOption(new Option("--server <url>").hideHelp()).addOption(
|
|
19634
19765
|
new Option("--log-level <level>", "info (default), warn, or debug").choices([
|