@openclaw-cloud/agent-controller 0.1.5 → 0.1.6

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.
@@ -5,6 +5,11 @@ jest.mock('ws', () => jest.fn());
5
5
 
6
6
  const MockCentrifuge = Centrifuge as jest.MockedClass<typeof Centrifuge>;
7
7
 
8
+ const BACKEND_URL = 'http://backend:3000';
9
+ const AGENT_TOKEN = 'agent-token';
10
+ const AGENT_ID = 'test-1';
11
+ const JWT = 'centrifugo.jwt.token';
12
+
8
13
  // Mock subscription instance
9
14
  function createMockSubscription(channel: string) {
10
15
  const handlers: Record<string, Function> = {};
@@ -29,37 +34,179 @@ function createMockClient(mockSubs: ReturnType<typeof createMockSubscription>[])
29
34
  };
30
35
  }
31
36
 
37
+ function mockFetchOk(token = JWT) {
38
+ global.fetch = jest.fn().mockResolvedValue({
39
+ ok: true,
40
+ json: () => Promise.resolve({ token }),
41
+ } as unknown as Response);
42
+ }
43
+
32
44
  beforeEach(() => {
33
45
  jest.clearAllMocks();
46
+ mockFetchOk();
34
47
  });
35
48
 
36
49
  describe('createConnection', () => {
37
- it('subscribes to agent: command channel only', async () => {
38
- const commandSub = createMockSubscription('agent:test-1');
50
+ it('passes getToken (not raw token) to Centrifuge when backendUrl is provided', async () => {
51
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
39
52
  const mockClient = createMockClient([commandSub]);
40
53
  MockCentrifuge.mockImplementation(() => mockClient as any);
41
54
 
42
55
  const { createConnection } = await import('../src/connection');
43
56
 
44
- const result = createConnection({
57
+ createConnection({
45
58
  url: 'ws://localhost:8000/connection/websocket',
46
- token: 'test-token',
47
- agentId: 'test-1',
59
+ token: AGENT_TOKEN,
60
+ agentId: AGENT_ID,
61
+ backendUrl: BACKEND_URL,
48
62
  });
49
63
 
50
64
  expect(MockCentrifuge).toHaveBeenCalledWith(
51
65
  'ws://localhost:8000/connection/websocket',
52
- expect.objectContaining({ token: 'test-token' })
66
+ expect.objectContaining({ getToken: expect.any(Function) })
53
67
  );
54
- expect(mockClient.newSubscription).toHaveBeenCalledWith('agent:test-1');
68
+ // Must NOT pass raw token as a field
69
+ const [[, opts]] = MockCentrifuge.mock.calls;
70
+ expect(opts).not.toHaveProperty('token');
71
+
72
+ expect(mockClient.newSubscription).toHaveBeenCalledWith(`agent:${AGENT_ID}`);
55
73
  expect(mockClient.newSubscription).toHaveBeenCalledTimes(1);
56
74
  expect(commandSub.subscribe).toHaveBeenCalled();
57
75
  expect(mockClient.connect).toHaveBeenCalled();
58
76
  expect(commandSub.on).toHaveBeenCalledWith('publication', expect.any(Function));
59
77
  });
60
78
 
79
+ it('getToken fetches JWT from backend with correct headers', async () => {
80
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
81
+ const mockClient = createMockClient([commandSub]);
82
+ let capturedGetToken: (() => Promise<string>) | undefined;
83
+ MockCentrifuge.mockImplementation((_url, opts: any) => {
84
+ capturedGetToken = opts.getToken;
85
+ return mockClient as any;
86
+ });
87
+
88
+ const { createConnection } = await import('../src/connection');
89
+ createConnection({
90
+ url: 'ws://localhost:8000/connection/websocket',
91
+ token: AGENT_TOKEN,
92
+ agentId: AGENT_ID,
93
+ backendUrl: BACKEND_URL,
94
+ });
95
+
96
+ expect(capturedGetToken).toBeDefined();
97
+ const result = await capturedGetToken!();
98
+
99
+ expect(result).toBe(JWT);
100
+ expect(global.fetch).toHaveBeenCalledWith(
101
+ `${BACKEND_URL}/api/internal/centrifugo-token`,
102
+ expect.objectContaining({
103
+ method: 'POST',
104
+ headers: expect.objectContaining({
105
+ 'Authorization': `Bearer ${AGENT_TOKEN}`,
106
+ 'Content-Type': 'application/json',
107
+ }),
108
+ body: JSON.stringify({ agentId: AGENT_ID }),
109
+ })
110
+ );
111
+ });
112
+
113
+ it('getToken retries once and succeeds on second attempt', async () => {
114
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
115
+ const mockClient = createMockClient([commandSub]);
116
+ let capturedGetToken: (() => Promise<string>) | undefined;
117
+ MockCentrifuge.mockImplementation((_url, opts: any) => {
118
+ capturedGetToken = opts.getToken;
119
+ return mockClient as any;
120
+ });
121
+
122
+ // First call fails, second succeeds
123
+ global.fetch = jest.fn()
124
+ .mockRejectedValueOnce(new Error('network error'))
125
+ .mockResolvedValueOnce({
126
+ ok: true,
127
+ json: () => Promise.resolve({ token: JWT }),
128
+ } as unknown as Response);
129
+
130
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
131
+
132
+ const { createConnection } = await import('../src/connection');
133
+ createConnection({
134
+ url: 'ws://localhost:8000/connection/websocket',
135
+ token: AGENT_TOKEN,
136
+ agentId: AGENT_ID,
137
+ backendUrl: BACKEND_URL,
138
+ });
139
+
140
+ const result = await capturedGetToken!();
141
+ expect(result).toBe(JWT);
142
+ expect(global.fetch).toHaveBeenCalledTimes(2);
143
+ expect(consoleSpy).toHaveBeenCalledWith(
144
+ expect.stringContaining('Failed to fetch Centrifugo JWT'),
145
+ expect.anything()
146
+ );
147
+ consoleSpy.mockRestore();
148
+ });
149
+
150
+ it('getToken throws after both attempts fail', async () => {
151
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
152
+ const mockClient = createMockClient([commandSub]);
153
+ let capturedGetToken: (() => Promise<string>) | undefined;
154
+ MockCentrifuge.mockImplementation((_url, opts: any) => {
155
+ capturedGetToken = opts.getToken;
156
+ return mockClient as any;
157
+ });
158
+
159
+ const fetchError = new Error('network down');
160
+ global.fetch = jest.fn().mockRejectedValue(fetchError);
161
+
162
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
163
+
164
+ const { createConnection } = await import('../src/connection');
165
+ createConnection({
166
+ url: 'ws://localhost:8000/connection/websocket',
167
+ token: AGENT_TOKEN,
168
+ agentId: AGENT_ID,
169
+ backendUrl: BACKEND_URL,
170
+ });
171
+
172
+ await expect(capturedGetToken!()).rejects.toThrow('network down');
173
+ expect(global.fetch).toHaveBeenCalledTimes(2);
174
+ consoleSpy.mockRestore();
175
+ });
176
+
177
+ it('getToken throws when backend returns non-ok HTTP status', async () => {
178
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
179
+ const mockClient = createMockClient([commandSub]);
180
+ let capturedGetToken: (() => Promise<string>) | undefined;
181
+ MockCentrifuge.mockImplementation((_url, opts: any) => {
182
+ capturedGetToken = opts.getToken;
183
+ return mockClient as any;
184
+ });
185
+
186
+ global.fetch = jest.fn().mockResolvedValue({
187
+ ok: false,
188
+ status: 401,
189
+ text: () => Promise.resolve('Unauthorized'),
190
+ } as unknown as Response);
191
+
192
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
193
+
194
+ const { createConnection } = await import('../src/connection');
195
+ createConnection({
196
+ url: 'ws://localhost:8000/connection/websocket',
197
+ token: AGENT_TOKEN,
198
+ agentId: AGENT_ID,
199
+ backendUrl: BACKEND_URL,
200
+ });
201
+
202
+ await expect(capturedGetToken!()).rejects.toThrow('HTTP 401');
203
+ // Both attempts return 401 → called twice
204
+ expect(global.fetch).toHaveBeenCalledTimes(2);
205
+ consoleSpy.mockRestore();
206
+ });
207
+
61
208
  it('routes exec command to handler', async () => {
62
- const commandSub = createMockSubscription('agent:test-1');
209
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
63
210
  const mockClient = createMockClient([commandSub]);
64
211
  MockCentrifuge.mockImplementation(() => mockClient as any);
65
212
 
@@ -70,7 +217,12 @@ describe('createConnection', () => {
70
217
  }));
71
218
 
72
219
  const { createConnection } = await import('../src/connection');
73
- createConnection({ url: 'ws://localhost:8000/connection/websocket', token: 'tok', agentId: 'test-1' });
220
+ createConnection({
221
+ url: 'ws://localhost:8000/connection/websocket',
222
+ token: AGENT_TOKEN,
223
+ agentId: AGENT_ID,
224
+ backendUrl: BACKEND_URL,
225
+ });
74
226
 
75
227
  // Simulate a publication on the command channel
76
228
  const publicationHandler = commandSub._handlers['publication'];
@@ -78,30 +230,35 @@ describe('createConnection', () => {
78
230
 
79
231
  await publicationHandler({
80
232
  data: { id: 'cmd-1', type: 'exec', payload: { command: 'echo hi' } },
81
- channel: 'agent:test-1',
233
+ channel: `agent:${AGENT_ID}`,
82
234
  });
83
235
 
84
236
  // Should have published a response to the agent channel
85
237
  expect(mockClient.publish).toHaveBeenCalledWith(
86
- 'agent:test-1',
238
+ `agent:${AGENT_ID}`,
87
239
  expect.objectContaining({ id: 'cmd-1', type: 'exec' })
88
240
  );
89
241
  });
90
242
 
91
243
  it('handles unknown command types gracefully', async () => {
92
- const commandSub = createMockSubscription('agent:test-1');
244
+ const commandSub = createMockSubscription(`agent:${AGENT_ID}`);
93
245
  const mockClient = createMockClient([commandSub]);
94
246
  MockCentrifuge.mockImplementation(() => mockClient as any);
95
247
 
96
248
  const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
97
249
 
98
250
  const { createConnection } = await import('../src/connection');
99
- createConnection({ url: 'ws://localhost:8000/connection/websocket', token: 'tok', agentId: 'test-1' });
251
+ createConnection({
252
+ url: 'ws://localhost:8000/connection/websocket',
253
+ token: AGENT_TOKEN,
254
+ agentId: AGENT_ID,
255
+ backendUrl: BACKEND_URL,
256
+ });
100
257
 
101
258
  const publicationHandler = commandSub._handlers['publication'];
102
259
  await publicationHandler({
103
260
  data: { id: 'cmd-2', type: 'unknown_cmd', payload: {} },
104
- channel: 'agent:test-1',
261
+ channel: `agent:${AGENT_ID}`,
105
262
  });
106
263
 
107
264
  expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown command type'));
@@ -0,0 +1,90 @@
1
+ import { handleFileDelete } from '../src/handlers/file-delete';
2
+ import type { AgentCommand } from '../src/types';
3
+ import fs from 'node:fs/promises';
4
+
5
+ jest.mock('node:fs/promises');
6
+
7
+ const mockUnlink = fs.unlink as jest.Mock;
8
+
9
+ const baseCmd: AgentCommand = { id: '1', type: 'file_delete', payload: {} };
10
+
11
+ describe('handleFileDelete', () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ mockUnlink.mockResolvedValue(undefined);
15
+ });
16
+
17
+ it('deletes file and returns success', async () => {
18
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'config/agent.yml' } };
19
+ const res = await handleFileDelete(cmd);
20
+ expect(res.success).toBe(true);
21
+ expect(mockUnlink).toHaveBeenCalledWith(expect.stringContaining('agent.yml'));
22
+ });
23
+
24
+ it('returns resolved path in data on success', async () => {
25
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'some/file.txt' } };
26
+ const res = await handleFileDelete(cmd);
27
+ expect(res.success).toBe(true);
28
+ expect(res.data?.path).toEqual(expect.stringContaining('file.txt'));
29
+ });
30
+
31
+ it('rejects path traversal with ".."', async () => {
32
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: '../etc/passwd' } };
33
+ const res = await handleFileDelete(cmd);
34
+ expect(res.success).toBe(false);
35
+ expect(res.error).toContain('Invalid path');
36
+ expect(mockUnlink).not.toHaveBeenCalled();
37
+ });
38
+
39
+ it('rejects nested path traversal (foo/../bar)', async () => {
40
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'foo/../bar/secret' } };
41
+ const res = await handleFileDelete(cmd);
42
+ expect(res.success).toBe(false);
43
+ expect(res.error).toContain('Invalid path');
44
+ expect(mockUnlink).not.toHaveBeenCalled();
45
+ });
46
+
47
+ it('rejects absolute path starting with "/"', async () => {
48
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: '/etc/passwd' } };
49
+ const res = await handleFileDelete(cmd);
50
+ expect(res.success).toBe(false);
51
+ expect(res.error).toContain('Invalid path');
52
+ expect(mockUnlink).not.toHaveBeenCalled();
53
+ });
54
+
55
+ it('returns error when path is missing', async () => {
56
+ const cmd: AgentCommand = { ...baseCmd, payload: {} };
57
+ const res = await handleFileDelete(cmd);
58
+ expect(res.success).toBe(false);
59
+ expect(res.error).toContain('Missing');
60
+ expect(mockUnlink).not.toHaveBeenCalled();
61
+ });
62
+
63
+ it('returns error when unlink fails (file not found)', async () => {
64
+ mockUnlink.mockRejectedValue(new Error('ENOENT: no such file or directory'));
65
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'missing.txt' } };
66
+ const res = await handleFileDelete(cmd);
67
+ expect(res.success).toBe(false);
68
+ expect(res.error).toContain('ENOENT');
69
+ });
70
+
71
+ it('returns error when unlink fails (permission denied)', async () => {
72
+ mockUnlink.mockRejectedValue(new Error('EACCES: permission denied'));
73
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'protected.txt' } };
74
+ const res = await handleFileDelete(cmd);
75
+ expect(res.success).toBe(false);
76
+ expect(res.error).toContain('EACCES');
77
+ });
78
+
79
+ it('response type is file_delete', async () => {
80
+ const cmd: AgentCommand = { ...baseCmd, payload: { path: 'file.txt' } };
81
+ const res = await handleFileDelete(cmd);
82
+ expect(res.type).toBe('file_delete');
83
+ });
84
+
85
+ it('response id matches command id', async () => {
86
+ const cmd: AgentCommand = { ...baseCmd, id: 'unique-cmd-id', payload: { path: 'file.txt' } };
87
+ const res = await handleFileDelete(cmd);
88
+ expect(res.id).toBe('unique-cmd-id');
89
+ });
90
+ });
@@ -3,7 +3,7 @@ export interface ConnectionOptions {
3
3
  url: string;
4
4
  token: string;
5
5
  agentId: string;
6
- backendUrl?: string;
6
+ backendUrl: string;
7
7
  }
