@cabane/companion 0.6.34 → 0.6.36
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/README.md +128 -36
- package/dist/cli.js +233 -696
- package/dist/runtime.js +50 -18
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -461,21 +461,36 @@ function consoleMessageFormat(log, messageKey) {
|
|
|
461
461
|
return short ? `${short} ${msg}` : msg;
|
|
462
462
|
}
|
|
463
463
|
var cached = null;
|
|
464
|
-
|
|
465
|
-
|
|
464
|
+
var consoleLogging = true;
|
|
465
|
+
function createLogger(destinations = {}) {
|
|
466
466
|
const path = companionLogPath();
|
|
467
|
-
mkdirSync2(dirname2(path), { recursive: true });
|
|
467
|
+
if (!destinations.file) mkdirSync2(dirname2(path), { recursive: true });
|
|
468
468
|
const streams = [];
|
|
469
469
|
if (process.env.CABANE_COMPANION_DAEMON !== "1") {
|
|
470
470
|
const consoleStream = pretty({
|
|
471
471
|
colorize: true,
|
|
472
472
|
ignore: CONSOLE_IGNORE,
|
|
473
|
-
messageFormat: (log, messageKey) => consoleMessageFormat(log, messageKey)
|
|
473
|
+
messageFormat: (log, messageKey) => consoleMessageFormat(log, messageKey),
|
|
474
|
+
...destinations.console ? { destination: destinations.console } : {}
|
|
475
|
+
});
|
|
476
|
+
streams.push({
|
|
477
|
+
level: "info",
|
|
478
|
+
stream: {
|
|
479
|
+
write(chunk) {
|
|
480
|
+
if (consoleLogging) consoleStream.write(chunk);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
474
483
|
});
|
|
475
|
-
streams.push({ level: "info", stream: consoleStream });
|
|
476
484
|
}
|
|
477
|
-
streams.push({
|
|
478
|
-
|
|
485
|
+
streams.push({
|
|
486
|
+
level: "debug",
|
|
487
|
+
stream: destinations.file ?? createWriteStream(path, { flags: "a" })
|
|
488
|
+
});
|
|
489
|
+
return pino({ level: "debug" }, pino.multistream(streams));
|
|
490
|
+
}
|
|
491
|
+
function getLogger() {
|
|
492
|
+
if (cached) return cached;
|
|
493
|
+
cached = createLogger();
|
|
479
494
|
return cached;
|
|
480
495
|
}
|
|
481
496
|
|
|
@@ -1269,8 +1284,12 @@ async function warnAboutHarnessReadiness(cfg, deps = {}) {
|
|
|
1269
1284
|
...claudeInstalled ? ["Claude Code"] : [],
|
|
1270
1285
|
...codexInstalled ? ["Codex"] : []
|
|
1271
1286
|
];
|
|
1287
|
+
const connectCommands = [
|
|
1288
|
+
...claudeInstalled ? ["`cabane-companion connect claude-code`"] : [],
|
|
1289
|
+
...codexInstalled ? ["`cabane-companion connect codex`"] : []
|
|
1290
|
+
];
|
|
1272
1291
|
warn(
|
|
1273
|
-
"No harness is connected on this device yet, so no agent turn can run here. " + (installed.length > 0 ? `We found ${installed.join(" and ")} on this machine \u2014 connect ${installed.length > 1 ? "one" : "it"}
|
|
1292
|
+
"No harness is connected on this device yet, so no agent turn can run here. " + (installed.length > 0 ? `We found ${installed.join(" and ")} on this machine \u2014 connect ${installed.length > 1 ? "one with " : "it with "}${connectCommands.join(" or ")} and turns start routing here.` : "Install a harness and sign in \u2014 Claude Code (`npm i -g @anthropic-ai/claude-code`), the Codex CLI (`codex login`), or `opencode serve` \u2014 then connect it with `cabane-companion connect <harness>` (pass `--url <url>` for opencode).")
|
|
1274
1293
|
);
|
|
1275
1294
|
}
|
|
1276
1295
|
|
|
@@ -1465,7 +1484,7 @@ async function shakeOutHarness(runtime, cfg, deps = {}) {
|
|
|
1465
1484
|
try {
|
|
1466
1485
|
if (runtime === "opencode") {
|
|
1467
1486
|
const url = cfg.opencode?.serverUrl;
|
|
1468
|
-
if (!url) return "
|
|
1487
|
+
if (!url) return "absent";
|
|
1469
1488
|
return await probeOpencode(url) !== null ? "ok" : "failed";
|
|
1470
1489
|
}
|
|
1471
1490
|
const { auth, presence } = runtime === "codex" ? {
|
|
@@ -1475,11 +1494,13 @@ async function shakeOutHarness(runtime, cfg, deps = {}) {
|
|
|
1475
1494
|
auth: ["claude", ["auth", "status"]],
|
|
1476
1495
|
presence: ["claude", ["--version"]]
|
|
1477
1496
|
};
|
|
1497
|
+
const presenceRun = await run(presence[0], [...presence[1]]);
|
|
1498
|
+
if (presenceRun.error === "spawn") return "absent";
|
|
1499
|
+
if (presenceRun.code !== 0) return "unverified";
|
|
1478
1500
|
const authRun = await run(auth[0], [...auth[1]]);
|
|
1479
1501
|
if (authRun.code === 0) return "ok";
|
|
1480
1502
|
if (looksUnsupported(authRun.output)) {
|
|
1481
|
-
|
|
1482
|
-
return presenceRun.code === 0 ? "unverified" : "failed";
|
|
1503
|
+
return "unverified";
|
|
1483
1504
|
}
|
|
1484
1505
|
return "failed";
|
|
1485
1506
|
} catch {
|
|
@@ -1487,6 +1508,7 @@ async function shakeOutHarness(runtime, cfg, deps = {}) {
|
|
|
1487
1508
|
}
|
|
1488
1509
|
}
|
|
1489
1510
|
function connectedLine(runtime, verdict) {
|
|
1511
|
+
if (verdict === "absent") throw new Error("an absent harness cannot be connected");
|
|
1490
1512
|
const label = HARNESS_LABELS[runtime];
|
|
1491
1513
|
if (verdict !== "failed") return `${label} connected.`;
|
|
1492
1514
|
return `${label} connected \u2014 ${FAILED_SUFFIX[runtime]}`;
|
|
@@ -1496,6 +1518,14 @@ var FAILED_SUFFIX = {
|
|
|
1496
1518
|
codex: "it doesn\u2019t look signed in yet. Run `codex login` once, then it\u2019s ready.",
|
|
1497
1519
|
opencode: "its server isn\u2019t answering. Start `opencode serve`, then it\u2019s ready."
|
|
1498
1520
|
};
|
|
1521
|
+
function absentLine(runtime) {
|
|
1522
|
+
if (runtime === "opencode") {
|
|
1523
|
+
return "No opencode server is configured \u2014 run `opencode serve` and connect with `--url`.";
|
|
1524
|
+
}
|
|
1525
|
+
const label = HARNESS_LABELS[runtime];
|
|
1526
|
+
const login = runtime === "codex" ? "`codex login`" : "`claude`";
|
|
1527
|
+
return `${label} isn\u2019t installed on this machine \u2014 install it and sign in (${login}), then run this again.`;
|
|
1528
|
+
}
|
|
1499
1529
|
function looksUnsupported(output) {
|
|
1500
1530
|
return /unrecognized|unknown (sub)?command|unexpected argument|invalid (sub)?command|no such (sub)?command|usage:|did you mean/i.test(
|
|
1501
1531
|
output
|
|
@@ -1504,17 +1534,17 @@ function looksUnsupported(output) {
|
|
|
1504
1534
|
function runBounded(command, args) {
|
|
1505
1535
|
return new Promise((resolve) => {
|
|
1506
1536
|
let settled = false;
|
|
1507
|
-
const done = (
|
|
1537
|
+
const done = (result) => {
|
|
1508
1538
|
if (settled) return;
|
|
1509
1539
|
settled = true;
|
|
1510
1540
|
clearTimeout(timer);
|
|
1511
|
-
resolve(
|
|
1541
|
+
resolve(result);
|
|
1512
1542
|
};
|
|
1513
1543
|
let child;
|
|
1514
1544
|
try {
|
|
1515
1545
|
child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1516
1546
|
} catch {
|
|
1517
|
-
resolve({ code: null, output: "" });
|
|
1547
|
+
resolve({ code: null, output: "", error: "spawn" });
|
|
1518
1548
|
return;
|
|
1519
1549
|
}
|
|
1520
1550
|
let out = "";
|
|
@@ -1525,11 +1555,11 @@ function runBounded(command, args) {
|
|
|
1525
1555
|
child.stderr?.on("data", capture);
|
|
1526
1556
|
const timer = setTimeout(() => {
|
|
1527
1557
|
child.kill("SIGKILL");
|
|
1528
|
-
done(null, out);
|
|
1558
|
+
done({ code: null, output: out, error: "timeout" });
|
|
1529
1559
|
}, CHECK_TIMEOUT_MS);
|
|
1530
1560
|
timer.unref?.();
|
|
1531
|
-
child.once("error", () => done(null, out));
|
|
1532
|
-
child.once("exit", (code) => done(code, out));
|
|
1561
|
+
child.once("error", () => done({ code: null, output: out, error: "spawn" }));
|
|
1562
|
+
child.once("exit", (code) => done({ code, output: out }));
|
|
1533
1563
|
});
|
|
1534
1564
|
}
|
|
1535
1565
|
|
|
@@ -9268,11 +9298,13 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
9268
9298
|
});
|
|
9269
9299
|
await supervisor.start();
|
|
9270
9300
|
const connectHarness = async (runtime, serverUrl) => {
|
|
9301
|
+
const candidate = runtime === "opencode" ? { ...supervisor.currentConfig(), opencode: { serverUrl: serverUrl ?? "" } } : supervisor.currentConfig();
|
|
9302
|
+
const verdict = await shakeOutHarness(runtime, candidate);
|
|
9303
|
+
if (verdict === "absent") return { ok: false, error: absentLine(runtime) };
|
|
9271
9304
|
const result = await supervisor.enableHarness(
|
|
9272
9305
|
runtime === "opencode" ? { runtime: "opencode", serverUrl: serverUrl ?? "" } : { runtime }
|
|
9273
9306
|
);
|
|
9274
9307
|
if (!result.ok) return { ok: false, error: result.error };
|
|
9275
|
-
const verdict = await shakeOutHarness(runtime, supervisor.currentConfig());
|
|
9276
9308
|
return { ok: true, message: connectedLine(runtime, verdict) };
|
|
9277
9309
|
};
|
|
9278
9310
|
const control = await startControlServer({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect a coding agent on your machine to your Cabane workspace as a responder — drive work against your own codebase, files, and MCP servers without putting any of it in Cabane.",
|
|
6
6
|
"license": "UNLICENSED",
|