@travisennis/acai 0.0.1 → 0.0.3
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 +3 -4
- package/dist/commands/health-command.d.ts +2 -0
- package/dist/commands/health-command.js +59 -0
- package/dist/commands/manager.js +2 -0
- package/dist/commands/paste-command.d.ts +1 -1
- package/dist/commands/paste-command.js +155 -11
- package/dist/commands/reset-command.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/models/openrouter-provider.d.ts +4 -1
- package/dist/models/openrouter-provider.js +46 -4
- package/dist/models/providers.d.ts +1 -1
- package/dist/prompts/manager.d.ts +1 -0
- package/dist/prompts/manager.js +10 -0
- package/dist/prompts.js +8 -6
- package/dist/repl.js +49 -26
- package/dist/terminal/formatting.d.ts +16 -5
- package/dist/terminal/formatting.js +40 -6
- package/dist/terminal/index.d.ts +1 -1
- package/dist/terminal/index.js +54 -14
- package/dist/terminal/markdown.js +0 -1
- package/dist/terminal/supports-color.d.ts +16 -0
- package/dist/terminal/supports-color.js +121 -0
- package/dist/terminal/supports-hyperlinks.d.ts +7 -0
- package/dist/terminal/supports-hyperlinks.js +98 -0
- package/dist/tools/bash.js +95 -117
- package/dist/tools/code-interpreter.js +11 -1
- package/dist/tools/command-validation.d.ts +7 -3
- package/dist/tools/command-validation.js +67 -23
- package/dist/tools/delete-file.d.ts +4 -1
- package/dist/tools/delete-file.js +47 -3
- package/dist/tools/git-utils.d.ts +6 -0
- package/dist/tools/git-utils.js +89 -12
- package/dist/tools/grep.d.ts +20 -0
- package/dist/tools/grep.js +128 -40
- package/dist/tools/index.d.ts +2 -18
- package/dist/tools/index.js +4 -18
- package/package.json +30 -20
- package/.acai/acai.json +0 -9
- package/.acai/prompts/add-openrouter-model.md +0 -13
- package/.acai/prompts/project-status.md +0 -4
- package/.acai/prompts/update-architecture-document.md +0 -9
- package/.acai/rules/learned-rules.md +0 -9
- package/.ai/docs/available-tools.txt +0 -3
- package/.ai/docs/cognitive_complexity_refactoring_progress.md +0 -65
- package/.ai/docs/deleted_tools.md +0 -168
- package/.ai/docs/deleted_tools_88ced9ef.md +0 -56
- package/.ai/docs/image-pasting.md +0 -46
- package/.ai/docs/initialize-app.md +0 -117
- package/.ai/docs/issue-4-plan.md +0 -44
- package/.ai/docs/marked-renderer-debug.md +0 -15
- package/.ai/docs/marked-renderer-refactor-plan.md +0 -64
- package/.ai/docs/memory-use-cases.md +0 -55
- package/.ai/docs/prompt-consistency.md +0 -31
- package/.ai/docs/refactoring-tools.md +0 -98
- package/.ai/docs/system-prompt-update.md +0 -174
- package/.ai/docs/system_prompt.txt +0 -210
- package/.ai/docs/tasks.md +0 -49
- package/.ai/plan.md +0 -131
- package/.ai/prompt.md +0 -1
- package/.ai/scripts/fetch_models.js +0 -27
- package/.ai/scripts/generateSystemPrompt.ts +0 -15
- package/.ai/scripts/list-tools.mjs +0 -4
- package/.ai/scripts/p5_geometric_shapes.js +0 -149
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -3
- package/.husky/pre-push +0 -1
- package/.ignore +0 -4
- package/AGENTS.md +0 -25
- package/ARCHITECTURE.md +0 -304
- package/TODO.md +0 -2
- package/biome.json +0 -61
- package/commitlint.config.js +0 -3
- package/dist/source/cli.d.ts +0 -19
- package/dist/source/cli.js +0 -116
- package/dist/source/commands/application-log-command.d.ts +0 -2
- package/dist/source/commands/application-log-command.js +0 -43
- package/dist/source/commands/clear-command.d.ts +0 -2
- package/dist/source/commands/clear-command.js +0 -12
- package/dist/source/commands/compact-command.d.ts +0 -2
- package/dist/source/commands/compact-command.js +0 -51
- package/dist/source/commands/copy-command.d.ts +0 -2
- package/dist/source/commands/copy-command.js +0 -51
- package/dist/source/commands/edit-command.d.ts +0 -2
- package/dist/source/commands/edit-command.js +0 -53
- package/dist/source/commands/edit-prompt-command.d.ts +0 -2
- package/dist/source/commands/edit-prompt-command.js +0 -25
- package/dist/source/commands/exit-command.d.ts +0 -2
- package/dist/source/commands/exit-command.js +0 -14
- package/dist/source/commands/files-command.d.ts +0 -2
- package/dist/source/commands/files-command.js +0 -63
- package/dist/source/commands/generate-rules-command.d.ts +0 -2
- package/dist/source/commands/generate-rules-command.js +0 -61
- package/dist/source/commands/help-command.d.ts +0 -2
- package/dist/source/commands/help-command.js +0 -19
- package/dist/source/commands/init-command.d.ts +0 -2
- package/dist/source/commands/init-command.js +0 -40
- package/dist/source/commands/last-log-command.d.ts +0 -2
- package/dist/source/commands/last-log-command.js +0 -76
- package/dist/source/commands/manager.d.ts +0 -22
- package/dist/source/commands/manager.js +0 -123
- package/dist/source/commands/model-command.d.ts +0 -2
- package/dist/source/commands/model-command.js +0 -84
- package/dist/source/commands/paste-command.d.ts +0 -2
- package/dist/source/commands/paste-command.js +0 -40
- package/dist/source/commands/prompt-command.d.ts +0 -2
- package/dist/source/commands/prompt-command.js +0 -111
- package/dist/source/commands/reset-command.d.ts +0 -2
- package/dist/source/commands/reset-command.js +0 -16
- package/dist/source/commands/rules-command.d.ts +0 -2
- package/dist/source/commands/rules-command.js +0 -68
- package/dist/source/commands/save-command.d.ts +0 -2
- package/dist/source/commands/save-command.js +0 -14
- package/dist/source/commands/types.d.ts +0 -26
- package/dist/source/commands/types.js +0 -1
- package/dist/source/commands/usage-command.d.ts +0 -2
- package/dist/source/commands/usage-command.js +0 -21
- package/dist/source/config.d.ts +0 -60
- package/dist/source/config.js +0 -193
- package/dist/source/conversation-analyzer.d.ts +0 -10
- package/dist/source/conversation-analyzer.js +0 -88
- package/dist/source/dedent.d.ts +0 -3
- package/dist/source/dedent.js +0 -38
- package/dist/source/formatting.d.ts +0 -17
- package/dist/source/formatting.js +0 -103
- package/dist/source/index.d.ts +0 -18
- package/dist/source/index.js +0 -213
- package/dist/source/logger.d.ts +0 -2
- package/dist/source/logger.js +0 -24
- package/dist/source/mentions.d.ts +0 -9
- package/dist/source/mentions.js +0 -182
- package/dist/source/messages.d.ts +0 -69
- package/dist/source/messages.js +0 -261
- package/dist/source/middleware/audit-message.d.ts +0 -5
- package/dist/source/middleware/audit-message.js +0 -95
- package/dist/source/middleware/index.d.ts +0 -2
- package/dist/source/middleware/index.js +0 -2
- package/dist/source/middleware/rate-limit.d.ts +0 -4
- package/dist/source/middleware/rate-limit.js +0 -17
- package/dist/source/models/ai-config.d.ts +0 -12
- package/dist/source/models/ai-config.js +0 -87
- package/dist/source/models/anthropic-provider.d.ts +0 -25
- package/dist/source/models/anthropic-provider.js +0 -184
- package/dist/source/models/deepseek-provider.d.ts +0 -20
- package/dist/source/models/deepseek-provider.js +0 -42
- package/dist/source/models/google-provider.d.ts +0 -19
- package/dist/source/models/google-provider.js +0 -56
- package/dist/source/models/manager.d.ts +0 -15
- package/dist/source/models/manager.js +0 -48
- package/dist/source/models/openai-provider.d.ts +0 -22
- package/dist/source/models/openai-provider.js +0 -70
- package/dist/source/models/openrouter-provider.d.ts +0 -36
- package/dist/source/models/openrouter-provider.js +0 -276
- package/dist/source/models/providers.d.ts +0 -33
- package/dist/source/models/providers.js +0 -116
- package/dist/source/models/xai-provider.d.ts +0 -20
- package/dist/source/models/xai-provider.js +0 -47
- package/dist/source/parsing.d.ts +0 -2
- package/dist/source/parsing.js +0 -18
- package/dist/source/prompts/manager.d.ts +0 -19
- package/dist/source/prompts/manager.js +0 -71
- package/dist/source/prompts.d.ts +0 -4
- package/dist/source/prompts.js +0 -158
- package/dist/source/repl-prompt.d.ts +0 -14
- package/dist/source/repl-prompt.js +0 -147
- package/dist/source/repl.d.ts +0 -27
- package/dist/source/repl.js +0 -431
- package/dist/source/terminal/formatting.d.ts +0 -37
- package/dist/source/terminal/formatting.js +0 -106
- package/dist/source/terminal/index.d.ts +0 -94
- package/dist/source/terminal/index.js +0 -420
- package/dist/source/terminal/markdown-utils.d.ts +0 -2
- package/dist/source/terminal/markdown-utils.js +0 -81
- package/dist/source/terminal/markdown.d.ts +0 -1
- package/dist/source/terminal/markdown.js +0 -111
- package/dist/source/terminal/types.d.ts +0 -71
- package/dist/source/terminal/types.js +0 -1
- package/dist/source/terminal-output.d.ts +0 -8
- package/dist/source/terminal-output.js +0 -213
- package/dist/source/terminal-output.test.d.ts +0 -8
- package/dist/source/terminal-output.test.js +0 -213
- package/dist/source/token-tracker.d.ts +0 -14
- package/dist/source/token-tracker.js +0 -53
- package/dist/source/token-utils.d.ts +0 -7
- package/dist/source/token-utils.js +0 -13
- package/dist/source/tools/agent.d.ts +0 -17
- package/dist/source/tools/agent.js +0 -87
- package/dist/source/tools/bash.d.ts +0 -19
- package/dist/source/tools/bash.js +0 -294
- package/dist/source/tools/code-interpreter.d.ts +0 -12
- package/dist/source/tools/code-interpreter.js +0 -131
- package/dist/source/tools/command-validation.d.ts +0 -8
- package/dist/source/tools/command-validation.js +0 -69
- package/dist/source/tools/delete-file.d.ts +0 -12
- package/dist/source/tools/delete-file.js +0 -56
- package/dist/source/tools/directory-tree.d.ts +0 -12
- package/dist/source/tools/directory-tree.js +0 -38
- package/dist/source/tools/edit-file.d.ts +0 -19
- package/dist/source/tools/edit-file.js +0 -107
- package/dist/source/tools/filesystem-utils.d.ts +0 -22
- package/dist/source/tools/filesystem-utils.js +0 -191
- package/dist/source/tools/git-utils.d.ts +0 -14
- package/dist/source/tools/git-utils.js +0 -64
- package/dist/source/tools/grep.d.ts +0 -17
- package/dist/source/tools/grep.js +0 -138
- package/dist/source/tools/index.d.ts +0 -161
- package/dist/source/tools/index.js +0 -209
- package/dist/source/tools/memory-read.d.ts +0 -13
- package/dist/source/tools/memory-read.js +0 -135
- package/dist/source/tools/memory-write.d.ts +0 -12
- package/dist/source/tools/memory-write.js +0 -83
- package/dist/source/tools/move-file.d.ts +0 -13
- package/dist/source/tools/move-file.js +0 -44
- package/dist/source/tools/read-file.d.ts +0 -17
- package/dist/source/tools/read-file.js +0 -86
- package/dist/source/tools/read-multiple-files.d.ts +0 -14
- package/dist/source/tools/read-multiple-files.js +0 -55
- package/dist/source/tools/save-file.d.ts +0 -17
- package/dist/source/tools/save-file.js +0 -98
- package/dist/source/tools/think.d.ts +0 -11
- package/dist/source/tools/think.js +0 -45
- package/dist/source/tools/types.d.ts +0 -29
- package/dist/source/tools/types.js +0 -14
- package/dist/source/tools/web-fetch.d.ts +0 -47
- package/dist/source/tools/web-fetch.js +0 -246
- package/dist/source/tools/web-search.d.ts +0 -13
- package/dist/source/tools/web-search.js +0 -80
- package/dist/source/utils/process.d.ts +0 -36
- package/dist/source/utils/process.js +0 -75
- package/dist/source/version.d.ts +0 -1
- package/dist/source/version.js +0 -21
- package/dist/terminal-output.d.ts +0 -8
- package/dist/terminal-output.js +0 -213
- package/dist/tools/memory-read.d.ts +0 -13
- package/dist/tools/memory-read.js +0 -135
- package/dist/tools/memory-write.d.ts +0 -12
- package/dist/tools/memory-write.js +0 -83
- package/knip.json +0 -5
- package/source/cli.ts +0 -172
- package/source/commands/application-log-command.ts +0 -53
- package/source/commands/clear-command.ts +0 -14
- package/source/commands/compact-command.ts +0 -64
- package/source/commands/copy-command.ts +0 -55
- package/source/commands/edit-command.ts +0 -63
- package/source/commands/edit-prompt-command.ts +0 -31
- package/source/commands/exit-command.ts +0 -18
- package/source/commands/files-command.ts +0 -85
- package/source/commands/generate-rules-command.ts +0 -82
- package/source/commands/help-command.ts +0 -27
- package/source/commands/init-command.ts +0 -48
- package/source/commands/last-log-command.ts +0 -88
- package/source/commands/manager.ts +0 -151
- package/source/commands/model-command.ts +0 -123
- package/source/commands/paste-command.ts +0 -62
- package/source/commands/prompt-command.ts +0 -150
- package/source/commands/reset-command.ts +0 -22
- package/source/commands/rules-command.ts +0 -76
- package/source/commands/save-command.ts +0 -20
- package/source/commands/types.ts +0 -28
- package/source/commands/usage-command.ts +0 -26
- package/source/config.ts +0 -223
- package/source/conversation-analyzer.ts +0 -115
- package/source/dedent.ts +0 -53
- package/source/formatting.ts +0 -132
- package/source/index.ts +0 -240
- package/source/logger.ts +0 -29
- package/source/mentions.ts +0 -227
- package/source/messages.ts +0 -360
- package/source/middleware/audit-message.ts +0 -133
- package/source/middleware/index.ts +0 -2
- package/source/middleware/rate-limit.ts +0 -24
- package/source/models/ai-config.ts +0 -109
- package/source/models/anthropic-provider.ts +0 -199
- package/source/models/deepseek-provider.ts +0 -53
- package/source/models/google-provider.ts +0 -68
- package/source/models/manager.ts +0 -84
- package/source/models/openai-provider.ts +0 -81
- package/source/models/openrouter-provider.ts +0 -288
- package/source/models/providers.ts +0 -197
- package/source/models/xai-provider.ts +0 -59
- package/source/parsing.ts +0 -20
- package/source/prompts/manager.ts +0 -90
- package/source/prompts.ts +0 -172
- package/source/repl-prompt.ts +0 -196
- package/source/repl.ts +0 -572
- package/source/terminal/formatting.ts +0 -121
- package/source/terminal/index.ts +0 -518
- package/source/terminal/markdown-utils.ts +0 -89
- package/source/terminal/markdown.ts +0 -155
- package/source/terminal/types.ts +0 -84
- package/source/terminal-output.test.ts +0 -266
- package/source/token-tracker.ts +0 -78
- package/source/token-utils.ts +0 -17
- package/source/tools/agent.ts +0 -107
- package/source/tools/bash.ts +0 -367
- package/source/tools/code-interpreter.ts +0 -172
- package/source/tools/command-validation.ts +0 -81
- package/source/tools/delete-file.ts +0 -71
- package/source/tools/directory-tree.ts +0 -54
- package/source/tools/edit-file.ts +0 -155
- package/source/tools/filesystem-utils.ts +0 -265
- package/source/tools/git-utils.ts +0 -70
- package/source/tools/grep.ts +0 -184
- package/source/tools/index.ts +0 -278
- package/source/tools/memory-read.ts +0 -174
- package/source/tools/memory-write.ts +0 -105
- package/source/tools/move-file.ts +0 -59
- package/source/tools/read-file.ts +0 -129
- package/source/tools/read-multiple-files.ts +0 -80
- package/source/tools/save-file.ts +0 -147
- package/source/tools/think.ts +0 -51
- package/source/tools/types.ts +0 -58
- package/source/tools/web-fetch.ts +0 -327
- package/source/tools/web-search.ts +0 -101
- package/source/utils/process.ts +0 -121
- package/source/version.ts +0 -21
- package/test/commands/copy-command.test.ts +0 -69
- package/test/config.test.ts +0 -200
- package/test/terminal/markdown-utils.test.ts +0 -124
- package/test/tools/bash-tool.test.ts +0 -58
- package/test/tools/code-interpreter.test.ts +0 -91
- package/test/tools/command-validation.test.ts +0 -48
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -30
package/dist/tools/bash.js
CHANGED
|
@@ -9,69 +9,6 @@ import { CommandValidation } from "./command-validation.js";
|
|
|
9
9
|
export const BashTool = {
|
|
10
10
|
name: "bash",
|
|
11
11
|
};
|
|
12
|
-
function tokenize(inputStr) {
|
|
13
|
-
const tokens = [];
|
|
14
|
-
let current = "";
|
|
15
|
-
let inSingle = false;
|
|
16
|
-
let inDouble = false;
|
|
17
|
-
for (let i = 0; i < inputStr.length; i++) {
|
|
18
|
-
const ch = inputStr[i];
|
|
19
|
-
if (ch === "'" && !inDouble) {
|
|
20
|
-
inSingle = !inSingle;
|
|
21
|
-
current += ch;
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (ch === '"' && !inSingle) {
|
|
25
|
-
inDouble = !inDouble;
|
|
26
|
-
current += ch;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (!inSingle && !inDouble && /\s/.test(ch)) {
|
|
30
|
-
if (current.length > 0) {
|
|
31
|
-
const raw = current;
|
|
32
|
-
const unquoted = raw.replace(/^['"]|['"]$/g, "");
|
|
33
|
-
tokens.push({ raw, unquoted });
|
|
34
|
-
current = "";
|
|
35
|
-
}
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (ch === "\\" && inDouble && i + 1 < inputStr.length) {
|
|
39
|
-
const next = inputStr[i + 1];
|
|
40
|
-
if (next === '"' || next === "\\") {
|
|
41
|
-
current += next;
|
|
42
|
-
i++;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
current += ch;
|
|
47
|
-
}
|
|
48
|
-
if (current.length > 0) {
|
|
49
|
-
const raw = current;
|
|
50
|
-
const unquoted = raw.replace(/^['"]|['"]$/g, "");
|
|
51
|
-
tokens.push({ raw, unquoted });
|
|
52
|
-
}
|
|
53
|
-
return tokens;
|
|
54
|
-
}
|
|
55
|
-
function shouldSkipPathValidation(tokens, index) {
|
|
56
|
-
if (index === 0)
|
|
57
|
-
return false;
|
|
58
|
-
const cmd = tokens[0]?.unquoted;
|
|
59
|
-
if (cmd !== "git")
|
|
60
|
-
return false;
|
|
61
|
-
const sub = tokens[1]?.unquoted;
|
|
62
|
-
if (sub !== "commit" &&
|
|
63
|
-
sub !== "tag" &&
|
|
64
|
-
!(sub === "notes" && tokens[2]?.unquoted === "add")) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
const prev = tokens[index - 1]?.unquoted;
|
|
68
|
-
if (prev === "-m" || prev === "--message")
|
|
69
|
-
return true;
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
function looksLikeUrl(str) {
|
|
73
|
-
return str.startsWith("http://") || str.startsWith("https://");
|
|
74
|
-
}
|
|
75
12
|
// Whitelist of allowed commands
|
|
76
13
|
const ALLOWED_COMMANDS = [
|
|
77
14
|
"chmod",
|
|
@@ -104,23 +41,84 @@ const ALLOWED_COMMANDS = [
|
|
|
104
41
|
];
|
|
105
42
|
// Command execution timeout in milliseconds
|
|
106
43
|
const DEFAULT_TIMEOUT = 1.5 * 60 * 1000; // 1.5 minutes
|
|
107
|
-
// Initialize command validator with allowed commands
|
|
108
|
-
const commandValidator = new CommandValidation(ALLOWED_COMMANDS);
|
|
109
44
|
// Ensure path is within base directory
|
|
110
45
|
function isPathWithinBaseDir(requestedPath, baseDir) {
|
|
111
46
|
const normalizedRequestedPath = path.normalize(requestedPath);
|
|
112
47
|
const normalizedBaseDir = path.normalize(baseDir);
|
|
113
48
|
return normalizedRequestedPath.startsWith(normalizedBaseDir);
|
|
114
49
|
}
|
|
50
|
+
// Validate path arguments to ensure they're within the project
|
|
51
|
+
function validatePaths(command, baseDir, cwd) {
|
|
52
|
+
// Simple tokenization - split on spaces but respect quotes
|
|
53
|
+
const tokens = [];
|
|
54
|
+
let current = "";
|
|
55
|
+
let inQuotes = false;
|
|
56
|
+
let quoteChar = "";
|
|
57
|
+
for (let i = 0; i < command.length; i++) {
|
|
58
|
+
const char = command[i];
|
|
59
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
|
60
|
+
inQuotes = true;
|
|
61
|
+
quoteChar = char;
|
|
62
|
+
current += char;
|
|
63
|
+
}
|
|
64
|
+
else if (char === quoteChar && inQuotes) {
|
|
65
|
+
inQuotes = false;
|
|
66
|
+
quoteChar = "";
|
|
67
|
+
current += char;
|
|
68
|
+
}
|
|
69
|
+
else if (char === " " && !inQuotes) {
|
|
70
|
+
if (current) {
|
|
71
|
+
tokens.push(current);
|
|
72
|
+
current = "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
current += char;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (current)
|
|
80
|
+
tokens.push(current);
|
|
81
|
+
// Check each token that looks like a path
|
|
82
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
83
|
+
// Skip the command itself
|
|
84
|
+
const token = tokens[i];
|
|
85
|
+
if (!token)
|
|
86
|
+
continue;
|
|
87
|
+
// Remove quotes for path checking
|
|
88
|
+
const cleanToken = token.replace(/^['"]|['"]$/g, "");
|
|
89
|
+
// Skip if it's clearly not a path
|
|
90
|
+
if (cleanToken.startsWith("-") ||
|
|
91
|
+
cleanToken.includes("://") ||
|
|
92
|
+
!cleanToken.includes("/")) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Skip git commit messages and other special cases
|
|
96
|
+
const prevToken = tokens[i - 1]?.replace(/^['"]|['"]$/g, "");
|
|
97
|
+
if (prevToken === "-m" || prevToken === "--message") {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const resolvedPath = path.resolve(cwd, cleanToken);
|
|
102
|
+
if (!isPathWithinBaseDir(resolvedPath, baseDir)) {
|
|
103
|
+
return {
|
|
104
|
+
isValid: false,
|
|
105
|
+
error: `Path '${cleanToken}' resolves outside the project directory (${resolvedPath}). All paths must be within ${baseDir}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (_e) { }
|
|
110
|
+
}
|
|
111
|
+
return { isValid: true };
|
|
112
|
+
}
|
|
113
|
+
// Initialize command validator with allowed commands
|
|
114
|
+
const commandValidator = new CommandValidation(ALLOWED_COMMANDS);
|
|
115
115
|
export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, autoAcceptAll, }) => {
|
|
116
116
|
let autoAcceptCommands = autoAcceptAll;
|
|
117
117
|
return {
|
|
118
118
|
[BashTool.name]: tool({
|
|
119
119
|
description: `Execute bash commands and return their output. Limited to a whitelist of safe commands: ${ALLOWED_COMMANDS.join(", ")}. Commands will only execute within the project directory for security. Always specify absolute paths to avoid errors.`,
|
|
120
120
|
inputSchema: z.object({
|
|
121
|
-
command: z
|
|
122
|
-
.string()
|
|
123
|
-
.describe("Full CLI command to execute. Must be from the allowed list without chaining operators."),
|
|
121
|
+
command: z.string().describe("Full CLI command to execute."),
|
|
124
122
|
cwd: z
|
|
125
123
|
.string()
|
|
126
124
|
.nullable()
|
|
@@ -139,61 +137,41 @@ export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, auto
|
|
|
139
137
|
id: toolCallId,
|
|
140
138
|
data: `Executing: ${chalk.cyan(command)} in ${chalk.cyan(safeCwd)}`,
|
|
141
139
|
});
|
|
142
|
-
// Validate command using CommandValidation
|
|
143
|
-
if (!commandValidator.isValid(command)) {
|
|
144
|
-
const errorMsg = `Command not allowed. Ensure all sub-commands are in the approved list: ${ALLOWED_COMMANDS.join(", ")} and no unsafe operators (>, <, \`, $()) are used.`;
|
|
145
|
-
sendData?.({ event: "tool-error", id: toolCallId, data: errorMsg });
|
|
146
|
-
return errorMsg;
|
|
147
|
-
}
|
|
148
140
|
// Validate working directory
|
|
149
141
|
if (!isPathWithinBaseDir(safeCwd, baseDir)) {
|
|
150
142
|
const errorMsg = `Working directory must be within the project directory: ${baseDir}`;
|
|
151
143
|
sendData?.({ event: "tool-error", id: toolCallId, data: errorMsg });
|
|
152
144
|
return errorMsg;
|
|
153
145
|
}
|
|
154
|
-
// Validate command
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (!isPathWithinBaseDir(resolvedPath, baseDir)) {
|
|
174
|
-
const errorMsg = `Command argument references path outside the project directory: ${part} (resolved to ${resolvedPath})`;
|
|
175
|
-
sendData?.({
|
|
176
|
-
event: "tool-error",
|
|
177
|
-
id: toolCallId,
|
|
178
|
-
data: errorMsg,
|
|
179
|
-
});
|
|
180
|
-
return errorMsg;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch (e) {
|
|
184
|
-
console.info(`Could not resolve potential path argument: ${part}`, e);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
146
|
+
// Validate command using improved validation
|
|
147
|
+
const commandValidation = commandValidator.isValid(command);
|
|
148
|
+
if (!commandValidation.isValid) {
|
|
149
|
+
sendData?.({
|
|
150
|
+
event: "tool-error",
|
|
151
|
+
id: toolCallId,
|
|
152
|
+
data: commandValidation.error ?? "Unknown error.",
|
|
153
|
+
});
|
|
154
|
+
return commandValidation.error ?? "Unknown error.";
|
|
155
|
+
}
|
|
156
|
+
// Validate paths
|
|
157
|
+
const pathValidation = validatePaths(command, baseDir, safeCwd);
|
|
158
|
+
if (!pathValidation.isValid) {
|
|
159
|
+
sendData?.({
|
|
160
|
+
event: "tool-error",
|
|
161
|
+
id: toolCallId,
|
|
162
|
+
data: pathValidation.error ?? "Unknown error.",
|
|
163
|
+
});
|
|
164
|
+
return pathValidation.error ?? "Unknown error.";
|
|
187
165
|
}
|
|
188
166
|
// Prompt user for command execution approval (only in interactive mode)
|
|
189
167
|
if (terminal) {
|
|
190
168
|
if (!autoAcceptCommands) {
|
|
191
169
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
170
|
+
terminal.lineBreak();
|
|
171
|
+
terminal.writeln(`${chalk.blue.bold("●")} About to execute command: ${chalk.cyan(command)}`);
|
|
172
|
+
terminal.writeln(`${chalk.gray("Working directory:")} ${safeCwd}`);
|
|
173
|
+
terminal.lineBreak();
|
|
192
174
|
}
|
|
193
|
-
terminal.lineBreak();
|
|
194
|
-
terminal.writeln(`${chalk.blue.bold("●")} About to execute command: ${chalk.cyan(command)}`);
|
|
195
|
-
terminal.writeln(`${chalk.gray("Working directory:")} ${safeCwd}`);
|
|
196
|
-
terminal.lineBreak();
|
|
197
175
|
let userChoice;
|
|
198
176
|
if (autoAcceptCommands) {
|
|
199
177
|
terminal.writeln(chalk.green("✓ Auto-accepting command (all future commands will be accepted)"));
|
|
@@ -231,6 +209,7 @@ export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, auto
|
|
|
231
209
|
return rejectionMsg;
|
|
232
210
|
}
|
|
233
211
|
}
|
|
212
|
+
// Execute command
|
|
234
213
|
try {
|
|
235
214
|
const result = await executeCommand(command, {
|
|
236
215
|
cwd: safeCwd,
|
|
@@ -262,28 +241,27 @@ export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, auto
|
|
|
262
241
|
}
|
|
263
242
|
catch (tokenError) {
|
|
264
243
|
console.error("Error calculating token count:", tokenError);
|
|
265
|
-
// Log or handle error, but don't block file return
|
|
266
244
|
}
|
|
267
245
|
const maxTokens = (await config.readProjectConfig()).tools.maxTokens;
|
|
268
|
-
|
|
269
|
-
const maxTokenMessage = `Output of commmand (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}). Please adjust how you call the command to get back more specific results`;
|
|
246
|
+
const maxTokenMessage = `Output of command (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}). Please adjust how you call the command to get back more specific results`;
|
|
270
247
|
const finalResult = tokenCount <= maxTokens ? formattedResult : maxTokenMessage;
|
|
271
248
|
sendData?.({
|
|
272
249
|
event: "tool-completion",
|
|
273
250
|
id: toolCallId,
|
|
274
251
|
data: tokenCount <= maxTokens
|
|
275
252
|
? "Command executed successfully."
|
|
276
|
-
: `Output of
|
|
253
|
+
: `Output of command (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}).`,
|
|
277
254
|
});
|
|
278
255
|
return finalResult;
|
|
279
256
|
}
|
|
280
257
|
catch (error) {
|
|
258
|
+
const errorMsg = `Command failed: ${error.message}`;
|
|
281
259
|
sendData?.({
|
|
282
260
|
event: "tool-error",
|
|
283
261
|
id: toolCallId,
|
|
284
|
-
data:
|
|
262
|
+
data: errorMsg,
|
|
285
263
|
});
|
|
286
|
-
return
|
|
264
|
+
return errorMsg;
|
|
287
265
|
}
|
|
288
266
|
},
|
|
289
267
|
}),
|
|
@@ -8,10 +8,20 @@ import { z } from "zod";
|
|
|
8
8
|
export const CodeInterpreterTool = {
|
|
9
9
|
name: "codeInterpreter",
|
|
10
10
|
};
|
|
11
|
+
const toolDescription = `Executes JavaScript code in a separate Node.js process using Node's Permission Model. By default, the child process has no permissions except read/write within the current working directory. The tool returns stdout, stderr, and exitCode. Use console.log/console.error to produce output.
|
|
12
|
+
|
|
13
|
+
⚠️ **IMPORTANT**: This tool uses ES Modules (ESM) only.
|
|
14
|
+
- Use \`import\` statements, NOT \`require()\`
|
|
15
|
+
- Examples: \`import fs from 'node:fs'\` NOT \`const fs = require('fs')\`
|
|
16
|
+
- Add file extensions for relative imports: \`import { utils } from './utils.js'\`
|
|
17
|
+
|
|
18
|
+
These scripts are run in the \`${process.cwd}/.acai-ci-tmp\`. Keep this in mind if you intend to import or reference files from this project in your script.
|
|
19
|
+
|
|
20
|
+
Timeout defaults to 5 seconds and can be extended up to 60 seconds.`;
|
|
11
21
|
export const createCodeInterpreterTool = ({ sendData, }) => {
|
|
12
22
|
return {
|
|
13
23
|
[CodeInterpreterTool.name]: tool({
|
|
14
|
-
description:
|
|
24
|
+
description: toolDescription,
|
|
15
25
|
inputSchema: z.object({
|
|
16
26
|
code: z.string().describe("JavaScript code to be executed."),
|
|
17
27
|
timeoutSeconds: z
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export declare class CommandValidation {
|
|
2
2
|
private readonly allowedCommands;
|
|
3
|
-
private readonly
|
|
3
|
+
private readonly dangerousPatterns;
|
|
4
4
|
constructor(allowedCommands: string[]);
|
|
5
5
|
private isCommandAllowed;
|
|
6
|
-
private
|
|
7
|
-
isValid(command: string):
|
|
6
|
+
private hasDangerousPatterns;
|
|
7
|
+
isValid(command: string): {
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
private splitOnPipes;
|
|
8
12
|
}
|
|
@@ -1,35 +1,40 @@
|
|
|
1
1
|
export class CommandValidation {
|
|
2
2
|
allowedCommands;
|
|
3
|
-
|
|
3
|
+
dangerousPatterns;
|
|
4
4
|
constructor(allowedCommands) {
|
|
5
5
|
this.allowedCommands = allowedCommands;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// Only block truly dangerous patterns, not useful shell operations
|
|
7
|
+
this.dangerousPatterns = [
|
|
8
|
+
/`/, // backticks (command substitution)
|
|
9
|
+
/\$\(/, // $() command substitution
|
|
10
|
+
/&&\s*rm\s+-rf/, // dangerous rm chains
|
|
11
|
+
/;\s*rm\s+-rf/, // dangerous rm chains
|
|
11
12
|
];
|
|
12
13
|
}
|
|
13
14
|
isCommandAllowed(command) {
|
|
14
15
|
const baseCommand = command.split(" ")[0] || "";
|
|
15
16
|
return this.allowedCommands.includes(baseCommand);
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
hasDangerousPatterns(command) {
|
|
18
19
|
// Remove all quoted segments first
|
|
19
20
|
const stripped = command
|
|
20
21
|
.replace(/'([^'\\]|\\.)*'/g, "")
|
|
21
22
|
.replace(/"([^"\\]|\\.)*"/g, "");
|
|
22
|
-
// Check for
|
|
23
|
-
return this.
|
|
23
|
+
// Check for dangerous patterns only in unquoted portions
|
|
24
|
+
return this.dangerousPatterns.some((re) => re.test(stripped));
|
|
24
25
|
}
|
|
25
26
|
isValid(command) {
|
|
26
|
-
if (!command.trim())
|
|
27
|
-
return false;
|
|
28
|
-
// First check for unsafe operators in unquoted portions
|
|
29
|
-
if (this.hasUnsafeOperators(command)) {
|
|
30
|
-
return false;
|
|
27
|
+
if (!command.trim()) {
|
|
28
|
+
return { isValid: false, error: "Command cannot be empty" };
|
|
31
29
|
}
|
|
32
|
-
//
|
|
30
|
+
// First check for dangerous patterns
|
|
31
|
+
if (this.hasDangerousPatterns(command)) {
|
|
32
|
+
return {
|
|
33
|
+
isValid: false,
|
|
34
|
+
error: "Command contains dangerous patterns (command substitution or unsafe rm chains)",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Process command while preserving quoted strings to extract sub-commands
|
|
33
38
|
const subCommands = [];
|
|
34
39
|
let currentSegment = "";
|
|
35
40
|
let inSingleQuote = false;
|
|
@@ -41,17 +46,16 @@ export class CommandValidation {
|
|
|
41
46
|
inSingleQuote = !inSingleQuote;
|
|
42
47
|
if (char === '"' && !inSingleQuote)
|
|
43
48
|
inDoubleQuote = !inDoubleQuote;
|
|
44
|
-
// Split on
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
(char === "&" || char === "|" || char === ";")) {
|
|
49
|
+
// Split on command separators only when not in quotes
|
|
50
|
+
// Note: We allow pipes (|) and redirects (>, <) but split on command separators
|
|
51
|
+
if (!inSingleQuote && !inDoubleQuote && (char === "&" || char === ";")) {
|
|
48
52
|
if (currentSegment.trim()) {
|
|
49
53
|
subCommands.push(currentSegment.trim());
|
|
50
54
|
currentSegment = "";
|
|
51
55
|
}
|
|
52
|
-
// Skip the operator and any subsequent same operators (like &&
|
|
56
|
+
// Skip the operator and any subsequent same operators (like &&)
|
|
53
57
|
while (i + 1 < command.length &&
|
|
54
|
-
["&", "
|
|
58
|
+
["&", ";"].includes(command[i + 1] ?? "")) {
|
|
55
59
|
i++;
|
|
56
60
|
}
|
|
57
61
|
}
|
|
@@ -63,7 +67,47 @@ export class CommandValidation {
|
|
|
63
67
|
if (currentSegment.trim()) {
|
|
64
68
|
subCommands.push(currentSegment.trim());
|
|
65
69
|
}
|
|
66
|
-
// Validate all sub-commands
|
|
67
|
-
|
|
70
|
+
// Validate all sub-commands (but be smart about pipes)
|
|
71
|
+
for (const subCmd of subCommands) {
|
|
72
|
+
// For piped commands, validate each part of the pipe
|
|
73
|
+
const pipeParts = this.splitOnPipes(subCmd);
|
|
74
|
+
for (const part of pipeParts) {
|
|
75
|
+
const trimmedPart = part.trim();
|
|
76
|
+
if (trimmedPart && !this.isCommandAllowed(trimmedPart)) {
|
|
77
|
+
const baseCmd = trimmedPart.split(" ")[0] || "";
|
|
78
|
+
return {
|
|
79
|
+
isValid: false,
|
|
80
|
+
error: `Command '${baseCmd}' is not allowed. Allowed commands: ${this.allowedCommands.join(", ")}`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { isValid: true };
|
|
86
|
+
}
|
|
87
|
+
splitOnPipes(command) {
|
|
88
|
+
const parts = [];
|
|
89
|
+
let current = "";
|
|
90
|
+
let inSingleQuote = false;
|
|
91
|
+
let inDoubleQuote = false;
|
|
92
|
+
for (let i = 0; i < command.length; i++) {
|
|
93
|
+
const char = command[i];
|
|
94
|
+
if (char === "'" && !inDoubleQuote)
|
|
95
|
+
inSingleQuote = !inSingleQuote;
|
|
96
|
+
if (char === '"' && !inSingleQuote)
|
|
97
|
+
inDoubleQuote = !inDoubleQuote;
|
|
98
|
+
if (char === "|" && !inSingleQuote && !inDoubleQuote) {
|
|
99
|
+
if (current.trim()) {
|
|
100
|
+
parts.push(current.trim());
|
|
101
|
+
current = "";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
current += char;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (current.trim()) {
|
|
109
|
+
parts.push(current.trim());
|
|
110
|
+
}
|
|
111
|
+
return parts;
|
|
68
112
|
}
|
|
69
113
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import type { Terminal } from "../terminal/index.ts";
|
|
1
2
|
import type { SendData } from "./types.ts";
|
|
2
3
|
export declare const DeleteFileTool: {
|
|
3
4
|
name: "deleteFile";
|
|
4
5
|
};
|
|
5
|
-
export declare const createDeleteFileTool: ({ workingDir, sendData, }: {
|
|
6
|
+
export declare const createDeleteFileTool: ({ workingDir, sendData, terminal, autoAcceptAll, }: {
|
|
6
7
|
workingDir: string;
|
|
7
8
|
sendData?: SendData;
|
|
9
|
+
terminal?: Terminal;
|
|
10
|
+
autoAcceptAll?: boolean;
|
|
8
11
|
}) => Promise<{
|
|
9
12
|
deleteFile: import("ai").Tool<{
|
|
10
13
|
path: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import { input, select } from "@inquirer/prompts";
|
|
3
4
|
import { tool } from "ai";
|
|
4
5
|
import chalk from "chalk";
|
|
5
6
|
import { z } from "zod";
|
|
@@ -7,8 +8,9 @@ import { joinWorkingDir, validatePath } from "./filesystem-utils.js";
|
|
|
7
8
|
export const DeleteFileTool = {
|
|
8
9
|
name: "deleteFile",
|
|
9
10
|
};
|
|
10
|
-
export const createDeleteFileTool = async ({ workingDir, sendData, }) => {
|
|
11
|
+
export const createDeleteFileTool = async ({ workingDir, sendData, terminal, autoAcceptAll, }) => {
|
|
11
12
|
const allowedDirectory = workingDir;
|
|
13
|
+
let autoAcceptDeletes = autoAcceptAll ?? false;
|
|
12
14
|
return {
|
|
13
15
|
[DeleteFileTool.name]: tool({
|
|
14
16
|
description: "Delete a file permanently.",
|
|
@@ -32,12 +34,54 @@ export const createDeleteFileTool = async ({ workingDir, sendData, }) => {
|
|
|
32
34
|
if (stats.isDirectory()) {
|
|
33
35
|
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
|
34
36
|
}
|
|
35
|
-
|
|
37
|
+
if (terminal) {
|
|
38
|
+
terminal.writeln(`\n${chalk.red.bold("●")} Proposing file deletion: ${chalk.cyan(userPath)}`);
|
|
39
|
+
terminal.lineBreak();
|
|
40
|
+
terminal.writeln("This action cannot be undone.");
|
|
41
|
+
terminal.lineBreak();
|
|
42
|
+
let userChoice;
|
|
43
|
+
if (autoAcceptDeletes) {
|
|
44
|
+
terminal.writeln(chalk.green("✓ Auto-accepting deletions (all future deletions will be accepted)"));
|
|
45
|
+
userChoice = "accept";
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
userChoice = await select({
|
|
49
|
+
message: "What would you like to do with this file?",
|
|
50
|
+
choices: [
|
|
51
|
+
{ name: "Accept and delete this file", value: "accept" },
|
|
52
|
+
{
|
|
53
|
+
name: "Accept all future deletions (including this)",
|
|
54
|
+
value: "accept-all",
|
|
55
|
+
},
|
|
56
|
+
{ name: "Reject this deletion", value: "reject" },
|
|
57
|
+
],
|
|
58
|
+
default: "accept",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
terminal.lineBreak();
|
|
62
|
+
if (userChoice === "accept-all") {
|
|
63
|
+
autoAcceptDeletes = true;
|
|
64
|
+
terminal.writeln(chalk.yellow("✓ Auto-accept mode enabled for all future deletions"));
|
|
65
|
+
terminal.lineBreak();
|
|
66
|
+
}
|
|
67
|
+
if (userChoice === "reject") {
|
|
68
|
+
const reason = await input({ message: "Feedback: " });
|
|
69
|
+
terminal.lineBreak();
|
|
70
|
+
sendData?.({
|
|
71
|
+
id: toolCallId,
|
|
72
|
+
event: "tool-completion",
|
|
73
|
+
data: `Deletion rejected by user. Reason: ${reason}`,
|
|
74
|
+
});
|
|
75
|
+
return `The user rejected this deletion. Reason: ${reason}`;
|
|
76
|
+
}
|
|
77
|
+
// If accepted, proceed to delete file
|
|
78
|
+
}
|
|
79
|
+
// Delete the file
|
|
36
80
|
await fs.unlink(filePath);
|
|
37
81
|
sendData?.({
|
|
38
82
|
id: toolCallId,
|
|
39
83
|
event: "tool-completion",
|
|
40
|
-
data:
|
|
84
|
+
data: "File deleted successfully",
|
|
41
85
|
});
|
|
42
86
|
return `Successfully deleted ${filePath}`;
|
|
43
87
|
}
|
|
@@ -3,6 +3,12 @@ export declare function getDiffStat(): Promise<{
|
|
|
3
3
|
insertions: number;
|
|
4
4
|
deletions: number;
|
|
5
5
|
}>;
|
|
6
|
+
export declare function getGitStatus(): Promise<{
|
|
7
|
+
added: number;
|
|
8
|
+
modified: number;
|
|
9
|
+
deleted: number;
|
|
10
|
+
untracked: number;
|
|
11
|
+
}>;
|
|
6
12
|
export declare const inGitDirectory: () => Promise<boolean>;
|
|
7
13
|
/**
|
|
8
14
|
* Check if there are uncommitted changes
|