@machina.ai/cell-cli-core 1.0.13-rc3
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/.last_build +0 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/src/__mocks__/fs/promises.d.ts +11 -0
- package/dist/src/__mocks__/fs/promises.js +17 -0
- package/dist/src/__mocks__/fs/promises.js.map +1 -0
- package/dist/src/code_assist/codeAssist.d.ts +9 -0
- package/dist/src/code_assist/codeAssist.js +19 -0
- package/dist/src/code_assist/codeAssist.js.map +1 -0
- package/dist/src/code_assist/converter.d.ts +68 -0
- package/dist/src/code_assist/converter.js +125 -0
- package/dist/src/code_assist/converter.js.map +1 -0
- package/dist/src/code_assist/converter.test.d.ts +6 -0
- package/dist/src/code_assist/converter.test.js +229 -0
- package/dist/src/code_assist/converter.test.js.map +1 -0
- package/dist/src/code_assist/oauth2.d.ts +20 -0
- package/dist/src/code_assist/oauth2.js +329 -0
- package/dist/src/code_assist/oauth2.js.map +1 -0
- package/dist/src/code_assist/oauth2.test.d.ts +6 -0
- package/dist/src/code_assist/oauth2.test.js +244 -0
- package/dist/src/code_assist/oauth2.test.js.map +1 -0
- package/dist/src/code_assist/server.d.ts +37 -0
- package/dist/src/code_assist/server.js +125 -0
- package/dist/src/code_assist/server.js.map +1 -0
- package/dist/src/code_assist/server.test.d.ts +6 -0
- package/dist/src/code_assist/server.test.js +131 -0
- package/dist/src/code_assist/server.test.js.map +1 -0
- package/dist/src/code_assist/setup.d.ts +20 -0
- package/dist/src/code_assist/setup.js +70 -0
- package/dist/src/code_assist/setup.js.map +1 -0
- package/dist/src/code_assist/setup.test.d.ts +6 -0
- package/dist/src/code_assist/setup.test.js +65 -0
- package/dist/src/code_assist/setup.test.js.map +1 -0
- package/dist/src/code_assist/types.d.ts +148 -0
- package/dist/src/code_assist/types.js +46 -0
- package/dist/src/code_assist/types.js.map +1 -0
- package/dist/src/config/config.d.ts +239 -0
- package/dist/src/config/config.js +484 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/config.test.d.ts +6 -0
- package/dist/src/config/config.test.js +304 -0
- package/dist/src/config/config.test.js.map +1 -0
- package/dist/src/config/flashFallback.test.d.ts +6 -0
- package/dist/src/config/flashFallback.test.js +115 -0
- package/dist/src/config/flashFallback.test.js.map +1 -0
- package/dist/src/config/models.d.ts +8 -0
- package/dist/src/config/models.js +9 -0
- package/dist/src/config/models.js.map +1 -0
- package/dist/src/core/client.d.ts +60 -0
- package/dist/src/core/client.js +502 -0
- package/dist/src/core/client.js.map +1 -0
- package/dist/src/core/client.test.d.ts +6 -0
- package/dist/src/core/client.test.js +853 -0
- package/dist/src/core/client.test.js.map +1 -0
- package/dist/src/core/contentGenerator.d.ts +36 -0
- package/dist/src/core/contentGenerator.js +110 -0
- package/dist/src/core/contentGenerator.js.map +1 -0
- package/dist/src/core/contentGenerator.test.d.ts +6 -0
- package/dist/src/core/contentGenerator.test.js +225 -0
- package/dist/src/core/contentGenerator.test.js.map +1 -0
- package/dist/src/core/coreToolScheduler.d.ts +104 -0
- package/dist/src/core/coreToolScheduler.js +410 -0
- package/dist/src/core/coreToolScheduler.js.map +1 -0
- package/dist/src/core/coreToolScheduler.test.d.ts +6 -0
- package/dist/src/core/coreToolScheduler.test.js +402 -0
- package/dist/src/core/coreToolScheduler.test.js.map +1 -0
- package/dist/src/core/geminiChat.d.ts +116 -0
- package/dist/src/core/geminiChat.js +511 -0
- package/dist/src/core/geminiChat.js.map +1 -0
- package/dist/src/core/geminiChat.test.d.ts +6 -0
- package/dist/src/core/geminiChat.test.js +424 -0
- package/dist/src/core/geminiChat.test.js.map +1 -0
- package/dist/src/core/geminiRequest.d.ts +13 -0
- package/dist/src/core/geminiRequest.js +45 -0
- package/dist/src/core/geminiRequest.js.map +1 -0
- package/dist/src/core/geminiRequest.test.d.ts +6 -0
- package/dist/src/core/geminiRequest.test.js +72 -0
- package/dist/src/core/geminiRequest.test.js.map +1 -0
- package/dist/src/core/logger.d.ts +35 -0
- package/dist/src/core/logger.js +235 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/logger.test.d.ts +6 -0
- package/dist/src/core/logger.test.js +387 -0
- package/dist/src/core/logger.test.js.map +1 -0
- package/dist/src/core/modelCheck.d.ts +14 -0
- package/dist/src/core/modelCheck.js +62 -0
- package/dist/src/core/modelCheck.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.d.ts +12 -0
- package/dist/src/core/nonInteractiveToolExecutor.js +97 -0
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.d.ts +6 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js +189 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -0
- package/dist/src/core/prompts.d.ts +12 -0
- package/dist/src/core/prompts.js +335 -0
- package/dist/src/core/prompts.js.map +1 -0
- package/dist/src/core/prompts.test.d.ts +6 -0
- package/dist/src/core/prompts.test.js +97 -0
- package/dist/src/core/prompts.test.js.map +1 -0
- package/dist/src/core/tokenLimits.d.ts +10 -0
- package/dist/src/core/tokenLimits.js +27 -0
- package/dist/src/core/tokenLimits.js.map +1 -0
- package/dist/src/core/turn.d.ts +111 -0
- package/dist/src/core/turn.js +139 -0
- package/dist/src/core/turn.js.map +1 -0
- package/dist/src/core/turn.test.d.ts +6 -0
- package/dist/src/core/turn.test.js +365 -0
- package/dist/src/core/turn.test.js.map +1 -0
- package/dist/src/index.d.ts +57 -0
- package/dist/src/index.js +64 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/index.test.d.ts +6 -0
- package/dist/src/index.test.js +12 -0
- package/dist/src/index.test.js.map +1 -0
- package/dist/src/mcp/oauth-provider.d.ts +142 -0
- package/dist/src/mcp/oauth-provider.js +446 -0
- package/dist/src/mcp/oauth-provider.js.map +1 -0
- package/dist/src/mcp/oauth-provider.test.d.ts +6 -0
- package/dist/src/mcp/oauth-provider.test.js +520 -0
- package/dist/src/mcp/oauth-provider.test.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.d.ts +81 -0
- package/dist/src/mcp/oauth-token-storage.js +149 -0
- package/dist/src/mcp/oauth-token-storage.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/oauth-token-storage.test.js +205 -0
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -0
- package/dist/src/mcp/oauth-utils.d.ts +109 -0
- package/dist/src/mcp/oauth-utils.js +183 -0
- package/dist/src/mcp/oauth-utils.js.map +1 -0
- package/dist/src/mcp/oauth-utils.test.d.ts +6 -0
- package/dist/src/mcp/oauth-utils.test.js +144 -0
- package/dist/src/mcp/oauth-utils.test.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.d.ts +35 -0
- package/dist/src/services/fileDiscoveryService.js +91 -0
- package/dist/src/services/fileDiscoveryService.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.test.d.ts +6 -0
- package/dist/src/services/fileDiscoveryService.test.js +102 -0
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -0
- package/dist/src/services/gitService.d.ts +21 -0
- package/dist/src/services/gitService.js +101 -0
- package/dist/src/services/gitService.js.map +1 -0
- package/dist/src/services/gitService.test.d.ts +6 -0
- package/dist/src/services/gitService.test.js +228 -0
- package/dist/src/services/gitService.test.js.map +1 -0
- package/dist/src/services/ideContext.d.ts +178 -0
- package/dist/src/services/ideContext.js +105 -0
- package/dist/src/services/ideContext.js.map +1 -0
- package/dist/src/services/ideContext.test.d.ts +6 -0
- package/dist/src/services/ideContext.test.js +111 -0
- package/dist/src/services/ideContext.test.js.map +1 -0
- package/dist/src/services/loopDetectionService.d.ts +51 -0
- package/dist/src/services/loopDetectionService.js +232 -0
- package/dist/src/services/loopDetectionService.js.map +1 -0
- package/dist/src/services/loopDetectionService.test.d.ts +6 -0
- package/dist/src/services/loopDetectionService.test.js +339 -0
- package/dist/src/services/loopDetectionService.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +37 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +456 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +49 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +127 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -0
- package/dist/src/telemetry/constants.d.ts +20 -0
- package/dist/src/telemetry/constants.js +21 -0
- package/dist/src/telemetry/constants.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +18 -0
- package/dist/src/telemetry/index.js +20 -0
- package/dist/src/telemetry/index.js.map +1 -0
- package/dist/src/telemetry/integration.test.circular.d.ts +6 -0
- package/dist/src/telemetry/integration.test.circular.js +53 -0
- package/dist/src/telemetry/integration.test.circular.js.map +1 -0
- package/dist/src/telemetry/loggers.d.ts +15 -0
- package/dist/src/telemetry/loggers.js +220 -0
- package/dist/src/telemetry/loggers.js.map +1 -0
- package/dist/src/telemetry/loggers.test.circular.d.ts +6 -0
- package/dist/src/telemetry/loggers.test.circular.js +100 -0
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -0
- package/dist/src/telemetry/loggers.test.d.ts +6 -0
- package/dist/src/telemetry/loggers.test.js +558 -0
- package/dist/src/telemetry/loggers.test.js.map +1 -0
- package/dist/src/telemetry/metrics.d.ts +19 -0
- package/dist/src/telemetry/metrics.js +144 -0
- package/dist/src/telemetry/metrics.js.map +1 -0
- package/dist/src/telemetry/metrics.test.d.ts +6 -0
- package/dist/src/telemetry/metrics.test.js +162 -0
- package/dist/src/telemetry/metrics.test.js.map +1 -0
- package/dist/src/telemetry/sdk.d.ts +9 -0
- package/dist/src/telemetry/sdk.js +116 -0
- package/dist/src/telemetry/sdk.js.map +1 -0
- package/dist/src/telemetry/telemetry.test.d.ts +6 -0
- package/dist/src/telemetry/telemetry.test.js +50 -0
- package/dist/src/telemetry/telemetry.test.js.map +1 -0
- package/dist/src/telemetry/types.d.ts +116 -0
- package/dist/src/telemetry/types.js +218 -0
- package/dist/src/telemetry/types.js.map +1 -0
- package/dist/src/telemetry/uiTelemetry.d.ts +68 -0
- package/dist/src/telemetry/uiTelemetry.js +138 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -0
- package/dist/src/telemetry/uiTelemetry.test.d.ts +6 -0
- package/dist/src/telemetry/uiTelemetry.test.js +504 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -0
- package/dist/src/tools/diffOptions.d.ts +7 -0
- package/dist/src/tools/diffOptions.js +10 -0
- package/dist/src/tools/diffOptions.js.map +1 -0
- package/dist/src/tools/edit.d.ts +79 -0
- package/dist/src/tools/edit.js +362 -0
- package/dist/src/tools/edit.js.map +1 -0
- package/dist/src/tools/edit.test.d.ts +6 -0
- package/dist/src/tools/edit.test.js +512 -0
- package/dist/src/tools/edit.test.js.map +1 -0
- package/dist/src/tools/glob.d.ts +58 -0
- package/dist/src/tools/glob.js +193 -0
- package/dist/src/tools/glob.js.map +1 -0
- package/dist/src/tools/glob.test.d.ts +6 -0
- package/dist/src/tools/glob.test.js +294 -0
- package/dist/src/tools/glob.test.js.map +1 -0
- package/dist/src/tools/grep.d.ts +78 -0
- package/dist/src/tools/grep.js +422 -0
- package/dist/src/tools/grep.js.map +1 -0
- package/dist/src/tools/grep.test.d.ts +6 -0
- package/dist/src/tools/grep.test.js +188 -0
- package/dist/src/tools/grep.test.js.map +1 -0
- package/dist/src/tools/ls.d.ts +86 -0
- package/dist/src/tools/ls.js +223 -0
- package/dist/src/tools/ls.js.map +1 -0
- package/dist/src/tools/mcp-client.d.ts +110 -0
- package/dist/src/tools/mcp-client.js +319 -0
- package/dist/src/tools/mcp-client.js.map +1 -0
- package/dist/src/tools/mcp-client.test.d.ts +6 -0
- package/dist/src/tools/mcp-client.test.js +156 -0
- package/dist/src/tools/mcp-client.test.js.map +1 -0
- package/dist/src/tools/mcp-tool.d.ts +29 -0
- package/dist/src/tools/mcp-tool.js +143 -0
- package/dist/src/tools/mcp-tool.js.map +1 -0
- package/dist/src/tools/mcp-tool.test.d.ts +6 -0
- package/dist/src/tools/mcp-tool.test.js +189 -0
- package/dist/src/tools/mcp-tool.test.js.map +1 -0
- package/dist/src/tools/memoryTool.d.ts +28 -0
- package/dist/src/tools/memoryTool.js +172 -0
- package/dist/src/tools/memoryTool.js.map +1 -0
- package/dist/src/tools/memoryTool.test.d.ts +6 -0
- package/dist/src/tools/memoryTool.test.js +181 -0
- package/dist/src/tools/memoryTool.test.js.map +1 -0
- package/dist/src/tools/modifiable-tool.d.ts +29 -0
- package/dist/src/tools/modifiable-tool.js +85 -0
- package/dist/src/tools/modifiable-tool.js.map +1 -0
- package/dist/src/tools/modifiable-tool.test.d.ts +6 -0
- package/dist/src/tools/modifiable-tool.test.js +204 -0
- package/dist/src/tools/modifiable-tool.test.js.map +1 -0
- package/dist/src/tools/read-file.d.ts +36 -0
- package/dist/src/tools/read-file.js +102 -0
- package/dist/src/tools/read-file.js.map +1 -0
- package/dist/src/tools/read-file.test.d.ts +6 -0
- package/dist/src/tools/read-file.test.js +181 -0
- package/dist/src/tools/read-file.test.js.map +1 -0
- package/dist/src/tools/read-many-files.d.ts +62 -0
- package/dist/src/tools/read-many-files.js +362 -0
- package/dist/src/tools/read-many-files.js.map +1 -0
- package/dist/src/tools/read-many-files.test.d.ts +6 -0
- package/dist/src/tools/read-many-files.test.js +315 -0
- package/dist/src/tools/read-many-files.test.js.map +1 -0
- package/dist/src/tools/shell.d.ts +43 -0
- package/dist/src/tools/shell.js +435 -0
- package/dist/src/tools/shell.js.map +1 -0
- package/dist/src/tools/shell.test.d.ts +6 -0
- package/dist/src/tools/shell.test.js +406 -0
- package/dist/src/tools/shell.test.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +66 -0
- package/dist/src/tools/tool-registry.js +329 -0
- package/dist/src/tools/tool-registry.js.map +1 -0
- package/dist/src/tools/tool-registry.test.d.ts +6 -0
- package/dist/src/tools/tool-registry.test.js +424 -0
- package/dist/src/tools/tool-registry.test.js.map +1 -0
- package/dist/src/tools/tools.d.ts +219 -0
- package/dist/src/tools/tools.js +111 -0
- package/dist/src/tools/tools.js.map +1 -0
- package/dist/src/tools/web-fetch.d.ts +29 -0
- package/dist/src/tools/web-fetch.js +246 -0
- package/dist/src/tools/web-fetch.js.map +1 -0
- package/dist/src/tools/web-fetch.test.d.ts +6 -0
- package/dist/src/tools/web-fetch.test.js +71 -0
- package/dist/src/tools/web-fetch.test.js.map +1 -0
- package/dist/src/tools/web-search.d.ts +49 -0
- package/dist/src/tools/web-search.js +120 -0
- package/dist/src/tools/web-search.js.map +1 -0
- package/dist/src/tools/write-file.d.ts +42 -0
- package/dist/src/tools/write-file.js +253 -0
- package/dist/src/tools/write-file.js.map +1 -0
- package/dist/src/tools/write-file.test.d.ts +6 -0
- package/dist/src/tools/write-file.test.js +413 -0
- package/dist/src/tools/write-file.test.js.map +1 -0
- package/dist/src/utils/LruCache.d.ts +13 -0
- package/dist/src/utils/LruCache.js +38 -0
- package/dist/src/utils/LruCache.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.d.ts +24 -0
- package/dist/src/utils/bfsFileSearch.js +65 -0
- package/dist/src/utils/bfsFileSearch.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.test.d.ts +6 -0
- package/dist/src/utils/bfsFileSearch.test.js +162 -0
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -0
- package/dist/src/utils/browser.d.ts +13 -0
- package/dist/src/utils/browser.js +49 -0
- package/dist/src/utils/browser.js.map +1 -0
- package/dist/src/utils/editCorrector.d.ts +53 -0
- package/dist/src/utils/editCorrector.js +546 -0
- package/dist/src/utils/editCorrector.js.map +1 -0
- package/dist/src/utils/editCorrector.test.d.ts +6 -0
- package/dist/src/utils/editCorrector.test.js +564 -0
- package/dist/src/utils/editCorrector.test.js.map +1 -0
- package/dist/src/utils/editor.d.ts +28 -0
- package/dist/src/utils/editor.js +169 -0
- package/dist/src/utils/editor.js.map +1 -0
- package/dist/src/utils/editor.test.d.ts +6 -0
- package/dist/src/utils/editor.test.js +393 -0
- package/dist/src/utils/editor.test.js.map +1 -0
- package/dist/src/utils/errorReporting.d.ts +14 -0
- package/dist/src/utils/errorReporting.js +88 -0
- package/dist/src/utils/errorReporting.js.map +1 -0
- package/dist/src/utils/errorReporting.test.d.ts +6 -0
- package/dist/src/utils/errorReporting.test.js +124 -0
- package/dist/src/utils/errorReporting.test.js.map +1 -0
- package/dist/src/utils/errors.d.ts +14 -0
- package/dist/src/utils/errors.js +54 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/fetch.d.ts +11 -0
- package/dist/src/utils/fetch.js +51 -0
- package/dist/src/utils/fetch.js.map +1 -0
- package/dist/src/utils/fileUtils.d.ts +49 -0
- package/dist/src/utils/fileUtils.js +300 -0
- package/dist/src/utils/fileUtils.js.map +1 -0
- package/dist/src/utils/fileUtils.test.d.ts +6 -0
- package/dist/src/utils/fileUtils.test.js +321 -0
- package/dist/src/utils/fileUtils.test.js.map +1 -0
- package/dist/src/utils/flashFallback.integration.test.d.ts +6 -0
- package/dist/src/utils/flashFallback.integration.test.js +112 -0
- package/dist/src/utils/flashFallback.integration.test.js.map +1 -0
- package/dist/src/utils/generateContentResponseUtilities.d.ts +14 -0
- package/dist/src/utils/generateContentResponseUtilities.js +92 -0
- package/dist/src/utils/generateContentResponseUtilities.js.map +1 -0
- package/dist/src/utils/generateContentResponseUtilities.test.d.ts +6 -0
- package/dist/src/utils/generateContentResponseUtilities.test.js +273 -0
- package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -0
- package/dist/src/utils/getFolderStructure.d.ts +31 -0
- package/dist/src/utils/getFolderStructure.js +246 -0
- package/dist/src/utils/getFolderStructure.js.map +1 -0
- package/dist/src/utils/getFolderStructure.test.d.ts +6 -0
- package/dist/src/utils/getFolderStructure.test.js +284 -0
- package/dist/src/utils/getFolderStructure.test.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.d.ts +20 -0
- package/dist/src/utils/gitIgnoreParser.js +67 -0
- package/dist/src/utils/gitIgnoreParser.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/gitIgnoreParser.test.js +157 -0
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/gitUtils.d.ts +17 -0
- package/dist/src/utils/gitUtils.js +61 -0
- package/dist/src/utils/gitUtils.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.d.ts +15 -0
- package/dist/src/utils/memoryDiscovery.js +226 -0
- package/dist/src/utils/memoryDiscovery.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.test.d.ts +6 -0
- package/dist/src/utils/memoryDiscovery.test.js +432 -0
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -0
- package/dist/src/utils/memoryImportProcessor.d.ts +35 -0
- package/dist/src/utils/memoryImportProcessor.js +141 -0
- package/dist/src/utils/memoryImportProcessor.js.map +1 -0
- package/dist/src/utils/memoryImportProcessor.test.d.ts +6 -0
- package/dist/src/utils/memoryImportProcessor.test.js +170 -0
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -0
- package/dist/src/utils/messageInspectors.d.ts +8 -0
- package/dist/src/utils/messageInspectors.js +16 -0
- package/dist/src/utils/messageInspectors.js.map +1 -0
- package/dist/src/utils/nextSpeakerChecker.d.ts +12 -0
- package/dist/src/utils/nextSpeakerChecker.js +112 -0
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -0
- package/dist/src/utils/nextSpeakerChecker.test.d.ts +6 -0
- package/dist/src/utils/nextSpeakerChecker.test.js +168 -0
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -0
- package/dist/src/utils/paths.d.ts +59 -0
- package/dist/src/utils/paths.js +153 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.d.ts +22 -0
- package/dist/src/utils/quotaErrorDetection.js +65 -0
- package/dist/src/utils/quotaErrorDetection.js.map +1 -0
- package/dist/src/utils/retry.d.ts +27 -0
- package/dist/src/utils/retry.js +276 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/dist/src/utils/retry.test.d.ts +6 -0
- package/dist/src/utils/retry.test.js +322 -0
- package/dist/src/utils/retry.test.js.map +1 -0
- package/dist/src/utils/safeJsonStringify.d.ts +13 -0
- package/dist/src/utils/safeJsonStringify.js +25 -0
- package/dist/src/utils/safeJsonStringify.js.map +1 -0
- package/dist/src/utils/safeJsonStringify.test.d.ts +6 -0
- package/dist/src/utils/safeJsonStringify.test.js +61 -0
- package/dist/src/utils/safeJsonStringify.test.js.map +1 -0
- package/dist/src/utils/schemaValidator.d.ts +22 -0
- package/dist/src/utils/schemaValidator.js +65 -0
- package/dist/src/utils/schemaValidator.js.map +1 -0
- package/dist/src/utils/session.d.ts +6 -0
- package/dist/src/utils/session.js +8 -0
- package/dist/src/utils/session.js.map +1 -0
- package/dist/src/utils/stripJsonCodeBlock.d.ts +13 -0
- package/dist/src/utils/stripJsonCodeBlock.js +21 -0
- package/dist/src/utils/stripJsonCodeBlock.js.map +1 -0
- package/dist/src/utils/summarizer.d.ts +25 -0
- package/dist/src/utils/summarizer.js +80 -0
- package/dist/src/utils/summarizer.js.map +1 -0
- package/dist/src/utils/summarizer.test.d.ts +6 -0
- package/dist/src/utils/summarizer.test.js +131 -0
- package/dist/src/utils/summarizer.test.js.map +1 -0
- package/dist/src/utils/systemEncoding.d.ts +40 -0
- package/dist/src/utils/systemEncoding.js +149 -0
- package/dist/src/utils/systemEncoding.js.map +1 -0
- package/dist/src/utils/systemEncoding.test.d.ts +6 -0
- package/dist/src/utils/systemEncoding.test.js +368 -0
- package/dist/src/utils/systemEncoding.test.js.map +1 -0
- package/dist/src/utils/testUtils.d.ts +29 -0
- package/dist/src/utils/testUtils.js +70 -0
- package/dist/src/utils/testUtils.js.map +1 -0
- package/dist/src/utils/user_account.d.ts +9 -0
- package/dist/src/utils/user_account.js +99 -0
- package/dist/src/utils/user_account.js.map +1 -0
- package/dist/src/utils/user_account.test.d.ts +6 -0
- package/dist/src/utils/user_account.test.js +153 -0
- package/dist/src/utils/user_account.test.js.map +1 -0
- package/dist/src/utils/user_id.d.ts +11 -0
- package/dist/src/utils/user_id.js +49 -0
- package/dist/src/utils/user_id.js.map +1 -0
- package/dist/src/utils/user_id.test.d.ts +6 -0
- package/dist/src/utils/user_id.test.js +21 -0
- package/dist/src/utils/user_id.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import mime from 'mime-types';
|
|
9
|
+
// Constants for text file processing
|
|
10
|
+
const DEFAULT_MAX_LINES_TEXT_FILE = 2000;
|
|
11
|
+
const MAX_LINE_LENGTH_TEXT_FILE = 2000;
|
|
12
|
+
// Default values for encoding and separator format
|
|
13
|
+
export const DEFAULT_ENCODING = 'utf-8';
|
|
14
|
+
/**
|
|
15
|
+
* Looks up the specific MIME type for a file path.
|
|
16
|
+
* @param filePath Path to the file.
|
|
17
|
+
* @returns The specific MIME type string (e.g., 'text/python', 'application/javascript') or undefined if not found or ambiguous.
|
|
18
|
+
*/
|
|
19
|
+
export function getSpecificMimeType(filePath) {
|
|
20
|
+
const lookedUpMime = mime.lookup(filePath);
|
|
21
|
+
return typeof lookedUpMime === 'string' ? lookedUpMime : undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Checks if a path is within a given root directory.
|
|
25
|
+
* @param pathToCheck The absolute path to check.
|
|
26
|
+
* @param rootDirectory The absolute root directory.
|
|
27
|
+
* @returns True if the path is within the root directory, false otherwise.
|
|
28
|
+
*/
|
|
29
|
+
export function isWithinRoot(pathToCheck, rootDirectory) {
|
|
30
|
+
const normalizedPathToCheck = path.resolve(pathToCheck);
|
|
31
|
+
const normalizedRootDirectory = path.resolve(rootDirectory);
|
|
32
|
+
// Ensure the rootDirectory path ends with a separator for correct startsWith comparison,
|
|
33
|
+
// unless it's the root path itself (e.g., '/' or 'C:\').
|
|
34
|
+
const rootWithSeparator = normalizedRootDirectory === path.sep ||
|
|
35
|
+
normalizedRootDirectory.endsWith(path.sep)
|
|
36
|
+
? normalizedRootDirectory
|
|
37
|
+
: normalizedRootDirectory + path.sep;
|
|
38
|
+
return (normalizedPathToCheck === normalizedRootDirectory ||
|
|
39
|
+
normalizedPathToCheck.startsWith(rootWithSeparator));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Determines if a file is likely binary based on content sampling.
|
|
43
|
+
* @param filePath Path to the file.
|
|
44
|
+
* @returns Promise that resolves to true if the file appears to be binary.
|
|
45
|
+
*/
|
|
46
|
+
export async function isBinaryFile(filePath) {
|
|
47
|
+
let fileHandle;
|
|
48
|
+
try {
|
|
49
|
+
fileHandle = await fs.promises.open(filePath, 'r');
|
|
50
|
+
// Read up to 4KB or file size, whichever is smaller
|
|
51
|
+
const stats = await fileHandle.stat();
|
|
52
|
+
const fileSize = stats.size;
|
|
53
|
+
if (fileSize === 0) {
|
|
54
|
+
// Empty file is not considered binary for content checking
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const bufferSize = Math.min(4096, fileSize);
|
|
58
|
+
const buffer = Buffer.alloc(bufferSize);
|
|
59
|
+
const result = await fileHandle.read(buffer, 0, buffer.length, 0);
|
|
60
|
+
const bytesRead = result.bytesRead;
|
|
61
|
+
if (bytesRead === 0)
|
|
62
|
+
return false;
|
|
63
|
+
let nonPrintableCount = 0;
|
|
64
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
65
|
+
if (buffer[i] === 0)
|
|
66
|
+
return true; // Null byte is a strong indicator
|
|
67
|
+
if (buffer[i] < 9 || (buffer[i] > 13 && buffer[i] < 32)) {
|
|
68
|
+
nonPrintableCount++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// If >30% non-printable characters, consider it binary
|
|
72
|
+
return nonPrintableCount / bytesRead > 0.3;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// Log error for debugging while maintaining existing behavior
|
|
76
|
+
console.warn(`Failed to check if file is binary: ${filePath}`, error instanceof Error ? error.message : String(error));
|
|
77
|
+
// If any error occurs (e.g. file not found, permissions),
|
|
78
|
+
// treat as not binary here; let higher-level functions handle existence/access errors.
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
// Safely close the file handle if it was successfully opened
|
|
83
|
+
if (fileHandle) {
|
|
84
|
+
try {
|
|
85
|
+
await fileHandle.close();
|
|
86
|
+
}
|
|
87
|
+
catch (closeError) {
|
|
88
|
+
// Log close errors for debugging while continuing with cleanup
|
|
89
|
+
console.warn(`Failed to close file handle for: ${filePath}`, closeError instanceof Error ? closeError.message : String(closeError));
|
|
90
|
+
// The important thing is that we attempted to clean up
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detects the type of file based on extension and content.
|
|
97
|
+
* @param filePath Path to the file.
|
|
98
|
+
* @returns Promise that resolves to 'text', 'image', 'pdf', 'audio', 'video', 'binary' or 'svg'.
|
|
99
|
+
*/
|
|
100
|
+
export async function detectFileType(filePath) {
|
|
101
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
102
|
+
// The mimetype for "ts" is MPEG transport stream (a video format) but we want
|
|
103
|
+
// to assume these are typescript files instead.
|
|
104
|
+
if (ext === '.ts') {
|
|
105
|
+
return 'text';
|
|
106
|
+
}
|
|
107
|
+
if (ext === '.svg') {
|
|
108
|
+
return 'svg';
|
|
109
|
+
}
|
|
110
|
+
const lookedUpMimeType = mime.lookup(filePath); // Returns false if not found, or the mime type string
|
|
111
|
+
if (lookedUpMimeType) {
|
|
112
|
+
if (lookedUpMimeType.startsWith('image/')) {
|
|
113
|
+
return 'image';
|
|
114
|
+
}
|
|
115
|
+
if (lookedUpMimeType.startsWith('audio/')) {
|
|
116
|
+
return 'audio';
|
|
117
|
+
}
|
|
118
|
+
if (lookedUpMimeType.startsWith('video/')) {
|
|
119
|
+
return 'video';
|
|
120
|
+
}
|
|
121
|
+
if (lookedUpMimeType === 'application/pdf') {
|
|
122
|
+
return 'pdf';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Stricter binary check for common non-text extensions before content check
|
|
126
|
+
// These are often not well-covered by mime-types or might be misidentified.
|
|
127
|
+
if ([
|
|
128
|
+
'.zip',
|
|
129
|
+
'.tar',
|
|
130
|
+
'.gz',
|
|
131
|
+
'.exe',
|
|
132
|
+
'.dll',
|
|
133
|
+
'.so',
|
|
134
|
+
'.class',
|
|
135
|
+
'.jar',
|
|
136
|
+
'.war',
|
|
137
|
+
'.7z',
|
|
138
|
+
'.doc',
|
|
139
|
+
'.docx',
|
|
140
|
+
'.xls',
|
|
141
|
+
'.xlsx',
|
|
142
|
+
'.ppt',
|
|
143
|
+
'.pptx',
|
|
144
|
+
'.odt',
|
|
145
|
+
'.ods',
|
|
146
|
+
'.odp',
|
|
147
|
+
'.bin',
|
|
148
|
+
'.dat',
|
|
149
|
+
'.obj',
|
|
150
|
+
'.o',
|
|
151
|
+
'.a',
|
|
152
|
+
'.lib',
|
|
153
|
+
'.wasm',
|
|
154
|
+
'.pyc',
|
|
155
|
+
'.pyo',
|
|
156
|
+
].includes(ext)) {
|
|
157
|
+
return 'binary';
|
|
158
|
+
}
|
|
159
|
+
// Fall back to content-based check if mime type wasn't conclusive for image/pdf
|
|
160
|
+
// and it's not a known binary extension.
|
|
161
|
+
if (await isBinaryFile(filePath)) {
|
|
162
|
+
return 'binary';
|
|
163
|
+
}
|
|
164
|
+
return 'text';
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Reads and processes a single file, handling text, images, and PDFs.
|
|
168
|
+
* @param filePath Absolute path to the file.
|
|
169
|
+
* @param rootDirectory Absolute path to the project root for relative path display.
|
|
170
|
+
* @param offset Optional offset for text files (0-based line number).
|
|
171
|
+
* @param limit Optional limit for text files (number of lines to read).
|
|
172
|
+
* @returns ProcessedFileReadResult object.
|
|
173
|
+
*/
|
|
174
|
+
export async function processSingleFileContent(filePath, rootDirectory, offset, limit) {
|
|
175
|
+
try {
|
|
176
|
+
if (!fs.existsSync(filePath)) {
|
|
177
|
+
// Sync check is acceptable before async read
|
|
178
|
+
return {
|
|
179
|
+
llmContent: '',
|
|
180
|
+
returnDisplay: 'File not found.',
|
|
181
|
+
error: `File not found: ${filePath}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const stats = await fs.promises.stat(filePath);
|
|
185
|
+
if (stats.isDirectory()) {
|
|
186
|
+
return {
|
|
187
|
+
llmContent: '',
|
|
188
|
+
returnDisplay: 'Path is a directory.',
|
|
189
|
+
error: `Path is a directory, not a file: ${filePath}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const fileSizeInBytes = stats.size;
|
|
193
|
+
// 20MB limit
|
|
194
|
+
const maxFileSize = 20 * 1024 * 1024;
|
|
195
|
+
if (fileSizeInBytes > maxFileSize) {
|
|
196
|
+
throw new Error(`File size exceeds the 20MB limit: ${filePath} (${(fileSizeInBytes /
|
|
197
|
+
(1024 * 1024)).toFixed(2)}MB)`);
|
|
198
|
+
}
|
|
199
|
+
const fileType = await detectFileType(filePath);
|
|
200
|
+
const relativePathForDisplay = path
|
|
201
|
+
.relative(rootDirectory, filePath)
|
|
202
|
+
.replace(/\\/g, '/');
|
|
203
|
+
switch (fileType) {
|
|
204
|
+
case 'binary': {
|
|
205
|
+
return {
|
|
206
|
+
llmContent: `Cannot display content of binary file: ${relativePathForDisplay}`,
|
|
207
|
+
returnDisplay: `Skipped binary file: ${relativePathForDisplay}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
case 'svg': {
|
|
211
|
+
const SVG_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
212
|
+
if (stats.size > SVG_MAX_SIZE_BYTES) {
|
|
213
|
+
return {
|
|
214
|
+
llmContent: `Cannot display content of SVG file larger than 1MB: ${relativePathForDisplay}`,
|
|
215
|
+
returnDisplay: `Skipped large SVG file (>1MB): ${relativePathForDisplay}`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
219
|
+
return {
|
|
220
|
+
llmContent: content,
|
|
221
|
+
returnDisplay: `Read SVG as text: ${relativePathForDisplay}`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
case 'text': {
|
|
225
|
+
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
226
|
+
const lines = content.split('\n');
|
|
227
|
+
const originalLineCount = lines.length;
|
|
228
|
+
const startLine = offset || 0;
|
|
229
|
+
const effectiveLimit = limit === undefined ? DEFAULT_MAX_LINES_TEXT_FILE : limit;
|
|
230
|
+
// Ensure endLine does not exceed originalLineCount
|
|
231
|
+
const endLine = Math.min(startLine + effectiveLimit, originalLineCount);
|
|
232
|
+
// Ensure selectedLines doesn't try to slice beyond array bounds if startLine is too high
|
|
233
|
+
const actualStartLine = Math.min(startLine, originalLineCount);
|
|
234
|
+
const selectedLines = lines.slice(actualStartLine, endLine);
|
|
235
|
+
let linesWereTruncatedInLength = false;
|
|
236
|
+
const formattedLines = selectedLines.map((line) => {
|
|
237
|
+
if (line.length > MAX_LINE_LENGTH_TEXT_FILE) {
|
|
238
|
+
linesWereTruncatedInLength = true;
|
|
239
|
+
return (line.substring(0, MAX_LINE_LENGTH_TEXT_FILE) + '... [truncated]');
|
|
240
|
+
}
|
|
241
|
+
return line;
|
|
242
|
+
});
|
|
243
|
+
const contentRangeTruncated = endLine < originalLineCount;
|
|
244
|
+
const isTruncated = contentRangeTruncated || linesWereTruncatedInLength;
|
|
245
|
+
let llmTextContent = '';
|
|
246
|
+
if (contentRangeTruncated) {
|
|
247
|
+
llmTextContent += `[File content truncated: showing lines ${actualStartLine + 1}-${endLine} of ${originalLineCount} total lines. Use offset/limit parameters to view more.]\n`;
|
|
248
|
+
}
|
|
249
|
+
else if (linesWereTruncatedInLength) {
|
|
250
|
+
llmTextContent += `[File content partially truncated: some lines exceeded maximum length of ${MAX_LINE_LENGTH_TEXT_FILE} characters.]\n`;
|
|
251
|
+
}
|
|
252
|
+
llmTextContent += formattedLines.join('\n');
|
|
253
|
+
return {
|
|
254
|
+
llmContent: llmTextContent,
|
|
255
|
+
returnDisplay: isTruncated ? '(truncated)' : '',
|
|
256
|
+
isTruncated,
|
|
257
|
+
originalLineCount,
|
|
258
|
+
linesShown: [actualStartLine + 1, endLine],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
case 'image':
|
|
262
|
+
case 'pdf':
|
|
263
|
+
case 'audio':
|
|
264
|
+
case 'video': {
|
|
265
|
+
const contentBuffer = await fs.promises.readFile(filePath);
|
|
266
|
+
const base64Data = contentBuffer.toString('base64');
|
|
267
|
+
return {
|
|
268
|
+
llmContent: {
|
|
269
|
+
inlineData: {
|
|
270
|
+
data: base64Data,
|
|
271
|
+
mimeType: mime.lookup(filePath) || 'application/octet-stream',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
returnDisplay: `Read ${fileType} file: ${relativePathForDisplay}`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
default: {
|
|
278
|
+
// Should not happen with current detectFileType logic
|
|
279
|
+
const exhaustiveCheck = fileType;
|
|
280
|
+
return {
|
|
281
|
+
llmContent: `Unhandled file type: ${exhaustiveCheck}`,
|
|
282
|
+
returnDisplay: `Skipped unhandled file type: ${relativePathForDisplay}`,
|
|
283
|
+
error: `Unhandled file type for ${filePath}`,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
290
|
+
const displayPath = path
|
|
291
|
+
.relative(rootDirectory, filePath)
|
|
292
|
+
.replace(/\\/g, '/');
|
|
293
|
+
return {
|
|
294
|
+
llmContent: `Error reading file ${displayPath}: ${errorMessage}`,
|
|
295
|
+
returnDisplay: `Error reading file ${displayPath}: ${errorMessage}`,
|
|
296
|
+
error: `Error reading file ${filePath}: ${errorMessage}`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=fileUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileUtils.js","sourceRoot":"","sources":["../../../src/utils/fileUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
|
|
7
|
+
import * as actualNodeFs from 'node:fs'; // For setup/teardown
|
|
8
|
+
import fsPromises from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import mime from 'mime-types';
|
|
12
|
+
import { isWithinRoot, isBinaryFile, detectFileType, processSingleFileContent, } from './fileUtils.js';
|
|
13
|
+
vi.mock('mime-types', () => ({
|
|
14
|
+
default: { lookup: vi.fn() },
|
|
15
|
+
lookup: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
const mockMimeLookup = mime.lookup;
|
|
18
|
+
describe('fileUtils', () => {
|
|
19
|
+
let tempRootDir;
|
|
20
|
+
const originalProcessCwd = process.cwd;
|
|
21
|
+
let testTextFilePath;
|
|
22
|
+
let testImageFilePath;
|
|
23
|
+
let testPdfFilePath;
|
|
24
|
+
let testBinaryFilePath;
|
|
25
|
+
let nonexistentFilePath;
|
|
26
|
+
let directoryPath;
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.resetAllMocks(); // Reset all mocks, including mime.lookup
|
|
29
|
+
tempRootDir = actualNodeFs.mkdtempSync(path.join(os.tmpdir(), 'fileUtils-test-'));
|
|
30
|
+
process.cwd = vi.fn(() => tempRootDir); // Mock cwd if necessary for relative path logic within tests
|
|
31
|
+
testTextFilePath = path.join(tempRootDir, 'test.txt');
|
|
32
|
+
testImageFilePath = path.join(tempRootDir, 'image.png');
|
|
33
|
+
testPdfFilePath = path.join(tempRootDir, 'document.pdf');
|
|
34
|
+
testBinaryFilePath = path.join(tempRootDir, 'app.exe');
|
|
35
|
+
nonexistentFilePath = path.join(tempRootDir, 'nonexistent.txt');
|
|
36
|
+
directoryPath = path.join(tempRootDir, 'subdir');
|
|
37
|
+
actualNodeFs.mkdirSync(directoryPath, { recursive: true }); // Ensure subdir exists
|
|
38
|
+
});
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
if (actualNodeFs.existsSync(tempRootDir)) {
|
|
41
|
+
actualNodeFs.rmSync(tempRootDir, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
process.cwd = originalProcessCwd;
|
|
44
|
+
vi.restoreAllMocks(); // Restore any spies
|
|
45
|
+
});
|
|
46
|
+
describe('isWithinRoot', () => {
|
|
47
|
+
const root = path.resolve('/project/root');
|
|
48
|
+
it('should return true for paths directly within the root', () => {
|
|
49
|
+
expect(isWithinRoot(path.join(root, 'file.txt'), root)).toBe(true);
|
|
50
|
+
expect(isWithinRoot(path.join(root, 'subdir', 'file.txt'), root)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it('should return true for the root path itself', () => {
|
|
53
|
+
expect(isWithinRoot(root, root)).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it('should return false for paths outside the root', () => {
|
|
56
|
+
expect(isWithinRoot(path.resolve('/project/other', 'file.txt'), root)).toBe(false);
|
|
57
|
+
expect(isWithinRoot(path.resolve('/unrelated', 'file.txt'), root)).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
it('should return false for paths that only partially match the root prefix', () => {
|
|
60
|
+
expect(isWithinRoot(path.resolve('/project/root-but-actually-different'), root)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('should handle paths with trailing slashes correctly', () => {
|
|
63
|
+
expect(isWithinRoot(path.join(root, 'file.txt') + path.sep, root)).toBe(true);
|
|
64
|
+
expect(isWithinRoot(root + path.sep, root)).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it('should handle different path separators (POSIX vs Windows)', () => {
|
|
67
|
+
const posixRoot = '/project/root';
|
|
68
|
+
const posixPathInside = '/project/root/file.txt';
|
|
69
|
+
const posixPathOutside = '/project/other/file.txt';
|
|
70
|
+
expect(isWithinRoot(posixPathInside, posixRoot)).toBe(true);
|
|
71
|
+
expect(isWithinRoot(posixPathOutside, posixRoot)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
it('should return false for a root path that is a sub-path of the path to check', () => {
|
|
74
|
+
const pathToCheck = path.resolve('/project/root/sub');
|
|
75
|
+
const rootSub = path.resolve('/project/root');
|
|
76
|
+
expect(isWithinRoot(pathToCheck, rootSub)).toBe(true);
|
|
77
|
+
const pathToCheckSuper = path.resolve('/project/root');
|
|
78
|
+
const rootSuper = path.resolve('/project/root/sub');
|
|
79
|
+
expect(isWithinRoot(pathToCheckSuper, rootSuper)).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('isBinaryFile', () => {
|
|
83
|
+
let filePathForBinaryTest;
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
filePathForBinaryTest = path.join(tempRootDir, 'binaryCheck.tmp');
|
|
86
|
+
});
|
|
87
|
+
afterEach(() => {
|
|
88
|
+
if (actualNodeFs.existsSync(filePathForBinaryTest)) {
|
|
89
|
+
actualNodeFs.unlinkSync(filePathForBinaryTest);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
it('should return false for an empty file', async () => {
|
|
93
|
+
actualNodeFs.writeFileSync(filePathForBinaryTest, '');
|
|
94
|
+
expect(await isBinaryFile(filePathForBinaryTest)).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it('should return false for a typical text file', async () => {
|
|
97
|
+
actualNodeFs.writeFileSync(filePathForBinaryTest, 'Hello, world!\nThis is a test file with normal text content.');
|
|
98
|
+
expect(await isBinaryFile(filePathForBinaryTest)).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
it('should return true for a file with many null bytes', async () => {
|
|
101
|
+
const binaryContent = Buffer.from([
|
|
102
|
+
0x48, 0x65, 0x00, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
103
|
+
]); // "He\0llo\0\0\0\0\0"
|
|
104
|
+
actualNodeFs.writeFileSync(filePathForBinaryTest, binaryContent);
|
|
105
|
+
expect(await isBinaryFile(filePathForBinaryTest)).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it('should return true for a file with high percentage of non-printable ASCII', async () => {
|
|
108
|
+
const binaryContent = Buffer.from([
|
|
109
|
+
0x41, 0x42, 0x01, 0x02, 0x03, 0x04, 0x05, 0x43, 0x44, 0x06,
|
|
110
|
+
]); // AB\x01\x02\x03\x04\x05CD\x06
|
|
111
|
+
actualNodeFs.writeFileSync(filePathForBinaryTest, binaryContent);
|
|
112
|
+
expect(await isBinaryFile(filePathForBinaryTest)).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
it('should return false if file access fails (e.g., ENOENT)', async () => {
|
|
115
|
+
// Ensure the file does not exist
|
|
116
|
+
if (actualNodeFs.existsSync(filePathForBinaryTest)) {
|
|
117
|
+
actualNodeFs.unlinkSync(filePathForBinaryTest);
|
|
118
|
+
}
|
|
119
|
+
expect(await isBinaryFile(filePathForBinaryTest)).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('detectFileType', () => {
|
|
123
|
+
let filePathForDetectTest;
|
|
124
|
+
beforeEach(() => {
|
|
125
|
+
filePathForDetectTest = path.join(tempRootDir, 'detectType.tmp');
|
|
126
|
+
// Default: create as a text file for isBinaryFile fallback
|
|
127
|
+
actualNodeFs.writeFileSync(filePathForDetectTest, 'Plain text content');
|
|
128
|
+
});
|
|
129
|
+
afterEach(() => {
|
|
130
|
+
if (actualNodeFs.existsSync(filePathForDetectTest)) {
|
|
131
|
+
actualNodeFs.unlinkSync(filePathForDetectTest);
|
|
132
|
+
}
|
|
133
|
+
vi.restoreAllMocks(); // Restore spies on actualNodeFs
|
|
134
|
+
});
|
|
135
|
+
it('should detect typescript type by extension (ts)', async () => {
|
|
136
|
+
expect(await detectFileType('file.ts')).toBe('text');
|
|
137
|
+
expect(await detectFileType('file.test.ts')).toBe('text');
|
|
138
|
+
});
|
|
139
|
+
it('should detect image type by extension (png)', async () => {
|
|
140
|
+
mockMimeLookup.mockReturnValueOnce('image/png');
|
|
141
|
+
expect(await detectFileType('file.png')).toBe('image');
|
|
142
|
+
});
|
|
143
|
+
it('should detect image type by extension (jpeg)', async () => {
|
|
144
|
+
mockMimeLookup.mockReturnValueOnce('image/jpeg');
|
|
145
|
+
expect(await detectFileType('file.jpg')).toBe('image');
|
|
146
|
+
});
|
|
147
|
+
it('should detect svg type by extension', async () => {
|
|
148
|
+
expect(await detectFileType('image.svg')).toBe('svg');
|
|
149
|
+
expect(await detectFileType('image.icon.svg')).toBe('svg');
|
|
150
|
+
});
|
|
151
|
+
it('should detect pdf type by extension', async () => {
|
|
152
|
+
mockMimeLookup.mockReturnValueOnce('application/pdf');
|
|
153
|
+
expect(await detectFileType('file.pdf')).toBe('pdf');
|
|
154
|
+
});
|
|
155
|
+
it('should detect audio type by extension', async () => {
|
|
156
|
+
mockMimeLookup.mockReturnValueOnce('audio/mpeg');
|
|
157
|
+
expect(await detectFileType('song.mp3')).toBe('audio');
|
|
158
|
+
});
|
|
159
|
+
it('should detect video type by extension', async () => {
|
|
160
|
+
mockMimeLookup.mockReturnValueOnce('video/mp4');
|
|
161
|
+
expect(await detectFileType('movie.mp4')).toBe('video');
|
|
162
|
+
});
|
|
163
|
+
it('should detect known binary extensions as binary (e.g. .zip)', async () => {
|
|
164
|
+
mockMimeLookup.mockReturnValueOnce('application/zip');
|
|
165
|
+
expect(await detectFileType('archive.zip')).toBe('binary');
|
|
166
|
+
});
|
|
167
|
+
it('should detect known binary extensions as binary (e.g. .exe)', async () => {
|
|
168
|
+
mockMimeLookup.mockReturnValueOnce('application/octet-stream'); // Common for .exe
|
|
169
|
+
expect(await detectFileType('app.exe')).toBe('binary');
|
|
170
|
+
});
|
|
171
|
+
it('should use isBinaryFile for unknown extensions and detect as binary', async () => {
|
|
172
|
+
mockMimeLookup.mockReturnValueOnce(false); // Unknown mime type
|
|
173
|
+
// Create a file that isBinaryFile will identify as binary
|
|
174
|
+
const binaryContent = Buffer.from([
|
|
175
|
+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
|
|
176
|
+
]);
|
|
177
|
+
actualNodeFs.writeFileSync(filePathForDetectTest, binaryContent);
|
|
178
|
+
expect(await detectFileType(filePathForDetectTest)).toBe('binary');
|
|
179
|
+
});
|
|
180
|
+
it('should default to text if mime type is unknown and content is not binary', async () => {
|
|
181
|
+
mockMimeLookup.mockReturnValueOnce(false); // Unknown mime type
|
|
182
|
+
// filePathForDetectTest is already a text file by default from beforeEach
|
|
183
|
+
expect(await detectFileType(filePathForDetectTest)).toBe('text');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('processSingleFileContent', () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
// Ensure files exist for statSync checks before readFile might be mocked
|
|
189
|
+
if (actualNodeFs.existsSync(testTextFilePath))
|
|
190
|
+
actualNodeFs.unlinkSync(testTextFilePath);
|
|
191
|
+
if (actualNodeFs.existsSync(testImageFilePath))
|
|
192
|
+
actualNodeFs.unlinkSync(testImageFilePath);
|
|
193
|
+
if (actualNodeFs.existsSync(testPdfFilePath))
|
|
194
|
+
actualNodeFs.unlinkSync(testPdfFilePath);
|
|
195
|
+
if (actualNodeFs.existsSync(testBinaryFilePath))
|
|
196
|
+
actualNodeFs.unlinkSync(testBinaryFilePath);
|
|
197
|
+
});
|
|
198
|
+
it('should read a text file successfully', async () => {
|
|
199
|
+
const content = 'Line 1\\nLine 2\\nLine 3';
|
|
200
|
+
actualNodeFs.writeFileSync(testTextFilePath, content);
|
|
201
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir);
|
|
202
|
+
expect(result.llmContent).toBe(content);
|
|
203
|
+
expect(result.returnDisplay).toBe('');
|
|
204
|
+
expect(result.error).toBeUndefined();
|
|
205
|
+
});
|
|
206
|
+
it('should handle file not found', async () => {
|
|
207
|
+
const result = await processSingleFileContent(nonexistentFilePath, tempRootDir);
|
|
208
|
+
expect(result.error).toContain('File not found');
|
|
209
|
+
expect(result.returnDisplay).toContain('File not found');
|
|
210
|
+
});
|
|
211
|
+
it('should handle read errors for text files', async () => {
|
|
212
|
+
actualNodeFs.writeFileSync(testTextFilePath, 'content'); // File must exist for initial statSync
|
|
213
|
+
const readError = new Error('Simulated read error');
|
|
214
|
+
vi.spyOn(fsPromises, 'readFile').mockRejectedValueOnce(readError);
|
|
215
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir);
|
|
216
|
+
expect(result.error).toContain('Simulated read error');
|
|
217
|
+
expect(result.returnDisplay).toContain('Simulated read error');
|
|
218
|
+
});
|
|
219
|
+
it('should handle read errors for image/pdf files', async () => {
|
|
220
|
+
actualNodeFs.writeFileSync(testImageFilePath, 'content'); // File must exist
|
|
221
|
+
mockMimeLookup.mockReturnValue('image/png');
|
|
222
|
+
const readError = new Error('Simulated image read error');
|
|
223
|
+
vi.spyOn(fsPromises, 'readFile').mockRejectedValueOnce(readError);
|
|
224
|
+
const result = await processSingleFileContent(testImageFilePath, tempRootDir);
|
|
225
|
+
expect(result.error).toContain('Simulated image read error');
|
|
226
|
+
expect(result.returnDisplay).toContain('Simulated image read error');
|
|
227
|
+
});
|
|
228
|
+
it('should process an image file', async () => {
|
|
229
|
+
const fakePngData = Buffer.from('fake png data');
|
|
230
|
+
actualNodeFs.writeFileSync(testImageFilePath, fakePngData);
|
|
231
|
+
mockMimeLookup.mockReturnValue('image/png');
|
|
232
|
+
const result = await processSingleFileContent(testImageFilePath, tempRootDir);
|
|
233
|
+
expect(result.llmContent.inlineData).toBeDefined();
|
|
234
|
+
expect(result.llmContent.inlineData
|
|
235
|
+
.mimeType).toBe('image/png');
|
|
236
|
+
expect(result.llmContent.inlineData.data).toBe(fakePngData.toString('base64'));
|
|
237
|
+
expect(result.returnDisplay).toContain('Read image file: image.png');
|
|
238
|
+
});
|
|
239
|
+
it('should process a PDF file', async () => {
|
|
240
|
+
const fakePdfData = Buffer.from('fake pdf data');
|
|
241
|
+
actualNodeFs.writeFileSync(testPdfFilePath, fakePdfData);
|
|
242
|
+
mockMimeLookup.mockReturnValue('application/pdf');
|
|
243
|
+
const result = await processSingleFileContent(testPdfFilePath, tempRootDir);
|
|
244
|
+
expect(result.llmContent.inlineData).toBeDefined();
|
|
245
|
+
expect(result.llmContent.inlineData
|
|
246
|
+
.mimeType).toBe('application/pdf');
|
|
247
|
+
expect(result.llmContent.inlineData.data).toBe(fakePdfData.toString('base64'));
|
|
248
|
+
expect(result.returnDisplay).toContain('Read pdf file: document.pdf');
|
|
249
|
+
});
|
|
250
|
+
it('should read an SVG file as text when under 1MB', async () => {
|
|
251
|
+
const svgContent = `
|
|
252
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
|
253
|
+
<rect width="100" height="100" fill="blue" />
|
|
254
|
+
</svg>
|
|
255
|
+
`;
|
|
256
|
+
const testSvgFilePath = path.join(tempRootDir, 'test.svg');
|
|
257
|
+
actualNodeFs.writeFileSync(testSvgFilePath, svgContent, 'utf-8');
|
|
258
|
+
mockMimeLookup.mockReturnValue('image/svg+xml');
|
|
259
|
+
const result = await processSingleFileContent(testSvgFilePath, tempRootDir);
|
|
260
|
+
expect(result.llmContent).toBe(svgContent);
|
|
261
|
+
expect(result.returnDisplay).toContain('Read SVG as text');
|
|
262
|
+
});
|
|
263
|
+
it('should skip binary files', async () => {
|
|
264
|
+
actualNodeFs.writeFileSync(testBinaryFilePath, Buffer.from([0x00, 0x01, 0x02]));
|
|
265
|
+
mockMimeLookup.mockReturnValueOnce('application/octet-stream');
|
|
266
|
+
// isBinaryFile will operate on the real file.
|
|
267
|
+
const result = await processSingleFileContent(testBinaryFilePath, tempRootDir);
|
|
268
|
+
expect(result.llmContent).toContain('Cannot display content of binary file');
|
|
269
|
+
expect(result.returnDisplay).toContain('Skipped binary file: app.exe');
|
|
270
|
+
});
|
|
271
|
+
it('should handle path being a directory', async () => {
|
|
272
|
+
const result = await processSingleFileContent(directoryPath, tempRootDir);
|
|
273
|
+
expect(result.error).toContain('Path is a directory');
|
|
274
|
+
expect(result.returnDisplay).toContain('Path is a directory');
|
|
275
|
+
});
|
|
276
|
+
it('should paginate text files correctly (offset and limit)', async () => {
|
|
277
|
+
const lines = Array.from({ length: 20 }, (_, i) => `Line ${i + 1}`);
|
|
278
|
+
actualNodeFs.writeFileSync(testTextFilePath, lines.join('\n'));
|
|
279
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir, 5, 5); // Read lines 6-10
|
|
280
|
+
const expectedContent = lines.slice(5, 10).join('\n');
|
|
281
|
+
expect(result.llmContent).toContain(expectedContent);
|
|
282
|
+
expect(result.llmContent).toContain('[File content truncated: showing lines 6-10 of 20 total lines. Use offset/limit parameters to view more.]');
|
|
283
|
+
expect(result.returnDisplay).toBe('(truncated)');
|
|
284
|
+
expect(result.isTruncated).toBe(true);
|
|
285
|
+
expect(result.originalLineCount).toBe(20);
|
|
286
|
+
expect(result.linesShown).toEqual([6, 10]);
|
|
287
|
+
});
|
|
288
|
+
it('should handle limit exceeding file length', async () => {
|
|
289
|
+
const lines = ['Line 1', 'Line 2'];
|
|
290
|
+
actualNodeFs.writeFileSync(testTextFilePath, lines.join('\n'));
|
|
291
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir, 0, 10);
|
|
292
|
+
const expectedContent = lines.join('\n');
|
|
293
|
+
expect(result.llmContent).toBe(expectedContent);
|
|
294
|
+
expect(result.returnDisplay).toBe('');
|
|
295
|
+
expect(result.isTruncated).toBe(false);
|
|
296
|
+
expect(result.originalLineCount).toBe(2);
|
|
297
|
+
expect(result.linesShown).toEqual([1, 2]);
|
|
298
|
+
});
|
|
299
|
+
it('should truncate long lines in text files', async () => {
|
|
300
|
+
const longLine = 'a'.repeat(2500);
|
|
301
|
+
actualNodeFs.writeFileSync(testTextFilePath, `Short line\n${longLine}\nAnother short line`);
|
|
302
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir);
|
|
303
|
+
expect(result.llmContent).toContain('Short line');
|
|
304
|
+
expect(result.llmContent).toContain(longLine.substring(0, 2000) + '... [truncated]');
|
|
305
|
+
expect(result.llmContent).toContain('Another short line');
|
|
306
|
+
expect(result.llmContent).toContain('[File content partially truncated: some lines exceeded maximum length of 2000 characters.]');
|
|
307
|
+
expect(result.isTruncated).toBe(true);
|
|
308
|
+
});
|
|
309
|
+
it('should return an error if the file size exceeds 20MB', async () => {
|
|
310
|
+
// Create a file just over 20MB
|
|
311
|
+
const twentyOneMB = 21 * 1024 * 1024;
|
|
312
|
+
const buffer = Buffer.alloc(twentyOneMB, 0x61); // Fill with 'a'
|
|
313
|
+
actualNodeFs.writeFileSync(testTextFilePath, buffer);
|
|
314
|
+
const result = await processSingleFileContent(testTextFilePath, tempRootDir);
|
|
315
|
+
expect(result.error).toContain('File size exceeds the 20MB limit');
|
|
316
|
+
expect(result.returnDisplay).toContain('File size exceeds the 20MB limit');
|
|
317
|
+
expect(result.llmContent).toContain('File size exceeds the 20MB limit');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
//# sourceMappingURL=fileUtils.test.js.map
|