@openclaw-cloud/agent-controller 0.2.6 → 0.2.8

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 (83) hide show
  1. package/bin/agent-controller.js +6 -0
  2. package/dist/commands/backup-cli.d.ts +1 -0
  3. package/dist/commands/backup-cli.js +65 -0
  4. package/dist/commands/backup-cli.js.map +1 -0
  5. package/dist/commands/install.js +3 -0
  6. package/dist/commands/install.js.map +1 -1
  7. package/dist/commands/knowledge-sync-cli.d.ts +1 -0
  8. package/dist/commands/knowledge-sync-cli.js +61 -0
  9. package/dist/commands/knowledge-sync-cli.js.map +1 -0
  10. package/dist/config-file.d.ts +9 -0
  11. package/dist/config-file.js +47 -0
  12. package/dist/config-file.js.map +1 -0
  13. package/dist/connection.d.ts +1 -0
  14. package/dist/connection.js +27 -13
  15. package/dist/connection.js.map +1 -1
  16. package/dist/handlers/backup.js +7 -2
  17. package/dist/handlers/backup.js.map +1 -1
  18. package/dist/handlers/knowledge-sync.d.ts +2 -0
  19. package/dist/handlers/knowledge-sync.js +51 -0
  20. package/dist/handlers/knowledge-sync.js.map +1 -0
  21. package/dist/heartbeat.d.ts +1 -0
  22. package/dist/heartbeat.js +30 -0
  23. package/dist/heartbeat.js.map +1 -1
  24. package/dist/index.js +7 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/types.d.ts +2 -1
  27. package/package.json +6 -1
  28. package/.claude/cc-notify.sh +0 -32
  29. package/.claude/settings.json +0 -31
  30. package/.husky/pre-commit +0 -1
  31. package/BIZPLAN.md +0 -530
  32. package/CLAUDE.md +0 -172
  33. package/Dockerfile +0 -9
  34. package/__tests__/api.test.ts +0 -183
  35. package/__tests__/backup.test.ts +0 -145
  36. package/__tests__/board-handler.test.ts +0 -323
  37. package/__tests__/chat.test.ts +0 -191
  38. package/__tests__/config.test.ts +0 -100
  39. package/__tests__/connection.test.ts +0 -289
  40. package/__tests__/file-delete.test.ts +0 -90
  41. package/__tests__/file-write.test.ts +0 -119
  42. package/__tests__/gateway-adapter.test.ts +0 -366
  43. package/__tests__/gateway-client.test.ts +0 -272
  44. package/__tests__/handlers.test.ts +0 -150
  45. package/__tests__/heartbeat.test.ts +0 -124
  46. package/__tests__/onboarding.test.ts +0 -55
  47. package/__tests__/package-install.test.ts +0 -109
  48. package/__tests__/pair.test.ts +0 -60
  49. package/__tests__/self-update.test.ts +0 -123
  50. package/__tests__/stop.test.ts +0 -38
  51. package/jest.config.ts +0 -16
  52. package/src/api.ts +0 -62
  53. package/src/commands/install.ts +0 -68
  54. package/src/commands/self-update.ts +0 -43
  55. package/src/commands/uninstall.ts +0 -19
  56. package/src/config-file.ts +0 -56
  57. package/src/connection.ts +0 -203
  58. package/src/debug.ts +0 -11
  59. package/src/handlers/backup.ts +0 -101
  60. package/src/handlers/board-handler.ts +0 -155
  61. package/src/handlers/chat.ts +0 -79
  62. package/src/handlers/config.ts +0 -48
  63. package/src/handlers/deploy.ts +0 -32
  64. package/src/handlers/exec.ts +0 -32
  65. package/src/handlers/file-delete.ts +0 -46
  66. package/src/handlers/file-write.ts +0 -65
  67. package/src/handlers/knowledge-sync.ts +0 -53
  68. package/src/handlers/onboarding.ts +0 -19
  69. package/src/handlers/package-install.ts +0 -69
  70. package/src/handlers/pair.ts +0 -26
  71. package/src/handlers/restart.ts +0 -19
  72. package/src/handlers/stop.ts +0 -17
  73. package/src/heartbeat.ts +0 -110
  74. package/src/index.ts +0 -97
  75. package/src/openclaw/gateway-adapter.ts +0 -129
  76. package/src/openclaw/gateway-client.ts +0 -131
  77. package/src/openclaw/index.ts +0 -17
  78. package/src/openclaw/types.ts +0 -41
  79. package/src/platform/linux.ts +0 -108
  80. package/src/platform/macos.ts +0 -122
  81. package/src/platform/windows.ts +0 -92
  82. package/src/types.ts +0 -94
  83. 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
- }