@alchemy.run/modal 0.0.0 → 2.0.0-beta.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +96 -4
- package/lib/App.d.ts +143 -0
- package/lib/App.d.ts.map +1 -0
- package/lib/App.js +198 -0
- package/lib/App.js.map +1 -0
- package/lib/AuthProvider.d.ts +53 -0
- package/lib/AuthProvider.d.ts.map +1 -0
- package/lib/AuthProvider.js +209 -0
- package/lib/AuthProvider.js.map +1 -0
- package/lib/BindingHttp.d.ts +56 -0
- package/lib/BindingHttp.d.ts.map +1 -0
- package/lib/BindingHttp.js +50 -0
- package/lib/BindingHttp.js.map +1 -0
- package/lib/Blob.d.ts +34 -0
- package/lib/Blob.d.ts.map +1 -0
- package/lib/Blob.js +59 -0
- package/lib/Blob.js.map +1 -0
- package/lib/Bootstrap.d.ts +8 -0
- package/lib/Bootstrap.d.ts.map +1 -0
- package/lib/Bootstrap.js +26 -0
- package/lib/Bootstrap.js.map +1 -0
- package/lib/Cbor.d.ts +10 -0
- package/lib/Cbor.d.ts.map +1 -0
- package/lib/Cbor.js +177 -0
- package/lib/Cbor.js.map +1 -0
- package/lib/ConnectSandbox.d.ts +64 -0
- package/lib/ConnectSandbox.d.ts.map +1 -0
- package/lib/ConnectSandbox.js +33 -0
- package/lib/ConnectSandbox.js.map +1 -0
- package/lib/Container.d.ts +124 -0
- package/lib/Container.d.ts.map +1 -0
- package/lib/Container.js +1 -0
- package/lib/Container.js.map +1 -0
- package/lib/Contributions.d.ts +7 -0
- package/lib/Contributions.d.ts.map +1 -0
- package/lib/Contributions.js +6 -0
- package/lib/Contributions.js.map +1 -0
- package/lib/CreateDict.d.ts +83 -0
- package/lib/CreateDict.d.ts.map +1 -0
- package/lib/CreateDict.js +3 -0
- package/lib/CreateDict.js.map +1 -0
- package/lib/CreateDictHttp.d.ts +33 -0
- package/lib/CreateDictHttp.d.ts.map +1 -0
- package/lib/CreateDictHttp.js +67 -0
- package/lib/CreateDictHttp.js.map +1 -0
- package/lib/CreateImage.d.ts +98 -0
- package/lib/CreateImage.d.ts.map +1 -0
- package/lib/CreateImage.js +3 -0
- package/lib/CreateImage.js.map +1 -0
- package/lib/CreateImageHttp.d.ts +51 -0
- package/lib/CreateImageHttp.d.ts.map +1 -0
- package/lib/CreateImageHttp.js +87 -0
- package/lib/CreateImageHttp.js.map +1 -0
- package/lib/CreateQueue.d.ts +83 -0
- package/lib/CreateQueue.d.ts.map +1 -0
- package/lib/CreateQueue.js +3 -0
- package/lib/CreateQueue.js.map +1 -0
- package/lib/CreateQueueHttp.d.ts +31 -0
- package/lib/CreateQueueHttp.d.ts.map +1 -0
- package/lib/CreateQueueHttp.js +67 -0
- package/lib/CreateQueueHttp.js.map +1 -0
- package/lib/CreateSandbox.d.ts +186 -0
- package/lib/CreateSandbox.d.ts.map +1 -0
- package/lib/CreateSandbox.js +3 -0
- package/lib/CreateSandbox.js.map +1 -0
- package/lib/CreateSandboxHttp.d.ts +17 -0
- package/lib/CreateSandboxHttp.d.ts.map +1 -0
- package/lib/CreateSandboxHttp.js +117 -0
- package/lib/CreateSandboxHttp.js.map +1 -0
- package/lib/CreateSecret.d.ts +95 -0
- package/lib/CreateSecret.d.ts.map +1 -0
- package/lib/CreateSecret.js +3 -0
- package/lib/CreateSecret.js.map +1 -0
- package/lib/CreateSecretHttp.d.ts +24 -0
- package/lib/CreateSecretHttp.d.ts.map +1 -0
- package/lib/CreateSecretHttp.js +60 -0
- package/lib/CreateSecretHttp.js.map +1 -0
- package/lib/CreateVolume.d.ts +84 -0
- package/lib/CreateVolume.d.ts.map +1 -0
- package/lib/CreateVolume.js +3 -0
- package/lib/CreateVolume.js.map +1 -0
- package/lib/CreateVolumeHttp.d.ts +32 -0
- package/lib/CreateVolumeHttp.d.ts.map +1 -0
- package/lib/CreateVolumeHttp.js +69 -0
- package/lib/CreateVolumeHttp.js.map +1 -0
- package/lib/Credentials.d.ts +32 -0
- package/lib/Credentials.d.ts.map +1 -0
- package/lib/Credentials.js +44 -0
- package/lib/Credentials.js.map +1 -0
- package/lib/Dict.d.ts +147 -0
- package/lib/Dict.d.ts.map +1 -0
- package/lib/Dict.js +175 -0
- package/lib/Dict.js.map +1 -0
- package/lib/DictClient.d.ts +55 -0
- package/lib/DictClient.d.ts.map +1 -0
- package/lib/DictClient.js +89 -0
- package/lib/DictClient.js.map +1 -0
- package/lib/Domain.d.ts +86 -0
- package/lib/Domain.d.ts.map +1 -0
- package/lib/Domain.js +113 -0
- package/lib/Domain.js.map +1 -0
- package/lib/Endpoint.d.ts +181 -0
- package/lib/Endpoint.d.ts.map +1 -0
- package/lib/Endpoint.js +283 -0
- package/lib/Endpoint.js.map +1 -0
- package/lib/Environment.d.ts +174 -0
- package/lib/Environment.d.ts.map +1 -0
- package/lib/Environment.js +244 -0
- package/lib/Environment.js.map +1 -0
- package/lib/FetchServer.d.ts +105 -0
- package/lib/FetchServer.d.ts.map +1 -0
- package/lib/FetchServer.js +3 -0
- package/lib/FetchServer.js.map +1 -0
- package/lib/FetchServerHttp.d.ts +21 -0
- package/lib/FetchServerHttp.d.ts.map +1 -0
- package/lib/FetchServerHttp.js +84 -0
- package/lib/FetchServerHttp.js.map +1 -0
- package/lib/Function.d.ts +236 -0
- package/lib/Function.d.ts.map +1 -0
- package/lib/Function.js +273 -0
- package/lib/Function.js.map +1 -0
- package/lib/FunctionAutoscaler.d.ts +99 -0
- package/lib/FunctionAutoscaler.d.ts.map +1 -0
- package/lib/FunctionAutoscaler.js +136 -0
- package/lib/FunctionAutoscaler.js.map +1 -0
- package/lib/FunctionTarget.d.ts +61 -0
- package/lib/FunctionTarget.d.ts.map +1 -0
- package/lib/FunctionTarget.js +136 -0
- package/lib/FunctionTarget.js.map +1 -0
- package/lib/Image.d.ts +159 -0
- package/lib/Image.d.ts.map +1 -0
- package/lib/Image.js +203 -0
- package/lib/Image.js.map +1 -0
- package/lib/ImageBuild.d.ts +108 -0
- package/lib/ImageBuild.d.ts.map +1 -0
- package/lib/ImageBuild.js +157 -0
- package/lib/ImageBuild.js.map +1 -0
- package/lib/ImagePlan.d.ts +31 -0
- package/lib/ImagePlan.d.ts.map +1 -0
- package/lib/ImagePlan.js +70 -0
- package/lib/ImagePlan.js.map +1 -0
- package/lib/InferEnv.d.ts +26 -0
- package/lib/InferEnv.d.ts.map +1 -0
- package/lib/InferEnv.js +1 -0
- package/lib/InferEnv.js.map +1 -0
- package/lib/InvokeFunction.d.ts +186 -0
- package/lib/InvokeFunction.d.ts.map +1 -0
- package/lib/InvokeFunction.js +10 -0
- package/lib/InvokeFunction.js.map +1 -0
- package/lib/InvokeFunctionHttp.d.ts +39 -0
- package/lib/InvokeFunctionHttp.d.ts.map +1 -0
- package/lib/InvokeFunctionHttp.js +210 -0
- package/lib/InvokeFunctionHttp.js.map +1 -0
- package/lib/ModalBindings.d.ts +138 -0
- package/lib/ModalBindings.d.ts.map +1 -0
- package/lib/ModalBindings.js +223 -0
- package/lib/ModalBindings.js.map +1 -0
- package/lib/MountBucket.d.ts +80 -0
- package/lib/MountBucket.d.ts.map +1 -0
- package/lib/MountBucket.js +44 -0
- package/lib/MountBucket.js.map +1 -0
- package/lib/MountVolume.d.ts +77 -0
- package/lib/MountVolume.d.ts.map +1 -0
- package/lib/MountVolume.js +41 -0
- package/lib/MountVolume.js.map +1 -0
- package/lib/Mounts.d.ts +134 -0
- package/lib/Mounts.d.ts.map +1 -0
- package/lib/Mounts.js +87 -0
- package/lib/Mounts.js.map +1 -0
- package/lib/PersistentSandbox.d.ts +75 -0
- package/lib/PersistentSandbox.d.ts.map +1 -0
- package/lib/PersistentSandbox.js +93 -0
- package/lib/PersistentSandbox.js.map +1 -0
- package/lib/Providers.d.ts +23 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +64 -0
- package/lib/Providers.js.map +1 -0
- package/lib/Proxy.d.ts +94 -0
- package/lib/Proxy.d.ts.map +1 -0
- package/lib/Proxy.js +132 -0
- package/lib/Proxy.js.map +1 -0
- package/lib/Queue.d.ts +147 -0
- package/lib/Queue.d.ts.map +1 -0
- package/lib/Queue.js +177 -0
- package/lib/Queue.js.map +1 -0
- package/lib/QueueClient.d.ts +64 -0
- package/lib/QueueClient.d.ts.map +1 -0
- package/lib/QueueClient.js +73 -0
- package/lib/QueueClient.js.map +1 -0
- package/lib/ReadApp.d.ts +85 -0
- package/lib/ReadApp.d.ts.map +1 -0
- package/lib/ReadApp.js +3 -0
- package/lib/ReadApp.js.map +1 -0
- package/lib/ReadAppHttp.d.ts +26 -0
- package/lib/ReadAppHttp.d.ts.map +1 -0
- package/lib/ReadAppHttp.js +106 -0
- package/lib/ReadAppHttp.js.map +1 -0
- package/lib/ReadDict.d.ts +39 -0
- package/lib/ReadDict.d.ts.map +1 -0
- package/lib/ReadDict.js +3 -0
- package/lib/ReadDict.js.map +1 -0
- package/lib/ReadDictHttp.d.ts +12 -0
- package/lib/ReadDictHttp.d.ts.map +1 -0
- package/lib/ReadDictHttp.js +23 -0
- package/lib/ReadDictHttp.js.map +1 -0
- package/lib/ReadFunction.d.ts +73 -0
- package/lib/ReadFunction.d.ts.map +1 -0
- package/lib/ReadFunction.js +3 -0
- package/lib/ReadFunction.js.map +1 -0
- package/lib/ReadFunctionHttp.d.ts +25 -0
- package/lib/ReadFunctionHttp.d.ts.map +1 -0
- package/lib/ReadFunctionHttp.js +63 -0
- package/lib/ReadFunctionHttp.js.map +1 -0
- package/lib/ReadQueue.d.ts +39 -0
- package/lib/ReadQueue.d.ts.map +1 -0
- package/lib/ReadQueue.js +3 -0
- package/lib/ReadQueue.js.map +1 -0
- package/lib/ReadQueueHttp.d.ts +12 -0
- package/lib/ReadQueueHttp.d.ts.map +1 -0
- package/lib/ReadQueueHttp.js +23 -0
- package/lib/ReadQueueHttp.js.map +1 -0
- package/lib/ReadServer.d.ts +72 -0
- package/lib/ReadServer.d.ts.map +1 -0
- package/lib/ReadServer.js +3 -0
- package/lib/ReadServer.js.map +1 -0
- package/lib/ReadServerHttp.d.ts +25 -0
- package/lib/ReadServerHttp.d.ts.map +1 -0
- package/lib/ReadServerHttp.js +70 -0
- package/lib/ReadServerHttp.js.map +1 -0
- package/lib/ReadVolume.d.ts +40 -0
- package/lib/ReadVolume.d.ts.map +1 -0
- package/lib/ReadVolume.js +3 -0
- package/lib/ReadVolume.js.map +1 -0
- package/lib/ReadVolumeHttp.d.ts +12 -0
- package/lib/ReadVolumeHttp.d.ts.map +1 -0
- package/lib/ReadVolumeHttp.js +24 -0
- package/lib/ReadVolumeHttp.js.map +1 -0
- package/lib/ReadWriteDict.d.ts +39 -0
- package/lib/ReadWriteDict.d.ts.map +1 -0
- package/lib/ReadWriteDict.js +3 -0
- package/lib/ReadWriteDict.js.map +1 -0
- package/lib/ReadWriteDictHttp.d.ts +12 -0
- package/lib/ReadWriteDictHttp.d.ts.map +1 -0
- package/lib/ReadWriteDictHttp.js +23 -0
- package/lib/ReadWriteDictHttp.js.map +1 -0
- package/lib/ReadWriteQueue.d.ts +40 -0
- package/lib/ReadWriteQueue.d.ts.map +1 -0
- package/lib/ReadWriteQueue.js +3 -0
- package/lib/ReadWriteQueue.js.map +1 -0
- package/lib/ReadWriteQueueHttp.d.ts +12 -0
- package/lib/ReadWriteQueueHttp.d.ts.map +1 -0
- package/lib/ReadWriteQueueHttp.js +23 -0
- package/lib/ReadWriteQueueHttp.js.map +1 -0
- package/lib/ReadWriteVolume.d.ts +40 -0
- package/lib/ReadWriteVolume.d.ts.map +1 -0
- package/lib/ReadWriteVolume.js +3 -0
- package/lib/ReadWriteVolume.js.map +1 -0
- package/lib/ReadWriteVolumeHttp.d.ts +12 -0
- package/lib/ReadWriteVolumeHttp.d.ts.map +1 -0
- package/lib/ReadWriteVolumeHttp.js +24 -0
- package/lib/ReadWriteVolumeHttp.js.map +1 -0
- package/lib/RuntimeOwner.d.ts +39 -0
- package/lib/RuntimeOwner.d.ts.map +1 -0
- package/lib/RuntimeOwner.js +85 -0
- package/lib/RuntimeOwner.js.map +1 -0
- package/lib/Sandbox.d.ts +215 -0
- package/lib/Sandbox.d.ts.map +1 -0
- package/lib/Sandbox.js +278 -0
- package/lib/Sandbox.js.map +1 -0
- package/lib/SandboxClient.d.ts +748 -0
- package/lib/SandboxClient.d.ts.map +1 -0
- package/lib/SandboxClient.js +710 -0
- package/lib/SandboxClient.js.map +1 -0
- package/lib/ScaleFunction.d.ts +54 -0
- package/lib/ScaleFunction.d.ts.map +1 -0
- package/lib/ScaleFunction.js +3 -0
- package/lib/ScaleFunction.js.map +1 -0
- package/lib/ScaleFunctionHttp.d.ts +21 -0
- package/lib/ScaleFunctionHttp.d.ts.map +1 -0
- package/lib/ScaleFunctionHttp.js +39 -0
- package/lib/ScaleFunctionHttp.js.map +1 -0
- package/lib/Secret.d.ts +170 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +196 -0
- package/lib/Secret.js.map +1 -0
- package/lib/Server.d.ts +155 -0
- package/lib/Server.d.ts.map +1 -0
- package/lib/Server.js +184 -0
- package/lib/Server.js.map +1 -0
- package/lib/ServiceUser.d.ts +139 -0
- package/lib/ServiceUser.d.ts.map +1 -0
- package/lib/ServiceUser.js +210 -0
- package/lib/ServiceUser.js.map +1 -0
- package/lib/Volume.d.ts +170 -0
- package/lib/Volume.d.ts.map +1 -0
- package/lib/Volume.js +220 -0
- package/lib/Volume.js.map +1 -0
- package/lib/VolumeClient.d.ts +53 -0
- package/lib/VolumeClient.d.ts.map +1 -0
- package/lib/VolumeClient.js +22 -0
- package/lib/VolumeClient.js.map +1 -0
- package/lib/VolumeFiles.d.ts +94 -0
- package/lib/VolumeFiles.d.ts.map +1 -0
- package/lib/VolumeFiles.js +139 -0
- package/lib/VolumeFiles.js.map +1 -0
- package/lib/WebhookToken.d.ts +102 -0
- package/lib/WebhookToken.d.ts.map +1 -0
- package/lib/WebhookToken.js +176 -0
- package/lib/WebhookToken.js.map +1 -0
- package/lib/WorkspaceSettings.d.ts +70 -0
- package/lib/WorkspaceSettings.d.ts.map +1 -0
- package/lib/WorkspaceSettings.js +105 -0
- package/lib/WorkspaceSettings.js.map +1 -0
- package/lib/WriteApp.d.ts +38 -0
- package/lib/WriteApp.d.ts.map +1 -0
- package/lib/WriteApp.js +3 -0
- package/lib/WriteApp.js.map +1 -0
- package/lib/WriteAppHttp.d.ts +23 -0
- package/lib/WriteAppHttp.d.ts.map +1 -0
- package/lib/WriteAppHttp.js +36 -0
- package/lib/WriteAppHttp.js.map +1 -0
- package/lib/WriteDict.d.ts +39 -0
- package/lib/WriteDict.d.ts.map +1 -0
- package/lib/WriteDict.js +3 -0
- package/lib/WriteDict.js.map +1 -0
- package/lib/WriteDictHttp.d.ts +12 -0
- package/lib/WriteDictHttp.d.ts.map +1 -0
- package/lib/WriteDictHttp.js +23 -0
- package/lib/WriteDictHttp.js.map +1 -0
- package/lib/WriteQueue.d.ts +39 -0
- package/lib/WriteQueue.d.ts.map +1 -0
- package/lib/WriteQueue.js +3 -0
- package/lib/WriteQueue.js.map +1 -0
- package/lib/WriteQueueHttp.d.ts +12 -0
- package/lib/WriteQueueHttp.d.ts.map +1 -0
- package/lib/WriteQueueHttp.js +23 -0
- package/lib/WriteQueueHttp.js.map +1 -0
- package/lib/WriteSecret.d.ts +53 -0
- package/lib/WriteSecret.d.ts.map +1 -0
- package/lib/WriteSecret.js +3 -0
- package/lib/WriteSecret.js.map +1 -0
- package/lib/WriteSecretHttp.d.ts +23 -0
- package/lib/WriteSecretHttp.d.ts.map +1 -0
- package/lib/WriteSecretHttp.js +44 -0
- package/lib/WriteSecretHttp.js.map +1 -0
- package/lib/WriteVolume.d.ts +39 -0
- package/lib/WriteVolume.d.ts.map +1 -0
- package/lib/WriteVolume.js +3 -0
- package/lib/WriteVolume.js.map +1 -0
- package/lib/WriteVolumeHttp.d.ts +12 -0
- package/lib/WriteVolumeHttp.d.ts.map +1 -0
- package/lib/WriteVolumeHttp.js +24 -0
- package/lib/WriteVolumeHttp.js.map +1 -0
- package/lib/index.d.ts +80 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +78 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/Declared.d.ts +29 -0
- package/lib/internal/Declared.d.ts.map +1 -0
- package/lib/internal/Declared.js +31 -0
- package/lib/internal/Declared.js.map +1 -0
- package/lib/internal/Deploy.d.ts +110 -0
- package/lib/internal/Deploy.d.ts.map +1 -0
- package/lib/internal/Deploy.js +230 -0
- package/lib/internal/Deploy.js.map +1 -0
- package/lib/internal/Entrypoint.d.ts +52 -0
- package/lib/internal/Entrypoint.d.ts.map +1 -0
- package/lib/internal/Entrypoint.js +193 -0
- package/lib/internal/Entrypoint.js.map +1 -0
- package/lib/internal/Rpc.d.ts +17 -0
- package/lib/internal/Rpc.d.ts.map +1 -0
- package/lib/internal/Rpc.js +49 -0
- package/lib/internal/Rpc.js.map +1 -0
- package/lib/internal/RpcAuth.d.ts +41 -0
- package/lib/internal/RpcAuth.d.ts.map +1 -0
- package/lib/internal/RpcAuth.js +100 -0
- package/lib/internal/RpcAuth.js.map +1 -0
- package/lib/internal/Runtime.d.ts +127 -0
- package/lib/internal/Runtime.d.ts.map +1 -0
- package/lib/internal/Runtime.js +246 -0
- package/lib/internal/Runtime.js.map +1 -0
- package/lib/internal/Tags.d.ts +19 -0
- package/lib/internal/Tags.d.ts.map +1 -0
- package/lib/internal/Tags.js +33 -0
- package/lib/internal/Tags.js.map +1 -0
- package/lib/internal/TcpConnect.d.ts +12 -0
- package/lib/internal/TcpConnect.d.ts.map +1 -0
- package/lib/internal/TcpConnect.js +46 -0
- package/lib/internal/TcpConnect.js.map +1 -0
- package/lib/internal/Transport.d.ts +13 -0
- package/lib/internal/Transport.d.ts.map +1 -0
- package/lib/internal/Transport.js +16 -0
- package/lib/internal/Transport.js.map +1 -0
- package/lib/internal.d.ts +71 -0
- package/lib/internal.d.ts.map +1 -0
- package/lib/internal.js +102 -0
- package/lib/internal.js.map +1 -0
- package/package.json +59 -3
- package/src/App.ts +262 -0
- package/src/AuthProvider.ts +295 -0
- package/src/BindingHttp.ts +112 -0
- package/src/Blob.ts +71 -0
- package/src/Bootstrap.ts +52 -0
- package/src/Cbor.ts +158 -0
- package/src/ConnectSandbox.ts +99 -0
- package/src/Container.ts +121 -0
- package/src/Contributions.ts +9 -0
- package/src/CreateDict.ts +95 -0
- package/src/CreateDictHttp.ts +91 -0
- package/src/CreateImage.ts +105 -0
- package/src/CreateImageHttp.ts +117 -0
- package/src/CreateQueue.ts +95 -0
- package/src/CreateQueueHttp.ts +91 -0
- package/src/CreateSandbox.ts +228 -0
- package/src/CreateSandboxHttp.ts +160 -0
- package/src/CreateSecret.ts +109 -0
- package/src/CreateSecretHttp.ts +83 -0
- package/src/CreateVolume.ts +96 -0
- package/src/CreateVolumeHttp.ts +93 -0
- package/src/Credentials.ts +74 -0
- package/src/Dict.ts +245 -0
- package/src/DictClient.ts +166 -0
- package/src/Domain.ts +147 -0
- package/src/Endpoint.ts +406 -0
- package/src/Environment.ts +373 -0
- package/src/FetchServer.ts +126 -0
- package/src/FetchServerHttp.ts +110 -0
- package/src/Function.ts +397 -0
- package/src/FunctionAutoscaler.ts +210 -0
- package/src/FunctionTarget.ts +181 -0
- package/src/Image.ts +281 -0
- package/src/ImageBuild.ts +228 -0
- package/src/ImagePlan.ts +78 -0
- package/src/InferEnv.ts +27 -0
- package/src/InvokeFunction.ts +211 -0
- package/src/InvokeFunctionHttp.ts +316 -0
- package/src/ModalBindings.ts +310 -0
- package/src/MountBucket.ts +137 -0
- package/src/MountVolume.ts +113 -0
- package/src/Mounts.ts +230 -0
- package/src/PersistentSandbox.ts +142 -0
- package/src/Providers.ts +100 -0
- package/src/Proxy.ts +191 -0
- package/src/Queue.ts +258 -0
- package/src/QueueClient.ts +149 -0
- package/src/ReadApp.ts +101 -0
- package/src/ReadAppHttp.ts +142 -0
- package/src/ReadDict.ts +44 -0
- package/src/ReadDictHttp.ts +27 -0
- package/src/ReadFunction.ts +107 -0
- package/src/ReadFunctionHttp.ts +91 -0
- package/src/ReadQueue.ts +44 -0
- package/src/ReadQueueHttp.ts +27 -0
- package/src/ReadServer.ts +98 -0
- package/src/ReadServerHttp.ts +101 -0
- package/src/ReadVolume.ts +45 -0
- package/src/ReadVolumeHttp.ts +28 -0
- package/src/ReadWriteDict.ts +44 -0
- package/src/ReadWriteDictHttp.ts +27 -0
- package/src/ReadWriteQueue.ts +45 -0
- package/src/ReadWriteQueueHttp.ts +27 -0
- package/src/ReadWriteVolume.ts +45 -0
- package/src/ReadWriteVolumeHttp.ts +28 -0
- package/src/RuntimeOwner.ts +111 -0
- package/src/Sandbox.ts +402 -0
- package/src/SandboxClient.ts +1404 -0
- package/src/ScaleFunction.ts +70 -0
- package/src/ScaleFunctionHttp.ts +54 -0
- package/src/Secret.ts +296 -0
- package/src/Server.ts +285 -0
- package/src/ServiceUser.ts +295 -0
- package/src/Volume.ts +320 -0
- package/src/VolumeClient.ts +108 -0
- package/src/VolumeFiles.ts +194 -0
- package/src/WebhookToken.ts +230 -0
- package/src/WorkspaceSettings.ts +160 -0
- package/src/WriteApp.ts +43 -0
- package/src/WriteAppHttp.ts +51 -0
- package/src/WriteDict.ts +44 -0
- package/src/WriteDictHttp.ts +27 -0
- package/src/WriteQueue.ts +44 -0
- package/src/WriteQueueHttp.ts +27 -0
- package/src/WriteSecret.ts +60 -0
- package/src/WriteSecretHttp.ts +56 -0
- package/src/WriteVolume.ts +44 -0
- package/src/WriteVolumeHttp.ts +28 -0
- package/src/globals.d.ts +2 -0
- package/src/index.ts +85 -0
- package/src/internal/Declared.ts +80 -0
- package/src/internal/Deploy.ts +309 -0
- package/src/internal/Entrypoint.ts +198 -0
- package/src/internal/Rpc.ts +59 -0
- package/src/internal/RpcAuth.ts +132 -0
- package/src/internal/Runtime.ts +386 -0
- package/src/internal/Tags.ts +44 -0
- package/src/internal/TcpConnect.ts +78 -0
- package/src/internal/Transport.ts +25 -0
- package/src/internal.ts +147 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
VolumeGetFile2Error,
|
|
3
|
+
VolumeListFiles2Error,
|
|
4
|
+
VolumeRemoveFile2Error,
|
|
5
|
+
VolumeCopyFiles2Error,
|
|
6
|
+
VolumePutFiles2Error,
|
|
7
|
+
} from "@distilled.cloud/modal/volume";
|
|
8
|
+
import type { RuntimeContext } from "alchemy/RuntimeContext";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import type { HttpClientError } from "effect/http/HttpClientError";
|
|
11
|
+
import {
|
|
12
|
+
copyVolumeFiles,
|
|
13
|
+
listVolumeFiles,
|
|
14
|
+
readVolumeFile,
|
|
15
|
+
removeVolumeFile,
|
|
16
|
+
writeVolumeFile,
|
|
17
|
+
type VolumeFileEntry,
|
|
18
|
+
type VolumeFileOps,
|
|
19
|
+
type VolumeTransferError,
|
|
20
|
+
} from "./VolumeFiles.ts";
|
|
21
|
+
|
|
22
|
+
/** Errors reading a volume file: `VolumeFileNotFound` when the path is missing. */
|
|
23
|
+
export type ReadVolumeFileError = VolumeGetFile2Error | VolumeTransferError | HttpClientError;
|
|
24
|
+
/** Errors listing a volume directory. */
|
|
25
|
+
export type ListVolumeFilesError = VolumeListFiles2Error;
|
|
26
|
+
/** Errors removing or copying volume files. */
|
|
27
|
+
export type ChangeVolumeFilesError = VolumeRemoveFile2Error | VolumeCopyFiles2Error;
|
|
28
|
+
/** Errors writing a volume file. */
|
|
29
|
+
export type WriteVolumeFileError = VolumePutFiles2Error | VolumeTransferError | HttpClientError;
|
|
30
|
+
|
|
31
|
+
/** Read access to one volume's files. */
|
|
32
|
+
export interface ReadVolumeClient {
|
|
33
|
+
/** The volume's id. */
|
|
34
|
+
readonly volumeId: Effect.Effect<string>;
|
|
35
|
+
/** Read a file. */
|
|
36
|
+
readFile(path: string): Effect.Effect<Uint8Array, ReadVolumeFileError, RuntimeContext>;
|
|
37
|
+
/** Read a UTF-8 file. */
|
|
38
|
+
readFileString(path: string): Effect.Effect<string, ReadVolumeFileError, RuntimeContext>;
|
|
39
|
+
/** List a directory (`recursive` walks it). */
|
|
40
|
+
listFiles(
|
|
41
|
+
path: string,
|
|
42
|
+
options?: { readonly recursive?: boolean },
|
|
43
|
+
): Effect.Effect<ReadonlyArray<VolumeFileEntry>, ListVolumeFilesError, RuntimeContext>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Write access to one volume's files. */
|
|
47
|
+
export interface WriteVolumeClient {
|
|
48
|
+
/** The volume's id. */
|
|
49
|
+
readonly volumeId: Effect.Effect<string>;
|
|
50
|
+
/** Write (create or replace) a file. */
|
|
51
|
+
writeFile(
|
|
52
|
+
path: string,
|
|
53
|
+
data: string | Uint8Array,
|
|
54
|
+
options?: { readonly mode?: number },
|
|
55
|
+
): Effect.Effect<void, WriteVolumeFileError, RuntimeContext>;
|
|
56
|
+
/** Remove a file, or a directory with `recursive`. */
|
|
57
|
+
removeFile(
|
|
58
|
+
path: string,
|
|
59
|
+
options?: { readonly recursive?: boolean },
|
|
60
|
+
): Effect.Effect<void, ChangeVolumeFilesError, RuntimeContext>;
|
|
61
|
+
/** Copy files to `destination` inside the volume. */
|
|
62
|
+
copyFiles(
|
|
63
|
+
sources: ReadonlyArray<string>,
|
|
64
|
+
destination: string,
|
|
65
|
+
options?: { readonly recursive?: boolean },
|
|
66
|
+
): Effect.Effect<void, ChangeVolumeFilesError, RuntimeContext>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Read and write access to one volume's files. */
|
|
70
|
+
export interface ReadWriteVolumeClient extends ReadVolumeClient, WriteVolumeClient {}
|
|
71
|
+
|
|
72
|
+
/** The read half of a volume client over `volumeId`. */
|
|
73
|
+
export const makeReadVolumeClient = (
|
|
74
|
+
volumeId: Effect.Effect<string>,
|
|
75
|
+
ops: VolumeFileOps,
|
|
76
|
+
): ReadVolumeClient => ({
|
|
77
|
+
volumeId,
|
|
78
|
+
readFile: (path) => Effect.flatMap(volumeId, (id) => readVolumeFile(ops, id, path)),
|
|
79
|
+
readFileString: (path) =>
|
|
80
|
+
Effect.flatMap(volumeId, (id) => readVolumeFile(ops, id, path)).pipe(
|
|
81
|
+
Effect.map((bytes) => new TextDecoder().decode(bytes)),
|
|
82
|
+
),
|
|
83
|
+
listFiles: (path, options) =>
|
|
84
|
+
Effect.flatMap(volumeId, (id) => listVolumeFiles(ops, id, path, options)),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/** The write half of a volume client over `volumeId`. */
|
|
88
|
+
export const makeWriteVolumeClient = (
|
|
89
|
+
volumeId: Effect.Effect<string>,
|
|
90
|
+
ops: VolumeFileOps,
|
|
91
|
+
): WriteVolumeClient => ({
|
|
92
|
+
volumeId,
|
|
93
|
+
writeFile: (path, data, options) =>
|
|
94
|
+
Effect.flatMap(volumeId, (id) => writeVolumeFile(ops, id, path, data, options)),
|
|
95
|
+
removeFile: (path, options) =>
|
|
96
|
+
Effect.flatMap(volumeId, (id) => removeVolumeFile(ops, id, path, options)),
|
|
97
|
+
copyFiles: (sources, destination, options) =>
|
|
98
|
+
Effect.flatMap(volumeId, (id) => copyVolumeFiles(ops, id, sources, destination, options)),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/** A read-write volume client over `volumeId`. */
|
|
102
|
+
export const makeReadWriteVolumeClient = (
|
|
103
|
+
volumeId: Effect.Effect<string>,
|
|
104
|
+
ops: VolumeFileOps,
|
|
105
|
+
): ReadWriteVolumeClient => ({
|
|
106
|
+
...makeReadVolumeClient(volumeId, ops),
|
|
107
|
+
...makeWriteVolumeClient(volumeId, ops),
|
|
108
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as volume from "@distilled.cloud/modal/volume";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
6
|
+
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
7
|
+
import { operations } from "./internal.ts";
|
|
8
|
+
|
|
9
|
+
/** A block upload or download to a URL Modal handed out failed. */
|
|
10
|
+
export class VolumeTransferError extends Data.TaggedError("Modal.VolumeTransferError")<{
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly message: string;
|
|
13
|
+
}> {}
|
|
14
|
+
|
|
15
|
+
/** One entry of a volume directory listing. */
|
|
16
|
+
export interface VolumeFileEntry {
|
|
17
|
+
/** Path relative to the volume root. */
|
|
18
|
+
readonly path: string;
|
|
19
|
+
readonly type: "file" | "directory" | "symlink" | "other";
|
|
20
|
+
/** Size in bytes (files). */
|
|
21
|
+
readonly size: number;
|
|
22
|
+
/** Modification time, seconds since the epoch. */
|
|
23
|
+
readonly mtime: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BLOCK_SIZE = 8 * 1024 * 1024;
|
|
27
|
+
|
|
28
|
+
/** The operations a volume's files need, resolved by `ModalBindings.withCredentials`. */
|
|
29
|
+
export const VolumeFileOps = operations({
|
|
30
|
+
getFile: volume.volumeGetFile2,
|
|
31
|
+
listFiles: volume.volumeListFiles2,
|
|
32
|
+
putFiles: volume.volumePutFiles2,
|
|
33
|
+
removeFile: volume.volumeRemoveFile2,
|
|
34
|
+
copyFiles: volume.volumeCopyFiles2,
|
|
35
|
+
http: HttpClient.HttpClient,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export type VolumeFileOps = Effect.Success<typeof VolumeFileOps>;
|
|
39
|
+
|
|
40
|
+
const typeOf = (type: string | undefined): VolumeFileEntry["type"] =>
|
|
41
|
+
type === "FILE"
|
|
42
|
+
? "file"
|
|
43
|
+
: type === "DIRECTORY"
|
|
44
|
+
? "directory"
|
|
45
|
+
: type === "SYMLINK"
|
|
46
|
+
? "symlink"
|
|
47
|
+
: "other";
|
|
48
|
+
|
|
49
|
+
const toBytes = (data: string | Uint8Array) =>
|
|
50
|
+
Effect.sync(() => (typeof data === "string" ? new TextEncoder().encode(data) : data));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a file from a (v2) volume: Modal returns presigned URLs for the
|
|
54
|
+
* file's blocks, which are fetched and concatenated.
|
|
55
|
+
*/
|
|
56
|
+
export const readVolumeFile = Effect.fn(function* (
|
|
57
|
+
ops: VolumeFileOps,
|
|
58
|
+
volumeId: string,
|
|
59
|
+
path: string,
|
|
60
|
+
) {
|
|
61
|
+
const { getUrls, size } = yield* ops.getFile({ volumeId, path });
|
|
62
|
+
const total = Number(size ?? 0);
|
|
63
|
+
const out = new Uint8Array(total);
|
|
64
|
+
let offset = 0;
|
|
65
|
+
for (const url of getUrls ?? []) {
|
|
66
|
+
// A presigned block URL returned by VolumeGetFile2 (allowed raw HTTP).
|
|
67
|
+
const response = yield* ops.http.execute(HttpClientRequest.get(url));
|
|
68
|
+
if (response.status >= 300) {
|
|
69
|
+
return yield* new VolumeTransferError({
|
|
70
|
+
path,
|
|
71
|
+
message: `Downloading a block of ${path} failed with HTTP ${response.status}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const buffer = yield* response.arrayBuffer;
|
|
75
|
+
const block = new Uint8Array(buffer);
|
|
76
|
+
out.set(block.subarray(0, Math.max(0, total - offset)), offset);
|
|
77
|
+
offset += Math.min(block.length, BLOCK_SIZE);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/** List a directory of a (v2) volume. */
|
|
83
|
+
export const listVolumeFiles = Effect.fn(function* (
|
|
84
|
+
ops: VolumeFileOps,
|
|
85
|
+
volumeId: string,
|
|
86
|
+
path: string,
|
|
87
|
+
options: { readonly recursive?: boolean } = {},
|
|
88
|
+
) {
|
|
89
|
+
const { items } = yield* ops.listFiles({
|
|
90
|
+
volumeId,
|
|
91
|
+
path,
|
|
92
|
+
recursive: options.recursive ?? false,
|
|
93
|
+
});
|
|
94
|
+
return items.flatMap((batch) =>
|
|
95
|
+
(batch.entries ?? []).map((entry): VolumeFileEntry => ({
|
|
96
|
+
path: entry.path ?? "",
|
|
97
|
+
type: typeOf(entry.type),
|
|
98
|
+
size: Number(entry.size ?? 0),
|
|
99
|
+
mtime: Number(entry.mtime ?? 0),
|
|
100
|
+
})),
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Write a file to a (v2) volume: send each 8 MiB block's SHA-256, upload
|
|
106
|
+
* the blocks Modal reports missing to the URLs it returns, then commit with
|
|
107
|
+
* the upload responses.
|
|
108
|
+
*/
|
|
109
|
+
export const writeVolumeFile = Effect.fn(function* (
|
|
110
|
+
ops: VolumeFileOps,
|
|
111
|
+
volumeId: string,
|
|
112
|
+
path: string,
|
|
113
|
+
data: string | Uint8Array,
|
|
114
|
+
options: { readonly mode?: number } = {},
|
|
115
|
+
) {
|
|
116
|
+
const bytes = yield* toBytes(data);
|
|
117
|
+
const blocks = yield* Effect.sync(() => {
|
|
118
|
+
const out: { readonly data: Uint8Array; readonly sha256: string }[] = [];
|
|
119
|
+
for (let offset = 0; offset === 0 || offset < bytes.length; offset += BLOCK_SIZE) {
|
|
120
|
+
const block = bytes.subarray(offset, offset + BLOCK_SIZE);
|
|
121
|
+
out.push({ data: block, sha256: createHash("sha256").update(block).digest("base64") });
|
|
122
|
+
if (bytes.length === 0) break;
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
});
|
|
126
|
+
const putResponses = new Map<number, string>();
|
|
127
|
+
for (let round = 0; round < 3; round++) {
|
|
128
|
+
const { missingBlocks } = yield* ops.putFiles({
|
|
129
|
+
volumeId,
|
|
130
|
+
files: [
|
|
131
|
+
{
|
|
132
|
+
path,
|
|
133
|
+
size: String(bytes.length),
|
|
134
|
+
mode: options.mode ?? 0o644,
|
|
135
|
+
blocks: blocks.map((block, index) => ({
|
|
136
|
+
contentsSha256: block.sha256,
|
|
137
|
+
putResponse: putResponses.get(index),
|
|
138
|
+
})),
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
if (!missingBlocks || missingBlocks.length === 0) return;
|
|
143
|
+
for (const missing of missingBlocks) {
|
|
144
|
+
const index = Number(missing.blockIndex ?? 0);
|
|
145
|
+
const block = blocks[index];
|
|
146
|
+
if (!block || !missing.putUrl) continue;
|
|
147
|
+
// A presigned block URL returned by VolumePutFiles2 (allowed raw HTTP).
|
|
148
|
+
const response = yield* ops.http.execute(
|
|
149
|
+
HttpClientRequest.put(missing.putUrl).pipe(
|
|
150
|
+
HttpClientRequest.bodyUint8Array(block.data, "application/octet-stream"),
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
if (response.status >= 300) {
|
|
154
|
+
return yield* new VolumeTransferError({
|
|
155
|
+
path,
|
|
156
|
+
message: `Uploading block ${index} of ${path} failed with HTTP ${response.status}`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const body = yield* response.arrayBuffer;
|
|
160
|
+
const encoded = yield* Effect.sync(() => Buffer.from(body).toString("base64"));
|
|
161
|
+
putResponses.set(index, encoded);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return yield* new VolumeTransferError({
|
|
165
|
+
path,
|
|
166
|
+
message: `Modal kept reporting blocks of ${path} missing after upload`,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/** Remove a file (or, with `recursive`, a directory) from a (v2) volume. */
|
|
171
|
+
export const removeVolumeFile = Effect.fn(function* (
|
|
172
|
+
ops: VolumeFileOps,
|
|
173
|
+
volumeId: string,
|
|
174
|
+
path: string,
|
|
175
|
+
options: { readonly recursive?: boolean } = {},
|
|
176
|
+
) {
|
|
177
|
+
yield* ops.removeFile({ volumeId, path, recursive: options.recursive ?? false });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/** Copy files inside a (v2) volume. */
|
|
181
|
+
export const copyVolumeFiles = Effect.fn(function* (
|
|
182
|
+
ops: VolumeFileOps,
|
|
183
|
+
volumeId: string,
|
|
184
|
+
sources: ReadonlyArray<string>,
|
|
185
|
+
destination: string,
|
|
186
|
+
options: { readonly recursive?: boolean } = {},
|
|
187
|
+
) {
|
|
188
|
+
yield* ops.copyFiles({
|
|
189
|
+
volumeId,
|
|
190
|
+
srcPaths: [...sources],
|
|
191
|
+
dstPath: destination,
|
|
192
|
+
recursive: options.recursive ?? false,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import * as environment from "@distilled.cloud/modal/environment";
|
|
2
|
+
import * as webhook from "@distilled.cloud/modal/webhook";
|
|
3
|
+
import { isResolved } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import { Resource } from "alchemy/Resource";
|
|
6
|
+
import * as Data from "effect/Data";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Redacted from "effect/Redacted";
|
|
9
|
+
import { objectName } from "./internal.ts";
|
|
10
|
+
import { declarable } from "./internal/Declared.ts";
|
|
11
|
+
import type { Providers } from "./Providers.ts";
|
|
12
|
+
|
|
13
|
+
export class WebhookTokenUnexpectedResponse extends Data.TaggedError(
|
|
14
|
+
"Modal.WebhookTokenUnexpectedResponse",
|
|
15
|
+
)<{
|
|
16
|
+
readonly message: string;
|
|
17
|
+
}> {}
|
|
18
|
+
|
|
19
|
+
export interface WebhookTokenProps {
|
|
20
|
+
/**
|
|
21
|
+
* Display name of the token. If omitted, one is generated from the stack,
|
|
22
|
+
* stage and logical ID. Changing it renames the token in place.
|
|
23
|
+
*/
|
|
24
|
+
name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Limit the token to the environments in {@link environments}. Changing it
|
|
27
|
+
* replaces the token (and its secret).
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
scoped?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Environments a scoped token works in: environment IDs, or yielded
|
|
33
|
+
* `Modal.Environment`s. Ignored unless {@link scoped} is set.
|
|
34
|
+
*/
|
|
35
|
+
environments?: Array<string | { readonly environmentId: string }>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface WebhookTokenAttributes {
|
|
39
|
+
/** Token ID (`wk-…`), sent as the `Modal-Key` header. */
|
|
40
|
+
tokenId: string;
|
|
41
|
+
/** Token secret (`ws-…`), sent as the `Modal-Secret` header. */
|
|
42
|
+
tokenSecret: Redacted.Redacted<string>;
|
|
43
|
+
/** Display name. */
|
|
44
|
+
name: string;
|
|
45
|
+
/** Whether the token is limited to {@link WebhookTokenProps.environments}. */
|
|
46
|
+
scoped: boolean;
|
|
47
|
+
/** Environment IDs a scoped token works in. */
|
|
48
|
+
environments: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A Modal proxy auth token: the `Modal-Key` / `Modal-Secret` header pair
|
|
53
|
+
* that web endpoints and sandbox tunnels created with proxy authentication
|
|
54
|
+
* require. A scoped token works only in the environments it lists.
|
|
55
|
+
*
|
|
56
|
+
* Modal shows a token's secret once, at creation, so a token deleted outside
|
|
57
|
+
* Alchemy is recreated with a new secret on the next deploy. Tokens have no
|
|
58
|
+
* unique names, so they can't be adopted and aren't listed for
|
|
59
|
+
* `alchemy nuke`.
|
|
60
|
+
*
|
|
61
|
+
* ### Creating a token
|
|
62
|
+
* **Example:** A workspace-wide token
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
65
|
+
*
|
|
66
|
+
* export const Caller = Modal.WebhookToken("Caller", {});
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* **Example:** A token scoped to one environment
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
72
|
+
*
|
|
73
|
+
* export const Agents = Modal.Environment("Agents", {});
|
|
74
|
+
* export const AgentsCaller = Modal.WebhookToken("AgentsCaller", {
|
|
75
|
+
* scoped: true,
|
|
76
|
+
* environments: [Agents],
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* ### Calling a protected endpoint
|
|
81
|
+
* **Example:** Send the header pair
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const token = yield* AgentsCaller;
|
|
84
|
+
* const response = yield* HttpClient.get(url, {
|
|
85
|
+
* headers: {
|
|
86
|
+
* "Modal-Key": token.tokenId,
|
|
87
|
+
* "Modal-Secret": Redacted.value(token.tokenSecret),
|
|
88
|
+
* },
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @resource
|
|
93
|
+
* @product WebhookToken
|
|
94
|
+
*/
|
|
95
|
+
export const WebhookToken = declarable<WebhookToken>()(
|
|
96
|
+
Resource<WebhookToken>("Modal.WebhookToken"),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
export type WebhookToken = Resource<
|
|
100
|
+
"Modal.WebhookToken",
|
|
101
|
+
WebhookTokenProps,
|
|
102
|
+
WebhookTokenAttributes,
|
|
103
|
+
never,
|
|
104
|
+
Providers
|
|
105
|
+
>;
|
|
106
|
+
|
|
107
|
+
export const WebhookTokenProvider = () =>
|
|
108
|
+
Provider.effect(
|
|
109
|
+
WebhookToken,
|
|
110
|
+
Effect.gen(function* () {
|
|
111
|
+
const observe = Effect.fn(function* (tokenId: string) {
|
|
112
|
+
const { tokens } = yield* webhook.listWebhookToken({});
|
|
113
|
+
return (tokens ?? []).find((token) => token.tokenId === tokenId);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
stables: ["tokenId", "tokenSecret", "scoped"],
|
|
118
|
+
diff: Effect.fn(function* ({ news, olds }) {
|
|
119
|
+
if (!isResolved(news)) return undefined;
|
|
120
|
+
if ((news.scoped ?? false) !== (olds?.scoped ?? false)) {
|
|
121
|
+
return { action: "replace" } as const;
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}),
|
|
125
|
+
read: Effect.fn(function* ({ output }) {
|
|
126
|
+
if (output === undefined) return undefined;
|
|
127
|
+
const found = yield* observe(output.tokenId);
|
|
128
|
+
if (!found) return undefined;
|
|
129
|
+
return { ...output, name: found.name ?? output.name, scoped: found.scoped ?? false };
|
|
130
|
+
}),
|
|
131
|
+
reconcile: Effect.fn(function* ({ id, news, output, session }) {
|
|
132
|
+
const name = yield* objectName(id, news.name);
|
|
133
|
+
const scoped = news.scoped ?? false;
|
|
134
|
+
|
|
135
|
+
// Observe: only a token we created (its secret is in state) can be kept.
|
|
136
|
+
const found = output !== undefined ? yield* observe(output.tokenId) : undefined;
|
|
137
|
+
let current: WebhookTokenAttributes;
|
|
138
|
+
if (found && output) {
|
|
139
|
+
current = { ...output, name: found.name ?? output.name };
|
|
140
|
+
} else {
|
|
141
|
+
// A token under our generated name that state doesn't hold is
|
|
142
|
+
// left from a create that failed before state was saved; its
|
|
143
|
+
// secret is lost, so replace it.
|
|
144
|
+
if (news.name === undefined) {
|
|
145
|
+
const { tokens } = yield* webhook.listWebhookToken({});
|
|
146
|
+
for (const stale of (tokens ?? []).filter((t) => t.name === name)) {
|
|
147
|
+
if (!stale.tokenId) continue;
|
|
148
|
+
yield* webhook
|
|
149
|
+
.deleteWebhookToken({ tokenId: stale.tokenId })
|
|
150
|
+
.pipe(Effect.catchTag("WebhookTokenNotFound", () => Effect.void));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Ensure
|
|
154
|
+
yield* session.note(`Creating webhook token ${name}...`);
|
|
155
|
+
const created = yield* webhook.createWebhookToken({ name, scoped });
|
|
156
|
+
if (!created.tokenId || created.tokenSecret === undefined) {
|
|
157
|
+
return yield* new WebhookTokenUnexpectedResponse({
|
|
158
|
+
message: `Modal returned no token for ${name}`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
current = {
|
|
162
|
+
tokenId: created.tokenId,
|
|
163
|
+
tokenSecret: Redacted.isRedacted(created.tokenSecret)
|
|
164
|
+
? created.tokenSecret
|
|
165
|
+
: Redacted.make(created.tokenSecret),
|
|
166
|
+
name,
|
|
167
|
+
scoped,
|
|
168
|
+
environments: [],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Sync the name.
|
|
173
|
+
if (current.name !== name) {
|
|
174
|
+
yield* webhook.updateWebhookToken({ tokenId: current.tokenId, name });
|
|
175
|
+
current = { ...current, name };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Sync environments against the observed set.
|
|
179
|
+
if (scoped) {
|
|
180
|
+
// Modal doesn't serve `WebhookTokenEnvironmentList`, so read the
|
|
181
|
+
// token's environments from each environment's token list.
|
|
182
|
+
const { items } = yield* environment.listEnvironment({});
|
|
183
|
+
const observed = new Set<string>();
|
|
184
|
+
for (const env of items ?? []) {
|
|
185
|
+
if (!env.environmentId || !env.name) continue;
|
|
186
|
+
// An environment deleted since the list holds no tokens.
|
|
187
|
+
const { tokens } = yield* webhook
|
|
188
|
+
.webhookTokenListForEnvironment({ environmentName: env.name })
|
|
189
|
+
.pipe(Effect.catchTag("EnvironmentNotFound", () => Effect.succeed({ tokens: [] })));
|
|
190
|
+
if ((tokens ?? []).some((token) => token.tokenId === current.tokenId)) {
|
|
191
|
+
observed.add(env.environmentId);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const desired = new Set(
|
|
195
|
+
(news.environments ?? []).flatMap((env) =>
|
|
196
|
+
typeof env === "string"
|
|
197
|
+
? [env]
|
|
198
|
+
: typeof env === "object" && env !== null && "environmentId" in env
|
|
199
|
+
? [env.environmentId]
|
|
200
|
+
: [],
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
for (const environmentId of desired) {
|
|
204
|
+
if (!observed.has(environmentId)) {
|
|
205
|
+
yield* webhook.addWebhookTokenEnvironment({
|
|
206
|
+
tokenId: current.tokenId,
|
|
207
|
+
environmentId,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const environmentId of observed) {
|
|
212
|
+
if (!desired.has(environmentId)) {
|
|
213
|
+
yield* webhook.removeWebhookTokenEnvironment({
|
|
214
|
+
tokenId: current.tokenId,
|
|
215
|
+
environmentId,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
current = { ...current, environments: [...desired] };
|
|
220
|
+
}
|
|
221
|
+
return current;
|
|
222
|
+
}),
|
|
223
|
+
delete: Effect.fn(function* ({ output }) {
|
|
224
|
+
yield* webhook
|
|
225
|
+
.deleteWebhookToken({ tokenId: output.tokenId })
|
|
226
|
+
.pipe(Effect.catchTag("WebhookTokenNotFound", () => Effect.void));
|
|
227
|
+
}),
|
|
228
|
+
};
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as workspace from "@distilled.cloud/modal/workspace";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import { envNameOf, type EnvironmentInput } from "./internal.ts";
|
|
6
|
+
import { declarable } from "./internal/Declared.ts";
|
|
7
|
+
import type { Providers } from "./Providers.ts";
|
|
8
|
+
|
|
9
|
+
export interface WorkspaceSettingsProps {
|
|
10
|
+
/**
|
|
11
|
+
* Environment that Modal clients use when none is named (the `modal` CLI,
|
|
12
|
+
* `environmentName: ""` on the API).
|
|
13
|
+
*/
|
|
14
|
+
defaultEnvironment?: EnvironmentInput;
|
|
15
|
+
/**
|
|
16
|
+
* Image builder version for new image builds in the workspace (e.g.
|
|
17
|
+
* `"2025.06"`). Changing it rebuilds images on their next use.
|
|
18
|
+
*/
|
|
19
|
+
imageBuilderVersion?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Require authentication on web endpoints and tunnels in environments
|
|
22
|
+
* that inherit the workspace default.
|
|
23
|
+
*/
|
|
24
|
+
blockUnauthenticatedResources?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WorkspaceSettingsAttributes {
|
|
28
|
+
/** The workspace's default environment. */
|
|
29
|
+
defaultEnvironment: string | undefined;
|
|
30
|
+
/** The workspace's image builder version. */
|
|
31
|
+
imageBuilderVersion: string | undefined;
|
|
32
|
+
/** Settings found before the first deploy, restored on destroy. */
|
|
33
|
+
original: {
|
|
34
|
+
defaultEnvironment: string | undefined;
|
|
35
|
+
imageBuilderVersion: string | undefined;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Workspace-wide Modal settings: the default environment, the image builder
|
|
41
|
+
* version, and whether endpoints in inheriting environments require
|
|
42
|
+
* authentication.
|
|
43
|
+
*
|
|
44
|
+
* A workspace has one set of settings, so declare this resource once. It
|
|
45
|
+
* captures the settings it finds on first deploy and restores them when
|
|
46
|
+
* destroyed.
|
|
47
|
+
*
|
|
48
|
+
* ### Configuring the workspace
|
|
49
|
+
* **Example:** Pin the image builder and default environment
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
52
|
+
*
|
|
53
|
+
* export const Agents = Modal.Environment("Agents", {});
|
|
54
|
+
* export const Settings = Modal.WorkspaceSettings("Settings", {
|
|
55
|
+
* defaultEnvironment: Agents,
|
|
56
|
+
* imageBuilderVersion: "2025.06",
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @resource
|
|
61
|
+
* @product Workspace
|
|
62
|
+
*/
|
|
63
|
+
export const WorkspaceSettings = declarable<WorkspaceSettings>()(
|
|
64
|
+
Resource<WorkspaceSettings>("Modal.WorkspaceSettings"),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
export type WorkspaceSettings = Resource<
|
|
68
|
+
"Modal.WorkspaceSettings",
|
|
69
|
+
WorkspaceSettingsProps,
|
|
70
|
+
WorkspaceSettingsAttributes,
|
|
71
|
+
never,
|
|
72
|
+
Providers
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
export const WorkspaceSettingsProvider = () =>
|
|
76
|
+
Provider.effect(
|
|
77
|
+
WorkspaceSettings,
|
|
78
|
+
Effect.gen(function* () {
|
|
79
|
+
const observe = workspace.workspaceSettings({});
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
read: Effect.fn(function* ({ output }) {
|
|
83
|
+
if (output === undefined) return undefined;
|
|
84
|
+
const current = yield* observe;
|
|
85
|
+
return {
|
|
86
|
+
...output,
|
|
87
|
+
defaultEnvironment: current.defaultEnvironmentName || undefined,
|
|
88
|
+
imageBuilderVersion: current.imageBuilderVersion || undefined,
|
|
89
|
+
};
|
|
90
|
+
}),
|
|
91
|
+
reconcile: Effect.fn(function* ({ news, output }) {
|
|
92
|
+
// Observe
|
|
93
|
+
const current = yield* observe;
|
|
94
|
+
const original = output?.original ?? {
|
|
95
|
+
defaultEnvironment: current.defaultEnvironmentName || undefined,
|
|
96
|
+
imageBuilderVersion: current.imageBuilderVersion || undefined,
|
|
97
|
+
};
|
|
98
|
+
// Sync each setting against the observed value.
|
|
99
|
+
const defaultEnvironment = envNameOf(news.defaultEnvironment);
|
|
100
|
+
if (
|
|
101
|
+
defaultEnvironment !== undefined &&
|
|
102
|
+
defaultEnvironment !== current.defaultEnvironmentName
|
|
103
|
+
) {
|
|
104
|
+
yield* workspace.workspaceSetDefaultEnvironment({
|
|
105
|
+
environmentName: defaultEnvironment,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (
|
|
109
|
+
news.imageBuilderVersion !== undefined &&
|
|
110
|
+
news.imageBuilderVersion !== current.imageBuilderVersion
|
|
111
|
+
) {
|
|
112
|
+
yield* workspace.workspaceSetImageBuilderVersion({
|
|
113
|
+
newImageBuilderVersion: news.imageBuilderVersion,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (news.blockUnauthenticatedResources !== undefined) {
|
|
117
|
+
yield* workspace.workspaceSetDefaultEnvironmentSettings({
|
|
118
|
+
blockUnauthenticatedResources: news.blockUnauthenticatedResources
|
|
119
|
+
? "ENVIRONMENT_BLOCK_UNAUTHENTICATED_RESOURCES_BLOCK"
|
|
120
|
+
: "ENVIRONMENT_BLOCK_UNAUTHENTICATED_RESOURCES_ALLOW",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const final = yield* observe;
|
|
124
|
+
return {
|
|
125
|
+
defaultEnvironment: final.defaultEnvironmentName || undefined,
|
|
126
|
+
imageBuilderVersion: final.imageBuilderVersion || undefined,
|
|
127
|
+
original,
|
|
128
|
+
};
|
|
129
|
+
}),
|
|
130
|
+
delete: Effect.fn(function* ({ output, olds }) {
|
|
131
|
+
// Restore what was there before Alchemy managed the settings.
|
|
132
|
+
const current = yield* observe;
|
|
133
|
+
if (
|
|
134
|
+
olds.defaultEnvironment !== undefined &&
|
|
135
|
+
output.original.defaultEnvironment !== undefined &&
|
|
136
|
+
current.defaultEnvironmentName !== output.original.defaultEnvironment
|
|
137
|
+
) {
|
|
138
|
+
yield* workspace.workspaceSetDefaultEnvironment({
|
|
139
|
+
environmentName: output.original.defaultEnvironment,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
olds.imageBuilderVersion !== undefined &&
|
|
144
|
+
output.original.imageBuilderVersion !== undefined &&
|
|
145
|
+
current.imageBuilderVersion !== output.original.imageBuilderVersion
|
|
146
|
+
) {
|
|
147
|
+
yield* workspace.workspaceSetImageBuilderVersion({
|
|
148
|
+
newImageBuilderVersion: output.original.imageBuilderVersion,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (olds.blockUnauthenticatedResources !== undefined) {
|
|
152
|
+
yield* workspace.workspaceSetDefaultEnvironmentSettings({
|
|
153
|
+
blockUnauthenticatedResources:
|
|
154
|
+
"ENVIRONMENT_BLOCK_UNAUTHENTICATED_RESOURCES_UNSPECIFIED",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
}),
|
|
160
|
+
);
|