@openclaw-cloud/agent-controller 0.1.0

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.
Files changed (53) hide show
  1. package/Dockerfile +9 -0
  2. package/__tests__/backup.test.ts +145 -0
  3. package/__tests__/connection.test.ts +111 -0
  4. package/__tests__/handlers.test.ts +150 -0
  5. package/__tests__/heartbeat.test.ts +80 -0
  6. package/bin/agent-controller.js +5 -0
  7. package/dist/connection.d.ts +11 -0
  8. package/dist/connection.js +117 -0
  9. package/dist/connection.js.map +1 -0
  10. package/dist/handlers/backup.d.ts +2 -0
  11. package/dist/handlers/backup.js +81 -0
  12. package/dist/handlers/backup.js.map +1 -0
  13. package/dist/handlers/config.d.ts +2 -0
  14. package/dist/handlers/config.js +43 -0
  15. package/dist/handlers/config.js.map +1 -0
  16. package/dist/handlers/deploy.d.ts +2 -0
  17. package/dist/handlers/deploy.js +30 -0
  18. package/dist/handlers/deploy.js.map +1 -0
  19. package/dist/handlers/exec.d.ts +2 -0
  20. package/dist/handlers/exec.js +29 -0
  21. package/dist/handlers/exec.js.map +1 -0
  22. package/dist/handlers/pair.d.ts +2 -0
  23. package/dist/handlers/pair.js +23 -0
  24. package/dist/handlers/pair.js.map +1 -0
  25. package/dist/handlers/restart.d.ts +2 -0
  26. package/dist/handlers/restart.js +18 -0
  27. package/dist/handlers/restart.js.map +1 -0
  28. package/dist/handlers/stop.d.ts +2 -0
  29. package/dist/handlers/stop.js +14 -0
  30. package/dist/handlers/stop.js.map +1 -0
  31. package/dist/heartbeat.d.ts +9 -0
  32. package/dist/heartbeat.js +60 -0
  33. package/dist/heartbeat.js.map +1 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +43 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/types.d.ts +28 -0
  38. package/dist/types.js +7 -0
  39. package/dist/types.js.map +1 -0
  40. package/jest.config.ts +16 -0
  41. package/package.json +26 -0
  42. package/src/connection.ts +135 -0
  43. package/src/handlers/backup.ts +97 -0
  44. package/src/handlers/config.ts +48 -0
  45. package/src/handlers/deploy.ts +32 -0
  46. package/src/handlers/exec.ts +32 -0
  47. package/src/handlers/pair.ts +26 -0
  48. package/src/handlers/restart.ts +19 -0
  49. package/src/handlers/stop.ts +17 -0
  50. package/src/heartbeat.ts +73 -0
  51. package/src/index.ts +47 -0
  52. package/src/types.ts +32 -0
  53. package/tsconfig.json +18 -0
