@openclaw-cloud/agent-controller 0.1.8 → 0.2.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/CLAUDE.md +9 -8
- package/__tests__/api.test.ts +60 -0
- package/__tests__/board-handler.test.ts +2 -0
- package/__tests__/connection.test.ts +25 -4
- package/__tests__/heartbeat.test.ts +22 -29
- package/bin/agent-controller.js +6 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +12 -0
- package/dist/api.js.map +1 -1
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +45 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +22 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/connection.d.ts +2 -0
- package/dist/connection.js +3 -3
- package/dist/connection.js.map +1 -1
- package/dist/heartbeat.d.ts +2 -2
- package/dist/heartbeat.js +4 -29
- package/dist/heartbeat.js.map +1 -1
- package/dist/index.js +28 -41
- package/dist/index.js.map +1 -1
- package/dist/platform/linux.d.ts +8 -0
- package/dist/platform/linux.js +96 -0
- package/dist/platform/linux.js.map +1 -0
- package/dist/platform/macos.d.ts +8 -0
- package/dist/platform/macos.js +109 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/platform/windows.d.ts +8 -0
- package/dist/platform/windows.js +77 -0
- package/dist/platform/windows.js.map +1 -0
- package/package.json +1 -1
- package/src/api.ts +14 -0
- package/src/commands/install.ts +50 -0
- package/src/commands/uninstall.ts +19 -0
- package/src/connection.ts +5 -3
- package/src/heartbeat.ts +6 -30
- package/src/index.ts +29 -42
- package/src/platform/linux.ts +108 -0
- package/src/platform/macos.ts +122 -0
- package/src/platform/windows.ts +92 -0
package/CLAUDE.md
CHANGED
|
@@ -91,7 +91,7 @@ src/
|
|
|
91
91
|
|
|
92
92
|
### Command dispatch flow
|
|
93
93
|
```
|
|
94
|
-
Centrifugo WS message → connection.ts (handler map) → handlers/<type>.ts → AgentResponse →
|
|
94
|
+
Centrifugo WS message → connection.ts (handler map) → handlers/<type>.ts → AgentResponse → POST /api/internal/agent-response
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Handler map in `connection.ts`:
|
|
@@ -132,13 +132,16 @@ Agent-controller is a Node.js process that runs inside every agent container (K8
|
|
|
132
132
|
### Communication Flow
|
|
133
133
|
```
|
|
134
134
|
Backend → Centrifugo WS (port 8000) → agent-controller → local agent process
|
|
135
|
-
agent-controller →
|
|
135
|
+
agent-controller → Backend HTTP API → /api/internal/agent-response (command responses)
|
|
136
|
+
agent-controller → Backend HTTP API → /api/internal/agent-heartbeat (heartbeat)
|
|
136
137
|
```
|
|
137
138
|
|
|
139
|
+
Centrifugo WS is **read-only** from agent-controller's perspective — all outbound traffic goes through the backend HTTP API, not directly to Centrifugo.
|
|
140
|
+
|
|
138
141
|
### Key Components
|
|
139
|
-
- **WebSocket client** — Connects to Centrifugo, subscribes to agent channel
|
|
140
|
-
- **Heartbeat publisher** — Sends periodic heartbeats via
|
|
141
|
-
- **Command handlers** — Processes commands from backend
|
|
142
|
+
- **WebSocket client** — Connects to Centrifugo, subscribes to agent channel (read-only)
|
|
143
|
+
- **Heartbeat publisher** — Sends periodic heartbeats via `POST /api/internal/agent-heartbeat`
|
|
144
|
+
- **Command handlers** — Processes commands from backend, sends responses via `POST /api/internal/agent-response`
|
|
142
145
|
- **Board handler** — Kanban board integration (claim cards, move cards, report status)
|
|
143
146
|
- **JWT auth** — Fetches JWT from backend's `/api/internal/centrifugo-token` endpoint, auto-refreshes before expiry (24h TTL)
|
|
144
147
|
|
|
@@ -146,9 +149,7 @@ agent-controller → Centrifugo HTTP API (port 9000) → Backend (heartbeat proc
|
|
|
146
149
|
- `AGENT_ID` — UUID of this agent
|
|
147
150
|
- `AGENT_TOKEN` — Internal auth token
|
|
148
151
|
- `CENTRIFUGO_URL` — WebSocket URL (port 8000, converted from http→ws)
|
|
149
|
-
- `
|
|
150
|
-
- `CENTRIFUGO_API_KEY` — API key for HTTP API auth
|
|
151
|
-
- `API_BASE_URL` — Backend API URL for JWT fetch
|
|
152
|
+
- `API_BASE_URL` — Backend API URL (JWT fetch, agent responses, heartbeat)
|
|
152
153
|
|
|
153
154
|
### Deployment
|
|
154
155
|
|
package/__tests__/api.test.ts
CHANGED
|
@@ -121,3 +121,63 @@ describe('createAgentApi – post()', () => {
|
|
|
121
121
|
await expect(api.post('/endpoint')).rejects.toThrow('timeout');
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
describe('createAgentApi – publishResponse()', () => {
|
|
126
|
+
it('POSTs to /api/internal/agent-response with agentId and data', async () => {
|
|
127
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
128
|
+
mockFetch.mockResolvedValue(makeOkResponse(null));
|
|
129
|
+
|
|
130
|
+
await api.publishResponse('agent-1', { id: 'cmd-1', type: 'exec', success: true });
|
|
131
|
+
|
|
132
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
133
|
+
expect(url).toBe('http://backend/api/internal/agent-response');
|
|
134
|
+
expect(init.method).toBe('POST');
|
|
135
|
+
expect(JSON.parse(init.body)).toEqual({
|
|
136
|
+
agentId: 'agent-1',
|
|
137
|
+
data: { id: 'cmd-1', type: 'exec', success: true },
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('throws when response is not ok', async () => {
|
|
142
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
143
|
+
mockFetch.mockResolvedValue(makeErrorResponse(503));
|
|
144
|
+
|
|
145
|
+
await expect(api.publishResponse('agent-1', {})).rejects.toThrow('HTTP 503');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('propagates network errors', async () => {
|
|
149
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
150
|
+
mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));
|
|
151
|
+
|
|
152
|
+
await expect(api.publishResponse('agent-1', {})).rejects.toThrow('ECONNREFUSED');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('createAgentApi – publishHeartbeat()', () => {
|
|
157
|
+
it('POSTs to /api/internal/agent-heartbeat with agentId and payload', async () => {
|
|
158
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
159
|
+
mockFetch.mockResolvedValue(makeOkResponse(null));
|
|
160
|
+
|
|
161
|
+
const payload = { type: 'heartbeat', agentId: 'agent-1', ts: 123, metrics: {} };
|
|
162
|
+
await api.publishHeartbeat('agent-1', payload);
|
|
163
|
+
|
|
164
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
165
|
+
expect(url).toBe('http://backend/api/internal/agent-heartbeat');
|
|
166
|
+
expect(init.method).toBe('POST');
|
|
167
|
+
expect(JSON.parse(init.body)).toEqual({ agentId: 'agent-1', payload });
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('throws when response is not ok', async () => {
|
|
171
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
172
|
+
mockFetch.mockResolvedValue(makeErrorResponse(500));
|
|
173
|
+
|
|
174
|
+
await expect(api.publishHeartbeat('agent-1', {})).rejects.toThrow('HTTP 500');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('propagates network errors', async () => {
|
|
178
|
+
const api = createAgentApi('http://backend', 'tok');
|
|
179
|
+
mockFetch.mockRejectedValue(new Error('timeout'));
|
|
180
|
+
|
|
181
|
+
await expect(api.publishHeartbeat('agent-1', {})).rejects.toThrow('timeout');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -10,6 +10,8 @@ function mockApi(overrides: Partial<AgentApi> = {}): AgentApi {
|
|
|
10
10
|
return {
|
|
11
11
|
get: jest.fn(),
|
|
12
12
|
post: jest.fn(),
|
|
13
|
+
publishResponse: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
publishHeartbeat: jest.fn().mockResolvedValue(undefined),
|
|
13
15
|
...overrides,
|
|
14
16
|
};
|
|
15
17
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Centrifuge } from 'centrifuge';
|
|
2
|
+
import type { AgentApi } from '../src/api';
|
|
2
3
|
|
|
3
4
|
jest.mock('centrifuge');
|
|
4
5
|
jest.mock('ws', () => jest.fn());
|
|
@@ -34,6 +35,15 @@ function createMockClient(mockSubs: ReturnType<typeof createMockSubscription>[])
|
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
function makeApi(): AgentApi & { publishResponse: jest.Mock } {
|
|
39
|
+
return {
|
|
40
|
+
get: jest.fn(),
|
|
41
|
+
post: jest.fn(),
|
|
42
|
+
publishResponse: jest.fn().mockResolvedValue(undefined),
|
|
43
|
+
publishHeartbeat: jest.fn().mockResolvedValue(undefined),
|
|
44
|
+
} as unknown as AgentApi & { publishResponse: jest.Mock };
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
function mockFetchOk(token = JWT) {
|
|
38
48
|
global.fetch = jest.fn().mockResolvedValue({
|
|
39
49
|
ok: true,
|
|
@@ -59,6 +69,7 @@ describe('createConnection', () => {
|
|
|
59
69
|
token: AGENT_TOKEN,
|
|
60
70
|
agentId: AGENT_ID,
|
|
61
71
|
backendUrl: BACKEND_URL,
|
|
72
|
+
api: makeApi(),
|
|
62
73
|
});
|
|
63
74
|
|
|
64
75
|
expect(MockCentrifuge).toHaveBeenCalledWith(
|
|
@@ -91,6 +102,7 @@ describe('createConnection', () => {
|
|
|
91
102
|
token: AGENT_TOKEN,
|
|
92
103
|
agentId: AGENT_ID,
|
|
93
104
|
backendUrl: BACKEND_URL,
|
|
105
|
+
api: makeApi(),
|
|
94
106
|
});
|
|
95
107
|
|
|
96
108
|
expect(capturedGetToken).toBeDefined();
|
|
@@ -135,6 +147,7 @@ describe('createConnection', () => {
|
|
|
135
147
|
token: AGENT_TOKEN,
|
|
136
148
|
agentId: AGENT_ID,
|
|
137
149
|
backendUrl: BACKEND_URL,
|
|
150
|
+
api: makeApi(),
|
|
138
151
|
});
|
|
139
152
|
|
|
140
153
|
const result = await capturedGetToken!();
|
|
@@ -167,6 +180,7 @@ describe('createConnection', () => {
|
|
|
167
180
|
token: AGENT_TOKEN,
|
|
168
181
|
agentId: AGENT_ID,
|
|
169
182
|
backendUrl: BACKEND_URL,
|
|
183
|
+
api: makeApi(),
|
|
170
184
|
});
|
|
171
185
|
|
|
172
186
|
await expect(capturedGetToken!()).rejects.toThrow('network down');
|
|
@@ -197,6 +211,7 @@ describe('createConnection', () => {
|
|
|
197
211
|
token: AGENT_TOKEN,
|
|
198
212
|
agentId: AGENT_ID,
|
|
199
213
|
backendUrl: BACKEND_URL,
|
|
214
|
+
api: makeApi(),
|
|
200
215
|
});
|
|
201
216
|
|
|
202
217
|
await expect(capturedGetToken!()).rejects.toThrow('HTTP 401');
|
|
@@ -205,7 +220,7 @@ describe('createConnection', () => {
|
|
|
205
220
|
consoleSpy.mockRestore();
|
|
206
221
|
});
|
|
207
222
|
|
|
208
|
-
it('routes exec command to handler', async () => {
|
|
223
|
+
it('routes exec command to handler and publishes via api.publishResponse', async () => {
|
|
209
224
|
const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
|
|
210
225
|
const mockClient = createMockClient([commandSub]);
|
|
211
226
|
MockCentrifuge.mockImplementation(() => mockClient as any);
|
|
@@ -216,12 +231,14 @@ describe('createConnection', () => {
|
|
|
216
231
|
}),
|
|
217
232
|
}));
|
|
218
233
|
|
|
234
|
+
const api = makeApi();
|
|
219
235
|
const { createConnection } = await import('../src/connection');
|
|
220
236
|
createConnection({
|
|
221
237
|
url: 'ws://localhost:8000/connection/websocket',
|
|
222
238
|
token: AGENT_TOKEN,
|
|
223
239
|
agentId: AGENT_ID,
|
|
224
240
|
backendUrl: BACKEND_URL,
|
|
241
|
+
api,
|
|
225
242
|
});
|
|
226
243
|
|
|
227
244
|
// Simulate a publication on the command channel
|
|
@@ -233,9 +250,10 @@ describe('createConnection', () => {
|
|
|
233
250
|
channel: `agent:${AGENT_ID}`,
|
|
234
251
|
});
|
|
235
252
|
|
|
236
|
-
// Should have published a response
|
|
237
|
-
expect(mockClient.publish).
|
|
238
|
-
|
|
253
|
+
// Should have published a response via api, not client.publish
|
|
254
|
+
expect(mockClient.publish).not.toHaveBeenCalled();
|
|
255
|
+
expect(api.publishResponse).toHaveBeenCalledWith(
|
|
256
|
+
AGENT_ID,
|
|
239
257
|
expect.objectContaining({ id: 'cmd-1', type: 'exec' })
|
|
240
258
|
);
|
|
241
259
|
});
|
|
@@ -247,12 +265,14 @@ describe('createConnection', () => {
|
|
|
247
265
|
|
|
248
266
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
249
267
|
|
|
268
|
+
const api = makeApi();
|
|
250
269
|
const { createConnection } = await import('../src/connection');
|
|
251
270
|
createConnection({
|
|
252
271
|
url: 'ws://localhost:8000/connection/websocket',
|
|
253
272
|
token: AGENT_TOKEN,
|
|
254
273
|
agentId: AGENT_ID,
|
|
255
274
|
backendUrl: BACKEND_URL,
|
|
275
|
+
api,
|
|
256
276
|
});
|
|
257
277
|
|
|
258
278
|
const publicationHandler = commandSub._handlers['publication'];
|
|
@@ -263,6 +283,7 @@ describe('createConnection', () => {
|
|
|
263
283
|
|
|
264
284
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown command type'));
|
|
265
285
|
expect(mockClient.publish).not.toHaveBeenCalled();
|
|
286
|
+
expect(api.publishResponse).not.toHaveBeenCalled();
|
|
266
287
|
consoleSpy.mockRestore();
|
|
267
288
|
});
|
|
268
289
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
|
|
3
3
|
import { getMetrics, startHeartbeat } from '../src/heartbeat';
|
|
4
|
+
import type { AgentApi } from '../src/api';
|
|
4
5
|
|
|
5
6
|
describe('getMetrics', () => {
|
|
6
7
|
it('returns cpu, mem, uptime', () => {
|
|
@@ -18,60 +19,52 @@ describe('getMetrics', () => {
|
|
|
18
19
|
});
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
function makeApi(publishHeartbeat = jest.fn().mockResolvedValue(undefined)): AgentApi {
|
|
23
|
+
return {
|
|
24
|
+
get: jest.fn(),
|
|
25
|
+
post: jest.fn(),
|
|
26
|
+
publishResponse: jest.fn(),
|
|
27
|
+
publishHeartbeat,
|
|
28
|
+
} as unknown as AgentApi;
|
|
29
|
+
}
|
|
23
30
|
|
|
31
|
+
describe('startHeartbeat', () => {
|
|
24
32
|
beforeEach(() => {
|
|
25
33
|
jest.useFakeTimers();
|
|
26
|
-
fetchSpy = jest.spyOn(globalThis, 'fetch').mockResolvedValue(
|
|
27
|
-
new Response('{}', { status: 200 }),
|
|
28
|
-
);
|
|
29
34
|
});
|
|
30
35
|
|
|
31
36
|
afterEach(() => {
|
|
32
37
|
jest.useRealTimers();
|
|
33
|
-
fetchSpy.mockRestore();
|
|
34
38
|
});
|
|
35
39
|
|
|
36
|
-
const opts = {
|
|
37
|
-
centrifugoInternalUrl: 'http://centrifugo-internal:9000',
|
|
38
|
-
centrifugoApiKey: 'test-key',
|
|
39
|
-
agentId: 'agent-42',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
40
|
it('publishes heartbeat immediately and at intervals', () => {
|
|
43
|
-
const
|
|
41
|
+
const publishHeartbeat = jest.fn().mockResolvedValue(undefined);
|
|
42
|
+
const api = makeApi(publishHeartbeat);
|
|
43
|
+
|
|
44
|
+
const timer = startHeartbeat({ api, agentId: 'agent-42' });
|
|
44
45
|
|
|
45
46
|
// Immediate call
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
48
|
-
'
|
|
47
|
+
expect(publishHeartbeat).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(publishHeartbeat).toHaveBeenCalledWith(
|
|
49
|
+
'agent-42',
|
|
49
50
|
expect.objectContaining({
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'Authorization': 'apikey test-key',
|
|
53
|
-
}),
|
|
51
|
+
agentId: 'agent-42',
|
|
52
|
+
type: 'heartbeat',
|
|
54
53
|
}),
|
|
55
54
|
);
|
|
56
55
|
|
|
57
|
-
// Check body contains correct channel and agent data
|
|
58
|
-
const body = JSON.parse(fetchSpy.mock.calls[0][1].body);
|
|
59
|
-
expect(body.channel).toBe('heartbeat:data');
|
|
60
|
-
expect(body.data.agentId).toBe('agent-42');
|
|
61
|
-
expect(body.data.type).toBe('heartbeat');
|
|
62
|
-
|
|
63
56
|
// After 30s
|
|
64
57
|
jest.advanceTimersByTime(30_000);
|
|
65
|
-
expect(
|
|
58
|
+
expect(publishHeartbeat).toHaveBeenCalledTimes(2);
|
|
66
59
|
|
|
67
60
|
clearInterval(timer);
|
|
68
61
|
});
|
|
69
62
|
|
|
70
63
|
it('handles publish errors gracefully', () => {
|
|
71
64
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
72
|
-
|
|
65
|
+
const api = makeApi(jest.fn().mockRejectedValue(new Error('network fail')));
|
|
73
66
|
|
|
74
|
-
const timer = startHeartbeat(
|
|
67
|
+
const timer = startHeartbeat({ api, agentId: 'agent-42' });
|
|
75
68
|
|
|
76
69
|
// Should not throw
|
|
77
70
|
clearInterval(timer);
|
package/bin/agent-controller.js
CHANGED
|
@@ -14,6 +14,12 @@ if (command === '--version' || command === '-v') {
|
|
|
14
14
|
} else if (command === 'self-update') {
|
|
15
15
|
const { selfUpdate } = await import('../dist/commands/self-update.js');
|
|
16
16
|
await selfUpdate(pkg.version);
|
|
17
|
+
} else if (command === 'install') {
|
|
18
|
+
const { install } = await import('../dist/commands/install.js');
|
|
19
|
+
await install();
|
|
20
|
+
} else if (command === 'uninstall') {
|
|
21
|
+
const { uninstall } = await import('../dist/commands/uninstall.js');
|
|
22
|
+
await uninstall();
|
|
17
23
|
} else {
|
|
18
24
|
const { main } = await import('../dist/index.js');
|
|
19
25
|
main();
|
package/dist/api.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export interface AgentApi {
|
|
2
2
|
get(path: string): Promise<unknown>;
|
|
3
3
|
post(path: string, body?: unknown): Promise<Response>;
|
|
4
|
+
publishResponse(agentId: string, data: unknown): Promise<void>;
|
|
5
|
+
publishHeartbeat(agentId: string, payload: unknown): Promise<void>;
|
|
4
6
|
}
|
|
5
7
|
export declare function createAgentApi(backendUrl: string, agentToken: string): AgentApi;
|
package/dist/api.js
CHANGED
|
@@ -37,6 +37,18 @@ export function createAgentApi(backendUrl, agentToken) {
|
|
|
37
37
|
async post(path, body) {
|
|
38
38
|
return request('POST', path, body);
|
|
39
39
|
},
|
|
40
|
+
async publishResponse(agentId, data) {
|
|
41
|
+
const res = await request('POST', '/api/internal/agent-response', { agentId, data });
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(`publishResponse failed: HTTP ${res.status}`);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
async publishHeartbeat(agentId, payload) {
|
|
47
|
+
const res = await request('POST', '/api/internal/agent-heartbeat', { agentId, payload });
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw new Error(`publishHeartbeat failed: HTTP ${res.status}`);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
40
52
|
};
|
|
41
53
|
}
|
|
42
54
|
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,OAAO,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function install(): Promise<void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
async function prompt(rl, question) {
|
|
3
|
+
return new Promise((resolve) => {
|
|
4
|
+
rl.question(question, (answer) => {
|
|
5
|
+
resolve(answer.trim());
|
|
6
|
+
});
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export async function install() {
|
|
10
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
+
console.log('OpenClaw Agent Controller — Installation Wizard');
|
|
12
|
+
console.log('================================================');
|
|
13
|
+
console.log('');
|
|
14
|
+
const centrifugoUrl = await prompt(rl, 'CENTRIFUGO_URL (e.g. ws://your-server:8000): ');
|
|
15
|
+
const agentToken = await prompt(rl, 'AGENT_TOKEN: ');
|
|
16
|
+
const agentId = await prompt(rl, 'AGENT_ID: ');
|
|
17
|
+
const backendInternalUrl = await prompt(rl, 'BACKEND_INTERNAL_URL (e.g. http://your-server:3000): ');
|
|
18
|
+
rl.close();
|
|
19
|
+
if (!centrifugoUrl || !agentToken || !agentId || !backendInternalUrl) {
|
|
20
|
+
console.error('All fields are required. Aborting.');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const config = { centrifugoUrl, agentToken, agentId, backendInternalUrl };
|
|
24
|
+
const platform = process.platform;
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(`Detected platform: ${platform}`);
|
|
27
|
+
console.log('');
|
|
28
|
+
if (platform === 'darwin') {
|
|
29
|
+
const { installMacOS } = await import('../platform/macos.js');
|
|
30
|
+
await installMacOS(config);
|
|
31
|
+
}
|
|
32
|
+
else if (platform === 'linux') {
|
|
33
|
+
const { installLinux } = await import('../platform/linux.js');
|
|
34
|
+
await installLinux(config);
|
|
35
|
+
}
|
|
36
|
+
else if (platform === 'win32') {
|
|
37
|
+
const { installWindows } = await import('../platform/windows.js');
|
|
38
|
+
await installWindows(config);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.error(`Unsupported platform: ${platform}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,KAAK,UAAU,MAAM,CAAC,EAAsC,EAAE,QAAgB;IAC5E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7E,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE,+CAA+C,CAAC,CAAC;IACxF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC/C,MAAM,kBAAkB,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE,uDAAuD,CAAC,CAAC;IAErG,EAAE,CAAC,KAAK,EAAE,CAAC;IAEX,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAE1E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC9D,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC9D,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAClE,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uninstall(): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function uninstall() {
|
|
2
|
+
const platform = process.platform;
|
|
3
|
+
console.log(`Uninstalling Agent Controller on platform: ${platform}`);
|
|
4
|
+
console.log('');
|
|
5
|
+
if (platform === 'darwin') {
|
|
6
|
+
const { uninstallMacOS } = await import('../platform/macos.js');
|
|
7
|
+
await uninstallMacOS();
|
|
8
|
+
}
|
|
9
|
+
else if (platform === 'linux') {
|
|
10
|
+
const { uninstallLinux } = await import('../platform/linux.js');
|
|
11
|
+
await uninstallLinux();
|
|
12
|
+
}
|
|
13
|
+
else if (platform === 'win32') {
|
|
14
|
+
const { uninstallWindows } = await import('../platform/windows.js');
|
|
15
|
+
await uninstallWindows();
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.error(`Unsupported platform: ${platform}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,8CAA8C,QAAQ,EAAE,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAChE,MAAM,cAAc,EAAE,CAAC;IACzB,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAChE,MAAM,cAAc,EAAE,CAAC;IACzB,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QACpE,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { AgentApi } from './api.js';
|
|
1
2
|
import { Centrifuge, Subscription } from 'centrifuge';
|
|
2
3
|
export interface ConnectionOptions {
|
|
3
4
|
url: string;
|
|
4
5
|
token: string;
|
|
5
6
|
agentId: string;
|
|
6
7
|
backendUrl: string;
|
|
8
|
+
api: AgentApi;
|
|
7
9
|
}
|
|
8
10
|
export declare function createConnection(opts: ConnectionOptions): {
|
|
9
11
|
client: Centrifuge;
|
package/dist/connection.js
CHANGED
|
@@ -92,7 +92,7 @@ export function createConnection(opts) {
|
|
|
92
92
|
}
|
|
93
93
|
// Chat commands — handle before generic dispatch
|
|
94
94
|
if (command.type === 'chat_list_sessions' || command.type === 'chat_history' || command.type === 'chat_send') {
|
|
95
|
-
const publishFn = async (data) => { await
|
|
95
|
+
const publishFn = async (data) => { await opts.api.publishResponse(opts.agentId, data); };
|
|
96
96
|
console.log(`[WS] Handling chat command: type=${command.type} id=${command.id}`);
|
|
97
97
|
switch (command.type) {
|
|
98
98
|
case 'chat_list_sessions':
|
|
@@ -115,7 +115,7 @@ export function createConnection(opts) {
|
|
|
115
115
|
console.log(`[WS] Handling command: type=${command.type} id=${command.id}`);
|
|
116
116
|
try {
|
|
117
117
|
const response = await handler(command);
|
|
118
|
-
await
|
|
118
|
+
await opts.api.publishResponse(opts.agentId, response);
|
|
119
119
|
}
|
|
120
120
|
catch (err) {
|
|
121
121
|
const errorResponse = {
|
|
@@ -124,7 +124,7 @@ export function createConnection(opts) {
|
|
|
124
124
|
success: false,
|
|
125
125
|
error: err instanceof Error ? err.message : String(err),
|
|
126
126
|
};
|
|
127
|
-
await
|
|
127
|
+
await opts.api.publishResponse(opts.agentId, errorResponse);
|
|
128
128
|
}
|
|
129
129
|
});
|
|
130
130
|
client.on('connected', (ctx) => {
|
package/dist/connection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AACtD,OAAO,SAAS,MAAM,IAAI,CAAC;AAE3B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAUpE,MAAM,QAAQ,GAAkE;IAC9E,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,aAAa;IACtB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,YAAY;IACpB,eAAe,EAAE,oBAAoB;IACrC,UAAU,EAAE,eAAe;IAC3B,WAAW,EAAE,gBAAgB;IAC7B,mBAAmB,EAAE,wBAAwB;CAC9C,CAAC;AAEF,KAAK,UAAU,oBAAoB,CAAC,UAAkB,EAAE,UAAkB,EAAE,OAAe;IACzF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,gCAAgC,EAAE;YACrE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,UAAU,EAAE;aACxC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;YACjC,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuB,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IAItD,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,IAAqB,EAAE;QAC3C,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACpF,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC3F,yEAAyE;YACzE,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAC1C,OAAO,KAAK,CAAC;YACf,CAAC;YAAC,OAAO,QAAQ,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAClG,MAAM,QAAQ,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,cAAc,GAA4B;QAC9C,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,kBAAkB;QACxB,QAAQ;QACR,iBAAiB,EAAE,IAAI;QACvB,iBAAiB,EAAE,KAAK;KACzB,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAExD,MAAM,cAAc,GAAG,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;IAE1D,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,OAAO,CAAC,GAAG,CAAC,4BAA4B,cAAc,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,QAAQ,CAAC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9F,MAAM,OAAO,GAAG,GAAG,CAAC,IAAoB,CAAC;QACzC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9E,OAAO;QACT,CAAC;QAED,iDAAiD;QACjD,IAAI,OAAO,CAAC,IAAI,KAAK,oBAAoB,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC7G,MAAM,SAAS,GAAG,KAAK,EAAE,IAAa,EAAiB,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAClH,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YACjF,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,oBAAoB;oBACvB,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAChE,MAAM;gBACR,KAAK,cAAc;oBACjB,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC3D,MAAM;gBACR,KAAK,WAAW;oBACd,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBACtE,MAAM;YACV,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,+BAA+B,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5E,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,aAAa,GAAkB;gBACnC,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACxD,CAAC;YACF,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;QAC7B,OAAO,CAAC,GAAG,CAAC,+BAA+B,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5D,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE;QAChC,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QAC9D,QAAQ,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;QAC9B,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QAC5D,QAAQ,CAAC,YAAY,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;QAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,cAAc,eAAe,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC;QACrF,QAAQ,CAAC,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE;QACnC,OAAO,CAAC,GAAG,CAAC,uBAAuB,cAAc,KAAK,GAAG,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACvF,QAAQ,CAAC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QAC7B,OAAO,CAAC,KAAK,CAAC,8BAA8B,cAAc,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE;QACpC,OAAO,CAAC,GAAG,CAAC,0BAA0B,cAAc,KAAK,GAAG,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,SAAS,EAAE,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,CAAC;IAEjB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC"}
|
package/dist/heartbeat.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { AgentApi } from './api.js';
|
|
1
2
|
import type { BoardState, HeartbeatPayload } from './types.js';
|
|
2
3
|
export interface HeartbeatOptions {
|
|
3
|
-
|
|
4
|
-
centrifugoApiKey: string;
|
|
4
|
+
api: AgentApi;
|
|
5
5
|
agentId: string;
|
|
6
6
|
getBoardStatus?: () => BoardState;
|
|
7
7
|
}
|
package/dist/heartbeat.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { debugLog } from './debug.js';
|
|
3
3
|
const HEARTBEAT_INTERVAL = 30_000;
|
|
4
|
-
const HEARTBEAT_CHANNEL = 'heartbeat:data';
|
|
5
4
|
function getMetrics() {
|
|
6
5
|
const cpus = os.cpus();
|
|
7
6
|
const cpuUsage = cpus.reduce((acc, cpu) => {
|
|
@@ -24,38 +23,14 @@ async function publishHeartbeat(opts) {
|
|
|
24
23
|
metrics: getMetrics(),
|
|
25
24
|
...(opts.getBoardStatus ? { boardStatus: opts.getBoardStatus() } : {}),
|
|
26
25
|
};
|
|
27
|
-
const controller = new AbortController();
|
|
28
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
29
26
|
try {
|
|
30
27
|
debugLog('heartbeat', '→ sending', opts.agentId, JSON.stringify(payload).slice(0, 500));
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
headers: {
|
|
34
|
-
'Content-Type': 'application/json',
|
|
35
|
-
'Authorization': `apikey ${opts.centrifugoApiKey}`,
|
|
36
|
-
},
|
|
37
|
-
body: JSON.stringify({ channel: HEARTBEAT_CHANNEL, data: payload }),
|
|
38
|
-
signal: controller.signal,
|
|
39
|
-
});
|
|
40
|
-
if (!res.ok) {
|
|
41
|
-
const body = await res.text().catch(() => '');
|
|
42
|
-
console.error(`Heartbeat publish failed (HTTP ${res.status}): ${body}`);
|
|
43
|
-
debugLog('heartbeat', '← error', res.status, body);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
debugLog('heartbeat', '← ok', res.status);
|
|
47
|
-
}
|
|
28
|
+
await opts.api.publishHeartbeat(opts.agentId, payload);
|
|
29
|
+
debugLog('heartbeat', '← ok');
|
|
48
30
|
}
|
|
49
31
|
catch (err) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.error('Heartbeat publish failed:', err instanceof Error ? err.message : err);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
clearTimeout(timeout);
|
|
32
|
+
console.error('Heartbeat publish failed:', err instanceof Error ? err.message : err);
|
|
33
|
+
debugLog('heartbeat', '← error', err);
|
|
59
34
|
}
|
|
60
35
|
}
|
|
61
36
|
export function startHeartbeat(opts) {
|
package/dist/heartbeat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAQlC,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAClE,OAAO,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IAC5C,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAEpB,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAE7B,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,GAAG;QACrC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;QAC9D,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE;KACpB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,IAAsB;IACpD,MAAM,OAAO,GAAqB;QAChC,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;QACd,OAAO,EAAE,UAAU,EAAE;QACrB,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC;IAEF,IAAI,CAAC;QACH,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACxF,MAAM,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACrF,QAAQ,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAsB;IACnD,MAAM,IAAI,GAAG,GAAG,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,IAAI,EAAE,CAAC;IACP,OAAO,WAAW,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;AAC/C,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|