@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.
@@ -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
+ ];