@travisennis/acai 0.0.4 → 0.0.5
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 +225 -6
- package/dist/api/exa/index.d.ts +177 -0
- package/dist/api/exa/index.d.ts.map +1 -0
- package/dist/api/exa/index.js +439 -0
- package/dist/cli.d.ts +3 -2
- package/dist/cli.d.ts.map +1 -0
- package/dist/commands/application-log-command.d.ts +1 -0
- package/dist/commands/application-log-command.d.ts.map +1 -0
- package/dist/commands/application-log-command.js +5 -3
- package/dist/commands/clear-command.d.ts +1 -0
- package/dist/commands/clear-command.d.ts.map +1 -0
- package/dist/commands/clear-command.js +2 -3
- package/dist/commands/compact-command.d.ts +1 -0
- package/dist/commands/compact-command.d.ts.map +1 -0
- package/dist/commands/compact-command.js +1 -1
- package/dist/commands/copy-command.d.ts +1 -0
- package/dist/commands/copy-command.d.ts.map +1 -0
- package/dist/commands/copy-command.js +3 -2
- package/dist/commands/edit-command.d.ts +1 -0
- package/dist/commands/edit-command.d.ts.map +1 -0
- package/dist/commands/edit-command.js +7 -5
- package/dist/commands/edit-prompt-command.d.ts +2 -1
- package/dist/commands/edit-prompt-command.d.ts.map +1 -0
- package/dist/commands/edit-prompt-command.js +15 -7
- package/dist/commands/exit-command.d.ts +13 -2
- package/dist/commands/exit-command.d.ts.map +1 -0
- package/dist/commands/exit-command.js +14 -2
- package/dist/commands/files-command.d.ts +1 -0
- package/dist/commands/files-command.d.ts.map +1 -0
- package/dist/commands/files-command.js +9 -8
- package/dist/commands/generate-rules-command.d.ts +1 -0
- package/dist/commands/generate-rules-command.d.ts.map +1 -0
- package/dist/commands/generate-rules-command.js +4 -3
- package/dist/commands/health-command.d.ts +3 -1
- package/dist/commands/health-command.d.ts.map +1 -0
- package/dist/commands/health-command.js +42 -5
- package/dist/commands/help-command.d.ts +1 -0
- package/dist/commands/help-command.d.ts.map +1 -0
- package/dist/commands/help-command.js +2 -3
- package/dist/commands/init-command.d.ts +1 -0
- package/dist/commands/init-command.d.ts.map +1 -0
- package/dist/commands/init-command.js +1 -2
- package/dist/commands/last-log-command.d.ts +1 -0
- package/dist/commands/last-log-command.d.ts.map +1 -0
- package/dist/commands/last-log-command.js +12 -17
- package/dist/commands/list-tools-command.d.ts +3 -0
- package/dist/commands/list-tools-command.d.ts.map +1 -0
- package/dist/commands/list-tools-command.js +61 -0
- package/dist/commands/manager.d.ts +7 -2
- package/dist/commands/manager.d.ts.map +1 -0
- package/dist/commands/manager.js +43 -6
- package/dist/commands/model-command.d.ts +1 -0
- package/dist/commands/model-command.d.ts.map +1 -0
- package/dist/commands/model-command.js +5 -5
- package/dist/commands/paste-command.d.ts +1 -0
- package/dist/commands/paste-command.d.ts.map +1 -0
- package/dist/commands/paste-command.js +6 -5
- package/dist/commands/prompt-command.d.ts +2 -1
- package/dist/commands/prompt-command.d.ts.map +1 -0
- package/dist/commands/prompt-command.js +62 -8
- package/dist/commands/reset-command.d.ts +1 -0
- package/dist/commands/reset-command.d.ts.map +1 -0
- package/dist/commands/reset-command.js +1 -1
- package/dist/commands/rules-command.d.ts +1 -0
- package/dist/commands/rules-command.d.ts.map +1 -0
- package/dist/commands/rules-command.js +5 -3
- package/dist/commands/save-command.d.ts +1 -0
- package/dist/commands/save-command.d.ts.map +1 -0
- package/dist/commands/save-command.js +1 -1
- package/dist/commands/shell-command.d.ts +3 -0
- package/dist/commands/shell-command.d.ts.map +1 -0
- package/dist/commands/shell-command.js +60 -0
- package/dist/commands/types.d.ts +9 -6
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/usage-command.d.ts +1 -0
- package/dist/commands/usage-command.d.ts.map +1 -0
- package/dist/commands/usage-command.js +2 -3
- package/dist/config.d.ts +22 -34
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -15
- package/dist/conversation-analyzer.d.ts +2 -1
- package/dist/conversation-analyzer.d.ts.map +1 -0
- package/dist/dedent.d.ts +1 -0
- package/dist/dedent.d.ts.map +1 -0
- package/dist/execution/index.d.ts +112 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +432 -0
- package/dist/formatting.d.ts +2 -13
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +5 -64
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -4
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/mentions.d.ts +4 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +42 -10
- package/dist/messages.d.ts +8 -20
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +33 -53
- package/dist/middleware/audit-message.d.ts +1 -0
- package/dist/middleware/audit-message.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/rate-limit.d.ts +1 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/models/ai-config.d.ts +1 -0
- package/dist/models/ai-config.d.ts.map +1 -0
- package/dist/models/anthropic-provider.d.ts +1 -0
- package/dist/models/anthropic-provider.d.ts.map +1 -0
- package/dist/models/deepseek-provider.d.ts +1 -0
- package/dist/models/deepseek-provider.d.ts.map +1 -0
- package/dist/models/google-provider.d.ts +1 -0
- package/dist/models/google-provider.d.ts.map +1 -0
- package/dist/models/groq-provider.d.ts +20 -0
- package/dist/models/groq-provider.d.ts.map +1 -0
- package/dist/models/groq-provider.js +31 -0
- package/dist/models/manager.d.ts +1 -0
- package/dist/models/manager.d.ts.map +1 -0
- package/dist/models/openai-provider.d.ts +2 -1
- package/dist/models/openai-provider.d.ts.map +1 -0
- package/dist/models/openrouter-provider.d.ts +31 -22
- package/dist/models/openrouter-provider.d.ts.map +1 -0
- package/dist/models/openrouter-provider.js +115 -1
- package/dist/models/providers.d.ts +4 -5
- package/dist/models/providers.d.ts.map +1 -0
- package/dist/models/providers.js +7 -3
- package/dist/models/xai-provider.d.ts +1 -0
- package/dist/models/xai-provider.d.ts.map +1 -0
- package/dist/parsing.d.ts +2 -1
- package/dist/parsing.d.ts.map +1 -0
- package/dist/prompts/manager.d.ts +14 -2
- package/dist/prompts/manager.d.ts.map +1 -0
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +15 -11
- package/dist/repl/display-tool-messages.d.ts +4 -0
- package/dist/repl/display-tool-messages.d.ts.map +1 -0
- package/dist/repl/display-tool-messages.js +55 -0
- package/dist/repl/display-tool-use.d.ts +14 -0
- package/dist/repl/display-tool-use.d.ts.map +1 -0
- package/dist/repl/display-tool-use.js +63 -0
- package/dist/repl/get-prompt-header.d.ts +8 -0
- package/dist/repl/get-prompt-header.d.ts.map +1 -0
- package/dist/repl/get-prompt-header.js +38 -0
- package/dist/repl/tool-call-repair.d.ts +4 -0
- package/dist/repl/tool-call-repair.d.ts.map +1 -0
- package/dist/repl/tool-call-repair.js +50 -0
- package/dist/repl-prompt.d.ts +1 -0
- package/dist/repl-prompt.d.ts.map +1 -0
- package/dist/repl.d.ts +8 -4
- package/dist/repl.d.ts.map +1 -0
- package/dist/repl.js +108 -252
- package/dist/terminal/ansi-styles.d.ts +77 -0
- package/dist/terminal/ansi-styles.d.ts.map +1 -0
- package/dist/terminal/ansi-styles.js +215 -0
- package/dist/terminal/checkbox-prompt.d.ts +36 -0
- package/dist/terminal/checkbox-prompt.d.ts.map +1 -0
- package/dist/terminal/checkbox-prompt.js +362 -0
- package/dist/terminal/default-theme.d.ts +6 -0
- package/dist/terminal/default-theme.d.ts.map +1 -0
- package/dist/terminal/default-theme.js +182 -0
- package/dist/terminal/east-asian-width.d.ts +8 -0
- package/dist/terminal/east-asian-width.d.ts.map +1 -0
- package/dist/terminal/east-asian-width.js +409 -0
- package/dist/terminal/editor-prompt.d.ts +10 -0
- package/dist/terminal/editor-prompt.d.ts.map +1 -0
- package/dist/terminal/editor-prompt.js +61 -0
- package/dist/terminal/errors.d.ts +19 -0
- package/dist/terminal/errors.d.ts.map +1 -0
- package/dist/terminal/errors.js +37 -0
- package/dist/terminal/formatting.d.ts +1 -11
- package/dist/terminal/formatting.d.ts.map +1 -0
- package/dist/terminal/formatting.js +4 -20
- package/dist/terminal/highlight/index.d.ts +53 -0
- package/dist/terminal/highlight/index.d.ts.map +1 -0
- package/dist/terminal/highlight/index.js +90 -0
- package/dist/terminal/highlight/theme.d.ts +233 -0
- package/dist/terminal/highlight/theme.d.ts.map +1 -0
- package/dist/terminal/highlight/theme.js +83 -0
- package/dist/terminal/index.d.ts +16 -9
- package/dist/terminal/index.d.ts.map +1 -0
- package/dist/terminal/index.js +42 -126
- package/dist/terminal/input-prompt.d.ts +16 -0
- package/dist/terminal/input-prompt.d.ts.map +1 -0
- package/dist/terminal/input-prompt.js +181 -0
- package/dist/terminal/markdown-utils.d.ts +1 -0
- package/dist/terminal/markdown-utils.d.ts.map +1 -0
- package/dist/terminal/markdown.d.ts +1 -0
- package/dist/terminal/markdown.d.ts.map +1 -0
- package/dist/terminal/markdown.js +17 -12
- package/dist/terminal/search-prompt.d.ts +20 -0
- package/dist/terminal/search-prompt.d.ts.map +1 -0
- package/dist/terminal/search-prompt.js +279 -0
- package/dist/terminal/select-prompt.d.ts +26 -0
- package/dist/terminal/select-prompt.d.ts.map +1 -0
- package/dist/terminal/select-prompt.js +298 -0
- package/dist/terminal/string-width.d.ts +7 -0
- package/dist/terminal/string-width.d.ts.map +1 -0
- package/dist/terminal/string-width.js +61 -0
- package/dist/terminal/strip-ansi.d.ts +2 -0
- package/dist/terminal/strip-ansi.d.ts.map +1 -0
- package/dist/terminal/strip-ansi.js +20 -0
- package/dist/terminal/style.d.ts +191 -0
- package/dist/terminal/style.d.ts.map +1 -0
- package/dist/terminal/style.js +259 -0
- package/dist/terminal/supports-color.d.ts +1 -0
- package/dist/terminal/supports-color.d.ts.map +1 -0
- package/dist/terminal/supports-hyperlinks.d.ts +1 -3
- package/dist/terminal/supports-hyperlinks.d.ts.map +1 -0
- package/dist/terminal/supports-hyperlinks.js +1 -1
- package/dist/terminal/types.d.ts +1 -37
- package/dist/terminal/types.d.ts.map +1 -0
- package/dist/terminal/wrap-ansi.d.ts +8 -0
- package/dist/terminal/wrap-ansi.d.ts.map +1 -0
- package/dist/terminal/wrap-ansi.js +190 -0
- package/dist/{token-utils.d.ts → tokens/counter.d.ts} +1 -0
- package/dist/tokens/counter.d.ts.map +1 -0
- package/dist/{token-utils.js → tokens/counter.js} +1 -1
- package/dist/tokens/manage-output.d.ts +34 -0
- package/dist/tokens/manage-output.d.ts.map +1 -0
- package/dist/tokens/manage-output.js +44 -0
- package/dist/{token-tracker.d.ts → tokens/tracker.d.ts} +1 -0
- package/dist/tokens/tracker.d.ts.map +1 -0
- package/dist/tool-executor.d.ts +28 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +74 -0
- package/dist/tools/agent.d.ts +3 -2
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/agent.js +7 -4
- package/dist/tools/bash-utils.d.ts +7 -0
- package/dist/tools/bash-utils.d.ts.map +1 -0
- package/dist/tools/bash-utils.js +212 -0
- package/dist/tools/bash.d.ts +9 -7
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +95 -212
- package/dist/tools/code-interpreter.d.ts +1 -1
- package/dist/tools/code-interpreter.d.ts.map +1 -0
- package/dist/tools/code-interpreter.js +31 -96
- package/dist/tools/delete-file.d.ts +5 -3
- package/dist/tools/delete-file.d.ts.map +1 -0
- package/dist/tools/delete-file.js +47 -33
- package/dist/tools/directory-tree.d.ts +10 -1
- package/dist/tools/directory-tree.d.ts.map +1 -0
- package/dist/tools/directory-tree.js +91 -8
- package/dist/tools/dynamic-tool-loader.d.ts +12 -0
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -0
- package/dist/tools/dynamic-tool-loader.js +280 -0
- package/dist/tools/dynamic-tool-parser.d.ts +20 -0
- package/dist/tools/dynamic-tool-parser.d.ts.map +1 -0
- package/dist/tools/dynamic-tool-parser.js +21 -0
- package/dist/tools/edit-file.d.ts +10 -2
- package/dist/tools/edit-file.d.ts.map +1 -0
- package/dist/tools/edit-file.js +117 -40
- package/dist/tools/file-editing-utils.d.ts +2 -0
- package/dist/tools/file-editing-utils.d.ts.map +1 -0
- package/dist/tools/file-editing-utils.js +135 -0
- package/dist/tools/filesystem-utils.d.ts +6 -21
- package/dist/tools/filesystem-utils.d.ts.map +1 -0
- package/dist/tools/filesystem-utils.js +96 -148
- package/dist/tools/git-utils.d.ts +1 -0
- package/dist/tools/git-utils.d.ts.map +1 -0
- package/dist/tools/grep.d.ts +5 -3
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +67 -27
- package/dist/tools/index.d.ts +10 -16
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +33 -22
- package/dist/tools/move-file.d.ts +1 -0
- package/dist/tools/move-file.d.ts.map +1 -0
- package/dist/tools/move-file.js +12 -5
- package/dist/tools/read-file.d.ts +2 -1
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +13 -6
- package/dist/tools/read-multiple-files.d.ts +2 -1
- package/dist/tools/read-multiple-files.d.ts.map +1 -0
- package/dist/tools/read-multiple-files.js +90 -9
- package/dist/tools/save-file.d.ts +5 -3
- package/dist/tools/save-file.d.ts.map +1 -0
- package/dist/tools/save-file.js +64 -36
- package/dist/tools/think.d.ts +1 -0
- package/dist/tools/think.d.ts.map +1 -0
- package/dist/tools/think.js +5 -1
- package/dist/tools/types.d.ts +14 -1
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/web-fetch.d.ts +4 -2
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +2 -2
- package/dist/tools/web-search.d.ts +2 -1
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +46 -11
- package/dist/utils/filesystem.d.ts +23 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +140 -0
- package/dist/utils/filetype-detection.d.ts +3 -0
- package/dist/utils/filetype-detection.d.ts.map +1 -0
- package/dist/utils/filetype-detection.js +112 -0
- package/dist/utils/glob.d.ts +52 -0
- package/dist/utils/glob.d.ts.map +1 -0
- package/dist/utils/glob.js +376 -0
- package/dist/utils/ignore.d.ts +104 -0
- package/dist/utils/ignore.d.ts.map +1 -0
- package/dist/utils/ignore.js +649 -0
- package/dist/utils/process.d.ts +3 -2
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +17 -2
- package/dist/utils/zod-utils.d.ts +4 -0
- package/dist/utils/zod-utils.d.ts.map +1 -0
- package/dist/utils/zod-utils.js +7 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +32 -30
- package/dist/tools/command-validation.d.ts +0 -11
- package/dist/tools/command-validation.js +0 -45
- /package/dist/{token-tracker.js → tokens/tracker.js} +0 -0
package/dist/tools/save-file.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { input, select } from "@inquirer/prompts";
|
|
4
3
|
import { tool } from "ai";
|
|
5
|
-
import chalk from "chalk";
|
|
6
4
|
import { z } from "zod";
|
|
5
|
+
import { formatCodeBlock } from "../formatting.js";
|
|
6
|
+
import style from "../terminal/style.js";
|
|
7
7
|
import { joinWorkingDir, validatePath } from "./filesystem-utils.js";
|
|
8
8
|
import { fileEncodingSchema } from "./types.js";
|
|
9
9
|
export const SaveFileTool = {
|
|
10
10
|
name: "saveFile",
|
|
11
11
|
};
|
|
12
|
-
export const createSaveFileTool = async ({ workingDir,
|
|
12
|
+
export const createSaveFileTool = async ({ workingDir, terminal, sendData, toolExecutor, }) => {
|
|
13
13
|
const allowedDirectory = workingDir;
|
|
14
|
-
let autoAcceptSaves = autoAcceptAll ?? false;
|
|
15
14
|
return {
|
|
16
15
|
[SaveFileTool.name]: tool({
|
|
17
16
|
description: "Create a new file or completely overwrite an existing file with new content. " +
|
|
17
|
+
"Automatically creates all missing parent directories. " +
|
|
18
18
|
"Use with caution as it will overwrite existing files without warning. " +
|
|
19
19
|
"Handles text content with proper encoding. Only works within allowed directories.",
|
|
20
20
|
inputSchema: z.object({
|
|
@@ -22,61 +22,89 @@ export const createSaveFileTool = async ({ workingDir, sendData, terminal, autoA
|
|
|
22
22
|
content: z.string().describe("Content to save in the file"),
|
|
23
23
|
encoding: fileEncodingSchema.describe('Encoding format for saving the file. Use "utf-8" as default for text files'),
|
|
24
24
|
}),
|
|
25
|
-
execute: async ({ path: userPath, content, encoding, }, { toolCallId }) => {
|
|
25
|
+
execute: async ({ path: userPath, content, encoding, }, { toolCallId, abortSignal }) => {
|
|
26
|
+
// Check if execution has been aborted
|
|
27
|
+
if (abortSignal?.aborted) {
|
|
28
|
+
throw new Error("File saving aborted");
|
|
29
|
+
}
|
|
26
30
|
sendData?.({
|
|
27
31
|
id: toolCallId,
|
|
28
32
|
event: "tool-init",
|
|
29
|
-
data: `Saving file: ${
|
|
33
|
+
data: `Saving file: ${style.cyan(userPath)}`,
|
|
30
34
|
});
|
|
31
35
|
try {
|
|
32
|
-
const filePath = await validatePath(joinWorkingDir(userPath, workingDir), allowedDirectory);
|
|
36
|
+
const filePath = await validatePath(joinWorkingDir(userPath, workingDir), allowedDirectory, { requireExistence: false, abortSignal });
|
|
33
37
|
if (terminal) {
|
|
34
|
-
terminal.writeln(`\n${
|
|
38
|
+
terminal.writeln(`\n${style.blue.bold("●")} Proposing file save: ${style.cyan(userPath)}`);
|
|
35
39
|
terminal.lineBreak();
|
|
36
40
|
terminal.writeln("Proposed file content:");
|
|
37
|
-
terminal.
|
|
38
|
-
terminal.display(content);
|
|
39
|
-
terminal.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
terminal.hr();
|
|
42
|
+
terminal.display(formatCodeBlock(userPath, content));
|
|
43
|
+
terminal.hr();
|
|
44
|
+
// Determine overwrite status for display
|
|
45
|
+
let overwriteMessage = "";
|
|
46
|
+
try {
|
|
47
|
+
const stat = await fs.stat(filePath);
|
|
48
|
+
if (stat.isFile()) {
|
|
49
|
+
overwriteMessage = style.yellow("(Will overwrite existing file)");
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
message: "What would you like to do with this file?",
|
|
48
|
-
choices: [
|
|
49
|
-
{ name: "Accept and save this file", value: "accept" },
|
|
50
|
-
{
|
|
51
|
-
name: "Accept all future saves (including this)",
|
|
52
|
-
value: "accept-all",
|
|
53
|
-
},
|
|
54
|
-
{ name: "Reject this save", value: "reject" },
|
|
55
|
-
],
|
|
56
|
-
default: "accept",
|
|
57
|
-
});
|
|
52
|
+
catch {
|
|
53
|
+
overwriteMessage = style.green("(Will create new file)");
|
|
58
54
|
}
|
|
55
|
+
let userResponse;
|
|
56
|
+
if (toolExecutor) {
|
|
57
|
+
const ctx = {
|
|
58
|
+
toolName: SaveFileTool.name,
|
|
59
|
+
toolCallId,
|
|
60
|
+
message: `What would you like to do with this save? ${overwriteMessage}`,
|
|
61
|
+
choices: {
|
|
62
|
+
accept: "Accept this save",
|
|
63
|
+
acceptAll: "Accept all future saves (including this)",
|
|
64
|
+
reject: "Reject this save",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
userResponse = await toolExecutor.ask(ctx, { abortSignal });
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
if (e.name === "AbortError") {
|
|
72
|
+
throw new Error("File saving aborted during user input");
|
|
73
|
+
}
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const { result: userChoice, reason } = userResponse ?? {
|
|
78
|
+
result: "accept",
|
|
79
|
+
};
|
|
59
80
|
terminal.lineBreak();
|
|
60
81
|
if (userChoice === "accept-all") {
|
|
61
|
-
|
|
62
|
-
terminal.writeln(chalk.yellow("✓ Auto-accept mode enabled for all future saves"));
|
|
82
|
+
terminal.writeln(style.yellow("✓ Auto-accept mode enabled for all saves"));
|
|
63
83
|
terminal.lineBreak();
|
|
64
84
|
}
|
|
65
85
|
if (userChoice === "reject") {
|
|
66
|
-
const reason = await input({ message: "Feedback: " });
|
|
67
86
|
terminal.lineBreak();
|
|
87
|
+
const rejectionReason = reason || "No reason provided";
|
|
68
88
|
sendData?.({
|
|
69
89
|
id: toolCallId,
|
|
70
90
|
event: "tool-completion",
|
|
71
|
-
data: `Save rejected by user. Reason: ${
|
|
91
|
+
data: `Save rejected by user. Reason: ${rejectionReason}`,
|
|
72
92
|
});
|
|
73
|
-
return `The user rejected this save. Reason: ${
|
|
93
|
+
return `The user rejected this save. Reason: ${rejectionReason}`;
|
|
74
94
|
}
|
|
75
95
|
// If accepted, proceed to write file
|
|
76
96
|
}
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
// Pre-side-effect check
|
|
98
|
+
if (abortSignal?.aborted) {
|
|
99
|
+
throw new Error("File saving aborted before writing");
|
|
100
|
+
}
|
|
101
|
+
// Ensure parent directory exists (create missing parents)
|
|
102
|
+
const parentDir = path.dirname(filePath);
|
|
103
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
104
|
+
await fs.writeFile(filePath, content, {
|
|
105
|
+
encoding,
|
|
106
|
+
signal: abortSignal,
|
|
107
|
+
});
|
|
80
108
|
sendData?.({
|
|
81
109
|
id: toolCallId,
|
|
82
110
|
event: "tool-completion",
|
package/dist/tools/think.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"think.d.ts","sourceRoot":"","sources":["../../source/tools/think.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,SAAS;;CAErB,CAAC;AAYF,eAAO,MAAM,eAAe,GAC1B,UAAS;IAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAO;;;;CAmClD,CAAC"}
|
package/dist/tools/think.js
CHANGED
|
@@ -20,7 +20,11 @@ export const createThinkTool = (options = {}) => {
|
|
|
20
20
|
inputSchema: z.object({
|
|
21
21
|
thought: z.string().describe("Your thought"),
|
|
22
22
|
}),
|
|
23
|
-
execute: ({ thought }, { toolCallId }) => {
|
|
23
|
+
execute: ({ thought }, { toolCallId, abortSignal }) => {
|
|
24
|
+
// Check if execution has been aborted
|
|
25
|
+
if (abortSignal?.aborted) {
|
|
26
|
+
throw new Error("Thinking process aborted");
|
|
27
|
+
}
|
|
24
28
|
// Replace literal '\\n' with actual newline characters
|
|
25
29
|
const formattedThought = thought.replace(/\\n/g, "\n");
|
|
26
30
|
sendData?.({
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const fileEncodingSchema: z.ZodEnum<
|
|
2
|
+
export declare const fileEncodingSchema: z.ZodEnum<{
|
|
3
|
+
utf8: "utf8";
|
|
4
|
+
ascii: "ascii";
|
|
5
|
+
"utf-8": "utf-8";
|
|
6
|
+
utf16le: "utf16le";
|
|
7
|
+
ucs2: "ucs2";
|
|
8
|
+
"ucs-2": "ucs-2";
|
|
9
|
+
base64: "base64";
|
|
10
|
+
base64url: "base64url";
|
|
11
|
+
latin1: "latin1";
|
|
12
|
+
binary: "binary";
|
|
13
|
+
hex: "hex";
|
|
14
|
+
}>;
|
|
3
15
|
interface MessageData {
|
|
4
16
|
primary: string;
|
|
5
17
|
secondary?: string[] | undefined;
|
|
@@ -27,3 +39,4 @@ interface ToolUpdateMessage extends BaseMessage {
|
|
|
27
39
|
export type Message = ToolInitMessage | ToolErrorMessage | ToolCompletionMessage | ToolUpdateMessage;
|
|
28
40
|
export type SendData = ({ data, event, id, retry, }: Message) => void | Promise<void>;
|
|
29
41
|
export {};
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/tools/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAY7B,CAAC;AAEH,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAClC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAgB,SAAQ,WAAW;IAC3C,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,gBAAiB,SAAQ,WAAW;IAC5C,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,qBAAsB,SAAQ,WAAW;IACjD,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iBAAkB,SAAQ,WAAW;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,MAAM,OAAO,GACf,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,QAAQ,GAAG,CAAC,EACtB,IAAI,EACJ,KAAK,EACL,EAAE,EACF,KAAK,GACN,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TokenCounter } from "../
|
|
1
|
+
import type { TokenCounter } from "../tokens/counter.ts";
|
|
2
2
|
import type { SendData } from "./types.ts";
|
|
3
3
|
export declare const WebFetchTool: {
|
|
4
4
|
name: "webFetch";
|
|
@@ -11,7 +11,7 @@ export declare const createWebFetchTool: (options: {
|
|
|
11
11
|
url: string;
|
|
12
12
|
}, string>;
|
|
13
13
|
};
|
|
14
|
-
|
|
14
|
+
type ContentType = "text/plain" | "text/html" | "text/markdown" | "application/json" | "application/xml" | "application/pdf" | "image/png" | "image/jpeg" | "image/gif" | "image/webp" | "image/svg+xml" | "audio/mpeg" | "audio/wav" | "video/mp4" | "video/webm" | "application/zip" | "application/octet-stream";
|
|
15
15
|
export type ReadUrlResult = {
|
|
16
16
|
contentType: ContentType;
|
|
17
17
|
data: string;
|
|
@@ -45,3 +45,5 @@ export declare class HtmlCleaner {
|
|
|
45
45
|
*/
|
|
46
46
|
private removeEmptyElements;
|
|
47
47
|
}
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=web-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.d.ts","sourceRoot":"","sources":["../../source/tools/web-fetch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,YAAY;;CAExB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS;IAC1C,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,YAAY,CAAC;CAC5B;;;;CAyCA,CAAC;AAEF,KAAK,WAAW,GACZ,YAAY,GACZ,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,eAAe,GACf,YAAY,GACZ,WAAW,GACX,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,aAAa,GAAG;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,GACpC,OAAO,CAAC,aAAa,CAAC,CA6HxB;AAED,qBAAa,WAAW;IACtB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAIrC,OAAO,CAAC,IAAI,CAAS;IAErB,OAAO;IAIP;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAuBhE;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAyCjC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAG5B"}
|
package/dist/tools/web-fetch.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { tool } from "ai";
|
|
2
|
-
import chalk from "chalk";
|
|
3
2
|
import { load } from "cheerio";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { logger } from "../logger.js";
|
|
5
|
+
import style from "../terminal/style.js";
|
|
6
6
|
export const WebFetchTool = {
|
|
7
7
|
name: "webFetch",
|
|
8
8
|
};
|
|
@@ -19,7 +19,7 @@ export const createWebFetchTool = (options) => {
|
|
|
19
19
|
sendData?.({
|
|
20
20
|
event: "tool-init",
|
|
21
21
|
id: toolCallId,
|
|
22
|
-
data: `Reading URL: ${
|
|
22
|
+
data: `Reading URL: ${style.cyan(url)}`,
|
|
23
23
|
});
|
|
24
24
|
logger.info(`Initiating fetch for URL: ${url}`);
|
|
25
25
|
const result = await readUrl(url, abortSignal);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TokenCounter } from "../
|
|
1
|
+
import type { TokenCounter } from "../tokens/counter.ts";
|
|
2
2
|
import type { SendData } from "./types.ts";
|
|
3
3
|
export declare const WebSearchTool: {
|
|
4
4
|
name: "webSearch";
|
|
@@ -11,3 +11,4 @@ export declare const createWebSearchTool: ({ sendData, tokenCounter, }: {
|
|
|
11
11
|
query: string;
|
|
12
12
|
}, string>;
|
|
13
13
|
};
|
|
14
|
+
//# sourceMappingURL=web-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../source/tools/web-search.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,aAAa;;CAEzB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,6BAGjC;IACD,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;CAC5B;;;;CA0CA,CAAC"}
|
package/dist/tools/web-search.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { tool } from "ai";
|
|
2
|
-
import chalk from "chalk";
|
|
3
2
|
import { SafeSearchType, search } from "duck-duck-scrape";
|
|
4
|
-
import Exa from "exa-js";
|
|
5
3
|
import { z } from "zod";
|
|
4
|
+
import Exa from "../api/exa/index.js";
|
|
5
|
+
import style from "../terminal/style.js";
|
|
6
6
|
export const WebSearchTool = {
|
|
7
7
|
name: "webSearch",
|
|
8
8
|
};
|
|
@@ -13,13 +13,20 @@ export const createWebSearchTool = ({ sendData, tokenCounter, }) => {
|
|
|
13
13
|
inputSchema: z.object({
|
|
14
14
|
query: z.string().describe("The search query."),
|
|
15
15
|
}),
|
|
16
|
-
execute: async ({ query }, { toolCallId }) => {
|
|
16
|
+
execute: async ({ query }, { toolCallId, abortSignal }) => {
|
|
17
|
+
// Check if execution has been aborted
|
|
18
|
+
if (abortSignal?.aborted) {
|
|
19
|
+
throw new Error("Web search aborted");
|
|
20
|
+
}
|
|
17
21
|
sendData?.({
|
|
18
22
|
id: toolCallId,
|
|
19
23
|
event: "tool-init",
|
|
20
|
-
data: `Web search: ${
|
|
24
|
+
data: `Web search: ${style.cyan(query)}`,
|
|
21
25
|
});
|
|
22
|
-
|
|
26
|
+
if (abortSignal?.aborted) {
|
|
27
|
+
throw new Error("Web search aborted before search execution");
|
|
28
|
+
}
|
|
29
|
+
const result = await performSearch(query, abortSignal);
|
|
23
30
|
const sources = result.results.map((source) => `## ${source.title}\nURL: ${source.url}\n\n${source.text}`);
|
|
24
31
|
const resultText = `# Search Results:\n\n${sources.join("\n\n")}`;
|
|
25
32
|
const tokenCount = tokenCounter.count(resultText);
|
|
@@ -33,36 +40,64 @@ export const createWebSearchTool = ({ sendData, tokenCounter, }) => {
|
|
|
33
40
|
}),
|
|
34
41
|
};
|
|
35
42
|
};
|
|
36
|
-
async function performSearch(query) {
|
|
43
|
+
async function performSearch(query, abortSignal) {
|
|
37
44
|
// Check if EXA API key is available
|
|
38
45
|
const hasExaApiKey = process.env["EXA_API_KEY"] && process.env["EXA_API_KEY"].trim() !== "";
|
|
39
46
|
if (hasExaApiKey) {
|
|
40
47
|
// Use Exa search
|
|
41
48
|
try {
|
|
49
|
+
if (abortSignal?.aborted) {
|
|
50
|
+
throw new Error("Web search aborted before Exa search");
|
|
51
|
+
}
|
|
42
52
|
const exa = new Exa(process.env["EXA_API_KEY"]);
|
|
43
|
-
|
|
53
|
+
// Create a promise that races with the abort signal
|
|
54
|
+
const searchPromise = exa.searchAndContents(query, {
|
|
44
55
|
numResults: 5,
|
|
45
56
|
text: true,
|
|
46
57
|
});
|
|
58
|
+
const result = await Promise.race([
|
|
59
|
+
searchPromise,
|
|
60
|
+
new Promise((_, reject) => {
|
|
61
|
+
if (abortSignal) {
|
|
62
|
+
abortSignal.addEventListener("abort", () => {
|
|
63
|
+
reject(new Error("Web search aborted during Exa search"));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
]);
|
|
47
68
|
return result;
|
|
48
69
|
}
|
|
49
70
|
catch (error) {
|
|
50
71
|
// If Exa fails, fall back to duck duck scrape
|
|
51
72
|
console.info("Exa search failed, falling back to DuckDuckGo:", error);
|
|
52
|
-
return await searchWithDuckDuckGo(query);
|
|
73
|
+
return await searchWithDuckDuckGo(query, abortSignal);
|
|
53
74
|
}
|
|
54
75
|
}
|
|
55
76
|
else {
|
|
56
77
|
// Use DuckDuckGo search as fallback
|
|
57
78
|
console.info("EXA_API_KEY not set, using DuckDuckGo search");
|
|
58
|
-
return await searchWithDuckDuckGo(query);
|
|
79
|
+
return await searchWithDuckDuckGo(query, abortSignal);
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
|
-
async function searchWithDuckDuckGo(query) {
|
|
82
|
+
async function searchWithDuckDuckGo(query, abortSignal) {
|
|
62
83
|
try {
|
|
63
|
-
|
|
84
|
+
if (abortSignal?.aborted) {
|
|
85
|
+
throw new Error("Web search aborted before DuckDuckGo search");
|
|
86
|
+
}
|
|
87
|
+
// Create a promise that races with the abort signal
|
|
88
|
+
const searchPromise = search(query, {
|
|
64
89
|
safeSearch: SafeSearchType.MODERATE,
|
|
65
90
|
});
|
|
91
|
+
const searchResults = await Promise.race([
|
|
92
|
+
searchPromise,
|
|
93
|
+
new Promise((_, reject) => {
|
|
94
|
+
if (abortSignal) {
|
|
95
|
+
abortSignal.addEventListener("abort", () => {
|
|
96
|
+
reject(new Error("Web search aborted during DuckDuckGo search"));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}),
|
|
100
|
+
]);
|
|
66
101
|
// Transform duck-duck-scrape results to match Exa format
|
|
67
102
|
// Take only first 5 results to match Exa behavior
|
|
68
103
|
const results = searchResults.results
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare function toPath(urlOrPath: URL | string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Converts Windows backslashes to POSIX forward slashes.
|
|
4
|
+
* Preserves UNC paths and extended-length paths.
|
|
5
|
+
* @param inputPath - Path to convert
|
|
6
|
+
* @returns Path with forward slashes
|
|
7
|
+
*/
|
|
8
|
+
export declare function slash(inputPath: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Checks if a path exists and is a directory.
|
|
11
|
+
* @param filePath - Path to check (URL or string)
|
|
12
|
+
* @returns Promise resolving to true if path is a directory
|
|
13
|
+
* @throws TypeError if input is invalid
|
|
14
|
+
*/
|
|
15
|
+
export declare function isDirectory(filePath: URL | string): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Safely clears all contents of a directory.
|
|
18
|
+
* @param directoryPath - Path to the directory to clear (URL or string)
|
|
19
|
+
* @returns Promise that resolves when directory is cleared
|
|
20
|
+
* @throws Error if directory cannot be cleared or if path is invalid/unsafe
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearDirectory(directoryPath: URL | string): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=filesystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../source/utils/filesystem.ts"],"names":[],"mappings":"AAIA,wBAAgB,MAAM,CAAC,SAAS,EAAE,GAAG,GAAG,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAS/C;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmB1E;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,aAAa,EAAE,GAAG,GAAG,MAAM,GAC1B,OAAO,CAAC,IAAI,CAAC,CAuGf"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as fsPromises from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
export function toPath(urlOrPath) {
|
|
5
|
+
return urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Converts Windows backslashes to POSIX forward slashes.
|
|
9
|
+
* Preserves UNC paths and extended-length paths.
|
|
10
|
+
* @param inputPath - Path to convert
|
|
11
|
+
* @returns Path with forward slashes
|
|
12
|
+
*/
|
|
13
|
+
export function slash(inputPath) {
|
|
14
|
+
const isExtendedLengthPath = inputPath.startsWith("\\\\?\\");
|
|
15
|
+
const isUncPath = inputPath.startsWith("\\\\");
|
|
16
|
+
if (isExtendedLengthPath || isUncPath) {
|
|
17
|
+
return inputPath;
|
|
18
|
+
}
|
|
19
|
+
return inputPath.replace(/\\/g, "/");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a path exists and is a directory.
|
|
23
|
+
* @param filePath - Path to check (URL or string)
|
|
24
|
+
* @returns Promise resolving to true if path is a directory
|
|
25
|
+
* @throws TypeError if input is invalid
|
|
26
|
+
*/
|
|
27
|
+
export async function isDirectory(filePath) {
|
|
28
|
+
const resolvedPath = path.resolve(toPath(filePath));
|
|
29
|
+
if (typeof resolvedPath !== "string" || !resolvedPath.trim()) {
|
|
30
|
+
throw new TypeError(`Expected a non-empty string or URL, got ${typeof filePath}`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const stats = await fsPromises.stat(resolvedPath);
|
|
34
|
+
return stats.isDirectory();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const err = error;
|
|
38
|
+
if (err.code === "ENOENT") {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Safely clears all contents of a directory.
|
|
46
|
+
* @param directoryPath - Path to the directory to clear (URL or string)
|
|
47
|
+
* @returns Promise that resolves when directory is cleared
|
|
48
|
+
* @throws Error if directory cannot be cleared or if path is invalid/unsafe
|
|
49
|
+
*/
|
|
50
|
+
export async function clearDirectory(directoryPath) {
|
|
51
|
+
// Convert URL to path and resolve to absolute path
|
|
52
|
+
const resolvedPath = path.resolve(toPath(directoryPath));
|
|
53
|
+
// Input validation
|
|
54
|
+
if (typeof resolvedPath !== "string" || !resolvedPath.trim()) {
|
|
55
|
+
throw new TypeError("Directory path must be a non-empty string");
|
|
56
|
+
}
|
|
57
|
+
// Safety checks - prevent clearing root or dangerous paths
|
|
58
|
+
const parsedPath = path.parse(resolvedPath);
|
|
59
|
+
const isRoot = resolvedPath === parsedPath.root;
|
|
60
|
+
const isCurrentDir = resolvedPath === "." || resolvedPath === path.resolve(".");
|
|
61
|
+
if (isRoot || isCurrentDir) {
|
|
62
|
+
throw new Error(`Refusing to clear dangerous path: ${resolvedPath}`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
// Check if directory exists and is accessible
|
|
66
|
+
try {
|
|
67
|
+
const lst = await fsPromises.lstat(resolvedPath);
|
|
68
|
+
if (lst.isSymbolicLink()) {
|
|
69
|
+
throw new Error(`Refusing to clear symlink directory: ${resolvedPath}`);
|
|
70
|
+
}
|
|
71
|
+
if (!lst.isDirectory()) {
|
|
72
|
+
throw new Error(`Path is not a directory: ${resolvedPath}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const err = error;
|
|
77
|
+
if (err.code === "ENOENT") {
|
|
78
|
+
// Directory doesn't exist, nothing to clear
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
// Read directory contents
|
|
84
|
+
const entries = await fsPromises.readdir(resolvedPath, {
|
|
85
|
+
withFileTypes: true,
|
|
86
|
+
});
|
|
87
|
+
// Delete all entries with concurrency control and proper error handling
|
|
88
|
+
const maxConcurrent = 50; // Limit concurrent operations to avoid EMFILE
|
|
89
|
+
const errors = [];
|
|
90
|
+
// Process entries in batches to control concurrency
|
|
91
|
+
for (let i = 0; i < entries.length; i += maxConcurrent) {
|
|
92
|
+
const batch = entries.slice(i, i + maxConcurrent);
|
|
93
|
+
const deletionPromises = batch.map(async (entry) => {
|
|
94
|
+
const entryPath = path.join(resolvedPath, entry.name);
|
|
95
|
+
const relativePath = path.relative(process.cwd(), entryPath);
|
|
96
|
+
try {
|
|
97
|
+
if (entry.isSymbolicLink()) {
|
|
98
|
+
// Always unlink symlinks, don't recurse
|
|
99
|
+
await fsPromises.unlink(entryPath);
|
|
100
|
+
}
|
|
101
|
+
else if (entry.isDirectory()) {
|
|
102
|
+
// Recursively delete subdirectories
|
|
103
|
+
await clearDirectory(entryPath);
|
|
104
|
+
// Use rm instead of deprecated rmdir
|
|
105
|
+
await fsPromises.rm(entryPath, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Delete files using rm for consistency
|
|
109
|
+
await fsPromises.rm(entryPath, { force: false });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
const err = error;
|
|
114
|
+
errors.push({
|
|
115
|
+
path: relativePath,
|
|
116
|
+
error: err,
|
|
117
|
+
op: entry.isDirectory() ? "rm" : "unlink",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
await Promise.all(deletionPromises);
|
|
122
|
+
}
|
|
123
|
+
// If any deletions failed, throw aggregated error
|
|
124
|
+
if (errors.length > 0) {
|
|
125
|
+
const errorDetails = errors
|
|
126
|
+
.map(({ path, error, op }) => {
|
|
127
|
+
const errnoError = error;
|
|
128
|
+
return `${path} (${op}): ${error.message} (${errnoError.code || "UNKNOWN"})`;
|
|
129
|
+
})
|
|
130
|
+
.join("; ");
|
|
131
|
+
throw new Error(`Failed to delete ${errors.length} entries: ${errorDetails}`, {
|
|
132
|
+
cause: errors[0].error,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const err = error;
|
|
138
|
+
throw new Error(`Failed to clear directory ${resolvedPath}: ${err.message}`, { cause: err });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filetype-detection.d.ts","sourceRoot":"","sources":["../../source/utils/filetype-detection.ts"],"names":[],"mappings":"AA+FA,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW5E;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW7E"}
|