@homeflare/alchemy 0.1.0

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 (417) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cloudflare/client.d.ts +45 -0
  4. package/dist/cloudflare/client.d.ts.map +1 -0
  5. package/dist/cloudflare/index.d.ts +11 -0
  6. package/dist/cloudflare/index.d.ts.map +1 -0
  7. package/dist/cloudflare/index.js +129 -0
  8. package/dist/cloudflare/index.js.map +13 -0
  9. package/dist/cloudflare/lock-rules.d.ts +50 -0
  10. package/dist/cloudflare/lock-rules.d.ts.map +1 -0
  11. package/dist/cloudflare/providers.d.ts +19 -0
  12. package/dist/cloudflare/providers.d.ts.map +1 -0
  13. package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
  14. package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
  15. package/dist/forgejo/branch-protection-form.d.ts +19 -0
  16. package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
  17. package/dist/forgejo/branch-protection.d.ts +47 -0
  18. package/dist/forgejo/branch-protection.d.ts.map +1 -0
  19. package/dist/forgejo/client.d.ts +37 -0
  20. package/dist/forgejo/client.d.ts.map +1 -0
  21. package/dist/forgejo/index.d.ts +18 -0
  22. package/dist/forgejo/index.d.ts.map +1 -0
  23. package/dist/forgejo/index.js +479 -0
  24. package/dist/forgejo/index.js.map +21 -0
  25. package/dist/forgejo/org-actions-secrets.d.ts +45 -0
  26. package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
  27. package/dist/forgejo/org-label.d.ts +46 -0
  28. package/dist/forgejo/org-label.d.ts.map +1 -0
  29. package/dist/forgejo/org-team.d.ts +42 -0
  30. package/dist/forgejo/org-team.d.ts.map +1 -0
  31. package/dist/forgejo/repo-webhook-form.d.ts +27 -0
  32. package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
  33. package/dist/forgejo/repo-webhook.d.ts +46 -0
  34. package/dist/forgejo/repo-webhook.d.ts.map +1 -0
  35. package/dist/forgejo/repository.d.ts +45 -0
  36. package/dist/forgejo/repository.d.ts.map +1 -0
  37. package/dist/forgejo/resource.d.ts +83 -0
  38. package/dist/forgejo/resource.d.ts.map +1 -0
  39. package/dist/forgejo/team-member.d.ts +39 -0
  40. package/dist/forgejo/team-member.d.ts.map +1 -0
  41. package/dist/forgejo/values.d.ts +24 -0
  42. package/dist/forgejo/values.d.ts.map +1 -0
  43. package/dist/index.d.ts +18 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +9 -0
  47. package/dist/openbao/auth-role-form.d.ts +38 -0
  48. package/dist/openbao/auth-role-form.d.ts.map +1 -0
  49. package/dist/openbao/auth-role.d.ts +20 -0
  50. package/dist/openbao/auth-role.d.ts.map +1 -0
  51. package/dist/openbao/bao-address.d.ts +72 -0
  52. package/dist/openbao/bao-address.d.ts.map +1 -0
  53. package/dist/openbao/bao-http.d.ts +71 -0
  54. package/dist/openbao/bao-http.d.ts.map +1 -0
  55. package/dist/openbao/bao-status.d.ts +74 -0
  56. package/dist/openbao/bao-status.d.ts.map +1 -0
  57. package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
  58. package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
  59. package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
  60. package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
  61. package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
  62. package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
  63. package/dist/openbao/cloudflare-parity.d.ts +14 -0
  64. package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
  65. package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
  66. package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
  67. package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
  68. package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
  69. package/dist/openbao/cloudflare-policy.d.ts +65 -0
  70. package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
  71. package/dist/openbao/cloudflare-role-form.d.ts +110 -0
  72. package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
  73. package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
  74. package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
  75. package/dist/openbao/cloudflare-role.d.ts +35 -0
  76. package/dist/openbao/cloudflare-role.d.ts.map +1 -0
  77. package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
  78. package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
  79. package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
  80. package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
  81. package/dist/openbao/digest.d.ts +24 -0
  82. package/dist/openbao/digest.d.ts.map +1 -0
  83. package/dist/openbao/fake-bao.d.ts +40 -0
  84. package/dist/openbao/fake-bao.d.ts.map +1 -0
  85. package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
  86. package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
  87. package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
  88. package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
  89. package/dist/openbao/index.d.ts +18 -0
  90. package/dist/openbao/index.d.ts.map +1 -0
  91. package/dist/openbao/index.js +1158 -0
  92. package/dist/openbao/index.js.map +33 -0
  93. package/dist/openbao/mount-form.d.ts +60 -0
  94. package/dist/openbao/mount-form.d.ts.map +1 -0
  95. package/dist/openbao/mount-wire.d.ts +24 -0
  96. package/dist/openbao/mount-wire.d.ts.map +1 -0
  97. package/dist/openbao/mount.d.ts +20 -0
  98. package/dist/openbao/mount.d.ts.map +1 -0
  99. package/dist/openbao/pki-role-form.d.ts +118 -0
  100. package/dist/openbao/pki-role-form.d.ts.map +1 -0
  101. package/dist/openbao/pki-role.d.ts +50 -0
  102. package/dist/openbao/pki-role.d.ts.map +1 -0
  103. package/dist/openbao/policy-assembly.d.ts +13 -0
  104. package/dist/openbao/policy-assembly.d.ts.map +1 -0
  105. package/dist/openbao/policy-wire.d.ts +41 -0
  106. package/dist/openbao/policy-wire.d.ts.map +1 -0
  107. package/dist/openbao/policy.d.ts +83 -0
  108. package/dist/openbao/policy.d.ts.map +1 -0
  109. package/dist/openbao/proxmox-role-form.d.ts +92 -0
  110. package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
  111. package/dist/openbao/proxmox-role.d.ts +47 -0
  112. package/dist/openbao/proxmox-role.d.ts.map +1 -0
  113. package/dist/openbao/ssh-role-form.d.ts +99 -0
  114. package/dist/openbao/ssh-role-form.d.ts.map +1 -0
  115. package/dist/openbao/ssh-role.d.ts +43 -0
  116. package/dist/openbao/ssh-role.d.ts.map +1 -0
  117. package/dist/proxmox/acl.d.ts +72 -0
  118. package/dist/proxmox/acl.d.ts.map +1 -0
  119. package/dist/proxmox/api-token-form.d.ts +59 -0
  120. package/dist/proxmox/api-token-form.d.ts.map +1 -0
  121. package/dist/proxmox/api-token.d.ts +159 -0
  122. package/dist/proxmox/api-token.d.ts.map +1 -0
  123. package/dist/proxmox/backup-job.d.ts +112 -0
  124. package/dist/proxmox/backup-job.d.ts.map +1 -0
  125. package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
  126. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
  127. package/dist/proxmox/ceph-daemon.d.ts +162 -0
  128. package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
  129. package/dist/proxmox/ceph-flag.d.ts +131 -0
  130. package/dist/proxmox/ceph-flag.d.ts.map +1 -0
  131. package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
  132. package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
  133. package/dist/proxmox/ceph-fs.d.ts +124 -0
  134. package/dist/proxmox/ceph-fs.d.ts.map +1 -0
  135. package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
  136. package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
  137. package/dist/proxmox/ceph-osd-write.d.ts +36 -0
  138. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
  139. package/dist/proxmox/ceph-osd.d.ts +102 -0
  140. package/dist/proxmox/ceph-osd.d.ts.map +1 -0
  141. package/dist/proxmox/ceph-pool-form.d.ts +217 -0
  142. package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
  143. package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
  144. package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
  145. package/dist/proxmox/ceph-pool.d.ts +48 -0
  146. package/dist/proxmox/ceph-pool.d.ts.map +1 -0
  147. package/dist/proxmox/client.d.ts +64 -0
  148. package/dist/proxmox/client.d.ts.map +1 -0
  149. package/dist/proxmox/credentials.d.ts +153 -0
  150. package/dist/proxmox/credentials.d.ts.map +1 -0
  151. package/dist/proxmox/firewall-alias-form.d.ts +98 -0
  152. package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
  153. package/dist/proxmox/firewall-alias.d.ts +141 -0
  154. package/dist/proxmox/firewall-alias.d.ts.map +1 -0
  155. package/dist/proxmox/generated/pbs.d.ts +388 -0
  156. package/dist/proxmox/generated/pbs.d.ts.map +1 -0
  157. package/dist/proxmox/generated/pve.d.ts +4713 -0
  158. package/dist/proxmox/generated/pve.d.ts.map +1 -0
  159. package/dist/proxmox/group.d.ts +104 -0
  160. package/dist/proxmox/group.d.ts.map +1 -0
  161. package/dist/proxmox/ha-resource.d.ts +104 -0
  162. package/dist/proxmox/ha-resource.d.ts.map +1 -0
  163. package/dist/proxmox/ha-rule-form.d.ts +103 -0
  164. package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
  165. package/dist/proxmox/ha-rule.d.ts +156 -0
  166. package/dist/proxmox/ha-rule.d.ts.map +1 -0
  167. package/dist/proxmox/index.d.ts +43 -0
  168. package/dist/proxmox/index.d.ts.map +1 -0
  169. package/dist/proxmox/index.js +2322 -0
  170. package/dist/proxmox/index.js.map +70 -0
  171. package/dist/proxmox/lease-cache.d.ts +98 -0
  172. package/dist/proxmox/lease-cache.d.ts.map +1 -0
  173. package/dist/proxmox/lxc.d.ts +59 -0
  174. package/dist/proxmox/lxc.d.ts.map +1 -0
  175. package/dist/proxmox/members.d.ts +56 -0
  176. package/dist/proxmox/members.d.ts.map +1 -0
  177. package/dist/proxmox/metric-server-form.d.ts +53 -0
  178. package/dist/proxmox/metric-server-form.d.ts.map +1 -0
  179. package/dist/proxmox/metric-server-otel.d.ts +49 -0
  180. package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
  181. package/dist/proxmox/metric-server.d.ts +123 -0
  182. package/dist/proxmox/metric-server.d.ts.map +1 -0
  183. package/dist/proxmox/network-apply-read.d.ts +93 -0
  184. package/dist/proxmox/network-apply-read.d.ts.map +1 -0
  185. package/dist/proxmox/network-apply.d.ts +102 -0
  186. package/dist/proxmox/network-apply.d.ts.map +1 -0
  187. package/dist/proxmox/node-network-form.d.ts +128 -0
  188. package/dist/proxmox/node-network-form.d.ts.map +1 -0
  189. package/dist/proxmox/node-network.d.ts +173 -0
  190. package/dist/proxmox/node-network.d.ts.map +1 -0
  191. package/dist/proxmox/notification-target-form.d.ts +44 -0
  192. package/dist/proxmox/notification-target-form.d.ts.map +1 -0
  193. package/dist/proxmox/notification-target.d.ts +146 -0
  194. package/dist/proxmox/notification-target.d.ts.map +1 -0
  195. package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
  196. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
  197. package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
  198. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
  199. package/dist/proxmox/pbs-datastore.d.ts +140 -0
  200. package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
  201. package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
  202. package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
  203. package/dist/proxmox/pbs-prune-job.d.ts +168 -0
  204. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
  205. package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
  206. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
  207. package/dist/proxmox/pbs-sync-job.d.ts +246 -0
  208. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
  209. package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
  210. package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
  211. package/dist/proxmox/pbs-verify-job.d.ts +145 -0
  212. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
  213. package/dist/proxmox/pool.d.ts +42 -0
  214. package/dist/proxmox/pool.d.ts.map +1 -0
  215. package/dist/proxmox/qemu.d.ts +46 -0
  216. package/dist/proxmox/qemu.d.ts.map +1 -0
  217. package/dist/proxmox/replication-job-form.d.ts +71 -0
  218. package/dist/proxmox/replication-job-form.d.ts.map +1 -0
  219. package/dist/proxmox/replication-job.d.ts +162 -0
  220. package/dist/proxmox/replication-job.d.ts.map +1 -0
  221. package/dist/proxmox/resource.d.ts +157 -0
  222. package/dist/proxmox/resource.d.ts.map +1 -0
  223. package/dist/proxmox/role.d.ts +97 -0
  224. package/dist/proxmox/role.d.ts.map +1 -0
  225. package/dist/proxmox/sdn-apply-read.d.ts +80 -0
  226. package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
  227. package/dist/proxmox/sdn-apply.d.ts +63 -0
  228. package/dist/proxmox/sdn-apply.d.ts.map +1 -0
  229. package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
  230. package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
  231. package/dist/proxmox/sdn-subnet.d.ts +161 -0
  232. package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
  233. package/dist/proxmox/sdn-vnet.d.ts +113 -0
  234. package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
  235. package/dist/proxmox/sdn-zone.d.ts +126 -0
  236. package/dist/proxmox/sdn-zone.d.ts.map +1 -0
  237. package/dist/proxmox/storage.d.ts +120 -0
  238. package/dist/proxmox/storage.d.ts.map +1 -0
  239. package/dist/proxmox/user.d.ts +107 -0
  240. package/dist/proxmox/user.d.ts.map +1 -0
  241. package/dist/proxmox/values.d.ts +134 -0
  242. package/dist/proxmox/values.d.ts.map +1 -0
  243. package/dist/proxmox/zfs-pool-write.d.ts +76 -0
  244. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
  245. package/dist/proxmox/zfs-pool.d.ts +146 -0
  246. package/dist/proxmox/zfs-pool.d.ts.map +1 -0
  247. package/dist/talos/credentials.d.ts +30 -0
  248. package/dist/talos/credentials.d.ts.map +1 -0
  249. package/dist/talos/index.d.ts +15 -0
  250. package/dist/talos/index.d.ts.map +1 -0
  251. package/dist/talos/index.js +392 -0
  252. package/dist/talos/index.js.map +16 -0
  253. package/dist/talos/kubeconfig.d.ts +53 -0
  254. package/dist/talos/kubeconfig.d.ts.map +1 -0
  255. package/dist/talos/resource.d.ts +32 -0
  256. package/dist/talos/resource.d.ts.map +1 -0
  257. package/dist/talos/talos-bootstrap.d.ts +27 -0
  258. package/dist/talos/talos-bootstrap.d.ts.map +1 -0
  259. package/dist/talos/talos-cluster-health.d.ts +36 -0
  260. package/dist/talos/talos-cluster-health.d.ts.map +1 -0
  261. package/dist/talos/talos-machine-config.d.ts +41 -0
  262. package/dist/talos/talos-machine-config.d.ts.map +1 -0
  263. package/dist/talos/talosctl.d.ts +29 -0
  264. package/dist/talos/talosctl.d.ts.map +1 -0
  265. package/dist/talos/values.d.ts +20 -0
  266. package/dist/talos/values.d.ts.map +1 -0
  267. package/package.json +57 -0
  268. package/src/cloudflare/client.ts +76 -0
  269. package/src/cloudflare/index.ts +10 -0
  270. package/src/cloudflare/lock-rules.ts +60 -0
  271. package/src/cloudflare/providers.ts +23 -0
  272. package/src/cloudflare/r2-bucket-lock.ts +220 -0
  273. package/src/forgejo/branch-protection-form.ts +29 -0
  274. package/src/forgejo/branch-protection.ts +101 -0
  275. package/src/forgejo/client.ts +136 -0
  276. package/src/forgejo/index.ts +17 -0
  277. package/src/forgejo/org-actions-secrets.ts +99 -0
  278. package/src/forgejo/org-label.ts +100 -0
  279. package/src/forgejo/org-team.ts +105 -0
  280. package/src/forgejo/repo-webhook-form.ts +47 -0
  281. package/src/forgejo/repo-webhook.ts +118 -0
  282. package/src/forgejo/repository.ts +126 -0
  283. package/src/forgejo/resource.ts +171 -0
  284. package/src/forgejo/team-member.ts +97 -0
  285. package/src/forgejo/values.test.ts +48 -0
  286. package/src/forgejo/values.ts +61 -0
  287. package/src/index.ts +18 -0
  288. package/src/openbao/auth-role-form.ts +112 -0
  289. package/src/openbao/auth-role.ts +101 -0
  290. package/src/openbao/bao-address.test.ts +89 -0
  291. package/src/openbao/bao-address.ts +97 -0
  292. package/src/openbao/bao-http-gate.test.ts +88 -0
  293. package/src/openbao/bao-http-unix.test.ts +70 -0
  294. package/src/openbao/bao-http.test.ts +161 -0
  295. package/src/openbao/bao-http.ts +185 -0
  296. package/src/openbao/bao-status.test.ts +114 -0
  297. package/src/openbao/bao-status.ts +137 -0
  298. package/src/openbao/cloudflare-group-scope.test.ts +103 -0
  299. package/src/openbao/cloudflare-group-scope.ts +83 -0
  300. package/src/openbao/cloudflare-parity-catalog.ts +104 -0
  301. package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
  302. package/src/openbao/cloudflare-parity.ts +152 -0
  303. package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
  304. package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
  305. package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
  306. package/src/openbao/cloudflare-permission-groups.ts +299 -0
  307. package/src/openbao/cloudflare-policy.ts +180 -0
  308. package/src/openbao/cloudflare-role-form.test.ts +177 -0
  309. package/src/openbao/cloudflare-role-form.ts +183 -0
  310. package/src/openbao/cloudflare-role-wire.test.ts +132 -0
  311. package/src/openbao/cloudflare-role-wire.ts +41 -0
  312. package/src/openbao/cloudflare-role.ts +189 -0
  313. package/src/openbao/cloudflare-roles-config.ts +230 -0
  314. package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
  315. package/src/openbao/cloudflare-roles-expand.ts +231 -0
  316. package/src/openbao/digest.ts +26 -0
  317. package/src/openbao/fake-bao.ts +84 -0
  318. package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
  319. package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
  320. package/src/openbao/forgejo-bootstrap.ts +181 -0
  321. package/src/openbao/index.ts +17 -0
  322. package/src/openbao/mount-form.ts +147 -0
  323. package/src/openbao/mount-wire.test.ts +108 -0
  324. package/src/openbao/mount-wire.ts +79 -0
  325. package/src/openbao/mount.ts +132 -0
  326. package/src/openbao/pki-role-form.ts +248 -0
  327. package/src/openbao/pki-role.ts +202 -0
  328. package/src/openbao/policy-assembly.test.ts +28 -0
  329. package/src/openbao/policy-assembly.ts +13 -0
  330. package/src/openbao/policy-wire.test.ts +90 -0
  331. package/src/openbao/policy-wire.ts +53 -0
  332. package/src/openbao/policy.ts +212 -0
  333. package/src/openbao/proxmox-role-form.ts +174 -0
  334. package/src/openbao/proxmox-role.ts +214 -0
  335. package/src/openbao/ssh-role-form.ts +248 -0
  336. package/src/openbao/ssh-role.ts +240 -0
  337. package/src/proxmox/acl.ts +267 -0
  338. package/src/proxmox/api-token-form.ts +122 -0
  339. package/src/proxmox/api-token.ts +222 -0
  340. package/src/proxmox/backup-job.ts +213 -0
  341. package/src/proxmox/ceph-daemon-form.ts +84 -0
  342. package/src/proxmox/ceph-daemon.ts +243 -0
  343. package/src/proxmox/ceph-flag.ts +249 -0
  344. package/src/proxmox/ceph-fs-wire.ts +246 -0
  345. package/src/proxmox/ceph-fs.ts +249 -0
  346. package/src/proxmox/ceph-osd-tree.ts +193 -0
  347. package/src/proxmox/ceph-osd-write.ts +76 -0
  348. package/src/proxmox/ceph-osd.ts +244 -0
  349. package/src/proxmox/ceph-pool-form.ts +249 -0
  350. package/src/proxmox/ceph-pool-settle.ts +70 -0
  351. package/src/proxmox/ceph-pool.ts +212 -0
  352. package/src/proxmox/client.test.ts +185 -0
  353. package/src/proxmox/client.ts +240 -0
  354. package/src/proxmox/credentials.test.ts +144 -0
  355. package/src/proxmox/credentials.ts +254 -0
  356. package/src/proxmox/firewall-alias-form.ts +112 -0
  357. package/src/proxmox/firewall-alias.ts +208 -0
  358. package/src/proxmox/generated/pbs.ts +461 -0
  359. package/src/proxmox/generated/pve.ts +7344 -0
  360. package/src/proxmox/group.ts +199 -0
  361. package/src/proxmox/ha-resource.ts +169 -0
  362. package/src/proxmox/ha-rule-form.ts +185 -0
  363. package/src/proxmox/ha-rule.ts +240 -0
  364. package/src/proxmox/index.ts +42 -0
  365. package/src/proxmox/lease-cache.test.ts +163 -0
  366. package/src/proxmox/lease-cache.ts +131 -0
  367. package/src/proxmox/lxc.ts +117 -0
  368. package/src/proxmox/members.test.ts +54 -0
  369. package/src/proxmox/members.ts +174 -0
  370. package/src/proxmox/metric-server-form.ts +128 -0
  371. package/src/proxmox/metric-server-otel.ts +99 -0
  372. package/src/proxmox/metric-server.ts +214 -0
  373. package/src/proxmox/network-apply-read.ts +228 -0
  374. package/src/proxmox/network-apply.ts +244 -0
  375. package/src/proxmox/node-network-form.ts +213 -0
  376. package/src/proxmox/node-network.ts +243 -0
  377. package/src/proxmox/notification-target-form.ts +104 -0
  378. package/src/proxmox/notification-target.ts +222 -0
  379. package/src/proxmox/pbs-datastore-form.ts +248 -0
  380. package/src/proxmox/pbs-datastore-guard.ts +105 -0
  381. package/src/proxmox/pbs-datastore.ts +271 -0
  382. package/src/proxmox/pbs-prune-job-form.ts +208 -0
  383. package/src/proxmox/pbs-prune-job.ts +227 -0
  384. package/src/proxmox/pbs-sync-job-form.ts +234 -0
  385. package/src/proxmox/pbs-sync-job.ts +258 -0
  386. package/src/proxmox/pbs-verify-job-form.ts +131 -0
  387. package/src/proxmox/pbs-verify-job.ts +227 -0
  388. package/src/proxmox/pool.test.ts +42 -0
  389. package/src/proxmox/pool.ts +80 -0
  390. package/src/proxmox/qemu.ts +89 -0
  391. package/src/proxmox/replication-job-form.ts +89 -0
  392. package/src/proxmox/replication-job.ts +248 -0
  393. package/src/proxmox/resource.ts +257 -0
  394. package/src/proxmox/role.ts +161 -0
  395. package/src/proxmox/sdn-apply-read.test.ts +152 -0
  396. package/src/proxmox/sdn-apply-read.ts +189 -0
  397. package/src/proxmox/sdn-apply.ts +154 -0
  398. package/src/proxmox/sdn-subnet-form.ts +156 -0
  399. package/src/proxmox/sdn-subnet.ts +238 -0
  400. package/src/proxmox/sdn-vnet.ts +228 -0
  401. package/src/proxmox/sdn-zone.ts +246 -0
  402. package/src/proxmox/storage.ts +245 -0
  403. package/src/proxmox/user.ts +214 -0
  404. package/src/proxmox/values.test.ts +193 -0
  405. package/src/proxmox/values.ts +203 -0
  406. package/src/proxmox/zfs-pool-write.ts +130 -0
  407. package/src/proxmox/zfs-pool.ts +253 -0
  408. package/src/talos/credentials.ts +136 -0
  409. package/src/talos/index.ts +14 -0
  410. package/src/talos/kubeconfig.ts +170 -0
  411. package/src/talos/resource.ts +31 -0
  412. package/src/talos/talos-bootstrap.ts +109 -0
  413. package/src/talos/talos-cluster-health.ts +119 -0
  414. package/src/talos/talos-machine-config.ts +146 -0
  415. package/src/talos/talosctl.ts +94 -0
  416. package/src/talos/values.test.ts +80 -0
  417. package/src/talos/values.ts +70 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * `Proxmox.ZfsPool` — a zpool built on one node's physical disks, declared.
