@cabane/companion 0.6.161 → 0.6.163
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 +367 -232
- package/dist/runtime.js +188 -68
- 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
|
};
|
|
@@ -13004,6 +13004,96 @@ function plainUi() {
|
|
|
13004
13004
|
};
|
|
13005
13005
|
}
|
|
13006
13006
|
|
|
13007
|
+
// src/stray-companion.ts
|
|
13008
|
+
init_config();
|
|
13009
|
+
import { spawnSync } from "child_process";
|
|
13010
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
13011
|
+
import { join as join10 } from "path";
|
|
13012
|
+
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
13013
|
+
var START = /\sstart(\s|$)/;
|
|
13014
|
+
function companionStarts(psOutput) {
|
|
13015
|
+
const found = [];
|
|
13016
|
+
for (const line of psOutput.split("\n")) {
|
|
13017
|
+
const match = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
13018
|
+
if (!match) continue;
|
|
13019
|
+
const command = match[2];
|
|
13020
|
+
if (COMPANION_ENTRY.test(command) && START.test(command)) {
|
|
13021
|
+
found.push({ pid: Number(match[1]), command });
|
|
13022
|
+
}
|
|
13023
|
+
}
|
|
13024
|
+
return found;
|
|
13025
|
+
}
|
|
13026
|
+
function stateDirOf(env) {
|
|
13027
|
+
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
13028
|
+
return home ? join10(home, ".cabane") : null;
|
|
13029
|
+
}
|
|
13030
|
+
function envFromPsE(output, command) {
|
|
13031
|
+
const line = output.replace(/\n$/, "");
|
|
13032
|
+
if (!line.startsWith(command)) return {};
|
|
13033
|
+
const env = {};
|
|
13034
|
+
for (const entry of line.slice(command.length).trim().split(/ (?=[A-Za-z_][A-Za-z0-9_]*=)/)) {
|
|
13035
|
+
const eq = entry.indexOf("=");
|
|
13036
|
+
if (eq > 0) env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
13037
|
+
}
|
|
13038
|
+
return env;
|
|
13039
|
+
}
|
|
13040
|
+
function envFromProc(pid) {
|
|
13041
|
+
try {
|
|
13042
|
+
const env = {};
|
|
13043
|
+
for (const entry of readFileSync4(`/proc/${pid}/environ`, "utf8").split("\0")) {
|
|
13044
|
+
const eq = entry.indexOf("=");
|
|
13045
|
+
if (eq > 0) env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
13046
|
+
}
|
|
13047
|
+
return env;
|
|
13048
|
+
} catch {
|
|
13049
|
+
return null;
|
|
13050
|
+
}
|
|
13051
|
+
}
|
|
13052
|
+
function envOf(pid, command) {
|
|
13053
|
+
if (process.platform === "linux") return envFromProc(pid);
|
|
13054
|
+
const result = spawnSync("ps", ["-E", "-ww", "-o", "command=", "-p", String(pid)], {
|
|
13055
|
+
encoding: "utf8",
|
|
13056
|
+
timeout: 5e3
|
|
13057
|
+
});
|
|
13058
|
+
if (result.error || result.status !== 0) return null;
|
|
13059
|
+
return envFromPsE(result.stdout, command);
|
|
13060
|
+
}
|
|
13061
|
+
function findStrayCompanions(exclude = [], deps = {}) {
|
|
13062
|
+
if (!deps.ps && (process.platform === "win32" || typeof process.getuid !== "function")) {
|
|
13063
|
+
return [];
|
|
13064
|
+
}
|
|
13065
|
+
const ps = deps.ps ?? (() => {
|
|
13066
|
+
const result = spawnSync("ps", ["-U", String(process.getuid()), "-o", "pid=,command="], {
|
|
13067
|
+
encoding: "utf8",
|
|
13068
|
+
timeout: 5e3,
|
|
13069
|
+
maxBuffer: 16 * 1024 * 1024
|
|
13070
|
+
});
|
|
13071
|
+
return result.error || typeof result.stdout !== "string" ? null : result.stdout;
|
|
13072
|
+
});
|
|
13073
|
+
const listing = ps();
|
|
13074
|
+
if (!listing) return [];
|
|
13075
|
+
const dir2 = deps.dir ?? cabaneDir();
|
|
13076
|
+
const skip = /* @__PURE__ */ new Set([...deps.self ?? [process.pid, process.ppid], ...exclude]);
|
|
13077
|
+
return companionStarts(listing).filter((c) => {
|
|
13078
|
+
if (skip.has(c.pid)) return false;
|
|
13079
|
+
const env = (deps.env ?? envOf)(c.pid, c.command);
|
|
13080
|
+
return env !== null && stateDirOf(env) === dir2;
|
|
13081
|
+
});
|
|
13082
|
+
}
|
|
13083
|
+
var SERVICE_ENTRY = /\.cabane\/boot-guard/;
|
|
13084
|
+
function strayCompanionLines(strays, retry) {
|
|
13085
|
+
const one = strays.length === 1;
|
|
13086
|
+
return [
|
|
13087
|
+
`Another Cabane companion is running for this computer's ~/.cabane, and ~/.cabane has no record of it:`,
|
|
13088
|
+
...strays.map((s) => ` pid ${s.pid} ${s.command}`),
|
|
13089
|
+
`Two companions for one ~/.cabane sign each other's agents out. Stop ${one ? "it" : "them"} first:`,
|
|
13090
|
+
...strays.map(
|
|
13091
|
+
(s) => SERVICE_ENTRY.test(s.command) ? ` pid ${s.pid} is a service, which restarts if killed: cabane service uninstall` : ` kill ${s.pid}`
|
|
13092
|
+
),
|
|
13093
|
+
`then run ${retry} again.`
|
|
13094
|
+
];
|
|
13095
|
+
}
|
|
13096
|
+
|
|
13007
13097
|
// src/service.ts
|
|
13008
13098
|
init_config();
|
|
13009
13099
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -13012,13 +13102,13 @@ import {
|
|
|
13012
13102
|
cpSync,
|
|
13013
13103
|
existsSync as existsSync8,
|
|
13014
13104
|
mkdirSync as mkdirSync9,
|
|
13015
|
-
readFileSync as
|
|
13105
|
+
readFileSync as readFileSync8,
|
|
13016
13106
|
renameSync as renameSync7,
|
|
13017
13107
|
rmSync as rmSync7,
|
|
13018
13108
|
writeFileSync as writeFileSync5
|
|
13019
13109
|
} from "fs";
|
|
13020
13110
|
import { homedir as homedir7, userInfo as userInfo2 } from "os";
|
|
13021
|
-
import { dirname as dirname10, join as
|
|
13111
|
+
import { dirname as dirname10, join as join15, sep as sep3 } from "path";
|
|
13022
13112
|
|
|
13023
13113
|
// src/install-layout.ts
|
|
13024
13114
|
init_config();
|
|
@@ -13026,39 +13116,39 @@ import {
|
|
|
13026
13116
|
existsSync as existsSync6,
|
|
13027
13117
|
lstatSync,
|
|
13028
13118
|
mkdirSync as mkdirSync5,
|
|
13029
|
-
readFileSync as
|
|
13119
|
+
readFileSync as readFileSync5,
|
|
13030
13120
|
readdirSync,
|
|
13031
13121
|
realpathSync as realpathSync4,
|
|
13032
13122
|
renameSync as renameSync3,
|
|
13033
13123
|
rmSync as rmSync4,
|
|
13034
13124
|
symlinkSync
|
|
13035
13125
|
} from "fs";
|
|
13036
|
-
import { dirname as dirname6, join as
|
|
13126
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
13037
13127
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13038
13128
|
function versionsDir() {
|
|
13039
|
-
return
|
|
13129
|
+
return join11(cabaneDir(), "versions");
|
|
13040
13130
|
}
|
|
13041
13131
|
function currentLink() {
|
|
13042
|
-
return
|
|
13132
|
+
return join11(cabaneDir(), "current");
|
|
13043
13133
|
}
|
|
13044
13134
|
function bootGuardPath() {
|
|
13045
|
-
return
|
|
13135
|
+
return join11(cabaneDir(), "boot-guard.mjs");
|
|
13046
13136
|
}
|
|
13047
13137
|
function pinnedNodeDir() {
|
|
13048
|
-
return
|
|
13138
|
+
return join11(cabaneDir(), "node");
|
|
13049
13139
|
}
|
|
13050
13140
|
function pinnedNodePath() {
|
|
13051
|
-
return
|
|
13141
|
+
return join11(pinnedNodeDir(), "bin", "node");
|
|
13052
13142
|
}
|
|
13053
13143
|
function versionPackageRoot(version) {
|
|
13054
|
-
return
|
|
13144
|
+
return join11(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
13055
13145
|
}
|
|
13056
13146
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
13057
13147
|
return dirname6(dirname6(fileURLToPath4(moduleUrl)));
|
|
13058
13148
|
}
|
|
13059
13149
|
function packageVersion(root) {
|
|
13060
13150
|
try {
|
|
13061
|
-
const pkg2 = JSON.parse(
|
|
13151
|
+
const pkg2 = JSON.parse(readFileSync5(join11(root, "package.json"), "utf8"));
|
|
13062
13152
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
13063
13153
|
} catch {
|
|
13064
13154
|
return null;
|
|
@@ -13067,7 +13157,7 @@ function packageVersion(root) {
|
|
|
13067
13157
|
function currentRoot() {
|
|
13068
13158
|
try {
|
|
13069
13159
|
const root = realpathSync4(currentLink());
|
|
13070
|
-
return existsSync6(
|
|
13160
|
+
return existsSync6(join11(root, "dist", "cli.js")) ? root : null;
|
|
13071
13161
|
} catch {
|
|
13072
13162
|
return null;
|
|
13073
13163
|
}
|
|
@@ -13090,7 +13180,7 @@ function pruneVersions(keep) {
|
|
|
13090
13180
|
const removed = [];
|
|
13091
13181
|
for (const name of readdirSync(dir2)) {
|
|
13092
13182
|
if (keep.includes(name)) continue;
|
|
13093
|
-
rmSync4(
|
|
13183
|
+
rmSync4(join11(dir2, name), { recursive: true, force: true });
|
|
13094
13184
|
removed.push(name);
|
|
13095
13185
|
}
|
|
13096
13186
|
return removed;
|
|
@@ -13110,19 +13200,19 @@ function compareVersions(a, b) {
|
|
|
13110
13200
|
// src/updater.ts
|
|
13111
13201
|
import { spawn as spawn4 } from "child_process";
|
|
13112
13202
|
import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync8, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
|
|
13113
|
-
import { dirname as dirname9, join as
|
|
13203
|
+
import { dirname as dirname9, join as join14 } from "path";
|
|
13114
13204
|
|
|
13115
13205
|
// src/boot-rule.ts
|
|
13116
13206
|
import {
|
|
13117
13207
|
mkdirSync as mkdirSync6,
|
|
13118
|
-
readFileSync as
|
|
13208
|
+
readFileSync as readFileSync6,
|
|
13119
13209
|
realpathSync as realpathSync5,
|
|
13120
13210
|
renameSync as renameSync4,
|
|
13121
13211
|
rmSync as rmSync5,
|
|
13122
13212
|
symlinkSync as symlinkSync2,
|
|
13123
13213
|
writeFileSync as writeFileSync3
|
|
13124
13214
|
} from "fs";
|
|
13125
|
-
import { dirname as dirname7, join as
|
|
13215
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
13126
13216
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
13127
13217
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
13128
13218
|
function superseded(pending, ...running) {
|
|
@@ -13145,8 +13235,8 @@ function rolledBackState(state, pending, reason, now) {
|
|
|
13145
13235
|
|
|
13146
13236
|
// src/update-state.ts
|
|
13147
13237
|
init_config();
|
|
13148
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
13149
|
-
import { dirname as dirname8, join as
|
|
13238
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
13239
|
+
import { dirname as dirname8, join as join13 } from "path";
|
|
13150
13240
|
import { z as z3 } from "zod";
|
|
13151
13241
|
var resultSchema = z3.object({
|
|
13152
13242
|
kind: z3.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -13170,11 +13260,11 @@ var stateSchema = z3.object({
|
|
|
13170
13260
|
failed: z3.object({ version: z3.string(), attempts: z3.number().int(), at: z3.string() }).optional()
|
|
13171
13261
|
});
|
|
13172
13262
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13173
|
-
return
|
|
13263
|
+
return join13(dir2, "update.json");
|
|
13174
13264
|
}
|
|
13175
13265
|
function readUpdateState(path = updateStatePath()) {
|
|
13176
13266
|
try {
|
|
13177
|
-
const parsed = stateSchema.safeParse(JSON.parse(
|
|
13267
|
+
const parsed = stateSchema.safeParse(JSON.parse(readFileSync7(path, "utf8")));
|
|
13178
13268
|
return parsed.success ? parsed.data : {};
|
|
13179
13269
|
} catch {
|
|
13180
13270
|
return {};
|
|
@@ -13219,7 +13309,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
13219
13309
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
13220
13310
|
}
|
|
13221
13311
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
13222
|
-
const built =
|
|
13312
|
+
const built = join14(root, "dist", "boot-guard.js");
|
|
13223
13313
|
if (!existsSync7(built)) return false;
|
|
13224
13314
|
const target = bootGuardPath();
|
|
13225
13315
|
mkdirSync8(dirname9(target), { recursive: true });
|
|
@@ -13259,17 +13349,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve2) => {
|
|
|
13259
13349
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
13260
13350
|
const run = deps.run ?? runProcess;
|
|
13261
13351
|
const node = deps.execPath ?? process.execPath;
|
|
13262
|
-
const target =
|
|
13352
|
+
const target = join14(versionsDir(), version);
|
|
13263
13353
|
const placed = versionPackageRoot(version);
|
|
13264
13354
|
if (existsSync7(placed)) {
|
|
13265
13355
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
13266
13356
|
return { ok: true, root: placed };
|
|
13267
13357
|
}
|
|
13268
|
-
const npm =
|
|
13358
|
+
const npm = join14(dirname9(node), "npm");
|
|
13269
13359
|
if (!existsSync7(npm))
|
|
13270
13360
|
return { ok: false, reason: `there is no npm beside Node at ${dirname9(node)}` };
|
|
13271
13361
|
mkdirSync8(versionsDir(), { recursive: true });
|
|
13272
|
-
const staging =
|
|
13362
|
+
const staging = join14(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
13273
13363
|
rmSync6(staging, { recursive: true, force: true });
|
|
13274
13364
|
const installed = await run(
|
|
13275
13365
|
npm,
|
|
@@ -13298,7 +13388,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13298
13388
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
13299
13389
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
13300
13390
|
}
|
|
13301
|
-
const root =
|
|
13391
|
+
const root = join14(staging, "node_modules", "@cabane", "companion");
|
|
13302
13392
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
13303
13393
|
if (verdict) {
|
|
13304
13394
|
rmSync6(staging, { recursive: true, force: true });
|
|
@@ -13310,7 +13400,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
13310
13400
|
}
|
|
13311
13401
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
13312
13402
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
13313
|
-
const answered = await deps.run(deps.execPath, [
|
|
13403
|
+
const answered = await deps.run(deps.execPath, [join14(root, "dist", "bin.js"), "--version"], {
|
|
13314
13404
|
timeoutMs: 6e4,
|
|
13315
13405
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
13316
13406
|
});
|
|
@@ -13318,7 +13408,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
13318
13408
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
13319
13409
|
}
|
|
13320
13410
|
if (harnesses.length === 0) return null;
|
|
13321
|
-
const probed = await deps.run(deps.execPath, [
|
|
13411
|
+
const probed = await deps.run(deps.execPath, [join14(root, "dist", "postinstall.js"), "--json"], {
|
|
13322
13412
|
timeoutMs: 6e4
|
|
13323
13413
|
});
|
|
13324
13414
|
let resolutions = {};
|
|
@@ -13486,7 +13576,12 @@ var CompanionUpdater = class {
|
|
|
13486
13576
|
const stage = this.deps.stage ?? stageVersion;
|
|
13487
13577
|
this.staging = (async () => {
|
|
13488
13578
|
this.deps.log.info({ version }, `Installing Cabane ${version} beside this one`);
|
|
13489
|
-
|
|
13579
|
+
let outcome;
|
|
13580
|
+
try {
|
|
13581
|
+
outcome = await stage(version, this.deps.harnesses());
|
|
13582
|
+
} catch (err) {
|
|
13583
|
+
outcome = { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
13584
|
+
}
|
|
13490
13585
|
if (outcome.ok) {
|
|
13491
13586
|
this.ready = version;
|
|
13492
13587
|
this.readyAt = this.now();
|
|
@@ -13526,11 +13621,16 @@ var CompanionUpdater = class {
|
|
|
13526
13621
|
})().finally(() => {
|
|
13527
13622
|
this.staging = null;
|
|
13528
13623
|
});
|
|
13529
|
-
|
|
13624
|
+
this.staging.then(() => this.trySwap()).catch(
|
|
13625
|
+
(err) => this.deps.log.warn(
|
|
13626
|
+
{ version, err: err instanceof Error ? err.message : String(err) },
|
|
13627
|
+
`Couldn't update to Cabane ${version}; trying again on a later check-in`
|
|
13628
|
+
)
|
|
13629
|
+
);
|
|
13530
13630
|
}
|
|
13531
13631
|
// Swap into the ready version: when nothing is running, when it has waited `SWAP_WAIT_MS` for
|
|
13532
13632
|
// that, when this version is below the server's minimum, or `now` (the person asked). Called on
|
|
13533
|
-
// each heartbeat until it does.
|
|
13633
|
+
// each heartbeat until it does, and only while auto-update is on unless the person asked.
|
|
13534
13634
|
async trySwap(now = false) {
|
|
13535
13635
|
const to = this.ready;
|
|
13536
13636
|
if (!to || this.swapping) return;
|
|
@@ -13540,6 +13640,7 @@ var CompanionUpdater = class {
|
|
|
13540
13640
|
this.deps.log.info({ version: to }, `Cabane ${to} is installed; it runs from the next start`);
|
|
13541
13641
|
return;
|
|
13542
13642
|
}
|
|
13643
|
+
if (!now && !this.updatesItself()) return;
|
|
13543
13644
|
if (!now && !this.deps.isIdle() && !this.waitedLongEnough() && !this.belowMinimum()) return;
|
|
13544
13645
|
this.swapping = true;
|
|
13545
13646
|
try {
|
|
@@ -13595,15 +13696,15 @@ function serviceNode(execPath = process.execPath) {
|
|
|
13595
13696
|
return existsSync8(pinnedNodePath()) ? pinnedNodePath() : execPath;
|
|
13596
13697
|
}
|
|
13597
13698
|
function servicePaths(platform, env = process.env, home = homedir7(), node = serviceNode()) {
|
|
13598
|
-
const unitFile = platform === "systemd" ?
|
|
13599
|
-
env.XDG_CONFIG_HOME ||
|
|
13699
|
+
const unitFile = platform === "systemd" ? join15(
|
|
13700
|
+
env.XDG_CONFIG_HOME || join15(home, ".config"),
|
|
13600
13701
|
"systemd",
|
|
13601
13702
|
"user",
|
|
13602
13703
|
`${serviceName(env)}.service`
|
|
13603
|
-
) :
|
|
13704
|
+
) : join15(home, "Library", "LaunchAgents", `${launchdLabel(env)}.plist`);
|
|
13604
13705
|
return {
|
|
13605
13706
|
unitFile,
|
|
13606
|
-
envFile:
|
|
13707
|
+
envFile: join15(cabaneDir(), "service.env"),
|
|
13607
13708
|
node,
|
|
13608
13709
|
entry: bootGuardPath()
|
|
13609
13710
|
};
|
|
@@ -13670,7 +13771,7 @@ function renderLaunchdPlist(paths, env = process.env, servicePath) {
|
|
|
13670
13771
|
" <key>ThrottleInterval</key><integer>5</integer>",
|
|
13671
13772
|
// The companion writes its own log; the console copy would only grow a second one.
|
|
13672
13773
|
" <key>StandardOutPath</key><string>/dev/null</string>",
|
|
13673
|
-
` <key>StandardErrorPath</key><string>${xml(
|
|
13774
|
+
` <key>StandardErrorPath</key><string>${xml(join15(cabaneDir(), "service-error.log"))}</string>`,
|
|
13674
13775
|
"</dict>",
|
|
13675
13776
|
"</plist>",
|
|
13676
13777
|
""
|
|
@@ -13726,7 +13827,7 @@ async function installService(deps = {}) {
|
|
|
13726
13827
|
const paths = servicePaths(platform, env, deps.home, node);
|
|
13727
13828
|
const servicePath = await loginShellPath(run, env);
|
|
13728
13829
|
mkdirSync9(dirname10(paths.envFile), { recursive: true });
|
|
13729
|
-
const existing = existsSync8(paths.envFile) ?
|
|
13830
|
+
const existing = existsSync8(paths.envFile) ? readFileSync8(paths.envFile, "utf8") : "";
|
|
13730
13831
|
writeFileSync5(paths.envFile, withServicePath(existing, servicePath), { mode: 384 });
|
|
13731
13832
|
chmodSync3(paths.envFile, 384);
|
|
13732
13833
|
mkdirSync9(dirname10(paths.unitFile), { recursive: true });
|
|
@@ -13810,15 +13911,15 @@ async function seedManagedInstall(deps) {
|
|
|
13810
13911
|
}
|
|
13811
13912
|
const target = versionPackageRoot(version);
|
|
13812
13913
|
if (!existsSync8(target)) {
|
|
13813
|
-
const staging =
|
|
13914
|
+
const staging = join15(versionsDir(), `.${version}.seed-${process.pid}`);
|
|
13814
13915
|
rmSync7(staging, { recursive: true, force: true });
|
|
13815
13916
|
try {
|
|
13816
|
-
cpSync(own,
|
|
13917
|
+
cpSync(own, join15(staging, "node_modules", "@cabane", "companion"), {
|
|
13817
13918
|
recursive: true,
|
|
13818
13919
|
verbatimSymlinks: true
|
|
13819
13920
|
});
|
|
13820
|
-
rmSync7(
|
|
13821
|
-
renameSync7(staging,
|
|
13921
|
+
rmSync7(join15(versionsDir(), version), { recursive: true, force: true });
|
|
13922
|
+
renameSync7(staging, join15(versionsDir(), version));
|
|
13822
13923
|
} catch (err) {
|
|
13823
13924
|
rmSync7(staging, { recursive: true, force: true });
|
|
13824
13925
|
return {
|
|
@@ -13829,7 +13930,7 @@ async function seedManagedInstall(deps) {
|
|
|
13829
13930
|
}
|
|
13830
13931
|
const unrunnable = await verifyInstall(target, version, [], deps);
|
|
13831
13932
|
if (unrunnable) {
|
|
13832
|
-
rmSync7(
|
|
13933
|
+
rmSync7(join15(versionsDir(), version), { recursive: true, force: true });
|
|
13833
13934
|
return { ok: false, reason: `The copy of Cabane for the service can't run: ${unrunnable}.` };
|
|
13834
13935
|
}
|
|
13835
13936
|
pointCurrentAt(target);
|
|
@@ -13841,7 +13942,7 @@ function tildeDir() {
|
|
|
13841
13942
|
function unitNode(platform, unitFile) {
|
|
13842
13943
|
let text;
|
|
13843
13944
|
try {
|
|
13844
|
-
text =
|
|
13945
|
+
text = readFileSync8(unitFile, "utf8");
|
|
13845
13946
|
} catch {
|
|
13846
13947
|
return null;
|
|
13847
13948
|
}
|
|
@@ -13911,12 +14012,12 @@ function userName() {
|
|
|
13911
14012
|
return process.env.USER ?? "";
|
|
13912
14013
|
}
|
|
13913
14014
|
}
|
|
13914
|
-
function rewriteScriptWrappers(dir2 =
|
|
14015
|
+
function rewriteScriptWrappers(dir2 = join15(cabaneDir(), "bin")) {
|
|
13915
14016
|
for (const name of ["cabane", "cabane-companion"]) {
|
|
13916
|
-
const path =
|
|
14017
|
+
const path = join15(dir2, name);
|
|
13917
14018
|
let text;
|
|
13918
14019
|
try {
|
|
13919
|
-
text =
|
|
14020
|
+
text = readFileSync8(path, "utf8");
|
|
13920
14021
|
} catch {
|
|
13921
14022
|
continue;
|
|
13922
14023
|
}
|
|
@@ -14328,6 +14429,12 @@ async function setupScript(opts, deps) {
|
|
|
14328
14429
|
ui.outro(["Nothing left to set up."]);
|
|
14329
14430
|
return;
|
|
14330
14431
|
}
|
|
14432
|
+
const strays = (deps.strays ?? findStrayCompanions)(running ? [running.pid] : []);
|
|
14433
|
+
if (strays.length > 0) {
|
|
14434
|
+
ui.cancel(strayCompanionLines(strays, cabane("setup")).join("\n"));
|
|
14435
|
+
process.exitCode = 1;
|
|
14436
|
+
return;
|
|
14437
|
+
}
|
|
14331
14438
|
let justPaired = false;
|
|
14332
14439
|
if (!paired) {
|
|
14333
14440
|
const device = await (deps.pair ?? pairDevice)({
|
|
@@ -14486,41 +14593,52 @@ import {
|
|
|
14486
14593
|
chmodSync as chmodSync4,
|
|
14487
14594
|
existsSync as existsSync9,
|
|
14488
14595
|
mkdirSync as mkdirSync11,
|
|
14489
|
-
readFileSync as
|
|
14596
|
+
readFileSync as readFileSync9,
|
|
14490
14597
|
renameSync as renameSync8,
|
|
14491
14598
|
rmSync as rmSync8,
|
|
14492
14599
|
writeFileSync as writeFileSync6
|
|
14493
14600
|
} from "fs";
|
|
14494
|
-
import {
|
|
14601
|
+
import { join as join16 } from "path";
|
|
14495
14602
|
import { z as z4 } from "zod";
|
|
14496
|
-
|
|
14497
|
-
|
|
14603
|
+
var credentialMapSchema = z4.record(z4.string(), z4.string());
|
|
14604
|
+
function credentialsDir() {
|
|
14605
|
+
return join16(cabaneDir(), "credentials");
|
|
14498
14606
|
}
|
|
14499
|
-
|
|
14500
|
-
|
|
14501
|
-
|
|
14502
|
-
|
|
14607
|
+
function legacyPath() {
|
|
14608
|
+
return join16(cabaneDir(), "credentials.json");
|
|
14609
|
+
}
|
|
14610
|
+
function devicePath(deviceId) {
|
|
14611
|
+
return join16(credentialsDir(), `${deviceId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
14612
|
+
}
|
|
14613
|
+
function read(path) {
|
|
14614
|
+
if (!existsSync9(path)) return null;
|
|
14503
14615
|
let raw;
|
|
14504
14616
|
try {
|
|
14505
|
-
raw =
|
|
14617
|
+
raw = readFileSync9(path, "utf8");
|
|
14506
14618
|
} catch {
|
|
14507
14619
|
return {};
|
|
14508
14620
|
}
|
|
14509
14621
|
if (raw.trim().length === 0) return {};
|
|
14510
14622
|
try {
|
|
14511
|
-
const result =
|
|
14623
|
+
const result = credentialMapSchema.safeParse(JSON.parse(raw));
|
|
14512
14624
|
return result.success ? result.data : {};
|
|
14513
14625
|
} catch {
|
|
14514
14626
|
return {};
|
|
14515
14627
|
}
|
|
14516
14628
|
}
|
|
14517
|
-
function
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14629
|
+
function load(deviceId) {
|
|
14630
|
+
return read(devicePath(deviceId)) ?? read(legacyPath()) ?? {};
|
|
14631
|
+
}
|
|
14632
|
+
function save(deviceId, map) {
|
|
14633
|
+
const dir2 = credentialsDir();
|
|
14634
|
+
mkdirSync11(dir2, { recursive: true });
|
|
14635
|
+
for (const d of [cabaneDir(), dir2]) {
|
|
14636
|
+
try {
|
|
14637
|
+
chmodSync4(d, 448);
|
|
14638
|
+
} catch {
|
|
14639
|
+
}
|
|
14523
14640
|
}
|
|
14641
|
+
const path = devicePath(deviceId);
|
|
14524
14642
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
14525
14643
|
try {
|
|
14526
14644
|
writeFileSync6(tmp, JSON.stringify(map, null, 2) + "\n", { mode: 384 });
|
|
@@ -14537,31 +14655,28 @@ function save(map) {
|
|
|
14537
14655
|
throw err;
|
|
14538
14656
|
}
|
|
14539
14657
|
}
|
|
14540
|
-
function getCredential(agentId) {
|
|
14541
|
-
return load()[agentId] ?? null;
|
|
14658
|
+
function getCredential(deviceId, agentId) {
|
|
14659
|
+
return load(deviceId)[agentId] ?? null;
|
|
14542
14660
|
}
|
|
14543
|
-
function setCredential(agentId, pat) {
|
|
14544
|
-
|
|
14545
|
-
|
|
14661
|
+
function setCredential(deviceId, agentId, pat) {
|
|
14662
|
+
if (read(devicePath(deviceId))?.[agentId] === pat) return;
|
|
14663
|
+
const map = load(deviceId);
|
|
14546
14664
|
map[agentId] = pat;
|
|
14547
|
-
save(map);
|
|
14665
|
+
save(deviceId, map);
|
|
14548
14666
|
}
|
|
14549
|
-
function pruneCredentials(keepAgentIds) {
|
|
14550
|
-
const map = load();
|
|
14667
|
+
function pruneCredentials(deviceId, keepAgentIds) {
|
|
14551
14668
|
const keep = new Set(keepAgentIds);
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
}
|
|
14559
|
-
if (removed > 0) save(map);
|
|
14669
|
+
if (keep.size === 0) return [];
|
|
14670
|
+
const own = read(devicePath(deviceId));
|
|
14671
|
+
const map = own ?? load(deviceId);
|
|
14672
|
+
const removed = Object.keys(map).filter((id) => !keep.has(id));
|
|
14673
|
+
for (const id of removed) delete map[id];
|
|
14674
|
+
if (removed.length > 0 || own === null) save(deviceId, map);
|
|
14560
14675
|
return removed;
|
|
14561
14676
|
}
|
|
14562
14677
|
function clearCredentials() {
|
|
14563
|
-
|
|
14564
|
-
if (existsSync9(
|
|
14678
|
+
rmSync8(credentialsDir(), { recursive: true, force: true });
|
|
14679
|
+
if (existsSync9(legacyPath())) writeFileSync6(legacyPath(), "", { mode: 384 });
|
|
14565
14680
|
}
|
|
14566
14681
|
|
|
14567
14682
|
// src/commands/logout.ts
|
|
@@ -14617,7 +14732,7 @@ function writeCompletedPairing(raw) {
|
|
|
14617
14732
|
}
|
|
14618
14733
|
|
|
14619
14734
|
// src/cli.ts
|
|
14620
|
-
import { readFileSync as
|
|
14735
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
14621
14736
|
|
|
14622
14737
|
// src/commands/logs.ts
|
|
14623
14738
|
init_cabane_command();
|
|
@@ -14709,7 +14824,7 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
14709
14824
|
|
|
14710
14825
|
// src/dashboard/server.ts
|
|
14711
14826
|
init_errors();
|
|
14712
|
-
import { dirname as
|
|
14827
|
+
import { dirname as dirname11, join as join18 } from "path";
|
|
14713
14828
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
14714
14829
|
import { serve } from "@hono/node-server";
|
|
14715
14830
|
import { Hono } from "hono";
|
|
@@ -14717,7 +14832,7 @@ import { Hono } from "hono";
|
|
|
14717
14832
|
// src/dashboard/routes.ts
|
|
14718
14833
|
import { openSync as openSync4, readSync, closeSync as closeSync3, fstatSync, existsSync as existsSync10 } from "fs";
|
|
14719
14834
|
import { readFile } from "fs/promises";
|
|
14720
|
-
import { extname, join as
|
|
14835
|
+
import { extname, join as join17, normalize } from "path";
|
|
14721
14836
|
import { streamSSE } from "hono/streaming";
|
|
14722
14837
|
|
|
14723
14838
|
// src/state.ts
|
|
@@ -14966,13 +15081,13 @@ var CONTENT_TYPES = {
|
|
|
14966
15081
|
function registerRoutes(app, deps) {
|
|
14967
15082
|
const { supervisor, hub, staticDir } = deps;
|
|
14968
15083
|
app.get("/", async (c) => {
|
|
14969
|
-
const html = await readFile(
|
|
15084
|
+
const html = await readFile(join17(staticDir, "index.html"), "utf8");
|
|
14970
15085
|
return c.html(html);
|
|
14971
15086
|
});
|
|
14972
15087
|
app.get("/static/:file", async (c) => {
|
|
14973
15088
|
const file = c.req.param("file");
|
|
14974
15089
|
const safe2 = normalize(file).replace(/^(\.\.[/\\])+/, "");
|
|
14975
|
-
const full =
|
|
15090
|
+
const full = join17(staticDir, safe2);
|
|
14976
15091
|
if (!full.startsWith(staticDir) || !existsSync10(full)) return c.notFound();
|
|
14977
15092
|
const body = await readFile(full);
|
|
14978
15093
|
const type = CONTENT_TYPES[extname(full).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -15163,7 +15278,7 @@ function isAddrInUse(err) {
|
|
|
15163
15278
|
return Boolean(err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE");
|
|
15164
15279
|
}
|
|
15165
15280
|
function resolveStaticDir() {
|
|
15166
|
-
return
|
|
15281
|
+
return join18(dirname11(fileURLToPath5(import.meta.url)), "static");
|
|
15167
15282
|
}
|
|
15168
15283
|
|
|
15169
15284
|
// src/supervisor.ts
|
|
@@ -15330,20 +15445,20 @@ init_config();
|
|
|
15330
15445
|
|
|
15331
15446
|
// src/dispatch-dedupe.ts
|
|
15332
15447
|
init_config();
|
|
15333
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
15334
|
-
import { join as
|
|
15448
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync9, existsSync as existsSync11 } from "fs";
|
|
15449
|
+
import { join as join19 } from "path";
|
|
15335
15450
|
var MAX_IDS = 256;
|
|
15336
15451
|
function dir(log2) {
|
|
15337
|
-
return
|
|
15452
|
+
return join19(cabaneDir(), log2);
|
|
15338
15453
|
}
|
|
15339
15454
|
function pathFor(log2, workspaceId) {
|
|
15340
|
-
return
|
|
15455
|
+
return join19(dir(log2), encodeURIComponent(workspaceId));
|
|
15341
15456
|
}
|
|
15342
15457
|
function readIds(log2, workspaceId) {
|
|
15343
15458
|
const path = pathFor(log2, workspaceId);
|
|
15344
15459
|
if (!existsSync11(path)) return [];
|
|
15345
15460
|
try {
|
|
15346
|
-
return
|
|
15461
|
+
return readFileSync10(path, "utf8").split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
15347
15462
|
} catch {
|
|
15348
15463
|
return [];
|
|
15349
15464
|
}
|
|
@@ -15374,17 +15489,17 @@ function markCompleted(workspaceId, eventId) {
|
|
|
15374
15489
|
}
|
|
15375
15490
|
var MAX_RESUME_ATTEMPTS = 3;
|
|
15376
15491
|
function resumeDir() {
|
|
15377
|
-
return
|
|
15492
|
+
return join19(cabaneDir(), "resume-attempts");
|
|
15378
15493
|
}
|
|
15379
15494
|
function resumePathFor(workspaceId) {
|
|
15380
|
-
return
|
|
15495
|
+
return join19(resumeDir(), encodeURIComponent(workspaceId));
|
|
15381
15496
|
}
|
|
15382
15497
|
function readResumeCounts(workspaceId) {
|
|
15383
15498
|
const out = /* @__PURE__ */ new Map();
|
|
15384
15499
|
const path = resumePathFor(workspaceId);
|
|
15385
15500
|
if (!existsSync11(path)) return out;
|
|
15386
15501
|
try {
|
|
15387
|
-
for (const line of
|
|
15502
|
+
for (const line of readFileSync10(path, "utf8").split("\n")) {
|
|
15388
15503
|
const trimmed = line.trim();
|
|
15389
15504
|
if (!trimmed) continue;
|
|
15390
15505
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -15413,10 +15528,10 @@ function bumpResumeAttempt(workspaceId, eventId) {
|
|
|
15413
15528
|
return next;
|
|
15414
15529
|
}
|
|
15415
15530
|
function turnDir() {
|
|
15416
|
-
return
|
|
15531
|
+
return join19(cabaneDir(), "turns");
|
|
15417
15532
|
}
|
|
15418
15533
|
function turnPathFor(workspaceId) {
|
|
15419
|
-
return
|
|
15534
|
+
return join19(turnDir(), encodeURIComponent(workspaceId));
|
|
15420
15535
|
}
|
|
15421
15536
|
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;
|
|
15422
15537
|
function readTurnIds(workspaceId) {
|
|
@@ -15424,7 +15539,7 @@ function readTurnIds(workspaceId) {
|
|
|
15424
15539
|
const path = turnPathFor(workspaceId);
|
|
15425
15540
|
if (!existsSync11(path)) return out;
|
|
15426
15541
|
try {
|
|
15427
|
-
for (const line of
|
|
15542
|
+
for (const line of readFileSync10(path, "utf8").split("\n")) {
|
|
15428
15543
|
const trimmed = line.trim();
|
|
15429
15544
|
if (!trimmed) continue;
|
|
15430
15545
|
const tab = trimmed.lastIndexOf(" ");
|
|
@@ -15644,13 +15759,13 @@ import {
|
|
|
15644
15759
|
existsSync as existsSync18,
|
|
15645
15760
|
mkdirSync as mkdirSync16,
|
|
15646
15761
|
readdirSync as readdirSync5,
|
|
15647
|
-
readFileSync as
|
|
15762
|
+
readFileSync as readFileSync15,
|
|
15648
15763
|
renameSync as renameSync11,
|
|
15649
15764
|
rmSync as rmSync13,
|
|
15650
15765
|
statSync as statSync4,
|
|
15651
15766
|
writeFileSync as writeFileSync9
|
|
15652
15767
|
} from "fs";
|
|
15653
|
-
import { join as
|
|
15768
|
+
import { join as join28 } from "path";
|
|
15654
15769
|
import { Writable } from "stream";
|
|
15655
15770
|
|
|
15656
15771
|
// src/providers/docker-cli.ts
|
|
@@ -15719,11 +15834,11 @@ import {
|
|
|
15719
15834
|
existsSync as existsSync16,
|
|
15720
15835
|
mkdirSync as mkdirSync15,
|
|
15721
15836
|
readdirSync as readdirSync4,
|
|
15722
|
-
readFileSync as
|
|
15837
|
+
readFileSync as readFileSync14,
|
|
15723
15838
|
renameSync as renameSync10,
|
|
15724
15839
|
rmSync as rmSync12
|
|
15725
15840
|
} from "fs";
|
|
15726
|
-
import { join as
|
|
15841
|
+
import { join as join26 } from "path";
|
|
15727
15842
|
function routes(t) {
|
|
15728
15843
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
15729
15844
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -15769,7 +15884,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15769
15884
|
const staged = readdirSync4(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
15770
15885
|
let entry = {};
|
|
15771
15886
|
try {
|
|
15772
|
-
entry = JSON.parse(
|
|
15887
|
+
entry = JSON.parse(readFileSync14(join26(from, name), "utf8"));
|
|
15773
15888
|
} catch {
|
|
15774
15889
|
}
|
|
15775
15890
|
return { name, entry };
|
|
@@ -15782,7 +15897,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15782
15897
|
let published = 0;
|
|
15783
15898
|
for (const { name } of staged) {
|
|
15784
15899
|
try {
|
|
15785
|
-
move(
|
|
15900
|
+
move(join26(from, name), join26(to, name));
|
|
15786
15901
|
} catch {
|
|
15787
15902
|
return { published, complete: false };
|
|
15788
15903
|
}
|
|
@@ -15795,7 +15910,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
15795
15910
|
// src/providers/runner-base.ts
|
|
15796
15911
|
import { createHash as createHash4 } from "crypto";
|
|
15797
15912
|
import { existsSync as existsSync17, realpathSync as realpathSync6 } from "fs";
|
|
15798
|
-
import { dirname as
|
|
15913
|
+
import { dirname as dirname13, join as join27, sep as sep4 } from "path";
|
|
15799
15914
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15800
15915
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
15801
15916
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -15841,13 +15956,13 @@ async function ensureBaseImage(docker, image, log2) {
|
|
|
15841
15956
|
return true;
|
|
15842
15957
|
}
|
|
15843
15958
|
function ownInstall(moduleFile = fileURLToPath6(import.meta.url)) {
|
|
15844
|
-
let dir2 =
|
|
15959
|
+
let dir2 = dirname13(moduleFile);
|
|
15845
15960
|
for (let i = 0; i < 4; i++) {
|
|
15846
|
-
if (existsSync17(
|
|
15847
|
-
dir2 =
|
|
15961
|
+
if (existsSync17(join27(dir2, "dist", "cli.js")) && existsSync17(join27(dir2, "package.json"))) break;
|
|
15962
|
+
dir2 = dirname13(dir2);
|
|
15848
15963
|
}
|
|
15849
|
-
if (!existsSync17(
|
|
15850
|
-
const sdk =
|
|
15964
|
+
if (!existsSync17(join27(dir2, "dist", "cli.js"))) return null;
|
|
15965
|
+
const sdk = join27(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
15851
15966
|
if (!existsSync17(sdk)) return null;
|
|
15852
15967
|
const root = realpathSync6(dir2);
|
|
15853
15968
|
return realpathSync6(sdk).startsWith(root + sep4) ? root : null;
|
|
@@ -15986,39 +16101,39 @@ function networkCreateError(stderr) {
|
|
|
15986
16101
|
}
|
|
15987
16102
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
15988
16103
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
15989
|
-
return
|
|
16104
|
+
return join28(deviceDir, "attempts", attemptId, "session.tar");
|
|
15990
16105
|
}
|
|
15991
16106
|
function pruneOtherAttempts(deviceDir, pair2, keep) {
|
|
15992
|
-
const root =
|
|
16107
|
+
const root = join28(deviceDir, "attempts");
|
|
15993
16108
|
if (!existsSync18(root)) return;
|
|
15994
16109
|
for (const id of readdirSync5(root)) {
|
|
15995
16110
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
15996
16111
|
let pairOf = "";
|
|
15997
16112
|
try {
|
|
15998
|
-
pairOf =
|
|
16113
|
+
pairOf = readFileSync15(join28(root, id, "pair"), "utf8");
|
|
15999
16114
|
} catch {
|
|
16000
16115
|
continue;
|
|
16001
16116
|
}
|
|
16002
16117
|
if (pairOf === `${pair2.conversationId}/${pair2.agentId}`) {
|
|
16003
|
-
rmSync13(
|
|
16118
|
+
rmSync13(join28(root, id), { recursive: true, force: true });
|
|
16004
16119
|
}
|
|
16005
16120
|
}
|
|
16006
16121
|
}
|
|
16007
16122
|
var IDLE_SESSION_ARCHIVE_MS = 30 * 24 * 60 * 6e4;
|
|
16008
16123
|
function pruneIdleSessionArchives(deviceDir, now = Date.now(), maxAgeMs = IDLE_SESSION_ARCHIVE_MS) {
|
|
16009
|
-
const root =
|
|
16124
|
+
const root = join28(deviceDir, "attempts");
|
|
16010
16125
|
if (!existsSync18(root)) return [];
|
|
16011
16126
|
const removed = [];
|
|
16012
16127
|
for (const id of readdirSync5(root)) {
|
|
16013
16128
|
if (!UUID_RE.test(id)) continue;
|
|
16014
16129
|
let kept;
|
|
16015
16130
|
try {
|
|
16016
|
-
kept = statSync4(
|
|
16131
|
+
kept = statSync4(join28(root, id)).mtimeMs;
|
|
16017
16132
|
} catch {
|
|
16018
16133
|
continue;
|
|
16019
16134
|
}
|
|
16020
16135
|
if (now - kept < maxAgeMs) continue;
|
|
16021
|
-
rmSync13(
|
|
16136
|
+
rmSync13(join28(root, id), { recursive: true, force: true });
|
|
16022
16137
|
removed.push(id);
|
|
16023
16138
|
}
|
|
16024
16139
|
return removed;
|
|
@@ -16219,11 +16334,11 @@ function createDockerProvider(opts) {
|
|
|
16219
16334
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
16220
16335
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
16221
16336
|
};
|
|
16222
|
-
const socketRoot = opts.socketRoot ??
|
|
16337
|
+
const socketRoot = opts.socketRoot ?? join28(
|
|
16223
16338
|
process.env.XDG_RUNTIME_DIR && existsSync18(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
16224
16339
|
`cabane-runners-${opts.deviceKey}`
|
|
16225
16340
|
);
|
|
16226
|
-
const socketDir = (name) =>
|
|
16341
|
+
const socketDir = (name) => join28(socketRoot, createHash5("sha256").update(name).digest("hex").slice(0, 12));
|
|
16227
16342
|
const create = async (name, attempt) => {
|
|
16228
16343
|
mkdirSync16(socketRoot, { recursive: true, mode: 448 });
|
|
16229
16344
|
mkdirSync16(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -16365,10 +16480,10 @@ function createDockerProvider(opts) {
|
|
|
16365
16480
|
}
|
|
16366
16481
|
};
|
|
16367
16482
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
16368
|
-
const pendingPath = (name) =>
|
|
16483
|
+
const pendingPath = (name) => join28(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
16369
16484
|
const readPending = (name) => {
|
|
16370
16485
|
try {
|
|
16371
|
-
const ids = JSON.parse(
|
|
16486
|
+
const ids = JSON.parse(readFileSync15(pendingPath(name), "utf8"));
|
|
16372
16487
|
return Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : [];
|
|
16373
16488
|
} catch {
|
|
16374
16489
|
return [];
|
|
@@ -16379,12 +16494,12 @@ function createDockerProvider(opts) {
|
|
|
16379
16494
|
rmSync13(pendingPath(name), { force: true });
|
|
16380
16495
|
return;
|
|
16381
16496
|
}
|
|
16382
|
-
mkdirSync16(
|
|
16497
|
+
mkdirSync16(join28(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
16383
16498
|
writeFileSync9(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
16384
16499
|
};
|
|
16385
16500
|
const keepSession = async (name, attempt, attemptId) => {
|
|
16386
16501
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
16387
|
-
mkdirSync16(
|
|
16502
|
+
mkdirSync16(join28(archive, ".."), { recursive: true });
|
|
16388
16503
|
const partial = `${archive}.${process.pid}.partial`;
|
|
16389
16504
|
const file = new CappedFile(partial, sessionCap);
|
|
16390
16505
|
const r = await execIn(
|
|
@@ -16401,7 +16516,7 @@ function createDockerProvider(opts) {
|
|
|
16401
16516
|
);
|
|
16402
16517
|
if (r.code === 0 && !file.over) {
|
|
16403
16518
|
renameSync11(partial, archive);
|
|
16404
|
-
writeFileSync9(
|
|
16519
|
+
writeFileSync9(join28(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
16405
16520
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
16406
16521
|
await writeMarker(name, attemptId);
|
|
16407
16522
|
log2.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -16415,7 +16530,7 @@ function createDockerProvider(opts) {
|
|
|
16415
16530
|
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"
|
|
16416
16531
|
);
|
|
16417
16532
|
};
|
|
16418
|
-
const stagingDir = (name) =>
|
|
16533
|
+
const stagingDir = (name) => join28(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
16419
16534
|
const ensureRunning = async (name) => {
|
|
16420
16535
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
16421
16536
|
if (seen.code !== 0) return false;
|
|
@@ -16423,7 +16538,7 @@ function createDockerProvider(opts) {
|
|
|
16423
16538
|
return (await docker.run(["start", name])).code === 0;
|
|
16424
16539
|
};
|
|
16425
16540
|
const dropArchive = (attemptId) => {
|
|
16426
|
-
rmSync13(
|
|
16541
|
+
rmSync13(join28(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
16427
16542
|
recursive: true,
|
|
16428
16543
|
force: true
|
|
16429
16544
|
});
|
|
@@ -16492,7 +16607,7 @@ function createDockerProvider(opts) {
|
|
|
16492
16607
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
16493
16608
|
await must(["start", name], "starting the container");
|
|
16494
16609
|
const git = await serveGitCredentials({
|
|
16495
|
-
socketPath:
|
|
16610
|
+
socketPath: join28(socketDir(name), "git.sock"),
|
|
16496
16611
|
hosts: opts.gitHosts,
|
|
16497
16612
|
fill: opts.gitFill,
|
|
16498
16613
|
log: log2
|
|
@@ -16641,15 +16756,15 @@ function createDockerProvider(opts) {
|
|
|
16641
16756
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
16642
16757
|
if (transcripts) {
|
|
16643
16758
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
16644
|
-
if (kept.length) mkdirSync16(
|
|
16645
|
-
for (const f of kept) writeFileSync9(
|
|
16759
|
+
if (kept.length) mkdirSync16(join28(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
16760
|
+
for (const f of kept) writeFileSync9(join28(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
16646
16761
|
await removeIn(
|
|
16647
16762
|
name,
|
|
16648
16763
|
`${CONTAINER_STATE}/transcripts`,
|
|
16649
16764
|
kept.map((f) => f.name)
|
|
16650
16765
|
);
|
|
16651
16766
|
}
|
|
16652
|
-
const outboxRel =
|
|
16767
|
+
const outboxRel = join28("outbox", encodeURIComponent(attempt.agentId));
|
|
16653
16768
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
16654
16769
|
let unpublished = false;
|
|
16655
16770
|
if (outbox) {
|
|
@@ -16675,7 +16790,7 @@ function createDockerProvider(opts) {
|
|
|
16675
16790
|
const staged = stagingDir(name);
|
|
16676
16791
|
mkdirSync16(staged, { recursive: true });
|
|
16677
16792
|
writeFileSync9(
|
|
16678
|
-
|
|
16793
|
+
join28(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
16679
16794
|
`${JSON.stringify(entry)}
|
|
16680
16795
|
`
|
|
16681
16796
|
);
|
|
@@ -16688,7 +16803,7 @@ function createDockerProvider(opts) {
|
|
|
16688
16803
|
if (outbox.complete) {
|
|
16689
16804
|
const published = publishStaged(
|
|
16690
16805
|
stagingDir(name),
|
|
16691
|
-
|
|
16806
|
+
join28(opts.hostStateDir, outboxRel),
|
|
16692
16807
|
opts.moveFile
|
|
16693
16808
|
);
|
|
16694
16809
|
queued = published.published;
|
|
@@ -17489,7 +17604,7 @@ var CompanionSupervisor = class {
|
|
|
17489
17604
|
saveConfig(this.config);
|
|
17490
17605
|
}
|
|
17491
17606
|
for (const it of items) {
|
|
17492
|
-
if (it.credential) setCredential(it.agentId, it.credential);
|
|
17607
|
+
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
17493
17608
|
}
|
|
17494
17609
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
17495
17610
|
for (const it of items) {
|
|
@@ -17500,8 +17615,17 @@ var CompanionSupervisor = class {
|
|
|
17500
17615
|
for (const wsId of [...this.workspaces.keys()]) {
|
|
17501
17616
|
if (!byWorkspace.has(wsId)) await this.removeWorkspace(wsId);
|
|
17502
17617
|
}
|
|
17503
|
-
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(wsId, list);
|
|
17504
|
-
pruneCredentials(
|
|
17618
|
+
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(device.id, wsId, list);
|
|
17619
|
+
const pruned = pruneCredentials(
|
|
17620
|
+
device.id,
|
|
17621
|
+
items.map((i) => i.agentId)
|
|
17622
|
+
);
|
|
17623
|
+
if (pruned.length > 0) {
|
|
17624
|
+
this.log.info(
|
|
17625
|
+
{ deviceId: device.id, agentIds: pruned },
|
|
17626
|
+
`Removed the saved sign-in of ${pruned.length === 1 ? "agent" : "agents"} ${pruned.join(", ")}: no longer assigned to this device (${device.id}).`
|
|
17627
|
+
);
|
|
17628
|
+
}
|
|
17505
17629
|
void this.recoverRuns();
|
|
17506
17630
|
} finally {
|
|
17507
17631
|
this.refreshing = false;
|
|
@@ -17515,7 +17639,7 @@ var CompanionSupervisor = class {
|
|
|
17515
17639
|
}
|
|
17516
17640
|
}
|
|
17517
17641
|
// ---- reconciliation ----
|
|
17518
|
-
reconcileWorkspace(workspaceId, list) {
|
|
17642
|
+
reconcileWorkspace(deviceId, workspaceId, list) {
|
|
17519
17643
|
const first = list[0];
|
|
17520
17644
|
let wr = this.workspaces.get(workspaceId);
|
|
17521
17645
|
if (!wr) {
|
|
@@ -17541,7 +17665,7 @@ var CompanionSupervisor = class {
|
|
|
17541
17665
|
if (!desired.has(agentId)) this.removeAgent(wr, agentId);
|
|
17542
17666
|
}
|
|
17543
17667
|
for (const it of list) {
|
|
17544
|
-
const credential = it.credential ?? getCredential(it.agentId);
|
|
17668
|
+
const credential = it.credential ?? getCredential(deviceId, it.agentId);
|
|
17545
17669
|
const runConfig = parseRunConfig(
|
|
17546
17670
|
it.runConfig,
|
|
17547
17671
|
(m) => this.log.warn(
|
|
@@ -18608,10 +18732,10 @@ function handleUncaught(log2, err, origin) {
|
|
|
18608
18732
|
|
|
18609
18733
|
// src/crash-marker.ts
|
|
18610
18734
|
init_config();
|
|
18611
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as
|
|
18612
|
-
import { join as
|
|
18735
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readFileSync as readFileSync18, rmSync as rmSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
18736
|
+
import { join as join31 } from "path";
|
|
18613
18737
|
function crashMarkerPath() {
|
|
18614
|
-
return
|
|
18738
|
+
return join31(cabaneDir(), "last-error.json");
|
|
18615
18739
|
}
|
|
18616
18740
|
function recordCrash(rec2) {
|
|
18617
18741
|
try {
|
|
@@ -18654,6 +18778,8 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
18654
18778
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18655
18779
|
const pre = readLiveRuntimeState();
|
|
18656
18780
|
if (pre && await verifyRuntime(pre) === "stale") clearRuntimeState();
|
|
18781
|
+
const strays = findStrayCompanions(pre ? [pre.pid] : []);
|
|
18782
|
+
if (strays.length > 0) return { ok: false, reason: "stray-companion", strays };
|
|
18657
18783
|
const claim = acquireRuntimeState({
|
|
18658
18784
|
pid: process.pid,
|
|
18659
18785
|
startedAt,
|
|
@@ -18833,6 +18959,11 @@ async function startSupervised(opts, interactive, daemon) {
|
|
|
18833
18959
|
if (daemon) write(`Running in the background (pid ${process.pid}). Stop with ${cabane("stop")}.`);
|
|
18834
18960
|
else blank();
|
|
18835
18961
|
if (!isDevicePaired()) {
|
|
18962
|
+
const strays = findStrayCompanions();
|
|
18963
|
+
if (strays.length > 0) {
|
|
18964
|
+
refuse(strayCompanionLines(strays, cabane("start")));
|
|
18965
|
+
return;
|
|
18966
|
+
}
|
|
18836
18967
|
const paired = await pairDevice({
|
|
18837
18968
|
baseUrl: resolvePairBaseUrl(opts.server),
|
|
18838
18969
|
interactive,
|
|
@@ -18868,6 +18999,10 @@ async function bringUp2() {
|
|
|
18868
18999
|
// the middle of it would be the same news, worse.
|
|
18869
19000
|
onReadinessWarning: (message2) => log2.warn(message2)
|
|
18870
19001
|
});
|
|
19002
|
+
if (!result.ok && result.reason === "stray-companion") {
|
|
19003
|
+
refuse(strayCompanionLines(result.strays, cabane("start")));
|
|
19004
|
+
return null;
|
|
19005
|
+
}
|
|
18871
19006
|
if (!result.ok) {
|
|
18872
19007
|
for (const line of alreadyRunningLines(result.existing?.pid)) write(`${INDENT}${line}`);
|
|
18873
19008
|
return null;
|
|
@@ -18914,7 +19049,7 @@ init_install_kind();
|
|
|
18914
19049
|
init_cabane_command();
|
|
18915
19050
|
import { readdirSync as readdirSync7, realpathSync as realpathSync8, existsSync as existsSync21 } from "fs";
|
|
18916
19051
|
import { homedir as homedir9 } from "os";
|
|
18917
|
-
import { dirname as
|
|
19052
|
+
import { dirname as dirname15, join as join32 } from "path";
|
|
18918
19053
|
init_config();
|
|
18919
19054
|
init_sandbox_policy();
|
|
18920
19055
|
init_secrets();
|
|
@@ -18980,10 +19115,10 @@ function versionLine() {
|
|
|
18980
19115
|
const install = companionInstall();
|
|
18981
19116
|
const own = installedCommandPath({ install });
|
|
18982
19117
|
if (install === "script") {
|
|
18983
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(
|
|
19118
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed by the install script (${tildePath(join32(homedir9(), ".cabane"))})`;
|
|
18984
19119
|
}
|
|
18985
19120
|
if (own)
|
|
18986
|
-
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(
|
|
19121
|
+
return `Cabane ${COMPANION_VERSION} \xB7 installed with npm (${tildePath(dirname15(dirname15(own)))})`;
|
|
18987
19122
|
return `Cabane ${COMPANION_VERSION}`;
|
|
18988
19123
|
}
|
|
18989
19124
|
function otherInstallLines(running) {
|
|
@@ -19006,7 +19141,7 @@ function otherInstall(install, own) {
|
|
|
19006
19141
|
}
|
|
19007
19142
|
};
|
|
19008
19143
|
if (install === "npm") {
|
|
19009
|
-
const script =
|
|
19144
|
+
const script = join32(
|
|
19010
19145
|
homedir9(),
|
|
19011
19146
|
".cabane",
|
|
19012
19147
|
"node",
|
|
@@ -19015,12 +19150,12 @@ function otherInstall(install, own) {
|
|
|
19015
19150
|
"@cabane",
|
|
19016
19151
|
"companion"
|
|
19017
19152
|
);
|
|
19018
|
-
return existsSync21(script) ?
|
|
19153
|
+
return existsSync21(script) ? join32(homedir9(), ".cabane") : null;
|
|
19019
19154
|
}
|
|
19020
19155
|
const found = firstOnPath("cabane-companion", process.env.PATH);
|
|
19021
19156
|
if (!found) return null;
|
|
19022
|
-
const ownAlias = own ? real(
|
|
19023
|
-
return real(found) === ownAlias ? null :
|
|
19157
|
+
const ownAlias = own ? real(join32(dirname15(own), "cabane-companion")) : null;
|
|
19158
|
+
return real(found) === ownAlias ? null : dirname15(found);
|
|
19024
19159
|
}
|
|
19025
19160
|
async function liveState() {
|
|
19026
19161
|
const running = readLiveRuntimeState();
|
|
@@ -19243,8 +19378,8 @@ init_cabane_command();
|
|
|
19243
19378
|
init_errors();
|
|
19244
19379
|
init_src();
|
|
19245
19380
|
init_transcript_writer();
|
|
19246
|
-
import { existsSync as existsSync22, readFileSync as
|
|
19247
|
-
import { isAbsolute, join as
|
|
19381
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, readdirSync as readdirSync8 } from "fs";
|
|
19382
|
+
import { isAbsolute, join as join33 } from "path";
|
|
19248
19383
|
async function transcript(opts = {}) {
|
|
19249
19384
|
const dir2 = transcriptsDir();
|
|
19250
19385
|
if (opts.follow) {
|
|
@@ -19261,7 +19396,7 @@ async function transcript(opts = {}) {
|
|
|
19261
19396
|
process.stdout.write(emptyMessage(dir2));
|
|
19262
19397
|
return;
|
|
19263
19398
|
}
|
|
19264
|
-
process.stdout.write(renderFile(
|
|
19399
|
+
process.stdout.write(renderFile(join33(dir2, newest)) + "\n");
|
|
19265
19400
|
return;
|
|
19266
19401
|
}
|
|
19267
19402
|
printList(dir2);
|
|
@@ -19344,7 +19479,7 @@ function isComplete(content) {
|
|
|
19344
19479
|
async function followTranscripts(dir2) {
|
|
19345
19480
|
const follower = new TranscriptFollower({
|
|
19346
19481
|
listFiles: () => listFiles(dir2),
|
|
19347
|
-
read: (f) =>
|
|
19482
|
+
read: (f) => readFileSync19(join33(dir2, f), "utf8"),
|
|
19348
19483
|
write: (s) => process.stdout.write(s),
|
|
19349
19484
|
// CSI: cursor up `n` lines, then erase from cursor to end of screen.
|
|
19350
19485
|
clearLines: (n) => process.stdout.write(`\x1B[${n}A\x1B[0J`),
|
|
@@ -19384,7 +19519,7 @@ function printList(dir2) {
|
|
|
19384
19519
|
|
|
19385
19520
|
`);
|
|
19386
19521
|
for (const f of files.slice(0, 20)) {
|
|
19387
|
-
const { meta, outcome } = peek(
|
|
19522
|
+
const { meta, outcome } = peek(join33(dir2, f));
|
|
19388
19523
|
const when = fmtTime(rec(meta)?.ts);
|
|
19389
19524
|
const ws = str2(rec(meta)?.workspaceSlug);
|
|
19390
19525
|
const o = rec(outcome);
|
|
@@ -19406,7 +19541,7 @@ function peek(path) {
|
|
|
19406
19541
|
let meta;
|
|
19407
19542
|
let outcome;
|
|
19408
19543
|
try {
|
|
19409
|
-
for (const line of
|
|
19544
|
+
for (const line of readFileSync19(path, "utf8").split("\n")) {
|
|
19410
19545
|
if (!line.trim()) continue;
|
|
19411
19546
|
const o = safeParse(line);
|
|
19412
19547
|
const t = str2(rec(o)?.type);
|
|
@@ -19422,10 +19557,10 @@ function resolveTarget(dir2, target) {
|
|
|
19422
19557
|
if (existsSync22(target)) return target;
|
|
19423
19558
|
throw new CompanionError(`no transcript at ${target}.`);
|
|
19424
19559
|
}
|
|
19425
|
-
const exact =
|
|
19560
|
+
const exact = join33(dir2, target);
|
|
19426
19561
|
if (existsSync22(exact)) return exact;
|
|
19427
19562
|
const matches = listFiles(dir2).filter((f) => f.includes(target));
|
|
19428
|
-
if (matches.length === 1) return
|
|
19563
|
+
if (matches.length === 1) return join33(dir2, matches[0]);
|
|
19429
19564
|
if (matches.length === 0) {
|
|
19430
19565
|
throw new CompanionError(
|
|
19431
19566
|
`no transcript matching "${target}" in ${dir2}. Run \`${cabane("transcript")}\` to list them.`
|
|
@@ -19439,7 +19574,7 @@ function resolveTarget(dir2, target) {
|
|
|
19439
19574
|
function renderFile(path) {
|
|
19440
19575
|
let content;
|
|
19441
19576
|
try {
|
|
19442
|
-
content =
|
|
19577
|
+
content = readFileSync19(path, "utf8");
|
|
19443
19578
|
} catch (err) {
|
|
19444
19579
|
throw new CompanionError(
|
|
19445
19580
|
`couldn't read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -19617,7 +19752,7 @@ program.command("pair", { hidden: true }).allowExcessArguments(false).option("--
|
|
|
19617
19752
|
});
|
|
19618
19753
|
});
|
|
19619
19754
|
program.command("write-paired-config", { hidden: true }).description("persist an already-completed device enrollment payload from stdin.").action(() => {
|
|
19620
|
-
writeCompletedPairing(
|
|
19755
|
+
writeCompletedPairing(readFileSync20(0, "utf8"));
|
|
19621
19756
|
});
|
|
19622
19757
|
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(
|
|
19623
19758
|
new Option("--log-level <level>", "info (default), warn, or debug").choices([
|