@@ -0,0 +1,81 @@
1
+ import { exec } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ const WORKSPACE_DIR = '/home/node/.openclaw/workspace';
5
+ const BACKUP_DIR = '/tmp';
6
+ const TAR_TIMEOUT = 120_000;
7
+ const UPLOAD_TIMEOUT = 120_000;
8
+ export function handleBackup(command) {
9
+ const uploadUrl = command.payload.uploadUrl;
10
+ if (!uploadUrl) {
11
+ return Promise.resolve({
12
+ id: command.id,
13
+ type: 'backup',
14
+ success: false,
15
+ error: 'Missing "uploadUrl" in payload',
16
+ });
17
+ }
18
+ const timestamp = Date.now();
19
+ const filename = `backup-${timestamp}.tar.gz`;
20
+ const archivePath = path.join(BACKUP_DIR, filename);
21
+ return createArchive(archivePath)
22
+ .then(() => uploadArchive(archivePath, uploadUrl))
23
+ .then(async (size) => {
24
+ await cleanup(archivePath);
25
+ return {
26
+ id: command.id,
27
+ type: 'backup',
28
+ success: true,
29
+ data: { size, filename },
30
+ };
31
+ })
32
+ .catch(async (err) => {
33
+ await cleanup(archivePath);
34
+ return {
35
+ id: command.id,
36
+ type: 'backup',
37
+ success: false,
38
+ error: err instanceof Error ? err.message : String(err),
39
+ };
40
+ });
41
+ }
42
+ function createArchive(archivePath) {
43
+ return new Promise((resolve, reject) => {
44
+ exec(`tar -czf ${archivePath} -C ${path.dirname(WORKSPACE_DIR)} ${path.basename(WORKSPACE_DIR)}`, { timeout: TAR_TIMEOUT }, (error, _stdout, stderr) => {
45
+ if (error) {
46
+ reject(new Error(`tar failed: ${stderr.toString() || error.message}`));
47
+ }
48
+ else {
49
+ resolve();
50
+ }
51
+ });
52
+ });
53
+ }
54
+ async function uploadArchive(archivePath, uploadUrl) {
55
+ const stat = await fs.stat(archivePath);
56
+ const fileBuffer = await fs.readFile(archivePath);
57
+ const boundary = `----backup-${Date.now()}`;
58
+ const filename = path.basename(archivePath);
59
+ const header = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename}"\r\nContent-Type: application/gzip\r\n\r\n`);
60
+ const footer = Buffer.from(`\r\n--${boundary}--\r\n`);
61
+ const body = Buffer.concat([header, fileBuffer, footer]);
62
+ const response = await fetch(uploadUrl, {
63
+ method: 'POST',
64
+ headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
65
+ body,
66
+ signal: AbortSignal.timeout(UPLOAD_TIMEOUT),
67
+ });
68
+ if (!response.ok) {
69
+ throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
70
+ }
71
+ return stat.size;
72
+ }
73
+ async function cleanup(archivePath) {
74
+ try {
75
+ await fs.unlink(archivePath);
76
+ }
77
+ catch {
78
+ // ignore cleanup errors
79
+ }
80
+ }
81
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/handlers/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleConfig(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,43 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { exec } from 'node:child_process';
4
+ const CONFIG_DIR = '/etc/openclaw';
5
+ export function handleConfig(command) {
6
+ const filename = command.payload.filename;
7
+ const content = command.payload.content;
8
+ if (!filename || content === undefined) {
9
+ return Promise.resolve({
10
+ id: command.id,
11
+ type: 'config',
12
+ success: false,
13
+ error: 'Missing "filename" or "content" in payload',
14
+ });
15
+ }
16
+ const configPath = path.join(CONFIG_DIR, path.basename(filename));
17
+ return fs.mkdir(CONFIG_DIR, { recursive: true })
18
+ .then(() => fs.writeFile(configPath, content, 'utf-8'))
19
+ .then(() => {
20
+ return new Promise((resolve) => {
21
+ exec('openclaw gateway restart', { timeout: 30_000 }, (error, stdout, stderr) => {
22
+ resolve({
23
+ id: command.id,
24
+ type: 'config',
25
+ success: !error,
26
+ data: {
27
+ path: configPath,
28
+ stdout: stdout.toString(),
29
+ stderr: stderr.toString(),
30
+ },
31
+ ...(error ? { error: error.message } : {}),
32
+ });
33
+ });
34
+ });
35
+ })
36
+ .catch((err) => ({
37
+ id: command.id,
38
+ type: 'config',
39
+ success: false,
40
+ error: err instanceof Error ? err.message : String(err),
41
+ }));
42
+ }
43
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/handlers/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,MAAM,UAAU,GAAG,eAAe,CAAC;AAEnC,MAAM,UAAU,YAAY,CAAC,OAAqB;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAkB,CAAC;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAiB,CAAC;IAElD,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,4CAA4C;SACpD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAElE,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SAC7C,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;SACtD,IAAI,CAAC,GAAG,EAAE;QACT,OAAO,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YAC5C,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBAC9E,OAAO,CAAC;oBACN,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,CAAC,KAAK;oBACf,IAAI,EAAE;wBACJ,IAAI,EAAE,UAAU;wBAChB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;wBACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;qBAC1B;oBACD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC3C,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACf,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;KACxD,CAAC,CAAC,CAAC;AACR,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleDeploy(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,30 @@
1
+ import { exec } from 'node:child_process';
2
+ export function handleDeploy(command) {
3
+ return new Promise((resolve) => {
4
+ exec('npm i -g openclaw', { timeout: 120_000 }, (installErr, installOut, installStderr) => {
5
+ if (installErr) {
6
+ resolve({
7
+ id: command.id,
8
+ type: 'deploy',
9
+ success: false,
10
+ data: { stdout: installOut.toString(), stderr: installStderr.toString() },
11
+ error: `Install failed: ${installErr.message}`,
12
+ });
13
+ return;
14
+ }
15
+ exec('openclaw gateway restart', { timeout: 30_000 }, (restartErr, restartOut, restartStderr) => {
16
+ resolve({
17
+ id: command.id,
18
+ type: 'deploy',
19
+ success: !restartErr,
20
+ data: {
21
+ install: { stdout: installOut.toString(), stderr: installStderr.toString() },
22
+ restart: { stdout: restartOut.toString(), stderr: restartStderr.toString() },
23
+ },
24
+ ...(restartErr ? { error: `Restart failed: ${restartErr.message}` } : {}),
25
+ });
26
+ });
27
+ });
28
+ });
29
+ }
30
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/handlers/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,MAAM,UAAU,YAAY,CAAC,OAAqB;IAChD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE;YACxF,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,CAAC;oBACN,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,EAAE;oBACzE,KAAK,EAAE,mBAAmB,UAAU,CAAC,OAAO,EAAE;iBAC/C,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE;gBAC9F,OAAO,CAAC;oBACN,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,CAAC,UAAU;oBACpB,IAAI,EAAE;wBACJ,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,EAAE;wBAC5E,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,EAAE;qBAC7E;oBACD,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,mBAAmB,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC1E,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleExec(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,29 @@
1
+ import { exec } from 'node:child_process';
2
+ const EXEC_TIMEOUT = 60_000;
3
+ export function handleExec(command) {
4
+ const cmd = command.payload.command;
5
+ if (!cmd) {
6
+ return Promise.resolve({
7
+ id: command.id,
8
+ type: 'exec',
9
+ success: false,
10
+ error: 'Missing "command" in payload',
11
+ });
12
+ }
13
+ return new Promise((resolve) => {
14
+ exec(cmd, { timeout: EXEC_TIMEOUT }, (error, stdout, stderr) => {
15
+ resolve({
16
+ id: command.id,
17
+ type: 'exec',
18
+ success: !error,
19
+ data: {
20
+ exitCode: error ? error.code ?? 1 : 0,
21
+ stdout: stdout.toString(),
22
+ stderr: stderr.toString(),
23
+ },
24
+ ...(error ? { error: error.message } : {}),
25
+ });
26
+ });
27
+ });
28
+ }
29
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/handlers/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAiB,CAAC;IAC9C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,8BAA8B;SACtC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC7D,OAAO,CAAC;gBACN,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,CAAC,KAAK;gBACf,IAAI,EAAE;oBACJ,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAE,KAAmD,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBACpF,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;oBACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;iBAC1B;gBACD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handlePair(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,23 @@
1
+ export function handlePair(command) {
2
+ const pairToken = command.payload.token;
3
+ const targetId = command.payload.targetId;
4
+ if (!pairToken || !targetId) {
5
+ return Promise.resolve({
6
+ id: command.id,
7
+ type: 'pair',
8
+ success: false,
9
+ error: 'Missing "token" or "targetId" in payload',
10
+ });
11
+ }
12
+ return Promise.resolve({
13
+ id: command.id,
14
+ type: 'pair',
15
+ success: true,
16
+ data: {
17
+ paired: true,
18
+ targetId,
19
+ agentId: process.env.AGENT_ID,
20
+ },
21
+ });
22
+ }
23
+ //# sourceMappingURL=pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.js","sourceRoot":"","sources":["../../src/handlers/pair.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,KAAe,CAAC;IAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAkB,CAAC;IAEpD,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,0CAA0C;SAClD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC;QACrB,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,IAAI;QACb,IAAI,EAAE;YACJ,MAAM,EAAE,IAAI;YACZ,QAAQ;YACR,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;SAC9B;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleRestart(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,18 @@
1
+ import { exec } from 'node:child_process';
2
+ export function handleRestart(command) {
3
+ return new Promise((resolve) => {
4
+ exec('openclaw gateway restart', { timeout: 30_000 }, (error, stdout, stderr) => {
5
+ resolve({
6
+ id: command.id,
7
+ type: 'restart',
8
+ success: !error,
9
+ data: {
10
+ stdout: stdout.toString(),
11
+ stderr: stderr.toString(),
12
+ },
13
+ ...(error ? { error: error.message } : {}),
14
+ });
15
+ });
16
+ });
17
+ }
18
+ //# sourceMappingURL=restart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restart.js","sourceRoot":"","sources":["../../src/handlers/restart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,MAAM,UAAU,aAAa,CAAC,OAAqB;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC9E,OAAO,CAAC;gBACN,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,CAAC,KAAK;gBACf,IAAI,EAAE;oBACJ,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;oBACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;iBAC1B;gBACD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AgentCommand, AgentResponse } from '../types.js';
2
+ export declare function handleStop(command: AgentCommand): Promise<AgentResponse>;
@@ -0,0 +1,14 @@
1
+ export function handleStop(command) {
2
+ const response = {
3
+ id: command.id,
4
+ type: 'stop',
5
+ success: true,
6
+ data: { message: 'Shutting down gracefully' },
7
+ };
8
+ setTimeout(() => {
9
+ console.log('Agent stopping gracefully...');
10
+ process.exit(0);
11
+ }, 500);
12
+ return Promise.resolve(response);
13
+ }
14
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/handlers/stop.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,MAAM,QAAQ,GAAkB;QAC9B,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE;KAC9C,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,EAAE,GAAG,CAAC,CAAC;IAER,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { HeartbeatPayload } from './types.js';
2
+ export interface HeartbeatOptions {
3
+ centrifugoInternalUrl: string;
4
+ centrifugoApiKey: string;
5
+ agentId: string;
6
+ }
7
+ declare function getMetrics(): HeartbeatPayload['metrics'];
8
+ export declare function startHeartbeat(opts: HeartbeatOptions): NodeJS.Timeout;
9
+ export { getMetrics };
@@ -0,0 +1,60 @@
1
+ import os from 'node:os';
2
+ const HEARTBEAT_INTERVAL = 30_000;
3
+ const HEARTBEAT_CHANNEL = 'heartbeat:data';
4
+ function getMetrics() {
5
+ const cpus = os.cpus();
6
+ const cpuUsage = cpus.reduce((acc, cpu) => {
7
+ const total = Object.values(cpu.times).reduce((a, b) => a + b, 0);
8
+ return acc + (1 - cpu.times.idle / total);
9
+ }, 0) / cpus.length;
10
+ const totalMem = os.totalmem();
11
+ const freeMem = os.freemem();
12
+ return {
13
+ cpu: Math.round(cpuUsage * 100) / 100,
14
+ mem: Math.round(((totalMem - freeMem) / totalMem) * 100) / 100,
15
+ uptime: os.uptime(),
16
+ };
17
+ }
18
+ async function publishHeartbeat(opts) {
19
+ const payload = {
20
+ type: 'heartbeat',
21
+ agentId: opts.agentId,
22
+ ts: Date.now(),
23
+ metrics: getMetrics(),
24
+ };
25
+ const controller = new AbortController();
26
+ const timeout = setTimeout(() => controller.abort(), 5000);
27
+ try {
28
+ const res = await fetch(`${opts.centrifugoInternalUrl}/api/publish`, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ 'Authorization': `apikey ${opts.centrifugoApiKey}`,
33
+ },
34
+ body: JSON.stringify({ channel: HEARTBEAT_CHANNEL, data: payload }),
35
+ signal: controller.signal,
36
+ });
37
+ if (!res.ok) {
38
+ const body = await res.text().catch(() => '');
39
+ console.error(`Heartbeat publish failed (HTTP ${res.status}): ${body}`);
40
+ }
41
+ }
42
+ catch (err) {
43
+ if (err.name === 'AbortError') {
44
+ console.error('Heartbeat publish timed out');
45
+ }
46
+ else {
47
+ console.error('Heartbeat publish failed:', err instanceof Error ? err.message : err);
48
+ }
49
+ }
50
+ finally {
51
+ clearTimeout(timeout);
52
+ }
53
+ }
54
+ export function startHeartbeat(opts) {
55
+ const send = () => { publishHeartbeat(opts).catch(() => { }); };
56
+ send();
57
+ return setInterval(send, HEARTBEAT_INTERVAL);
58
+ }
59
+ export { getMetrics };
60
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function main(): void;
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ import { createConnection } from './connection.js';
2
+ import { startHeartbeat } from './heartbeat.js';
3
+ function requireEnv(name) {
4
+ const value = process.env[name];
5
+ if (!value) {
6
+ console.error(`Missing required environment variable: ${name}`);
7
+ process.exit(1);
8
+ }
9
+ return value;
10
+ }
11
+ export function main() {
12
+ const rawUrl = requireEnv('CENTRIFUGO_URL');
13
+ // Centrifuge client requires ws:// scheme
14
+ const url = rawUrl.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://') + '/connection/websocket';
15
+ const token = requireEnv('AGENT_TOKEN');
16
+ const agentId = requireEnv('AGENT_ID');
17
+ const backendUrl = process.env.BACKEND_INTERNAL_URL || '';
18
+ const centrifugoInternalUrl = process.env.CENTRIFUGO_INTERNAL_URL || '';
19
+ const centrifugoApiKey = process.env.CENTRIFUGO_API_KEY || '';
20
+ console.log(`Starting agent-controller for agent: ${agentId}`);
21
+ if (backendUrl) {
22
+ console.log(`Backend URL: ${backendUrl} (JWT mode)`);
23
+ }
24
+ const { client } = createConnection({ url, token, agentId, backendUrl: backendUrl || undefined });
25
+ let heartbeatTimer;
26
+ if (centrifugoInternalUrl && centrifugoApiKey) {
27
+ console.log(`Heartbeat via Centrifugo internal API: ${centrifugoInternalUrl}`);
28
+ heartbeatTimer = startHeartbeat({ centrifugoInternalUrl, centrifugoApiKey, agentId });
29
+ }
30
+ else {
31
+ console.warn('CENTRIFUGO_INTERNAL_URL or CENTRIFUGO_API_KEY not set, heartbeat disabled');
32
+ }
33
+ const shutdown = () => {
34
+ console.log('Shutting down...');
35
+ if (heartbeatTimer)
36
+ clearInterval(heartbeatTimer);
37
+ client.disconnect();
38
+ process.exit(0);
39
+ };
40
+ process.on('SIGINT', shutdown);
41
+ process.on('SIGTERM', shutdown);
42
+ }
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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;AAEhD,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,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC;IAC1D,MAAM,qBAAqB,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC;IACxE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;IAE9D,OAAO,CAAC,GAAG,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAC;IAC/D,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,gBAAgB,UAAU,aAAa,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,IAAI,SAAS,EAAE,CAAC,CAAC;IAElG,IAAI,cAA0C,CAAC;IAC/C,IAAI,qBAAqB,IAAI,gBAAgB,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,0CAA0C,qBAAqB,EAAE,CAAC,CAAC;QAC/E,cAAc,GAAG,cAAc,CAAC,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAChC,IAAI,cAAc;YAAE,aAAa,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,CAAC,UAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Centrifugo channel layout:
3
+ * agent:<agentId> — commands from backend, responses from agent
4
+ * heartbeat:<agentId> — heartbeat publications from agent
5
+ */
6
+ export type CommandType = 'exec' | 'restart' | 'deploy' | 'config' | 'pair' | 'stop' | 'backup';
7
+ export interface AgentCommand {
8
+ id: string;
9
+ type: CommandType;
10
+ payload: Record<string, unknown>;
11
+ }
12
+ export interface AgentResponse {
13
+ id: string;
14
+ type: CommandType;
15
+ success: boolean;
16
+ data?: Record<string, unknown>;
17
+ error?: string;
18
+ }
19
+ export interface HeartbeatPayload {
20
+ type: 'heartbeat';
21
+ agentId: string;
22
+ ts: number;
23
+ metrics: {
24
+ cpu: number;
25
+ mem: number;
26
+ uptime: number;
27
+ };
28
+ }
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Centrifugo channel layout:
3
+ * agent:<agentId> — commands from backend, responses from agent
4
+ * heartbeat:<agentId> — heartbeat publications from agent
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/jest.config.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { Config } from 'jest';
2
+
3
+ const config: Config = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ roots: ['<rootDir>/__tests__'],
7
+ moduleFileExtensions: ['ts', 'js', 'json'],
8
+ transform: {
9
+ '^.+\\.ts$': ['ts-jest', { useESM: false, tsconfig: { module: 'commonjs' } }],
10
+ },
11
+ moduleNameMapper: {
12
+ '^(\\.{1,2}/.*)\\.js$': '$1',
13
+ },
14
+ };
15
+
16
+ export default config;
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@openclaw-cloud/agent-controller",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "agent-controller": "bin/agent-controller.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "node --experimental-vm-modules node_modules/.bin/jest",
12
+ "start": "node bin/agent-controller.js"
13
+ },
14
+ "dependencies": {
15
+ "centrifuge": "^5.5.3",
16
+ "ws": "^8.19.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/jest": "^30.0.0",
20
+ "@types/node": "^25.2.3",
21
+ "@types/ws": "^8.18.1",
22
+ "jest": "^30.2.0",
23
+ "ts-jest": "^29.4.6",
24
+ "typescript": "^5.9.3"
25
+ }
26
+ }