takos-control 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/package.json +169 -0
- package/src/__tests__/db-runtime-contracts.test.ts +57 -0
- package/src/adapters/analytics-engine-binding.ts +104 -0
- package/src/adapters/dynamo-kv-store.ts +284 -0
- package/src/adapters/firestore-kv-store.ts +287 -0
- package/src/adapters/gcs-object-store.ts +506 -0
- package/src/adapters/openai-binding.ts +83 -0
- package/src/adapters/pgvector-store.ts +236 -0
- package/src/adapters/pubsub-queue.ts +127 -0
- package/src/adapters/r2-compat-types.ts +54 -0
- package/src/adapters/s3-object-store.ts +539 -0
- package/src/adapters/sqs-queue.ts +133 -0
- package/src/adapters/workflow-binding.ts +131 -0
- package/src/application/services/actions/actions-env.ts +49 -0
- package/src/application/services/actions/actions-execution.ts +156 -0
- package/src/application/services/actions/actions-triggers.ts +224 -0
- package/src/application/services/actions/index.ts +18 -0
- package/src/application/services/activitypub/remote-install.ts +170 -0
- package/src/application/services/activitypub/remote-store-client.ts +435 -0
- package/src/application/services/activitypub/store-registry.ts +341 -0
- package/src/application/services/activitypub/store-subscription.ts +240 -0
- package/src/application/services/activitypub/stores.ts +389 -0
- package/src/application/services/agent/agent-models.ts +55 -0
- package/src/application/services/agent/delegation.ts +328 -0
- package/src/application/services/agent/execute-run.ts +46 -0
- package/src/application/services/agent/index.ts +46 -0
- package/src/application/services/agent/langgraph-agent.ts +282 -0
- package/src/application/services/agent/langgraph-checkpointer.ts +556 -0
- package/src/application/services/agent/langgraph-graph.ts +357 -0
- package/src/application/services/agent/langgraph-runner.ts +326 -0
- package/src/application/services/agent/langgraph-tools.ts +157 -0
- package/src/application/services/agent/llm-manager.ts +51 -0
- package/src/application/services/agent/llm.ts +106 -0
- package/src/application/services/agent/memory-manager.ts +84 -0
- package/src/application/services/agent/message-persistence.ts +151 -0
- package/src/application/services/agent/message-utils.ts +167 -0
- package/src/application/services/agent/model-catalog.ts +71 -0
- package/src/application/services/agent/official-skills.ts +437 -0
- package/src/application/services/agent/prompt-assets.generated.ts +23 -0
- package/src/application/services/agent/prompt-budget.ts +140 -0
- package/src/application/services/agent/prompt-builder.ts +72 -0
- package/src/application/services/agent/prompts/core.md +9 -0
- package/src/application/services/agent/prompts/general-workflow.md +6 -0
- package/src/application/services/agent/prompts/modes/assistant.md +4 -0
- package/src/application/services/agent/prompts/modes/default.md +8 -0
- package/src/application/services/agent/prompts/modes/implementer.md +5 -0
- package/src/application/services/agent/prompts/modes/planner.md +4 -0
- package/src/application/services/agent/prompts/modes/researcher.md +5 -0
- package/src/application/services/agent/prompts/modes/reviewer.md +4 -0
- package/src/application/services/agent/prompts/response-guidelines.md +20 -0
- package/src/application/services/agent/prompts/runtime-tool-catalog-empty.md +3 -0
- package/src/application/services/agent/prompts/runtime-tool-catalog-header.md +3 -0
- package/src/application/services/agent/prompts/runtime-tool-catalog-selective.md +8 -0
- package/src/application/services/agent/prompts/skills/planning-structurer.en.md +1 -0
- package/src/application/services/agent/prompts/skills/planning-structurer.ja.md +1 -0
- package/src/application/services/agent/prompts/skills/repo-app-operator.en.md +1 -0
- package/src/application/services/agent/prompts/skills/repo-app-operator.ja.md +1 -0
- package/src/application/services/agent/prompts/skills/research-brief.en.md +1 -0
- package/src/application/services/agent/prompts/skills/research-brief.ja.md +1 -0
- package/src/application/services/agent/prompts/skills/slides-author.en.md +1 -0
- package/src/application/services/agent/prompts/skills/slides-author.ja.md +1 -0
- package/src/application/services/agent/prompts/skills/writing-draft.en.md +1 -0
- package/src/application/services/agent/prompts/skills/writing-draft.ja.md +1 -0
- package/src/application/services/agent/prompts/tool-runtime-rules.md +6 -0
- package/src/application/services/agent/providers/llm-providers.ts +484 -0
- package/src/application/services/agent/remote-tool-executor.ts +78 -0
- package/src/application/services/agent/run-lifecycle.ts +59 -0
- package/src/application/services/agent/runner-config.ts +77 -0
- package/src/application/services/agent/runner-events.ts +169 -0
- package/src/application/services/agent/runner-history.ts +368 -0
- package/src/application/services/agent/runner-types.ts +73 -0
- package/src/application/services/agent/runner.ts +682 -0
- package/src/application/services/agent/security/injection-detector.ts +145 -0
- package/src/application/services/agent/session-closer.ts +438 -0
- package/src/application/services/agent/simple-loop.ts +367 -0
- package/src/application/services/agent/skill-contracts.ts +21 -0
- package/src/application/services/agent/skill-loader.ts +319 -0
- package/src/application/services/agent/skill-plan.ts +67 -0
- package/src/application/services/agent/skill-resolution.ts +328 -0
- package/src/application/services/agent/skill-scoring.ts +261 -0
- package/src/application/services/agent/skill-templates.ts +51 -0
- package/src/application/services/agent/skills.ts +54 -0
- package/src/application/services/agent/thread-context.ts +472 -0
- package/src/application/services/agent/workflow-pr.ts +150 -0
- package/src/application/services/agent/workflow-review.ts +116 -0
- package/src/application/services/agent/workflow-session.ts +156 -0
- package/src/application/services/agent/workflow-types.ts +133 -0
- package/src/application/services/agent/workflow.ts +264 -0
- package/src/application/services/billing/billing-accounts.ts +165 -0
- package/src/application/services/billing/billing-plans.ts +252 -0
- package/src/application/services/billing/billing-run-usage.ts +89 -0
- package/src/application/services/billing/billing-types.ts +94 -0
- package/src/application/services/billing/billing-usage.ts +370 -0
- package/src/application/services/billing/billing.ts +64 -0
- package/src/application/services/billing/stripe.ts +331 -0
- package/src/application/services/cloudflare/api-client.ts +195 -0
- package/src/application/services/cloudflare/resources.ts +134 -0
- package/src/application/services/common-env/audit.ts +65 -0
- package/src/application/services/common-env/crypto.ts +184 -0
- package/src/application/services/common-env/db-helpers.ts +16 -0
- package/src/application/services/common-env/index.ts +4 -0
- package/src/application/services/common-env/link-state.ts +70 -0
- package/src/application/services/common-env/maintenance.ts +36 -0
- package/src/application/services/common-env/manual-link-ops.ts +385 -0
- package/src/application/services/common-env/orchestrator.ts +146 -0
- package/src/application/services/common-env/reconcile-jobs.ts +529 -0
- package/src/application/services/common-env/reconciler.ts +103 -0
- package/src/application/services/common-env/repository.ts +185 -0
- package/src/application/services/common-env/service-link-ops.ts +144 -0
- package/src/application/services/common-env/service.ts +355 -0
- package/src/application/services/common-env/space-env-ops.ts +245 -0
- package/src/application/services/common-env/takos-builtins.ts +386 -0
- package/src/application/services/deployment/artifacts.ts +105 -0
- package/src/application/services/deployment/deployment-artifacts.ts +143 -0
- package/src/application/services/deployment/execute.ts +364 -0
- package/src/application/services/deployment/group-deploy-manifest.ts +71 -0
- package/src/application/services/deployment/group-deploy-types.ts +116 -0
- package/src/application/services/deployment/group-deploy.ts +349 -0
- package/src/application/services/deployment/index.ts +7 -0
- package/src/application/services/deployment/models.ts +154 -0
- package/src/application/services/deployment/provider.ts +358 -0
- package/src/application/services/deployment/resource-provisioner.ts +175 -0
- package/src/application/services/deployment/rollback-orchestrator.ts +260 -0
- package/src/application/services/deployment/rollback.ts +110 -0
- package/src/application/services/deployment/routing.ts +311 -0
- package/src/application/services/deployment/service.ts +325 -0
- package/src/application/services/deployment/state.ts +85 -0
- package/src/application/services/deployment/store.ts +386 -0
- package/src/application/services/deployment/wrangler-config-gen.ts +182 -0
- package/src/application/services/execution/embeddings.ts +457 -0
- package/src/application/services/execution/run-creation.ts +138 -0
- package/src/application/services/execution/run-events.ts +127 -0
- package/src/application/services/execution/runtime-request-handler.ts +173 -0
- package/src/application/services/execution/sql-validation.ts +202 -0
- package/src/application/services/execution/workflow-engine-converters.ts +75 -0
- package/src/application/services/execution/workflow-engine-types.ts +77 -0
- package/src/application/services/execution/workflow-engine.ts +104 -0
- package/src/application/services/execution/workflow-job-scheduler.ts +344 -0
- package/src/application/services/execution/workflow-run-lifecycle.ts +389 -0
- package/src/application/services/execution/workflow-storage.ts +83 -0
- package/src/application/services/git-smart/client/fetch-pack.ts +167 -0
- package/src/application/services/git-smart/client/fetch-refs.ts +134 -0
- package/src/application/services/git-smart/client/index.ts +10 -0
- package/src/application/services/git-smart/core/commit-index.ts +436 -0
- package/src/application/services/git-smart/core/merge.ts +102 -0
- package/src/application/services/git-smart/core/object-store.ts +235 -0
- package/src/application/services/git-smart/core/object.ts +238 -0
- package/src/application/services/git-smart/core/readable-commit.ts +59 -0
- package/src/application/services/git-smart/core/refs.ts +364 -0
- package/src/application/services/git-smart/core/sha1.ts +40 -0
- package/src/application/services/git-smart/core/tree-ops.ts +228 -0
- package/src/application/services/git-smart/git-objects.ts +122 -0
- package/src/application/services/git-smart/index.ts +135 -0
- package/src/application/services/git-smart/operations.ts +154 -0
- package/src/application/services/git-smart/protocol/capabilities.ts +28 -0
- package/src/application/services/git-smart/protocol/packfile-reader.ts +368 -0
- package/src/application/services/git-smart/protocol/packfile-writer.ts +129 -0
- package/src/application/services/git-smart/protocol/pkt-line.ts +97 -0
- package/src/application/services/git-smart/smart-http/info-refs.ts +67 -0
- package/src/application/services/git-smart/smart-http/receive-pack.ts +485 -0
- package/src/application/services/git-smart/smart-http/upload-pack.ts +72 -0
- package/src/application/services/identity/auth-utils.ts +432 -0
- package/src/application/services/identity/locale.ts +19 -0
- package/src/application/services/identity/membership-resolver.ts +28 -0
- package/src/application/services/identity/principals.ts +76 -0
- package/src/application/services/identity/profile-activity.ts +248 -0
- package/src/application/services/identity/response-formatters.ts +88 -0
- package/src/application/services/identity/session.ts +221 -0
- package/src/application/services/identity/shortcut-groups.ts +421 -0
- package/src/application/services/identity/shortcuts.ts +216 -0
- package/src/application/services/identity/space-access.ts +128 -0
- package/src/application/services/identity/space-crud.ts +494 -0
- package/src/application/services/identity/space-members.ts +177 -0
- package/src/application/services/identity/space-models.ts +50 -0
- package/src/application/services/identity/spaces.ts +29 -0
- package/src/application/services/identity/takos-access-tokens.ts +149 -0
- package/src/application/services/identity/user-cache.ts +66 -0
- package/src/application/services/identity/user-settings.ts +146 -0
- package/src/application/services/maintenance/backup-maintenance.ts +453 -0
- package/src/application/services/maintenance/custom-domain-maintenance.ts +415 -0
- package/src/application/services/maintenance/index.ts +31 -0
- package/src/application/services/maintenance/resource-orphan-gc.ts +66 -0
- package/src/application/services/maintenance/session-maintenance.ts +50 -0
- package/src/application/services/maintenance/snapshot-maintenance.ts +147 -0
- package/src/application/services/memory/consolidation.ts +365 -0
- package/src/application/services/memory/extractor.ts +320 -0
- package/src/application/services/memory/index.ts +17 -0
- package/src/application/services/memory/llm-parser.ts +44 -0
- package/src/application/services/memory/memories.ts +392 -0
- package/src/application/services/memory-graph/activation.ts +60 -0
- package/src/application/services/memory-graph/claim-store.ts +306 -0
- package/src/application/services/memory-graph/graph-models.ts +119 -0
- package/src/application/services/memory-graph/memory-graph-runtime.ts +220 -0
- package/src/application/services/memory-graph/observer.ts +158 -0
- package/src/application/services/memory-graph/overlay.ts +101 -0
- package/src/application/services/notifications/notification-models.ts +58 -0
- package/src/application/services/notifications/service.ts +522 -0
- package/src/application/services/oauth/audit.ts +50 -0
- package/src/application/services/oauth/authorization.ts +286 -0
- package/src/application/services/oauth/client.ts +346 -0
- package/src/application/services/oauth/consent.ts +244 -0
- package/src/application/services/oauth/device.ts +295 -0
- package/src/application/services/oauth/pkce.ts +60 -0
- package/src/application/services/oauth/scopes.ts +61 -0
- package/src/application/services/oauth/token.ts +555 -0
- package/src/application/services/offload/index.ts +31 -0
- package/src/application/services/offload/messages.ts +67 -0
- package/src/application/services/offload/run-events.ts +128 -0
- package/src/application/services/offload/usage-client.ts +39 -0
- package/src/application/services/offload/usage-events.ts +100 -0
- package/src/application/services/platform/app-deployments.ts +397 -0
- package/src/application/services/platform/capabilities.ts +288 -0
- package/src/application/services/platform/custom-domains/access.ts +93 -0
- package/src/application/services/platform/custom-domains/cloudflare.ts +64 -0
- package/src/application/services/platform/custom-domains/dns.ts +55 -0
- package/src/application/services/platform/custom-domains/domain-crud.ts +230 -0
- package/src/application/services/platform/custom-domains/domain-models.ts +71 -0
- package/src/application/services/platform/custom-domains/domain-verification.ts +345 -0
- package/src/application/services/platform/custom-domains.ts +27 -0
- package/src/application/services/platform/desired-state-types.ts +111 -0
- package/src/application/services/platform/env-state-resolution.ts +249 -0
- package/src/application/services/platform/infra.ts +246 -0
- package/src/application/services/platform/mcp/crud.ts +343 -0
- package/src/application/services/platform/mcp/crypto.ts +63 -0
- package/src/application/services/platform/mcp/mcp-models.ts +119 -0
- package/src/application/services/platform/mcp/oauth.ts +375 -0
- package/src/application/services/platform/mcp/validation.ts +69 -0
- package/src/application/services/platform/mcp.ts +51 -0
- package/src/application/services/platform/resource-bindings.ts +172 -0
- package/src/application/services/platform/rollout-health.ts +48 -0
- package/src/application/services/platform/rollout.ts +358 -0
- package/src/application/services/platform/runtime-config.ts +155 -0
- package/src/application/services/platform/ui-extensions.ts +190 -0
- package/src/application/services/platform/worker-desired-state.ts +397 -0
- package/src/application/services/platform/workers.ts +426 -0
- package/src/application/services/platform/workflow-artifacts.ts +204 -0
- package/src/application/services/pull-requests/ai-review.ts +362 -0
- package/src/application/services/pull-requests/event-tasks.ts +67 -0
- package/src/application/services/pull-requests/index.ts +25 -0
- package/src/application/services/pull-requests/merge-resolution.ts +501 -0
- package/src/application/services/r2/orphaned-object-gc.ts +268 -0
- package/src/application/services/resources/access.ts +180 -0
- package/src/application/services/resources/bindings.ts +185 -0
- package/src/application/services/resources/format.ts +76 -0
- package/src/application/services/resources/index.ts +32 -0
- package/src/application/services/resources/lifecycle.ts +92 -0
- package/src/application/services/resources/store.ts +387 -0
- package/src/application/services/routing/cache.ts +176 -0
- package/src/application/services/routing/phase.ts +20 -0
- package/src/application/services/routing/resolver.ts +227 -0
- package/src/application/services/routing/routing-models.ts +46 -0
- package/src/application/services/routing/service.ts +298 -0
- package/src/application/services/routing/sharding.ts +41 -0
- package/src/application/services/run-notifier/client.ts +32 -0
- package/src/application/services/run-notifier/index.ts +20 -0
- package/src/application/services/run-notifier/run-events-contract.ts +114 -0
- package/src/application/services/run-notifier/run-failure-events.ts +62 -0
- package/src/application/services/run-notifier/run-notifier-payload.ts +28 -0
- package/src/application/services/runs/create-thread-run-store.ts +527 -0
- package/src/application/services/runs/create-thread-run-validation.ts +88 -0
- package/src/application/services/runs/run-serialization.ts +102 -0
- package/src/application/services/seed-repositories.ts +37 -0
- package/src/application/services/source/__tests__/app-manifest-template.test.ts +211 -0
- package/src/application/services/source/app-manifest-bundle.ts +427 -0
- package/src/application/services/source/app-manifest-parser.ts +375 -0
- package/src/application/services/source/app-manifest-template.ts +93 -0
- package/src/application/services/source/app-manifest-types.ts +241 -0
- package/src/application/services/source/app-manifest-validation.ts +212 -0
- package/src/application/services/source/app-manifest.ts +38 -0
- package/src/application/services/source/apps.ts +130 -0
- package/src/application/services/source/explore-catalog.ts +446 -0
- package/src/application/services/source/explore-packages.ts +612 -0
- package/src/application/services/source/explore-repos.ts +189 -0
- package/src/application/services/source/explore-types.ts +155 -0
- package/src/application/services/source/explore.ts +20 -0
- package/src/application/services/source/external-import-utils.ts +126 -0
- package/src/application/services/source/external-import.ts +430 -0
- package/src/application/services/source/fork.ts +349 -0
- package/src/application/services/source/git.ts +556 -0
- package/src/application/services/source/info-units.ts +383 -0
- package/src/application/services/source/official-packages.ts +50 -0
- package/src/application/services/source/repo-release-assets.ts +55 -0
- package/src/application/services/source/repos.ts +231 -0
- package/src/application/services/source/search.ts +247 -0
- package/src/application/services/source/skill-search.ts +315 -0
- package/src/application/services/source/skills.ts +584 -0
- package/src/application/services/source/source-exploration.ts +221 -0
- package/src/application/services/source/space-storage.ts +484 -0
- package/src/application/services/sync/git-sync-types.ts +35 -0
- package/src/application/services/sync/git-sync.ts +228 -0
- package/src/application/services/sync/index.ts +6 -0
- package/src/application/services/sync/models.ts +65 -0
- package/src/application/services/sync/runtime-session.ts +444 -0
- package/src/application/services/sync/session-files.ts +370 -0
- package/src/application/services/sync/snapshot-cleanup.ts +290 -0
- package/src/application/services/sync/snapshot-compressor.ts +79 -0
- package/src/application/services/sync/snapshot-storage.ts +136 -0
- package/src/application/services/sync/snapshot.ts +426 -0
- package/src/application/services/threads/thread-export.ts +140 -0
- package/src/application/services/threads/thread-history.ts +422 -0
- package/src/application/services/threads/thread-search.ts +299 -0
- package/src/application/services/threads/thread-service.ts +394 -0
- package/src/application/services/threads/thread-shares.ts +200 -0
- package/src/application/services/threads/thread-timeline.ts +44 -0
- package/src/application/services/wfp/assets.ts +218 -0
- package/src/application/services/wfp/bindings.ts +176 -0
- package/src/application/services/wfp/client.ts +199 -0
- package/src/application/services/wfp/d1.ts +133 -0
- package/src/application/services/wfp/index.ts +23 -0
- package/src/application/services/wfp/kv.ts +38 -0
- package/src/application/services/wfp/orchestrator.ts +339 -0
- package/src/application/services/wfp/queues.ts +77 -0
- package/src/application/services/wfp/r2.ts +131 -0
- package/src/application/services/wfp/service.ts +341 -0
- package/src/application/services/wfp/vectorize.ts +48 -0
- package/src/application/services/wfp/wfp-contracts.ts +102 -0
- package/src/application/services/wfp/worker-metadata.ts +54 -0
- package/src/application/services/wfp/workers.ts +307 -0
- package/src/application/services/workflow-runs/commands.ts +354 -0
- package/src/application/services/workflow-runs/read-model.ts +202 -0
- package/src/application/services/workflow-runs/stream.ts +54 -0
- package/src/application/tools/builtin/agent.ts +383 -0
- package/src/application/tools/builtin/artifact.ts +163 -0
- package/src/application/tools/builtin/browser/definitions.ts +167 -0
- package/src/application/tools/builtin/browser/handler-action.ts +76 -0
- package/src/application/tools/builtin/browser/handler-close.ts +31 -0
- package/src/application/tools/builtin/browser/handler-extract.ts +42 -0
- package/src/application/tools/builtin/browser/handler-goto.ts +41 -0
- package/src/application/tools/builtin/browser/handler-html.ts +38 -0
- package/src/application/tools/builtin/browser/handler-open.ts +68 -0
- package/src/application/tools/builtin/browser/handler-screenshot.ts +33 -0
- package/src/application/tools/builtin/browser/session.ts +51 -0
- package/src/application/tools/builtin/browser.ts +27 -0
- package/src/application/tools/builtin/container/availability.ts +65 -0
- package/src/application/tools/builtin/container/definitions.ts +119 -0
- package/src/application/tools/builtin/container/handler-commit.ts +182 -0
- package/src/application/tools/builtin/container/handler-create-repository.ts +54 -0
- package/src/application/tools/builtin/container/handler-start.ts +236 -0
- package/src/application/tools/builtin/container/handler-status.ts +108 -0
- package/src/application/tools/builtin/container/handler-stop.ts +64 -0
- package/src/application/tools/builtin/container/session.ts +116 -0
- package/src/application/tools/builtin/container.ts +39 -0
- package/src/application/tools/builtin/deploy.ts +65 -0
- package/src/application/tools/builtin/discovery.ts +142 -0
- package/src/application/tools/builtin/file/definitions.ts +220 -0
- package/src/application/tools/builtin/file/file-operations.ts +112 -0
- package/src/application/tools/builtin/file/handler-copy.ts +77 -0
- package/src/application/tools/builtin/file/handler-delete.ts +34 -0
- package/src/application/tools/builtin/file/handler-list.ts +26 -0
- package/src/application/tools/builtin/file/handler-mkdir.ts +25 -0
- package/src/application/tools/builtin/file/handler-read.ts +27 -0
- package/src/application/tools/builtin/file/handler-rename.ts +85 -0
- package/src/application/tools/builtin/file/handler-write-binary.ts +63 -0
- package/src/application/tools/builtin/file/handler-write.ts +41 -0
- package/src/application/tools/builtin/file/limits.ts +114 -0
- package/src/application/tools/builtin/file/session.ts +86 -0
- package/src/application/tools/builtin/file.ts +54 -0
- package/src/application/tools/builtin/index.ts +9 -0
- package/src/application/tools/builtin/info-unit.ts +403 -0
- package/src/application/tools/builtin/mcp.ts +232 -0
- package/src/application/tools/builtin/memory-graph.ts +97 -0
- package/src/application/tools/builtin/memory.ts +247 -0
- package/src/application/tools/builtin/platform/deployment-history.ts +185 -0
- package/src/application/tools/builtin/platform/deployments.ts +298 -0
- package/src/application/tools/builtin/platform/domains.ts +264 -0
- package/src/application/tools/builtin/platform/worker-settings.ts +482 -0
- package/src/application/tools/builtin/platform.ts +63 -0
- package/src/application/tools/builtin/registry.ts +133 -0
- package/src/application/tools/builtin/repo.ts +135 -0
- package/src/application/tools/builtin/runtime-tool-executor.ts +259 -0
- package/src/application/tools/builtin/space-app-deployments.ts +136 -0
- package/src/application/tools/builtin/space-common-env.ts +125 -0
- package/src/application/tools/builtin/space-files.ts +414 -0
- package/src/application/tools/builtin/space-skills.ts +482 -0
- package/src/application/tools/builtin/space-source.ts +193 -0
- package/src/application/tools/builtin/storage/d1.ts +309 -0
- package/src/application/tools/builtin/storage/kv.ts +201 -0
- package/src/application/tools/builtin/storage/r2.ts +344 -0
- package/src/application/tools/builtin/storage/resources.ts +288 -0
- package/src/application/tools/builtin/storage/validators.ts +35 -0
- package/src/application/tools/builtin/storage.ts +64 -0
- package/src/application/tools/builtin/web.ts +492 -0
- package/src/application/tools/candidate-selector.ts +131 -0
- package/src/application/tools/capabilities.ts +51 -0
- package/src/application/tools/capability-registry.ts +79 -0
- package/src/application/tools/capability-types.ts +44 -0
- package/src/application/tools/circuit-breaker.ts +149 -0
- package/src/application/tools/descriptor-builder.ts +185 -0
- package/src/application/tools/executor-setup.ts +195 -0
- package/src/application/tools/executor-utils.ts +21 -0
- package/src/application/tools/executor.ts +399 -0
- package/src/application/tools/idempotency.ts +137 -0
- package/src/application/tools/index.ts +61 -0
- package/src/application/tools/loaders/mcp-tools.ts +261 -0
- package/src/application/tools/mcp-client.ts +116 -0
- package/src/application/tools/namespace-map.ts +133 -0
- package/src/application/tools/resolver.ts +125 -0
- package/src/application/tools/tool-circuit-breaker.ts +84 -0
- package/src/application/tools/tool-definitions.ts +140 -0
- package/src/application/tools/tool-error-classifier.ts +115 -0
- package/src/application/tools/tool-permission.ts +103 -0
- package/src/application/tools/tool-policy-helpers.ts +107 -0
- package/src/application/tools/tool-policy-types.ts +98 -0
- package/src/application/tools/tool-policy.ts +579 -0
- package/src/dispatch.ts +183 -0
- package/src/index.ts +3 -0
- package/src/infra/db/client.ts +28 -0
- package/src/infra/db/index.ts +174 -0
- package/src/infra/db/schema-accounts.ts +198 -0
- package/src/infra/db/schema-agents.ts +305 -0
- package/src/infra/db/schema-auth.ts +58 -0
- package/src/infra/db/schema-billing.ts +120 -0
- package/src/infra/db/schema-oauth.ts +194 -0
- package/src/infra/db/schema-platform.ts +437 -0
- package/src/infra/db/schema-repos.ts +348 -0
- package/src/infra/db/schema-services.ts +74 -0
- package/src/infra/db/schema-workers.ts +359 -0
- package/src/infra/db/schema-workflows.ts +114 -0
- package/src/infra/db/schema.ts +172 -0
- package/src/local-platform/adapters/local.redis.test.ts +145 -0
- package/src/local-platform/bootstrap.test.ts +1374 -0
- package/src/local-platform/bootstrap.ts +1 -0
- package/src/local-platform/cloudflare-containers-shim.mjs +28 -0
- package/src/local-platform/cloudflare-workers-shim.mjs +8 -0
- package/src/local-platform/container-backend.ts +65 -0
- package/src/local-platform/d1-migrations.ts +338 -0
- package/src/local-platform/d1-prepared-statement.ts +104 -0
- package/src/local-platform/d1-shared.ts +131 -0
- package/src/local-platform/d1-sql-rewrite.ts +386 -0
- package/src/local-platform/docker-container-backend.ts +205 -0
- package/src/local-platform/execution-context.ts +24 -0
- package/src/local-platform/executor-control-rpc.ts +398 -0
- package/src/local-platform/fetch-server.ts +8 -0
- package/src/local-platform/in-memory-bindings.test.ts +49 -0
- package/src/local-platform/in-memory-bindings.ts +91 -0
- package/src/local-platform/in-memory-d1.ts +126 -0
- package/src/local-platform/in-memory-kv.ts +84 -0
- package/src/local-platform/in-memory-queue.ts +23 -0
- package/src/local-platform/in-memory-r2.ts +272 -0
- package/src/local-platform/k8s-container-backend.ts +364 -0
- package/src/local-platform/load-adapter.ts +42 -0
- package/src/local-platform/miniflare-bindings.ts +353 -0
- package/src/local-platform/miniflare-registry.ts +351 -0
- package/src/local-platform/node-fetch-server.ts +70 -0
- package/src/local-platform/node-resolve-loader.mjs +97 -0
- package/src/local-platform/oci-orchestrator-node.ts +23 -0
- package/src/local-platform/oci-orchestrator.test.ts +110 -0
- package/src/local-platform/oci-orchestrator.ts +529 -0
- package/src/local-platform/persistent-bindings.test.ts +173 -0
- package/src/local-platform/persistent-bindings.ts +6 -0
- package/src/local-platform/persistent-d1.ts +171 -0
- package/src/local-platform/persistent-durable-objects.ts +57 -0
- package/src/local-platform/persistent-kv.ts +121 -0
- package/src/local-platform/persistent-queue.ts +59 -0
- package/src/local-platform/persistent-r2.test.ts +72 -0
- package/src/local-platform/persistent-r2.ts +376 -0
- package/src/local-platform/persistent-shared.ts +27 -0
- package/src/local-platform/public-runtime-contract.test.ts +211 -0
- package/src/local-platform/queue-runtime.ts +85 -0
- package/src/local-platform/redis-bindings.ts +185 -0
- package/src/local-platform/routing-store.ts +118 -0
- package/src/local-platform/run-smoke-proxyless.ts +67 -0
- package/src/local-platform/run-smoke.ts +176 -0
- package/src/local-platform/runtime-env.ts +114 -0
- package/src/local-platform/runtime-gateway-stubs.ts +168 -0
- package/src/local-platform/runtime-host-fetch.ts +154 -0
- package/src/local-platform/runtime-http.ts +63 -0
- package/src/local-platform/runtime-types.ts +96 -0
- package/src/local-platform/runtime.ts +114 -0
- package/src/local-platform/tenant-binding-polyfills.ts +203 -0
- package/src/local-platform/tenant-binding-rpc.ts +216 -0
- package/src/local-platform/tenant-resource-limits.ts +32 -0
- package/src/local-platform/tenant-worker-runtime.ts +179 -0
- package/src/local-platform/url-registry.ts +75 -0
- package/src/local-platform/worker.test.ts +78 -0
- package/src/local-platform/worker.ts +186 -0
- package/src/node-platform/env-builder.ts +317 -0
- package/src/node-platform/index.ts +7 -0
- package/src/node-platform/resolvers/ai-resolver.ts +39 -0
- package/src/node-platform/resolvers/bucket-resolver.ts +89 -0
- package/src/node-platform/resolvers/db-resolver.ts +17 -0
- package/src/node-platform/resolvers/dispatch-resolver.ts +119 -0
- package/src/node-platform/resolvers/durable-object-resolver.ts +17 -0
- package/src/node-platform/resolvers/env-helpers.ts +47 -0
- package/src/node-platform/resolvers/kv-resolver.ts +39 -0
- package/src/node-platform/resolvers/queue-resolver.ts +99 -0
- package/src/node-platform/resolvers/routing-resolver.ts +103 -0
- package/src/platform/accessors.ts +100 -0
- package/src/platform/adapters/node.ts +236 -0
- package/src/platform/adapters/shared.ts +161 -0
- package/src/platform/adapters/workers.ts +133 -0
- package/src/platform/context.ts +26 -0
- package/src/platform/index.ts +43 -0
- package/src/platform/platform-config.ts +170 -0
- package/src/platform/providers/cloudflare/pdf-render.ts +30 -0
- package/src/platform/providers/cloudflare/resources.ts +19 -0
- package/src/platform/providers/cloudflare/wfp.ts +22 -0
- package/src/platform/providers/node/pdf-render.ts +123 -0
- package/src/runtime/container-hosts/browser-session-host.ts +359 -0
- package/src/runtime/container-hosts/browser-session-types.ts +33 -0
- package/src/runtime/container-hosts/container-runtime.ts +52 -0
- package/src/runtime/container-hosts/d1-raw.ts +22 -0
- package/src/runtime/container-hosts/executor-auth.ts +161 -0
- package/src/runtime/container-hosts/executor-control-rpc.ts +449 -0
- package/src/runtime/container-hosts/executor-dispatch.ts +84 -0
- package/src/runtime/container-hosts/executor-host.ts +447 -0
- package/src/runtime/container-hosts/executor-proxy-config.ts +38 -0
- package/src/runtime/container-hosts/executor-proxy-handlers.ts +427 -0
- package/src/runtime/container-hosts/executor-run-state.ts +389 -0
- package/src/runtime/container-hosts/executor-utils.ts +269 -0
- package/src/runtime/container-hosts/proxy-token-manager.ts +188 -0
- package/src/runtime/container-hosts/runtime-host.ts +241 -0
- package/src/runtime/durable-objects/do-header-utils.ts +160 -0
- package/src/runtime/durable-objects/git-push-lock.ts +94 -0
- package/src/runtime/durable-objects/notification-notifier.ts +257 -0
- package/src/runtime/durable-objects/rate-limiter.ts +268 -0
- package/src/runtime/durable-objects/routing.ts +339 -0
- package/src/runtime/durable-objects/run-notifier.ts +555 -0
- package/src/runtime/durable-objects/session.ts +167 -0
- package/src/runtime/executor-proxy-api.ts +211 -0
- package/src/runtime/indexer/handlers.ts +133 -0
- package/src/runtime/indexer/index.ts +103 -0
- package/src/runtime/queues/deploy-jobs.ts +89 -0
- package/src/runtime/queues/parallel-steps.ts +433 -0
- package/src/runtime/queues/workflow-dlq.ts +132 -0
- package/src/runtime/queues/workflow-events.ts +34 -0
- package/src/runtime/queues/workflow-expressions.ts +69 -0
- package/src/runtime/queues/workflow-job-handler.ts +193 -0
- package/src/runtime/queues/workflow-job-phases.ts +335 -0
- package/src/runtime/queues/workflow-jobs.ts +43 -0
- package/src/runtime/queues/workflow-runner.ts +87 -0
- package/src/runtime/queues/workflow-runtime-client.ts +180 -0
- package/src/runtime/queues/workflow-secrets.ts +78 -0
- package/src/runtime/queues/workflow-steps.ts +62 -0
- package/src/runtime/queues/workflow-types.ts +163 -0
- package/src/runtime/runner/cron-handler.ts +78 -0
- package/src/runtime/runner/index.ts +20 -0
- package/src/runtime/runner/queue-handler.ts +247 -0
- package/src/runtime/runner/runner-constants.ts +7 -0
- package/src/runtime/worker/egress.ts +375 -0
- package/src/runtime/worker/env.ts +63 -0
- package/src/runtime/worker/index.ts +11 -0
- package/src/runtime/worker/runtime-factory.ts +111 -0
- package/src/server/middleware/auth.ts +210 -0
- package/src/server/middleware/billing.ts +62 -0
- package/src/server/middleware/body-size.ts +59 -0
- package/src/server/middleware/cache.ts +192 -0
- package/src/server/middleware/content-type.ts +59 -0
- package/src/server/middleware/git-auth.ts +103 -0
- package/src/server/middleware/oauth-auth.ts +204 -0
- package/src/server/middleware/param-validation.ts +59 -0
- package/src/server/middleware/plan-gates.ts +71 -0
- package/src/server/middleware/space-access.ts +125 -0
- package/src/server/middleware/static-assets.ts +25 -0
- package/src/server/middleware/trust-tier.ts +42 -0
- package/src/server/middleware/turnstile.ts +50 -0
- package/src/server/routes/activitypub-store/activitypub-queries.ts +234 -0
- package/src/server/routes/activitypub-store/routes.ts +512 -0
- package/src/server/routes/agent-tasks-handlers.ts +211 -0
- package/src/server/routes/agent-tasks.ts +352 -0
- package/src/server/routes/api.ts +399 -0
- package/src/server/routes/app-deployments.ts +201 -0
- package/src/server/routes/apps.ts +393 -0
- package/src/server/routes/auth/cli.ts +226 -0
- package/src/server/routes/auth/external.ts +366 -0
- package/src/server/routes/auth/html.ts +398 -0
- package/src/server/routes/auth/link.ts +158 -0
- package/src/server/routes/auth/provisioning.ts +210 -0
- package/src/server/routes/auth/session.ts +277 -0
- package/src/server/routes/auth-api.ts +155 -0
- package/src/server/routes/billing/routes.ts +494 -0
- package/src/server/routes/billing/stripe.ts +128 -0
- package/src/server/routes/browser-sessions.ts +258 -0
- package/src/server/routes/common-env/handlers.ts +23 -0
- package/src/server/routes/custom-domains.ts +132 -0
- package/src/server/routes/explore/explore-filters.ts +262 -0
- package/src/server/routes/explore/index.ts +1 -0
- package/src/server/routes/explore/packages.ts +392 -0
- package/src/server/routes/explore/repos.ts +218 -0
- package/src/server/routes/explore/routes.ts +14 -0
- package/src/server/routes/explore/users.ts +139 -0
- package/src/server/routes/git.ts +218 -0
- package/src/server/routes/index/graph.ts +127 -0
- package/src/server/routes/index/handlers.ts +210 -0
- package/src/server/routes/index/index-context.ts +80 -0
- package/src/server/routes/index/index.ts +50 -0
- package/src/server/routes/index/jobs.ts +159 -0
- package/src/server/routes/mcp.ts +343 -0
- package/src/server/routes/me.ts +408 -0
- package/src/server/routes/memories.ts +366 -0
- package/src/server/routes/notifications-sse.ts +54 -0
- package/src/server/routes/notifications.ts +158 -0
- package/src/server/routes/oauth/authorize.ts +311 -0
- package/src/server/routes/oauth/device.ts +344 -0
- package/src/server/routes/oauth/introspect.ts +82 -0
- package/src/server/routes/oauth/register.ts +185 -0
- package/src/server/routes/oauth/request-utils.ts +94 -0
- package/src/server/routes/oauth/revoke.ts +69 -0
- package/src/server/routes/oauth/routes.ts +255 -0
- package/src/server/routes/oauth/token.ts +409 -0
- package/src/server/routes/oauth/userinfo.ts +101 -0
- package/src/server/routes/oauth-consent-api.ts +468 -0
- package/src/server/routes/profiles/api.ts +14 -0
- package/src/server/routes/profiles/block-follow-helpers.ts +166 -0
- package/src/server/routes/profiles/block-mute.ts +127 -0
- package/src/server/routes/profiles/dto.ts +42 -0
- package/src/server/routes/profiles/follow.ts +449 -0
- package/src/server/routes/profiles/index.ts +12 -0
- package/src/server/routes/profiles/profile-crud.ts +276 -0
- package/src/server/routes/profiles/profile-queries.ts +245 -0
- package/src/server/routes/profiles/register.ts +20 -0
- package/src/server/routes/profiles/repo.ts +382 -0
- package/src/server/routes/profiles/view.ts +213 -0
- package/src/server/routes/public-share.ts +143 -0
- package/src/server/routes/pull-requests/comments.ts +159 -0
- package/src/server/routes/pull-requests/diff.ts +309 -0
- package/src/server/routes/pull-requests/dto.ts +238 -0
- package/src/server/routes/pull-requests/git-store.ts +2 -0
- package/src/server/routes/pull-requests/index.ts +12 -0
- package/src/server/routes/pull-requests/merge-handlers.ts +266 -0
- package/src/server/routes/pull-requests/merge.ts +423 -0
- package/src/server/routes/pull-requests/read-model.ts +201 -0
- package/src/server/routes/pull-requests/reviews.ts +259 -0
- package/src/server/routes/pull-requests/routes.ts +305 -0
- package/src/server/routes/pull-requests/workflow-trigger.ts +61 -0
- package/src/server/routes/reminders.ts +173 -0
- package/src/server/routes/repos/actions/artifacts.ts +90 -0
- package/src/server/routes/repos/actions/jobs.ts +137 -0
- package/src/server/routes/repos/actions/logs.ts +74 -0
- package/src/server/routes/repos/actions/runs.ts +174 -0
- package/src/server/routes/repos/actions/secrets.ts +145 -0
- package/src/server/routes/repos/external-import.ts +158 -0
- package/src/server/routes/repos/forks.ts +196 -0
- package/src/server/routes/repos/git-advanced.ts +509 -0
- package/src/server/routes/repos/git-commits.ts +344 -0
- package/src/server/routes/repos/git-files.ts +218 -0
- package/src/server/routes/repos/git-refs.ts +203 -0
- package/src/server/routes/repos/git-shared.ts +57 -0
- package/src/server/routes/repos/git-write-operations.ts +217 -0
- package/src/server/routes/repos/git.ts +12 -0
- package/src/server/routes/repos/index.ts +30 -0
- package/src/server/routes/repos/release-assets.ts +299 -0
- package/src/server/routes/repos/release-crud.ts +419 -0
- package/src/server/routes/repos/release-shared.ts +59 -0
- package/src/server/routes/repos/releases.ts +8 -0
- package/src/server/routes/repos/repo-helpers.ts +168 -0
- package/src/server/routes/repos/routes.ts +338 -0
- package/src/server/routes/repos/shared.ts +110 -0
- package/src/server/routes/repos/stars.ts +200 -0
- package/src/server/routes/repos/sync.ts +500 -0
- package/src/server/routes/repos/workflows.ts +388 -0
- package/src/server/routes/resources/access.ts +109 -0
- package/src/server/routes/resources/bindings.ts +157 -0
- package/src/server/routes/resources/d1.ts +294 -0
- package/src/server/routes/resources/index.ts +16 -0
- package/src/server/routes/resources/r2.ts +181 -0
- package/src/server/routes/resources/routes.ts +407 -0
- package/src/server/routes/resources/tokens.ts +365 -0
- package/src/server/routes/rpc-types.ts +87 -0
- package/src/server/routes/runs/access.ts +34 -0
- package/src/server/routes/runs/create.ts +42 -0
- package/src/server/routes/runs/list.ts +120 -0
- package/src/server/routes/runs/observation.ts +161 -0
- package/src/server/routes/runs/routes.ts +274 -0
- package/src/server/routes/runs/sse.ts +62 -0
- package/src/server/routes/search.ts +220 -0
- package/src/server/routes/seed-repositories.ts +17 -0
- package/src/server/routes/sessions/auth.ts +33 -0
- package/src/server/routes/sessions/heartbeat.ts +120 -0
- package/src/server/routes/sessions/index.ts +57 -0
- package/src/server/routes/sessions/lifecycle.ts +337 -0
- package/src/server/routes/sessions/session-mappers.ts +49 -0
- package/src/server/routes/setup.ts +111 -0
- package/src/server/routes/shared/route-auth.ts +245 -0
- package/src/server/routes/shortcuts.ts +335 -0
- package/src/server/routes/skills.ts +443 -0
- package/src/server/routes/smart-http.ts +380 -0
- package/src/server/routes/spaces/common-env.ts +92 -0
- package/src/server/routes/spaces/members.ts +326 -0
- package/src/server/routes/spaces/repositories.ts +69 -0
- package/src/server/routes/spaces/routes.ts +386 -0
- package/src/server/routes/spaces/storage-downloads.ts +257 -0
- package/src/server/routes/spaces/storage-management.ts +354 -0
- package/src/server/routes/spaces/storage-operations.ts +46 -0
- package/src/server/routes/spaces/storage-uploads.ts +195 -0
- package/src/server/routes/spaces/storage.ts +10 -0
- package/src/server/routes/spaces/store-registry.ts +413 -0
- package/src/server/routes/spaces/stores.ts +139 -0
- package/src/server/routes/thread-messages.ts +141 -0
- package/src/server/routes/thread-shares.ts +113 -0
- package/src/server/routes/threads.ts +450 -0
- package/src/server/routes/well-known.ts +120 -0
- package/src/server/routes/workers/deployments.ts +339 -0
- package/src/server/routes/workers/index.ts +12 -0
- package/src/server/routes/workers/routes.ts +305 -0
- package/src/server/routes/workers/settings-bindings.ts +184 -0
- package/src/server/routes/workers/settings-common-env.ts +272 -0
- package/src/server/routes/workers/settings-config.ts +95 -0
- package/src/server/routes/workers/settings-env-vars.ts +107 -0
- package/src/server/routes/workers/settings.ts +14 -0
- package/src/server/routes/workers/slug.ts +149 -0
- package/src/server/routes/zod-validator.ts +24 -0
- package/src/shared/config/index.ts +38 -0
- package/src/shared/config/limits.ts +104 -0
- package/src/shared/config/timeouts.ts +71 -0
- package/src/shared/constants/app.ts +62 -0
- package/src/shared/constants/dns.ts +12 -0
- package/src/shared/constants/index.ts +11 -0
- package/src/shared/constants/roles.ts +45 -0
- package/src/shared/types/bindings.ts +71 -0
- package/src/shared/types/drizzle-helpers.ts +5 -0
- package/src/shared/types/env.ts +151 -0
- package/src/shared/types/index.ts +172 -0
- package/src/shared/types/models.ts +506 -0
- package/src/shared/types/oauth.ts +216 -0
- package/src/shared/types/queue-messages.ts +140 -0
- package/src/shared/types/routing.ts +54 -0
- package/src/shared/utils/content-type.ts +39 -0
- package/src/shared/utils/crypto.ts +181 -0
- package/src/shared/utils/date-utils.ts +16 -0
- package/src/shared/utils/db-guards.ts +39 -0
- package/src/shared/utils/db-transaction.ts +157 -0
- package/src/shared/utils/device-auth-rate-limit.ts +40 -0
- package/src/shared/utils/domain-validation.ts +428 -0
- package/src/shared/utils/encoding-utils.ts +77 -0
- package/src/shared/utils/error-response.ts +51 -0
- package/src/shared/utils/gzip.ts +67 -0
- package/src/shared/utils/hash.ts +36 -0
- package/src/shared/utils/http-response.ts +37 -0
- package/src/shared/utils/index.ts +65 -0
- package/src/shared/utils/lcs-diff.ts +65 -0
- package/src/shared/utils/logger.ts +220 -0
- package/src/shared/utils/naming-utils.ts +16 -0
- package/src/shared/utils/path-validation.ts +124 -0
- package/src/shared/utils/rate-limiter.ts +149 -0
- package/src/shared/utils/service-client.ts +109 -0
- package/src/shared/utils/sliding-window.ts +103 -0
- package/src/shared/utils/spa-fallback.ts +26 -0
- package/src/shared/utils/token-bucket.ts +106 -0
- package/src/shared/utils/unified-diff.ts +109 -0
- package/src/shared/utils/url-utils.ts +18 -0
- package/src/shared/utils/validate-env.ts +126 -0
- package/src/shared/utils/with-timeout.ts +32 -0
- package/src/shared/utils/zip-stream.ts +226 -0
- package/src/web.ts +505 -0
- package/src/worker-emulation/redis-durable-object.ts +246 -0
- package/src/worker-emulation/sse-notifier.ts +316 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Space Storage Service
|
|
3
|
+
*
|
|
4
|
+
* Provides file and folder storage capabilities for spaces.
|
|
5
|
+
* Uses R2 for object storage and D1 for metadata.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { D1Database, R2Bucket } from '../../../shared/types/bindings.ts';
|
|
9
|
+
import type { SpaceStorageFileType } from '../../../shared/types';
|
|
10
|
+
import type { SelectOf } from '../../../shared/types/drizzle-helpers';
|
|
11
|
+
import { getDb, accountStorageFiles } from '../../../infra/db';
|
|
12
|
+
import type { Database } from '../../../infra/db';
|
|
13
|
+
import { eq, and, desc, asc, sql } from 'drizzle-orm';
|
|
14
|
+
import { validatePathSegment } from '../../../shared/utils/path-validation';
|
|
15
|
+
import { toIsoString } from '../../../shared/utils';
|
|
16
|
+
import { logWarn } from '../../../shared/utils/logger';
|
|
17
|
+
|
|
18
|
+
type StorageFileRow = SelectOf<typeof accountStorageFiles>;
|
|
19
|
+
|
|
20
|
+
const R2_KEY_PREFIX = 'ws-storage';
|
|
21
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024;
|
|
22
|
+
const PRESIGN_EXPIRY_SECONDS = 900;
|
|
23
|
+
const MAX_CONTENT_SIZE = 50 * 1024 * 1024; // 50MB - safe for Worker memory
|
|
24
|
+
const MAX_PATH_LENGTH = 1024;
|
|
25
|
+
const MAX_LIST_ITEMS = 5000;
|
|
26
|
+
const MAX_ZIP_ENTRIES = 10000;
|
|
27
|
+
|
|
28
|
+
export interface CreateFolderInput { name: string; parentPath?: string; }
|
|
29
|
+
export interface CreateFileInput { name: string; parentPath?: string; size: number; mimeType?: string; sha256?: string; }
|
|
30
|
+
export interface RenameInput { name: string; }
|
|
31
|
+
export interface MoveInput { parentPath: string; }
|
|
32
|
+
|
|
33
|
+
export interface StorageFileResponse {
|
|
34
|
+
id: string; space_id: string; parent_id: string | null; name: string; path: string;
|
|
35
|
+
type: SpaceStorageFileType; size: number; mime_type: string | null; sha256: string | null;
|
|
36
|
+
uploaded_by: string | null; created_at: string; updated_at: string;
|
|
37
|
+
}
|
|
38
|
+
export interface UploadUrlResponse { file_id: string; upload_url: string; r2_key: string; expires_at: string; }
|
|
39
|
+
export interface DownloadUrlResponse { download_url: string; expires_at: string; }
|
|
40
|
+
export interface BulkDeleteStorageResult { r2Keys: string[]; deletedCount: number; failedIds: string[]; }
|
|
41
|
+
|
|
42
|
+
export class StorageError extends Error {
|
|
43
|
+
constructor(message: string, public readonly code: 'NOT_FOUND' | 'CONFLICT' | 'VALIDATION' | 'STORAGE_ERROR' | 'TOO_LARGE') {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = 'StorageError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizePath(path: string): string {
|
|
50
|
+
let normalized = path.replace(/\/+/g, '/').replace(/\/+$/, '');
|
|
51
|
+
if (!normalized.startsWith('/')) normalized = '/' + normalized;
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function validateFullPath(path: string): void {
|
|
56
|
+
if (path.length > MAX_PATH_LENGTH) {
|
|
57
|
+
throw new StorageError(`Path too long (max ${MAX_PATH_LENGTH} characters)`, 'VALIDATION');
|
|
58
|
+
}
|
|
59
|
+
const segments = path.split('/').filter(Boolean);
|
|
60
|
+
for (const seg of segments) {
|
|
61
|
+
if (seg === '.' || seg === '..') {
|
|
62
|
+
throw new StorageError('Invalid path: path traversal not allowed', 'VALIDATION');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getParentPath(path: string): string {
|
|
68
|
+
const normalized = normalizePath(path);
|
|
69
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
70
|
+
if (lastSlash <= 0) return '/';
|
|
71
|
+
return normalized.substring(0, lastSlash);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function toApiResponse(file: StorageFileRow): StorageFileResponse {
|
|
75
|
+
return {
|
|
76
|
+
id: file.id, space_id: file.accountId, parent_id: file.parentId, name: file.name,
|
|
77
|
+
path: file.path, type: file.type as SpaceStorageFileType, size: file.size,
|
|
78
|
+
mime_type: file.mimeType, sha256: file.sha256, uploaded_by: file.uploadedByAccountId ?? null,
|
|
79
|
+
created_at: toIsoString(file.createdAt), updated_at: toIsoString(file.updatedAt),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildR2Key(spaceId: string, fileId: string): string { return `${R2_KEY_PREFIX}/${spaceId}/${fileId}`; }
|
|
84
|
+
function buildFullPath(parentPath: string, name: string): string { return parentPath === '/' ? `/${name}` : `${parentPath}/${name}`; }
|
|
85
|
+
export function escapeSqlLike(value: string): string { return value.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_'); }
|
|
86
|
+
|
|
87
|
+
function isUniqueConstraintError(err: unknown): boolean {
|
|
88
|
+
if (err instanceof Error) {
|
|
89
|
+
const msg = err.message.toLowerCase();
|
|
90
|
+
return msg.includes('unique') || msg.includes('constraint');
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function resolveParentId(db: Database, spaceId: string, normalizedParentPath: string, errorMessage: string): Promise<string | null> {
|
|
96
|
+
if (normalizedParentPath === '/') return null;
|
|
97
|
+
const parent = await db.select({ id: accountStorageFiles.id }).from(accountStorageFiles)
|
|
98
|
+
.where(and(eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.path, normalizedParentPath), eq(accountStorageFiles.type, 'folder'))).get();
|
|
99
|
+
if (!parent) throw new StorageError(errorMessage, 'NOT_FOUND');
|
|
100
|
+
return parent.id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function updateDescendantPaths(d1: D1Database, spaceId: string, oldPath: string, newPath: string, now: string): Promise<void> {
|
|
104
|
+
const oldPfx = oldPath + '/';
|
|
105
|
+
const newPfx = newPath + '/';
|
|
106
|
+
await d1.prepare(`UPDATE account_storage_files SET path = ? || SUBSTR(path, ?), updated_at = ? WHERE account_id = ? AND path LIKE ? ESCAPE '\\'`)
|
|
107
|
+
.bind(newPfx, oldPfx.length + 1, now, spaceId, `${escapeSqlLike(oldPfx)}%`).run();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function revertParentUpdate(db: Database, spaceId: string, fileId: string, oldName: string, oldPath: string, oldParentId: string | null, oldTimestamp: string): Promise<void> {
|
|
111
|
+
await db.update(accountStorageFiles)
|
|
112
|
+
.set({ name: oldName, path: oldPath, parentId: oldParentId, updatedAt: oldTimestamp })
|
|
113
|
+
.where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ListStorageFilesResult { files: StorageFileResponse[]; truncated: boolean; }
|
|
117
|
+
|
|
118
|
+
export async function listStorageFiles(d1: D1Database, spaceId: string, parentPath: string = '/'): Promise<ListStorageFilesResult> {
|
|
119
|
+
const db = getDb(d1);
|
|
120
|
+
const normalizedPath = normalizePath(parentPath);
|
|
121
|
+
let parentId: string | null = null;
|
|
122
|
+
if (normalizedPath !== '/') {
|
|
123
|
+
const parent = await db.select({ id: accountStorageFiles.id }).from(accountStorageFiles)
|
|
124
|
+
.where(and(eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.path, normalizedPath), eq(accountStorageFiles.type, 'folder'))).get();
|
|
125
|
+
if (!parent) return { files: [], truncated: false };
|
|
126
|
+
parentId = parent.id;
|
|
127
|
+
}
|
|
128
|
+
const cond = parentId
|
|
129
|
+
? and(eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.parentId, parentId))
|
|
130
|
+
: and(eq(accountStorageFiles.accountId, spaceId), sql`${accountStorageFiles.parentId} IS NULL`);
|
|
131
|
+
// Fetch one extra to detect truncation
|
|
132
|
+
const results = await db.select().from(accountStorageFiles).where(cond).orderBy(desc(accountStorageFiles.type), asc(accountStorageFiles.name)).limit(MAX_LIST_ITEMS + 1).all();
|
|
133
|
+
const truncated = results.length > MAX_LIST_ITEMS;
|
|
134
|
+
const files = (truncated ? results.slice(0, MAX_LIST_ITEMS) : results).map(toApiResponse);
|
|
135
|
+
return { files, truncated };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function getStorageItem(d1: D1Database, spaceId: string, fileId: string): Promise<StorageFileResponse | null> {
|
|
139
|
+
const db = getDb(d1);
|
|
140
|
+
const file = await db.select().from(accountStorageFiles).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId))).get();
|
|
141
|
+
return file ? toApiResponse(file) : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function getStorageItemByPath(d1: D1Database, spaceId: string, path: string): Promise<StorageFileResponse | null> {
|
|
145
|
+
const db = getDb(d1);
|
|
146
|
+
const file = await db.select().from(accountStorageFiles).where(and(eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.path, normalizePath(path)))).get();
|
|
147
|
+
return file ? toApiResponse(file) : null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function createFolder(d1: D1Database, spaceId: string, userId: string, input: CreateFolderInput): Promise<StorageFileResponse> {
|
|
151
|
+
const db = getDb(d1);
|
|
152
|
+
const { name, parentPath = '/' } = input;
|
|
153
|
+
if (!validatePathSegment(name)) throw new StorageError('Invalid folder name', 'VALIDATION');
|
|
154
|
+
const normalizedParentPath = normalizePath(parentPath);
|
|
155
|
+
const fullPath = buildFullPath(normalizedParentPath, name);
|
|
156
|
+
validateFullPath(fullPath);
|
|
157
|
+
const parentId = await resolveParentId(db, spaceId, normalizedParentPath, 'Parent folder not found');
|
|
158
|
+
const id = crypto.randomUUID();
|
|
159
|
+
const timestamp = new Date().toISOString();
|
|
160
|
+
try {
|
|
161
|
+
await db.insert(accountStorageFiles).values({ id, accountId: spaceId, parentId, name, path: fullPath, type: 'folder', size: 0, uploadedByAccountId: userId, createdAt: timestamp, updatedAt: timestamp });
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (isUniqueConstraintError(err)) throw new StorageError('A file or folder with this name already exists', 'CONFLICT');
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
const created = await getStorageItem(d1, spaceId, id);
|
|
167
|
+
if (!created) throw new Error('Failed to create folder');
|
|
168
|
+
return created;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function createFileRecord(d1: D1Database, spaceId: string, userId: string, input: CreateFileInput): Promise<{ file: StorageFileResponse; r2Key: string }> {
|
|
172
|
+
const db = getDb(d1);
|
|
173
|
+
const { name, parentPath = '/', size, mimeType, sha256 } = input;
|
|
174
|
+
if (!validatePathSegment(name)) throw new StorageError('Invalid file name', 'VALIDATION');
|
|
175
|
+
if (size > MAX_FILE_SIZE) throw new StorageError(`File size exceeds maximum of ${MAX_FILE_SIZE / 1024 / 1024}MB`, 'TOO_LARGE');
|
|
176
|
+
const normalizedParentPath = normalizePath(parentPath);
|
|
177
|
+
const fullPath = buildFullPath(normalizedParentPath, name);
|
|
178
|
+
validateFullPath(fullPath);
|
|
179
|
+
const parentId = await resolveParentId(db, spaceId, normalizedParentPath, 'Parent folder not found');
|
|
180
|
+
const id = crypto.randomUUID();
|
|
181
|
+
const r2Key = buildR2Key(spaceId, id);
|
|
182
|
+
const timestamp = new Date().toISOString();
|
|
183
|
+
try {
|
|
184
|
+
await db.insert(accountStorageFiles).values({ id, accountId: spaceId, parentId, name, path: fullPath, type: 'file', size, mimeType: mimeType || null, r2Key, sha256: sha256 || null, uploadedByAccountId: userId, createdAt: timestamp, updatedAt: timestamp });
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (isUniqueConstraintError(err)) throw new StorageError('A file or folder with this name already exists', 'CONFLICT');
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
const created = await getStorageItem(d1, spaceId, id);
|
|
190
|
+
if (!created) throw new Error('Failed to create file record');
|
|
191
|
+
return { file: created, r2Key };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function confirmUpload(d1: D1Database, r2Bucket: R2Bucket, spaceId: string, fileId: string, sha256?: string): Promise<StorageFileResponse | null> {
|
|
195
|
+
const db = getDb(d1);
|
|
196
|
+
const fileRecord = await db.select({ r2Key: accountStorageFiles.r2Key }).from(accountStorageFiles)
|
|
197
|
+
.where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.type, 'file'))).get();
|
|
198
|
+
if (!fileRecord?.r2Key) return null;
|
|
199
|
+
|
|
200
|
+
const head = await r2Bucket.head(fileRecord.r2Key);
|
|
201
|
+
if (!head) {
|
|
202
|
+
throw new StorageError('File content not uploaded to storage', 'STORAGE_ERROR');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const timestamp = new Date().toISOString();
|
|
206
|
+
await db.update(accountStorageFiles).set({
|
|
207
|
+
size: head.size,
|
|
208
|
+
updatedAt: timestamp,
|
|
209
|
+
...(sha256 ? { sha256 } : {}),
|
|
210
|
+
}).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
211
|
+
return getStorageItem(d1, spaceId, fileId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function deleteStorageItem(d1: D1Database, spaceId: string, fileId: string): Promise<string[]> {
|
|
215
|
+
const db = getDb(d1);
|
|
216
|
+
const file = await getStorageItem(d1, spaceId, fileId);
|
|
217
|
+
if (!file) throw new StorageError('File or folder not found', 'NOT_FOUND');
|
|
218
|
+
const r2KeysToDelete: string[] = [];
|
|
219
|
+
if (file.type === 'folder') {
|
|
220
|
+
const descendantPattern = `${escapeSqlLike(`${file.path}/`)}%`;
|
|
221
|
+
const descendants = await db.select({ r2Key: accountStorageFiles.r2Key }).from(accountStorageFiles)
|
|
222
|
+
.where(and(eq(accountStorageFiles.accountId, spaceId), sql`${accountStorageFiles.path} LIKE ${descendantPattern} ESCAPE '\\'`, eq(accountStorageFiles.type, 'file'))).all();
|
|
223
|
+
for (const d of descendants) { if (d.r2Key) r2KeysToDelete.push(d.r2Key); }
|
|
224
|
+
await db.delete(accountStorageFiles).where(and(eq(accountStorageFiles.accountId, spaceId), sql`${accountStorageFiles.path} LIKE ${descendantPattern} ESCAPE '\\'`));
|
|
225
|
+
} else {
|
|
226
|
+
const rec = await db.select({ r2Key: accountStorageFiles.r2Key }).from(accountStorageFiles).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId))).get();
|
|
227
|
+
if (rec?.r2Key) r2KeysToDelete.push(rec.r2Key);
|
|
228
|
+
}
|
|
229
|
+
await db.delete(accountStorageFiles).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
230
|
+
return r2KeysToDelete;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function renameStorageItem(d1: D1Database, spaceId: string, fileId: string, input: RenameInput): Promise<StorageFileResponse> {
|
|
234
|
+
const db = getDb(d1);
|
|
235
|
+
const { name } = input;
|
|
236
|
+
if (!validatePathSegment(name)) throw new StorageError('Invalid name', 'VALIDATION');
|
|
237
|
+
const file = await getStorageItem(d1, spaceId, fileId);
|
|
238
|
+
if (!file) throw new StorageError('File or folder not found', 'NOT_FOUND');
|
|
239
|
+
const parentPath = getParentPath(file.path);
|
|
240
|
+
const newPath = buildFullPath(parentPath, name);
|
|
241
|
+
validateFullPath(newPath);
|
|
242
|
+
const timestamp = new Date().toISOString();
|
|
243
|
+
try {
|
|
244
|
+
await db.update(accountStorageFiles).set({ name, path: newPath, updatedAt: timestamp }).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (isUniqueConstraintError(err)) throw new StorageError('A file or folder with this name already exists', 'CONFLICT');
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
if (file.type === 'folder') {
|
|
250
|
+
try {
|
|
251
|
+
await updateDescendantPaths(d1, spaceId, file.path, newPath, timestamp);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
// Rollback parent update to keep consistency; preserve original error
|
|
254
|
+
try { await revertParentUpdate(db, spaceId, fileId, file.name, file.path, file.parent_id, file.updated_at); } catch (err) { logWarn('Rename rollback of parent update failed (non-critical)', { module: 'space-storage', error: err instanceof Error ? err.message : String(err) }); }
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const updated = await getStorageItem(d1, spaceId, fileId);
|
|
259
|
+
if (!updated) throw new Error('Failed to rename');
|
|
260
|
+
return updated;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function moveStorageItem(d1: D1Database, spaceId: string, fileId: string, input: MoveInput): Promise<StorageFileResponse> {
|
|
264
|
+
const db = getDb(d1);
|
|
265
|
+
const file = await getStorageItem(d1, spaceId, fileId);
|
|
266
|
+
if (!file) throw new StorageError('File or folder not found', 'NOT_FOUND');
|
|
267
|
+
const normalizedParentPath = normalizePath(input.parentPath);
|
|
268
|
+
validateFullPath(normalizedParentPath);
|
|
269
|
+
const newPath = buildFullPath(normalizedParentPath, file.name);
|
|
270
|
+
validateFullPath(newPath);
|
|
271
|
+
if (file.type === 'folder' && (normalizedParentPath === file.path || normalizedParentPath.startsWith(file.path + '/'))) throw new StorageError('Cannot move a folder into itself', 'VALIDATION');
|
|
272
|
+
const newParentId = await resolveParentId(db, spaceId, normalizedParentPath, 'Destination folder not found');
|
|
273
|
+
const timestamp = new Date().toISOString();
|
|
274
|
+
const oldPath = file.path;
|
|
275
|
+
try {
|
|
276
|
+
await db.update(accountStorageFiles).set({ parentId: newParentId, path: newPath, updatedAt: timestamp }).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
277
|
+
} catch (err) {
|
|
278
|
+
if (isUniqueConstraintError(err)) throw new StorageError('A file or folder with this name already exists', 'CONFLICT');
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
if (file.type === 'folder') {
|
|
282
|
+
try {
|
|
283
|
+
await updateDescendantPaths(d1, spaceId, oldPath, newPath, timestamp);
|
|
284
|
+
} catch (err) {
|
|
285
|
+
// Rollback parent update to keep consistency; preserve original error
|
|
286
|
+
try { await revertParentUpdate(db, spaceId, fileId, file.name, file.path, file.parent_id, file.updated_at); } catch (err) { logWarn('Move rollback of parent update failed (non-critical)', { module: 'space-storage', error: err instanceof Error ? err.message : String(err) }); }
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const updated = await getStorageItem(d1, spaceId, fileId);
|
|
291
|
+
if (!updated) throw new Error('Failed to move');
|
|
292
|
+
return updated;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export async function bulkDeleteStorageItems(d1: D1Database, spaceId: string, fileIds: string[]): Promise<BulkDeleteStorageResult> {
|
|
296
|
+
const allR2Keys: string[] = [];
|
|
297
|
+
const failedIds: string[] = [];
|
|
298
|
+
let deletedCount = 0;
|
|
299
|
+
for (const fileId of fileIds) {
|
|
300
|
+
try {
|
|
301
|
+
const keys = await deleteStorageItem(d1, spaceId, fileId);
|
|
302
|
+
allR2Keys.push(...keys);
|
|
303
|
+
deletedCount += 1;
|
|
304
|
+
} catch {
|
|
305
|
+
failedIds.push(fileId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return { r2Keys: allR2Keys, deletedCount, failedIds };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export async function deleteR2Objects(r2Bucket: R2Bucket, keys: string[]): Promise<void> {
|
|
312
|
+
const batches: string[][] = [];
|
|
313
|
+
for (let i = 0; i < keys.length; i += 1000) batches.push(keys.slice(i, i + 1000));
|
|
314
|
+
for (const batch of batches) await r2Bucket.delete(batch);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function detectTextFromContent(buf: ArrayBuffer): boolean {
|
|
318
|
+
const bytes = new Uint8Array(buf, 0, Math.min(buf.byteLength, 8192));
|
|
319
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
320
|
+
if (bytes[i] === 0) return false;
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function arrayBufferToBase64(buf: ArrayBuffer): string {
|
|
326
|
+
const bytes = new Uint8Array(buf);
|
|
327
|
+
const chunkSize = 8192;
|
|
328
|
+
const chunks: string[] = [];
|
|
329
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
330
|
+
const slice = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));
|
|
331
|
+
chunks.push(String.fromCharCode(...slice));
|
|
332
|
+
}
|
|
333
|
+
return btoa(chunks.join(''));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export async function readFileContent(d1: D1Database, r2Bucket: R2Bucket, spaceId: string, fileId: string, maxSize: number = MAX_CONTENT_SIZE): Promise<{ content: string; encoding: 'utf-8' | 'base64'; file: StorageFileResponse }> {
|
|
337
|
+
const db = getDb(d1);
|
|
338
|
+
const fileRecord = await db.select().from(accountStorageFiles).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.type, 'file'))).get();
|
|
339
|
+
if (!fileRecord) throw new StorageError('File not found', 'NOT_FOUND');
|
|
340
|
+
if (!fileRecord.r2Key) throw new StorageError('File has no storage content', 'STORAGE_ERROR');
|
|
341
|
+
if (fileRecord.size > maxSize) throw new StorageError(`File too large (${fileRecord.size} bytes, max ${maxSize})`, 'TOO_LARGE');
|
|
342
|
+
const obj = await r2Bucket.get(fileRecord.r2Key);
|
|
343
|
+
if (!obj) throw new StorageError('File content not found in storage', 'NOT_FOUND');
|
|
344
|
+
const file = toApiResponse(fileRecord);
|
|
345
|
+
const buf = await obj.arrayBuffer();
|
|
346
|
+
if (detectTextFromContent(buf)) {
|
|
347
|
+
const content = new TextDecoder().decode(buf);
|
|
348
|
+
return { content, encoding: 'utf-8', file };
|
|
349
|
+
}
|
|
350
|
+
return { content: arrayBufferToBase64(buf), encoding: 'base64', file };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export async function writeFileContent(d1: D1Database, r2Bucket: R2Bucket, spaceId: string, fileId: string, content: string, userId: string, mimeType?: string): Promise<StorageFileResponse> {
|
|
354
|
+
const db = getDb(d1);
|
|
355
|
+
const fileRecord = await db.select().from(accountStorageFiles).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId), eq(accountStorageFiles.type, 'file'))).get();
|
|
356
|
+
if (!fileRecord) throw new StorageError('File not found', 'NOT_FOUND');
|
|
357
|
+
if (!fileRecord.r2Key) throw new StorageError('File has no storage key', 'STORAGE_ERROR');
|
|
358
|
+
const encoded = new TextEncoder().encode(content);
|
|
359
|
+
if (encoded.length > MAX_CONTENT_SIZE) throw new StorageError(`Content too large (${encoded.length} bytes, max ${MAX_CONTENT_SIZE})`, 'TOO_LARGE');
|
|
360
|
+
const resolvedMimeType = mimeType || fileRecord.mimeType || 'text/plain';
|
|
361
|
+
await r2Bucket.put(fileRecord.r2Key, encoded, { httpMetadata: { contentType: resolvedMimeType } });
|
|
362
|
+
const timestamp = new Date().toISOString();
|
|
363
|
+
await db.update(accountStorageFiles).set({ size: encoded.length, updatedAt: timestamp, ...(mimeType ? { mimeType } : {}) }).where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
364
|
+
const updated = await getStorageItem(d1, spaceId, fileId);
|
|
365
|
+
if (!updated) throw new Error('Failed to update file');
|
|
366
|
+
return updated;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export async function createFileWithContent(d1: D1Database, r2Bucket: R2Bucket, spaceId: string, userId: string, path: string, content: string, mimeType?: string): Promise<StorageFileResponse> {
|
|
370
|
+
const db = getDb(d1);
|
|
371
|
+
const normalizedPath = normalizePath(path);
|
|
372
|
+
validateFullPath(normalizedPath);
|
|
373
|
+
const parts = normalizedPath.split('/').filter(Boolean);
|
|
374
|
+
if (parts.length === 0) throw new StorageError('Invalid file path', 'VALIDATION');
|
|
375
|
+
const fileName = parts[parts.length - 1];
|
|
376
|
+
if (!validatePathSegment(fileName)) throw new StorageError('Invalid file name', 'VALIDATION');
|
|
377
|
+
const parentPath = parts.length > 1 ? '/' + parts.slice(0, -1).join('/') : '/';
|
|
378
|
+
const parentId = await resolveParentId(db, spaceId, parentPath, 'Parent folder not found');
|
|
379
|
+
const encoded = new TextEncoder().encode(content);
|
|
380
|
+
if (encoded.length > MAX_CONTENT_SIZE) throw new StorageError(`Content too large (${encoded.length} bytes, max ${MAX_CONTENT_SIZE})`, 'TOO_LARGE');
|
|
381
|
+
const id = crypto.randomUUID();
|
|
382
|
+
const r2Key = buildR2Key(spaceId, id);
|
|
383
|
+
const timestamp = new Date().toISOString();
|
|
384
|
+
const resolvedMimeType = mimeType || 'application/octet-stream';
|
|
385
|
+
await r2Bucket.put(r2Key, encoded, { httpMetadata: { contentType: resolvedMimeType } });
|
|
386
|
+
try {
|
|
387
|
+
await db.insert(accountStorageFiles).values({ id, accountId: spaceId, parentId, name: fileName, path: normalizedPath, type: 'file', size: encoded.length, mimeType: resolvedMimeType, r2Key, uploadedByAccountId: userId, createdAt: timestamp, updatedAt: timestamp });
|
|
388
|
+
} catch (err) {
|
|
389
|
+
await r2Bucket.delete(r2Key);
|
|
390
|
+
if (isUniqueConstraintError(err)) throw new StorageError('A file or folder with this name already exists', 'CONFLICT');
|
|
391
|
+
throw err;
|
|
392
|
+
}
|
|
393
|
+
const created = await getStorageItem(d1, spaceId, id);
|
|
394
|
+
if (!created) throw new Error('Failed to create file');
|
|
395
|
+
return created;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export async function uploadPendingFileContent(
|
|
399
|
+
d1: D1Database,
|
|
400
|
+
r2Bucket: R2Bucket,
|
|
401
|
+
spaceId: string,
|
|
402
|
+
fileId: string,
|
|
403
|
+
content: ArrayBuffer,
|
|
404
|
+
declaredSize: number,
|
|
405
|
+
mimeType?: string,
|
|
406
|
+
): Promise<StorageFileResponse> {
|
|
407
|
+
const db = getDb(d1);
|
|
408
|
+
const fileRecord = await db.select().from(accountStorageFiles)
|
|
409
|
+
.where(and(
|
|
410
|
+
eq(accountStorageFiles.id, fileId),
|
|
411
|
+
eq(accountStorageFiles.accountId, spaceId),
|
|
412
|
+
eq(accountStorageFiles.type, 'file'),
|
|
413
|
+
))
|
|
414
|
+
.get();
|
|
415
|
+
|
|
416
|
+
if (!fileRecord) throw new StorageError('File not found', 'NOT_FOUND');
|
|
417
|
+
if (!fileRecord.r2Key) throw new StorageError('File has no storage key', 'STORAGE_ERROR');
|
|
418
|
+
if (content.byteLength <= 0) throw new StorageError('Uploaded content is empty', 'VALIDATION');
|
|
419
|
+
if (content.byteLength > MAX_FILE_SIZE) {
|
|
420
|
+
throw new StorageError(`File size exceeds maximum of ${MAX_FILE_SIZE / 1024 / 1024}MB`, 'TOO_LARGE');
|
|
421
|
+
}
|
|
422
|
+
// Verify actual upload size matches declared size (always check when declaredSize is set)
|
|
423
|
+
if (declaredSize > 0 && content.byteLength !== declaredSize) {
|
|
424
|
+
throw new StorageError(`Upload size mismatch: declared ${declaredSize} bytes but received ${content.byteLength} bytes`, 'VALIDATION');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const resolvedMimeType = mimeType || fileRecord.mimeType || 'application/octet-stream';
|
|
428
|
+
await r2Bucket.put(fileRecord.r2Key, content, {
|
|
429
|
+
httpMetadata: { contentType: resolvedMimeType },
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const timestamp = new Date().toISOString();
|
|
433
|
+
await db.update(accountStorageFiles)
|
|
434
|
+
.set({
|
|
435
|
+
size: content.byteLength,
|
|
436
|
+
mimeType: resolvedMimeType,
|
|
437
|
+
updatedAt: timestamp,
|
|
438
|
+
})
|
|
439
|
+
.where(and(eq(accountStorageFiles.id, fileId), eq(accountStorageFiles.accountId, spaceId)));
|
|
440
|
+
|
|
441
|
+
const updated = await getStorageItem(d1, spaceId, fileId);
|
|
442
|
+
if (!updated) throw new Error('Failed to update file');
|
|
443
|
+
return updated;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const CLEANUP_BATCH_SIZE = 500;
|
|
447
|
+
|
|
448
|
+
export async function cleanupOrphanedUploads(d1: D1Database, r2Bucket: R2Bucket, spaceId: string, maxAgeMs: number = 24 * 60 * 60 * 1000): Promise<number> {
|
|
449
|
+
const db = getDb(d1);
|
|
450
|
+
const cutoff = new Date(Date.now() - maxAgeMs).toISOString();
|
|
451
|
+
let totalCleaned = 0;
|
|
452
|
+
|
|
453
|
+
// Process in batches to avoid unbounded memory usage
|
|
454
|
+
while (true) {
|
|
455
|
+
const orphans = await db.select({ id: accountStorageFiles.id, r2Key: accountStorageFiles.r2Key })
|
|
456
|
+
.from(accountStorageFiles)
|
|
457
|
+
.where(and(
|
|
458
|
+
eq(accountStorageFiles.accountId, spaceId),
|
|
459
|
+
eq(accountStorageFiles.type, 'file'),
|
|
460
|
+
eq(accountStorageFiles.size, 0),
|
|
461
|
+
sql`${accountStorageFiles.createdAt} < ${cutoff}`,
|
|
462
|
+
sql`${accountStorageFiles.createdAt} = ${accountStorageFiles.updatedAt}`,
|
|
463
|
+
)).limit(CLEANUP_BATCH_SIZE).all();
|
|
464
|
+
|
|
465
|
+
if (orphans.length === 0) break;
|
|
466
|
+
|
|
467
|
+
// Delete DB records first (source of truth), then clean up R2 (best-effort)
|
|
468
|
+
for (const orphan of orphans) {
|
|
469
|
+
await db.delete(accountStorageFiles).where(and(eq(accountStorageFiles.id, orphan.id), eq(accountStorageFiles.accountId, spaceId)));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const r2Keys = orphans.map(o => o.r2Key).filter((k): k is string => !!k);
|
|
473
|
+
if (r2Keys.length > 0) {
|
|
474
|
+
try { await deleteR2Objects(r2Bucket, r2Keys); } catch (err) { logWarn('R2 orphan cleanup failed (non-critical)', { module: 'space-storage', error: err instanceof Error ? err.message : String(err) }); }
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
totalCleaned += orphans.length;
|
|
478
|
+
if (orphans.length < CLEANUP_BATCH_SIZE) break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return totalCleaned;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export { MAX_FILE_SIZE, MAX_CONTENT_SIZE, PRESIGN_EXPIRY_SECONDS, R2_KEY_PREFIX, MAX_PATH_LENGTH, MAX_LIST_ITEMS, MAX_ZIP_ENTRIES };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** A file entry used for session init and repo file transfer. */
|
|
2
|
+
export interface SessionFileEntry {
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
encoding?: 'utf-8' | 'base64';
|
|
6
|
+
is_binary?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Shared result type for sync operations. */
|
|
10
|
+
export interface SyncResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
committed: boolean;
|
|
13
|
+
commitHash?: string;
|
|
14
|
+
pushed: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SessionRepoMount {
|
|
19
|
+
repoId: string;
|
|
20
|
+
repoName: string;
|
|
21
|
+
branch?: string;
|
|
22
|
+
mountPath?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SessionSnapshot {
|
|
26
|
+
files: Array<{
|
|
27
|
+
path: string;
|
|
28
|
+
content: string;
|
|
29
|
+
size: number;
|
|
30
|
+
is_binary?: boolean;
|
|
31
|
+
encoding?: 'utf-8' | 'base64';
|
|
32
|
+
}>;
|
|
33
|
+
file_count: number;
|
|
34
|
+
total_size?: number;
|
|
35
|
+
}
|