alchemy 2.0.0-beta.46 → 2.0.0-beta.48
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/bin/exec.js +18 -10
- package/bin/exec.js.map +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
- package/lib/Apply.js +2 -2
- package/lib/Apply.js.map +1 -1
- package/lib/Cli/commands/_shared.d.ts +7 -1
- package/lib/Cli/commands/_shared.d.ts.map +1 -1
- package/lib/Cli/commands/_shared.js +1 -0
- package/lib/Cli/commands/_shared.js.map +1 -1
- package/lib/Cli/commands/cloudflare.d.ts +4 -1
- package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
- package/lib/Cli/commands/cloudflare.js +274 -1
- package/lib/Cli/commands/cloudflare.js.map +1 -1
- package/lib/Cli/commands/login.d.ts +1 -1
- package/lib/Cli/commands/login.d.ts.map +1 -1
- package/lib/Cli/commands/login.js +45 -42
- package/lib/Cli/commands/login.js.map +1 -1
- package/lib/Cli/commands/logs.js +1 -1
- package/lib/Cli/commands/logs.js.map +1 -1
- package/lib/Cli/commands/state.js +1 -1
- package/lib/Cli/commands/state.js.map +1 -1
- package/lib/Cli/commands/tail.js +1 -1
- package/lib/Cli/commands/tail.js.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts +112 -1
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.js +114 -2
- package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts +43 -10
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.js +38 -10
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.js.map +1 -1
- package/lib/Cloudflare/AiGateway/LanguageModel.d.ts +35 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.d.ts.map +1 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.js +576 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.js.map +1 -0
- package/lib/Cloudflare/AiGateway/index.d.ts +1 -0
- package/lib/Cloudflare/AiGateway/index.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/index.js +1 -0
- package/lib/Cloudflare/AiGateway/index.js.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +40 -2
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.js +49 -6
- package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
- package/lib/Cloudflare/ApiToken/Common.d.ts +26 -3
- package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/Common.js +8 -0
- package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +38 -2
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.js +47 -6
- package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
- package/lib/Cloudflare/Browser/Browser.d.ts +121 -0
- package/lib/Cloudflare/Browser/Browser.d.ts.map +1 -0
- package/lib/Cloudflare/{BrowserRendering/BrowserRendering.js → Browser/Browser.js} +19 -26
- package/lib/Cloudflare/Browser/Browser.js.map +1 -0
- package/lib/Cloudflare/Browser/BrowserBinding.d.ts +55 -0
- package/lib/Cloudflare/Browser/BrowserBinding.d.ts.map +1 -0
- package/lib/Cloudflare/{BrowserRendering/BrowserRenderingBinding.js → Browser/BrowserBinding.js} +11 -11
- package/lib/Cloudflare/Browser/BrowserBinding.js.map +1 -0
- package/lib/Cloudflare/Browser/index.d.ts +3 -0
- package/lib/Cloudflare/Browser/index.d.ts.map +1 -0
- package/lib/Cloudflare/Browser/index.js +3 -0
- package/lib/Cloudflare/Browser/index.js.map +1 -0
- package/lib/Cloudflare/Container/ContainerBinding.d.ts +2 -1
- package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Database.js +2 -1
- package/lib/Cloudflare/D1/D1Database.js.map +1 -1
- package/lib/Cloudflare/Email/EmailRouting.d.ts +1 -1
- package/lib/Cloudflare/Email/EmailRouting.d.ts.map +1 -1
- package/lib/Cloudflare/Email/EmailRouting.js +1 -1
- package/lib/Cloudflare/Email/EmailRouting.js.map +1 -1
- package/lib/Cloudflare/Email/EmailRule.d.ts +1 -1
- package/lib/Cloudflare/Email/EmailRule.d.ts.map +1 -1
- package/lib/Cloudflare/Email/EmailRule.js +1 -1
- package/lib/Cloudflare/Email/EmailRule.js.map +1 -1
- package/lib/Cloudflare/Images/Images.d.ts +32 -23
- package/lib/Cloudflare/Images/Images.d.ts.map +1 -1
- package/lib/Cloudflare/Images/Images.js +10 -13
- package/lib/Cloudflare/Images/Images.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +10 -3
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.d.ts +1 -1
- package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.js +13 -7
- package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
- package/lib/Cloudflare/RateLimit/RateLimit.d.ts +132 -0
- package/lib/Cloudflare/RateLimit/RateLimit.d.ts.map +1 -0
- package/lib/Cloudflare/RateLimit/RateLimit.js +80 -0
- package/lib/Cloudflare/RateLimit/RateLimit.js.map +1 -0
- package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts +30 -0
- package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts.map +1 -0
- package/lib/Cloudflare/RateLimit/RateLimitBinding.js +50 -0
- package/lib/Cloudflare/RateLimit/RateLimitBinding.js.map +1 -0
- package/lib/Cloudflare/RateLimit/index.d.ts +3 -0
- package/lib/Cloudflare/RateLimit/index.d.ts.map +1 -0
- package/lib/Cloudflare/RateLimit/index.js +3 -0
- package/lib/Cloudflare/RateLimit/index.js.map +1 -0
- package/lib/Cloudflare/SecretsStore/Secret.d.ts +2 -1
- package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
- package/lib/Cloudflare/SecretsStore/Secret.js +7 -4
- package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
- package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/State.js +82 -78
- package/lib/Cloudflare/StateStore/State.js.map +1 -1
- package/lib/Cloudflare/Tunnel/Tunnel.d.ts +27 -0
- package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
- package/lib/Cloudflare/Tunnel/Tunnel.js +27 -0
- package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
- package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +48 -0
- package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelBinding.js +56 -0
- package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelRead.d.ts +83 -0
- package/lib/Cloudflare/Tunnel/TunnelRead.d.ts.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelRead.js +79 -0
- package/lib/Cloudflare/Tunnel/TunnelRead.js.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts +70 -0
- package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelReadWrite.js +63 -0
- package/lib/Cloudflare/Tunnel/TunnelReadWrite.js.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts +92 -0
- package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts.map +1 -0
- package/lib/Cloudflare/Tunnel/TunnelWrite.js +88 -0
- package/lib/Cloudflare/Tunnel/TunnelWrite.js.map +1 -0
- package/lib/Cloudflare/Tunnel/index.d.ts +4 -0
- package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
- package/lib/Cloudflare/Tunnel/index.js +4 -0
- package/lib/Cloudflare/Tunnel/index.js.map +1 -1
- package/lib/Cloudflare/Vectorize/VectorizeIndex.js +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -1
- package/lib/Cloudflare/VpcService/VpcService.d.ts +1 -1
- package/lib/Cloudflare/VpcService/VpcService.d.ts.map +1 -1
- package/lib/Cloudflare/VpcService/VpcService.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +10 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts +119 -11
- package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DynamicWorkerLoader.js +88 -9
- package/lib/Cloudflare/Workers/DynamicWorkerLoader.js.map +1 -1
- package/lib/Cloudflare/Workers/InferEnv.d.ts +1 -1
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +15 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +20 -2
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +18 -2
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +5 -3
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.js +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.js +20 -2
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
- package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +1 -1
- package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -1
- package/lib/Cloudflare/Zaraz/ZarazConfig.js +1 -1
- package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -1
- package/lib/Cloudflare/Zone/Zone.d.ts +170 -0
- package/lib/Cloudflare/Zone/Zone.d.ts.map +1 -0
- package/lib/Cloudflare/Zone/Zone.js +180 -0
- package/lib/Cloudflare/Zone/Zone.js.map +1 -0
- package/lib/Cloudflare/Zone/index.d.ts +3 -0
- package/lib/Cloudflare/Zone/index.d.ts.map +1 -0
- package/lib/Cloudflare/Zone/index.js +3 -0
- package/lib/Cloudflare/Zone/index.js.map +1 -0
- package/lib/Cloudflare/Zone/lookup.d.ts +33 -0
- package/lib/Cloudflare/Zone/lookup.d.ts.map +1 -0
- package/lib/Cloudflare/{Zone.js → Zone/lookup.js} +2 -2
- package/lib/Cloudflare/Zone/lookup.js.map +1 -0
- package/lib/Cloudflare/index.d.ts +3 -1
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +3 -1
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Output.js +2 -2
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts +2 -2
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +3 -3
- package/lib/Plan.js.map +1 -1
- package/lib/Platform.d.ts +5 -5
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js.map +1 -1
- package/lib/RemovalPolicy.js +2 -2
- package/lib/RemovalPolicy.js.map +1 -1
- package/lib/Resource.d.ts +14 -1
- package/lib/Resource.d.ts.map +1 -1
- package/lib/Resource.js +3 -2
- package/lib/Resource.js.map +1 -1
- package/lib/Stack.d.ts.map +1 -1
- package/lib/Stack.js +6 -0
- package/lib/Stack.js.map +1 -1
- package/lib/State/InMemoryState.d.ts +2 -1
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +3 -3
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +5 -1
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +2 -2
- package/lib/State/State.d.ts.map +1 -1
- package/lib/State/State.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/Apply.ts +2 -2
- package/src/Cli/commands/_shared.ts +7 -1
- package/src/Cli/commands/cloudflare.ts +394 -1
- package/src/Cli/commands/login.ts +69 -60
- package/src/Cli/commands/logs.ts +1 -1
- package/src/Cli/commands/state.ts +1 -1
- package/src/Cli/commands/tail.ts +1 -1
- package/src/Cloudflare/AiGateway/AiGateway.ts +114 -2
- package/src/Cloudflare/AiGateway/AiGatewayBinding.ts +52 -10
- package/src/Cloudflare/AiGateway/LanguageModel.ts +907 -0
- package/src/Cloudflare/AiGateway/index.ts +1 -0
- package/src/Cloudflare/ApiToken/AccountApiToken.ts +60 -7
- package/src/Cloudflare/ApiToken/Common.ts +32 -3
- package/src/Cloudflare/ApiToken/UserApiToken.ts +58 -7
- package/src/Cloudflare/Browser/Browser.ts +140 -0
- package/src/Cloudflare/Browser/BrowserBinding.ts +128 -0
- package/src/Cloudflare/Browser/index.ts +11 -0
- package/src/Cloudflare/D1/D1Database.ts +6 -2
- package/src/Cloudflare/Email/EmailRouting.ts +1 -1
- package/src/Cloudflare/Email/EmailRule.ts +6 -6
- package/src/Cloudflare/Images/Images.ts +40 -27
- package/src/Cloudflare/Providers.ts +15 -3
- package/src/Cloudflare/R2/R2Bucket.ts +17 -9
- package/src/Cloudflare/RateLimit/RateLimit.ts +160 -0
- package/src/Cloudflare/RateLimit/RateLimitBinding.ts +90 -0
- package/src/Cloudflare/RateLimit/index.ts +2 -0
- package/src/Cloudflare/SecretsStore/Secret.ts +12 -6
- package/src/Cloudflare/StateStore/State.ts +113 -107
- package/src/Cloudflare/Tunnel/Tunnel.ts +27 -0
- package/src/Cloudflare/Tunnel/TunnelBinding.ts +114 -0
- package/src/Cloudflare/Tunnel/TunnelRead.ts +168 -0
- package/src/Cloudflare/Tunnel/TunnelReadWrite.ts +88 -0
- package/src/Cloudflare/Tunnel/TunnelWrite.ts +194 -0
- package/src/Cloudflare/Tunnel/index.ts +4 -0
- package/src/Cloudflare/Vectorize/VectorizeIndex.ts +2 -1
- package/src/Cloudflare/VpcService/VpcService.ts +4 -2
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +10 -1
- package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +139 -13
- package/src/Cloudflare/Workers/InferEnv.ts +19 -15
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +26 -3
- package/src/Cloudflare/Workers/Rpc.ts +38 -9
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +16 -2
- package/src/Cloudflare/Workers/WorkerBinding.ts +6 -2
- package/src/Cloudflare/Workers/WorkerBridge.ts +20 -2
- package/src/Cloudflare/Zaraz/ZarazConfig.ts +1 -1
- package/src/Cloudflare/Zone/Zone.ts +349 -0
- package/src/Cloudflare/Zone/index.ts +2 -0
- package/src/Cloudflare/{Zone.ts → Zone/lookup.ts} +16 -3
- package/src/Cloudflare/index.ts +3 -1
- package/src/Output.ts +2 -2
- package/src/Plan.ts +4 -4
- package/src/Platform.ts +8 -12
- package/src/RemovalPolicy.ts +2 -2
- package/src/Resource.ts +18 -1
- package/src/Stack.ts +6 -0
- package/src/State/InMemoryState.ts +87 -85
- package/src/State/LocalState.ts +15 -1
- package/src/State/State.ts +5 -4
- package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts +0 -116
- package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts.map +0 -1
- package/lib/Cloudflare/BrowserRendering/BrowserRendering.js.map +0 -1
- package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts +0 -55
- package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts.map +0 -1
- package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js.map +0 -1
- package/lib/Cloudflare/BrowserRendering/index.d.ts +0 -3
- package/lib/Cloudflare/BrowserRendering/index.d.ts.map +0 -1
- package/lib/Cloudflare/BrowserRendering/index.js +0 -3
- package/lib/Cloudflare/BrowserRendering/index.js.map +0 -1
- package/lib/Cloudflare/Zone.d.ts +0 -14
- package/lib/Cloudflare/Zone.d.ts.map +0 -1
- package/lib/Cloudflare/Zone.js.map +0 -1
- package/src/Cloudflare/BrowserRendering/BrowserRendering.ts +0 -132
- package/src/Cloudflare/BrowserRendering/BrowserRenderingBinding.ts +0 -137
- package/src/Cloudflare/BrowserRendering/index.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.48",
|
|
4
4
|
"homepage": "https://alchemy.run",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
@@ -291,15 +291,15 @@
|
|
|
291
291
|
"@alchemy.run/node-utils": "0.0.4",
|
|
292
292
|
"@aws-sdk/credential-providers": "^3.0.0",
|
|
293
293
|
"@clack/prompts": "^0.11.0",
|
|
294
|
-
"@distilled.cloud/aws": "^0.22.
|
|
295
|
-
"@distilled.cloud/axiom": "^0.22.
|
|
296
|
-
"@distilled.cloud/cloudflare": "^0.22.
|
|
294
|
+
"@distilled.cloud/aws": "^0.22.3",
|
|
295
|
+
"@distilled.cloud/axiom": "^0.22.3",
|
|
296
|
+
"@distilled.cloud/cloudflare": "^0.22.3",
|
|
297
297
|
"@distilled.cloud/cloudflare-rolldown-plugin": "0.10.1",
|
|
298
298
|
"@distilled.cloud/cloudflare-runtime": "0.10.1",
|
|
299
299
|
"@distilled.cloud/cloudflare-vite-plugin": "0.10.1",
|
|
300
|
-
"@distilled.cloud/core": "^0.22.
|
|
301
|
-
"@distilled.cloud/neon": "^0.22.
|
|
302
|
-
"@distilled.cloud/planetscale": "^0.22.
|
|
300
|
+
"@distilled.cloud/core": "^0.22.3",
|
|
301
|
+
"@distilled.cloud/neon": "^0.22.3",
|
|
302
|
+
"@distilled.cloud/planetscale": "^0.22.3",
|
|
303
303
|
"@effect/vitest": ">=4.0.0-beta.74 || >=4.0.0",
|
|
304
304
|
"@libsql/client": "^0.17.0",
|
|
305
305
|
"@octokit/rest": "^22.0.1",
|
package/src/Apply.ts
CHANGED
|
@@ -138,7 +138,7 @@ export const apply = <P extends Plan>(
|
|
|
138
138
|
Effect.gen(function* () {
|
|
139
139
|
const cli = yield* Cli;
|
|
140
140
|
const session = yield* cli.startApplySession(plan);
|
|
141
|
-
const state = yield* State;
|
|
141
|
+
const state = yield* yield* State;
|
|
142
142
|
const stack = yield* Stack;
|
|
143
143
|
const stage = yield* Stage;
|
|
144
144
|
const stackName = stack.name;
|
|
@@ -1397,7 +1397,7 @@ const collectGarbage = Effect.fnUntraced(function* (
|
|
|
1397
1397
|
plan: Plan,
|
|
1398
1398
|
session: PlanStatusSession,
|
|
1399
1399
|
) {
|
|
1400
|
-
const state = yield* State;
|
|
1400
|
+
const state = yield* yield* State;
|
|
1401
1401
|
const stack = yield* Stack;
|
|
1402
1402
|
const stackName = stack.name;
|
|
1403
1403
|
const stage = yield* Stage;
|
|
@@ -2,6 +2,7 @@ import * as Cause from "effect/Cause";
|
|
|
2
2
|
import * as Config from "effect/Config";
|
|
3
3
|
import * as Console from "effect/Console";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
5
6
|
import * as Option from "effect/Option";
|
|
6
7
|
import * as Path from "effect/Path";
|
|
7
8
|
import * as S from "effect/Schema";
|
|
@@ -312,5 +313,10 @@ export const importStack = Effect.fn(function* (main: string) {
|
|
|
312
313
|
),
|
|
313
314
|
);
|
|
314
315
|
}
|
|
315
|
-
return stackEffect
|
|
316
|
+
return stackEffect as typeof stackEffect & {
|
|
317
|
+
stackName: string;
|
|
318
|
+
stage: string;
|
|
319
|
+
providers: Layer.Layer<never>;
|
|
320
|
+
state: Layer.Layer<never>;
|
|
321
|
+
};
|
|
316
322
|
});
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import * as cfAccounts from "@distilled.cloud/cloudflare/accounts";
|
|
2
|
+
import { apiKeyCredentials } from "@distilled.cloud/cloudflare/Credentials";
|
|
3
|
+
import * as user from "@distilled.cloud/cloudflare/user";
|
|
4
|
+
import * as Config from "effect/Config";
|
|
1
5
|
import * as ConfigProvider from "effect/ConfigProvider";
|
|
2
6
|
import * as Console from "effect/Console";
|
|
3
7
|
import * as Effect from "effect/Effect";
|
|
@@ -6,6 +10,7 @@ import * as Logger from "effect/Logger";
|
|
|
6
10
|
import * as Option from "effect/Option";
|
|
7
11
|
import * as Stream from "effect/Stream";
|
|
8
12
|
import { Command, Flag } from "effect/unstable/cli";
|
|
13
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
9
14
|
|
|
10
15
|
import { AuthProviders } from "../../Auth/AuthProvider.ts";
|
|
11
16
|
import { withProfileOverride } from "../../Auth/Profile.ts";
|
|
@@ -16,6 +21,7 @@ import * as CloudflareCredentials from "../../Cloudflare/Credentials.ts";
|
|
|
16
21
|
import { CloudflareLogs } from "../../Cloudflare/Logs.ts";
|
|
17
22
|
import { STATE_STORE_SCRIPT_NAME } from "../../Cloudflare/StateStore/Api.ts";
|
|
18
23
|
import { bootstrap as bootstrapCloudflare } from "../../Cloudflare/StateStore/State.ts";
|
|
24
|
+
import * as Clank from "../../Util/Clank.ts";
|
|
19
25
|
import { loadConfigProvider } from "../../Util/ConfigProvider.ts";
|
|
20
26
|
import { fileLogger } from "../../Util/FileLogger.ts";
|
|
21
27
|
|
|
@@ -110,6 +116,393 @@ const bootstrapCommand = Command.make(
|
|
|
110
116
|
),
|
|
111
117
|
);
|
|
112
118
|
|
|
119
|
+
/**
|
|
120
|
+
* A single resolved Cloudflare token policy in the shape expected by
|
|
121
|
+
* `POST /user/tokens`.
|
|
122
|
+
*/
|
|
123
|
+
type CreateTokenPolicy = {
|
|
124
|
+
effect: "allow";
|
|
125
|
+
permissionGroups: { id: string }[];
|
|
126
|
+
resources: Record<string, unknown>;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Curated set of permission groups granted by `create-token` when
|
|
131
|
+
* `--all-permissions` is NOT passed. Covers the services Alchemy commonly
|
|
132
|
+
* deploys (Workers + storage + zone/DNS) without handing out a god token.
|
|
133
|
+
* Names are matched against the account's live permission groups; any name
|
|
134
|
+
* not available for the account is silently skipped.
|
|
135
|
+
*/
|
|
136
|
+
const ALCHEMY_DEFAULT_PERMISSION_GROUP_NAMES: ReadonlySet<string> = new Set([
|
|
137
|
+
"Account Settings Read",
|
|
138
|
+
"Workers Scripts Read",
|
|
139
|
+
"Workers Scripts Write",
|
|
140
|
+
"Workers KV Storage Read",
|
|
141
|
+
"Workers KV Storage Write",
|
|
142
|
+
"Workers R2 Storage Read",
|
|
143
|
+
"Workers R2 Storage Write",
|
|
144
|
+
"Workers Routes Read",
|
|
145
|
+
"Workers Routes Write",
|
|
146
|
+
"Workers Tail Read",
|
|
147
|
+
"Workers Observability Read",
|
|
148
|
+
"Workers Observability Write",
|
|
149
|
+
"Workers AI Read",
|
|
150
|
+
"Workers AI Write",
|
|
151
|
+
"D1 Read",
|
|
152
|
+
"D1 Write",
|
|
153
|
+
"Queues Read",
|
|
154
|
+
"Queues Write",
|
|
155
|
+
"Hyperdrive Read",
|
|
156
|
+
"Hyperdrive Write",
|
|
157
|
+
"Pages Read",
|
|
158
|
+
"Pages Write",
|
|
159
|
+
"Vectorize Read",
|
|
160
|
+
"Vectorize Write",
|
|
161
|
+
"Pipelines Read",
|
|
162
|
+
"Pipelines Write",
|
|
163
|
+
"AI Gateway Read",
|
|
164
|
+
"AI Gateway Run",
|
|
165
|
+
"AI Gateway Write",
|
|
166
|
+
"Browser Rendering Read",
|
|
167
|
+
"Browser Rendering Write",
|
|
168
|
+
"Images Read",
|
|
169
|
+
"Images Write",
|
|
170
|
+
"Stream Read",
|
|
171
|
+
"Stream Write",
|
|
172
|
+
"Secrets Store Read",
|
|
173
|
+
"Secrets Store Write",
|
|
174
|
+
"DNS Read",
|
|
175
|
+
"DNS Write",
|
|
176
|
+
"Zone Read",
|
|
177
|
+
"Zone Settings Read",
|
|
178
|
+
"Zone Settings Write",
|
|
179
|
+
"SSL and Certificates Read",
|
|
180
|
+
"SSL and Certificates Write",
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Group permission groups by their Cloudflare scope and produce one policy
|
|
185
|
+
* per scope, wiring up the right resource selector for each:
|
|
186
|
+
*
|
|
187
|
+
* - `com.cloudflare.api.account` → scoped to the account ID
|
|
188
|
+
* - `com.cloudflare.api.account.zone` → all zones (`*`)
|
|
189
|
+
* - `com.cloudflare.edge.r2.bucket` → all buckets (`*`)
|
|
190
|
+
*
|
|
191
|
+
* Mirrors the upstream `alchemy` "god token" policy shape. Groups with an
|
|
192
|
+
* unrecognized scope are skipped, and empty policies are dropped.
|
|
193
|
+
*/
|
|
194
|
+
const buildTokenPolicies = (
|
|
195
|
+
accountId: string,
|
|
196
|
+
groups: readonly { id: string; scopes: readonly string[] }[],
|
|
197
|
+
): CreateTokenPolicy[] => {
|
|
198
|
+
const buckets: Record<string, CreateTokenPolicy> = {
|
|
199
|
+
"com.cloudflare.api.account": {
|
|
200
|
+
effect: "allow",
|
|
201
|
+
permissionGroups: [],
|
|
202
|
+
resources: { [`com.cloudflare.api.account.${accountId}`]: "*" },
|
|
203
|
+
},
|
|
204
|
+
"com.cloudflare.api.account.zone": {
|
|
205
|
+
effect: "allow",
|
|
206
|
+
permissionGroups: [],
|
|
207
|
+
resources: { "com.cloudflare.api.account.zone.*": "*" },
|
|
208
|
+
},
|
|
209
|
+
"com.cloudflare.edge.r2.bucket": {
|
|
210
|
+
effect: "allow",
|
|
211
|
+
permissionGroups: [],
|
|
212
|
+
resources: { "com.cloudflare.edge.r2.bucket.*": "*" },
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
const seen = new Set<string>();
|
|
216
|
+
for (const group of groups) {
|
|
217
|
+
const bucket = buckets[group.scopes[0]!];
|
|
218
|
+
if (!bucket || seen.has(group.id)) continue;
|
|
219
|
+
seen.add(group.id);
|
|
220
|
+
bucket.permissionGroups.push({ id: group.id });
|
|
221
|
+
}
|
|
222
|
+
return Object.values(buckets).filter((p) => p.permissionGroups.length > 0);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Let the user pick which Cloudflare account the token is scoped to. Lists
|
|
227
|
+
* the accounts visible to the configured credentials and prompts a selection
|
|
228
|
+
* (defaulting the cursor to the profile's account). If there's exactly one
|
|
229
|
+
* account, it's used without prompting; if the API returns none, falls back
|
|
230
|
+
* to the profile's account.
|
|
231
|
+
*/
|
|
232
|
+
const selectAccountId = (defaultAccountId: string | undefined) =>
|
|
233
|
+
Effect.gen(function* () {
|
|
234
|
+
const list = yield* cfAccounts.listAccounts;
|
|
235
|
+
const response = yield* list({});
|
|
236
|
+
const accounts = response.result;
|
|
237
|
+
|
|
238
|
+
if (accounts.length === 0) {
|
|
239
|
+
if (defaultAccountId) return defaultAccountId;
|
|
240
|
+
return yield* Effect.die(
|
|
241
|
+
"No Cloudflare accounts found for these credentials.",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (accounts.length === 1) {
|
|
245
|
+
const account = accounts[0]!;
|
|
246
|
+
yield* Clank.info(`Using account: ${account.name} (${account.id})`);
|
|
247
|
+
return account.id;
|
|
248
|
+
}
|
|
249
|
+
return yield* Clank.select({
|
|
250
|
+
message: "Select a Cloudflare account",
|
|
251
|
+
initialValue: defaultAccountId,
|
|
252
|
+
options: accounts.map((a) => ({
|
|
253
|
+
value: a.id,
|
|
254
|
+
label: a.name,
|
|
255
|
+
hint: a.id === defaultAccountId ? `${a.id} (current profile)` : a.id,
|
|
256
|
+
})),
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const allPermissionsFlag = Flag.boolean("all-permissions").pipe(
|
|
261
|
+
Flag.withDescription(
|
|
262
|
+
"Grant the token EVERY Cloudflare permission group (a 'god token'). " +
|
|
263
|
+
"Use with care — it has full access to your account.",
|
|
264
|
+
),
|
|
265
|
+
Flag.withDefault(false),
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const tokenNameFlag = Flag.string("name").pipe(
|
|
269
|
+
Flag.withDescription(
|
|
270
|
+
"Name for the API token. Defaults to 'alchemy' (or 'alchemy-all-permissions').",
|
|
271
|
+
),
|
|
272
|
+
Flag.optional,
|
|
273
|
+
Flag.map(Option.getOrUndefined),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const tokenAccountIdFlag = Flag.string("account-id").pipe(
|
|
277
|
+
Flag.withDescription(
|
|
278
|
+
"Cloudflare account ID to scope the token to. Defaults to the profile's account.",
|
|
279
|
+
),
|
|
280
|
+
Flag.optional,
|
|
281
|
+
Flag.map(Option.getOrUndefined),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* `alchemy cloudflare create-token` — mint a Cloudflare API token
|
|
286
|
+
* (`POST /user/tokens`).
|
|
287
|
+
*
|
|
288
|
+
* This command is **standalone**: it does not use an Alchemy auth profile.
|
|
289
|
+
* Cloudflare only mints a token whose permissions the authenticating
|
|
290
|
+
* credential is allowed to grant — and OAuth/scoped tokens silently produce a
|
|
291
|
+
* token with zero permissions — so it always authenticates with the account's
|
|
292
|
+
* **Global API Key** (read from `CLOUDFLARE_API_KEY` / `CLOUDFLARE_EMAIL`,
|
|
293
|
+
* otherwise prompted). The key is used only to create the token and is never
|
|
294
|
+
* stored.
|
|
295
|
+
*
|
|
296
|
+
* With `--all-permissions` it builds a "superuser" token spanning every
|
|
297
|
+
* permission group (after a confirmation prompt). Otherwise it grants a
|
|
298
|
+
* curated set covering the services Alchemy commonly deploys.
|
|
299
|
+
*/
|
|
300
|
+
const createTokenCommand = Command.make(
|
|
301
|
+
"create-token",
|
|
302
|
+
{
|
|
303
|
+
envFile,
|
|
304
|
+
allPermissions: allPermissionsFlag,
|
|
305
|
+
name: tokenNameFlag,
|
|
306
|
+
accountId: tokenAccountIdFlag,
|
|
307
|
+
},
|
|
308
|
+
instrumentCommand(
|
|
309
|
+
"cloudflare.create-token",
|
|
310
|
+
(a: { allPermissions: boolean }) => ({
|
|
311
|
+
"alchemy.all_permissions": a.allPermissions,
|
|
312
|
+
}),
|
|
313
|
+
)(
|
|
314
|
+
Effect.fnUntraced(function* ({ envFile, allPermissions, name, accountId }) {
|
|
315
|
+
const configProvider = ConfigProvider.layer(
|
|
316
|
+
yield* loadConfigProvider(envFile),
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
yield* Effect.gen(function* () {
|
|
320
|
+
// The Global API Key is a dashboard-only secret — there is no API to
|
|
321
|
+
// fetch it — so read it from the environment or prompt for it. It is
|
|
322
|
+
// used only to create the token and is never stored.
|
|
323
|
+
const envApiKey = yield* Config.string("CLOUDFLARE_API_KEY").pipe(
|
|
324
|
+
Config.option,
|
|
325
|
+
Config.map(Option.getOrUndefined),
|
|
326
|
+
);
|
|
327
|
+
const apiKey =
|
|
328
|
+
envApiKey ??
|
|
329
|
+
(yield* Clank.password({
|
|
330
|
+
message:
|
|
331
|
+
"Paste your Global API Key (see bottom of https://dash.cloudflare.com/profile/api-tokens)",
|
|
332
|
+
validate: (v) => (v.trim().length === 0 ? "Required" : undefined),
|
|
333
|
+
}));
|
|
334
|
+
|
|
335
|
+
const envEmail = yield* Config.string("CLOUDFLARE_EMAIL").pipe(
|
|
336
|
+
Config.option,
|
|
337
|
+
Config.map(Option.getOrUndefined),
|
|
338
|
+
);
|
|
339
|
+
const email =
|
|
340
|
+
envEmail ??
|
|
341
|
+
(yield* Clank.text({
|
|
342
|
+
message: "Cloudflare account email",
|
|
343
|
+
validate: (v) => (v.trim().length === 0 ? "Required" : undefined),
|
|
344
|
+
}));
|
|
345
|
+
|
|
346
|
+
const credentials = Effect.succeed(
|
|
347
|
+
apiKeyCredentials({ apiKey, email }),
|
|
348
|
+
);
|
|
349
|
+
const withCreds = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
350
|
+
self.pipe(
|
|
351
|
+
Effect.provideService(
|
|
352
|
+
CloudflareCredentials.Credentials,
|
|
353
|
+
credentials,
|
|
354
|
+
),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const resolvedAccountId =
|
|
358
|
+
accountId ?? (yield* withCreds(selectAccountId(undefined)));
|
|
359
|
+
|
|
360
|
+
const tokenName =
|
|
361
|
+
name ??
|
|
362
|
+
(yield* Clank.text({
|
|
363
|
+
message: "Token name",
|
|
364
|
+
placeholder: allPermissions ? "alchemy-superuser" : "alchemy",
|
|
365
|
+
validate: (v) =>
|
|
366
|
+
v.trim().length === 0 ? "Token name is required" : undefined,
|
|
367
|
+
}));
|
|
368
|
+
|
|
369
|
+
// Resolve permission groups live from Cloudflare instead of a static
|
|
370
|
+
// catalog. Cloudflare silently ignores permission-group IDs it doesn't
|
|
371
|
+
// recognize, so a stale local list yields a token with zero
|
|
372
|
+
// permissions. `/user/tokens/permission_groups` returns exactly the
|
|
373
|
+
// groups (and IDs) valid for this credential.
|
|
374
|
+
//
|
|
375
|
+
// We hit the endpoint with a raw GET rather than the typed distilled
|
|
376
|
+
// client: the client hard-codes the set of valid `scopes` literals,
|
|
377
|
+
// and Cloudflare keeps adding new ones (e.g. `com.cloudflare.edge.
|
|
378
|
+
// worker.script`), which makes the strict schema reject the whole
|
|
379
|
+
// response. Parsing leniently keeps us forward-compatible.
|
|
380
|
+
const http = yield* HttpClient.HttpClient;
|
|
381
|
+
const pgResponse = yield* http.get(
|
|
382
|
+
"https://api.cloudflare.com/client/v4/user/tokens/permission_groups",
|
|
383
|
+
{
|
|
384
|
+
headers: {
|
|
385
|
+
"X-Auth-Key": apiKey,
|
|
386
|
+
"X-Auth-Email": email,
|
|
387
|
+
Accept: "application/json",
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
const pgBody = (yield* pgResponse.json) as {
|
|
392
|
+
result?: { id?: string; name?: string; scopes?: string[] }[];
|
|
393
|
+
};
|
|
394
|
+
const liveGroups = (pgBody.result ?? []).flatMap((g) =>
|
|
395
|
+
g.id && g.scopes && g.scopes.length > 0
|
|
396
|
+
? [{ id: g.id, name: g.name ?? "", scopes: g.scopes }]
|
|
397
|
+
: [],
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const selected = allPermissions
|
|
401
|
+
? liveGroups
|
|
402
|
+
: liveGroups.filter((g) =>
|
|
403
|
+
ALCHEMY_DEFAULT_PERMISSION_GROUP_NAMES.has(g.name),
|
|
404
|
+
);
|
|
405
|
+
const policies = buildTokenPolicies(resolvedAccountId, selected);
|
|
406
|
+
|
|
407
|
+
if (policies.length === 0) {
|
|
408
|
+
return yield* Effect.die(
|
|
409
|
+
"No permission groups resolved for this account; cannot create a token.",
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (allPermissions) {
|
|
414
|
+
yield* Clank.warn(
|
|
415
|
+
"This token will have FULL access to your Cloudflare account. " +
|
|
416
|
+
"Keep it secret — anyone with it can control your account.",
|
|
417
|
+
);
|
|
418
|
+
const ok = yield* Clank.confirm({
|
|
419
|
+
message: "Create a superuser token with all permissions?",
|
|
420
|
+
initialValue: false,
|
|
421
|
+
});
|
|
422
|
+
if (!ok) {
|
|
423
|
+
yield* Console.log("Cancelled.");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const result = yield* withCreds(
|
|
429
|
+
user.createToken({ name: tokenName, policies }),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
if (!result.value) {
|
|
433
|
+
return yield* Effect.die(
|
|
434
|
+
"Cloudflare did not return a token value. Try again.",
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Cloudflare echoes back the policies it actually accepted. It silently
|
|
439
|
+
// drops permission groups the authenticating user isn't allowed to
|
|
440
|
+
// grant — so a token can come back with zero permissions even though
|
|
441
|
+
// the request was well-formed. Count what was granted and warn loudly
|
|
442
|
+
// if it's empty (almost always an account-role problem, not a bug).
|
|
443
|
+
const granted = (result.policies ?? []).reduce(
|
|
444
|
+
(n, p) => n + (p.permissionGroups?.length ?? 0),
|
|
445
|
+
0,
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Verify the freshly minted token actually authenticates. The
|
|
449
|
+
// Cloudflare dashboard has a long-standing rendering bug where
|
|
450
|
+
// API-created tokens show a blank permission summary (and a disabled
|
|
451
|
+
// "View"), which makes a perfectly good token look empty. A live
|
|
452
|
+
// `/user/tokens/verify` is the source of truth.
|
|
453
|
+
const status = yield* http
|
|
454
|
+
.get("https://api.cloudflare.com/client/v4/user/tokens/verify", {
|
|
455
|
+
headers: {
|
|
456
|
+
Authorization: `Bearer ${result.value}`,
|
|
457
|
+
Accept: "application/json",
|
|
458
|
+
},
|
|
459
|
+
})
|
|
460
|
+
.pipe(
|
|
461
|
+
Effect.flatMap((r) => r.json),
|
|
462
|
+
Effect.map(
|
|
463
|
+
(b) => (b as { result?: { status?: string } }).result?.status,
|
|
464
|
+
),
|
|
465
|
+
Effect.catch(() => Effect.succeed(undefined)),
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
yield* Console.log("");
|
|
469
|
+
yield* Console.log(
|
|
470
|
+
`Created Cloudflare API token "${result.name ?? tokenName}" (${result.id ?? "unknown id"}).`,
|
|
471
|
+
);
|
|
472
|
+
yield* Console.log(
|
|
473
|
+
`Granted ${granted} permission group(s) across ${result.policies?.length ?? 0} policy(ies)` +
|
|
474
|
+
(status ? `; token status: ${status}.` : "."),
|
|
475
|
+
);
|
|
476
|
+
yield* Console.log("");
|
|
477
|
+
yield* Console.log(result.value);
|
|
478
|
+
yield* Console.log("");
|
|
479
|
+
yield* Console.log(
|
|
480
|
+
"Store this value now — Cloudflare only shows it once. " +
|
|
481
|
+
"Use it as CLOUDFLARE_API_TOKEN.",
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
if (granted === 0) {
|
|
485
|
+
yield* Clank.warn(
|
|
486
|
+
"Cloudflare granted 0 permissions. A token can only carry permissions " +
|
|
487
|
+
"the authenticating user already holds, so this usually means the " +
|
|
488
|
+
"Global API Key's user is not a Super Administrator on the selected " +
|
|
489
|
+
"account. Check the user's role in the Cloudflare dashboard " +
|
|
490
|
+
"(Members) and retry with an owner/Super Administrator.",
|
|
491
|
+
);
|
|
492
|
+
} else {
|
|
493
|
+
// Token is good; preempt the "it looks empty" confusion.
|
|
494
|
+
yield* Clank.info(
|
|
495
|
+
"Heads up: the Cloudflare dashboard often renders API-created tokens " +
|
|
496
|
+
'with a blank permission summary (and a greyed-out "View") — this is ' +
|
|
497
|
+
"a known UI bug, not a broken token. The permissions above are applied. " +
|
|
498
|
+
'To see them in the dashboard, open the token and click "← Edit token".',
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
}).pipe(Effect.provide(configProvider));
|
|
502
|
+
}),
|
|
503
|
+
),
|
|
504
|
+
);
|
|
505
|
+
|
|
113
506
|
const tailFlag = Flag.boolean("tail").pipe(
|
|
114
507
|
Flag.withDescription(
|
|
115
508
|
"Stream logs in real time via the Cloudflare tail websocket instead of fetching past entries.",
|
|
@@ -220,5 +613,5 @@ const stateCommand = Command.make("state", {}).pipe(
|
|
|
220
613
|
);
|
|
221
614
|
|
|
222
615
|
export const cloudflareCommand = Command.make("cloudflare", {}).pipe(
|
|
223
|
-
Command.withSubcommands([bootstrapCommand, stateCommand]),
|
|
616
|
+
Command.withSubcommands([bootstrapCommand, createTokenCommand, stateCommand]),
|
|
224
617
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as Cause from "effect/Cause";
|
|
2
1
|
import * as Config from "effect/Config";
|
|
3
2
|
import * as ConfigProvider from "effect/ConfigProvider";
|
|
4
3
|
import * as Console from "effect/Console";
|
|
@@ -10,10 +9,10 @@ import { Command, Flag } from "effect/unstable/cli";
|
|
|
10
9
|
import { AuthProviders } from "../../Auth/AuthProvider.ts";
|
|
11
10
|
import { Profile, withProfileOverride } from "../../Auth/Profile.ts";
|
|
12
11
|
import { Stage } from "../../Stage.ts";
|
|
13
|
-
import * as State from "../../State/index.ts";
|
|
14
12
|
import { loadConfigProvider } from "../../Util/ConfigProvider.ts";
|
|
15
13
|
import { fileLogger } from "../../Util/FileLogger.ts";
|
|
16
14
|
|
|
15
|
+
import { Stack } from "../../Stack.ts";
|
|
17
16
|
import {
|
|
18
17
|
envFile,
|
|
19
18
|
importStack,
|
|
@@ -59,73 +58,83 @@ export const loginCommand = Command.make(
|
|
|
59
58
|
|
|
60
59
|
const authProviders: AuthProviders["Service"] = {};
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
// build the state + providers layer to capture the Auth Providers
|
|
62
|
+
yield* Layer.build(
|
|
63
|
+
(stackEffect.providers ?? Layer.empty).pipe(
|
|
64
|
+
Layer.provideMerge(stackEffect.state ?? Layer.empty),
|
|
65
|
+
Layer.provideMerge(
|
|
66
|
+
Layer.mergeAll(
|
|
67
|
+
Layer.succeed(AuthProviders, authProviders),
|
|
68
|
+
ConfigProvider.layer(
|
|
69
|
+
withProfileOverride(
|
|
70
|
+
yield* loadConfigProvider(envFile),
|
|
71
|
+
profile,
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
Logger.layer([fileLogger("out")], { mergeWithExisting: true }),
|
|
75
|
+
Layer.succeed(Stage, stage),
|
|
76
|
+
Layer.succeed(Stack, {
|
|
77
|
+
actions: {},
|
|
78
|
+
bindings: {},
|
|
79
|
+
name: stackEffect.stackName,
|
|
80
|
+
resources: {},
|
|
81
|
+
stage,
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
66
85
|
),
|
|
67
|
-
Logger.layer([fileLogger("out")], { mergeWithExisting: true }),
|
|
68
|
-
Layer.succeed(Stage, stage),
|
|
69
|
-
State.localState(),
|
|
70
86
|
);
|
|
71
87
|
|
|
72
|
-
yield*
|
|
73
|
-
const profiles = yield* Profile;
|
|
74
|
-
yield* Effect.catchCause(stackEffect, (cause) =>
|
|
75
|
-
Console.warn(
|
|
76
|
-
`Ignoring error while building stack for login (likely due to missing or broken credentials):\n${Cause.pretty(cause)}`,
|
|
77
|
-
),
|
|
78
|
-
);
|
|
88
|
+
const profiles = yield* Profile;
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
|
|
91
|
+
const providers = Object.values(authProviders);
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
if (providers.length === 0) {
|
|
94
|
+
yield* Console.log(
|
|
95
|
+
"No AuthProviders registered. Make sure the stack's providers() layer includes AuthProviderLayer entries.",
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
yield* Effect.forEach(
|
|
101
|
+
providers,
|
|
102
|
+
(provider) =>
|
|
103
|
+
Effect.gen(function* () {
|
|
104
|
+
const existing = yield* profiles.getProfile(profile);
|
|
105
|
+
// --configure treats every provider as missing, so configure
|
|
106
|
+
// runs unconditionally and overwrites the stored entry.
|
|
107
|
+
const stored = configure ? undefined : existing?.[provider.name];
|
|
98
108
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
let cfg: { method: string };
|
|
110
|
+
if (stored == null) {
|
|
111
|
+
cfg = yield* provider.configure(profile, { ci });
|
|
112
|
+
yield* profiles.setProfile(profile, {
|
|
113
|
+
...existing,
|
|
114
|
+
[provider.name]: cfg,
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
cfg = stored;
|
|
118
|
+
}
|
|
109
119
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
// `read` succeeds when creds are present and not expired
|
|
121
|
+
// (refreshing OAuth proactively if near expiry). Any failure
|
|
122
|
+
// — missing file, dead refresh token, etc. — falls through
|
|
123
|
+
// to `login`.
|
|
124
|
+
yield* provider
|
|
125
|
+
.read(profile, cfg)
|
|
126
|
+
.pipe(Effect.catch(() => provider.login(profile, cfg)));
|
|
127
|
+
}),
|
|
128
|
+
{ discard: true },
|
|
129
|
+
);
|
|
120
130
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}).pipe(Effect.provide(services));
|
|
131
|
+
// Print the resulting profile using the same renderer as
|
|
132
|
+
// `alchemy profile show`.
|
|
133
|
+
const final = yield* profiles.getProfile(profile);
|
|
134
|
+
if (final != null) {
|
|
135
|
+
yield* Console.log("");
|
|
136
|
+
yield* printProfile(profile, final, authProviders);
|
|
137
|
+
}
|
|
129
138
|
}),
|
|
130
139
|
),
|
|
131
140
|
);
|
package/src/Cli/commands/logs.ts
CHANGED
|
@@ -89,7 +89,7 @@ export const logsCommand = Command.make(
|
|
|
89
89
|
const stack = yield* stackEffect;
|
|
90
90
|
|
|
91
91
|
yield* Effect.gen(function* () {
|
|
92
|
-
const state = yield* State.State;
|
|
92
|
+
const state = yield* yield* State.State;
|
|
93
93
|
const filterSet = parseResourceFilter(filter);
|
|
94
94
|
const availableIds = [
|
|
95
95
|
...new Set(Object.values(stack.resources).map((r) => r.LogicalId)),
|
|
@@ -90,7 +90,7 @@ const withStateService = <A, E>(
|
|
|
90
90
|
return yield* Effect.gen(function* () {
|
|
91
91
|
const stack = yield* stackEffect;
|
|
92
92
|
return yield* Effect.gen(function* () {
|
|
93
|
-
const state = yield* State.State;
|
|
93
|
+
const state = yield* yield* State.State;
|
|
94
94
|
return yield* body(state);
|
|
95
95
|
}).pipe(Effect.provide(stack.services));
|
|
96
96
|
}).pipe(Effect.provide(services));
|
package/src/Cli/commands/tail.ts
CHANGED
|
@@ -62,7 +62,7 @@ export const tailCommand = Command.make(
|
|
|
62
62
|
const stack = yield* stackEffect;
|
|
63
63
|
|
|
64
64
|
yield* Effect.gen(function* () {
|
|
65
|
-
const state = yield* State.State;
|
|
65
|
+
const state = yield* yield* State.State;
|
|
66
66
|
const filterSet = parseResourceFilter(filter);
|
|
67
67
|
const availableIds = [
|
|
68
68
|
...new Set(Object.values(stack.resources).map((r) => r.LogicalId)),
|