@projectservan8n/cnapse 0.2.0 → 0.4.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.
- package/dist/Setup-Q32JPHGP.js +174 -0
- package/dist/chunk-COKO6V5J.js +50 -0
- package/dist/index.js +1203 -97
- package/package.json +4 -2
- package/src/agents/coder.ts +62 -0
- package/src/agents/computer.ts +61 -0
- package/src/agents/executor.ts +179 -0
- package/src/agents/filer.ts +56 -0
- package/src/agents/index.ts +12 -0
- package/src/agents/router.ts +160 -0
- package/src/agents/shell.ts +67 -0
- package/src/agents/types.ts +80 -0
- package/src/components/App.tsx +225 -8
- package/src/components/Header.tsx +11 -1
- package/src/components/HelpMenu.tsx +143 -0
- package/src/components/Setup.tsx +203 -0
- package/src/components/TaskProgress.tsx +68 -0
- package/src/index.tsx +14 -3
- package/src/lib/api.ts +2 -2
- package/src/lib/config.ts +21 -0
- package/src/lib/screen.ts +118 -0
- package/src/lib/tasks.ts +268 -0
- package/src/lib/vision.ts +254 -0
- package/src/services/telegram.ts +278 -0
- package/src/tools/clipboard.ts +55 -0
- package/src/tools/computer.ts +454 -0
- package/src/tools/filesystem.ts +272 -0
- package/src/tools/index.ts +35 -0
- package/src/tools/network.ts +204 -0
- package/src/tools/process.ts +194 -0
- package/src/tools/shell.ts +140 -0
- package/src/tools/vision.ts +65 -0
- package/src/types/screenshot-desktop.d.ts +10 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
import { ToolResult, ok, err } from './index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read file contents
|
|
11
|
+
*/
|
|
12
|
+
export async function readFile(path: string): Promise<ToolResult> {
|
|
13
|
+
try {
|
|
14
|
+
const content = await fs.readFile(path, 'utf-8');
|
|
15
|
+
return ok(content);
|
|
16
|
+
} catch (error: any) {
|
|
17
|
+
return err(`Failed to read file: ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Write file contents
|
|
23
|
+
*/
|
|
24
|
+
export async function writeFile(path: string, content: string): Promise<ToolResult> {
|
|
25
|
+
try {
|
|
26
|
+
// Create parent directories if needed
|
|
27
|
+
const dir = dirname(path);
|
|
28
|
+
await fs.mkdir(dir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
await fs.writeFile(path, content, 'utf-8');
|
|
31
|
+
return ok(`Written ${content.length} bytes to ${path}`);
|
|
32
|
+
} catch (error: any) {
|
|
33
|
+
return err(`Failed to write file: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* List directory contents
|
|
39
|
+
*/
|
|
40
|
+
export async function listDir(path: string, recursive = false): Promise<ToolResult> {
|
|
41
|
+
try {
|
|
42
|
+
const stat = await fs.stat(path);
|
|
43
|
+
if (!stat.isDirectory()) {
|
|
44
|
+
return err(`Not a directory: ${path}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const entries: string[] = [];
|
|
48
|
+
|
|
49
|
+
async function walkDir(dir: string, prefix: string): Promise<void> {
|
|
50
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
51
|
+
for (const item of items) {
|
|
52
|
+
const displayPath = prefix ? `${prefix}/${item.name}` : item.name;
|
|
53
|
+
if (item.isDirectory()) {
|
|
54
|
+
entries.push(`${displayPath}/`);
|
|
55
|
+
if (recursive) {
|
|
56
|
+
await walkDir(join(dir, item.name), displayPath);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
entries.push(displayPath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await walkDir(path, '');
|
|
65
|
+
entries.sort();
|
|
66
|
+
return ok(entries.join('\n'));
|
|
67
|
+
} catch (error: any) {
|
|
68
|
+
return err(`Failed to list directory: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Copy file
|
|
74
|
+
*/
|
|
75
|
+
export async function copyFile(src: string, dst: string): Promise<ToolResult> {
|
|
76
|
+
try {
|
|
77
|
+
const srcStat = await fs.stat(src);
|
|
78
|
+
if (!srcStat.isFile()) {
|
|
79
|
+
return err('Source is not a file');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Create parent directories
|
|
83
|
+
const dir = dirname(dst);
|
|
84
|
+
await fs.mkdir(dir, { recursive: true });
|
|
85
|
+
|
|
86
|
+
await fs.copyFile(src, dst);
|
|
87
|
+
return ok(`Copied ${src} to ${dst}`);
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
return err(`Failed to copy: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Move/rename file or directory
|
|
95
|
+
*/
|
|
96
|
+
export async function movePath(src: string, dst: string): Promise<ToolResult> {
|
|
97
|
+
try {
|
|
98
|
+
await fs.rename(src, dst);
|
|
99
|
+
return ok(`Moved ${src} to ${dst}`);
|
|
100
|
+
} catch (error: any) {
|
|
101
|
+
return err(`Failed to move: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Delete file or directory
|
|
107
|
+
*/
|
|
108
|
+
export async function deletePath(path: string, force = false): Promise<ToolResult> {
|
|
109
|
+
try {
|
|
110
|
+
const stat = await fs.stat(path);
|
|
111
|
+
|
|
112
|
+
if (stat.isDirectory()) {
|
|
113
|
+
if (force) {
|
|
114
|
+
await fs.rm(path, { recursive: true, force: true });
|
|
115
|
+
return ok(`Deleted directory: ${path}`);
|
|
116
|
+
} else {
|
|
117
|
+
return err('Use force=true to delete directories');
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
await fs.unlink(path);
|
|
121
|
+
return ok(`Deleted file: ${path}`);
|
|
122
|
+
}
|
|
123
|
+
} catch (error: any) {
|
|
124
|
+
return err(`Failed to delete: ${error.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get file metadata
|
|
130
|
+
*/
|
|
131
|
+
export async function fileInfo(path: string): Promise<ToolResult> {
|
|
132
|
+
try {
|
|
133
|
+
const stat = await fs.stat(path);
|
|
134
|
+
|
|
135
|
+
const fileType = stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : 'other';
|
|
136
|
+
const modified = stat.mtime.toISOString();
|
|
137
|
+
|
|
138
|
+
return ok(
|
|
139
|
+
`Type: ${fileType}\n` +
|
|
140
|
+
`Size: ${stat.size} bytes\n` +
|
|
141
|
+
`Modified: ${modified}\n` +
|
|
142
|
+
`Read-only: ${!(stat.mode & 0o200)}`
|
|
143
|
+
);
|
|
144
|
+
} catch (error: any) {
|
|
145
|
+
return err(`Failed to get file info: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Search for files by pattern
|
|
151
|
+
*/
|
|
152
|
+
export async function findFiles(
|
|
153
|
+
directory: string,
|
|
154
|
+
pattern: string,
|
|
155
|
+
maxResults = 100
|
|
156
|
+
): Promise<ToolResult> {
|
|
157
|
+
try {
|
|
158
|
+
const { globby } = await import('globby');
|
|
159
|
+
const results = await globby(pattern, {
|
|
160
|
+
cwd: directory,
|
|
161
|
+
gitignore: true,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const limited = results.slice(0, maxResults);
|
|
165
|
+
if (results.length > maxResults) {
|
|
166
|
+
return ok(limited.join('\n') + `\n... and ${results.length - maxResults} more`);
|
|
167
|
+
}
|
|
168
|
+
return ok(limited.join('\n') || '(no matches)');
|
|
169
|
+
} catch (error: any) {
|
|
170
|
+
return err(`Failed to search: ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* File system tool definitions for agents
|
|
176
|
+
*/
|
|
177
|
+
export const filesystemTools = [
|
|
178
|
+
{
|
|
179
|
+
name: 'read_file',
|
|
180
|
+
description: 'Read file contents',
|
|
181
|
+
parameters: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
path: { type: 'string', description: 'File path' },
|
|
185
|
+
},
|
|
186
|
+
required: ['path'],
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'write_file',
|
|
191
|
+
description: 'Write content to file',
|
|
192
|
+
parameters: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
properties: {
|
|
195
|
+
path: { type: 'string', description: 'File path' },
|
|
196
|
+
content: { type: 'string', description: 'Content to write' },
|
|
197
|
+
},
|
|
198
|
+
required: ['path', 'content'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'list_dir',
|
|
203
|
+
description: 'List directory contents',
|
|
204
|
+
parameters: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
properties: {
|
|
207
|
+
path: { type: 'string', description: 'Directory path' },
|
|
208
|
+
recursive: { type: 'boolean', description: 'Include subdirectories' },
|
|
209
|
+
},
|
|
210
|
+
required: ['path'],
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'copy_file',
|
|
215
|
+
description: 'Copy file',
|
|
216
|
+
parameters: {
|
|
217
|
+
type: 'object',
|
|
218
|
+
properties: {
|
|
219
|
+
src: { type: 'string', description: 'Source path' },
|
|
220
|
+
dst: { type: 'string', description: 'Destination path' },
|
|
221
|
+
},
|
|
222
|
+
required: ['src', 'dst'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'move_path',
|
|
227
|
+
description: 'Move or rename file/directory',
|
|
228
|
+
parameters: {
|
|
229
|
+
type: 'object',
|
|
230
|
+
properties: {
|
|
231
|
+
src: { type: 'string', description: 'Source path' },
|
|
232
|
+
dst: { type: 'string', description: 'Destination path' },
|
|
233
|
+
},
|
|
234
|
+
required: ['src', 'dst'],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: 'delete_path',
|
|
239
|
+
description: 'Delete file or directory',
|
|
240
|
+
parameters: {
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties: {
|
|
243
|
+
path: { type: 'string', description: 'Path to delete' },
|
|
244
|
+
force: { type: 'boolean', description: 'Force delete directories' },
|
|
245
|
+
},
|
|
246
|
+
required: ['path'],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'file_info',
|
|
251
|
+
description: 'Get file metadata',
|
|
252
|
+
parameters: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
path: { type: 'string', description: 'File path' },
|
|
256
|
+
},
|
|
257
|
+
required: ['path'],
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'find_files',
|
|
262
|
+
description: 'Search for files by glob pattern',
|
|
263
|
+
parameters: {
|
|
264
|
+
type: 'object',
|
|
265
|
+
properties: {
|
|
266
|
+
directory: { type: 'string', description: 'Search directory' },
|
|
267
|
+
pattern: { type: 'string', description: 'Glob pattern (e.g., "**/*.ts")' },
|
|
268
|
+
},
|
|
269
|
+
required: ['directory', 'pattern'],
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool implementations for C-napse agents
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './shell.js';
|
|
6
|
+
export * from './filesystem.js';
|
|
7
|
+
export * from './clipboard.js';
|
|
8
|
+
export * from './network.js';
|
|
9
|
+
export * from './process.js';
|
|
10
|
+
export * from './computer.js';
|
|
11
|
+
export * from './vision.js';
|
|
12
|
+
|
|
13
|
+
export interface ToolResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
output: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ok(output: string): ToolResult {
|
|
20
|
+
return { success: true, output };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function err(error: string): ToolResult {
|
|
24
|
+
return { success: false, output: '', error };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ToolDefinition {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
parameters: {
|
|
31
|
+
type: string;
|
|
32
|
+
properties: Record<string, unknown>;
|
|
33
|
+
required?: string[];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createServer, Socket } from 'net';
|
|
6
|
+
import { networkInterfaces } from 'os';
|
|
7
|
+
import { ToolResult, ok, err } from './index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if a port is in use
|
|
11
|
+
*/
|
|
12
|
+
export function checkPort(port: number): Promise<ToolResult> {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const server = createServer();
|
|
15
|
+
|
|
16
|
+
server.once('error', () => {
|
|
17
|
+
resolve(ok(`Port ${port} is in use`));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
server.once('listening', () => {
|
|
21
|
+
server.close();
|
|
22
|
+
resolve(ok(`Port ${port} is available`));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
server.listen(port, '127.0.0.1');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Find available port in range
|
|
31
|
+
*/
|
|
32
|
+
export async function findAvailablePort(start: number, end: number): Promise<ToolResult> {
|
|
33
|
+
for (let port = start; port <= end; port++) {
|
|
34
|
+
const result = await checkPort(port);
|
|
35
|
+
if (result.output.includes('available')) {
|
|
36
|
+
return ok(port.toString());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return err(`No available ports in range ${start}-${end}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if a host:port is reachable
|
|
44
|
+
*/
|
|
45
|
+
export function checkConnection(
|
|
46
|
+
host: string,
|
|
47
|
+
port: number,
|
|
48
|
+
timeoutMs = 5000
|
|
49
|
+
): Promise<ToolResult> {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const socket = new Socket();
|
|
52
|
+
let resolved = false;
|
|
53
|
+
|
|
54
|
+
const cleanup = () => {
|
|
55
|
+
if (!resolved) {
|
|
56
|
+
resolved = true;
|
|
57
|
+
socket.destroy();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
socket.setTimeout(timeoutMs);
|
|
62
|
+
|
|
63
|
+
socket.on('connect', () => {
|
|
64
|
+
cleanup();
|
|
65
|
+
resolve(ok(`Connection to ${host}:${port} successful`));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
socket.on('timeout', () => {
|
|
69
|
+
cleanup();
|
|
70
|
+
resolve(err(`Connection to ${host}:${port} timed out`));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
socket.on('error', (error: any) => {
|
|
74
|
+
cleanup();
|
|
75
|
+
resolve(err(`Connection to ${host}:${port} failed: ${error.message}`));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
socket.connect(port, host);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get local IP addresses
|
|
84
|
+
*/
|
|
85
|
+
export function getLocalIp(): ToolResult {
|
|
86
|
+
const interfaces = networkInterfaces();
|
|
87
|
+
const addresses: string[] = [];
|
|
88
|
+
|
|
89
|
+
for (const [name, nets] of Object.entries(interfaces)) {
|
|
90
|
+
if (!nets) continue;
|
|
91
|
+
for (const net of nets) {
|
|
92
|
+
if (!net.internal && net.family === 'IPv4') {
|
|
93
|
+
addresses.push(`${name}: ${net.address}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (addresses.length === 0) {
|
|
99
|
+
return err('No external IP addresses found');
|
|
100
|
+
}
|
|
101
|
+
return ok(addresses.join('\n'));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* List all network interfaces
|
|
106
|
+
*/
|
|
107
|
+
export function listInterfaces(): ToolResult {
|
|
108
|
+
const interfaces = networkInterfaces();
|
|
109
|
+
const info: string[] = [];
|
|
110
|
+
|
|
111
|
+
for (const [name, nets] of Object.entries(interfaces)) {
|
|
112
|
+
if (!nets) continue;
|
|
113
|
+
for (const net of nets) {
|
|
114
|
+
info.push(`${name}: ${net.address} (${net.family}${net.internal ? ', internal' : ''})`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return ok(info.join('\n'));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fetch URL content
|
|
123
|
+
*/
|
|
124
|
+
export async function fetchUrl(url: string): Promise<ToolResult> {
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(url);
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
return err(`HTTP ${response.status}: ${response.statusText}`);
|
|
129
|
+
}
|
|
130
|
+
const text = await response.text();
|
|
131
|
+
return ok(text);
|
|
132
|
+
} catch (error: any) {
|
|
133
|
+
return err(`Failed to fetch: ${error.message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Network tool definitions for agents
|
|
139
|
+
*/
|
|
140
|
+
export const networkTools = [
|
|
141
|
+
{
|
|
142
|
+
name: 'check_port',
|
|
143
|
+
description: 'Check if a port is in use',
|
|
144
|
+
parameters: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
port: { type: 'number', description: 'Port number' },
|
|
148
|
+
},
|
|
149
|
+
required: ['port'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'find_available_port',
|
|
154
|
+
description: 'Find available port in range',
|
|
155
|
+
parameters: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
start: { type: 'number', description: 'Start port' },
|
|
159
|
+
end: { type: 'number', description: 'End port' },
|
|
160
|
+
},
|
|
161
|
+
required: ['start', 'end'],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'check_connection',
|
|
166
|
+
description: 'Check if host:port is reachable',
|
|
167
|
+
parameters: {
|
|
168
|
+
type: 'object',
|
|
169
|
+
properties: {
|
|
170
|
+
host: { type: 'string', description: 'Hostname' },
|
|
171
|
+
port: { type: 'number', description: 'Port number' },
|
|
172
|
+
timeout: { type: 'number', description: 'Timeout in ms' },
|
|
173
|
+
},
|
|
174
|
+
required: ['host', 'port'],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'get_local_ip',
|
|
179
|
+
description: 'Get local IP addresses',
|
|
180
|
+
parameters: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'list_interfaces',
|
|
187
|
+
description: 'List network interfaces',
|
|
188
|
+
parameters: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'fetch_url',
|
|
195
|
+
description: 'Fetch content from URL',
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
url: { type: 'string', description: 'URL to fetch' },
|
|
200
|
+
},
|
|
201
|
+
required: ['url'],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process management tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import { platform, cpus, totalmem, freemem, hostname, version } from 'os';
|
|
8
|
+
import { ToolResult, ok, err } from './index.js';
|
|
9
|
+
|
|
10
|
+
const execAsync = promisify(exec);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* List running processes
|
|
14
|
+
*/
|
|
15
|
+
export async function listProcesses(): Promise<ToolResult> {
|
|
16
|
+
try {
|
|
17
|
+
const isWindows = process.platform === 'win32';
|
|
18
|
+
|
|
19
|
+
if (isWindows) {
|
|
20
|
+
const { stdout } = await execAsync(
|
|
21
|
+
'tasklist /FO CSV /NH',
|
|
22
|
+
{ maxBuffer: 10 * 1024 * 1024 }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const lines = stdout.trim().split('\n').slice(0, 50);
|
|
26
|
+
const header = 'Name\tPID\tMemory';
|
|
27
|
+
const processes = lines.map(line => {
|
|
28
|
+
const parts = line.split('","').map(p => p.replace(/"/g, ''));
|
|
29
|
+
return `${parts[0]}\t${parts[1]}\t${parts[4]}`;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return ok(`${header}\n${processes.join('\n')}`);
|
|
33
|
+
} else {
|
|
34
|
+
const { stdout } = await execAsync(
|
|
35
|
+
'ps aux --sort=-%mem | head -50',
|
|
36
|
+
{ maxBuffer: 10 * 1024 * 1024 }
|
|
37
|
+
);
|
|
38
|
+
return ok(stdout);
|
|
39
|
+
}
|
|
40
|
+
} catch (error: any) {
|
|
41
|
+
return err(`Failed to list processes: ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get information about a specific process
|
|
47
|
+
*/
|
|
48
|
+
export async function processInfo(pid: number): Promise<ToolResult> {
|
|
49
|
+
try {
|
|
50
|
+
const isWindows = process.platform === 'win32';
|
|
51
|
+
|
|
52
|
+
if (isWindows) {
|
|
53
|
+
const { stdout } = await execAsync(
|
|
54
|
+
`tasklist /FI "PID eq ${pid}" /FO LIST`
|
|
55
|
+
);
|
|
56
|
+
return ok(stdout || `No process found with PID ${pid}`);
|
|
57
|
+
} else {
|
|
58
|
+
const { stdout } = await execAsync(
|
|
59
|
+
`ps -p ${pid} -o pid,ppid,user,%cpu,%mem,stat,start,command`
|
|
60
|
+
);
|
|
61
|
+
return ok(stdout || `No process found with PID ${pid}`);
|
|
62
|
+
}
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
return err(`Failed to get process info: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Kill a process
|
|
70
|
+
*/
|
|
71
|
+
export async function killProcess(pid: number, force = false): Promise<ToolResult> {
|
|
72
|
+
try {
|
|
73
|
+
const isWindows = process.platform === 'win32';
|
|
74
|
+
|
|
75
|
+
if (isWindows) {
|
|
76
|
+
const flag = force ? '/F' : '';
|
|
77
|
+
await execAsync(`taskkill /PID ${pid} ${flag}`);
|
|
78
|
+
} else {
|
|
79
|
+
const signal = force ? '-9' : '-15';
|
|
80
|
+
await execAsync(`kill ${signal} ${pid}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return ok(`Killed process: ${pid}`);
|
|
84
|
+
} catch (error: any) {
|
|
85
|
+
return err(`Failed to kill process: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Find processes by name
|
|
91
|
+
*/
|
|
92
|
+
export async function findProcess(name: string): Promise<ToolResult> {
|
|
93
|
+
try {
|
|
94
|
+
const isWindows = process.platform === 'win32';
|
|
95
|
+
|
|
96
|
+
if (isWindows) {
|
|
97
|
+
const { stdout } = await execAsync(
|
|
98
|
+
`tasklist /FI "IMAGENAME eq *${name}*" /FO CSV /NH`
|
|
99
|
+
);
|
|
100
|
+
if (!stdout.trim() || stdout.includes('No tasks')) {
|
|
101
|
+
return err(`No processes found matching: ${name}`);
|
|
102
|
+
}
|
|
103
|
+
return ok(stdout);
|
|
104
|
+
} else {
|
|
105
|
+
const { stdout } = await execAsync(
|
|
106
|
+
`pgrep -l -i "${name}" || echo "No processes found"`
|
|
107
|
+
);
|
|
108
|
+
if (stdout.includes('No processes')) {
|
|
109
|
+
return err(`No processes found matching: ${name}`);
|
|
110
|
+
}
|
|
111
|
+
return ok(stdout);
|
|
112
|
+
}
|
|
113
|
+
} catch (error: any) {
|
|
114
|
+
return err(`No processes found matching: ${name}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get system information
|
|
120
|
+
*/
|
|
121
|
+
export function systemInfo(): ToolResult {
|
|
122
|
+
const totalMemGB = (totalmem() / 1024 / 1024 / 1024).toFixed(1);
|
|
123
|
+
const freeMemGB = (freemem() / 1024 / 1024 / 1024).toFixed(1);
|
|
124
|
+
const usedMemGB = ((totalmem() - freemem()) / 1024 / 1024 / 1024).toFixed(1);
|
|
125
|
+
|
|
126
|
+
const info = [
|
|
127
|
+
`OS: ${platform()} ${version()}`,
|
|
128
|
+
`Hostname: ${hostname()}`,
|
|
129
|
+
`CPUs: ${cpus().length}`,
|
|
130
|
+
`CPU Model: ${cpus()[0]?.model || 'Unknown'}`,
|
|
131
|
+
`Total Memory: ${totalMemGB} GB`,
|
|
132
|
+
`Used Memory: ${usedMemGB} GB`,
|
|
133
|
+
`Free Memory: ${freeMemGB} GB`,
|
|
134
|
+
`Node Version: ${process.version}`,
|
|
135
|
+
].join('\n');
|
|
136
|
+
|
|
137
|
+
return ok(info);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Process management tool definitions for agents
|
|
142
|
+
*/
|
|
143
|
+
export const processTools = [
|
|
144
|
+
{
|
|
145
|
+
name: 'list_processes',
|
|
146
|
+
description: 'List running processes',
|
|
147
|
+
parameters: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'process_info',
|
|
154
|
+
description: 'Get info about specific process',
|
|
155
|
+
parameters: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
pid: { type: 'number', description: 'Process ID' },
|
|
159
|
+
},
|
|
160
|
+
required: ['pid'],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'kill_process',
|
|
165
|
+
description: 'Kill a process',
|
|
166
|
+
parameters: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
pid: { type: 'number', description: 'Process ID' },
|
|
170
|
+
force: { type: 'boolean', description: 'Force kill' },
|
|
171
|
+
},
|
|
172
|
+
required: ['pid'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'find_process',
|
|
177
|
+
description: 'Find processes by name',
|
|
178
|
+
parameters: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
properties: {
|
|
181
|
+
name: { type: 'string', description: 'Process name' },
|
|
182
|
+
},
|
|
183
|
+
required: ['name'],
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'system_info',
|
|
188
|
+
description: 'Get system information',
|
|
189
|
+
parameters: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
];
|