@gguf/coder 0.2.9 → 0.3.1
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/.editorconfig +16 -0
- package/.env.example +63 -0
- package/.gitattributes +1 -0
- package/.semgrepignore +19 -0
- package/coder-dummy-file.ts +52 -0
- package/coder.config.example.json +59 -0
- package/coder.config.json +13 -0
- package/color_picker.html +36 -0
- package/dist/tools/execute-bash.js +3 -3
- package/dist/tools/execute-bash.js.map +1 -1
- package/dist/tools/fetch-url.js +3 -3
- package/dist/tools/fetch-url.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +1 -1
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/lsp-get-diagnostics.js +1 -1
- package/dist/tools/lsp-get-diagnostics.js.map +1 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +6 -6
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/search-file-contents.d.ts.map +1 -1
- package/dist/tools/search-file-contents.js +1 -1
- package/dist/tools/search-file-contents.js.map +1 -1
- package/dist/tools/string-replace.js +11 -11
- package/dist/tools/string-replace.js.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools/write-file.js +4 -4
- package/dist/tools/write-file.js.map +1 -1
- package/dist/utils/tool-result-display.d.ts.map +1 -1
- package/dist/utils/tool-result-display.js +3 -3
- package/dist/utils/tool-result-display.js.map +1 -1
- package/package.json +2 -14
- package/scripts/extract-changelog.js +73 -0
- package/scripts/fetch-models.js +143 -0
- package/scripts/test.sh +40 -0
- package/scripts/update-homebrew-formula.sh +125 -0
- package/scripts/update-nix-version.sh +157 -0
- package/source/ai-sdk-client/AISDKClient.spec.ts +117 -0
- package/source/ai-sdk-client/AISDKClient.ts +155 -0
- package/source/ai-sdk-client/chat/chat-handler.spec.ts +121 -0
- package/source/ai-sdk-client/chat/chat-handler.ts +276 -0
- package/source/ai-sdk-client/chat/streaming-handler.spec.ts +173 -0
- package/source/ai-sdk-client/chat/streaming-handler.ts +110 -0
- package/source/ai-sdk-client/chat/tool-processor.spec.ts +92 -0
- package/source/ai-sdk-client/chat/tool-processor.ts +70 -0
- package/source/ai-sdk-client/converters/message-converter.spec.ts +220 -0
- package/source/ai-sdk-client/converters/message-converter.ts +113 -0
- package/source/ai-sdk-client/converters/tool-converter.spec.ts +90 -0
- package/source/ai-sdk-client/converters/tool-converter.ts +46 -0
- package/source/ai-sdk-client/error-handling/error-extractor.spec.ts +55 -0
- package/source/ai-sdk-client/error-handling/error-extractor.ts +15 -0
- package/source/ai-sdk-client/error-handling/error-parser.spec.ts +169 -0
- package/source/ai-sdk-client/error-handling/error-parser.ts +161 -0
- package/source/ai-sdk-client/index.ts +7 -0
- package/source/ai-sdk-client/providers/provider-factory.spec.ts +71 -0
- package/source/ai-sdk-client/providers/provider-factory.ts +41 -0
- package/source/ai-sdk-client/types.ts +9 -0
- package/source/ai-sdk-client-empty-message.spec.ts +141 -0
- package/source/ai-sdk-client-error-handling.spec.ts +186 -0
- package/source/ai-sdk-client-maxretries.spec.ts +114 -0
- package/source/ai-sdk-client-preparestep.spec.ts +279 -0
- package/source/app/App.spec.tsx +32 -0
- package/source/app/App.tsx +480 -0
- package/source/app/components/AppContainer.spec.tsx +96 -0
- package/source/app/components/AppContainer.tsx +56 -0
- package/source/app/components/ChatInterface.spec.tsx +163 -0
- package/source/app/components/ChatInterface.tsx +144 -0
- package/source/app/components/ModalSelectors.spec.tsx +141 -0
- package/source/app/components/ModalSelectors.tsx +135 -0
- package/source/app/helpers.spec.ts +97 -0
- package/source/app/helpers.ts +63 -0
- package/source/app/index.ts +4 -0
- package/source/app/types.ts +39 -0
- package/source/app/utils/appUtils.ts +294 -0
- package/source/app/utils/conversationState.ts +310 -0
- package/source/app.spec.tsx +244 -0
- package/source/cli.spec.ts +73 -0
- package/source/cli.tsx +51 -0
- package/source/client-factory.spec.ts +48 -0
- package/source/client-factory.ts +178 -0
- package/source/command-parser.spec.ts +127 -0
- package/source/command-parser.ts +36 -0
- package/source/commands/checkpoint.spec.tsx +277 -0
- package/source/commands/checkpoint.tsx +366 -0
- package/source/commands/clear.tsx +22 -0
- package/source/commands/custom-commands.tsx +121 -0
- package/source/commands/exit.ts +21 -0
- package/source/commands/export.spec.tsx +131 -0
- package/source/commands/export.tsx +79 -0
- package/source/commands/help.tsx +120 -0
- package/source/commands/index.ts +17 -0
- package/source/commands/init.tsx +339 -0
- package/source/commands/lsp-command.spec.tsx +281 -0
- package/source/commands/lsp.tsx +120 -0
- package/source/commands/mcp-command.spec.tsx +313 -0
- package/source/commands/mcp.tsx +162 -0
- package/source/commands/model-database.spec.tsx +758 -0
- package/source/commands/model-database.tsx +418 -0
- package/source/commands/model.ts +12 -0
- package/source/commands/provider.ts +12 -0
- package/source/commands/setup-config.tsx +16 -0
- package/source/commands/simple-commands.spec.tsx +175 -0
- package/source/commands/status.ts +12 -0
- package/source/commands/theme.ts +12 -0
- package/source/commands/update.spec.tsx +261 -0
- package/source/commands/update.tsx +201 -0
- package/source/commands/usage.spec.tsx +495 -0
- package/source/commands/usage.tsx +100 -0
- package/source/commands.spec.ts +436 -0
- package/source/commands.ts +83 -0
- package/source/components/assistant-message.spec.tsx +796 -0
- package/source/components/assistant-message.tsx +34 -0
- package/source/components/bash-execution-indicator.tsx +21 -0
- package/source/components/cancelling-indicator.tsx +16 -0
- package/source/components/chat-queue.spec.tsx +83 -0
- package/source/components/chat-queue.tsx +36 -0
- package/source/components/checkpoint-display.spec.tsx +219 -0
- package/source/components/checkpoint-display.tsx +126 -0
- package/source/components/checkpoint-selector.spec.tsx +173 -0
- package/source/components/checkpoint-selector.tsx +173 -0
- package/source/components/development-mode-indicator.spec.tsx +268 -0
- package/source/components/development-mode-indicator.tsx +38 -0
- package/source/components/message-box.spec.tsx +427 -0
- package/source/components/message-box.tsx +87 -0
- package/source/components/model-selector.tsx +132 -0
- package/source/components/provider-selector.tsx +75 -0
- package/source/components/random-spinner.tsx +19 -0
- package/source/components/security-disclaimer.tsx +73 -0
- package/source/components/status-connection-display.spec.tsx +133 -0
- package/source/components/status.tsx +267 -0
- package/source/components/theme-selector.tsx +126 -0
- package/source/components/tool-confirmation.tsx +190 -0
- package/source/components/tool-execution-indicator.tsx +33 -0
- package/source/components/tool-message.tsx +85 -0
- package/source/components/ui/titled-box.spec.tsx +207 -0
- package/source/components/ui/titled-box.tsx +57 -0
- package/source/components/usage/progress-bar.spec.tsx +398 -0
- package/source/components/usage/progress-bar.tsx +30 -0
- package/source/components/usage/usage-display.spec.tsx +780 -0
- package/source/components/usage/usage-display.tsx +291 -0
- package/source/components/user-input.spec.tsx +327 -0
- package/source/components/user-input.tsx +533 -0
- package/source/components/user-message.spec.tsx +230 -0
- package/source/components/user-message.tsx +84 -0
- package/source/components/welcome-message.tsx +76 -0
- package/source/config/env-substitution.ts +65 -0
- package/source/config/index.spec.ts +171 -0
- package/source/config/index.ts +154 -0
- package/source/config/paths.spec.ts +241 -0
- package/source/config/paths.ts +55 -0
- package/source/config/preferences.ts +51 -0
- package/source/config/themes.ts +315 -0
- package/source/constants.ts +130 -0
- package/source/context/mode-context.spec.ts +79 -0
- package/source/context/mode-context.ts +24 -0
- package/source/custom-commands/executor.spec.ts +142 -0
- package/source/custom-commands/executor.ts +64 -0
- package/source/custom-commands/loader.spec.ts +314 -0
- package/source/custom-commands/loader.ts +153 -0
- package/source/custom-commands/parser.ts +196 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.spec.ts +39 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.tsx +511 -0
- package/source/hooks/chat-handler/conversation/tool-executor.spec.ts +50 -0
- package/source/hooks/chat-handler/conversation/tool-executor.tsx +109 -0
- package/source/hooks/chat-handler/index.ts +12 -0
- package/source/hooks/chat-handler/state/streaming-state.spec.ts +26 -0
- package/source/hooks/chat-handler/state/streaming-state.ts +19 -0
- package/source/hooks/chat-handler/types.ts +38 -0
- package/source/hooks/chat-handler/useChatHandler.spec.tsx +321 -0
- package/source/hooks/chat-handler/useChatHandler.tsx +194 -0
- package/source/hooks/chat-handler/utils/context-checker.spec.ts +60 -0
- package/source/hooks/chat-handler/utils/context-checker.tsx +73 -0
- package/source/hooks/chat-handler/utils/message-helpers.spec.ts +42 -0
- package/source/hooks/chat-handler/utils/message-helpers.tsx +36 -0
- package/source/hooks/chat-handler/utils/tool-filters.spec.ts +109 -0
- package/source/hooks/chat-handler/utils/tool-filters.ts +64 -0
- package/source/hooks/useAppHandlers.tsx +291 -0
- package/source/hooks/useAppInitialization.tsx +422 -0
- package/source/hooks/useAppState.tsx +311 -0
- package/source/hooks/useDirectoryTrust.tsx +98 -0
- package/source/hooks/useInputState.ts +414 -0
- package/source/hooks/useModeHandlers.tsx +302 -0
- package/source/hooks/useNonInteractiveMode.ts +140 -0
- package/source/hooks/useTerminalWidth.tsx +81 -0
- package/source/hooks/useTheme.ts +18 -0
- package/source/hooks/useToolHandler.tsx +349 -0
- package/source/hooks/useUIState.ts +61 -0
- package/source/init/agents-template-generator.ts +421 -0
- package/source/init/existing-rules-extractor.ts +319 -0
- package/source/init/file-scanner.spec.ts +227 -0
- package/source/init/file-scanner.ts +238 -0
- package/source/init/framework-detector.ts +382 -0
- package/source/init/language-detector.ts +269 -0
- package/source/init/project-analyzer.spec.ts +231 -0
- package/source/init/project-analyzer.ts +458 -0
- package/source/lsp/index.ts +31 -0
- package/source/lsp/lsp-client.spec.ts +508 -0
- package/source/lsp/lsp-client.ts +487 -0
- package/source/lsp/lsp-manager.spec.ts +477 -0
- package/source/lsp/lsp-manager.ts +419 -0
- package/source/lsp/protocol.spec.ts +502 -0
- package/source/lsp/protocol.ts +360 -0
- package/source/lsp/server-discovery.spec.ts +654 -0
- package/source/lsp/server-discovery.ts +515 -0
- package/source/markdown-parser/html-entities.spec.ts +88 -0
- package/source/markdown-parser/html-entities.ts +45 -0
- package/source/markdown-parser/index.spec.ts +281 -0
- package/source/markdown-parser/index.ts +126 -0
- package/source/markdown-parser/table-parser.spec.ts +133 -0
- package/source/markdown-parser/table-parser.ts +114 -0
- package/source/markdown-parser/utils.spec.ts +70 -0
- package/source/markdown-parser/utils.ts +13 -0
- package/source/mcp/mcp-client.spec.ts +81 -0
- package/source/mcp/mcp-client.ts +625 -0
- package/source/mcp/transport-factory.spec.ts +406 -0
- package/source/mcp/transport-factory.ts +312 -0
- package/source/message-handler.ts +67 -0
- package/source/model-database/database-engine.spec.ts +494 -0
- package/source/model-database/database-engine.ts +50 -0
- package/source/model-database/model-database.spec.ts +363 -0
- package/source/model-database/model-database.ts +91 -0
- package/source/model-database/model-engine.spec.ts +447 -0
- package/source/model-database/model-engine.ts +65 -0
- package/source/model-database/model-fetcher.spec.ts +583 -0
- package/source/model-database/model-fetcher.ts +330 -0
- package/source/models/index.ts +1 -0
- package/source/models/models-cache.spec.ts +214 -0
- package/source/models/models-cache.ts +78 -0
- package/source/models/models-dev-client.spec.ts +379 -0
- package/source/models/models-dev-client.ts +329 -0
- package/source/models/models-types.ts +68 -0
- package/source/prompt-history.ts +155 -0
- package/source/security/command-injection.spec.ts +240 -0
- package/source/services/checkpoint-manager.spec.ts +523 -0
- package/source/services/checkpoint-manager.ts +466 -0
- package/source/services/file-snapshot.spec.ts +569 -0
- package/source/services/file-snapshot.ts +220 -0
- package/source/test-utils/render-with-theme.tsx +48 -0
- package/source/tokenization/index.ts +1 -0
- package/source/tokenization/tokenizer-factory.spec.ts +170 -0
- package/source/tokenization/tokenizer-factory.ts +125 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.spec.ts +200 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.ts +43 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.spec.ts +236 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.ts +26 -0
- package/source/tokenization/tokenizers/llama-tokenizer.spec.ts +224 -0
- package/source/tokenization/tokenizers/llama-tokenizer.ts +41 -0
- package/source/tokenization/tokenizers/openai-tokenizer.spec.ts +184 -0
- package/source/tokenization/tokenizers/openai-tokenizer.ts +57 -0
- package/source/tool-calling/index.ts +5 -0
- package/source/tool-calling/json-parser.spec.ts +639 -0
- package/source/tool-calling/json-parser.ts +247 -0
- package/source/tool-calling/tool-parser.spec.ts +395 -0
- package/source/tool-calling/tool-parser.ts +120 -0
- package/source/tool-calling/xml-parser.spec.ts +662 -0
- package/source/tool-calling/xml-parser.ts +289 -0
- package/source/tools/execute-bash.spec.tsx +353 -0
- package/source/tools/execute-bash.tsx +219 -0
- package/source/tools/execute-function.spec.ts +130 -0
- package/source/tools/fetch-url.spec.tsx +342 -0
- package/source/tools/fetch-url.tsx +172 -0
- package/source/tools/find-files.spec.tsx +924 -0
- package/source/tools/find-files.tsx +293 -0
- package/source/tools/index.ts +102 -0
- package/source/tools/lsp-get-diagnostics.tsx +192 -0
- package/source/tools/needs-approval.spec.ts +282 -0
- package/source/tools/read-file.spec.tsx +801 -0
- package/source/tools/read-file.tsx +387 -0
- package/source/tools/search-file-contents.spec.tsx +1273 -0
- package/source/tools/search-file-contents.tsx +293 -0
- package/source/tools/string-replace.spec.tsx +730 -0
- package/source/tools/string-replace.tsx +548 -0
- package/source/tools/tool-manager.ts +210 -0
- package/source/tools/tool-registry.spec.ts +415 -0
- package/source/tools/tool-registry.ts +228 -0
- package/source/tools/web-search.tsx +223 -0
- package/source/tools/write-file.spec.tsx +559 -0
- package/source/tools/write-file.tsx +228 -0
- package/source/types/app.ts +37 -0
- package/source/types/checkpoint.ts +48 -0
- package/source/types/commands.ts +46 -0
- package/source/types/components.ts +27 -0
- package/source/types/config.ts +103 -0
- package/source/types/core-connection-status.spec.ts +67 -0
- package/source/types/core.ts +181 -0
- package/source/types/hooks.ts +50 -0
- package/source/types/index.ts +12 -0
- package/source/types/markdown-parser.ts +11 -0
- package/source/types/mcp.ts +52 -0
- package/source/types/system.ts +16 -0
- package/source/types/tokenization.ts +41 -0
- package/source/types/ui.ts +40 -0
- package/source/types/usage.ts +58 -0
- package/source/types/utils.ts +16 -0
- package/source/usage/calculator.spec.ts +385 -0
- package/source/usage/calculator.ts +104 -0
- package/source/usage/storage.spec.ts +703 -0
- package/source/usage/storage.ts +238 -0
- package/source/usage/tracker.spec.ts +456 -0
- package/source/usage/tracker.ts +102 -0
- package/source/utils/atomic-deletion.spec.ts +194 -0
- package/source/utils/atomic-deletion.ts +127 -0
- package/source/utils/bounded-map.spec.ts +300 -0
- package/source/utils/bounded-map.ts +193 -0
- package/source/utils/checkpoint-utils.spec.ts +222 -0
- package/source/utils/checkpoint-utils.ts +92 -0
- package/source/utils/error-formatter.spec.ts +169 -0
- package/source/utils/error-formatter.ts +194 -0
- package/source/utils/file-autocomplete.spec.ts +173 -0
- package/source/utils/file-autocomplete.ts +196 -0
- package/source/utils/file-cache.spec.ts +309 -0
- package/source/utils/file-cache.ts +195 -0
- package/source/utils/file-content-loader.spec.ts +180 -0
- package/source/utils/file-content-loader.ts +179 -0
- package/source/utils/file-mention-handler.spec.ts +261 -0
- package/source/utils/file-mention-handler.ts +84 -0
- package/source/utils/file-mention-parser.spec.ts +182 -0
- package/source/utils/file-mention-parser.ts +170 -0
- package/source/utils/fuzzy-matching.spec.ts +149 -0
- package/source/utils/fuzzy-matching.ts +146 -0
- package/source/utils/indentation-normalizer.spec.ts +216 -0
- package/source/utils/indentation-normalizer.ts +76 -0
- package/source/utils/installation-detector.spec.ts +178 -0
- package/source/utils/installation-detector.ts +153 -0
- package/source/utils/logging/config.spec.ts +311 -0
- package/source/utils/logging/config.ts +210 -0
- package/source/utils/logging/console-facade.spec.ts +184 -0
- package/source/utils/logging/console-facade.ts +384 -0
- package/source/utils/logging/correlation.spec.ts +679 -0
- package/source/utils/logging/correlation.ts +474 -0
- package/source/utils/logging/formatters.spec.ts +464 -0
- package/source/utils/logging/formatters.ts +207 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.spec.ts +93 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.ts +79 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.ts +43 -0
- package/source/utils/logging/health-monitor/checks/logging-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/logging-check.ts +58 -0
- package/source/utils/logging/health-monitor/checks/memory-check.spec.ts +100 -0
- package/source/utils/logging/health-monitor/checks/memory-check.ts +78 -0
- package/source/utils/logging/health-monitor/checks/performance-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/performance-check.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.ts +76 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.spec.ts +70 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.ts +138 -0
- package/source/utils/logging/health-monitor/core/health-monitor.spec.ts +58 -0
- package/source/utils/logging/health-monitor/core/health-monitor.ts +344 -0
- package/source/utils/logging/health-monitor/core/scoring.spec.ts +65 -0
- package/source/utils/logging/health-monitor/core/scoring.ts +91 -0
- package/source/utils/logging/health-monitor/index.ts +15 -0
- package/source/utils/logging/health-monitor/instances.ts +48 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.spec.ts +141 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.ts +75 -0
- package/source/utils/logging/health-monitor/types.ts +126 -0
- package/source/utils/logging/index.spec.ts +284 -0
- package/source/utils/logging/index.ts +236 -0
- package/source/utils/logging/integration.spec.ts +441 -0
- package/source/utils/logging/log-method-factory.spec.ts +573 -0
- package/source/utils/logging/log-method-factory.ts +233 -0
- package/source/utils/logging/log-query/aggregation/aggregator.spec.ts +277 -0
- package/source/utils/logging/log-query/aggregation/aggregator.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.spec.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.ts +47 -0
- package/source/utils/logging/log-query/index.ts +23 -0
- package/source/utils/logging/log-query/query/filter-predicates.spec.ts +247 -0
- package/source/utils/logging/log-query/query/filter-predicates.ts +154 -0
- package/source/utils/logging/log-query/query/query-builder.spec.ts +182 -0
- package/source/utils/logging/log-query/query/query-builder.ts +151 -0
- package/source/utils/logging/log-query/query/query-engine.spec.ts +214 -0
- package/source/utils/logging/log-query/query/query-engine.ts +45 -0
- package/source/utils/logging/log-query/storage/circular-buffer.spec.ts +143 -0
- package/source/utils/logging/log-query/storage/circular-buffer.ts +75 -0
- package/source/utils/logging/log-query/storage/index-manager.spec.ts +150 -0
- package/source/utils/logging/log-query/storage/index-manager.ts +71 -0
- package/source/utils/logging/log-query/storage/log-storage.spec.ts +257 -0
- package/source/utils/logging/log-query/storage/log-storage.ts +80 -0
- package/source/utils/logging/log-query/types.ts +163 -0
- package/source/utils/logging/log-query/utils/helpers.spec.ts +263 -0
- package/source/utils/logging/log-query/utils/helpers.ts +72 -0
- package/source/utils/logging/log-query/utils/sorting.spec.ts +182 -0
- package/source/utils/logging/log-query/utils/sorting.ts +61 -0
- package/source/utils/logging/logger-provider.spec.ts +262 -0
- package/source/utils/logging/logger-provider.ts +362 -0
- package/source/utils/logging/performance.spec.ts +209 -0
- package/source/utils/logging/performance.ts +757 -0
- package/source/utils/logging/pino-logger.spec.ts +425 -0
- package/source/utils/logging/pino-logger.ts +514 -0
- package/source/utils/logging/redaction.spec.ts +490 -0
- package/source/utils/logging/redaction.ts +267 -0
- package/source/utils/logging/request-tracker.spec.ts +1198 -0
- package/source/utils/logging/request-tracker.ts +803 -0
- package/source/utils/logging/transports.spec.ts +505 -0
- package/source/utils/logging/transports.ts +305 -0
- package/source/utils/logging/types.ts +216 -0
- package/source/utils/message-builder.spec.ts +179 -0
- package/source/utils/message-builder.ts +101 -0
- package/source/utils/message-queue.tsx +486 -0
- package/source/utils/paste-detection.spec.ts +69 -0
- package/source/utils/paste-detection.ts +124 -0
- package/source/utils/paste-roundtrip.spec.ts +442 -0
- package/source/utils/paste-utils.spec.ts +128 -0
- package/source/utils/paste-utils.ts +52 -0
- package/source/utils/programming-language-helper.spec.ts +74 -0
- package/source/utils/programming-language-helper.ts +32 -0
- package/source/utils/prompt-assembly.spec.ts +221 -0
- package/source/utils/prompt-processor.ts +173 -0
- package/source/utils/tool-args-parser.spec.ts +136 -0
- package/source/utils/tool-args-parser.ts +54 -0
- package/source/utils/tool-cancellation.spec.ts +230 -0
- package/source/utils/tool-cancellation.ts +28 -0
- package/source/utils/tool-result-display.spec.tsx +469 -0
- package/source/utils/tool-result-display.tsx +90 -0
- package/source/utils/update-checker.spec.ts +383 -0
- package/source/utils/update-checker.ts +183 -0
- package/source/wizard/config-wizard.spec.tsx +103 -0
- package/source/wizard/config-wizard.tsx +382 -0
- package/source/wizard/steps/location-step.spec.tsx +186 -0
- package/source/wizard/steps/location-step.tsx +147 -0
- package/source/wizard/steps/mcp-step.spec.tsx +607 -0
- package/source/wizard/steps/mcp-step.tsx +632 -0
- package/source/wizard/steps/provider-step.spec.tsx +342 -0
- package/source/wizard/steps/provider-step.tsx +957 -0
- package/source/wizard/steps/summary-step.spec.tsx +749 -0
- package/source/wizard/steps/summary-step.tsx +228 -0
- package/source/wizard/templates/mcp-templates.spec.ts +613 -0
- package/source/wizard/templates/mcp-templates.ts +570 -0
- package/source/wizard/templates/provider-templates.spec.ts +152 -0
- package/source/wizard/templates/provider-templates.ts +485 -0
- package/source/wizard/utils/fetch-cloud-models.spec.ts +428 -0
- package/source/wizard/utils/fetch-cloud-models.ts +223 -0
- package/source/wizard/utils/fetch-local-models.spec.ts +297 -0
- package/source/wizard/utils/fetch-local-models.ts +192 -0
- package/source/wizard/validation-array.spec.ts +264 -0
- package/source/wizard/validation.spec.ts +373 -0
- package/source/wizard/validation.ts +232 -0
- package/source/app/prompts/main-prompt.md +0 -122
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { platform } from 'node:os';
|
|
4
|
+
import { highlight } from 'cli-highlight';
|
|
5
|
+
import { Box, Text } from 'ink';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import ToolMessage from '@/components/tool-message';
|
|
9
|
+
import { TRUNCATION_OUTPUT_LIMIT } from '@/constants';
|
|
10
|
+
import { ThemeContext } from '@/hooks/useTheme';
|
|
11
|
+
import { jsonSchema, tool } from '@/types/core';
|
|
12
|
+
import type { CoderToolExport } from '@/types/core';
|
|
13
|
+
|
|
14
|
+
const runShellCommand = (
|
|
15
|
+
shell: string,
|
|
16
|
+
shellArgs: string[],
|
|
17
|
+
script: string,
|
|
18
|
+
): Promise<string> => {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const proc = spawn(shell, [...shellArgs, script]);
|
|
21
|
+
let stdout = '';
|
|
22
|
+
let stderr = '';
|
|
23
|
+
|
|
24
|
+
proc.stdout.on('data', (data: Buffer) => {
|
|
25
|
+
stdout += data.toString();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
proc.stderr.on('data', (data: Buffer) => {
|
|
29
|
+
stderr += data.toString();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
proc.on('close', (code: number | null) => {
|
|
33
|
+
let fullOutput = '';
|
|
34
|
+
|
|
35
|
+
// Include exit code information
|
|
36
|
+
const exitCodeInfo = code !== null ? `EXIT_CODE: ${code}\n` : '';
|
|
37
|
+
|
|
38
|
+
if (stderr) {
|
|
39
|
+
fullOutput = `${exitCodeInfo}STDERR:
|
|
40
|
+
${stderr}
|
|
41
|
+
STDOUT:
|
|
42
|
+
${stdout}`;
|
|
43
|
+
} else {
|
|
44
|
+
fullOutput = `${exitCodeInfo}${stdout}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Limit the context for LLM to first TRUNCATION_OUTPUT_LIMIT characters to prevent overwhelming the model
|
|
48
|
+
const llmContext =
|
|
49
|
+
fullOutput.length > TRUNCATION_OUTPUT_LIMIT
|
|
50
|
+
? fullOutput.substring(0, TRUNCATION_OUTPUT_LIMIT) +
|
|
51
|
+
'\n... [Output truncated. Use more specific commands to see full output]'
|
|
52
|
+
: fullOutput;
|
|
53
|
+
|
|
54
|
+
// Return ONLY the llmContext to avoid sending massive outputs to the model
|
|
55
|
+
// The formatter will need to be updated to handle plain strings
|
|
56
|
+
resolve(llmContext);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
proc.on('error', error => {
|
|
60
|
+
reject(error);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const executeExecuteBash = async (args: { command: string }): Promise<string> => {
|
|
66
|
+
try {
|
|
67
|
+
return await runShellCommand('sh', ['-c'], args.command);
|
|
68
|
+
} catch (error: any) {
|
|
69
|
+
if (error.code === 'ENOENT' && platform() === 'win32') {
|
|
70
|
+
// Try to find Git Bash in common locations
|
|
71
|
+
const gitBashPaths = [
|
|
72
|
+
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
73
|
+
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
74
|
+
`C:\\Users\\${process.env['USERNAME'] || 'User'
|
|
75
|
+
}\\AppData\\Local\\Programs\\Git\\bin\\bash.exe`,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const bashPath of gitBashPaths) {
|
|
79
|
+
if (existsSync(bashPath)) {
|
|
80
|
+
try {
|
|
81
|
+
return await runShellCommand(bashPath, ['-c'], args.command);
|
|
82
|
+
} catch {
|
|
83
|
+
// Continue to next fallback
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Fallback to PowerShell
|
|
89
|
+
try {
|
|
90
|
+
return await runShellCommand('powershell', ['-Command'], args.command);
|
|
91
|
+
} catch {
|
|
92
|
+
// If PowerShell also fails, throw the original error
|
|
93
|
+
throw new Error(`Error executing command: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new Error(`Error executing command: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const executeBashCoreTool = tool({
|
|
102
|
+
description:
|
|
103
|
+
'Execute a bash command and return the output (use for running commands)',
|
|
104
|
+
inputSchema: jsonSchema<{ command: string }>({
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
command: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
description: 'The bash command to execute.',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
required: ['command'],
|
|
113
|
+
}),
|
|
114
|
+
// High risk: bash commands always require approval in all modes
|
|
115
|
+
needsApproval: true,
|
|
116
|
+
execute: async (args, _options) => {
|
|
117
|
+
return await executeExecuteBash(args);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Create a component that will re-render when theme changes
|
|
122
|
+
const ExecuteBashFormatter = React.memo(
|
|
123
|
+
({ args, result }: { args: { command: string }; result?: string }) => {
|
|
124
|
+
const themeContext = React.useContext(ThemeContext);
|
|
125
|
+
if (!themeContext) {
|
|
126
|
+
throw new Error('ThemeContext is required');
|
|
127
|
+
}
|
|
128
|
+
const { colors } = themeContext;
|
|
129
|
+
const command = args.command || 'unknown';
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
highlight(command, {
|
|
133
|
+
language: 'bash',
|
|
134
|
+
theme: 'default',
|
|
135
|
+
});
|
|
136
|
+
} catch {
|
|
137
|
+
// Syntax highlighting failed, will use plain command
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Result is now a plain string (truncated output)
|
|
141
|
+
let outputSize = 0;
|
|
142
|
+
let estimatedTokens = 0;
|
|
143
|
+
if (result) {
|
|
144
|
+
outputSize = result.length;
|
|
145
|
+
estimatedTokens = Math.ceil(outputSize / 4); // ~4 characters per token
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const messageContent = (
|
|
149
|
+
<Box flexDirection="column">
|
|
150
|
+
<Text color={colors.tool}>☕︎ execute_bash</Text>
|
|
151
|
+
|
|
152
|
+
<Box>
|
|
153
|
+
<Text color={colors.secondary}>Command: </Text>
|
|
154
|
+
<Text color={colors.primary}>{command}</Text>
|
|
155
|
+
</Box>
|
|
156
|
+
|
|
157
|
+
{result && (
|
|
158
|
+
<Box>
|
|
159
|
+
<Text color={colors.secondary}>Output: </Text>
|
|
160
|
+
<Text color={colors.white}>
|
|
161
|
+
{outputSize} characters (~{estimatedTokens} tokens sent to LLM)
|
|
162
|
+
</Text>
|
|
163
|
+
</Box>
|
|
164
|
+
)}
|
|
165
|
+
</Box>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return <ToolMessage message={messageContent} hideBox={true} />;
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const executeBashFormatter = (
|
|
173
|
+
args: { command: string },
|
|
174
|
+
result?: string,
|
|
175
|
+
): React.ReactElement => {
|
|
176
|
+
return <ExecuteBashFormatter args={args} result={result} />;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const executeBashValidator = (args: {
|
|
180
|
+
command: string;
|
|
181
|
+
}): Promise<{ valid: true } | { valid: false; error: string }> => {
|
|
182
|
+
const command = args.command?.trim();
|
|
183
|
+
|
|
184
|
+
// Check if command is empty
|
|
185
|
+
if (!command) {
|
|
186
|
+
return Promise.resolve({
|
|
187
|
+
valid: false,
|
|
188
|
+
error: '☕︎ Command cannot be empty',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check for extremely dangerous commands
|
|
193
|
+
const dangerousPatterns = [
|
|
194
|
+
/rm\s+-rf\s+\/(?!\w)/i, // rm -rf / (but allow /path)
|
|
195
|
+
/mkfs/i, // Format filesystem
|
|
196
|
+
/dd\s+if=/i, // Direct disk write
|
|
197
|
+
/:(){:|:&};:/i, // Fork bomb
|
|
198
|
+
/>\s*\/dev\/sd[a-z]/i, // Writing to raw disk devices
|
|
199
|
+
/chmod\s+-R\s+000/i, // Remove all permissions recursively
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
for (const pattern of dangerousPatterns) {
|
|
203
|
+
if (pattern.test(command)) {
|
|
204
|
+
return Promise.resolve({
|
|
205
|
+
valid: false,
|
|
206
|
+
error: `☕︎ Command contains potentially destructive operation: "${command}". This command is blocked for safety.`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return Promise.resolve({ valid: true });
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const executeBashTool: CoderToolExport = {
|
|
215
|
+
name: 'execute_bash' as const,
|
|
216
|
+
tool: executeBashCoreTool,
|
|
217
|
+
formatter: executeBashFormatter,
|
|
218
|
+
validator: executeBashValidator,
|
|
219
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {tmpdir} from 'os';
|
|
2
|
+
import {resolve} from 'path';
|
|
3
|
+
import test from 'ava';
|
|
4
|
+
import {unlink, writeFile} from 'fs/promises';
|
|
5
|
+
import {executeBashTool} from './execute-bash.js';
|
|
6
|
+
import {readFileTool} from './read-file.js';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Tests for AI SDK v6 Native Tools
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// These tests validate that the native AI SDK tools work correctly with their
|
|
12
|
+
// execute functions. Since tools now only export the native AI SDK tool,
|
|
13
|
+
// there's no separate handler to compare against.
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Bash Execute Function
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
test('execute_bash tool works correctly', async t => {
|
|
20
|
+
const result = (await executeBashTool.tool.execute!(
|
|
21
|
+
{command: 'echo "test"'},
|
|
22
|
+
{
|
|
23
|
+
toolCallId: 'test-1',
|
|
24
|
+
messages: [],
|
|
25
|
+
},
|
|
26
|
+
)) as string;
|
|
27
|
+
|
|
28
|
+
t.truthy(result);
|
|
29
|
+
t.regex(result, /test/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('execute_bash includes exit code', async t => {
|
|
33
|
+
const result = (await executeBashTool.tool.execute!(
|
|
34
|
+
{command: 'echo "success"'},
|
|
35
|
+
{
|
|
36
|
+
toolCallId: 'test-2',
|
|
37
|
+
messages: [],
|
|
38
|
+
},
|
|
39
|
+
)) as string;
|
|
40
|
+
|
|
41
|
+
t.truthy(result);
|
|
42
|
+
t.regex(result, /EXIT_CODE: 0/);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('execute_bash captures stderr', async t => {
|
|
46
|
+
const result = (await executeBashTool.tool.execute!(
|
|
47
|
+
{command: 'echo "error" >&2'},
|
|
48
|
+
{
|
|
49
|
+
toolCallId: 'test-3',
|
|
50
|
+
messages: [],
|
|
51
|
+
},
|
|
52
|
+
)) as string;
|
|
53
|
+
|
|
54
|
+
t.truthy(result);
|
|
55
|
+
t.regex(result, /STDERR:/);
|
|
56
|
+
t.regex(result, /error/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('execute_bash tool name constant is correct', t => {
|
|
60
|
+
t.is(executeBashTool.name, 'execute_bash');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Read File Execute Function
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
test('read_file reads existing file', async t => {
|
|
68
|
+
const testFile = resolve(tmpdir(), `coder-test-${Date.now()}.txt`);
|
|
69
|
+
await writeFile(testFile, 'test content', 'utf-8');
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const result = (await readFileTool.tool.execute!(
|
|
73
|
+
{path: testFile},
|
|
74
|
+
{
|
|
75
|
+
toolCallId: 'test-4',
|
|
76
|
+
messages: [],
|
|
77
|
+
},
|
|
78
|
+
)) as string;
|
|
79
|
+
|
|
80
|
+
t.truthy(result);
|
|
81
|
+
t.regex(result, /test content/);
|
|
82
|
+
} finally {
|
|
83
|
+
await unlink(testFile).catch(() => {});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('read_file handles non-existent file', async t => {
|
|
88
|
+
const nonExistentFile = resolve(tmpdir(), 'non-existent-file.txt');
|
|
89
|
+
|
|
90
|
+
await t.throwsAsync(
|
|
91
|
+
async () => {
|
|
92
|
+
await readFileTool.tool.execute!(
|
|
93
|
+
{path: nonExistentFile},
|
|
94
|
+
{
|
|
95
|
+
toolCallId: 'test-5',
|
|
96
|
+
messages: [],
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
{message: /does not exist|no such file or directory|ENOENT/i},
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('read_file reads with line ranges', async t => {
|
|
105
|
+
const testFile = resolve(tmpdir(), `coder-test-${Date.now()}.txt`);
|
|
106
|
+
await writeFile(testFile, 'line1\nline2\nline3\nline4\nline5', 'utf-8');
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const result = (await readFileTool.tool.execute!(
|
|
110
|
+
{path: testFile, start_line: 2, end_line: 4},
|
|
111
|
+
{
|
|
112
|
+
toolCallId: 'test-6',
|
|
113
|
+
messages: [],
|
|
114
|
+
},
|
|
115
|
+
)) as string;
|
|
116
|
+
|
|
117
|
+
t.truthy(result);
|
|
118
|
+
t.regex(result, /line2/);
|
|
119
|
+
t.regex(result, /line3/);
|
|
120
|
+
t.regex(result, /line4/);
|
|
121
|
+
t.notRegex(result, /line1/);
|
|
122
|
+
t.notRegex(result, /line5/);
|
|
123
|
+
} finally {
|
|
124
|
+
await unlink(testFile).catch(() => {});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('read_file tool name constant is correct', t => {
|
|
129
|
+
t.is(readFileTool.name, 'read_file');
|
|
130
|
+
});
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import {render} from 'ink-testing-library';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {themes} from '../config/themes';
|
|
5
|
+
import {ThemeContext} from '../hooks/useTheme';
|
|
6
|
+
|
|
7
|
+
console.log(`\nfetch-url.spec.tsx – ${React.version}`);
|
|
8
|
+
|
|
9
|
+
// Polyfill File for undici in Node.js test environment
|
|
10
|
+
if (typeof File === 'undefined') {
|
|
11
|
+
(global as any).File = class File {
|
|
12
|
+
constructor(
|
|
13
|
+
public parts: any[],
|
|
14
|
+
public name: string,
|
|
15
|
+
public options?: any,
|
|
16
|
+
) {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Dynamically import to avoid loading undici in test environment
|
|
21
|
+
let fetchUrlTool: any;
|
|
22
|
+
|
|
23
|
+
test.before(async () => {
|
|
24
|
+
// Only import when we need it, and handle the case where undici might not work
|
|
25
|
+
try {
|
|
26
|
+
const module = await import('./fetch-url.js');
|
|
27
|
+
fetchUrlTool = module.fetchUrlTool;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// If undici fails to load (e.g., in CI), we'll skip handler tests
|
|
30
|
+
console.warn('Failed to load fetch-url module:', error);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Mock ThemeProvider for testing
|
|
35
|
+
const MockThemeProvider = ({children}: {children: React.ReactNode}) => {
|
|
36
|
+
const mockTheme = {
|
|
37
|
+
currentTheme: 'default' as const,
|
|
38
|
+
colors: themes['tokyo-night'].colors, // Use tokyo-night theme colors
|
|
39
|
+
setCurrentTheme: () => {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ThemeContext.Provider value={mockTheme}>{children}</ThemeContext.Provider>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Note: These tests validate the tool configuration and validator logic.
|
|
48
|
+
// Handler tests that make actual network requests are skipped to avoid flakiness.
|
|
49
|
+
// For handler testing, consider using integration tests with known stable URLs
|
|
50
|
+
// or mocking the convertToMarkdown function at the module level.
|
|
51
|
+
|
|
52
|
+
test('handler validates URL format', async t => {
|
|
53
|
+
if (!fetchUrlTool) {
|
|
54
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await t.throwsAsync(
|
|
59
|
+
async () => {
|
|
60
|
+
await fetchUrlTool.tool.execute!(
|
|
61
|
+
{url: 'not-a-valid-url'},
|
|
62
|
+
{toolCallId: 'test', messages: []},
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
{message: /Invalid URL/},
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('validator accepts valid HTTP URLs', async t => {
|
|
70
|
+
if (!fetchUrlTool) {
|
|
71
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const result = await fetchUrlTool.validator!({url: 'https://example.com'});
|
|
75
|
+
|
|
76
|
+
t.true(result.valid);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('validator accepts valid HTTPS URLs', async t => {
|
|
80
|
+
if (!fetchUrlTool) {
|
|
81
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const result = await fetchUrlTool.validator!({url: 'http://example.com'});
|
|
85
|
+
|
|
86
|
+
t.true(result.valid);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('validator rejects invalid URL formats', async t => {
|
|
90
|
+
if (!fetchUrlTool) {
|
|
91
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const result = await fetchUrlTool.validator!({url: 'not a url'});
|
|
95
|
+
|
|
96
|
+
t.false(result.valid);
|
|
97
|
+
if (!result.valid) {
|
|
98
|
+
t.true(result.error.includes('Invalid URL format'));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('validator rejects non-HTTP/HTTPS protocols', async t => {
|
|
103
|
+
if (!fetchUrlTool) {
|
|
104
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const result = await fetchUrlTool.validator!({url: 'ftp://example.com'});
|
|
108
|
+
|
|
109
|
+
t.false(result.valid);
|
|
110
|
+
if (!result.valid) {
|
|
111
|
+
t.true(result.error.includes('Invalid URL protocol'));
|
|
112
|
+
t.true(result.error.includes('ftp:'));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('validator rejects localhost URLs', async t => {
|
|
117
|
+
if (!fetchUrlTool) {
|
|
118
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const result = await fetchUrlTool.validator!({url: 'http://localhost:3000'});
|
|
122
|
+
|
|
123
|
+
t.false(result.valid);
|
|
124
|
+
if (!result.valid) {
|
|
125
|
+
t.true(result.error.includes('internal/private network'));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('validator rejects 127.0.0.1 URLs', async t => {
|
|
130
|
+
if (!fetchUrlTool) {
|
|
131
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const result = await fetchUrlTool.validator!({
|
|
135
|
+
url: 'http://127.0.0.1:8080',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
t.false(result.valid);
|
|
139
|
+
if (!result.valid) {
|
|
140
|
+
t.true(result.error.includes('internal/private network'));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('validator rejects 192.168.x.x URLs', async t => {
|
|
145
|
+
if (!fetchUrlTool) {
|
|
146
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const result = await fetchUrlTool.validator!({
|
|
150
|
+
url: 'http://192.168.1.1',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
t.false(result.valid);
|
|
154
|
+
if (!result.valid) {
|
|
155
|
+
t.true(result.error.includes('internal/private network'));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('validator rejects 10.x.x.x URLs', async t => {
|
|
160
|
+
if (!fetchUrlTool) {
|
|
161
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const result = await fetchUrlTool.validator!({url: 'http://10.0.0.1'});
|
|
165
|
+
|
|
166
|
+
t.false(result.valid);
|
|
167
|
+
if (!result.valid) {
|
|
168
|
+
t.true(result.error.includes('internal/private network'));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('validator rejects 172.16-31.x.x URLs', async t => {
|
|
173
|
+
if (!fetchUrlTool) {
|
|
174
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const result = await fetchUrlTool.validator!({url: 'http://172.16.0.1'});
|
|
178
|
+
|
|
179
|
+
t.false(result.valid);
|
|
180
|
+
if (!result.valid) {
|
|
181
|
+
t.true(result.error.includes('internal/private network'));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('validator accepts external IP addresses', async t => {
|
|
186
|
+
if (!fetchUrlTool) {
|
|
187
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const result = await fetchUrlTool.validator!({url: 'http://8.8.8.8'});
|
|
191
|
+
|
|
192
|
+
t.true(result.valid);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('tool has correct name', t => {
|
|
196
|
+
if (!fetchUrlTool) {
|
|
197
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
t.is(fetchUrlTool.name, 'fetch_url');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('tool does not require confirmation', t => {
|
|
204
|
+
if (!fetchUrlTool) {
|
|
205
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
t.false(fetchUrlTool.tool.needsApproval);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('formatter is a function', t => {
|
|
212
|
+
if (!fetchUrlTool) {
|
|
213
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
t.is(typeof fetchUrlTool.formatter, 'function');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('formatter returns a Promise', async t => {
|
|
220
|
+
if (!fetchUrlTool) {
|
|
221
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const result = fetchUrlTool.formatter!({url: 'https://example.com'});
|
|
225
|
+
|
|
226
|
+
t.true(result instanceof Promise);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ============================================================================
|
|
230
|
+
// Component Rendering Tests
|
|
231
|
+
// ============================================================================
|
|
232
|
+
|
|
233
|
+
test('formatter renders component with URL', async t => {
|
|
234
|
+
if (!fetchUrlTool) {
|
|
235
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const component = await fetchUrlTool.formatter!({
|
|
239
|
+
url: 'https://example.com',
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const {lastFrame} = render(
|
|
243
|
+
<MockThemeProvider>{component}</MockThemeProvider>,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const output = lastFrame();
|
|
247
|
+
t.truthy(output);
|
|
248
|
+
t.regex(output!, /fetch_url/);
|
|
249
|
+
t.regex(output!, /https:\/\/example\.com/);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('formatter renders component with result stats', async t => {
|
|
253
|
+
if (!fetchUrlTool) {
|
|
254
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const mockResult = 'Test content with some markdown';
|
|
258
|
+
|
|
259
|
+
const component = await fetchUrlTool.formatter!(
|
|
260
|
+
{url: 'https://example.com'},
|
|
261
|
+
mockResult,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const {lastFrame} = render(
|
|
265
|
+
<MockThemeProvider>{component}</MockThemeProvider>,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const output = lastFrame();
|
|
269
|
+
t.truthy(output);
|
|
270
|
+
t.regex(output!, /fetch_url/);
|
|
271
|
+
t.regex(output!, /https:\/\/example\.com/);
|
|
272
|
+
t.regex(output!, /Content:/);
|
|
273
|
+
t.regex(output!, /characters/);
|
|
274
|
+
t.regex(output!, /tokens/);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('formatter shows truncation warning when content is truncated', async t => {
|
|
278
|
+
if (!fetchUrlTool) {
|
|
279
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const truncatedResult =
|
|
283
|
+
'x'.repeat(100000) +
|
|
284
|
+
'\n\n[Content truncated - original size was 150000 characters]';
|
|
285
|
+
|
|
286
|
+
const component = await fetchUrlTool.formatter!(
|
|
287
|
+
{url: 'https://large-content.com'},
|
|
288
|
+
truncatedResult,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const {lastFrame} = render(
|
|
292
|
+
<MockThemeProvider>{component}</MockThemeProvider>,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const output = lastFrame();
|
|
296
|
+
t.truthy(output);
|
|
297
|
+
t.regex(output!, /Content was truncated to 100KB/);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('formatter renders without result (before execution)', async t => {
|
|
301
|
+
if (!fetchUrlTool) {
|
|
302
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const component = await fetchUrlTool.formatter!({
|
|
306
|
+
url: 'https://example.com',
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const {lastFrame} = render(
|
|
310
|
+
<MockThemeProvider>{component}</MockThemeProvider>,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const output = lastFrame();
|
|
314
|
+
t.truthy(output);
|
|
315
|
+
t.regex(output!, /fetch_url/);
|
|
316
|
+
t.regex(output!, /https:\/\/example\.com/);
|
|
317
|
+
// Should not show content stats before execution
|
|
318
|
+
t.notRegex(output!, /Content:/);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('formatter calculates token estimate correctly', async t => {
|
|
322
|
+
if (!fetchUrlTool) {
|
|
323
|
+
t.pass('Skipping test - fetch-url module not available');
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// 100 characters should estimate ~25 tokens (divide by 4)
|
|
327
|
+
const mockResult = 'a'.repeat(100);
|
|
328
|
+
|
|
329
|
+
const component = await fetchUrlTool.formatter!(
|
|
330
|
+
{url: 'https://example.com'},
|
|
331
|
+
mockResult,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const {lastFrame} = render(
|
|
335
|
+
<MockThemeProvider>{component}</MockThemeProvider>,
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
const output = lastFrame();
|
|
339
|
+
t.truthy(output);
|
|
340
|
+
t.regex(output!, /100.*characters/);
|
|
341
|
+
t.regex(output!, /25.*tokens/);
|
|
342
|
+
});
|