@travisennis/acai 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mentions.js +1 -1
- package/dist/prompts.js +6 -4
- package/dist/tools/bash.js +2 -2
- package/dist/tools/code-interpreter.d.ts +1 -0
- package/dist/tools/code-interpreter.js +95 -5
- package/dist/tools/command-validation.d.ts +0 -1
- package/dist/tools/command-validation.js +16 -84
- package/dist/tools/index.d.ts +2 -0
- package/dist/utils/process.d.ts +8 -0
- package/dist/utils/process.js +87 -3
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/mentions.js
CHANGED
package/dist/prompts.js
CHANGED
|
@@ -85,10 +85,12 @@ function toolUsage() {
|
|
|
85
85
|
- Outline multi-step tasks before execution
|
|
86
86
|
|
|
87
87
|
### Bash Commands (\`${BashTool.name}\`)
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
88
|
+
- Shell is disabled. Do NOT use: |, >, >>, <, <<, ;, &&, ||, &, \`...\`, $().
|
|
89
|
+
- Run a single allowed command with quoted args; no command substitution or chaining.
|
|
90
|
+
- Compose multi-step flows via multiple tool calls or program flags (rg, jq, grep options, etc.).
|
|
91
|
+
- For large gh/git messages, prefer --message-file instead of inlining big strings.
|
|
92
|
+
- Commands execute only within the project directory; always use absolute paths.
|
|
93
|
+
- Avoid interactive commands; prefer non-interactive flags (e.g., npm init -y).
|
|
92
94
|
|
|
93
95
|
### Code Interpreter (\`${CodeInterpreterTool.name}\`)
|
|
94
96
|
- Executes JavaScript code in a separate Node.js process using Node's Permission Model
|
package/dist/tools/bash.js
CHANGED
|
@@ -116,7 +116,7 @@ export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, auto
|
|
|
116
116
|
let autoAcceptCommands = autoAcceptAll;
|
|
117
117
|
return {
|
|
118
118
|
[BashTool.name]: tool({
|
|
119
|
-
description: `Execute
|
|
119
|
+
description: `Execute commands without a shell. Restrictions: no pipes (|), redirects (>, >>, <, <<), chaining (&&, ||, ;), background (&), or command substitution (\`...\`, $()). Pass a single allowed base command with quoted args. Compose by running multiple tool calls or using program flags (e.g., rg, jq). Commands execute only within the project directory. Always use absolute paths. Allowed commands: ${ALLOWED_COMMANDS.join(", ")}.`,
|
|
120
120
|
inputSchema: z.object({
|
|
121
121
|
command: z.string().describe("Full CLI command to execute."),
|
|
122
122
|
cwd: z
|
|
@@ -214,7 +214,7 @@ export const createBashTool = ({ baseDir, sendData, tokenCounter, terminal, auto
|
|
|
214
214
|
const result = await executeCommand(command, {
|
|
215
215
|
cwd: safeCwd,
|
|
216
216
|
timeout: safeTimeout,
|
|
217
|
-
shell:
|
|
217
|
+
shell: false,
|
|
218
218
|
throwOnError: false,
|
|
219
219
|
});
|
|
220
220
|
if (result.signal === "SIGTERM") {
|
|
@@ -8,7 +8,12 @@ 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.
|
|
11
|
+
const toolDescription = `Executes JavaScript or Typescript code in a separate Node.js process using Node's Permission Model.
|
|
12
|
+
|
|
13
|
+
⚠️ **IMPORTANT TYPE SELECTION**:
|
|
14
|
+
- Use "JavaScript" for plain JavaScript code (no TypeScript syntax)
|
|
15
|
+
- Use "Typescript" for code containing interfaces, type annotations, generics, etc.
|
|
16
|
+
- Code type is automatically validated before execution
|
|
12
17
|
|
|
13
18
|
⚠️ **IMPORTANT**: This tool uses ES Modules (ESM) only.
|
|
14
19
|
- Use \`import\` statements, NOT \`require()\`
|
|
@@ -18,12 +23,85 @@ const toolDescription = `Executes JavaScript code in a separate Node.js process
|
|
|
18
23
|
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
24
|
|
|
20
25
|
Timeout defaults to 5 seconds and can be extended up to 60 seconds.`;
|
|
26
|
+
/**
|
|
27
|
+
* Detects if code contains TypeScript syntax patterns
|
|
28
|
+
*/
|
|
29
|
+
function containsTypeScriptSyntax(code) {
|
|
30
|
+
// Common TypeScript patterns that don't exist in JavaScript
|
|
31
|
+
const tsPatterns = [
|
|
32
|
+
// Type annotations
|
|
33
|
+
/:\s*[a-zA-Z]\w*(?:\s*\[])?\s*(?=[,;=)])/g, // Type annotations after variables/parameters (including any, string[], etc.)
|
|
34
|
+
/:\s*\{[^}]*\}\s*(?=[,;=)])/g, // Object type annotations
|
|
35
|
+
/:\s*\([^)]*\)\s*=>/g, // Function type annotations
|
|
36
|
+
// Type declarations
|
|
37
|
+
/^\s*interface\s+\w+/gm, // Interface declarations
|
|
38
|
+
/^\s*type\s+\w+\s*=/gm, // Type aliases
|
|
39
|
+
/^\s*enum\s+\w+/gm, // Enum declarations
|
|
40
|
+
// Generic types
|
|
41
|
+
/<\s*[A-Z]\w*(?:\s*,\s*[A-Z]\w*)*\s*>/g, // Generic type parameters
|
|
42
|
+
/\w+\s*<\s*[^<>]+?\s*>/g, // Generic type usage
|
|
43
|
+
// TypeScript-specific keywords (in specific contexts)
|
|
44
|
+
/\b(?:implements|extends\s+[A-Z]\w*|readonly|private|protected|public)\b/g,
|
|
45
|
+
// Utility types
|
|
46
|
+
/\b(?:Partial|Required|Pick|Omit|Record|Exclude|Extract)\b/g,
|
|
47
|
+
];
|
|
48
|
+
return tsPatterns.some((pattern) => pattern.test(code));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates that code content matches the specified type
|
|
52
|
+
*/
|
|
53
|
+
function validateCodeTypeMatch(code, specifiedType) {
|
|
54
|
+
if (!code?.trim()) {
|
|
55
|
+
return "No code provided";
|
|
56
|
+
}
|
|
57
|
+
const detectedType = containsTypeScriptSyntax(code)
|
|
58
|
+
? "Typescript"
|
|
59
|
+
: "JavaScript";
|
|
60
|
+
const expectedType = specifiedType ?? "JavaScript";
|
|
61
|
+
// If TypeScript syntax detected but JavaScript specified
|
|
62
|
+
if (detectedType === "Typescript" && expectedType === "JavaScript") {
|
|
63
|
+
return `Code contains TypeScript syntax but is specified as JavaScript. Please either:
|
|
64
|
+
1. Change type to "Typescript", or
|
|
65
|
+
2. Remove TypeScript syntax (interfaces, type annotations, generics, etc.)
|
|
66
|
+
|
|
67
|
+
Detected TypeScript patterns: ${getTypeScriptPatternsFound(code).join(", ")}`;
|
|
68
|
+
}
|
|
69
|
+
// If no TypeScript syntax but TypeScript specified (warning, not error)
|
|
70
|
+
if (detectedType === "JavaScript" && expectedType === "Typescript") {
|
|
71
|
+
// This is not an error, just potentially unnecessary
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Identifies specific TypeScript patterns found in code
|
|
78
|
+
*/
|
|
79
|
+
function getTypeScriptPatternsFound(code) {
|
|
80
|
+
const patterns = [];
|
|
81
|
+
if (/\binterface\s+\w+/.test(code))
|
|
82
|
+
patterns.push("interface");
|
|
83
|
+
if (/\btype\s+\w+\s*=/.test(code))
|
|
84
|
+
patterns.push("type alias");
|
|
85
|
+
if (/:\s*[a-zA-Z]\w*(?:\s*\[])?\s*(?=[,;=)])/g.test(code))
|
|
86
|
+
patterns.push("type annotations");
|
|
87
|
+
if (/<\s*[A-Z]\w*\s*>/.test(code))
|
|
88
|
+
patterns.push("generics");
|
|
89
|
+
if (/\benum\s+\w+/.test(code))
|
|
90
|
+
patterns.push("enum");
|
|
91
|
+
return patterns;
|
|
92
|
+
}
|
|
21
93
|
export const createCodeInterpreterTool = ({ sendData, }) => {
|
|
22
94
|
return {
|
|
23
95
|
[CodeInterpreterTool.name]: tool({
|
|
24
96
|
description: toolDescription,
|
|
25
97
|
inputSchema: z.object({
|
|
26
|
-
code: z
|
|
98
|
+
code: z
|
|
99
|
+
.string()
|
|
100
|
+
.describe("The JavaScript or Typescript code to be executed."),
|
|
101
|
+
type: z
|
|
102
|
+
.enum(["JavaScript", "Typescript"])
|
|
103
|
+
.nullable()
|
|
104
|
+
.describe("The type of code. Either Javascript or Typescript."),
|
|
27
105
|
timeoutSeconds: z
|
|
28
106
|
.number()
|
|
29
107
|
.int()
|
|
@@ -32,21 +110,32 @@ export const createCodeInterpreterTool = ({ sendData, }) => {
|
|
|
32
110
|
.nullable()
|
|
33
111
|
.describe("Execution timeout in seconds (1-60). Default 5."),
|
|
34
112
|
}),
|
|
35
|
-
execute: async ({ code, timeoutSeconds }, { toolCallId }) => {
|
|
113
|
+
execute: async ({ code, type, timeoutSeconds }, { toolCallId }) => {
|
|
36
114
|
const workingDirectory = process.cwd();
|
|
37
115
|
try {
|
|
116
|
+
// Pre-execution validation
|
|
117
|
+
const validationError = validateCodeTypeMatch(code, type);
|
|
118
|
+
if (validationError) {
|
|
119
|
+
sendData?.({
|
|
120
|
+
event: "tool-error",
|
|
121
|
+
id: toolCallId,
|
|
122
|
+
data: validationError,
|
|
123
|
+
});
|
|
124
|
+
return validationError;
|
|
125
|
+
}
|
|
38
126
|
sendData?.({
|
|
39
127
|
event: "tool-init",
|
|
40
128
|
id: toolCallId,
|
|
41
129
|
data: "Initializing code interpreter environment",
|
|
42
130
|
});
|
|
131
|
+
const scriptType = (type ?? "JavaScript").toLowerCase();
|
|
43
132
|
sendData?.({
|
|
44
133
|
event: "tool-update",
|
|
45
134
|
id: toolCallId,
|
|
46
135
|
data: {
|
|
47
136
|
primary: "Executing...",
|
|
48
137
|
secondary: [
|
|
49
|
-
`${"`".repeat(3)}
|
|
138
|
+
`${"`".repeat(3)} ${scriptType}}\n${code.slice(0, 500)}${"`".repeat(3)}`,
|
|
50
139
|
],
|
|
51
140
|
},
|
|
52
141
|
});
|
|
@@ -56,7 +145,8 @@ export const createCodeInterpreterTool = ({ sendData, }) => {
|
|
|
56
145
|
const timeoutMs = Math.min(Math.max((timeoutSeconds ?? 5) * 1000, 1000), 60000);
|
|
57
146
|
const tmpBase = join(workingDirectory, ".acai-ci-tmp");
|
|
58
147
|
await mkdir(tmpBase, { recursive: true });
|
|
59
|
-
const
|
|
148
|
+
const ext = type === "JavaScript" ? ".mjs" : ".ts";
|
|
149
|
+
const scriptPath = join(tmpBase, `temp_script_${Date.now()}_${randomUUID()}${ext}`);
|
|
60
150
|
await writeFile(scriptPath, code, { encoding: "utf8" });
|
|
61
151
|
const args = [
|
|
62
152
|
"--permission",
|
|
@@ -3,12 +3,14 @@ export class CommandValidation {
|
|
|
3
3
|
dangerousPatterns;
|
|
4
4
|
constructor(allowedCommands) {
|
|
5
5
|
this.allowedCommands = allowedCommands;
|
|
6
|
-
//
|
|
6
|
+
// Block shell operators and substitutions outright
|
|
7
7
|
this.dangerousPatterns = [
|
|
8
8
|
/`/, // backticks (command substitution)
|
|
9
9
|
/\$\(/, // $() command substitution
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/\|/, // pipes
|
|
11
|
+
/>|>>|<|<</, // redirects
|
|
12
|
+
/;|&&|\|\||&/, // chaining and backgrounding
|
|
13
|
+
/[\r\n]/, // newlines
|
|
12
14
|
];
|
|
13
15
|
}
|
|
14
16
|
isCommandAllowed(command) {
|
|
@@ -16,98 +18,28 @@ export class CommandValidation {
|
|
|
16
18
|
return this.allowedCommands.includes(baseCommand);
|
|
17
19
|
}
|
|
18
20
|
hasDangerousPatterns(command) {
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
.replace(/'([^'\\]|\\.)*'/g, "")
|
|
22
|
-
.replace(/"([^"\\]|\\.)*"/g, "");
|
|
23
|
-
// Check for dangerous patterns only in unquoted portions
|
|
24
|
-
return this.dangerousPatterns.some((re) => re.test(stripped));
|
|
21
|
+
// Do not strip quotes; reject if any dangerous pattern appears anywhere
|
|
22
|
+
return this.dangerousPatterns.some((re) => re.test(command));
|
|
25
23
|
}
|
|
26
24
|
isValid(command) {
|
|
27
25
|
if (!command.trim()) {
|
|
28
26
|
return { isValid: false, error: "Command cannot be empty" };
|
|
29
27
|
}
|
|
30
|
-
// First check for dangerous patterns
|
|
31
28
|
if (this.hasDangerousPatterns(command)) {
|
|
32
29
|
return {
|
|
33
30
|
isValid: false,
|
|
34
|
-
error: "
|
|
31
|
+
error: "Pipes, redirects, command substitution, chaining, and newlines are disabled for security.",
|
|
35
32
|
};
|
|
36
33
|
}
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (char === "'" && !inDoubleQuote)
|
|
46
|
-
inSingleQuote = !inSingleQuote;
|
|
47
|
-
if (char === '"' && !inSingleQuote)
|
|
48
|
-
inDoubleQuote = !inDoubleQuote;
|
|
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 === ";")) {
|
|
52
|
-
if (currentSegment.trim()) {
|
|
53
|
-
subCommands.push(currentSegment.trim());
|
|
54
|
-
currentSegment = "";
|
|
55
|
-
}
|
|
56
|
-
// Skip the operator and any subsequent same operators (like &&)
|
|
57
|
-
while (i + 1 < command.length &&
|
|
58
|
-
["&", ";"].includes(command[i + 1] ?? "")) {
|
|
59
|
-
i++;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
currentSegment += char;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// Add the last segment
|
|
67
|
-
if (currentSegment.trim()) {
|
|
68
|
-
subCommands.push(currentSegment.trim());
|
|
69
|
-
}
|
|
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
|
-
}
|
|
34
|
+
// No pipes to split now; validate the single command only
|
|
35
|
+
const trimmed = command.trim();
|
|
36
|
+
if (!this.isCommandAllowed(trimmed)) {
|
|
37
|
+
const baseCmd = trimmed.split(" ")[0] || "";
|
|
38
|
+
return {
|
|
39
|
+
isValid: false,
|
|
40
|
+
error: `Command '${baseCmd}' is not allowed. Allowed commands: ${this.allowedCommands.join(", ")}`,
|
|
41
|
+
};
|
|
84
42
|
}
|
|
85
43
|
return { isValid: true };
|
|
86
44
|
}
|
|
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;
|
|
112
|
-
}
|
|
113
45
|
}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare function initTools({ terminal, tokenCounter, events, autoAcceptAl
|
|
|
35
35
|
}, string>;
|
|
36
36
|
readonly codeInterpreter: import("ai").Tool<{
|
|
37
37
|
code: string;
|
|
38
|
+
type: "Typescript" | "JavaScript" | null;
|
|
38
39
|
timeoutSeconds: number | null;
|
|
39
40
|
}, string>;
|
|
40
41
|
readonly deleteFile: import("ai").Tool<{
|
|
@@ -98,6 +99,7 @@ export declare function initCliTools({ tokenCounter, }: {
|
|
|
98
99
|
}, string>;
|
|
99
100
|
readonly codeInterpreter: import("ai").Tool<{
|
|
100
101
|
code: string;
|
|
102
|
+
type: "Typescript" | "JavaScript" | null;
|
|
101
103
|
timeoutSeconds: number | null;
|
|
102
104
|
}, string>;
|
|
103
105
|
readonly deleteFile: import("ai").Tool<{
|
package/dist/utils/process.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface ExecuteResult {
|
|
|
24
24
|
/** The signal that terminated the process, if any */
|
|
25
25
|
signal?: NodeJS.Signals;
|
|
26
26
|
}
|
|
27
|
+
export type ParseResult = {
|
|
28
|
+
ok: true;
|
|
29
|
+
argv: [string, ...string[]];
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
error: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function parseArgv(input: string): ParseResult;
|
|
27
35
|
/**
|
|
28
36
|
* Executes a command and returns the result, providing unified error handling
|
|
29
37
|
*
|
package/dist/utils/process.js
CHANGED
|
@@ -3,6 +3,81 @@ import { isUndefined } from "@travisennis/stdlib/typeguards";
|
|
|
3
3
|
const MS_IN_SECOND = 1000;
|
|
4
4
|
const SECONDS_IN_MINUTE = 60;
|
|
5
5
|
const DEFAULT_TIMEOUT = 2 * SECONDS_IN_MINUTE * MS_IN_SECOND;
|
|
6
|
+
// Quote/escape-aware argv tokenizer that forbids command substitution
|
|
7
|
+
export function parseArgv(input) {
|
|
8
|
+
const argv = [];
|
|
9
|
+
let buf = "";
|
|
10
|
+
let i = 0;
|
|
11
|
+
const n = input.length;
|
|
12
|
+
let inSingle = false;
|
|
13
|
+
let inDouble = false;
|
|
14
|
+
while (i < n) {
|
|
15
|
+
const ch = input[i] ?? "";
|
|
16
|
+
// Reject shell-only constructs early
|
|
17
|
+
if (ch === "`")
|
|
18
|
+
return { ok: false, error: "Backticks are not allowed" };
|
|
19
|
+
if (ch === "$" && i + 1 < n && input[i + 1] === "(") {
|
|
20
|
+
return { ok: false, error: "Command substitution $() is not allowed" };
|
|
21
|
+
}
|
|
22
|
+
if (!inSingle && !inDouble && /\s/.test(ch)) {
|
|
23
|
+
if (buf.length > 0) {
|
|
24
|
+
argv.push(buf);
|
|
25
|
+
buf = "";
|
|
26
|
+
}
|
|
27
|
+
i += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!inDouble && ch === "'" && !inSingle) {
|
|
31
|
+
inSingle = true;
|
|
32
|
+
i += 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (inSingle && ch === "'") {
|
|
36
|
+
inSingle = false;
|
|
37
|
+
i += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!inSingle && ch === '"' && !inDouble) {
|
|
41
|
+
inDouble = true;
|
|
42
|
+
i += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (inDouble && ch === '"') {
|
|
46
|
+
inDouble = false;
|
|
47
|
+
i += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!inSingle && ch === "\\") {
|
|
51
|
+
i += 1;
|
|
52
|
+
if (i >= n)
|
|
53
|
+
return { ok: false, error: "Dangling escape" };
|
|
54
|
+
const next = input[i] ?? "";
|
|
55
|
+
// Inside double quotes, only escape " and \\ reliably
|
|
56
|
+
if (inDouble && next !== '"' && next !== "\\") {
|
|
57
|
+
// Keep backslash literally for safety
|
|
58
|
+
buf += `\\${next}`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
buf += next;
|
|
62
|
+
}
|
|
63
|
+
i += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
buf += ch;
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
if (inSingle || inDouble)
|
|
70
|
+
return { ok: false, error: "Unterminated quote" };
|
|
71
|
+
if (buf.length > 0)
|
|
72
|
+
argv.push(buf);
|
|
73
|
+
if (argv.length === 0)
|
|
74
|
+
return { ok: false, error: "Empty command" };
|
|
75
|
+
const first = argv[0];
|
|
76
|
+
if (typeof first !== "string" || first.trim() === "") {
|
|
77
|
+
return { ok: false, error: "Missing command" };
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, argv: argv };
|
|
80
|
+
}
|
|
6
81
|
/**
|
|
7
82
|
* Executes a command and returns the result, providing unified error handling
|
|
8
83
|
*
|
|
@@ -19,9 +94,18 @@ export function executeCommand(command, options) {
|
|
|
19
94
|
[cmd, ...args] = command;
|
|
20
95
|
}
|
|
21
96
|
else {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
97
|
+
const parsed = parseArgv(command);
|
|
98
|
+
if (!parsed.ok) {
|
|
99
|
+
const result = {
|
|
100
|
+
stdout: "",
|
|
101
|
+
stderr: parsed.error,
|
|
102
|
+
code: 1,
|
|
103
|
+
};
|
|
104
|
+
return throwOnError
|
|
105
|
+
? Promise.reject(new Error(parsed.error))
|
|
106
|
+
: Promise.resolve(result);
|
|
107
|
+
}
|
|
108
|
+
[cmd, ...args] = parsed.argv;
|
|
25
109
|
}
|
|
26
110
|
if (isUndefined(cmd) || cmd.trim() === "") {
|
|
27
111
|
const result = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travisennis/acai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "An AI assistant for developing software.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,26 +44,26 @@
|
|
|
44
44
|
"typecheck:staged": "tsc --noEmit --pretty -p tsconfig.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@ai-sdk/anthropic": "^2.0.
|
|
48
|
-
"@ai-sdk/deepseek": "^1.0.
|
|
49
|
-
"@ai-sdk/google": "^2.0.
|
|
50
|
-
"@ai-sdk/openai": "^2.0.
|
|
51
|
-
"@ai-sdk/openai-compatible": "^1.0.
|
|
47
|
+
"@ai-sdk/anthropic": "^2.0.9",
|
|
48
|
+
"@ai-sdk/deepseek": "^1.0.13",
|
|
49
|
+
"@ai-sdk/google": "^2.0.11",
|
|
50
|
+
"@ai-sdk/openai": "^2.0.23",
|
|
51
|
+
"@ai-sdk/openai-compatible": "^1.0.13",
|
|
52
52
|
"@crosscopy/clipboard": "^0.2.8",
|
|
53
53
|
"@inquirer/prompts": "^7.8.4",
|
|
54
54
|
"@travisennis/stdlib": "^0.0.14",
|
|
55
|
-
"ai": "^5.0.
|
|
55
|
+
"ai": "^5.0.28",
|
|
56
56
|
"chalk": "^5.6.0",
|
|
57
57
|
"cheerio": "^1.1.2",
|
|
58
58
|
"cli-highlight": "^2.1.11",
|
|
59
59
|
"cli-table3": "^0.6.5",
|
|
60
60
|
"diff": "^8.0.2",
|
|
61
61
|
"duck-duck-scrape": "^2.2.7",
|
|
62
|
-
"exa-js": "^1.9.
|
|
62
|
+
"exa-js": "^1.9.3",
|
|
63
63
|
"globby": "^14.1.0",
|
|
64
64
|
"ignore": "^7.0.5",
|
|
65
65
|
"log-update": "^6.1.0",
|
|
66
|
-
"marked": "16.2.
|
|
66
|
+
"marked": "16.2.1",
|
|
67
67
|
"ora": "^8.2.0",
|
|
68
68
|
"p-throttle": "^8.0.0",
|
|
69
69
|
"pino": "^9.9.0",
|