@oscharko-dev/keiko-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/assistant-response.d.ts +6 -0
- package/dist/assistant-response.d.ts.map +1 -0
- package/dist/assistant-response.js +12 -0
- package/dist/browser.d.ts +11 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +245 -0
- package/dist/chat-handlers.d.ts +48 -0
- package/dist/chat-handlers.d.ts.map +1 -0
- package/dist/chat-handlers.js +821 -0
- package/dist/chat-stream-handlers.d.ts +4 -0
- package/dist/chat-stream-handlers.d.ts.map +1 -0
- package/dist/chat-stream-handlers.js +136 -0
- package/dist/conversation-prompt.d.ts +8 -0
- package/dist/conversation-prompt.d.ts.map +1 -0
- package/dist/conversation-prompt.js +36 -0
- package/dist/conversation-validation.d.ts +26 -0
- package/dist/conversation-validation.d.ts.map +1 -0
- package/dist/conversation-validation.js +125 -0
- package/dist/credentialPersistence.d.ts +23 -0
- package/dist/credentialPersistence.d.ts.map +1 -0
- package/dist/credentialPersistence.js +93 -0
- package/dist/credentialVault.d.ts +30 -0
- package/dist/credentialVault.d.ts.map +1 -0
- package/dist/credentialVault.js +206 -0
- package/dist/csp.d.ts +3 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +75 -0
- package/dist/deps.d.ts +78 -0
- package/dist/deps.d.ts.map +1 -0
- package/dist/deps.js +457 -0
- package/dist/editor/agentRoutes.d.ts +7 -0
- package/dist/editor/agentRoutes.d.ts.map +1 -0
- package/dist/editor/agentRoutes.js +197 -0
- package/dist/editor/assuredGateRunner.d.ts +36 -0
- package/dist/editor/assuredGateRunner.d.ts.map +1 -0
- package/dist/editor/assuredGateRunner.js +100 -0
- package/dist/editor/assuredPreFilter.d.ts +34 -0
- package/dist/editor/assuredPreFilter.d.ts.map +1 -0
- package/dist/editor/assuredPreFilter.js +134 -0
- package/dist/editor/assuredPreFilterRunner.d.ts +31 -0
- package/dist/editor/assuredPreFilterRunner.d.ts.map +1 -0
- package/dist/editor/assuredPreFilterRunner.js +312 -0
- package/dist/editor/builtinLanguageProviders.d.ts +6 -0
- package/dist/editor/builtinLanguageProviders.d.ts.map +1 -0
- package/dist/editor/builtinLanguageProviders.js +221 -0
- package/dist/editor/codingContext.d.ts +12 -0
- package/dist/editor/codingContext.d.ts.map +1 -0
- package/dist/editor/codingContext.js +121 -0
- package/dist/editor/codingContextEvidence.d.ts +7 -0
- package/dist/editor/codingContextEvidence.d.ts.map +1 -0
- package/dist/editor/codingContextEvidence.js +52 -0
- package/dist/editor/codingContextProviders.d.ts +36 -0
- package/dist/editor/codingContextProviders.d.ts.map +1 -0
- package/dist/editor/codingContextProviders.js +348 -0
- package/dist/editor/completionModelEvidence.d.ts +16 -0
- package/dist/editor/completionModelEvidence.d.ts.map +1 -0
- package/dist/editor/completionModelEvidence.js +50 -0
- package/dist/editor/completionRoutes.d.ts +37 -0
- package/dist/editor/completionRoutes.d.ts.map +1 -0
- package/dist/editor/completionRoutes.js +411 -0
- package/dist/editor/contextRoutes.d.ts +6 -0
- package/dist/editor/contextRoutes.d.ts.map +1 -0
- package/dist/editor/contextRoutes.js +411 -0
- package/dist/editor/disposableAssuredExecution.d.ts +22 -0
- package/dist/editor/disposableAssuredExecution.d.ts.map +1 -0
- package/dist/editor/disposableAssuredExecution.js +57 -0
- package/dist/editor/editorCompletionModel.d.ts +47 -0
- package/dist/editor/editorCompletionModel.d.ts.map +1 -0
- package/dist/editor/editorCompletionModel.js +156 -0
- package/dist/editor/editorInlineCompletionModel.d.ts +34 -0
- package/dist/editor/editorInlineCompletionModel.d.ts.map +1 -0
- package/dist/editor/editorInlineCompletionModel.js +112 -0
- package/dist/editor/editorModelTokenBudget.d.ts +46 -0
- package/dist/editor/editorModelTokenBudget.d.ts.map +1 -0
- package/dist/editor/editorModelTokenBudget.js +121 -0
- package/dist/editor/inlineCompletionRateLimiter.d.ts +19 -0
- package/dist/editor/inlineCompletionRateLimiter.d.ts.map +1 -0
- package/dist/editor/inlineCompletionRateLimiter.js +46 -0
- package/dist/editor/inlineCompletionRoutes.d.ts +26 -0
- package/dist/editor/inlineCompletionRoutes.d.ts.map +1 -0
- package/dist/editor/inlineCompletionRoutes.js +404 -0
- package/dist/editor/inlineCompletionTelemetryEvidence.d.ts +5 -0
- package/dist/editor/inlineCompletionTelemetryEvidence.d.ts.map +1 -0
- package/dist/editor/inlineCompletionTelemetryEvidence.js +42 -0
- package/dist/editor/languageCancellation.d.ts +19 -0
- package/dist/editor/languageCancellation.d.ts.map +1 -0
- package/dist/editor/languageCancellation.js +48 -0
- package/dist/editor/languageProvider.d.ts +39 -0
- package/dist/editor/languageProvider.d.ts.map +1 -0
- package/dist/editor/languageProvider.js +11 -0
- package/dist/editor/languageRoutes.d.ts +15 -0
- package/dist/editor/languageRoutes.d.ts.map +1 -0
- package/dist/editor/languageRoutes.js +106 -0
- package/dist/editor/languageSanitize.d.ts +8 -0
- package/dist/editor/languageSanitize.d.ts.map +1 -0
- package/dist/editor/languageSanitize.js +101 -0
- package/dist/editor/languageService.d.ts +36 -0
- package/dist/editor/languageService.d.ts.map +1 -0
- package/dist/editor/languageService.js +93 -0
- package/dist/editor/languageServiceHost.d.ts +14 -0
- package/dist/editor/languageServiceHost.d.ts.map +1 -0
- package/dist/editor/languageServiceHost.js +242 -0
- package/dist/editor/localKnowledgeRetrieval.d.ts +21 -0
- package/dist/editor/localKnowledgeRetrieval.d.ts.map +1 -0
- package/dist/editor/localKnowledgeRetrieval.js +44 -0
- package/dist/editor/patchApplyEvidence.d.ts +21 -0
- package/dist/editor/patchApplyEvidence.d.ts.map +1 -0
- package/dist/editor/patchApplyEvidence.js +87 -0
- package/dist/editor/patchApplyRoutes.d.ts +16 -0
- package/dist/editor/patchApplyRoutes.d.ts.map +1 -0
- package/dist/editor/patchApplyRoutes.js +307 -0
- package/dist/editor/postApplyVerification.d.ts +42 -0
- package/dist/editor/postApplyVerification.d.ts.map +1 -0
- package/dist/editor/postApplyVerification.js +177 -0
- package/dist/editor/testGenerationEvidence.d.ts +6 -0
- package/dist/editor/testGenerationEvidence.d.ts.map +1 -0
- package/dist/editor/testGenerationEvidence.js +72 -0
- package/dist/editor/testGenerationPatch.d.ts +10 -0
- package/dist/editor/testGenerationPatch.d.ts.map +1 -0
- package/dist/editor/testGenerationPatch.js +66 -0
- package/dist/editor/testGenerationRoutes.d.ts +21 -0
- package/dist/editor/testGenerationRoutes.d.ts.map +1 -0
- package/dist/editor/testGenerationRoutes.js +254 -0
- package/dist/editor/testGenerationRunner.d.ts +23 -0
- package/dist/editor/testGenerationRunner.d.ts.map +1 -0
- package/dist/editor/testGenerationRunner.js +120 -0
- package/dist/editor/textOffsets.d.ts +6 -0
- package/dist/editor/textOffsets.d.ts.map +1 -0
- package/dist/editor/textOffsets.js +82 -0
- package/dist/editor/typescriptLanguageProvider.d.ts +3 -0
- package/dist/editor/typescriptLanguageProvider.d.ts.map +1 -0
- package/dist/editor/typescriptLanguageProvider.js +217 -0
- package/dist/evidence.d.ts +28 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +145 -0
- package/dist/files-deny.d.ts +3 -0
- package/dist/files-deny.d.ts.map +1 -0
- package/dist/files-deny.js +12 -0
- package/dist/files.d.ts +97 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +733 -0
- package/dist/gateway-setup.d.ts +10 -0
- package/dist/gateway-setup.d.ts.map +1 -0
- package/dist/gateway-setup.js +896 -0
- package/dist/governed-workflow.d.ts +17 -0
- package/dist/governed-workflow.d.ts.map +1 -0
- package/dist/governed-workflow.js +147 -0
- package/dist/grounded-answer.d.ts +12 -0
- package/dist/grounded-answer.d.ts.map +1 -0
- package/dist/grounded-answer.js +69 -0
- package/dist/grounded-context-index.d.ts +25 -0
- package/dist/grounded-context-index.d.ts.map +1 -0
- package/dist/grounded-context-index.js +169 -0
- package/dist/grounded-document-evidence.d.ts +28 -0
- package/dist/grounded-document-evidence.d.ts.map +1 -0
- package/dist/grounded-document-evidence.js +430 -0
- package/dist/grounded-handoff.d.ts +4 -0
- package/dist/grounded-handoff.d.ts.map +1 -0
- package/dist/grounded-handoff.js +445 -0
- package/dist/grounded-orchestrator.d.ts +43 -0
- package/dist/grounded-orchestrator.d.ts.map +1 -0
- package/dist/grounded-orchestrator.js +1445 -0
- package/dist/grounded-prompt.d.ts +2 -0
- package/dist/grounded-prompt.d.ts.map +1 -0
- package/dist/grounded-prompt.js +17 -0
- package/dist/grounded-qa-hybrid.d.ts +36 -0
- package/dist/grounded-qa-hybrid.d.ts.map +1 -0
- package/dist/grounded-qa-hybrid.js +762 -0
- package/dist/grounded-qa-multi-source.d.ts +38 -0
- package/dist/grounded-qa-multi-source.d.ts.map +1 -0
- package/dist/grounded-qa-multi-source.js +461 -0
- package/dist/grounded-qa.d.ts +45 -0
- package/dist/grounded-qa.d.ts.map +1 -0
- package/dist/grounded-qa.js +877 -0
- package/dist/grounded-rerank.d.ts +26 -0
- package/dist/grounded-rerank.d.ts.map +1 -0
- package/dist/grounded-rerank.js +72 -0
- package/dist/grounded-turn-registry.d.ts +23 -0
- package/dist/grounded-turn-registry.d.ts.map +1 -0
- package/dist/grounded-turn-registry.js +102 -0
- package/dist/headers.d.ts +3 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +22 -0
- package/dist/host-check.d.ts +3 -0
- package/dist/host-check.d.ts.map +1 -0
- package/dist/host-check.js +58 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/load-csp.d.ts +3 -0
- package/dist/load-csp.d.ts.map +1 -0
- package/dist/load-csp.js +100 -0
- package/dist/local-knowledge-grounded-qa.d.ts +42 -0
- package/dist/local-knowledge-grounded-qa.d.ts.map +1 -0
- package/dist/local-knowledge-grounded-qa.js +678 -0
- package/dist/local-knowledge-handlers.d.ts +24 -0
- package/dist/local-knowledge-handlers.d.ts.map +1 -0
- package/dist/local-knowledge-handlers.js +1285 -0
- package/dist/local-knowledge-indexing-registry.d.ts +13 -0
- package/dist/local-knowledge-indexing-registry.d.ts.map +1 -0
- package/dist/local-knowledge-indexing-registry.js +53 -0
- package/dist/localKnowledgeKeyProvider.d.ts +11 -0
- package/dist/localKnowledgeKeyProvider.d.ts.map +1 -0
- package/dist/localKnowledgeKeyProvider.js +48 -0
- package/dist/memory-audit-event-builders.d.ts +21 -0
- package/dist/memory-audit-event-builders.d.ts.map +1 -0
- package/dist/memory-audit-event-builders.js +187 -0
- package/dist/memory-audit-handler.d.ts +23 -0
- package/dist/memory-audit-handler.d.ts.map +1 -0
- package/dist/memory-audit-handler.js +191 -0
- package/dist/memory-capture-policy.d.ts +10 -0
- package/dist/memory-capture-policy.d.ts.map +1 -0
- package/dist/memory-capture-policy.js +44 -0
- package/dist/memory-consolidation-handlers.d.ts +6 -0
- package/dist/memory-consolidation-handlers.d.ts.map +1 -0
- package/dist/memory-consolidation-handlers.js +491 -0
- package/dist/memory-consolidation-registry.d.ts +47 -0
- package/dist/memory-consolidation-registry.d.ts.map +1 -0
- package/dist/memory-consolidation-registry.js +106 -0
- package/dist/memory-conv-handlers.d.ts +8 -0
- package/dist/memory-conv-handlers.d.ts.map +1 -0
- package/dist/memory-conv-handlers.js +369 -0
- package/dist/memory-conversation-context.d.ts +13 -0
- package/dist/memory-conversation-context.d.ts.map +1 -0
- package/dist/memory-conversation-context.js +22 -0
- package/dist/memory-diagnostics.d.ts +29 -0
- package/dist/memory-diagnostics.d.ts.map +1 -0
- package/dist/memory-diagnostics.js +122 -0
- package/dist/memory-embedding.d.ts +21 -0
- package/dist/memory-embedding.d.ts.map +1 -0
- package/dist/memory-embedding.js +264 -0
- package/dist/memory-handlers.d.ts +19 -0
- package/dist/memory-handlers.d.ts.map +1 -0
- package/dist/memory-handlers.js +1204 -0
- package/dist/memory-maintenance-handlers.d.ts +35 -0
- package/dist/memory-maintenance-handlers.d.ts.map +1 -0
- package/dist/memory-maintenance-handlers.js +219 -0
- package/dist/memory-record-builders.d.ts +4 -0
- package/dist/memory-record-builders.d.ts.map +1 -0
- package/dist/memory-record-builders.js +19 -0
- package/dist/memory-retention.d.ts +31 -0
- package/dist/memory-retention.d.ts.map +1 -0
- package/dist/memory-retention.js +151 -0
- package/dist/memory-retrieval-signals.d.ts +12 -0
- package/dist/memory-retrieval-signals.d.ts.map +1 -0
- package/dist/memory-retrieval-signals.js +100 -0
- package/dist/memory-salience.d.ts +12 -0
- package/dist/memory-salience.d.ts.map +1 -0
- package/dist/memory-salience.js +154 -0
- package/dist/memory-scope-sanitizer.d.ts +6 -0
- package/dist/memory-scope-sanitizer.d.ts.map +1 -0
- package/dist/memory-scope-sanitizer.js +106 -0
- package/dist/memory-target-resolver.d.ts +4 -0
- package/dist/memory-target-resolver.d.ts.map +1 -0
- package/dist/memory-target-resolver.js +73 -0
- package/dist/memory-workflow-port.d.ts +14 -0
- package/dist/memory-workflow-port.d.ts.map +1 -0
- package/dist/memory-workflow-port.js +186 -0
- package/dist/private-json.d.ts +3 -0
- package/dist/private-json.d.ts.map +1 -0
- package/dist/private-json.js +62 -0
- package/dist/promptEnhancer/index.d.ts +3 -0
- package/dist/promptEnhancer/index.d.ts.map +1 -0
- package/dist/promptEnhancer/index.js +5 -0
- package/dist/promptEnhancer/orchestrate.d.ts +2 -0
- package/dist/promptEnhancer/orchestrate.d.ts.map +1 -0
- package/dist/promptEnhancer/orchestrate.js +5 -0
- package/dist/promptEnhancer/routes.d.ts +9 -0
- package/dist/promptEnhancer/routes.d.ts.map +1 -0
- package/dist/promptEnhancer/routes.js +205 -0
- package/dist/qualityIntelligence/capsuleAdapter.d.ts +27 -0
- package/dist/qualityIntelligence/capsuleAdapter.d.ts.map +1 -0
- package/dist/qualityIntelligence/capsuleAdapter.js +57 -0
- package/dist/qualityIntelligence/connectorAuthorization.d.ts +22 -0
- package/dist/qualityIntelligence/connectorAuthorization.d.ts.map +1 -0
- package/dist/qualityIntelligence/connectorAuthorization.js +35 -0
- package/dist/qualityIntelligence/connectorErrors.d.ts +16 -0
- package/dist/qualityIntelligence/connectorErrors.d.ts.map +1 -0
- package/dist/qualityIntelligence/connectorErrors.js +56 -0
- package/dist/qualityIntelligence/connectorRoutes.d.ts +7 -0
- package/dist/qualityIntelligence/connectorRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/connectorRoutes.js +167 -0
- package/dist/qualityIntelligence/editRoutes.d.ts +5 -0
- package/dist/qualityIntelligence/editRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/editRoutes.js +293 -0
- package/dist/qualityIntelligence/exportAssembly.d.ts +22 -0
- package/dist/qualityIntelligence/exportAssembly.d.ts.map +1 -0
- package/dist/qualityIntelligence/exportAssembly.js +352 -0
- package/dist/qualityIntelligence/exportRoutes.d.ts +5 -0
- package/dist/qualityIntelligence/exportRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/exportRoutes.js +320 -0
- package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts +8 -0
- package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConcurrency.js +34 -0
- package/dist/qualityIntelligence/figma/figmaConnector.d.ts +65 -0
- package/dist/qualityIntelligence/figma/figmaConnector.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConnector.js +184 -0
- package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts +52 -0
- package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConnectorAudit.js +63 -0
- package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts +31 -0
- package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConnectorErrors.js +220 -0
- package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts +44 -0
- package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConnectorMetrics.js +49 -0
- package/dist/qualityIntelligence/figma/figmaConsent.d.ts +39 -0
- package/dist/qualityIntelligence/figma/figmaConsent.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaConsent.js +62 -0
- package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts +28 -0
- package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaHttpPort.js +70 -0
- package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts +49 -0
- package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaObservedActions.js +89 -0
- package/dist/qualityIntelligence/figma/figmaReadiness.d.ts +32 -0
- package/dist/qualityIntelligence/figma/figmaReadiness.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaReadiness.js +67 -0
- package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts +29 -0
- package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaRenderPort.js +93 -0
- package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts +28 -0
- package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaResnapshot.js +38 -0
- package/dist/qualityIntelligence/figma/figmaRetry.d.ts +31 -0
- package/dist/qualityIntelligence/figma/figmaRetry.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaRetry.js +62 -0
- package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts +9 -0
- package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaScopeRef.js +18 -0
- package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts +86 -0
- package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaScopedPagination.js +308 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts +31 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.js +314 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts +18 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotHash.js +63 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts +65 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaSnapshotTypes.js +13 -0
- package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts +9 -0
- package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaTokenSource.js +61 -0
- package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts +19 -0
- package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaTokenStore.js +156 -0
- package/dist/qualityIntelligence/figma/figmaUrl.d.ts +6 -0
- package/dist/qualityIntelligence/figma/figmaUrl.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/figmaUrl.js +36 -0
- package/dist/qualityIntelligence/figma/index.d.ts +20 -0
- package/dist/qualityIntelligence/figma/index.d.ts.map +1 -0
- package/dist/qualityIntelligence/figma/index.js +26 -0
- package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts +28 -0
- package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/figmaCodegenRoutes.js +165 -0
- package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts +55 -0
- package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts.map +1 -0
- package/dist/qualityIntelligence/figmaSnapshotAdapter.js +219 -0
- package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts +64 -0
- package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts.map +1 -0
- package/dist/qualityIntelligence/figmaSnapshotOrchestration.js +203 -0
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +112 -0
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1063 -0
- package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts +19 -0
- package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts.map +1 -0
- package/dist/qualityIntelligence/figmaSnapshotScreenIds.js +75 -0
- package/dist/qualityIntelligence/generationPort.d.ts +15 -0
- package/dist/qualityIntelligence/generationPort.d.ts.map +1 -0
- package/dist/qualityIntelligence/generationPort.js +185 -0
- package/dist/qualityIntelligence/handoffErrors.d.ts +9 -0
- package/dist/qualityIntelligence/handoffErrors.d.ts.map +1 -0
- package/dist/qualityIntelligence/handoffErrors.js +21 -0
- package/dist/qualityIntelligence/handoffRoutes.d.ts +15 -0
- package/dist/qualityIntelligence/handoffRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/handoffRoutes.js +341 -0
- package/dist/qualityIntelligence/index.d.ts +17 -0
- package/dist/qualityIntelligence/index.d.ts.map +1 -0
- package/dist/qualityIntelligence/index.js +36 -0
- package/dist/qualityIntelligence/judgePort.d.ts +30 -0
- package/dist/qualityIntelligence/judgePort.d.ts.map +1 -0
- package/dist/qualityIntelligence/judgePort.js +326 -0
- package/dist/qualityIntelligence/modelSelection.d.ts +58 -0
- package/dist/qualityIntelligence/modelSelection.d.ts.map +1 -0
- package/dist/qualityIntelligence/modelSelection.js +148 -0
- package/dist/qualityIntelligence/reCheckRoutes.d.ts +6 -0
- package/dist/qualityIntelligence/reCheckRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/reCheckRoutes.js +1157 -0
- package/dist/qualityIntelligence/retentionEnforcement.d.ts +13 -0
- package/dist/qualityIntelligence/retentionEnforcement.d.ts.map +1 -0
- package/dist/qualityIntelligence/retentionEnforcement.js +47 -0
- package/dist/qualityIntelligence/retentionRoutes.d.ts +8 -0
- package/dist/qualityIntelligence/retentionRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/retentionRoutes.js +74 -0
- package/dist/qualityIntelligence/reviewRoutes.d.ts +5 -0
- package/dist/qualityIntelligence/reviewRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/reviewRoutes.js +145 -0
- package/dist/qualityIntelligence/reviewStore.d.ts +75 -0
- package/dist/qualityIntelligence/reviewStore.d.ts.map +1 -0
- package/dist/qualityIntelligence/reviewStore.js +170 -0
- package/dist/qualityIntelligence/runExecution.d.ts +36 -0
- package/dist/qualityIntelligence/runExecution.d.ts.map +1 -0
- package/dist/qualityIntelligence/runExecution.js +180 -0
- package/dist/qualityIntelligence/runIngestion.d.ts +70 -0
- package/dist/qualityIntelligence/runIngestion.d.ts.map +1 -0
- package/dist/qualityIntelligence/runIngestion.js +1235 -0
- package/dist/qualityIntelligence/runRegistry.d.ts +31 -0
- package/dist/qualityIntelligence/runRegistry.d.ts.map +1 -0
- package/dist/qualityIntelligence/runRegistry.js +66 -0
- package/dist/qualityIntelligence/runRoutes.d.ts +16 -0
- package/dist/qualityIntelligence/runRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/runRoutes.js +357 -0
- package/dist/qualityIntelligence/traceabilityRoutes.d.ts +5 -0
- package/dist/qualityIntelligence/traceabilityRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/traceabilityRoutes.js +173 -0
- package/dist/qualityIntelligence/uiRoutes.d.ts +7 -0
- package/dist/qualityIntelligence/uiRoutes.d.ts.map +1 -0
- package/dist/qualityIntelligence/uiRoutes.js +336 -0
- package/dist/read-handlers.d.ts +9 -0
- package/dist/read-handlers.d.ts.map +1 -0
- package/dist/read-handlers.js +265 -0
- package/dist/relationship-handlers.d.ts +191 -0
- package/dist/relationship-handlers.d.ts.map +1 -0
- package/dist/relationship-handlers.js +0 -0
- package/dist/routes.d.ts +37 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +507 -0
- package/dist/run-engine.d.ts +25 -0
- package/dist/run-engine.d.ts.map +1 -0
- package/dist/run-engine.js +385 -0
- package/dist/run-handlers.d.ts +9 -0
- package/dist/run-handlers.d.ts.map +1 -0
- package/dist/run-handlers.js +465 -0
- package/dist/run-request.d.ts +17 -0
- package/dist/run-request.d.ts.map +1 -0
- package/dist/run-request.js +219 -0
- package/dist/runs.d.ts +47 -0
- package/dist/runs.d.ts.map +1 -0
- package/dist/runs.js +100 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +152 -0
- package/dist/sink.d.ts +28 -0
- package/dist/sink.d.ts.map +1 -0
- package/dist/sink.js +80 -0
- package/dist/sse-write.d.ts +9 -0
- package/dist/sse-write.d.ts.map +1 -0
- package/dist/sse-write.js +26 -0
- package/dist/sse.d.ts +8 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +27 -0
- package/dist/static.d.ts +5 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/static.js +76 -0
- package/dist/store/chats.d.ts +17 -0
- package/dist/store/chats.d.ts.map +1 -0
- package/dist/store/chats.js +624 -0
- package/dist/store/db.d.ts +11 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +203 -0
- package/dist/store/errors.d.ts +13 -0
- package/dist/store/errors.d.ts.map +1 -0
- package/dist/store/errors.js +30 -0
- package/dist/store/index.d.ts +7 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +6 -0
- package/dist/store/messages.d.ts +8 -0
- package/dist/store/messages.d.ts.map +1 -0
- package/dist/store/messages.js +149 -0
- package/dist/store/paths.d.ts +5 -0
- package/dist/store/paths.d.ts.map +1 -0
- package/dist/store/paths.js +84 -0
- package/dist/store/projects.d.ts +8 -0
- package/dist/store/projects.d.ts.map +1 -0
- package/dist/store/projects.js +59 -0
- package/dist/store/relationship-audit.d.ts +42 -0
- package/dist/store/relationship-audit.d.ts.map +1 -0
- package/dist/store/relationship-audit.js +155 -0
- package/dist/store/relationships.d.ts +191 -0
- package/dist/store/relationships.d.ts.map +1 -0
- package/dist/store/relationships.js +724 -0
- package/dist/store/schema.d.ts +4 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +220 -0
- package/dist/store/types.d.ts +29 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +8 -0
- package/dist/store/validation.d.ts +7 -0
- package/dist/store/validation.d.ts.map +1 -0
- package/dist/store/validation.js +117 -0
- package/dist/store-handlers.d.ts +17 -0
- package/dist/store-handlers.d.ts.map +1 -0
- package/dist/store-handlers.js +872 -0
- package/dist/terminal-errors.d.ts +22 -0
- package/dist/terminal-errors.d.ts.map +1 -0
- package/dist/terminal-errors.js +45 -0
- package/dist/terminal-evidence.d.ts +21 -0
- package/dist/terminal-evidence.d.ts.map +1 -0
- package/dist/terminal-evidence.js +65 -0
- package/dist/terminal-routes.d.ts +10 -0
- package/dist/terminal-routes.d.ts.map +1 -0
- package/dist/terminal-routes.js +219 -0
- package/dist/terminal.d.ts +68 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +855 -0
- package/package.json +52 -0
package/dist/files.js
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
// Read-only filesystem browser for the desktop Files widget. The browser receives
|
|
2
|
+
// preview or editor content; every request is contained inside a selected root after
|
|
3
|
+
// realpath resolution.
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { lstat, opendir, open, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { basename, dirname, extname, isAbsolute, join, parse as parsePath, posix as pathPosix, relative, resolve, } from "node:path";
|
|
7
|
+
import { redact } from "@oscharko-dev/keiko-security";
|
|
8
|
+
import { EDITOR_SESSION_SCHEMA_VERSION, parseEditorDocumentVersion, } from "@oscharko-dev/keiko-contracts";
|
|
9
|
+
import { DENIED_MESSAGE, pathIsDenied } from "./files-deny.js";
|
|
10
|
+
import { errorBody } from "./routes.js";
|
|
11
|
+
const MAX_DIRECTORY_ENTRIES = 1_000;
|
|
12
|
+
const MAX_TEXT_PREVIEW_BYTES = 1_000_000;
|
|
13
|
+
const MAX_IMAGE_PREVIEW_BYTES = 3_000_000;
|
|
14
|
+
const STABLE_CONTENT_READ_ATTEMPTS = 3;
|
|
15
|
+
const staticFilesMetadataRedactor = (value) => typeof value === "string" ? redact(value) : value;
|
|
16
|
+
class BodyTooLargeError extends Error {
|
|
17
|
+
constructor() {
|
|
18
|
+
super("request body too large");
|
|
19
|
+
this.name = "BodyTooLargeError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Exported for reuse by the editor language-service route (#1198) so denied/invalid roots map to the
|
|
23
|
+
// same status + content-free code envelope as the files routes.
|
|
24
|
+
export class FilesError extends Error {
|
|
25
|
+
status;
|
|
26
|
+
code;
|
|
27
|
+
constructor(status, code, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.name = "FilesError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function filesErrorResult(error) {
|
|
35
|
+
return { status: error.status, body: errorBody(error.code, error.message) };
|
|
36
|
+
}
|
|
37
|
+
export async function runFilesHandler(work) {
|
|
38
|
+
try {
|
|
39
|
+
return await work();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof FilesError)
|
|
43
|
+
return filesErrorResult(error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function isRecord(value) {
|
|
48
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
function isRouteResult(value) {
|
|
51
|
+
return isRecord(value) && typeof value.status === "number" && "body" in value;
|
|
52
|
+
}
|
|
53
|
+
async function resolveDirectory(candidate) {
|
|
54
|
+
let resolved;
|
|
55
|
+
try {
|
|
56
|
+
resolved = await realpath(candidate);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new FilesError(400, "INVALID_DIRECTORY", "The directory does not exist.");
|
|
60
|
+
}
|
|
61
|
+
const info = await stat(resolved);
|
|
62
|
+
if (!info.isDirectory()) {
|
|
63
|
+
throw new FilesError(400, "INVALID_DIRECTORY", "The selected path must be a directory.");
|
|
64
|
+
}
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
function projectFor(store, projectId) {
|
|
68
|
+
return store.listProjects().find((project) => project.path === projectId);
|
|
69
|
+
}
|
|
70
|
+
function rootPathIsDenied(rootPath) {
|
|
71
|
+
return pathIsDenied(rootPath);
|
|
72
|
+
}
|
|
73
|
+
function assertMetadataSafe(value, redactor) {
|
|
74
|
+
const redacted = redactor(value);
|
|
75
|
+
if (typeof redacted === "string" && redacted !== value) {
|
|
76
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function metadataIsSafe(value, redactor) {
|
|
80
|
+
const redacted = redactor(value);
|
|
81
|
+
return typeof redacted !== "string" || redacted === value;
|
|
82
|
+
}
|
|
83
|
+
async function resolveRegisteredRoot(project, redactor) {
|
|
84
|
+
assertMetadataSafe(project.path, redactor);
|
|
85
|
+
if (rootPathIsDenied(project.path)) {
|
|
86
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
87
|
+
}
|
|
88
|
+
const realRoot = await resolveDirectory(project.path);
|
|
89
|
+
assertMetadataSafe(realRoot, redactor);
|
|
90
|
+
if (rootPathIsDenied(realRoot)) {
|
|
91
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
92
|
+
}
|
|
93
|
+
return { root: project.path, realRoot };
|
|
94
|
+
}
|
|
95
|
+
// Epic #532 — Keiko is a workspace for EVERYONE, not only devs: a Files window may browse ANY folder
|
|
96
|
+
// on the machine, not just a registered project. An arbitrary input becomes its own root, gated by
|
|
97
|
+
// the SAME realpath + FULL-PATH deny-list the grounded connect path enforces (deny matches on every
|
|
98
|
+
// segment, so a root inside `.ssh`/`.aws`/credential dirs is rejected even when its basename is
|
|
99
|
+
// innocuous). Browse and connect therefore accept the identical set of roots. The deny check runs
|
|
100
|
+
// once on the raw input and again on the realpath, so a symlink whose target lands in a denied
|
|
101
|
+
// location is caught after resolution.
|
|
102
|
+
async function resolveArbitraryRoot(rootInput, redactor) {
|
|
103
|
+
if (!isAbsolute(rootInput)) {
|
|
104
|
+
throw new FilesError(400, "BAD_ROOT", "The root must be an absolute directory path.");
|
|
105
|
+
}
|
|
106
|
+
assertMetadataSafe(rootInput, redactor);
|
|
107
|
+
if (pathIsDenied(rootInput)) {
|
|
108
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
109
|
+
}
|
|
110
|
+
const realRoot = await resolveDirectory(rootInput);
|
|
111
|
+
assertMetadataSafe(realRoot, redactor);
|
|
112
|
+
if (pathIsDenied(realRoot)) {
|
|
113
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
114
|
+
}
|
|
115
|
+
return { root: rootInput, realRoot };
|
|
116
|
+
}
|
|
117
|
+
export async function resolveRoot(store, rootInput, redactor) {
|
|
118
|
+
if (rootInput === null || rootInput.trim().length === 0) {
|
|
119
|
+
throw new FilesError(400, "BAD_REQUEST", "The root query parameter is required.");
|
|
120
|
+
}
|
|
121
|
+
const project = projectFor(store, rootInput);
|
|
122
|
+
return project === undefined
|
|
123
|
+
? resolveArbitraryRoot(rootInput.trim(), redactor)
|
|
124
|
+
: resolveRegisteredRoot(project, redactor);
|
|
125
|
+
}
|
|
126
|
+
function directoryRoots(projectRoot) {
|
|
127
|
+
return [{ label: "Project root", path: projectRoot }];
|
|
128
|
+
}
|
|
129
|
+
function parentPath(pathValue, projectRoot) {
|
|
130
|
+
if (pathValue === projectRoot)
|
|
131
|
+
return null;
|
|
132
|
+
const parsed = parsePath(pathValue);
|
|
133
|
+
return pathValue === parsed.root ? null : dirname(pathValue);
|
|
134
|
+
}
|
|
135
|
+
function normalizeRelativePath(pathInput) {
|
|
136
|
+
const raw = pathInput ?? "";
|
|
137
|
+
if (raw.includes("\0") || isAbsolute(raw)) {
|
|
138
|
+
throw new FilesError(400, "BAD_PATH", "The path must be relative to the selected root.");
|
|
139
|
+
}
|
|
140
|
+
const normalized = pathPosix.normalize(raw.replaceAll("\\", "/"));
|
|
141
|
+
if (normalized === ".")
|
|
142
|
+
return "";
|
|
143
|
+
if (normalized === ".." || normalized.startsWith("../")) {
|
|
144
|
+
throw new FilesError(400, "PATH_ESCAPE", "The requested path is outside the selected root.");
|
|
145
|
+
}
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
148
|
+
function nativePath(root, relativePath) {
|
|
149
|
+
if (relativePath.length === 0)
|
|
150
|
+
return root;
|
|
151
|
+
return resolve(root, ...relativePath.split("/").filter((part) => part.length > 0));
|
|
152
|
+
}
|
|
153
|
+
function isContained(root, target) {
|
|
154
|
+
const rootCmp = process.platform === "win32" ? root.toLowerCase() : root;
|
|
155
|
+
const targetCmp = process.platform === "win32" ? target.toLowerCase() : target;
|
|
156
|
+
const rel = relative(rootCmp, targetCmp);
|
|
157
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
158
|
+
}
|
|
159
|
+
function rootRelativePosixPath(root, target) {
|
|
160
|
+
const rel = relative(root, target);
|
|
161
|
+
return rel.replaceAll("\\", "/");
|
|
162
|
+
}
|
|
163
|
+
function normalizeDirectoryPath(pathInput, registeredRoot, realRoot) {
|
|
164
|
+
const raw = pathInput?.trim();
|
|
165
|
+
if (raw === undefined || raw.length === 0)
|
|
166
|
+
return realRoot;
|
|
167
|
+
if (raw.includes("\0")) {
|
|
168
|
+
throw new FilesError(400, "BAD_PATH", "The path must stay inside the selected project.");
|
|
169
|
+
}
|
|
170
|
+
const candidate = isAbsolute(raw) ? resolve(raw) : resolve(realRoot, raw);
|
|
171
|
+
if (!isContained(realRoot, candidate) && !isContained(registeredRoot, candidate)) {
|
|
172
|
+
throw new FilesError(403, "PATH_ESCAPE", "The requested path is outside the selected project.");
|
|
173
|
+
}
|
|
174
|
+
return candidate;
|
|
175
|
+
}
|
|
176
|
+
async function resolveDirectoryInsideRoot(store, rootInput, pathInput, redactor) {
|
|
177
|
+
const root = await resolveRoot(store, rootInput, redactor);
|
|
178
|
+
const candidate = normalizeDirectoryPath(pathInput, root.root, root.realRoot);
|
|
179
|
+
const pathValue = await resolveDirectory(candidate);
|
|
180
|
+
assertMetadataSafe(pathValue, redactor);
|
|
181
|
+
if (!isContained(root.realRoot, pathValue)) {
|
|
182
|
+
throw new FilesError(403, "PATH_ESCAPE", "The requested path is outside the selected project.");
|
|
183
|
+
}
|
|
184
|
+
const relativePath = rootRelativePosixPath(root.realRoot, pathValue);
|
|
185
|
+
assertMetadataSafe(relativePath, redactor);
|
|
186
|
+
if (pathIsDenied(relativePath)) {
|
|
187
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
188
|
+
}
|
|
189
|
+
return { ...root, path: pathValue, relativePath };
|
|
190
|
+
}
|
|
191
|
+
async function resolveInsideRoot(store, rootInput, pathInput, redactor) {
|
|
192
|
+
const root = await resolveRoot(store, rootInput, redactor);
|
|
193
|
+
const relativePath = normalizeRelativePath(pathInput);
|
|
194
|
+
assertMetadataSafe(relativePath, redactor);
|
|
195
|
+
// Deny check runs BEFORE realpath so existence of a denied path is not
|
|
196
|
+
// observable via the 403/404 status-code difference. A non-existent denied
|
|
197
|
+
// path returns 403, identical to an existing denied path.
|
|
198
|
+
if (pathIsDenied(relativePath)) {
|
|
199
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
200
|
+
}
|
|
201
|
+
const candidate = nativePath(root.realRoot, relativePath);
|
|
202
|
+
let target;
|
|
203
|
+
try {
|
|
204
|
+
target = await realpath(candidate);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
throw new FilesError(404, "NOT_FOUND", "The requested path was not found.");
|
|
208
|
+
}
|
|
209
|
+
if (!isContained(root.realRoot, target)) {
|
|
210
|
+
throw new FilesError(403, "PATH_ESCAPE", "The requested path is outside the selected root.");
|
|
211
|
+
}
|
|
212
|
+
const targetRelativePath = rootRelativePosixPath(root.realRoot, target);
|
|
213
|
+
assertMetadataSafe(targetRelativePath, redactor);
|
|
214
|
+
if (pathIsDenied(targetRelativePath)) {
|
|
215
|
+
throw new FilesError(403, "DENIED", DENIED_MESSAGE);
|
|
216
|
+
}
|
|
217
|
+
const linkStats = await lstat(candidate);
|
|
218
|
+
const targetStats = await stat(target);
|
|
219
|
+
return {
|
|
220
|
+
root: root.root,
|
|
221
|
+
realRoot: root.realRoot,
|
|
222
|
+
relativePath,
|
|
223
|
+
path: target,
|
|
224
|
+
stats: targetStats,
|
|
225
|
+
symlink: linkStats.isSymbolicLink(),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function directoryEntries(root, pathValue, redactor) {
|
|
229
|
+
const entries = [];
|
|
230
|
+
const dir = await opendir(pathValue);
|
|
231
|
+
try {
|
|
232
|
+
for await (const entry of dir) {
|
|
233
|
+
if (!entry.isDirectory())
|
|
234
|
+
continue;
|
|
235
|
+
const entryPath = join(pathValue, entry.name);
|
|
236
|
+
const relativePath = rootRelativePosixPath(root, entryPath);
|
|
237
|
+
if (!metadataIsSafe(relativePath, redactor))
|
|
238
|
+
continue;
|
|
239
|
+
if (pathIsDenied(relativePath))
|
|
240
|
+
continue;
|
|
241
|
+
entries.push({ name: entry.name, path: entryPath });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await dir.close().catch(() => undefined);
|
|
246
|
+
}
|
|
247
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
248
|
+
}
|
|
249
|
+
export async function listFilesDirectories(store, rootInput, pathInput, redactor = staticFilesMetadataRedactor) {
|
|
250
|
+
const target = await resolveDirectoryInsideRoot(store, rootInput, pathInput, redactor);
|
|
251
|
+
return {
|
|
252
|
+
path: target.path,
|
|
253
|
+
parent: parentPath(target.path, target.realRoot),
|
|
254
|
+
entries: await directoryEntries(target.realRoot, target.path, redactor),
|
|
255
|
+
roots: directoryRoots(target.root),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function extensionOf(name) {
|
|
259
|
+
const lower = name.toLowerCase();
|
|
260
|
+
if (lower === "dockerfile")
|
|
261
|
+
return "dockerfile";
|
|
262
|
+
if (lower === ".env" || lower.startsWith(".env."))
|
|
263
|
+
return "env";
|
|
264
|
+
const ext = extname(lower).replace(/^\./u, "");
|
|
265
|
+
return ext.length > 0 ? ext : null;
|
|
266
|
+
}
|
|
267
|
+
async function classifyEntry(root, parentRelativePath, parentNativePath, entry, redactor) {
|
|
268
|
+
const childRelativePath = parentRelativePath.length === 0 ? entry.name : `${parentRelativePath}/${entry.name}`;
|
|
269
|
+
assertMetadataSafe(childRelativePath, redactor);
|
|
270
|
+
const entryPath = join(parentNativePath, entry.name);
|
|
271
|
+
const linkStats = await lstat(entryPath);
|
|
272
|
+
const symlink = linkStats.isSymbolicLink();
|
|
273
|
+
const base = {
|
|
274
|
+
name: entry.name,
|
|
275
|
+
path: childRelativePath,
|
|
276
|
+
sizeBytes: linkStats.size,
|
|
277
|
+
modifiedAt: linkStats.mtimeMs,
|
|
278
|
+
extension: extensionOf(entry.name),
|
|
279
|
+
symlink,
|
|
280
|
+
};
|
|
281
|
+
if (!symlink) {
|
|
282
|
+
const kind = linkStats.isDirectory() ? "directory" : "file";
|
|
283
|
+
return { ...base, kind, readable: true };
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const target = await realpath(entryPath);
|
|
287
|
+
const targetStats = await stat(target);
|
|
288
|
+
const contained = isContained(root, target);
|
|
289
|
+
const denied = contained && pathIsDenied(rootRelativePosixPath(root, target));
|
|
290
|
+
const kind = targetStats.isDirectory()
|
|
291
|
+
? "directory"
|
|
292
|
+
: targetStats.isFile()
|
|
293
|
+
? "file"
|
|
294
|
+
: "symlink";
|
|
295
|
+
return { ...base, kind, readable: contained && !denied };
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return { ...base, kind: "symlink", readable: false };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function entryRank(entry) {
|
|
302
|
+
if (entry.kind === "directory")
|
|
303
|
+
return 0;
|
|
304
|
+
if (entry.kind === "file")
|
|
305
|
+
return 1;
|
|
306
|
+
return 2;
|
|
307
|
+
}
|
|
308
|
+
function childRelative(parentRelativePath, name) {
|
|
309
|
+
return parentRelativePath.length === 0 ? name : `${parentRelativePath}/${name}`;
|
|
310
|
+
}
|
|
311
|
+
function skipEntry(rel) {
|
|
312
|
+
return pathIsDenied(rel);
|
|
313
|
+
}
|
|
314
|
+
async function listTreeEntries(root, relativePath, pathValue, redactor) {
|
|
315
|
+
const entries = [];
|
|
316
|
+
const dir = await opendir(pathValue);
|
|
317
|
+
let truncated = false;
|
|
318
|
+
try {
|
|
319
|
+
for await (const entry of dir) {
|
|
320
|
+
// Deny filtering happens BEFORE the truncation counter so a directory packed with denied
|
|
321
|
+
// entries (e.g. node_modules/**) cannot exhaust the 1000-entry budget and hide real files
|
|
322
|
+
// behind `truncated: true`. .gitignore is intentionally not a Files visibility filter:
|
|
323
|
+
// safe dotfiles and generated files must remain visible and connectable.
|
|
324
|
+
const rel = childRelative(relativePath, entry.name);
|
|
325
|
+
if (!metadataIsSafe(rel, redactor))
|
|
326
|
+
continue;
|
|
327
|
+
if (skipEntry(rel))
|
|
328
|
+
continue;
|
|
329
|
+
if (entries.length >= MAX_DIRECTORY_ENTRIES) {
|
|
330
|
+
truncated = true;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
entries.push(await classifyEntry(root, relativePath, pathValue, entry, redactor));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
await dir.close().catch(() => undefined);
|
|
338
|
+
}
|
|
339
|
+
entries.sort((a, b) => entryRank(a) - entryRank(b) || a.name.localeCompare(b.name));
|
|
340
|
+
return { entries, truncated };
|
|
341
|
+
}
|
|
342
|
+
export async function readFilesTree(store, rootInput, pathInput, redactor = staticFilesMetadataRedactor) {
|
|
343
|
+
const target = await resolveInsideRoot(store, rootInput, pathInput, redactor);
|
|
344
|
+
if (!target.stats.isDirectory()) {
|
|
345
|
+
throw new FilesError(400, "NOT_DIRECTORY", "The requested path is not a directory.");
|
|
346
|
+
}
|
|
347
|
+
const listed = await listTreeEntries(target.realRoot, target.relativePath, target.path, redactor);
|
|
348
|
+
return {
|
|
349
|
+
root: target.root,
|
|
350
|
+
path: target.relativePath,
|
|
351
|
+
entries: listed.entries,
|
|
352
|
+
truncated: listed.truncated,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
const IMAGE_MIME = {
|
|
356
|
+
png: "image/png",
|
|
357
|
+
jpg: "image/jpeg",
|
|
358
|
+
jpeg: "image/jpeg",
|
|
359
|
+
gif: "image/gif",
|
|
360
|
+
webp: "image/webp",
|
|
361
|
+
svg: "image/svg+xml",
|
|
362
|
+
};
|
|
363
|
+
const TEXT_EXTENSIONS = new Set([
|
|
364
|
+
"bash",
|
|
365
|
+
"c",
|
|
366
|
+
"cjs",
|
|
367
|
+
"css",
|
|
368
|
+
"csv",
|
|
369
|
+
"dockerfile",
|
|
370
|
+
"env",
|
|
371
|
+
"go",
|
|
372
|
+
"graphql",
|
|
373
|
+
"gql",
|
|
374
|
+
"gradle",
|
|
375
|
+
"html",
|
|
376
|
+
"java",
|
|
377
|
+
"js",
|
|
378
|
+
"json",
|
|
379
|
+
"jsx",
|
|
380
|
+
"kt",
|
|
381
|
+
"kts",
|
|
382
|
+
"md",
|
|
383
|
+
"mjs",
|
|
384
|
+
"properties",
|
|
385
|
+
"py",
|
|
386
|
+
"rs",
|
|
387
|
+
"sh",
|
|
388
|
+
"sql",
|
|
389
|
+
"toml",
|
|
390
|
+
"ts",
|
|
391
|
+
"tsx",
|
|
392
|
+
"txt",
|
|
393
|
+
"xml",
|
|
394
|
+
"yaml",
|
|
395
|
+
"yml",
|
|
396
|
+
]);
|
|
397
|
+
function mimeOf(extension) {
|
|
398
|
+
if (extension !== null && IMAGE_MIME[extension] !== undefined)
|
|
399
|
+
return IMAGE_MIME[extension];
|
|
400
|
+
if (extension === "json")
|
|
401
|
+
return "application/json";
|
|
402
|
+
if (extension === "md")
|
|
403
|
+
return "text/markdown";
|
|
404
|
+
if (extension === "html")
|
|
405
|
+
return "text/html";
|
|
406
|
+
if (extension === "css")
|
|
407
|
+
return "text/css";
|
|
408
|
+
if (isKnownTextExtension(extension))
|
|
409
|
+
return "text/plain";
|
|
410
|
+
return "application/octet-stream";
|
|
411
|
+
}
|
|
412
|
+
function isImageExtension(extension) {
|
|
413
|
+
return extension !== null && IMAGE_MIME[extension] !== undefined;
|
|
414
|
+
}
|
|
415
|
+
function isKnownTextExtension(extension) {
|
|
416
|
+
return extension !== null && TEXT_EXTENSIONS.has(extension);
|
|
417
|
+
}
|
|
418
|
+
function isLikelyUtf8Text(buffer) {
|
|
419
|
+
if (buffer.length === 0)
|
|
420
|
+
return true;
|
|
421
|
+
if (buffer.includes(0))
|
|
422
|
+
return false;
|
|
423
|
+
const decoded = buffer.toString("utf8");
|
|
424
|
+
if (decoded.includes("\uFFFD"))
|
|
425
|
+
return false;
|
|
426
|
+
let printable = 0;
|
|
427
|
+
for (const char of decoded) {
|
|
428
|
+
const code = char.charCodeAt(0);
|
|
429
|
+
if (code === 9 || code === 10 || code === 13 || code >= 32)
|
|
430
|
+
printable += 1;
|
|
431
|
+
}
|
|
432
|
+
return printable / decoded.length > 0.85;
|
|
433
|
+
}
|
|
434
|
+
async function readPrefix(pathValue, maxBytes) {
|
|
435
|
+
const file = await open(pathValue, "r");
|
|
436
|
+
try {
|
|
437
|
+
const buffer = Buffer.allocUnsafe(maxBytes + 1);
|
|
438
|
+
const result = await file.read(buffer, 0, maxBytes + 1, 0);
|
|
439
|
+
return {
|
|
440
|
+
buffer: buffer.subarray(0, Math.min(result.bytesRead, maxBytes)),
|
|
441
|
+
truncated: result.bytesRead > maxBytes,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
finally {
|
|
445
|
+
await file.close();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function readBody(req, maxBytes) {
|
|
449
|
+
return new Promise((resolveBody, reject) => {
|
|
450
|
+
const chunks = [];
|
|
451
|
+
let total = 0;
|
|
452
|
+
let capped = false;
|
|
453
|
+
req.on("data", (chunk) => {
|
|
454
|
+
total += chunk.length;
|
|
455
|
+
if (total > maxBytes) {
|
|
456
|
+
if (!capped) {
|
|
457
|
+
capped = true;
|
|
458
|
+
chunks.length = 0;
|
|
459
|
+
reject(new BodyTooLargeError());
|
|
460
|
+
req.resume();
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
chunks.push(chunk);
|
|
465
|
+
});
|
|
466
|
+
req.on("end", () => {
|
|
467
|
+
if (!capped)
|
|
468
|
+
resolveBody(Buffer.concat(chunks).toString("utf8"));
|
|
469
|
+
});
|
|
470
|
+
req.on("error", reject);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
export async function readJsonObject(req, maxBytes) {
|
|
474
|
+
let raw;
|
|
475
|
+
try {
|
|
476
|
+
raw = await readBody(req, maxBytes);
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
if (error instanceof BodyTooLargeError) {
|
|
480
|
+
return {
|
|
481
|
+
status: 413,
|
|
482
|
+
body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
let parsed;
|
|
488
|
+
try {
|
|
489
|
+
parsed = raw.length === 0 ? {} : JSON.parse(raw);
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "Request body is not valid JSON.") };
|
|
493
|
+
}
|
|
494
|
+
if (!isRecord(parsed)) {
|
|
495
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "Request body must be a JSON object.") };
|
|
496
|
+
}
|
|
497
|
+
return parsed;
|
|
498
|
+
}
|
|
499
|
+
function basePreview(target) {
|
|
500
|
+
const name = basename(target.relativePath);
|
|
501
|
+
const extension = extensionOf(name);
|
|
502
|
+
return {
|
|
503
|
+
root: target.root,
|
|
504
|
+
path: target.relativePath,
|
|
505
|
+
name,
|
|
506
|
+
sizeBytes: target.stats.size,
|
|
507
|
+
modifiedAt: target.stats.mtimeMs,
|
|
508
|
+
extension,
|
|
509
|
+
mime: mimeOf(extension),
|
|
510
|
+
symlink: target.symlink,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
async function imagePreview(target, base) {
|
|
514
|
+
if (target.stats.size > MAX_IMAGE_PREVIEW_BYTES) {
|
|
515
|
+
return { ...base, kind: "binary", reason: "too_large", maxBytes: MAX_IMAGE_PREVIEW_BYTES };
|
|
516
|
+
}
|
|
517
|
+
const buffer = await readFile(target.path);
|
|
518
|
+
return {
|
|
519
|
+
...base,
|
|
520
|
+
kind: "image",
|
|
521
|
+
dataUrl: `data:${base.mime};base64,${buffer.toString("base64")}`,
|
|
522
|
+
maxBytes: MAX_IMAGE_PREVIEW_BYTES,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
async function textPreview(target, base, redactor) {
|
|
526
|
+
const prefix = await readPrefix(target.path, MAX_TEXT_PREVIEW_BYTES);
|
|
527
|
+
const content = prefix.buffer.toString("utf8");
|
|
528
|
+
const redacted = redactor(content);
|
|
529
|
+
return {
|
|
530
|
+
...base,
|
|
531
|
+
kind: "text",
|
|
532
|
+
content: typeof redacted === "string" ? redacted : content,
|
|
533
|
+
truncated: prefix.truncated,
|
|
534
|
+
maxBytes: MAX_TEXT_PREVIEW_BYTES,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
// Issue #1197: content-free document version. The hash is a one-way SHA-256 of the editable
|
|
538
|
+
// UTF-8 content — it never echoes the content itself.
|
|
539
|
+
function sha256Hex(content) {
|
|
540
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
541
|
+
}
|
|
542
|
+
function documentVersion(content, stats) {
|
|
543
|
+
return { sizeBytes: stats.size, modifiedAt: stats.mtimeMs, contentHash: sha256Hex(content) };
|
|
544
|
+
}
|
|
545
|
+
function editorSession(version) {
|
|
546
|
+
return { schemaVersion: EDITOR_SESSION_SCHEMA_VERSION, version };
|
|
547
|
+
}
|
|
548
|
+
function statsMatch(left, right) {
|
|
549
|
+
return left.size === right.size && left.mtimeMs === right.mtimeMs;
|
|
550
|
+
}
|
|
551
|
+
async function readStableEditableContent(target) {
|
|
552
|
+
let before = target.stats;
|
|
553
|
+
for (let attempt = 0; attempt < STABLE_CONTENT_READ_ATTEMPTS; attempt += 1) {
|
|
554
|
+
if (before.size > MAX_TEXT_PREVIEW_BYTES) {
|
|
555
|
+
throw new FilesError(413, "FILE_TOO_LARGE", `This file is too large to edit here (limit ${String(MAX_TEXT_PREVIEW_BYTES)} bytes).`);
|
|
556
|
+
}
|
|
557
|
+
const content = await readFile(target.path, "utf8");
|
|
558
|
+
const after = await stat(target.path);
|
|
559
|
+
if (statsMatch(before, after))
|
|
560
|
+
return { content, stats: after };
|
|
561
|
+
before = after;
|
|
562
|
+
}
|
|
563
|
+
throw new FilesError(409, "STALE_SESSION", "This file changed while it was being opened. Reload it before editing.");
|
|
564
|
+
}
|
|
565
|
+
// Issue #1197: version-aware optimistic concurrency. Rejects a save when the on-disk document no
|
|
566
|
+
// longer matches the revision the editor opened. Size/mtime are compared first so the content is
|
|
567
|
+
// only re-read (bounded by the editable size limit) when those cheap signals match.
|
|
568
|
+
async function assertSessionNotStale(target, baseVersion) {
|
|
569
|
+
const sizeMatches = target.stats.size === baseVersion.sizeBytes;
|
|
570
|
+
const mtimeMatches = Math.abs(target.stats.mtimeMs - baseVersion.modifiedAt) <= 1;
|
|
571
|
+
let hashMatches = false;
|
|
572
|
+
if (sizeMatches && mtimeMatches && target.stats.size <= MAX_TEXT_PREVIEW_BYTES) {
|
|
573
|
+
// Bounded re-read (mirrors the content-classification read): if the file grew past the editable
|
|
574
|
+
// limit between the stat and this read, treat the truncated result as a mismatch.
|
|
575
|
+
const current = await readPrefix(target.path, MAX_TEXT_PREVIEW_BYTES);
|
|
576
|
+
hashMatches =
|
|
577
|
+
!current.truncated && sha256Hex(current.buffer.toString("utf8")) === baseVersion.contentHash;
|
|
578
|
+
}
|
|
579
|
+
if (!sizeMatches || !mtimeMatches || !hashMatches) {
|
|
580
|
+
throw new FilesError(409, "STALE_SESSION", "This file changed since it was opened. Reload it before saving again.");
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// Optimistic-concurrency gate for a save. The version-aware baseVersion check (Issue #1197)
|
|
584
|
+
// supersedes the legacy mtime-only `expectedModifiedAt` check; either may be absent (forced save).
|
|
585
|
+
async function assertNoWriteConflict(target, baseVersion, expectedModifiedAt) {
|
|
586
|
+
if (baseVersion !== undefined) {
|
|
587
|
+
await assertSessionNotStale(target, baseVersion);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (expectedModifiedAt !== undefined && Math.abs(target.stats.mtimeMs - expectedModifiedAt) > 1) {
|
|
591
|
+
throw new FilesError(409, "WRITE_CONFLICT", "This file changed on disk. Reload it before saving again.");
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function editableTextContent(target) {
|
|
595
|
+
const snapshot = await readStableEditableContent(target);
|
|
596
|
+
const stableTarget = { ...target, stats: snapshot.stats };
|
|
597
|
+
const base = basePreview(stableTarget);
|
|
598
|
+
return {
|
|
599
|
+
...base,
|
|
600
|
+
content: snapshot.content,
|
|
601
|
+
maxBytes: MAX_TEXT_PREVIEW_BYTES,
|
|
602
|
+
session: editorSession(documentVersion(snapshot.content, snapshot.stats)),
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
export async function readFilesContent(store, rootInput, pathInput, redactor = staticFilesMetadataRedactor) {
|
|
606
|
+
const target = await resolveInsideRoot(store, rootInput, pathInput, redactor);
|
|
607
|
+
if (!target.stats.isFile()) {
|
|
608
|
+
throw new FilesError(400, "NOT_FILE", "The requested path is not a file.");
|
|
609
|
+
}
|
|
610
|
+
const base = basePreview(target);
|
|
611
|
+
const prefix = await readPrefix(target.path, Math.min(target.stats.size, 4096));
|
|
612
|
+
if (!isKnownTextExtension(base.extension) && !isLikelyUtf8Text(prefix.buffer)) {
|
|
613
|
+
throw new FilesError(400, "UNSUPPORTED_FILE", "This file cannot be edited in the workspace.");
|
|
614
|
+
}
|
|
615
|
+
return editableTextContent(target);
|
|
616
|
+
}
|
|
617
|
+
async function writeResolvedFilesContent(args) {
|
|
618
|
+
if (!args.target.stats.isFile()) {
|
|
619
|
+
throw new FilesError(400, "NOT_FILE", "The requested path is not a file.");
|
|
620
|
+
}
|
|
621
|
+
const base = basePreview(args.target);
|
|
622
|
+
const prefix = await readPrefix(args.target.path, Math.min(args.target.stats.size, 4096));
|
|
623
|
+
if (!isKnownTextExtension(base.extension) && !isLikelyUtf8Text(prefix.buffer)) {
|
|
624
|
+
throw new FilesError(400, "UNSUPPORTED_FILE", "This file cannot be edited in the workspace.");
|
|
625
|
+
}
|
|
626
|
+
await assertNoWriteConflict(args.target, args.baseVersion, args.expectedModifiedAt);
|
|
627
|
+
if (Buffer.byteLength(args.content, "utf8") > MAX_TEXT_PREVIEW_BYTES) {
|
|
628
|
+
throw new FilesError(413, "FILE_TOO_LARGE", `This file is too large to edit here (limit ${String(MAX_TEXT_PREVIEW_BYTES)} bytes).`);
|
|
629
|
+
}
|
|
630
|
+
await writeFile(args.target.path, args.content, "utf8");
|
|
631
|
+
const updatedStats = await stat(args.target.path);
|
|
632
|
+
return {
|
|
633
|
+
...base,
|
|
634
|
+
sizeBytes: updatedStats.size,
|
|
635
|
+
modifiedAt: updatedStats.mtimeMs,
|
|
636
|
+
content: args.content,
|
|
637
|
+
maxBytes: MAX_TEXT_PREVIEW_BYTES,
|
|
638
|
+
session: editorSession(documentVersion(args.content, updatedStats)),
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
export async function writeFilesContent(args) {
|
|
642
|
+
const target = await resolveInsideRoot(args.store, args.rootInput, args.pathInput, args.redactor ?? staticFilesMetadataRedactor);
|
|
643
|
+
return writeResolvedFilesContent({
|
|
644
|
+
target,
|
|
645
|
+
content: args.content,
|
|
646
|
+
expectedModifiedAt: args.expectedModifiedAt,
|
|
647
|
+
baseVersion: args.baseVersion,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
export async function readFilesPreview(store, rootInput, pathInput, redactor = staticFilesMetadataRedactor) {
|
|
651
|
+
const target = await resolveInsideRoot(store, rootInput, pathInput, redactor);
|
|
652
|
+
if (!target.stats.isFile()) {
|
|
653
|
+
throw new FilesError(400, "NOT_FILE", "The requested path is not a file.");
|
|
654
|
+
}
|
|
655
|
+
const base = basePreview(target);
|
|
656
|
+
if (isImageExtension(base.extension))
|
|
657
|
+
return imagePreview(target, base);
|
|
658
|
+
const prefix = await readPrefix(target.path, Math.min(target.stats.size, 4096));
|
|
659
|
+
if (isKnownTextExtension(base.extension) || isLikelyUtf8Text(prefix.buffer)) {
|
|
660
|
+
return textPreview(target, base, redactor);
|
|
661
|
+
}
|
|
662
|
+
return { ...base, kind: "binary", reason: "unsupported" };
|
|
663
|
+
}
|
|
664
|
+
export async function handleFilesDirectories(ctx, deps) {
|
|
665
|
+
return runFilesHandler(async () => {
|
|
666
|
+
const requestedRoot = ctx.url.searchParams.get("root");
|
|
667
|
+
const requestedPath = ctx.url.searchParams.get("path") ?? undefined;
|
|
668
|
+
return {
|
|
669
|
+
status: 200,
|
|
670
|
+
body: await listFilesDirectories(deps.store, requestedRoot, requestedPath, deps.redactor),
|
|
671
|
+
};
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
export async function handleFilesTree(ctx, deps) {
|
|
675
|
+
return runFilesHandler(async () => ({
|
|
676
|
+
status: 200,
|
|
677
|
+
body: await readFilesTree(deps.store, ctx.url.searchParams.get("root"), ctx.url.searchParams.get("path"), deps.redactor),
|
|
678
|
+
}));
|
|
679
|
+
}
|
|
680
|
+
export async function handleFilesPreview(ctx, deps) {
|
|
681
|
+
return runFilesHandler(async () => ({
|
|
682
|
+
status: 200,
|
|
683
|
+
body: await readFilesPreview(deps.store, ctx.url.searchParams.get("root"), ctx.url.searchParams.get("path"), deps.redactor),
|
|
684
|
+
}));
|
|
685
|
+
}
|
|
686
|
+
function readFilesWriteFields(body) {
|
|
687
|
+
const rootInput = typeof body.root === "string" ? body.root : null;
|
|
688
|
+
const pathInput = typeof body.path === "string" ? body.path : null;
|
|
689
|
+
const content = body.content;
|
|
690
|
+
if (rootInput === null || pathInput === null || typeof content !== "string") {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
return { rootInput, pathInput, content };
|
|
694
|
+
}
|
|
695
|
+
async function readFilesContentRoute(ctx, deps) {
|
|
696
|
+
return {
|
|
697
|
+
status: 200,
|
|
698
|
+
body: await readFilesContent(deps.store, ctx.url.searchParams.get("root"), ctx.url.searchParams.get("path"), deps.redactor),
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
async function writeFilesContentRoute(ctx, deps) {
|
|
702
|
+
const body = await readJsonObject(ctx.req, MAX_TEXT_PREVIEW_BYTES * 2 + 16_384);
|
|
703
|
+
if (isRouteResult(body))
|
|
704
|
+
return body;
|
|
705
|
+
const fields = readFilesWriteFields(body);
|
|
706
|
+
if (fields === null) {
|
|
707
|
+
const message = "root, path, and content are required for a file save request.";
|
|
708
|
+
return { status: 400, body: errorBody("BAD_REQUEST", message) };
|
|
709
|
+
}
|
|
710
|
+
const target = await resolveInsideRoot(deps.store, fields.rootInput, fields.pathInput, deps.redactor);
|
|
711
|
+
let baseVersion;
|
|
712
|
+
if (body.baseVersion !== undefined) {
|
|
713
|
+
const parsed = parseEditorDocumentVersion(body.baseVersion);
|
|
714
|
+
if (!parsed.ok) {
|
|
715
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "baseVersion is not a valid version.") };
|
|
716
|
+
}
|
|
717
|
+
baseVersion = parsed.value;
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
status: 200,
|
|
721
|
+
body: await writeResolvedFilesContent({
|
|
722
|
+
target,
|
|
723
|
+
content: fields.content,
|
|
724
|
+
expectedModifiedAt: typeof body.expectedModifiedAt === "number" ? body.expectedModifiedAt : undefined,
|
|
725
|
+
baseVersion,
|
|
726
|
+
}),
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
export async function handleFilesContent(ctx, deps) {
|
|
730
|
+
return runFilesHandler(async () => (ctx.req.method ?? "GET").toUpperCase() === "GET"
|
|
731
|
+
? readFilesContentRoute(ctx, deps)
|
|
732
|
+
: writeFilesContentRoute(ctx, deps));
|
|
733
|
+
}
|