@travisennis/acai 0.0.9 → 0.0.10
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 +49 -762
- package/bin/acai +52 -0
- package/dist/agent/index.d.ts +12 -2
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +378 -199
- package/dist/agent/sub-agent.d.ts +23 -0
- package/dist/agent/sub-agent.d.ts.map +1 -0
- package/dist/agent/sub-agent.js +109 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/{cli.js → cli/index.js} +84 -77
- package/dist/cli/stdin.d.ts.map +1 -0
- package/dist/{stdin.js → cli/stdin.js} +11 -0
- package/dist/commands/copy/index.js +2 -2
- package/dist/commands/copy/utils.d.ts.map +1 -1
- package/dist/commands/copy/utils.js +15 -13
- package/dist/commands/generate-rules/index.d.ts +1 -1
- package/dist/commands/generate-rules/index.d.ts.map +1 -1
- package/dist/commands/generate-rules/index.js +16 -101
- package/dist/commands/generate-rules/service.d.ts +21 -0
- package/dist/commands/generate-rules/service.d.ts.map +1 -0
- package/dist/commands/generate-rules/service.js +103 -0
- package/dist/commands/handoff/index.js +2 -2
- package/dist/commands/health/index.js +1 -1
- package/dist/commands/health/utils.d.ts.map +1 -1
- package/dist/commands/health/utils.js +6 -0
- package/dist/commands/history/index.d.ts +1 -1
- package/dist/commands/history/index.d.ts.map +1 -1
- package/dist/commands/history/index.js +17 -18
- package/dist/commands/history/types.d.ts +38 -0
- package/dist/commands/history/types.d.ts.map +1 -1
- package/dist/commands/history/utils.d.ts.map +1 -1
- package/dist/commands/history/utils.js +63 -58
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +3 -8
- package/dist/commands/init-project/index.d.ts.map +1 -1
- package/dist/commands/init-project/index.js +3 -3
- package/dist/commands/init-project/utils.d.ts.map +1 -1
- package/dist/commands/init-project/utils.js +10 -2
- package/dist/commands/list-tools/index.d.ts.map +1 -1
- package/dist/commands/list-tools/index.js +7 -31
- package/dist/commands/manager.d.ts +2 -2
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +55 -33
- package/dist/commands/model/index.d.ts.map +1 -1
- package/dist/commands/model/index.js +20 -151
- package/dist/commands/model/model-panel.d.ts +4 -0
- package/dist/commands/model/model-panel.d.ts.map +1 -0
- package/dist/commands/model/model-panel.js +144 -0
- package/dist/commands/paste/index.d.ts.map +1 -1
- package/dist/commands/paste/index.js +59 -62
- package/dist/commands/paste/utils.d.ts.map +1 -1
- package/dist/commands/paste/utils.js +88 -58
- package/dist/commands/pickup/index.d.ts.map +1 -1
- package/dist/commands/pickup/index.js +6 -3
- package/dist/commands/pickup/utils.js +3 -3
- package/dist/commands/resources/index.d.ts.map +1 -1
- package/dist/commands/resources/index.js +33 -50
- package/dist/commands/review/index.d.ts.map +1 -1
- package/dist/commands/review/index.js +3 -117
- package/dist/commands/review/review-panel.d.ts +3 -0
- package/dist/commands/review/review-panel.d.ts.map +1 -0
- package/dist/commands/review/review-panel.js +186 -0
- package/dist/commands/review/utils.d.ts +9 -0
- package/dist/commands/review/utils.d.ts.map +1 -1
- package/dist/commands/review/utils.js +127 -68
- package/dist/commands/session/index.d.ts +1 -1
- package/dist/commands/session/index.d.ts.map +1 -1
- package/dist/commands/session/index.js +124 -135
- package/dist/commands/shell/index.d.ts.map +1 -1
- package/dist/commands/shell/index.js +16 -1
- package/dist/commands/types.d.ts +2 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/{config.d.ts → config/index.d.ts} +20 -9
- package/dist/config/index.d.ts.map +1 -0
- package/dist/{config.js → config/index.js} +43 -42
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +75 -55
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -141
- package/dist/middleware/cache.d.ts.map +1 -1
- package/dist/middleware/cache.js +18 -36
- package/dist/models/ai-config.d.ts +1 -0
- package/dist/models/ai-config.d.ts.map +1 -1
- package/dist/models/ai-config.js +4 -3
- package/dist/models/anthropic-provider.d.ts +2 -5
- package/dist/models/anthropic-provider.d.ts.map +1 -1
- package/dist/models/anthropic-provider.js +3 -70
- package/dist/models/deepseek-provider.d.ts +1 -0
- package/dist/models/deepseek-provider.d.ts.map +1 -1
- package/dist/models/google-provider.d.ts +2 -3
- package/dist/models/google-provider.d.ts.map +1 -1
- package/dist/models/google-provider.js +0 -26
- package/dist/models/groq-provider.d.ts +1 -0
- package/dist/models/groq-provider.d.ts.map +1 -1
- package/dist/models/manager.d.ts +13 -2
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +20 -8
- package/dist/models/openai-provider.d.ts +2 -5
- package/dist/models/openai-provider.d.ts.map +1 -1
- package/dist/models/openai-provider.js +0 -52
- package/dist/models/opencode-zen-provider.d.ts +7 -3
- package/dist/models/opencode-zen-provider.d.ts.map +1 -1
- package/dist/models/opencode-zen-provider.js +49 -10
- package/dist/models/openrouter-provider.d.ts +24 -31
- package/dist/models/openrouter-provider.d.ts.map +1 -1
- package/dist/models/openrouter-provider.js +84 -182
- package/dist/models/providers.d.ts +1 -1
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/xai-provider.d.ts +4 -3
- package/dist/models/xai-provider.d.ts.map +1 -1
- package/dist/models/xai-provider.js +18 -18
- package/dist/modes/manager.d.ts +23 -0
- package/dist/modes/manager.d.ts.map +1 -0
- package/dist/modes/manager.js +77 -0
- package/dist/modes/prompts.d.ts +2 -0
- package/dist/modes/prompts.d.ts.map +1 -0
- package/dist/modes/prompts.js +143 -0
- package/dist/prompts/mentions.d.ts +11 -0
- package/dist/prompts/mentions.d.ts.map +1 -0
- package/dist/{mentions.js → prompts/mentions.js} +21 -80
- package/dist/{prompts.d.ts → prompts/system-prompt.d.ts} +7 -2
- package/dist/prompts/system-prompt.d.ts.map +1 -0
- package/dist/{prompts.js → prompts/system-prompt.js} +31 -16
- package/dist/repl/index.d.ts +174 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/{repl-new.js → repl/index.js} +389 -76
- package/dist/repl/project-status.d.ts +1 -0
- package/dist/repl/project-status.d.ts.map +1 -1
- package/dist/repl/project-status.js +4 -1
- package/dist/sessions/manager.d.ts +93 -1
- package/dist/sessions/manager.d.ts.map +1 -1
- package/dist/sessions/manager.js +262 -9
- package/dist/sessions/summary.d.ts +4 -0
- package/dist/sessions/summary.d.ts.map +1 -0
- package/dist/sessions/summary.js +30 -0
- package/dist/{skills.d.ts → skills/index.d.ts} +14 -2
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +294 -0
- package/dist/subagents/index.d.ts +15 -0
- package/dist/subagents/index.d.ts.map +1 -0
- package/dist/subagents/index.js +231 -0
- package/dist/terminal/control.d.ts +1 -1
- package/dist/terminal/control.d.ts.map +1 -1
- package/dist/terminal/control.js +3 -3
- package/dist/terminal/east-asian-width.d.ts.map +1 -1
- package/dist/terminal/east-asian-width.js +404 -351
- package/dist/terminal/keys.d.ts +17 -0
- package/dist/terminal/keys.d.ts.map +1 -1
- package/dist/terminal/keys.js +37 -0
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +24 -12
- package/dist/terminal/string-width.d.ts.map +1 -1
- package/dist/terminal/string-width.js +25 -27
- package/dist/terminal/style.d.ts.map +1 -1
- package/dist/terminal/style.js +4 -7
- package/dist/terminal/supports-color.d.ts.map +1 -1
- package/dist/terminal/supports-color.js +41 -27
- package/dist/terminal/table/cell.d.ts +12 -0
- package/dist/terminal/table/cell.d.ts.map +1 -1
- package/dist/terminal/table/cell.js +40 -25
- package/dist/terminal/table/layout-manager.d.ts.map +1 -1
- package/dist/terminal/table/layout-manager.js +100 -68
- package/dist/terminal/table/utils.d.ts.map +1 -1
- package/dist/terminal/table/utils.js +17 -10
- package/dist/terminal/wrap-ansi.d.ts.map +1 -1
- package/dist/terminal/wrap-ansi.js +172 -103
- package/dist/tokens/tracker.d.ts +1 -0
- package/dist/tokens/tracker.d.ts.map +1 -1
- package/dist/tokens/tracker.js +3 -0
- package/dist/tools/agent.d.ts +27 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/agent.js +81 -0
- package/dist/tools/bash.d.ts +4 -3
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +324 -137
- package/dist/tools/code-search.d.ts +41 -0
- package/dist/tools/code-search.d.ts.map +1 -0
- package/dist/tools/code-search.js +195 -0
- package/dist/tools/directory-tree.d.ts +3 -3
- package/dist/tools/directory-tree.d.ts.map +1 -1
- package/dist/tools/directory-tree.js +8 -5
- package/dist/tools/dynamic-tool-loader.d.ts +2 -5
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +20 -4
- package/dist/tools/edit-file.d.ts +7 -7
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +164 -66
- package/dist/tools/glob.d.ts +6 -6
- package/dist/tools/glob.d.ts.map +1 -1
- package/dist/tools/glob.js +95 -55
- package/dist/tools/grep.d.ts +15 -12
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +300 -192
- package/dist/tools/index.d.ts +143 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +39 -24
- package/dist/tools/ls.d.ts +2 -2
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +7 -5
- package/dist/tools/read-file.d.ts +3 -3
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +74 -34
- package/dist/tools/save-file.d.ts +3 -3
- package/dist/tools/save-file.d.ts.map +1 -1
- package/dist/tools/save-file.js +11 -11
- package/dist/tools/skill.d.ts +23 -0
- package/dist/tools/skill.d.ts.map +1 -0
- package/dist/tools/skill.js +65 -0
- package/dist/tools/think.d.ts.map +1 -1
- package/dist/tools/think.js +2 -9
- package/dist/tools/utils.d.ts +2 -0
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +12 -0
- package/dist/tools/web-fetch.d.ts +62 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +429 -0
- package/dist/tools/web-search.d.ts +62 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +226 -0
- package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
- package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/attachment-provider.js +25 -78
- package/dist/tui/autocomplete/base-provider.d.ts +1 -0
- package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
- package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/combined-provider.js +3 -17
- package/dist/tui/autocomplete/command-provider.d.ts +1 -0
- package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/command-provider.js +3 -0
- package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
- package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/file-search-provider.js +36 -16
- package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
- package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/skill-provider.js +49 -0
- package/dist/tui/autocomplete.d.ts +2 -2
- package/dist/tui/autocomplete.d.ts.map +1 -1
- package/dist/tui/autocomplete.js +3 -5
- package/dist/tui/components/assistant-message.d.ts.map +1 -1
- package/dist/tui/components/assistant-message.js +0 -4
- package/dist/tui/components/editor.d.ts +16 -2
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +211 -237
- package/dist/tui/components/footer.d.ts +6 -4
- package/dist/tui/components/footer.d.ts.map +1 -1
- package/dist/tui/components/footer.js +49 -25
- package/dist/tui/components/markdown.d.ts +8 -5
- package/dist/tui/components/markdown.d.ts.map +1 -1
- package/dist/tui/components/markdown.js +57 -39
- package/dist/tui/components/modal.d.ts.map +1 -1
- package/dist/tui/components/modal.js +35 -33
- package/dist/tui/components/notification.d.ts +13 -2
- package/dist/tui/components/notification.d.ts.map +1 -1
- package/dist/tui/components/notification.js +36 -2
- package/dist/tui/components/progress-bar.js +1 -1
- package/dist/tui/components/select-list.d.ts +1 -0
- package/dist/tui/components/select-list.d.ts.map +1 -1
- package/dist/tui/components/select-list.js +14 -11
- package/dist/tui/components/text.d.ts +16 -0
- package/dist/tui/components/text.d.ts.map +1 -1
- package/dist/tui/components/text.js +72 -57
- package/dist/tui/components/thinking-block.d.ts +9 -0
- package/dist/tui/components/thinking-block.d.ts.map +1 -1
- package/dist/tui/components/thinking-block.js +43 -11
- package/dist/tui/components/tool-execution.d.ts +5 -1
- package/dist/tui/components/tool-execution.d.ts.map +1 -1
- package/dist/tui/components/tool-execution.js +19 -10
- package/dist/tui/components/user-message.d.ts.map +1 -1
- package/dist/tui/components/user-message.js +0 -3
- package/dist/tui/components/welcome.js +2 -2
- package/dist/tui/terminal.d.ts.map +1 -1
- package/dist/tui/terminal.js +10 -2
- package/dist/tui/tui.d.ts +42 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +157 -41
- package/dist/utils/bash/parse.d.ts +19 -0
- package/dist/utils/bash/parse.d.ts.map +1 -0
- package/dist/utils/bash/parse.js +223 -0
- package/dist/utils/bash/quote.d.ts +6 -0
- package/dist/utils/bash/quote.d.ts.map +1 -0
- package/dist/utils/bash/quote.js +23 -0
- package/dist/utils/bash.d.ts.map +1 -1
- package/dist/utils/bash.js +211 -126
- package/dist/utils/command-protection.d.ts +28 -0
- package/dist/utils/command-protection.d.ts.map +1 -0
- package/dist/utils/command-protection.js +324 -0
- package/dist/utils/dedent.d.ts.map +1 -0
- package/dist/utils/env-expand.d.ts +2 -0
- package/dist/utils/env-expand.d.ts.map +1 -0
- package/dist/utils/env-expand.js +8 -0
- package/dist/utils/filesystem/path-display.d.ts +11 -0
- package/dist/utils/filesystem/path-display.d.ts.map +1 -0
- package/dist/utils/filesystem/path-display.js +32 -0
- package/dist/utils/filesystem/security.d.ts +2 -2
- package/dist/utils/filesystem/security.d.ts.map +1 -1
- package/dist/utils/filesystem/security.js +28 -30
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/{formatting.js → utils/formatting.js} +1 -1
- package/dist/utils/git.d.ts +4 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +30 -0
- package/dist/utils/glob.d.ts +1 -1
- package/dist/utils/glob.d.ts.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/{logger.js → utils/logger.js} +1 -1
- package/dist/utils/parsing.d.ts.map +1 -0
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +90 -37
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +24 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/{version.js → utils/version.js} +1 -1
- package/package.json +34 -25
- package/dist/cli.d.ts +0 -23
- package/dist/cli.d.ts.map +0 -1
- package/dist/commands/exit/index.d.ts +0 -10
- package/dist/commands/exit/index.d.ts.map +0 -1
- package/dist/commands/exit/index.js +0 -21
- package/dist/commands/exit/types.d.ts +0 -8
- package/dist/commands/exit/types.d.ts.map +0 -1
- package/dist/commands/exit/types.js +0 -1
- package/dist/commands/exit/utils.d.ts +0 -2
- package/dist/commands/exit/utils.d.ts.map +0 -1
- package/dist/commands/exit/utils.js +0 -13
- package/dist/commands/prompt/index.d.ts +0 -5
- package/dist/commands/prompt/index.d.ts.map +0 -1
- package/dist/commands/prompt/index.js +0 -122
- package/dist/commands/prompt/types.d.ts +0 -15
- package/dist/commands/prompt/types.d.ts.map +0 -1
- package/dist/commands/prompt/types.js +0 -1
- package/dist/commands/prompt/utils.d.ts +0 -12
- package/dist/commands/prompt/utils.d.ts.map +0 -1
- package/dist/commands/prompt/utils.js +0 -107
- package/dist/commands/reset/index.d.ts +0 -3
- package/dist/commands/reset/index.d.ts.map +0 -1
- package/dist/commands/reset/index.js +0 -25
- package/dist/commands/reset/types.d.ts +0 -1
- package/dist/commands/reset/types.d.ts.map +0 -1
- package/dist/commands/reset/types.js +0 -3
- package/dist/commands/save/index.d.ts +0 -3
- package/dist/commands/save/index.d.ts.map +0 -1
- package/dist/commands/save/index.js +0 -19
- package/dist/config.d.ts.map +0 -1
- package/dist/dedent.d.ts.map +0 -1
- package/dist/formatting.d.ts.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/mentions.d.ts +0 -14
- package/dist/mentions.d.ts.map +0 -1
- package/dist/parsing.d.ts.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/repl-new.d.ts +0 -65
- package/dist/repl-new.d.ts.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js +0 -233
- package/dist/stdin.d.ts.map +0 -1
- package/dist/tui/autocomplete/path-provider.d.ts +0 -21
- package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
- package/dist/tui/autocomplete/path-provider.js +0 -164
- package/dist/version.d.ts.map +0 -1
- /package/dist/{stdin.d.ts → cli/stdin.d.ts} +0 -0
- /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
- /package/dist/{dedent.js → utils/dedent.js} +0 -0
- /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
- /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
- /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
- /package/dist/{parsing.js → utils/parsing.js} +0 -0
- /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
package/dist/utils/bash.js
CHANGED
|
@@ -2,63 +2,158 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isPathWithinAllowedDirs } from "./filesystem/security.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let inQuotes = false;
|
|
11
|
-
let quoteChar = "";
|
|
12
|
-
for (let i = 0; i < command.length; i++) {
|
|
13
|
-
const char = command[i] ?? "";
|
|
14
|
-
if ((char === '"' || char === "'") && !inQuotes) {
|
|
15
|
-
inQuotes = true;
|
|
16
|
-
quoteChar = char;
|
|
17
|
-
current += char;
|
|
5
|
+
function handleNormalMode(state, char, command, tokens) {
|
|
6
|
+
if (/\s/.test(char)) {
|
|
7
|
+
if (state.current) {
|
|
8
|
+
tokens.push(state.current);
|
|
9
|
+
state.current = "";
|
|
18
10
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (char === "'") {
|
|
14
|
+
state.mode = "single";
|
|
15
|
+
state.current += char;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (char === '"') {
|
|
19
|
+
state.mode = "double";
|
|
20
|
+
state.current += char;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (char === "\\") {
|
|
24
|
+
const next = command[state.i + 1];
|
|
25
|
+
if (next !== undefined) {
|
|
26
|
+
state.current += char + next;
|
|
27
|
+
state.i++;
|
|
28
|
+
return;
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
}
|
|
31
|
+
state.current += char;
|
|
32
|
+
}
|
|
33
|
+
function handleSingleQuoteMode(state, char) {
|
|
34
|
+
state.current += char;
|
|
35
|
+
if (char === "'")
|
|
36
|
+
state.mode = "normal";
|
|
37
|
+
}
|
|
38
|
+
function handleDoubleQuoteMode(state, char, command) {
|
|
39
|
+
state.current += char;
|
|
40
|
+
if (char === "\\") {
|
|
41
|
+
const next = command[state.i + 1];
|
|
42
|
+
if (next !== undefined) {
|
|
43
|
+
state.current += next;
|
|
44
|
+
state.i++;
|
|
29
45
|
}
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (char === '"')
|
|
49
|
+
state.mode = "normal";
|
|
50
|
+
}
|
|
51
|
+
// Tokenize shell command respecting quotes and escapes
|
|
52
|
+
function tokenizeShellWords(command) {
|
|
53
|
+
const tokens = [];
|
|
54
|
+
const state = { current: "", mode: "normal", i: 0 };
|
|
55
|
+
for (state.i = 0; state.i < command.length; state.i++) {
|
|
56
|
+
const char = command[state.i] ?? "";
|
|
57
|
+
if (state.mode === "normal")
|
|
58
|
+
handleNormalMode(state, char, command, tokens);
|
|
59
|
+
else if (state.mode === "single")
|
|
60
|
+
handleSingleQuoteMode(state, char);
|
|
61
|
+
else
|
|
62
|
+
handleDoubleQuoteMode(state, char, command);
|
|
63
|
+
}
|
|
64
|
+
if (state.current)
|
|
65
|
+
tokens.push(state.current);
|
|
66
|
+
return tokens;
|
|
67
|
+
}
|
|
68
|
+
// Strip surrounding quotes from a token
|
|
69
|
+
function stripQuotes(token) {
|
|
70
|
+
if ((token.startsWith('"') && token.endsWith('"')) ||
|
|
71
|
+
(token.startsWith("'") && token.endsWith("'"))) {
|
|
72
|
+
return token.slice(1, -1);
|
|
73
|
+
}
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
// Check if a token is fully quoted (content should not be path-validated)
|
|
77
|
+
function isFullyQuoted(token) {
|
|
78
|
+
if (token.length < 2)
|
|
79
|
+
return false;
|
|
80
|
+
const first = token[0];
|
|
81
|
+
const last = token[token.length - 1];
|
|
82
|
+
return (first === '"' && last === '"') || (first === "'" && last === "'");
|
|
83
|
+
}
|
|
84
|
+
// Compute which tokens should be skipped from path validation
|
|
85
|
+
function computeSkipTokenMask(tokens) {
|
|
86
|
+
const skip = new Array(tokens.length).fill(false);
|
|
87
|
+
const bin = stripQuotes(tokens[0] ?? "");
|
|
88
|
+
const sub = stripQuotes(tokens[1] ?? "");
|
|
89
|
+
// Commands where -m/--message is a string argument (not a path)
|
|
90
|
+
const gitMessageSubs = new Set(["commit", "merge", "tag", "revert", "notes"]);
|
|
91
|
+
if (bin === "git" && gitMessageSubs.has(sub)) {
|
|
92
|
+
let seenDoubleDash = false;
|
|
93
|
+
for (let i = 2; i < tokens.length; i++) {
|
|
94
|
+
const t = stripQuotes(tokens[i] ?? "");
|
|
95
|
+
if (t === "--") {
|
|
96
|
+
seenDoubleDash = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (seenDoubleDash)
|
|
100
|
+
continue;
|
|
101
|
+
// --message=<msg> - skip this entire token
|
|
102
|
+
if (t.startsWith("--message=")) {
|
|
103
|
+
skip[i] = true;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
// -m<msg> (attached message) - skip this entire token
|
|
107
|
+
if (/^-m.+/.test(t)) {
|
|
108
|
+
skip[i] = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// -m or --message consumes next token
|
|
112
|
+
if (t === "-m" || t === "--message") {
|
|
113
|
+
if (i + 1 < tokens.length)
|
|
114
|
+
skip[i + 1] = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Combined short opts containing m, e.g. -am, -avm
|
|
118
|
+
if (/^-[^-]+$/.test(t) && t.includes("m")) {
|
|
119
|
+
if (i + 1 < tokens.length)
|
|
120
|
+
skip[i + 1] = true;
|
|
121
|
+
}
|
|
32
122
|
}
|
|
33
123
|
}
|
|
34
|
-
|
|
35
|
-
|
|
124
|
+
return skip;
|
|
125
|
+
}
|
|
126
|
+
// Validate path arguments to ensure they're within the project
|
|
127
|
+
export function validatePaths(command, allowedDirs, cwd) {
|
|
128
|
+
const tokens = tokenizeShellWords(command);
|
|
129
|
+
const skip = computeSkipTokenMask(tokens);
|
|
36
130
|
// Check each token that looks like a path
|
|
37
131
|
for (let i = 1; i < tokens.length; i++) {
|
|
38
|
-
// Skip the command itself
|
|
39
132
|
const token = tokens[i];
|
|
40
133
|
if (!token)
|
|
41
134
|
continue;
|
|
135
|
+
// Skip tokens marked by command-aware logic
|
|
136
|
+
if (skip[i])
|
|
137
|
+
continue;
|
|
138
|
+
// Skip fully quoted tokens - they're string arguments, not paths
|
|
139
|
+
// (paths are typically unquoted or only quoted to handle spaces)
|
|
140
|
+
if (isFullyQuoted(token) && token.includes("\n")) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
42
143
|
// Remove quotes for path checking
|
|
43
|
-
const cleanToken = token
|
|
144
|
+
const cleanToken = stripQuotes(token);
|
|
44
145
|
// Skip if it's clearly not a path
|
|
45
146
|
if (cleanToken.startsWith("-") ||
|
|
46
147
|
cleanToken.includes("://") ||
|
|
47
148
|
(!cleanToken.includes("/") && cleanToken !== "~")) {
|
|
48
149
|
continue;
|
|
49
150
|
}
|
|
50
|
-
// Skip git commit messages and other special cases
|
|
51
|
-
const prevToken = tokens[i - 1]?.replace(/^['"]|['"]$/g, "");
|
|
52
|
-
if (prevToken === "-m" || prevToken === "--message") {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
151
|
try {
|
|
56
152
|
// Expand ~ to home directory for proper validation
|
|
57
153
|
const expandedToken = cleanToken.startsWith("~/") || cleanToken === "~"
|
|
58
154
|
? path.join(os.homedir(), cleanToken.slice(1))
|
|
59
155
|
: cleanToken;
|
|
60
156
|
const resolvedPath = path.resolve(cwd, expandedToken);
|
|
61
|
-
// Allow access to explicitly allowed paths
|
|
62
157
|
if (!isPathWithinAllowedDirs(resolvedPath, allowedDirs)) {
|
|
63
158
|
return {
|
|
64
159
|
isValid: false,
|
|
@@ -125,6 +220,87 @@ export const resolveCwd = (cwdInput, workingDir, allowedDirs) => {
|
|
|
125
220
|
}
|
|
126
221
|
return target;
|
|
127
222
|
};
|
|
223
|
+
const mutatingBinaries = new Set([
|
|
224
|
+
"rm",
|
|
225
|
+
"mv",
|
|
226
|
+
"cp",
|
|
227
|
+
"mkdir",
|
|
228
|
+
"rmdir",
|
|
229
|
+
"touch",
|
|
230
|
+
"chmod",
|
|
231
|
+
"chown",
|
|
232
|
+
"ln",
|
|
233
|
+
"truncate",
|
|
234
|
+
"dd",
|
|
235
|
+
"tee",
|
|
236
|
+
]);
|
|
237
|
+
const npmMutating = new Set([
|
|
238
|
+
"install",
|
|
239
|
+
"uninstall",
|
|
240
|
+
"update",
|
|
241
|
+
"ci",
|
|
242
|
+
"publish",
|
|
243
|
+
"link",
|
|
244
|
+
"dedupe",
|
|
245
|
+
"prune",
|
|
246
|
+
"rebuild",
|
|
247
|
+
"add",
|
|
248
|
+
]);
|
|
249
|
+
const gitMutating = new Set([
|
|
250
|
+
"add",
|
|
251
|
+
"am",
|
|
252
|
+
"apply",
|
|
253
|
+
"branch",
|
|
254
|
+
"checkout",
|
|
255
|
+
"switch",
|
|
256
|
+
"cherry-pick",
|
|
257
|
+
"clean",
|
|
258
|
+
"commit",
|
|
259
|
+
"merge",
|
|
260
|
+
"mv",
|
|
261
|
+
"pull",
|
|
262
|
+
"push",
|
|
263
|
+
"rebase",
|
|
264
|
+
"reset",
|
|
265
|
+
"revert",
|
|
266
|
+
"stash",
|
|
267
|
+
"tag",
|
|
268
|
+
"worktree",
|
|
269
|
+
"submodule",
|
|
270
|
+
"config",
|
|
271
|
+
]);
|
|
272
|
+
const actionMutating = new Set(["create", "update", "upgrade", "install"]);
|
|
273
|
+
const packageManagers = new Set(["npm", "pnpm", "yarn"]);
|
|
274
|
+
function isSegmentMutating(seg) {
|
|
275
|
+
const tokens = seg.split(/\s+/);
|
|
276
|
+
if (tokens.length === 0)
|
|
277
|
+
return false;
|
|
278
|
+
const bin = tokens[0];
|
|
279
|
+
if (!bin)
|
|
280
|
+
return false;
|
|
281
|
+
if (tokens.some((t) => actionMutating.has(t))) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
if (bin === "sed" && tokens.some((t) => /^-i/.test(t))) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
if (mutatingBinaries.has(bin)) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
if (bin === "git" && tokens.length > 1) {
|
|
291
|
+
const sub = tokens[1];
|
|
292
|
+
if (typeof sub === "string" && gitMutating.has(sub)) {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (packageManagers.has(bin) && tokens.length > 1) {
|
|
297
|
+
const sub = tokens[1];
|
|
298
|
+
if (typeof sub === "string" && npmMutating.has(sub)) {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
128
304
|
export const isMutatingCommand = (rawCommand) => {
|
|
129
305
|
const command = rawCommand.trim();
|
|
130
306
|
// Redirections that write to disk
|
|
@@ -136,96 +312,5 @@ export const isMutatingCommand = (rawCommand) => {
|
|
|
136
312
|
.split(/\s*(?:&&|\|\||;|\|)\s*/)
|
|
137
313
|
.map((s) => s.trim())
|
|
138
314
|
.filter((s) => s.length > 0);
|
|
139
|
-
|
|
140
|
-
"rm",
|
|
141
|
-
"mv",
|
|
142
|
-
"cp",
|
|
143
|
-
"mkdir",
|
|
144
|
-
"rmdir",
|
|
145
|
-
"touch",
|
|
146
|
-
"chmod",
|
|
147
|
-
"chown",
|
|
148
|
-
"ln",
|
|
149
|
-
"truncate",
|
|
150
|
-
"dd",
|
|
151
|
-
"tee",
|
|
152
|
-
]);
|
|
153
|
-
const npmMutating = new Set([
|
|
154
|
-
"install",
|
|
155
|
-
"uninstall",
|
|
156
|
-
"update",
|
|
157
|
-
"ci",
|
|
158
|
-
"publish",
|
|
159
|
-
"link",
|
|
160
|
-
"dedupe",
|
|
161
|
-
"prune",
|
|
162
|
-
"rebuild",
|
|
163
|
-
"add",
|
|
164
|
-
]);
|
|
165
|
-
const gitMutating = new Set([
|
|
166
|
-
"add",
|
|
167
|
-
"am",
|
|
168
|
-
"apply",
|
|
169
|
-
"branch",
|
|
170
|
-
"checkout",
|
|
171
|
-
"switch",
|
|
172
|
-
"cherry-pick",
|
|
173
|
-
"clean",
|
|
174
|
-
"commit",
|
|
175
|
-
"merge",
|
|
176
|
-
"mv",
|
|
177
|
-
"pull",
|
|
178
|
-
"push",
|
|
179
|
-
"rebase",
|
|
180
|
-
"reset",
|
|
181
|
-
"revert",
|
|
182
|
-
"stash",
|
|
183
|
-
"tag",
|
|
184
|
-
"worktree",
|
|
185
|
-
"submodule",
|
|
186
|
-
"config",
|
|
187
|
-
]);
|
|
188
|
-
// Generic action words that should be considered mutating when present in the command
|
|
189
|
-
const actionMutating = new Set(["create", "update", "upgrade", "install"]);
|
|
190
|
-
for (const seg of segments) {
|
|
191
|
-
const tokens = seg.split(/\s+/);
|
|
192
|
-
if (tokens.length === 0)
|
|
193
|
-
continue;
|
|
194
|
-
const bin = tokens[0];
|
|
195
|
-
if (!bin)
|
|
196
|
-
continue;
|
|
197
|
-
// If any token is an action-like mutating word, consider mutating
|
|
198
|
-
if (tokens.some((t) => actionMutating.has(t))) {
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
|
-
// sed -i is mutating
|
|
202
|
-
if (bin === "sed") {
|
|
203
|
-
if (tokens.some((t) => /^-i/.test(t))) {
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
// sed without -i is not mutating
|
|
207
|
-
}
|
|
208
|
-
if (mutatingBinaries.has(bin)) {
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
if (bin === "git" && tokens.length > 1) {
|
|
212
|
-
const sub = tokens[1];
|
|
213
|
-
if (typeof sub === "string" && gitMutating.has(sub)) {
|
|
214
|
-
return true;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (bin === "npm" && tokens.length > 1) {
|
|
218
|
-
const sub = tokens[1];
|
|
219
|
-
if (typeof sub === "string" && npmMutating.has(sub)) {
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if ((bin === "pnpm" || bin === "yarn") && tokens.length > 1) {
|
|
224
|
-
const sub = tokens[1];
|
|
225
|
-
if (typeof sub === "string" && npmMutating.has(sub)) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return false;
|
|
315
|
+
return segments.some((seg) => isSegmentMutating(seg));
|
|
231
316
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Protection Module
|
|
3
|
+
* Detects and blocks destructive commands that could cause data loss
|
|
4
|
+
*/
|
|
5
|
+
export interface BlockedCommandResult {
|
|
6
|
+
blocked: true;
|
|
7
|
+
reason: string;
|
|
8
|
+
command: string;
|
|
9
|
+
tip: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SafeCommandResult {
|
|
12
|
+
blocked: false;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result type for command safety check
|
|
16
|
+
*/
|
|
17
|
+
export type CommandSafetyResult = BlockedCommandResult | SafeCommandResult;
|
|
18
|
+
/**
|
|
19
|
+
* Detects if a command is destructive and should be blocked
|
|
20
|
+
* @param command - The full command string to check
|
|
21
|
+
* @returns BlockedCommandResult if destructive, SafeCommandResult if safe
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectDestructiveCommand(command: string): CommandSafetyResult;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a user-friendly blocked command message
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatBlockedCommandMessage(result: BlockedCommandResult): string;
|
|
28
|
+
//# sourceMappingURL=command-protection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-protection.d.ts","sourceRoot":"","sources":["../../source/utils/command-protection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,GAAG,iBAAiB,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CA4B7E;AA8UD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CAQR"}
|