@engineeros/connector 0.6.0 → 0.6.1
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/bin/engineeros-connector.mjs +39 -4
- package/package.json +2 -2
- package/src/connection.mjs +26 -0
|
@@ -19,6 +19,10 @@ import {
|
|
|
19
19
|
stopProcess,
|
|
20
20
|
workspaceSnapshot,
|
|
21
21
|
} from "../src/runner.mjs";
|
|
22
|
+
import {
|
|
23
|
+
describeWebSocketError,
|
|
24
|
+
startConnectionWatchdog,
|
|
25
|
+
} from "../src/connection.mjs";
|
|
22
26
|
|
|
23
27
|
const { command, positional, flags } = parseArgs(process.argv.slice(2));
|
|
24
28
|
|
|
@@ -101,12 +105,18 @@ const assessments = [];
|
|
|
101
105
|
const prompts = [];
|
|
102
106
|
let socket;
|
|
103
107
|
let pingTimer;
|
|
108
|
+
let reconnectTimer;
|
|
109
|
+
let clearConnectionWatchdog;
|
|
104
110
|
let reconnectDelay = 1_000;
|
|
105
111
|
let connectionRejected = false;
|
|
112
|
+
let lastConnectionError;
|
|
106
113
|
let snapshotInFlight = false;
|
|
107
114
|
|
|
108
115
|
process.on("SIGINT", async () => {
|
|
109
116
|
stopped = true;
|
|
117
|
+
clearConnectionWatchdog?.();
|
|
118
|
+
clearTimeout(reconnectTimer);
|
|
119
|
+
clearInterval(pingTimer);
|
|
110
120
|
await stopProcess(active?.child);
|
|
111
121
|
socket?.close();
|
|
112
122
|
process.exit(0);
|
|
@@ -117,13 +127,22 @@ await connect();
|
|
|
117
127
|
async function connect() {
|
|
118
128
|
console.log(`Connecting ${config.name} to ${config.server_url}`);
|
|
119
129
|
connectionRejected = false;
|
|
130
|
+
lastConnectionError = undefined;
|
|
120
131
|
socket = new WebSocket(config.server_url);
|
|
132
|
+
clearConnectionWatchdog = startConnectionWatchdog(socket, config.server_url, {
|
|
133
|
+
onTimeout: (message) => {
|
|
134
|
+
lastConnectionError = message;
|
|
135
|
+
console.error(message);
|
|
136
|
+
scheduleReconnect();
|
|
137
|
+
},
|
|
138
|
+
});
|
|
121
139
|
socket.addEventListener("open", () =>
|
|
122
140
|
socket.send(JSON.stringify(firstMessage)),
|
|
123
141
|
);
|
|
124
142
|
socket.addEventListener("message", async (event) => {
|
|
125
143
|
const message = JSON.parse(String(event.data));
|
|
126
144
|
if (message.type === "paired") {
|
|
145
|
+
clearConnectionWatchdog?.();
|
|
127
146
|
config = {
|
|
128
147
|
...config,
|
|
129
148
|
connector_id: message.connector.id,
|
|
@@ -142,6 +161,7 @@ async function connect() {
|
|
|
142
161
|
return;
|
|
143
162
|
}
|
|
144
163
|
if (message.type === "authenticated") {
|
|
164
|
+
clearConnectionWatchdog?.();
|
|
145
165
|
console.log("Connected and waiting for EngineerOS runs.");
|
|
146
166
|
reconnectDelay = 1_000;
|
|
147
167
|
startPings();
|
|
@@ -220,6 +240,7 @@ async function connect() {
|
|
|
220
240
|
}
|
|
221
241
|
});
|
|
222
242
|
socket.addEventListener("close", () => {
|
|
243
|
+
clearConnectionWatchdog?.();
|
|
223
244
|
clearInterval(pingTimer);
|
|
224
245
|
if (active?.kind === "prompt") {
|
|
225
246
|
active.cancelled = true;
|
|
@@ -233,13 +254,27 @@ async function connect() {
|
|
|
233
254
|
return;
|
|
234
255
|
}
|
|
235
256
|
if (stopped) return;
|
|
257
|
+
scheduleReconnect();
|
|
258
|
+
});
|
|
259
|
+
socket.addEventListener("error", (event) => {
|
|
260
|
+
lastConnectionError = describeWebSocketError(event);
|
|
236
261
|
console.error(
|
|
237
|
-
`
|
|
262
|
+
`WebSocket connection to ${config.server_url} failed: ${lastConnectionError}`,
|
|
238
263
|
);
|
|
239
|
-
setTimeout(connect, reconnectDelay);
|
|
240
|
-
reconnectDelay = Math.min(30_000, reconnectDelay * 2);
|
|
241
264
|
});
|
|
242
|
-
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function scheduleReconnect() {
|
|
268
|
+
if (stopped || connectionRejected || reconnectTimer) return;
|
|
269
|
+
const detail = lastConnectionError ? ` Last error: ${lastConnectionError}` : "";
|
|
270
|
+
console.error(
|
|
271
|
+
`Connection unavailable.${detail} Retrying in ${Math.round(reconnectDelay / 1_000)}s.`,
|
|
272
|
+
);
|
|
273
|
+
reconnectTimer = setTimeout(() => {
|
|
274
|
+
reconnectTimer = undefined;
|
|
275
|
+
void connect();
|
|
276
|
+
}, reconnectDelay);
|
|
277
|
+
reconnectDelay = Math.min(30_000, reconnectDelay * 2);
|
|
243
278
|
}
|
|
244
279
|
|
|
245
280
|
async function submitWorkspaceSnapshot() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@engineeros/connector",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"start": "node ./bin/engineeros-connector.mjs",
|
|
18
18
|
"test": "node --test --test-concurrency=1",
|
|
19
|
-
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/runner.mjs"
|
|
19
|
+
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/connection.mjs && node --check ./src/runner.mjs"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=22"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function describeWebSocketError(event) {
|
|
2
|
+
return (
|
|
3
|
+
event?.error?.message ||
|
|
4
|
+
event?.message ||
|
|
5
|
+
"The WebSocket connection failed without providing an error detail."
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function startConnectionWatchdog(
|
|
10
|
+
socket,
|
|
11
|
+
url,
|
|
12
|
+
{ timeoutMs = 15_000, onTimeout = console.error } = {},
|
|
13
|
+
) {
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
onTimeout(
|
|
16
|
+
`EngineerOS did not complete the WebSocket handshake at ${url} within ${Math.round(timeoutMs / 1_000)} seconds. Check that the backend is running and the URL is reachable from this machine.`,
|
|
17
|
+
);
|
|
18
|
+
try {
|
|
19
|
+
socket.close();
|
|
20
|
+
} catch {
|
|
21
|
+
// Reconnect scheduling is owned by the caller.
|
|
22
|
+
}
|
|
23
|
+
}, timeoutMs);
|
|
24
|
+
|
|
25
|
+
return () => clearTimeout(timer);
|
|
26
|
+
}
|