alchemy 2.0.0-beta.62 → 2.0.0-beta.63

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 (489) hide show
  1. package/README.md +12 -12
  2. package/bin/exec.js +1959 -70
  3. package/bin/exec.js.map +1 -1
  4. package/lib/AWS/AuthProvider.js +1 -1
  5. package/lib/AWS/AuthProvider.js.map +1 -1
  6. package/lib/AWS/EC2/hosted.d.ts.map +1 -1
  7. package/lib/AWS/EC2/hosted.js +2 -1
  8. package/lib/AWS/EC2/hosted.js.map +1 -1
  9. package/lib/AWS/ECS/Task.d.ts.map +1 -1
  10. package/lib/AWS/ECS/Task.js +2 -1
  11. package/lib/AWS/ECS/Task.js.map +1 -1
  12. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  13. package/lib/AWS/Lambda/Function.js +13 -40
  14. package/lib/AWS/Lambda/Function.js.map +1 -1
  15. package/lib/AWS/Lambda/MicrovmRuntimeContext.d.ts.map +1 -1
  16. package/lib/AWS/Lambda/MicrovmRuntimeContext.js +9 -21
  17. package/lib/AWS/Lambda/MicrovmRuntimeContext.js.map +1 -1
  18. package/lib/Action.d.ts +8 -0
  19. package/lib/Action.d.ts.map +1 -1
  20. package/lib/Action.js +16 -5
  21. package/lib/Action.js.map +1 -1
  22. package/lib/ActionRuntimeContext.d.ts +14 -0
  23. package/lib/ActionRuntimeContext.d.ts.map +1 -0
  24. package/lib/ActionRuntimeContext.js +56 -0
  25. package/lib/ActionRuntimeContext.js.map +1 -0
  26. package/lib/Apply.d.ts.map +1 -1
  27. package/lib/Apply.js +40 -6
  28. package/lib/Apply.js.map +1 -1
  29. package/lib/Auth/AuthProvider.d.ts +3 -3
  30. package/lib/Auth/AuthProvider.d.ts.map +1 -1
  31. package/lib/Auth/AuthProvider.js +1 -1
  32. package/lib/Auth/AuthProvider.js.map +1 -1
  33. package/lib/Auth/Lock.d.ts.map +1 -1
  34. package/lib/Auth/Lock.js +5 -2
  35. package/lib/Auth/Lock.js.map +1 -1
  36. package/lib/Bundle/PurePlugin.d.ts.map +1 -1
  37. package/lib/Bundle/PurePlugin.js +26 -1
  38. package/lib/Bundle/PurePlugin.js.map +1 -1
  39. package/lib/Bundle/Vite.d.ts +2 -0
  40. package/lib/Bundle/Vite.d.ts.map +1 -1
  41. package/lib/Bundle/Vite.js +24 -1
  42. package/lib/Bundle/Vite.js.map +1 -1
  43. package/lib/Cli/LoggingCli.d.ts.map +1 -1
  44. package/lib/Cli/LoggingCli.js +7 -4
  45. package/lib/Cli/LoggingCli.js.map +1 -1
  46. package/lib/Cli/commands/_shared.d.ts +19 -0
  47. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  48. package/lib/Cli/commands/_shared.js +22 -0
  49. package/lib/Cli/commands/_shared.js.map +1 -1
  50. package/lib/Cli/commands/login.d.ts +7 -3
  51. package/lib/Cli/commands/login.d.ts.map +1 -1
  52. package/lib/Cli/commands/login.js +70 -12
  53. package/lib/Cli/commands/login.js.map +1 -1
  54. package/lib/Cli/commands/profile.d.ts.map +1 -1
  55. package/lib/Cli/commands/profile.js +7 -20
  56. package/lib/Cli/commands/profile.js.map +1 -1
  57. package/lib/Cloudflare/AI/LanguageModel.js +21 -4
  58. package/lib/Cloudflare/AI/LanguageModel.js.map +1 -1
  59. package/lib/Cloudflare/AI/QuerySearchLocal.d.ts +37 -0
  60. package/lib/Cloudflare/AI/QuerySearchLocal.d.ts.map +1 -0
  61. package/lib/Cloudflare/AI/QuerySearchLocal.js +53 -0
  62. package/lib/Cloudflare/AI/QuerySearchLocal.js.map +1 -0
  63. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.d.ts +35 -0
  64. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.d.ts.map +1 -0
  65. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.js +47 -0
  66. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.js.map +1 -0
  67. package/lib/Cloudflare/AI/SearchHttpClient.d.ts +34 -0
  68. package/lib/Cloudflare/AI/SearchHttpClient.d.ts.map +1 -0
  69. package/lib/Cloudflare/AI/SearchHttpClient.js +262 -0
  70. package/lib/Cloudflare/AI/SearchHttpClient.js.map +1 -0
  71. package/lib/Cloudflare/AI/index.d.ts +2 -0
  72. package/lib/Cloudflare/AI/index.d.ts.map +1 -1
  73. package/lib/Cloudflare/AI/index.js +2 -0
  74. package/lib/Cloudflare/AI/index.js.map +1 -1
  75. package/lib/Cloudflare/Auth/AuthProvider.js +1 -1
  76. package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
  77. package/lib/Cloudflare/Containers/ContainerBundle.d.ts.map +1 -1
  78. package/lib/Cloudflare/Containers/ContainerBundle.js +11 -1
  79. package/lib/Cloudflare/Containers/ContainerBundle.js.map +1 -1
  80. package/lib/Cloudflare/Containers/ContainerPlatform.d.ts.map +1 -1
  81. package/lib/Cloudflare/Containers/ContainerPlatform.js +12 -33
  82. package/lib/Cloudflare/Containers/ContainerPlatform.js.map +1 -1
  83. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts +24 -0
  84. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts.map +1 -0
  85. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js +106 -0
  86. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js.map +1 -0
  87. package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts +38 -0
  88. package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts.map +1 -0
  89. package/lib/Cloudflare/D1/QueryDatabaseLocal.js +54 -0
  90. package/lib/Cloudflare/D1/QueryDatabaseLocal.js.map +1 -0
  91. package/lib/Cloudflare/D1/index.d.ts +1 -0
  92. package/lib/Cloudflare/D1/index.d.ts.map +1 -1
  93. package/lib/Cloudflare/D1/index.js +1 -0
  94. package/lib/Cloudflare/D1/index.js.map +1 -1
  95. package/lib/Cloudflare/DNS/DnsHttp.d.ts +16 -1
  96. package/lib/Cloudflare/DNS/DnsHttp.d.ts.map +1 -1
  97. package/lib/Cloudflare/DNS/DnsHttp.js +6 -1
  98. package/lib/Cloudflare/DNS/DnsHttp.js.map +1 -1
  99. package/lib/Cloudflare/DNS/DnsLocal.d.ts +22 -0
  100. package/lib/Cloudflare/DNS/DnsLocal.d.ts.map +1 -0
  101. package/lib/Cloudflare/DNS/DnsLocal.js +31 -0
  102. package/lib/Cloudflare/DNS/DnsLocal.js.map +1 -0
  103. package/lib/Cloudflare/DNS/ReadDnsHttp.d.ts +3 -3
  104. package/lib/Cloudflare/DNS/ReadDnsHttp.d.ts.map +1 -1
  105. package/lib/Cloudflare/DNS/ReadDnsHttp.js +3 -4
  106. package/lib/Cloudflare/DNS/ReadDnsHttp.js.map +1 -1
  107. package/lib/Cloudflare/DNS/ReadDnsLocal.d.ts +32 -0
  108. package/lib/Cloudflare/DNS/ReadDnsLocal.d.ts.map +1 -0
  109. package/lib/Cloudflare/DNS/ReadDnsLocal.js +35 -0
  110. package/lib/Cloudflare/DNS/ReadDnsLocal.js.map +1 -0
  111. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.d.ts +3 -3
  112. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.d.ts.map +1 -1
  113. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.js +4 -4
  114. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.js.map +1 -1
  115. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.d.ts +40 -0
  116. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.d.ts.map +1 -0
  117. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.js +43 -0
  118. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.js.map +1 -0
  119. package/lib/Cloudflare/DNS/WriteDnsHttp.d.ts +3 -3
  120. package/lib/Cloudflare/DNS/WriteDnsHttp.d.ts.map +1 -1
  121. package/lib/Cloudflare/DNS/WriteDnsHttp.js +3 -4
  122. package/lib/Cloudflare/DNS/WriteDnsHttp.js.map +1 -1
  123. package/lib/Cloudflare/DNS/WriteDnsLocal.d.ts +38 -0
  124. package/lib/Cloudflare/DNS/WriteDnsLocal.d.ts.map +1 -0
  125. package/lib/Cloudflare/DNS/WriteDnsLocal.js +41 -0
  126. package/lib/Cloudflare/DNS/WriteDnsLocal.js.map +1 -0
  127. package/lib/Cloudflare/DNS/index.d.ts +3 -0
  128. package/lib/Cloudflare/DNS/index.d.ts.map +1 -1
  129. package/lib/Cloudflare/DNS/index.js +3 -0
  130. package/lib/Cloudflare/DNS/index.js.map +1 -1
  131. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.d.ts +27 -0
  132. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.d.ts.map +1 -0
  133. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.js +86 -0
  134. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.js.map +1 -0
  135. package/lib/Cloudflare/Flagship/ReadFlagsLocal.d.ts +41 -0
  136. package/lib/Cloudflare/Flagship/ReadFlagsLocal.d.ts.map +1 -0
  137. package/lib/Cloudflare/Flagship/ReadFlagsLocal.js +57 -0
  138. package/lib/Cloudflare/Flagship/ReadFlagsLocal.js.map +1 -0
  139. package/lib/Cloudflare/Flagship/index.d.ts +1 -0
  140. package/lib/Cloudflare/Flagship/index.d.ts.map +1 -1
  141. package/lib/Cloudflare/Flagship/index.js +1 -0
  142. package/lib/Cloudflare/Flagship/index.js.map +1 -1
  143. package/lib/Cloudflare/KV/NamespaceHttp.d.ts +16 -1
  144. package/lib/Cloudflare/KV/NamespaceHttp.d.ts.map +1 -1
  145. package/lib/Cloudflare/KV/NamespaceHttp.js +8 -2
  146. package/lib/Cloudflare/KV/NamespaceHttp.js.map +1 -1
  147. package/lib/Cloudflare/KV/NamespaceLocal.d.ts +22 -0
  148. package/lib/Cloudflare/KV/NamespaceLocal.d.ts.map +1 -0
  149. package/lib/Cloudflare/KV/NamespaceLocal.js +32 -0
  150. package/lib/Cloudflare/KV/NamespaceLocal.js.map +1 -0
  151. package/lib/Cloudflare/KV/ReadNamespaceHttp.d.ts +2 -2
  152. package/lib/Cloudflare/KV/ReadNamespaceHttp.d.ts.map +1 -1
  153. package/lib/Cloudflare/KV/ReadNamespaceHttp.js +5 -6
  154. package/lib/Cloudflare/KV/ReadNamespaceHttp.js.map +1 -1
  155. package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts +27 -0
  156. package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts.map +1 -0
  157. package/lib/Cloudflare/KV/ReadNamespaceLocal.js +30 -0
  158. package/lib/Cloudflare/KV/ReadNamespaceLocal.js.map +1 -0
  159. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.d.ts +3 -3
  160. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.d.ts.map +1 -1
  161. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.js +6 -6
  162. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.js.map +1 -1
  163. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts +34 -0
  164. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts.map +1 -0
  165. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js +37 -0
  166. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js.map +1 -0
  167. package/lib/Cloudflare/KV/WriteNamespaceHttp.d.ts +2 -2
  168. package/lib/Cloudflare/KV/WriteNamespaceHttp.d.ts.map +1 -1
  169. package/lib/Cloudflare/KV/WriteNamespaceHttp.js +5 -6
  170. package/lib/Cloudflare/KV/WriteNamespaceHttp.js.map +1 -1
  171. package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts +27 -0
  172. package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts.map +1 -0
  173. package/lib/Cloudflare/KV/WriteNamespaceLocal.js +30 -0
  174. package/lib/Cloudflare/KV/WriteNamespaceLocal.js.map +1 -0
  175. package/lib/Cloudflare/KV/index.d.ts +3 -0
  176. package/lib/Cloudflare/KV/index.d.ts.map +1 -1
  177. package/lib/Cloudflare/KV/index.js +3 -0
  178. package/lib/Cloudflare/KV/index.js.map +1 -1
  179. package/lib/Cloudflare/Queues/QueueHttp.d.ts +20 -1
  180. package/lib/Cloudflare/Queues/QueueHttp.d.ts.map +1 -1
  181. package/lib/Cloudflare/Queues/QueueHttp.js +2 -2
  182. package/lib/Cloudflare/Queues/QueueHttp.js.map +1 -1
  183. package/lib/Cloudflare/Queues/WriteQueueHttp.d.ts +9 -3
  184. package/lib/Cloudflare/Queues/WriteQueueHttp.d.ts.map +1 -1
  185. package/lib/Cloudflare/Queues/WriteQueueHttp.js +11 -6
  186. package/lib/Cloudflare/Queues/WriteQueueHttp.js.map +1 -1
  187. package/lib/Cloudflare/Queues/WriteQueueLocal.d.ts +36 -0
  188. package/lib/Cloudflare/Queues/WriteQueueLocal.d.ts.map +1 -0
  189. package/lib/Cloudflare/Queues/WriteQueueLocal.js +51 -0
  190. package/lib/Cloudflare/Queues/WriteQueueLocal.js.map +1 -0
  191. package/lib/Cloudflare/Queues/index.d.ts +1 -0
  192. package/lib/Cloudflare/Queues/index.d.ts.map +1 -1
  193. package/lib/Cloudflare/Queues/index.js +1 -0
  194. package/lib/Cloudflare/Queues/index.js.map +1 -1
  195. package/lib/Cloudflare/R2/BucketHttp.d.ts +15 -1
  196. package/lib/Cloudflare/R2/BucketHttp.d.ts.map +1 -1
  197. package/lib/Cloudflare/R2/BucketHttp.js +3 -3
  198. package/lib/Cloudflare/R2/BucketHttp.js.map +1 -1
  199. package/lib/Cloudflare/R2/BucketLocal.d.ts +21 -0
  200. package/lib/Cloudflare/R2/BucketLocal.d.ts.map +1 -0
  201. package/lib/Cloudflare/R2/BucketLocal.js +32 -0
  202. package/lib/Cloudflare/R2/BucketLocal.js.map +1 -0
  203. package/lib/Cloudflare/R2/ReadBucketHttp.d.ts +2 -2
  204. package/lib/Cloudflare/R2/ReadBucketHttp.d.ts.map +1 -1
  205. package/lib/Cloudflare/R2/ReadBucketHttp.js +4 -4
  206. package/lib/Cloudflare/R2/ReadBucketHttp.js.map +1 -1
  207. package/lib/Cloudflare/R2/ReadBucketLocal.d.ts +27 -0
  208. package/lib/Cloudflare/R2/ReadBucketLocal.d.ts.map +1 -0
  209. package/lib/Cloudflare/R2/ReadBucketLocal.js +30 -0
  210. package/lib/Cloudflare/R2/ReadBucketLocal.js.map +1 -0
  211. package/lib/Cloudflare/R2/ReadWriteBucketHttp.d.ts +2 -2
  212. package/lib/Cloudflare/R2/ReadWriteBucketHttp.d.ts.map +1 -1
  213. package/lib/Cloudflare/R2/ReadWriteBucketHttp.js +5 -4
  214. package/lib/Cloudflare/R2/ReadWriteBucketHttp.js.map +1 -1
  215. package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts +30 -0
  216. package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts.map +1 -0
  217. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js +33 -0
  218. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js.map +1 -0
  219. package/lib/Cloudflare/R2/WriteBucketHttp.d.ts +2 -2
  220. package/lib/Cloudflare/R2/WriteBucketHttp.d.ts.map +1 -1
  221. package/lib/Cloudflare/R2/WriteBucketHttp.js +4 -4
  222. package/lib/Cloudflare/R2/WriteBucketHttp.js.map +1 -1
  223. package/lib/Cloudflare/R2/WriteBucketLocal.d.ts +27 -0
  224. package/lib/Cloudflare/R2/WriteBucketLocal.d.ts.map +1 -0
  225. package/lib/Cloudflare/R2/WriteBucketLocal.js +30 -0
  226. package/lib/Cloudflare/R2/WriteBucketLocal.js.map +1 -0
  227. package/lib/Cloudflare/R2/index.d.ts +3 -0
  228. package/lib/Cloudflare/R2/index.d.ts.map +1 -1
  229. package/lib/Cloudflare/R2/index.js +3 -0
  230. package/lib/Cloudflare/R2/index.js.map +1 -1
  231. package/lib/Cloudflare/StateStore/Api.d.ts +2 -0
  232. package/lib/Cloudflare/Tunnel/ReadTunnel.d.ts +3 -3
  233. package/lib/Cloudflare/Tunnel/ReadTunnel.d.ts.map +1 -1
  234. package/lib/Cloudflare/Tunnel/ReadTunnel.js +7 -8
  235. package/lib/Cloudflare/Tunnel/ReadTunnel.js.map +1 -1
  236. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.d.ts +29 -0
  237. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.d.ts.map +1 -0
  238. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.js +30 -0
  239. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.js.map +1 -0
  240. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.d.ts +3 -3
  241. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.d.ts.map +1 -1
  242. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.js +4 -4
  243. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.js.map +1 -1
  244. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.d.ts +35 -0
  245. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.d.ts.map +1 -0
  246. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.js +36 -0
  247. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.js.map +1 -0
  248. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +17 -1
  249. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -1
  250. package/lib/Cloudflare/Tunnel/TunnelBinding.js +7 -1
  251. package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -1
  252. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.d.ts +19 -0
  253. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.d.ts.map +1 -0
  254. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.js +29 -0
  255. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.js.map +1 -0
  256. package/lib/Cloudflare/Tunnel/WarpConnector.d.ts +3 -3
  257. package/lib/Cloudflare/Tunnel/WarpConnector.d.ts.map +1 -1
  258. package/lib/Cloudflare/Tunnel/WarpConnector.js +7 -4
  259. package/lib/Cloudflare/Tunnel/WarpConnector.js.map +1 -1
  260. package/lib/Cloudflare/Tunnel/WriteTunnel.d.ts +3 -3
  261. package/lib/Cloudflare/Tunnel/WriteTunnel.d.ts.map +1 -1
  262. package/lib/Cloudflare/Tunnel/WriteTunnel.js +7 -8
  263. package/lib/Cloudflare/Tunnel/WriteTunnel.js.map +1 -1
  264. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.d.ts +29 -0
  265. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.d.ts.map +1 -0
  266. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.js +30 -0
  267. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.js.map +1 -0
  268. package/lib/Cloudflare/Tunnel/index.d.ts +3 -0
  269. package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
  270. package/lib/Cloudflare/Tunnel/index.js +3 -0
  271. package/lib/Cloudflare/Tunnel/index.js.map +1 -1
  272. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.d.ts +30 -0
  273. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.d.ts.map +1 -0
  274. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.js +100 -0
  275. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.js.map +1 -0
  276. package/lib/Cloudflare/Vectorize/SearchIndexLocal.d.ts +42 -0
  277. package/lib/Cloudflare/Vectorize/SearchIndexLocal.d.ts.map +1 -0
  278. package/lib/Cloudflare/Vectorize/SearchIndexLocal.js +58 -0
  279. package/lib/Cloudflare/Vectorize/SearchIndexLocal.js.map +1 -0
  280. package/lib/Cloudflare/Vectorize/index.d.ts +1 -0
  281. package/lib/Cloudflare/Vectorize/index.d.ts.map +1 -1
  282. package/lib/Cloudflare/Vectorize/index.js +1 -0
  283. package/lib/Cloudflare/Vectorize/index.js.map +1 -1
  284. package/lib/Cloudflare/Workers/BrowserHttpClient.d.ts +32 -0
  285. package/lib/Cloudflare/Workers/BrowserHttpClient.d.ts.map +1 -0
  286. package/lib/Cloudflare/Workers/BrowserHttpClient.js +100 -0
  287. package/lib/Cloudflare/Workers/BrowserHttpClient.js.map +1 -0
  288. package/lib/Cloudflare/Workers/BrowserLocal.d.ts +37 -0
  289. package/lib/Cloudflare/Workers/BrowserLocal.d.ts.map +1 -0
  290. package/lib/Cloudflare/Workers/BrowserLocal.js +53 -0
  291. package/lib/Cloudflare/Workers/BrowserLocal.js.map +1 -0
  292. package/lib/Cloudflare/Workers/ConfigProvider.d.ts +8 -0
  293. package/lib/Cloudflare/Workers/ConfigProvider.d.ts.map +1 -1
  294. package/lib/Cloudflare/Workers/ConfigProvider.js +10 -1
  295. package/lib/Cloudflare/Workers/ConfigProvider.js.map +1 -1
  296. package/lib/Cloudflare/Workers/DurableObject.d.ts +137 -3
  297. package/lib/Cloudflare/Workers/DurableObject.d.ts.map +1 -1
  298. package/lib/Cloudflare/Workers/DurableObject.js +141 -6
  299. package/lib/Cloudflare/Workers/DurableObject.js.map +1 -1
  300. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +2 -185
  301. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  302. package/lib/Cloudflare/Workers/Vite.d.ts.map +1 -1
  303. package/lib/Cloudflare/Workers/Vite.js +81 -2
  304. package/lib/Cloudflare/Workers/Vite.js.map +1 -1
  305. package/lib/Cloudflare/Workers/Worker.d.ts +15 -1
  306. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  307. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  308. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  309. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +2 -1
  310. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  311. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +22 -1
  312. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  313. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  314. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  315. package/lib/Cloudflare/Workers/WorkerBridge.js +7 -2
  316. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  317. package/lib/Cloudflare/Workers/WorkerProvider.d.ts +81 -0
  318. package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
  319. package/lib/Cloudflare/Workers/WorkerProvider.js +450 -61
  320. package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
  321. package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -1
  322. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +11 -37
  323. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -1
  324. package/lib/Cloudflare/Workers/index.d.ts +1 -0
  325. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  326. package/lib/Cloudflare/Workers/index.js +1 -0
  327. package/lib/Cloudflare/Workers/index.js.map +1 -1
  328. package/lib/Diff.d.ts +16 -0
  329. package/lib/Diff.d.ts.map +1 -1
  330. package/lib/Diff.js +33 -0
  331. package/lib/Diff.js.map +1 -1
  332. package/lib/GitHub/Secrets.d.ts +1 -1
  333. package/lib/GitHub/Secrets.js +1 -1
  334. package/lib/GitHub/Variables.d.ts +1 -1
  335. package/lib/GitHub/Variables.js +1 -1
  336. package/lib/Local/RpcSerialization.d.ts.map +1 -1
  337. package/lib/Local/RpcSerialization.js +6 -2
  338. package/lib/Local/RpcSerialization.js.map +1 -1
  339. package/lib/Local/RpcSpawner.d.ts.map +1 -1
  340. package/lib/Local/RpcSpawner.js +14 -8
  341. package/lib/Local/RpcSpawner.js.map +1 -1
  342. package/lib/Neon/AuthProvider.js +1 -1
  343. package/lib/Neon/AuthProvider.js.map +1 -1
  344. package/lib/Plan.d.ts.map +1 -1
  345. package/lib/Plan.js +11 -4
  346. package/lib/Plan.js.map +1 -1
  347. package/lib/Planetscale/Database.d.ts +2 -1
  348. package/lib/Planetscale/Database.d.ts.map +1 -1
  349. package/lib/Planetscale/MySQL/MySQLClusterSize.d.ts +41 -3
  350. package/lib/Planetscale/MySQL/MySQLClusterSize.d.ts.map +1 -1
  351. package/lib/Planetscale/MySQL/MySQLClusterSize.js +88 -11
  352. package/lib/Planetscale/MySQL/MySQLClusterSize.js.map +1 -1
  353. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts +14 -0
  354. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts.map +1 -1
  355. package/lib/Planetscale/MySQL/MySQLDatabase.js +24 -11
  356. package/lib/Planetscale/MySQL/MySQLDatabase.js.map +1 -1
  357. package/lib/Platform.d.ts.map +1 -1
  358. package/lib/Platform.js +11 -1
  359. package/lib/Platform.js.map +1 -1
  360. package/lib/Runtime.d.ts +23 -0
  361. package/lib/Runtime.d.ts.map +1 -1
  362. package/lib/Runtime.js +83 -0
  363. package/lib/Runtime.js.map +1 -1
  364. package/lib/RuntimeContext.d.ts +46 -0
  365. package/lib/RuntimeContext.d.ts.map +1 -1
  366. package/lib/RuntimeContext.js +60 -0
  367. package/lib/RuntimeContext.js.map +1 -1
  368. package/lib/Server/Process.d.ts +1 -1
  369. package/lib/Server/Process.d.ts.map +1 -1
  370. package/lib/Server/Process.js +8 -8
  371. package/lib/Server/Process.js.map +1 -1
  372. package/lib/Stack.js +1 -1
  373. package/lib/Stack.js.map +1 -1
  374. package/lib/State/HttpStateApi.d.ts +10 -10
  375. package/lib/Test/Alchemy.d.ts +74 -0
  376. package/lib/Test/Alchemy.d.ts.map +1 -0
  377. package/lib/Test/Alchemy.js +143 -0
  378. package/lib/Test/Alchemy.js.map +1 -0
  379. package/lib/Util/cached-function.d.ts +1 -1
  380. package/lib/Util/cached-function.d.ts.map +1 -1
  381. package/lib/Util/cached-function.js +2 -2
  382. package/lib/Util/cached-function.js.map +1 -1
  383. package/lib/Util/proxy-chain.js +3 -0
  384. package/lib/Util/proxy-chain.js.map +1 -1
  385. package/package.json +10 -10
  386. package/src/AWS/AuthProvider.ts +1 -1
  387. package/src/AWS/EC2/hosted.ts +2 -1
  388. package/src/AWS/ECS/Task.ts +2 -1
  389. package/src/AWS/Lambda/Function.ts +13 -47
  390. package/src/AWS/Lambda/MicrovmRuntimeContext.ts +11 -38
  391. package/src/Action.ts +39 -6
  392. package/src/ActionRuntimeContext.ts +68 -0
  393. package/src/Apply.ts +46 -8
  394. package/src/Auth/AuthProvider.ts +1 -1
  395. package/src/Auth/Lock.ts +5 -2
  396. package/src/Bundle/PurePlugin.ts +34 -1
  397. package/src/Bundle/Vite.ts +51 -1
  398. package/src/Cli/LoggingCli.ts +7 -4
  399. package/src/Cli/commands/_shared.ts +52 -0
  400. package/src/Cli/commands/login.ts +90 -14
  401. package/src/Cli/commands/profile.ts +7 -41
  402. package/src/Cloudflare/AI/LanguageModel.ts +25 -5
  403. package/src/Cloudflare/AI/QuerySearchLocal.ts +66 -0
  404. package/src/Cloudflare/AI/QuerySearchNamespaceLocal.ts +60 -0
  405. package/src/Cloudflare/AI/SearchHttpClient.ts +423 -0
  406. package/src/Cloudflare/AI/index.ts +2 -0
  407. package/src/Cloudflare/Auth/AuthProvider.ts +1 -1
  408. package/src/Cloudflare/Containers/ContainerBundle.ts +11 -1
  409. package/src/Cloudflare/Containers/ContainerPlatform.ts +14 -52
  410. package/src/Cloudflare/D1/QueryDatabaseHttpClient.ts +176 -0
  411. package/src/Cloudflare/D1/QueryDatabaseLocal.ts +66 -0
  412. package/src/Cloudflare/D1/index.ts +1 -0
  413. package/src/Cloudflare/DNS/DnsHttp.ts +24 -2
  414. package/src/Cloudflare/DNS/DnsLocal.ts +41 -0
  415. package/src/Cloudflare/DNS/ReadDnsHttp.ts +4 -5
  416. package/src/Cloudflare/DNS/ReadDnsLocal.ts +38 -0
  417. package/src/Cloudflare/DNS/ReadWriteDnsHttp.ts +5 -5
  418. package/src/Cloudflare/DNS/ReadWriteDnsLocal.ts +46 -0
  419. package/src/Cloudflare/DNS/WriteDnsHttp.ts +4 -5
  420. package/src/Cloudflare/DNS/WriteDnsLocal.ts +44 -0
  421. package/src/Cloudflare/DNS/index.ts +3 -0
  422. package/src/Cloudflare/Flagship/ReadFlagsHttpClient.ts +183 -0
  423. package/src/Cloudflare/Flagship/ReadFlagsLocal.ts +69 -0
  424. package/src/Cloudflare/Flagship/index.ts +1 -0
  425. package/src/Cloudflare/KV/NamespaceHttp.ts +25 -2
  426. package/src/Cloudflare/KV/NamespaceLocal.ts +42 -0
  427. package/src/Cloudflare/KV/ReadNamespaceHttp.ts +7 -6
  428. package/src/Cloudflare/KV/ReadNamespaceLocal.ts +35 -0
  429. package/src/Cloudflare/KV/ReadWriteNamespaceHttp.ts +11 -6
  430. package/src/Cloudflare/KV/ReadWriteNamespaceLocal.ts +42 -0
  431. package/src/Cloudflare/KV/WriteNamespaceHttp.ts +7 -6
  432. package/src/Cloudflare/KV/WriteNamespaceLocal.ts +35 -0
  433. package/src/Cloudflare/KV/index.ts +3 -0
  434. package/src/Cloudflare/Queues/QueueHttp.ts +24 -2
  435. package/src/Cloudflare/Queues/WriteQueueHttp.ts +16 -7
  436. package/src/Cloudflare/Queues/WriteQueueLocal.ts +64 -0
  437. package/src/Cloudflare/Queues/index.ts +1 -0
  438. package/src/Cloudflare/R2/BucketHttp.ts +20 -3
  439. package/src/Cloudflare/R2/BucketLocal.ts +47 -0
  440. package/src/Cloudflare/R2/ReadBucketHttp.ts +10 -5
  441. package/src/Cloudflare/R2/ReadBucketLocal.ts +33 -0
  442. package/src/Cloudflare/R2/ReadWriteBucketHttp.ts +11 -5
  443. package/src/Cloudflare/R2/ReadWriteBucketLocal.ts +36 -0
  444. package/src/Cloudflare/R2/WriteBucketHttp.ts +10 -5
  445. package/src/Cloudflare/R2/WriteBucketLocal.ts +33 -0
  446. package/src/Cloudflare/R2/index.ts +3 -0
  447. package/src/Cloudflare/Tunnel/ReadTunnel.ts +8 -9
  448. package/src/Cloudflare/Tunnel/ReadTunnelLocal.ts +33 -0
  449. package/src/Cloudflare/Tunnel/ReadWriteTunnel.ts +5 -5
  450. package/src/Cloudflare/Tunnel/ReadWriteTunnelLocal.ts +39 -0
  451. package/src/Cloudflare/Tunnel/TunnelBinding.ts +26 -2
  452. package/src/Cloudflare/Tunnel/TunnelLocalBinding.ts +38 -0
  453. package/src/Cloudflare/Tunnel/WarpConnector.ts +8 -5
  454. package/src/Cloudflare/Tunnel/WriteTunnel.ts +8 -9
  455. package/src/Cloudflare/Tunnel/WriteTunnelLocal.ts +33 -0
  456. package/src/Cloudflare/Tunnel/index.ts +3 -0
  457. package/src/Cloudflare/Vectorize/SearchIndexHttpClient.ts +183 -0
  458. package/src/Cloudflare/Vectorize/SearchIndexLocal.ts +70 -0
  459. package/src/Cloudflare/Vectorize/index.ts +1 -0
  460. package/src/Cloudflare/Workers/BrowserHttpClient.ts +201 -0
  461. package/src/Cloudflare/Workers/BrowserLocal.ts +65 -0
  462. package/src/Cloudflare/Workers/ConfigProvider.ts +15 -1
  463. package/src/Cloudflare/Workers/DurableObject.ts +227 -6
  464. package/src/Cloudflare/Workers/Vite.ts +93 -11
  465. package/src/Cloudflare/Workers/Worker.ts +19 -1
  466. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +6 -5
  467. package/src/Cloudflare/Workers/WorkerBinding.ts +20 -1
  468. package/src/Cloudflare/Workers/WorkerBridge.ts +12 -2
  469. package/src/Cloudflare/Workers/WorkerProvider.ts +664 -117
  470. package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +14 -45
  471. package/src/Cloudflare/Workers/index.ts +1 -0
  472. package/src/Diff.ts +33 -0
  473. package/src/GitHub/Secrets.ts +1 -1
  474. package/src/GitHub/Variables.ts +1 -1
  475. package/src/Local/RpcSerialization.ts +6 -2
  476. package/src/Local/RpcSpawner.ts +21 -8
  477. package/src/Neon/AuthProvider.ts +1 -1
  478. package/src/Plan.ts +17 -4
  479. package/src/Planetscale/Database.ts +2 -1
  480. package/src/Planetscale/MySQL/MySQLClusterSize.ts +120 -21
  481. package/src/Planetscale/MySQL/MySQLDatabase.ts +84 -44
  482. package/src/Platform.ts +12 -3
  483. package/src/Runtime.ts +92 -0
  484. package/src/RuntimeContext.ts +79 -0
  485. package/src/Server/Process.ts +11 -18
  486. package/src/Stack.ts +1 -1
  487. package/src/Test/Alchemy.ts +282 -0
  488. package/src/Util/cached-function.ts +5 -5
  489. package/src/Util/proxy-chain.ts +3 -0
