codebot-ai 1.3.0 → 1.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/README.md +16 -1
- package/dist/cli.js +1 -1
- package/dist/tools/code-analysis.d.ts +33 -0
- package/dist/tools/code-analysis.js +232 -0
- package/dist/tools/code-review.d.ts +32 -0
- package/dist/tools/code-review.js +228 -0
- package/dist/tools/database.d.ts +35 -0
- package/dist/tools/database.js +129 -0
- package/dist/tools/diff-viewer.d.ts +39 -0
- package/dist/tools/diff-viewer.js +145 -0
- package/dist/tools/docker.d.ts +26 -0
- package/dist/tools/docker.js +101 -0
- package/dist/tools/git.d.ts +26 -0
- package/dist/tools/git.js +58 -0
- package/dist/tools/http-client.d.ts +39 -0
- package/dist/tools/http-client.js +114 -0
- package/dist/tools/image-info.d.ts +23 -0
- package/dist/tools/image-info.js +170 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/multi-search.d.ts +28 -0
- package/dist/tools/multi-search.js +153 -0
- package/dist/tools/notification.d.ts +38 -0
- package/dist/tools/notification.js +96 -0
- package/dist/tools/package-manager.d.ts +31 -0
- package/dist/tools/package-manager.js +161 -0
- package/dist/tools/pdf-extract.d.ts +33 -0
- package/dist/tools/pdf-extract.js +178 -0
- package/dist/tools/ssh-remote.d.ts +39 -0
- package/dist/tools/ssh-remote.js +84 -0
- package/dist/tools/task-planner.d.ts +42 -0
- package/dist/tools/task-planner.js +161 -0
- package/dist/tools/test-runner.d.ts +36 -0
- package/dist/tools/test-runner.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DatabaseTool = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const BLOCKED_SQL = [
|
|
40
|
+
/\bDROP\s+(TABLE|DATABASE|INDEX|VIEW)\b/i,
|
|
41
|
+
/\bDELETE\s+FROM\b/i,
|
|
42
|
+
/\bTRUNCATE\b/i,
|
|
43
|
+
/\bALTER\s+TABLE\s+.*\bDROP\b/i,
|
|
44
|
+
];
|
|
45
|
+
class DatabaseTool {
|
|
46
|
+
name = 'database';
|
|
47
|
+
description = 'Query SQLite databases. Actions: query, tables, schema, info. Blocks DROP/DELETE/TRUNCATE for safety.';
|
|
48
|
+
permission = 'prompt';
|
|
49
|
+
parameters = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
action: { type: 'string', description: 'Action: query, tables, schema, info' },
|
|
53
|
+
db: { type: 'string', description: 'Path to SQLite database file' },
|
|
54
|
+
sql: { type: 'string', description: 'SQL query to execute (for "query" action)' },
|
|
55
|
+
table: { type: 'string', description: 'Table name (for "schema" action)' },
|
|
56
|
+
},
|
|
57
|
+
required: ['action', 'db'],
|
|
58
|
+
};
|
|
59
|
+
async execute(args) {
|
|
60
|
+
const action = args.action;
|
|
61
|
+
const dbPath = args.db;
|
|
62
|
+
if (!action)
|
|
63
|
+
return 'Error: action is required';
|
|
64
|
+
if (!dbPath)
|
|
65
|
+
return 'Error: db path is required';
|
|
66
|
+
if (!fs.existsSync(dbPath))
|
|
67
|
+
return `Error: database not found: ${dbPath}`;
|
|
68
|
+
switch (action) {
|
|
69
|
+
case 'query': return this.runQuery(dbPath, args);
|
|
70
|
+
case 'tables': return this.listTables(dbPath);
|
|
71
|
+
case 'schema': return this.showSchema(dbPath, args);
|
|
72
|
+
case 'info': return this.dbInfo(dbPath);
|
|
73
|
+
default: return `Error: unknown action "${action}". Use: query, tables, schema, info`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
runQuery(dbPath, args) {
|
|
77
|
+
const sql = args.sql;
|
|
78
|
+
if (!sql)
|
|
79
|
+
return 'Error: sql is required for query';
|
|
80
|
+
// Block destructive queries
|
|
81
|
+
for (const pattern of BLOCKED_SQL) {
|
|
82
|
+
if (pattern.test(sql)) {
|
|
83
|
+
return `Error: destructive SQL blocked for safety. Pattern matched: ${pattern.source}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return this.sqlite(dbPath, sql);
|
|
87
|
+
}
|
|
88
|
+
listTables(dbPath) {
|
|
89
|
+
return this.sqlite(dbPath, ".tables");
|
|
90
|
+
}
|
|
91
|
+
showSchema(dbPath, args) {
|
|
92
|
+
const table = args.table;
|
|
93
|
+
if (table) {
|
|
94
|
+
// Sanitize table name
|
|
95
|
+
if (!/^[a-zA-Z_]\w*$/.test(table))
|
|
96
|
+
return 'Error: invalid table name';
|
|
97
|
+
return this.sqlite(dbPath, `.schema ${table}`);
|
|
98
|
+
}
|
|
99
|
+
return this.sqlite(dbPath, ".schema");
|
|
100
|
+
}
|
|
101
|
+
dbInfo(dbPath) {
|
|
102
|
+
const stat = fs.statSync(dbPath);
|
|
103
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(2);
|
|
104
|
+
const tables = this.sqlite(dbPath, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
|
|
105
|
+
return `Database: ${dbPath}\nSize: ${sizeMB} MB\nModified: ${stat.mtime.toISOString()}\n\nTables:\n${tables}`;
|
|
106
|
+
}
|
|
107
|
+
sqlite(dbPath, command) {
|
|
108
|
+
try {
|
|
109
|
+
// Try sqlite3 CLI first
|
|
110
|
+
const output = (0, child_process_1.execSync)(`sqlite3 "${dbPath}" "${command.replace(/"/g, '\\"')}"`, {
|
|
111
|
+
timeout: 15_000,
|
|
112
|
+
maxBuffer: 1024 * 1024,
|
|
113
|
+
encoding: 'utf-8',
|
|
114
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
115
|
+
});
|
|
116
|
+
return output.trim() || '(no results)';
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const e = err;
|
|
120
|
+
const msg = (e.stderr || '').trim();
|
|
121
|
+
if (msg.includes('not found')) {
|
|
122
|
+
return 'Error: sqlite3 is not installed. Install it with: brew install sqlite (macOS) or apt install sqlite3 (Linux)';
|
|
123
|
+
}
|
|
124
|
+
return `Error: ${msg || 'query failed'}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.DatabaseTool = DatabaseTool;
|
|
129
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class DiffViewerTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
file_a: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
file_b: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
path: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
ref: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
required: string[];
|
|
31
|
+
};
|
|
32
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
33
|
+
private diffFiles;
|
|
34
|
+
private gitDiff;
|
|
35
|
+
private gitStaged;
|
|
36
|
+
private gitCommitDiff;
|
|
37
|
+
private runGit;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=diff-viewer.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DiffViewerTool = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
class DiffViewerTool {
|
|
40
|
+
name = 'diff_viewer';
|
|
41
|
+
description = 'View diffs. Actions: files (compare two files), git_diff (working tree changes), staged (staged changes), commit (show a commit diff).';
|
|
42
|
+
permission = 'auto';
|
|
43
|
+
parameters = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
action: { type: 'string', description: 'Action: files, git_diff, staged, commit' },
|
|
47
|
+
file_a: { type: 'string', description: 'First file path (for "files" action)' },
|
|
48
|
+
file_b: { type: 'string', description: 'Second file path (for "files" action)' },
|
|
49
|
+
path: { type: 'string', description: 'File or directory to diff (for git_diff/staged)' },
|
|
50
|
+
ref: { type: 'string', description: 'Commit hash or ref (for "commit" action)' },
|
|
51
|
+
},
|
|
52
|
+
required: ['action'],
|
|
53
|
+
};
|
|
54
|
+
async execute(args) {
|
|
55
|
+
const action = args.action;
|
|
56
|
+
if (!action)
|
|
57
|
+
return 'Error: action is required';
|
|
58
|
+
switch (action) {
|
|
59
|
+
case 'files': return this.diffFiles(args);
|
|
60
|
+
case 'git_diff': return this.gitDiff(args);
|
|
61
|
+
case 'staged': return this.gitStaged(args);
|
|
62
|
+
case 'commit': return this.gitCommitDiff(args);
|
|
63
|
+
default: return `Error: unknown action "${action}". Use: files, git_diff, staged, commit`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
diffFiles(args) {
|
|
67
|
+
const fileA = args.file_a;
|
|
68
|
+
const fileB = args.file_b;
|
|
69
|
+
if (!fileA || !fileB)
|
|
70
|
+
return 'Error: file_a and file_b are required';
|
|
71
|
+
let contentA, contentB;
|
|
72
|
+
try {
|
|
73
|
+
contentA = fs.readFileSync(fileA, 'utf-8');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return `Error: cannot read ${fileA}`;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
contentB = fs.readFileSync(fileB, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return `Error: cannot read ${fileB}`;
|
|
83
|
+
}
|
|
84
|
+
const linesA = contentA.split('\n');
|
|
85
|
+
const linesB = contentB.split('\n');
|
|
86
|
+
const diff = [`--- ${fileA}`, `+++ ${fileB}`];
|
|
87
|
+
// Simple line-by-line diff
|
|
88
|
+
const maxLen = Math.max(linesA.length, linesB.length);
|
|
89
|
+
let changes = 0;
|
|
90
|
+
for (let i = 0; i < maxLen; i++) {
|
|
91
|
+
const a = linesA[i];
|
|
92
|
+
const b = linesB[i];
|
|
93
|
+
if (a === undefined && b !== undefined) {
|
|
94
|
+
diff.push(`+${i + 1}: ${b}`);
|
|
95
|
+
changes++;
|
|
96
|
+
}
|
|
97
|
+
else if (b === undefined && a !== undefined) {
|
|
98
|
+
diff.push(`-${i + 1}: ${a}`);
|
|
99
|
+
changes++;
|
|
100
|
+
}
|
|
101
|
+
else if (a !== b) {
|
|
102
|
+
diff.push(`-${i + 1}: ${a}`);
|
|
103
|
+
diff.push(`+${i + 1}: ${b}`);
|
|
104
|
+
changes++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (changes === 0)
|
|
108
|
+
return 'Files are identical.';
|
|
109
|
+
return `${changes} line(s) differ:\n${diff.join('\n')}`;
|
|
110
|
+
}
|
|
111
|
+
gitDiff(args) {
|
|
112
|
+
const target = args.path || '';
|
|
113
|
+
return this.runGit(`diff ${target}`.trim());
|
|
114
|
+
}
|
|
115
|
+
gitStaged(args) {
|
|
116
|
+
const target = args.path || '';
|
|
117
|
+
return this.runGit(`diff --staged ${target}`.trim());
|
|
118
|
+
}
|
|
119
|
+
gitCommitDiff(args) {
|
|
120
|
+
const ref = args.ref;
|
|
121
|
+
if (!ref)
|
|
122
|
+
return 'Error: ref (commit hash) is required';
|
|
123
|
+
// Sanitize ref
|
|
124
|
+
if (!/^[a-zA-Z0-9_\-./~^]+$/.test(ref))
|
|
125
|
+
return 'Error: invalid ref format';
|
|
126
|
+
return this.runGit(`show --stat --patch ${ref}`);
|
|
127
|
+
}
|
|
128
|
+
runGit(cmd) {
|
|
129
|
+
try {
|
|
130
|
+
const output = (0, child_process_1.execSync)(`git ${cmd}`, {
|
|
131
|
+
timeout: 15_000,
|
|
132
|
+
maxBuffer: 1024 * 1024,
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
135
|
+
});
|
|
136
|
+
return output.trim() || 'No changes.';
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
const e = err;
|
|
140
|
+
return `Error: ${(e.stderr || 'git command failed').trim()}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.DiffViewerTool = DiffViewerTool;
|
|
145
|
+
//# sourceMappingURL=diff-viewer.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class DockerTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
args: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
cwd: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=docker.d.ts.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DockerTool = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const ALLOWED_ACTIONS = [
|
|
6
|
+
'ps', 'images', 'run', 'stop', 'rm', 'build', 'logs', 'exec',
|
|
7
|
+
'compose_up', 'compose_down', 'compose_ps', 'inspect', 'pull',
|
|
8
|
+
];
|
|
9
|
+
class DockerTool {
|
|
10
|
+
name = 'docker';
|
|
11
|
+
description = 'Run Docker operations. Actions: ps, images, run, stop, rm, build, logs, exec, compose_up, compose_down, compose_ps, inspect, pull.';
|
|
12
|
+
permission = 'prompt';
|
|
13
|
+
parameters = {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
action: { type: 'string', description: 'Docker action to perform' },
|
|
17
|
+
args: { type: 'string', description: 'Additional arguments (image name, container ID, etc.)' },
|
|
18
|
+
cwd: { type: 'string', description: 'Working directory for compose commands' },
|
|
19
|
+
},
|
|
20
|
+
required: ['action'],
|
|
21
|
+
};
|
|
22
|
+
async execute(args) {
|
|
23
|
+
const action = args.action;
|
|
24
|
+
if (!action)
|
|
25
|
+
return 'Error: action is required';
|
|
26
|
+
if (!ALLOWED_ACTIONS.includes(action)) {
|
|
27
|
+
return `Error: unknown action "${action}". Allowed: ${ALLOWED_ACTIONS.join(', ')}`;
|
|
28
|
+
}
|
|
29
|
+
const extra = args.args || '';
|
|
30
|
+
const cwd = args.cwd || process.cwd();
|
|
31
|
+
// Build the command
|
|
32
|
+
let cmd;
|
|
33
|
+
switch (action) {
|
|
34
|
+
case 'ps':
|
|
35
|
+
cmd = `docker ps ${extra}`;
|
|
36
|
+
break;
|
|
37
|
+
case 'images':
|
|
38
|
+
cmd = `docker images ${extra}`;
|
|
39
|
+
break;
|
|
40
|
+
case 'run':
|
|
41
|
+
cmd = `docker run ${extra}`;
|
|
42
|
+
break;
|
|
43
|
+
case 'stop':
|
|
44
|
+
cmd = `docker stop ${extra}`;
|
|
45
|
+
break;
|
|
46
|
+
case 'rm':
|
|
47
|
+
cmd = `docker rm ${extra}`;
|
|
48
|
+
break;
|
|
49
|
+
case 'build':
|
|
50
|
+
cmd = `docker build ${extra}`;
|
|
51
|
+
break;
|
|
52
|
+
case 'logs':
|
|
53
|
+
cmd = `docker logs --tail 100 ${extra}`;
|
|
54
|
+
break;
|
|
55
|
+
case 'exec':
|
|
56
|
+
cmd = `docker exec ${extra}`;
|
|
57
|
+
break;
|
|
58
|
+
case 'inspect':
|
|
59
|
+
cmd = `docker inspect ${extra}`;
|
|
60
|
+
break;
|
|
61
|
+
case 'pull':
|
|
62
|
+
cmd = `docker pull ${extra}`;
|
|
63
|
+
break;
|
|
64
|
+
case 'compose_up':
|
|
65
|
+
cmd = `docker compose up -d ${extra}`;
|
|
66
|
+
break;
|
|
67
|
+
case 'compose_down':
|
|
68
|
+
cmd = `docker compose down ${extra}`;
|
|
69
|
+
break;
|
|
70
|
+
case 'compose_ps':
|
|
71
|
+
cmd = `docker compose ps ${extra}`;
|
|
72
|
+
break;
|
|
73
|
+
default: return `Error: unhandled action "${action}"`;
|
|
74
|
+
}
|
|
75
|
+
// Safety: block --privileged and dangerous volume mounts
|
|
76
|
+
if (/--privileged/.test(cmd))
|
|
77
|
+
return 'Error: --privileged flag is blocked for safety.';
|
|
78
|
+
if (/-v\s+\/:\//i.test(cmd))
|
|
79
|
+
return 'Error: mounting root filesystem is blocked for safety.';
|
|
80
|
+
try {
|
|
81
|
+
const output = (0, child_process_1.execSync)(cmd, {
|
|
82
|
+
cwd,
|
|
83
|
+
timeout: 60_000,
|
|
84
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
85
|
+
encoding: 'utf-8',
|
|
86
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
87
|
+
});
|
|
88
|
+
return output.trim() || '(no output)';
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const e = err;
|
|
92
|
+
const msg = (e.stderr || e.stdout || 'command failed').trim();
|
|
93
|
+
if (msg.includes('not found') || msg.includes('Cannot connect')) {
|
|
94
|
+
return 'Error: Docker is not installed or not running. Install Docker Desktop or start the Docker daemon.';
|
|
95
|
+
}
|
|
96
|
+
return `Exit ${e.status || 1}: ${msg}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.DockerTool = DockerTool;
|
|
101
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class GitTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
args: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
cwd: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitTool = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const ALLOWED_ACTIONS = [
|
|
6
|
+
'status', 'diff', 'log', 'commit', 'branch', 'checkout',
|
|
7
|
+
'stash', 'push', 'pull', 'merge', 'blame', 'tag', 'add', 'reset',
|
|
8
|
+
];
|
|
9
|
+
class GitTool {
|
|
10
|
+
name = 'git';
|
|
11
|
+
description = 'Run git operations. Actions: status, diff, log, commit, branch, checkout, stash, push, pull, merge, blame, tag, add, reset.';
|
|
12
|
+
permission = 'prompt';
|
|
13
|
+
parameters = {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
action: { type: 'string', description: 'Git action (status, diff, log, commit, branch, checkout, stash, push, pull, merge, blame, tag, add, reset)' },
|
|
17
|
+
args: { type: 'string', description: 'Additional arguments (e.g., file path, branch name, commit message)' },
|
|
18
|
+
cwd: { type: 'string', description: 'Working directory (defaults to current)' },
|
|
19
|
+
},
|
|
20
|
+
required: ['action'],
|
|
21
|
+
};
|
|
22
|
+
async execute(args) {
|
|
23
|
+
const action = args.action;
|
|
24
|
+
if (!action)
|
|
25
|
+
return 'Error: action is required';
|
|
26
|
+
if (!ALLOWED_ACTIONS.includes(action)) {
|
|
27
|
+
return `Error: unknown action "${action}". Allowed: ${ALLOWED_ACTIONS.join(', ')}`;
|
|
28
|
+
}
|
|
29
|
+
const extra = args.args || '';
|
|
30
|
+
const cwd = args.cwd || process.cwd();
|
|
31
|
+
// Block destructive force operations
|
|
32
|
+
const fullCmd = `git ${action} ${extra}`.trim();
|
|
33
|
+
if (/--force\s+.*(main|master)/i.test(fullCmd)) {
|
|
34
|
+
return 'Error: force push to main/master is blocked for safety.';
|
|
35
|
+
}
|
|
36
|
+
if (/clean\s+-[a-z]*f/i.test(fullCmd)) {
|
|
37
|
+
return 'Error: git clean -f is blocked for safety.';
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const output = (0, child_process_1.execSync)(fullCmd, {
|
|
41
|
+
cwd,
|
|
42
|
+
timeout: 30_000,
|
|
43
|
+
maxBuffer: 1024 * 1024,
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
});
|
|
47
|
+
return output.trim() || '(no output)';
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const e = err;
|
|
51
|
+
const stderr = (e.stderr || '').trim();
|
|
52
|
+
const stdout = (e.stdout || '').trim();
|
|
53
|
+
return `Exit ${e.status || 1}${stdout ? `\n${stdout}` : ''}${stderr ? `\nError: ${stderr}` : ''}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.GitTool = GitTool;
|
|
58
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class HttpClientTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
method: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
url: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
headers: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
body: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
auth: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
timeout: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
required: string[];
|
|
35
|
+
};
|
|
36
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
37
|
+
private isBlocked;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=http-client.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpClientTool = void 0;
|
|
4
|
+
class HttpClientTool {
|
|
5
|
+
name = 'http_client';
|
|
6
|
+
description = 'Make HTTP requests. Supports GET, POST, PUT, DELETE, PATCH with headers, auth, and body.';
|
|
7
|
+
permission = 'prompt';
|
|
8
|
+
parameters = {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
method: { type: 'string', description: 'HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD (default: GET)' },
|
|
12
|
+
url: { type: 'string', description: 'Full URL to request' },
|
|
13
|
+
headers: { type: 'object', description: 'Request headers as key-value pairs' },
|
|
14
|
+
body: { type: 'string', description: 'Request body (JSON string or plain text)' },
|
|
15
|
+
auth: { type: 'string', description: 'Authorization header value (e.g., "Bearer token123")' },
|
|
16
|
+
timeout: { type: 'number', description: 'Timeout in ms (default: 30000)' },
|
|
17
|
+
},
|
|
18
|
+
required: ['url'],
|
|
19
|
+
};
|
|
20
|
+
async execute(args) {
|
|
21
|
+
const url = args.url;
|
|
22
|
+
if (!url)
|
|
23
|
+
return 'Error: url is required';
|
|
24
|
+
// Validate URL
|
|
25
|
+
let parsedUrl;
|
|
26
|
+
try {
|
|
27
|
+
parsedUrl = new URL(url);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return `Error: invalid URL: ${url}`;
|
|
31
|
+
}
|
|
32
|
+
// Block private IPs and file protocol
|
|
33
|
+
if (this.isBlocked(parsedUrl)) {
|
|
34
|
+
return 'Error: requests to private/local addresses are blocked for security.';
|
|
35
|
+
}
|
|
36
|
+
const method = (args.method || 'GET').toUpperCase();
|
|
37
|
+
const timeoutMs = args.timeout || 30_000;
|
|
38
|
+
const headers = {};
|
|
39
|
+
// Set headers
|
|
40
|
+
if (args.headers && typeof args.headers === 'object') {
|
|
41
|
+
for (const [k, v] of Object.entries(args.headers)) {
|
|
42
|
+
headers[k] = String(v);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (args.auth) {
|
|
46
|
+
headers['Authorization'] = args.auth;
|
|
47
|
+
}
|
|
48
|
+
// Auto-set content type for body
|
|
49
|
+
const body = args.body;
|
|
50
|
+
if (body && !headers['Content-Type'] && !headers['content-type']) {
|
|
51
|
+
try {
|
|
52
|
+
JSON.parse(body);
|
|
53
|
+
headers['Content-Type'] = 'application/json';
|
|
54
|
+
}
|
|
55
|
+
catch { /* leave as-is */ }
|
|
56
|
+
}
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(url, {
|
|
61
|
+
method,
|
|
62
|
+
headers,
|
|
63
|
+
body: ['GET', 'HEAD'].includes(method) ? undefined : body,
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
});
|
|
66
|
+
const contentType = res.headers.get('content-type') || '';
|
|
67
|
+
let responseBody;
|
|
68
|
+
try {
|
|
69
|
+
responseBody = await res.text();
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
// Try to pretty-print JSON
|
|
75
|
+
if (contentType.includes('json') || responseBody.startsWith('{') || responseBody.startsWith('[')) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(responseBody);
|
|
78
|
+
responseBody = JSON.stringify(parsed, null, 2);
|
|
79
|
+
}
|
|
80
|
+
catch { /* keep raw */ }
|
|
81
|
+
}
|
|
82
|
+
// Truncate huge responses
|
|
83
|
+
if (responseBody.length > 10_000) {
|
|
84
|
+
responseBody = responseBody.substring(0, 10_000) + '\n...(truncated)';
|
|
85
|
+
}
|
|
86
|
+
const headerLines = Array.from(res.headers.entries())
|
|
87
|
+
.slice(0, 10)
|
|
88
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
89
|
+
.join('\n');
|
|
90
|
+
return `${res.status} ${res.statusText}\n\nHeaders:\n${headerLines}\n\nBody:\n${responseBody}`;
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
95
|
+
if (msg.includes('abort'))
|
|
96
|
+
return `Error: request timed out after ${timeoutMs}ms`;
|
|
97
|
+
return `Error: ${msg}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
isBlocked(url) {
|
|
101
|
+
const host = url.hostname.toLowerCase();
|
|
102
|
+
if (url.protocol === 'file:')
|
|
103
|
+
return true;
|
|
104
|
+
if (host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '0.0.0.0')
|
|
105
|
+
return true;
|
|
106
|
+
if (host === '169.254.169.254')
|
|
107
|
+
return true; // cloud metadata
|
|
108
|
+
if (/^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host))
|
|
109
|
+
return true;
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.HttpClientTool = HttpClientTool;
|
|
114
|
+
//# sourceMappingURL=http-client.js.map
|