@aigne/aos 0.2.0-beta
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/CHANGELOG.md +49 -0
- package/LICENSE.md +26 -0
- package/dist/agent-dsl.cjs +116 -0
- package/dist/agent-dsl.d.cts +31 -0
- package/dist/agent-dsl.d.cts.map +1 -0
- package/dist/agent-dsl.d.mts +31 -0
- package/dist/agent-dsl.d.mts.map +1 -0
- package/dist/agent-dsl.mjs +115 -0
- package/dist/agent-dsl.mjs.map +1 -0
- package/dist/arc-compat.cjs +125 -0
- package/dist/arc-compat.d.cts +17 -0
- package/dist/arc-compat.d.cts.map +1 -0
- package/dist/arc-compat.d.mts +17 -0
- package/dist/arc-compat.d.mts.map +1 -0
- package/dist/arc-compat.mjs +123 -0
- package/dist/arc-compat.mjs.map +1 -0
- package/dist/auto-surface/compiler.cjs +36 -0
- package/dist/auto-surface/compiler.d.cts +7 -0
- package/dist/auto-surface/compiler.d.cts.map +1 -0
- package/dist/auto-surface/compiler.d.mts +7 -0
- package/dist/auto-surface/compiler.d.mts.map +1 -0
- package/dist/auto-surface/compiler.mjs +37 -0
- package/dist/auto-surface/compiler.mjs.map +1 -0
- package/dist/auto-surface/definition-dsl.cjs +333 -0
- package/dist/auto-surface/definition-dsl.d.cts +41 -0
- package/dist/auto-surface/definition-dsl.d.cts.map +1 -0
- package/dist/auto-surface/definition-dsl.d.mts +41 -0
- package/dist/auto-surface/definition-dsl.d.mts.map +1 -0
- package/dist/auto-surface/definition-dsl.mjs +332 -0
- package/dist/auto-surface/definition-dsl.mjs.map +1 -0
- package/dist/auto-surface/fragments.cjs +177 -0
- package/dist/auto-surface/fragments.d.cts +23 -0
- package/dist/auto-surface/fragments.d.cts.map +1 -0
- package/dist/auto-surface/fragments.d.mts +23 -0
- package/dist/auto-surface/fragments.d.mts.map +1 -0
- package/dist/auto-surface/fragments.mjs +175 -0
- package/dist/auto-surface/fragments.mjs.map +1 -0
- package/dist/auto-surface/index.d.mts +9 -0
- package/dist/auto-surface/layering.cjs +43 -0
- package/dist/auto-surface/layering.d.cts +7 -0
- package/dist/auto-surface/layering.d.cts.map +1 -0
- package/dist/auto-surface/layering.d.mts +7 -0
- package/dist/auto-surface/layering.d.mts.map +1 -0
- package/dist/auto-surface/layering.mjs +43 -0
- package/dist/auto-surface/layering.mjs.map +1 -0
- package/dist/auto-surface/projection.cjs +11 -0
- package/dist/auto-surface/projection.d.cts +11 -0
- package/dist/auto-surface/projection.d.cts.map +1 -0
- package/dist/auto-surface/projection.d.mts +11 -0
- package/dist/auto-surface/projection.d.mts.map +1 -0
- package/dist/auto-surface/projection.mjs +12 -0
- package/dist/auto-surface/projection.mjs.map +1 -0
- package/dist/auto-surface/projectors/settings-shell.cjs +114 -0
- package/dist/auto-surface/projectors/settings-shell.d.cts +10 -0
- package/dist/auto-surface/projectors/settings-shell.d.cts.map +1 -0
- package/dist/auto-surface/projectors/settings-shell.d.mts +10 -0
- package/dist/auto-surface/projectors/settings-shell.d.mts.map +1 -0
- package/dist/auto-surface/projectors/settings-shell.mjs +114 -0
- package/dist/auto-surface/projectors/settings-shell.mjs.map +1 -0
- package/dist/auto-surface/scenario.cjs +38 -0
- package/dist/auto-surface/scenario.d.cts +31 -0
- package/dist/auto-surface/scenario.d.cts.map +1 -0
- package/dist/auto-surface/scenario.d.mts +31 -0
- package/dist/auto-surface/scenario.d.mts.map +1 -0
- package/dist/auto-surface/scenario.mjs +37 -0
- package/dist/auto-surface/scenario.mjs.map +1 -0
- package/dist/auto-surface/types.cjs +9 -0
- package/dist/auto-surface/types.d.cts +153 -0
- package/dist/auto-surface/types.d.cts.map +1 -0
- package/dist/auto-surface/types.d.mts +153 -0
- package/dist/auto-surface/types.d.mts.map +1 -0
- package/dist/auto-surface/types.mjs +9 -0
- package/dist/auto-surface/types.mjs.map +1 -0
- package/dist/auto-surface/value-dsl.cjs +80 -0
- package/dist/auto-surface/value-dsl.d.cts +24 -0
- package/dist/auto-surface/value-dsl.d.cts.map +1 -0
- package/dist/auto-surface/value-dsl.d.mts +24 -0
- package/dist/auto-surface/value-dsl.d.mts.map +1 -0
- package/dist/auto-surface/value-dsl.mjs +79 -0
- package/dist/auto-surface/value-dsl.mjs.map +1 -0
- package/dist/blocklet/activation.cjs +94 -0
- package/dist/blocklet/activation.d.cts +30 -0
- package/dist/blocklet/activation.d.cts.map +1 -0
- package/dist/blocklet/activation.d.mts +30 -0
- package/dist/blocklet/activation.d.mts.map +1 -0
- package/dist/blocklet/activation.mjs +94 -0
- package/dist/blocklet/activation.mjs.map +1 -0
- package/dist/blocklet/did-space-registry.cjs +282 -0
- package/dist/blocklet/did-space-registry.d.cts +82 -0
- package/dist/blocklet/did-space-registry.d.cts.map +1 -0
- package/dist/blocklet/did-space-registry.d.mts +82 -0
- package/dist/blocklet/did-space-registry.d.mts.map +1 -0
- package/dist/blocklet/did-space-registry.mjs +281 -0
- package/dist/blocklet/did-space-registry.mjs.map +1 -0
- package/dist/blocklet/handlers.cjs +66 -0
- package/dist/blocklet/handlers.d.cts +16 -0
- package/dist/blocklet/handlers.d.cts.map +1 -0
- package/dist/blocklet/handlers.d.mts +16 -0
- package/dist/blocklet/handlers.d.mts.map +1 -0
- package/dist/blocklet/handlers.mjs +65 -0
- package/dist/blocklet/handlers.mjs.map +1 -0
- package/dist/blocklet/index.d.mts +7 -0
- package/dist/blocklet/manifest.d.cts +2 -0
- package/dist/blocklet/manifest.d.mts +2 -0
- package/dist/blocklet/manifest.mjs +3 -0
- package/dist/blocklet/registry.cjs +447 -0
- package/dist/blocklet/registry.d.cts +197 -0
- package/dist/blocklet/registry.d.cts.map +1 -0
- package/dist/blocklet/registry.d.mts +197 -0
- package/dist/blocklet/registry.d.mts.map +1 -0
- package/dist/blocklet/registry.mjs +441 -0
- package/dist/blocklet/registry.mjs.map +1 -0
- package/dist/blocklet/static-render.cjs +183 -0
- package/dist/blocklet/static-render.d.cts +55 -0
- package/dist/blocklet/static-render.d.cts.map +1 -0
- package/dist/blocklet/static-render.d.mts +55 -0
- package/dist/blocklet/static-render.d.mts.map +1 -0
- package/dist/blocklet/static-render.mjs +181 -0
- package/dist/blocklet/static-render.mjs.map +1 -0
- package/dist/blocklet/types.d.cts +70 -0
- package/dist/blocklet/types.d.cts.map +1 -0
- package/dist/blocklet/types.d.mts +70 -0
- package/dist/blocklet/types.d.mts.map +1 -0
- package/dist/devices/chain.cjs +127 -0
- package/dist/devices/chain.d.cts +48 -0
- package/dist/devices/chain.d.cts.map +1 -0
- package/dist/devices/chain.d.mts +48 -0
- package/dist/devices/chain.d.mts.map +1 -0
- package/dist/devices/chain.mjs +125 -0
- package/dist/devices/chain.mjs.map +1 -0
- package/dist/dsl-shared.cjs +290 -0
- package/dist/dsl-shared.d.cts +54 -0
- package/dist/dsl-shared.d.cts.map +1 -0
- package/dist/dsl-shared.d.mts +54 -0
- package/dist/dsl-shared.d.mts.map +1 -0
- package/dist/dsl-shared.mjs +287 -0
- package/dist/dsl-shared.mjs.map +1 -0
- package/dist/http/afs-mcp.cjs +85 -0
- package/dist/http/afs-mcp.d.cts +14 -0
- package/dist/http/afs-mcp.d.cts.map +1 -0
- package/dist/http/afs-mcp.d.mts +14 -0
- package/dist/http/afs-mcp.d.mts.map +1 -0
- package/dist/http/afs-mcp.mjs +86 -0
- package/dist/http/afs-mcp.mjs.map +1 -0
- package/dist/http/afs-raw.cjs +264 -0
- package/dist/http/afs-raw.d.cts +31 -0
- package/dist/http/afs-raw.d.cts.map +1 -0
- package/dist/http/afs-raw.d.mts +31 -0
- package/dist/http/afs-raw.d.mts.map +1 -0
- package/dist/http/afs-raw.mjs +264 -0
- package/dist/http/afs-raw.mjs.map +1 -0
- package/dist/http/afs-rest.cjs +30 -0
- package/dist/http/afs-rest.d.cts +21 -0
- package/dist/http/afs-rest.d.cts.map +1 -0
- package/dist/http/afs-rest.d.mts +21 -0
- package/dist/http/afs-rest.d.mts.map +1 -0
- package/dist/http/afs-rest.mjs +31 -0
- package/dist/http/afs-rest.mjs.map +1 -0
- package/dist/http/afs-rpc-wire.cjs +271 -0
- package/dist/http/afs-rpc-wire.d.cts +195 -0
- package/dist/http/afs-rpc-wire.d.cts.map +1 -0
- package/dist/http/afs-rpc-wire.d.mts +195 -0
- package/dist/http/afs-rpc-wire.d.mts.map +1 -0
- package/dist/http/afs-rpc-wire.mjs +262 -0
- package/dist/http/afs-rpc-wire.mjs.map +1 -0
- package/dist/http/afs-rpc.cjs +729 -0
- package/dist/http/afs-rpc.d.cts +103 -0
- package/dist/http/afs-rpc.d.cts.map +1 -0
- package/dist/http/afs-rpc.d.mts +103 -0
- package/dist/http/afs-rpc.d.mts.map +1 -0
- package/dist/http/afs-rpc.mjs +729 -0
- package/dist/http/afs-rpc.mjs.map +1 -0
- package/dist/http/afs-upload.cjs +116 -0
- package/dist/http/afs-upload.d.cts +40 -0
- package/dist/http/afs-upload.d.cts.map +1 -0
- package/dist/http/afs-upload.d.mts +40 -0
- package/dist/http/afs-upload.d.mts.map +1 -0
- package/dist/http/afs-upload.mjs +116 -0
- package/dist/http/afs-upload.mjs.map +1 -0
- package/dist/http/aup-client.cjs +60 -0
- package/dist/http/aup-client.d.cts +22 -0
- package/dist/http/aup-client.d.cts.map +1 -0
- package/dist/http/aup-client.d.mts +22 -0
- package/dist/http/aup-client.d.mts.map +1 -0
- package/dist/http/aup-client.mjs +59 -0
- package/dist/http/aup-client.mjs.map +1 -0
- package/dist/http/aup-event.cjs +100 -0
- package/dist/http/aup-event.d.cts +38 -0
- package/dist/http/aup-event.d.cts.map +1 -0
- package/dist/http/aup-event.d.mts +38 -0
- package/dist/http/aup-event.d.mts.map +1 -0
- package/dist/http/aup-event.mjs +101 -0
- package/dist/http/aup-event.mjs.map +1 -0
- package/dist/http/auth-context.cjs +66 -0
- package/dist/http/auth-context.d.cts +64 -0
- package/dist/http/auth-context.d.cts.map +1 -0
- package/dist/http/auth-context.d.mts +64 -0
- package/dist/http/auth-context.d.mts.map +1 -0
- package/dist/http/auth-context.mjs +66 -0
- package/dist/http/auth-context.mjs.map +1 -0
- package/dist/http/csrf.cjs +50 -0
- package/dist/http/csrf.d.cts +8 -0
- package/dist/http/csrf.d.cts.map +1 -0
- package/dist/http/csrf.d.mts +8 -0
- package/dist/http/csrf.d.mts.map +1 -0
- package/dist/http/csrf.mjs +49 -0
- package/dist/http/csrf.mjs.map +1 -0
- package/dist/http/handlers.cjs +106 -0
- package/dist/http/handlers.d.cts +39 -0
- package/dist/http/handlers.d.cts.map +1 -0
- package/dist/http/handlers.d.mts +39 -0
- package/dist/http/handlers.d.mts.map +1 -0
- package/dist/http/handlers.mjs +104 -0
- package/dist/http/handlers.mjs.map +1 -0
- package/dist/http/index.d.mts +1 -0
- package/dist/http/ingest-facade.cjs +315 -0
- package/dist/http/ingest-facade.mjs +314 -0
- package/dist/http/ingest-facade.mjs.map +1 -0
- package/dist/http/raw-url.cjs +136 -0
- package/dist/http/raw-url.d.cts +54 -0
- package/dist/http/raw-url.d.cts.map +1 -0
- package/dist/http/raw-url.d.mts +54 -0
- package/dist/http/raw-url.d.mts.map +1 -0
- package/dist/http/raw-url.mjs +136 -0
- package/dist/http/raw-url.mjs.map +1 -0
- package/dist/http/request-context.cjs +178 -0
- package/dist/http/request-context.d.cts +76 -0
- package/dist/http/request-context.d.cts.map +1 -0
- package/dist/http/request-context.d.mts +76 -0
- package/dist/http/request-context.d.mts.map +1 -0
- package/dist/http/request-context.mjs +179 -0
- package/dist/http/request-context.mjs.map +1 -0
- package/dist/index.cjs +197 -0
- package/dist/index.d.cts +63 -0
- package/dist/index.d.mts +68 -0
- package/dist/index.mjs +61 -0
- package/dist/mcp/index.cjs +9 -0
- package/dist/mcp/index.d.cts +5 -0
- package/dist/mcp/index.d.mts +5 -0
- package/dist/mcp/index.mjs +6 -0
- package/dist/mcp/prompts.cjs +17 -0
- package/dist/mcp/prompts.d.cts +8 -0
- package/dist/mcp/prompts.d.cts.map +1 -0
- package/dist/mcp/prompts.d.mts +8 -0
- package/dist/mcp/prompts.d.mts.map +1 -0
- package/dist/mcp/prompts.mjs +17 -0
- package/dist/mcp/prompts.mjs.map +1 -0
- package/dist/mcp/resources.cjs +22 -0
- package/dist/mcp/resources.d.cts +8 -0
- package/dist/mcp/resources.d.cts.map +1 -0
- package/dist/mcp/resources.d.mts +8 -0
- package/dist/mcp/resources.d.mts.map +1 -0
- package/dist/mcp/resources.mjs +22 -0
- package/dist/mcp/resources.mjs.map +1 -0
- package/dist/mcp/server.cjs +51 -0
- package/dist/mcp/server.d.cts +20 -0
- package/dist/mcp/server.d.cts.map +1 -0
- package/dist/mcp/server.d.mts +20 -0
- package/dist/mcp/server.d.mts.map +1 -0
- package/dist/mcp/server.mjs +52 -0
- package/dist/mcp/server.mjs.map +1 -0
- package/dist/mcp/tools.cjs +218 -0
- package/dist/mcp/tools.d.cts +8 -0
- package/dist/mcp/tools.d.cts.map +1 -0
- package/dist/mcp/tools.d.mts +8 -0
- package/dist/mcp/tools.d.mts.map +1 -0
- package/dist/mcp/tools.mjs +219 -0
- package/dist/mcp/tools.mjs.map +1 -0
- package/dist/mcp/utils.cjs +75 -0
- package/dist/mcp/utils.mjs +69 -0
- package/dist/mcp/utils.mjs.map +1 -0
- package/dist/mount/index.cjs +4 -0
- package/dist/mount/index.d.cts +3 -0
- package/dist/mount/index.d.mts +3 -0
- package/dist/mount/index.mjs +3 -0
- package/dist/mount/materializer.cjs +167 -0
- package/dist/mount/materializer.d.cts +97 -0
- package/dist/mount/materializer.d.cts.map +1 -0
- package/dist/mount/materializer.d.mts +97 -0
- package/dist/mount/materializer.d.mts.map +1 -0
- package/dist/mount/materializer.mjs +167 -0
- package/dist/mount/materializer.mjs.map +1 -0
- package/dist/mount/types.d.cts +90 -0
- package/dist/mount/types.d.cts.map +1 -0
- package/dist/mount/types.d.mts +90 -0
- package/dist/mount/types.d.mts.map +1 -0
- package/dist/mount-config.cjs +61 -0
- package/dist/mount-config.d.cts +19 -0
- package/dist/mount-config.d.cts.map +1 -0
- package/dist/mount-config.d.mts +19 -0
- package/dist/mount-config.d.mts.map +1 -0
- package/dist/mount-config.mjs +61 -0
- package/dist/mount-config.mjs.map +1 -0
- package/dist/projectors/aup-projector.cjs +190 -0
- package/dist/projectors/aup-projector.d.cts +59 -0
- package/dist/projectors/aup-projector.d.cts.map +1 -0
- package/dist/projectors/aup-projector.d.mts +59 -0
- package/dist/projectors/aup-projector.d.mts.map +1 -0
- package/dist/projectors/aup-projector.mjs +188 -0
- package/dist/projectors/aup-projector.mjs.map +1 -0
- package/dist/projectors/index.cjs +6 -0
- package/dist/projectors/index.d.cts +2 -0
- package/dist/projectors/index.d.mts +2 -0
- package/dist/projectors/index.mjs +3 -0
- package/dist/scaffold/afs.cjs +195 -0
- package/dist/scaffold/afs.d.cts +16 -0
- package/dist/scaffold/afs.d.cts.map +1 -0
- package/dist/scaffold/afs.d.mts +16 -0
- package/dist/scaffold/afs.d.mts.map +1 -0
- package/dist/scaffold/afs.mjs +195 -0
- package/dist/scaffold/afs.mjs.map +1 -0
- package/dist/scaffold/composition.cjs +92 -0
- package/dist/scaffold/composition.d.cts +10 -0
- package/dist/scaffold/composition.d.cts.map +1 -0
- package/dist/scaffold/composition.d.mts +10 -0
- package/dist/scaffold/composition.d.mts.map +1 -0
- package/dist/scaffold/composition.mjs +92 -0
- package/dist/scaffold/composition.mjs.map +1 -0
- package/dist/scaffold/dsl-error.cjs +21 -0
- package/dist/scaffold/dsl-error.d.cts +11 -0
- package/dist/scaffold/dsl-error.d.cts.map +1 -0
- package/dist/scaffold/dsl-error.d.mts +11 -0
- package/dist/scaffold/dsl-error.d.mts.map +1 -0
- package/dist/scaffold/dsl-error.mjs +20 -0
- package/dist/scaffold/dsl-error.mjs.map +1 -0
- package/dist/scaffold/index.d.mts +12 -0
- package/dist/scaffold/manifest-dsl.cjs +188 -0
- package/dist/scaffold/manifest-dsl.d.cts +8 -0
- package/dist/scaffold/manifest-dsl.d.cts.map +1 -0
- package/dist/scaffold/manifest-dsl.d.mts +8 -0
- package/dist/scaffold/manifest-dsl.d.mts.map +1 -0
- package/dist/scaffold/manifest-dsl.mjs +188 -0
- package/dist/scaffold/manifest-dsl.mjs.map +1 -0
- package/dist/scaffold/module-dsl.cjs +284 -0
- package/dist/scaffold/module-dsl.d.cts +8 -0
- package/dist/scaffold/module-dsl.d.cts.map +1 -0
- package/dist/scaffold/module-dsl.d.mts +8 -0
- package/dist/scaffold/module-dsl.d.mts.map +1 -0
- package/dist/scaffold/module-dsl.mjs +284 -0
- package/dist/scaffold/module-dsl.mjs.map +1 -0
- package/dist/scaffold/resolver.cjs +156 -0
- package/dist/scaffold/resolver.d.cts +7 -0
- package/dist/scaffold/resolver.d.cts.map +1 -0
- package/dist/scaffold/resolver.d.mts +7 -0
- package/dist/scaffold/resolver.d.mts.map +1 -0
- package/dist/scaffold/resolver.mjs +156 -0
- package/dist/scaffold/resolver.mjs.map +1 -0
- package/dist/scaffold/runtime.cjs +24 -0
- package/dist/scaffold/runtime.d.cts +19 -0
- package/dist/scaffold/runtime.d.cts.map +1 -0
- package/dist/scaffold/runtime.d.mts +19 -0
- package/dist/scaffold/runtime.d.mts.map +1 -0
- package/dist/scaffold/runtime.mjs +25 -0
- package/dist/scaffold/runtime.mjs.map +1 -0
- package/dist/scaffold/shell-dsl.cjs +279 -0
- package/dist/scaffold/shell-dsl.d.cts +8 -0
- package/dist/scaffold/shell-dsl.d.cts.map +1 -0
- package/dist/scaffold/shell-dsl.d.mts +8 -0
- package/dist/scaffold/shell-dsl.d.mts.map +1 -0
- package/dist/scaffold/shell-dsl.mjs +279 -0
- package/dist/scaffold/shell-dsl.mjs.map +1 -0
- package/dist/scaffold/surface-dsl.cjs +182 -0
- package/dist/scaffold/surface-dsl.d.cts +8 -0
- package/dist/scaffold/surface-dsl.d.cts.map +1 -0
- package/dist/scaffold/surface-dsl.d.mts +8 -0
- package/dist/scaffold/surface-dsl.d.mts.map +1 -0
- package/dist/scaffold/surface-dsl.mjs +182 -0
- package/dist/scaffold/surface-dsl.mjs.map +1 -0
- package/dist/scaffold/surfaces.cjs +84 -0
- package/dist/scaffold/surfaces.d.cts +23 -0
- package/dist/scaffold/surfaces.d.cts.map +1 -0
- package/dist/scaffold/surfaces.d.mts +24 -0
- package/dist/scaffold/surfaces.d.mts.map +1 -0
- package/dist/scaffold/surfaces.mjs +84 -0
- package/dist/scaffold/surfaces.mjs.map +1 -0
- package/dist/scaffold/types.d.cts +264 -0
- package/dist/scaffold/types.d.cts.map +1 -0
- package/dist/scaffold/types.d.mts +265 -0
- package/dist/scaffold/types.d.mts.map +1 -0
- package/dist/scaffold/workspace-dsl.cjs +339 -0
- package/dist/scaffold/workspace-dsl.d.cts +49 -0
- package/dist/scaffold/workspace-dsl.d.cts.map +1 -0
- package/dist/scaffold/workspace-dsl.d.mts +49 -0
- package/dist/scaffold/workspace-dsl.d.mts.map +1 -0
- package/dist/scaffold/workspace-dsl.mjs +337 -0
- package/dist/scaffold/workspace-dsl.mjs.map +1 -0
- package/dist/scope/index.d.mts +3 -0
- package/dist/scope/read-blocklet-scope.cjs +29 -0
- package/dist/scope/read-blocklet-scope.d.cts +8 -0
- package/dist/scope/read-blocklet-scope.d.cts.map +1 -0
- package/dist/scope/read-blocklet-scope.d.mts +8 -0
- package/dist/scope/read-blocklet-scope.d.mts.map +1 -0
- package/dist/scope/read-blocklet-scope.mjs +30 -0
- package/dist/scope/read-blocklet-scope.mjs.map +1 -0
- package/dist/scope/resolve-scoped-afs.cjs +87 -0
- package/dist/scope/resolve-scoped-afs.d.cts +7 -0
- package/dist/scope/resolve-scoped-afs.d.cts.map +1 -0
- package/dist/scope/resolve-scoped-afs.d.mts +7 -0
- package/dist/scope/resolve-scoped-afs.d.mts.map +1 -0
- package/dist/scope/resolve-scoped-afs.mjs +88 -0
- package/dist/scope/resolve-scoped-afs.mjs.map +1 -0
- package/dist/scope/types.d.cts +25 -0
- package/dist/scope/types.d.cts.map +1 -0
- package/dist/scope/types.d.mts +25 -0
- package/dist/scope/types.d.mts.map +1 -0
- package/dist/session/backends.cjs +90 -0
- package/dist/session/backends.d.cts +35 -0
- package/dist/session/backends.d.cts.map +1 -0
- package/dist/session/backends.d.mts +35 -0
- package/dist/session/backends.d.mts.map +1 -0
- package/dist/session/backends.mjs +89 -0
- package/dist/session/backends.mjs.map +1 -0
- package/dist/session/replication-resolver.cjs +323 -0
- package/dist/session/replication-resolver.d.cts +71 -0
- package/dist/session/replication-resolver.d.cts.map +1 -0
- package/dist/session/replication-resolver.d.mts +71 -0
- package/dist/session/replication-resolver.d.mts.map +1 -0
- package/dist/session/replication-resolver.mjs +323 -0
- package/dist/session/replication-resolver.mjs.map +1 -0
- package/dist/session/role-level.cjs +36 -0
- package/dist/session/role-level.d.cts +24 -0
- package/dist/session/role-level.d.cts.map +1 -0
- package/dist/session/role-level.d.mts +24 -0
- package/dist/session/role-level.d.mts.map +1 -0
- package/dist/session/role-level.mjs +35 -0
- package/dist/session/role-level.mjs.map +1 -0
- package/dist/session/session-dir-providers.cjs +254 -0
- package/dist/session/session-dir-providers.d.cts +32 -0
- package/dist/session/session-dir-providers.d.cts.map +1 -0
- package/dist/session/session-dir-providers.d.mts +32 -0
- package/dist/session/session-dir-providers.d.mts.map +1 -0
- package/dist/session/session-dir-providers.mjs +252 -0
- package/dist/session/session-dir-providers.mjs.map +1 -0
- package/dist/session/session-id.cjs +20 -0
- package/dist/session/session-id.d.cts +15 -0
- package/dist/session/session-id.d.cts.map +1 -0
- package/dist/session/session-id.d.mts +15 -0
- package/dist/session/session-id.d.mts.map +1 -0
- package/dist/session/session-id.mjs +20 -0
- package/dist/session/session-id.mjs.map +1 -0
- package/dist/session/session-user-afs.cjs +899 -0
- package/dist/session/session-user-afs.d.cts +296 -0
- package/dist/session/session-user-afs.d.cts.map +1 -0
- package/dist/session/session-user-afs.d.mts +296 -0
- package/dist/session/session-user-afs.d.mts.map +1 -0
- package/dist/session/session-user-afs.mjs +896 -0
- package/dist/session/session-user-afs.mjs.map +1 -0
- package/dist/session/settings-cascade.cjs +69 -0
- package/dist/session/settings-cascade.d.cts +81 -0
- package/dist/session/settings-cascade.d.cts.map +1 -0
- package/dist/session/settings-cascade.d.mts +81 -0
- package/dist/session/settings-cascade.d.mts.map +1 -0
- package/dist/session/settings-cascade.mjs +69 -0
- package/dist/session/settings-cascade.mjs.map +1 -0
- package/dist/session/settings-resolver.cjs +770 -0
- package/dist/session/settings-resolver.d.cts +177 -0
- package/dist/session/settings-resolver.d.cts.map +1 -0
- package/dist/session/settings-resolver.d.mts +177 -0
- package/dist/session/settings-resolver.d.mts.map +1 -0
- package/dist/session/settings-resolver.mjs +771 -0
- package/dist/session/settings-resolver.mjs.map +1 -0
- package/dist/session/settings-schema-store.cjs +0 -0
- package/dist/session/settings-schema-store.d.cts +72 -0
- package/dist/session/settings-schema-store.d.cts.map +1 -0
- package/dist/session/settings-schema-store.d.mts +72 -0
- package/dist/session/settings-schema-store.d.mts.map +1 -0
- package/dist/session/settings-schema-store.mjs +0 -0
- package/dist/session/settings-schema-store.mjs.map +1 -0
- package/dist/system-mounts/index.cjs +14 -0
- package/dist/system-mounts/index.d.cts +6 -0
- package/dist/system-mounts/index.d.mts +6 -0
- package/dist/system-mounts/index.mjs +6 -0
- package/dist/system-mounts/install.cjs +101 -0
- package/dist/system-mounts/install.d.cts +36 -0
- package/dist/system-mounts/install.d.cts.map +1 -0
- package/dist/system-mounts/install.d.mts +37 -0
- package/dist/system-mounts/install.d.mts.map +1 -0
- package/dist/system-mounts/install.mjs +102 -0
- package/dist/system-mounts/install.mjs.map +1 -0
- package/dist/system-mounts/installers.cjs +247 -0
- package/dist/system-mounts/installers.d.cts +13 -0
- package/dist/system-mounts/installers.d.cts.map +1 -0
- package/dist/system-mounts/installers.d.mts +14 -0
- package/dist/system-mounts/installers.d.mts.map +1 -0
- package/dist/system-mounts/installers.mjs +244 -0
- package/dist/system-mounts/installers.mjs.map +1 -0
- package/dist/system-mounts/lists.cjs +122 -0
- package/dist/system-mounts/lists.d.cts +34 -0
- package/dist/system-mounts/lists.d.cts.map +1 -0
- package/dist/system-mounts/lists.d.mts +34 -0
- package/dist/system-mounts/lists.d.mts.map +1 -0
- package/dist/system-mounts/lists.mjs +122 -0
- package/dist/system-mounts/lists.mjs.map +1 -0
- package/dist/system-mounts/types.d.cts +124 -0
- package/dist/system-mounts/types.d.cts.map +1 -0
- package/dist/system-mounts/types.d.mts +125 -0
- package/dist/system-mounts/types.d.mts.map +1 -0
- package/dist/system-mounts/validate.cjs +56 -0
- package/dist/system-mounts/validate.d.cts +34 -0
- package/dist/system-mounts/validate.d.cts.map +1 -0
- package/dist/system-mounts/validate.d.mts +34 -0
- package/dist/system-mounts/validate.d.mts.map +1 -0
- package/dist/system-mounts/validate.mjs +56 -0
- package/dist/system-mounts/validate.mjs.map +1 -0
- package/dist/terminal-llm.cjs +90 -0
- package/dist/terminal-llm.d.cts +50 -0
- package/dist/terminal-llm.d.cts.map +1 -0
- package/dist/terminal-llm.d.mts +50 -0
- package/dist/terminal-llm.d.mts.map +1 -0
- package/dist/terminal-llm.mjs +90 -0
- package/dist/terminal-llm.mjs.map +1 -0
- package/dist/terminal-repl.cjs +371 -0
- package/dist/terminal-repl.d.cts +69 -0
- package/dist/terminal-repl.d.cts.map +1 -0
- package/dist/terminal-repl.d.mts +69 -0
- package/dist/terminal-repl.d.mts.map +1 -0
- package/dist/terminal-repl.mjs +370 -0
- package/dist/terminal-repl.mjs.map +1 -0
- package/dist/transport/idle-timer.cjs +136 -0
- package/dist/transport/idle-timer.d.cts +92 -0
- package/dist/transport/idle-timer.d.cts.map +1 -0
- package/dist/transport/idle-timer.d.mts +92 -0
- package/dist/transport/idle-timer.d.mts.map +1 -0
- package/dist/transport/idle-timer.mjs +132 -0
- package/dist/transport/idle-timer.mjs.map +1 -0
- package/dist/web-component-dsl.cjs +153 -0
- package/dist/web-component-dsl.d.cts +27 -0
- package/dist/web-component-dsl.d.cts.map +1 -0
- package/dist/web-component-dsl.d.mts +27 -0
- package/dist/web-component-dsl.d.mts.map +1 -0
- package/dist/web-component-dsl.mjs +152 -0
- package/dist/web-component-dsl.mjs.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
let ufo = require("ufo");
|
|
2
|
+
let _aigne_afs = require("@aigne/afs");
|
|
3
|
+
let _aigne_afs_did_space = require("@aigne/afs-did-space");
|
|
4
|
+
|
|
5
|
+
//#region src/http/ingest-facade.ts
|
|
6
|
+
/**
|
|
7
|
+
* ingest-facade — the browser upload plane's server host (design §4.2, D1
|
|
8
|
+
* candidate-b). The afs-rpc handler intercepts the root-visible facade actions
|
|
9
|
+
*
|
|
10
|
+
* /.actions/ingest-prepare — plan + (grant) mint PUT grants + open a session
|
|
11
|
+
* /.actions/commit-upload — commit uploaded objects' metadata
|
|
12
|
+
* /.actions/abort-upload — abort a session (staged objects → GC)
|
|
13
|
+
*
|
|
14
|
+
* and injects them into `list('/.actions')`, so the browser only ever speaks in
|
|
15
|
+
* ROOT-VISIBLE paths (`/user/docs/a.pdf`) and never learns the mount boundary.
|
|
16
|
+
*
|
|
17
|
+
* Why a handler intercept (not a provider): the upload-plane actions
|
|
18
|
+
* (prepare/commit/abort-upload) take PROVIDER-RELATIVE paths (`/docs/a.pdf`) but
|
|
19
|
+
* the browser only knows root-visible paths. The intercept holds the composed
|
|
20
|
+
* per-request `SessionUserAFS` (writable `/user`) + verified caller, runs the
|
|
21
|
+
* shared `planIngest()` (one planning impl, core), then strips the mount prefix
|
|
22
|
+
* and delegates to the target mount's prepare/commit/abort-upload. The
|
|
23
|
+
* root-visible↔provider-relative translation lives only here — the browser never
|
|
24
|
+
* does mount math, and we never produce a `tree/user/...` double-prefix key.
|
|
25
|
+
*
|
|
26
|
+
* Transport split (design §4.4a): these are all `exec` (HTTP fast-path, CSRF +
|
|
27
|
+
* effect-gated). The legacy fallback's batchWrite is the browser's job (WS).
|
|
28
|
+
*/
|
|
29
|
+
/** Capability-discovery version the browser checks before using this chain. */
|
|
30
|
+
const INGEST_PROTOCOL_VERSION = 1;
|
|
31
|
+
/** The three root-visible facade action names. */
|
|
32
|
+
const INGEST_FACADE_ACTIONS = [
|
|
33
|
+
"ingest-prepare",
|
|
34
|
+
"commit-upload",
|
|
35
|
+
"abort-upload"
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* v1 grant mount used when there is no path to derive it from (abort-upload
|
|
39
|
+
* carries only a sessionId). v1 supports a single grant mount; prepare/commit
|
|
40
|
+
* derive the mount from their path args, abort falls back to this.
|
|
41
|
+
*/
|
|
42
|
+
const V1_GRANT_MOUNT = "/user";
|
|
43
|
+
/** True for the three root-visible facade exec paths (exact match only). */
|
|
44
|
+
function isIngestFacadePath(path) {
|
|
45
|
+
return INGEST_FACADE_ACTIONS.some((a) => path === `/.actions/${a}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Append the three facade entries to a `list('/.actions')` result so the browser
|
|
49
|
+
* discovers the chain by listing the ROOT `/.actions` (design §4.5). Idempotent
|
|
50
|
+
* (won't duplicate an id already present) — applied identically in all scopes.
|
|
51
|
+
*/
|
|
52
|
+
function injectIngestFacadeList(data) {
|
|
53
|
+
const arr = Array.isArray(data) ? [...data] : [];
|
|
54
|
+
const have = new Set(arr.map((e) => e?.id));
|
|
55
|
+
for (const name of INGEST_FACADE_ACTIONS) {
|
|
56
|
+
if (have.has(name)) continue;
|
|
57
|
+
arr.push({
|
|
58
|
+
id: name,
|
|
59
|
+
path: (0, ufo.joinURL)("/.actions", name),
|
|
60
|
+
meta: {
|
|
61
|
+
kind: "afs:executable",
|
|
62
|
+
protocolVersion: INGEST_PROTOCOL_VERSION
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return arr;
|
|
67
|
+
}
|
|
68
|
+
/** v1: the mount prefix is the first path segment (`/user/docs` → `/user`). */
|
|
69
|
+
function mountPrefixOf(path) {
|
|
70
|
+
const seg = path.split("/").filter(Boolean)[0];
|
|
71
|
+
return seg ? `/${seg}` : null;
|
|
72
|
+
}
|
|
73
|
+
/** Strip the mount prefix → provider-relative path (`/user/docs/a.pdf` → `/docs/a.pdf`). */
|
|
74
|
+
function stripMount(path, mountPrefix) {
|
|
75
|
+
if (path === mountPrefix) return "/";
|
|
76
|
+
const rel = path.slice(mountPrefix.length);
|
|
77
|
+
return rel.startsWith("/") ? rel : `/${rel}`;
|
|
78
|
+
}
|
|
79
|
+
/** Re-add the mount prefix → root-visible path (`/docs/a.pdf` → `/user/docs/a.pdf`). */
|
|
80
|
+
function addMount(providerRel, mountPrefix) {
|
|
81
|
+
return (0, ufo.joinURL)(mountPrefix, providerRel);
|
|
82
|
+
}
|
|
83
|
+
/** Read the target mount's `prepare-upload.grantCapability` (design §4.5). */
|
|
84
|
+
async function isGrantCapable(afs, mountPrefix, context) {
|
|
85
|
+
try {
|
|
86
|
+
const r = await afs.list((0, ufo.joinURL)(mountPrefix, ".actions"), { context });
|
|
87
|
+
return (Array.isArray(r?.data) ? r.data : []).find((e) => e.id === "prepare-upload")?.meta?.grantCapability === true;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function asStr(v) {
|
|
93
|
+
return typeof v === "string" && v ? v : void 0;
|
|
94
|
+
}
|
|
95
|
+
async function execIngestPrepare(afs, args, context, limits) {
|
|
96
|
+
const targetDir = asStr(args.targetDir);
|
|
97
|
+
if (!targetDir) return {
|
|
98
|
+
ok: false,
|
|
99
|
+
code: "AFS_VALIDATION_ERROR",
|
|
100
|
+
error: "targetDir must be a non-empty string"
|
|
101
|
+
};
|
|
102
|
+
const conflict = (0, _aigne_afs.normalizeConflict)(args.conflict) ?? "rename";
|
|
103
|
+
const rawEntries = Array.isArray(args.entries) ? args.entries : null;
|
|
104
|
+
if (!rawEntries || rawEntries.length === 0) return {
|
|
105
|
+
ok: false,
|
|
106
|
+
code: "AFS_VALIDATION_ERROR",
|
|
107
|
+
error: "entries must be a non-empty array"
|
|
108
|
+
};
|
|
109
|
+
const inputs = [];
|
|
110
|
+
for (const raw of rawEntries) {
|
|
111
|
+
const entry = raw;
|
|
112
|
+
const relPath = asStr(entry.relPath);
|
|
113
|
+
if (!relPath) return {
|
|
114
|
+
ok: false,
|
|
115
|
+
code: "AFS_VALIDATION_ERROR",
|
|
116
|
+
error: "every entry needs a non-empty relPath"
|
|
117
|
+
};
|
|
118
|
+
inputs.push({
|
|
119
|
+
relPath,
|
|
120
|
+
size: typeof entry.size === "number" && entry.size >= 0 ? entry.size : 0,
|
|
121
|
+
mimeType: asStr(entry.mimeType),
|
|
122
|
+
cid: asStr(entry.cid)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const plan = await (0, _aigne_afs.planIngest)(afs, targetDir, inputs.map((e) => ({
|
|
126
|
+
name: e.relPath,
|
|
127
|
+
relPath: e.relPath
|
|
128
|
+
})), conflict);
|
|
129
|
+
const mountPrefix = mountPrefixOf(targetDir);
|
|
130
|
+
const grantCapable = mountPrefix ? await isGrantCapable(afs, mountPrefix, context) : false;
|
|
131
|
+
const wantAbsent = conflict === "rename" || conflict === "error";
|
|
132
|
+
const responseEntries = [];
|
|
133
|
+
const grantInputs = [];
|
|
134
|
+
for (const pe of plan.entries) {
|
|
135
|
+
if (pe.status === "skipped") {
|
|
136
|
+
responseEntries.push({
|
|
137
|
+
relPath: pe.relPath,
|
|
138
|
+
finalPath: pe.finalPath,
|
|
139
|
+
status: "skipped"
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (pe.status === "failed") {
|
|
144
|
+
responseEntries.push({
|
|
145
|
+
relPath: pe.relPath,
|
|
146
|
+
finalPath: pe.finalPath,
|
|
147
|
+
status: "failed",
|
|
148
|
+
error: pe.error
|
|
149
|
+
});
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const finalPath = pe.finalPath;
|
|
153
|
+
const ifMatch = wantAbsent ? _aigne_afs_did_space.AFS_IFMATCH_ABSENT : void 0;
|
|
154
|
+
const ref = {
|
|
155
|
+
relPath: pe.relPath,
|
|
156
|
+
finalPath,
|
|
157
|
+
status: "needs-upload",
|
|
158
|
+
...ifMatch ? { ifMatch } : {}
|
|
159
|
+
};
|
|
160
|
+
responseEntries.push(ref);
|
|
161
|
+
const src = inputs[pe.index];
|
|
162
|
+
const size = src?.size ?? 0;
|
|
163
|
+
const failTooLarge = (capBytes, planeNote) => {
|
|
164
|
+
ref.status = "failed";
|
|
165
|
+
ref.error = {
|
|
166
|
+
code: "FILE_TOO_LARGE",
|
|
167
|
+
message: `File exceeds the ${capBytes}-byte ${planeNote} (declared ${size} bytes)`
|
|
168
|
+
};
|
|
169
|
+
delete ref.ifMatch;
|
|
170
|
+
};
|
|
171
|
+
if (size > limits.maxBytes) failTooLarge(limits.maxBytes, "upload limit");
|
|
172
|
+
else if (grantCapable && mountPrefix && src?.cid && size > 0) grantInputs.push({
|
|
173
|
+
ref,
|
|
174
|
+
providerRelPath: stripMount(finalPath, mountPrefix),
|
|
175
|
+
cid: src.cid,
|
|
176
|
+
size,
|
|
177
|
+
mimeType: src.mimeType,
|
|
178
|
+
ifMatch
|
|
179
|
+
});
|
|
180
|
+
else if (size > limits.legacyMaxBytes) failTooLarge(limits.legacyMaxBytes, "legacy upload limit (configure an R2 signer for larger files)");
|
|
181
|
+
}
|
|
182
|
+
let mode = "legacy";
|
|
183
|
+
let sessionId;
|
|
184
|
+
let expiresAt;
|
|
185
|
+
if (grantInputs.length > 0 && mountPrefix) {
|
|
186
|
+
const prepRes = await afs.exec((0, ufo.joinURL)(mountPrefix, ".actions/prepare-upload"), { entries: grantInputs.map((g) => ({
|
|
187
|
+
path: g.providerRelPath,
|
|
188
|
+
cid: g.cid,
|
|
189
|
+
size: g.size,
|
|
190
|
+
...g.mimeType ? { mimeType: g.mimeType } : {},
|
|
191
|
+
...g.ifMatch ? { ifMatch: g.ifMatch } : {}
|
|
192
|
+
})) }, { context });
|
|
193
|
+
if (!prepRes?.success) return {
|
|
194
|
+
ok: false,
|
|
195
|
+
code: prepRes?.error?.code ?? "AFS_ERROR",
|
|
196
|
+
error: prepRes?.error?.message ?? "prepare-upload failed"
|
|
197
|
+
};
|
|
198
|
+
const pdata = prepRes.data;
|
|
199
|
+
mode = "grant";
|
|
200
|
+
sessionId = pdata.sessionId;
|
|
201
|
+
expiresAt = pdata.expiresAt;
|
|
202
|
+
const byPath = new Map((pdata.entries ?? []).map((e) => [e.path, e]));
|
|
203
|
+
for (const g of grantInputs) {
|
|
204
|
+
const pe = byPath.get(g.providerRelPath);
|
|
205
|
+
if (!pe) continue;
|
|
206
|
+
g.ref.objectKey = pe.objectKey;
|
|
207
|
+
g.ref.checksumSha256 = pe.checksumSha256;
|
|
208
|
+
if (pe.grant) g.ref.grant = pe.grant;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
ok: true,
|
|
213
|
+
data: {
|
|
214
|
+
mode,
|
|
215
|
+
protocolVersion: INGEST_PROTOCOL_VERSION,
|
|
216
|
+
...sessionId ? {
|
|
217
|
+
sessionId,
|
|
218
|
+
expiresAt
|
|
219
|
+
} : {},
|
|
220
|
+
entries: responseEntries
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function execCommitUpload(afs, args, context) {
|
|
225
|
+
const sessionId = asStr(args.sessionId);
|
|
226
|
+
if (!sessionId) return {
|
|
227
|
+
ok: false,
|
|
228
|
+
code: "AFS_VALIDATION_ERROR",
|
|
229
|
+
error: "sessionId required"
|
|
230
|
+
};
|
|
231
|
+
const rawEntries = Array.isArray(args.entries) ? args.entries : null;
|
|
232
|
+
if (!rawEntries || rawEntries.length === 0) return {
|
|
233
|
+
ok: false,
|
|
234
|
+
code: "AFS_VALIDATION_ERROR",
|
|
235
|
+
error: "entries must be a non-empty array"
|
|
236
|
+
};
|
|
237
|
+
const firstPath = asStr(rawEntries[0]?.path);
|
|
238
|
+
const mountPrefix = firstPath ? mountPrefixOf(firstPath) : null;
|
|
239
|
+
if (!mountPrefix) return {
|
|
240
|
+
ok: false,
|
|
241
|
+
code: "AFS_VALIDATION_ERROR",
|
|
242
|
+
error: "entry.path must be a root-visible path"
|
|
243
|
+
};
|
|
244
|
+
const providerEntries = rawEntries.map((raw) => {
|
|
245
|
+
const e = raw;
|
|
246
|
+
const p = asStr(e.path);
|
|
247
|
+
return {
|
|
248
|
+
...e,
|
|
249
|
+
path: p ? stripMount(p, mountPrefix) : p
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
const res = await afs.exec((0, ufo.joinURL)(mountPrefix, ".actions/commit-upload"), {
|
|
253
|
+
sessionId,
|
|
254
|
+
entries: providerEntries
|
|
255
|
+
}, { context });
|
|
256
|
+
if (!res?.success) return {
|
|
257
|
+
ok: false,
|
|
258
|
+
code: res?.error?.code ?? "AFS_ERROR",
|
|
259
|
+
error: res?.error?.message ?? "commit-upload failed"
|
|
260
|
+
};
|
|
261
|
+
const data = res.data;
|
|
262
|
+
if (Array.isArray(data?.results)) data.results = data.results.map((r) => {
|
|
263
|
+
const out = { ...r };
|
|
264
|
+
const rp = asStr(r.path);
|
|
265
|
+
if (rp) out.path = addMount(rp, mountPrefix);
|
|
266
|
+
const inner = r.data;
|
|
267
|
+
if (inner && asStr(inner.path)) out.data = {
|
|
268
|
+
...inner,
|
|
269
|
+
path: addMount(inner.path, mountPrefix)
|
|
270
|
+
};
|
|
271
|
+
return out;
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
ok: true,
|
|
275
|
+
data
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async function execAbortUpload(afs, args, context) {
|
|
279
|
+
const sessionId = asStr(args.sessionId);
|
|
280
|
+
if (!sessionId) return {
|
|
281
|
+
ok: false,
|
|
282
|
+
code: "AFS_VALIDATION_ERROR",
|
|
283
|
+
error: "sessionId required"
|
|
284
|
+
};
|
|
285
|
+
const res = await afs.exec((0, ufo.joinURL)(V1_GRANT_MOUNT, ".actions/abort-upload"), { sessionId }, { context });
|
|
286
|
+
if (!res?.success) return {
|
|
287
|
+
ok: false,
|
|
288
|
+
code: res?.error?.code ?? "AFS_ERROR",
|
|
289
|
+
error: res?.error?.message ?? "abort-upload failed"
|
|
290
|
+
};
|
|
291
|
+
return {
|
|
292
|
+
ok: true,
|
|
293
|
+
data: res.data
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Run a root-visible facade exec. Caller MUST pre-check {@link isIngestFacadePath}.
|
|
298
|
+
* The authed-caller floor (design §4.7: all three are write-effect) is enforced
|
|
299
|
+
* here — the CSRF/Origin guard is the afs-rpc handler's job upstream.
|
|
300
|
+
*/
|
|
301
|
+
async function execIngestFacade(input) {
|
|
302
|
+
const { afs, path, args, context, caller } = input;
|
|
303
|
+
if (!caller?.did) return {
|
|
304
|
+
status: 401,
|
|
305
|
+
error: "Authentication required for upload"
|
|
306
|
+
};
|
|
307
|
+
if (path === "/.actions/ingest-prepare") return execIngestPrepare(afs, args, context, input.limits ?? _aigne_afs.UPLOAD_LIMIT_DEFAULTS);
|
|
308
|
+
if (path === "/.actions/commit-upload") return execCommitUpload(afs, args, context);
|
|
309
|
+
return execAbortUpload(afs, args, context);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
exports.execIngestFacade = execIngestFacade;
|
|
314
|
+
exports.injectIngestFacadeList = injectIngestFacadeList;
|
|
315
|
+
exports.isIngestFacadePath = isIngestFacadePath;
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { joinURL } from "ufo";
|
|
2
|
+
import { UPLOAD_LIMIT_DEFAULTS, normalizeConflict, planIngest } from "@aigne/afs";
|
|
3
|
+
import { AFS_IFMATCH_ABSENT } from "@aigne/afs-did-space";
|
|
4
|
+
|
|
5
|
+
//#region src/http/ingest-facade.ts
|
|
6
|
+
/**
|
|
7
|
+
* ingest-facade — the browser upload plane's server host (design §4.2, D1
|
|
8
|
+
* candidate-b). The afs-rpc handler intercepts the root-visible facade actions
|
|
9
|
+
*
|
|
10
|
+
* /.actions/ingest-prepare — plan + (grant) mint PUT grants + open a session
|
|
11
|
+
* /.actions/commit-upload — commit uploaded objects' metadata
|
|
12
|
+
* /.actions/abort-upload — abort a session (staged objects → GC)
|
|
13
|
+
*
|
|
14
|
+
* and injects them into `list('/.actions')`, so the browser only ever speaks in
|
|
15
|
+
* ROOT-VISIBLE paths (`/user/docs/a.pdf`) and never learns the mount boundary.
|
|
16
|
+
*
|
|
17
|
+
* Why a handler intercept (not a provider): the upload-plane actions
|
|
18
|
+
* (prepare/commit/abort-upload) take PROVIDER-RELATIVE paths (`/docs/a.pdf`) but
|
|
19
|
+
* the browser only knows root-visible paths. The intercept holds the composed
|
|
20
|
+
* per-request `SessionUserAFS` (writable `/user`) + verified caller, runs the
|
|
21
|
+
* shared `planIngest()` (one planning impl, core), then strips the mount prefix
|
|
22
|
+
* and delegates to the target mount's prepare/commit/abort-upload. The
|
|
23
|
+
* root-visible↔provider-relative translation lives only here — the browser never
|
|
24
|
+
* does mount math, and we never produce a `tree/user/...` double-prefix key.
|
|
25
|
+
*
|
|
26
|
+
* Transport split (design §4.4a): these are all `exec` (HTTP fast-path, CSRF +
|
|
27
|
+
* effect-gated). The legacy fallback's batchWrite is the browser's job (WS).
|
|
28
|
+
*/
|
|
29
|
+
/** Capability-discovery version the browser checks before using this chain. */
|
|
30
|
+
const INGEST_PROTOCOL_VERSION = 1;
|
|
31
|
+
/** The three root-visible facade action names. */
|
|
32
|
+
const INGEST_FACADE_ACTIONS = [
|
|
33
|
+
"ingest-prepare",
|
|
34
|
+
"commit-upload",
|
|
35
|
+
"abort-upload"
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* v1 grant mount used when there is no path to derive it from (abort-upload
|
|
39
|
+
* carries only a sessionId). v1 supports a single grant mount; prepare/commit
|
|
40
|
+
* derive the mount from their path args, abort falls back to this.
|
|
41
|
+
*/
|
|
42
|
+
const V1_GRANT_MOUNT = "/user";
|
|
43
|
+
/** True for the three root-visible facade exec paths (exact match only). */
|
|
44
|
+
function isIngestFacadePath(path) {
|
|
45
|
+
return INGEST_FACADE_ACTIONS.some((a) => path === `/.actions/${a}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Append the three facade entries to a `list('/.actions')` result so the browser
|
|
49
|
+
* discovers the chain by listing the ROOT `/.actions` (design §4.5). Idempotent
|
|
50
|
+
* (won't duplicate an id already present) — applied identically in all scopes.
|
|
51
|
+
*/
|
|
52
|
+
function injectIngestFacadeList(data) {
|
|
53
|
+
const arr = Array.isArray(data) ? [...data] : [];
|
|
54
|
+
const have = new Set(arr.map((e) => e?.id));
|
|
55
|
+
for (const name of INGEST_FACADE_ACTIONS) {
|
|
56
|
+
if (have.has(name)) continue;
|
|
57
|
+
arr.push({
|
|
58
|
+
id: name,
|
|
59
|
+
path: joinURL("/.actions", name),
|
|
60
|
+
meta: {
|
|
61
|
+
kind: "afs:executable",
|
|
62
|
+
protocolVersion: INGEST_PROTOCOL_VERSION
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return arr;
|
|
67
|
+
}
|
|
68
|
+
/** v1: the mount prefix is the first path segment (`/user/docs` → `/user`). */
|
|
69
|
+
function mountPrefixOf(path) {
|
|
70
|
+
const seg = path.split("/").filter(Boolean)[0];
|
|
71
|
+
return seg ? `/${seg}` : null;
|
|
72
|
+
}
|
|
73
|
+
/** Strip the mount prefix → provider-relative path (`/user/docs/a.pdf` → `/docs/a.pdf`). */
|
|
74
|
+
function stripMount(path, mountPrefix) {
|
|
75
|
+
if (path === mountPrefix) return "/";
|
|
76
|
+
const rel = path.slice(mountPrefix.length);
|
|
77
|
+
return rel.startsWith("/") ? rel : `/${rel}`;
|
|
78
|
+
}
|
|
79
|
+
/** Re-add the mount prefix → root-visible path (`/docs/a.pdf` → `/user/docs/a.pdf`). */
|
|
80
|
+
function addMount(providerRel, mountPrefix) {
|
|
81
|
+
return joinURL(mountPrefix, providerRel);
|
|
82
|
+
}
|
|
83
|
+
/** Read the target mount's `prepare-upload.grantCapability` (design §4.5). */
|
|
84
|
+
async function isGrantCapable(afs, mountPrefix, context) {
|
|
85
|
+
try {
|
|
86
|
+
const r = await afs.list(joinURL(mountPrefix, ".actions"), { context });
|
|
87
|
+
return (Array.isArray(r?.data) ? r.data : []).find((e) => e.id === "prepare-upload")?.meta?.grantCapability === true;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function asStr(v) {
|
|
93
|
+
return typeof v === "string" && v ? v : void 0;
|
|
94
|
+
}
|
|
95
|
+
async function execIngestPrepare(afs, args, context, limits) {
|
|
96
|
+
const targetDir = asStr(args.targetDir);
|
|
97
|
+
if (!targetDir) return {
|
|
98
|
+
ok: false,
|
|
99
|
+
code: "AFS_VALIDATION_ERROR",
|
|
100
|
+
error: "targetDir must be a non-empty string"
|
|
101
|
+
};
|
|
102
|
+
const conflict = normalizeConflict(args.conflict) ?? "rename";
|
|
103
|
+
const rawEntries = Array.isArray(args.entries) ? args.entries : null;
|
|
104
|
+
if (!rawEntries || rawEntries.length === 0) return {
|
|
105
|
+
ok: false,
|
|
106
|
+
code: "AFS_VALIDATION_ERROR",
|
|
107
|
+
error: "entries must be a non-empty array"
|
|
108
|
+
};
|
|
109
|
+
const inputs = [];
|
|
110
|
+
for (const raw of rawEntries) {
|
|
111
|
+
const entry = raw;
|
|
112
|
+
const relPath = asStr(entry.relPath);
|
|
113
|
+
if (!relPath) return {
|
|
114
|
+
ok: false,
|
|
115
|
+
code: "AFS_VALIDATION_ERROR",
|
|
116
|
+
error: "every entry needs a non-empty relPath"
|
|
117
|
+
};
|
|
118
|
+
inputs.push({
|
|
119
|
+
relPath,
|
|
120
|
+
size: typeof entry.size === "number" && entry.size >= 0 ? entry.size : 0,
|
|
121
|
+
mimeType: asStr(entry.mimeType),
|
|
122
|
+
cid: asStr(entry.cid)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const plan = await planIngest(afs, targetDir, inputs.map((e) => ({
|
|
126
|
+
name: e.relPath,
|
|
127
|
+
relPath: e.relPath
|
|
128
|
+
})), conflict);
|
|
129
|
+
const mountPrefix = mountPrefixOf(targetDir);
|
|
130
|
+
const grantCapable = mountPrefix ? await isGrantCapable(afs, mountPrefix, context) : false;
|
|
131
|
+
const wantAbsent = conflict === "rename" || conflict === "error";
|
|
132
|
+
const responseEntries = [];
|
|
133
|
+
const grantInputs = [];
|
|
134
|
+
for (const pe of plan.entries) {
|
|
135
|
+
if (pe.status === "skipped") {
|
|
136
|
+
responseEntries.push({
|
|
137
|
+
relPath: pe.relPath,
|
|
138
|
+
finalPath: pe.finalPath,
|
|
139
|
+
status: "skipped"
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (pe.status === "failed") {
|
|
144
|
+
responseEntries.push({
|
|
145
|
+
relPath: pe.relPath,
|
|
146
|
+
finalPath: pe.finalPath,
|
|
147
|
+
status: "failed",
|
|
148
|
+
error: pe.error
|
|
149
|
+
});
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const finalPath = pe.finalPath;
|
|
153
|
+
const ifMatch = wantAbsent ? AFS_IFMATCH_ABSENT : void 0;
|
|
154
|
+
const ref = {
|
|
155
|
+
relPath: pe.relPath,
|
|
156
|
+
finalPath,
|
|
157
|
+
status: "needs-upload",
|
|
158
|
+
...ifMatch ? { ifMatch } : {}
|
|
159
|
+
};
|
|
160
|
+
responseEntries.push(ref);
|
|
161
|
+
const src = inputs[pe.index];
|
|
162
|
+
const size = src?.size ?? 0;
|
|
163
|
+
const failTooLarge = (capBytes, planeNote) => {
|
|
164
|
+
ref.status = "failed";
|
|
165
|
+
ref.error = {
|
|
166
|
+
code: "FILE_TOO_LARGE",
|
|
167
|
+
message: `File exceeds the ${capBytes}-byte ${planeNote} (declared ${size} bytes)`
|
|
168
|
+
};
|
|
169
|
+
delete ref.ifMatch;
|
|
170
|
+
};
|
|
171
|
+
if (size > limits.maxBytes) failTooLarge(limits.maxBytes, "upload limit");
|
|
172
|
+
else if (grantCapable && mountPrefix && src?.cid && size > 0) grantInputs.push({
|
|
173
|
+
ref,
|
|
174
|
+
providerRelPath: stripMount(finalPath, mountPrefix),
|
|
175
|
+
cid: src.cid,
|
|
176
|
+
size,
|
|
177
|
+
mimeType: src.mimeType,
|
|
178
|
+
ifMatch
|
|
179
|
+
});
|
|
180
|
+
else if (size > limits.legacyMaxBytes) failTooLarge(limits.legacyMaxBytes, "legacy upload limit (configure an R2 signer for larger files)");
|
|
181
|
+
}
|
|
182
|
+
let mode = "legacy";
|
|
183
|
+
let sessionId;
|
|
184
|
+
let expiresAt;
|
|
185
|
+
if (grantInputs.length > 0 && mountPrefix) {
|
|
186
|
+
const prepRes = await afs.exec(joinURL(mountPrefix, ".actions/prepare-upload"), { entries: grantInputs.map((g) => ({
|
|
187
|
+
path: g.providerRelPath,
|
|
188
|
+
cid: g.cid,
|
|
189
|
+
size: g.size,
|
|
190
|
+
...g.mimeType ? { mimeType: g.mimeType } : {},
|
|
191
|
+
...g.ifMatch ? { ifMatch: g.ifMatch } : {}
|
|
192
|
+
})) }, { context });
|
|
193
|
+
if (!prepRes?.success) return {
|
|
194
|
+
ok: false,
|
|
195
|
+
code: prepRes?.error?.code ?? "AFS_ERROR",
|
|
196
|
+
error: prepRes?.error?.message ?? "prepare-upload failed"
|
|
197
|
+
};
|
|
198
|
+
const pdata = prepRes.data;
|
|
199
|
+
mode = "grant";
|
|
200
|
+
sessionId = pdata.sessionId;
|
|
201
|
+
expiresAt = pdata.expiresAt;
|
|
202
|
+
const byPath = new Map((pdata.entries ?? []).map((e) => [e.path, e]));
|
|
203
|
+
for (const g of grantInputs) {
|
|
204
|
+
const pe = byPath.get(g.providerRelPath);
|
|
205
|
+
if (!pe) continue;
|
|
206
|
+
g.ref.objectKey = pe.objectKey;
|
|
207
|
+
g.ref.checksumSha256 = pe.checksumSha256;
|
|
208
|
+
if (pe.grant) g.ref.grant = pe.grant;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
ok: true,
|
|
213
|
+
data: {
|
|
214
|
+
mode,
|
|
215
|
+
protocolVersion: INGEST_PROTOCOL_VERSION,
|
|
216
|
+
...sessionId ? {
|
|
217
|
+
sessionId,
|
|
218
|
+
expiresAt
|
|
219
|
+
} : {},
|
|
220
|
+
entries: responseEntries
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function execCommitUpload(afs, args, context) {
|
|
225
|
+
const sessionId = asStr(args.sessionId);
|
|
226
|
+
if (!sessionId) return {
|
|
227
|
+
ok: false,
|
|
228
|
+
code: "AFS_VALIDATION_ERROR",
|
|
229
|
+
error: "sessionId required"
|
|
230
|
+
};
|
|
231
|
+
const rawEntries = Array.isArray(args.entries) ? args.entries : null;
|
|
232
|
+
if (!rawEntries || rawEntries.length === 0) return {
|
|
233
|
+
ok: false,
|
|
234
|
+
code: "AFS_VALIDATION_ERROR",
|
|
235
|
+
error: "entries must be a non-empty array"
|
|
236
|
+
};
|
|
237
|
+
const firstPath = asStr(rawEntries[0]?.path);
|
|
238
|
+
const mountPrefix = firstPath ? mountPrefixOf(firstPath) : null;
|
|
239
|
+
if (!mountPrefix) return {
|
|
240
|
+
ok: false,
|
|
241
|
+
code: "AFS_VALIDATION_ERROR",
|
|
242
|
+
error: "entry.path must be a root-visible path"
|
|
243
|
+
};
|
|
244
|
+
const providerEntries = rawEntries.map((raw) => {
|
|
245
|
+
const e = raw;
|
|
246
|
+
const p = asStr(e.path);
|
|
247
|
+
return {
|
|
248
|
+
...e,
|
|
249
|
+
path: p ? stripMount(p, mountPrefix) : p
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
const res = await afs.exec(joinURL(mountPrefix, ".actions/commit-upload"), {
|
|
253
|
+
sessionId,
|
|
254
|
+
entries: providerEntries
|
|
255
|
+
}, { context });
|
|
256
|
+
if (!res?.success) return {
|
|
257
|
+
ok: false,
|
|
258
|
+
code: res?.error?.code ?? "AFS_ERROR",
|
|
259
|
+
error: res?.error?.message ?? "commit-upload failed"
|
|
260
|
+
};
|
|
261
|
+
const data = res.data;
|
|
262
|
+
if (Array.isArray(data?.results)) data.results = data.results.map((r) => {
|
|
263
|
+
const out = { ...r };
|
|
264
|
+
const rp = asStr(r.path);
|
|
265
|
+
if (rp) out.path = addMount(rp, mountPrefix);
|
|
266
|
+
const inner = r.data;
|
|
267
|
+
if (inner && asStr(inner.path)) out.data = {
|
|
268
|
+
...inner,
|
|
269
|
+
path: addMount(inner.path, mountPrefix)
|
|
270
|
+
};
|
|
271
|
+
return out;
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
ok: true,
|
|
275
|
+
data
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async function execAbortUpload(afs, args, context) {
|
|
279
|
+
const sessionId = asStr(args.sessionId);
|
|
280
|
+
if (!sessionId) return {
|
|
281
|
+
ok: false,
|
|
282
|
+
code: "AFS_VALIDATION_ERROR",
|
|
283
|
+
error: "sessionId required"
|
|
284
|
+
};
|
|
285
|
+
const res = await afs.exec(joinURL(V1_GRANT_MOUNT, ".actions/abort-upload"), { sessionId }, { context });
|
|
286
|
+
if (!res?.success) return {
|
|
287
|
+
ok: false,
|
|
288
|
+
code: res?.error?.code ?? "AFS_ERROR",
|
|
289
|
+
error: res?.error?.message ?? "abort-upload failed"
|
|
290
|
+
};
|
|
291
|
+
return {
|
|
292
|
+
ok: true,
|
|
293
|
+
data: res.data
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Run a root-visible facade exec. Caller MUST pre-check {@link isIngestFacadePath}.
|
|
298
|
+
* The authed-caller floor (design §4.7: all three are write-effect) is enforced
|
|
299
|
+
* here — the CSRF/Origin guard is the afs-rpc handler's job upstream.
|
|
300
|
+
*/
|
|
301
|
+
async function execIngestFacade(input) {
|
|
302
|
+
const { afs, path, args, context, caller } = input;
|
|
303
|
+
if (!caller?.did) return {
|
|
304
|
+
status: 401,
|
|
305
|
+
error: "Authentication required for upload"
|
|
306
|
+
};
|
|
307
|
+
if (path === "/.actions/ingest-prepare") return execIngestPrepare(afs, args, context, input.limits ?? UPLOAD_LIMIT_DEFAULTS);
|
|
308
|
+
if (path === "/.actions/commit-upload") return execCommitUpload(afs, args, context);
|
|
309
|
+
return execAbortUpload(afs, args, context);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
export { execIngestFacade, injectIngestFacadeList, isIngestFacadePath };
|
|
314
|
+
//# sourceMappingURL=ingest-facade.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-facade.mjs","names":[],"sources":["../../src/http/ingest-facade.ts"],"sourcesContent":["/**\n * ingest-facade — the browser upload plane's server host (design §4.2, D1\n * candidate-b). The afs-rpc handler intercepts the root-visible facade actions\n *\n * /.actions/ingest-prepare — plan + (grant) mint PUT grants + open a session\n * /.actions/commit-upload — commit uploaded objects' metadata\n * /.actions/abort-upload — abort a session (staged objects → GC)\n *\n * and injects them into `list('/.actions')`, so the browser only ever speaks in\n * ROOT-VISIBLE paths (`/user/docs/a.pdf`) and never learns the mount boundary.\n *\n * Why a handler intercept (not a provider): the upload-plane actions\n * (prepare/commit/abort-upload) take PROVIDER-RELATIVE paths (`/docs/a.pdf`) but\n * the browser only knows root-visible paths. The intercept holds the composed\n * per-request `SessionUserAFS` (writable `/user`) + verified caller, runs the\n * shared `planIngest()` (one planning impl, core), then strips the mount prefix\n * and delegates to the target mount's prepare/commit/abort-upload. The\n * root-visible↔provider-relative translation lives only here — the browser never\n * does mount math, and we never produce a `tree/user/...` double-prefix key.\n *\n * Transport split (design §4.4a): these are all `exec` (HTTP fast-path, CSRF +\n * effect-gated). The legacy fallback's batchWrite is the browser's job (WS).\n */\n\nimport {\n type IngestAFS,\n normalizeConflict,\n planIngest,\n UPLOAD_LIMIT_DEFAULTS,\n type UploadLimits,\n} from \"@aigne/afs\";\nimport { AFS_IFMATCH_ABSENT } from \"@aigne/afs-did-space\";\nimport { joinURL } from \"ufo\";\n\n/** Capability-discovery version the browser checks before using this chain. */\nexport const INGEST_PROTOCOL_VERSION = 1;\n\n/** The three root-visible facade action names. */\nexport const INGEST_FACADE_ACTIONS = [\"ingest-prepare\", \"commit-upload\", \"abort-upload\"] as const;\n\n/**\n * v1 grant mount used when there is no path to derive it from (abort-upload\n * carries only a sessionId). v1 supports a single grant mount; prepare/commit\n * derive the mount from their path args, abort falls back to this.\n */\nconst V1_GRANT_MOUNT = \"/user\";\n\n/** A minimal AFS surface the facade needs: plan via stat, delegate via exec/list. */\nexport interface FacadeAFS extends IngestAFS {\n exec(\n path: string,\n args: Record<string, unknown>,\n options?: unknown,\n ): Promise<{\n success?: boolean;\n data?: unknown;\n error?: { code?: string; message?: string };\n }>;\n list(path: string, options?: unknown): Promise<{ data?: unknown }>;\n}\n\n/** Caller shape the facade needs (authed-caller gate). */\ninterface FacadeCaller {\n did?: string | null;\n}\n\n/**\n * Discriminated facade outcome. The afs-rpc handler maps it to a Response:\n * - `{ status }` → `fail(status, error)` (401 authed-caller floor)\n * - `{ ok: true }` → 200 `{ ok: true, data }`\n * - `{ ok: false }` → 200 `{ ok: false, error, code? }` (semantic failure)\n */\nexport type FacadeResult =\n | { status: 401 | 403; error: string }\n | { ok: true; data: unknown }\n | { ok: false; error: string; code?: string };\n\n/** True for the three root-visible facade exec paths (exact match only). */\nexport function isIngestFacadePath(path: string): boolean {\n return INGEST_FACADE_ACTIONS.some((a) => path === `/.actions/${a}`);\n}\n\n/**\n * Append the three facade entries to a `list('/.actions')` result so the browser\n * discovers the chain by listing the ROOT `/.actions` (design §4.5). Idempotent\n * (won't duplicate an id already present) — applied identically in all scopes.\n */\nexport function injectIngestFacadeList(data: unknown): unknown[] {\n const arr = Array.isArray(data) ? [...data] : [];\n const have = new Set(arr.map((e) => (e as { id?: string } | null)?.id));\n for (const name of INGEST_FACADE_ACTIONS) {\n if (have.has(name)) continue;\n arr.push({\n id: name,\n path: joinURL(\"/.actions\", name),\n meta: { kind: \"afs:executable\", protocolVersion: INGEST_PROTOCOL_VERSION },\n });\n }\n return arr;\n}\n\n// ── helpers ──────────────────────────────────────────────────────────────────\n\n/** v1: the mount prefix is the first path segment (`/user/docs` → `/user`). */\nfunction mountPrefixOf(path: string): string | null {\n const seg = path.split(\"/\").filter(Boolean)[0];\n return seg ? `/${seg}` : null;\n}\n\n/** Strip the mount prefix → provider-relative path (`/user/docs/a.pdf` → `/docs/a.pdf`). */\nfunction stripMount(path: string, mountPrefix: string): string {\n if (path === mountPrefix) return \"/\";\n const rel = path.slice(mountPrefix.length);\n return rel.startsWith(\"/\") ? rel : `/${rel}`;\n}\n\n/** Re-add the mount prefix → root-visible path (`/docs/a.pdf` → `/user/docs/a.pdf`). */\nfunction addMount(providerRel: string, mountPrefix: string): string {\n return joinURL(mountPrefix, providerRel);\n}\n\n/** Read the target mount's `prepare-upload.grantCapability` (design §4.5). */\nasync function isGrantCapable(\n afs: FacadeAFS,\n mountPrefix: string,\n context: Record<string, unknown>,\n): Promise<boolean> {\n try {\n const r = await afs.list(joinURL(mountPrefix, \".actions\"), { context });\n const entries = (Array.isArray(r?.data) ? r.data : []) as Array<{\n id?: string;\n meta?: { grantCapability?: unknown };\n }>;\n return entries.find((e) => e.id === \"prepare-upload\")?.meta?.grantCapability === true;\n } catch {\n return false;\n }\n}\n\nfunction asStr(v: unknown): string | undefined {\n return typeof v === \"string\" && v ? v : undefined;\n}\n\n// ── ingest-prepare ───────────────────────────────────────────────────────────\n\ninterface PrepareEntryInput {\n relPath: string;\n size: number;\n mimeType?: string;\n cid?: string;\n}\n\nasync function execIngestPrepare(\n afs: FacadeAFS,\n args: Record<string, unknown>,\n context: Record<string, unknown>,\n limits: UploadLimits,\n): Promise<FacadeResult> {\n const targetDir = asStr(args.targetDir);\n if (!targetDir) {\n return {\n ok: false,\n code: \"AFS_VALIDATION_ERROR\",\n error: \"targetDir must be a non-empty string\",\n };\n }\n const conflict = normalizeConflict(args.conflict) ?? \"rename\";\n const rawEntries = Array.isArray(args.entries) ? args.entries : null;\n if (!rawEntries || rawEntries.length === 0) {\n return { ok: false, code: \"AFS_VALIDATION_ERROR\", error: \"entries must be a non-empty array\" };\n }\n const inputs: PrepareEntryInput[] = [];\n for (const raw of rawEntries) {\n const entry = raw as { relPath?: unknown; size?: unknown; mimeType?: unknown; cid?: unknown };\n const relPath = asStr(entry.relPath);\n if (!relPath) {\n return {\n ok: false,\n code: \"AFS_VALIDATION_ERROR\",\n error: \"every entry needs a non-empty relPath\",\n };\n }\n inputs.push({\n relPath,\n size: typeof entry.size === \"number\" && entry.size >= 0 ? entry.size : 0,\n mimeType: asStr(entry.mimeType),\n cid: asStr(entry.cid),\n });\n }\n\n // Plan (the single shared planning impl, core). finalPath is root-visible.\n const plan = await planIngest(\n afs,\n targetDir,\n inputs.map((e) => ({ name: e.relPath, relPath: e.relPath })),\n conflict,\n );\n\n const mountPrefix = mountPrefixOf(targetDir);\n const grantCapable = mountPrefix ? await isGrantCapable(afs, mountPrefix, context) : false;\n\n // The conflict intent that must survive the prepare→write window (D3 §4.2a):\n // rename/error mean \"do not clobber\" → expected-absent precondition.\n const wantAbsent = conflict === \"rename\" || conflict === \"error\";\n\n interface ResponseEntry {\n relPath: string;\n finalPath: string | null;\n status: string;\n error?: { code: string; message: string };\n ifMatch?: string;\n objectKey?: string;\n checksumSha256?: string;\n grant?: unknown;\n }\n interface GrantInput {\n ref: ResponseEntry;\n providerRelPath: string;\n cid: string;\n size: number;\n mimeType?: string;\n ifMatch?: string;\n }\n\n const responseEntries: ResponseEntry[] = [];\n const grantInputs: GrantInput[] = [];\n\n for (const pe of plan.entries) {\n if (pe.status === \"skipped\") {\n responseEntries.push({ relPath: pe.relPath, finalPath: pe.finalPath, status: \"skipped\" });\n continue;\n }\n if (pe.status === \"failed\") {\n responseEntries.push({\n relPath: pe.relPath,\n finalPath: pe.finalPath,\n status: \"failed\",\n error: pe.error,\n });\n continue;\n }\n // pending ⇒ finalPath non-null (planIngest invariant).\n const finalPath = pe.finalPath!;\n const ifMatch = wantAbsent ? AFS_IFMATCH_ABSENT : undefined;\n const ref: ResponseEntry = {\n relPath: pe.relPath,\n finalPath,\n status: \"needs-upload\",\n ...(ifMatch ? { ifMatch } : {}),\n };\n responseEntries.push(ref);\n\n const src = inputs[pe.index];\n const size = src?.size ?? 0;\n // upload-limits Phase 2 — declared-size routing + early reject (design §5).\n // Best-effort BANDWIDTH optimization (the real boundary is Phase 1's\n // decoded-byte WS cap). FILE_TOO_LARGE is PER-ENTRY (status:\"failed\" +\n // error.code, like NO_PLAN_ENTRY/REL_TRAVERSAL) — NOT a throw — so the rest\n // of the batch still plans + grants. The server decides routing here; it\n // never trusts the client to self-limit.\n const failTooLarge = (capBytes: number, planeNote: string) => {\n ref.status = \"failed\";\n ref.error = {\n code: \"FILE_TOO_LARGE\",\n message: `File exceeds the ${capBytes}-byte ${planeNote} (declared ${size} bytes)`,\n };\n delete ref.ifMatch; // a rejected entry is never uploaded\n };\n if (size > limits.maxBytes) {\n // Over the grant-plane (R2-direct) product cap — reject on either plane.\n failTooLarge(limits.maxBytes, \"upload limit\");\n } else if (grantCapable && mountPrefix && src?.cid && size > 0) {\n // grant-capable(signer) AND cid present AND 0 < size ≤ maxBytes → grant.\n grantInputs.push({\n ref,\n providerRelPath: stripMount(finalPath, mountPrefix),\n cid: src.cid,\n size,\n mimeType: src.mimeType,\n ifMatch,\n });\n } else if (size > limits.legacyMaxBytes) {\n // No grant capability (no signer / no cid / unknown size) AND too big for\n // the legacy through-memory plane → reject. CRITICAL: never silently fall\n // through to an UNCAPPED legacy write (design §5 / review R2).\n failTooLarge(\n limits.legacyMaxBytes,\n \"legacy upload limit (configure an R2 signer for larger files)\",\n );\n }\n // else → legacy: ref stays \"needs-upload\" (Phase 1 decoded-byte cap backstops);\n // unknown/0 declared size also lands here (never granted on a 0 size).\n }\n\n let mode: \"grant\" | \"legacy\" = \"legacy\";\n let sessionId: string | undefined;\n let expiresAt: string | undefined;\n\n if (grantInputs.length > 0 && mountPrefix) {\n const prepRes = await afs.exec(\n joinURL(mountPrefix, \".actions/prepare-upload\"),\n {\n entries: grantInputs.map((g) => ({\n path: g.providerRelPath,\n cid: g.cid,\n size: g.size,\n ...(g.mimeType ? { mimeType: g.mimeType } : {}),\n ...(g.ifMatch ? { ifMatch: g.ifMatch } : {}),\n })),\n },\n { context },\n );\n if (!prepRes?.success) {\n return {\n ok: false,\n code: prepRes?.error?.code ?? \"AFS_ERROR\",\n error: prepRes?.error?.message ?? \"prepare-upload failed\",\n };\n }\n const pdata = prepRes.data as {\n sessionId?: string;\n expiresAt?: string;\n entries?: Array<{\n path?: string;\n objectKey?: string;\n checksumSha256?: string;\n grant?: unknown;\n }>;\n };\n mode = \"grant\";\n sessionId = pdata.sessionId;\n expiresAt = pdata.expiresAt;\n const byPath = new Map((pdata.entries ?? []).map((e) => [e.path, e]));\n for (const g of grantInputs) {\n const pe = byPath.get(g.providerRelPath);\n if (!pe) continue;\n g.ref.objectKey = pe.objectKey;\n g.ref.checksumSha256 = pe.checksumSha256;\n if (pe.grant) g.ref.grant = pe.grant;\n }\n }\n\n return {\n ok: true,\n data: {\n mode,\n protocolVersion: INGEST_PROTOCOL_VERSION,\n ...(sessionId ? { sessionId, expiresAt } : {}),\n entries: responseEntries,\n },\n };\n}\n\n// ── commit-upload ────────────────────────────────────────────────────────────\n\nasync function execCommitUpload(\n afs: FacadeAFS,\n args: Record<string, unknown>,\n context: Record<string, unknown>,\n): Promise<FacadeResult> {\n const sessionId = asStr(args.sessionId);\n if (!sessionId) {\n return { ok: false, code: \"AFS_VALIDATION_ERROR\", error: \"sessionId required\" };\n }\n const rawEntries = Array.isArray(args.entries) ? args.entries : null;\n if (!rawEntries || rawEntries.length === 0) {\n return { ok: false, code: \"AFS_VALIDATION_ERROR\", error: \"entries must be a non-empty array\" };\n }\n const firstPath = asStr((rawEntries[0] as { path?: unknown })?.path);\n const mountPrefix = firstPath ? mountPrefixOf(firstPath) : null;\n if (!mountPrefix) {\n return {\n ok: false,\n code: \"AFS_VALIDATION_ERROR\",\n error: \"entry.path must be a root-visible path\",\n };\n }\n\n // Translate every entry's root-visible path → provider-relative for the mount.\n const providerEntries = rawEntries.map((raw) => {\n const e = raw as Record<string, unknown>;\n const p = asStr(e.path);\n return { ...e, path: p ? stripMount(p, mountPrefix) : p };\n });\n\n const res = await afs.exec(\n joinURL(mountPrefix, \".actions/commit-upload\"),\n { sessionId, entries: providerEntries },\n { context },\n );\n if (!res?.success) {\n return {\n ok: false,\n code: res?.error?.code ?? \"AFS_ERROR\",\n error: res?.error?.message ?? \"commit-upload failed\",\n };\n }\n // Re-add the mount prefix on every result path → root-visible for the browser.\n const data = res.data as { results?: Array<Record<string, unknown>> } & Record<string, unknown>;\n if (Array.isArray(data?.results)) {\n data.results = data.results.map((r) => {\n const out = { ...r };\n const rp = asStr(r.path);\n if (rp) out.path = addMount(rp, mountPrefix);\n const inner = r.data as { path?: unknown } | undefined;\n if (inner && asStr(inner.path)) {\n out.data = { ...inner, path: addMount(inner.path as string, mountPrefix) };\n }\n return out;\n });\n }\n return { ok: true, data };\n}\n\n// ── abort-upload ─────────────────────────────────────────────────────────────\n\nasync function execAbortUpload(\n afs: FacadeAFS,\n args: Record<string, unknown>,\n context: Record<string, unknown>,\n): Promise<FacadeResult> {\n const sessionId = asStr(args.sessionId);\n if (!sessionId) {\n return { ok: false, code: \"AFS_VALIDATION_ERROR\", error: \"sessionId required\" };\n }\n // abort carries no path; v1 routes to the single grant mount.\n const res = await afs.exec(\n joinURL(V1_GRANT_MOUNT, \".actions/abort-upload\"),\n { sessionId },\n { context },\n );\n if (!res?.success) {\n return {\n ok: false,\n code: res?.error?.code ?? \"AFS_ERROR\",\n error: res?.error?.message ?? \"abort-upload failed\",\n };\n }\n return { ok: true, data: res.data };\n}\n\n// ── dispatch ─────────────────────────────────────────────────────────────────\n\n/**\n * Run a root-visible facade exec. Caller MUST pre-check {@link isIngestFacadePath}.\n * The authed-caller floor (design §4.7: all three are write-effect) is enforced\n * here — the CSRF/Origin guard is the afs-rpc handler's job upstream.\n */\nexport async function execIngestFacade(input: {\n afs: FacadeAFS;\n path: string;\n args: Record<string, unknown>;\n context: Record<string, unknown>;\n caller: FacadeCaller | null;\n /**\n * Server-enforced upload caps (upload-limits Phase 2). The handler resolves\n * them at the runtime single env-read point; only ingest-prepare consumes them\n * (declared-size routing). Optional → safe defaults (never unlimited).\n */\n limits?: UploadLimits;\n}): Promise<FacadeResult> {\n const { afs, path, args, context, caller } = input;\n if (!caller?.did) {\n return { status: 401, error: \"Authentication required for upload\" };\n }\n if (path === \"/.actions/ingest-prepare\") {\n return execIngestPrepare(afs, args, context, input.limits ?? UPLOAD_LIMIT_DEFAULTS);\n }\n if (path === \"/.actions/commit-upload\") return execCommitUpload(afs, args, context);\n return execAbortUpload(afs, args, context);\n}\n"],"mappings":"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"}
|