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.
Files changed (295) hide show
  1. package/bin/exec.js +18 -10
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  4. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  5. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  6. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  7. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  8. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  9. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  10. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  11. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  12. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  13. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  14. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  15. package/lib/Apply.js +2 -2
  16. package/lib/Apply.js.map +1 -1
  17. package/lib/Cli/commands/_shared.d.ts +7 -1
  18. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  19. package/lib/Cli/commands/_shared.js +1 -0
  20. package/lib/Cli/commands/_shared.js.map +1 -1
  21. package/lib/Cli/commands/cloudflare.d.ts +4 -1
  22. package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
  23. package/lib/Cli/commands/cloudflare.js +274 -1
  24. package/lib/Cli/commands/cloudflare.js.map +1 -1
  25. package/lib/Cli/commands/login.d.ts +1 -1
  26. package/lib/Cli/commands/login.d.ts.map +1 -1
  27. package/lib/Cli/commands/login.js +45 -42
  28. package/lib/Cli/commands/login.js.map +1 -1
  29. package/lib/Cli/commands/logs.js +1 -1
  30. package/lib/Cli/commands/logs.js.map +1 -1
  31. package/lib/Cli/commands/state.js +1 -1
  32. package/lib/Cli/commands/state.js.map +1 -1
  33. package/lib/Cli/commands/tail.js +1 -1
  34. package/lib/Cli/commands/tail.js.map +1 -1
  35. package/lib/Cloudflare/AiGateway/AiGateway.d.ts +112 -1
  36. package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
  37. package/lib/Cloudflare/AiGateway/AiGateway.js +114 -2
  38. package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
  39. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts +43 -10
  40. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts.map +1 -1
  41. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js +38 -10
  42. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js.map +1 -1
  43. package/lib/Cloudflare/AiGateway/LanguageModel.d.ts +35 -0
  44. package/lib/Cloudflare/AiGateway/LanguageModel.d.ts.map +1 -0
  45. package/lib/Cloudflare/AiGateway/LanguageModel.js +576 -0
  46. package/lib/Cloudflare/AiGateway/LanguageModel.js.map +1 -0
  47. package/lib/Cloudflare/AiGateway/index.d.ts +1 -0
  48. package/lib/Cloudflare/AiGateway/index.d.ts.map +1 -1
  49. package/lib/Cloudflare/AiGateway/index.js +1 -0
  50. package/lib/Cloudflare/AiGateway/index.js.map +1 -1
  51. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +40 -2
  52. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  53. package/lib/Cloudflare/ApiToken/AccountApiToken.js +49 -6
  54. package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
  55. package/lib/Cloudflare/ApiToken/Common.d.ts +26 -3
  56. package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
  57. package/lib/Cloudflare/ApiToken/Common.js +8 -0
  58. package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
  59. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +38 -2
  60. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
  61. package/lib/Cloudflare/ApiToken/UserApiToken.js +47 -6
  62. package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
  63. package/lib/Cloudflare/Browser/Browser.d.ts +121 -0
  64. package/lib/Cloudflare/Browser/Browser.d.ts.map +1 -0
  65. package/lib/Cloudflare/{BrowserRendering/BrowserRendering.js → Browser/Browser.js} +19 -26
  66. package/lib/Cloudflare/Browser/Browser.js.map +1 -0
  67. package/lib/Cloudflare/Browser/BrowserBinding.d.ts +55 -0
  68. package/lib/Cloudflare/Browser/BrowserBinding.d.ts.map +1 -0
  69. package/lib/Cloudflare/{BrowserRendering/BrowserRenderingBinding.js → Browser/BrowserBinding.js} +11 -11
  70. package/lib/Cloudflare/Browser/BrowserBinding.js.map +1 -0
  71. package/lib/Cloudflare/Browser/index.d.ts +3 -0
  72. package/lib/Cloudflare/Browser/index.d.ts.map +1 -0
  73. package/lib/Cloudflare/Browser/index.js +3 -0
  74. package/lib/Cloudflare/Browser/index.js.map +1 -0
  75. package/lib/Cloudflare/Container/ContainerBinding.d.ts +2 -1
  76. package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
  77. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  78. package/lib/Cloudflare/D1/D1Database.js +2 -1
  79. package/lib/Cloudflare/D1/D1Database.js.map +1 -1
  80. package/lib/Cloudflare/Email/EmailRouting.d.ts +1 -1
  81. package/lib/Cloudflare/Email/EmailRouting.d.ts.map +1 -1
  82. package/lib/Cloudflare/Email/EmailRouting.js +1 -1
  83. package/lib/Cloudflare/Email/EmailRouting.js.map +1 -1
  84. package/lib/Cloudflare/Email/EmailRule.d.ts +1 -1
  85. package/lib/Cloudflare/Email/EmailRule.d.ts.map +1 -1
  86. package/lib/Cloudflare/Email/EmailRule.js +1 -1
  87. package/lib/Cloudflare/Email/EmailRule.js.map +1 -1
  88. package/lib/Cloudflare/Images/Images.d.ts +32 -23
  89. package/lib/Cloudflare/Images/Images.d.ts.map +1 -1
  90. package/lib/Cloudflare/Images/Images.js +10 -13
  91. package/lib/Cloudflare/Images/Images.js.map +1 -1
  92. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  93. package/lib/Cloudflare/Providers.js +10 -3
  94. package/lib/Cloudflare/Providers.js.map +1 -1
  95. package/lib/Cloudflare/R2/R2Bucket.d.ts +1 -1
  96. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  97. package/lib/Cloudflare/R2/R2Bucket.js +13 -7
  98. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  99. package/lib/Cloudflare/RateLimit/RateLimit.d.ts +132 -0
  100. package/lib/Cloudflare/RateLimit/RateLimit.d.ts.map +1 -0
  101. package/lib/Cloudflare/RateLimit/RateLimit.js +80 -0
  102. package/lib/Cloudflare/RateLimit/RateLimit.js.map +1 -0
  103. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts +30 -0
  104. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts.map +1 -0
  105. package/lib/Cloudflare/RateLimit/RateLimitBinding.js +50 -0
  106. package/lib/Cloudflare/RateLimit/RateLimitBinding.js.map +1 -0
  107. package/lib/Cloudflare/RateLimit/index.d.ts +3 -0
  108. package/lib/Cloudflare/RateLimit/index.d.ts.map +1 -0
  109. package/lib/Cloudflare/RateLimit/index.js +3 -0
  110. package/lib/Cloudflare/RateLimit/index.js.map +1 -0
  111. package/lib/Cloudflare/SecretsStore/Secret.d.ts +2 -1
  112. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  113. package/lib/Cloudflare/SecretsStore/Secret.js +7 -4
  114. package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
  115. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  116. package/lib/Cloudflare/StateStore/State.js +82 -78
  117. package/lib/Cloudflare/StateStore/State.js.map +1 -1
  118. package/lib/Cloudflare/Tunnel/Tunnel.d.ts +27 -0
  119. package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
  120. package/lib/Cloudflare/Tunnel/Tunnel.js +27 -0
  121. package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
  122. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +48 -0
  123. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -0
  124. package/lib/Cloudflare/Tunnel/TunnelBinding.js +56 -0
  125. package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -0
  126. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts +83 -0
  127. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts.map +1 -0
  128. package/lib/Cloudflare/Tunnel/TunnelRead.js +79 -0
  129. package/lib/Cloudflare/Tunnel/TunnelRead.js.map +1 -0
  130. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts +70 -0
  131. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts.map +1 -0
  132. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js +63 -0
  133. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js.map +1 -0
  134. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts +92 -0
  135. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts.map +1 -0
  136. package/lib/Cloudflare/Tunnel/TunnelWrite.js +88 -0
  137. package/lib/Cloudflare/Tunnel/TunnelWrite.js.map +1 -0
  138. package/lib/Cloudflare/Tunnel/index.d.ts +4 -0
  139. package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
  140. package/lib/Cloudflare/Tunnel/index.js +4 -0
  141. package/lib/Cloudflare/Tunnel/index.js.map +1 -1
  142. package/lib/Cloudflare/Vectorize/VectorizeIndex.js +1 -0
  143. package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -1
  144. package/lib/Cloudflare/VpcService/VpcService.d.ts +1 -1
  145. package/lib/Cloudflare/VpcService/VpcService.d.ts.map +1 -1
  146. package/lib/Cloudflare/VpcService/VpcService.js.map +1 -1
  147. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  148. package/lib/Cloudflare/Workers/DurableObjectBridge.js +10 -1
  149. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  150. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts +119 -11
  151. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts.map +1 -1
  152. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js +88 -9
  153. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js.map +1 -1
  154. package/lib/Cloudflare/Workers/InferEnv.d.ts +1 -1
  155. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  156. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  157. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +15 -1
  158. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  159. package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
  160. package/lib/Cloudflare/Workers/Rpc.js +20 -2
  161. package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
  162. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  163. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +18 -2
  164. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  165. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +5 -3
  166. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  167. package/lib/Cloudflare/Workers/WorkerBinding.js +1 -1
  168. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  169. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  170. package/lib/Cloudflare/Workers/WorkerBridge.js +20 -2
  171. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  172. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +1 -1
  173. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -1
  174. package/lib/Cloudflare/Zaraz/ZarazConfig.js +1 -1
  175. package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -1
  176. package/lib/Cloudflare/Zone/Zone.d.ts +170 -0
  177. package/lib/Cloudflare/Zone/Zone.d.ts.map +1 -0
  178. package/lib/Cloudflare/Zone/Zone.js +180 -0
  179. package/lib/Cloudflare/Zone/Zone.js.map +1 -0
  180. package/lib/Cloudflare/Zone/index.d.ts +3 -0
  181. package/lib/Cloudflare/Zone/index.d.ts.map +1 -0
  182. package/lib/Cloudflare/Zone/index.js +3 -0
  183. package/lib/Cloudflare/Zone/index.js.map +1 -0
  184. package/lib/Cloudflare/Zone/lookup.d.ts +33 -0
  185. package/lib/Cloudflare/Zone/lookup.d.ts.map +1 -0
  186. package/lib/Cloudflare/{Zone.js → Zone/lookup.js} +2 -2
  187. package/lib/Cloudflare/Zone/lookup.js.map +1 -0
  188. package/lib/Cloudflare/index.d.ts +3 -1
  189. package/lib/Cloudflare/index.d.ts.map +1 -1
  190. package/lib/Cloudflare/index.js +3 -1
  191. package/lib/Cloudflare/index.js.map +1 -1
  192. package/lib/Output.js +2 -2
  193. package/lib/Output.js.map +1 -1
  194. package/lib/Plan.d.ts +2 -2
  195. package/lib/Plan.d.ts.map +1 -1
  196. package/lib/Plan.js +3 -3
  197. package/lib/Plan.js.map +1 -1
  198. package/lib/Platform.d.ts +5 -5
  199. package/lib/Platform.d.ts.map +1 -1
  200. package/lib/Platform.js.map +1 -1
  201. package/lib/RemovalPolicy.js +2 -2
  202. package/lib/RemovalPolicy.js.map +1 -1
  203. package/lib/Resource.d.ts +14 -1
  204. package/lib/Resource.d.ts.map +1 -1
  205. package/lib/Resource.js +3 -2
  206. package/lib/Resource.js.map +1 -1
  207. package/lib/Stack.d.ts.map +1 -1
  208. package/lib/Stack.js +6 -0
  209. package/lib/Stack.js.map +1 -1
  210. package/lib/State/InMemoryState.d.ts +2 -1
  211. package/lib/State/InMemoryState.d.ts.map +1 -1
  212. package/lib/State/InMemoryState.js +3 -3
  213. package/lib/State/InMemoryState.js.map +1 -1
  214. package/lib/State/LocalState.d.ts.map +1 -1
  215. package/lib/State/LocalState.js +5 -1
  216. package/lib/State/LocalState.js.map +1 -1
  217. package/lib/State/State.d.ts +2 -2
  218. package/lib/State/State.d.ts.map +1 -1
  219. package/lib/State/State.js.map +1 -1
  220. package/lib/tsconfig.test.tsbuildinfo +1 -1
  221. package/package.json +7 -7
  222. package/src/Apply.ts +2 -2
  223. package/src/Cli/commands/_shared.ts +7 -1
  224. package/src/Cli/commands/cloudflare.ts +394 -1
  225. package/src/Cli/commands/login.ts +69 -60
  226. package/src/Cli/commands/logs.ts +1 -1
  227. package/src/Cli/commands/state.ts +1 -1
  228. package/src/Cli/commands/tail.ts +1 -1
  229. package/src/Cloudflare/AiGateway/AiGateway.ts +114 -2
  230. package/src/Cloudflare/AiGateway/AiGatewayBinding.ts +52 -10
  231. package/src/Cloudflare/AiGateway/LanguageModel.ts +907 -0
  232. package/src/Cloudflare/AiGateway/index.ts +1 -0
  233. package/src/Cloudflare/ApiToken/AccountApiToken.ts +60 -7
  234. package/src/Cloudflare/ApiToken/Common.ts +32 -3
  235. package/src/Cloudflare/ApiToken/UserApiToken.ts +58 -7
  236. package/src/Cloudflare/Browser/Browser.ts +140 -0
  237. package/src/Cloudflare/Browser/BrowserBinding.ts +128 -0
  238. package/src/Cloudflare/Browser/index.ts +11 -0
  239. package/src/Cloudflare/D1/D1Database.ts +6 -2
  240. package/src/Cloudflare/Email/EmailRouting.ts +1 -1
  241. package/src/Cloudflare/Email/EmailRule.ts +6 -6
  242. package/src/Cloudflare/Images/Images.ts +40 -27
  243. package/src/Cloudflare/Providers.ts +15 -3
  244. package/src/Cloudflare/R2/R2Bucket.ts +17 -9
  245. package/src/Cloudflare/RateLimit/RateLimit.ts +160 -0
  246. package/src/Cloudflare/RateLimit/RateLimitBinding.ts +90 -0
  247. package/src/Cloudflare/RateLimit/index.ts +2 -0
  248. package/src/Cloudflare/SecretsStore/Secret.ts +12 -6
  249. package/src/Cloudflare/StateStore/State.ts +113 -107
  250. package/src/Cloudflare/Tunnel/Tunnel.ts +27 -0
  251. package/src/Cloudflare/Tunnel/TunnelBinding.ts +114 -0
  252. package/src/Cloudflare/Tunnel/TunnelRead.ts +168 -0
  253. package/src/Cloudflare/Tunnel/TunnelReadWrite.ts +88 -0
  254. package/src/Cloudflare/Tunnel/TunnelWrite.ts +194 -0
  255. package/src/Cloudflare/Tunnel/index.ts +4 -0
  256. package/src/Cloudflare/Vectorize/VectorizeIndex.ts +2 -1
  257. package/src/Cloudflare/VpcService/VpcService.ts +4 -2
  258. package/src/Cloudflare/Workers/DurableObjectBridge.ts +10 -1
  259. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +139 -13
  260. package/src/Cloudflare/Workers/InferEnv.ts +19 -15
  261. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +26 -3
  262. package/src/Cloudflare/Workers/Rpc.ts +38 -9
  263. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +16 -2
  264. package/src/Cloudflare/Workers/WorkerBinding.ts +6 -2
  265. package/src/Cloudflare/Workers/WorkerBridge.ts +20 -2
  266. package/src/Cloudflare/Zaraz/ZarazConfig.ts +1 -1
  267. package/src/Cloudflare/Zone/Zone.ts +349 -0
  268. package/src/Cloudflare/Zone/index.ts +2 -0
  269. package/src/Cloudflare/{Zone.ts → Zone/lookup.ts} +16 -3
  270. package/src/Cloudflare/index.ts +3 -1
  271. package/src/Output.ts +2 -2
  272. package/src/Plan.ts +4 -4
  273. package/src/Platform.ts +8 -12
  274. package/src/RemovalPolicy.ts +2 -2
  275. package/src/Resource.ts +18 -1
  276. package/src/Stack.ts +6 -0
  277. package/src/State/InMemoryState.ts +87 -85
  278. package/src/State/LocalState.ts +15 -1
  279. package/src/State/State.ts +5 -4
  280. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts +0 -116
  281. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts.map +0 -1
  282. package/lib/Cloudflare/BrowserRendering/BrowserRendering.js.map +0 -1
  283. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts +0 -55
  284. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts.map +0 -1
  285. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js.map +0 -1
  286. package/lib/Cloudflare/BrowserRendering/index.d.ts +0 -3
  287. package/lib/Cloudflare/BrowserRendering/index.d.ts.map +0 -1
  288. package/lib/Cloudflare/BrowserRendering/index.js +0 -3
  289. package/lib/Cloudflare/BrowserRendering/index.js.map +0 -1
  290. package/lib/Cloudflare/Zone.d.ts +0 -14
  291. package/lib/Cloudflare/Zone.d.ts.map +0 -1
  292. package/lib/Cloudflare/Zone.js.map +0 -1
  293. package/src/Cloudflare/BrowserRendering/BrowserRendering.ts +0 -132
  294. package/src/Cloudflare/BrowserRendering/BrowserRenderingBinding.ts +0 -137
  295. 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.46",
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.2",
295
- "@distilled.cloud/axiom": "^0.22.2",
296
- "@distilled.cloud/cloudflare": "^0.22.2",
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.2",
301
- "@distilled.cloud/neon": "^0.22.2",
302
- "@distilled.cloud/planetscale": "^0.22.2",
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
- const services = Layer.mergeAll(
63
- Layer.succeed(AuthProviders, authProviders),
64
- ConfigProvider.layer(
65
- withProfileOverride(yield* loadConfigProvider(envFile), profile),
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* Effect.gen(function* () {
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
- const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
81
- const providers = Object.values(authProviders);
90
+ const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
91
+ const providers = Object.values(authProviders);
82
92
 
83
- if (providers.length === 0) {
84
- yield* Console.log(
85
- "No AuthProviders registered. Make sure the stack's providers() layer includes AuthProviderLayer entries.",
86
- );
87
- return;
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
- yield* Effect.forEach(
91
- providers,
92
- (provider) =>
93
- Effect.gen(function* () {
94
- const existing = yield* profiles.getProfile(profile);
95
- // --configure treats every provider as missing, so configure
96
- // runs unconditionally and overwrites the stored entry.
97
- const stored = configure ? undefined : existing?.[provider.name];
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
- let cfg: { method: string };
100
- if (stored == null) {
101
- cfg = yield* provider.configure(profile, { ci });
102
- yield* profiles.setProfile(profile, {
103
- ...existing,
104
- [provider.name]: cfg,
105
- });
106
- } else {
107
- cfg = stored;
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
- // `read` succeeds when creds are present and not expired
111
- // (refreshing OAuth proactively if near expiry). Any failure
112
- // — missing file, dead refresh token, etc. — falls through
113
- // to `login`.
114
- yield* provider
115
- .read(profile, cfg)
116
- .pipe(Effect.catch(() => provider.login(profile, cfg)));
117
- }),
118
- { discard: true },
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
- // Print the resulting profile using the same renderer as
122
- // `alchemy profile show`.
123
- const final = yield* profiles.getProfile(profile);
124
- if (final != null) {
125
- yield* Console.log("");
126
- yield* printProfile(profile, final, authProviders);
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
  );
@@ -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));
@@ -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)),