3
+ *
4
+ * ⛔ A CREATE HERE WIPES THE DISKS IT IS GIVEN AND THERE IS NO UNDO. `POST /nodes/{node}/disks/zfs`
5
+ * runs `zpool create` on the block devices named in `devices`; whatever was on them is gone the
6
+ * moment the worker runs. Every other decision in this file follows from that one sentence, and
7
+ * each of them is written out below rather than left to be inferred.
8
+ *
9
+ * ⛔ DESTROY IS REFUSED. `delete` MAKES NO API CALL — removing this resource from a stack leaves
10
+ * the zpool and its data exactly where they are, and Alchemy simply forgets about them. The
11
+ * asymmetry is the whole argument: an ORPHANED pool is recoverable (declare it again, or destroy
12
+ * it by hand once a human has looked at which disks are in it), a DESTROYED pool is not. Alchemy
13
+ * deletes for reasons that have nothing to do with anybody intending it — a line removed from a
14
+ * stack file, a resource renamed so the old id is dropped, a whole stack torn down by the wrong
15
+ * command, a replace triggered by a prop nobody meant to touch. Handing `zpool destroy` to any
16
+ * of those is not a feature. MEASURED in the node's own source: the DELETE handler forks
17
+ * `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with `cleanup-disks` then
18
+ * calls `wipe_blockdev` on every member. Neither flag is offered as a prop, because there is no
19
+ * call here for them to reach.
20
+ * ★ THE OPERATOR'S PATH IS `pvesh delete /nodes/<node>/disks/zfs/<name>`, typed by a person who
21
+ * has just looked at the pool. Do not "complete the CRUD" by wiring `delete` to `ops.destroy`:
22
+ * that one line turns a text edit into an unrecoverable one.
23
+ *
24
+ * ⛔ THERE IS NO PUT ON THIS FAMILY AT ALL. MEASURED from the cluster's own published schema
25
+ * (`/usr/share/pve-docs/api-viewer/apidoc.js`, read on n2 2026-09-13): `/nodes/{node}/disks/zfs`
26
+ * has GET and POST, `/nodes/{node}/disks/zfs/{name}` has GET and DELETE, and that is the whole
27
+ * surface. So `updateForm` is omitted — which, per `resource.ts`, makes any `matches` = false a
28
+ * REPLACE. A replace is a delete followed by a create, and on this family the delete is the
29
+ * thing above. Read the ⛔ on `matches` before adding anything to it.
30
+ *
31
+ * ⛔ NOT ONE CREATE PARAMETER COMES BACK ON READ, SO `matches` COMPARES NOTHING. MEASURED against
32
+ * the live cluster: `GET /nodes/n2/disks/zfs/rpool` answers exactly
33
+ * `{action, children, errors, leaf, name, scan, state, status}` — no `ashift`, no `compression`,
34
+ * no `raidlevel`, no `devices`, no `add_storage`. Every create parameter is write-only. There is
35
+ * therefore no field a declaration and a live pool can both be asked about, and a `matches` that
36
+ * invented one would plan a replace — i.e. a `zpool destroy` — over a difference it could never
37
+ * verify in the first place.
38
+ *
39
+ * ⚠️ AND WHAT THE INDEX RETURNS IS LIVE TELEMETRY, NOT CONFIGURATION. `GET /nodes/n2/disks/zfs`
40
+ * gives `alloc`, `free`, `frag`, `dedup`, `size`, `health`. MEASURED: two reads of n2 seconds
41
+ * apart returned `alloc` 14474944512 then 14472740864 — it moves on its own, with no declaration
42
+ * anywhere near it. `scan` in the detail read moves the same way (it carries the last scrub), and
43
+ * `status`/`action` appear and vanish as ZFS feature flags and faults come and go. None of them
44
+ * is compared, and the three churning strings are kept out of the state store entirely rather
45
+ * than rewriting this resource's row every time somebody writes a file.
46
+ *
47
+ * ⚠️ TWO HANDLERS ARE OVERRIDDEN AND THE OTHER THREE COME FROM THE FACTORY — see the ★ above
48
+ * `handlers`. `delete` is the ⛔ above. `reconcile` is because the create is a FORKED WORKER, so
49
+ * the factory's immediate read-back would report a successful create as a failure; the measured
50
+ * detail is in the ⛔ on `createPool` in zfs-pool-create.ts.
51
+ *
52
+ * ⚠️ PRIVILEGES, FROM THE SCHEMA. Read and diff need `Sys.Audit` on `/` — both GETs check it.
53
+ * Reconcile needs `Sys.Modify` on `/` for the POST. `Datastore.Allocate` on `/storage` is NOT
54
+ * needed, because `add_storage` is not offered — see the ⛔ on `ZfsPoolProps`. `delete` needs
55
+ * nothing at all, since it calls nothing.
56
+ */
57
+ import { Resource } from 'alchemy';
58
+ import * as Provider from 'alchemy/Provider';
59
+ import { type PveRequirements, type WithTarget } from './resource.ts';
60
+ /** PVE's layouts. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an even one. */
61
+ export type ZfsRaidLevel = 'single' | 'mirror' | 'raid10' | 'raidz' | 'raidz2' | 'raidz3' | 'draid' | 'draid2' | 'draid3';
62
+ export type ZfsCompression = 'on' | 'off' | 'gzip' | 'lz4' | 'lzjb' | 'zle' | 'zstd';
63
+ /**
64
+ * ⛔ THERE IS NO `add_storage`, `cleanup-config` OR `cleanup-disks` PROP, AND THAT IS NOT AN
65
+ * OVERSIGHT. The last two belong to the DELETE this resource refuses to make. `add_storage=1`
66
+ * would have the POST build a `Proxmox.Storage`-shaped object this resource does not own, cannot
67
+ * see on any later read, and cannot remove — invisible to every diff forever, and a collision
68
+ * waiting for the day somebody also declares the storage properly. Declare a `Proxmox.Storage`
69
+ * with `type: 'zfspool'` and `locator: { pool: <this pool's name> }` instead, reading the `name`
70
+ * attribute below so Alchemy orders the storage after the pool.
71
+ */
72
+ export interface ZfsPoolProps extends WithTarget {
73
+ /**
74
+ * ⛔ A ZPOOL IS NODE-LOCAL AND THIS IS HALF ITS IDENTITY. Changing it moves nothing: `path` then
75
+ * points at a different node, the read answers absent, and reconcile tries to CREATE the pool
76
+ * over there — on whatever `/dev/...` happens to answer to those names on that machine.
77
+ * Declare a separate resource per node instead.
78
+ */
79
+ node: string;
80
+ /** The zpool name, and PVE's key for it on this node. ⚠️ Changing it creates a SECOND pool. */
81
+ name: string;
82
+ /**
83
+ * The block devices to build it on.
84
+ *
85
+ * ⛔ NAME THEM BY `/dev/disk/by-id/...`, NOT `/dev/sdb`. Kernel names are not stable across
86
+ * reboots; a by-id link is. MEASURED in the node's source: PVE resolves what it is handed to a
87
+ * by-id link only AFTER it has decided the device is free, so a stale `/dev/sdb` in a stack
88
+ * file is checked against, and then wiped, whichever disk currently answers to that name.
89
+ * ⚠️ ORDER IS MEANING FOR `raid10` AND IS PRESERVED — PVE walks the list two at a time and makes
90
+ * each pair a mirror. This is why the form uses a plain join and NOT `csv()` from values.ts:
91
+ * `csv` sorts, and sorting this list silently re-pairs the mirrors into a different pool.
92
+ * ⚠️ PVE's only guard is `assert_disk_unused`, i.e. its own `disk_is_used` over the same `used`
93
+ * column the Disks view shows. A blank disk it considers free is wiped with no confirmation.
94
+ */
95
+ devices: readonly string[];
96
+ raidlevel: ZfsRaidLevel;
97
+ /**
98
+ * Sector size exponent, 9–16, PVE default 12.
99
+ *
100
+ * ⛔ WRITE-ONLY AND IMMUTABLE FOR THE LIFE OF THE POOL. It is consumed by `zpool create -o
101
+ * ashift=` and never returned by any read on this family, so changing it later plans as `noop`
102
+ * and changes nothing. A different ashift means destroying and rebuilding the pool, by hand,
103
+ * with the data moved off first.
104
+ */
105
+ ashift?: number;
106
+ /**
107
+ * ⚠️ Write-only in the same way as `ashift`: applied by `zfs set compression=` at create time and
108
+ * never read back, so a later change plans `noop`. Change it with `zfs set` on the node.
109
+ */
110
+ compression?: ZfsCompression;
111
+ /**
112
+ * `data=<integer>,spares=<integer>`. ⚠️ THE WIRE NAME IS KEPT, HYPHEN AND ALL, for the reason
113
+ * `storage.ts` keeps `prune-backups`: a second spelling is a second name for one thing.
114
+ * ⚠️ PVE refuses it outright unless `raidlevel` is one of the `draid*` levels.
115
+ */
116
+ 'draid-config'?: string;
117
+ }
118
+ /**
119
+ * ⚠️ EVERYTHING HERE IS REPORTED, NOTHING HERE IS COMPARED — see the ⛔ on `matches`. These exist
120
+ * so a plan and the state store can say what the pool actually is, on a family where the
121
+ * declaration and the live object share no comparable field at all.
122
+ */
123
+ export interface ZfsPoolAttributes {
124
+ /** ★ The value a `Proxmox.Storage` should read to order itself after this pool. */
125
+ name: string;
126
+ node: string;
127
+ /** `ONLINE` | `DEGRADED` | `FAULTED` | … Health, not configuration. */
128
+ state: string;
129
+ /** ZFS's own error summary, e.g. `No known data errors`. */
130
+ errors: string;
131
+ /**
132
+ * The leaf devices ZFS reports, in vdev order, comma-joined.
133
+ *
134
+ * ⚠️ THIS IS WHAT ZFS RESOLVED, NOT WHAT WAS DECLARED, and the two normally differ: PVE rewrites
135
+ * a `/dev/sdb` into a by-id link before creating, and ZFS reports partition paths (`…-part3`)
136
+ * for a pool built on partitions — MEASURED, that is exactly what n2's `rpool` reports. Never
137
+ * compare it with `props.devices`.
138
+ */
139
+ devices: string;
140
+ }
141
+ export interface ProxmoxZfsPool extends Resource<'Proxmox.ZfsPool', ZfsPoolProps, ZfsPoolAttributes, never, PveRequirements> {
142
+ }
143
+ /** ★ `retain` by default — a pool holding datasets cannot be rebuilt. See the ★ in resource.ts. */
144
+ export declare const ProxmoxZfsPool: import("alchemy").ResourceClass<ProxmoxZfsPool>;
145
+ export declare const ProxmoxZfsPoolProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxZfsPool>, never, import("effect/unstable/http/HttpClient").HttpClient>;
146
+ //# sourceMappingURL=zfs-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zfs-pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,UAAU,EAGhB,MAAM,eAAe,CAAC;AAIvB,oGAAoG;AACpG,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAErF;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;OAYG;IACH,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,SAAS,EAAE,YAAY,CAAC;IACxB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,mGAAmG;AACnG,eAAO,MAAM,cAAc,iDAEzB,CAAC;AA6EH,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
@@ -0,0 +1,30 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import * as ChildProcessSpawner from 'effect/unstable/process/ChildProcessSpawner';
3
+ /** Where credentials come from. HomeFlare-specific mount names live in the stack, not here. */
4
+ export type TalosTarget = {
5
+ /** OpenBao KV mount holding Talos material, e.g. `talos-tb4`. */
6
+ readonly mount: string;
7
+ /** Logical cluster name — safe to persist and log. */
8
+ readonly cluster: string;
9
+ /**
10
+ * Path under the mount for the talosconfig YAML.
11
+ *
12
+ * ⚠️ DEFAULT IS REASONED. The first real deploy will confirm or correct the path; a wrong path
13
+ * fails closed with a bao error rather than writing an empty talosconfig.
14
+ */
15
+ readonly talosconfigKey?: string;
16
+ };
17
+ export type TalosCredential = {
18
+ /** Absolute path to a temp talosconfig. ⛔ NEVER PERSIST, NEVER LOG CONTENTS. */
19
+ readonly talosconfigPath: string;
20
+ };
21
+ /**
22
+ * Mint one talosconfig file for `target`.
23
+ *
24
+ * ★ SHELLS OUT TO `bao` RATHER THAN SPEAKING THE HTTP API — same reasoning as proxmox credentials.
25
+ * The estate's BAO_ADDR / BAO_NAMESPACE / approle contract already lives in that binary.
26
+ */
27
+ export declare const mintTalosconfig: (target: TalosTarget) => Effect.Effect<{
28
+ talosconfigPath: string;
29
+ }, Error | import("effect/PlatformError").PlatformError, ChildProcessSpawner.ChildProcessSpawner>;
30
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/talos/credentials.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,mBAAmB,MAAM,6CAA6C,CAAC;AAEnF,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GAAG;IACxB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC,CAAC;AAIF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,WAAY,WAAW;;iGAsFhD,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Talos providers for Alchemy.
3
+ *
4
+ * ⛔ THIS BARREL IS THE PUBLIC API, AND IT IS DELIBERATELY SMALLER THAN THE DIRECTORY.
5
+ * These are the symbols a real stack consumes; the rest of the files are internals a
6
+ * provider needs but a consumer should not depend on. An `export *` here would publish
7
+ * every helper as API and make the next refactor a breaking change.
8
+ * ★ Anything unlisted is still reachable by path if you genuinely need it — that is a
9
+ * deliberate, visible act rather than an accident of barrelling.
10
+ */
11
+ export { TalosKubeconfigProvider } from './kubeconfig.ts';
12
+ export { TalosBootstrapProvider } from './talos-bootstrap.ts';
13
+ export { TalosClusterHealthProvider } from './talos-cluster-health.ts';
14
+ export { TalosMachineConfigProvider } from './talos-machine-config.ts';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/talos/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,392 @@
1
+ // src/talos/kubeconfig.ts
2
+ import { Resource } from "alchemy";
3
+ import { isResolved } from "alchemy/Diff";
4
+ import * as Provider from "alchemy/Provider";
5
+ import * as Effect3 from "effect/Effect";
6
+
7
+ // src/talos/credentials.ts
8
+ import { closeSync, openSync, writeFileSync } from "node:fs";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Stream from "effect/Stream";
11
+ import * as ChildProcess from "effect/unstable/process/ChildProcess";
12
+ import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
13
+ var defaultKey = (target) => target.talosconfigKey ?? "data/talosconfig";
14
+ var mintTalosconfig = (target) => Effect.scoped(Effect.gen(function* () {
15
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
16
+ const key = defaultKey(target);
17
+ const result = yield* ChildProcess.make("bao", ["kv", "get", "-format=json", `${target.mount}/${key}`], { detached: false, extendEnv: true, stderr: "pipe", stdin: "ignore", stdout: "pipe" }).pipe(spawner.spawn, Effect.flatMap((child) => Effect.all({
18
+ exitCode: child.exitCode,
19
+ stderr: child.stderr.pipe(Stream.decodeText, Stream.mkString, Effect.map((text) => text.trim())),
20
+ stdout: child.stdout.pipe(Stream.decodeText, Stream.mkString, Effect.map((text) => text.trim()))
21
+ }, { concurrency: "unbounded" })), Effect.scoped);
22
+ if (result.exitCode !== 0) {
23
+ return yield* Effect.fail(new Error(`bao kv get ${target.mount}/${key} exited ${String(result.exitCode)}: ${result.stderr}`));
24
+ }
25
+ const parsed = JSON.parse(result.stdout);
26
+ const data = parsed.data?.data;
27
+ const raw = data?.["talosconfig"] ?? data?.["config"];
28
+ if (typeof raw !== "string" || raw.trim() === "") {
29
+ return yield* Effect.fail(new Error(`${target.mount}/${key} returned no talosconfig field. Expected a KV key named 'talosconfig' or 'config' — never store the value in Alchemy props.`));
30
+ }
31
+ const talosconfigPath = `${Bun.env["TMPDIR"] ?? "/tmp"}/hf-talos-${target.cluster}-${Bun.randomUUIDv7()}.yaml`;
32
+ yield* Effect.try({
33
+ try: () => {
34
+ const fd = openSync(talosconfigPath, "wx", 384);
35
+ try {
36
+ writeFileSync(fd, raw);
37
+ } finally {
38
+ closeSync(fd);
39
+ }
40
+ },
41
+ catch: (cause) => new Error(`writing temp talosconfig: ${String(cause)}`)
42
+ });
43
+ yield* Effect.addFinalizer(() => Effect.tryPromise({
44
+ try: () => Bun.file(talosconfigPath).delete(),
45
+ catch: () => {
46
+ return;
47
+ }
48
+ }).pipe(Effect.orElseSucceed(() => {
49
+ return;
50
+ })));
51
+ return { talosconfigPath };
52
+ }));
53
+
54
+ // src/talos/talosctl.ts
55
+ import * as Effect2 from "effect/Effect";
56
+ import * as Stream2 from "effect/Stream";
57
+ import * as ChildProcess2 from "effect/unstable/process/ChildProcess";
58
+ import * as ChildProcessSpawner3 from "effect/unstable/process/ChildProcessSpawner";
59
+
60
+ class TalosError extends Error {
61
+ command;
62
+ exitCode;
63
+ constructor(command, exitCode, detail) {
64
+ super(`talosctl ${command} -> ${String(exitCode)}: ${detail}`);
65
+ this.command = command;
66
+ this.exitCode = exitCode;
67
+ this.name = "TalosError";
68
+ }
69
+ }
70
+ var talosctl = (args, options) => Effect2.gen(function* () {
71
+ const spawner = yield* ChildProcessSpawner3.ChildProcessSpawner;
72
+ const argv = [
73
+ ...args,
74
+ "--talosconfig",
75
+ options.talosconfigPath,
76
+ ...options.nodes === undefined ? [] : ["--nodes", options.nodes.join(",")],
77
+ ...options.endpoints === undefined ? [] : ["--endpoints", options.endpoints.join(",")]
78
+ ];
79
+ const result = yield* ChildProcess2.make("talosctl", argv, {
80
+ detached: false,
81
+ extendEnv: true,
82
+ stderr: "pipe",
83
+ stdin: "ignore",
84
+ stdout: "pipe"
85
+ }).pipe(spawner.spawn, Effect2.flatMap((child) => Effect2.all({
86
+ exitCode: child.exitCode,
87
+ stderr: child.stderr.pipe(Stream2.decodeText, Stream2.mkString, Effect2.map((text) => text.trim())),
88
+ stdout: child.stdout.pipe(Stream2.decodeText, Stream2.mkString, Effect2.map((text) => text.trim()))
89
+ }, { concurrency: "unbounded" })), Effect2.scoped);
90
+ const command = args.join(" ");
91
+ if (result.exitCode !== 0) {
92
+ return yield* Effect2.fail(new TalosError(command, result.exitCode, result.stderr.slice(0, 300)));
93
+ }
94
+ return result.stdout;
95
+ });
96
+ var talosctlOrAlready = (args, options) => talosctl(args, options).pipe(Effect2.catchIf((cause) => cause instanceof TalosError && cause.exitCode !== 0 && /already|exists|bootstrapped/i.test(cause.message), () => Effect2.succeed("")));
97
+
98
+ // src/talos/values.ts
99
+ import { createHash } from "node:crypto";
100
+ var canonicalText = (text) => text.trimEnd();
101
+ var sha256 = (text) => createHash("sha256").update(canonicalText(text)).digest("hex");
102
+ var configDigest = (text) => sha256(text);
103
+ var kubeconfigMetadata = (yamlText, contextName) => {
104
+ let doc;
105
+ try {
106
+ doc = Bun.YAML.parse(yamlText);
107
+ } catch {
108
+ return;
109
+ }
110
+ const ctx = doc.contexts?.find((row) => row.name === contextName)?.context;
111
+ const clusterName = ctx?.cluster;
112
+ const userName = ctx?.user;
113
+ if (clusterName === undefined || userName === undefined)
114
+ return;
115
+ const cluster = doc.clusters?.find((row) => row.name === clusterName)?.cluster;
116
+ const user = doc.users?.find((row) => row.name === userName)?.user;
117
+ const endpoint = cluster?.server;
118
+ const ca = cluster?.["certificate-authority-data"];
119
+ const cert = user?.["client-certificate-data"] ?? user?.["client-key-data"];
120
+ if (endpoint === undefined || ca === undefined || cert === undefined)
121
+ return;
122
+ return {
123
+ caFingerprint: sha256(ca),
124
+ clientFingerprint: sha256(cert),
125
+ endpoint
126
+ };
127
+ };
128
+ var resolveConfigPath = (stackDir, configFile) => configFile.startsWith("/") ? configFile : `${stackDir}/${configFile}`;
129
+
130
+ // src/talos/kubeconfig.ts
131
+ var TalosKubeconfig = Resource("Talos.Kubeconfig");
132
+ var generation = (meta) => sha256(`${meta.context}
133
+ ${meta.endpoint}
134
+ ${meta.caFingerprint}
135
+ ${meta.clientFingerprint}`);
136
+ var toConnection = (props) => ({
137
+ auth: { context: props.context, kind: "kubeconfig", path: props.runtimePath }
138
+ });
139
+ var readFileMeta = (props) => Effect3.gen(function* () {
140
+ const text = yield* Effect3.tryPromise({
141
+ try: () => Bun.file(props.runtimePath).text(),
142
+ catch: (cause) => new Error(String(cause))
143
+ }).pipe(Effect3.orElseSucceed(() => {
144
+ return;
145
+ }));
146
+ if (text === undefined)
147
+ return;
148
+ const meta = kubeconfigMetadata(text, props.context);
149
+ if (meta === undefined)
150
+ return;
151
+ return {
152
+ certificateAuthorityFingerprint: meta.caFingerprint,
153
+ clientCertificateFingerprint: meta.clientFingerprint,
154
+ connection: toConnection(props),
155
+ context: props.context,
156
+ credentialGeneration: generation({ ...meta, context: props.context }),
157
+ endpoint: meta.endpoint
158
+ };
159
+ });
160
+ var read = (props) => readFileMeta(props).pipe(Effect3.map((meta) => meta === undefined ? {
161
+ certificateAuthorityFingerprint: "",
162
+ clientCertificateFingerprint: "",
163
+ connection: toConnection(props),
164
+ context: props.context,
165
+ credentialGeneration: "",
166
+ endpoint: ""
167
+ } : meta));
168
+ var diff = (news, output) => Effect3.gen(function* () {
169
+ if (output === undefined || !isResolved(news))
170
+ return;
171
+ const live = yield* read(news);
172
+ if (live.credentialGeneration !== "" && output.credentialGeneration === live.credentialGeneration && output.context === live.context && output.endpoint === live.endpoint) {
173
+ return { action: "noop" };
174
+ }
175
+ return { action: "update" };
176
+ });
177
+ var reconcile = (props) => Effect3.gen(function* () {
178
+ const credential = yield* mintTalosconfig(props.target);
179
+ yield* talosctl([
180
+ "kubeconfig",
181
+ props.runtimePath,
182
+ "--merge=false",
183
+ "--force",
184
+ "--force-context-name",
185
+ props.context
186
+ ], { nodes: [props.node], talosconfigPath: credential.talosconfigPath });
187
+ const meta = yield* readFileMeta(props);
188
+ if (meta === undefined) {
189
+ return yield* Effect3.die(new Error(`${props.runtimePath}: talosctl kubeconfig returned no error but the file is missing ` + "or unparsable. Read back rather than trusting the exit code alone."));
190
+ }
191
+ return meta;
192
+ });
193
+ var handlers = {
194
+ delete: () => Effect3.void,
195
+ diff: ({
196
+ news,
197
+ output
198
+ }) => diff(news, output),
199
+ list: () => Effect3.succeed([]),
200
+ read: ({ olds }) => read(olds),
201
+ reconcile: ({ news }) => reconcile(news)
202
+ };
203
+ var TalosKubeconfigProvider = () => Provider.effect(TalosKubeconfig, Effect3.succeed(TalosKubeconfig.Provider.of(handlers)));
204
+ // src/talos/talos-bootstrap.ts
205
+ import { Resource as Resource2 } from "alchemy";
206
+ import { isResolved as isResolved2 } from "alchemy/Diff";
207
+ import * as Provider2 from "alchemy/Provider";
208
+ import * as Effect4 from "effect/Effect";
209
+ var TalosBootstrap = Resource2("Talos.Bootstrap", {
210
+ defaultRemovalPolicy: "retain"
211
+ });
212
+ var isBootstrapped = (props, talosconfigPath) => talosctl(["get", "etcdmembers", "-o", "json"], {
213
+ nodes: [props.node],
214
+ talosconfigPath
215
+ }).pipe(Effect4.map((text) => text.includes('"id"') || text.includes('"member"')), Effect4.orElseSucceed(() => false));
216
+ var read2 = (props) => Effect4.gen(function* () {
217
+ const credential = yield* mintTalosconfig(props.target);
218
+ const bootstrapped = yield* isBootstrapped(props, credential.talosconfigPath);
219
+ return { bootstrapped, node: props.node };
220
+ });
221
+ var diff2 = (news, output) => Effect4.gen(function* () {
222
+ if (output === undefined || !isResolved2(news))
223
+ return;
224
+ const live = yield* read2(news);
225
+ if (live.bootstrapped)
226
+ return { action: "noop" };
227
+ return { action: "update" };
228
+ });
229
+ var reconcile2 = (props) => Effect4.gen(function* () {
230
+ const credential = yield* mintTalosconfig(props.target);
231
+ const before = yield* isBootstrapped(props, credential.talosconfigPath);
232
+ if (!before) {
233
+ yield* talosctlOrAlready(["bootstrap"], {
234
+ nodes: [props.node],
235
+ talosconfigPath: credential.talosconfigPath
236
+ });
237
+ }
238
+ const after = yield* isBootstrapped(props, credential.talosconfigPath);
239
+ if (!after) {
240
+ return yield* Effect4.die(new Error(`${props.node}: bootstrap returned no error but etcd members are still absent. ` + "Read back rather than trusting the exit code alone."));
241
+ }
242
+ return { bootstrapped: true, node: props.node };
243
+ });
244
+ var handlers2 = {
245
+ delete: () => Effect4.void,
246
+ diff: ({
247
+ news,
248
+ output
249
+ }) => diff2(news, output),
250
+ list: () => Effect4.succeed([]),
251
+ read: ({ olds }) => read2(olds),
252
+ reconcile: ({ news }) => reconcile2(news)
253
+ };
254
+ var TalosBootstrapProvider = () => Provider2.effect(TalosBootstrap, Effect4.succeed(TalosBootstrap.Provider.of(handlers2)));
255
+ // src/talos/talos-cluster-health.ts
256
+ import { Resource as Resource3 } from "alchemy";
257
+ import { isResolved as isResolved3 } from "alchemy/Diff";
258
+ import * as Provider3 from "alchemy/Provider";
259
+ import * as Effect5 from "effect/Effect";
260
+ var TalosClusterHealth = Resource3("Talos.ClusterHealth");
261
+ var nodeCsv = (nodes) => nodes === undefined || nodes.length === 0 ? "" : nodes.join(",");
262
+ var healthArgs = (props, waitTimeout) => {
263
+ const args = ["health", "--wait-timeout", waitTimeout];
264
+ const cp = nodeCsv(props.controlPlaneNodes);
265
+ const workers = nodeCsv(props.workerNodes);
266
+ if (cp !== "")
267
+ args.push("--control-plane-nodes", cp);
268
+ if (workers !== "")
269
+ args.push("--worker-nodes", workers);
270
+ return args;
271
+ };
272
+ var check = (props, waitTimeout) => Effect5.gen(function* () {
273
+ const credential = yield* mintTalosconfig(props.target);
274
+ yield* talosctl(healthArgs(props, waitTimeout), {
275
+ nodes: [...props.controlPlaneNodes],
276
+ talosconfigPath: credential.talosconfigPath
277
+ });
278
+ return {
279
+ controlPlaneNodes: nodeCsv(props.controlPlaneNodes),
280
+ healthy: true,
281
+ workerNodes: nodeCsv(props.workerNodes)
282
+ };
283
+ });
284
+ var read3 = (props) => check(props, "5s").pipe(Effect5.orElseSucceed(() => ({
285
+ controlPlaneNodes: nodeCsv(props.controlPlaneNodes),
286
+ healthy: false,
287
+ workerNodes: nodeCsv(props.workerNodes)
288
+ })));
289
+ var diff3 = (news, output) => Effect5.gen(function* () {
290
+ if (output === undefined || !isResolved3(news))
291
+ return;
292
+ const live = yield* read3(news);
293
+ if (live.healthy)
294
+ return { action: "noop" };
295
+ return { action: "update" };
296
+ });
297
+ var reconcile3 = (props) => check(props, props.waitTimeout ?? "20m0s").pipe(Effect5.mapError((cause) => new Error(`Talos.ClusterHealth ${props.target.cluster}: cluster not healthy within ` + `${props.waitTimeout ?? "20m0s"} — ${String(cause)}`)));
298
+ var handlers3 = {
299
+ delete: () => Effect5.void,
300
+ diff: ({
301
+ news,
302
+ output
303
+ }) => diff3(news, output),
304
+ list: () => Effect5.succeed([]),
305
+ read: ({ olds }) => read3(olds),
306
+ reconcile: ({ news }) => reconcile3(news)
307
+ };
308
+ var TalosClusterHealthProvider = () => Provider3.effect(TalosClusterHealth, Effect5.succeed(TalosClusterHealth.Provider.of(handlers3)));
309
+ // src/talos/talos-machine-config.ts
310
+ import { Resource as Resource4 } from "alchemy";
311
+ import { isResolved as isResolved4 } from "alchemy/Diff";
312
+ import * as Provider4 from "alchemy/Provider";
313
+ import * as Effect6 from "effect/Effect";
314
+ var STACK_DIR = new URL("..", import.meta.url).pathname;
315
+ var TalosMachineConfig = Resource4("Talos.MachineConfig");
316
+ var loadDigest = (props) => Effect6.gen(function* () {
317
+ const path = resolveConfigPath(STACK_DIR, props.configFile);
318
+ const text = yield* Effect6.tryPromise({
319
+ try: () => Bun.file(path).text(),
320
+ catch: (cause) => new Error(`reading ${props.configFile}: ${String(cause)}`)
321
+ });
322
+ return configDigest(text);
323
+ });
324
+ var liveDigest = (props, talosconfigPath) => talosctl(["get", "machineconfig", "-o", "yaml"], {
325
+ nodes: [props.node],
326
+ talosconfigPath
327
+ }).pipe(Effect6.map((yaml) => configDigest(yaml)), Effect6.orElseSucceed(() => {
328
+ return;
329
+ }));
330
+ var read4 = (props) => Effect6.gen(function* () {
331
+ const wanted = yield* loadDigest(props);
332
+ const credential = yield* mintTalosconfig(props.target);
333
+ const live = yield* liveDigest(props, credential.talosconfigPath);
334
+ return {
335
+ configDigest: wanted,
336
+ converged: live === wanted,
337
+ mode: props.mode ?? "auto",
338
+ node: props.node
339
+ };
340
+ });
341
+ var diff4 = (news, output) => Effect6.gen(function* () {
342
+ if (output === undefined || !isResolved4(news))
343
+ return;
344
+ const live = yield* read4(news);
345
+ if (live.converged)
346
+ return { action: "noop" };
347
+ return { action: "update" };
348
+ });
349
+ var reconcile4 = (props) => Effect6.gen(function* () {
350
+ const path = resolveConfigPath(STACK_DIR, props.configFile);
351
+ const wanted = yield* loadDigest(props);
352
+ const credential = yield* mintTalosconfig(props.target);
353
+ const args = [
354
+ "apply-config",
355
+ "--file",
356
+ path,
357
+ "--mode",
358
+ props.mode ?? "auto",
359
+ ...props.insecure === true ? ["--insecure"] : []
360
+ ];
361
+ yield* talosctl(args, { nodes: [props.node], talosconfigPath: credential.talosconfigPath });
362
+ const live = yield* liveDigest(props, credential.talosconfigPath);
363
+ if (live !== wanted) {
364
+ return yield* Effect6.die(new Error(`${props.node}: apply-config returned no error but machineconfig digest still differs. ` + "Read back rather than trusting the exit code alone."));
365
+ }
366
+ return {
367
+ configDigest: wanted,
368
+ converged: true,
369
+ mode: props.mode ?? "auto",
370
+ node: props.node
371
+ };
372
+ });
373
+ var handlers4 = {
374
+ delete: () => Effect6.void,
375
+ diff: ({
376
+ news,
377
+ output
378
+ }) => diff4(news, output),
379
+ list: () => Effect6.succeed([]),
380
+ read: ({ olds }) => read4(olds),
381
+ reconcile: ({ news }) => reconcile4(news)
382
+ };
383
+ var TalosMachineConfigProvider = () => Provider4.effect(TalosMachineConfig, Effect6.succeed(TalosMachineConfig.Provider.of(handlers4)));
384
+ export {
385
+ TalosBootstrapProvider,
386
+ TalosClusterHealthProvider,
387
+ TalosKubeconfigProvider,
388
+ TalosMachineConfigProvider
389
+ };
390
+
391
+ //# debugId=6CD90391393B548364756E2164756E21
392
+ //# sourceMappingURL=index.js.map