@cabane/companion 0.6.203 → 0.6.204
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 +25 -1
- package/dist/cli.js +1544 -1730
- package/dist/launch.js +5 -23
- package/dist/pairing-config.js +29 -45
- package/dist/runtime.js +92 -111
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -167,25 +167,7 @@ var prepareHookSchema = z.object({
|
|
|
167
167
|
// Wall-clock cap for the hook. Provisioning is slow (minutes), so the
|
|
168
168
|
// default is generous; a hook that hangs past this is killed and the turn
|
|
169
169
|
// fails with a clear timeout message rather than pinning the companion.
|
|
170
|
-
timeoutMs: z.number().int().positive().optional()
|
|
171
|
-
// Re-invoke the hook when a prepared result already exists, in VALIDATE
|
|
172
|
-
// mode: the cached result is handed back as `prepared` and stdout is
|
|
173
|
-
// ignored, so the cwd cannot move. Its only power is to exit non-zero and
|
|
174
|
-
// fail the turn. The hook resolves once per (conversation, agent), so a
|
|
175
|
-
// later message asking for a different environment is otherwise silently
|
|
176
|
-
// ignored; this lets a hook refuse it out loud.
|
|
177
|
-
//
|
|
178
|
-
// Default OFF: a hook that provisions unconditionally must not start
|
|
179
|
-
// running on every turn because the companion upgraded.
|
|
180
|
-
validateCached: z.boolean().optional(),
|
|
181
|
-
// Independent operator-owned reporter. Receives sanitized failure metadata
|
|
182
|
-
// on stdin; never re-enters this hook and cannot change the turn outcome.
|
|
183
|
-
failureReporter: z.object({
|
|
184
|
-
command: z.string().min(1),
|
|
185
|
-
args: z.array(z.string()).optional(),
|
|
186
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
187
|
-
timeoutMs: z.number().int().positive().max(3e4).optional()
|
|
188
|
-
}).strict().optional()
|
|
170
|
+
timeoutMs: z.number().int().positive().optional()
|
|
189
171
|
}).strict();
|
|
190
172
|
var DEFAULT_TIMEOUT_MS = 10 * 6e4;
|
|
191
173
|
var prepareResultSchema = z.object({
|
|
@@ -260,8 +242,7 @@ var companionConfigSchema = z2.object({
|
|
|
260
242
|
// The DEVICE-LEVEL prepare hook, for every agent assigned here unless its own
|
|
261
243
|
// `agents` row names one. Per-agent hooks alone make the config a second
|
|
262
244
|
// silent roster: an agent nobody listed gets no hook and no error, and starts
|
|
263
|
-
// in the operator's `$HOME
|
|
264
|
-
// 2026-08-17).
|
|
245
|
+
// in the operator's `$HOME`.
|
|
265
246
|
prepareHook: prepareHookSchema.optional(),
|
|
266
247
|
// `dashboardPort` and `autoOpen` are IGNORED by the CLI (no port, no browser)
|
|
267
248
|
// but stay in the schema so an existing file still parses; the Electron shell
|
|
@@ -298,9 +279,10 @@ var companionConfigSchema = z2.object({
|
|
|
298
279
|
root: z2.string().min(1).refine(isRootPath, { message: "must be an absolute path, or start with ~/" }).optional(),
|
|
299
280
|
// An argv prefix the operator puts in front of every turn's runner: the companion starts
|
|
300
281
|
// `<prefix…> <its own run-turn command…>` instead of the runner bare. What the prefix does
|
|
301
|
-
// is the operator's (a
|
|
282
|
+
// is the operator's (a container or VM per turn, say); the companion knows nothing of it
|
|
302
283
|
// beyond handing the runner over, with the turn's inputs in its environment and the
|
|
303
|
-
// credential and secrets on its stdin (`runner/launch.ts`, `
|
|
284
|
+
// credential and secrets on its stdin (`runner/launch.ts`, `wrapped`). The README lists
|
|
285
|
+
// what a wrapper can rely on.
|
|
304
286
|
runner: z2.object({
|
|
305
287
|
command: z2.array(z2.string().min(1)).min(1)
|
|
306
288
|
}).strict().optional()
|
|
@@ -312,33 +294,38 @@ function workRoot(cfg) {
|
|
|
312
294
|
if (root.startsWith("~/")) return join4(homedir5(), root.slice(2));
|
|
313
295
|
return root;
|
|
314
296
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
297
|
+
var RETIRED_ROW_KEYS = ["sandbox"];
|
|
298
|
+
var RETIRED_HOOK_KEYS = ["validateCached", "failureReporter"];
|
|
299
|
+
function isObject(value) {
|
|
300
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
301
|
+
}
|
|
302
|
+
function splitRetired(raw) {
|
|
318
303
|
const keys = [];
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
304
|
+
const without = (obj, names, path) => {
|
|
305
|
+
const out = {};
|
|
306
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
307
|
+
if (names.includes(k)) keys.push(`${path}${k}`);
|
|
308
|
+
else out[k] = v;
|
|
309
|
+
}
|
|
310
|
+
return out;
|
|
311
|
+
};
|
|
312
|
+
const hook = (row, path) => isObject(row.prepareHook) ? { ...row, prepareHook: without(row.prepareHook, RETIRED_HOOK_KEYS, `${path}prepareHook.`) } : row;
|
|
313
|
+
const kept = hook(without(raw, ["runners"], ""), "");
|
|
314
|
+
if (isObject(kept.agents)) {
|
|
315
|
+
const agents = {};
|
|
316
|
+
for (const [key, row] of Object.entries(kept.agents)) {
|
|
317
|
+
const path = `agents.${key}.`;
|
|
318
|
+
agents[key] = isObject(row) ? hook(without(row, RETIRED_ROW_KEYS, path), path) : row;
|
|
324
319
|
}
|
|
320
|
+
kept.agents = agents;
|
|
325
321
|
}
|
|
326
|
-
return keys;
|
|
322
|
+
return { keys, kept };
|
|
323
|
+
}
|
|
324
|
+
function retiredConfigKeys(raw) {
|
|
325
|
+
return isObject(raw) ? splitRetired(raw).keys : [];
|
|
327
326
|
}
|
|
328
327
|
function withoutRetiredKeys(raw) {
|
|
329
|
-
|
|
330
|
-
const { runners: _runners, ...rest } = raw;
|
|
331
|
-
if (!rest.agents || typeof rest.agents !== "object") return rest;
|
|
332
|
-
const agents = {};
|
|
333
|
-
for (const [key, row] of Object.entries(rest.agents)) {
|
|
334
|
-
if (row && typeof row === "object" && "sandbox" in row) {
|
|
335
|
-
const { sandbox: _sandbox, ...kept } = row;
|
|
336
|
-
agents[key] = kept;
|
|
337
|
-
} else {
|
|
338
|
-
agents[key] = row;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
return { ...rest, agents };
|
|
328
|
+
return isObject(raw) ? splitRetired(raw).kept : raw;
|
|
342
329
|
}
|
|
343
330
|
function retiredConfigKeysOnDisk() {
|
|
344
331
|
try {
|
|
@@ -1598,7 +1585,7 @@ var emptyHeartbeatStatus = () => ({
|
|
|
1598
1585
|
});
|
|
1599
1586
|
|
|
1600
1587
|
// src/supervisor.ts
|
|
1601
|
-
import { hostname
|
|
1588
|
+
import { hostname } from "os";
|
|
1602
1589
|
|
|
1603
1590
|
// packages/agent-runtime/src/failure.ts
|
|
1604
1591
|
import { z as z4 } from "zod";
|
|
@@ -6378,7 +6365,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
6378
6365
|
if (signal.aborted) return;
|
|
6379
6366
|
if (compactions === null) {
|
|
6380
6367
|
deps.onWarn?.(
|
|
6381
|
-
"codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions
|
|
6368
|
+
"codex adapter: the thread's compaction count could not be read \u2014 re-sending the standing instructions",
|
|
6382
6369
|
{ threadId: decision.resume }
|
|
6383
6370
|
);
|
|
6384
6371
|
} else if (decision.compactions === compactions) {
|
|
@@ -6388,7 +6375,7 @@ function createCodexAdapter(deps = {}) {
|
|
|
6388
6375
|
};
|
|
6389
6376
|
} else if (decision.compactions !== null && compactions > decision.compactions) {
|
|
6390
6377
|
deps.onWarn?.(
|
|
6391
|
-
"codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions
|
|
6378
|
+
"codex adapter: the thread compacted since the last turn \u2014 re-sending the standing instructions",
|
|
6392
6379
|
{
|
|
6393
6380
|
threadId: decision.resume,
|
|
6394
6381
|
compactions,
|
|
@@ -7539,8 +7526,6 @@ var ConnectorHealthStore = class {
|
|
|
7539
7526
|
|
|
7540
7527
|
// src/dispatcher.ts
|
|
7541
7528
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7542
|
-
import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
7543
|
-
import { join as join21 } from "path";
|
|
7544
7529
|
|
|
7545
7530
|
// src/runner/launch.ts
|
|
7546
7531
|
import { createInterface } from "readline";
|
|
@@ -7790,7 +7775,7 @@ function selfRunnerCommand() {
|
|
|
7790
7775
|
function wrappedRunnerCommand(prefix, own) {
|
|
7791
7776
|
return [...prefix ?? [], ...own];
|
|
7792
7777
|
}
|
|
7793
|
-
var
|
|
7778
|
+
var WRAPPED_ENV_DROPPED = [
|
|
7794
7779
|
"ANTHROPIC_API_KEY",
|
|
7795
7780
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
7796
7781
|
"CODEX_API_KEY",
|
|
@@ -7799,11 +7784,11 @@ var BOXED_ENV_DROPPED = [
|
|
|
7799
7784
|
"DBUS_SESSION_BUS_ADDRESS",
|
|
7800
7785
|
"XDG_RUNTIME_DIR"
|
|
7801
7786
|
];
|
|
7802
|
-
function runnerExecEnv(base,
|
|
7787
|
+
function runnerExecEnv(base, wrapped) {
|
|
7803
7788
|
const env = {};
|
|
7804
7789
|
for (const [k, v] of Object.entries(base)) {
|
|
7805
7790
|
if (v === void 0) continue;
|
|
7806
|
-
if (
|
|
7791
|
+
if (wrapped && WRAPPED_ENV_DROPPED.includes(k)) continue;
|
|
7807
7792
|
env[k] = v;
|
|
7808
7793
|
}
|
|
7809
7794
|
return env;
|
|
@@ -8025,13 +8010,13 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
8025
8010
|
return { ok: false, durationMs: durationMs2, reason: reason2 };
|
|
8026
8011
|
}
|
|
8027
8012
|
try {
|
|
8028
|
-
const
|
|
8029
|
-
const env = runnerExecEnv(launch.env ?? process.env,
|
|
8013
|
+
const wrapped = launch.wrapped === true;
|
|
8014
|
+
const env = runnerExecEnv(launch.env ?? process.env, wrapped);
|
|
8030
8015
|
const grants = minted.deviceGrants.length > 0 ? launch.grants?.(minted.deviceGrants) ?? {} : {};
|
|
8031
8016
|
const input = {
|
|
8032
8017
|
baseUrl: opts.baseUrl,
|
|
8033
8018
|
credential: minted.credential,
|
|
8034
|
-
...
|
|
8019
|
+
...wrapped ? { secrets: launch.secrets?.() ?? {} } : {},
|
|
8035
8020
|
...Object.keys(grants).length > 0 ? { grants } : {},
|
|
8036
8021
|
dispatch: payload,
|
|
8037
8022
|
turnId,
|
|
@@ -8105,7 +8090,7 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
8105
8090
|
locks?.release(lockOwner(event.id));
|
|
8106
8091
|
return;
|
|
8107
8092
|
case "process_group":
|
|
8108
|
-
if (!
|
|
8093
|
+
if (!wrapped) groups.push(event.pgid);
|
|
8109
8094
|
return;
|
|
8110
8095
|
case "remote_session":
|
|
8111
8096
|
remote.push({ serverUrl: event.serverUrl, sessionId: event.sessionId });
|
|
@@ -8180,7 +8165,7 @@ async function runTurnInRunner(opts, launch, duties, payload, handleOpts) {
|
|
|
8180
8165
|
|
|
8181
8166
|
// src/turn-execution.ts
|
|
8182
8167
|
import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
|
|
8183
|
-
import { mkdirSync as mkdirSync9 } from "fs";
|
|
8168
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
|
|
8184
8169
|
|
|
8185
8170
|
// src/grants.ts
|
|
8186
8171
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
@@ -8234,7 +8219,7 @@ function loadGrantsFile(report) {
|
|
|
8234
8219
|
const result = grantsFileSchema.safeParse(parsed);
|
|
8235
8220
|
if (!result.success) {
|
|
8236
8221
|
report.unreadable(
|
|
8237
|
-
`${path} must map a secret's variable name to an object of "VARIABLE": "value" pairs (e.g. { "
|
|
8222
|
+
`${path} must map a secret's variable name to an object of "VARIABLE": "value" pairs (e.g. { "HAS_STAGING_DB": { "DATABASE_URL": "postgres://\u2026" } }).`
|
|
8238
8223
|
);
|
|
8239
8224
|
return {};
|
|
8240
8225
|
}
|
|
@@ -8277,10 +8262,6 @@ import { homedir as homedir7 } from "os";
|
|
|
8277
8262
|
import { join as join17 } from "path";
|
|
8278
8263
|
import { createInterface as createInterface2 } from "readline";
|
|
8279
8264
|
|
|
8280
|
-
// src/prepare-failure.ts
|
|
8281
|
-
import { spawn as spawn3 } from "child_process";
|
|
8282
|
-
import { hostname } from "os";
|
|
8283
|
-
|
|
8284
8265
|
// src/prepared.ts
|
|
8285
8266
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "fs";
|
|
8286
8267
|
import { join as join18 } from "path";
|
|
@@ -8356,7 +8337,7 @@ function requiredSecretNames(mcpServers) {
|
|
|
8356
8337
|
}
|
|
8357
8338
|
|
|
8358
8339
|
// src/harness-usable.ts
|
|
8359
|
-
import { spawn as
|
|
8340
|
+
import { spawn as spawn3 } from "child_process";
|
|
8360
8341
|
|
|
8361
8342
|
// src/harness-versions.ts
|
|
8362
8343
|
var EMPTY = { claudeCode: null, opencode: null, codex: null };
|
|
@@ -8459,7 +8440,7 @@ function runBounded(path, args, env) {
|
|
|
8459
8440
|
};
|
|
8460
8441
|
let child;
|
|
8461
8442
|
try {
|
|
8462
|
-
child =
|
|
8443
|
+
child = spawn3(path, args, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
8463
8444
|
} catch {
|
|
8464
8445
|
resolve({ code: null, output: "", error: "spawn" });
|
|
8465
8446
|
return;
|
|
@@ -8626,7 +8607,7 @@ function hostReport() {
|
|
|
8626
8607
|
}
|
|
8627
8608
|
|
|
8628
8609
|
// src/providers/local-process.ts
|
|
8629
|
-
import { spawn as
|
|
8610
|
+
import { spawn as spawn4 } from "child_process";
|
|
8630
8611
|
import { constants as constants2 } from "os";
|
|
8631
8612
|
var DEFAULT_KILL_GRACE_MS = 3e4;
|
|
8632
8613
|
var DEFAULT_REAP_GRACE_MS = 3e3;
|
|
@@ -8637,7 +8618,7 @@ function exitCode(code, signal) {
|
|
|
8637
8618
|
}
|
|
8638
8619
|
function createLocalProcessProvider(opts = {}) {
|
|
8639
8620
|
const killGraceMs = opts.killGraceMs ?? DEFAULT_KILL_GRACE_MS;
|
|
8640
|
-
const spawnFn = opts.spawnFn ??
|
|
8621
|
+
const spawnFn = opts.spawnFn ?? spawn4;
|
|
8641
8622
|
const reapGraceMs = opts.reapGraceMs ?? DEFAULT_REAP_GRACE_MS;
|
|
8642
8623
|
const killFn = opts.killFn ?? ((pid, signal) => process.kill(pid, signal));
|
|
8643
8624
|
const groupAlive = (pgid) => {
|
|
@@ -8841,7 +8822,7 @@ function checkReason(check) {
|
|
|
8841
8822
|
|
|
8842
8823
|
// src/harness-copy.ts
|
|
8843
8824
|
import { homedir as homedir8 } from "os";
|
|
8844
|
-
import { join as
|
|
8825
|
+
import { join as join21 } from "path";
|
|
8845
8826
|
var HARNESS_LABELS = {
|
|
8846
8827
|
"claude-code": "Claude Code",
|
|
8847
8828
|
codex: "Codex",
|
|
@@ -8855,7 +8836,7 @@ function failureText(runtime, reason) {
|
|
|
8855
8836
|
case "spawn":
|
|
8856
8837
|
return `Couldn't check whether ${label} is signed in: Cabane's bundled ${label} wouldn't start. Repair it with ${repairSteps()} and try again.${spaced(repairCost())}`;
|
|
8857
8838
|
case "config":
|
|
8858
|
-
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(
|
|
8839
|
+
return `Couldn't check whether ${label} is signed in: its configuration file didn't load${runtime === "codex" ? ` (${tildePath(join21(homedir8(), ".codex", "config.toml"))})` : ""}. Fix it, then try again.`;
|
|
8859
8840
|
case "unreadable":
|
|
8860
8841
|
return `Couldn't check whether ${label} is signed in: it answered in a way Cabane couldn't read. Try again, and if it keeps happening, update Cabane.`;
|
|
8861
8842
|
}
|
|
@@ -9114,13 +9095,13 @@ var DispatchClaims = class {
|
|
|
9114
9095
|
import {
|
|
9115
9096
|
existsSync as existsSync14,
|
|
9116
9097
|
mkdirSync as mkdirSync10,
|
|
9117
|
-
readdirSync as
|
|
9098
|
+
readdirSync as readdirSync2,
|
|
9118
9099
|
readFileSync as readFileSync11,
|
|
9119
9100
|
renameSync as renameSync5,
|
|
9120
9101
|
rmSync as rmSync7,
|
|
9121
9102
|
writeFileSync as writeFileSync6
|
|
9122
9103
|
} from "fs";
|
|
9123
|
-
import { join as
|
|
9104
|
+
import { join as join22 } from "path";
|
|
9124
9105
|
var MAX_ENTRIES = 2e3;
|
|
9125
9106
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
9126
9107
|
var Outbox = class {
|
|
@@ -9133,10 +9114,10 @@ var Outbox = class {
|
|
|
9133
9114
|
// Resolved lazily (per call) so tests that swap HOME between
|
|
9134
9115
|
// cases route writes at the right tmpdir.
|
|
9135
9116
|
dir() {
|
|
9136
|
-
return
|
|
9117
|
+
return join22(cabaneDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
9137
9118
|
}
|
|
9138
9119
|
fileFor(turnId, seq) {
|
|
9139
|
-
return
|
|
9120
|
+
return join22(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
9140
9121
|
}
|
|
9141
9122
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
9142
9123
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -9171,14 +9152,14 @@ var Outbox = class {
|
|
|
9171
9152
|
if (!existsSync14(dir2)) return [];
|
|
9172
9153
|
let names;
|
|
9173
9154
|
try {
|
|
9174
|
-
names =
|
|
9155
|
+
names = readdirSync2(dir2);
|
|
9175
9156
|
} catch {
|
|
9176
9157
|
return [];
|
|
9177
9158
|
}
|
|
9178
9159
|
const entries = [];
|
|
9179
9160
|
for (const name of names) {
|
|
9180
9161
|
if (!name.endsWith(".json")) continue;
|
|
9181
|
-
const full =
|
|
9162
|
+
const full = join22(dir2, name);
|
|
9182
9163
|
try {
|
|
9183
9164
|
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
9184
9165
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -9215,7 +9196,7 @@ var Outbox = class {
|
|
|
9215
9196
|
maxSeqForTurn(turnId) {
|
|
9216
9197
|
const dir2 = this.dir();
|
|
9217
9198
|
if (!existsSync14(dir2)) return 0;
|
|
9218
|
-
const names =
|
|
9199
|
+
const names = readdirSync2(dir2);
|
|
9219
9200
|
const prefix = `${encodeURIComponent(turnId)}__`;
|
|
9220
9201
|
let max = 0;
|
|
9221
9202
|
for (const name of names) {
|
|
@@ -9236,7 +9217,7 @@ var Outbox = class {
|
|
|
9236
9217
|
const dir2 = this.dir();
|
|
9237
9218
|
if (!existsSync14(dir2)) return 0;
|
|
9238
9219
|
try {
|
|
9239
|
-
return
|
|
9220
|
+
return readdirSync2(dir2).filter((n) => n.endsWith(".json")).length;
|
|
9240
9221
|
} catch {
|
|
9241
9222
|
return 0;
|
|
9242
9223
|
}
|
|
@@ -9400,9 +9381,9 @@ var SseSubscriber = class {
|
|
|
9400
9381
|
};
|
|
9401
9382
|
|
|
9402
9383
|
// src/updater.ts
|
|
9403
|
-
import { spawn as
|
|
9384
|
+
import { spawn as spawn5 } from "child_process";
|
|
9404
9385
|
import { copyFileSync as copyFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync14, renameSync as renameSync9, rmSync as rmSync10 } from "fs";
|
|
9405
|
-
import { dirname as dirname11, join as
|
|
9386
|
+
import { dirname as dirname11, join as join26 } from "path";
|
|
9406
9387
|
|
|
9407
9388
|
// src/boot-rule.ts
|
|
9408
9389
|
import {
|
|
@@ -9414,7 +9395,7 @@ import {
|
|
|
9414
9395
|
symlinkSync,
|
|
9415
9396
|
writeFileSync as writeFileSync7
|
|
9416
9397
|
} from "fs";
|
|
9417
|
-
import { dirname as dirname8, join as
|
|
9398
|
+
import { dirname as dirname8, join as join23 } from "path";
|
|
9418
9399
|
var UNCONFIRMED_WINDOW_MS = 10 * 6e4;
|
|
9419
9400
|
var MAX_STAGE_ATTEMPTS = 3;
|
|
9420
9401
|
function superseded(pending, ...running) {
|
|
@@ -9441,32 +9422,32 @@ import {
|
|
|
9441
9422
|
lstatSync,
|
|
9442
9423
|
mkdirSync as mkdirSync12,
|
|
9443
9424
|
readFileSync as readFileSync13,
|
|
9444
|
-
readdirSync as
|
|
9425
|
+
readdirSync as readdirSync3,
|
|
9445
9426
|
realpathSync as realpathSync5,
|
|
9446
9427
|
renameSync as renameSync7,
|
|
9447
9428
|
rmSync as rmSync9,
|
|
9448
9429
|
symlinkSync as symlinkSync2
|
|
9449
9430
|
} from "fs";
|
|
9450
|
-
import { dirname as dirname9, join as
|
|
9431
|
+
import { dirname as dirname9, join as join24 } from "path";
|
|
9451
9432
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
9452
9433
|
function versionsDir() {
|
|
9453
|
-
return
|
|
9434
|
+
return join24(cabaneDir(), "versions");
|
|
9454
9435
|
}
|
|
9455
9436
|
function currentLink() {
|
|
9456
|
-
return
|
|
9437
|
+
return join24(cabaneDir(), "current");
|
|
9457
9438
|
}
|
|
9458
9439
|
function bootGuardPath() {
|
|
9459
|
-
return
|
|
9440
|
+
return join24(cabaneDir(), "boot-guard.mjs");
|
|
9460
9441
|
}
|
|
9461
9442
|
function versionPackageRoot(version) {
|
|
9462
|
-
return
|
|
9443
|
+
return join24(versionsDir(), version, "node_modules", "@cabane", "companion");
|
|
9463
9444
|
}
|
|
9464
9445
|
function ownPackageRoot(moduleUrl = import.meta.url) {
|
|
9465
9446
|
return dirname9(dirname9(fileURLToPath5(moduleUrl)));
|
|
9466
9447
|
}
|
|
9467
9448
|
function packageVersion(root) {
|
|
9468
9449
|
try {
|
|
9469
|
-
const pkg2 = JSON.parse(readFileSync13(
|
|
9450
|
+
const pkg2 = JSON.parse(readFileSync13(join24(root, "package.json"), "utf8"));
|
|
9470
9451
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
9471
9452
|
} catch {
|
|
9472
9453
|
return null;
|
|
@@ -9475,7 +9456,7 @@ function packageVersion(root) {
|
|
|
9475
9456
|
function currentRoot() {
|
|
9476
9457
|
try {
|
|
9477
9458
|
const root = realpathSync5(currentLink());
|
|
9478
|
-
return existsSync15(
|
|
9459
|
+
return existsSync15(join24(root, "dist", "cli.js")) ? root : null;
|
|
9479
9460
|
} catch {
|
|
9480
9461
|
return null;
|
|
9481
9462
|
}
|
|
@@ -9496,9 +9477,9 @@ function pruneVersions(keep) {
|
|
|
9496
9477
|
const dir2 = versionsDir();
|
|
9497
9478
|
if (!existsSync15(dir2)) return [];
|
|
9498
9479
|
const removed = [];
|
|
9499
|
-
for (const name of
|
|
9480
|
+
for (const name of readdirSync3(dir2)) {
|
|
9500
9481
|
if (keep.includes(name)) continue;
|
|
9501
|
-
rmSync9(
|
|
9482
|
+
rmSync9(join24(dir2, name), { recursive: true, force: true });
|
|
9502
9483
|
removed.push(name);
|
|
9503
9484
|
}
|
|
9504
9485
|
return removed;
|
|
@@ -9517,7 +9498,7 @@ function compareVersions(a, b) {
|
|
|
9517
9498
|
|
|
9518
9499
|
// src/update-state.ts
|
|
9519
9500
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
9520
|
-
import { dirname as dirname10, join as
|
|
9501
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
9521
9502
|
import { z as z18 } from "zod";
|
|
9522
9503
|
var resultSchema = z18.object({
|
|
9523
9504
|
kind: z18.enum(["updated", "rolled_back", "failed"]),
|
|
@@ -9541,7 +9522,7 @@ var stateSchema = z18.object({
|
|
|
9541
9522
|
failed: z18.object({ version: z18.string(), attempts: z18.number().int(), at: z18.string() }).optional()
|
|
9542
9523
|
});
|
|
9543
9524
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
9544
|
-
return
|
|
9525
|
+
return join25(dir2, "update.json");
|
|
9545
9526
|
}
|
|
9546
9527
|
function readUpdateState(path = updateStatePath()) {
|
|
9547
9528
|
try {
|
|
@@ -9590,7 +9571,7 @@ function rollBack(pending, reason, now = /* @__PURE__ */ new Date()) {
|
|
|
9590
9571
|
return updateUpdateState((state) => rolledBackState(state, pending, reason, now));
|
|
9591
9572
|
}
|
|
9592
9573
|
function writeBootGuard(root = ownPackageRoot()) {
|
|
9593
|
-
const built =
|
|
9574
|
+
const built = join26(root, "dist", "boot-guard.js");
|
|
9594
9575
|
if (!existsSync16(built)) return false;
|
|
9595
9576
|
const target = bootGuardPath();
|
|
9596
9577
|
mkdirSync14(dirname11(target), { recursive: true });
|
|
@@ -9607,7 +9588,7 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve) => {
|
|
|
9607
9588
|
let stderr = "";
|
|
9608
9589
|
let child;
|
|
9609
9590
|
try {
|
|
9610
|
-
child =
|
|
9591
|
+
child = spawn5(command, [...args], {
|
|
9611
9592
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9612
9593
|
env: opts.env ?? process.env
|
|
9613
9594
|
});
|
|
@@ -9630,17 +9611,17 @@ var runProcess = (command, args, opts = {}) => new Promise((resolve) => {
|
|
|
9630
9611
|
async function stageVersion(version, harnesses, deps = {}) {
|
|
9631
9612
|
const run = deps.run ?? runProcess;
|
|
9632
9613
|
const node = deps.execPath ?? process.execPath;
|
|
9633
|
-
const target =
|
|
9614
|
+
const target = join26(versionsDir(), version);
|
|
9634
9615
|
const placed = versionPackageRoot(version);
|
|
9635
9616
|
if (existsSync16(placed)) {
|
|
9636
9617
|
if (!await verifyInstall(placed, version, harnesses, { run, execPath: node }))
|
|
9637
9618
|
return { ok: true, root: placed };
|
|
9638
9619
|
}
|
|
9639
|
-
const npm =
|
|
9620
|
+
const npm = join26(dirname11(node), "npm");
|
|
9640
9621
|
if (!existsSync16(npm))
|
|
9641
9622
|
return { ok: false, reason: `there is no npm beside Node at ${dirname11(node)}` };
|
|
9642
9623
|
mkdirSync14(versionsDir(), { recursive: true });
|
|
9643
|
-
const staging =
|
|
9624
|
+
const staging = join26(versionsDir(), `.${version}.staging-${process.pid}`);
|
|
9644
9625
|
rmSync10(staging, { recursive: true, force: true });
|
|
9645
9626
|
const installed = await run(
|
|
9646
9627
|
npm,
|
|
@@ -9669,7 +9650,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
9669
9650
|
return { ok: false, reason: `version ${version} isn't published yet`, unpublished: true };
|
|
9670
9651
|
return { ok: false, reason: `npm couldn't install it: ${lastLine(installed.stderr)}` };
|
|
9671
9652
|
}
|
|
9672
|
-
const root =
|
|
9653
|
+
const root = join26(staging, "node_modules", "@cabane", "companion");
|
|
9673
9654
|
const verdict = await verifyInstall(root, version, harnesses, { run, execPath: node });
|
|
9674
9655
|
if (verdict) {
|
|
9675
9656
|
rmSync10(staging, { recursive: true, force: true });
|
|
@@ -9681,7 +9662,7 @@ async function stageVersion(version, harnesses, deps = {}) {
|
|
|
9681
9662
|
}
|
|
9682
9663
|
async function verifyInstall(root, version, harnesses, deps) {
|
|
9683
9664
|
if (packageVersion(root) !== version) return `the installed package isn't version ${version}`;
|
|
9684
|
-
const answered = await deps.run(deps.execPath, [
|
|
9665
|
+
const answered = await deps.run(deps.execPath, [join26(root, "dist", "bin.js"), "--version"], {
|
|
9685
9666
|
timeoutMs: 6e4,
|
|
9686
9667
|
env: { ...process.env, CABANE_LAUNCH_SELF: "1" }
|
|
9687
9668
|
});
|
|
@@ -9689,7 +9670,7 @@ async function verifyInstall(root, version, harnesses, deps) {
|
|
|
9689
9670
|
return `it doesn't start: ${lastLine(answered.stderr) || "no version printed"}`;
|
|
9690
9671
|
}
|
|
9691
9672
|
if (harnesses.length === 0) return null;
|
|
9692
|
-
const probed = await deps.run(deps.execPath, [
|
|
9673
|
+
const probed = await deps.run(deps.execPath, [join26(root, "dist", "postinstall.js"), "--json"], {
|
|
9693
9674
|
timeoutMs: 6e4
|
|
9694
9675
|
});
|
|
9695
9676
|
let resolutions = {};
|
|
@@ -10106,13 +10087,13 @@ var CompanionSupervisor = class {
|
|
|
10106
10087
|
if (this.stopped) return;
|
|
10107
10088
|
this.log.info(
|
|
10108
10089
|
{ protocolVersion: TURN_PROTOCOL_VERSION, version: COMPANION_VERSION },
|
|
10109
|
-
`Companion ${COMPANION_VERSION} starting on ${this.config.deviceLabel ??
|
|
10090
|
+
`Companion ${COMPANION_VERSION} starting on ${this.config.deviceLabel ?? hostname()}`
|
|
10110
10091
|
);
|
|
10111
10092
|
const retired = retiredConfigKeysOnDisk();
|
|
10112
10093
|
if (retired.length > 0) {
|
|
10113
10094
|
this.log.warn(
|
|
10114
10095
|
{ keys: retired },
|
|
10115
|
-
`config.json: ${retired.join(", ")} ${retired.length === 1 ? "is" : "are"} no longer read and can be removed
|
|
10096
|
+
`config.json: ${retired.join(", ")} ${retired.length === 1 ? "is" : "are"} no longer read and can be removed.`
|
|
10116
10097
|
);
|
|
10117
10098
|
}
|
|
10118
10099
|
if (this.config.deviceToken) {
|
|
@@ -10152,7 +10133,7 @@ var CompanionSupervisor = class {
|
|
|
10152
10133
|
log: this.log,
|
|
10153
10134
|
lastEventId: null,
|
|
10154
10135
|
onOpen: () => {
|
|
10155
|
-
this.log.info(`Connected to Cabane as ${this.hub.statusJson().device_label ??
|
|
10136
|
+
this.log.info(`Connected to Cabane as ${this.hub.statusJson().device_label ?? hostname()}`);
|
|
10156
10137
|
this.deviceConnected = true;
|
|
10157
10138
|
for (const wr of this.workspaces.values()) {
|
|
10158
10139
|
this.hub.setConnected(wr.workspaceId, true);
|
|
@@ -10698,9 +10679,9 @@ var CompanionSupervisor = class {
|
|
|
10698
10679
|
this.runnerCommand ?? selfRunnerCommand()
|
|
10699
10680
|
),
|
|
10700
10681
|
...this.config.runner?.command ? {
|
|
10701
|
-
|
|
10682
|
+
wrapped: true,
|
|
10702
10683
|
secrets: () => loadSecretValuesTolerant(
|
|
10703
|
-
(m) => this.log.warn({ err: m }, "Could not read the secrets file for a
|
|
10684
|
+
(m) => this.log.warn({ err: m }, "Could not read the secrets file for a wrapped turn.")
|
|
10704
10685
|
)
|
|
10705
10686
|
} : {},
|
|
10706
10687
|
// Read per dispatch, for the secret variables its mint named and no others.
|
|
@@ -11379,9 +11360,9 @@ async function waitForBoundedHeartbeat(heartbeat) {
|
|
|
11379
11360
|
}
|
|
11380
11361
|
function defaultReexec() {
|
|
11381
11362
|
clearRuntimeState();
|
|
11382
|
-
void import("child_process").then(({ spawn:
|
|
11363
|
+
void import("child_process").then(({ spawn: spawn6 }) => {
|
|
11383
11364
|
try {
|
|
11384
|
-
const child =
|
|
11365
|
+
const child = spawn6(process.execPath, process.argv.slice(1), {
|
|
11385
11366
|
stdio: "inherit",
|
|
11386
11367
|
detached: false
|
|
11387
11368
|
});
|
|
@@ -11452,9 +11433,9 @@ function handleUncaught(log, err, origin) {
|
|
|
11452
11433
|
|
|
11453
11434
|
// src/crash-marker.ts
|
|
11454
11435
|
import { existsSync as existsSync17, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
11455
|
-
import { join as
|
|
11436
|
+
import { join as join27 } from "path";
|
|
11456
11437
|
function crashMarkerPath() {
|
|
11457
|
-
return
|
|
11438
|
+
return join27(cabaneDir(), "last-error.json");
|
|
11458
11439
|
}
|
|
11459
11440
|
function recordCrash(rec) {
|
|
11460
11441
|
try {
|
|
@@ -11474,7 +11455,7 @@ function clearCrash() {
|
|
|
11474
11455
|
// src/stray-companion.ts
|
|
11475
11456
|
import { spawnSync } from "child_process";
|
|
11476
11457
|
import { readFileSync as readFileSync16 } from "fs";
|
|
11477
|
-
import { join as
|
|
11458
|
+
import { join as join28 } from "path";
|
|
11478
11459
|
var COMPANION_ENTRY = /@cabane\/companion\/|cabane-companion|(^|[\s/])cabane\s|\.cabane\/(versions|current|boot-guard)/;
|
|
11479
11460
|
var START = /\sstart(\s|$)/;
|
|
11480
11461
|
function companionStarts(psOutput) {
|
|
@@ -11491,7 +11472,7 @@ function companionStarts(psOutput) {
|
|
|
11491
11472
|
}
|
|
11492
11473
|
function stateDirOf(env) {
|
|
11493
11474
|
const home = env.CABANE_COMPANION_HOME || env.HOME;
|
|
11494
|
-
return home ?
|
|
11475
|
+
return home ? join28(home, ".cabane") : null;
|
|
11495
11476
|
}
|
|
11496
11477
|
function envFromPsE(output, command) {
|
|
11497
11478
|
const line = output.replace(/\n$/, "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.204",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Cabane Companion (headless): connect an 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",
|