@cabane/companion 0.6.187 → 0.6.188
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 +203 -88
- package/dist/runtime.js +72 -45
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12420,14 +12420,19 @@ var DeviceApi = class {
|
|
|
12420
12420
|
return parsed;
|
|
12421
12421
|
}
|
|
12422
12422
|
// Pull the agents this device should run, across all the owner's workspaces.
|
|
12423
|
-
getAssignments() {
|
|
12424
|
-
return this.request("GET", "/api/companion/assignments");
|
|
12423
|
+
getAssignments(signal) {
|
|
12424
|
+
return this.request("GET", "/api/companion/assignments", void 0, requestSignal(signal));
|
|
12425
12425
|
}
|
|
12426
12426
|
// Ask again for the credential of an agent assigned to this device, when the saved copy is
|
|
12427
12427
|
// gone. The previous credential stops working; any agent not assigned here is a 404, and a
|
|
12428
12428
|
// second ask inside a minute a 429.
|
|
12429
|
-
reissueAgentCredential(agentId) {
|
|
12430
|
-
return this.request(
|
|
12429
|
+
reissueAgentCredential(agentId, signal) {
|
|
12430
|
+
return this.request(
|
|
12431
|
+
"POST",
|
|
12432
|
+
`/api/companion/agents/${encodeURIComponent(agentId)}/credential`,
|
|
12433
|
+
void 0,
|
|
12434
|
+
requestSignal(signal, 3e4)
|
|
12435
|
+
);
|
|
12431
12436
|
}
|
|
12432
12437
|
getRuns(cursor) {
|
|
12433
12438
|
const query = new URLSearchParams({ limit: "100", order: "asc" });
|
|
@@ -12467,6 +12472,10 @@ function errorMessage2(status2, body) {
|
|
|
12467
12472
|
if (typeof body === "string" && body.length > 0) return `${status2} ${body.slice(0, 200)}`;
|
|
12468
12473
|
return `${status2} error`;
|
|
12469
12474
|
}
|
|
12475
|
+
function requestSignal(signal, timeoutMs = 5e3) {
|
|
12476
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
12477
|
+
return signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
12478
|
+
}
|
|
12470
12479
|
|
|
12471
12480
|
// src/supervisor.ts
|
|
12472
12481
|
init_config();
|
|
@@ -13842,6 +13851,7 @@ var CompanionSupervisor = class {
|
|
|
13842
13851
|
pollTimer = null;
|
|
13843
13852
|
refreshing = false;
|
|
13844
13853
|
stopped = false;
|
|
13854
|
+
reconciliationAbort = new AbortController();
|
|
13845
13855
|
// The version-skew warning is logged once, not every heartbeat.
|
|
13846
13856
|
versionSkewWarned = false;
|
|
13847
13857
|
// This device's id, captured from the heartbeat / assignments response. The SSE
|
|
@@ -13887,6 +13897,7 @@ var CompanionSupervisor = class {
|
|
|
13887
13897
|
// heartbeat + poll loops. A companion with no device token (logged out) does
|
|
13888
13898
|
// nothing but say so.
|
|
13889
13899
|
async start() {
|
|
13900
|
+
if (this.stopped) return;
|
|
13890
13901
|
this.log.info(
|
|
13891
13902
|
{ protocolVersion: TURN_PROTOCOL_VERSION, version: COMPANION_VERSION },
|
|
13892
13903
|
`Companion ${COMPANION_VERSION} starting on ${this.config.deviceLabel ?? hostname2()}`
|
|
@@ -13971,6 +13982,7 @@ var CompanionSupervisor = class {
|
|
|
13971
13982
|
});
|
|
13972
13983
|
this.updater.start();
|
|
13973
13984
|
await this.refreshAssignments();
|
|
13985
|
+
if (this.stopped) return;
|
|
13974
13986
|
this.deviceSub.start();
|
|
13975
13987
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
13976
13988
|
this.heartbeatTimer.unref?.();
|
|
@@ -14083,10 +14095,12 @@ var CompanionSupervisor = class {
|
|
|
14083
14095
|
let items;
|
|
14084
14096
|
let device;
|
|
14085
14097
|
try {
|
|
14086
|
-
const resp = await this.deviceApi.getAssignments();
|
|
14098
|
+
const resp = await this.deviceApi.getAssignments(this.reconciliationAbort.signal);
|
|
14099
|
+
if (this.stopped) return;
|
|
14087
14100
|
items = resp.assignments;
|
|
14088
14101
|
device = resp.device;
|
|
14089
14102
|
} catch (err) {
|
|
14103
|
+
if (this.stopped) return;
|
|
14090
14104
|
this.log.error(
|
|
14091
14105
|
{ ...apiErrorLogFields(err) },
|
|
14092
14106
|
"Couldn't load the assigned agents. Check that this device is active in Cabane; trying again shortly."
|
|
@@ -14105,6 +14119,7 @@ var CompanionSupervisor = class {
|
|
|
14105
14119
|
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
14106
14120
|
}
|
|
14107
14121
|
items = await this.restoreMissingCredentials(device.id, items);
|
|
14122
|
+
if (this.stopped) return;
|
|
14108
14123
|
this.uncredentialed = new Set(
|
|
14109
14124
|
items.filter((it) => !it.credential && !getCredential(device.id, it.agentId)).map((it) => `${it.workspaceId}|${it.agentId}`)
|
|
14110
14125
|
);
|
|
@@ -14117,6 +14132,7 @@ var CompanionSupervisor = class {
|
|
|
14117
14132
|
for (const wsId of [...this.workspaces.keys()]) {
|
|
14118
14133
|
if (!byWorkspace.has(wsId)) await this.removeWorkspace(wsId);
|
|
14119
14134
|
}
|
|
14135
|
+
if (this.stopped) return;
|
|
14120
14136
|
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(device.id, wsId, list);
|
|
14121
14137
|
const pruned = pruneCredentials(
|
|
14122
14138
|
device.id,
|
|
@@ -14148,6 +14164,7 @@ var CompanionSupervisor = class {
|
|
|
14148
14164
|
async restoreMissingCredentials(deviceId, items) {
|
|
14149
14165
|
const restored = [];
|
|
14150
14166
|
for (const it of items) {
|
|
14167
|
+
if (this.stopped) return restored;
|
|
14151
14168
|
if (it.credential || getCredential(deviceId, it.agentId)) {
|
|
14152
14169
|
restored.push(it);
|
|
14153
14170
|
continue;
|
|
@@ -14159,7 +14176,11 @@ var CompanionSupervisor = class {
|
|
|
14159
14176
|
continue;
|
|
14160
14177
|
}
|
|
14161
14178
|
try {
|
|
14162
|
-
const { credential } = await this.deviceApi.reissueAgentCredential(
|
|
14179
|
+
const { credential } = await this.deviceApi.reissueAgentCredential(
|
|
14180
|
+
it.agentId,
|
|
14181
|
+
this.reconciliationAbort.signal
|
|
14182
|
+
);
|
|
14183
|
+
if (this.stopped) return restored;
|
|
14163
14184
|
setCredential(deviceId, it.agentId, credential);
|
|
14164
14185
|
this.log.info(
|
|
14165
14186
|
{ agentId: it.agentId, agentName: it.agentDisplayName },
|
|
@@ -15034,6 +15055,7 @@ var CompanionSupervisor = class {
|
|
|
15034
15055
|
// ---- control actions ----
|
|
15035
15056
|
async shutdown() {
|
|
15036
15057
|
this.stopped = true;
|
|
15058
|
+
this.reconciliationAbort.abort();
|
|
15037
15059
|
this.updater?.stop();
|
|
15038
15060
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
15039
15061
|
if (this.detectTimer) clearInterval(this.detectTimer);
|
|
@@ -15311,33 +15333,46 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15311
15333
|
});
|
|
15312
15334
|
let restartForUpdate = async () => {
|
|
15313
15335
|
};
|
|
15314
|
-
|
|
15315
|
-
config: cfg,
|
|
15316
|
-
log: log2,
|
|
15317
|
-
hub,
|
|
15318
|
-
...opts.checkHarness ? { checkHarness: opts.checkHarness } : {},
|
|
15319
|
-
...embeddedRunner({
|
|
15320
|
-
electron: Boolean(process.versions.electron),
|
|
15321
|
-
execPath: process.execPath,
|
|
15322
|
-
moduleUrl: import.meta.url,
|
|
15323
|
-
env: process.env
|
|
15324
|
-
}) ?? {},
|
|
15325
|
-
restartForUpdate: () => restartForUpdate()
|
|
15326
|
-
});
|
|
15327
|
-
await supervisor.start();
|
|
15336
|
+
let supervisor;
|
|
15328
15337
|
let control = null;
|
|
15329
15338
|
let dashboard = null;
|
|
15339
|
+
let stopped = false;
|
|
15340
|
+
const stop2 = async () => {
|
|
15341
|
+
if (stopped) return;
|
|
15342
|
+
stopped = true;
|
|
15343
|
+
clearRuntimeStateIfOurs(instanceId);
|
|
15344
|
+
process.removeListener("exit", clearRuntimeOnExit);
|
|
15345
|
+
await Promise.allSettled([
|
|
15346
|
+
...supervisor ? [supervisor.shutdown()] : [],
|
|
15347
|
+
...control ? [control.close()] : [],
|
|
15348
|
+
...dashboard ? [dashboard.close()] : []
|
|
15349
|
+
]);
|
|
15350
|
+
};
|
|
15330
15351
|
try {
|
|
15352
|
+
supervisor = new CompanionSupervisor({
|
|
15353
|
+
config: cfg,
|
|
15354
|
+
log: log2,
|
|
15355
|
+
hub,
|
|
15356
|
+
...opts.checkHarness ? { checkHarness: opts.checkHarness } : {},
|
|
15357
|
+
...embeddedRunner({
|
|
15358
|
+
electron: Boolean(process.versions.electron),
|
|
15359
|
+
execPath: process.execPath,
|
|
15360
|
+
moduleUrl: import.meta.url,
|
|
15361
|
+
env: process.env
|
|
15362
|
+
}) ?? {},
|
|
15363
|
+
restartForUpdate: () => restartForUpdate()
|
|
15364
|
+
});
|
|
15365
|
+
const active2 = supervisor;
|
|
15331
15366
|
control = await startControlServer({
|
|
15332
15367
|
status: () => ({
|
|
15333
15368
|
...hub.statusJson(),
|
|
15334
|
-
...
|
|
15335
|
-
detection:
|
|
15369
|
+
...active2.lastHeartbeat(),
|
|
15370
|
+
detection: active2.currentDetection()
|
|
15336
15371
|
}),
|
|
15337
|
-
detect: () =>
|
|
15338
|
-
allow: (runtime) =>
|
|
15339
|
-
update: (auto) =>
|
|
15340
|
-
stop: () => void
|
|
15372
|
+
detect: () => active2.redetect(),
|
|
15373
|
+
allow: (runtime) => active2.allowHarness(runtime),
|
|
15374
|
+
update: (auto) => active2.update(auto),
|
|
15375
|
+
stop: () => void stop2().then(() => active2.requestStop())
|
|
15341
15376
|
});
|
|
15342
15377
|
if (opts.dashboard) {
|
|
15343
15378
|
const preferredPort = opts.port ?? cfg.dashboardPort;
|
|
@@ -15348,6 +15383,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15348
15383
|
});
|
|
15349
15384
|
hub.setDashboardUrl(dashboard.url);
|
|
15350
15385
|
}
|
|
15386
|
+
if (stopped) throw new Error("The companion was stopped during startup.");
|
|
15351
15387
|
writeRuntimeState({
|
|
15352
15388
|
pid: process.pid,
|
|
15353
15389
|
socket: control.path,
|
|
@@ -15365,7 +15401,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15365
15401
|
clearRuntimeStateIfOurs(instanceId);
|
|
15366
15402
|
process.removeListener("exit", clearRuntimeOnExit);
|
|
15367
15403
|
await Promise.allSettled([
|
|
15368
|
-
supervisor.shutdown(),
|
|
15404
|
+
...supervisor ? [supervisor.shutdown()] : [],
|
|
15369
15405
|
...control ? [control.close()] : [],
|
|
15370
15406
|
...dashboard ? [dashboard.close()] : []
|
|
15371
15407
|
]);
|
|
@@ -15377,22 +15413,17 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15377
15413
|
});
|
|
15378
15414
|
throw err;
|
|
15379
15415
|
}
|
|
15380
|
-
|
|
15381
|
-
const stop2 = async () => {
|
|
15382
|
-
if (stopped) return;
|
|
15383
|
-
stopped = true;
|
|
15384
|
-
clearRuntimeStateIfOurs(instanceId);
|
|
15385
|
-
process.removeListener("exit", clearRuntimeOnExit);
|
|
15386
|
-
try {
|
|
15387
|
-
await supervisor.shutdown();
|
|
15388
|
-
await closeSurfaces(control, dashboard);
|
|
15389
|
-
} catch {
|
|
15390
|
-
}
|
|
15391
|
-
};
|
|
15416
|
+
const active = supervisor;
|
|
15392
15417
|
restartForUpdate = async () => {
|
|
15393
15418
|
await stop2();
|
|
15394
15419
|
process.exit(0);
|
|
15395
15420
|
};
|
|
15421
|
+
void active.start().catch(async (err) => {
|
|
15422
|
+
log2.error({ err }, "Couldn't initialize the companion.");
|
|
15423
|
+
recordCrash({ reason: String(err), origin: "startup", at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
15424
|
+
await stop2();
|
|
15425
|
+
process.exitCode = 1;
|
|
15426
|
+
});
|
|
15396
15427
|
return {
|
|
15397
15428
|
ok: true,
|
|
15398
15429
|
runtime: {
|
|
@@ -15400,15 +15431,38 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15400
15431
|
socketPath: control.path,
|
|
15401
15432
|
config: cfg,
|
|
15402
15433
|
stop: stop2,
|
|
15403
|
-
heartbeatNow: () =>
|
|
15434
|
+
heartbeatNow: () => active.heartbeatNow(),
|
|
15404
15435
|
harnesses: () => hub.statusJson().harnesses ?? [],
|
|
15405
|
-
detect: () =>
|
|
15436
|
+
detect: () => active.redetect()
|
|
15406
15437
|
}
|
|
15407
15438
|
};
|
|
15408
15439
|
}
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15440
|
+
|
|
15441
|
+
// src/start-observation.ts
|
|
15442
|
+
init_cabane_command();
|
|
15443
|
+
async function observeStart(deps = {}) {
|
|
15444
|
+
const read2 = deps.readState ?? readLiveRuntimeState;
|
|
15445
|
+
const verify = deps.verify ?? verifyRuntime;
|
|
15446
|
+
const now = deps.now ?? Date.now;
|
|
15447
|
+
const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
15448
|
+
const deadline = now() + 8e3;
|
|
15449
|
+
for (; ; ) {
|
|
15450
|
+
const state = await read2();
|
|
15451
|
+
if (state?.socket && await verify(state) === "ours") return state;
|
|
15452
|
+
if (now() >= deadline) return null;
|
|
15453
|
+
await sleep3(Math.min(150, Math.max(0, deadline - now())));
|
|
15454
|
+
}
|
|
15455
|
+
}
|
|
15456
|
+
function markLaunchRequester() {
|
|
15457
|
+
process.title = "cabane-launcher";
|
|
15458
|
+
}
|
|
15459
|
+
function unconfirmedStartLines(service2) {
|
|
15460
|
+
return [
|
|
15461
|
+
"Cabane's start was requested, but readiness isn't confirmed yet. It may still finish starting in the background.",
|
|
15462
|
+
`Check: ${cabane("status")}`,
|
|
15463
|
+
...service2 ? [`Service: ${cabane("service status")}`] : [],
|
|
15464
|
+
`Log: ${cabane("logs")}`
|
|
15465
|
+
];
|
|
15412
15466
|
}
|
|
15413
15467
|
|
|
15414
15468
|
// src/commands/start.ts
|
|
@@ -15817,15 +15871,11 @@ init_config();
|
|
|
15817
15871
|
import { spawn as spawn7 } from "child_process";
|
|
15818
15872
|
import { closeSync as closeSync3, mkdirSync as mkdirSync17, openSync as openSync4 } from "fs";
|
|
15819
15873
|
init_config();
|
|
15820
|
-
var STARTUP_TIMEOUT_MS = 8e3;
|
|
15821
|
-
var POLL_INTERVAL_MS = 150;
|
|
15822
15874
|
async function startDaemon(opts = {}, deps = {}) {
|
|
15823
15875
|
const ensureRuntime = deps.ensureRuntime ?? warnAboutHarnessReadiness;
|
|
15824
15876
|
const readState = deps.readState ?? readLiveRuntimeState;
|
|
15825
15877
|
const verify = deps.verify ?? ((s) => verifyRuntime(s));
|
|
15826
15878
|
const spawnDetached = deps.spawnDetached ?? defaultSpawnDetached;
|
|
15827
|
-
const sleep3 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15828
|
-
const now = deps.now ?? (() => Date.now());
|
|
15829
15879
|
const quiet = opts.report === "failures" || opts.report === "silent";
|
|
15830
15880
|
const cfg = (deps.requireCfg ?? requireConfig)();
|
|
15831
15881
|
ensureRuntime(cfg, {
|
|
@@ -15837,7 +15887,18 @@ async function startDaemon(opts = {}, deps = {}) {
|
|
|
15837
15887
|
});
|
|
15838
15888
|
const existing = readState();
|
|
15839
15889
|
if (existing) {
|
|
15840
|
-
|
|
15890
|
+
const verdict = await verify(existing);
|
|
15891
|
+
if (verdict !== "stale") {
|
|
15892
|
+
if (verdict !== "ours") {
|
|
15893
|
+
const state2 = await observeStart({ ...deps, readState, verify });
|
|
15894
|
+
if (!state2) {
|
|
15895
|
+
if (opts.report !== "silent")
|
|
15896
|
+
process.stdout.write(unconfirmedStartLines(false).join("\n") + "\n");
|
|
15897
|
+
process.exitCode = 1;
|
|
15898
|
+
return { started: false, state: null, unconfirmed: true };
|
|
15899
|
+
}
|
|
15900
|
+
return { started: false, state: state2 };
|
|
15901
|
+
}
|
|
15841
15902
|
if (opts.report !== "silent")
|
|
15842
15903
|
process.stdout.write(alreadyRunningLines(existing.pid).join("\n") + "\n");
|
|
15843
15904
|
return { started: false, state: existing };
|
|
@@ -15855,28 +15916,21 @@ async function startDaemon(opts = {}, deps = {}) {
|
|
|
15855
15916
|
spawned.unref();
|
|
15856
15917
|
return spawned;
|
|
15857
15918
|
};
|
|
15858
|
-
|
|
15859
|
-
const waitForReady = async () => {
|
|
15860
|
-
const deadline = now() + STARTUP_TIMEOUT_MS;
|
|
15861
|
-
let seen = readState();
|
|
15862
|
-
while (!ready(seen) && now() < deadline) {
|
|
15863
|
-
await sleep3(POLL_INTERVAL_MS);
|
|
15864
|
-
seen = readState();
|
|
15865
|
-
}
|
|
15866
|
-
return ready(seen) ? seen : null;
|
|
15867
|
-
};
|
|
15919
|
+
markLaunchRequester();
|
|
15868
15920
|
const child = launchDetached();
|
|
15869
|
-
const state = await
|
|
15921
|
+
const state = await observeStart({ ...deps, readState, verify });
|
|
15870
15922
|
if (!state) {
|
|
15871
|
-
const error =
|
|
15923
|
+
const error = child.failure?.();
|
|
15924
|
+
if (error) {
|
|
15925
|
+
if (opts.report !== "silent") process.stdout.write(`Cabane couldn't start: ${error}
|
|
15926
|
+
`);
|
|
15927
|
+
process.exitCode = 1;
|
|
15928
|
+
return { started: false, state: null, error };
|
|
15929
|
+
}
|
|
15872
15930
|
if (opts.report !== "silent")
|
|
15873
|
-
process.stdout.write(
|
|
15874
|
-
`Cabane Companion was launched (pid ${child?.pid ?? "?"}) but didn't report ready within ${STARTUP_TIMEOUT_MS / 1e3}s.
|
|
15875
|
-
Check ${companionLogPath()} for errors, or run \`${cabane("status")}\`.
|
|
15876
|
-
`
|
|
15877
|
-
);
|
|
15931
|
+
process.stdout.write(unconfirmedStartLines(false).join("\n") + "\n");
|
|
15878
15932
|
process.exitCode = 1;
|
|
15879
|
-
return { started: false, state: null,
|
|
15933
|
+
return { started: false, state: null, unconfirmed: true };
|
|
15880
15934
|
}
|
|
15881
15935
|
if (quiet) return { started: true, state };
|
|
15882
15936
|
process.stdout.write(
|
|
@@ -15896,11 +15950,19 @@ function defaultSpawnDetached(args) {
|
|
|
15896
15950
|
mkdirSync17(cabaneDir(), { recursive: true });
|
|
15897
15951
|
const logFd = openSync4(companionLogPath(), "a");
|
|
15898
15952
|
try {
|
|
15899
|
-
|
|
15953
|
+
const child = spawn7(process.execPath, [cliPath, ...args], {
|
|
15900
15954
|
detached: true,
|
|
15901
15955
|
stdio: ["ignore", logFd, logFd],
|
|
15902
15956
|
env: { ...process.env, CABANE_COMPANION_DAEMON: "1" }
|
|
15903
15957
|
});
|
|
15958
|
+
let failure;
|
|
15959
|
+
child.once("error", (err) => {
|
|
15960
|
+
failure = err.message;
|
|
15961
|
+
});
|
|
15962
|
+
child.once("exit", (code, signal) => {
|
|
15963
|
+
failure = `The background process exited (${signal ?? code}). Check cabane logs.`;
|
|
15964
|
+
});
|
|
15965
|
+
return { pid: child.pid, unref: () => child.unref(), failure: () => failure };
|
|
15904
15966
|
} finally {
|
|
15905
15967
|
closeSync3(logFd);
|
|
15906
15968
|
}
|
|
@@ -16020,8 +16082,10 @@ async function start(opts = {}, deps = {}) {
|
|
|
16020
16082
|
try {
|
|
16021
16083
|
return await startScript(opts, deps);
|
|
16022
16084
|
} catch (err) {
|
|
16023
|
-
|
|
16024
|
-
|
|
16085
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
16086
|
+
if (process.env.CABANE_COMPANION_SUPERVISED === "1")
|
|
16087
|
+
getLogger().error({ err }, `Cabane couldn't start: ${reason}`);
|
|
16088
|
+
for (const line of startFailure(reason, reloadLine())) write(line);
|
|
16025
16089
|
process.exitCode = 1;
|
|
16026
16090
|
return null;
|
|
16027
16091
|
} finally {
|
|
@@ -16057,23 +16121,41 @@ async function startScript(opts, deps) {
|
|
|
16057
16121
|
mode = "attached";
|
|
16058
16122
|
}
|
|
16059
16123
|
} else if (installed) {
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16124
|
+
markLaunchRequester();
|
|
16125
|
+
const before = await serviceState();
|
|
16126
|
+
if (!serviceLaunchPending(before)) {
|
|
16127
|
+
const result = await startService();
|
|
16128
|
+
if (!result.ok) {
|
|
16129
|
+
running = await observeStart({ ...deps, readState: deps.live });
|
|
16130
|
+
const current2 = await serviceState();
|
|
16131
|
+
if (!running && !serviceLaunchPending(current2)) throw new Error(result.lines.join(" "));
|
|
16132
|
+
}
|
|
16066
16133
|
}
|
|
16067
|
-
|
|
16068
|
-
|
|
16134
|
+
running ??= await observeStart({ ...deps, readState: deps.live });
|
|
16135
|
+
if (!running) {
|
|
16136
|
+
const current2 = await serviceState();
|
|
16137
|
+
if (!current2.nodeOk || current2.state === "failed")
|
|
16138
|
+
throw new Error(
|
|
16139
|
+
!current2.nodeOk ? "The service Node executable is missing." : "The service failed. Check cabane logs."
|
|
16140
|
+
);
|
|
16141
|
+
return reportUnconfirmed(true);
|
|
16142
|
+
}
|
|
16143
|
+
const current = await serviceState();
|
|
16144
|
+
mode = current.state === "running" && current.pid === running.pid ? "service" : "existing";
|
|
16069
16145
|
} else {
|
|
16070
16146
|
const result = await startDaemon({ ...opts, report: "silent" });
|
|
16147
|
+
if (result.unconfirmed) return reportUnconfirmed(false);
|
|
16071
16148
|
if (!result.state)
|
|
16072
16149
|
throw new Error(result.error ?? "The background process did not report ready.");
|
|
16073
16150
|
running = result.state;
|
|
16074
16151
|
mode = result.started ? "background" : "existing";
|
|
16075
16152
|
}
|
|
16076
16153
|
}
|
|
16154
|
+
if (!runtime && mode === "existing") {
|
|
16155
|
+
running = await observeStart({ ...deps, readState: deps.live });
|
|
16156
|
+
if (!running) return reportUnconfirmed(installed);
|
|
16157
|
+
}
|
|
16158
|
+
const attached = runtime ? runAttached(runtime) : null;
|
|
16077
16159
|
const socket = runtime?.socketPath ?? running?.socket;
|
|
16078
16160
|
let status2 = await (deps.waitAck ?? waitForAcknowledgement)(socket);
|
|
16079
16161
|
let detection = status2.detection ?? null;
|
|
@@ -16087,6 +16169,26 @@ async function startScript(opts, deps) {
|
|
|
16087
16169
|
const latest = await readTerminalStatus(socket);
|
|
16088
16170
|
if (latest.lastHeartbeatAckAt || latest.lastHeartbeatRefused) status2 = latest;
|
|
16089
16171
|
}
|
|
16172
|
+
if (!runtime) {
|
|
16173
|
+
const current = await (deps.live ?? liveCompanionState)();
|
|
16174
|
+
if (!current || await (deps.verify ?? verifyRuntime)(current) !== "ours") {
|
|
16175
|
+
if (installed) {
|
|
16176
|
+
const service2 = await serviceState();
|
|
16177
|
+
if (!service2.pid && ["not loaded", "dead", "inactive", "failed"].includes(service2.state))
|
|
16178
|
+
throw new Error("The service stopped before startup completed.");
|
|
16179
|
+
}
|
|
16180
|
+
return reportUnconfirmed(installed);
|
|
16181
|
+
}
|
|
16182
|
+
if (current.pid !== running?.pid || current.instanceId !== running?.instanceId) {
|
|
16183
|
+
status2 = await readTerminalStatus(current.socket);
|
|
16184
|
+
detection = status2.detection ?? null;
|
|
16185
|
+
}
|
|
16186
|
+
running = current;
|
|
16187
|
+
if (mode === "service") {
|
|
16188
|
+
const service2 = await serviceState();
|
|
16189
|
+
if (service2.state !== "running" || service2.pid !== current.pid) mode = "existing";
|
|
16190
|
+
}
|
|
16191
|
+
}
|
|
16090
16192
|
const personalUrl = `${cfg.baseUrl.replace(/\/$/, "")}/settings/devices`;
|
|
16091
16193
|
const address = opts.pairing ? opts.pairing.devicesUrl : personalUrl;
|
|
16092
16194
|
if (!daemon)
|
|
@@ -16103,10 +16205,18 @@ async function startScript(opts, deps) {
|
|
|
16103
16205
|
write(line);
|
|
16104
16206
|
if (runtime) {
|
|
16105
16207
|
setConsoleLogging(true);
|
|
16106
|
-
await
|
|
16208
|
+
await attached;
|
|
16107
16209
|
}
|
|
16108
16210
|
return detection;
|
|
16109
16211
|
}
|
|
16212
|
+
function serviceLaunchPending(state) {
|
|
16213
|
+
return !!state.pid || ["running", "activating", "auto-restart"].includes(state.state) || state.platform === "launchd" && state.installed && !["not loaded", "unknown", "failed"].includes(state.state);
|
|
16214
|
+
}
|
|
16215
|
+
function reportUnconfirmed(service2) {
|
|
16216
|
+
for (const line of unconfirmedStartLines(service2)) write(line);
|
|
16217
|
+
process.exitCode = 1;
|
|
16218
|
+
return null;
|
|
16219
|
+
}
|
|
16110
16220
|
async function detectOverSocket(socket) {
|
|
16111
16221
|
const body = await controlRequest(socket, { cmd: "detect" }, 15e3);
|
|
16112
16222
|
return body.detection;
|
|
@@ -16480,7 +16590,7 @@ init_term();
|
|
|
16480
16590
|
|
|
16481
16591
|
// src/commands/stop.ts
|
|
16482
16592
|
var TERM_GRACE_MS = 6e3;
|
|
16483
|
-
var
|
|
16593
|
+
var POLL_INTERVAL_MS = 150;
|
|
16484
16594
|
async function stop(deps = {}) {
|
|
16485
16595
|
if ((deps.serviceInstalled ?? serviceInstalled)()) {
|
|
16486
16596
|
const outcome = await (deps.stopService ?? stopService)();
|
|
@@ -16526,7 +16636,7 @@ async function stop(deps = {}) {
|
|
|
16526
16636
|
process.stdout.write("companion: stopped.\n");
|
|
16527
16637
|
return;
|
|
16528
16638
|
}
|
|
16529
|
-
await sleep3(
|
|
16639
|
+
await sleep3(POLL_INTERVAL_MS);
|
|
16530
16640
|
}
|
|
16531
16641
|
process.stdout.write(
|
|
16532
16642
|
`companion: didn't exit within ${TERM_GRACE_MS / 1e3}s, sending SIGKILL.
|
|
@@ -16683,7 +16793,10 @@ function row2(label, value) {
|
|
|
16683
16793
|
async function status(deps = {}) {
|
|
16684
16794
|
const out = (line) => void process.stdout.write(`${line}
|
|
16685
16795
|
`);
|
|
16686
|
-
const
|
|
16796
|
+
const candidate = readLiveRuntimeState({ sweep: false });
|
|
16797
|
+
const verdict = candidate ? await verifyRuntime(candidate) : null;
|
|
16798
|
+
const running = verdict === "stale" ? null : candidate;
|
|
16799
|
+
const service2 = await serviceState();
|
|
16687
16800
|
for (const line of pathLines(reloadLine())) out(line);
|
|
16688
16801
|
const strays = strayStatusLines(running, deps.strays);
|
|
16689
16802
|
out(` ${versionLine()}`);
|
|
@@ -16705,7 +16818,7 @@ async function status(deps = {}) {
|
|
|
16705
16818
|
out(row2("Paired with", `${cfg.baseUrl}${cfg.deviceLabel ? ` as ${cfg.deviceLabel}` : ""}`));
|
|
16706
16819
|
let detection = null;
|
|
16707
16820
|
if (running) {
|
|
16708
|
-
const mode =
|
|
16821
|
+
const mode = verdict !== "ours" ? "identity unconfirmed" : runningMode(running, service2);
|
|
16709
16822
|
const uptime = formatUptime(running.startedAt);
|
|
16710
16823
|
out(row2("Running", `${mode} (pid ${running.pid}${uptime ? `, up ${uptime}` : ""})`));
|
|
16711
16824
|
detection = await runningDetection(running);
|
|
@@ -16723,7 +16836,6 @@ async function status(deps = {}) {
|
|
|
16723
16836
|
)
|
|
16724
16837
|
);
|
|
16725
16838
|
for (const line of strays) out(line);
|
|
16726
|
-
const service2 = await serviceState();
|
|
16727
16839
|
for (const line of serviceRows(service2)) out(line);
|
|
16728
16840
|
for (const line of updateRows(readUpdateState(), cfg.update?.auto, {
|
|
16729
16841
|
version: false,
|
|
@@ -16793,10 +16905,13 @@ function otherInstall(install, own) {
|
|
|
16793
16905
|
const ownAlias = own ? real(join30(dirname13(own), "cabane-companion")) : null;
|
|
16794
16906
|
return real(found) === ownAlias ? null : dirname13(found);
|
|
16795
16907
|
}
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16908
|
+
function runningMode(running, service2) {
|
|
16909
|
+
if (service2.installed) {
|
|
16910
|
+
if (service2.state === "running" && service2.pid === running.pid) return "as a service";
|
|
16911
|
+
if (service2.state === "unknown" || service2.pid === running.pid || service2.state === "running" && !service2.pid)
|
|
16912
|
+
return "run mode unconfirmed";
|
|
16913
|
+
}
|
|
16914
|
+
return running.daemon ? "in the background" : "in a terminal";
|
|
16800
16915
|
}
|
|
16801
16916
|
async function runningDetection(running) {
|
|
16802
16917
|
if (!running.socket) return null;
|
package/dist/runtime.js
CHANGED
|
@@ -2344,14 +2344,19 @@ var DeviceApi = class {
|
|
|
2344
2344
|
return parsed;
|
|
2345
2345
|
}
|
|
2346
2346
|
// Pull the agents this device should run, across all the owner's workspaces.
|
|
2347
|
-
getAssignments() {
|
|
2348
|
-
return this.request("GET", "/api/companion/assignments");
|
|
2347
|
+
getAssignments(signal) {
|
|
2348
|
+
return this.request("GET", "/api/companion/assignments", void 0, requestSignal(signal));
|
|
2349
2349
|
}
|
|
2350
2350
|
// Ask again for the credential of an agent assigned to this device, when the saved copy is
|
|
2351
2351
|
// gone. The previous credential stops working; any agent not assigned here is a 404, and a
|
|
2352
2352
|
// second ask inside a minute a 429.
|
|
2353
|
-
reissueAgentCredential(agentId) {
|
|
2354
|
-
return this.request(
|
|
2353
|
+
reissueAgentCredential(agentId, signal) {
|
|
2354
|
+
return this.request(
|
|
2355
|
+
"POST",
|
|
2356
|
+
`/api/companion/agents/${encodeURIComponent(agentId)}/credential`,
|
|
2357
|
+
void 0,
|
|
2358
|
+
requestSignal(signal, 3e4)
|
|
2359
|
+
);
|
|
2355
2360
|
}
|
|
2356
2361
|
getRuns(cursor) {
|
|
2357
2362
|
const query = new URLSearchParams({ limit: "100", order: "asc" });
|
|
@@ -2391,6 +2396,10 @@ function errorMessage2(status, body) {
|
|
|
2391
2396
|
if (typeof body === "string" && body.length > 0) return `${status} ${body.slice(0, 200)}`;
|
|
2392
2397
|
return `${status} error`;
|
|
2393
2398
|
}
|
|
2399
|
+
function requestSignal(signal, timeoutMs = 5e3) {
|
|
2400
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
2401
|
+
return signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
2402
|
+
}
|
|
2394
2403
|
|
|
2395
2404
|
// src/credentials.ts
|
|
2396
2405
|
import {
|
|
@@ -9623,6 +9632,7 @@ var CompanionSupervisor = class {
|
|
|
9623
9632
|
pollTimer = null;
|
|
9624
9633
|
refreshing = false;
|
|
9625
9634
|
stopped = false;
|
|
9635
|
+
reconciliationAbort = new AbortController();
|
|
9626
9636
|
// The version-skew warning is logged once, not every heartbeat.
|
|
9627
9637
|
versionSkewWarned = false;
|
|
9628
9638
|
// This device's id, captured from the heartbeat / assignments response. The SSE
|
|
@@ -9668,6 +9678,7 @@ var CompanionSupervisor = class {
|
|
|
9668
9678
|
// heartbeat + poll loops. A companion with no device token (logged out) does
|
|
9669
9679
|
// nothing but say so.
|
|
9670
9680
|
async start() {
|
|
9681
|
+
if (this.stopped) return;
|
|
9671
9682
|
this.log.info(
|
|
9672
9683
|
{ protocolVersion: TURN_PROTOCOL_VERSION, version: COMPANION_VERSION },
|
|
9673
9684
|
`Companion ${COMPANION_VERSION} starting on ${this.config.deviceLabel ?? hostname2()}`
|
|
@@ -9752,6 +9763,7 @@ var CompanionSupervisor = class {
|
|
|
9752
9763
|
});
|
|
9753
9764
|
this.updater.start();
|
|
9754
9765
|
await this.refreshAssignments();
|
|
9766
|
+
if (this.stopped) return;
|
|
9755
9767
|
this.deviceSub.start();
|
|
9756
9768
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
9757
9769
|
this.heartbeatTimer.unref?.();
|
|
@@ -9864,10 +9876,12 @@ var CompanionSupervisor = class {
|
|
|
9864
9876
|
let items;
|
|
9865
9877
|
let device;
|
|
9866
9878
|
try {
|
|
9867
|
-
const resp = await this.deviceApi.getAssignments();
|
|
9879
|
+
const resp = await this.deviceApi.getAssignments(this.reconciliationAbort.signal);
|
|
9880
|
+
if (this.stopped) return;
|
|
9868
9881
|
items = resp.assignments;
|
|
9869
9882
|
device = resp.device;
|
|
9870
9883
|
} catch (err) {
|
|
9884
|
+
if (this.stopped) return;
|
|
9871
9885
|
this.log.error(
|
|
9872
9886
|
{ ...apiErrorLogFields(err) },
|
|
9873
9887
|
"Couldn't load the assigned agents. Check that this device is active in Cabane; trying again shortly."
|
|
@@ -9886,6 +9900,7 @@ var CompanionSupervisor = class {
|
|
|
9886
9900
|
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
9887
9901
|
}
|
|
9888
9902
|
items = await this.restoreMissingCredentials(device.id, items);
|
|
9903
|
+
if (this.stopped) return;
|
|
9889
9904
|
this.uncredentialed = new Set(
|
|
9890
9905
|
items.filter((it) => !it.credential && !getCredential(device.id, it.agentId)).map((it) => `${it.workspaceId}|${it.agentId}`)
|
|
9891
9906
|
);
|
|
@@ -9898,6 +9913,7 @@ var CompanionSupervisor = class {
|
|
|
9898
9913
|
for (const wsId of [...this.workspaces.keys()]) {
|
|
9899
9914
|
if (!byWorkspace.has(wsId)) await this.removeWorkspace(wsId);
|
|
9900
9915
|
}
|
|
9916
|
+
if (this.stopped) return;
|
|
9901
9917
|
for (const [wsId, list] of byWorkspace) this.reconcileWorkspace(device.id, wsId, list);
|
|
9902
9918
|
const pruned = pruneCredentials(
|
|
9903
9919
|
device.id,
|
|
@@ -9929,6 +9945,7 @@ var CompanionSupervisor = class {
|
|
|
9929
9945
|
async restoreMissingCredentials(deviceId, items) {
|
|
9930
9946
|
const restored = [];
|
|
9931
9947
|
for (const it of items) {
|
|
9948
|
+
if (this.stopped) return restored;
|
|
9932
9949
|
if (it.credential || getCredential(deviceId, it.agentId)) {
|
|
9933
9950
|
restored.push(it);
|
|
9934
9951
|
continue;
|
|
@@ -9940,7 +9957,11 @@ var CompanionSupervisor = class {
|
|
|
9940
9957
|
continue;
|
|
9941
9958
|
}
|
|
9942
9959
|
try {
|
|
9943
|
-
const { credential } = await this.deviceApi.reissueAgentCredential(
|
|
9960
|
+
const { credential } = await this.deviceApi.reissueAgentCredential(
|
|
9961
|
+
it.agentId,
|
|
9962
|
+
this.reconciliationAbort.signal
|
|
9963
|
+
);
|
|
9964
|
+
if (this.stopped) return restored;
|
|
9944
9965
|
setCredential(deviceId, it.agentId, credential);
|
|
9945
9966
|
this.log.info(
|
|
9946
9967
|
{ agentId: it.agentId, agentName: it.agentDisplayName },
|
|
@@ -10815,6 +10836,7 @@ var CompanionSupervisor = class {
|
|
|
10815
10836
|
// ---- control actions ----
|
|
10816
10837
|
async shutdown() {
|
|
10817
10838
|
this.stopped = true;
|
|
10839
|
+
this.reconciliationAbort.abort();
|
|
10818
10840
|
this.updater?.stop();
|
|
10819
10841
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
10820
10842
|
if (this.detectTimer) clearInterval(this.detectTimer);
|
|
@@ -11090,33 +11112,46 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
11090
11112
|
});
|
|
11091
11113
|
let restartForUpdate = async () => {
|
|
11092
11114
|
};
|
|
11093
|
-
|
|
11094
|
-
config: cfg,
|
|
11095
|
-
log,
|
|
11096
|
-
hub,
|
|
11097
|
-
...opts.checkHarness ? { checkHarness: opts.checkHarness } : {},
|
|
11098
|
-
...embeddedRunner({
|
|
11099
|
-
electron: Boolean(process.versions.electron),
|
|
11100
|
-
execPath: process.execPath,
|
|
11101
|
-
moduleUrl: import.meta.url,
|
|
11102
|
-
env: process.env
|
|
11103
|
-
}) ?? {},
|
|
11104
|
-
restartForUpdate: () => restartForUpdate()
|
|
11105
|
-
});
|
|
11106
|
-
await supervisor.start();
|
|
11115
|
+
let supervisor;
|
|
11107
11116
|
let control = null;
|
|
11108
11117
|
let dashboard = null;
|
|
11118
|
+
let stopped = false;
|
|
11119
|
+
const stop = async () => {
|
|
11120
|
+
if (stopped) return;
|
|
11121
|
+
stopped = true;
|
|
11122
|
+
clearRuntimeStateIfOurs(instanceId);
|
|
11123
|
+
process.removeListener("exit", clearRuntimeOnExit);
|
|
11124
|
+
await Promise.allSettled([
|
|
11125
|
+
...supervisor ? [supervisor.shutdown()] : [],
|
|
11126
|
+
...control ? [control.close()] : [],
|
|
11127
|
+
...dashboard ? [dashboard.close()] : []
|
|
11128
|
+
]);
|
|
11129
|
+
};
|
|
11109
11130
|
try {
|
|
11131
|
+
supervisor = new CompanionSupervisor({
|
|
11132
|
+
config: cfg,
|
|
11133
|
+
log,
|
|
11134
|
+
hub,
|
|
11135
|
+
...opts.checkHarness ? { checkHarness: opts.checkHarness } : {},
|
|
11136
|
+
...embeddedRunner({
|
|
11137
|
+
electron: Boolean(process.versions.electron),
|
|
11138
|
+
execPath: process.execPath,
|
|
11139
|
+
moduleUrl: import.meta.url,
|
|
11140
|
+
env: process.env
|
|
11141
|
+
}) ?? {},
|
|
11142
|
+
restartForUpdate: () => restartForUpdate()
|
|
11143
|
+
});
|
|
11144
|
+
const active2 = supervisor;
|
|
11110
11145
|
control = await startControlServer({
|
|
11111
11146
|
status: () => ({
|
|
11112
11147
|
...hub.statusJson(),
|
|
11113
|
-
...
|
|
11114
|
-
detection:
|
|
11148
|
+
...active2.lastHeartbeat(),
|
|
11149
|
+
detection: active2.currentDetection()
|
|
11115
11150
|
}),
|
|
11116
|
-
detect: () =>
|
|
11117
|
-
allow: (runtime) =>
|
|
11118
|
-
update: (auto) =>
|
|
11119
|
-
stop: () => void
|
|
11151
|
+
detect: () => active2.redetect(),
|
|
11152
|
+
allow: (runtime) => active2.allowHarness(runtime),
|
|
11153
|
+
update: (auto) => active2.update(auto),
|
|
11154
|
+
stop: () => void stop().then(() => active2.requestStop())
|
|
11120
11155
|
});
|
|
11121
11156
|
if (opts.dashboard) {
|
|
11122
11157
|
const preferredPort = opts.port ?? cfg.dashboardPort;
|
|
@@ -11127,6 +11162,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
11127
11162
|
});
|
|
11128
11163
|
hub.setDashboardUrl(dashboard.url);
|
|
11129
11164
|
}
|
|
11165
|
+
if (stopped) throw new Error("The companion was stopped during startup.");
|
|
11130
11166
|
writeRuntimeState({
|
|
11131
11167
|
pid: process.pid,
|
|
11132
11168
|
socket: control.path,
|
|
@@ -11144,7 +11180,7 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
11144
11180
|
clearRuntimeStateIfOurs(instanceId);
|
|
11145
11181
|
process.removeListener("exit", clearRuntimeOnExit);
|
|
11146
11182
|
await Promise.allSettled([
|
|
11147
|
-
supervisor.shutdown(),
|
|
11183
|
+
...supervisor ? [supervisor.shutdown()] : [],
|
|
11148
11184
|
...control ? [control.close()] : [],
|
|
11149
11185
|
...dashboard ? [dashboard.close()] : []
|
|
11150
11186
|
]);
|
|
@@ -11156,22 +11192,17 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
11156
11192
|
});
|
|
11157
11193
|
throw err;
|
|
11158
11194
|
}
|
|
11159
|
-
|
|
11160
|
-
const stop = async () => {
|
|
11161
|
-
if (stopped) return;
|
|
11162
|
-
stopped = true;
|
|
11163
|
-
clearRuntimeStateIfOurs(instanceId);
|
|
11164
|
-
process.removeListener("exit", clearRuntimeOnExit);
|
|
11165
|
-
try {
|
|
11166
|
-
await supervisor.shutdown();
|
|
11167
|
-
await closeSurfaces(control, dashboard);
|
|
11168
|
-
} catch {
|
|
11169
|
-
}
|
|
11170
|
-
};
|
|
11195
|
+
const active = supervisor;
|
|
11171
11196
|
restartForUpdate = async () => {
|
|
11172
11197
|
await stop();
|
|
11173
11198
|
process.exit(0);
|
|
11174
11199
|
};
|
|
11200
|
+
void active.start().catch(async (err) => {
|
|
11201
|
+
log.error({ err }, "Couldn't initialize the companion.");
|
|
11202
|
+
recordCrash({ reason: String(err), origin: "startup", at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11203
|
+
await stop();
|
|
11204
|
+
process.exitCode = 1;
|
|
11205
|
+
});
|
|
11175
11206
|
return {
|
|
11176
11207
|
ok: true,
|
|
11177
11208
|
runtime: {
|
|
@@ -11179,16 +11210,12 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
11179
11210
|
socketPath: control.path,
|
|
11180
11211
|
config: cfg,
|
|
11181
11212
|
stop,
|
|
11182
|
-
heartbeatNow: () =>
|
|
11213
|
+
heartbeatNow: () => active.heartbeatNow(),
|
|
11183
11214
|
harnesses: () => hub.statusJson().harnesses ?? [],
|
|
11184
|
-
detect: () =>
|
|
11215
|
+
detect: () => active.redetect()
|
|
11185
11216
|
}
|
|
11186
11217
|
};
|
|
11187
11218
|
}
|
|
11188
|
-
async function closeSurfaces(control, dashboard) {
|
|
11189
|
-
await control.close();
|
|
11190
|
-
if (dashboard) await dashboard.close();
|
|
11191
|
-
}
|
|
11192
11219
|
export {
|
|
11193
11220
|
createCompanionRuntime,
|
|
11194
11221
|
embeddedRunner,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.188",
|
|
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",
|