dodo-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +425 -0
- package/dist/auth/adapter.js +43 -0
- package/dist/auth/adapter.js.map +1 -0
- package/dist/auth/clientDeletion.js +45 -0
- package/dist/auth/clientDeletion.js.map +1 -0
- package/dist/auth/clients.js +55 -0
- package/dist/auth/clients.js.map +1 -0
- package/dist/auth/constants.js +10 -0
- package/dist/auth/constants.js.map +1 -0
- package/dist/auth/interactions.js +184 -0
- package/dist/auth/interactions.js.map +1 -0
- package/dist/auth/keys.js +56 -0
- package/dist/auth/keys.js.map +1 -0
- package/dist/auth/provider.js +131 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/verifier.js +74 -0
- package/dist/auth/verifier.js.map +1 -0
- package/dist/cli/kill.js +124 -0
- package/dist/cli/kill.js.map +1 -0
- package/dist/cli/main.js +1393 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/menu.js +127 -0
- package/dist/cli/menu.js.map +1 -0
- package/dist/cli/terminal.js +30 -0
- package/dist/cli/terminal.js.map +1 -0
- package/dist/config/globalConfig.js +144 -0
- package/dist/config/globalConfig.js.map +1 -0
- package/dist/config/limits.js +42 -0
- package/dist/config/limits.js.map +1 -0
- package/dist/config/paths.js +94 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/projectConfig.js +63 -0
- package/dist/config/projectConfig.js.map +1 -0
- package/dist/config/stateImport.js +306 -0
- package/dist/config/stateImport.js.map +1 -0
- package/dist/config/tunnelConfig.js +23 -0
- package/dist/config/tunnelConfig.js.map +1 -0
- package/dist/errors.js +82 -0
- package/dist/errors.js.map +1 -0
- package/dist/evaluation/contracts.js +86 -0
- package/dist/evaluation/contracts.js.map +1 -0
- package/dist/evaluation/dodoBench.js +69 -0
- package/dist/evaluation/dodoBench.js.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/authentication.js +117 -0
- package/dist/ipc/authentication.js.map +1 -0
- package/dist/ipc/client.js +86 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/installationClient.js +83 -0
- package/dist/ipc/installationClient.js.map +1 -0
- package/dist/ipc/ownerControl.js +46 -0
- package/dist/ipc/ownerControl.js.map +1 -0
- package/dist/ipc/protocol.js +2 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/server.js +108 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/platform/execResolve.js +64 -0
- package/dist/platform/execResolve.js.map +1 -0
- package/dist/platform/fileIdentity.js +26 -0
- package/dist/platform/fileIdentity.js.map +1 -0
- package/dist/platform/fsRetry.js +22 -0
- package/dist/platform/fsRetry.js.map +1 -0
- package/dist/platform/pathPolicy.js +29 -0
- package/dist/platform/pathPolicy.js.map +1 -0
- package/dist/platform/privateFs.js +116 -0
- package/dist/platform/privateFs.js.map +1 -0
- package/dist/platform/processTree.js +31 -0
- package/dist/platform/processTree.js.map +1 -0
- package/dist/platform/shell.js +74 -0
- package/dist/platform/shell.js.map +1 -0
- package/dist/platform/speech.js +94 -0
- package/dist/platform/speech.js.map +1 -0
- package/dist/platform/system.js +28 -0
- package/dist/platform/system.js.map +1 -0
- package/dist/platform/windowsSandbox.js +129 -0
- package/dist/platform/windowsSandbox.js.map +1 -0
- package/dist/projects/federation.js +312 -0
- package/dist/projects/federation.js.map +1 -0
- package/dist/projects/registry.js +291 -0
- package/dist/projects/registry.js.map +1 -0
- package/dist/security/accessMode.js +19 -0
- package/dist/security/accessMode.js.map +1 -0
- package/dist/security/approvals.js +56 -0
- package/dist/security/approvals.js.map +1 -0
- package/dist/security/env.js +57 -0
- package/dist/security/env.js.map +1 -0
- package/dist/security/mutationQueue.js +78 -0
- package/dist/security/mutationQueue.js.map +1 -0
- package/dist/security/policy.js +22 -0
- package/dist/security/policy.js.map +1 -0
- package/dist/security/projectAuthority.js +22 -0
- package/dist/security/projectAuthority.js.map +1 -0
- package/dist/security/redact.js +30 -0
- package/dist/security/redact.js.map +1 -0
- package/dist/server/aiAdmin.js +201 -0
- package/dist/server/aiAdmin.js.map +1 -0
- package/dist/server/appServer.js +461 -0
- package/dist/server/appServer.js.map +1 -0
- package/dist/server/bootstrap.js +185 -0
- package/dist/server/bootstrap.js.map +1 -0
- package/dist/server/configUi/app.css +347 -0
- package/dist/server/configUi/app.js +958 -0
- package/dist/server/configUi/index.html +379 -0
- package/dist/server/configUi/ui/alerts.js +92 -0
- package/dist/server/configUi/ui/dom.js +19 -0
- package/dist/server/configUi/ui/tooltips.js +115 -0
- package/dist/server/configUi/vendor/VERSION.txt +1 -0
- package/dist/server/configUi/vendor/sweetalert2.min.css +1 -0
- package/dist/server/configUi/vendor/sweetalert2.min.js +5 -0
- package/dist/server/configUi/workbench.css +62 -0
- package/dist/server/configUi/workbench.js +394 -0
- package/dist/server/installationRuntime.js +155 -0
- package/dist/server/installationRuntime.js.map +1 -0
- package/dist/server/instructions.js +17 -0
- package/dist/server/instructions.js.map +1 -0
- package/dist/server/ipcDispatch.js +225 -0
- package/dist/server/ipcDispatch.js.map +1 -0
- package/dist/server/localConfig.js +561 -0
- package/dist/server/localConfig.js.map +1 -0
- package/dist/server/optionalServices.js +30 -0
- package/dist/server/optionalServices.js.map +1 -0
- package/dist/server/projectLease.js +46 -0
- package/dist/server/projectLease.js.map +1 -0
- package/dist/server/stdioServer.js +75 -0
- package/dist/server/stdioServer.js.map +1 -0
- package/dist/server/version.js +13 -0
- package/dist/server/version.js.map +1 -0
- package/dist/server/workspaceHost.js +194 -0
- package/dist/server/workspaceHost.js.map +1 -0
- package/dist/services/agent/agentService.js +598 -0
- package/dist/services/agent/agentService.js.map +1 -0
- package/dist/services/agent/contracts.js +102 -0
- package/dist/services/agent/contracts.js.map +1 -0
- package/dist/services/ai/adapters.js +287 -0
- package/dist/services/ai/adapters.js.map +1 -0
- package/dist/services/ai/contracts.js +39 -0
- package/dist/services/ai/contracts.js.map +1 -0
- package/dist/services/ai/credentials.js +77 -0
- package/dist/services/ai/credentials.js.map +1 -0
- package/dist/services/ai/network.js +113 -0
- package/dist/services/ai/network.js.map +1 -0
- package/dist/services/ai/settings.js +178 -0
- package/dist/services/ai/settings.js.map +1 -0
- package/dist/services/ai/subagents.js +468 -0
- package/dist/services/ai/subagents.js.map +1 -0
- package/dist/services/assistance/analysis.js +352 -0
- package/dist/services/assistance/analysis.js.map +1 -0
- package/dist/services/assistance/contracts.js +54 -0
- package/dist/services/assistance/contracts.js.map +1 -0
- package/dist/services/assistance/verification.js +274 -0
- package/dist/services/assistance/verification.js.map +1 -0
- package/dist/services/brain/brainParser.js +97 -0
- package/dist/services/brain/brainParser.js.map +1 -0
- package/dist/services/brain/brainService.js +733 -0
- package/dist/services/brain/brainService.js.map +1 -0
- package/dist/services/brain/brainWorker.js +212 -0
- package/dist/services/brain/brainWorker.js.map +1 -0
- package/dist/services/brain/contracts.js +154 -0
- package/dist/services/brain/contracts.js.map +1 -0
- package/dist/services/brain/protocol.js +2 -0
- package/dist/services/brain/protocol.js.map +1 -0
- package/dist/services/changes/applier.js +544 -0
- package/dist/services/changes/applier.js.map +1 -0
- package/dist/services/changes/contentBudget.js +11 -0
- package/dist/services/changes/contentBudget.js.map +1 -0
- package/dist/services/changes/planner.js +284 -0
- package/dist/services/changes/planner.js.map +1 -0
- package/dist/services/changes/types.js +3 -0
- package/dist/services/changes/types.js.map +1 -0
- package/dist/services/context/contextEngine.js +746 -0
- package/dist/services/context/contextEngine.js.map +1 -0
- package/dist/services/context/contracts.js +88 -0
- package/dist/services/context/contracts.js.map +1 -0
- package/dist/services/desktop/desktopPolicy.js +76 -0
- package/dist/services/desktop/desktopPolicy.js.map +1 -0
- package/dist/services/desktop/desktopService.js +130 -0
- package/dist/services/desktop/desktopService.js.map +1 -0
- package/dist/services/desktop/nativeBackend.js +174 -0
- package/dist/services/desktop/nativeBackend.js.map +1 -0
- package/dist/services/desktop/protocol.js +32 -0
- package/dist/services/desktop/protocol.js.map +1 -0
- package/dist/services/files/readService.js +136 -0
- package/dist/services/files/readService.js.map +1 -0
- package/dist/services/git/gitService.js +369 -0
- package/dist/services/git/gitService.js.map +1 -0
- package/dist/services/intelligence/intelService.js +136 -0
- package/dist/services/intelligence/intelService.js.map +1 -0
- package/dist/services/intelligence/protocol.js +2 -0
- package/dist/services/intelligence/protocol.js.map +1 -0
- package/dist/services/intelligence/tsWorker.js +419 -0
- package/dist/services/intelligence/tsWorker.js.map +1 -0
- package/dist/services/jobs/commandInput.js +34 -0
- package/dist/services/jobs/commandInput.js.map +1 -0
- package/dist/services/jobs/jobManager.js +398 -0
- package/dist/services/jobs/jobManager.js.map +1 -0
- package/dist/services/jobs/sandbox.js +156 -0
- package/dist/services/jobs/sandbox.js.map +1 -0
- package/dist/services/jobs/sandboxWiring.js +37 -0
- package/dist/services/jobs/sandboxWiring.js.map +1 -0
- package/dist/services/jobs/spool.js +112 -0
- package/dist/services/jobs/spool.js.map +1 -0
- package/dist/services/jobs/windowsSandboxChild.js +24 -0
- package/dist/services/jobs/windowsSandboxChild.js.map +1 -0
- package/dist/services/jobs/windowsSandboxWorker.js +155 -0
- package/dist/services/jobs/windowsSandboxWorker.js.map +1 -0
- package/dist/services/lsp/documentUri.js +16 -0
- package/dist/services/lsp/documentUri.js.map +1 -0
- package/dist/services/lsp/jsonrpc.js +382 -0
- package/dist/services/lsp/jsonrpc.js.map +1 -0
- package/dist/services/lsp/lspService.js +1051 -0
- package/dist/services/lsp/lspService.js.map +1 -0
- package/dist/services/memory/contracts.js +94 -0
- package/dist/services/memory/contracts.js.map +1 -0
- package/dist/services/memory/memoryService.js +683 -0
- package/dist/services/memory/memoryService.js.map +1 -0
- package/dist/services/multimodal/browserService.js +407 -0
- package/dist/services/multimodal/browserService.js.map +1 -0
- package/dist/services/multimodal/byteRange.js +31 -0
- package/dist/services/multimodal/byteRange.js.map +1 -0
- package/dist/services/multimodal/contracts.js +59 -0
- package/dist/services/multimodal/contracts.js.map +1 -0
- package/dist/services/multimodal/gameService.js +95 -0
- package/dist/services/multimodal/gameService.js.map +1 -0
- package/dist/services/multimodal/images.js +107 -0
- package/dist/services/multimodal/images.js.map +1 -0
- package/dist/services/multimodal/mediaService.js +279 -0
- package/dist/services/multimodal/mediaService.js.map +1 -0
- package/dist/services/multimodal/mediaWorker.js +143 -0
- package/dist/services/multimodal/mediaWorker.js.map +1 -0
- package/dist/services/multimodal/multimodalService.js +51 -0
- package/dist/services/multimodal/multimodalService.js.map +1 -0
- package/dist/services/multimodal/network.js +69 -0
- package/dist/services/multimodal/network.js.map +1 -0
- package/dist/services/multimodal/operations.js +40 -0
- package/dist/services/multimodal/operations.js.map +1 -0
- package/dist/services/multimodal/storage.js +160 -0
- package/dist/services/multimodal/storage.js.map +1 -0
- package/dist/services/multimodal/workflowService.js +179 -0
- package/dist/services/multimodal/workflowService.js.map +1 -0
- package/dist/services/overview.js +284 -0
- package/dist/services/overview.js.map +1 -0
- package/dist/services/resources/casStore.js +304 -0
- package/dist/services/resources/casStore.js.map +1 -0
- package/dist/services/resources/contracts.js +75 -0
- package/dist/services/resources/contracts.js.map +1 -0
- package/dist/services/resources/mime.js +77 -0
- package/dist/services/resources/mime.js.map +1 -0
- package/dist/services/resources/providerRegistry.js +22 -0
- package/dist/services/resources/providerRegistry.js.map +1 -0
- package/dist/services/resources/providers.js +120 -0
- package/dist/services/resources/providers.js.map +1 -0
- package/dist/services/resources/resourceService.js +282 -0
- package/dist/services/resources/resourceService.js.map +1 -0
- package/dist/services/runtime/contracts.js +65 -0
- package/dist/services/runtime/contracts.js.map +1 -0
- package/dist/services/runtime/runtimeService.js +364 -0
- package/dist/services/runtime/runtimeService.js.map +1 -0
- package/dist/services/schedules/scheduleService.js +198 -0
- package/dist/services/schedules/scheduleService.js.map +1 -0
- package/dist/services/search/searchService.js +434 -0
- package/dist/services/search/searchService.js.map +1 -0
- package/dist/services/search/searchWorker.js +58 -0
- package/dist/services/search/searchWorker.js.map +1 -0
- package/dist/setup/componentProbes.js +259 -0
- package/dist/setup/componentProbes.js.map +1 -0
- package/dist/setup/download.js +139 -0
- package/dist/setup/download.js.map +1 -0
- package/dist/setup/managedTools.js +118 -0
- package/dist/setup/managedTools.js.map +1 -0
- package/dist/setup/sandboxProbe.js +172 -0
- package/dist/setup/sandboxProbe.js.map +1 -0
- package/dist/setup/setup.js +522 -0
- package/dist/setup/setup.js.map +1 -0
- package/dist/setup/windowsChromiumAcl.js +56 -0
- package/dist/setup/windowsChromiumAcl.js.map +1 -0
- package/dist/setup/windowsSandboxInstall.js +94 -0
- package/dist/setup/windowsSandboxInstall.js.map +1 -0
- package/dist/setup/windowsSpeechInstall.js +86 -0
- package/dist/setup/windowsSpeechInstall.js.map +1 -0
- package/dist/store/db.js +711 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/scopeEncoding.js +15 -0
- package/dist/store/scopeEncoding.js.map +1 -0
- package/dist/store/store.js +467 -0
- package/dist/store/store.js.map +1 -0
- package/dist/tools/agentRuntimeTools.js +216 -0
- package/dist/tools/agentRuntimeTools.js.map +1 -0
- package/dist/tools/agentTools.js +317 -0
- package/dist/tools/agentTools.js.map +1 -0
- package/dist/tools/assistanceTools.js +126 -0
- package/dist/tools/assistanceTools.js.map +1 -0
- package/dist/tools/brainTools.js +130 -0
- package/dist/tools/brainTools.js.map +1 -0
- package/dist/tools/catalog.js +30 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/changeTools.js +188 -0
- package/dist/tools/changeTools.js.map +1 -0
- package/dist/tools/context.js +243 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/contextTools.js +57 -0
- package/dist/tools/contextTools.js.map +1 -0
- package/dist/tools/coreCatalog.js +82 -0
- package/dist/tools/coreCatalog.js.map +1 -0
- package/dist/tools/desktopTools.js +41 -0
- package/dist/tools/desktopTools.js.map +1 -0
- package/dist/tools/directTools.js +273 -0
- package/dist/tools/directTools.js.map +1 -0
- package/dist/tools/envelope.js +71 -0
- package/dist/tools/envelope.js.map +1 -0
- package/dist/tools/extraTools.js +298 -0
- package/dist/tools/extraTools.js.map +1 -0
- package/dist/tools/gitTools.js +93 -0
- package/dist/tools/gitTools.js.map +1 -0
- package/dist/tools/jobTools.js +454 -0
- package/dist/tools/jobTools.js.map +1 -0
- package/dist/tools/memoryTools.js +102 -0
- package/dist/tools/memoryTools.js.map +1 -0
- package/dist/tools/metaTools.js +153 -0
- package/dist/tools/metaTools.js.map +1 -0
- package/dist/tools/multimodalTools.js +262 -0
- package/dist/tools/multimodalTools.js.map +1 -0
- package/dist/tools/projectTools.js +261 -0
- package/dist/tools/projectTools.js.map +1 -0
- package/dist/tools/resourceTools.js +81 -0
- package/dist/tools/resourceTools.js.map +1 -0
- package/dist/tools/runtimeTools.js +137 -0
- package/dist/tools/runtimeTools.js.map +1 -0
- package/dist/tools/scheduleTools.js +10 -0
- package/dist/tools/scheduleTools.js.map +1 -0
- package/dist/tools/semanticTools.js +141 -0
- package/dist/tools/semanticTools.js.map +1 -0
- package/dist/tools/subagentTools.js +32 -0
- package/dist/tools/subagentTools.js.map +1 -0
- package/dist/tools/surface.js +644 -0
- package/dist/tools/surface.js.map +1 -0
- package/dist/tunnel/control.js +113 -0
- package/dist/tunnel/control.js.map +1 -0
- package/dist/tunnel/credentials.js +392 -0
- package/dist/tunnel/credentials.js.map +1 -0
- package/dist/tunnel/log.js +84 -0
- package/dist/tunnel/log.js.map +1 -0
- package/dist/tunnel/runtime.js +55 -0
- package/dist/tunnel/runtime.js.map +1 -0
- package/dist/tunnel/supervisor.js +280 -0
- package/dist/tunnel/supervisor.js.map +1 -0
- package/dist/util/bytes.js +53 -0
- package/dist/util/bytes.js.map +1 -0
- package/dist/util/hash.js +53 -0
- package/dist/util/hash.js.map +1 -0
- package/dist/workspace/fs.js +305 -0
- package/dist/workspace/fs.js.map +1 -0
- package/dist/workspace/identity.js +17 -0
- package/dist/workspace/identity.js.map +1 -0
- package/dist/workspace/ignores.js +173 -0
- package/dist/workspace/ignores.js.map +1 -0
- package/dist/workspace/root.js +59 -0
- package/dist/workspace/root.js.map +1 -0
- package/docs/AGENT_RUNTIME.md +154 -0
- package/docs/AI_PROVIDERS.md +110 -0
- package/docs/ARCHITECTURE.md +331 -0
- package/docs/ASSISTANCE.md +31 -0
- package/docs/AUTH.md +59 -0
- package/docs/BRAIN.md +92 -0
- package/docs/COMPATIBILITY.md +92 -0
- package/docs/CONFIRMATION.md +13 -0
- package/docs/CONTEXT.md +87 -0
- package/docs/DESKTOP.md +35 -0
- package/docs/EVALUATION.md +86 -0
- package/docs/LARGE_INPUTS.md +21 -0
- package/docs/LSP.md +21 -0
- package/docs/MANUAL_ACCEPTANCE.md +283 -0
- package/docs/MEMORY.md +128 -0
- package/docs/MULTIMODAL.md +32 -0
- package/docs/PROJECTS.md +146 -0
- package/docs/RELEASE_1.0.0.md +111 -0
- package/docs/RELEASE_NOTES.md +92 -0
- package/docs/RESOURCES.md +83 -0
- package/docs/RUNTIME.md +125 -0
- package/docs/SCHEDULES.md +23 -0
- package/docs/SECURITY.md +250 -0
- package/docs/TEST_REPORT.md +312 -0
- package/docs/TUNNEL.md +86 -0
- package/docs/WEB_CLIENTS.md +43 -0
- package/docs/WINDOWS.md +79 -0
- package/docs/adr/001-one-process-one-workspace.md +22 -0
- package/docs/adr/002-external-tunnel.md +19 -0
- package/docs/adr/003-sdk-v2-legacy-stateless.md +20 -0
- package/docs/adr/004-embedded-oauth-provider.md +19 -0
- package/docs/adr/005-static-client-baseline.md +19 -0
- package/docs/adr/006-guarded-fs-explicit-exec.md +16 -0
- package/docs/adr/007-preview-hash-journal.md +18 -0
- package/docs/adr/008-ts-js-semantics-first.md +17 -0
- package/docs/adr/009-jobs-polling.md +18 -0
- package/docs/adr/010-sqlite-better-sqlite3.md +18 -0
- package/docs/adr/011-env-example-secure-default.md +19 -0
- package/docs/adr/012-workspace-scope-grant.md +24 -0
- package/docs/adr/013-loopback-proxy-trust.md +18 -0
- package/docs/adr/014-direct-coding-tier.md +48 -0
- package/docs/adr/015-coding-agent-parity.md +56 -0
- package/docs/adr/016-oidc-openid-consent.md +15 -0
- package/docs/adr/017-installation-identity-local-config.md +44 -0
- package/docs/adr/018-access-scope-encoding.md +11 -0
- package/docs/adr/019-runtime-workspace-switch.md +59 -0
- package/docs/adr/020-desktop-access.md +50 -0
- package/docs/adr/021-persistent-desktop-consent.md +39 -0
- package/docs/adr/022-owner-kill-preserves-login.md +30 -0
- package/docs/adr/023-large-coding-inputs.md +49 -0
- package/docs/adr/024-usage-rounds-and-schedules.md +72 -0
- package/docs/adr/025-owner-chat-whitelist.md +53 -0
- package/docs/adr/026-remove-conversation-confirmation.md +35 -0
- package/docs/adr/027-workspace-client-list.md +20 -0
- package/docs/adr/028-owner-client-deletion.md +25 -0
- package/docs/adr/029-compact-tool-surface.md +121 -0
- package/docs/adr/030-windows-native-candidate.md +71 -0
- package/docs/adr/031-owner-selected-tunnel-supervision.md +13 -0
- package/docs/adr/032-owner-project-registry.md +43 -0
- package/docs/adr/033-read-only-project-federation.md +61 -0
- package/docs/adr/034-universal-resource-cas.md +60 -0
- package/docs/adr/035-project-brain-incremental-index.md +51 -0
- package/docs/adr/036-context-engine-evidence.md +53 -0
- package/docs/adr/037-owner-reviewed-memory.md +46 -0
- package/docs/adr/038-runtime-intelligence.md +46 -0
- package/docs/adr/039-advanced-agent-runtime.md +57 -0
- package/docs/adr/040-dodobench-release-gate.md +43 -0
- package/docs/adr/041-local-macos-docker-linux-gates.md +32 -0
- package/docs/adr/042-project-directory-generation-identity.md +36 -0
- package/docs/adr/043-trusted-self-hosted-platform-gates.md +38 -0
- package/docs/adr/044-global-launcher-and-cli-menu.md +43 -0
- package/docs/adr/045-ai-providers-multiproject.md +56 -0
- package/docs/adr/046-personal-access-mode.md +32 -0
- package/docs/adr/README.md +64 -0
- package/examples/.dodo.json.example +20 -0
- package/examples/cloudflared.yml +23 -0
- package/examples/reverse-proxy-fixture.mjs +37 -0
- package/native/desktop-linux.py +387 -0
- package/native/desktop-windows.cs +180 -0
- package/native/desktop.swift +212 -0
- package/package.json +145 -0
- package/schemas/global-config.schema.json +497 -0
- package/schemas/project-config.schema.json +75 -0
- package/schemas/tools.compact.json +3236 -0
- package/schemas/tools.hybrid.json +8194 -0
- package/schemas/tools.json +34518 -0
- package/scripts/guard-publish.mjs +12 -0
- package/scripts/setup-multimodal.mjs +70 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { DodoError, toDodoError } from '../../errors.js';
|
|
2
|
+
import { digestOf, newId, sha256Bytes } from '../../util/hash.js';
|
|
3
|
+
import { actorKey, liveAccess } from '../multimodal/storage.js';
|
|
4
|
+
import { RUNTIME_SCHEMA_VERSION, RuntimeDiagnosis, RuntimeEvidence, RuntimeSession, RuntimeSessionStatus, RuntimeTask, } from './contracts.js';
|
|
5
|
+
const SESSION_DEFAULT_MS = 60 * 60 * 1000;
|
|
6
|
+
const SESSION_MAX_MS = 24 * 60 * 60 * 1000;
|
|
7
|
+
const EVIDENCE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
8
|
+
const MAX_SESSIONS = 32;
|
|
9
|
+
const MAX_RETAINED_SESSIONS = 128;
|
|
10
|
+
const MAX_TASKS = 32;
|
|
11
|
+
const MAX_EVIDENCE_PER_SESSION = 500;
|
|
12
|
+
const MAX_EVIDENCE_TOTAL = 2000;
|
|
13
|
+
const OUTPUT_SAMPLE_BYTES = 32 * 1024;
|
|
14
|
+
/** Durable runtime intelligence. It stores metadata and hashes only, never raw process output, DOM, headers, cookies or environment values. */
|
|
15
|
+
export class RuntimeService {
|
|
16
|
+
services;
|
|
17
|
+
closed = false;
|
|
18
|
+
constructor(services) {
|
|
19
|
+
this.services = services;
|
|
20
|
+
this.recover();
|
|
21
|
+
}
|
|
22
|
+
close() { this.closed = true; }
|
|
23
|
+
open(ctx, label, ttlMinutes) {
|
|
24
|
+
this.check();
|
|
25
|
+
liveAccess(ctx, 'dodo:write');
|
|
26
|
+
this.recover();
|
|
27
|
+
const owner = actorKey(ctx.principal);
|
|
28
|
+
const count = this.services.store.db.prepare("SELECT COUNT(*) AS n FROM runtime_sessions WHERE workspace_id=? AND owner=? AND status='OPEN'").get(this.services.workspaceId, owner).n;
|
|
29
|
+
if (count >= MAX_SESSIONS)
|
|
30
|
+
throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_SESSIONS} open runtime sessions per client and workspace`);
|
|
31
|
+
const retained = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM runtime_sessions WHERE workspace_id=? AND owner=?').get(this.services.workspaceId, owner).n;
|
|
32
|
+
if (retained >= MAX_RETAINED_SESSIONS)
|
|
33
|
+
throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_RETAINED_SESSIONS} retained runtime sessions per client and workspace; wait for retention cleanup`);
|
|
34
|
+
const now = Date.now(), id = newId('runtime'), expiresAt = now + Math.min(SESSION_MAX_MS, Math.max(60_000, ttlMinutes * 60_000 || SESSION_DEFAULT_MS));
|
|
35
|
+
this.services.store.db.prepare(`INSERT INTO runtime_sessions(id,workspace_id,opened_epoch,owner,label,status,created_at,last_seen_at,expires_at,closed_at)
|
|
36
|
+
VALUES (?,?,?,?,?,'OPEN',?,?,?,NULL)`).run(id, this.services.workspaceId, this.services.epoch, owner, label, now, now, expiresAt);
|
|
37
|
+
return this.sessionData(this.row(id, owner));
|
|
38
|
+
}
|
|
39
|
+
status(ctx, sessionId) {
|
|
40
|
+
this.check();
|
|
41
|
+
liveAccess(ctx, 'dodo:read');
|
|
42
|
+
this.recover();
|
|
43
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, true);
|
|
44
|
+
const tasks = this.taskRows(session.id, owner).map((row) => this.taskData(row));
|
|
45
|
+
const counts = this.services.store.db.prepare('SELECT status,COUNT(*) AS n FROM runtime_evidence WHERE session_id=? AND owner=? GROUP BY status').all(session.id, owner);
|
|
46
|
+
const by = new Map(counts.map((r) => [r.status, r.n]));
|
|
47
|
+
return RuntimeSessionStatus.parse({
|
|
48
|
+
session: this.sessionData(session), tasks,
|
|
49
|
+
evidence: { current: by.get('CURRENT') ?? 0, stale: by.get('STALE') ?? 0, expired: by.get('EXPIRED') ?? 0 },
|
|
50
|
+
reconnectable: true,
|
|
51
|
+
note: 'Session/task metadata is durable across MCP reconnects and server restarts. Runtime evidence is untrusted and never grants permission.',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
diagnostics(ctx) {
|
|
55
|
+
this.check();
|
|
56
|
+
liveAccess(ctx, 'dodo:read');
|
|
57
|
+
this.recover();
|
|
58
|
+
const owner = actorKey(ctx.principal);
|
|
59
|
+
const row = this.services.store.db.prepare(`SELECT
|
|
60
|
+
(SELECT COUNT(*) FROM runtime_sessions WHERE workspace_id=? AND owner=? AND status='OPEN') AS sessions,
|
|
61
|
+
(SELECT COUNT(*) FROM runtime_tasks WHERE workspace_id=? AND owner=?) AS tasks,
|
|
62
|
+
(SELECT COUNT(*) FROM runtime_evidence WHERE workspace_id=? AND owner=? AND status='CURRENT') AS evidence`).get(this.services.workspaceId, owner, this.services.workspaceId, owner, this.services.workspaceId, owner);
|
|
63
|
+
return { schemaVersion: RUNTIME_SCHEMA_VERSION, available: true, ...row, retentionHours: 24, storesRawOutput: false, autonomousMonitoring: false };
|
|
64
|
+
}
|
|
65
|
+
closeSession(ctx, sessionId) {
|
|
66
|
+
this.check();
|
|
67
|
+
liveAccess(ctx, 'dodo:write');
|
|
68
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, true);
|
|
69
|
+
if (session.status === 'CLOSED')
|
|
70
|
+
return this.sessionData(session);
|
|
71
|
+
if (session.status === 'EXPIRED')
|
|
72
|
+
throw new DodoError('CONFLICT', 'runtime session is expired');
|
|
73
|
+
const running = this.taskRows(session.id, owner).filter((task) => this.services.jobs.getJobChecked(task.job_id, this.services.workspaceId).status === 'running');
|
|
74
|
+
if (running.length)
|
|
75
|
+
throw new DodoError('CONFLICT', `${running.length} runtime task(s) are still running`, { recovery: 'cancel them with runtime_task_cancel or wait for completion before closing the session' });
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
this.services.store.db.prepare("UPDATE runtime_sessions SET status='CLOSED',closed_at=?,last_seen_at=? WHERE id=? AND owner=?").run(now, now, session.id, owner);
|
|
78
|
+
return this.sessionData(this.row(session.id, owner));
|
|
79
|
+
}
|
|
80
|
+
prepareTask(ctx, sessionId) {
|
|
81
|
+
this.check();
|
|
82
|
+
liveAccess(ctx, 'dodo:exec');
|
|
83
|
+
this.requireSession(sessionId, actorKey(ctx.principal), false);
|
|
84
|
+
}
|
|
85
|
+
attachTask(ctx, sessionId, jobId, kind) {
|
|
86
|
+
this.check();
|
|
87
|
+
liveAccess(ctx, 'dodo:exec');
|
|
88
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, false);
|
|
89
|
+
const count = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM runtime_tasks WHERE session_id=? AND owner=?').get(session.id, owner).n;
|
|
90
|
+
if (count >= MAX_TASKS)
|
|
91
|
+
throw new DodoError('RESOURCE_LIMIT', `at most ${MAX_TASKS} tasks per runtime session`);
|
|
92
|
+
const job = this.services.jobs.getJobChecked(jobId, this.services.workspaceId);
|
|
93
|
+
if (job.principal !== ctx.principal.grantId)
|
|
94
|
+
throw new DodoError('FORBIDDEN', 'runtime tasks can attach only to jobs created by this OAuth grant');
|
|
95
|
+
const id = newId('rtask'), now = Date.now();
|
|
96
|
+
this.services.store.db.prepare('INSERT INTO runtime_tasks(id,session_id,workspace_id,owner,job_id,kind,created_at) VALUES (?,?,?,?,?,?,?)')
|
|
97
|
+
.run(id, session.id, this.services.workspaceId, owner, jobId, kind, now);
|
|
98
|
+
return this.taskData({ id, session_id: session.id, workspace_id: this.services.workspaceId, owner, job_id: jobId, kind, created_at: now });
|
|
99
|
+
}
|
|
100
|
+
observeTask(ctx, sessionId, taskId, reportPath) {
|
|
101
|
+
this.check();
|
|
102
|
+
liveAccess(ctx, 'dodo:read');
|
|
103
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, false), task = this.requireTask(session.id, taskId, owner);
|
|
104
|
+
const payload = this.taskPayload(task, reportPath);
|
|
105
|
+
return { task: this.taskData(task), evidence: this.putEvidence(session, owner, task.kind, `job:${task.job_id}${reportPath ? `:report:${reportPath}` : ''}`, payload) };
|
|
106
|
+
}
|
|
107
|
+
cancelTask(ctx, sessionId, taskId) {
|
|
108
|
+
this.check();
|
|
109
|
+
liveAccess(ctx, 'dodo:exec');
|
|
110
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, false), task = this.requireTask(session.id, taskId, owner);
|
|
111
|
+
const result = this.services.jobs.cancel(task.job_id, this.services.workspaceId);
|
|
112
|
+
return { taskId: task.id, jobId: task.job_id, status: result.status };
|
|
113
|
+
}
|
|
114
|
+
async collectBrowser(ctx, sessionId, browserSessionId) {
|
|
115
|
+
this.check();
|
|
116
|
+
liveAccess(ctx, 'dodo:read');
|
|
117
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, false);
|
|
118
|
+
const browser = this.services.multimodal?.browser;
|
|
119
|
+
if (!browser)
|
|
120
|
+
throw new DodoError('NOT_SUPPORTED', 'browser runtime collection is unavailable in this build');
|
|
121
|
+
const observation = await browser.observe(ctx.principal, browserSessionId);
|
|
122
|
+
liveAccess(ctx, 'dodo:read');
|
|
123
|
+
const safe = await browser.verifyObservation(ctx.principal, browserSessionId, observation.observationId);
|
|
124
|
+
const payload = {
|
|
125
|
+
browserSessionId, observationId: observation.observationId, url: safe.url,
|
|
126
|
+
titleHash: sha256Bytes(observation.title), domHash: sha256Bytes(JSON.stringify({ text: observation.text, elements: observation.elements })),
|
|
127
|
+
screenshotHash: observation.asset.sha256, elementCount: observation.elements.length, mediaCount: observation.media.length,
|
|
128
|
+
consoleCount: observation.console.length, networkCount: observation.network.length,
|
|
129
|
+
blockedNetworkCount: observation.network.filter((entry) => entry.startsWith('BLOCKED ')).length,
|
|
130
|
+
webSocketPolicy: 'blocked', timing: safe.timing, truncated: observation.truncated,
|
|
131
|
+
};
|
|
132
|
+
const evidence = this.putEvidence(session, owner, 'browser', `browser:${browserSessionId}:${observation.observationId}`, payload, safe.sourceHash);
|
|
133
|
+
return { observation, evidence };
|
|
134
|
+
}
|
|
135
|
+
snapshot(ctx, sessionId) {
|
|
136
|
+
this.check();
|
|
137
|
+
liveAccess(ctx, 'dodo:read');
|
|
138
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, false), payload = this.snapshotPayload(ctx);
|
|
139
|
+
return this.putEvidence(session, owner, 'snapshot', `workspace:${this.services.workspaceId}`, payload);
|
|
140
|
+
}
|
|
141
|
+
async evidence(ctx, sessionId, evidenceId, refresh) {
|
|
142
|
+
this.check();
|
|
143
|
+
liveAccess(ctx, 'dodo:read');
|
|
144
|
+
this.recover();
|
|
145
|
+
const owner = actorKey(ctx.principal), session = this.requireSession(sessionId, owner, true);
|
|
146
|
+
let row = this.services.store.db.prepare('SELECT * FROM runtime_evidence WHERE id=? AND session_id=? AND workspace_id=? AND owner=?')
|
|
147
|
+
.get(evidenceId, session.id, this.services.workspaceId, owner);
|
|
148
|
+
if (!row)
|
|
149
|
+
throw new DodoError('NOT_FOUND', 'runtime evidence was not found for this session, workspace and client');
|
|
150
|
+
if (refresh && row.status === 'CURRENT')
|
|
151
|
+
row = await this.refreshEvidence(ctx, row);
|
|
152
|
+
return this.evidenceData(row);
|
|
153
|
+
}
|
|
154
|
+
async diagnose(ctx, sessionId, evidenceIds) {
|
|
155
|
+
this.check();
|
|
156
|
+
liveAccess(ctx, 'dodo:read');
|
|
157
|
+
if (!evidenceIds.length)
|
|
158
|
+
throw new DodoError('INVALID_INPUT', 'provide at least one runtime evidence ID');
|
|
159
|
+
const evidence = await Promise.all(evidenceIds.map((id) => this.evidence(ctx, sessionId, id, true)));
|
|
160
|
+
const facts = [], observations = [], inferences = [];
|
|
161
|
+
for (const item of evidence) {
|
|
162
|
+
facts.push(`${item.kind} evidence ${item.evidenceId} is ${item.status} and source-hash bound`);
|
|
163
|
+
const p = item.payload;
|
|
164
|
+
if (item.kind === 'process' || item.kind === 'test' || item.kind === 'container') {
|
|
165
|
+
observations.push(`task ${String(p['taskId'])} status=${String(p['status'])} exitCode=${String(p['exitCode'])}`);
|
|
166
|
+
if (p['status'] === 'exited' && p['exitCode'] === 0)
|
|
167
|
+
inferences.push({ statement: 'The observed process completed successfully at the OS process level.', confidence: 'high', evidenceIds: [item.evidenceId] });
|
|
168
|
+
else if (p['status'] !== 'running')
|
|
169
|
+
inferences.push({ statement: 'The observed process did not complete successfully; inspect bounded job output before changing code.', confidence: 'high', evidenceIds: [item.evidenceId] });
|
|
170
|
+
}
|
|
171
|
+
else if (item.kind === 'browser') {
|
|
172
|
+
observations.push(`browser observation recorded ${String(p['elementCount'])} elements, ${String(p['consoleCount'])} console events and ${String(p['blockedNetworkCount'])} blocked network requests`);
|
|
173
|
+
if (Number(p['blockedNetworkCount']) > 0)
|
|
174
|
+
inferences.push({ statement: 'Blocked browser requests may explain missing runtime behavior.', confidence: 'medium', evidenceIds: [item.evidenceId] });
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
observations.push(`workspace snapshot contains ${String(p['fileCount'])} guarded file metadata entries and ${String(p['rollbackCandidates']?.length ?? 0)} rollback candidates`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return RuntimeDiagnosis.parse({
|
|
181
|
+
sessionId, facts, observations, inferences,
|
|
182
|
+
limitations: ['Diagnosis is deterministic over bounded metadata and hashes; it does not inspect raw stdout, secrets, cookies, headers, environment values or hidden browser inputs.', 'Process exit success does not by itself prove product behavior; verify source and user-visible state.'],
|
|
183
|
+
trust: 'untrusted_runtime_evidence',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Bounded current evidence used by Context Engine; same caller/workspace only. */
|
|
187
|
+
contextHits(ctx, terms, limit) {
|
|
188
|
+
this.check();
|
|
189
|
+
liveAccess(ctx, 'dodo:read');
|
|
190
|
+
this.recover();
|
|
191
|
+
const owner = actorKey(ctx.principal);
|
|
192
|
+
const rows = this.services.store.db.prepare("SELECT * FROM runtime_evidence WHERE workspace_id=? AND owner=? AND status='CURRENT' AND expires_at>? ORDER BY created_at DESC LIMIT 200")
|
|
193
|
+
.all(this.services.workspaceId, owner, Date.now());
|
|
194
|
+
const wanted = terms.map((term) => term.toLocaleLowerCase('en-US'));
|
|
195
|
+
return rows.map((row) => {
|
|
196
|
+
const payload = this.parsePayload(row.payload), haystack = `${row.kind} ${row.source_ref} ${JSON.stringify(payload)}`.toLocaleLowerCase('en-US');
|
|
197
|
+
const matched = wanted.filter((term) => haystack.includes(term)).length;
|
|
198
|
+
return { row, payload, matched };
|
|
199
|
+
}).filter((item) => wanted.length === 0 || item.matched > 0).sort((a, b) => b.matched - a.matched || b.row.created_at - a.row.created_at).slice(0, limit).map(({ row, payload, matched }) => ({
|
|
200
|
+
evidenceId: row.id,
|
|
201
|
+
claim: this.safeClaim(row.kind, payload),
|
|
202
|
+
contentHash: row.content_hash,
|
|
203
|
+
score: 68 + Math.min(16, matched * 4),
|
|
204
|
+
limitations: ['Bounded runtime metadata; raw command/browser data is intentionally not retained.'],
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
manifest(ctx) {
|
|
208
|
+
this.check();
|
|
209
|
+
liveAccess(ctx, 'dodo:read');
|
|
210
|
+
this.recover();
|
|
211
|
+
const owner = actorKey(ctx.principal);
|
|
212
|
+
const rows = this.services.store.db.prepare("SELECT id,content_hash,status,last_verified_at FROM runtime_evidence WHERE workspace_id=? AND owner=? AND expires_at>? ORDER BY id")
|
|
213
|
+
.all(this.services.workspaceId, owner, Date.now());
|
|
214
|
+
return digestOf(rows);
|
|
215
|
+
}
|
|
216
|
+
dependencyHash(ctx, evidenceId) {
|
|
217
|
+
const owner = actorKey(ctx.principal);
|
|
218
|
+
const row = this.services.store.db.prepare("SELECT content_hash,status,expires_at FROM runtime_evidence WHERE id=? AND workspace_id=? AND owner=?")
|
|
219
|
+
.get(evidenceId, this.services.workspaceId, owner);
|
|
220
|
+
return row && row.status === 'CURRENT' && row.expires_at > Date.now() ? row.content_hash : undefined;
|
|
221
|
+
}
|
|
222
|
+
taskPayload(task, reportPath) {
|
|
223
|
+
const job = this.services.jobs.getJobChecked(task.job_id, this.services.workspaceId);
|
|
224
|
+
if (job.principal !== this.grantForOwner(task.owner))
|
|
225
|
+
throw new DodoError('FORBIDDEN', 'runtime task ownership no longer matches its job');
|
|
226
|
+
const stdout = this.services.jobs.inlineOutput(job.id, this.services.workspaceId, 'stdout', OUTPUT_SAMPLE_BYTES);
|
|
227
|
+
const stderr = this.services.jobs.inlineOutput(job.id, this.services.workspaceId, 'stderr', OUTPUT_SAMPLE_BYTES);
|
|
228
|
+
const payload = {
|
|
229
|
+
taskId: task.id, jobId: job.id, kind: task.kind, status: job.status, exitCode: job.exitCode, signal: job.signal,
|
|
230
|
+
startedAt: job.startedAt, endedAt: job.endedAt, timeoutMs: job.timeoutMs,
|
|
231
|
+
durationMs: job.startedAt !== null && job.endedAt !== null ? Math.max(0, job.endedAt - job.startedAt) : null,
|
|
232
|
+
stdout: { bytes: stdout.totalBytes, sampleSha256: sha256Bytes(`${stdout.content}${stdout.tail ?? ''}`), truncated: stdout.truncated },
|
|
233
|
+
stderr: { bytes: stderr.totalBytes, sampleSha256: sha256Bytes(`${stderr.content}${stderr.tail ?? ''}`), truncated: stderr.truncated },
|
|
234
|
+
};
|
|
235
|
+
if (reportPath)
|
|
236
|
+
payload['testReport'] = this.testReport(reportPath);
|
|
237
|
+
return payload;
|
|
238
|
+
}
|
|
239
|
+
/** Resolve the grant id for a hashed owner through the attached job, without storing or exposing it in evidence. */
|
|
240
|
+
grantForOwner(owner) {
|
|
241
|
+
const row = this.services.store.db.prepare('SELECT j.principal FROM runtime_tasks t JOIN jobs j ON j.id=t.job_id WHERE t.owner=? ORDER BY t.created_at DESC LIMIT 1').get(owner);
|
|
242
|
+
return row?.principal ?? '';
|
|
243
|
+
}
|
|
244
|
+
testReport(reportPath) {
|
|
245
|
+
const file = this.services.wfs.readTextFile(reportPath, 2 * 1024 * 1024);
|
|
246
|
+
let value;
|
|
247
|
+
try {
|
|
248
|
+
value = JSON.parse(file.text);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw new DodoError('INVALID_INPUT', 'test report must be valid JSON');
|
|
252
|
+
}
|
|
253
|
+
const root = value && typeof value === 'object' ? value : {};
|
|
254
|
+
const number = (...keys) => { for (const key of keys)
|
|
255
|
+
if (typeof root[key] === 'number' && Number.isFinite(root[key]))
|
|
256
|
+
return Math.max(0, Math.trunc(root[key])); return null; };
|
|
257
|
+
return { path: file.rel, hash: file.hash, total: number('numTotalTests', 'total'), passed: number('numPassedTests', 'passed'), failed: number('numFailedTests', 'failed'), skipped: number('numPendingTests', 'skipped'), success: typeof root['success'] === 'boolean' ? root['success'] : null };
|
|
258
|
+
}
|
|
259
|
+
snapshotPayload(ctx) {
|
|
260
|
+
const limit = Math.min(10_000, this.services.limits.semanticFilesMax * 4), files = [];
|
|
261
|
+
for (const entry of this.services.wfs.walk({ maxEntries: limit + 1, maxDepth: 64 })) {
|
|
262
|
+
if (entry.stat.isFile())
|
|
263
|
+
files.push({ path: entry.rel, bytes: entry.stat.size, mtimeMs: entry.stat.mtimeMs, ctimeMs: entry.stat.ctimeMs });
|
|
264
|
+
if (files.length > limit)
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
const visible = files.slice(0, limit), manifestHash = digestOf(visible);
|
|
268
|
+
const rollbackCandidates = this.services.store.listChangesets(this.services.workspaceId, 50)
|
|
269
|
+
.filter((change) => change.principal === ctx.principal.grantId && change.kind === 'apply' && change.status === 'committed')
|
|
270
|
+
.map((change) => ({ changesetId: change.id, createdAt: change.createdAt, summaryHash: sha256Bytes(change.summary ?? '') }));
|
|
271
|
+
return { manifestHash, fileCount: visible.length, truncated: files.length > limit, rollbackCandidates, note: 'Snapshot stores guarded file metadata hash only. Rollback remains an explicit rollback_changes call with its existing conflict checks.' };
|
|
272
|
+
}
|
|
273
|
+
async refreshEvidence(ctx, row) {
|
|
274
|
+
let payload, sourceHash, reason = null;
|
|
275
|
+
try {
|
|
276
|
+
if (row.kind === 'process' || row.kind === 'test' || row.kind === 'container') {
|
|
277
|
+
const taskId = String(this.parsePayload(row.payload)['taskId']);
|
|
278
|
+
const task = this.requireTask(row.session_id, taskId, row.owner);
|
|
279
|
+
payload = this.taskPayload(task, typeof this.parsePayload(row.payload)['testReport']?.['path'] === 'string' ? String(this.parsePayload(row.payload)['testReport']['path']) : undefined);
|
|
280
|
+
}
|
|
281
|
+
else if (row.kind === 'browser') {
|
|
282
|
+
const old = this.parsePayload(row.payload), browser = this.services.multimodal?.browser;
|
|
283
|
+
if (!browser)
|
|
284
|
+
throw new DodoError('NOT_SUPPORTED', 'browser unavailable');
|
|
285
|
+
const verified = await browser.verifyObservation(ctx.principal, String(old['browserSessionId']), String(old['observationId']));
|
|
286
|
+
sourceHash = verified.sourceHash;
|
|
287
|
+
payload = old;
|
|
288
|
+
}
|
|
289
|
+
else
|
|
290
|
+
payload = this.snapshotPayload(ctx);
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
reason = `source recheck failed (${toDodoError(error).code})`;
|
|
294
|
+
}
|
|
295
|
+
const nextSource = sourceHash ?? (payload ? digestOf(payload) : row.source_hash);
|
|
296
|
+
const status = reason || nextSource !== row.source_hash ? 'STALE' : 'CURRENT';
|
|
297
|
+
if (!reason && status === 'STALE')
|
|
298
|
+
reason = 'source hash changed since evidence was recorded';
|
|
299
|
+
// Evidence is immutable: a changed source marks the original record stale.
|
|
300
|
+
// A fresh runtime_task_observe/runtime_snapshot call creates a new record.
|
|
301
|
+
this.services.store.db.prepare('UPDATE runtime_evidence SET status=?,stale_reason=?,last_verified_at=? WHERE id=?')
|
|
302
|
+
.run(status, reason, Date.now(), row.id);
|
|
303
|
+
return this.services.store.db.prepare('SELECT * FROM runtime_evidence WHERE id=?').get(row.id);
|
|
304
|
+
}
|
|
305
|
+
putEvidence(session, owner, kind, sourceRef, payload, explicitSourceHash) {
|
|
306
|
+
this.enforceEvidenceQuota(session.id, owner);
|
|
307
|
+
const sourceHash = explicitSourceHash ?? digestOf(payload), contentHash = digestOf(payload), now = Date.now();
|
|
308
|
+
const existing = this.services.store.db.prepare('SELECT * FROM runtime_evidence WHERE session_id=? AND owner=? AND kind=? AND source_ref=? AND source_hash=?')
|
|
309
|
+
.get(session.id, owner, kind, sourceRef, sourceHash);
|
|
310
|
+
if (existing) {
|
|
311
|
+
this.services.store.db.prepare('UPDATE runtime_evidence SET last_verified_at=? WHERE id=?').run(now, existing.id);
|
|
312
|
+
return this.evidenceData({ ...existing, last_verified_at: now });
|
|
313
|
+
}
|
|
314
|
+
const id = newId('runtimeev');
|
|
315
|
+
this.services.store.db.prepare(`INSERT INTO runtime_evidence(id,session_id,workspace_id,owner,kind,payload,source_ref,source_hash,content_hash,status,stale_reason,created_at,last_verified_at,expires_at)
|
|
316
|
+
VALUES (?,?,?,?,?,?,?,?,?,'CURRENT',NULL,?,?,?)`).run(id, session.id, this.services.workspaceId, owner, kind, JSON.stringify(payload), sourceRef, sourceHash, contentHash, now, now, now + EVIDENCE_TTL_MS);
|
|
317
|
+
return this.evidenceData(this.services.store.db.prepare('SELECT * FROM runtime_evidence WHERE id=?').get(id));
|
|
318
|
+
}
|
|
319
|
+
enforceEvidenceQuota(sessionId, owner) {
|
|
320
|
+
const local = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM runtime_evidence WHERE session_id=? AND owner=?').get(sessionId, owner).n;
|
|
321
|
+
const total = this.services.store.db.prepare('SELECT COUNT(*) AS n FROM runtime_evidence WHERE workspace_id=? AND owner=?').get(this.services.workspaceId, owner).n;
|
|
322
|
+
if (local >= MAX_EVIDENCE_PER_SESSION || total >= MAX_EVIDENCE_TOTAL)
|
|
323
|
+
throw new DodoError('RESOURCE_LIMIT', 'runtime evidence retention quota reached; close old sessions or wait for expiry');
|
|
324
|
+
}
|
|
325
|
+
row(id, owner) { return this.services.store.db.prepare('SELECT * FROM runtime_sessions WHERE id=? AND workspace_id=? AND owner=?').get(id, this.services.workspaceId, owner); }
|
|
326
|
+
requireSession(id, owner, allowClosed) {
|
|
327
|
+
const row = this.row(id, owner);
|
|
328
|
+
if (!row)
|
|
329
|
+
throw new DodoError('NOT_FOUND', 'runtime session was not found for this workspace and client');
|
|
330
|
+
if (!allowClosed && row.status !== 'OPEN')
|
|
331
|
+
throw new DodoError('CONFLICT', `runtime session is ${row.status.toLowerCase()}`);
|
|
332
|
+
if (row.status === 'OPEN')
|
|
333
|
+
this.services.store.db.prepare('UPDATE runtime_sessions SET last_seen_at=? WHERE id=?').run(Date.now(), id);
|
|
334
|
+
return { ...row, last_seen_at: row.status === 'OPEN' ? Date.now() : row.last_seen_at };
|
|
335
|
+
}
|
|
336
|
+
taskRows(sessionId, owner) { return this.services.store.db.prepare('SELECT * FROM runtime_tasks WHERE session_id=? AND workspace_id=? AND owner=? ORDER BY created_at DESC').all(sessionId, this.services.workspaceId, owner); }
|
|
337
|
+
requireTask(sessionId, id, owner) { const row = this.services.store.db.prepare('SELECT * FROM runtime_tasks WHERE id=? AND session_id=? AND workspace_id=? AND owner=?').get(id, sessionId, this.services.workspaceId, owner); if (!row)
|
|
338
|
+
throw new DodoError('NOT_FOUND', 'runtime task was not found for this session, workspace and client'); return row; }
|
|
339
|
+
sessionData(row) { return RuntimeSession.parse({ schemaVersion: RUNTIME_SCHEMA_VERSION, sessionId: row.id, label: row.label, status: row.status, workspaceId: row.workspace_id, openedEpoch: row.opened_epoch, createdAt: row.created_at, lastSeenAt: row.last_seen_at, expiresAt: row.expires_at, closedAt: row.closed_at }); }
|
|
340
|
+
taskData(row) { const job = this.services.jobs.getJobChecked(row.job_id, this.services.workspaceId); return RuntimeTask.parse({ taskId: row.id, sessionId: row.session_id, jobId: row.job_id, kind: row.kind, status: job.status, exitCode: job.exitCode, signal: job.signal, createdAt: row.created_at, startedAt: job.startedAt, endedAt: job.endedAt, timeoutMs: job.timeoutMs }); }
|
|
341
|
+
evidenceData(row) { return RuntimeEvidence.parse({ schemaVersion: RUNTIME_SCHEMA_VERSION, evidenceId: row.id, sessionId: row.session_id, kind: row.kind, status: row.status, sourceRef: row.source_ref, sourceHash: row.source_hash, contentHash: row.content_hash, payload: this.parsePayload(row.payload), staleReason: row.stale_reason, createdAt: row.created_at, lastVerifiedAt: row.last_verified_at, expiresAt: row.expires_at, trust: 'untrusted_runtime_evidence' }); }
|
|
342
|
+
parsePayload(value) { try {
|
|
343
|
+
const parsed = JSON.parse(value);
|
|
344
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return {};
|
|
348
|
+
} }
|
|
349
|
+
safeClaim(kind, payload) { if (kind === 'snapshot')
|
|
350
|
+
return `Runtime snapshot: ${String(payload['fileCount'])} guarded files; manifest ${String(payload['manifestHash'])}`; if (kind === 'browser')
|
|
351
|
+
return `Browser runtime observation: ${String(payload['elementCount'])} elements, ${String(payload['consoleCount'])} console events, ${String(payload['blockedNetworkCount'])} blocked requests`; return `${kind} runtime task ${String(payload['taskId'])}: status ${String(payload['status'])}, exit ${String(payload['exitCode'])}`; }
|
|
352
|
+
recover() {
|
|
353
|
+
const now = Date.now();
|
|
354
|
+
this.services.store.db.prepare("UPDATE runtime_sessions SET status='EXPIRED',closed_at=? WHERE status='OPEN' AND expires_at<=?").run(now, now);
|
|
355
|
+
// Expired evidence handles cease to exist and free their bounded quota.
|
|
356
|
+
this.services.store.db.prepare('DELETE FROM runtime_evidence WHERE expires_at<=?').run(now);
|
|
357
|
+
// Keep closed/expired session metadata for one evidence-retention window, then
|
|
358
|
+
// cascade task/evidence references. Jobs themselves remain in the job store.
|
|
359
|
+
this.services.store.db.prepare("DELETE FROM runtime_sessions WHERE status IN ('CLOSED','EXPIRED') AND COALESCE(closed_at,expires_at)<=?").run(now - EVIDENCE_TTL_MS);
|
|
360
|
+
}
|
|
361
|
+
check() { if (this.closed)
|
|
362
|
+
throw new DodoError('STALE_WORKSPACE', 'runtime workspace is closed'); }
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=runtimeService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeService.js","sourceRoot":"","sources":["../../../src/services/runtime/runtimeService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,WAAW,GAIZ,MAAM,gBAAgB,CAAC;AAExB,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC1C,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5C,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,wBAAwB,GAAG,GAAG,CAAC;AACrC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAatC,+IAA+I;AAC/I,MAAM,OAAO,cAAc;IAGI;IAFrB,MAAM,GAAG,KAAK,CAAC;IAEvB,YAA6B,QAAqB;QAArB,aAAQ,GAAR,QAAQ,CAAa;QAAI,IAAI,CAAC,OAAO,EAAE,CAAC;IAAC,CAAC;IAEvE,KAAK,KAAW,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAErC,IAAI,CAAC,GAAY,EAAE,KAAa,EAAE,UAAkB;QAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,KAAK,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,+FAA+F,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC;QACzM,IAAI,KAAK,IAAI,YAAY;YAAE,MAAM,IAAI,SAAS,CAAC,gBAAgB,EAAE,WAAW,YAAY,iDAAiD,CAAC,CAAC;QAC3I,MAAM,QAAQ,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,6EAA6E,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC;QAC1L,IAAI,QAAQ,IAAI,qBAAqB;YAAE,MAAM,IAAI,SAAS,CAAC,gBAAgB,EAAE,WAAW,qBAAqB,iFAAiF,CAAC,CAAC;QAChM,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,IAAI,kBAAkB,CAAC,CAAC,CAAC;QACvJ,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC;2CACQ,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;QACpI,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,GAAY,EAAE,SAAiB;QACpC,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7F,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,kGAAkG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAyC,CAAC;QACjN,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,oBAAoB,CAAC,KAAK,CAAC;YAChC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK;YACzC,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;YAC3G,aAAa,EAAE,IAAI;YACnB,IAAI,EAAE,wIAAwI;SAC/I,CAAC,CAAC;IACL,CAAC;IAED,WAAW,CAAC,GAAY;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC;;;gHAGiE,CAAC,CAAC,GAAG,CAC7G,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAC5C,CAAC;QAC7D,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;IACrJ,CAAC;IAED,YAAY,CAAC,GAAY,EAAE,SAAiB;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7F,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAClE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,SAAS,CAAC,UAAU,EAAE,4BAA4B,CAAC,CAAC;QAChG,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QACjK,IAAI,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,UAAU,EAAE,GAAG,OAAO,CAAC,MAAM,oCAAoC,EAAE,EAAE,QAAQ,EAAE,wFAAwF,EAAE,CAAC,CAAC;QACnN,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,+FAA+F,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACjK,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAE,CAAC,CAAC;IACxD,CAAC;IAED,WAAW,CAAC,GAAY,EAAE,SAAiB;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IACjE,CAAC;IAED,UAAU,CAAC,GAAY,EAAE,SAAiB,EAAE,KAAa,EAAE,IAAqB;QAC9E,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC9F,MAAM,KAAK,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,wEAAwE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC;QACnK,IAAI,KAAK,IAAI,SAAS;YAAE,MAAM,IAAI,SAAS,CAAC,gBAAgB,EAAE,WAAW,SAAS,4BAA4B,CAAC,CAAC;QAChH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC/E,IAAI,GAAG,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS,CAAC,OAAO;YAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,mEAAmE,CAAC,CAAC;QACnJ,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2GAA2G,CAAC;aACxI,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7I,CAAC;IAED,WAAW,CAAC,GAAY,EAAE,SAAiB,EAAE,MAAc,EAAE,UAAmB;QAC9E,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAClJ,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACnD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;IACzK,CAAC;IAED,UAAU,CAAC,GAAY,EAAE,SAAiB,EAAE,MAAc;QACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAClJ,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACjF,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,GAAY,EAAE,SAAiB,EAAE,gBAAwB;QAC5E,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;QAClD,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,yDAAyD,CAAC,CAAC;QAC9G,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;QAC3E,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAgB,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;QACzG,MAAM,OAAO,GAAG;YACd,gBAAgB,EAAE,aAAa,EAAE,WAAW,CAAC,aAAa,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG;YACzE,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC3I,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM;YACzH,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM;YAClF,mBAAmB,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;YAC/F,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS;SAClF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,gBAAgB,IAAI,WAAW,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACnJ,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IACnC,CAAC;IAED,QAAQ,CAAC,GAAY,EAAE,SAAiB;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACnI,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;IACzG,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAY,EAAE,SAAiB,EAAE,UAAkB,EAAE,OAAgB;QAClF,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7F,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2FAA2F,CAAC;aAClI,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAA4B,CAAC;QAC5F,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,uEAAuE,CAAC,CAAC;QACpH,IAAI,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,GAAG,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpF,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAY,EAAE,SAAiB,EAAE,WAAqB;QACnE,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,0CAA0C,CAAC,CAAC;QAC1G,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACrG,MAAM,KAAK,GAAa,EAAE,EAAE,YAAY,GAAa,EAAE,EAAE,UAAU,GAA+F,EAAE,CAAC;QACrK,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,UAAU,OAAO,IAAI,CAAC,MAAM,wBAAwB,CAAC,CAAC;YAC/F,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YACvB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACjF,YAAY,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,aAAa,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;gBACjH,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,sEAAsE,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;qBAC3M,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,SAAS;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,sGAAsG,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACjO,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACnC,YAAY,CAAC,IAAI,CAAC,gCAAgC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,cAAc,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,uBAAuB,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,2BAA2B,CAAC,CAAC;gBACtM,IAAI,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,gEAAgE,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACnM,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,IAAI,CAAC,+BAA+B,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,sCAAsC,MAAM,CAAE,CAAC,CAAC,oBAAoB,CAA2B,EAAE,MAAM,IAAI,CAAC,CAAC,sBAAsB,CAAC,CAAC;YAC9M,CAAC;QACH,CAAC;QACD,OAAO,gBAAgB,CAAC,KAAK,CAAC;YAC5B,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU;YAC1C,WAAW,EAAE,CAAC,sKAAsK,EAAE,uGAAuG,CAAC;YAC9R,KAAK,EAAE,4BAA4B;SACpC,CAAC,CAAC;IACL,CAAC;IAED,mFAAmF;IACnF,WAAW,CAAC,GAAY,EAAE,KAAe,EAAE,KAAa;QACtD,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,0IAA0I,CAAC;aACpL,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAkB,CAAC;QACtE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACtB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACjJ,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;YACxE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5L,UAAU,EAAE,GAAG,CAAC,EAAE;YAClB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC;YACxC,WAAW,EAAE,GAAG,CAAC,YAAY;YAC7B,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;YACrC,WAAW,EAAE,CAAC,mFAAmF,CAAC;SACnG,CAAC,CAAC,CAAC;IACN,CAAC;IAED,QAAQ,CAAC,GAAY;QACnB,IAAI,CAAC,KAAK,EAAE,CAAC;QAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAAC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAClG,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,oIAAoI,CAAC;aAC9K,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACrD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,cAAc,CAAC,GAAY,EAAE,UAAkB;QAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,uGAAuG,CAAC;aAChJ,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAA6E,CAAC;QACjI,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IACvG,CAAC;IAEO,WAAW,CAAC,IAAa,EAAE,UAAmB;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACrF,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,kDAAkD,CAAC,CAAC;QAC3I,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACjH,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACjH,MAAM,OAAO,GAA4B;YACvC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM;YAC/G,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS;YACxE,UAAU,EAAE,GAAG,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI;YAC5G,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,WAAW,CAAC,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE;YACrI,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,WAAW,CAAC,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE;SACtI,CAAC;QACF,IAAI,UAAU;YAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACpE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,oHAAoH;IAC5G,aAAa,CAAC,KAAa;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,yHAAyH,CAAC,CAAC,GAAG,CAAC,KAAK,CAAsC,CAAC;QACtN,OAAO,GAAG,EAAE,SAAS,IAAI,EAAE,CAAC;IAC9B,CAAC;IAEO,UAAU,CAAC,UAAkB;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QACzE,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,gCAAgC,CAAC,CAAC;QAAC,CAAC;QACxH,MAAM,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAgC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,MAAM,MAAM,GAAG,CAAC,GAAG,IAAc,EAAiB,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAW,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACpN,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrS,CAAC;IAEO,eAAe,CAAC,GAAY;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,GAA6E,EAAE,CAAC;QAChK,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YACpF,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3I,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK;gBAAE,MAAM;QAClC,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxE,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;aACzF,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC;aAC1H,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9H,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,wIAAwI,EAAE,CAAC;IAC1P,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,GAAY,EAAE,GAAgB;QAC1D,IAAI,OAA4C,EAAE,UAA8B,EAAE,MAAM,GAAkB,IAAI,CAAC;QAC/G,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAChE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;gBACjE,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,OAAQ,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,CAAyC,EAAE,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,CAA6B,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAChQ,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAClC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;gBACxF,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,qBAAqB,CAAC,CAAC;gBAC1E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;gBAC/H,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;gBACjC,OAAO,GAAG,GAAG,CAAC;YAChB,CAAC;;gBAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,MAAM,GAAG,0BAA0B,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC;QAAC,CAAC;QAClF,MAAM,UAAU,GAAG,UAAU,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,IAAI,UAAU,KAAK,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,GAAG,iDAAiD,CAAC;QAC9F,2EAA2E;QAC3E,2EAA2E;QAC3E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,mFAAmF,CAAC;aAChH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAgB,CAAC;IAChH,CAAC;IAEO,WAAW,CAAC,OAAmB,EAAE,KAAa,EAAE,IAAiC,EAAE,SAAiB,EAAE,OAAgC,EAAE,kBAA2B;QACzK,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,kBAAkB,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9G,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,6GAA6G,CAAC;aAC3J,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,CAA4B,CAAC;QAClF,IAAI,QAAQ,EAAE,CAAC;YAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC,YAAY,CAAC,EAAE,GAAG,QAAQ,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;QAAC,CAAC;QACtM,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC;sDACmB,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,eAAe,CAAC,CAAC;QAC9M,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,EAAE,CAAgB,CAAC,CAAC;IAC/H,CAAC;IAEO,oBAAoB,CAAC,SAAiB,EAAE,KAAa;QAC3D,MAAM,KAAK,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,2EAA2E,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC;QACrK,MAAM,KAAK,GAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,6EAA6E,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAmB,CAAC,CAAC,CAAC;QACvL,IAAI,KAAK,IAAI,wBAAwB,IAAI,KAAK,IAAI,kBAAkB;YAAE,MAAM,IAAI,SAAS,CAAC,gBAAgB,EAAE,iFAAiF,CAAC,CAAC;IACjM,CAAC;IAEO,GAAG,CAAC,EAAU,EAAE,KAAa,IAA4B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,0EAA0E,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAA2B,CAAC,CAAC,CAAC;IACjP,cAAc,CAAC,EAAU,EAAE,KAAa,EAAE,WAAoB;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAAC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,6DAA6D,CAAC,CAAC;QAC3I,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,UAAU,EAAE,sBAAsB,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC7H,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;YAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACvI,OAAO,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IACzF,CAAC;IACO,QAAQ,CAAC,SAAiB,EAAE,KAAa,IAAe,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,wGAAwG,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAc,CAAC,CAAC,CAAC;IACxQ,WAAW,CAAC,SAAiB,EAAE,EAAU,EAAE,KAAa,IAAa,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,wFAAwF,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAwB,CAAC,CAAC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,mEAAmE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IACrZ,WAAW,CAAC,GAAe,IAAwB,OAAO,cAAc,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,sBAAsB,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;IAChW,QAAQ,CAAC,GAAY,IAAqB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;IACjZ,YAAY,CAAC,GAAgB,IAAyB,OAAO,eAAe,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,sBAAsB,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,CAAC,gBAAgB,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC,CAAC,CAAC;IACnf,YAAY,CAAC,KAAa,IAA6B,IAAI,CAAC;QAAC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAiC,CAAC,CAAC,CAAC,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC,CAAC,CAAC;IACvO,SAAS,CAAC,IAAiC,EAAE,OAAgC,IAAY,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,qBAAqB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,4BAA4B,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,gCAAgC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,cAAc,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,oBAAoB,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI,iBAAiB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,YAAY,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1kB,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,gGAAgG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/I,wEAAwE;QACxE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5F,+EAA+E;QAC/E,6EAA6E;QAC7E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,yGAAyG,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,eAAe,CAAC,CAAC;IACvK,CAAC;IACO,KAAK,KAAW,IAAI,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,iBAAiB,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;CAClH"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { encodeFileIdentity } from '../../platform/fileIdentity.js';
|
|
4
|
+
import { sandboxAvailability } from '../jobs/sandbox.js';
|
|
5
|
+
import { CronExpressionParser } from 'cron-parser';
|
|
6
|
+
import { DodoError } from '../../errors.js';
|
|
7
|
+
import { digestOf, newId } from '../../util/hash.js';
|
|
8
|
+
import { validateShellCommand } from '../jobs/commandInput.js';
|
|
9
|
+
export const ScheduleInput = z.object({
|
|
10
|
+
name: z.string().min(1).max(120),
|
|
11
|
+
command: z.string().min(1).max(64 * 1024),
|
|
12
|
+
cwd: z.string().max(1024).default('.'),
|
|
13
|
+
cron: z.string().min(9).max(128).describe('Five-field cron, minute precision'),
|
|
14
|
+
timezone: z.string().min(1).max(100).default('UTC'),
|
|
15
|
+
expiresAt: z.number().int().positive().describe('Unix milliseconds; at most 30 days ahead'),
|
|
16
|
+
timeoutMs: z.number().int().min(1000).max(30 * 60_000).default(300_000),
|
|
17
|
+
sandbox: z.boolean().default(true),
|
|
18
|
+
network: z.boolean().default(false),
|
|
19
|
+
}).strict();
|
|
20
|
+
/** Internal timer; no OS cron entries or external services. Only approved active-root jobs run. */
|
|
21
|
+
export class ScheduleService {
|
|
22
|
+
s;
|
|
23
|
+
timer;
|
|
24
|
+
bootedAt = Date.now();
|
|
25
|
+
constructor(s) {
|
|
26
|
+
this.s = s;
|
|
27
|
+
}
|
|
28
|
+
policy() {
|
|
29
|
+
const row = this.s.store.db.prepare('SELECT max(id) AS id FROM policy_versions WHERE workspace_id=?').get(this.s.workspaceId);
|
|
30
|
+
return digestOf({ config: this.s.config, policyVersion: row });
|
|
31
|
+
}
|
|
32
|
+
next(spec, from) {
|
|
33
|
+
try {
|
|
34
|
+
if (spec.cron.trim().split(/\s+/).length !== 5)
|
|
35
|
+
throw new Error('five fields required');
|
|
36
|
+
new Intl.DateTimeFormat('en', { timeZone: spec.timezone });
|
|
37
|
+
return CronExpressionParser.parse(spec.cron, { tz: spec.timezone, currentDate: from }).next().getTime();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new DodoError('INVALID_INPUT', 'invalid five-field cron or IANA timezone');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
propose(input, principal) {
|
|
44
|
+
const spec = ScheduleInput.parse(input);
|
|
45
|
+
if (process.platform === 'win32' && spec.sandbox && !sandboxAvailability(process.env, undefined, this.s.wfs.root).available)
|
|
46
|
+
throw new DodoError('NOT_SUPPORTED', 'Windows sandbox is not ready; run dodo setup --components sandbox locally and pass its confinement probes. Alternatively the owner may explicitly request sandbox:false and approve that separate schedule. No policy was downgraded.');
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (spec.expiresAt <= now || spec.expiresAt > now + 30 * 86400_000)
|
|
49
|
+
throw new DodoError('INVALID_INPUT', 'schedule consent must expire within 30 days');
|
|
50
|
+
validateShellCommand(spec.command, Math.min(64 * 1024, this.s.limits.commandBytes));
|
|
51
|
+
const cwd = this.s.wfs.resolve(spec.cwd);
|
|
52
|
+
if (!cwd.stat?.isDirectory())
|
|
53
|
+
throw new DodoError('PATH_DENIED', 'schedule cwd must be a workspace directory');
|
|
54
|
+
spec.cwd = cwd.rel;
|
|
55
|
+
if (this.next(spec, now) >= spec.expiresAt)
|
|
56
|
+
throw new DodoError('INVALID_INPUT', 'no scheduled occurrence before expiry');
|
|
57
|
+
const n = this.s.store.db.prepare("SELECT count(*) AS n FROM schedules WHERE workspace_id=? AND status IN ('pending','approved') AND expires_at>?").get(this.s.workspaceId, now);
|
|
58
|
+
if (n.n >= 64)
|
|
59
|
+
throw new DodoError('RESOURCE_LIMIT', 'at most 64 pending/approved schedules per workspace');
|
|
60
|
+
const root = this.s.store.getWorkspace(this.s.workspaceId);
|
|
61
|
+
const payload = { spec, root: root.root, dev: root.dev, ino: root.ino, policy: this.policy(), clientId: principal?.clientId ?? null, grantId: principal?.grantId ?? null };
|
|
62
|
+
const id = newId('sched', 24);
|
|
63
|
+
const digest = digestOf(payload);
|
|
64
|
+
this.s.store.db.prepare('INSERT INTO schedules VALUES (?,?,?,?,?,?,?,?,NULL)').run(id, this.s.workspaceId, JSON.stringify(payload), digest, 'pending', now, spec.expiresAt, null);
|
|
65
|
+
this.audit(id, 'schedule.propose');
|
|
66
|
+
return this.inspect(id);
|
|
67
|
+
}
|
|
68
|
+
row(id) {
|
|
69
|
+
const r = this.s.store.db.prepare('SELECT * FROM schedules WHERE id=? AND workspace_id=?').get(id, this.s.workspaceId);
|
|
70
|
+
if (!r)
|
|
71
|
+
throw new DodoError('NOT_FOUND', 'unknown schedule');
|
|
72
|
+
return r;
|
|
73
|
+
}
|
|
74
|
+
checked(r) {
|
|
75
|
+
const p = JSON.parse(r.payload);
|
|
76
|
+
if (digestOf(p) !== r.digest)
|
|
77
|
+
throw new DodoError('CONFLICT', 'schedule content changed since review');
|
|
78
|
+
const root = this.s.store.getWorkspace(this.s.workspaceId);
|
|
79
|
+
const liveRoot = fs.statSync(this.s.wfs.resolve('.').abs, { bigint: true });
|
|
80
|
+
if (!liveRoot.isDirectory() || encodeFileIdentity(liveRoot.dev) !== p.dev || encodeFileIdentity(liveRoot.ino) !== p.ino)
|
|
81
|
+
throw new DodoError('FORBIDDEN', 'workspace directory identity changed');
|
|
82
|
+
if (root.root !== p.root || root.dev !== p.dev || root.ino !== p.ino || p.policy !== this.policy() || this.s.store.trustMode(this.s.workspaceId) !== 'trusted')
|
|
83
|
+
throw new DodoError('FORBIDDEN', 'root or policy changed, or saved trust is not trusted; propose and approve again');
|
|
84
|
+
if (p.clientId !== null && p.clientId !== 'stdio') {
|
|
85
|
+
const g = p.grantId ? this.s.store.getGrant(p.grantId) : undefined;
|
|
86
|
+
if (!g || !this.s.store.oauthFind('Grant', p.grantId) || g.revokedAt !== null || !g.scopes.includes('dodo:exec') || !this.s.store.clientAccess(this.s.workspaceId, p.clientId).includes('dodo:exec'))
|
|
87
|
+
throw new DodoError('FORBIDDEN', 'schedule client grant/access revoked');
|
|
88
|
+
}
|
|
89
|
+
return p;
|
|
90
|
+
}
|
|
91
|
+
inspect(id) {
|
|
92
|
+
const r = this.row(id);
|
|
93
|
+
const p = JSON.parse(r.payload);
|
|
94
|
+
return { id: r.id, digest: r.digest, status: r.expires_at <= Date.now() ? 'expired' : r.status, ...p, nextAt: r.next_at, expiresAt: r.expires_at, warning: 'Runs current project code with OS-user privileges; command hash does not pin dependencies or project contents.' };
|
|
95
|
+
}
|
|
96
|
+
list() {
|
|
97
|
+
return this.s.store.db.prepare('SELECT id FROM schedules WHERE workspace_id=? ORDER BY created_at DESC LIMIT 100').all(this.s.workspaceId).map(r => this.inspect(r.id));
|
|
98
|
+
}
|
|
99
|
+
approve(id, digest) {
|
|
100
|
+
const r = this.row(id);
|
|
101
|
+
if (r.status !== 'pending' || r.expires_at <= Date.now() || digest !== r.digest)
|
|
102
|
+
throw new DodoError('CONFLICT', 'schedule expired, decided or review hash mismatch');
|
|
103
|
+
const p = this.checked(r);
|
|
104
|
+
const next = this.next(p.spec, Date.now());
|
|
105
|
+
if (next >= r.expires_at)
|
|
106
|
+
throw new DodoError('INVALID_INPUT', 'no occurrence before expiry');
|
|
107
|
+
this.s.store.db.prepare("UPDATE schedules SET status='approved', approved_at=?, next_at=? WHERE id=?").run(Date.now(), next, id);
|
|
108
|
+
this.audit(id, 'schedule.approve');
|
|
109
|
+
return this.inspect(id);
|
|
110
|
+
}
|
|
111
|
+
revoke(id) {
|
|
112
|
+
this.row(id);
|
|
113
|
+
this.s.store.db.prepare("UPDATE schedules SET status='revoked', next_at=NULL WHERE id=?").run(id);
|
|
114
|
+
const owned = this.s.store.db.prepare("SELECT id FROM jobs WHERE workspace_id=? AND epoch=? AND principal=? AND status='running'").all(this.s.workspaceId, this.s.epoch, `schedule:${id}`);
|
|
115
|
+
for (const j of owned)
|
|
116
|
+
this.s.jobs.cancel(j.id, this.s.workspaceId, 500);
|
|
117
|
+
this.audit(id, 'schedule.revoke');
|
|
118
|
+
return { id, status: 'revoked' };
|
|
119
|
+
}
|
|
120
|
+
history(id) {
|
|
121
|
+
this.row(id);
|
|
122
|
+
const runs = this.s.store.db.prepare('SELECT * FROM schedule_runs WHERE schedule_id=? ORDER BY due_at DESC LIMIT 50').all(id);
|
|
123
|
+
return runs.map(r => ({ ...r, job: r.job_id ? this.s.store.getJob(r.job_id) ?? null : null }));
|
|
124
|
+
}
|
|
125
|
+
start(ready) {
|
|
126
|
+
if (this.timer)
|
|
127
|
+
return;
|
|
128
|
+
this.bootedAt = Date.now();
|
|
129
|
+
this.timer = setInterval(() => { if (ready()) {
|
|
130
|
+
try {
|
|
131
|
+
this.tick();
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
this.stop();
|
|
135
|
+
console.error('[dodo] scheduler stopped after a state error; inspect schedules locally before restarting');
|
|
136
|
+
}
|
|
137
|
+
} }, 1000);
|
|
138
|
+
this.timer.unref();
|
|
139
|
+
}
|
|
140
|
+
stop() { if (this.timer)
|
|
141
|
+
clearInterval(this.timer); this.timer = undefined; }
|
|
142
|
+
/** Synchronous claim->spawn: revoke cannot race between permission check and launch. */
|
|
143
|
+
tick(now = Date.now()) {
|
|
144
|
+
const rows = this.s.store.db.prepare("SELECT * FROM schedules WHERE workspace_id=? AND status='approved' ORDER BY next_at LIMIT 64").all(this.s.workspaceId);
|
|
145
|
+
for (const row of rows) {
|
|
146
|
+
if (row.expires_at <= now) {
|
|
147
|
+
this.revoke(row.id);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (row.next_at === null || row.next_at > now)
|
|
151
|
+
continue;
|
|
152
|
+
let p;
|
|
153
|
+
try {
|
|
154
|
+
p = this.checked(row);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
this.s.store.db.prepare("UPDATE schedules SET status='paused', next_at=NULL WHERE id=?").run(row.id);
|
|
158
|
+
this.audit(row.id, 'schedule.policy-paused');
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// A schedule's own overlap still gets a receipt; other mutations defer
|
|
162
|
+
// this tick without claiming it, so no command bypasses the shared queue.
|
|
163
|
+
const ownRunning = !!this.s.store.db.prepare("SELECT id FROM jobs WHERE workspace_id=? AND principal=? AND status='running' LIMIT 1").get(this.s.workspaceId, `schedule:${row.id}`);
|
|
164
|
+
if (this.s.jobs.mutations?.busy && !ownRunning)
|
|
165
|
+
continue;
|
|
166
|
+
const due = row.next_at;
|
|
167
|
+
const next = this.next(p.spec, now);
|
|
168
|
+
const claimed = this.s.store.db.transaction(() => {
|
|
169
|
+
const update = this.s.store.db.prepare("UPDATE schedules SET next_at=? WHERE id=? AND status='approved' AND next_at=?").run(next, row.id, due);
|
|
170
|
+
if (!update.changes)
|
|
171
|
+
return false;
|
|
172
|
+
return !!this.s.store.db.prepare("INSERT OR IGNORE INTO schedule_runs VALUES (?,?,'claimed',NULL,NULL)").run(row.id, due).changes;
|
|
173
|
+
}).immediate();
|
|
174
|
+
if (!claimed)
|
|
175
|
+
continue;
|
|
176
|
+
const finish = (status, jobId = null, error = null) => this.s.store.db.prepare('UPDATE schedule_runs SET status=?,job_id=?,error=? WHERE schedule_id=? AND due_at=?').run(status, jobId, error, row.id, due);
|
|
177
|
+
// Missed ticks, including downtime, are skipped. Claimed/uncertain ticks are never replayed.
|
|
178
|
+
if (due < this.bootedAt || now - due > 60_000) {
|
|
179
|
+
finish('skipped_missed');
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (this.s.store.db.prepare("SELECT id FROM jobs WHERE workspace_id=? AND principal=? AND status='running' LIMIT 1").get(this.s.workspaceId, `schedule:${row.id}`)) {
|
|
183
|
+
finish('skipped_overlap');
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const job = this.s.jobs.start({ workspaceId: this.s.workspaceId, epoch: this.s.epoch, principal: `schedule:${row.id}`, kind: 'exec', program: p.spec.command, args: [], cwdRel: p.spec.cwd, timeoutMs: p.spec.timeoutMs, shell: true, sandbox: p.spec.sandbox, network: p.spec.network, stdin: false });
|
|
188
|
+
finish('launched', job.jobId);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
finish('launch_failed', null, 'Unable to launch within current limits and sandbox policy');
|
|
192
|
+
}
|
|
193
|
+
this.audit(row.id, 'schedule.tick');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
audit(id, tool) { this.s.store.audit({ workspaceId: this.s.workspaceId, principal: 'owner-schedule', tool, refId: id, result: 'ok' }); }
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=scheduleService.js.map
|