@cabane/companion 0.6.29 → 0.6.30
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 +31 -6
- package/dist/runtime.js +31 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8807,6 +8807,7 @@ var SseSubscriber = class {
|
|
|
8807
8807
|
try {
|
|
8808
8808
|
await this.connect();
|
|
8809
8809
|
backoff = 500;
|
|
8810
|
+
if (!this.aborted) await sleep3(backoff);
|
|
8810
8811
|
} catch (err) {
|
|
8811
8812
|
if (this.aborted) return;
|
|
8812
8813
|
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
@@ -8832,8 +8833,8 @@ var SseSubscriber = class {
|
|
|
8832
8833
|
}
|
|
8833
8834
|
async connect() {
|
|
8834
8835
|
const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
8835
|
-
const query = this.lastEventId ? "" : "?replayPending=1";
|
|
8836
|
-
const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
8836
|
+
const query = this.opts.path ? "" : this.lastEventId ? "" : "?replayPending=1";
|
|
8837
|
+
const url = this.opts.path ? `${base}${this.opts.path}` : `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
8837
8838
|
this.controller = new AbortController();
|
|
8838
8839
|
const headers = {
|
|
8839
8840
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -8925,6 +8926,7 @@ var CompanionSupervisor = class {
|
|
|
8925
8926
|
reexecFn;
|
|
8926
8927
|
dispatcherFactory;
|
|
8927
8928
|
deviceApi = null;
|
|
8929
|
+
deviceSub = null;
|
|
8928
8930
|
heartbeatTimer = null;
|
|
8929
8931
|
inFlightHeartbeat = null;
|
|
8930
8932
|
pollTimer = null;
|
|
@@ -8978,6 +8980,24 @@ var CompanionSupervisor = class {
|
|
|
8978
8980
|
baseUrl: this.config.baseUrl,
|
|
8979
8981
|
deviceToken: this.config.deviceToken
|
|
8980
8982
|
});
|
|
8983
|
+
this.deviceSub = new SseSubscriber({
|
|
8984
|
+
baseUrl: this.config.baseUrl,
|
|
8985
|
+
workspaceId: "device-control",
|
|
8986
|
+
path: "/api/companion/events",
|
|
8987
|
+
token: this.config.deviceToken,
|
|
8988
|
+
log: this.log,
|
|
8989
|
+
lastEventId: null,
|
|
8990
|
+
onMessage: async (ev) => {
|
|
8991
|
+
if (ev.event === "assignments_changed") {
|
|
8992
|
+
if (this.refreshing && this.inFlightRefresh) await this.inFlightRefresh;
|
|
8993
|
+
await this.refreshAssignments();
|
|
8994
|
+
}
|
|
8995
|
+
},
|
|
8996
|
+
onAuthFailure: () => {
|
|
8997
|
+
this.log.error("companion: device control stream authentication failed");
|
|
8998
|
+
}
|
|
8999
|
+
});
|
|
9000
|
+
this.deviceSub.start();
|
|
8981
9001
|
await this.refreshAssignments();
|
|
8982
9002
|
this.kickHeartbeat();
|
|
8983
9003
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
@@ -8990,7 +9010,10 @@ var CompanionSupervisor = class {
|
|
|
8990
9010
|
// at least the loops keeping the event loop alive; the SSE promises let a
|
|
8991
9011
|
// clean teardown resolve.
|
|
8992
9012
|
finishedPromises() {
|
|
8993
|
-
return [
|
|
9013
|
+
return [
|
|
9014
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9015
|
+
...[...this.workspaces.values()].flatMap((w) => w.sub ? [w.sub.finished] : [])
|
|
9016
|
+
];
|
|
8994
9017
|
}
|
|
8995
9018
|
// ---- device-level loops ----
|
|
8996
9019
|
kickHeartbeat() {
|
|
@@ -9674,13 +9697,15 @@ var CompanionSupervisor = class {
|
|
|
9674
9697
|
this.stopped = true;
|
|
9675
9698
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
9676
9699
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
9700
|
+
this.deviceSub?.stop();
|
|
9677
9701
|
for (const wr of this.workspaces.values()) {
|
|
9678
9702
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
9679
9703
|
if (wr.sub) wr.sub.stop();
|
|
9680
9704
|
}
|
|
9681
|
-
await Promise.all(
|
|
9682
|
-
|
|
9683
|
-
|
|
9705
|
+
await Promise.all([
|
|
9706
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
9707
|
+
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
9708
|
+
]);
|
|
9684
9709
|
}
|
|
9685
9710
|
async drainForRestart(graceMs) {
|
|
9686
9711
|
this.restartPending = true;
|
package/dist/runtime.js
CHANGED
|
@@ -7919,6 +7919,7 @@ var SseSubscriber = class {
|
|
|
7919
7919
|
try {
|
|
7920
7920
|
await this.connect();
|
|
7921
7921
|
backoff = 500;
|
|
7922
|
+
if (!this.aborted) await sleep2(backoff);
|
|
7922
7923
|
} catch (err) {
|
|
7923
7924
|
if (this.aborted) return;
|
|
7924
7925
|
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
|
|
@@ -7944,8 +7945,8 @@ var SseSubscriber = class {
|
|
|
7944
7945
|
}
|
|
7945
7946
|
async connect() {
|
|
7946
7947
|
const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
7947
|
-
const query = this.lastEventId ? "" : "?replayPending=1";
|
|
7948
|
-
const url = `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
7948
|
+
const query = this.opts.path ? "" : this.lastEventId ? "" : "?replayPending=1";
|
|
7949
|
+
const url = this.opts.path ? `${base}${this.opts.path}` : `${base}/api/workspaces/${this.opts.workspaceId}/events${query}`;
|
|
7949
7950
|
this.controller = new AbortController();
|
|
7950
7951
|
const headers = {
|
|
7951
7952
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -8037,6 +8038,7 @@ var CompanionSupervisor = class {
|
|
|
8037
8038
|
reexecFn;
|
|
8038
8039
|
dispatcherFactory;
|
|
8039
8040
|
deviceApi = null;
|
|
8041
|
+
deviceSub = null;
|
|
8040
8042
|
heartbeatTimer = null;
|
|
8041
8043
|
inFlightHeartbeat = null;
|
|
8042
8044
|
pollTimer = null;
|
|
@@ -8090,6 +8092,24 @@ var CompanionSupervisor = class {
|
|
|
8090
8092
|
baseUrl: this.config.baseUrl,
|
|
8091
8093
|
deviceToken: this.config.deviceToken
|
|
8092
8094
|
});
|
|
8095
|
+
this.deviceSub = new SseSubscriber({
|
|
8096
|
+
baseUrl: this.config.baseUrl,
|
|
8097
|
+
workspaceId: "device-control",
|
|
8098
|
+
path: "/api/companion/events",
|
|
8099
|
+
token: this.config.deviceToken,
|
|
8100
|
+
log: this.log,
|
|
8101
|
+
lastEventId: null,
|
|
8102
|
+
onMessage: async (ev) => {
|
|
8103
|
+
if (ev.event === "assignments_changed") {
|
|
8104
|
+
if (this.refreshing && this.inFlightRefresh) await this.inFlightRefresh;
|
|
8105
|
+
await this.refreshAssignments();
|
|
8106
|
+
}
|
|
8107
|
+
},
|
|
8108
|
+
onAuthFailure: () => {
|
|
8109
|
+
this.log.error("companion: device control stream authentication failed");
|
|
8110
|
+
}
|
|
8111
|
+
});
|
|
8112
|
+
this.deviceSub.start();
|
|
8093
8113
|
await this.refreshAssignments();
|
|
8094
8114
|
this.kickHeartbeat();
|
|
8095
8115
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
@@ -8102,7 +8122,10 @@ var CompanionSupervisor = class {
|
|
|
8102
8122
|
// at least the loops keeping the event loop alive; the SSE promises let a
|
|
8103
8123
|
// clean teardown resolve.
|
|
8104
8124
|
finishedPromises() {
|
|
8105
|
-
return [
|
|
8125
|
+
return [
|
|
8126
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
8127
|
+
...[...this.workspaces.values()].flatMap((w) => w.sub ? [w.sub.finished] : [])
|
|
8128
|
+
];
|
|
8106
8129
|
}
|
|
8107
8130
|
// ---- device-level loops ----
|
|
8108
8131
|
kickHeartbeat() {
|
|
@@ -8786,13 +8809,15 @@ var CompanionSupervisor = class {
|
|
|
8786
8809
|
this.stopped = true;
|
|
8787
8810
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
8788
8811
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
8812
|
+
this.deviceSub?.stop();
|
|
8789
8813
|
for (const wr of this.workspaces.values()) {
|
|
8790
8814
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
8791
8815
|
if (wr.sub) wr.sub.stop();
|
|
8792
8816
|
}
|
|
8793
|
-
await Promise.all(
|
|
8794
|
-
|
|
8795
|
-
|
|
8817
|
+
await Promise.all([
|
|
8818
|
+
...this.deviceSub ? [this.deviceSub.finished] : [],
|
|
8819
|
+
...[...this.workspaces.values()].flatMap((wr) => wr.sub ? [wr.sub.finished] : [])
|
|
8820
|
+
]);
|
|
8796
8821
|
}
|
|
8797
8822
|
async drainForRestart(graceMs) {
|
|
8798
8823
|
this.restartPending = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cabane/companion",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.30",
|
|
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",
|