@openclaw-cloud/agent-controller 0.2.6 → 0.2.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.
Files changed (76) hide show
  1. package/dist/commands/install.js +3 -0
  2. package/dist/commands/install.js.map +1 -1
  3. package/dist/config-file.d.ts +9 -0
  4. package/dist/config-file.js +47 -0
  5. package/dist/config-file.js.map +1 -0
  6. package/dist/connection.d.ts +1 -0
  7. package/dist/connection.js +27 -13
  8. package/dist/connection.js.map +1 -1
  9. package/dist/handlers/backup.js +7 -2
  10. package/dist/handlers/backup.js.map +1 -1
  11. package/dist/handlers/knowledge-sync.d.ts +2 -0
  12. package/dist/handlers/knowledge-sync.js +51 -0
  13. package/dist/handlers/knowledge-sync.js.map +1 -0
  14. package/dist/heartbeat.d.ts +1 -0
  15. package/dist/heartbeat.js +30 -0
  16. package/dist/heartbeat.js.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/types.d.ts +2 -1
  20. package/package.json +6 -1
  21. package/.claude/cc-notify.sh +0 -32
  22. package/.claude/settings.json +0 -31
  23. package/.husky/pre-commit +0 -1
  24. package/BIZPLAN.md +0 -530
  25. package/CLAUDE.md +0 -172
  26. package/Dockerfile +0 -9
  27. package/__tests__/api.test.ts +0 -183
  28. package/__tests__/backup.test.ts +0 -145
  29. package/__tests__/board-handler.test.ts +0 -323
  30. package/__tests__/chat.test.ts +0 -191
  31. package/__tests__/config.test.ts +0 -100
  32. package/__tests__/connection.test.ts +0 -289
  33. package/__tests__/file-delete.test.ts +0 -90
  34. package/__tests__/file-write.test.ts +0 -119
  35. package/__tests__/gateway-adapter.test.ts +0 -366
  36. package/__tests__/gateway-client.test.ts +0 -272
  37. package/__tests__/handlers.test.ts +0 -150
  38. package/__tests__/heartbeat.test.ts +0 -124
  39. package/__tests__/onboarding.test.ts +0 -55
  40. package/__tests__/package-install.test.ts +0 -109
  41. package/__tests__/pair.test.ts +0 -60
  42. package/__tests__/self-update.test.ts +0 -123
  43. package/__tests__/stop.test.ts +0 -38
  44. package/jest.config.ts +0 -16
  45. package/src/api.ts +0 -62
  46. package/src/commands/install.ts +0 -68
  47. package/src/commands/self-update.ts +0 -43
  48. package/src/commands/uninstall.ts +0 -19
  49. package/src/config-file.ts +0 -56
  50. package/src/connection.ts +0 -203
  51. package/src/debug.ts +0 -11
  52. package/src/handlers/backup.ts +0 -101
  53. package/src/handlers/board-handler.ts +0 -155
  54. package/src/handlers/chat.ts +0 -79
  55. package/src/handlers/config.ts +0 -48
  56. package/src/handlers/deploy.ts +0 -32
  57. package/src/handlers/exec.ts +0 -32
  58. package/src/handlers/file-delete.ts +0 -46
  59. package/src/handlers/file-write.ts +0 -65
  60. package/src/handlers/knowledge-sync.ts +0 -53
  61. package/src/handlers/onboarding.ts +0 -19
  62. package/src/handlers/package-install.ts +0 -69
  63. package/src/handlers/pair.ts +0 -26
  64. package/src/handlers/restart.ts +0 -19
  65. package/src/handlers/stop.ts +0 -17
  66. package/src/heartbeat.ts +0 -110
  67. package/src/index.ts +0 -97
  68. package/src/openclaw/gateway-adapter.ts +0 -129
  69. package/src/openclaw/gateway-client.ts +0 -131
  70. package/src/openclaw/index.ts +0 -17
  71. package/src/openclaw/types.ts +0 -41
  72. package/src/platform/linux.ts +0 -108
  73. package/src/platform/macos.ts +0 -122
  74. package/src/platform/windows.ts +0 -92
  75. package/src/types.ts +0 -94
  76. package/tsconfig.json +0 -18