package/bin/exec.js CHANGED
@@ -8,10 +8,14 @@ import * as FileSystem from "effect/FileSystem";
8
8
  import * as Path$1 from "effect/Path";
9
9
  import { Path } from "effect/Path";
10
10
  import * as Redacted from "effect/Redacted";
11
+ import * as path$1 from "pathe";
11
12
  import path from "pathe";
12
13
  import * as Config from "effect/Config";
14
+ import * as NodeOs from "node:os";
13
15
  import os from "node:os";
14
16
  import * as Semaphore from "effect/Semaphore";
17
+ import { lock } from "@alchemy.run/node-utils/lockfile";
18
+ import * as fs from "node:fs/promises";
15
19
  import { newWebSocketRpcSession } from "capnweb";
16
20
  import * as Cache from "effect/Cache";
17
21
  import * as HttpBody from "effect/unstable/http/HttpBody";
@@ -27,6 +31,7 @@ import { SingleShotGen } from "effect/Utils";
27
31
  import * as Effectable from "effect/Effectable";
28
32
  import "effect/Predicate";
29
33
  import "effect/Record";
34
+ import http from "node:http";
30
35
  import * as Cause from "effect/Cause";
31
36
  import * as Exit from "effect/Exit";
32
37
  import * as Stream from "effect/Stream";
@@ -38,6 +43,22 @@ import * as Metric from "effect/Metric";
38
43
  import * as Argument from "effect/unstable/cli/Argument";
39
44
  import * as CliError from "effect/unstable/cli/CliError";
40
45
  import { pathToFileURL } from "node:url";
46
+ import * as DistilledAuth from "@distilled.cloud/aws/Auth";
47
+ import { Credentials } from "@distilled.cloud/aws/Credentials";
48
+ import * as STS from "@distilled.cloud/aws/sts";
49
+ import * as Match from "effect/Match";
50
+ import { ChildProcess } from "effect/unstable/process";
51
+ import * as NodeCrypto from "node:crypto";
52
+ import crypto$1 from "node:crypto";
53
+ import * as p from "@clack/prompts";
54
+ import * as Region from "@distilled.cloud/aws/Region";
55
+ import { DEFAULT_API_BASE_URL } from "@distilled.cloud/axiom/Credentials";
56
+ import * as cfAccounts from "@distilled.cloud/cloudflare/accounts";
57
+ import * as CfCredentialsModule from "@distilled.cloud/cloudflare/Credentials";
58
+ import * as ChildProcess$1 from "effect/unstable/process/ChildProcess";
59
+ import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
60
+ import * as PsCredentialsModule from "@distilled.cloud/planetscale/Credentials";
61
+ import { listOrganizations } from "@distilled.cloud/planetscale/Operations";
41
62
  import { jsx, jsxs } from "react/jsx-runtime";
42
63
  import { Box, Text, render, useApp, useInput } from "ink";
