@openclaw-cloud/agent-controller 0.1.5 → 0.1.7

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 CHANGED
@@ -3,6 +3,7 @@ import { startHeartbeat } from './heartbeat.js';
3
3
  import { createAgentApi } from './api.js';
4
4
  import { BoardHandler } from './handlers/board-handler.js';
5
5
  import { createChatProvider } from './openclaw/index.js';
6
+ import { DEBUG } from './debug.js';
6
7
  function requireEnv(name) {
7
8
  const value = process.env[name];
8
9
  if (!value) {
@@ -17,14 +18,14 @@ export function main() {
17
18
  const url = rawUrl.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://') + '/connection/websocket';
18
19
  const token = requireEnv('AGENT_TOKEN');
19
20
  const agentId = requireEnv('AGENT_ID');
20
- const backendUrl = process.env.BACKEND_INTERNAL_URL || '';
21
+ const backendUrl = requireEnv('BACKEND_INTERNAL_URL');
21
22
  const centrifugoInternalUrl = process.env.CENTRIFUGO_INTERNAL_URL || '';
22
23
  const centrifugoApiKey = process.env.CENTRIFUGO_API_KEY || '';
24
+ if (DEBUG)
25
+ console.log('[debug] Debug mode enabled');
23
26
  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 });
27
+ console.log(`Backend URL: ${backendUrl} (JWT mode)`);
28
+ const { client } = createConnection({ url, token, agentId, backendUrl });
28
29
  // Chat provider via OpenClaw Gateway WS
29
30
  const gatewayWsUrl = process.env.OPENCLAW_GATEWAY_WS || 'ws://localhost:18789';
30
31
  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.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/api.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { DEBUG, debugLog } from './debug.js';
2
+
1
3
  const TIMEOUT = 10_000;
2
4
 
3
5
  export interface AgentApi {
@@ -10,6 +12,7 @@ export function createAgentApi(backendUrl: string, agentToken: string): AgentApi
10
12
  const controller = new AbortController();
11
13
  const timeout = setTimeout(() => controller.abort(), TIMEOUT);
12
14
  try {
15
+ debugLog('api', '→', method, path, body ? JSON.stringify(body).slice(0, 500) : '');
13
16
  const res = await fetch(`${backendUrl}${path}`, {
14
17
  method,
15
18
  headers: {
@@ -19,6 +22,11 @@ export function createAgentApi(backendUrl: string, agentToken: string): AgentApi
19
22
  ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
20
23
  signal: controller.signal,
21
24
  });
25
+ if (DEBUG) {
26
+ const clone = res.clone();
27
+ const bodyText = await clone.text().catch(() => '');
28
+ debugLog('api', '←', method, path, res.status, bodyText.slice(0, 1000));
29
+ }
22
30
  return res;
23
31
  } finally {
24
32
  clearTimeout(timeout);
package/src/connection.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { debugLog } from './debug.js';
1
2
  import { Centrifuge, Subscription } from 'centrifuge';
2
3
  import WebSocket from 'ws';
3
4
  import type { AgentCommand, AgentResponse } from './types.js';
@@ -11,13 +12,14 @@ import { handleBackup } from './handlers/backup.js';
11
12
  import { handleChatListSessions, handleChatHistory, handleChatSend } from './handlers/chat.js';
12
13
  import { handlePackageInstall } from './handlers/package-install.js';
13
14
  import { handleFileWrite } from './handlers/file-write.js';
15
+ import { handleFileDelete } from './handlers/file-delete.js';
14
16
  import { handleOnboardingComplete } from './handlers/onboarding.js';
15
17
 
16
18
  export interface ConnectionOptions {
17
19
  url: string;
18
20
  token: string;
19
21
  agentId: string;
20
- backendUrl?: string;
22
+ backendUrl: string;
21
23
  }
22
24
 
23
25
  const handlers: Record<string, (cmd: AgentCommand) => Promise<AgentResponse>> = {
@@ -30,6 +32,7 @@ const handlers: Record<string, (cmd: AgentCommand) => Promise<AgentResponse>> =
30
32
  backup: handleBackup,
31
33
  package_install: handlePackageInstall,
32
34
  file_write: handleFileWrite,
35
+ file_delete: handleFileDelete,
33
36
  onboarding_complete: handleOnboardingComplete,
34
37
  };
35
38
 
@@ -60,40 +63,35 @@ export function createConnection(opts: ConnectionOptions): {
60
63
  client: Centrifuge;
61
64
  commandSub: Subscription;
62
65
  } {
66
+ // Dynamic JWT: fetch fresh token on every connect/reconnect
67
+ const getToken = async (): Promise<string> => {
68
+ console.log('Fetching Centrifugo JWT from backend...');
69
+ try {
70
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
71
+ console.log('Got Centrifugo JWT');
72
+ return token;
73
+ } catch (err) {
74
+ console.error('Failed to fetch Centrifugo JWT:', err instanceof Error ? err.message : err);
75
+ // Retry once before giving up — centrifuge will handle reconnect backoff
76
+ try {
77
+ const token = await fetchCentrifugoToken(opts.backendUrl, opts.token, opts.agentId);
78
+ console.log('Got Centrifugo JWT (retry)');
79
+ return token;
80
+ } catch (retryErr) {
81
+ console.error('JWT fetch retry failed:', retryErr instanceof Error ? retryErr.message : retryErr);
82
+ throw retryErr;
83
+ }
84
+ }
85
+ };
86
+
63
87
  const centrifugeOpts: Record<string, unknown> = {
64
88
  websocket: WebSocket,
65
89
  name: 'agent-controller',
90
+ getToken,
66
91
  minReconnectDelay: 1000,
67
92
  maxReconnectDelay: 30000,
68
93
  };
69
94
 
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
95
  const client = new Centrifuge(opts.url, centrifugeOpts);
98
96
 
99
97
  const commandChannel = `agent:${opts.agentId}`;
@@ -101,6 +99,7 @@ export function createConnection(opts: ConnectionOptions): {
101
99
 
102
100
  commandSub.on('publication', async (ctx) => {
103
101
  console.log(`[WS] Received message on ${commandChannel}:`, JSON.stringify(ctx.data));
102
+ debugLog('connection', 'publication', commandChannel, JSON.stringify(ctx.data).slice(0, 500));
104
103
  const command = ctx.data as AgentCommand;
105
104
  if (!command || !command.type) {
106
105
  console.warn('[WS] Ignoring message without type:', JSON.stringify(ctx.data));
@@ -148,22 +147,27 @@ export function createConnection(opts: ConnectionOptions): {
148
147
 
149
148
  client.on('connected', (ctx) => {
150
149
  console.log(`Connected to Centrifugo via ${ctx.transport}`);
150
+ debugLog('connection', 'connected', ctx);
151
151
  });
152
152
 
153
153
  client.on('disconnected', (ctx) => {
154
154
  console.log(`Disconnected: ${ctx.reason} (code ${ctx.code})`);
155
+ debugLog('connection', 'disconnected', ctx);
155
156
  });
156
157
 
157
158
  client.on('connecting', (ctx) => {
158
159
  console.log(`Connecting: ${ctx.reason} (code ${ctx.code})`);
160
+ debugLog('connection', 'connecting', ctx);
159
161
  });
160
162
 
161
163
  commandSub.on('subscribed', (ctx) => {
162
164
  console.log(`[WS] Subscribed to ${commandChannel} (recovered=${ctx.wasRecovering})`);
165
+ debugLog('connection', 'subscribed', commandChannel, ctx);
163
166
  });
164
167
 
165
168
  commandSub.on('subscribing', (ctx) => {
166
169
  console.log(`[WS] Subscribing to ${commandChannel}: ${ctx.reason} (code ${ctx.code})`);
170
+ debugLog('connection', 'subscribing', commandChannel, ctx);
167
171
  });
168
172
 
169
173
  commandSub.on('error', (ctx) => {
package/src/debug.ts ADDED
@@ -0,0 +1,11 @@
1
+ export const DEBUG = !!(process.env.AC_DEBUG || process.env.DEBUG);
2
+
3
+ export function debugLog(prefix: string, ...args: unknown[]): void {
4
+ if (!DEBUG) return;
5
+ console.log(`[debug][${prefix}]`, ...args);
6
+ }
7
+
8
+ export function maskToken(token: string): string {
9
+ if (!token) return '';
10
+ return token.slice(0, 10) + '...';
11
+ }
@@ -0,0 +1,46 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import type { AgentCommand, AgentResponse } from '../types.js';
4
+
5
+ const WORKSPACE_DIR = process.env.WORKSPACE_DIR ?? '/etc/openclaw/workspace';
6
+
7
+ export async function handleFileDelete(command: AgentCommand): Promise<AgentResponse> {
8
+ const filePath = command.payload.path as string;
9
+
10
+ if (!filePath) {
11
+ return {
12
+ id: command.id,
13
+ type: 'file_delete',
14
+ success: false,
15
+ error: 'Missing "path" in payload',
16
+ };
17
+ }
18
+
19
+ if (filePath.includes('..') || filePath.startsWith('/')) {
20
+ return {
21
+ id: command.id,
22
+ type: 'file_delete',
23
+ success: false,
24
+ error: 'Invalid path: must not contain ".." or start with "/"',
25
+ };
26
+ }
27
+
28
+ const resolvedPath = path.join(WORKSPACE_DIR, filePath);
29
+
30
+ try {
31
+ await fs.unlink(resolvedPath);
32
+ return {
33
+ id: command.id,
34
+ type: 'file_delete',
35
+ success: true,
36
+ data: { path: resolvedPath },
37
+ };
38
+ } catch (err) {
39
+ return {
40
+ id: command.id,
41
+ type: 'file_delete',
42
+ success: false,
43
+ error: err instanceof Error ? err.message : String(err),
44
+ };
45
+ }
46
+ }
package/src/heartbeat.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import os from 'node:os';
2
+ import { debugLog } from './debug.js';
2
3
  import type { BoardState, HeartbeatPayload } from './types.js';
3
4
 
4
5
  const HEARTBEAT_INTERVAL = 30_000;
@@ -41,6 +42,7 @@ async function publishHeartbeat(opts: HeartbeatOptions): Promise<void> {
41
42
  const timeout = setTimeout(() => controller.abort(), 5000);
42
43
 
43
44
  try {
45
+ debugLog('heartbeat', '→ sending', opts.agentId, JSON.stringify(payload).slice(0, 500));
44
46
  const res = await fetch(`${opts.centrifugoInternalUrl}/api/publish`, {
45
47
  method: 'POST',
46
48
  headers: {
@@ -54,6 +56,9 @@ async function publishHeartbeat(opts: HeartbeatOptions): Promise<void> {
54
56
  if (!res.ok) {
55
57
  const body = await res.text().catch(() => '');
56
58
  console.error(`Heartbeat publish failed (HTTP ${res.status}): ${body}`);
59
+ debugLog('heartbeat', '← error', res.status, body);
60
+ } else {
61
+ debugLog('heartbeat', '← ok', res.status);
57
62
  }
58
63
  } catch (err) {
59
64
  if ((err as Error).name === 'AbortError') {
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import { startHeartbeat } from './heartbeat.js';
3
3
  import { createAgentApi } from './api.js';
4
4
  import { BoardHandler } from './handlers/board-handler.js';
5
5
  import { createChatProvider } from './openclaw/index.js';
6
+ import { DEBUG } from './debug.js';
6
7
  import type { BoardEvent } from './types.js';
7
8
 
8
9
  function requireEnv(name: string): string {
@@ -20,16 +21,15 @@ export function main(): void {
20
21
  const url = rawUrl.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://') + '/connection/websocket';
21
22
  const token = requireEnv('AGENT_TOKEN');
22
23
  const agentId = requireEnv('AGENT_ID');
23
- const backendUrl = process.env.BACKEND_INTERNAL_URL || '';
24
+ const backendUrl = requireEnv('BACKEND_INTERNAL_URL');
24
25
  const centrifugoInternalUrl = process.env.CENTRIFUGO_INTERNAL_URL || '';
25
26
  const centrifugoApiKey = process.env.CENTRIFUGO_API_KEY || '';
26
27
 
28
+ if (DEBUG) console.log('[debug] Debug mode enabled');
27
29
  console.log(`Starting agent-controller for agent: ${agentId}`);
28
- if (backendUrl) {
29
- console.log(`Backend URL: ${backendUrl} (JWT mode)`);
30
- }
30
+ console.log(`Backend URL: ${backendUrl} (JWT mode)`);
31
31
 
32
- const { client } = createConnection({ url, token, agentId, backendUrl: backendUrl || undefined });
32
+ const { client } = createConnection({ url, token, agentId, backendUrl });
33
33
 
34
34
  // Chat provider via OpenClaw Gateway WS
35
35
  const gatewayWsUrl = process.env.OPENCLAW_GATEWAY_WS || 'ws://localhost:18789';
package/src/types.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  export type CommandType =
8
8
  | 'exec' | 'restart' | 'deploy' | 'config' | 'pair' | 'stop' | 'backup'
9
9
  | 'chat_list_sessions' | 'chat_history' | 'chat_send'
10
- | 'package_install' | 'file_write' | 'onboarding_complete';
10
+ | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete';
11
11
 
12
12
  export interface AgentCommand {
13
13
  id: string;