@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/dist/index.js
CHANGED
|
@@ -19,13 +19,12 @@ 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
|
-
const centrifugoInternalUrl = process.env.CENTRIFUGO_INTERNAL_URL || '';
|
|
23
|
-
const centrifugoApiKey = process.env.CENTRIFUGO_API_KEY || '';
|
|
24
22
|
if (DEBUG)
|
|
25
23
|
console.log('[debug] Debug mode enabled');
|
|
26
24
|
console.log(`Starting agent-controller for agent: ${agentId}`);
|
|
27
25
|
console.log(`Backend URL: ${backendUrl} (JWT mode)`);
|
|
28
|
-
const
|
|
26
|
+
const api = createAgentApi(backendUrl, token);
|
|
27
|
+
const { client } = createConnection({ url, token, agentId, backendUrl, api });
|
|
29
28
|
// Chat provider via OpenClaw Gateway WS
|
|
30
29
|
const gatewayWsUrl = process.env.OPENCLAW_GATEWAY_WS || 'ws://localhost:18789';
|
|
31
30
|
const gatewayToken = process.env.OPENCLAW_GATEWAY_TOKEN || '';
|
|
@@ -40,47 +39,35 @@ export function main() {
|
|
|
40
39
|
else {
|
|
41
40
|
console.warn('OPENCLAW_GATEWAY_TOKEN not set, chat provider disabled');
|
|
42
41
|
}
|
|
43
|
-
// Board handler
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
// Board handler
|
|
43
|
+
const boardHandler = new BoardHandler(api);
|
|
44
|
+
// Initialize board handler after connection is established
|
|
45
|
+
boardHandler.initialize().then((workspaceId) => {
|
|
46
|
+
if (!workspaceId) {
|
|
47
|
+
console.log('No board found for this agent, board handler dormant');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const boardChannel = `board:${workspaceId}`;
|
|
51
|
+
const boardSub = client.newSubscription(boardChannel);
|
|
52
|
+
boardSub.on('publication', (ctx) => {
|
|
53
|
+
const event = ctx.data;
|
|
54
|
+
if (!event || !event.event)
|
|
53
55
|
return;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const boardSub = client.newSubscription(boardChannel);
|
|
57
|
-
boardSub.on('publication', (ctx) => {
|
|
58
|
-
const event = ctx.data;
|
|
59
|
-
if (!event || !event.event)
|
|
60
|
-
return;
|
|
61
|
-
boardHandler.onBoardEvent(event).catch((err) => {
|
|
62
|
-
console.error('Board event handler error:', err instanceof Error ? err.message : err);
|
|
63
|
-
});
|
|
56
|
+
boardHandler.onBoardEvent(event).catch((err) => {
|
|
57
|
+
console.error('Board event handler error:', err instanceof Error ? err.message : err);
|
|
64
58
|
});
|
|
65
|
-
boardSub.subscribe();
|
|
66
|
-
console.log(`Subscribed to board channel: ${boardChannel}`);
|
|
67
|
-
}).catch((err) => {
|
|
68
|
-
console.error('Board handler init error:', err instanceof Error ? err.message : err);
|
|
69
59
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.warn('CENTRIFUGO_INTERNAL_URL or CENTRIFUGO_API_KEY not set, heartbeat disabled');
|
|
83
|
-
}
|
|
60
|
+
boardSub.subscribe();
|
|
61
|
+
console.log(`Subscribed to board channel: ${boardChannel}`);
|
|
62
|
+
}).catch((err) => {
|
|
63
|
+
console.error('Board handler init error:', err instanceof Error ? err.message : err);
|
|
64
|
+
});
|
|
65
|
+
console.log('Heartbeat via backend API');
|
|
66
|
+
const heartbeatTimer = startHeartbeat({
|
|
67
|
+
api,
|
|
68
|
+
agentId,
|
|
69
|
+
getBoardStatus: () => boardHandler.getBoardStatus(),
|
|
70
|
+
});
|
|
84
71
|
const shutdown = () => {
|
|
85
72
|
console.log('Shutting down...');
|
|
86
73
|
if (heartbeatTimer)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,IAAI;IAClB,MAAM,MAAM,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC5C,0CAA0C;IAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,uBAAuB,CAAC;IAC7G,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,UAAU,CAAC,sBAAsB,CAAC,CAAC;IACtD,
|
|
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
package/src/api.ts
CHANGED
|
@@ -5,6 +5,8 @@ const TIMEOUT = 10_000;
|
|
|
5
5
|
export interface AgentApi {
|
|
6
6
|
get(path: string): Promise<unknown>;
|
|
7
7
|
post(path: string, body?: unknown): Promise<Response>;
|
|
8
|
+
publishResponse(agentId: string, data: unknown): Promise<void>;
|
|
9
|
+
publishHeartbeat(agentId: string, payload: unknown): Promise<void>;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export function createAgentApi(backendUrl: string, agentToken: string): AgentApi {
|
|
@@ -44,5 +46,17 @@ export function createAgentApi(backendUrl: string, agentToken: string): AgentApi
|
|
|
44
46
|
async post(path: string, body?: unknown): Promise<Response> {
|
|
45
47
|
return request('POST', path, body);
|
|
46
48
|
},
|
|
49
|
+
async publishResponse(agentId: string, data: unknown): Promise<void> {
|
|
50
|
+
const res = await request('POST', '/api/internal/agent-response', { agentId, data });
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(`publishResponse failed: HTTP ${res.status}`);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async publishHeartbeat(agentId: string, payload: unknown): Promise<void> {
|
|
56
|
+
const res = await request('POST', '/api/internal/agent-heartbeat', { agentId, payload });
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw new Error(`publishHeartbeat failed: HTTP ${res.status}`);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
47
61
|
};
|
|
48
62
|
}
|
|
@@ -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/connection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { debugLog } from './debug.js';
|
|
2
|
+
import type { AgentApi } from './api.js';
|
|
2
3
|
import { Centrifuge, Subscription } from 'centrifuge';
|
|
3
4
|
import WebSocket from 'ws';
|
|
4
5
|
import type { AgentCommand, AgentResponse } from './types.js';
|
|
@@ -20,6 +21,7 @@ export interface ConnectionOptions {
|
|
|
20
21
|
token: string;
|
|
21
22
|
agentId: string;
|
|
22
23
|
backendUrl: string;
|
|
24
|
+
api: AgentApi;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
const handlers: Record<string, (cmd: AgentCommand) => Promise<AgentResponse>> = {
|
|
@@ -108,7 +110,7 @@ export function createConnection(opts: ConnectionOptions): {
|
|
|
108
110
|
|
|
109
111
|
// Chat commands — handle before generic dispatch
|
|
110
112
|
if (command.type === 'chat_list_sessions' || command.type === 'chat_history' || command.type === 'chat_send') {
|
|
111
|
-
const publishFn = async (data: unknown): Promise<void> => { await
|
|
113
|
+
const publishFn = async (data: unknown): Promise<void> => { await opts.api.publishResponse(opts.agentId, data); };
|
|
112
114
|
console.log(`[WS] Handling chat command: type=${command.type} id=${command.id}`);
|
|
113
115
|
switch (command.type) {
|
|
114
116
|
case 'chat_list_sessions':
|
|
@@ -133,7 +135,7 @@ export function createConnection(opts: ConnectionOptions): {
|
|
|
133
135
|
|
|
134
136
|
try {
|
|
135
137
|
const response = await handler(command);
|
|
136
|
-
await
|
|
138
|
+
await opts.api.publishResponse(opts.agentId, response);
|
|
137
139
|
} catch (err) {
|
|
138
140
|
const errorResponse: AgentResponse = {
|
|
139
141
|
id: command.id,
|
|
@@ -141,7 +143,7 @@ export function createConnection(opts: ConnectionOptions): {
|
|
|
141
143
|
success: false,
|
|
142
144
|
error: err instanceof Error ? err.message : String(err),
|
|
143
145
|
};
|
|
144
|
-
await
|
|
146
|
+
await opts.api.publishResponse(opts.agentId, errorResponse);
|
|
145
147
|
}
|
|
146
148
|
});
|
|
147
149
|
|
package/src/heartbeat.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { debugLog } from './debug.js';
|
|
3
|
+
import type { AgentApi } from './api.js';
|
|
3
4
|
import type { BoardState, HeartbeatPayload } from './types.js';
|
|
4
5
|
|
|
5
6
|
const HEARTBEAT_INTERVAL = 30_000;
|
|
6
|
-
const HEARTBEAT_CHANNEL = 'heartbeat:data';
|
|
7
7
|
|
|
8
8
|
export interface HeartbeatOptions {
|
|
9
|
-
|
|
10
|
-
centrifugoApiKey: string;
|
|
9
|
+
api: AgentApi;
|
|
11
10
|
agentId: string;
|
|
12
11
|
getBoardStatus?: () => BoardState;
|
|
13
12
|
}
|
|
@@ -38,36 +37,13 @@ async function publishHeartbeat(opts: HeartbeatOptions): Promise<void> {
|
|
|
38
37
|
...(opts.getBoardStatus ? { boardStatus: opts.getBoardStatus() } : {}),
|
|
39
38
|
};
|
|
40
39
|
|
|
41
|
-
const controller = new AbortController();
|
|
42
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
43
|
-
|
|
44
40
|
try {
|
|
45
41
|
debugLog('heartbeat', '→ sending', opts.agentId, JSON.stringify(payload).slice(0, 500));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
headers: {
|
|
49
|
-
'Content-Type': 'application/json',
|
|
50
|
-
'Authorization': `apikey ${opts.centrifugoApiKey}`,
|
|
51
|
-
},
|
|
52
|
-
body: JSON.stringify({ channel: HEARTBEAT_CHANNEL, data: payload }),
|
|
53
|
-
signal: controller.signal,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (!res.ok) {
|
|
57
|
-
const body = await res.text().catch(() => '');
|
|
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);
|
|
62
|
-
}
|
|
42
|
+
await opts.api.publishHeartbeat(opts.agentId, payload);
|
|
43
|
+
debugLog('heartbeat', '← ok');
|
|
63
44
|
} catch (err) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} else {
|
|
67
|
-
console.error('Heartbeat publish failed:', err instanceof Error ? err.message : err);
|
|
68
|
-
}
|
|
69
|
-
} finally {
|
|
70
|
-
clearTimeout(timeout);
|
|
45
|
+
console.error('Heartbeat publish failed:', err instanceof Error ? err.message : err);
|
|
46
|
+
debugLog('heartbeat', '← error', err);
|
|
71
47
|
}
|
|
72
48
|
}
|
|
73
49
|
|