aws-runtime-bridge 1.9.33 → 1.9.41
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/adapter/AcodeSdkAdapter.d.ts +66 -5
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/AcodeSdkAdapter.js +771 -69
- package/dist/adapter/ClaudeSdkAdapter.d.ts +6 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/ClaudeSdkAdapter.js +24 -1
- package/dist/adapter/OpencodeSdkAdapter.d.ts +19 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +62 -0
- package/dist/adapter/idle-poll-loop.d.ts +2 -1
- package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
- package/dist/adapter/types.d.ts +86 -9
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +15 -5
- package/dist/index.js +15 -0
- package/dist/routes/acode-todos.d.ts +11 -0
- package/dist/routes/acode-todos.d.ts.map +1 -0
- package/dist/routes/acode-todos.js +75 -0
- package/dist/routes/ai-sources.js +4 -1
- package/dist/routes/dashboard.d.ts.map +1 -1
- package/dist/routes/dashboard.js +1 -1
- package/dist/routes/memory.d.ts +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +3 -3
- package/dist/routes/pty.d.ts.map +1 -1
- package/dist/routes/pty.js +9 -0
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +6 -3
- package/dist/routes/sessions.js +89 -4
- package/dist/routes/sub-agents.d.ts +14 -0
- package/dist/routes/sub-agents.d.ts.map +1 -0
- package/dist/routes/sub-agents.js +267 -0
- package/dist/routes/terminal.d.ts +12 -1
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +326 -24
- package/dist/services/acode-package.d.ts +8 -1
- package/dist/services/acode-package.d.ts.map +1 -1
- package/dist/services/acode-package.js +33 -4
- package/dist/services/agent-event-store.d.ts +1 -0
- package/dist/services/agent-event-store.d.ts.map +1 -1
- package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
- package/dist/services/aws-client-agent-mcp.js +11 -4
- package/dist/services/event-delta-pusher.js +4 -4
- package/dist/services/fileSnapshotStore.js +1 -1
- package/dist/services/legacy-path-migration.d.ts +3 -0
- package/dist/services/legacy-path-migration.d.ts.map +1 -0
- package/dist/services/legacy-path-migration.js +66 -0
- package/dist/services/lifecycle-state.d.ts.map +1 -1
- package/dist/services/lifecycle-state.js +1 -1
- package/dist/services/runtime-binding.d.ts +5 -0
- package/dist/services/runtime-binding.d.ts.map +1 -1
- package/dist/services/runtime-binding.js +33 -1
- package/dist/services/session-output.d.ts +10 -1
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +54 -4
- package/dist/services/sub-agent-manager-instance.d.ts +4 -0
- package/dist/services/sub-agent-manager-instance.d.ts.map +1 -0
- package/dist/services/sub-agent-manager-instance.js +12 -0
- package/dist/services/workspace-files.d.ts +1 -0
- package/dist/services/workspace-files.d.ts.map +1 -1
- package/dist/services/workspace-files.js +57 -1
- package/package/acode/dist/ask-user-question-tool.d.ts +47 -0
- package/package/acode/dist/ask-user-question-tool.d.ts.map +1 -0
- package/package/acode/dist/ask-user-question-tool.js +233 -0
- package/package/acode/dist/built-in-file-tools.d.ts +2 -0
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.js +504 -3
- package/package/acode/dist/compaction.d.ts +242 -0
- package/package/acode/dist/compaction.d.ts.map +1 -0
- package/package/acode/dist/compaction.js +919 -0
- package/package/acode/dist/config.d.ts +6 -1
- package/package/acode/dist/config.d.ts.map +1 -1
- package/package/acode/dist/config.js +27 -2
- package/package/acode/dist/index.d.ts +4 -0
- package/package/acode/dist/index.d.ts.map +1 -1
- package/package/acode/dist/index.js +4 -0
- package/package/acode/dist/runtime.d.ts +257 -0
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +1843 -191
- package/package/acode/dist/skill-tool.d.ts +30 -0
- package/package/acode/dist/skill-tool.d.ts.map +1 -0
- package/package/acode/dist/skill-tool.js +174 -0
- package/package/acode/dist/sub-agent-manager.d.ts +264 -0
- package/package/acode/dist/sub-agent-manager.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-manager.js +825 -0
- package/package/acode/dist/sub-agent-store.d.ts +40 -0
- package/package/acode/dist/sub-agent-store.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-store.js +164 -0
- package/package/acode/dist/sub-agent-tools.d.ts +72 -0
- package/package/acode/dist/sub-agent-tools.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-tools.js +429 -0
- package/package/acode/dist/sub-agent-types.d.ts +85 -0
- package/package/acode/dist/sub-agent-types.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-types.js +8 -0
- package/package/acode/dist/todo-store.d.ts +42 -0
- package/package/acode/dist/todo-store.d.ts.map +1 -0
- package/package/acode/dist/todo-store.js +68 -0
- package/package/acode/dist/todo-tool.d.ts +6 -0
- package/package/acode/dist/todo-tool.d.ts.map +1 -0
- package/package/acode/dist/todo-tool.js +138 -0
- package/package/acode/dist/types.d.ts +41 -1
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/acode/package.json +13 -2
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +9 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +69 -10
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/config.js +4 -0
- package/package/aws-client-agent-mcp/dist/config.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/constants.js +3 -1
- package/package/aws-client-agent-mcp/dist/constants.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts +4 -1
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/context-manager.js +6 -2
- package/package/aws-client-agent-mcp/dist/context-manager.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +5 -3
- package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +5 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +15 -1
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +162 -71
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +1007 -504
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +371 -253
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +6 -17
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +20 -75
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +45 -45
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +114 -162
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +175 -164
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +3 -9
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js +31 -58
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +40 -9
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.js +123 -23
- package/package/aws-client-agent-mcp/dist/message-buffer.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +13 -13
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +23 -16
- package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +8 -5
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +93 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +12 -25
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +34 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +146 -18
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package.json +17 -7
- package/dist/adapter/AcodeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/AcodeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/AcodeSdkAdapter.test.js +0 -2000
- package/dist/adapter/AdapterRegistry.test.d.ts +0 -5
- package/dist/adapter/AdapterRegistry.test.d.ts.map +0 -1
- package/dist/adapter/AdapterRegistry.test.js +0 -118
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/ClaudeSdkAdapter.test.js +0 -311
- package/dist/adapter/CodexSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/CodexSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/CodexSdkAdapter.test.js +0 -270
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/OpencodeSdkAdapter.test.js +0 -158
- package/dist/adapter/acode-tool-names.test.d.ts +0 -2
- package/dist/adapter/acode-tool-names.test.d.ts.map +0 -1
- package/dist/adapter/acode-tool-names.test.js +0 -28
- package/dist/adapter/adapter.test.d.ts +0 -5
- package/dist/adapter/adapter.test.d.ts.map +0 -1
- package/dist/adapter/adapter.test.js +0 -180
- package/dist/adapter/idle-poll-loop.test.d.ts +0 -2
- package/dist/adapter/idle-poll-loop.test.d.ts.map +0 -1
- package/dist/adapter/idle-poll-loop.test.js +0 -85
- package/dist/adapter/types.test.d.ts +0 -2
- package/dist/adapter/types.test.d.ts.map +0 -1
- package/dist/adapter/types.test.js +0 -50
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-acode.test.js +0 -80
- package/dist/adapters/cc-switch/mcp-claudecode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-claudecode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-claudecode.test.js +0 -75
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-codex.test.js +0 -126
- package/dist/adapters/cc-switch/mcp-opencode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-opencode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-opencode.test.js +0 -62
- package/dist/adapters/cc-switch/skill-directory.test.d.ts +0 -2
- package/dist/adapters/cc-switch/skill-directory.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-directory.test.js +0 -74
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -107
- package/dist/routes/ai-sources.test.d.ts +0 -2
- package/dist/routes/ai-sources.test.d.ts.map +0 -1
- package/dist/routes/ai-sources.test.js +0 -169
- package/dist/routes/file-browser.test.d.ts +0 -5
- package/dist/routes/file-browser.test.d.ts.map +0 -1
- package/dist/routes/file-browser.test.js +0 -155
- package/dist/routes/git.test.d.ts +0 -2
- package/dist/routes/git.test.d.ts.map +0 -1
- package/dist/routes/git.test.js +0 -330
- package/dist/routes/instance.test.d.ts +0 -5
- package/dist/routes/instance.test.d.ts.map +0 -1
- package/dist/routes/instance.test.js +0 -389
- package/dist/routes/mcp.test.d.ts +0 -5
- package/dist/routes/mcp.test.d.ts.map +0 -1
- package/dist/routes/mcp.test.js +0 -92
- package/dist/routes/memory-route-wiring.test.d.ts +0 -2
- package/dist/routes/memory-route-wiring.test.d.ts.map +0 -1
- package/dist/routes/memory-route-wiring.test.js +0 -15
- package/dist/routes/memory.test.d.ts +0 -2
- package/dist/routes/memory.test.d.ts.map +0 -1
- package/dist/routes/memory.test.js +0 -177
- package/dist/routes/properties.test.d.ts +0 -5
- package/dist/routes/properties.test.d.ts.map +0 -1
- package/dist/routes/properties.test.js +0 -72
- package/dist/routes/pty.test.d.ts +0 -2
- package/dist/routes/pty.test.d.ts.map +0 -1
- package/dist/routes/pty.test.js +0 -82
- package/dist/routes/runtime-binding.test.d.ts +0 -2
- package/dist/routes/runtime-binding.test.d.ts.map +0 -1
- package/dist/routes/runtime-binding.test.js +0 -32
- package/dist/routes/sessions.test.d.ts +0 -5
- package/dist/routes/sessions.test.d.ts.map +0 -1
- package/dist/routes/sessions.test.js +0 -86
- package/dist/routes/skills.test.d.ts +0 -5
- package/dist/routes/skills.test.d.ts.map +0 -1
- package/dist/routes/skills.test.js +0 -50
- package/dist/routes/system-metrics.test.d.ts +0 -2
- package/dist/routes/system-metrics.test.d.ts.map +0 -1
- package/dist/routes/system-metrics.test.js +0 -16
- package/dist/routes/terminal.stop-options.test.d.ts +0 -2
- package/dist/routes/terminal.stop-options.test.d.ts.map +0 -1
- package/dist/routes/terminal.stop-options.test.js +0 -39
- package/dist/routes/terminal.test.d.ts +0 -2
- package/dist/routes/terminal.test.d.ts.map +0 -1
- package/dist/routes/terminal.test.js +0 -746
- package/dist/routes/yml.test.d.ts +0 -5
- package/dist/routes/yml.test.d.ts.map +0 -1
- package/dist/routes/yml.test.js +0 -58
- package/dist/services/ai-source-apply.test.d.ts +0 -2
- package/dist/services/ai-source-apply.test.d.ts.map +0 -1
- package/dist/services/ai-source-apply.test.js +0 -138
- package/dist/services/auto-register.test.d.ts +0 -2
- package/dist/services/auto-register.test.d.ts.map +0 -1
- package/dist/services/auto-register.test.js +0 -184
- package/dist/services/aws-client-agent-mcp.test.d.ts +0 -2
- package/dist/services/aws-client-agent-mcp.test.d.ts.map +0 -1
- package/dist/services/aws-client-agent-mcp.test.js +0 -242
- package/dist/services/bridge-package-publish-hooks.test.d.ts +0 -2
- package/dist/services/bridge-package-publish-hooks.test.d.ts.map +0 -1
- package/dist/services/bridge-package-publish-hooks.test.js +0 -136
- package/dist/services/cc-switch-discovery.test.d.ts +0 -2
- package/dist/services/cc-switch-discovery.test.d.ts.map +0 -1
- package/dist/services/cc-switch-discovery.test.js +0 -56
- package/dist/services/cli-commands.test.d.ts +0 -2
- package/dist/services/cli-commands.test.d.ts.map +0 -1
- package/dist/services/cli-commands.test.js +0 -230
- package/dist/services/instance-init-service.test.d.ts +0 -2
- package/dist/services/instance-init-service.test.d.ts.map +0 -1
- package/dist/services/instance-init-service.test.js +0 -140
- package/dist/services/instance-state.test.d.ts +0 -2
- package/dist/services/instance-state.test.d.ts.map +0 -1
- package/dist/services/instance-state.test.js +0 -245
- package/dist/services/mcp-launch-binding-queue.test.d.ts +0 -2
- package/dist/services/mcp-launch-binding-queue.test.d.ts.map +0 -1
- package/dist/services/mcp-launch-binding-queue.test.js +0 -181
- package/dist/services/runtime-binding.test.d.ts +0 -2
- package/dist/services/runtime-binding.test.d.ts.map +0 -1
- package/dist/services/runtime-binding.test.js +0 -78
- package/dist/services/runtime-lifecycle-policy.test.d.ts +0 -2
- package/dist/services/runtime-lifecycle-policy.test.d.ts.map +0 -1
- package/dist/services/runtime-lifecycle-policy.test.js +0 -41
- package/dist/services/session-output.test.d.ts +0 -5
- package/dist/services/session-output.test.d.ts.map +0 -1
- package/dist/services/session-output.test.js +0 -177
- package/dist/services/startup-config-wizard.test.d.ts +0 -2
- package/dist/services/startup-config-wizard.test.d.ts.map +0 -1
- package/dist/services/startup-config-wizard.test.js +0 -184
- package/dist/services/terminal-persistence.test.d.ts +0 -5
- package/dist/services/terminal-persistence.test.d.ts.map +0 -1
- package/dist/services/terminal-persistence.test.js +0 -159
- package/dist/services/tool-installer.test.d.ts +0 -2
- package/dist/services/tool-installer.test.d.ts.map +0 -1
- package/dist/services/tool-installer.test.js +0 -225
- package/dist/services/workspace-files.test.d.ts +0 -2
- package/dist/services/workspace-files.test.d.ts.map +0 -1
- package/dist/services/workspace-files.test.js +0 -648
- package/dist/services/workspace-watch.test.d.ts +0 -2
- package/dist/services/workspace-watch.test.d.ts.map +0 -1
- package/dist/services/workspace-watch.test.js +0 -38
- package/dist/utils/file-utils.test.d.ts +0 -2
- package/dist/utils/file-utils.test.d.ts.map +0 -1
- package/dist/utils/file-utils.test.js +0 -244
- package/dist/utils/logger.test.d.ts +0 -2
- package/dist/utils/logger.test.d.ts.map +0 -1
- package/dist/utils/logger.test.js +0 -93
- package/dist/utils/sdk-package-loader.test.d.ts +0 -2
- package/dist/utils/sdk-package-loader.test.d.ts.map +0 -1
- package/dist/utils/sdk-package-loader.test.js +0 -84
- package/dist/utils/validation.test.d.ts +0 -2
- package/dist/utils/validation.test.d.ts.map +0 -1
- package/dist/utils/validation.test.js +0 -88
- package/dist/utils/yaml-utils.test.d.ts +0 -2
- package/dist/utils/yaml-utils.test.d.ts.map +0 -1
- package/dist/utils/yaml-utils.test.js +0 -363
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
2
|
+
import { promises as fs, createReadStream } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import readline from "node:readline";
|
|
4
5
|
import { runShellCommand, } from "./builtins/shellCommandRunner.js";
|
|
6
|
+
// ── read_file pagination & truncation constants ──────────────────────────────
|
|
7
|
+
const DEFAULT_READ_LIMIT = 2000;
|
|
8
|
+
const MAX_LINE_LENGTH = 2000;
|
|
9
|
+
const MAX_READ_BYTES = 50 * 1024; // 50 KB
|
|
10
|
+
const BINARY_SAMPLE_BYTES = 4096;
|
|
11
|
+
const BINARY_NON_PRINTABLE_RATIO = 0.3;
|
|
12
|
+
const BINARY_EXTENSIONS = new Set([
|
|
13
|
+
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".webp", ".tiff", ".tif",
|
|
14
|
+
".zip", ".gz", ".tar", ".rar", ".7z", ".bz2", ".xz", ".lz",
|
|
15
|
+
".exe", ".dll", ".so", ".dylib", ".bin", ".o", ".a",
|
|
16
|
+
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
|
|
17
|
+
".mp3", ".mp4", ".avi", ".mov", ".wav", ".flv", ".mkv", ".ogg",
|
|
18
|
+
".woff", ".woff2", ".ttf", ".otf", ".eot",
|
|
19
|
+
".class", ".jar", ".war", ".ear",
|
|
20
|
+
".pyc", ".pyo", ".node",
|
|
21
|
+
".sqlite", ".db", ".mdb",
|
|
22
|
+
".wasm",
|
|
23
|
+
]);
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
// ── grep search constants ────────────────────────────────────────────────────
|
|
26
|
+
const MAX_GREP_FILES = 1000;
|
|
27
|
+
const MAX_GREP_FILE_SIZE = 1024 * 1024; // 1 MB – skip larger files
|
|
28
|
+
const MAX_GREP_OUTPUT_BYTES = 256 * 1024; // 256 KB – cap total output
|
|
29
|
+
const DEFAULT_GREP_MAX_RESULTS = 1000;
|
|
30
|
+
const GREP_SKIP_DIRS = new Set([
|
|
31
|
+
".git", "node_modules", "dist", "build", ".next", ".nuxt",
|
|
32
|
+
".cache", ".turbo", ".output", "coverage", ".nyc_output",
|
|
33
|
+
]);
|
|
5
34
|
function toDisplayPath(targetPath) {
|
|
6
35
|
return targetPath.replace(/\\/g, "/");
|
|
7
36
|
}
|
|
@@ -58,6 +87,42 @@ function readOptionalPositiveIntegerArg(args, names, fallback) {
|
|
|
58
87
|
}
|
|
59
88
|
return fallback;
|
|
60
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* 检测文件是否为二进制文件。
|
|
92
|
+
* 主流程:先查扩展名黑名单 -> 再采样前 4KB 检测 NUL 字节或高比例不可见字符。
|
|
93
|
+
*/
|
|
94
|
+
async function isBinaryFile(filePath) {
|
|
95
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
96
|
+
if (BINARY_EXTENSIONS.has(ext)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const handle = await fs.open(filePath, "r");
|
|
100
|
+
try {
|
|
101
|
+
const buffer = Buffer.alloc(BINARY_SAMPLE_BYTES);
|
|
102
|
+
const { bytesRead } = await handle.read(buffer, 0, BINARY_SAMPLE_BYTES, 0);
|
|
103
|
+
if (bytesRead === 0) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const sample = buffer.subarray(0, bytesRead);
|
|
107
|
+
// NUL byte = definitely binary
|
|
108
|
+
if (sample.includes(0)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
// >30% non-printable characters = likely binary
|
|
112
|
+
let nonPrintable = 0;
|
|
113
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
114
|
+
const byte = sample[i];
|
|
115
|
+
// Allow: tab(0x09), LF(0x0a), CR(0x0d), printable ASCII (0x20-0x7e), UTF-8 high bytes (0x80-0xff)
|
|
116
|
+
if (byte < 0x09 || (byte > 0x0d && byte < 0x20)) {
|
|
117
|
+
nonPrintable++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return nonPrintable / bytesRead > BINARY_NON_PRINTABLE_RATIO;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await handle.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
61
126
|
function ensureInsideWorkspace(workspaceRoot, targetPath) {
|
|
62
127
|
const resolvedTargetPath = path.resolve(targetPath);
|
|
63
128
|
const relativePath = path.relative(workspaceRoot, resolvedTargetPath);
|
|
@@ -178,6 +243,249 @@ async function readFileInsideWorkspace(workingDirectory, filePath) {
|
|
|
178
243
|
content: await fs.readFile(realTargetPath, "utf-8"),
|
|
179
244
|
};
|
|
180
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* 分页流式读取工作区内文本文件,带行号前缀、双重截断上限和续读提示。
|
|
248
|
+
*
|
|
249
|
+
* 主流程:
|
|
250
|
+
* 1. 路径解析与安全校验(复用 resolveWorkspaceTarget + ensureInsideWorkspace + realpath)
|
|
251
|
+
* 2. 二进制检测(扩展名黑名单 + 4KB 采样)
|
|
252
|
+
* 3. createReadStream + readline 逐行流式读取,跳过 offset 前行
|
|
253
|
+
* 4. 收集 limit 行,同时持续计数总行数(用于底部提示)
|
|
254
|
+
* 5. 双重上限:行数 2000 + 字节 50KB,先触发先截断
|
|
255
|
+
* 6. 拼装行号前缀 + 底部续读提示
|
|
256
|
+
*
|
|
257
|
+
* 不修改 readFileInsideWorkspace,edit_file 仍使用全量读取。
|
|
258
|
+
*/
|
|
259
|
+
async function readFileWithPagination(workingDirectory, filePath, offset, limit) {
|
|
260
|
+
const target = await resolveWorkspaceTarget(workingDirectory, filePath);
|
|
261
|
+
const realTargetPath = ensureInsideWorkspace(target.workspaceRoot, await fs.realpath(target.targetPath));
|
|
262
|
+
const stat = await fs.stat(realTargetPath);
|
|
263
|
+
if (!stat.isFile()) {
|
|
264
|
+
throw new Error(`Path is not a file: ${realTargetPath}`);
|
|
265
|
+
}
|
|
266
|
+
// ── 二进制检测 ──
|
|
267
|
+
if (await isBinaryFile(realTargetPath)) {
|
|
268
|
+
return {
|
|
269
|
+
workspaceRoot: target.workspaceRoot,
|
|
270
|
+
targetPath: realTargetPath,
|
|
271
|
+
content: "[Binary file, content skipped]",
|
|
272
|
+
isBinary: true,
|
|
273
|
+
truncated: false,
|
|
274
|
+
totalLines: 0,
|
|
275
|
+
shownFromLine: 0,
|
|
276
|
+
shownToLine: 0,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
// ── 流式逐行读取 ──
|
|
280
|
+
const stream = createReadStream(realTargetPath, { encoding: "utf-8" });
|
|
281
|
+
const rl = readline.createInterface({
|
|
282
|
+
input: stream,
|
|
283
|
+
crlfDelay: Infinity,
|
|
284
|
+
});
|
|
285
|
+
try {
|
|
286
|
+
const outputLines = [];
|
|
287
|
+
let lineNumber = 0; // 0-indexed 内部计数
|
|
288
|
+
let totalLines = 0;
|
|
289
|
+
let bytes = 0;
|
|
290
|
+
let truncatedByLines = false;
|
|
291
|
+
let truncatedByBytes = false;
|
|
292
|
+
const startIndex = offset - 1; // 转为 0-indexed
|
|
293
|
+
for await (const text of rl) {
|
|
294
|
+
totalLines++;
|
|
295
|
+
// 跳过 offset 前的行
|
|
296
|
+
if (lineNumber < startIndex) {
|
|
297
|
+
lineNumber++;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
// 已达 limit 上限:停止收集但继续计数
|
|
301
|
+
if (outputLines.length >= limit) {
|
|
302
|
+
truncatedByLines = true;
|
|
303
|
+
lineNumber++;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
// 单行截断
|
|
307
|
+
let line = text;
|
|
308
|
+
if (line.length > MAX_LINE_LENGTH) {
|
|
309
|
+
line =
|
|
310
|
+
line.slice(0, MAX_LINE_LENGTH) +
|
|
311
|
+
`... (line truncated to ${MAX_LINE_LENGTH} chars)`;
|
|
312
|
+
}
|
|
313
|
+
const numberedLine = `${lineNumber + 1}: ${line}`;
|
|
314
|
+
const lineBytes = Buffer.byteLength(numberedLine, "utf-8") + 1; // +1 for \n
|
|
315
|
+
// 字节上限:停止读取(totalLines 不再准确)
|
|
316
|
+
if (bytes + lineBytes > MAX_READ_BYTES) {
|
|
317
|
+
truncatedByBytes = true;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
outputLines.push(numberedLine);
|
|
321
|
+
bytes += lineBytes;
|
|
322
|
+
lineNumber++;
|
|
323
|
+
}
|
|
324
|
+
// offset 超出文件总行数(空文件除外)
|
|
325
|
+
if (totalLines > 0 &&
|
|
326
|
+
outputLines.length === 0 &&
|
|
327
|
+
!truncatedByLines &&
|
|
328
|
+
!truncatedByBytes &&
|
|
329
|
+
startIndex >= totalLines) {
|
|
330
|
+
throw new Error(`offset ${offset} exceeds total line count ${totalLines} in file: ${realTargetPath}`);
|
|
331
|
+
}
|
|
332
|
+
const shownFromLine = outputLines.length > 0 ? offset : 0;
|
|
333
|
+
const shownToLine = outputLines.length > 0 ? offset + outputLines.length - 1 : 0;
|
|
334
|
+
// ── 底部续读提示 ──
|
|
335
|
+
let footer;
|
|
336
|
+
if (truncatedByBytes) {
|
|
337
|
+
footer = `Output capped at 50 KB. Showing lines ${shownFromLine}-${shownToLine}. Use offset=${shownToLine + 1} to continue.`;
|
|
338
|
+
}
|
|
339
|
+
else if (truncatedByLines) {
|
|
340
|
+
footer = `Showing lines ${shownFromLine}-${shownToLine} of ${totalLines}. Use offset=${shownToLine + 1} to continue.`;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
footer = `End of file - total ${totalLines} lines`;
|
|
344
|
+
}
|
|
345
|
+
const content = outputLines.length > 0
|
|
346
|
+
? outputLines.join("\n") + "\n" + footer
|
|
347
|
+
: footer;
|
|
348
|
+
return {
|
|
349
|
+
workspaceRoot: target.workspaceRoot,
|
|
350
|
+
targetPath: realTargetPath,
|
|
351
|
+
content,
|
|
352
|
+
isBinary: false,
|
|
353
|
+
truncated: truncatedByLines || truncatedByBytes,
|
|
354
|
+
totalLines: truncatedByBytes ? -1 : totalLines,
|
|
355
|
+
shownFromLine,
|
|
356
|
+
shownToLine,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
rl.close();
|
|
361
|
+
stream.destroy();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// ── grep helpers ──────────────────────────────────────────────────────────────
|
|
365
|
+
/**
|
|
366
|
+
* 将 glob 通配符转换为 RegExp(匹配文件 basename)。
|
|
367
|
+
* 支持:* (不跨 / ), ? (单个非 / 字符), {a,b} (分组), [abc] (字符类)。
|
|
368
|
+
*/
|
|
369
|
+
function globToRegex(pattern) {
|
|
370
|
+
let regex = "";
|
|
371
|
+
let i = 0;
|
|
372
|
+
while (i < pattern.length) {
|
|
373
|
+
const char = pattern[i];
|
|
374
|
+
if (char === "*") {
|
|
375
|
+
regex += "[^/]*";
|
|
376
|
+
}
|
|
377
|
+
else if (char === "?") {
|
|
378
|
+
regex += "[^/]";
|
|
379
|
+
}
|
|
380
|
+
else if (char === "{") {
|
|
381
|
+
const end = pattern.indexOf("}", i);
|
|
382
|
+
if (end >= 0) {
|
|
383
|
+
const content = pattern.slice(i + 1, end);
|
|
384
|
+
regex += `(${content.split(",").join("|")})`;
|
|
385
|
+
i = end;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
regex += "\\{";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else if (char === "[") {
|
|
392
|
+
const end = pattern.indexOf("]", i);
|
|
393
|
+
if (end >= 0) {
|
|
394
|
+
regex += pattern.slice(i, end + 1);
|
|
395
|
+
i = end;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
regex += "\\[";
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else if ("+^$.()|\\".includes(char)) {
|
|
402
|
+
regex += "\\" + char;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
regex += char;
|
|
406
|
+
}
|
|
407
|
+
i++;
|
|
408
|
+
}
|
|
409
|
+
return new RegExp(`^${regex}$`);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 递归遍历目录,返回所有匹配 include/exclude 的文件路径。
|
|
413
|
+
* 主流程:readdir -> 跳过 GREP_SKIP_DIRS 和隐藏目录 -> 递归子目录 -> 过滤文件名。
|
|
414
|
+
*/
|
|
415
|
+
async function walkDirectory(dir, includeRegex, excludeRegex, maxFiles) {
|
|
416
|
+
const results = [];
|
|
417
|
+
async function walk(currentDir) {
|
|
418
|
+
if (results.length >= maxFiles)
|
|
419
|
+
return;
|
|
420
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
421
|
+
for (const entry of entries) {
|
|
422
|
+
if (results.length >= maxFiles)
|
|
423
|
+
return;
|
|
424
|
+
if (entry.name.startsWith(".") && entry.isDirectory())
|
|
425
|
+
continue;
|
|
426
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
427
|
+
if (entry.isDirectory()) {
|
|
428
|
+
if (GREP_SKIP_DIRS.has(entry.name))
|
|
429
|
+
continue;
|
|
430
|
+
await walk(fullPath);
|
|
431
|
+
}
|
|
432
|
+
else if (entry.isFile()) {
|
|
433
|
+
if (includeRegex && !includeRegex.test(entry.name))
|
|
434
|
+
continue;
|
|
435
|
+
if (excludeRegex && excludeRegex.test(entry.name))
|
|
436
|
+
continue;
|
|
437
|
+
results.push(fullPath);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
await walk(dir);
|
|
442
|
+
return results;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* 在单个文件中搜索正则匹配,返回匹配行(含行号)或计数。
|
|
446
|
+
* 主流程:文件大小检查 -> 二进制检测 -> 流式逐行读取 -> 正则匹配 -> 收集结果。
|
|
447
|
+
*/
|
|
448
|
+
async function searchInFile(filePath, workspaceRoot, options) {
|
|
449
|
+
const stat = await fs.stat(filePath);
|
|
450
|
+
if (stat.size > MAX_GREP_FILE_SIZE) {
|
|
451
|
+
return { matches: [], count: 0 };
|
|
452
|
+
}
|
|
453
|
+
if (await isBinaryFile(filePath)) {
|
|
454
|
+
return { matches: [], count: 0 };
|
|
455
|
+
}
|
|
456
|
+
const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, "/");
|
|
457
|
+
const matches = [];
|
|
458
|
+
let count = 0;
|
|
459
|
+
const stream = createReadStream(filePath, { encoding: "utf-8" });
|
|
460
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
461
|
+
try {
|
|
462
|
+
let lineNumber = 0;
|
|
463
|
+
for await (const text of rl) {
|
|
464
|
+
lineNumber++;
|
|
465
|
+
if (options.regex.test(text)) {
|
|
466
|
+
count++;
|
|
467
|
+
if (options.collectContent) {
|
|
468
|
+
let content = text;
|
|
469
|
+
if (content.length > MAX_LINE_LENGTH) {
|
|
470
|
+
content =
|
|
471
|
+
content.slice(0, MAX_LINE_LENGTH) +
|
|
472
|
+
`... (line truncated to ${MAX_LINE_LENGTH} chars)`;
|
|
473
|
+
}
|
|
474
|
+
matches.push({ file: relativePath, line: lineNumber, content });
|
|
475
|
+
if (matches.length >= options.maxResults)
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
if (options.stopOnFirstMatch)
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
rl.close();
|
|
485
|
+
stream.destroy();
|
|
486
|
+
}
|
|
487
|
+
return { matches, count };
|
|
488
|
+
}
|
|
181
489
|
const MAX_COMMAND_TIMEOUT_MS = 10 * 60_000;
|
|
182
490
|
const DEFAULT_COMMAND_TIMEOUT_MS = MAX_COMMAND_TIMEOUT_MS;
|
|
183
491
|
const DEFAULT_COMMAND_INACTIVITY_TIMEOUT_MS = 60_000;
|
|
@@ -273,7 +581,12 @@ function readFileTool(workingDirectory) {
|
|
|
273
581
|
kind: "builtin",
|
|
274
582
|
toolName: "read_file",
|
|
275
583
|
exposedName: "read_file",
|
|
276
|
-
description: "Read a UTF-8 text file inside the current workspace."
|
|
584
|
+
description: "Read a UTF-8 text file inside the current workspace. " +
|
|
585
|
+
"Returns content with line numbers in the format '<line>: <content>'. " +
|
|
586
|
+
"Supports offset (1-indexed line number to start from) and limit (max lines to return, default 2000). " +
|
|
587
|
+
"Output is capped at 2000 lines or 50 KB, whichever is reached first. " +
|
|
588
|
+
"Individual lines longer than 2000 characters are truncated. " +
|
|
589
|
+
"Avoid tiny repeated slices (e.g. 30-line chunks); read a larger window if more context is needed.",
|
|
277
590
|
inputSchema: {
|
|
278
591
|
type: "object",
|
|
279
592
|
properties: {
|
|
@@ -281,19 +594,34 @@ function readFileTool(workingDirectory) {
|
|
|
281
594
|
type: "string",
|
|
282
595
|
description: "Workspace-relative file path to read.",
|
|
283
596
|
},
|
|
597
|
+
offset: {
|
|
598
|
+
type: "number",
|
|
599
|
+
description: "Line number to start reading from (1-indexed). Defaults to 1.",
|
|
600
|
+
},
|
|
601
|
+
limit: {
|
|
602
|
+
type: "number",
|
|
603
|
+
description: "Maximum number of lines to return. Defaults to 2000.",
|
|
604
|
+
},
|
|
284
605
|
},
|
|
285
606
|
required: ["file_path"],
|
|
286
607
|
additionalProperties: false,
|
|
287
608
|
},
|
|
288
609
|
async execute(args) {
|
|
289
610
|
const filePath = readStringArg(args, ["file_path", "filePath"], "file_path");
|
|
290
|
-
const
|
|
611
|
+
const offset = readOptionalPositiveIntegerArg(args, ["offset"], 1);
|
|
612
|
+
const limit = readOptionalPositiveIntegerArg(args, ["limit"], DEFAULT_READ_LIMIT);
|
|
613
|
+
const result = await readFileWithPagination(workingDirectory, filePath, offset, limit);
|
|
291
614
|
return {
|
|
292
615
|
ok: true,
|
|
293
616
|
tool: "read_file",
|
|
294
617
|
workspacePath: toDisplayPath(result.workspaceRoot),
|
|
295
618
|
filePath: toDisplayPath(result.targetPath),
|
|
296
619
|
content: result.content,
|
|
620
|
+
isBinary: result.isBinary,
|
|
621
|
+
truncated: result.truncated,
|
|
622
|
+
totalLines: result.totalLines,
|
|
623
|
+
shownFromLine: result.shownFromLine,
|
|
624
|
+
shownToLine: result.shownToLine,
|
|
297
625
|
};
|
|
298
626
|
},
|
|
299
627
|
};
|
|
@@ -371,12 +699,185 @@ function bashTool(workingDirectory, agentId) {
|
|
|
371
699
|
},
|
|
372
700
|
};
|
|
373
701
|
}
|
|
702
|
+
function grepTool(workingDirectory) {
|
|
703
|
+
return {
|
|
704
|
+
kind: "builtin",
|
|
705
|
+
toolName: "grep",
|
|
706
|
+
exposedName: "grep",
|
|
707
|
+
description: "Search file contents using regular expressions within the workspace. " +
|
|
708
|
+
"Returns matched lines with file paths and line numbers. " +
|
|
709
|
+
"Supports include/exclude glob patterns, case-insensitive matching, and three output modes " +
|
|
710
|
+
"(content, files_with_matches, count). Searches recursively through directories, " +
|
|
711
|
+
"skipping binary files, hidden directories, and common build/dependency directories (node_modules, .git, dist, etc.).",
|
|
712
|
+
inputSchema: {
|
|
713
|
+
type: "object",
|
|
714
|
+
properties: {
|
|
715
|
+
pattern: {
|
|
716
|
+
type: "string",
|
|
717
|
+
description: "Regular expression pattern to search for in file contents.",
|
|
718
|
+
},
|
|
719
|
+
path: {
|
|
720
|
+
type: "string",
|
|
721
|
+
description: "Workspace-relative file or directory path to search in. Defaults to the workspace root.",
|
|
722
|
+
},
|
|
723
|
+
include: {
|
|
724
|
+
type: "string",
|
|
725
|
+
description: "Glob pattern to filter files by name (e.g. '*.ts', '*.{js,ts}'). Only matching files are searched.",
|
|
726
|
+
},
|
|
727
|
+
exclude: {
|
|
728
|
+
type: "string",
|
|
729
|
+
description: "Glob pattern to exclude files by name (e.g. '*.test.ts'). Matching files are skipped.",
|
|
730
|
+
},
|
|
731
|
+
case_insensitive: {
|
|
732
|
+
type: "boolean",
|
|
733
|
+
description: "Case-insensitive matching. Default false.",
|
|
734
|
+
},
|
|
735
|
+
output_mode: {
|
|
736
|
+
type: "string",
|
|
737
|
+
enum: ["content", "files_with_matches", "count"],
|
|
738
|
+
description: "Output format: 'content' returns matched lines with line numbers (default), " +
|
|
739
|
+
"'files_with_matches' returns only file paths with matches, " +
|
|
740
|
+
"'count' returns match counts per file.",
|
|
741
|
+
},
|
|
742
|
+
max_results: {
|
|
743
|
+
type: "number",
|
|
744
|
+
description: "Maximum number of matched lines to return (content mode). Default 1000.",
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
required: ["pattern"],
|
|
748
|
+
additionalProperties: false,
|
|
749
|
+
},
|
|
750
|
+
/**
|
|
751
|
+
* grep 工具执行主流程:
|
|
752
|
+
* 1. 解析参数(pattern, path, include/exclude, output_mode 等)
|
|
753
|
+
* 2. 编译正则(含 case_insensitive 标志)
|
|
754
|
+
* 3. 解析搜索路径并校验 workspace 边界
|
|
755
|
+
* 4. 收集待搜索文件列表(单文件或递归遍历目录)
|
|
756
|
+
* 5. 逐文件搜索(流式读取 + 正则匹配)
|
|
757
|
+
* 6. 根据 output_mode 构建响应
|
|
758
|
+
*/
|
|
759
|
+
async execute(args) {
|
|
760
|
+
const pattern = readStringArg(args, ["pattern"], "pattern");
|
|
761
|
+
const searchPath = readOptionalStringArg(args, ["path"]) || ".";
|
|
762
|
+
const includeGlob = readOptionalStringArg(args, ["include"]);
|
|
763
|
+
const excludeGlob = readOptionalStringArg(args, ["exclude"]);
|
|
764
|
+
const caseInsensitive = args.case_insensitive === true;
|
|
765
|
+
const outputMode = (() => {
|
|
766
|
+
const mode = args.output_mode;
|
|
767
|
+
if (mode === "files_with_matches" || mode === "count")
|
|
768
|
+
return mode;
|
|
769
|
+
return "content";
|
|
770
|
+
})();
|
|
771
|
+
const maxResults = readOptionalPositiveIntegerArg(args, ["max_results"], DEFAULT_GREP_MAX_RESULTS);
|
|
772
|
+
// ── 编译正则 ──
|
|
773
|
+
const flags = caseInsensitive ? "i" : "";
|
|
774
|
+
let regex;
|
|
775
|
+
try {
|
|
776
|
+
regex = new RegExp(pattern, flags);
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
throw new Error(`Invalid regex pattern: ${error instanceof Error ? error.message : String(error)}`);
|
|
780
|
+
}
|
|
781
|
+
const includeRegex = includeGlob ? globToRegex(includeGlob) : null;
|
|
782
|
+
const excludeRegex = excludeGlob ? globToRegex(excludeGlob) : null;
|
|
783
|
+
// ── 解析搜索路径(复用 workspace 安全校验) ──
|
|
784
|
+
const target = await resolveWorkspaceTarget(workingDirectory, searchPath);
|
|
785
|
+
const realTargetPath = ensureInsideWorkspace(target.workspaceRoot, await fs.realpath(target.targetPath));
|
|
786
|
+
const targetStat = await fs.stat(realTargetPath);
|
|
787
|
+
// ── 收集文件列表 ──
|
|
788
|
+
let files;
|
|
789
|
+
if (targetStat.isFile()) {
|
|
790
|
+
files = [realTargetPath];
|
|
791
|
+
}
|
|
792
|
+
else if (targetStat.isDirectory()) {
|
|
793
|
+
files = await walkDirectory(realTargetPath, includeRegex, excludeRegex, MAX_GREP_FILES);
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
throw new Error(`Path is neither a file nor a directory: ${realTargetPath}`);
|
|
797
|
+
}
|
|
798
|
+
// ── 逐文件搜索 ──
|
|
799
|
+
const allMatches = [];
|
|
800
|
+
const fileCounts = [];
|
|
801
|
+
const matchedFiles = [];
|
|
802
|
+
let totalMatches = 0;
|
|
803
|
+
let outputBytes = 0;
|
|
804
|
+
let truncated = false;
|
|
805
|
+
for (const filePath of files) {
|
|
806
|
+
// content 模式:全局 maxResults 上限
|
|
807
|
+
if (outputMode === "content" && allMatches.length >= maxResults) {
|
|
808
|
+
truncated = true;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
const searchOptions = {
|
|
812
|
+
regex,
|
|
813
|
+
maxResults: outputMode === "content"
|
|
814
|
+
? maxResults - allMatches.length
|
|
815
|
+
: Number.MAX_SAFE_INTEGER,
|
|
816
|
+
collectContent: outputMode === "content",
|
|
817
|
+
stopOnFirstMatch: outputMode === "files_with_matches",
|
|
818
|
+
};
|
|
819
|
+
const result = await searchInFile(filePath, target.workspaceRoot, searchOptions);
|
|
820
|
+
if (result.count > 0) {
|
|
821
|
+
const relativePath = path
|
|
822
|
+
.relative(target.workspaceRoot, filePath)
|
|
823
|
+
.replace(/\\/g, "/");
|
|
824
|
+
matchedFiles.push(relativePath);
|
|
825
|
+
totalMatches += result.count;
|
|
826
|
+
if (outputMode === "content") {
|
|
827
|
+
for (const match of result.matches) {
|
|
828
|
+
const matchBytes = Buffer.byteLength(match.content, "utf-8") + 64;
|
|
829
|
+
if (outputBytes + matchBytes > MAX_GREP_OUTPUT_BYTES) {
|
|
830
|
+
truncated = true;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
outputBytes += matchBytes;
|
|
834
|
+
allMatches.push(match);
|
|
835
|
+
}
|
|
836
|
+
if (truncated)
|
|
837
|
+
break;
|
|
838
|
+
// max_results 截断:收集完本文件匹配后检查是否已达上限
|
|
839
|
+
if (allMatches.length >= maxResults) {
|
|
840
|
+
truncated = true;
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
else if (outputMode === "count") {
|
|
845
|
+
fileCounts.push({ file: relativePath, count: result.count });
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// ── 构建响应 ──
|
|
850
|
+
const response = {
|
|
851
|
+
ok: true,
|
|
852
|
+
tool: "grep",
|
|
853
|
+
pattern,
|
|
854
|
+
searchPath: toDisplayPath(target.targetPath),
|
|
855
|
+
outputMode,
|
|
856
|
+
filesSearched: files.length,
|
|
857
|
+
filesMatched: matchedFiles.length,
|
|
858
|
+
totalMatches,
|
|
859
|
+
truncated,
|
|
860
|
+
};
|
|
861
|
+
if (outputMode === "content") {
|
|
862
|
+
response.matches = allMatches;
|
|
863
|
+
}
|
|
864
|
+
else if (outputMode === "files_with_matches") {
|
|
865
|
+
response.matches = matchedFiles;
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
response.matches = fileCounts;
|
|
869
|
+
}
|
|
870
|
+
return response;
|
|
871
|
+
},
|
|
872
|
+
};
|
|
873
|
+
}
|
|
374
874
|
export function createBuiltInFileTools(workingDirectory, agentId) {
|
|
375
875
|
const resolvedAgentId = agentId || "unknown";
|
|
376
876
|
return [
|
|
377
877
|
readFileTool(workingDirectory),
|
|
378
878
|
writeFileTool(workingDirectory),
|
|
379
879
|
editFileTool(workingDirectory),
|
|
880
|
+
grepTool(workingDirectory),
|
|
380
881
|
bashTool(workingDirectory, resolvedAgentId),
|
|
381
882
|
];
|
|
382
883
|
}
|