@@ -1,155 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentApi } from '../api.js';
3
- import type { BoardState, BoardEvent, BoardInfo, CardDetail } from '../types.js';
4
-
5
- const OPENCLAW_TIMEOUT = 30_000;
6
-
7
- export class BoardHandler {
8
- private boardState: BoardState = { state: 'idle', cardId: null };
9
- private myColumnIds: string[] = [];
10
- private boardId: string | null = null;
11
- private workspaceId: string | null = null;
12
- private api: AgentApi;
13
-
14
- constructor(api: AgentApi) {
15
- this.api = api;
16
- }
17
-
18
- async initialize(): Promise<string | null> {
19
- try {
20
- const data = await this.api.get('/api/agent/board') as BoardInfo;
21
- this.boardId = data.board.id;
22
- this.workspaceId = data.board.workspaceId;
23
- this.myColumnIds = data.board.myColumnIds;
24
- console.log(`Board initialized: ${this.boardId} (workspace: ${this.workspaceId}), watching ${this.myColumnIds.length} column(s)`);
25
-
26
- // Backfill: check for existing unassigned cards in my columns
27
- await this.checkQueue(data);
28
- // Return workspaceId for channel subscription (board:<workspaceId>)
29
- return this.workspaceId;
30
- } catch (err) {
31
- console.error('Board initialization failed:', err instanceof Error ? err.message : err);
32
- return null;
33
- }
34
- }
35
-
36
- async onBoardEvent(event: BoardEvent): Promise<void> {
37
- switch (event.event) {
38
- case 'card:entered':
39
- if (!event.columnId || !this.myColumnIds.includes(event.columnId)) return;
40
- if (this.boardState.state === 'working') return;
41
- await this.tryClaimCard(event.cardId);
42
- break;
43
- case 'card:claimed':
44
- case 'card:moved':
45
- case 'card:commented':
46
- // Info only, ignore
47
- break;
48
- }
49
- }
50
-
51
- async tryClaimCard(cardId: string): Promise<void> {
52
- try {
53
- const res = await this.api.post(`/api/agent/board/cards/${cardId}/claim`);
54
- if (res.ok) {
55
- this.boardState = { state: 'working', cardId };
56
- console.log(`Claimed card: ${cardId}`);
57
- await this.startTask(cardId);
58
- } else if (res.status === 409) {
59
- console.log(`Card ${cardId} already claimed or agent busy, skipping`);
60
- } else {
61
- const body = await res.text().catch(() => '');
62
- console.error(`Claim card ${cardId} failed (HTTP ${res.status}): ${body}`);
63
- }
64
- } catch (err) {
65
- console.error('tryClaimCard error:', err instanceof Error ? err.message : err);
66
- }
67
- }
68
-
69
- async startTask(cardId: string): Promise<void> {
70
- try {
71
- const data = await this.api.get(`/api/agent/board/cards/${cardId}`) as CardDetail;
72
- const card = data.card;
73
- const message = [
74
- `New task from board: ${card.title}`,
75
- card.description ? `Description: ${card.description}` : null,
76
- card.priority ? `Priority: ${card.priority}` : null,
77
- '',
78
- 'Instructions:',
79
- '- Complete the task described above.',
80
- '- Post progress and results as comments using: POST /api/agent/board/cards/' + cardId + '/comments',
81
- '- When done, move the card forward using: POST /api/agent/board/cards/' + cardId + '/move',
82
- ].filter(Boolean).join('\n');
83
-
84
- await this.execOpenclaw(message);
85
- } catch (err) {
86
- console.error(`startTask(${cardId}) error:`, err instanceof Error ? err.message : err);
87
- }
88
- }
89
-
90
- completeTask(cardId: string): void {
91
- console.log(`Task completed: ${cardId}`);
92
- this.boardState = { state: 'idle', cardId: null };
93
- this.checkQueue().catch((err) => {
94
- console.error('checkQueue after completeTask error:', err instanceof Error ? err.message : err);
95
- });
96
- }
97
-
98
- async checkQueue(existingData?: BoardInfo): Promise<void> {
99
- try {
100
- const data = existingData ?? (await this.api.get('/api/agent/board') as BoardInfo);
101
- const candidates: Array<{ id: string; priority?: string; createdAt?: string }> = [];
102
-
103
- for (const col of data.board.columns) {
104
- if (!this.myColumnIds.includes(col.id)) continue;
105
- for (const card of col.cards) {
106
- if (!card.assignedAgentId) {
107
- candidates.push(card);
108
- }
109
- }
110
- }
111
-
112
- if (candidates.length === 0) return;
113
-
114
- // Sort by priority (high > medium > low) then by creation date (oldest first)
115
- const priorityOrder: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3 };
116
- candidates.sort((a, b) => {
117
- const pa = priorityOrder[a.priority ?? 'medium'] ?? 2;
118
- const pb = priorityOrder[b.priority ?? 'medium'] ?? 2;
119
- if (pa !== pb) return pa - pb;
120
- return (a.createdAt ?? '').localeCompare(b.createdAt ?? '');
121
- });
122
-
123
- console.log(`Queue: ${candidates.length} unassigned card(s) in my columns, trying first`);
124
- await this.tryClaimCard(candidates[0].id);
125
- } catch (err) {
126
- console.error('checkQueue error:', err instanceof Error ? err.message : err);
127
- }
128
- }
129
-
130
- getBoardStatus(): BoardState {
131
- return this.boardState;
132
- }
133
-
134
- getBoardId(): string | null {
135
- return this.boardId;
136
- }
137
-
138
- private execOpenclaw(message: string): Promise<void> {
139
- return new Promise((resolve) => {
140
- const escaped = message.replace(/'/g, "'\\''");
141
- exec(
142
- `openclaw system event --text '${escaped}' --mode now`,
143
- { timeout: OPENCLAW_TIMEOUT },
144
- (error, stdout, stderr) => {
145
- if (error) {
146
- console.error('openclaw system event failed:', stderr.toString() || error.message);
147
- } else {
148
- console.log('openclaw system event sent');
149
- }
150
- resolve();
151
- },
152
- );
153
- });
154
- }
155
- }
@@ -1,79 +0,0 @@
1
- import type { AgentCommand } from '../types.js';
2
- import { getChatProvider } from '../openclaw/index.js';
3
-
4
- export async function handleChatListSessions(
5
- command: AgentCommand,
6
- publish: (data: unknown) => Promise<void>,
7
- ): Promise<void> {
8
- const provider = getChatProvider();
9
- if (!provider) {
10
- await publish({ type: 'chat_sessions_response', correlationId: command.id, sessions: [], error: 'Chat provider not initialized' });
11
- return;
12
- }
13
- try {
14
- const sessions = await provider.listSessions();
15
- await publish({ type: 'chat_sessions_response', correlationId: command.id, sessions });
16
- } catch (err) {
17
- await publish({ type: 'chat_sessions_response', correlationId: command.id, sessions: [], error: err instanceof Error ? err.message : String(err) });
18
- }
19
- }
20
-
21
- export async function handleChatHistory(
22
- command: AgentCommand,
23
- publish: (data: unknown) => Promise<void>,
24
- ): Promise<void> {
25
- const { sessionKey, limit } = command.payload as { sessionKey: string; limit?: number };
26
- const provider = getChatProvider();
27
- if (!provider) {
28
- await publish({ type: 'chat_history_response', correlationId: command.id, messages: [], error: 'Chat provider not initialized' });
29
- return;
30
- }
31
- try {
32
- const messages = await provider.getHistory(sessionKey, limit ?? 200);
33
- await publish({ type: 'chat_history_response', correlationId: command.id, messages });
34
- } catch (err) {
35
- await publish({ type: 'chat_history_response', correlationId: command.id, messages: [], error: err instanceof Error ? err.message : String(err) });
36
- }
37
- }
38
-
39
- export async function handleChatSend(
40
- command: AgentCommand,
41
- publish: (data: unknown) => Promise<void>,
42
- agentId: string,
43
- ): Promise<void> {
44
- const { sessionKey, text, attachments } = command.payload as {
45
- sessionKey: string;
46
- text: string;
47
- attachments?: Array<{ type: 'image'; mimeType: string; content: string }>;
48
- };
49
- const correlationId = command.id;
50
-
51
- // Typing indicator
52
- await publish({ type: 'chat_typing', agentId, state: true }).catch(() => {});
53
-
54
- const provider = getChatProvider();
55
- if (!provider) {
56
- await publish({ type: 'chat_response', correlationId, sessionKey, text: '', error: 'Chat provider not initialized' });
57
- return;
58
- }
59
-
60
- try {
61
- await provider.sendMessage({
62
- sessionKey,
63
- text,
64
- idempotencyKey: correlationId,
65
- attachments, // base64 inline — Centrifugo limit raised to 10MB
66
- onDelta: async (accumulated) => {
67
- await publish({ type: 'chat_delta', correlationId, sessionKey, text: accumulated }).catch(() => {});
68
- },
69
- onDone: async (finalText) => {
70
- await publish({ type: 'chat_response', correlationId, sessionKey, text: finalText }).catch(() => {});
71
- },
72
- onError: async (error) => {
73
- await publish({ type: 'chat_response', correlationId, sessionKey, text: '', error }).catch(() => {});
74
- },
75
- });
76
- } catch (err) {
77
- await publish({ type: 'chat_response', correlationId, sessionKey, text: '', error: err instanceof Error ? err.message : String(err) }).catch(() => {});
78
- }
79
- }
@@ -1,48 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { exec } from 'node:child_process';
4
- import type { AgentCommand, AgentResponse } from '../types.js';
5
-
6
- const CONFIG_DIR = '/etc/openclaw';
7
-
8
- export function handleConfig(command: AgentCommand): Promise<AgentResponse> {
9
- const filename = command.payload.filename as string;
10
- const content = command.payload.content as string;
11
-
12
- if (!filename || content === undefined) {
13
- return Promise.resolve({
14
- id: command.id,
15
- type: 'config',
16
- success: false,
17
- error: 'Missing "filename" or "content" in payload',
18
- });
19
- }
20
-
21
- const configPath = path.join(CONFIG_DIR, path.basename(filename));
22
-
23
- return fs.mkdir(CONFIG_DIR, { recursive: true })
24
- .then(() => fs.writeFile(configPath, content, 'utf-8'))
25
- .then(() => {
26
- return new Promise<AgentResponse>((resolve) => {
27
- exec('openclaw gateway restart', { timeout: 30_000 }, (error, stdout, stderr) => {
28
- resolve({
29
- id: command.id,
30
- type: 'config',
31
- success: !error,
32
- data: {
33
- path: configPath,
34
- stdout: stdout.toString(),
35
- stderr: stderr.toString(),
36
- },
37
- ...(error ? { error: error.message } : {}),
38
- });
39
- });
40
- });
41
- })
42
- .catch((err) => ({
43
- id: command.id,
44
- type: 'config',
45
- success: false,
46
- error: err instanceof Error ? err.message : String(err),
47
- }));
48
- }
@@ -1,32 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentCommand, AgentResponse } from '../types.js';
3
-
4
- export function handleDeploy(command: AgentCommand): Promise<AgentResponse> {
5
- return new Promise((resolve) => {
6
- exec('npm i -g openclaw', { timeout: 120_000 }, (installErr, installOut, installStderr) => {
7
- if (installErr) {
8
- resolve({
9
- id: command.id,
10
- type: 'deploy',
11
- success: false,
12
- data: { stdout: installOut.toString(), stderr: installStderr.toString() },
13
- error: `Install failed: ${installErr.message}`,
14
- });
15
- return;
16
- }
17
-
18
- exec('openclaw gateway restart', { timeout: 30_000 }, (restartErr, restartOut, restartStderr) => {
19
- resolve({
20
- id: command.id,
21
- type: 'deploy',
22
- success: !restartErr,
23
- data: {
24
- install: { stdout: installOut.toString(), stderr: installStderr.toString() },
25
- restart: { stdout: restartOut.toString(), stderr: restartStderr.toString() },
26
- },
27
- ...(restartErr ? { error: `Restart failed: ${restartErr.message}` } : {}),
28
- });
29
- });
30
- });
31
- });
32
- }
@@ -1,32 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentCommand, AgentResponse } from '../types.js';
3
-
4
- const EXEC_TIMEOUT = 60_000;
5
-
6
- export function handleExec(command: AgentCommand): Promise<AgentResponse> {
7
- const cmd = command.payload.command as string;
8
- if (!cmd) {
9
- return Promise.resolve({
10
- id: command.id,
11
- type: 'exec',
12
- success: false,
13
- error: 'Missing "command" in payload',
14
- });
15
- }
16
-
17
- return new Promise((resolve) => {
18
- exec(cmd, { timeout: EXEC_TIMEOUT }, (error, stdout, stderr) => {
19
- resolve({
20
- id: command.id,
21
- type: 'exec',
22
- success: !error,
23
- data: {
24
- exitCode: error ? (error as NodeJS.ErrnoException & { code?: number }).code ?? 1 : 0,
25
- stdout: stdout.toString(),
26
- stderr: stderr.toString(),
27
- },
28
- ...(error ? { error: error.message } : {}),
29
- });
30
- });
31
- });
32
- }
@@ -1,46 +0,0 @@
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
- }
@@ -1,65 +0,0 @@
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 handleFileWrite(command: AgentCommand): Promise<AgentResponse> {
8
- const filePath = command.payload.path as string;
9
- const content = command.payload.content;
10
- const overwrite = (command.payload.overwrite as boolean | undefined) ?? true;
11
-
12
- if (!filePath || content === undefined) {
13
- return {
14
- id: command.id,
15
- type: 'file_write',
16
- success: false,
17
- error: 'Missing "path" or "content" in payload',
18
- };
19
- }
20
-
21
- if (filePath.includes('..') || filePath.startsWith('/')) {
22
- return {
23
- id: command.id,
24
- type: 'file_write',
25
- success: false,
26
- error: 'Invalid path: must not contain ".." or start with "/"',
27
- };
28
- }
29
-
30
- const resolvedPath = path.join(WORKSPACE_DIR, filePath);
31
-
32
- try {
33
- if (!overwrite) {
34
- try {
35
- await fs.access(resolvedPath);
36
- // File exists — skip
37
- return {
38
- id: command.id,
39
- type: 'file_write',
40
- success: true,
41
- data: { path: resolvedPath, written: false, skipped: true },
42
- };
43
- } catch {
44
- // File does not exist — proceed with write
45
- }
46
- }
47
-
48
- await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
49
- await fs.writeFile(resolvedPath, content as string, 'utf-8');
50
-
51
- return {
52
- id: command.id,
53
- type: 'file_write',
54
- success: true,
55
- data: { path: resolvedPath, written: true },
56
- };
57
- } catch (err) {
58
- return {
59
- id: command.id,
60
- type: 'file_write',
61
- success: false,
62
- error: err instanceof Error ? err.message : String(err),
63
- };
64
- }
65
- }
@@ -1,53 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import type { AgentCommand, AgentResponse } from '../types.js';
5
-
6
- function getWorkspaceDir(): string {
7
- const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() ?? path.join(os.homedir(), '.openclaw');
8
- return path.join(stateDir, 'workspace');
9
- }
10
-
11
- export async function handleKnowledgeSync(command: AgentCommand): Promise<AgentResponse> {
12
- const requestedPaths = command.payload.paths as string[];
13
- if (!Array.isArray(requestedPaths)) {
14
- return { id: command.id, type: 'knowledge_sync', success: false, error: 'Missing "paths" in payload' };
15
- }
16
-
17
- const workspaceDir = getWorkspaceDir();
18
- const resolvedPaths: string[] = [];
19
-
20
- for (const p of requestedPaths) {
21
- if (p.endsWith('/')) {
22
- // Directory — scan for .md files
23
- const dir = path.join(workspaceDir, p);
24
- try {
25
- const entries = await fs.readdir(dir);
26
- for (const entry of entries) {
27
- if (entry.endsWith('.md')) resolvedPaths.push(path.join(p, entry));
28
- }
29
- } catch {
30
- // Directory doesn't exist — skip
31
- }
32
- } else {
33
- resolvedPaths.push(p);
34
- }
35
- }
36
-
37
- const files: { path: string; content: string }[] = [];
38
- for (const filePath of resolvedPaths) {
39
- try {
40
- const content = await fs.readFile(path.join(workspaceDir, filePath), 'utf-8');
41
- files.push({ path: filePath, content });
42
- } catch {
43
- // File doesn't exist or unreadable — skip
44
- }
45
- }
46
-
47
- return {
48
- id: command.id,
49
- type: 'knowledge_sync',
50
- success: true,
51
- data: { files },
52
- };
53
- }
@@ -1,19 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentCommand, AgentResponse } from '../types.js';
3
-
4
- export function handleOnboardingComplete(command: AgentCommand): Promise<AgentResponse> {
5
- return new Promise((resolve) => {
6
- exec('openclaw gateway restart', { timeout: 30_000 }, (error, stdout, stderr) => {
7
- resolve({
8
- id: command.id,
9
- type: 'onboarding_complete',
10
- success: !error,
11
- data: {
12
- stdout: stdout.toString(),
13
- stderr: stderr.toString(),
14
- },
15
- ...(error ? { error: error.message } : {}),
16
- });
17
- });
18
- });
19
- }
@@ -1,69 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentCommand, AgentResponse } from '../types.js';
3
-
4
- const INSTALL_TIMEOUT_MS = 300_000; // 5 minutes total
5
-
6
- function execPackage(cmd: string, timeoutMs: number): Promise<void> {
7
- return new Promise((resolve, reject) => {
8
- exec(cmd, { timeout: timeoutMs }, (error) => {
9
- if (error) reject(error);
10
- else resolve();
11
- });
12
- });
13
- }
14
-
15
- export async function handlePackageInstall(command: AgentCommand): Promise<AgentResponse> {
16
- const packages = command.payload.packages as {
17
- apt?: string[];
18
- npm?: string[];
19
- pip?: string[];
20
- } | undefined;
21
-
22
- const apt = packages?.apt ?? [];
23
- const npm = packages?.npm ?? [];
24
- const pip = packages?.pip ?? [];
25
-
26
- if (apt.length === 0 && npm.length === 0 && pip.length === 0) {
27
- return {
28
- id: command.id,
29
- type: 'package_install',
30
- success: true,
31
- data: { installed: { apt: [], npm: [], pip: [] }, errors: [] },
32
- };
33
- }
34
-
35
- const installed: { apt: string[]; npm: string[]; pip: string[] } = { apt: [], npm: [], pip: [] };
36
- const errors: Array<{ name: string; error: string }> = [];
37
- const deadline = Date.now() + INSTALL_TIMEOUT_MS;
38
-
39
- async function tryInstall(pkg: string, cmd: string, type: 'apt' | 'npm' | 'pip'): Promise<void> {
40
- const remaining = deadline - Date.now();
41
- if (remaining <= 0) {
42
- errors.push({ name: pkg, error: 'Install timeout exceeded' });
43
- return;
44
- }
45
- try {
46
- await execPackage(cmd, remaining);
47
- installed[type].push(pkg);
48
- } catch (err) {
49
- errors.push({ name: pkg, error: err instanceof Error ? err.message : String(err) });
50
- }
51
- }
52
-
53
- for (const pkg of apt) {
54
- await tryInstall(pkg, `apt-get install -y ${pkg}`, 'apt');
55
- }
56
- for (const pkg of npm) {
57
- await tryInstall(pkg, `npm install -g ${pkg}`, 'npm');
58
- }
59
- for (const pkg of pip) {
60
- await tryInstall(pkg, `pip install ${pkg}`, 'pip');
61
- }
62
-
63
- return {
64
- id: command.id,
65
- type: 'package_install',
66
- success: errors.length === 0,
67
- data: { installed, errors },
68
- };
69
- }
@@ -1,26 +0,0 @@
1
- import type { AgentCommand, AgentResponse } from '../types.js';
2
-
3
- export function handlePair(command: AgentCommand): Promise<AgentResponse> {
4
- const pairToken = command.payload.token as string;
5
- const targetId = command.payload.targetId as string;
6
-
7
- if (!pairToken || !targetId) {
8
- return Promise.resolve({
9
- id: command.id,
10
- type: 'pair',
11
- success: false,
12
- error: 'Missing "token" or "targetId" in payload',
13
- });
14
- }
15
-
16
- return Promise.resolve({
17
- id: command.id,
18
- type: 'pair',
19
- success: true,
20
- data: {
21
- paired: true,
22
- targetId,
23
- agentId: process.env.AGENT_ID,
24
- },
25
- });
26
- }
@@ -1,19 +0,0 @@
1
- import { exec } from 'node:child_process';
2
- import type { AgentCommand, AgentResponse } from '../types.js';
3
-
4
- export function handleRestart(command: AgentCommand): Promise<AgentResponse> {
5
- return new Promise((resolve) => {
6
- exec('openclaw gateway restart', { timeout: 30_000 }, (error, stdout, stderr) => {
7
- resolve({
8
- id: command.id,
9
- type: 'restart',
10
- success: !error,
11
- data: {
12
- stdout: stdout.toString(),
13
- stderr: stderr.toString(),
14
- },
15
- ...(error ? { error: error.message } : {}),
16
- });
17
- });
18
- });
19
- }
@@ -1,17 +0,0 @@
1
- import type { AgentCommand, AgentResponse } from '../types.js';
2
-
3
- export function handleStop(command: AgentCommand): Promise<AgentResponse> {
4
- const response: AgentResponse = {
5
- id: command.id,
6
- type: 'stop',
7
- success: true,
8
- data: { message: 'Shutting down gracefully' },
9
- };
10
-
11
- setTimeout(() => {
12
- console.log('Agent stopping gracefully...');
13
- process.exit(0);
14
- }, 500);
15
-
16
- return Promise.resolve(response);
17
- }