43
64
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -63,9 +84,9 @@ var AlchemyContext, AlchemyContextLive;
63
84
  var init_AlchemyContext = __esm({ "lib/AlchemyContext.js": (() => {
64
85
  AlchemyContext = class extends EffectContext.Service()("alchemy/Context") {};
65
86
  AlchemyContextLive = Layer.effect(AlchemyContext, Effect.gen(function* () {
66
- const fs = yield* FileSystem.FileSystem;
87
+ const fs$1 = yield* FileSystem.FileSystem;
67
88
  const dir = (yield* Path$1.Path).join(process.cwd(), ".alchemy");
68
- yield* fs.makeDirectory(dir, { recursive: true });
89
+ yield* fs$1.makeDirectory(dir, { recursive: true });
69
90
  return {
70
91
  dotAlchemy: dir,
71
92
  updateStateStore: false,
@@ -90,19 +111,90 @@ var init_interactive = __esm({ "lib/Util/interactive.js": (() => {
90
111
  };
91
112
  }) });
92
113
 
114
+ //#endregion
115
+ //#region lib/Auth/Lock.js
116
+ var sanitizeLockKey, UNLOCKABLE_FS_CODES, withLock;
117
+ var init_Lock = __esm({ "lib/Auth/Lock.js": (() => {
118
+ init_Profile();
119
+ sanitizeLockKey = (key) => key.replace(/[^A-Za-z0-9._-]/g, "_");
120
+ UNLOCKABLE_FS_CODES = new Set([
121
+ "EROFS",
122
+ "EACCES",
123
+ "EPERM",
124
+ "ENOSPC"
125
+ ]);
126
+ withLock = (key, effect) => {
127
+ const lockDir = path$1.join(rootDir, "lock");
128
+ const lockPath = path$1.join(lockDir, `${sanitizeLockKey(key)}.lock`);
129
+ return Effect.acquireUseRelease(Effect.promise(async () => {
130
+ try {
131
+ await fs.mkdir(lockDir, { recursive: true });
132
+ return await lock(lockPath, {
133
+ retries: {
134
+ retries: 2400,
135
+ minTimeout: 50,
136
+ maxTimeout: 50
137
+ },
138
+ stale: 3e4,
139
+ realpath: false,
140
+ onCompromised: (err) => {
141
+ console.warn(`auth lock compromised (continuing): ${err.message}`);
142
+ }
143
+ });
144
+ } catch (err) {
145
+ const code = err.code;
146
+ if (code !== void 0 && UNLOCKABLE_FS_CODES.has(code)) {
147
+ console.warn(`auth lock unavailable (${code} at '${lockPath}') — continuing without cross-process locking`);
148
+ return async () => {};
149
+ }
150
+ if (err instanceof Error && err.message.includes("already being held")) throw new Error(`Timed out waiting for the alchemy auth lock '${lockPath}' — another alchemy process has held it for over 120s. If no other alchemy process is running, delete the lock file and retry.`, { cause: err });
151
+ throw err;
152
+ }
153
+ }), () => effect, (release) => Effect.promise(() => release().catch(() => {})));
154
+ };
155
+ }) });
156
+
93
157
  //#endregion
94
158
  //#region lib/Auth/AuthProvider.js
95
- var AUTH_LANDING_HOST, AUTH_SUCCESS_URL, AUTH_ERROR_URL, interactiveMutex, AuthError, AuthProviders;
159
+ var AUTH_LANDING_HOST, AUTH_SUCCESS_URL, AUTH_ERROR_URL, LOCKED_METHODS, INTERACTIVE_METHODS, interactiveMutex, AuthError, AuthProviders, AuthProvider, AuthProviderLayer, getAuthProvider;
96
160
  var init_AuthProvider = __esm({ "lib/Auth/AuthProvider.js": (() => {
97
- AUTH_LANDING_HOST = "https://v2.alchemy.run";
161
+ init_Lock();
162
+ AUTH_LANDING_HOST = "https://alchemy.run";
98
163
  AUTH_SUCCESS_URL = `${AUTH_LANDING_HOST}/auth/success`;
99
164
  AUTH_ERROR_URL = `${AUTH_LANDING_HOST}/auth/error`;
165
+ LOCKED_METHODS = new Set([
166
+ "read",
167
+ "login",
168
+ "logout",
169
+ "configure"
170
+ ]);
171
+ INTERACTIVE_METHODS = new Set(["login", "configure"]);
100
172
  interactiveMutex = Semaphore.makeUnsafe(1);
101
173
  AuthError = class extends Schema.TaggedErrorClass()("AuthError", {
102
174
  message: Schema.String,
103
175
  cause: Schema.optional(Schema.Defect())
104
176
  }) {};
105
177
  AuthProviders = class extends EffectContext.Service()("AuthProviders") {};
178
+ AuthProvider = () => (name, impl) => Effect.gen(function* () {
179
+ const ctx = yield* Effect.context();
180
+ const providers = yield* AuthProviders;
181
+ const service = yield* Effect.isEffect(impl) ? impl : Effect.succeed(impl);
182
+ return yield* Effect.sync(() => providers[name] = {
183
+ kind: "AuthProvider",
184
+ name,
185
+ ...Object.fromEntries(Object.entries(service).map(([methodName, fn]) => [methodName, (...args) => {
186
+ let eff = fn(...args).pipe(Effect.provideContext(ctx));
187
+ if (LOCKED_METHODS.has(methodName)) {
188
+ const profileName = args[0];
189
+ eff = withLock(`${profileName}-${name}`, eff);
190
+ }
191
+ if (INTERACTIVE_METHODS.has(methodName)) eff = Semaphore.withPermits(interactiveMutex, 1)(eff);
192
+ return eff;
193
+ }]))
194
+ });
195
+ });
196
+ AuthProviderLayer = () => (name, impl) => Layer.effectDiscard(AuthProvider()(name, impl));
197
+ getAuthProvider = (name) => AuthProviders.use((registry) => registry[name] == null ? Effect.fail(new AuthError({ message: `AuthProvider '${name}' is not registered. Make sure its layer has been provided.` })) : Effect.succeed(registry[name]));
106
198
  }) });
107
199
 
108
200
  //#endregion
@@ -121,8 +213,8 @@ var init_Profile = __esm({ "lib/Auth/Profile.js": (() => {
121
213
  });
122
214
  AlchemyProfile = class extends EffectContext.Service()("Alchemy::Profile") {};
123
215
  ProfileLive = Layer.effect(AlchemyProfile, Effect.gen(function* () {
124
- const fs = yield* FileSystem.FileSystem;
125
- const readConfig = fs.readFileString(configFilePath).pipe(Effect.flatMap((data) => Effect.try({
216
+ const fs$1 = yield* FileSystem.FileSystem;
217
+ const readConfig = fs$1.readFileString(configFilePath).pipe(Effect.flatMap((data) => Effect.try({
126
218
  try: () => {
127
219
  const parsed = JSON.parse(data);
128
220
  if (parsed?.version !== CONFIG_VERSION) return emptyConfig();
@@ -130,7 +222,7 @@ var init_Profile = __esm({ "lib/Auth/Profile.js": (() => {
130
222
  },
131
223
  catch: emptyConfig
132
224
  })), Effect.orElseSucceed(emptyConfig));
133
- const writeConfig = (config) => fs.makeDirectory(path.dirname(configFilePath), { recursive: true }).pipe(Effect.flatMap(() => fs.writeFileString(configFilePath, JSON.stringify(config, null, 2))));
225
+ const writeConfig = (config) => fs$1.makeDirectory(path.dirname(configFilePath), { recursive: true }).pipe(Effect.flatMap(() => fs$1.writeFileString(configFilePath, JSON.stringify(config, null, 2))));
134
226
  const getProfile = (name) => readConfig.pipe(Effect.map((config) => config.profiles[name]));
135
227
  const setProfile = (name, profile$1) => readConfig.pipe(Effect.tap((config) => Effect.sync(() => config.profiles[name] = profile$1)), Effect.flatMap(writeConfig));
136
228
  const deleteProfile = (name) => readConfig.pipe(Effect.flatMap((config) => {
@@ -156,16 +248,21 @@ var init_Profile = __esm({ "lib/Auth/Profile.js": (() => {
156
248
  loadOrConfigure
157
249
  };
158
250
  }));
159
- withProfileOverride = (base, profile$1) => {
160
- if (profile$1 === void 0) return base;
251
+ withProfileOverride = (base$1, profile$1) => {
252
+ if (profile$1 === void 0) return base$1;
161
253
  const overrides = { ALCHEMY_PROFILE: profile$1 };
162
- const overrideProvider = ConfigProvider.make((path$1) => Effect.succeed(path$1.length === 1 && typeof path$1[0] === "string" && path$1[0] in overrides ? ConfigProvider.makeValue(overrides[path$1[0]]) : void 0));
163
- return ConfigProvider.orElse(base)(overrideProvider);
254
+ const overrideProvider = ConfigProvider.make((path$2) => Effect.succeed(path$2.length === 1 && typeof path$2[0] === "string" && path$2[0] in overrides ? ConfigProvider.makeValue(overrides[path$2[0]]) : void 0));
255
+ return ConfigProvider.orElse(base$1)(overrideProvider);
164
256
  };
165
257
  }) });
166
258
 
167
259
  //#endregion
168
260
  //#region lib/Auth/Credentials.js
261
+ function displayRedacted(r, visibleChars = 4) {
262
+ const raw = Redacted.value(r);
263
+ if (raw.length <= visibleChars) return "****";
264
+ return `${raw.slice(0, visibleChars)}****`;
265
+ }
169
266
  var credentialsDirPath, profileCredentialsDirPath, credentialsFilePath, CredentialsStore, CredentialsStoreLive;
170
267
  var init_Credentials = __esm({ "lib/Auth/Credentials.js": (() => {
171
268
  init_Profile();
@@ -174,8 +271,8 @@ var init_Credentials = __esm({ "lib/Auth/Credentials.js": (() => {
174
271
  credentialsFilePath = (profile$1, provider) => path.join(profileCredentialsDirPath(profile$1), `${provider}.json`);
175
272
  CredentialsStore = class extends EffectContext.Service()("Alchemy::CredentialsStore") {};
176
273
  CredentialsStoreLive = Layer.effect(CredentialsStore, Effect.gen(function* () {
177
- const fs = yield* FileSystem.FileSystem;
178
- const read = (profile$1, provider) => fs.readFileString(credentialsFilePath(profile$1, provider)).pipe(Effect.catch(() => Effect.succeed(void 0)), Effect.map((data) => {
274
+ const fs$1 = yield* FileSystem.FileSystem;
275
+ const read = (profile$1, provider) => fs$1.readFileString(credentialsFilePath(profile$1, provider)).pipe(Effect.catch(() => Effect.succeed(void 0)), Effect.map((data) => {
179
276
  if (data === void 0) return void 0;
180
277
  try {
181
278
  return JSON.parse(data);
@@ -185,12 +282,12 @@ var init_Credentials = __esm({ "lib/Auth/Credentials.js": (() => {
185
282
  }));
186
283
  const write = (profile$1, provider, credentials) => {
187
284
  const filePath = credentialsFilePath(profile$1, provider);
188
- return fs.makeDirectory(path.dirname(filePath), { recursive: true }).pipe(Effect.flatMap(() => {
189
- return fs.writeFileString(filePath, JSON.stringify(credentials, null, 2));
285
+ return fs$1.makeDirectory(path.dirname(filePath), { recursive: true }).pipe(Effect.flatMap(() => {
286
+ return fs$1.writeFileString(filePath, JSON.stringify(credentials, null, 2));
190
287
  }));
191
288
  };
192
- const remove_ = (profile$1, provider) => fs.remove(credentialsFilePath(profile$1, provider)).pipe(Effect.catch(() => Effect.void));
193
- const deleteProfile = (profile$1) => fs.remove(profileCredentialsDirPath(profile$1), { recursive: true }).pipe(Effect.catch(() => Effect.void));
289
+ const remove_ = (profile$1, provider) => fs$1.remove(credentialsFilePath(profile$1, provider)).pipe(Effect.catch(() => Effect.void));
290
+ const deleteProfile = (profile$1) => fs$1.remove(profileCredentialsDirPath(profile$1), { recursive: true }).pipe(Effect.catch(() => Effect.void));
194
291
  return {
195
292
  read,
196
293
  write,
@@ -276,13 +373,13 @@ var init_FQN = __esm({ "lib/FQN.js": (() => {
276
373
  FQN_SEPARATOR = "/";
277
374
  toPath = (ns) => {
278
375
  if (!ns) return [];
279
- const path$1 = [];
376
+ const path$2 = [];
280
377
  let current = ns;
281
378
  while (current) {
282
- path$1.unshift(current.Id);
379
+ path$2.unshift(current.Id);
283
380
  current = current.Parent;
284
381
  }
285
- return path$1;
382
+ return path$2;
286
383
  };
287
384
  parseFqn = (fqn) => {
288
385
  const parts = fqn.split(FQN_SEPARATOR);
@@ -387,7 +484,7 @@ function proxy(self) {
387
484
  });
388
485
  return proxy$1;
389
486
  }
390
- var inspect, of, asOutput, isOutput, ExprSymbol, exprKind, isExpr, BaseExpr, isResourceExpr, ResourceExpr, isPropExpr, PropExpr, isLiteralExpr, LiteralExpr, VoidExpr, isApplyExpr, ApplyExpr, isFlatMapExpr, FlatMapExpr, isEffectExpr, EffectExpr, isNamedExpr, NamedExpr, isAllExpr, AllExpr, isRefExpr, RefExpr, isStackRefExpr, StackRefExpr, stackRef, MissingSourceError, InvalidReferenceError, evaluate, hasOutputs, upstreamAny, upstream, resolveUpstream, toObject;
487
+ var inspect, of$1, asOutput, isOutput, ExprSymbol, exprKind, isExpr, BaseExpr, isResourceExpr, ResourceExpr, isPropExpr, PropExpr, isLiteralExpr, LiteralExpr, VoidExpr, isApplyExpr, ApplyExpr, isFlatMapExpr, FlatMapExpr, isEffectExpr, EffectExpr, isNamedExpr, NamedExpr, isAllExpr, AllExpr, isRefExpr, RefExpr, isStackRefExpr, StackRefExpr, stackRef, MissingSourceError, InvalidReferenceError, evaluate, hasOutputs, upstreamAny, upstream, resolveUpstream, toObject;
391
488
  var init_Output = __esm({ "lib/Output.js": (() => {
392
489
  init_Ref();
393
490
  init_Resource();
@@ -397,7 +494,7 @@ var init_Output = __esm({ "lib/Output.js": (() => {
397
494
  init_State();
398
495
  init_data();
399
496
  inspect = Symbol.for("nodejs.util.inspect.custom");
400
- of = (resource) => {
497
+ of$1 = (resource) => {
401
498
  if (isRef(resource)) {
402
499
  const metadata = getRefMetadata(resource);
403
500
  return new RefExpr(metadata.stack, metadata.stage, metadata.id, metadata.type !== void 0 ? { Type: metadata.type } : void 0);
@@ -582,7 +679,7 @@ var init_Output = __esm({ "lib/Output.js": (() => {
582
679
  return `stackRef(${this.stack}${this.stage ? `, { stage: ${this.stage} }` : ""})`;
583
680
  }
584
681
  };
585
- stackRef = (stack, options = {}) => Effect.succeed(new StackRefExpr(stack, options.stage));
682
+ stackRef = (stack, options$6 = {}) => Effect.succeed(new StackRefExpr(stack, options$6.stage));
586
683
  MissingSourceError = class extends Data.TaggedError("MissingSourceError") {};
587
684
  InvalidReferenceError = class extends Data.TaggedError("InvalidReferenceError") {};
588
685
  evaluate = (expr, upstream$1) => Effect.gen(function* () {
@@ -720,10 +817,10 @@ var init_FileLogger = __esm({ "lib/Util/FileLogger.js": (() => {
720
817
  init_AlchemyContext();
721
818
  fileLogger = Effect.fn(function* (...segments) {
722
819
  const { dotAlchemy } = yield* AlchemyContext;
723
- const fs = yield* FileSystem.FileSystem;
724
- const path$1 = yield* Path;
725
- const logFile = path$1.join(dotAlchemy, "log", ...segments);
726
- yield* fs.makeDirectory(path$1.dirname(logFile), { recursive: true });
820
+ const fs$1 = yield* FileSystem.FileSystem;
821
+ const path$2 = yield* Path;
822
+ const logFile = path$2.join(dotAlchemy, "log", ...segments);
823
+ yield* fs$1.makeDirectory(path$2.dirname(logFile), { recursive: true });
727
824
  return yield* Logger.formatLogFmt.pipe(Logger.toFile(logFile, { flag: "a" }));
728
825
  });
729
826
  }) });
@@ -747,9 +844,9 @@ var init_PlatformServices = __esm({ "lib/Util/PlatformServices.js": (() => {
747
844
  return Layer.merge(NodeServices.layer, NodeSocket.layerWebSocketConstructor);
748
845
  }
749
846
  });
750
- runMain = (effect, options) => {
751
- if (isBun) import("@effect/platform-bun/BunRuntime").then((BunRuntime) => BunRuntime.runMain(effect, options));
752
- else import("@effect/platform-node/NodeRuntime").then((NodeRuntime) => NodeRuntime.runMain(effect, options));
847
+ runMain = (effect, options$6) => {
848
+ if (isBun) import("@effect/platform-bun/BunRuntime").then((BunRuntime) => BunRuntime.runMain(effect, options$6));
849
+ else import("@effect/platform-node/NodeRuntime").then((NodeRuntime) => NodeRuntime.runMain(effect, options$6));
753
850
  };
754
851
  }) });
755
852
 
@@ -764,31 +861,31 @@ var init_Stack = __esm({ "lib/Stack.js": (() => {
764
861
  init_effect();
765
862
  init_FileLogger();
766
863
  init_PlatformServices();
767
- Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => {
864
+ Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options$6, eff) => {
768
865
  if (!stackName) return (stackName$1) => Object.assign(stackRef(stackName$1).pipe(effectClass), {
769
866
  stackName: stackName$1,
770
867
  stage: createStageProxy(stackName$1),
771
- state: options?.state,
772
- providers: options?.providers,
773
- make: (options$1, eff$1) => Stack(stackName$1, options$1, eff$1)
868
+ state: options$6?.state,
869
+ providers: options$6?.providers,
870
+ make: (options$7, eff$1) => Stack(stackName$1, options$7, eff$1)
774
871
  });
775
872
  return eff.pipe(make$2({
776
873
  name: stackName,
777
- ...options
874
+ ...options$6
778
875
  }), (eff$1) => Object.assign(eff$1, {
779
876
  stackName,
780
877
  stage: createStageProxy(stackName),
781
- state: options?.state,
782
- providers: options?.providers
878
+ state: options$6?.state,
879
+ providers: options$6?.providers
783
880
  }));
784
881
  }));
785
882
  createStageProxy = (stackName) => new Proxy({}, { get: (_, stage$1) => stackRef(stackName, { stage: stage$1 }) });
786
883
  StackName = Stack.use((stack) => Effect.succeed(stack.name));
787
- make$2 = (options) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
788
- if (options.state == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a state store. Add a \`state\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(),\n state: Cloudflare.state(), // <-- required\n }, ...)\nSee https://v2.alchemy.run/state-store for available state stores.`));
789
- if (options.providers == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a providers layer. Add a \`providers\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(), // <-- required\n state: Cloudflare.state(),\n }, ...)`));
790
- return options.providers.pipe(Layer.provideMerge(options.state), Layer.provideMerge(Layer.effect(Stack, Stage.pipe(Effect.map((stage$1) => options.stack ?? {
791
- name: options.name,
884
+ make$2 = (options$6) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
885
+ if (options$6.state == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options$6.name}" is missing a state store. Add a \`state\` layer to the stack options, e.g.:\n Alchemy.Stack("${options$6.name}", {\n providers: Cloudflare.providers(),\n state: Cloudflare.state(), // <-- required\n }, ...)\nSee https://alchemy.run/state-store for available state stores.`));
886
+ if (options$6.providers == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options$6.name}" is missing a providers layer. Add a \`providers\` layer to the stack options, e.g.:\n Alchemy.Stack("${options$6.name}", {\n providers: Cloudflare.providers(), // <-- required\n state: Cloudflare.state(),\n }, ...)`));
887
+ return options$6.providers.pipe(Layer.provideMerge(options$6.state), Layer.provideMerge(Layer.effect(Stack, Stage.pipe(Effect.map((stage$1) => options$6.stack ?? {
888
+ name: options$6.name,
792
889
  stage: stage$1,
793
890
  resources: {},
794
891
  bindings: {},
@@ -876,6 +973,44 @@ const make$1 = Effect.fn(function* (spawnerUrl) {
876
973
  });
877
974
  const fromEnv = () => Layer.effect(RpcProviderProxy, Config.string(SPAWNER_URL_ENV_KEY).pipe(Effect.flatMap(make$1)));
878
975
 
976
+ //#endregion
977
+ //#region lib/ActionRuntimeContext.js
978
+ /**
979
+ * Runtime context bridge that lets an {@link Action} read Resource Outputs.
980
+ *
981
+ * Actions are peculiar: their init Effect runs at plan/stack-eval time (before
982
+ * any resource exists) while their body runs at apply time (after upstreams are
983
+ * materialized). To make `yield* db.databaseId` work inside an Action we split
984
+ * the {@link RuntimeContext} into two cooperating halves that share nothing but
985
+ * a sanitized key:
986
+ *
987
+ * - {@link makeCaptureContext} is provided while the init Effect runs. Its
988
+ * `set(key, output)` records the referenced {@link Output} (so the engine can
989
+ * add a dependency edge and later resolve it) and its `get(key)` hands back a
990
+ * *deferred* accessor — an Effect that re-reads whatever `RuntimeContext` is
991
+ * ambient when it actually runs.
992
+ * - {@link makeResolveContext} is provided around the Action body at apply
993
+ * time. Its `get(key)` returns the already-resolved value for that key.
994
+ *
995
+ * Because the accessor produced at init re-reads `RuntimeContext`, yielding it
996
+ * inside the body resolves against the apply-time context — no shared mutable
997
+ * state, no infinite recursion (the resolve context's `get` is terminal).
998
+ */
999
+ const base = (id) => ({
1000
+ Type: "Action",
1001
+ id,
1002
+ env: {}
1003
+ });
1004
+ /**
1005
+ * Resolve context — provided around an Action body at apply time. `resolved`
1006
+ * maps each captured key to its value (already evaluated against the tracker).
1007
+ */
1008
+ const makeResolveContext = (resolved) => ({
1009
+ ...base("resolve"),
1010
+ set: (key) => Effect.succeed(key),
1011
+ get: (key) => Effect.succeed(resolved[key])
1012
+ });
1013
+
879
1014
  //#endregion
880
1015
  //#region lib/Diff.js
881
1016
  init_Output();
@@ -888,6 +1023,30 @@ const _hasUnresolved = (value) => {
888
1023
  if (typeof value === "object") return Object.values(value).some(_hasUnresolved);
889
1024
  return false;
890
1025
  };
1026
+ /**
1027
+ * Deeply replace every unresolved plan-time expression (an `Output`/`Expr`
1028
+ * or an un-evaluated `Effect`) with `undefined`, leaving resolved values
1029
+ * (including opaque `Redacted`/`Duration` instances) intact.
1030
+ *
1031
+ * Persisted resource state must only ever hold plain data. Durable (JSON)
1032
+ * state stores already enforce this implicitly — Output proxies are
1033
+ * function-typed, so `JSON.stringify` silently drops them — but the
1034
+ * in-memory store used by tests retains live proxies, which would later be
1035
+ * fed back into provider lifecycle operations as `olds` after an
1036
+ * interrupted apply (e.g. `read` during a destroy plan) and blow up on
1037
+ * first string coercion. Sanitizing at the commit boundary keeps both
1038
+ * store kinds consistent with the provider contract that `olds` is
1039
+ * resolved `Props`.
1040
+ */
1041
+ const stripUnresolved = (value) => _stripUnresolved(value);
1042
+ const _stripUnresolved = (value) => {
1043
+ if (value == null || isPrimitive(value)) return value;
1044
+ if (isExpr(value) || Effect.isEffect(value)) return void 0;
1045
+ if (Redacted.isRedacted(value) || Duration.isDuration(value)) return value;
1046
+ if (Array.isArray(value)) return value.map(_stripUnresolved);
1047
+ if (typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, _stripUnresolved(item)]));
1048
+ return value;
1049
+ };
891
1050
  const havePropsChanged = (oldProps, newProps) => hasOutputs(newProps) || JSON.stringify(canonicalize(oldProps ?? {}, false)) !== JSON.stringify(canonicalize(newProps ?? {}, false));
892
1051
  const canonicalize = (value, stripNullish) => {
893
1052
  if (stripNullish && value == null) return void 0;
@@ -1133,7 +1292,7 @@ init_Output();
1133
1292
  init_Provider();
1134
1293
  init_Resource();
1135
1294
  init_sha256();
1136
- const make = (stack, options = {}) => Effect.gen(function* () {
1295
+ const make = (stack, options$6 = {}) => Effect.gen(function* () {
1137
1296
  const state = yield* yield* State;
1138
1297
  const resources = Object.values(stack.resources);
1139
1298
  const actions = Object.values(stack.actions ?? {});
@@ -1201,7 +1360,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1201
1360
  else if (Duration.isDuration(input) || Redacted.isRedacted(input)) return input;
1202
1361
  else if (Array.isArray(input)) return yield* Effect.all(input.map(resolveInput), { concurrency: "unbounded" });
1203
1362
  else if (isResource(input)) {
1204
- const resolved = yield* resolveOutput(of(input));
1363
+ const resolved = yield* resolveOutput(of$1(input));
1205
1364
  if (isResourceExpr(resolved) && resolved.stables) return yield* resolveInput(resolved.stables);
1206
1365
  return yield* resolveInput(resolved);
1207
1366
  } else if (typeof input === "object") return Object.fromEntries(yield* Effect.all(Object.entries(input).map(([key, value]) => resolveInput(value).pipe(Effect.map((value$1) => [key, value$1]))), { concurrency: "unbounded" }));
@@ -1257,7 +1416,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1257
1416
  const newResourceFqns = new Set(resources.map((r) => r.FQN));
1258
1417
  const newActionFqns = new Set(actions.map((t) => t.FQN));
1259
1418
  const newNodeFqns = new Set([...newResourceFqns, ...newActionFqns]);
1260
- const newUpstreamDependencies = Object.fromEntries([...resources.map((resource) => [resource.FQN, Object.values(upstreamAny(resource.Props)).map((r) => r.FQN)]), ...actions.map((action) => [action.FQN, Object.values(upstreamAny(action.Input)).map((r) => r.FQN)])]);
1419
+ const newUpstreamDependencies = Object.fromEntries([...resources.map((resource) => [resource.FQN, Object.values(upstreamAny(resource.Props)).map((r) => r.FQN)]), ...actions.map((action) => [action.FQN, Array.from(new Set([...Object.values(upstreamAny(action.Input)).map((r) => r.FQN), ...Object.values(upstreamAny(action.Captures)).map((r) => r.FQN)]))])]);
1261
1420
  const bindingUpstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.values(upstreamAny(stack.bindings[resource.FQN] ?? [])).map((r) => r.FQN)]));
1262
1421
  const rawUpstreamDependencies = Object.fromEntries([...resources.map((resource) => {
1263
1422
  const fqn = resource.FQN;
@@ -1382,7 +1541,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1382
1541
  news,
1383
1542
  oldBindings,
1384
1543
  newBindings
1385
- }).pipe(providePlanScope(fqn, oldState.instanceId))).pipe(Effect.map((diff$1) => diff$1 ?? { action: havePropsChanged(oldProps, news) || bindingDiffs.some((b) => b.action !== "noop") ? "update" : "noop" }), Effect.map((diff$1) => options.force && diff$1.action === "noop" ? { action: "update" } : diff$1), Effect.map((diff$1) => forceUpdateAfterAdoption && diff$1.action === "noop" ? { action: "update" } : diff$1));
1544
+ }).pipe(providePlanScope(fqn, oldState.instanceId))).pipe(Effect.map((diff$1) => diff$1 ?? { action: havePropsChanged(oldProps, news) || bindingDiffs.some((b) => b.action !== "noop") ? "update" : "noop" }), Effect.map((diff$1) => options$6.force && diff$1.action === "noop" ? { action: "update" } : diff$1), Effect.map((diff$1) => forceUpdateAfterAdoption && diff$1.action === "noop" ? { action: "update" } : diff$1));
1386
1545
  if (oldState.status === "creating") if (diff.action === "noop") return Node({
1387
1546
  action: "create",
1388
1547
  props: news,
@@ -1491,7 +1650,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1491
1650
  forced: false
1492
1651
  }];
1493
1652
  const prior = oldState;
1494
- if (prior?.status === "ran" && prior.inputHash === inputHash && !options.force) return [fqn, {
1653
+ if (prior?.status === "ran" && prior.inputHash === inputHash && !options$6.force) return [fqn, {
1495
1654
  kind: "action",
1496
1655
  action: "noop",
1497
1656
  def: action,
@@ -1505,7 +1664,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1505
1664
  input: action.Input,
1506
1665
  state: prior,
1507
1666
  downstream,
1508
- forced: !!options.force
1667
+ forced: !!options$6.force
1509
1668
  }];
1510
1669
  })), { concurrency: "unbounded" }));
1511
1670
  const cycleMembers = combinedCycleMembers;
@@ -1560,6 +1719,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1560
1719
  LogicalId: logicalId,
1561
1720
  Type: persisted.actionType,
1562
1721
  Input: persisted.input,
1722
+ Captures: {},
1563
1723
  Run: () => void 0,
1564
1724
  Output: void 0
1565
1725
  }
@@ -1641,7 +1801,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1641
1801
  "alchemy.stack": stack.name,
1642
1802
  "alchemy.stage": stack.stage,
1643
1803
  "alchemy.resources.count": Object.keys(stack.resources).length,
1644
- "alchemy.force": !!options.force
1804
+ "alchemy.force": !!options$6.force
1645
1805
  } }));
1646
1806
  const providePlanScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
1647
1807
  var DeleteResourceHasDownstreamDependencies = class extends Data.TaggedError("DeleteResourceHasDownstreamDependencies") {};
@@ -1649,6 +1809,7 @@ var UnsatisfiedResourceCycle = class extends Data.TaggedError("UnsatisfiedResour
1649
1809
 
1650
1810
  //#endregion
1651
1811
  //#region lib/Apply.js
1812
+ init_RuntimeContext();
1652
1813
  init_Artifacts();
1653
1814
  init_Cli();
1654
1815
  init_Output();
@@ -1728,6 +1889,7 @@ const executeNode = (fqn, node, tracker, ready, readyStable, terminalStatuses, s
1728
1889
  fqn,
1729
1890
  value: {
1730
1891
  ...value,
1892
+ props: stripUnresolved(value.props),
1731
1893
  namespace
1732
1894
  }
1733
1895
  });
@@ -2148,6 +2310,16 @@ const executeNode = (fqn, node, tracker, ready, readyStable, terminalStatuses, s
2148
2310
  "alchemy.resource.logical_id": node.resource.LogicalId,
2149
2311
  "alchemy.resource.action": node.action
2150
2312
  } }));
2313
+ /**
2314
+ * Run an Action body, resolving any Outputs it captured via `yield* output`
2315
+ * during init against the current tracker and exposing them to the body through
2316
+ * the resolve {@link RuntimeContext}. See {@link makeCaptureContext}.
2317
+ */
2318
+ const runAction = Effect.fn("apply.runAction")(function* (task, input, outputs) {
2319
+ const resolved = {};
2320
+ for (const [key, output] of Object.entries(task.Captures)) resolved[key] = yield* evaluate(output, outputs);
2321
+ return yield* task.Run(input).pipe(Effect.provideService(RuntimeContext, makeResolveContext(resolved)));
2322
+ });
2151
2323
  const executeActionNode = (fqn, node, tracker, ready, readyStable, terminalStatuses, session, state, stackName, stage$1, getOutputs, waitForDeps, failures) => Effect.gen(function* () {
2152
2324
  const task = node.def;
2153
2325
  const logicalId = task.LogicalId;
@@ -2187,7 +2359,7 @@ const executeActionNode = (fqn, node, tracker, ready, readyStable, terminalStatu
2187
2359
  return;
2188
2360
  }
2189
2361
  yield* report("pending");
2190
- yield* waitForDeps(Object.keys(resolveUpstream(node.input)).filter((f) => f in readyStable));
2362
+ yield* waitForDeps([...new Set([...Object.keys(resolveUpstream(node.input)), ...Object.keys(upstreamAny(task.Captures))])].filter((f) => f in readyStable));
2191
2363
  const outputs = getOutputs();
2192
2364
  const resolvedInput = yield* evaluate(node.input, outputs);
2193
2365
  const inputHashValue = yield* hashInput(resolvedInput);
@@ -2202,7 +2374,7 @@ const executeActionNode = (fqn, node, tracker, ready, readyStable, terminalStatu
2202
2374
  downstream: node.downstream
2203
2375
  });
2204
2376
  yield* report("running");
2205
- const result = yield* task.Run(resolvedInput);
2377
+ const result = yield* runAction(task, resolvedInput, outputs);
2206
2378
  yield* commit({
2207
2379
  kind: "action",
2208
2380
  status: "ran",
@@ -2341,7 +2513,7 @@ const converge = Effect.fn(function* (plan, tracker, terminalStatuses, session,
2341
2513
  downstream: node.downstream
2342
2514
  }
2343
2515
  });
2344
- const result = yield* node.def.Run(newInput);
2516
+ const result = yield* runAction(node.def, newInput, outputs);
2345
2517
  yield* state.set({
2346
2518
  stack: stackName,
2347
2519
  stage: stage$1,
@@ -2429,6 +2601,7 @@ const collectGarbage = Effect.fn(function* (plan, session) {
2429
2601
  fqn,
2430
2602
  value: {
2431
2603
  ...value,
2604
+ props: stripUnresolved(value.props),
2432
2605
  namespace
2433
2606
  }
2434
2607
  });
@@ -2546,6 +2719,1709 @@ const excludeDeletedBindings = (bindings) => bindings.flatMap(({ action, sid, da
2546
2719
  data
2547
2720
  }]);
2548
2721
 
2722
+ //#endregion
2723
+ //#region lib/Util/Clank.js
2724
+ var PromptCancelled = class extends Data.TaggedError("PromptCancelled") {};
2725
+ const retryIfCancelled = Effect.retry({ while: (e) => e instanceof PromptCancelled });
2726
+ /**
2727
+ * Wraps a clack prompt (which returns a `Promise<T | symbol>` where the
2728
+ * symbol indicates user cancellation) in an Effect.
2729
+ *
2730
+ * Returns `undefined` if the user cancels (Ctrl+C / Escape).
2731
+ *
2732
+ * Uses `Effect.callback` so fiber interruption propagates via the abort
2733
+ * signal to any async resources we own; the clack prompt itself is left
2734
+ * to resolve — its result is ignored after interruption.
2735
+ */
2736
+ const prompt = (fn) => Effect.callback((resume, signal) => {
2737
+ let settled = false;
2738
+ fn().then((result) => {
2739
+ if (settled || signal.aborted) return;
2740
+ settled = true;
2741
+ if (p.isCancel(result)) resume(Effect.fail(new PromptCancelled()));
2742
+ else resume(Effect.succeed(result));
2743
+ }, (err) => {
2744
+ if (settled || signal.aborted) return;
2745
+ settled = true;
2746
+ resume(Effect.die(err));
2747
+ });
2748
+ });
2749
+ const success = (str) => Effect.sync(() => p.log.success(str));
2750
+ const warn = (str) => Effect.sync(() => p.log.warn(str));
2751
+ const info = (str) => Effect.sync(() => p.log.info(str));
2752
+ const text = (opts) => prompt(() => p.text(opts));
2753
+ const password = (opts) => prompt(() => p.password(opts));
2754
+ const select = (opts) => prompt(() => p.select(opts));
2755
+ const confirm = (opts) => prompt(() => p.confirm(opts));
2756
+ const multiselect = (opts) => prompt(() => p.multiselect(opts));
2757
+ /**
2758
+ * Open a URL in the user's default browser.
2759
+ *
2760
+ * On Windows, uses rundll32's FileProtocolHandler — a built-in shim that
2761
+ * opens URLs in the default browser. It accepts the URL as a direct
2762
+ * argument (no shell, no quoting of `&`). cmd.exe `start` would treat
2763
+ * `&` in OAuth URLs as a command separator, and `explorer.exe` treats
2764
+ * its arg as a path.
2765
+ */
2766
+ const openUrl = (url) => Effect.gen(function* () {
2767
+ const [cmd, args] = process.platform === "win32" ? ["rundll32.exe", ["url.dll,FileProtocolHandler", url]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
2768
+ yield* (yield* ChildProcess.make(cmd, [...args], { shell: false })).exitCode;
2769
+ }).pipe(Effect.scoped);
2770
+
2771
+ //#endregion
2772
+ //#region lib/Auth/Env.js
2773
+ init_AuthProvider();
2774
+ const getEnv = (key) => Config.string(key).pipe(Effect.orElseSucceed(() => void 0));
2775
+ const getEnvRequired = (key) => Config.string(key).pipe(Effect.catch(() => Effect.fail(new AuthError({ message: `Missing required env: ${key}` }))));
2776
+ const getEnvRedacted = (key) => Config.redacted(key).pipe(Effect.orElseSucceed(() => void 0));
2777
+ const getEnvRedactedRequired = (key) => Config.redacted(key).pipe(Effect.catch(() => Effect.fail(new AuthError({ message: `Missing required env: ${key}` }))));
2778
+ const retryOnce = (self) => self.pipe(Effect.retry({
2779
+ times: 1,
2780
+ while: (e) => e instanceof PromptCancelled
2781
+ }), Effect.mapError((e) => new AuthError({
2782
+ message: "User cancelled prompt",
2783
+ cause: e
2784
+ })));
2785
+
2786
+ //#endregion
2787
+ //#region lib/AWS/Environment.js
2788
+ init_AuthProvider();
2789
+ init_Profile();
2790
+ const AWS_PROFILE = Config.string("AWS_PROFILE").pipe(Config.withDefault("default"));
2791
+ const AWS_REGION = Config.string("AWS_REGION");
2792
+ const AWS_ACCOUNT_ID = Config.string("AWS_ACCOUNT_ID");
2793
+ const AWS_ACCESS_KEY_ID = Config.string("AWS_ACCESS_KEY_ID");
2794
+ const AWS_SECRET_ACCESS_KEY = Config.redacted("AWS_SECRET_ACCESS_KEY");
2795
+ const AWS_SESSION_TOKEN = Config.redacted("AWS_SESSION_TOKEN");
2796
+ var AWSEnvironment = class AWSEnvironment extends EffectContext.Service()("AWS::Environment") {
2797
+ static current = AWSEnvironment.use((env) => env);
2798
+ kind = "Environment";
2799
+ };
2800
+ const Default = Layer.effect(AWSEnvironment, Effect.gen(function* () {
2801
+ const profile$1 = yield* AlchemyProfile;
2802
+ const auth = yield* getAuthProvider(AWS_AUTH_PROVIDER_NAME);
2803
+ const profileName = yield* ALCHEMY_PROFILE;
2804
+ const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
2805
+ return yield* profile$1.loadOrConfigure(auth, profileName, { ci }).pipe(Effect.flatMap((config) => auth.read(profileName, config)), Effect.orDie, Effect.cached);
2806
+ })).pipe(Layer.orDie);
2807
+
2808
+ //#endregion
2809
+ //#region lib/AWS/Region.js
2810
+ const of = (region) => Layer.succeed(Region.Region, Effect.succeed(region));
2811
+ const CurrentRegion = Effect.suspend(() => AWSEnvironment.use((env) => Effect.flatMap(env, ({ region }) => Effect.succeed(region))));
2812
+ /**
2813
+ * Derive the AWS region from the surrounding {@link AWSEnvironment}.
2814
+ */
2815
+ const fromEnvironment = Layer.effect(Region.Region, Effect.gen(function* () {
2816
+ const env = yield* AWSEnvironment;
2817
+ return Effect.map(env, (env$1) => env$1.region);
2818
+ }));
2819
+
2820
+ //#endregion
2821
+ //#region lib/AWS/AuthProvider.js
2822
+ init_AuthProvider();
2823
+ init_Credentials();
2824
+ const AWS_AUTH_PROVIDER_NAME = "AWS";
2825
+ const options$5 = [
2826
+ {
2827
+ value: "sso",
2828
+ label: "SSO",
2829
+ hint: "aws sso login — credentials loaded from AWS SSO cache"
2830
+ },
2831
+ {
2832
+ value: "env",
2833
+ label: "Environment Variables",
2834
+ hint: "AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY"
2835
+ },
2836
+ {
2837
+ value: "stored",
2838
+ label: "Stored",
2839
+ hint: "stored in ~/.alchemy/credentials"
2840
+ }
2841
+ ];
2842
+ /**
2843
+ * Layer that registers the AWS {@link AuthProvider} into the
2844
+ * {@link AuthProviders} registry when built. Include this in the AWS
2845
+ * `providers()` layer so `alchemy login` can discover it.
2846
+ */
2847
+ const AwsAuth = AuthProviderLayer()(AWS_AUTH_PROVIDER_NAME, Effect.gen(function* () {
2848
+ const store = yield* CredentialsStore;
2849
+ const getAccountId = ({ accessKeyId, secretAccessKey, sessionToken, region }) => STS.getCallerIdentity({}).pipe(Effect.provide(Layer.mergeAll(Layer.succeed(Credentials, Effect.succeed({
2850
+ accessKeyId,
2851
+ secretAccessKey,
2852
+ sessionToken
2853
+ })), of(region))), Effect.flatMap((self) => self.Account ? Effect.succeed(self.Account) : Effect.die(/* @__PURE__ */ new Error("No account ID found"))));
2854
+ const loginStored = Effect.fn(function* (profileName) {
2855
+ const accessKeyId = yield* text({
2856
+ message: "AWS Access Key ID",
2857
+ validate: (v) => v.length === 0 ? "Required" : void 0
2858
+ }).pipe(retryOnce);
2859
+ const secretAccessKey = yield* password({
2860
+ message: "AWS Secret Access Key",
2861
+ validate: (v) => v.length === 0 ? "Required" : void 0
2862
+ }).pipe(retryOnce);
2863
+ const sessionToken = yield* text({
2864
+ message: "AWS Session Token (optional — press Enter or Esc to skip)",
2865
+ placeholder: "(none)"
2866
+ }).pipe(Effect.catch(() => Effect.succeed("")));
2867
+ const region = yield* text({
2868
+ message: "AWS Region",
2869
+ placeholder: "us-east-1",
2870
+ defaultValue: "us-east-1"
2871
+ }).pipe(retryOnce);
2872
+ const accountId = yield* getAccountId({
2873
+ accessKeyId: Redacted.make(accessKeyId),
2874
+ secretAccessKey: Redacted.make(secretAccessKey),
2875
+ sessionToken: sessionToken ? Redacted.make(sessionToken) : void 0,
2876
+ region
2877
+ });
2878
+ yield* store.write(profileName, "aws", {
2879
+ accountId,
2880
+ accessKeyId,
2881
+ secretAccessKey,
2882
+ sessionToken,
2883
+ region
2884
+ });
2885
+ yield* success("AWS credentials saved.");
2886
+ return { method: "stored" };
2887
+ });
2888
+ const configureInteractive = (profileName) => select({
2889
+ message: "AWS authentication method",
2890
+ options: options$5
2891
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("sso", () => Effect.gen(function* () {
2892
+ const config = {
2893
+ method: "sso",
2894
+ ssoProfile: (yield* text({
2895
+ message: "AWS profile name (from ~/.aws/config)",
2896
+ placeholder: "default",
2897
+ defaultValue: "default"
2898
+ })) ?? "default"
2899
+ };
2900
+ yield* loginSSO(config);
2901
+ return config;
2902
+ })), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
2903
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
2904
+ if (ctx.ci) return { method: "env" };
2905
+ return yield* configureInteractive(profileName);
2906
+ }).pipe(Effect.mapError((e) => new AuthError({
2907
+ message: "failed to configure credentials",
2908
+ cause: e
2909
+ })));
2910
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
2911
+ const accessKeyId = yield* getEnvRedactedRequired("AWS_ACCESS_KEY_ID");
2912
+ const secretAccessKey = yield* getEnvRedactedRequired("AWS_SECRET_ACCESS_KEY");
2913
+ const sessionToken = yield* getEnvRedacted("AWS_SESSION_TOKEN");
2914
+ const region = yield* getEnv("AWS_REGION").pipe(Effect.flatMap((region$1) => region$1 ? Effect.succeed(region$1) : getEnv("AWS_DEFAULT_REGION")));
2915
+ if (!region) return yield* Effect.fail(new AuthError({ message: "AWS region not found. Set AWS_REGION or AWS_DEFAULT_REGION." }));
2916
+ return {
2917
+ accountId: yield* getEnvRequired("AWS_ACCOUNT_ID").pipe(Effect.catch(() => getAccountId({
2918
+ accessKeyId,
2919
+ secretAccessKey,
2920
+ sessionToken,
2921
+ region
2922
+ }))),
2923
+ credentials: Effect.succeed({
2924
+ accessKeyId,
2925
+ secretAccessKey,
2926
+ sessionToken
2927
+ }),
2928
+ region,
2929
+ source: { type: "env" }
2930
+ };
2931
+ })), Match.when({ method: "stored" }, () => store.read(profileName, "aws").pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "AWS stored credentials not found. Run: alchemy login --configure" })) : Effect.succeed({
2932
+ accountId: creds.accountId,
2933
+ credentials: Effect.succeed({
2934
+ accessKeyId: Redacted.make(creds.accessKeyId),
2935
+ secretAccessKey: Redacted.make(creds.secretAccessKey),
2936
+ sessionToken: creds.sessionToken ? Redacted.make(creds.sessionToken) : void 0
2937
+ }),
2938
+ region: creds.region,
2939
+ source: { type: "stored" }
2940
+ })), Effect.flatMap((creds) => creds.accountId ? Effect.succeed(creds) : creds.credentials.pipe(Effect.flatMap((resolved) => getAccountId({
2941
+ accessKeyId: resolved.accessKeyId,
2942
+ secretAccessKey: resolved.secretAccessKey,
2943
+ sessionToken: resolved.sessionToken,
2944
+ region: creds.region
2945
+ })), Effect.map((accountId) => ({
2946
+ ...creds,
2947
+ accountId
2948
+ })), Effect.tap((creds$1) => creds$1.credentials.pipe(Effect.tap(({ accessKeyId, secretAccessKey, sessionToken }) => store.write(profileName, "aws", {
2949
+ accessKeyId: Redacted.value(accessKeyId),
2950
+ secretAccessKey: Redacted.value(secretAccessKey),
2951
+ sessionToken: sessionToken ? Redacted.value(sessionToken) : void 0,
2952
+ region: creds$1.region,
2953
+ accountId: creds$1.accountId
2954
+ })))))))), Match.when({ method: "sso" }, (config$1) => Effect.gen(function* () {
2955
+ const auth = yield* DistilledAuth.Default;
2956
+ const profile$1 = yield* auth.loadProfile(config$1.ssoProfile).pipe(Effect.catch(() => Effect.succeed(void 0)));
2957
+ return {
2958
+ accountId: profile$1?.sso_account_id,
2959
+ credentials: auth.loadProfileCredentials(config$1.ssoProfile).pipe(Effect.mapError((e) => new AuthError({
2960
+ message: "failed to load credentials",
2961
+ cause: e
2962
+ })), Effect.orDie),
2963
+ region: profile$1?.region,
2964
+ source: {
2965
+ type: "sso",
2966
+ details: config$1.ssoProfile
2967
+ }
2968
+ };
2969
+ })), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
2970
+ message: "login failed",
2971
+ cause: e
2972
+ })));
2973
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap(Effect.fn(function* (creds) {
2974
+ const { accessKeyId, secretAccessKey, sessionToken } = yield* creds.credentials;
2975
+ yield* Console.log(` accessKeyId: ${displayRedacted(accessKeyId)}`);
2976
+ yield* Console.log(` secretAccessKey: ${displayRedacted(secretAccessKey)}`);
2977
+ if (sessionToken) yield* Console.log(` sessionToken: ${displayRedacted(sessionToken)}`);
2978
+ if (creds.region) yield* Console.log(` region: ${creds.region}`);
2979
+ yield* Console.log(` source: ${creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type}`);
2980
+ })));
2981
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "sso" }, (config$1) => info(`AWS: running 'aws sso logout --profile ${config$1.ssoProfile}'...`).pipe(Effect.zip(runSsoCommand("logout", config$1.ssoProfile)), Effect.zip(clearDistilledSsoCache(config$1.ssoProfile)), Effect.match({
2982
+ onSuccess: () => success("AWS: SSO logout complete"),
2983
+ onFailure: (e) => warn(`AWS: SSO logout failed: \`${e.message}\``)
2984
+ }))), Match.when({ method: "stored" }, () => store.delete(profileName, "aws").pipe(Effect.andThen(success("AWS: stored credentials removed")))), Match.exhaustive);
2985
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "sso" }, loginSSO), Match.when({ method: "stored" }, () => store.read(profileName, "aws").pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
2986
+ message: "login failed",
2987
+ cause: e
2988
+ })));
2989
+ return {
2990
+ configure: configureCredentials,
2991
+ login,
2992
+ logout,
2993
+ prettyPrint,
2994
+ read: resolveCredentials
2995
+ };
2996
+ }));
2997
+ const runSsoCommand = (command, ssoProfile) => Effect.gen(function* () {
2998
+ const exit = yield* (yield* ChildProcess.make("aws", [
2999
+ "sso",
3000
+ command,
3001
+ "--profile",
3002
+ ssoProfile
3003
+ ], {
3004
+ shell: false,
3005
+ stdin: "inherit",
3006
+ stdout: "inherit",
3007
+ stderr: "inherit"
3008
+ })).exitCode;
3009
+ if (exit !== 0) return yield* new AuthError({ message: `aws sso ${command} exited with code ${exit}` });
3010
+ }).pipe(Effect.scoped);
3011
+ const loginSSO = (config) => info(`AWS SSO: running 'aws sso login --profile ${config.ssoProfile}'...`).pipe(Effect.andThen(runSsoCommand("login", config.ssoProfile)), Effect.matchEffect({
3012
+ onSuccess: () => success("AWS SSO: login complete"),
3013
+ onFailure: (e) => warn(`AWS SSO: login faield: \`${e.message}\``)
3014
+ }));
3015
+ /**
3016
+ * `aws sso logout` only clears AWS CLI's own caches — it does not know about the
3017
+ * `<sha1(sso_session)>.credentials.json` file that `@distilled.cloud/aws`
3018
+ * writes alongside the SSO token. Without this cleanup, `loadProfileCredentials`
3019
+ * short-circuits on the stale distilled cache file after logout and appears to
3020
+ * stay logged in until the role creds hit their TTL.
3021
+ */
3022
+ const clearDistilledSsoCache = (ssoProfile) => Effect.gen(function* () {
3023
+ const ssoSession = (yield* (yield* DistilledAuth.Default).loadProfile(ssoProfile)).sso_session;
3024
+ if (!ssoSession) return;
3025
+ const fs$1 = yield* FileSystem.FileSystem;
3026
+ const path$2 = yield* Path$1.Path;
3027
+ const hash = NodeCrypto.createHash("sha1").update(ssoSession).digest("hex");
3028
+ const cacheFile = path$2.join(NodeOs.homedir(), ".aws", "sso", "cache", `${hash}.credentials.json`);
3029
+ yield* fs$1.remove(cacheFile).pipe(Effect.catch(() => Effect.void));
3030
+ }).pipe(Effect.catch(() => Effect.void));
3031
+
3032
+ //#endregion
3033
+ //#region lib/Axiom/AuthProvider.js
3034
+ init_AuthProvider();
3035
+ init_Credentials();
3036
+ const STORAGE_KEY = "axiom-stored";
3037
+ const options$4 = [{
3038
+ value: "env",
3039
+ label: "Environment Variables",
3040
+ hint: "AXIOM_TOKEN (or AXIOM_API_KEY) + optional AXIOM_ORG_ID, AXIOM_URL"
3041
+ }, {
3042
+ value: "stored",
3043
+ label: "API Token or Personal Access Token",
3044
+ hint: "enter credentials interactively, stored in ~/.alchemy/credentials"
3045
+ }];
3046
+ const AXIOM_AUTH_PROVIDER_NAME = "Axiom";
3047
+ const promptOrgId = (required) => getEnv("AXIOM_ORG_ID").pipe(Effect.flatMap((envOrgId) => text({
3048
+ message: required ? "Axiom Org ID (required for PAT)" : "Axiom Org ID (Enter to skip)",
3049
+ placeholder: envOrgId ?? "",
3050
+ defaultValue: envOrgId ?? "",
3051
+ validate: required ? (v) => v.length === 0 ? "Required" : void 0 : void 0
3052
+ }).pipe(retryOnce)));
3053
+ /**
3054
+ * Layer that registers the Axiom {@link AuthProvider} into the
3055
+ * {@link AuthProviders} registry when built. Include this in the Axiom
3056
+ * `providers()` layer so `alchemy login` can discover it.
3057
+ */
3058
+ const AxiomAuth = AuthProviderLayer()(AXIOM_AUTH_PROVIDER_NAME, Effect.gen(function* () {
3059
+ const store = yield* CredentialsStore;
3060
+ const loginStored = Effect.fn(function* (profileName) {
3061
+ const credentialType = yield* select({
3062
+ message: "Axiom credential type",
3063
+ options: [{
3064
+ value: "apiToken",
3065
+ label: "API Token",
3066
+ hint: "recommended — scoped to specific datasets/actions"
3067
+ }, {
3068
+ value: "pat",
3069
+ label: "Personal Access Token",
3070
+ hint: "full account control, requires Org ID"
3071
+ }]
3072
+ }).pipe(retryOnce);
3073
+ const apiBaseUrlPrompt = yield* text({
3074
+ message: "Axiom API Base URL (Enter for default)",
3075
+ placeholder: DEFAULT_API_BASE_URL,
3076
+ defaultValue: DEFAULT_API_BASE_URL
3077
+ }).pipe(retryOnce);
3078
+ const apiBaseUrl = apiBaseUrlPrompt && apiBaseUrlPrompt.length > 0 ? apiBaseUrlPrompt : void 0;
3079
+ return yield* Match.value(credentialType).pipe(Match.when("apiToken", () => Effect.gen(function* () {
3080
+ const apiToken = yield* password({
3081
+ message: "Axiom API Token",
3082
+ validate: (v) => v.length === 0 ? "Required" : void 0
3083
+ }).pipe(retryOnce);
3084
+ const orgId = yield* promptOrgId(false);
3085
+ yield* store.write(profileName, STORAGE_KEY, {
3086
+ type: "apiToken",
3087
+ apiToken,
3088
+ apiBaseUrl,
3089
+ orgId: orgId && orgId.length > 0 ? orgId : void 0
3090
+ });
3091
+ yield* success("Axiom: credentials saved.");
3092
+ return {
3093
+ method: "stored",
3094
+ credentialType: "apiToken"
3095
+ };
3096
+ })), Match.when("pat", () => Effect.gen(function* () {
3097
+ const apiToken = yield* password({
3098
+ message: "Axiom Personal Access Token",
3099
+ validate: (v) => v.length === 0 ? "Required" : void 0
3100
+ }).pipe(retryOnce);
3101
+ const orgId = yield* promptOrgId(true);
3102
+ yield* store.write(profileName, STORAGE_KEY, {
3103
+ type: "pat",
3104
+ apiToken,
3105
+ apiBaseUrl,
3106
+ orgId
3107
+ });
3108
+ yield* success("Axiom: credentials saved.");
3109
+ return {
3110
+ method: "stored",
3111
+ credentialType: "pat"
3112
+ };
3113
+ })), Match.exhaustive);
3114
+ });
3115
+ const configureInteractive = (profileName) => select({
3116
+ message: "Axiom authentication method",
3117
+ options: options$4
3118
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
3119
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
3120
+ if (ctx.ci) return { method: "env" };
3121
+ return yield* configureInteractive(profileName);
3122
+ }).pipe(Effect.mapError((e) => new AuthError({
3123
+ message: "failed to configure credentials",
3124
+ cause: e
3125
+ })));
3126
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
3127
+ const apiToken = (yield* getEnvRedacted("AXIOM_TOKEN")) ?? (yield* getEnvRedacted("AXIOM_API_KEY"));
3128
+ if (!apiToken) return yield* new AuthError({ message: "Axiom env credentials not found. Set AXIOM_TOKEN (or AXIOM_API_KEY)." });
3129
+ const apiBaseUrl = (yield* getEnv("AXIOM_URL")) ?? DEFAULT_API_BASE_URL;
3130
+ const orgId = yield* getEnv("AXIOM_ORG_ID");
3131
+ if (orgId) return {
3132
+ type: "pat",
3133
+ apiToken,
3134
+ apiBaseUrl,
3135
+ orgId,
3136
+ source: { type: "env" }
3137
+ };
3138
+ return {
3139
+ type: "apiToken",
3140
+ apiToken,
3141
+ apiBaseUrl,
3142
+ source: { type: "env" }
3143
+ };
3144
+ })), Match.when({ method: "stored" }, () => store.read(profileName, STORAGE_KEY).pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "Axiom stored credentials not found. Run: alchemy login --configure" })) : Effect.succeed(Match.value(creds).pipe(Match.when({ type: "apiToken" }, (c$1) => ({
3145
+ type: "apiToken",
3146
+ apiToken: Redacted.make(c$1.apiToken),
3147
+ apiBaseUrl: c$1.apiBaseUrl ?? DEFAULT_API_BASE_URL,
3148
+ orgId: c$1.orgId,
3149
+ source: { type: "stored" }
3150
+ })), Match.when({ type: "pat" }, (c$1) => ({
3151
+ type: "pat",
3152
+ apiToken: Redacted.make(c$1.apiToken),
3153
+ apiBaseUrl: c$1.apiBaseUrl ?? DEFAULT_API_BASE_URL,
3154
+ orgId: c$1.orgId,
3155
+ source: { type: "stored" }
3156
+ })), Match.exhaustive))))), Match.exhaustive);
3157
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.delete(profileName, STORAGE_KEY).pipe(Effect.andThen(success("Axiom: stored credentials removed")))), Match.exhaustive);
3158
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.read(profileName, STORAGE_KEY).pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
3159
+ message: "login failed",
3160
+ cause: e
3161
+ })));
3162
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap((creds) => {
3163
+ const sourceStr = creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type;
3164
+ return Match.value(creds).pipe(Match.when({ type: "apiToken" }, (c$1) => Effect.all([
3165
+ Console.log(` apiToken: ${displayRedacted(c$1.apiToken, 9)}`),
3166
+ Console.log(` apiBaseUrl: ${c$1.apiBaseUrl}`),
3167
+ Console.log(` orgId: ${c$1.orgId ?? "(none)"}`),
3168
+ Console.log(` source: ${sourceStr}`)
3169
+ ])), Match.when({ type: "pat" }, (c$1) => Effect.all([
3170
+ Console.log(` apiToken: ${displayRedacted(c$1.apiToken, 9)}`),
3171
+ Console.log(` apiBaseUrl: ${c$1.apiBaseUrl}`),
3172
+ Console.log(` orgId: ${c$1.orgId}`),
3173
+ Console.log(` source: ${sourceStr}`)
3174
+ ])), Match.exhaustive);
3175
+ }));
3176
+ return {
3177
+ configure: configureCredentials,
3178
+ logout,
3179
+ login,
3180
+ prettyPrint,
3181
+ read: resolveCredentials
3182
+ };
3183
+ }));
3184
+
3185
+ //#endregion
3186
+ //#region lib/Cloudflare/StateStore/CredentialsFile.js
3187
+ /**
3188
+ * Filename (under `~/.alchemy/credentials/{profile}/`) used to cache the
3189
+ * Cloudflare-deployed HTTP state store's endpoint + bearer token.
3190
+ *
3191
+ * Kept in this leaf module (rather than `StateStore/State.ts`) so the
3192
+ * Cloudflare auth provider can invalidate the cache on
3193
+ * `login --configure` / `logout` without importing the heavy state-store
3194
+ * module and creating an import cycle
3195
+ * (`StateStore/State.ts` -> `Providers.ts` -> `Auth/AuthProvider.ts`).
3196
+ */
3197
+ const CREDENTIALS_FILE = "cloudflare-state-store";
3198
+
3199
+ //#endregion
3200
+ //#region lib/Cloudflare/Auth/OAuthClient.js
3201
+ init_AuthProvider();
3202
+ var OAuthError$1 = class extends Data.TaggedError("OAuthError") {};
3203
+ function generateState$1(length = 32) {
3204
+ return crypto$1.randomBytes(length).toString("base64url");
3205
+ }
3206
+ function generatePKCE(length = 96) {
3207
+ const verifier = crypto$1.randomBytes(length).toString("base64url");
3208
+ return {
3209
+ verifier,
3210
+ challenge: crypto$1.createHash("sha256").update(verifier).digest("base64url")
3211
+ };
3212
+ }
3213
+ function extractCredentials$1(json) {
3214
+ return {
3215
+ type: "oauth",
3216
+ access: json.access_token,
3217
+ refresh: json.refresh_token,
3218
+ expires: Date.now() + json.expires_in * 1e3,
3219
+ scopes: json.scope.split(" ")
3220
+ };
3221
+ }
3222
+ const tokenRequest$1 = (body) => Effect.gen(function* () {
3223
+ const res = yield* Effect.tryPromise({
3224
+ try: () => fetch(OAUTH_ENDPOINTS$1.token, {
3225
+ method: "POST",
3226
+ headers: {
3227
+ Accept: "application/json",
3228
+ "Content-Type": "application/x-www-form-urlencoded"
3229
+ },
3230
+ body: new URLSearchParams(body).toString()
3231
+ }),
3232
+ catch: (err) => new OAuthError$1({
3233
+ error: "network_error",
3234
+ errorDescription: `Token request failed: ${err}`
3235
+ })
3236
+ });
3237
+ if (!res.ok) {
3238
+ const json = yield* Effect.tryPromise({
3239
+ try: () => res.json(),
3240
+ catch: () => new OAuthError$1({
3241
+ error: "parse_error",
3242
+ errorDescription: `Token endpoint returned ${res.status}`
3243
+ })
3244
+ });
3245
+ return yield* new OAuthError$1({
3246
+ error: json.error,
3247
+ errorDescription: json.error_description
3248
+ });
3249
+ }
3250
+ return extractCredentials$1(yield* Effect.tryPromise({
3251
+ try: () => res.json(),
3252
+ catch: () => new OAuthError$1({
3253
+ error: "parse_error",
3254
+ errorDescription: "Failed to parse token response"
3255
+ })
3256
+ }));
3257
+ });
3258
+ /**
3259
+ * Generate an authorization URL with PKCE for the given scopes.
3260
+ */
3261
+ function authorize$1(scopes) {
3262
+ const state = generateState$1();
3263
+ const pkce = generatePKCE();
3264
+ const url = new URL(OAUTH_ENDPOINTS$1.authorize);
3265
+ url.searchParams.set("client_id", OAUTH_CLIENT_ID$1);
3266
+ url.searchParams.set("redirect_uri", OAUTH_REDIRECT_URI$1);
3267
+ url.searchParams.set("response_type", "code");
3268
+ url.searchParams.set("scope", scopes.join(" "));
3269
+ url.searchParams.set("state", state);
3270
+ url.searchParams.set("code_challenge", pkce.challenge);
3271
+ url.searchParams.set("code_challenge_method", "S256");
3272
+ return {
3273
+ url: url.toString(),
3274
+ state,
3275
+ verifier: pkce.verifier
3276
+ };
3277
+ }
3278
+ /**
3279
+ * Exchange an authorization code for OAuth credentials.
3280
+ */
3281
+ const exchange$1 = (code, verifier) => tokenRequest$1({
3282
+ grant_type: "authorization_code",
3283
+ code,
3284
+ code_verifier: verifier,
3285
+ client_id: OAUTH_CLIENT_ID$1,
3286
+ redirect_uri: OAUTH_REDIRECT_URI$1
3287
+ });
3288
+ /**
3289
+ * Refresh expired OAuth credentials.
3290
+ */
3291
+ const refresh$1 = (credentials) => tokenRequest$1({
3292
+ grant_type: "refresh_token",
3293
+ refresh_token: credentials.refresh,
3294
+ client_id: OAUTH_CLIENT_ID$1,
3295
+ redirect_uri: OAUTH_REDIRECT_URI$1
3296
+ });
3297
+ /**
3298
+ * Revoke OAuth credentials.
3299
+ */
3300
+ const revoke = (credentials) => Effect.gen(function* () {
3301
+ yield* Effect.tryPromise({
3302
+ try: () => fetch(OAUTH_ENDPOINTS$1.revoke, {
3303
+ method: "POST",
3304
+ headers: {
3305
+ Accept: "application/json",
3306
+ "Content-Type": "application/x-www-form-urlencoded"
3307
+ },
3308
+ body: new URLSearchParams({
3309
+ refresh_token: credentials.refresh,
3310
+ client_id: OAUTH_CLIENT_ID$1,
3311
+ redirect_uri: OAUTH_REDIRECT_URI$1
3312
+ }).toString()
3313
+ }),
3314
+ catch: (err) => new OAuthError$1({
3315
+ error: "network_error",
3316
+ errorDescription: `Revoke request failed: ${err}`
3317
+ })
3318
+ });
3319
+ });
3320
+ /**
3321
+ * Start a local HTTP server to listen for the OAuth callback, exchange
3322
+ * the authorization code, and return the credentials.
3323
+ *
3324
+ * Times out after 5 minutes.
3325
+ */
3326
+ const callback$1 = (authorization) => Effect.tryPromise({
3327
+ try: () => callbackPromise$1(authorization),
3328
+ catch: (err) => {
3329
+ if (err instanceof OAuthError$1) return err;
3330
+ return new OAuthError$1({
3331
+ error: "callback_error",
3332
+ errorDescription: `OAuth callback failed: ${err}`
3333
+ });
3334
+ }
3335
+ });
3336
+ function callbackPromise$1(authorization) {
3337
+ const { pathname, port } = new URL(OAUTH_REDIRECT_URI$1);
3338
+ return new Promise((resolve, reject) => {
3339
+ const server = http.createServer(async (req, res) => {
3340
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
3341
+ if (url.pathname !== pathname) {
3342
+ res.statusCode = 404;
3343
+ res.end("Not Found");
3344
+ return;
3345
+ }
3346
+ const error = url.searchParams.get("error");
3347
+ const errorDescription = url.searchParams.get("error_description");
3348
+ if (error) {
3349
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
3350
+ res.end();
3351
+ cleanup();
3352
+ reject(new OAuthError$1({
3353
+ error,
3354
+ errorDescription: errorDescription ?? "An unknown error occurred."
3355
+ }));
3356
+ return;
3357
+ }
3358
+ const code = url.searchParams.get("code");
3359
+ const state = url.searchParams.get("state");
3360
+ if (!code || !state) {
3361
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
3362
+ res.end();
3363
+ cleanup();
3364
+ reject(new OAuthError$1({
3365
+ error: "invalid_request",
3366
+ errorDescription: "Missing code or state"
3367
+ }));
3368
+ return;
3369
+ }
3370
+ if (state !== authorization.state) {
3371
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
3372
+ res.end();
3373
+ cleanup();
3374
+ reject(new OAuthError$1({
3375
+ error: "invalid_request",
3376
+ errorDescription: "Invalid state"
3377
+ }));
3378
+ return;
3379
+ }
3380
+ try {
3381
+ const credentials = await Effect.runPromise(exchange$1(code, authorization.verifier));
3382
+ res.writeHead(302, { Location: AUTH_SUCCESS_URL });
3383
+ res.end();
3384
+ cleanup();
3385
+ resolve(credentials);
3386
+ } catch (err) {
3387
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
3388
+ res.end();
3389
+ cleanup();
3390
+ reject(err);
3391
+ }
3392
+ });
3393
+ const timeout = setTimeout(() => {
3394
+ cleanup();
3395
+ reject(new OAuthError$1({
3396
+ error: "timeout",
3397
+ errorDescription: "The authorization process timed out."
3398
+ }));
3399
+ }, 300 * 1e3);
3400
+ function cleanup() {
3401
+ clearTimeout(timeout);
3402
+ server.close();
3403
+ }
3404
+ server.on("error", (err) => {
3405
+ cleanup();
3406
+ reject(new OAuthError$1({
3407
+ error: "server_error",
3408
+ errorDescription: `Failed to start callback server: ${err.message}`
3409
+ }));
3410
+ });
3411
+ server.listen(Number(port));
3412
+ });
3413
+ }
3414
+
3415
+ //#endregion
3416
+ //#region lib/Cloudflare/Auth/AuthProvider.js
3417
+ init_AuthProvider();
3418
+ init_Credentials();
3419
+ const options$3 = [
3420
+ {
3421
+ value: "oauth",
3422
+ label: "OAuth",
3423
+ hint: "recommended — browser-based login with automatic token refresh"
3424
+ },
3425
+ {
3426
+ value: "env",
3427
+ label: "Environment Variables",
3428
+ hint: "CLOUDFLARE_API_TOKEN or CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL"
3429
+ },
3430
+ {
3431
+ value: "stored",
3432
+ label: "API Token or API Key",
3433
+ hint: "enter credentials interactively, stored in ~/.alchemy/credentials"
3434
+ }
3435
+ ];
3436
+ const CLOUDFLARE_AUTH_PROVIDER_NAME = "Cloudflare";
3437
+ const withOAuthCredentials$1 = (accessToken, effect) => Effect.provide(effect, Layer.mergeAll(CfCredentialsModule.fromOAuth({
3438
+ load: Effect.succeed({ accessToken }),
3439
+ refresh: () => Effect.die("refresh not expected during account selection")
3440
+ }), FetchHttpClient.layer));
3441
+ const selectAccount = (accessToken) => Effect.gen(function* () {
3442
+ const accounts = (yield* (yield* cfAccounts.listAccounts)({})).result;
3443
+ if (accounts.length === 0) return yield* new AuthError({ message: "Cloudflare: no accounts found for this credential." });
3444
+ if (accounts.length === 1) {
3445
+ const account = accounts[0];
3446
+ yield* info(`Cloudflare: using account: ${account.name} (${account.id})`);
3447
+ return account.id;
3448
+ }
3449
+ return yield* select({
3450
+ message: "Select a Cloudflare account",
3451
+ options: accounts.map((a) => ({
3452
+ value: a.id,
3453
+ label: a.name,
3454
+ hint: a.id
3455
+ }))
3456
+ }).pipe(retryOnce);
3457
+ }).pipe((e) => withOAuthCredentials$1(accessToken, e));
3458
+ /**
3459
+ * Cloudflare account IDs are 32 lowercase hex characters. Placeholder
3460
+ * values ("", "-", "dummy", …) end up interpolated into API paths and
3461
+ * surface as baffling `InvalidRoute: Could not route to
3462
+ * /accounts/<value>/...` errors, so reject them up front with an
3463
+ * actionable message instead.
3464
+ */
3465
+ const ACCOUNT_ID_PATTERN = /^[0-9a-f]{32}$/i;
3466
+ const validateAccountId = (accountId, source) => {
3467
+ const trimmed = accountId?.trim() ?? "";
3468
+ if (trimmed.length === 0) return Effect.fail(new AuthError({ message: `Cloudflare account ID is missing (${source}). Set CLOUDFLARE_ACCOUNT_ID or re-run 'alchemy login' and provide your account ID (found in the Cloudflare dashboard under Workers & Pages → Account details).` }));
3469
+ if (!ACCOUNT_ID_PATTERN.test(trimmed)) return Effect.fail(new AuthError({ message: `'${trimmed}' is not a valid Cloudflare account ID (${source}) — expected 32 hex characters. Copy the account ID from the Cloudflare dashboard (Workers & Pages → Account details) into CLOUDFLARE_ACCOUNT_ID or re-run 'alchemy login'.` }));
3470
+ return Effect.succeed(trimmed.toLowerCase());
3471
+ };
3472
+ const promptAccountId = () => getEnv("CLOUDFLARE_ACCOUNT_ID").pipe(Effect.flatMap((envAccountId) => text({
3473
+ message: "Cloudflare Account ID (Enter to skip)",
3474
+ placeholder: envAccountId ?? "",
3475
+ defaultValue: envAccountId ?? "",
3476
+ validate: (v) => v.trim().length === 0 || ACCOUNT_ID_PATTERN.test(v.trim()) ? void 0 : "Expected a 32-character hex account ID (Workers & Pages → Account details)"
3477
+ }).pipe(retryOnce)));
3478
+ const promptOAuthScopes = () => confirm({
3479
+ message: "Customize OAuth scopes? (default covers typical use cases)",
3480
+ initialValue: false
3481
+ }).pipe(retryOnce, Effect.flatMap((customize) => {
3482
+ if (!customize) return Effect.succeed([...DEFAULT_SCOPES]);
3483
+ return multiselect({
3484
+ message: "Select OAuth scopes",
3485
+ initialValues: DEFAULT_SCOPES,
3486
+ options: Object.entries(ALL_SCOPES).map(([value, hint]) => ({
3487
+ value,
3488
+ label: value,
3489
+ hint
3490
+ })),
3491
+ required: true
3492
+ }).pipe(Effect.map((s) => s), retryOnce);
3493
+ }));
3494
+ /**
3495
+ * Layer that registers the Cloudflare {@link AuthProvider} into the
3496
+ * {@link AuthProviders} registry when built. Include this in the Cloudflare
3497
+ * `providers()` layer so `alchemy login` can discover it.
3498
+ */
3499
+ const CloudflareAuth = AuthProviderLayer()(CLOUDFLARE_AUTH_PROVIDER_NAME, Effect.gen(function* () {
3500
+ const store = yield* CredentialsStore;
3501
+ const oauthLogin = (profileName, scopes) => Effect.gen(function* () {
3502
+ const authorization = authorize$1([...scopes, "offline_access"]);
3503
+ yield* info("Cloudflare: opening browser for OAuth login...");
3504
+ yield* info(authorization.url);
3505
+ yield* openUrl(authorization.url).pipe(Effect.catch(() => warn("Cloudflare: could not open browser automatically. Please open the URL above manually.")));
3506
+ yield* info("Cloudflare: waiting for authorization (up to 5 minutes)...");
3507
+ const credentials = yield* callback$1(authorization);
3508
+ yield* store.write(profileName, "cf-oauth", credentials);
3509
+ yield* success("Cloudflare: OAuth credentials saved.");
3510
+ return credentials;
3511
+ });
3512
+ const loginStored = Effect.fn(function* (profileName) {
3513
+ const credentialType = yield* select({
3514
+ message: "Cloudflare credential type",
3515
+ options: [{
3516
+ value: "apiToken",
3517
+ label: "API Token",
3518
+ hint: "recommended"
3519
+ }, {
3520
+ value: "apiKey",
3521
+ label: "API Key + Email"
3522
+ }]
3523
+ }).pipe(retryOnce);
3524
+ return yield* Match.value(credentialType).pipe(Match.when("apiToken", () => Effect.gen(function* () {
3525
+ const apiToken = yield* password({
3526
+ message: "Cloudflare API Token",
3527
+ validate: (v) => v.length === 0 ? "Required" : void 0
3528
+ }).pipe(retryOnce);
3529
+ const accountId = yield* promptAccountId();
3530
+ yield* store.write(profileName, "cf-stored", {
3531
+ type: "apiToken",
3532
+ apiToken,
3533
+ accountId
3534
+ });
3535
+ yield* success("Cloudflare: credentials saved.");
3536
+ return {
3537
+ method: "stored",
3538
+ credentialType: "apiToken"
3539
+ };
3540
+ })), Match.when("apiKey", () => Effect.gen(function* () {
3541
+ const apiKey = yield* text({
3542
+ message: "Cloudflare API Key",
3543
+ validate: (v) => v.length === 0 ? "Required" : void 0
3544
+ }).pipe(retryOnce);
3545
+ const email = yield* text({
3546
+ message: "Cloudflare Email",
3547
+ validate: (v) => v.length === 0 ? "Required" : void 0
3548
+ }).pipe(retryOnce);
3549
+ const accountId = yield* promptAccountId();
3550
+ yield* store.write(profileName, "cf-stored", {
3551
+ type: "apiKey",
3552
+ apiKey,
3553
+ email,
3554
+ accountId
3555
+ });
3556
+ yield* success("Cloudflare: credentials saved.");
3557
+ return {
3558
+ method: "stored",
3559
+ credentialType: "apiKey"
3560
+ };
3561
+ })), Match.exhaustive);
3562
+ });
3563
+ const configureOAuth = Effect.fn(function* (profileName) {
3564
+ const scopes = yield* promptOAuthScopes();
3565
+ return {
3566
+ method: "oauth",
3567
+ scopes,
3568
+ accountId: yield* selectAccount((yield* oauthLogin(profileName, scopes)).access).pipe(Effect.mapError((e) => new AuthError({
3569
+ message: "Cloudflare: could not list accounts",
3570
+ cause: e
3571
+ })))
3572
+ };
3573
+ });
3574
+ const configureInteractive = (profileName) => select({
3575
+ message: "Cloudflare authentication method",
3576
+ options: options$3
3577
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("oauth", () => configureOAuth(profileName)), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
3578
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
3579
+ const config = ctx.ci ? { method: "env" } : yield* configureInteractive(profileName);
3580
+ yield* store.delete(profileName, CREDENTIALS_FILE).pipe(Effect.ignore);
3581
+ return config;
3582
+ }).pipe(Effect.mapError((e) => new AuthError({
3583
+ message: "failed to configure credentials",
3584
+ cause: e
3585
+ })));
3586
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
3587
+ const accountId = yield* getEnvRequired("CLOUDFLARE_ACCOUNT_ID").pipe(Effect.flatMap((id) => validateAccountId(id, "from the CLOUDFLARE_ACCOUNT_ID env var")));
3588
+ const apiToken = yield* getEnvRedacted("CLOUDFLARE_API_TOKEN");
3589
+ if (apiToken) return {
3590
+ type: "apiToken",
3591
+ apiToken,
3592
+ accountId,
3593
+ source: { type: "env" }
3594
+ };
3595
+ const apiKey = yield* getEnvRedacted("CLOUDFLARE_API_KEY");
3596
+ const email = yield* getEnvRedacted("CLOUDFLARE_EMAIL");
3597
+ if (apiKey && email) return {
3598
+ type: "apiKey",
3599
+ apiKey,
3600
+ email,
3601
+ accountId,
3602
+ source: { type: "env" }
3603
+ };
3604
+ return yield* new AuthError({ message: "Cloudflare env credentials not found. Set CLOUDFLARE_API_TOKEN or CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL." });
3605
+ })), Match.when({ method: "stored" }, () => store.read(profileName, "cf-stored").pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "Cloudflare stored credentials not found. Run: alchemy login --configure" })) : Effect.gen(function* () {
3606
+ const envAccountId = yield* getEnv("CLOUDFLARE_ACCOUNT_ID");
3607
+ const accountId = yield* validateAccountId(creds.accountId?.trim() || envAccountId, `stored for profile '${profileName}'`);
3608
+ return Match.value(creds).pipe(Match.when({ type: "apiToken" }, (c$1) => ({
3609
+ type: "apiToken",
3610
+ apiToken: Redacted.make(c$1.apiToken),
3611
+ accountId,
3612
+ source: { type: "stored" }
3613
+ })), Match.when({ type: "apiKey" }, (c$1) => ({
3614
+ type: "apiKey",
3615
+ apiKey: Redacted.make(c$1.apiKey),
3616
+ email: Redacted.make(c$1.email),
3617
+ accountId,
3618
+ source: { type: "stored" }
3619
+ })), Match.exhaustive);
3620
+ })))), Match.when({ method: "oauth" }, (cfg) => Effect.gen(function* () {
3621
+ const accountId = yield* validateAccountId(cfg.accountId, `configured for profile '${profileName}'`);
3622
+ const creds = yield* store.read(profileName, "cf-oauth");
3623
+ if (creds == null || creds.type !== "oauth") return yield* Effect.fail(new AuthError({ message: "Cloudflare OAuth credentials not found. Run: alchemy login" }));
3624
+ const fresh = creds.expires > Date.now() + 1e4 ? creds : yield* refresh$1(creds).pipe(Effect.tap((refreshed) => store.write(profileName, "cf-oauth", refreshed)), Effect.mapError((e) => new AuthError({
3625
+ message: "Cloudflare OAuth refresh failed. Run: alchemy login",
3626
+ cause: e
3627
+ })));
3628
+ return {
3629
+ type: "oauth",
3630
+ accessToken: Redacted.make(fresh.access),
3631
+ expires: fresh.expires,
3632
+ accountId,
3633
+ source: { type: "oauth" }
3634
+ };
3635
+ })), Match.exhaustive);
3636
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.delete(profileName, "cf-stored").pipe(Effect.andThen(success("Cloudflare: stored credentials removed")))), Match.when({ method: "oauth" }, () => store.read(profileName, "cf-oauth").pipe(Effect.tap((creds) => creds?.type === "oauth" ? revoke(creds).pipe(Effect.catchTag("OAuthError", (err) => warn(`Cloudflare: could not revoke OAuth token: ${err.errorDescription}`))) : Effect.void), Effect.andThen(store.delete(profileName, "cf-oauth")), Effect.andThen(success("Cloudflare: OAuth credentials removed.")))), Match.exhaustive).pipe(Effect.andThen(store.delete(profileName, CREDENTIALS_FILE).pipe(Effect.ignore)));
3637
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.read(profileName, "cf-stored").pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.when({ method: "oauth" }, (c$1) => Effect.gen(function* () {
3638
+ const creds = yield* store.read(profileName, "cf-oauth");
3639
+ if (creds?.type === "oauth") {
3640
+ yield* info("Cloudflare: refreshing OAuth credentials...");
3641
+ yield* refresh$1(creds).pipe(Effect.flatMap((refreshed) => store.write(profileName, "cf-oauth", refreshed).pipe(Effect.andThen(success("Cloudflare: OAuth credentials refreshed.")))), Effect.catchTag("OAuthError", () => oauthLogin(profileName, c$1.scopes).pipe(Effect.asVoid)));
3642
+ return;
3643
+ }
3644
+ yield* oauthLogin(profileName, c$1.scopes);
3645
+ })), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
3646
+ message: "login failed",
3647
+ cause: e
3648
+ })));
3649
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap((creds) => {
3650
+ const sourceStr = creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type;
3651
+ return Match.value(creds).pipe(Match.when({ type: "apiToken" }, (c$1) => Effect.all([
3652
+ Console.log(` apiToken: ${displayRedacted(c$1.apiToken, 9)}`),
3653
+ Console.log(` accountId: ${c$1.accountId}`),
3654
+ Console.log(` source: ${sourceStr}`)
3655
+ ])), Match.when({ type: "apiKey" }, (c$1) => Effect.all([
3656
+ Console.log(` apiKey: ${displayRedacted(c$1.apiKey)}`),
3657
+ Console.log(` email: ${displayRedacted(c$1.email)}`),
3658
+ Console.log(` accountId: ${c$1.accountId}`),
3659
+ Console.log(` source: ${sourceStr}`)
3660
+ ])), Match.when({ type: "oauth" }, (c$1) => {
3661
+ const remainingMs = c$1.expires - Date.now();
3662
+ const expiresAt = new Date(c$1.expires).toISOString();
3663
+ const expiresStr = remainingMs <= 0 ? `expired (${expiresAt})` : `in ${Duration.format(Duration.millis(remainingMs))} (${expiresAt})`;
3664
+ return Effect.all([
3665
+ Console.log(` accessToken: ${displayRedacted(c$1.accessToken)}`),
3666
+ Console.log(` expires: ${expiresStr}`),
3667
+ Console.log(` accountId: ${c$1.accountId}`),
3668
+ Console.log(` source: ${sourceStr}`)
3669
+ ]);
3670
+ }), Match.exhaustive);
3671
+ }));
3672
+ return {
3673
+ configure: configureCredentials,
3674
+ logout,
3675
+ login,
3676
+ prettyPrint,
3677
+ read: resolveCredentials
3678
+ };
3679
+ }));
3680
+ const ALL_SCOPES = {
3681
+ "access:read": "See Cloudflare Access data such as zones, applications, certificates, device postures, groups, identity providers, login counts, organizations, policies, service tokens, and users",
3682
+ "access:write": "See and change Cloudflare Access data such as zones, applications, certificates, device postures, groups, identity providers, login counts, organizations, policies, service tokens, and users",
3683
+ "account:read": "See your account info such as account details, analytics, and memberships",
3684
+ "agw:read": "Grants read level access to Agents Gateway",
3685
+ "agw:run": "Grants run level access to Agents Gateway",
3686
+ "agw:write": "Grants read and write level access to Agents Gateway",
3687
+ "ai:read": "Grants read level access to Workers AI",
3688
+ "ai:write": "Grants write level access to Workers AI",
3689
+ "aiaudit:read": "Grants read level access to AI Audit",
3690
+ "aiaudit:write": "Grants write level access to AI Audit",
3691
+ "aig:read": "Grants read level access to AI Gateway",
3692
+ "aig:write": "Grants write level access to AI Gateway",
3693
+ "auditlogs:read": "View Cloudflare Account Audit Logs",
3694
+ "browser:read": "Grants read level access to Browser Rendering",
3695
+ "browser:write": "Grants write level access to Browser Rendering",
3696
+ "cfone:read": "Grants read level access to Cloudforce One data",
3697
+ "cfone:write": "Grants write level access to Cloudforce One data",
3698
+ "cloudchamber:write": "See and make changes to Cloudchamber",
3699
+ "connectivity:admin": "See, change, and bind to Connectivity Directory services, including creating services targeting Cloudflare Tunnel",
3700
+ "connectivity:bind": "read, list, and bind to Connectivity Directory services, as well as read and list Cloudflare Tunnels",
3701
+ "connectivity:read": "See Connectivity Directory services and Cloudflare Tunnels",
3702
+ "constellation:write": "Grants write access to Constellation configuration and models",
3703
+ "containers:write": "See and make changes to Workers Containers",
3704
+ "d1:write": "See and make changes to D1",
3705
+ "dex:read": "Grants read level access to Cloudflare DEX",
3706
+ "dex:write": "Grants write level access to Cloudflare DEX",
3707
+ "dns_analytics:read": "Grants read level access to Cloudflare DNS Analytics",
3708
+ "dns_records:edit": "Grants edit level access to dns records",
3709
+ "dns_records:read": "Grants read level access to dns records",
3710
+ "dns_settings:read": "Grants read level access to Cloudflare DNS Settings",
3711
+ "firstpartytags:write": "Can see, edit and publish Google tag gateway configuration.",
3712
+ "lb:edit": "Grants edit level access to lb and lb pools",
3713
+ "lb:read": "Grants read level access to lb and lb pools",
3714
+ "logpush:read": "See Cloudflare Logpush data",
3715
+ "logpush:write": "See and change Cloudflare Logpush data",
3716
+ "notification:read": "View Cloudflare Notifications",
3717
+ "notification:write": "View and Modify Cloudflare Notifications",
3718
+ "pages:read": "See Cloudflare Pages projects, settings and deployments",
3719
+ "pages:write": "See and change Cloudflare Pages projects, settings and deployments",
3720
+ "pipelines:read": "Grants read level access to Cloudflare Pipelines",
3721
+ "pipelines:setup": "Grants permission to generate R2 tokens for Workers Pipelines",
3722
+ "pipelines:write": "Grants write level access to Cloudflare Pipelines",
3723
+ "query_cache:write": "See and make changes to Hyperdrive",
3724
+ "queues:write": "See and change Cloudflare Queues settings and data",
3725
+ "r2_catalog:write": "Grants write level access to R2 Data Catalog",
3726
+ "radar:read": "Grants access to read Cloudflare Radar data",
3727
+ "ai-search:read": "Grants read level access to AI Search",
3728
+ "ai-search:write": "Grants write level access to AI Search",
3729
+ "ai-search:run": "Grants run level access to AI Search",
3730
+ "secrets_store:read": "Grants read level access to Secrets Store",
3731
+ "secrets_store:write": "Grants write level access to Secrets Store",
3732
+ "ssl_certs:write": "Grants read and write access to SSL MTLS certificates or Certificate Store",
3733
+ "sso-connector:read": "See Cloudflare SSO connectors",
3734
+ "sso-connector:write": "See Cloudflare SSO connectors to toggle activation and deactivation of SSO",
3735
+ "teams:pii": "See personally identifiable Cloudflare Teams data",
3736
+ "teams:read": "See Cloudflare Teams data such as zones, gateway, and argo tunnel details",
3737
+ "teams:secure_location": "See all DNS Location data but can only change secure DNS Locations",
3738
+ "teams:write": "See and change Cloudflare Teams data such as zones, gateway, and argo tunnel details",
3739
+ "url_scanner:read": "Grants read level access to URL Scanner",
3740
+ "url_scanner:write": "Grants write level access to URL Scanner",
3741
+ "user:read": "See your user info such as name, email address, and account memberships",
3742
+ "vectorize:write": "See and make changes to Vectorize",
3743
+ "workers:read": "See Cloudflare Workers data such as zones, KV storage, R2 storage, scripts, and routes",
3744
+ "workers:write": "See and change Cloudflare Workers data such as zones, KV storage, R2 storage, scripts, and routes",
3745
+ "workers_builds:read": "See Cloudflare Workers Builds data such as builds, build configuration, and build logs",
3746
+ "workers_builds:write": "See and change Cloudflare Workers Builds data such as builds, build configuration, and build logs",
3747
+ "workers_kv:write": "See and change Cloudflare Workers KV Storage data such as keys and namespaces",
3748
+ "workers_observability:read": "Grants read access to Cloudflare Workers Observability",
3749
+ "workers_observability:write": "Grants read and write access to Cloudflare Workers Observability",
3750
+ "workers_observability_telemetry:write": "Grants write access to Cloudflare Workers Observability Telemetry API",
3751
+ "workers_routes:write": "See and change Cloudflare Workers data such as filters and routes",
3752
+ "workers_scripts:write": "See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data",
3753
+ "workers_tail:read": "See Cloudflare Workers tail and script data",
3754
+ "zone:read": "Grants read level access to account zone"
3755
+ };
3756
+ const DEFAULT_SCOPES = [
3757
+ "account:read",
3758
+ "ai-search:write",
3759
+ "ai-search:run",
3760
+ "ai:write",
3761
+ "aig:read",
3762
+ "aig:write",
3763
+ "cloudchamber:write",
3764
+ "connectivity:admin",
3765
+ "containers:write",
3766
+ "d1:write",
3767
+ "pages:write",
3768
+ "pipelines:write",
3769
+ "queues:write",
3770
+ "secrets_store:write",
3771
+ "ssl_certs:write",
3772
+ "user:read",
3773
+ "vectorize:write",
3774
+ "workers_kv:write",
3775
+ "workers_observability:read",
3776
+ "workers_observability:write",
3777
+ "workers_observability_telemetry:write",
3778
+ "workers_routes:write",
3779
+ "workers_scripts:write",
3780
+ "workers_tail:read",
3781
+ "workers:write",
3782
+ "zone:read"
3783
+ ];
3784
+ const OAUTH_CLIENT_ID$1 = "6d8c2255-0773-45f6-b376-2914632e6f91";
3785
+ const OAUTH_REDIRECT_URI$1 = "http://localhost:9976/auth/callback";
3786
+ const OAUTH_ENDPOINTS$1 = {
3787
+ authorize: "https://dash.cloudflare.com/oauth2/authorize",
3788
+ token: "https://dash.cloudflare.com/oauth2/token",
3789
+ revoke: "https://dash.cloudflare.com/oauth2/revoke"
3790
+ };
3791
+
3792
+ //#endregion
3793
+ //#region lib/GitHub/AuthProvider.js
3794
+ init_AuthProvider();
3795
+ init_Credentials();
3796
+ const options$2 = [
3797
+ {
3798
+ value: "gh-cli",
3799
+ label: "GitHub CLI",
3800
+ hint: "delegate to `gh auth token` (run `gh auth login` first)"
3801
+ },
3802
+ {
3803
+ value: "env",
3804
+ label: "Environment Variables",
3805
+ hint: "GITHUB_ACCESS_TOKEN or GITHUB_TOKEN"
3806
+ },
3807
+ {
3808
+ value: "stored",
3809
+ label: "Personal Access Token",
3810
+ hint: "enter PAT interactively, stored in ~/.alchemy/credentials"
3811
+ }
3812
+ ];
3813
+ const GITHUB_AUTH_PROVIDER_NAME = "GitHub";
3814
+ var GhCliError = class extends Error {
3815
+ _tag = "GhCliError";
3816
+ };
3817
+ /**
3818
+ * Layer that registers the GitHub {@link AuthProvider} into the
3819
+ * {@link AuthProviders} registry when built. Include this in the GitHub
3820
+ * `providers()` layer so `alchemy login` can discover it.
3821
+ *
3822
+ * Supported methods:
3823
+ * - `gh-cli`: shells out to `gh auth token` (recommended).
3824
+ * - `env`: reads `GITHUB_ACCESS_TOKEN` or `GITHUB_TOKEN`.
3825
+ * - `stored`: prompts for a PAT and writes it to `~/.alchemy/credentials`.
3826
+ *
3827
+ * Browser/device OAuth is intentionally not implemented: GitHub's
3828
+ * OAuth App flow requires a `client_secret` we cannot ship, and
3829
+ * device flow is exactly what `gh auth login` already does.
3830
+ */
3831
+ const GitHubAuth = AuthProviderLayer()(GITHUB_AUTH_PROVIDER_NAME, Effect.gen(function* () {
3832
+ const store = yield* CredentialsStore;
3833
+ const cp = yield* ChildProcessSpawner;
3834
+ const ghCliToken = () => Effect.gen(function* () {
3835
+ const handle = yield* cp.spawn(ChildProcess$1.make("gh", ["auth", "token"], { shell: false }));
3836
+ const [exitCode, stdout, stderr] = yield* Effect.all([
3837
+ handle.exitCode,
3838
+ Stream.mkString(Stream.decodeText(handle.stdout)),
3839
+ Stream.mkString(Stream.decodeText(handle.stderr))
3840
+ ], { concurrency: 3 });
3841
+ if (exitCode !== 0) return yield* Effect.fail(new GhCliError(`gh auth token exited with ${exitCode}: ${stderr.trim() || stdout.trim()}`));
3842
+ const token = stdout.trim();
3843
+ if (!token) return yield* Effect.fail(new GhCliError("gh auth token returned empty output"));
3844
+ return token;
3845
+ }).pipe(Effect.scoped, Effect.mapError((e) => e instanceof GhCliError ? new AuthError({
3846
+ message: e.message,
3847
+ cause: e
3848
+ }) : new AuthError({
3849
+ message: "Could not invoke `gh`. Install GitHub CLI from https://cli.github.com/ and run `gh auth login`.",
3850
+ cause: e
3851
+ })));
3852
+ const loginStored = Effect.fn(function* (profileName) {
3853
+ const token = yield* password({
3854
+ message: "GitHub Personal Access Token (needs `repo` scope; `workflow` for Actions)",
3855
+ validate: (v) => v.length === 0 ? "Required" : void 0
3856
+ }).pipe(retryOnce);
3857
+ yield* store.write(profileName, "gh-stored", {
3858
+ type: "pat",
3859
+ token
3860
+ });
3861
+ yield* success("GitHub: credentials saved.");
3862
+ return { method: "stored" };
3863
+ });
3864
+ const configureInteractive = (profileName) => select({
3865
+ message: "GitHub authentication method",
3866
+ options: options$2
3867
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("gh-cli", () => ghCliToken().pipe(Effect.as({ method: "gh-cli" }), Effect.mapError((e) => new AuthError({
3868
+ message: `gh CLI not available: ${e.message}`,
3869
+ cause: e
3870
+ })))), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
3871
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
3872
+ if (ctx.ci) return { method: "env" };
3873
+ return yield* configureInteractive(profileName);
3874
+ }).pipe(Effect.mapError((e) => new AuthError({
3875
+ message: "failed to configure credentials",
3876
+ cause: e
3877
+ })));
3878
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
3879
+ const access = yield* getEnvRedacted("GITHUB_ACCESS_TOKEN");
3880
+ if (access) return {
3881
+ type: "token",
3882
+ token: access,
3883
+ source: {
3884
+ type: "env",
3885
+ details: "GITHUB_ACCESS_TOKEN"
3886
+ }
3887
+ };
3888
+ const token = yield* getEnvRedacted("GITHUB_TOKEN");
3889
+ if (token) return {
3890
+ type: "token",
3891
+ token,
3892
+ source: {
3893
+ type: "env",
3894
+ details: "GITHUB_TOKEN"
3895
+ }
3896
+ };
3897
+ return yield* new AuthError({ message: "GitHub env credentials not found. Set GITHUB_ACCESS_TOKEN or GITHUB_TOKEN." });
3898
+ })), Match.when({ method: "stored" }, () => store.read(profileName, "gh-stored").pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "GitHub stored credentials not found. Run: alchemy login --configure" })) : Effect.succeed({
3899
+ type: "token",
3900
+ token: Redacted.make(creds.token),
3901
+ source: { type: "stored" }
3902
+ })))), Match.when({ method: "gh-cli" }, () => ghCliToken().pipe(Effect.map((token) => ({
3903
+ type: "token",
3904
+ token: Redacted.make(token),
3905
+ source: { type: "gh-cli" }
3906
+ })))), Match.exhaustive);
3907
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "gh-cli" }, () => Effect.void), Match.when({ method: "stored" }, () => store.delete(profileName, "gh-stored").pipe(Effect.andThen(success("GitHub: stored credentials removed")))), Match.exhaustive);
3908
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "gh-cli" }, () => ghCliToken().pipe(Effect.tap(() => success("GitHub: gh CLI authentication available.")), Effect.asVoid)), Match.when({ method: "stored" }, () => store.read(profileName, "gh-stored").pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
3909
+ message: "login failed",
3910
+ cause: e
3911
+ })));
3912
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap((creds) => {
3913
+ const sourceStr = creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type;
3914
+ return Effect.all([Console.log(` token: ${displayRedacted(creds.token, 6)}`), Console.log(` source: ${sourceStr}`)]);
3915
+ }));
3916
+ return {
3917
+ configure: configureCredentials,
3918
+ logout,
3919
+ login,
3920
+ prettyPrint,
3921
+ read: resolveCredentials
3922
+ };
3923
+ }));
3924
+
3925
+ //#endregion
3926
+ //#region lib/Neon/AuthProvider.js
3927
+ init_AuthProvider();
3928
+ init_Credentials();
3929
+ init_Profile();
3930
+ const NEON_AUTH_PROVIDER_NAME = "Neon";
3931
+ const options$1 = [{
3932
+ value: "env",
3933
+ label: "Environment Variable",
3934
+ hint: "NEON_API_KEY"
3935
+ }, {
3936
+ value: "stored",
3937
+ label: "API Key",
3938
+ hint: "enter interactively, stored in ~/.alchemy/credentials"
3939
+ }];
3940
+ /**
3941
+ * Layer that registers the Neon {@link AuthProvider} into the
3942
+ * {@link AuthProviders} registry.
3943
+ */
3944
+ const NeonAuth = AuthProviderLayer()(NEON_AUTH_PROVIDER_NAME, Effect.gen(function* () {
3945
+ const profiles = yield* AlchemyProfile;
3946
+ const store = yield* CredentialsStore;
3947
+ const loginStored = Effect.fn(function* (profileName) {
3948
+ const apiKey = yield* password({
3949
+ message: "Neon API Key",
3950
+ validate: (v) => v.length === 0 ? "Required" : void 0
3951
+ }).pipe(retryOnce);
3952
+ yield* store.write(profileName, "neon-stored", {
3953
+ type: "apiKey",
3954
+ apiKey
3955
+ });
3956
+ yield* success("Neon: credentials saved.");
3957
+ return { method: "stored" };
3958
+ });
3959
+ const configureInteractive = (profileName) => select({
3960
+ message: "Neon authentication method",
3961
+ options: options$1
3962
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
3963
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
3964
+ if (ctx.ci) return { method: "env" };
3965
+ return yield* configureInteractive(profileName);
3966
+ }).pipe(Effect.mapError((e) => new AuthError({
3967
+ message: "failed to configure credentials",
3968
+ cause: e
3969
+ })));
3970
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
3971
+ const apiKey = yield* getEnvRedacted("NEON_API_KEY");
3972
+ if (!apiKey) return yield* new AuthError({ message: "Neon env credentials not found. Set NEON_API_KEY." });
3973
+ return {
3974
+ type: "apiKey",
3975
+ apiKey,
3976
+ source: { type: "env" }
3977
+ };
3978
+ })), Match.when({ method: "stored" }, () => store.read(profileName, "neon-stored").pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "Neon stored credentials not found. Run: alchemy login --configure" })) : Effect.succeed({
3979
+ type: "apiKey",
3980
+ apiKey: Redacted.make(creds.apiKey),
3981
+ source: { type: "stored" }
3982
+ })))), Match.exhaustive);
3983
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.delete(profileName, "neon-stored").pipe(Effect.andThen(success("Neon: stored credentials removed")))), Match.exhaustive);
3984
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => getEnvRedacted("NEON_API_KEY").pipe(Effect.flatMap((apiKey) => apiKey ? Effect.void : Effect.gen(function* () {
3985
+ const next = yield* configureInteractive(profileName);
3986
+ const existing = yield* profiles.getProfile(profileName);
3987
+ yield* profiles.setProfile(profileName, {
3988
+ ...existing,
3989
+ [NEON_AUTH_PROVIDER_NAME]: next
3990
+ });
3991
+ })))), Match.when({ method: "stored" }, () => store.read(profileName, "neon-stored").pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
3992
+ message: "login failed",
3993
+ cause: e
3994
+ })));
3995
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap((creds) => {
3996
+ const sourceStr = creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type;
3997
+ return Effect.all([Console.log(` apiKey: ${displayRedacted(creds.apiKey, 9)}`), Console.log(` source: ${sourceStr}`)]);
3998
+ }));
3999
+ return {
4000
+ configure: configureCredentials,
4001
+ logout,
4002
+ login,
4003
+ prettyPrint,
4004
+ read: resolveCredentials
4005
+ };
4006
+ }));
4007
+
4008
+ //#endregion
4009
+ //#region lib/Planetscale/OAuthClient.js
4010
+ init_AuthProvider();
4011
+ var OAuthError = class extends Data.TaggedError("OAuthError") {};
4012
+ /**
4013
+ * Registered PlanetScale OAuth application credentials.
4014
+ *
4015
+ * Unlike Cloudflare, PlanetScale OAuth has **no public-client flow** — the
4016
+ * token endpoint requires client authentication for every grant (PKCE is
4017
+ * advertised in their discovery doc but does not lift that requirement), so
4018
+ * exchanging the authorization code (and refreshing the token) requires the
4019
+ * application's `client_secret`. There is no way to keep that secret out of
4020
+ * a distributed CLI, so it ships here: the exposure is the same posture as a
4021
+ * public `client_id` (a stolen refresh token is usable, exactly like
4022
+ * Cloudflare's secret-less refresh), and it can be rotated by cutting a new
4023
+ * release. PlanetScale's own CLI ships its OAuth `client_secret` in source
4024
+ * the same way:
4025
+ * https://github.com/planetscale/cli/blob/main/internal/auth/authenticator.go
4026
+ *
4027
+ * Registered at https://app.planetscale.com with redirect URI
4028
+ * {@link OAUTH_REDIRECT_URI}. Scopes are configured on the application
4029
+ * itself, not requested per-authorization. Rotate by registering a new
4030
+ * secret and cutting a release.
4031
+ */
4032
+ const OAUTH_CLIENT_ID = "pscale_app_aa12e3938baebb788aac443f66e422da";
4033
+ const OAUTH_CLIENT_SECRET = "pscale_app_secret_yyZ3Q8oe99GP9_yA5wrA5er6RuN6Lz9dC66Bj1OJzpg";
4034
+ const OAUTH_REDIRECT_URI = "http://localhost:9976/auth/callback";
4035
+ const OAUTH_ENDPOINTS = {
4036
+ authorize: "https://app.planetscale.com/oauth/authorize",
4037
+ token: "https://auth.planetscale.com/oauth/token"
4038
+ };
4039
+ function generateState(length = 32) {
4040
+ return crypto$1.randomBytes(length).toString("base64url");
4041
+ }
4042
+ function extractCredentials(json) {
4043
+ return {
4044
+ type: "oauth",
4045
+ access: json.access_token,
4046
+ refresh: json.refresh_token,
4047
+ expires: Date.now() + json.expires_in * 1e3,
4048
+ scopes: json.scope ? json.scope.split(" ") : []
4049
+ };
4050
+ }
4051
+ const tokenRequest = (params) => Effect.gen(function* () {
4052
+ const url = new URL(OAUTH_ENDPOINTS.token);
4053
+ for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
4054
+ const res = yield* Effect.tryPromise({
4055
+ try: () => fetch(url.toString(), {
4056
+ method: "POST",
4057
+ headers: { Accept: "application/json" }
4058
+ }),
4059
+ catch: (err) => new OAuthError({
4060
+ error: "network_error",
4061
+ errorDescription: `Token request failed: ${err}`
4062
+ })
4063
+ });
4064
+ if (!res.ok) {
4065
+ const json = yield* Effect.tryPromise({
4066
+ try: () => res.json(),
4067
+ catch: () => new OAuthError({
4068
+ error: "parse_error",
4069
+ errorDescription: `Token endpoint returned ${res.status}`
4070
+ })
4071
+ });
4072
+ return yield* new OAuthError({
4073
+ error: json.error,
4074
+ errorDescription: json.error_description
4075
+ });
4076
+ }
4077
+ return extractCredentials(yield* Effect.tryPromise({
4078
+ try: () => res.json(),
4079
+ catch: () => new OAuthError({
4080
+ error: "parse_error",
4081
+ errorDescription: "Failed to parse token response"
4082
+ })
4083
+ }));
4084
+ });
4085
+ /**
4086
+ * Generate a PlanetScale authorization URL.
4087
+ *
4088
+ * No `scope` parameter is sent: PlanetScale scopes are configured on the
4089
+ * OAuth application itself, not requested per-authorization, so the
4090
+ * consent screen shows whatever the app is registered with.
4091
+ */
4092
+ function authorize() {
4093
+ const state = generateState();
4094
+ const url = new URL(OAUTH_ENDPOINTS.authorize);
4095
+ url.searchParams.set("client_id", OAUTH_CLIENT_ID);
4096
+ url.searchParams.set("redirect_uri", OAUTH_REDIRECT_URI);
4097
+ url.searchParams.set("response_type", "code");
4098
+ url.searchParams.set("state", state);
4099
+ return {
4100
+ url: url.toString(),
4101
+ state
4102
+ };
4103
+ }
4104
+ /**
4105
+ * Exchange an authorization code for OAuth credentials.
4106
+ */
4107
+ const exchange = (code) => tokenRequest({
4108
+ grant_type: "authorization_code",
4109
+ code,
4110
+ client_id: OAUTH_CLIENT_ID,
4111
+ client_secret: OAUTH_CLIENT_SECRET,
4112
+ redirect_uri: OAUTH_REDIRECT_URI
4113
+ });
4114
+ /**
4115
+ * Refresh expired OAuth credentials.
4116
+ */
4117
+ const refresh = (credentials) => tokenRequest({
4118
+ grant_type: "refresh_token",
4119
+ refresh_token: credentials.refresh,
4120
+ client_id: OAUTH_CLIENT_ID,
4121
+ client_secret: OAUTH_CLIENT_SECRET
4122
+ });
4123
+ /**
4124
+ * Start a local HTTP server to listen for the OAuth callback, exchange
4125
+ * the authorization code, and return the credentials.
4126
+ *
4127
+ * Times out after 5 minutes.
4128
+ */
4129
+ const callback = (authorization) => Effect.tryPromise({
4130
+ try: () => callbackPromise(authorization),
4131
+ catch: (err) => {
4132
+ if (err instanceof OAuthError) return err;
4133
+ return new OAuthError({
4134
+ error: "callback_error",
4135
+ errorDescription: `OAuth callback failed: ${err}`
4136
+ });
4137
+ }
4138
+ });
4139
+ function callbackPromise(authorization) {
4140
+ const { pathname, port } = new URL(OAUTH_REDIRECT_URI);
4141
+ return new Promise((resolve, reject) => {
4142
+ const server = http.createServer(async (req, res) => {
4143
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
4144
+ if (url.pathname !== pathname) {
4145
+ res.statusCode = 404;
4146
+ res.end("Not Found");
4147
+ return;
4148
+ }
4149
+ const error = url.searchParams.get("error");
4150
+ const errorDescription = url.searchParams.get("error_description");
4151
+ if (error) {
4152
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
4153
+ res.end();
4154
+ cleanup();
4155
+ reject(new OAuthError({
4156
+ error,
4157
+ errorDescription: errorDescription ?? "An unknown error occurred."
4158
+ }));
4159
+ return;
4160
+ }
4161
+ const code = url.searchParams.get("code");
4162
+ const state = url.searchParams.get("state");
4163
+ if (!code || !state) {
4164
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
4165
+ res.end();
4166
+ cleanup();
4167
+ reject(new OAuthError({
4168
+ error: "invalid_request",
4169
+ errorDescription: "Missing code or state"
4170
+ }));
4171
+ return;
4172
+ }
4173
+ if (state !== authorization.state) {
4174
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
4175
+ res.end();
4176
+ cleanup();
4177
+ reject(new OAuthError({
4178
+ error: "invalid_request",
4179
+ errorDescription: "Invalid state"
4180
+ }));
4181
+ return;
4182
+ }
4183
+ try {
4184
+ const credentials = await Effect.runPromise(exchange(code));
4185
+ res.writeHead(302, { Location: AUTH_SUCCESS_URL });
4186
+ res.end();
4187
+ cleanup();
4188
+ resolve(credentials);
4189
+ } catch (err) {
4190
+ res.writeHead(302, { Location: AUTH_ERROR_URL });
4191
+ res.end();
4192
+ cleanup();
4193
+ reject(err);
4194
+ }
4195
+ });
4196
+ const timeout = setTimeout(() => {
4197
+ cleanup();
4198
+ reject(new OAuthError({
4199
+ error: "timeout",
4200
+ errorDescription: "The authorization process timed out."
4201
+ }));
4202
+ }, 300 * 1e3);
4203
+ function cleanup() {
4204
+ clearTimeout(timeout);
4205
+ server.close();
4206
+ }
4207
+ server.on("error", (err) => {
4208
+ cleanup();
4209
+ reject(new OAuthError({
4210
+ error: "server_error",
4211
+ errorDescription: `Failed to start callback server: ${err.message}`
4212
+ }));
4213
+ });
4214
+ server.listen(Number(port));
4215
+ });
4216
+ }
4217
+
4218
+ //#endregion
4219
+ //#region lib/Planetscale/AuthProvider.js
4220
+ init_AuthProvider();
4221
+ init_Credentials();
4222
+ /**
4223
+ * Canonical name registered in {@link AuthProviders}. Use this key to look
4224
+ * up the PlanetScale {@link AuthProvider} from inside provider Layers.
4225
+ */
4226
+ const PLANETSCALE_AUTH_PROVIDER_NAME = "Planetscale";
4227
+ /**
4228
+ * Provide PlanetScale `Credentials` + `HttpClient` to an Effect using a
4229
+ * just-obtained OAuth access token. Used during configure to call
4230
+ * org-discovery endpoints before the user has chosen an org.
4231
+ *
4232
+ * `organization` is required by the credential type but isn't consulted by
4233
+ * `listOrganizations` (it's a user-scoped endpoint), so an empty string is
4234
+ * fine here.
4235
+ */
4236
+ const withOAuthCredentials = (accessToken, effect) => Effect.provide(effect, Layer.mergeAll(PsCredentialsModule.fromOAuth({
4237
+ accessToken,
4238
+ organization: ""
4239
+ }), FetchHttpClient.layer));
4240
+ /**
4241
+ * List the organizations the OAuth user belongs to and either auto-pick
4242
+ * (one org) or prompt the user to choose. Returns the org's URL slug
4243
+ * (`name` field, used as `{organization}` in API paths).
4244
+ */
4245
+ const selectOrganization = (accessToken) => Effect.gen(function* () {
4246
+ const orgs = (yield* (yield* listOrganizations)({})).data;
4247
+ if (orgs.length === 0) return yield* new AuthError({ message: "Planetscale: no organizations found for this credential." });
4248
+ if (orgs.length === 1) {
4249
+ const org = orgs[0];
4250
+ yield* info(`Planetscale: using organization: ${org.name} (${org.id})`);
4251
+ return org.name;
4252
+ }
4253
+ return yield* select({
4254
+ message: "Select a Planetscale organization",
4255
+ options: orgs.map((o) => ({
4256
+ value: o.name,
4257
+ label: o.name,
4258
+ hint: o.id
4259
+ }))
4260
+ }).pipe(retryOnce);
4261
+ }).pipe((e) => withOAuthCredentials(accessToken, e));
4262
+ const options = [
4263
+ {
4264
+ value: "env",
4265
+ label: "Environment Variables",
4266
+ hint: "PLANETSCALE_API_TOKEN_ID + PLANETSCALE_API_TOKEN + PLANETSCALE_ORGANIZATION"
4267
+ },
4268
+ {
4269
+ value: "oauth",
4270
+ label: "OAuth",
4271
+ hint: "recommended — browser-based login with automatic token refresh"
4272
+ },
4273
+ {
4274
+ value: "stored",
4275
+ label: "Service Token",
4276
+ hint: "enter service token interactively, stored in ~/.alchemy/credentials"
4277
+ }
4278
+ ];
4279
+ /**
4280
+ * Layer that registers the PlanetScale {@link AuthProvider} into the
4281
+ * {@link AuthProviders} registry when built. Include this in the
4282
+ * PlanetScale `providers()` layer so `alchemy login` can discover it.
4283
+ *
4284
+ * Supported methods:
4285
+ * - `env`: reads `PLANETSCALE_API_TOKEN_ID`/`PLANETSCALE_API_TOKEN`/`PLANETSCALE_ORGANIZATION`.
4286
+ * - `stored`: prompts for a service token interactively and writes it to
4287
+ * `~/.alchemy/credentials/<profile>/planetscale-stored.json`.
4288
+ * - `oauth`: browser-based login storing access/refresh tokens at
4289
+ * `~/.alchemy/credentials/<profile>/planetscale-oauth.json`.
4290
+ */
4291
+ const PlanetscaleAuth = AuthProviderLayer()(PLANETSCALE_AUTH_PROVIDER_NAME, Effect.gen(function* () {
4292
+ const store = yield* CredentialsStore;
4293
+ const oauthLogin = (profileName) => Effect.gen(function* () {
4294
+ const authorization = authorize();
4295
+ yield* info("Planetscale: opening browser for OAuth login...");
4296
+ yield* info(authorization.url);
4297
+ yield* openUrl(authorization.url).pipe(Effect.catch(() => warn("Planetscale: could not open browser automatically. Please open the URL above manually.")));
4298
+ yield* info("Planetscale: waiting for authorization (up to 5 minutes)...");
4299
+ const credentials = yield* callback(authorization);
4300
+ yield* store.write(profileName, "planetscale-oauth", credentials);
4301
+ yield* success("Planetscale: OAuth credentials saved.");
4302
+ return credentials;
4303
+ });
4304
+ const configureOAuth = Effect.fn(function* (profileName) {
4305
+ return {
4306
+ method: "oauth",
4307
+ organization: yield* selectOrganization((yield* oauthLogin(profileName)).access).pipe(Effect.catch((e) => Effect.gen(function* () {
4308
+ yield* warn(`Planetscale: could not auto-list organizations (${String(e)}). Falling back to manual entry.`);
4309
+ return yield* text({
4310
+ message: "Planetscale Organization (URL slug)",
4311
+ validate: (v) => v.length === 0 ? "Required" : void 0
4312
+ }).pipe(retryOnce);
4313
+ })))
4314
+ };
4315
+ });
4316
+ const loginStored = Effect.fn(function* (profileName) {
4317
+ const tokenId = yield* text({
4318
+ message: "Planetscale Service Token ID",
4319
+ validate: (v) => v.length === 0 ? "Required" : void 0
4320
+ }).pipe(retryOnce);
4321
+ const token = yield* password({
4322
+ message: "Planetscale Service Token",
4323
+ validate: (v) => v.length === 0 ? "Required" : void 0
4324
+ }).pipe(retryOnce);
4325
+ const organization = yield* text({
4326
+ message: "Planetscale Organization (URL slug)",
4327
+ validate: (v) => v.length === 0 ? "Required" : void 0
4328
+ }).pipe(retryOnce);
4329
+ yield* store.write(profileName, "planetscale-stored", {
4330
+ type: "apiToken",
4331
+ tokenId,
4332
+ token,
4333
+ organization
4334
+ });
4335
+ yield* success("Planetscale: credentials saved.");
4336
+ return { method: "stored" };
4337
+ });
4338
+ const configureInteractive = (profileName) => select({
4339
+ message: "Planetscale authentication method",
4340
+ options
4341
+ }).pipe(Effect.flatMap((method) => Match.value(method).pipe(Match.when("env", () => Effect.succeed({ method: "env" })), Match.when("oauth", () => configureOAuth(profileName)), Match.when("stored", () => loginStored(profileName)), Match.exhaustive)));
4342
+ const configureCredentials = (profileName, ctx) => Effect.gen(function* () {
4343
+ if (ctx.ci) return { method: "env" };
4344
+ return yield* configureInteractive(profileName);
4345
+ }).pipe(Effect.mapError((e) => new AuthError({
4346
+ message: "failed to configure credentials",
4347
+ cause: e
4348
+ })));
4349
+ const resolveCredentials = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, Effect.fn(function* () {
4350
+ return {
4351
+ type: "apiToken",
4352
+ tokenId: yield* getEnvRedactedRequired("PLANETSCALE_API_TOKEN_ID"),
4353
+ token: yield* getEnvRedactedRequired("PLANETSCALE_API_TOKEN"),
4354
+ organization: yield* getEnvRequired("PLANETSCALE_ORGANIZATION"),
4355
+ source: {
4356
+ type: "env",
4357
+ details: "PLANETSCALE_API_TOKEN_ID/PLANETSCALE_API_TOKEN"
4358
+ }
4359
+ };
4360
+ })), Match.when({ method: "stored" }, () => store.read(profileName, "planetscale-stored").pipe(Effect.flatMap((creds) => creds == null ? Effect.fail(new AuthError({ message: "Planetscale stored credentials not found. Run: alchemy login --configure" })) : Effect.succeed({
4361
+ type: "apiToken",
4362
+ tokenId: Redacted.make(creds.tokenId),
4363
+ token: Redacted.make(creds.token),
4364
+ organization: creds.organization,
4365
+ source: {
4366
+ type: "stored",
4367
+ details: void 0
4368
+ }
4369
+ })))), Match.when({ method: "oauth" }, (cfg) => Effect.gen(function* () {
4370
+ const creds = yield* store.read(profileName, "planetscale-oauth");
4371
+ if (creds == null || creds.type !== "oauth") return yield* Effect.fail(new AuthError({ message: "Planetscale OAuth credentials not found. Run: alchemy login" }));
4372
+ const fresh = creds.expires > Date.now() + 1e4 ? creds : yield* refresh(creds).pipe(Effect.tap((refreshed) => store.write(profileName, "planetscale-oauth", refreshed)), Effect.mapError((e) => new AuthError({
4373
+ message: "Planetscale OAuth refresh failed. Run: alchemy login",
4374
+ cause: e
4375
+ })));
4376
+ return {
4377
+ type: "oauth",
4378
+ accessToken: Redacted.make(fresh.access),
4379
+ expires: fresh.expires,
4380
+ organization: cfg.organization,
4381
+ source: { type: "oauth" }
4382
+ };
4383
+ })), Match.exhaustive);
4384
+ const logout = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.delete(profileName, "planetscale-stored").pipe(Effect.andThen(success("Planetscale: stored credentials removed")))), Match.when({ method: "oauth" }, () => store.delete(profileName, "planetscale-oauth").pipe(Effect.andThen(success("Planetscale: OAuth credentials removed.")))), Match.exhaustive);
4385
+ const login = (profileName, config) => Match.value(config).pipe(Match.when({ method: "env" }, () => Effect.void), Match.when({ method: "stored" }, () => store.read(profileName, "planetscale-stored").pipe(Effect.flatMap((creds) => creds == null ? loginStored(profileName) : Effect.void))), Match.when({ method: "oauth" }, (c$1) => Effect.gen(function* () {
4386
+ const creds = yield* store.read(profileName, "planetscale-oauth");
4387
+ if (creds?.type === "oauth") {
4388
+ yield* info("Planetscale: refreshing OAuth credentials...");
4389
+ yield* refresh(creds).pipe(Effect.flatMap((refreshed) => store.write(profileName, "planetscale-oauth", refreshed).pipe(Effect.andThen(success("Planetscale: OAuth credentials refreshed.")))), Effect.catchTag("OAuthError", () => oauthLogin(profileName).pipe(Effect.asVoid)));
4390
+ return;
4391
+ }
4392
+ yield* oauthLogin(profileName);
4393
+ })), Match.exhaustive).pipe(Effect.mapError((e) => new AuthError({
4394
+ message: "login failed",
4395
+ cause: e
4396
+ })));
4397
+ const prettyPrint = (profileName, config) => resolveCredentials(profileName, config).pipe(Effect.tap((creds) => {
4398
+ const sourceStr = creds.source.details ? `${creds.source.type} - ${creds.source.details}` : creds.source.type;
4399
+ return Match.value(creds).pipe(Match.when({ type: "apiToken" }, (c$1) => Effect.all([
4400
+ Console.log(` tokenId: ${displayRedacted(c$1.tokenId, 3)}`),
4401
+ Console.log(` token: ${displayRedacted(c$1.token, 6)}`),
4402
+ Console.log(` organization: ${c$1.organization}`),
4403
+ Console.log(` source: ${sourceStr}`)
4404
+ ])), Match.when({ type: "oauth" }, (c$1) => {
4405
+ const remainingMs = c$1.expires - Date.now();
4406
+ const expiresAt = new Date(c$1.expires).toISOString();
4407
+ const expiresStr = remainingMs <= 0 ? `expired (${expiresAt})` : `in ${Duration.format(Duration.millis(remainingMs))} (${expiresAt})`;
4408
+ return Effect.all([
4409
+ Console.log(` accessToken: ${displayRedacted(c$1.accessToken)}`),
4410
+ Console.log(` expires: ${expiresStr}`),
4411
+ Console.log(` organization: ${c$1.organization}`),
4412
+ Console.log(` source: ${sourceStr}`)
4413
+ ]);
4414
+ }), Match.exhaustive);
4415
+ }));
4416
+ return {
4417
+ configure: configureCredentials,
4418
+ logout,
4419
+ login,
4420
+ prettyPrint,
4421
+ read: resolveCredentials
4422
+ };
4423
+ }));
4424
+
2549
4425
  //#endregion
