@openclaw-cloud/agent-controller 0.2.0 → 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 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 → publish back
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 → Centrifugo HTTP API (port 9000) Backend (heartbeat processing)
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 Centrifugo HTTP API (server-side publish)
141
- - **Command handlers** — Processes commands from backend (backup, reconfigure, etc.)
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
- - `CENTRIFUGO_INTERNAL_URL` — HTTP API URL (port 9000) for server-side publish
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
 
@@ -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();
@@ -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/index.js CHANGED
@@ -19,9 +19,6 @@ export function main() {
19
19
  const token = requireEnv('AGENT_TOKEN');
20
20
  const agentId = requireEnv('AGENT_ID');
21
21
  const backendUrl = requireEnv('BACKEND_INTERNAL_URL');
22
- // Read for backward compatibility but no longer used directly
23
- void process.env.CENTRIFUGO_INTERNAL_URL;
24
- void process.env.CENTRIFUGO_API_KEY;
25
22
  if (DEBUG)
26
23
  console.log('[debug] Debug mode enabled');
27
24
  console.log(`Starting agent-controller for agent: ${agentId}`);
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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"}
@@ -0,0 +1,8 @@
1
+ export interface PlatformConfig {
2
+ centrifugoUrl: string;
3
+ agentToken: string;
4
+ agentId: string;
5
+ backendInternalUrl: string;
6
+ }
7
+ export declare function installLinux(config: PlatformConfig): Promise<void>;
8
+ export declare function uninstallLinux(): Promise<void>;
@@ -0,0 +1,96 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ function execAsync(cmd) {
6
+ return new Promise((resolve, reject) => {
7
+ exec(cmd, (err, stdout) => {
8
+ if (err)
9
+ reject(err);
10
+ else
11
+ resolve(stdout.toString().trim());
12
+ });
13
+ });
14
+ }
15
+ async function which(bin) {
16
+ try {
17
+ return await execAsync(`which "${bin}"`);
18
+ }
19
+ catch {
20
+ return '';
21
+ }
22
+ }
23
+ export async function installLinux(config) {
24
+ const home = homedir();
25
+ const systemdDir = join(home, '.config', 'systemd', 'user');
26
+ const servicePath = join(systemdDir, 'agent-controller.service');
27
+ const nodePath = process.execPath;
28
+ let agentControllerPath = await which('agent-controller');
29
+ if (!agentControllerPath) {
30
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
31
+ }
32
+ mkdirSync(systemdDir, { recursive: true });
33
+ const service = `[Unit]
34
+ Description=OpenClaw Agent Controller
35
+ After=network.target
36
+
37
+ [Service]
38
+ Type=simple
39
+ Restart=always
40
+ RestartSec=10
41
+ Environment=CENTRIFUGO_URL=${config.centrifugoUrl}
42
+ Environment=AGENT_TOKEN=${config.agentToken}
43
+ Environment=AGENT_ID=${config.agentId}
44
+ Environment=BACKEND_INTERNAL_URL=${config.backendInternalUrl}
45
+ ExecStart="${nodePath}" "${agentControllerPath}"
46
+
47
+ [Install]
48
+ WantedBy=default.target
49
+ `;
50
+ writeFileSync(servicePath, service, 'utf8');
51
+ console.log(`Written: ${servicePath}`);
52
+ try {
53
+ await execAsync('systemctl --user daemon-reload');
54
+ console.log('systemd daemon reloaded.');
55
+ await execAsync('systemctl --user enable --now agent-controller');
56
+ console.log('Service enabled and started.');
57
+ }
58
+ catch (err) {
59
+ console.warn('systemctl command failed:', err instanceof Error ? err.message : err);
60
+ }
61
+ console.log('');
62
+ console.log('Agent Controller installed as a systemd user service.');
63
+ console.log('');
64
+ console.log('Useful commands:');
65
+ console.log(' Start: systemctl --user start agent-controller');
66
+ console.log(' Stop: systemctl --user stop agent-controller');
67
+ console.log(' Status: systemctl --user status agent-controller');
68
+ console.log(' Logs: journalctl --user -u agent-controller -f');
69
+ console.log(' Uninstall: agent-controller uninstall');
70
+ }
71
+ export async function uninstallLinux() {
72
+ try {
73
+ await execAsync('systemctl --user disable --now agent-controller');
74
+ console.log('Service disabled and stopped.');
75
+ }
76
+ catch (err) {
77
+ console.warn('systemctl disable failed (may not be enabled):', err instanceof Error ? err.message : err);
78
+ }
79
+ const servicePath = join(homedir(), '.config', 'systemd', 'user', 'agent-controller.service');
80
+ const { unlinkSync, existsSync } = await import('node:fs');
81
+ if (existsSync(servicePath)) {
82
+ unlinkSync(servicePath);
83
+ console.log(`Removed: ${servicePath}`);
84
+ }
85
+ else {
86
+ console.log(`Service file not found: ${servicePath}`);
87
+ }
88
+ try {
89
+ await execAsync('systemctl --user daemon-reload');
90
+ }
91
+ catch {
92
+ // best-effort
93
+ }
94
+ console.log('Agent Controller uninstalled.');
95
+ }
96
+ //# sourceMappingURL=linux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux.js","sourceRoot":"","sources":["../../src/platform/linux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface PlatformConfig {
2
+ centrifugoUrl: string;
3
+ agentToken: string;
4
+ agentId: string;
5
+ backendInternalUrl: string;
6
+ }
7
+ export declare function installMacOS(config: PlatformConfig): Promise<void>;
8
+ export declare function uninstallMacOS(): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ function execAsync(cmd) {
6
+ return new Promise((resolve, reject) => {
7
+ exec(cmd, (err, stdout) => {
8
+ if (err)
9
+ reject(err);
10
+ else
11
+ resolve(stdout.toString().trim());
12
+ });
13
+ });
14
+ }
15
+ async function which(bin) {
16
+ try {
17
+ return await execAsync(`which "${bin}"`);
18
+ }
19
+ catch {
20
+ return '';
21
+ }
22
+ }
23
+ export async function installMacOS(config) {
24
+ const home = homedir();
25
+ const launchAgentsDir = join(home, 'Library', 'LaunchAgents');
26
+ const plistPath = join(launchAgentsDir, 'com.openclaw.agent-controller.plist');
27
+ const logDir = join(home, 'Library', 'Logs');
28
+ const logFile = join(logDir, 'agent-controller.log');
29
+ const errFile = join(logDir, 'agent-controller.err');
30
+ const nodePath = process.execPath;
31
+ let agentControllerPath = await which('agent-controller');
32
+ if (!agentControllerPath) {
33
+ // Fallback: derive from node path
34
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
35
+ }
36
+ mkdirSync(launchAgentsDir, { recursive: true });
37
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
38
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
39
+ <plist version="1.0">
40
+ <dict>
41
+ <key>Label</key>
42
+ <string>com.openclaw.agent-controller</string>
43
+ <key>ProgramArguments</key>
44
+ <array>
45
+ <string>${nodePath}</string>
46
+ <string>${agentControllerPath}</string>
47
+ </array>
48
+ <key>EnvironmentVariables</key>
49
+ <dict>
50
+ <key>CENTRIFUGO_URL</key>
51
+ <string>${config.centrifugoUrl}</string>
52
+ <key>AGENT_TOKEN</key>
53
+ <string>${config.agentToken}</string>
54
+ <key>AGENT_ID</key>
55
+ <string>${config.agentId}</string>
56
+ <key>BACKEND_INTERNAL_URL</key>
57
+ <string>${config.backendInternalUrl}</string>
58
+ </dict>
59
+ <key>RunAtLoad</key>
60
+ <true/>
61
+ <key>KeepAlive</key>
62
+ <true/>
63
+ <key>StandardOutPath</key>
64
+ <string>${logFile}</string>
65
+ <key>StandardErrorPath</key>
66
+ <string>${errFile}</string>
67
+ </dict>
68
+ </plist>
69
+ `;
70
+ writeFileSync(plistPath, plist, 'utf8');
71
+ console.log(`Written: ${plistPath}`);
72
+ try {
73
+ await execAsync(`launchctl load -w "${plistPath}"`);
74
+ console.log('Service loaded via launchctl.');
75
+ }
76
+ catch (err) {
77
+ console.warn('launchctl load failed (may already be loaded):', err instanceof Error ? err.message : err);
78
+ }
79
+ console.log('');
80
+ console.log('Agent Controller installed as a macOS LaunchAgent.');
81
+ console.log('');
82
+ console.log('Useful commands:');
83
+ console.log(` Start: launchctl load -w "${plistPath}"`);
84
+ console.log(` Stop: launchctl unload "${plistPath}"`);
85
+ console.log(` Logs: tail -f "${logFile}"`);
86
+ console.log(` Errors: tail -f "${errFile}"`);
87
+ console.log(` Uninstall: agent-controller uninstall`);
88
+ }
89
+ export async function uninstallMacOS() {
90
+ const home = homedir();
91
+ const plistPath = join(home, 'Library', 'LaunchAgents', 'com.openclaw.agent-controller.plist');
92
+ try {
93
+ await execAsync(`launchctl unload "${plistPath}"`);
94
+ console.log('Service unloaded via launchctl.');
95
+ }
96
+ catch (err) {
97
+ console.warn('launchctl unload failed (may not be loaded):', err instanceof Error ? err.message : err);
98
+ }
99
+ const { unlinkSync, existsSync } = await import('node:fs');
100
+ if (existsSync(plistPath)) {
101
+ unlinkSync(plistPath);
102
+ console.log(`Removed: ${plistPath}`);
103
+ }
104
+ else {
105
+ console.log(`Plist not found: ${plistPath}`);
106
+ }
107
+ console.log('Agent Controller uninstalled.');
108
+ }
109
+ //# sourceMappingURL=macos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos.js","sourceRoot":"","sources":["../../src/platform/macos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface PlatformConfig {
2
+ centrifugoUrl: string;
3
+ agentToken: string;
4
+ agentId: string;
5
+ backendInternalUrl: string;
6
+ }
7
+ export declare function installWindows(config: PlatformConfig): Promise<void>;
8
+ export declare function uninstallWindows(): Promise<void>;
@@ -0,0 +1,77 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ function execAsync(cmd) {
5
+ return new Promise((resolve, reject) => {
6
+ exec(cmd, (err, stdout) => {
7
+ if (err)
8
+ reject(err);
9
+ else
10
+ resolve(stdout.toString().trim());
11
+ });
12
+ });
13
+ }
14
+ async function which(bin) {
15
+ try {
16
+ return await execAsync(`where "${bin}"`);
17
+ }
18
+ catch {
19
+ return '';
20
+ }
21
+ }
22
+ export async function installWindows(config) {
23
+ const appData = process.env['APPDATA'] ?? join(process.env['USERPROFILE'] ?? 'C:\\Users\\Default', 'AppData', 'Roaming');
24
+ const openclawDir = join(appData, 'openclaw');
25
+ const wrapperPath = join(openclawDir, 'agent-controller.cmd');
26
+ const nodePath = process.execPath;
27
+ let agentControllerPath = await which('agent-controller');
28
+ if (!agentControllerPath) {
29
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
30
+ }
31
+ mkdirSync(openclawDir, { recursive: true });
32
+ const wrapper = `@echo off
33
+ set CENTRIFUGO_URL=${config.centrifugoUrl}
34
+ set AGENT_TOKEN=${config.agentToken}
35
+ set AGENT_ID=${config.agentId}
36
+ set BACKEND_INTERNAL_URL=${config.backendInternalUrl}
37
+ "${nodePath}" "${agentControllerPath}"
38
+ `;
39
+ writeFileSync(wrapperPath, wrapper, 'utf8');
40
+ console.log(`Written: ${wrapperPath}`);
41
+ try {
42
+ await execAsync(`schtasks /create /tn "OpenClaw Agent Controller" /tr "${wrapperPath}" /sc ONLOGON /rl HIGHEST /f`);
43
+ console.log('Scheduled task created.');
44
+ }
45
+ catch (err) {
46
+ console.warn('schtasks create failed:', err instanceof Error ? err.message : err);
47
+ }
48
+ console.log('');
49
+ console.log('Agent Controller installed as a Windows scheduled task (ONLOGON).');
50
+ console.log('');
51
+ console.log('Useful commands:');
52
+ console.log(' Start: schtasks /run /tn "OpenClaw Agent Controller"');
53
+ console.log(' Stop: schtasks /end /tn "OpenClaw Agent Controller"');
54
+ console.log(' Uninstall: agent-controller uninstall');
55
+ console.log(` Wrapper: ${wrapperPath}`);
56
+ }
57
+ export async function uninstallWindows() {
58
+ try {
59
+ await execAsync('schtasks /delete /tn "OpenClaw Agent Controller" /f');
60
+ console.log('Scheduled task deleted.');
61
+ }
62
+ catch (err) {
63
+ console.warn('schtasks delete failed (may not exist):', err instanceof Error ? err.message : err);
64
+ }
65
+ const appData = process.env['APPDATA'] ?? join(process.env['USERPROFILE'] ?? 'C:\\Users\\Default', 'AppData', 'Roaming');
66
+ const wrapperPath = join(appData, 'openclaw', 'agent-controller.cmd');
67
+ const { unlinkSync, existsSync } = await import('node:fs');
68
+ if (existsSync(wrapperPath)) {
69
+ unlinkSync(wrapperPath);
70
+ console.log(`Removed: ${wrapperPath}`);
71
+ }
72
+ else {
73
+ console.log(`Wrapper not found: ${wrapperPath}`);
74
+ }
75
+ console.log('Agent Controller uninstalled.');
76
+ }
77
+ //# sourceMappingURL=windows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows.js","sourceRoot":"","sources":["../../src/platform/windows.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw-cloud/agent-controller",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -0,0 +1,50 @@
1
+ import { createInterface } from 'node:readline';
2
+
3
+ async function prompt(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {
4
+ return new Promise((resolve) => {
5
+ rl.question(question, (answer) => {
6
+ resolve(answer.trim());
7
+ });
8
+ });
9
+ }
10
+
11
+ export async function install(): Promise<void> {
12
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
13
+
14
+ console.log('OpenClaw Agent Controller — Installation Wizard');
15
+ console.log('================================================');
16
+ console.log('');
17
+
18
+ const centrifugoUrl = await prompt(rl, 'CENTRIFUGO_URL (e.g. ws://your-server:8000): ');
19
+ const agentToken = await prompt(rl, 'AGENT_TOKEN: ');
20
+ const agentId = await prompt(rl, 'AGENT_ID: ');
21
+ const backendInternalUrl = await prompt(rl, 'BACKEND_INTERNAL_URL (e.g. http://your-server:3000): ');
22
+
23
+ rl.close();
24
+
25
+ if (!centrifugoUrl || !agentToken || !agentId || !backendInternalUrl) {
26
+ console.error('All fields are required. Aborting.');
27
+ process.exit(1);
28
+ }
29
+
30
+ const config = { centrifugoUrl, agentToken, agentId, backendInternalUrl };
31
+
32
+ const platform = process.platform;
33
+ console.log('');
34
+ console.log(`Detected platform: ${platform}`);
35
+ console.log('');
36
+
37
+ if (platform === 'darwin') {
38
+ const { installMacOS } = await import('../platform/macos.js');
39
+ await installMacOS(config);
40
+ } else if (platform === 'linux') {
41
+ const { installLinux } = await import('../platform/linux.js');
42
+ await installLinux(config);
43
+ } else if (platform === 'win32') {
44
+ const { installWindows } = await import('../platform/windows.js');
45
+ await installWindows(config);
46
+ } else {
47
+ console.error(`Unsupported platform: ${platform}`);
48
+ process.exit(1);
49
+ }
50
+ }
@@ -0,0 +1,19 @@
1
+ export async function uninstall(): Promise<void> {
2
+ const platform = process.platform;
3
+ console.log(`Uninstalling Agent Controller on platform: ${platform}`);
4
+ console.log('');
5
+
6
+ if (platform === 'darwin') {
7
+ const { uninstallMacOS } = await import('../platform/macos.js');
8
+ await uninstallMacOS();
9
+ } else if (platform === 'linux') {
10
+ const { uninstallLinux } = await import('../platform/linux.js');
11
+ await uninstallLinux();
12
+ } else if (platform === 'win32') {
13
+ const { uninstallWindows } = await import('../platform/windows.js');
14
+ await uninstallWindows();
15
+ } else {
16
+ console.error(`Unsupported platform: ${platform}`);
17
+ process.exit(1);
18
+ }
19
+ }
package/src/index.ts CHANGED
@@ -22,10 +22,6 @@ export function main(): void {
22
22
  const token = requireEnv('AGENT_TOKEN');
23
23
  const agentId = requireEnv('AGENT_ID');
24
24
  const backendUrl = requireEnv('BACKEND_INTERNAL_URL');
25
- // Read for backward compatibility but no longer used directly
26
- void process.env.CENTRIFUGO_INTERNAL_URL;
27
- void process.env.CENTRIFUGO_API_KEY;
28
-
29
25
  if (DEBUG) console.log('[debug] Debug mode enabled');
30
26
  console.log(`Starting agent-controller for agent: ${agentId}`);
31
27
  console.log(`Backend URL: ${backendUrl} (JWT mode)`);
@@ -0,0 +1,108 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ export interface PlatformConfig {
7
+ centrifugoUrl: string;
8
+ agentToken: string;
9
+ agentId: string;
10
+ backendInternalUrl: string;
11
+ }
12
+
13
+ function execAsync(cmd: string): Promise<string> {
14
+ return new Promise((resolve, reject) => {
15
+ exec(cmd, (err, stdout) => {
16
+ if (err) reject(err);
17
+ else resolve(stdout.toString().trim());
18
+ });
19
+ });
20
+ }
21
+
22
+ async function which(bin: string): Promise<string> {
23
+ try {
24
+ return await execAsync(`which "${bin}"`);
25
+ } catch {
26
+ return '';
27
+ }
28
+ }
29
+
30
+ export async function installLinux(config: PlatformConfig): Promise<void> {
31
+ const home = homedir();
32
+ const systemdDir = join(home, '.config', 'systemd', 'user');
33
+ const servicePath = join(systemdDir, 'agent-controller.service');
34
+
35
+ const nodePath = process.execPath;
36
+ let agentControllerPath = await which('agent-controller');
37
+ if (!agentControllerPath) {
38
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
39
+ }
40
+
41
+ mkdirSync(systemdDir, { recursive: true });
42
+
43
+ const service = `[Unit]
44
+ Description=OpenClaw Agent Controller
45
+ After=network.target
46
+
47
+ [Service]
48
+ Type=simple
49
+ Restart=always
50
+ RestartSec=10
51
+ Environment=CENTRIFUGO_URL=${config.centrifugoUrl}
52
+ Environment=AGENT_TOKEN=${config.agentToken}
53
+ Environment=AGENT_ID=${config.agentId}
54
+ Environment=BACKEND_INTERNAL_URL=${config.backendInternalUrl}
55
+ ExecStart="${nodePath}" "${agentControllerPath}"
56
+
57
+ [Install]
58
+ WantedBy=default.target
59
+ `;
60
+
61
+ writeFileSync(servicePath, service, 'utf8');
62
+ console.log(`Written: ${servicePath}`);
63
+
64
+ try {
65
+ await execAsync('systemctl --user daemon-reload');
66
+ console.log('systemd daemon reloaded.');
67
+ await execAsync('systemctl --user enable --now agent-controller');
68
+ console.log('Service enabled and started.');
69
+ } catch (err) {
70
+ console.warn('systemctl command failed:', err instanceof Error ? err.message : err);
71
+ }
72
+
73
+ console.log('');
74
+ console.log('Agent Controller installed as a systemd user service.');
75
+ console.log('');
76
+ console.log('Useful commands:');
77
+ console.log(' Start: systemctl --user start agent-controller');
78
+ console.log(' Stop: systemctl --user stop agent-controller');
79
+ console.log(' Status: systemctl --user status agent-controller');
80
+ console.log(' Logs: journalctl --user -u agent-controller -f');
81
+ console.log(' Uninstall: agent-controller uninstall');
82
+ }
83
+
84
+ export async function uninstallLinux(): Promise<void> {
85
+ try {
86
+ await execAsync('systemctl --user disable --now agent-controller');
87
+ console.log('Service disabled and stopped.');
88
+ } catch (err) {
89
+ console.warn('systemctl disable failed (may not be enabled):', err instanceof Error ? err.message : err);
90
+ }
91
+
92
+ const servicePath = join(homedir(), '.config', 'systemd', 'user', 'agent-controller.service');
93
+ const { unlinkSync, existsSync } = await import('node:fs');
94
+ if (existsSync(servicePath)) {
95
+ unlinkSync(servicePath);
96
+ console.log(`Removed: ${servicePath}`);
97
+ } else {
98
+ console.log(`Service file not found: ${servicePath}`);
99
+ }
100
+
101
+ try {
102
+ await execAsync('systemctl --user daemon-reload');
103
+ } catch {
104
+ // best-effort
105
+ }
106
+
107
+ console.log('Agent Controller uninstalled.');
108
+ }
@@ -0,0 +1,122 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ export interface PlatformConfig {
7
+ centrifugoUrl: string;
8
+ agentToken: string;
9
+ agentId: string;
10
+ backendInternalUrl: string;
11
+ }
12
+
13
+ function execAsync(cmd: string): Promise<string> {
14
+ return new Promise((resolve, reject) => {
15
+ exec(cmd, (err, stdout) => {
16
+ if (err) reject(err);
17
+ else resolve(stdout.toString().trim());
18
+ });
19
+ });
20
+ }
21
+
22
+ async function which(bin: string): Promise<string> {
23
+ try {
24
+ return await execAsync(`which "${bin}"`);
25
+ } catch {
26
+ return '';
27
+ }
28
+ }
29
+
30
+ export async function installMacOS(config: PlatformConfig): Promise<void> {
31
+ const home = homedir();
32
+ const launchAgentsDir = join(home, 'Library', 'LaunchAgents');
33
+ const plistPath = join(launchAgentsDir, 'com.openclaw.agent-controller.plist');
34
+ const logDir = join(home, 'Library', 'Logs');
35
+ const logFile = join(logDir, 'agent-controller.log');
36
+ const errFile = join(logDir, 'agent-controller.err');
37
+
38
+ const nodePath = process.execPath;
39
+ let agentControllerPath = await which('agent-controller');
40
+ if (!agentControllerPath) {
41
+ // Fallback: derive from node path
42
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
43
+ }
44
+
45
+ mkdirSync(launchAgentsDir, { recursive: true });
46
+
47
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
48
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
49
+ <plist version="1.0">
50
+ <dict>
51
+ <key>Label</key>
52
+ <string>com.openclaw.agent-controller</string>
53
+ <key>ProgramArguments</key>
54
+ <array>
55
+ <string>${nodePath}</string>
56
+ <string>${agentControllerPath}</string>
57
+ </array>
58
+ <key>EnvironmentVariables</key>
59
+ <dict>
60
+ <key>CENTRIFUGO_URL</key>
61
+ <string>${config.centrifugoUrl}</string>
62
+ <key>AGENT_TOKEN</key>
63
+ <string>${config.agentToken}</string>
64
+ <key>AGENT_ID</key>
65
+ <string>${config.agentId}</string>
66
+ <key>BACKEND_INTERNAL_URL</key>
67
+ <string>${config.backendInternalUrl}</string>
68
+ </dict>
69
+ <key>RunAtLoad</key>
70
+ <true/>
71
+ <key>KeepAlive</key>
72
+ <true/>
73
+ <key>StandardOutPath</key>
74
+ <string>${logFile}</string>
75
+ <key>StandardErrorPath</key>
76
+ <string>${errFile}</string>
77
+ </dict>
78
+ </plist>
79
+ `;
80
+
81
+ writeFileSync(plistPath, plist, 'utf8');
82
+ console.log(`Written: ${plistPath}`);
83
+
84
+ try {
85
+ await execAsync(`launchctl load -w "${plistPath}"`);
86
+ console.log('Service loaded via launchctl.');
87
+ } catch (err) {
88
+ console.warn('launchctl load failed (may already be loaded):', err instanceof Error ? err.message : err);
89
+ }
90
+
91
+ console.log('');
92
+ console.log('Agent Controller installed as a macOS LaunchAgent.');
93
+ console.log('');
94
+ console.log('Useful commands:');
95
+ console.log(` Start: launchctl load -w "${plistPath}"`);
96
+ console.log(` Stop: launchctl unload "${plistPath}"`);
97
+ console.log(` Logs: tail -f "${logFile}"`);
98
+ console.log(` Errors: tail -f "${errFile}"`);
99
+ console.log(` Uninstall: agent-controller uninstall`);
100
+ }
101
+
102
+ export async function uninstallMacOS(): Promise<void> {
103
+ const home = homedir();
104
+ const plistPath = join(home, 'Library', 'LaunchAgents', 'com.openclaw.agent-controller.plist');
105
+
106
+ try {
107
+ await execAsync(`launchctl unload "${plistPath}"`);
108
+ console.log('Service unloaded via launchctl.');
109
+ } catch (err) {
110
+ console.warn('launchctl unload failed (may not be loaded):', err instanceof Error ? err.message : err);
111
+ }
112
+
113
+ const { unlinkSync, existsSync } = await import('node:fs');
114
+ if (existsSync(plistPath)) {
115
+ unlinkSync(plistPath);
116
+ console.log(`Removed: ${plistPath}`);
117
+ } else {
118
+ console.log(`Plist not found: ${plistPath}`);
119
+ }
120
+
121
+ console.log('Agent Controller uninstalled.');
122
+ }
@@ -0,0 +1,92 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync, mkdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+
5
+ export interface PlatformConfig {
6
+ centrifugoUrl: string;
7
+ agentToken: string;
8
+ agentId: string;
9
+ backendInternalUrl: string;
10
+ }
11
+
12
+ function execAsync(cmd: string): Promise<string> {
13
+ return new Promise((resolve, reject) => {
14
+ exec(cmd, (err, stdout) => {
15
+ if (err) reject(err);
16
+ else resolve(stdout.toString().trim());
17
+ });
18
+ });
19
+ }
20
+
21
+ async function which(bin: string): Promise<string> {
22
+ try {
23
+ return await execAsync(`where "${bin}"`);
24
+ } catch {
25
+ return '';
26
+ }
27
+ }
28
+
29
+ export async function installWindows(config: PlatformConfig): Promise<void> {
30
+ const appData = process.env['APPDATA'] ?? join(process.env['USERPROFILE'] ?? 'C:\\Users\\Default', 'AppData', 'Roaming');
31
+ const openclawDir = join(appData, 'openclaw');
32
+ const wrapperPath = join(openclawDir, 'agent-controller.cmd');
33
+
34
+ const nodePath = process.execPath;
35
+ let agentControllerPath = await which('agent-controller');
36
+ if (!agentControllerPath) {
37
+ agentControllerPath = join(nodePath, '..', 'agent-controller');
38
+ }
39
+
40
+ mkdirSync(openclawDir, { recursive: true });
41
+
42
+ const wrapper = `@echo off
43
+ set CENTRIFUGO_URL=${config.centrifugoUrl}
44
+ set AGENT_TOKEN=${config.agentToken}
45
+ set AGENT_ID=${config.agentId}
46
+ set BACKEND_INTERNAL_URL=${config.backendInternalUrl}
47
+ "${nodePath}" "${agentControllerPath}"
48
+ `;
49
+
50
+ writeFileSync(wrapperPath, wrapper, 'utf8');
51
+ console.log(`Written: ${wrapperPath}`);
52
+
53
+ try {
54
+ await execAsync(
55
+ `schtasks /create /tn "OpenClaw Agent Controller" /tr "${wrapperPath}" /sc ONLOGON /rl HIGHEST /f`
56
+ );
57
+ console.log('Scheduled task created.');
58
+ } catch (err) {
59
+ console.warn('schtasks create failed:', err instanceof Error ? err.message : err);
60
+ }
61
+
62
+ console.log('');
63
+ console.log('Agent Controller installed as a Windows scheduled task (ONLOGON).');
64
+ console.log('');
65
+ console.log('Useful commands:');
66
+ console.log(' Start: schtasks /run /tn "OpenClaw Agent Controller"');
67
+ console.log(' Stop: schtasks /end /tn "OpenClaw Agent Controller"');
68
+ console.log(' Uninstall: agent-controller uninstall');
69
+ console.log(` Wrapper: ${wrapperPath}`);
70
+ }
71
+
72
+ export async function uninstallWindows(): Promise<void> {
73
+ try {
74
+ await execAsync('schtasks /delete /tn "OpenClaw Agent Controller" /f');
75
+ console.log('Scheduled task deleted.');
76
+ } catch (err) {
77
+ console.warn('schtasks delete failed (may not exist):', err instanceof Error ? err.message : err);
78
+ }
79
+
80
+ const appData = process.env['APPDATA'] ?? join(process.env['USERPROFILE'] ?? 'C:\\Users\\Default', 'AppData', 'Roaming');
81
+ const wrapperPath = join(appData, 'openclaw', 'agent-controller.cmd');
82
+
83
+ const { unlinkSync, existsSync } = await import('node:fs');
84
+ if (existsSync(wrapperPath)) {
85
+ unlinkSync(wrapperPath);
86
+ console.log(`Removed: ${wrapperPath}`);
87
+ } else {
88
+ console.log(`Wrapper not found: ${wrapperPath}`);
89
+ }
90
+
91
+ console.log('Agent Controller uninstalled.');
92
+ }