@slock-ai/daemon 0.29.0 → 0.30.0
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/index.js +70 -10
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -10,26 +10,39 @@ import { fileURLToPath } from "url";
|
|
|
10
10
|
|
|
11
11
|
// src/connection.ts
|
|
12
12
|
import WebSocket from "ws";
|
|
13
|
+
var systemClock = {
|
|
14
|
+
now: () => Date.now(),
|
|
15
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
16
|
+
clearTimeout: (timer) => clearTimeout(timer)
|
|
17
|
+
};
|
|
18
|
+
var INBOUND_WATCHDOG_MS = 7e4;
|
|
13
19
|
var DaemonConnection = class {
|
|
14
20
|
ws = null;
|
|
15
21
|
options;
|
|
22
|
+
clock;
|
|
16
23
|
reconnectTimer = null;
|
|
17
|
-
|
|
24
|
+
watchdogTimer = null;
|
|
25
|
+
reconnectDelay;
|
|
18
26
|
maxReconnectDelay = 3e4;
|
|
19
27
|
shouldConnect = true;
|
|
20
28
|
reconnectAttempt = 0;
|
|
21
29
|
lastDroppedSendLogAt = 0;
|
|
22
30
|
constructor(options) {
|
|
23
31
|
this.options = options;
|
|
32
|
+
this.clock = options.clock ?? systemClock;
|
|
33
|
+
this.reconnectDelay = options.minReconnectDelayMs ?? 1e3;
|
|
24
34
|
}
|
|
25
35
|
connect() {
|
|
26
36
|
this.shouldConnect = true;
|
|
37
|
+
if (this.reconnectTimer) return;
|
|
38
|
+
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) return;
|
|
27
39
|
this.doConnect();
|
|
28
40
|
}
|
|
29
41
|
disconnect() {
|
|
30
42
|
this.shouldConnect = false;
|
|
43
|
+
this.clearWatchdog();
|
|
31
44
|
if (this.reconnectTimer) {
|
|
32
|
-
clearTimeout(this.reconnectTimer);
|
|
45
|
+
this.clock.clearTimeout(this.reconnectTimer);
|
|
33
46
|
this.reconnectTimer = null;
|
|
34
47
|
}
|
|
35
48
|
if (this.ws) {
|
|
@@ -43,7 +56,7 @@ var DaemonConnection = class {
|
|
|
43
56
|
this.ws.send(JSON.stringify(msg));
|
|
44
57
|
return;
|
|
45
58
|
}
|
|
46
|
-
const now =
|
|
59
|
+
const now = this.clock.now();
|
|
47
60
|
if (now - this.lastDroppedSendLogAt > 5e3) {
|
|
48
61
|
this.lastDroppedSendLogAt = now;
|
|
49
62
|
console.warn(`[Daemon] Dropping outbound message while disconnected: ${msg.type}`);
|
|
@@ -54,16 +67,23 @@ var DaemonConnection = class {
|
|
|
54
67
|
}
|
|
55
68
|
doConnect() {
|
|
56
69
|
if (!this.shouldConnect) return;
|
|
70
|
+
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) return;
|
|
57
71
|
const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + `/daemon/connect?key=${this.options.apiKey}`;
|
|
58
72
|
console.log(`[Daemon] Connecting to ${this.options.serverUrl}...`);
|
|
59
|
-
|
|
60
|
-
this.ws
|
|
73
|
+
const ws = this.options.wsFactory ? this.options.wsFactory(wsUrl) : new WebSocket(wsUrl);
|
|
74
|
+
this.ws = ws;
|
|
75
|
+
ws.on("open", () => {
|
|
76
|
+
if (this.ws !== ws) return;
|
|
77
|
+
if (!this.shouldConnect) return;
|
|
61
78
|
console.log("[Daemon] Connected to server");
|
|
62
79
|
this.reconnectAttempt = 0;
|
|
63
|
-
this.reconnectDelay = 1e3;
|
|
80
|
+
this.reconnectDelay = this.options.minReconnectDelayMs ?? 1e3;
|
|
81
|
+
this.resetWatchdog();
|
|
64
82
|
this.options.onConnect();
|
|
65
83
|
});
|
|
66
|
-
|
|
84
|
+
ws.on("message", (data) => {
|
|
85
|
+
if (this.ws !== ws) return;
|
|
86
|
+
this.resetWatchdog();
|
|
67
87
|
try {
|
|
68
88
|
const msg = JSON.parse(data.toString());
|
|
69
89
|
this.options.onMessage(msg);
|
|
@@ -71,7 +91,10 @@ var DaemonConnection = class {
|
|
|
71
91
|
console.error("[Daemon] Invalid message from server:", err);
|
|
72
92
|
}
|
|
73
93
|
});
|
|
74
|
-
|
|
94
|
+
ws.on("close", (code, reasonBuffer) => {
|
|
95
|
+
if (this.ws !== ws) return;
|
|
96
|
+
this.ws = null;
|
|
97
|
+
this.clearWatchdog();
|
|
75
98
|
const reason = reasonBuffer.toString("utf8");
|
|
76
99
|
console.log(
|
|
77
100
|
`[Daemon] Disconnected from server (code=${code}, reason=${JSON.stringify(reason)}, reconnecting=${this.shouldConnect})`
|
|
@@ -79,7 +102,8 @@ var DaemonConnection = class {
|
|
|
79
102
|
this.options.onDisconnect();
|
|
80
103
|
this.scheduleReconnect();
|
|
81
104
|
});
|
|
82
|
-
|
|
105
|
+
ws.on("error", (err) => {
|
|
106
|
+
if (this.ws !== ws) return;
|
|
83
107
|
console.error("[Daemon] WebSocket error:", err.message);
|
|
84
108
|
});
|
|
85
109
|
}
|
|
@@ -88,12 +112,29 @@ var DaemonConnection = class {
|
|
|
88
112
|
if (this.reconnectTimer) return;
|
|
89
113
|
this.reconnectAttempt += 1;
|
|
90
114
|
console.log(`[Daemon] Reconnecting to server in ${this.reconnectDelay}ms (attempt ${this.reconnectAttempt})`);
|
|
91
|
-
this.reconnectTimer = setTimeout(() => {
|
|
115
|
+
this.reconnectTimer = this.clock.setTimeout(() => {
|
|
92
116
|
this.reconnectTimer = null;
|
|
93
117
|
this.doConnect();
|
|
94
118
|
}, this.reconnectDelay);
|
|
95
119
|
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
|
|
96
120
|
}
|
|
121
|
+
resetWatchdog() {
|
|
122
|
+
this.clearWatchdog();
|
|
123
|
+
const ms = this.options.inboundWatchdogMs ?? INBOUND_WATCHDOG_MS;
|
|
124
|
+
this.watchdogTimer = this.clock.setTimeout(() => {
|
|
125
|
+
console.warn(`[Daemon] No inbound traffic for ${ms / 1e3}s \u2014 forcing reconnect`);
|
|
126
|
+
try {
|
|
127
|
+
this.ws?.terminate();
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}, ms);
|
|
131
|
+
}
|
|
132
|
+
clearWatchdog() {
|
|
133
|
+
if (this.watchdogTimer) {
|
|
134
|
+
this.clock.clearTimeout(this.watchdogTimer);
|
|
135
|
+
this.watchdogTimer = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
97
138
|
};
|
|
98
139
|
|
|
99
140
|
// src/agentProcessManager.ts
|
|
@@ -1288,6 +1329,16 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1288
1329
|
getRunningAgentIds() {
|
|
1289
1330
|
return [...this.agents.keys()];
|
|
1290
1331
|
}
|
|
1332
|
+
getAgentSessionId(agentId) {
|
|
1333
|
+
return this.agents.get(agentId)?.sessionId ?? null;
|
|
1334
|
+
}
|
|
1335
|
+
getIdleAgentSessionIds() {
|
|
1336
|
+
const result = [];
|
|
1337
|
+
for (const [agentId, { sessionId }] of this.idleAgentConfigs) {
|
|
1338
|
+
if (sessionId) result.push({ agentId, sessionId });
|
|
1339
|
+
}
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1291
1342
|
// Machine-level workspace scanning
|
|
1292
1343
|
async scanAllWorkspaces() {
|
|
1293
1344
|
const results = [];
|
|
@@ -1851,6 +1902,15 @@ connection = new DaemonConnection({
|
|
|
1851
1902
|
os: `${os2.platform()} ${os2.arch()}`,
|
|
1852
1903
|
daemonVersion: DAEMON_VERSION
|
|
1853
1904
|
});
|
|
1905
|
+
for (const agentId of agentManager.getRunningAgentIds()) {
|
|
1906
|
+
const sessionId = agentManager.getAgentSessionId(agentId);
|
|
1907
|
+
if (sessionId) {
|
|
1908
|
+
connection.send({ type: "agent:session", agentId, sessionId });
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
for (const { agentId, sessionId } of agentManager.getIdleAgentSessionIds()) {
|
|
1912
|
+
connection.send({ type: "agent:session", agentId, sessionId });
|
|
1913
|
+
}
|
|
1854
1914
|
},
|
|
1855
1915
|
onDisconnect: () => {
|
|
1856
1916
|
console.log("[Daemon] Lost connection \u2014 agents continue running locally");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slock-ai/daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"slock-daemon": "dist/index.js"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dev": "tsx watch src/index.ts",
|
|
33
33
|
"start": "tsx src/index.ts",
|
|
34
34
|
"build": "tsup",
|
|
35
|
-
"test": "node --import tsx --test src/**/*.test.ts",
|
|
35
|
+
"test": "node --import tsx --test --test-force-exit 'src/**/*.test.ts'",
|
|
36
36
|
"typecheck": "tsc --noEmit",
|
|
37
37
|
"release:patch": "npm version patch --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
38
38
|
"release:minor": "npm version minor --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|