@cabane/companion 0.6.163 → 0.6.165
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 +60 -10
- package/dist/runtime.js +47 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12437,7 +12437,9 @@ function clearRuntimeStateIfOurs(instanceId) {
|
|
|
12437
12437
|
}
|
|
12438
12438
|
rmSync3(path, { force: true });
|
|
12439
12439
|
}
|
|
12440
|
-
function readLiveRuntimeState(
|
|
12440
|
+
function readLiveRuntimeState({
|
|
12441
|
+
sweep = true
|
|
12442
|
+
} = {}) {
|
|
12441
12443
|
const path = runtimePath();
|
|
12442
12444
|
if (!existsSync5(path)) return null;
|
|
12443
12445
|
let parsed;
|
|
@@ -12450,7 +12452,7 @@ function readLiveRuntimeState() {
|
|
|
12450
12452
|
try {
|
|
12451
12453
|
process.kill(parsed.pid, 0);
|
|
12452
12454
|
} catch {
|
|
12453
|
-
clearRuntimeState();
|
|
12455
|
+
if (sweep) clearRuntimeState();
|
|
12454
12456
|
return null;
|
|
12455
12457
|
}
|
|
12456
12458
|
return parsed;
|
|
@@ -15401,6 +15403,12 @@ var DeviceApi = class {
|
|
|
15401
15403
|
getAssignments() {
|
|
15402
15404
|
return this.request("GET", "/api/companion/assignments");
|
|
15403
15405
|
}
|
|
15406
|
+
// Ask again for the credential of an agent assigned to this device, when the saved copy is
|
|
15407
|
+
// gone. The previous credential stops working; any agent not assigned here is a 404, and a
|
|
15408
|
+
// second ask inside a minute a 429.
|
|
15409
|
+
reissueAgentCredential(agentId) {
|
|
15410
|
+
return this.request("POST", `/api/companion/agents/${encodeURIComponent(agentId)}/credential`);
|
|
15411
|
+
}
|
|
15404
15412
|
getRuns(cursor) {
|
|
15405
15413
|
const query = new URLSearchParams({ limit: "100", order: "asc" });
|
|
15406
15414
|
if (cursor) query.set("cursor", cursor);
|
|
@@ -17606,6 +17614,7 @@ var CompanionSupervisor = class {
|
|
|
17606
17614
|
for (const it of items) {
|
|
17607
17615
|
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
17608
17616
|
}
|
|
17617
|
+
items = await this.restoreMissingCredentials(device.id, items);
|
|
17609
17618
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
17610
17619
|
for (const it of items) {
|
|
17611
17620
|
const list = byWorkspace.get(it.workspaceId) ?? [];
|
|
@@ -17638,6 +17647,42 @@ var CompanionSupervisor = class {
|
|
|
17638
17647
|
this.inFlightRefresh = null;
|
|
17639
17648
|
}
|
|
17640
17649
|
}
|
|
17650
|
+
// An assigned agent whose saved credential is gone (the file wiped or edited, a crash between
|
|
17651
|
+
// delivery and write) gets it back here, before reconciling. A runner still holding it in
|
|
17652
|
+
// memory writes it back; otherwise Cabane is asked for a new one, which revokes the old. The
|
|
17653
|
+
// result rides the item as a fresh delivery, so reconcile builds the runner on it. A refused
|
|
17654
|
+
// ask leaves the item as it was, and reconcile reports the agent can't run.
|
|
17655
|
+
async restoreMissingCredentials(deviceId, items) {
|
|
17656
|
+
const restored = [];
|
|
17657
|
+
for (const it of items) {
|
|
17658
|
+
if (it.credential || getCredential(deviceId, it.agentId)) {
|
|
17659
|
+
restored.push(it);
|
|
17660
|
+
continue;
|
|
17661
|
+
}
|
|
17662
|
+
const held = this.workspaces.get(it.workspaceId)?.agents.get(it.agentId)?.credential;
|
|
17663
|
+
if (held) {
|
|
17664
|
+
setCredential(deviceId, it.agentId, held);
|
|
17665
|
+
restored.push(it);
|
|
17666
|
+
continue;
|
|
17667
|
+
}
|
|
17668
|
+
try {
|
|
17669
|
+
const { credential } = await this.deviceApi.reissueAgentCredential(it.agentId);
|
|
17670
|
+
setCredential(deviceId, it.agentId, credential);
|
|
17671
|
+
this.log.info(
|
|
17672
|
+
{ agentId: it.agentId, agentName: it.agentDisplayName },
|
|
17673
|
+
`Restored ${it.agentDisplayName}'s sign-in to Cabane: it was missing on this device.`
|
|
17674
|
+
);
|
|
17675
|
+
restored.push({ ...it, credential });
|
|
17676
|
+
} catch (err) {
|
|
17677
|
+
this.log.warn(
|
|
17678
|
+
{ agentId: it.agentId, agentName: it.agentDisplayName, ...apiErrorLogFields(err) },
|
|
17679
|
+
`Couldn't restore ${it.agentDisplayName}'s sign-in to Cabane.`
|
|
17680
|
+
);
|
|
17681
|
+
restored.push(it);
|
|
17682
|
+
}
|
|
17683
|
+
}
|
|
17684
|
+
return restored;
|
|
17685
|
+
}
|
|
17641
17686
|
// ---- reconciliation ----
|
|
17642
17687
|
reconcileWorkspace(deviceId, workspaceId, list) {
|
|
17643
17688
|
const first = list[0];
|
|
@@ -19060,11 +19105,13 @@ var LABEL_WIDTH2 = 13;
|
|
|
19060
19105
|
function row2(label, value) {
|
|
19061
19106
|
return ` ${label.padEnd(LABEL_WIDTH2)}${value}`;
|
|
19062
19107
|
}
|
|
19063
|
-
async function status() {
|
|
19108
|
+
async function status(deps = {}) {
|
|
19064
19109
|
const out = (line) => void process.stdout.write(`${line}
|
|
19065
19110
|
`);
|
|
19111
|
+
const running = await liveState();
|
|
19112
|
+
const strays = strayStatusLines(running, deps.strays);
|
|
19066
19113
|
out(` ${versionLine()}`);
|
|
19067
|
-
for (const line of otherInstallLines(
|
|
19114
|
+
for (const line of otherInstallLines(running)) out(line);
|
|
19068
19115
|
let cfg = null;
|
|
19069
19116
|
try {
|
|
19070
19117
|
cfg = loadConfig();
|
|
@@ -19073,11 +19120,11 @@ async function status() {
|
|
|
19073
19120
|
}
|
|
19074
19121
|
if (!cfg || !cfg.deviceToken) {
|
|
19075
19122
|
out(` Not paired yet. Run ${cabane("setup")} to set up this device.`);
|
|
19123
|
+
for (const line of strays) out(line);
|
|
19076
19124
|
process.exitCode = 1;
|
|
19077
19125
|
return;
|
|
19078
19126
|
}
|
|
19079
19127
|
out(row2("Paired with", `${cfg.baseUrl}${cfg.deviceLabel ? ` as ${cfg.deviceLabel}` : ""}`));
|
|
19080
|
-
const running = await liveState();
|
|
19081
19128
|
let detection = null;
|
|
19082
19129
|
if (running) {
|
|
19083
19130
|
const mode = running.daemon ? "in the background" : "in a terminal";
|
|
@@ -19087,6 +19134,7 @@ async function status() {
|
|
|
19087
19134
|
} else {
|
|
19088
19135
|
out(row2("Running", `no \u2014 start it with ${cabane("start")}`));
|
|
19089
19136
|
}
|
|
19137
|
+
for (const line of strays) out(line);
|
|
19090
19138
|
const service2 = await serviceState();
|
|
19091
19139
|
for (const line of serviceRows(service2)) out(line);
|
|
19092
19140
|
for (const line of updateRows(readUpdateState(), cfg.update?.auto, {
|
|
@@ -19158,11 +19206,8 @@ function otherInstall(install, own) {
|
|
|
19158
19206
|
return real(found) === ownAlias ? null : dirname15(found);
|
|
19159
19207
|
}
|
|
19160
19208
|
async function liveState() {
|
|
19161
|
-
const running = readLiveRuntimeState();
|
|
19162
|
-
if (running && await verifyRuntime(running) === "stale")
|
|
19163
|
-
clearRuntimeState();
|
|
19164
|
-
return null;
|
|
19165
|
-
}
|
|
19209
|
+
const running = readLiveRuntimeState({ sweep: false });
|
|
19210
|
+
if (running && await verifyRuntime(running) === "stale") return null;
|
|
19166
19211
|
return running;
|
|
19167
19212
|
}
|
|
19168
19213
|
async function runningDetection(running) {
|
|
@@ -19178,6 +19223,11 @@ async function runningDetection(running) {
|
|
|
19178
19223
|
return null;
|
|
19179
19224
|
}
|
|
19180
19225
|
}
|
|
19226
|
+
function strayStatusLines(running, find = findStrayCompanions) {
|
|
19227
|
+
const strays = find(running ? [running.pid] : []);
|
|
19228
|
+
if (strays.length === 0) return [];
|
|
19229
|
+
return strayCompanionLines(strays, cabane("status")).map((line) => ` ${line}`);
|
|
19230
|
+
}
|
|
19181
19231
|
async function agentLines2(cfg, running) {
|
|
19182
19232
|
const detection = running ?? await detectHarnesses(cfg);
|
|
19183
19233
|
return agentRows(detection, { connected: false }).map((agent) => row2(agent.label, agent.text));
|
package/dist/runtime.js
CHANGED
|
@@ -1678,7 +1678,9 @@ function clearRuntimeStateIfOurs(instanceId) {
|
|
|
1678
1678
|
}
|
|
1679
1679
|
rmSync3(path, { force: true });
|
|
1680
1680
|
}
|
|
1681
|
-
function readLiveRuntimeState(
|
|
1681
|
+
function readLiveRuntimeState({
|
|
1682
|
+
sweep = true
|
|
1683
|
+
} = {}) {
|
|
1682
1684
|
const path = runtimePath();
|
|
1683
1685
|
if (!existsSync6(path)) return null;
|
|
1684
1686
|
let parsed;
|
|
@@ -1691,7 +1693,7 @@ function readLiveRuntimeState() {
|
|
|
1691
1693
|
try {
|
|
1692
1694
|
process.kill(parsed.pid, 0);
|
|
1693
1695
|
} catch {
|
|
1694
|
-
clearRuntimeState();
|
|
1696
|
+
if (sweep) clearRuntimeState();
|
|
1695
1697
|
return null;
|
|
1696
1698
|
}
|
|
1697
1699
|
return parsed;
|
|
@@ -2494,6 +2496,12 @@ var DeviceApi = class {
|
|
|
2494
2496
|
getAssignments() {
|
|
2495
2497
|
return this.request("GET", "/api/companion/assignments");
|
|
2496
2498
|
}
|
|
2499
|
+
// Ask again for the credential of an agent assigned to this device, when the saved copy is
|
|
2500
|
+
// gone. The previous credential stops working; any agent not assigned here is a 404, and a
|
|
2501
|
+
// second ask inside a minute a 429.
|
|
2502
|
+
reissueAgentCredential(agentId) {
|
|
2503
|
+
return this.request("POST", `/api/companion/agents/${encodeURIComponent(agentId)}/credential`);
|
|
2504
|
+
}
|
|
2497
2505
|
getRuns(cursor) {
|
|
2498
2506
|
const query = new URLSearchParams({ limit: "100", order: "asc" });
|
|
2499
2507
|
if (cursor) query.set("cursor", cursor);
|
|
@@ -14172,6 +14180,7 @@ var CompanionSupervisor = class {
|
|
|
14172
14180
|
for (const it of items) {
|
|
14173
14181
|
if (it.credential) setCredential(device.id, it.agentId, it.credential);
|
|
14174
14182
|
}
|
|
14183
|
+
items = await this.restoreMissingCredentials(device.id, items);
|
|
14175
14184
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
14176
14185
|
for (const it of items) {
|
|
14177
14186
|
const list = byWorkspace.get(it.workspaceId) ?? [];
|
|
@@ -14204,6 +14213,42 @@ var CompanionSupervisor = class {
|
|
|
14204
14213
|
this.inFlightRefresh = null;
|
|
14205
14214
|
}
|
|
14206
14215
|
}
|
|
14216
|
+
// An assigned agent whose saved credential is gone (the file wiped or edited, a crash between
|
|
14217
|
+
// delivery and write) gets it back here, before reconciling. A runner still holding it in
|
|
14218
|
+
// memory writes it back; otherwise Cabane is asked for a new one, which revokes the old. The
|
|
14219
|
+
// result rides the item as a fresh delivery, so reconcile builds the runner on it. A refused
|
|
14220
|
+
// ask leaves the item as it was, and reconcile reports the agent can't run.
|
|
14221
|
+
async restoreMissingCredentials(deviceId, items) {
|
|
14222
|
+
const restored = [];
|
|
14223
|
+
for (const it of items) {
|
|
14224
|
+
if (it.credential || getCredential(deviceId, it.agentId)) {
|
|
14225
|
+
restored.push(it);
|
|
14226
|
+
continue;
|
|
14227
|
+
}
|
|
14228
|
+
const held = this.workspaces.get(it.workspaceId)?.agents.get(it.agentId)?.credential;
|
|
14229
|
+
if (held) {
|
|
14230
|
+
setCredential(deviceId, it.agentId, held);
|
|
14231
|
+
restored.push(it);
|
|
14232
|
+
continue;
|
|
14233
|
+
}
|
|
14234
|
+
try {
|
|
14235
|
+
const { credential } = await this.deviceApi.reissueAgentCredential(it.agentId);
|
|
14236
|
+
setCredential(deviceId, it.agentId, credential);
|
|
14237
|
+
this.log.info(
|
|
14238
|
+
{ agentId: it.agentId, agentName: it.agentDisplayName },
|
|
14239
|
+
`Restored ${it.agentDisplayName}'s sign-in to Cabane: it was missing on this device.`
|
|
14240
|
+
);
|
|
14241
|
+
restored.push({ ...it, credential });
|
|
14242
|
+
} catch (err) {
|
|
14243
|
+
this.log.warn(
|
|
14244
|
+
{ agentId: it.agentId, agentName: it.agentDisplayName, ...apiErrorLogFields(err) },
|
|
14245
|
+
`Couldn't restore ${it.agentDisplayName}'s sign-in to Cabane.`
|
|
14246
|
+
);
|
|
14247
|
+
restored.push(it);
|
|
14248
|
+
}
|
|
14249
|
+
}
|
|
14250
|
+
return restored;
|
|
14251
|
+
}
|
|
14207
14252
|
// ---- reconciliation ----
|
|
14208
14253
|
reconcileWorkspace(deviceId, workspaceId, list) {
|
|
14209
14254
|
const first = list[0];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.165",
|
|
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",
|