2550
4426
  //#region lib/Cli/commands/_shared.js
2551
4427
  init_AuthProvider();
@@ -2554,11 +4430,11 @@ init_Stack();
2554
4430
  init_Stage();
2555
4431
  init_ConfigProvider();
2556
4432
  init_FileLogger();
2557
- var __rewriteRelativeImportExtension = void 0 && (void 0).__rewriteRelativeImportExtension || function(path$1, preserveJsx) {
2558
- if (typeof path$1 === "string" && /^\.\.?\//.test(path$1)) return path$1.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
4433
+ var __rewriteRelativeImportExtension = void 0 && (void 0).__rewriteRelativeImportExtension || function(path$2, preserveJsx) {
4434
+ if (typeof path$2 === "string" && /^\.\.?\//.test(path$2)) return path$2.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
2559
4435
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
2560
4436
  });
2561
- return path$1;
4437
+ return path$2;
2562
4438
  };
2563
4439
  const USER = Config.string("USER").pipe(Config.orElse(() => Config.string("USERNAME")), Config.withDefault("unknown"));
2564
4440
  const STAGE = Config.string("stage").pipe(Config.option, (a) => a, Effect.map(Option.getOrUndefined));
@@ -2661,16 +4537,32 @@ const placeholderStack = (name) => ({
2661
4537
  * entrypoint is fatal (login / nuke let it propagate) or best-effort
2662
4538
  * (profile show wraps the call in `Effect.catchCause`).
2663
4539
  */
2664
- const buildStackProviders = Effect.fn("buildStackProviders")(function* (options) {
2665
- const authProviders = options.registry ?? {};
2666
- const stackEffect = yield* importStack(options.main);
2667
- const configProvider = withProfileOverride(yield* loadConfigProvider(options.envFile), options.profile);
4540
+ const buildStackProviders = Effect.fn("buildStackProviders")(function* (options$6) {
4541
+ const authProviders = options$6.registry ?? {};
4542
+ const stackEffect = yield* importStack(options$6.main);
4543
+ const configProvider = withProfileOverride(yield* loadConfigProvider(options$6.envFile), options$6.profile);
2668
4544
  return {
2669
4545
  authProviders,
2670
- context: yield* Layer.build((stackEffect.providers ?? Layer.empty).pipe(Layer.provideMerge(stackEffect.state ?? Layer.empty), Layer.provideMerge(Layer.mergeAll(Layer.succeed(AuthProviders, authProviders), ConfigProvider.layer(configProvider), options.logger ?? Logger.layer([fileLogger("out")], { mergeWithExisting: true }), Layer.succeed(Stage, "placeholder"), Layer.succeed(Stack, placeholderStack(stackEffect.stackName)), options.extra ?? Layer.empty)))),
4546
+ context: yield* Layer.build((stackEffect.providers ?? Layer.empty).pipe(Layer.provideMerge(stackEffect.state ?? Layer.empty), Layer.provideMerge(Layer.mergeAll(Layer.succeed(AuthProviders, authProviders), ConfigProvider.layer(configProvider), options$6.logger ?? Logger.layer([fileLogger("out")], { mergeWithExisting: true }), Layer.succeed(Stage, "placeholder"), Layer.succeed(Stack, placeholderStack(stackEffect.stackName)), options$6.extra ?? Layer.empty)))),
2671
4547
  stackEffect
2672
4548
  };
2673
4549
  });
4550
+ /**
4551
+ * The auth providers Alchemy ships with. Used as the baseline registry so
4552
+ * `alchemy login` works from any folder (no `alchemy.run.ts` required) and
4553
+ * `alchemy profile show` can pretty-print any provider a profile mentions,
4554
+ * even one the current stack doesn't wire up.
4555
+ */
4556
+ const builtinAuth = Layer.mergeAll(AwsAuth, AxiomAuth, CloudflareAuth, GitHubAuth, NeonAuth, PlanetscaleAuth);
4557
+ /**
4558
+ * Build {@link builtinAuth} against `registry` so every built-in auth
4559
+ * provider registers itself, without importing any stack entrypoint.
4560
+ */
4561
+ const buildBuiltinAuthProviders = Effect.fn("buildBuiltinAuthProviders")(function* (options$6) {
4562
+ const authProviders = options$6.registry ?? {};
4563
+ yield* Layer.build(Layer.provide(builtinAuth, Layer.mergeAll(Layer.succeed(AuthProviders, authProviders), ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(options$6.envFile), options$6.profile)), Logger.layer([fileLogger("out")], { mergeWithExisting: true }))));
4564
+ return authProviders;
4565
+ });
2674
4566
 
2675
4567
  //#endregion
2676
4568
  //#region lib/Cli/commands/deploy.js
@@ -2847,17 +4739,14 @@ const LoggingCli = Layer.succeed(Cli, Cli.of({
2847
4739
  fail: 0
2848
4740
  };
2849
4741
  return {
2850
- emit: (event) => Effect.sync(() => {
2851
- if (event.kind === "annotate") {
2852
- console.log(`${tag(event.id)} ${blue(event.message)}`);
2853
- return;
2854
- }
4742
+ emit: (event) => Effect.suspend(() => {
4743
+ if (event.kind === "annotate") return Console.log(`${tag(event.id)} ${blue(event.message)}`);
2855
4744
  const id = event.bindingId ? `${event.id}/${event.bindingId}` : event.id;
2856
4745
  const status = statusColor$1(event.status)(event.status);
2857
4746
  const msg = event.message ? ` ${dim("—")} ${event.message}` : "";
2858
- console.log(`${tag(id)} ${status}${msg}`);
2859
4747
  if (isTerminal(event.status)) if (event.status === "fail") counts.fail++;
2860
4748
  else counts.ok++;
4749
+ return Console.log(`${tag(id)} ${status}${msg}`);
2861
4750
  }),
2862
4751
  done: () => Console.log(`\n${bold("Done:")} ${green(`${counts.ok} succeeded`)}${counts.fail ? dim(", ") + red(`${counts.fail} failed`) : ""}`)
2863
4752
  };
@@ -2874,15 +4763,15 @@ function buildNamespaceTree(items, actions = []) {
2874
4763
  resources: [],
2875
4764
  actions: []
2876
4765
  };
2877
- const getNode = (path$1) => {
4766
+ const getNode = (path$2) => {
2878
4767
  let current = root;
2879
- for (let i = 0; i < path$1.length; i++) {
2880
- const segment = path$1[i];
4768
+ for (let i = 0; i < path$2.length; i++) {
4769
+ const segment = path$2[i];
2881
4770
  let child = current.children.get(segment);
2882
4771
  if (!child) {
2883
4772
  child = {
2884
4773
  id: segment,
2885
- path: path$1.slice(0, i + 1),
4774
+ path: path$2.slice(0, i + 1),
2886
4775
  children: /* @__PURE__ */ new Map(),
2887
4776
  resources: [],
2888
4777
  actions: []