@cabane/companion 0.6.42 → 0.6.44
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 +26 -4
- package/dist/runtime.js +26 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8974,6 +8974,7 @@ var COMPANION_VERSION = pkg.version;
|
|
|
8974
8974
|
|
|
8975
8975
|
// src/supervisor.ts
|
|
8976
8976
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
8977
|
+
var SHUTDOWN_HEARTBEAT_WAIT_MS = 1e3;
|
|
8977
8978
|
function positiveIntEnv(name) {
|
|
8978
8979
|
const raw = process.env[name];
|
|
8979
8980
|
if (!raw) return void 0;
|
|
@@ -9054,7 +9055,8 @@ var CompanionSupervisor = class {
|
|
|
9054
9055
|
{ protocolVersion: TURN_PROTOCOL_VERSION, version: COMPANION_VERSION },
|
|
9055
9056
|
"companion: starting"
|
|
9056
9057
|
);
|
|
9057
|
-
|
|
9058
|
+
const startupHarnessRefresh = this.refreshHarnessStatuses();
|
|
9059
|
+
this.trackHeartbeat(startupHarnessRefresh);
|
|
9058
9060
|
if (!this.config.deviceToken) {
|
|
9059
9061
|
this.log.warn("companion: not paired (no device token) \u2014 run `cabane-companion pair`");
|
|
9060
9062
|
process.stdout.write(
|
|
@@ -9085,7 +9087,11 @@ var CompanionSupervisor = class {
|
|
|
9085
9087
|
});
|
|
9086
9088
|
this.deviceSub.start();
|
|
9087
9089
|
await this.refreshAssignments();
|
|
9088
|
-
|
|
9090
|
+
const firstHeartbeat = startupHarnessRefresh.then(async () => {
|
|
9091
|
+
if (this.stopped) return;
|
|
9092
|
+
await this.sendHeartbeat();
|
|
9093
|
+
});
|
|
9094
|
+
this.trackHeartbeat(firstHeartbeat);
|
|
9089
9095
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
9090
9096
|
this.heartbeatTimer.unref?.();
|
|
9091
9097
|
this.pollTimer = setInterval(() => void this.refreshAssignments(), ASSIGNMENTS_POLL_MS);
|
|
@@ -9103,8 +9109,10 @@ var CompanionSupervisor = class {
|
|
|
9103
9109
|
}
|
|
9104
9110
|
// ---- device-level loops ----
|
|
9105
9111
|
kickHeartbeat() {
|
|
9106
|
-
if (this.inFlightHeartbeat) return;
|
|
9107
|
-
|
|
9112
|
+
if (this.stopped || this.inFlightHeartbeat) return;
|
|
9113
|
+
this.trackHeartbeat(this.sendHeartbeat());
|
|
9114
|
+
}
|
|
9115
|
+
trackHeartbeat(pending) {
|
|
9108
9116
|
this.inFlightHeartbeat = pending;
|
|
9109
9117
|
void pending.finally(() => {
|
|
9110
9118
|
if (this.inFlightHeartbeat === pending) this.inFlightHeartbeat = null;
|
|
@@ -9851,12 +9859,14 @@ var CompanionSupervisor = class {
|
|
|
9851
9859
|
this.stopped = true;
|
|
9852
9860
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
9853
9861
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
9862
|
+
const heartbeat = this.inFlightHeartbeat;
|
|
9854
9863
|
this.deviceSub?.stop();
|
|
9855
9864
|
for (const wr of this.workspaces.values()) {
|
|
9856
9865
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
9857
9866
|
if (wr.sub) wr.sub.stop();
|
|
9858
9867
|
}
|
|
9859
9868
|
await Promise.all([
|
|
9869
|
+
...heartbeat ? [waitForBoundedHeartbeat(heartbeat)] : [],
|
|
9860
9870
|
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9861
9871
|
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
9862
9872
|
]);
|
|
@@ -9869,6 +9879,18 @@ var CompanionSupervisor = class {
|
|
|
9869
9879
|
this.reexecFn();
|
|
9870
9880
|
}
|
|
9871
9881
|
};
|
|
9882
|
+
async function waitForBoundedHeartbeat(heartbeat) {
|
|
9883
|
+
let timer = null;
|
|
9884
|
+
const timeout = new Promise((resolve) => {
|
|
9885
|
+
timer = setTimeout(resolve, SHUTDOWN_HEARTBEAT_WAIT_MS);
|
|
9886
|
+
timer.unref?.();
|
|
9887
|
+
});
|
|
9888
|
+
try {
|
|
9889
|
+
await Promise.race([heartbeat, timeout]);
|
|
9890
|
+
} finally {
|
|
9891
|
+
if (timer) clearTimeout(timer);
|
|
9892
|
+
}
|
|
9893
|
+
}
|
|
9872
9894
|
function defaultReexec() {
|
|
9873
9895
|
clearRuntimeState();
|
|
9874
9896
|
void import("child_process").then(({ spawn: spawn6 }) => {
|
package/dist/runtime.js
CHANGED
|
@@ -8483,6 +8483,7 @@ var COMPANION_VERSION = pkg.version;
|
|
|
8483
8483
|
|
|
8484
8484
|
// src/supervisor.ts
|
|
8485
8485
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
8486
|
+
var SHUTDOWN_HEARTBEAT_WAIT_MS = 1e3;
|
|
8486
8487
|
function positiveIntEnv(name) {
|
|
8487
8488
|
const raw = process.env[name];
|
|
8488
8489
|
if (!raw) return void 0;
|
|
@@ -8563,7 +8564,8 @@ var CompanionSupervisor = class {
|
|
|
8563
8564
|
{ protocolVersion: TURN_PROTOCOL_VERSION, version: COMPANION_VERSION },
|
|
8564
8565
|
"companion: starting"
|
|
8565
8566
|
);
|
|
8566
|
-
|
|
8567
|
+
const startupHarnessRefresh = this.refreshHarnessStatuses();
|
|
8568
|
+
this.trackHeartbeat(startupHarnessRefresh);
|
|
8567
8569
|
if (!this.config.deviceToken) {
|
|
8568
8570
|
this.log.warn("companion: not paired (no device token) \u2014 run `cabane-companion pair`");
|
|
8569
8571
|
process.stdout.write(
|
|
@@ -8594,7 +8596,11 @@ var CompanionSupervisor = class {
|
|
|
8594
8596
|
});
|
|
8595
8597
|
this.deviceSub.start();
|
|
8596
8598
|
await this.refreshAssignments();
|
|
8597
|
-
|
|
8599
|
+
const firstHeartbeat = startupHarnessRefresh.then(async () => {
|
|
8600
|
+
if (this.stopped) return;
|
|
8601
|
+
await this.sendHeartbeat();
|
|
8602
|
+
});
|
|
8603
|
+
this.trackHeartbeat(firstHeartbeat);
|
|
8598
8604
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
8599
8605
|
this.heartbeatTimer.unref?.();
|
|
8600
8606
|
this.pollTimer = setInterval(() => void this.refreshAssignments(), ASSIGNMENTS_POLL_MS);
|
|
@@ -8612,8 +8618,10 @@ var CompanionSupervisor = class {
|
|
|
8612
8618
|
}
|
|
8613
8619
|
// ---- device-level loops ----
|
|
8614
8620
|
kickHeartbeat() {
|
|
8615
|
-
if (this.inFlightHeartbeat) return;
|
|
8616
|
-
|
|
8621
|
+
if (this.stopped || this.inFlightHeartbeat) return;
|
|
8622
|
+
this.trackHeartbeat(this.sendHeartbeat());
|
|
8623
|
+
}
|
|
8624
|
+
trackHeartbeat(pending) {
|
|
8617
8625
|
this.inFlightHeartbeat = pending;
|
|
8618
8626
|
void pending.finally(() => {
|
|
8619
8627
|
if (this.inFlightHeartbeat === pending) this.inFlightHeartbeat = null;
|
|
@@ -9360,12 +9368,14 @@ var CompanionSupervisor = class {
|
|
|
9360
9368
|
this.stopped = true;
|
|
9361
9369
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
9362
9370
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
9371
|
+
const heartbeat = this.inFlightHeartbeat;
|
|
9363
9372
|
this.deviceSub?.stop();
|
|
9364
9373
|
for (const wr of this.workspaces.values()) {
|
|
9365
9374
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
9366
9375
|
if (wr.sub) wr.sub.stop();
|
|
9367
9376
|
}
|
|
9368
9377
|
await Promise.all([
|
|
9378
|
+
...heartbeat ? [waitForBoundedHeartbeat(heartbeat)] : [],
|
|
9369
9379
|
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9370
9380
|
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
9371
9381
|
]);
|
|
@@ -9378,6 +9388,18 @@ var CompanionSupervisor = class {
|
|
|
9378
9388
|
this.reexecFn();
|
|
9379
9389
|
}
|
|
9380
9390
|
};
|
|
9391
|
+
async function waitForBoundedHeartbeat(heartbeat) {
|
|
9392
|
+
let timer = null;
|
|
9393
|
+
const timeout = new Promise((resolve) => {
|
|
9394
|
+
timer = setTimeout(resolve, SHUTDOWN_HEARTBEAT_WAIT_MS);
|
|
9395
|
+
timer.unref?.();
|
|
9396
|
+
});
|
|
9397
|
+
try {
|
|
9398
|
+
await Promise.race([heartbeat, timeout]);
|
|
9399
|
+
} finally {
|
|
9400
|
+
if (timer) clearTimeout(timer);
|
|
9401
|
+
}
|
|
9402
|
+
}
|
|
9381
9403
|
function defaultReexec() {
|
|
9382
9404
|
clearRuntimeState();
|
|
9383
9405
|
void import("child_process").then(({ spawn: spawn5 }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.44",
|
|
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",
|