8
8
  export declare function createConnection(opts: ConnectionOptions): {
9
9
  client: Centrifuge;
@@ -10,6 +10,7 @@ import { handleBackup } from './handlers/backup.js';
10
10
  import { handleChatListSessions, handleChatHistory, handleChatSend } from './handlers/chat.js';
11
11
  import { handlePackageInstall } from './handlers/package-install.js';
12
12
  import { handleFileWrite } from './handlers/file-write.js';
13
+ import { handleFileDelete } from './handlers/file-delete.js';
13
14
  import { handleOnboardingComplete } from './handlers/onboarding.js';
14
15
  const handlers = {
15
16
  exec: handleExec,
@@ -21,6 +22,7 @@ const handlers = {
21
22
  backup: handleBackup,
22
23
  package_install: handlePackageInstall,
23
24
  file_write: handleFileWrite,
25
+ file_delete: handleFileDelete,
24
26
  onboarding_complete: handleOnboardingComplete,
25
27
  };
26
28
  async function fetchCentrifugoToken(backendUrl, agentToken, agentId) {
@@ -47,41 +49,35 @@ async function fetchCentrifugoToken(backendUrl, agentToken, agentId) {
47
49
  }
48
50
  }
49
51
  export function createConnection(opts) {
52
+ // Dynamic JWT: fetch fresh token on every connect/reconnect
53
+ const getToken = async () => {
54
+ console.log('Fetching Centrifugo JWT from backend...');
55
+ try {
56
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
57
+ console.log('Got Centrifugo JWT');
58
+ return token;
59
+ }
60
+ catch (err) {
61
+ console.error('Failed to fetch Centrifugo JWT:', err instanceof Error ? err.message : err);
62
+ // Retry once before giving up — centrifuge will handle reconnect backoff
63
+ try {
64
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
65
+ console.log('Got Centrifugo JWT (retry)');
66
+ return token;
67
+ }
68
+ catch (retryErr) {
69
+ console.error('JWT fetch retry failed:', retryErr instanceof Error ? retryErr.message : retryErr);
70
+ throw retryErr;
71
+ }
72
+ }
73
+ };
50
74
  const centrifugeOpts = {
51
75
  websocket: WebSocket,
52
76
  name: 'agent-controller',
77
+ getToken,
53
78
  minReconnectDelay: 1000,
54
79
  maxReconnectDelay: 30000,
55
80
  };
56
- if (opts.backendUrl) {
57
- // Dynamic JWT: fetch fresh token on every connect/reconnect
58
- centrifugeOpts.getToken = async () => {
59
- console.log('Fetching Centrifugo JWT from backend...');
60
- try {
61
- const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
62
- console.log('Got Centrifugo JWT');
63
- return token;
64
- }
65
- catch (err) {
66
- console.error('Failed to fetch Centrifugo JWT:', err instanceof Error ? err.message : err);
67
- // Retry once
68
- try {
69
- const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
70
- console.log('Got Centrifugo JWT (retry)');
71
- return token;
72
- }
73
- catch (retryErr) {
74
- console.error('JWT fetch retry failed:', retryErr instanceof Error ? retryErr.message : retryErr);
75
- throw retryErr;
76
- }
77
- }
78
- };
79
- }
80
- else {
81
- // Fallback: use raw token (backward compat, won't work with Centrifugo JWT validation)
82
- console.warn('BACKEND_INTERNAL_URL not set, using AGENT_TOKEN directly (may not work)');
83
- centrifugeOpts.token = opts.token;
84
- }
85
81
  const client = new Centrifuge(opts.url, centrifugeOpts);
86
82
  const commandChannel = `agent:${opts.agentId}`;
87
83
  const commandSub = client.newSubscription(commandChannel);
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleFileDelete(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const WORKSPACE_DIR = process.env.WORKSPACE_DIR ?? '/etc/openclaw/workspace';
4
+ export async function handleFileDelete(command) {
5
+ const filePath = command.payload.path;
6
+ if (!filePath) {
7
+ return {
8
+ id: command.id,
9
+ type: 'file_delete',
10
+ success: false,
11
+ error: 'Missing "path" in payload',
12
+ };
13
+ }
14
+ if (filePath.includes('..') || filePath.startsWith('/')) {
15
+ return {
16
+ id: command.id,
17
+ type: 'file_delete',
18
+ success: false,
19
+ error: 'Invalid path: must not contain ".." or start with "/"',
20
+ };
21
+ }
22
+ const resolvedPath = path.join(WORKSPACE_DIR, filePath);
23
+ try {
24
+ await fs.unlink(resolvedPath);
25
+ return {
26
+ id: command.id,
27
+ type: 'file_delete',
28
+ success: true,
29
+ data: { path: resolvedPath },
30
+ };
31
+ }
32
+ catch (err) {
33
+ return {
34
+ id: command.id,
35
+ type: 'file_delete',
36
+ success: false,
37
+ error: err instanceof Error ? err.message : String(err),
38
+ };
39
+ }
40
+ }
41
+ //# sourceMappingURL=file-delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-delete.js","sourceRoot":"","sources":["../../src/handlers/file-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CAAC;AAE7E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAqB;IAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAc,CAAC;IAEhD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,2BAA2B;SACnC,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,uDAAuD;SAC/D,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;SAC7B,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/index.js CHANGED
@@ -17,14 +17,12 @@ export function main() {
17
17
  const url = rawUrl.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://') + '/connection/websocket';
18
18
  const token = requireEnv('AGENT_TOKEN');
19
19
  const agentId = requireEnv('AGENT_ID');
20
- const backendUrl = process.env.BACKEND_INTERNAL_URL || '';
20
+ const backendUrl = requireEnv('BACKEND_INTERNAL_URL');
21
21
  const centrifugoInternalUrl = process.env.CENTRIFUGO_INTERNAL_URL || '';
22
22
  const centrifugoApiKey = process.env.CENTRIFUGO_API_KEY || '';
23
23
  console.log(`Starting agent-controller for agent: ${agentId}`);
24
- if (backendUrl) {
25
- console.log(`Backend URL: ${backendUrl} (JWT mode)`);
26
- }
27
- const { client } = createConnection({ url, token, agentId, backendUrl: backendUrl || undefined });
24
+ console.log(`Backend URL: ${backendUrl} (JWT mode)`);
25
+ const { client } = createConnection({ url, token, agentId, backendUrl });
28
26
  // Chat provider via OpenClaw Gateway WS
29
27
  const gatewayWsUrl = process.env.OPENCLAW_GATEWAY_WS || 'ws://localhost:18789';
30
28
  const gatewayToken = process.env.OPENCLAW_GATEWAY_TOKEN || '';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * agent:<agentId> — commands from backend, responses from agent
4
4
  * heartbeat:<agentId> — heartbeat publications from agent
5
5
  */
6
- export type CommandType = 'exec' | 'restart' | 'deploy' | 'config' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'package_install' | 'file_write' | 'onboarding_complete';
6
+ export type CommandType = 'exec' | 'restart' | 'deploy' | 'config' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete';
7
7
  export interface AgentCommand {
8
8
  id: string;
9
9
  type: CommandType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw-cloud/agent-controller",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/connection.ts CHANGED
@@ -11,13 +11,14 @@ import { handleBackup } from './handlers/backup.js';
11
11
  import { handleChatListSessions, handleChatHistory, handleChatSend } from './handlers/chat.js';
12
12
  import { handlePackageInstall } from './handlers/package-install.js';
13
13
  import { handleFileWrite } from './handlers/file-write.js';
14
+ import { handleFileDelete } from './handlers/file-delete.js';
14
15
  import { handleOnboardingComplete } from './handlers/onboarding.js';
15
16
 
16
17
  export interface ConnectionOptions {
17
18
  url: string;
18
19
  token: string;
19
20
  agentId: string;
20
- backendUrl?: string;
21
+ backendUrl: string;
21
22
  }
22
23
 
23
24
  const handlers: Record<string, (cmd: AgentCommand) => Promise<AgentResponse>> = {
@@ -30,6 +31,7 @@ const handlers: Record<string, (cmd: AgentCommand) => Promise<AgentResponse>> =
30
31
  backup: handleBackup,
31
32
  package_install: handlePackageInstall,
32
33
  file_write: handleFileWrite,
34
+ file_delete: handleFileDelete,
33
35
  onboarding_complete: handleOnboardingComplete,
34
36
  };
35
37
 
@@ -60,40 +62,35 @@ export function createConnection(opts: ConnectionOptions): {
60
62
  client: Centrifuge;
61
63
  commandSub: Subscription;
62
64
  } {
65
+ // Dynamic JWT: fetch fresh token on every connect/reconnect
66
+ const getToken = async (): Promise<string> => {
67
+ console.log('Fetching Centrifugo JWT from backend...');
68
+ try {
69
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
70
+ console.log('Got Centrifugo JWT');
71
+ return token;
72
+ } catch (err) {
73
+ console.error('Failed to fetch Centrifugo JWT:', err instanceof Error ? err.message : err);
74
+ // Retry once before giving up — centrifuge will handle reconnect backoff
75
+ try {
76
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
77
+ console.log('Got Centrifugo JWT (retry)');
78
+ return token;
79
+ } catch (retryErr) {
80
+ console.error('JWT fetch retry failed:', retryErr instanceof Error ? retryErr.message : retryErr);
81
+ throw retryErr;
82
+ }
83
+ }
84
+ };
85
+
63
86
  const centrifugeOpts: Record<string, unknown> = {
64
87
  websocket: WebSocket,
65
88
  name: 'agent-controller',
89
+ getToken,
66
90
  minReconnectDelay: 1000,
67
91
  maxReconnectDelay: 30000,
68
92
  };
69
93
 
70
- if (opts.backendUrl) {
71
- // Dynamic JWT: fetch fresh token on every connect/reconnect
72
- centrifugeOpts.getToken = async () => {
73
- console.log('Fetching Centrifugo JWT from backend...');
74
- try {
75
- const token = await fetchCentrifugoToken(opts.backendUrl!, opts.token, opts.agentId);
76
- console.log('Got Centrifugo JWT');
77
- return token;
78
- } catch (err) {
79
- console.error('Failed to fetch Centrifugo JWT:', err instanceof Error ? err.message : err);
80
- // Retry once
81
- try {
82
- const token = await fetchCentrifugoToken(opts.backendUrl!, opts.token, opts.agentId);
83
- console.log('Got Centrifugo JWT (retry)');
84
- return token;
85
- } catch (retryErr) {
86
- console.error('JWT fetch retry failed:', retryErr instanceof Error ? retryErr.message : retryErr);
87
- throw retryErr;
88
- }
89
- }
90
- };
91
- } else {
92
- // Fallback: use raw token (backward compat, won't work with Centrifugo JWT validation)
93
- console.warn('BACKEND_INTERNAL_URL not set, using AGENT_TOKEN directly (may not work)');
94
- centrifugeOpts.token = opts.token;
95
- }
96
-
97
94
  const client = new Centrifuge(opts.url, centrifugeOpts);
98
95
 
99
96
  const commandChannel = `agent:${opts.agentId}`;