@4onstudios/iris-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +690 -0
- package/dist/api/acp/acpServer.d.ts +23 -0
- package/dist/api/acp/acpServer.js +814 -0
- package/dist/api/acp/index.d.ts +2 -0
- package/dist/api/acp/index.js +2 -0
- package/dist/api/acp/irisClient.d.ts +58 -0
- package/dist/api/acp/irisClient.js +214 -0
- package/dist/api/agent.d.ts +14 -0
- package/dist/api/agent.js +4171 -0
- package/dist/api/backendService.d.ts +4 -0
- package/dist/api/backendService.js +14 -0
- package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
- package/dist/api/core/agent/host/AgentContract.js +1 -0
- package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
- package/dist/api/core/agent/host/AgentRegistry.js +125 -0
- package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
- package/dist/api/core/agent/host/defaultRegistry.js +46 -0
- package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
- package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
- package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
- package/dist/api/core/agent/host/hostSessionManager.js +352 -0
- package/dist/api/core/agent/host/index.d.ts +5 -0
- package/dist/api/core/agent/host/index.js +5 -0
- package/dist/api/core/agent/index.d.ts +87 -0
- package/dist/api/core/agent/index.js +1024 -0
- package/dist/api/core/agent/publicContracts.d.ts +35 -0
- package/dist/api/core/agent/publicContracts.js +103 -0
- package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
- package/dist/api/core/agent/tools/applyDiff.js +131 -0
- package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
- package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
- package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
- package/dist/api/core/agent/tools/createDirectory.js +77 -0
- package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
- package/dist/api/core/agent/tools/deleteFile.js +66 -0
- package/dist/api/core/agent/tools/editFile.d.ts +73 -0
- package/dist/api/core/agent/tools/editFile.js +173 -0
- package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
- package/dist/api/core/agent/tools/executeCommand.js +250 -0
- package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
- package/dist/api/core/agent/tools/fileContent.js +227 -0
- package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
- package/dist/api/core/agent/tools/findDefinition.js +109 -0
- package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
- package/dist/api/core/agent/tools/findReferences.js +135 -0
- package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
- package/dist/api/core/agent/tools/formatDocument.js +90 -0
- package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
- package/dist/api/core/agent/tools/getCodeActions.js +97 -0
- package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
- package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
- package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
- package/dist/api/core/agent/tools/getCodeContext.js +282 -0
- package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
- package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
- package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
- package/dist/api/core/agent/tools/getSymbols.js +164 -0
- package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
- package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
- package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
- package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
- package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
- package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
- package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
- package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
- package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
- package/dist/api/core/agent/tools/grepSearch.js +211 -0
- package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
- package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
- package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
- package/dist/api/core/agent/tools/listDirectory.js +161 -0
- package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
- package/dist/api/core/agent/tools/mcpTools.js +442 -0
- package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
- package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
- package/dist/api/core/agent/tools/readFile.d.ts +92 -0
- package/dist/api/core/agent/tools/readFile.js +337 -0
- package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
- package/dist/api/core/agent/tools/renameFile.js +86 -0
- package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
- package/dist/api/core/agent/tools/renameSymbol.js +106 -0
- package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
- package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
- package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
- package/dist/api/core/agent/tools/searchFiles.js +280 -0
- package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
- package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
- package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
- package/dist/api/core/agent/tools/treeTraversal.js +217 -0
- package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
- package/dist/api/core/agent/tools/webSearch.js +80 -0
- package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
- package/dist/api/core/agent/tools/writeFile.js +97 -0
- package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
- package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
- package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
- package/dist/api/core/agent/utils/diffUtils.js +39 -0
- package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
- package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
- package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
- package/dist/api/core/agent/utils/errorRecovery.js +229 -0
- package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
- package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
- package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
- package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
- package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
- package/dist/api/core/agent/utils/pathRecovery.js +29 -0
- package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
- package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
- package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
- package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
- package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
- package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
- package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
- package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
- package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
- package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
- package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
- package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
- package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
- package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
- package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
- package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
- package/dist/api/core/library/BrowserManager.d.ts +11 -0
- package/dist/api/core/library/BrowserManager.js +66 -0
- package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
- package/dist/api/core/library/desktopWorkspace.js +3 -0
- package/dist/api/core/library/extensionManager.d.ts +10 -0
- package/dist/api/core/library/extensionManager.js +27 -0
- package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
- package/dist/api/core/library/knowledgeGraph.js +989 -0
- package/dist/api/core/library/languageModelTools.d.ts +141 -0
- package/dist/api/core/library/languageModelTools.js +207 -0
- package/dist/api/core/library/localRuntime.d.ts +23 -0
- package/dist/api/core/library/localRuntime.js +144 -0
- package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
- package/dist/api/core/library/lsp/coreLsp.js +2076 -0
- package/dist/api/core/library/lsp/protocol.d.ts +25 -0
- package/dist/api/core/library/lsp/protocol.js +1 -0
- package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
- package/dist/api/core/library/lsp/serverManager.js +427 -0
- package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
- package/dist/api/core/library/mcpServerProvider.js +152 -0
- package/dist/api/core/library/mcpSettings.d.ts +20 -0
- package/dist/api/core/library/mcpSettings.js +131 -0
- package/dist/api/core/library/patternMatcher.d.ts +66 -0
- package/dist/api/core/library/patternMatcher.js +284 -0
- package/dist/api/core/library/regexEscape.d.ts +4 -0
- package/dist/api/core/library/regexEscape.js +4 -0
- package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
- package/dist/api/core/library/runtimeEventBus.js +78 -0
- package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
- package/dist/api/core/library/safetyMiddleware.js +215 -0
- package/dist/api/core/library/tauri.d.ts +71 -0
- package/dist/api/core/library/tauri.js +222 -0
- package/dist/api/core/library/tauriImport.d.ts +5 -0
- package/dist/api/core/library/tauriImport.js +7 -0
- package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
- package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
- package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
- package/dist/api/core/library/workspaceIdentity.js +49 -0
- package/dist/api/core/library/workspaceSummary.d.ts +19 -0
- package/dist/api/core/library/workspaceSummary.js +35 -0
- package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
- package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
- package/dist/api/core/skills/create-pr/SKILL.md +32 -0
- package/dist/api/core/skills/dev-server/SKILL.md +20 -0
- package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
- package/dist/api/core/skills/github-search/SKILL.md +21 -0
- package/dist/api/core/skills/github-summary/SKILL.md +25 -0
- package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
- package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
- package/dist/api/core/skills/project-setup/SKILL.md +20 -0
- package/dist/api/core/skills/search-integration/SKILL.md +19 -0
- package/dist/api/core/skills/search-results/SKILL.md +19 -0
- package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
- package/dist/api/data/runStore.d.ts +46 -0
- package/dist/api/data/runStore.js +244 -0
- package/dist/api/helpers/agentUtils.d.ts +11 -0
- package/dist/api/helpers/agentUtils.js +23 -0
- package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
- package/dist/api/helpers/modelTokenLimits.js +158 -0
- package/dist/api/helpers/observationalMemory.d.ts +10 -0
- package/dist/api/helpers/observationalMemory.js +36 -0
- package/dist/api/helpers/promptBudget.d.ts +30 -0
- package/dist/api/helpers/promptBudget.js +216 -0
- package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
- package/dist/api/helpers/resolveImageMessageParts.js +140 -0
- package/dist/api/helpers/slashCommands.d.ts +31 -0
- package/dist/api/helpers/slashCommands.js +129 -0
- package/dist/api/helpers/tokenUsage.d.ts +19 -0
- package/dist/api/helpers/tokenUsage.js +81 -0
- package/dist/api/routes/fileRoutes.d.ts +2 -0
- package/dist/api/routes/fileRoutes.js +108 -0
- package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
- package/dist/api/routes/lspDocumentRoutes.js +84 -0
- package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
- package/dist/api/routes/lspHierarchyRoutes.js +122 -0
- package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
- package/dist/api/routes/lspPositionRoutes.js +263 -0
- package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
- package/dist/api/routes/lspQueryRoutes.js +679 -0
- package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
- package/dist/api/routes/lspResolveRoutes.js +143 -0
- package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
- package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
- package/dist/api/routes/semanticRoutes.d.ts +2 -0
- package/dist/api/routes/semanticRoutes.js +126 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +160 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +38 -0
- package/package.json +100 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { generateDiff } from "../utils/diffUtils.js";
|
|
5
|
+
const countOccurrences = (source, token) => {
|
|
6
|
+
if (!token)
|
|
7
|
+
return 0;
|
|
8
|
+
let count = 0;
|
|
9
|
+
let from = 0;
|
|
10
|
+
while (true) {
|
|
11
|
+
const index = source.indexOf(token, from);
|
|
12
|
+
if (index === -1)
|
|
13
|
+
return count;
|
|
14
|
+
count += 1;
|
|
15
|
+
from = index + token.length;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Tool for editing files by replacing content or specific lines
|
|
20
|
+
* @param {Object} params - The parameters for editing a file
|
|
21
|
+
* @param {string} params.filePath - The path to the file to edit
|
|
22
|
+
* @param {string} [params.oldContent] - The old content to replace (must match exactly)
|
|
23
|
+
* @param {string} [params.newContent] - The new content to replace with
|
|
24
|
+
* @param {number} [params.startLine] - Starting line number to replace (1-indexed)
|
|
25
|
+
* @param {number} [params.endLine] - Ending line number to replace (1-indexed)
|
|
26
|
+
* @param {string} [params.lineContent] - New content for the line range
|
|
27
|
+
* @param {string} [params.encoding='utf8'] - The encoding to use
|
|
28
|
+
* @returns {Promise<Object>} Object containing success status and edit info or error
|
|
29
|
+
*/
|
|
30
|
+
export async function editFile({ filePath, oldContent, newContent, startLine, endLine, lineContent, encoding = "utf8", }) {
|
|
31
|
+
try {
|
|
32
|
+
// Validate input
|
|
33
|
+
if (!filePath) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: "File path is required",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Resolve to absolute path
|
|
40
|
+
const absolutePath = path.resolve(filePath);
|
|
41
|
+
// Check if file exists
|
|
42
|
+
try {
|
|
43
|
+
await fs.access(absolutePath, fs.constants.R_OK | fs.constants.W_OK);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: `File not found or not writable: ${filePath}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Read current content
|
|
52
|
+
const currentContent = await fs.readFile(absolutePath, encoding);
|
|
53
|
+
let updatedContent;
|
|
54
|
+
// Mode 1: Replace by content matching
|
|
55
|
+
if (oldContent !== undefined && newContent !== undefined) {
|
|
56
|
+
const matches = countOccurrences(currentContent, oldContent);
|
|
57
|
+
if (matches === 0) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: "Old content not found in file. Content must match exactly.",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (matches > 1) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: "Old content appears multiple times. Provide a unique anchor (for example line-based edit with matching oldContent).",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
updatedContent = currentContent.replace(oldContent, newContent);
|
|
70
|
+
}
|
|
71
|
+
// Mode 2: Replace by line numbers
|
|
72
|
+
else if (startLine !== undefined && lineContent !== undefined) {
|
|
73
|
+
const lines = currentContent.split("\n");
|
|
74
|
+
if (startLine < 1 || startLine > lines.length) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: `Start line ${startLine} is out of range (file has ${lines.length} lines)`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const start = startLine - 1; // Convert to 0-indexed
|
|
81
|
+
const end = endLine !== undefined ? endLine : startLine;
|
|
82
|
+
if (end < startLine || end > lines.length) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: `End line ${end} is invalid`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const selectedLines = lines.slice(startLine - 1, end);
|
|
89
|
+
const selectedContent = selectedLines.join("\n");
|
|
90
|
+
if (oldContent === undefined) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: "Line-based edits require oldContent to match the current line range exactly.",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (selectedContent !== oldContent) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: "Line range content does not match oldContent. Re-read the file and retry with current content.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Replace the line range
|
|
103
|
+
lines.splice(start, end - startLine + 1, lineContent);
|
|
104
|
+
updatedContent = lines.join("\n");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: "Must provide either (oldContent + newContent) or (startLine + lineContent)",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Write updated content
|
|
113
|
+
await fs.writeFile(absolutePath, updatedContent, encoding);
|
|
114
|
+
// Get file stats
|
|
115
|
+
const stats = await fs.stat(absolutePath);
|
|
116
|
+
// Generate diff for UI display (includes accurate line counts)
|
|
117
|
+
const { diff, linesAdded, linesRemoved } = generateDiff(currentContent, updatedContent, absolutePath);
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
filePath: absolutePath,
|
|
121
|
+
fileExisted: true,
|
|
122
|
+
size: stats.size,
|
|
123
|
+
modified: stats.mtime,
|
|
124
|
+
linesAdded,
|
|
125
|
+
linesRemoved,
|
|
126
|
+
diff,
|
|
127
|
+
oldContent: currentContent,
|
|
128
|
+
newContent: updatedContent,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const err = error;
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: err.message,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Tool metadata for agent system
|
|
141
|
+
*/
|
|
142
|
+
export const editFileTool = {
|
|
143
|
+
description: "Edit a file by replacing uniquely matched content or a line range anchored by exact oldContent",
|
|
144
|
+
parameters: z.object({
|
|
145
|
+
filePath: z.string().describe("The path to the file to edit"),
|
|
146
|
+
oldContent: z
|
|
147
|
+
.string()
|
|
148
|
+
.optional()
|
|
149
|
+
.describe("The current file content to replace or the exact current content of the target line range"),
|
|
150
|
+
newContent: z
|
|
151
|
+
.string()
|
|
152
|
+
.optional()
|
|
153
|
+
.describe("The new content to replace with"),
|
|
154
|
+
startLine: z
|
|
155
|
+
.number()
|
|
156
|
+
.optional()
|
|
157
|
+
.describe("Starting line number to replace (1-indexed)"),
|
|
158
|
+
endLine: z
|
|
159
|
+
.number()
|
|
160
|
+
.optional()
|
|
161
|
+
.describe("Ending line number to replace (1-indexed)"),
|
|
162
|
+
lineContent: z
|
|
163
|
+
.string()
|
|
164
|
+
.optional()
|
|
165
|
+
.describe("New content for the line range (requires oldContent to match the current range)"),
|
|
166
|
+
encoding: z
|
|
167
|
+
.enum(["utf8", "ascii", "base64", "binary", "hex"])
|
|
168
|
+
.default("utf8")
|
|
169
|
+
.describe("The encoding to use"),
|
|
170
|
+
}),
|
|
171
|
+
execute: editFile,
|
|
172
|
+
};
|
|
173
|
+
export default editFileTool;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type TerminalApprovalPolicy } from "./terminalAutoApprove.js";
|
|
3
|
+
import type { TerminalAutoApproveRules } from "../../library/terminalAutoApproveSettings.js";
|
|
4
|
+
type ExecuteCommandParams = {
|
|
5
|
+
command?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
workspaceRoot?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
runInBackground?: boolean;
|
|
11
|
+
description?: string;
|
|
12
|
+
skipConfirmation?: boolean;
|
|
13
|
+
approvalPolicy?: TerminalApprovalPolicy;
|
|
14
|
+
autoApproveRules?: TerminalAutoApproveRules;
|
|
15
|
+
};
|
|
16
|
+
type CommandResult = Record<string, unknown> & {
|
|
17
|
+
success?: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function executeCommand({ command, cwd, workspaceRoot, timeout, env, runInBackground, description, skipConfirmation, approvalPolicy, autoApproveRules, }: ExecuteCommandParams): Promise<CommandResult>;
|
|
21
|
+
export declare const executeCommandTool: {
|
|
22
|
+
description: string;
|
|
23
|
+
parameters: z.ZodObject<{
|
|
24
|
+
command: z.ZodOptional<z.ZodString>;
|
|
25
|
+
cwd: z.ZodDefault<z.ZodString>;
|
|
26
|
+
workspaceRoot: z.ZodOptional<z.ZodString>;
|
|
27
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
28
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
29
|
+
runInBackground: z.ZodDefault<z.ZodBoolean>;
|
|
30
|
+
description: z.ZodOptional<z.ZodString>;
|
|
31
|
+
skipConfirmation: z.ZodDefault<z.ZodBoolean>;
|
|
32
|
+
approvalPolicy: z.ZodDefault<z.ZodEnum<["always_prompt", "auto_approve_safe"]>>;
|
|
33
|
+
autoApproveRules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
34
|
+
}, "strip", z.ZodTypeAny, {
|
|
35
|
+
command?: string;
|
|
36
|
+
cwd?: string;
|
|
37
|
+
env?: Record<string, string>;
|
|
38
|
+
description?: string;
|
|
39
|
+
timeout?: number;
|
|
40
|
+
workspaceRoot?: string;
|
|
41
|
+
runInBackground?: boolean;
|
|
42
|
+
skipConfirmation?: boolean;
|
|
43
|
+
approvalPolicy?: "always_prompt" | "auto_approve_safe";
|
|
44
|
+
autoApproveRules?: Record<string, any>;
|
|
45
|
+
}, {
|
|
46
|
+
command?: string;
|
|
47
|
+
cwd?: string;
|
|
48
|
+
env?: Record<string, string>;
|
|
49
|
+
description?: string;
|
|
50
|
+
timeout?: number;
|
|
51
|
+
workspaceRoot?: string;
|
|
52
|
+
runInBackground?: boolean;
|
|
53
|
+
skipConfirmation?: boolean;
|
|
54
|
+
approvalPolicy?: "always_prompt" | "auto_approve_safe";
|
|
55
|
+
autoApproveRules?: Record<string, any>;
|
|
56
|
+
}>;
|
|
57
|
+
execute: typeof executeCommand;
|
|
58
|
+
};
|
|
59
|
+
export default executeCommandTool;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { exec, execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { startBackgroundTask } from "./backgroundTasks.js";
|
|
7
|
+
import { shouldAutoApproveTerminalCommand, } from "./terminalAutoApprove.js";
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
function isPathWithinWorkspace(resolvedPath, workspacePath) {
|
|
11
|
+
const normalizedResolved = path.resolve(resolvedPath);
|
|
12
|
+
const normalizedWorkspace = path.resolve(workspacePath);
|
|
13
|
+
return (normalizedResolved.startsWith(normalizedWorkspace + path.sep) ||
|
|
14
|
+
normalizedResolved === normalizedWorkspace);
|
|
15
|
+
}
|
|
16
|
+
function detectWorkspaceRootFromCwd(cwd) {
|
|
17
|
+
let current = path.resolve(cwd);
|
|
18
|
+
while (true) {
|
|
19
|
+
const gitMarker = path.join(current, ".git");
|
|
20
|
+
if (fs.existsSync(gitMarker)) {
|
|
21
|
+
return current;
|
|
22
|
+
}
|
|
23
|
+
const parent = path.dirname(current);
|
|
24
|
+
if (parent === current) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
current = parent;
|
|
28
|
+
}
|
|
29
|
+
return path.resolve(cwd);
|
|
30
|
+
}
|
|
31
|
+
function requestConfirmation(commandInfo) {
|
|
32
|
+
const confirmationId = `cmd_${Date.now()}_${Math.random()
|
|
33
|
+
.toString(36)
|
|
34
|
+
.substr(2, 9)}`;
|
|
35
|
+
return {
|
|
36
|
+
status: "pending_confirmation",
|
|
37
|
+
confirmationId,
|
|
38
|
+
command: commandInfo.command,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function resolveLegacyPythonCommand(command, cwd, env) {
|
|
42
|
+
if (!/^python(?=\s|$)/.test(command)) {
|
|
43
|
+
return command;
|
|
44
|
+
}
|
|
45
|
+
const options = {
|
|
46
|
+
cwd,
|
|
47
|
+
timeout: 3000,
|
|
48
|
+
env: { ...process.env, ...env },
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
const current = await execFileAsync("python", ["-c", "import sys; print(sys.version_info[0])"], options);
|
|
52
|
+
if (current.stdout.trim() !== "2") {
|
|
53
|
+
return command;
|
|
54
|
+
}
|
|
55
|
+
const replacement = await execFileAsync("python3", ["-c", "import sys; print(sys.version_info[0])"], options);
|
|
56
|
+
if (replacement.stdout.trim() === "3") {
|
|
57
|
+
return command.replace(/^python(?=\s|$)/, "python3");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Preserve the original command so normal execution reports the useful error.
|
|
62
|
+
}
|
|
63
|
+
return command;
|
|
64
|
+
}
|
|
65
|
+
export async function executeCommand({ command, cwd = process.cwd(), workspaceRoot, timeout = 30000, env = {}, runInBackground = false, description, skipConfirmation = false, approvalPolicy = "always_prompt", autoApproveRules, }) {
|
|
66
|
+
try {
|
|
67
|
+
if (!command) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: "'command' is required",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const dangerousPatterns = [
|
|
74
|
+
/rm\s+-rf\s+\//,
|
|
75
|
+
/:\(\)\{.*;\};/,
|
|
76
|
+
/mkfs/,
|
|
77
|
+
/dd\s+if=/,
|
|
78
|
+
/>\s*\/dev\/sda/,
|
|
79
|
+
];
|
|
80
|
+
const injectionPatterns = [
|
|
81
|
+
/`[^`]*`/,
|
|
82
|
+
/\$\([^)]*\)/,
|
|
83
|
+
/>\s*\/dev\//,
|
|
84
|
+
];
|
|
85
|
+
for (const pattern of dangerousPatterns) {
|
|
86
|
+
if (pattern.test(command)) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: "Command blocked for safety reasons",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const baseCommandMatch = command.match(/^(\S+)/);
|
|
94
|
+
const baseCommand = baseCommandMatch ? baseCommandMatch[1] : "";
|
|
95
|
+
const isSafeBaseCommand = /^(echo|cat|ls|pwd|cd|mkdir|touch|cp|mv|which|npm|node|git)$/.test(baseCommand);
|
|
96
|
+
const hasShellOperators = /[`$();|&<>]/.test(command);
|
|
97
|
+
if (!isSafeBaseCommand || hasShellOperators) {
|
|
98
|
+
for (const pattern of injectionPatterns) {
|
|
99
|
+
if (pattern.test(command)) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: "Command contains potentially unsafe characters. Use simple commands without shell operators.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const absoluteCwd = path.resolve(cwd);
|
|
108
|
+
if (!skipConfirmation) {
|
|
109
|
+
const canAutoApprove = approvalPolicy === "auto_approve_safe" &&
|
|
110
|
+
shouldAutoApproveTerminalCommand(command, autoApproveRules);
|
|
111
|
+
if (!canAutoApprove) {
|
|
112
|
+
return requestConfirmation({
|
|
113
|
+
command,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const validationRoot = workspaceRoot
|
|
118
|
+
? path.resolve(workspaceRoot)
|
|
119
|
+
: detectWorkspaceRootFromCwd(absoluteCwd);
|
|
120
|
+
if (!workspaceRoot) {
|
|
121
|
+
console.warn(`⚠️ Security warning: workspaceRoot not provided, inferred workspace root for validation: ${validationRoot}`);
|
|
122
|
+
}
|
|
123
|
+
if (!isPathWithinWorkspace(absoluteCwd, validationRoot)) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: "Working directory is outside workspace boundary. Access denied for security.",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const resolvedCommand = await resolveLegacyPythonCommand(command, absoluteCwd, env);
|
|
130
|
+
if (runInBackground) {
|
|
131
|
+
return startBackgroundTask({
|
|
132
|
+
command: resolvedCommand,
|
|
133
|
+
cwd: absoluteCwd,
|
|
134
|
+
env,
|
|
135
|
+
description,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const startTime = Date.now();
|
|
139
|
+
const parts = resolvedCommand.trim().split(/\s+/);
|
|
140
|
+
const execCommand = parts[0];
|
|
141
|
+
const args = parts.slice(1);
|
|
142
|
+
// Package managers commonly use >= / <= version specs; those are not shell operators.
|
|
143
|
+
const needsShell = /[`$();|&<>]/.test(resolvedCommand.replace(/[<>]=/g, ""));
|
|
144
|
+
try {
|
|
145
|
+
let stdout = "";
|
|
146
|
+
let stderr = "";
|
|
147
|
+
if (!needsShell) {
|
|
148
|
+
const result = await execFileAsync(execCommand, args, {
|
|
149
|
+
cwd: absoluteCwd,
|
|
150
|
+
timeout,
|
|
151
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
152
|
+
env: { ...process.env, ...env },
|
|
153
|
+
});
|
|
154
|
+
stdout = result.stdout;
|
|
155
|
+
stderr = result.stderr;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
const result = await execAsync(resolvedCommand, {
|
|
159
|
+
cwd: absoluteCwd,
|
|
160
|
+
timeout,
|
|
161
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
162
|
+
env: { ...process.env, ...env },
|
|
163
|
+
});
|
|
164
|
+
stdout = result.stdout;
|
|
165
|
+
stderr = result.stderr;
|
|
166
|
+
}
|
|
167
|
+
const executionTime = Date.now() - startTime;
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
command,
|
|
171
|
+
...(resolvedCommand !== command ? { resolvedCommand } : {}),
|
|
172
|
+
cwd: absoluteCwd,
|
|
173
|
+
stdout: stdout.trim(),
|
|
174
|
+
stderr: stderr.trim(),
|
|
175
|
+
executionTime,
|
|
176
|
+
exitCode: 0,
|
|
177
|
+
executionMethod: needsShell ? "shell" : "direct",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (execError) {
|
|
181
|
+
const error = execError;
|
|
182
|
+
const executionTime = Date.now() - startTime;
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
command,
|
|
186
|
+
...(resolvedCommand !== command ? { resolvedCommand } : {}),
|
|
187
|
+
cwd: absoluteCwd,
|
|
188
|
+
error: error.message,
|
|
189
|
+
stdout: error.stdout ? error.stdout.trim() : "",
|
|
190
|
+
stderr: error.stderr ? error.stderr.trim() : "",
|
|
191
|
+
exitCode: typeof error.code === "number" ? error.code : 1,
|
|
192
|
+
timedOut: error.killed && error.signal === "SIGTERM",
|
|
193
|
+
executionTime,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const err = error;
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
command,
|
|
202
|
+
error: err.message,
|
|
203
|
+
stdout: err.stdout ? err.stdout.trim() : "",
|
|
204
|
+
stderr: err.stderr ? err.stderr.trim() : "",
|
|
205
|
+
exitCode: typeof err.code === "number" ? err.code : 1,
|
|
206
|
+
timedOut: err.killed && err.signal === "SIGTERM",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export const executeCommandTool = {
|
|
211
|
+
description: "Execute terminal commands and return output. Commands require approval unless skipConfirmation is set internally or safe auto-approval policy applies.",
|
|
212
|
+
parameters: z.object({
|
|
213
|
+
command: z.string().optional().describe("The shell command to execute"),
|
|
214
|
+
cwd: z
|
|
215
|
+
.string()
|
|
216
|
+
.default(process.cwd())
|
|
217
|
+
.describe("Current working directory for the command"),
|
|
218
|
+
workspaceRoot: z
|
|
219
|
+
.string()
|
|
220
|
+
.optional()
|
|
221
|
+
.describe("Workspace root path for security validation - ensures paths stay within this boundary"),
|
|
222
|
+
timeout: z.number().default(30000).describe("Timeout in milliseconds"),
|
|
223
|
+
env: z
|
|
224
|
+
.record(z.string())
|
|
225
|
+
.optional()
|
|
226
|
+
.describe("Environment variables to pass to the command"),
|
|
227
|
+
runInBackground: z
|
|
228
|
+
.boolean()
|
|
229
|
+
.default(false)
|
|
230
|
+
.describe("Run the command as a managed background task and return a taskId for TaskList/TaskOutput/TaskStop"),
|
|
231
|
+
description: z
|
|
232
|
+
.string()
|
|
233
|
+
.optional()
|
|
234
|
+
.describe("Optional short description for background task tracking"),
|
|
235
|
+
skipConfirmation: z
|
|
236
|
+
.boolean()
|
|
237
|
+
.default(false)
|
|
238
|
+
.describe("Skip user confirmation - only used internally after user approves the command"),
|
|
239
|
+
approvalPolicy: z
|
|
240
|
+
.enum(["always_prompt", "auto_approve_safe"])
|
|
241
|
+
.default("always_prompt")
|
|
242
|
+
.describe("Approval behavior for the command. Use auto_approve_safe only for wrapper-aware readonly auto-approval."),
|
|
243
|
+
autoApproveRules: z
|
|
244
|
+
.record(z.any())
|
|
245
|
+
.optional()
|
|
246
|
+
.describe("Optional auto-approve rule map used for terminal command approval."),
|
|
247
|
+
}),
|
|
248
|
+
execute: executeCommand,
|
|
249
|
+
};
|
|
250
|
+
export default executeCommandTool;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
type FindFileContentParams = {
|
|
3
|
+
rootFolder: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
concurrency?: number;
|
|
6
|
+
isWebMode?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type FileContentSuccessResult = {
|
|
9
|
+
found: true;
|
|
10
|
+
path: string;
|
|
11
|
+
content: string | null;
|
|
12
|
+
error?: string;
|
|
13
|
+
};
|
|
14
|
+
type FileContentErrorResult = {
|
|
15
|
+
found: false;
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
18
|
+
type FileContentResult = FileContentSuccessResult | FileContentErrorResult;
|
|
19
|
+
/**
|
|
20
|
+
* Tool for finding and reading file content by searching through directory tree
|
|
21
|
+
* Supports both filesystem (Node.js) and web-based File System Access API
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} params - The parameters for finding file content
|
|
24
|
+
* @param {string} params.rootFolder - Root directory to start searching from
|
|
25
|
+
* @param {string} params.fileName - Exact file name to search for (including extension)
|
|
26
|
+
* @param {number} [params.concurrency=32] - Maximum number of parallel filesystem operations
|
|
27
|
+
* @param {boolean} [params.isWebMode=false] - Whether running in web mode (uses File System Access API)
|
|
28
|
+
* @returns {Promise<Object>} Object containing found status, file path, and content or error
|
|
29
|
+
*/
|
|
30
|
+
export declare function findFileContent({ rootFolder, fileName, concurrency, isWebMode, }: FindFileContentParams): Promise<FileContentResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Tool metadata for agent system
|
|
33
|
+
*/
|
|
34
|
+
export declare const findFileContentTool: {
|
|
35
|
+
description: string;
|
|
36
|
+
parameters: z.ZodObject<{
|
|
37
|
+
rootFolder: z.ZodString;
|
|
38
|
+
fileName: z.ZodString;
|
|
39
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
40
|
+
isWebMode: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
rootFolder?: string;
|
|
43
|
+
fileName?: string;
|
|
44
|
+
concurrency?: number;
|
|
45
|
+
isWebMode?: boolean;
|
|
46
|
+
}, {
|
|
47
|
+
rootFolder?: string;
|
|
48
|
+
fileName?: string;
|
|
49
|
+
concurrency?: number;
|
|
50
|
+
isWebMode?: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
execute: typeof findFileContent;
|
|
53
|
+
};
|
|
54
|
+
export default findFileContentTool;
|