@homeflare/alchemy 0.14.0 → 0.19.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 (567) hide show
  1. package/README.md +29 -0
  2. package/dist/linux/directory-lifecycle.d.ts +50 -0
  3. package/dist/linux/directory-lifecycle.d.ts.map +1 -0
  4. package/dist/linux/directory.d.ts +19 -0
  5. package/dist/linux/directory.d.ts.map +1 -0
  6. package/dist/linux/fake-linux-host.d.ts +35 -0
  7. package/dist/linux/fake-linux-host.d.ts.map +1 -0
  8. package/dist/linux/fake-systemd.d.ts +34 -0
  9. package/dist/linux/fake-systemd.d.ts.map +1 -0
  10. package/dist/linux/index.d.ts +27 -0
  11. package/dist/linux/index.d.ts.map +1 -0
  12. package/dist/linux/index.js +1554 -0
  13. package/dist/linux/index.js.map +37 -0
  14. package/dist/linux/providers.d.ts +17 -0
  15. package/dist/linux/providers.d.ts.map +1 -0
  16. package/dist/linux/region.d.ts +43 -0
  17. package/dist/linux/region.d.ts.map +1 -0
  18. package/dist/linux/remote-file-form.d.ts +47 -0
  19. package/dist/linux/remote-file-form.d.ts.map +1 -0
  20. package/dist/linux/remote-file-lifecycle.d.ts +36 -0
  21. package/dist/linux/remote-file-lifecycle.d.ts.map +1 -0
  22. package/dist/linux/remote-file-plan.d.ts +33 -0
  23. package/dist/linux/remote-file-plan.d.ts.map +1 -0
  24. package/dist/linux/remote-file.d.ts +28 -0
  25. package/dist/linux/remote-file.d.ts.map +1 -0
  26. package/dist/linux/ssh-command.d.ts +68 -0
  27. package/dist/linux/ssh-command.d.ts.map +1 -0
  28. package/dist/linux/ssh-runner.d.ts +22 -0
  29. package/dist/linux/ssh-runner.d.ts.map +1 -0
  30. package/dist/linux/ssh-scripts.d.ts +27 -0
  31. package/dist/linux/ssh-scripts.d.ts.map +1 -0
  32. package/dist/linux/systemctl.d.ts +67 -0
  33. package/dist/linux/systemctl.d.ts.map +1 -0
  34. package/dist/linux/unit-form.d.ts +78 -0
  35. package/dist/linux/unit-form.d.ts.map +1 -0
  36. package/dist/linux/unit-handlers.d.ts +15 -0
  37. package/dist/linux/unit-handlers.d.ts.map +1 -0
  38. package/dist/linux/unit-lifecycle.d.ts +44 -0
  39. package/dist/linux/unit-lifecycle.d.ts.map +1 -0
  40. package/dist/linux/unit-settle.d.ts +31 -0
  41. package/dist/linux/unit-settle.d.ts.map +1 -0
  42. package/dist/linux/unit.d.ts +35 -0
  43. package/dist/linux/unit.d.ts.map +1 -0
  44. package/dist/netbox/client.d.ts +52 -0
  45. package/dist/netbox/client.d.ts.map +1 -0
  46. package/dist/netbox/constraint-guard.d.ts +26 -0
  47. package/dist/netbox/constraint-guard.d.ts.map +1 -0
  48. package/dist/netbox/constraints.d.ts +76 -0
  49. package/dist/netbox/constraints.d.ts.map +1 -0
  50. package/dist/netbox/generated/constraints/index.d.ts +19 -0
  51. package/dist/netbox/generated/constraints/index.d.ts.map +1 -0
  52. package/dist/netbox/generated/constraints/netbox-ipam.d.ts +21 -0
  53. package/dist/netbox/generated/constraints/netbox-ipam.d.ts.map +1 -0
  54. package/dist/netbox/index.d.ts +17 -0
  55. package/dist/netbox/index.d.ts.map +1 -0
  56. package/dist/netbox/index.js +331 -0
  57. package/dist/netbox/index.js.map +18 -0
  58. package/dist/netbox/prefix-form.d.ts +54 -0
  59. package/dist/netbox/prefix-form.d.ts.map +1 -0
  60. package/dist/netbox/prefix.d.ts +80 -0
  61. package/dist/netbox/prefix.d.ts.map +1 -0
  62. package/dist/netbox/resource.d.ts +94 -0
  63. package/dist/netbox/resource.d.ts.map +1 -0
  64. package/dist/netbox/values.d.ts +24 -0
  65. package/dist/netbox/values.d.ts.map +1 -0
  66. package/dist/openbao/index.d.ts +10 -3
  67. package/dist/openbao/index.d.ts.map +1 -1
  68. package/dist/proxmox/acl.d.ts.map +1 -1
  69. package/dist/proxmox/api-token-form.d.ts.map +1 -1
  70. package/dist/proxmox/apply-endpoints.d.ts +30 -0
  71. package/dist/proxmox/apply-endpoints.d.ts.map +1 -0
  72. package/dist/proxmox/backup-job.d.ts.map +1 -1
  73. package/dist/proxmox/ceph-daemon-form.d.ts +29 -1
  74. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
  75. package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
  76. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  77. package/dist/proxmox/ceph-fs.d.ts.map +1 -1
  78. package/dist/proxmox/ceph-osd-write.d.ts +19 -0
  79. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  80. package/dist/proxmox/ceph-pool.d.ts.map +1 -1
  81. package/dist/proxmox/constraint-guard.d.ts +28 -0
  82. package/dist/proxmox/constraint-guard.d.ts.map +1 -0
  83. package/dist/proxmox/constraints.d.ts +94 -0
  84. package/dist/proxmox/constraints.d.ts.map +1 -0
  85. package/dist/proxmox/firewall-alias.d.ts.map +1 -1
  86. package/dist/proxmox/generated/constraints/index.d.ts +19 -0
  87. package/dist/proxmox/generated/constraints/index.d.ts.map +1 -0
  88. package/dist/proxmox/generated/constraints/pbs-config.d.ts +23 -0
  89. package/dist/proxmox/generated/constraints/pbs-config.d.ts.map +1 -0
  90. package/dist/proxmox/generated/constraints/pve-access.d.ts +23 -0
  91. package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -0
  92. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +23 -0
  93. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts.map +1 -0
  94. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +23 -0
  95. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts.map +1 -0
  96. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +23 -0
  97. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts.map +1 -0
  98. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +23 -0
  99. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts.map +1 -0
  100. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +23 -0
  101. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts.map +1 -0
  102. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +23 -0
  103. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts.map +1 -0
  104. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +23 -0
  105. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts.map +1 -0
  106. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +23 -0
  107. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts.map +1 -0
  108. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +23 -0
  109. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts.map +1 -0
  110. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +23 -0
  111. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts.map +1 -0
  112. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +23 -0
  113. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts.map +1 -0
  114. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +23 -0
  115. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts.map +1 -0
  116. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +23 -0
  117. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -0
  118. package/dist/proxmox/generated/constraints/pve-pools.d.ts +23 -0
  119. package/dist/proxmox/generated/constraints/pve-pools.d.ts.map +1 -0
  120. package/dist/proxmox/generated/constraints/pve-storage.d.ts +23 -0
  121. package/dist/proxmox/generated/constraints/pve-storage.d.ts.map +1 -0
  122. package/dist/proxmox/generated/pbs/access-users.d.ts +137 -0
  123. package/dist/proxmox/generated/pbs/access-users.d.ts.map +1 -0
  124. package/dist/proxmox/generated/pbs/access.d.ts +171 -0
  125. package/dist/proxmox/generated/pbs/access.d.ts.map +1 -0
  126. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts +252 -0
  127. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts.map +1 -0
  128. package/dist/proxmox/generated/pbs/admin-metrics.d.ts +147 -0
  129. package/dist/proxmox/generated/pbs/admin-metrics.d.ts.map +1 -0
  130. package/dist/proxmox/generated/pbs/admin.d.ts +217 -0
  131. package/dist/proxmox/generated/pbs/admin.d.ts.map +1 -0
  132. package/dist/proxmox/generated/pbs/backup.d.ts +114 -0
  133. package/dist/proxmox/generated/pbs/backup.d.ts.map +1 -0
  134. package/dist/proxmox/generated/pbs/config-access-pam.d.ts +218 -0
  135. package/dist/proxmox/generated/pbs/config-access-pam.d.ts.map +1 -0
  136. package/dist/proxmox/generated/pbs/config-access.d.ts +203 -0
  137. package/dist/proxmox/generated/pbs/config-access.d.ts.map +1 -0
  138. package/dist/proxmox/generated/pbs/config-datastore.d.ts +195 -0
  139. package/dist/proxmox/generated/pbs/config-datastore.d.ts.map +1 -0
  140. package/dist/proxmox/generated/pbs/config-media-pool.d.ts +207 -0
  141. package/dist/proxmox/generated/pbs/config-media-pool.d.ts.map +1 -0
  142. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts +159 -0
  143. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts.map +1 -0
  144. package/dist/proxmox/generated/pbs/config-notifications.d.ts +199 -0
  145. package/dist/proxmox/generated/pbs/config-notifications.d.ts.map +1 -0
  146. package/dist/proxmox/generated/pbs/config-remote.d.ts +175 -0
  147. package/dist/proxmox/generated/pbs/config-remote.d.ts.map +1 -0
  148. package/dist/proxmox/generated/pbs/config-sync.d.ts +145 -0
  149. package/dist/proxmox/generated/pbs/config-sync.d.ts.map +1 -0
  150. package/dist/proxmox/generated/pbs/config-tape.d.ts +200 -0
  151. package/dist/proxmox/generated/pbs/config-tape.d.ts.map +1 -0
  152. package/dist/proxmox/generated/pbs/config-verify.d.ts +77 -0
  153. package/dist/proxmox/generated/pbs/config-verify.d.ts.map +1 -0
  154. package/dist/proxmox/generated/pbs/config.d.ts +119 -0
  155. package/dist/proxmox/generated/pbs/config.d.ts.map +1 -0
  156. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts +178 -0
  157. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts.map +1 -0
  158. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts +205 -0
  159. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts.map +1 -0
  160. package/dist/proxmox/generated/pbs/nodes-node.d.ts +166 -0
  161. package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -0
  162. package/dist/proxmox/generated/pbs/nodes.d.ts +171 -0
  163. package/dist/proxmox/generated/pbs/nodes.d.ts.map +1 -0
  164. package/dist/proxmox/generated/pbs/ping.d.ts +18 -0
  165. package/dist/proxmox/generated/pbs/ping.d.ts.map +1 -0
  166. package/dist/proxmox/generated/pbs/pull.d.ts +37 -0
  167. package/dist/proxmox/generated/pbs/pull.d.ts.map +1 -0
  168. package/dist/proxmox/generated/pbs/push.d.ts +36 -0
  169. package/dist/proxmox/generated/pbs/push.d.ts.map +1 -0
  170. package/dist/proxmox/generated/pbs/reader.d.ts +41 -0
  171. package/dist/proxmox/generated/pbs/reader.d.ts.map +1 -0
  172. package/dist/proxmox/generated/pbs/root.d.ts +16 -0
  173. package/dist/proxmox/generated/pbs/root.d.ts.map +1 -0
  174. package/dist/proxmox/generated/pbs/status.d.ts +54 -0
  175. package/dist/proxmox/generated/pbs/status.d.ts.map +1 -0
  176. package/dist/proxmox/generated/pbs/tape-drive.d.ts +181 -0
  177. package/dist/proxmox/generated/pbs/tape-drive.d.ts.map +1 -0
  178. package/dist/proxmox/generated/pbs/tape-media.d.ts +126 -0
  179. package/dist/proxmox/generated/pbs/tape-media.d.ts.map +1 -0
  180. package/dist/proxmox/generated/pbs/tape.d.ts +90 -0
  181. package/dist/proxmox/generated/pbs/tape.d.ts.map +1 -0
  182. package/dist/proxmox/generated/pbs/version.d.ts +20 -0
  183. package/dist/proxmox/generated/pbs/version.d.ts.map +1 -0
  184. package/dist/proxmox/generated/pbs.d.ts +44 -662
  185. package/dist/proxmox/generated/pbs.d.ts.map +1 -1
  186. package/dist/proxmox/generated/pve/access-roles.d.ts +164 -0
  187. package/dist/proxmox/generated/pve/access-roles.d.ts.map +1 -0
  188. package/dist/proxmox/generated/pve/access-users.d.ts +152 -0
  189. package/dist/proxmox/generated/pve/access-users.d.ts.map +1 -0
  190. package/dist/proxmox/generated/pve/access.d.ts +221 -0
  191. package/dist/proxmox/generated/pve/access.d.ts.map +1 -0
  192. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts +45 -0
  193. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts.map +1 -0
  194. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts +47 -0
  195. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts.map +1 -0
  196. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts +240 -0
  197. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts.map +1 -0
  198. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts +200 -0
  199. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts.map +1 -0
  200. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts +109 -0
  201. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts.map +1 -0
  202. package/dist/proxmox/generated/pve/cluster-firewall.d.ts +225 -0
  203. package/dist/proxmox/generated/pve/cluster-firewall.d.ts.map +1 -0
  204. package/dist/proxmox/generated/pve/cluster-ha.d.ts +199 -0
  205. package/dist/proxmox/generated/pve/cluster-ha.d.ts.map +1 -0
  206. package/dist/proxmox/generated/pve/cluster-jobs.d.ts +183 -0
  207. package/dist/proxmox/generated/pve/cluster-jobs.d.ts.map +1 -0
  208. package/dist/proxmox/generated/pve/cluster-metrics.d.ts +110 -0
  209. package/dist/proxmox/generated/pve/cluster-metrics.d.ts.map +1 -0
  210. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts +224 -0
  211. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts.map +1 -0
  212. package/dist/proxmox/generated/pve/cluster-notifications.d.ts +91 -0
  213. package/dist/proxmox/generated/pve/cluster-notifications.d.ts.map +1 -0
  214. package/dist/proxmox/generated/pve/cluster-options.d.ts +85 -0
  215. package/dist/proxmox/generated/pve/cluster-options.d.ts.map +1 -0
  216. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts +48 -0
  217. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts.map +1 -0
  218. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts +45 -0
  219. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts.map +1 -0
  220. package/dist/proxmox/generated/pve/cluster-replication.d.ts +130 -0
  221. package/dist/proxmox/generated/pve/cluster-replication.d.ts.map +1 -0
  222. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +220 -0
  223. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -0
  224. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts +229 -0
  225. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts.map +1 -0
  226. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts +229 -0
  227. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts.map +1 -0
  228. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts +51 -0
  229. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts.map +1 -0
  230. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts +225 -0
  231. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts.map +1 -0
  232. package/dist/proxmox/generated/pve/cluster-sdn.d.ts +202 -0
  233. package/dist/proxmox/generated/pve/cluster-sdn.d.ts.map +1 -0
  234. package/dist/proxmox/generated/pve/cluster.d.ts +69 -0
  235. package/dist/proxmox/generated/pve/cluster.d.ts.map +1 -0
  236. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts +167 -0
  237. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts.map +1 -0
  238. package/dist/proxmox/generated/pve/nodes-node-config.d.ts +43 -0
  239. package/dist/proxmox/generated/pve/nodes-node-config.d.ts.map +1 -0
  240. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts +225 -0
  241. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts.map +1 -0
  242. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts +145 -0
  243. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts.map +1 -0
  244. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts +195 -0
  245. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts.map +1 -0
  246. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts +191 -0
  247. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts.map +1 -0
  248. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts +33 -0
  249. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts.map +1 -0
  250. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts +56 -0
  251. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts.map +1 -0
  252. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts +28 -0
  253. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts.map +1 -0
  254. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts +186 -0
  255. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts.map +1 -0
  256. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts +191 -0
  257. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts.map +1 -0
  258. package/dist/proxmox/generated/pve/nodes-node-network.d.ts +150 -0
  259. package/dist/proxmox/generated/pve/nodes-node-network.d.ts.map +1 -0
  260. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts +301 -0
  261. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts.map +1 -0
  262. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts +170 -0
  263. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts.map +1 -0
  264. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts +144 -0
  265. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts.map +1 -0
  266. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts +34 -0
  267. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts.map +1 -0
  268. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts +137 -0
  269. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts.map +1 -0
  270. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts +179 -0
  271. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts.map +1 -0
  272. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts +143 -0
  273. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts.map +1 -0
  274. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts +158 -0
  275. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts.map +1 -0
  276. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts +187 -0
  277. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts.map +1 -0
  278. package/dist/proxmox/generated/pve/nodes-node-services.d.ts +115 -0
  279. package/dist/proxmox/generated/pve/nodes-node-services.d.ts.map +1 -0
  280. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts +202 -0
  281. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts.map +1 -0
  282. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts +40 -0
  283. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts.map +1 -0
  284. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts +173 -0
  285. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts.map +1 -0
  286. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts +97 -0
  287. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts.map +1 -0
  288. package/dist/proxmox/generated/pve/nodes-node.d.ts +207 -0
  289. package/dist/proxmox/generated/pve/nodes-node.d.ts.map +1 -0
  290. package/dist/proxmox/generated/pve/nodes.d.ts +236 -0
  291. package/dist/proxmox/generated/pve/nodes.d.ts.map +1 -0
  292. package/dist/proxmox/generated/pve/pools.d.ts +82 -0
  293. package/dist/proxmox/generated/pve/pools.d.ts.map +1 -0
  294. package/dist/proxmox/generated/pve/storage.d.ts +159 -0
  295. package/dist/proxmox/generated/pve/storage.d.ts.map +1 -0
  296. package/dist/proxmox/generated/pve/version.d.ts +21 -0
  297. package/dist/proxmox/generated/pve/version.d.ts.map +1 -0
  298. package/dist/proxmox/generated/pve.d.ts +69 -4766
  299. package/dist/proxmox/generated/pve.d.ts.map +1 -1
  300. package/dist/proxmox/group.d.ts.map +1 -1
  301. package/dist/proxmox/ha-resource.d.ts.map +1 -1
  302. package/dist/proxmox/ha-rule.d.ts.map +1 -1
  303. package/dist/proxmox/index.d.ts +6 -1
  304. package/dist/proxmox/index.d.ts.map +1 -1
  305. package/dist/proxmox/index.js +1483 -300
  306. package/dist/proxmox/index.js.map +70 -45
  307. package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
  308. package/dist/proxmox/metric-server-form.d.ts +11 -0
  309. package/dist/proxmox/metric-server-form.d.ts.map +1 -1
  310. package/dist/proxmox/metric-server.d.ts.map +1 -1
  311. package/dist/proxmox/network-apply.d.ts.map +1 -1
  312. package/dist/proxmox/node-network-form.d.ts +15 -0
  313. package/dist/proxmox/node-network-form.d.ts.map +1 -1
  314. package/dist/proxmox/node-network.d.ts.map +1 -1
  315. package/dist/proxmox/notification-matcher.d.ts.map +1 -1
  316. package/dist/proxmox/notification-target-endpoint.d.ts +26 -0
  317. package/dist/proxmox/notification-target-endpoint.d.ts.map +1 -0
  318. package/dist/proxmox/notification-target-form.d.ts +7 -0
  319. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  320. package/dist/proxmox/notification-target.d.ts.map +1 -1
  321. package/dist/proxmox/pbs-datastore-endpoint.d.ts +22 -0
  322. package/dist/proxmox/pbs-datastore-endpoint.d.ts.map +1 -0
  323. package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
  324. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
  325. package/dist/proxmox/pbs-notification-target-endpoint.d.ts +29 -0
  326. package/dist/proxmox/pbs-notification-target-endpoint.d.ts.map +1 -0
  327. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  328. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
  329. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
  330. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
  331. package/dist/proxmox/pool.d.ts.map +1 -1
  332. package/dist/proxmox/provision-baseline.d.ts +30 -5
  333. package/dist/proxmox/provision-baseline.d.ts.map +1 -1
  334. package/dist/proxmox/qemu.d.ts +10 -0
  335. package/dist/proxmox/qemu.d.ts.map +1 -1
  336. package/dist/proxmox/replication-job.d.ts.map +1 -1
  337. package/dist/proxmox/resource-guard.d.ts +15 -0
  338. package/dist/proxmox/resource-guard.d.ts.map +1 -0
  339. package/dist/proxmox/resource-spec.d.ts +131 -0
  340. package/dist/proxmox/resource-spec.d.ts.map +1 -0
  341. package/dist/proxmox/resource.d.ts +17 -90
  342. package/dist/proxmox/resource.d.ts.map +1 -1
  343. package/dist/proxmox/role.d.ts.map +1 -1
  344. package/dist/proxmox/sdn-apply.d.ts.map +1 -1
  345. package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
  346. package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
  347. package/dist/proxmox/sdn-zone.d.ts.map +1 -1
  348. package/dist/proxmox/storage.d.ts.map +1 -1
  349. package/dist/proxmox/user.d.ts.map +1 -1
  350. package/dist/proxmox/zfs-pool-write.d.ts +11 -0
  351. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
  352. package/dist/proxmox/zfs-pool.d.ts.map +1 -1
  353. package/docs/linux-host-measured.md +56 -0
  354. package/docs/linux-host.md +186 -0
  355. package/docs/netbox.md +103 -0
  356. package/docs/provision-baseline.md +34 -7
  357. package/docs/proxmox-lxc.md +40 -0
  358. package/package.json +9 -1
  359. package/src/linux/directory-lifecycle.ts +220 -0
  360. package/src/linux/directory.test.ts +132 -0
  361. package/src/linux/directory.ts +71 -0
  362. package/src/linux/fake-linux-host.ts +197 -0
  363. package/src/linux/fake-systemd.ts +103 -0
  364. package/src/linux/index.ts +32 -0
  365. package/src/linux/providers.ts +26 -0
  366. package/src/linux/region.test.ts +89 -0
  367. package/src/linux/region.ts +118 -0
  368. package/src/linux/remote-file-form.ts +96 -0
  369. package/src/linux/remote-file-lifecycle.test.ts +191 -0
  370. package/src/linux/remote-file-lifecycle.ts +161 -0
  371. package/src/linux/remote-file-plan.ts +154 -0
  372. package/src/linux/remote-file.ts +88 -0
  373. package/src/linux/ssh-command.test.ts +140 -0
  374. package/src/linux/ssh-command.ts +99 -0
  375. package/src/linux/ssh-runner.ts +188 -0
  376. package/src/linux/ssh-scripts.ts +105 -0
  377. package/src/linux/systemctl.ts +145 -0
  378. package/src/linux/unit-form.ts +183 -0
  379. package/src/linux/unit-handlers.ts +73 -0
  380. package/src/linux/unit-lifecycle.test.ts +220 -0
  381. package/src/linux/unit-lifecycle.ts +193 -0
  382. package/src/linux/unit-settle.ts +110 -0
  383. package/src/linux/unit.ts +81 -0
  384. package/src/netbox/client.test.ts +67 -0
  385. package/src/netbox/client.ts +146 -0
  386. package/src/netbox/constraint-guard.ts +57 -0
  387. package/src/netbox/constraints.test.ts +114 -0
  388. package/src/netbox/constraints.ts +152 -0
  389. package/src/netbox/generated/constraints/index.ts +23 -0
  390. package/src/netbox/generated/constraints/netbox-ipam.ts +32 -0
  391. package/src/netbox/index.ts +34 -0
  392. package/src/netbox/prefix-form.test.ts +127 -0
  393. package/src/netbox/prefix-form.ts +84 -0
  394. package/src/netbox/prefix.ts +136 -0
  395. package/src/netbox/resource.ts +208 -0
  396. package/src/netbox/values.test.ts +66 -0
  397. package/src/netbox/values.ts +40 -0
  398. package/src/openbao/index-types.test.ts +87 -0
  399. package/src/openbao/index.ts +15 -3
  400. package/src/proxmox/acl.ts +2 -0
  401. package/src/proxmox/api-token-form.ts +5 -0
  402. package/src/proxmox/apply-endpoints.ts +34 -0
  403. package/src/proxmox/backup-job.ts +2 -0
  404. package/src/proxmox/ceph-daemon-form.ts +56 -2
  405. package/src/proxmox/ceph-daemon.ts +10 -21
  406. package/src/proxmox/ceph-flag.ts +1 -0
  407. package/src/proxmox/ceph-fs.ts +1 -0
  408. package/src/proxmox/ceph-osd-write.ts +30 -4
  409. package/src/proxmox/ceph-pool.ts +10 -0
  410. package/src/proxmox/constraint-guard.ts +53 -0
  411. package/src/proxmox/constraints-dialect.test.ts +165 -0
  412. package/src/proxmox/constraints-forms.test.ts +220 -0
  413. package/src/proxmox/constraints-host-forms.test.ts +132 -0
  414. package/src/proxmox/constraints-live.test.ts +155 -0
  415. package/src/proxmox/constraints-node-forms.test.ts +188 -0
  416. package/src/proxmox/constraints-required.test.ts +172 -0
  417. package/src/proxmox/constraints.test.ts +205 -0
  418. package/src/proxmox/constraints.ts +160 -0
  419. package/src/proxmox/firewall-alias.ts +5 -0
  420. package/src/proxmox/generated/constraints/index.ts +55 -0
  421. package/src/proxmox/generated/constraints/pbs-config.ts +203 -0
  422. package/src/proxmox/generated/constraints/pve-access.ts +69 -0
  423. package/src/proxmox/generated/constraints/pve-cluster-backup.ts +74 -0
  424. package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +27 -0
  425. package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +35 -0
  426. package/src/proxmox/generated/constraints/pve-cluster-ha.ts +55 -0
  427. package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +59 -0
  428. package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +80 -0
  429. package/src/proxmox/generated/constraints/pve-cluster-replication.ts +43 -0
  430. package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +90 -0
  431. package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +57 -0
  432. package/src/proxmox/generated/constraints/pve-nodes-disks.ts +31 -0
  433. package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +82 -0
  434. package/src/proxmox/generated/constraints/pve-nodes-network.ts +77 -0
  435. package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +125 -0
  436. package/src/proxmox/generated/constraints/pve-pools.ts +31 -0
  437. package/src/proxmox/generated/constraints/pve-storage.ts +80 -0
  438. package/src/proxmox/generated/pbs/access-users.ts +137 -0
  439. package/src/proxmox/generated/pbs/access.ts +232 -0
  440. package/src/proxmox/generated/pbs/admin-datastore-store.ts +238 -0
  441. package/src/proxmox/generated/pbs/admin-metrics.ts +147 -0
  442. package/src/proxmox/generated/pbs/admin.ts +225 -0
  443. package/src/proxmox/generated/pbs/backup.ts +121 -0
  444. package/src/proxmox/generated/pbs/config-access-pam.ts +228 -0
  445. package/src/proxmox/generated/pbs/config-access.ts +228 -0
  446. package/src/proxmox/generated/pbs/config-datastore.ts +217 -0
  447. package/src/proxmox/generated/pbs/config-media-pool.ts +232 -0
  448. package/src/proxmox/generated/pbs/config-notifications-matchers.ts +187 -0
  449. package/src/proxmox/generated/pbs/config-notifications.ts +221 -0
  450. package/src/proxmox/generated/pbs/config-remote.ts +192 -0
  451. package/src/proxmox/generated/pbs/config-sync.ts +167 -0
  452. package/src/proxmox/generated/pbs/config-tape.ts +226 -0
  453. package/src/proxmox/generated/pbs/config-verify.ts +87 -0
  454. package/src/proxmox/generated/pbs/config.ts +135 -0
  455. package/src/proxmox/generated/pbs/nodes-node-dns.ts +229 -0
  456. package/src/proxmox/generated/pbs/nodes-node-services.ts +181 -0
  457. package/src/proxmox/generated/pbs/nodes-node.ts +244 -0
  458. package/src/proxmox/generated/pbs/nodes.ts +160 -0
  459. package/src/proxmox/generated/pbs/ping.ts +16 -0
  460. package/src/proxmox/generated/pbs/pull.ts +37 -0
  461. package/src/proxmox/generated/pbs/push.ts +36 -0
  462. package/src/proxmox/generated/pbs/reader.ts +41 -0
  463. package/src/proxmox/generated/pbs/root.ts +16 -0
  464. package/src/proxmox/generated/pbs/status.ts +50 -0
  465. package/src/proxmox/generated/pbs/tape-drive.ts +220 -0
  466. package/src/proxmox/generated/pbs/tape-media.ts +133 -0
  467. package/src/proxmox/generated/pbs/tape.ts +92 -0
  468. package/src/proxmox/generated/pbs/version.ts +16 -0
  469. package/src/proxmox/generated/pbs.ts +44 -787
  470. package/src/proxmox/generated/pve/access-roles.ts +168 -0
  471. package/src/proxmox/generated/pve/access-users.ts +155 -0
  472. package/src/proxmox/generated/pve/access.ts +217 -0
  473. package/src/proxmox/generated/pve/cluster-acme-plugins-id.ts +367 -0
  474. package/src/proxmox/generated/pve/cluster-acme-plugins.ts +366 -0
  475. package/src/proxmox/generated/pve/cluster-acme-tos.ts +232 -0
  476. package/src/proxmox/generated/pve/cluster-bulk-action.ts +227 -0
  477. package/src/proxmox/generated/pve/cluster-firewall-refs.ts +110 -0
  478. package/src/proxmox/generated/pve/cluster-firewall.ts +236 -0
  479. package/src/proxmox/generated/pve/cluster-ha.ts +203 -0
  480. package/src/proxmox/generated/pve/cluster-jobs.ts +190 -0
  481. package/src/proxmox/generated/pve/cluster-metrics.ts +116 -0
  482. package/src/proxmox/generated/pve/cluster-notifications-endpoints.ts +244 -0
  483. package/src/proxmox/generated/pve/cluster-notifications.ts +99 -0
  484. package/src/proxmox/generated/pve/cluster-options.ts +189 -0
  485. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.ts +272 -0
  486. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models.ts +268 -0
  487. package/src/proxmox/generated/pve/cluster-replication.ts +127 -0
  488. package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +218 -0
  489. package/src/proxmox/generated/pve/cluster-sdn-ipams.ts +229 -0
  490. package/src/proxmox/generated/pve/cluster-sdn-vnets-vnet.ts +245 -0
  491. package/src/proxmox/generated/pve/cluster-sdn-vnets.ts +49 -0
  492. package/src/proxmox/generated/pve/cluster-sdn-zones.ts +224 -0
  493. package/src/proxmox/generated/pve/cluster-sdn.ts +201 -0
  494. package/src/proxmox/generated/pve/cluster.ts +71 -0
  495. package/src/proxmox/generated/pve/nodes-node-ceph.ts +167 -0
  496. package/src/proxmox/generated/pve/nodes-node-config.ts +56 -0
  497. package/src/proxmox/generated/pve/nodes-node-disks.ts +237 -0
  498. package/src/proxmox/generated/pve/nodes-node-firewall.ts +239 -0
  499. package/src/proxmox/generated/pve/nodes-node-hardware.ts +215 -0
  500. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.ts +232 -0
  501. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.ts +1059 -0
  502. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.ts +49 -0
  503. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-resize.ts +285 -0
  504. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.ts +184 -0
  505. package/src/proxmox/generated/pve/nodes-node-lxc-vmid.ts +240 -0
  506. package/src/proxmox/generated/pve/nodes-node-network.ts +234 -0
  507. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-config.ts +441 -0
  508. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.ts +215 -0
  509. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.ts +145 -0
  510. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.ts +664 -0
  511. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.ts +188 -0
  512. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-status.ts +233 -0
  513. package/src/proxmox/generated/pve/nodes-node-qemu-vmid.ts +187 -0
  514. package/src/proxmox/generated/pve/nodes-node-qemu.ts +204 -0
  515. package/src/proxmox/generated/pve/nodes-node-replication.ts +187 -0
  516. package/src/proxmox/generated/pve/nodes-node-services.ts +221 -0
  517. package/src/proxmox/generated/pve/nodes-node-storage-storage.ts +226 -0
  518. package/src/proxmox/generated/pve/nodes-node-storage.ts +40 -0
  519. package/src/proxmox/generated/pve/nodes-node-subscription.ts +168 -0
  520. package/src/proxmox/generated/pve/nodes-node-vzdump.ts +96 -0
  521. package/src/proxmox/generated/pve/nodes-node.ts +211 -0
  522. package/src/proxmox/generated/pve/nodes.ts +235 -0
  523. package/src/proxmox/generated/pve/pools.ts +78 -0
  524. package/src/proxmox/generated/pve/storage.ts +213 -0
  525. package/src/proxmox/generated/pve/version.ts +21 -0
  526. package/src/proxmox/generated/pve.ts +69 -7403
  527. package/src/proxmox/group.ts +2 -0
  528. package/src/proxmox/ha-resource.ts +5 -0
  529. package/src/proxmox/ha-rule.ts +2 -0
  530. package/src/proxmox/index.ts +12 -0
  531. package/src/proxmox/lxc-interior.test.ts +77 -0
  532. package/src/proxmox/lxc-lifecycle.ts +48 -9
  533. package/src/proxmox/metric-server-form.ts +10 -0
  534. package/src/proxmox/metric-server.ts +11 -2
  535. package/src/proxmox/network-apply.ts +4 -0
  536. package/src/proxmox/node-network-form.ts +19 -0
  537. package/src/proxmox/node-network.ts +9 -3
  538. package/src/proxmox/notification-matcher.ts +5 -0
  539. package/src/proxmox/notification-target-endpoint.ts +46 -0
  540. package/src/proxmox/notification-target-form.ts +11 -0
  541. package/src/proxmox/notification-target.ts +5 -2
  542. package/src/proxmox/pbs-datastore-endpoint.ts +29 -0
  543. package/src/proxmox/pbs-datastore.ts +8 -1
  544. package/src/proxmox/pbs-notification-matcher.ts +5 -0
  545. package/src/proxmox/pbs-notification-target-endpoint.ts +45 -0
  546. package/src/proxmox/pbs-notification-target-lifecycle.ts +16 -14
  547. package/src/proxmox/pbs-prune-job.ts +2 -0
  548. package/src/proxmox/pbs-sync-job-form.ts +2 -0
  549. package/src/proxmox/pbs-verify-job.ts +2 -0
  550. package/src/proxmox/pool.ts +2 -0
  551. package/src/proxmox/provision-baseline.test.ts +38 -0
  552. package/src/proxmox/provision-baseline.ts +73 -14
  553. package/src/proxmox/provision-bootstrap.test.ts +51 -0
  554. package/src/proxmox/qemu.ts +9 -1
  555. package/src/proxmox/replication-job.ts +5 -0
  556. package/src/proxmox/resource-guard.ts +61 -0
  557. package/src/proxmox/resource-spec.ts +129 -0
  558. package/src/proxmox/resource.ts +52 -93
  559. package/src/proxmox/role.ts +2 -0
  560. package/src/proxmox/sdn-apply.ts +4 -0
  561. package/src/proxmox/sdn-subnet.ts +5 -0
  562. package/src/proxmox/sdn-vnet.ts +2 -0
  563. package/src/proxmox/sdn-zone.ts +2 -0
  564. package/src/proxmox/storage.ts +2 -0
  565. package/src/proxmox/user.ts +2 -0
  566. package/src/proxmox/zfs-pool-write.ts +12 -0
  567. package/src/proxmox/zfs-pool.ts +2 -1
@@ -4,13 +4,14 @@ Every `@homeflare/alchemy/proxmox` resource runs as a short-lived PVE token that
4
4
  a **provision user**. That user, and the access it holds, is the baseline a cluster needs before a
5
5
  stack can manage anything on it. This subpath ships it as one description with two readers:
6
6
 
7
- | export | what it is |
8
- | --------------------------- | ------------------------------------------------------------------------------------------- |
9
- | `PROVISION_PRIVILEGES` | the provision role's privileges: 27, sorted, the exact set |
10
- | `PROVISION_DEFAULTS` | the generic names in the last table, each overridable |
11
- | `provisionBaseline(names)` | the resolved baseline both readers use; refuses names that are not PVE-shaped |
12
- | `declareProvisionBaseline` | the baseline as `Proxmox.Role`, `Proxmox.Group`, `Proxmox.User` and `Proxmox.Acl` resources |
13
- | `provisionBootstrap(names)` | the one-time root commands for a new cluster or node, as a `sh` script |
7
+ | export | what it is |
8
+ | --------------------------- | --------------------------------------------------------------------------------------------------- |
9
+ | `PROVISION_PRIVILEGES` | the provision role's privileges: 27, sorted, the exact set |
10
+ | `PROVISION_DEFAULTS` | the generic names in the last table, each overridable |
11
+ | `ProvisionNames` | those names, plus `groupComment` / `provisionComment` / `readComment`, each defaulting to `comment` |
12
+ | `provisionBaseline(names)` | the resolved baseline both readers use; refuses names that are not PVE-shaped |
13
+ | `declareProvisionBaseline` | the baseline as `Proxmox.Role`, `Proxmox.Group`, `Proxmox.User` and `Proxmox.Acl` resources |
14
+ | `provisionBootstrap(names)` | the one-time root commands for a new cluster or node, as a `sh` script |
14
15
 
15
16
  ## The chicken and the egg
16
17
 
@@ -52,6 +53,30 @@ So root makes it once, and the stack takes it over from there:
52
53
  clean adoption and the deploy writes nothing. From then on, a hand edit (a privilege added in
53
54
  the UI, a user dropped from the group) shows up as a diff on the next plan.
54
55
 
56
+ ## A cluster that already exists
57
+
58
+ ⚠️ **The common case is not a blank cluster.** One that already has a mint group and a
59
+ read user — with their own live comments, which another stack may already declare at
60
+ those values — must not have them rewritten by a bootstrap that only came to add a
61
+ provision lane. A comment per object says that, and each one defaults to `comment`:
62
+
63
+ ```ts
64
+ const names = {
65
+ role: 'LXCProvisioner',
66
+ groupComment: '', // live: no comment at all
67
+ readComment: 'mint target: read (ops)', // live: its own wording
68
+ provisionComment: 'mint target: provision (ops)', // the one new object
69
+ };
70
+ ```
71
+
72
+ The script then prints `group hf-mint: ok` and `user hf-read@pve: ok` and writes only
73
+ the role, the new user and its grant. ⛔ With one shared comment it would modify all
74
+ three, and the next deploy of the stack that declares the other two would write them
75
+ back — a loop that looks like drift and is not.
76
+
77
+ ★ `readComment` goes with its lane: a `null` `readUser` drops the user, so the field is
78
+ neither used nor checked.
79
+
55
80
  ## Every cluster, every node
56
81
 
57
82
  - **A new cluster:** run the bootstrap on one node, then deploy.
@@ -71,6 +96,8 @@ declareProvisionBaseline('pve', target, names); // the resources
71
96
  ```
72
97
 
73
98
  - `readUser: null` gives a baseline with no read lane.
99
+ - `comment` is every object's comment; `groupComment`, `provisionComment` and
100
+ `readComment` override it one object at a time.
74
101
  - `readRole` must already exist (`PVEAuditor` is built in). The script stops before any write if
75
102
  it does not.
76
103
  - A name that is not PVE-shaped, or that would need quoting in a shell, is refused by both
@@ -143,6 +143,46 @@ guest that needs `/dev/net/tun` (a Mesh door) or `/dev/dri` is created in two st
143
143
  - **Pool membership, HA, firewall rules, snapshots, replication** are not declared here. Use their
144
144
  own resources, or manage them by hand.
145
145
 
146
+ ### ⛔ The guest's INSIDE is not reachable, and no kit Resource can change that
147
+
148
+ This resource declares a guest's PVE-level config: the keys in
149
+ `/nodes/{node}/lxc/{vmid}/config`. It cannot install a package, write a file into the guest, or
150
+ define a service — and **neither can anything else built on PVE's API**, because PVE does not
151
+ expose it for containers:
152
+
153
+ | Reach inside | QEMU VM | LXC container |
154
+ | ------------- | ------------------------------------- | ------------- |
155
+ | Run a command | `POST …/qemu/{vmid}/agent/exec` | — |
156
+ | Write a file | `POST …/qemu/{vmid}/agent/file-write` | — |
157
+ | Read a file | `GET …/qemu/{vmid}/agent/file-read` | — |
158
+ | cloud-init | `…/qemu/{vmid}/cloudinit` | — |
159
+
160
+ Measured across the whole `/nodes/{node}/lxc/{vmid}/…` endpoint set in
161
+ `src/proxmox/generated/pve.ts`, and pinned by `src/proxmox/lxc-interior.test.ts` so that it fails
162
+ the day PVE adds one. The only reach inside is `termproxy` / `vncwebsocket`, an interactive
163
+ console for a person — not something a resource can diff.
164
+
165
+ ⚠️ **Sibling families are not at parity.** QEMU and LXC sit under the same `/nodes/{node}/…` tree
166
+ and have completely different reach. Do not infer one from the other, here or anywhere else.
167
+
168
+ So a generic, vendor-API-based Resource for a container's interior **cannot be written**: there is
169
+ nothing to wrap. What is left, in the order that keeps a change declarative:
170
+
171
+ 1. **Bake it into the template.** Publish a rootfs tarball to `vztmpl` and name it as
172
+ `ostemplate`. ⚠️ `ostemplate` is create-only and never read back, so the interior gets no drift
173
+ detection, and changing it REPLACES the guest. Right for something stateless and immutable; a
174
+ decision for anything else.
175
+ 2. **A first-boot artifact** the image already carries, which configures itself from what it can
176
+ see locally.
177
+ 3. **A recorded one-time human step**, named as undeclared.
178
+
179
+ ⛔ **An exec-over-SSH resource is not option zero.** It is reachable, but not through the vendor,
180
+ so it needs a credential and a network path _to the guest_ — and if the guest is what provides
181
+ credentials, naming or network reach to others, that inverts the bootstrap: the new system's first
182
+ boot then depends on its own output, and it fails when the dependency is down. The
183
+ `@homeflare/alchemy/launchd` `HostRunner` seam is where such a runner would plug in, and the kit
184
+ ships only `localRunner()` and `sudoRunner()` on purpose.
185
+
146
186
  ## Adopting
147
187
 
148
188
  ⛔ **No live guest is adopted without `adopt(true)` or `--adopt`, and an adoption never changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homeflare/alchemy",
3
- "version": "0.14.0",
3
+ "version": "0.19.0",
4
4
  "description": "Custom Alchemy providers for Cloudflare, Proxmox, OpenBao, Forgejo, Talos, launchd and Caddy, and a verifier that proves adoptions are no-ops.",
5
5
  "homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/alchemy#readme",
6
6
  "bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
@@ -56,6 +56,14 @@
56
56
  "types": "./dist/launchd/index.d.ts",
57
57
  "default": "./dist/launchd/index.js"
58
58
  },
59
+ "./netbox": {
60
+ "types": "./dist/netbox/index.d.ts",
61
+ "default": "./dist/netbox/index.js"
62
+ },
63
+ "./linux": {
64
+ "types": "./dist/linux/index.d.ts",
65
+ "default": "./dist/linux/index.js"
66
+ },
59
67
  "./caddy": {
60
68
  "types": "./dist/caddy/index.d.ts",
61
69
  "default": "./dist/caddy/index.js"
@@ -0,0 +1,220 @@
1
+ /**
2
+ * HostDirectory's props, validation and lifecycle over a HostRunner.
3
+ *
4
+ * ★ WHY IT EXISTS AT ALL. `Host.File` and `Remote.File` both refuse to create a parent directory,
5
+ * on purpose — creating one would invent an owner and a mode nobody declared. The consequence is
6
+ * that a first deploy into a new tree fails on its first file. This resource is the declaration
7
+ * that was missing, not a convenience flag on the file.
8
+ * ⛔ ONE DIRECTORY, NEVER A CHAIN. A missing parent is a refusal that names it, so a tree is a
9
+ * chain of declarations whose modes and owners are all visible in the plan. `mkdir -p` would
10
+ * create the intermediate levels at whatever umask the deploy happened to run with.
11
+ * ⛔ DELETE IS `rmdir`, NEVER RECURSIVE. A directory that still holds files is a refusal: removing
12
+ * a declaration must not be able to remove data the declaration never mentioned.
13
+ */
14
+ import type { Diff } from 'alchemy/Diff';
15
+ import { pathProblems } from '../launchd/host-file-form.ts';
16
+ import { type HostRunner, canActAsRoot } from '../launchd/runner.ts';
17
+
18
+ export interface HostDirectoryProps {
19
+ /** Absolute path. ⚠️ Its parent must already exist — declare it as another HostDirectory. */
20
+ path: string;
21
+ /** Permission bits. @default 0o755 */
22
+ mode?: number;
23
+ /** User name or numeric uid. Omitted: whoever the runner writes as. */
24
+ owner?: string | number;
25
+ /** Group name or numeric gid. Omitted: the parent's default group. */
26
+ group?: string | number;
27
+ }
28
+
29
+ export interface HostDirectoryAttributes {
30
+ path: string;
31
+ mode: number;
32
+ uid: number;
33
+ gid: number;
34
+ }
35
+
36
+ export const DEFAULT_DIR_MODE = 0o755;
37
+
38
+ export const refuse = (path: string, message: string): Error =>
39
+ new Error(`Host.Directory ${path}: ${message}`);
40
+
41
+ const idProblems = (name: string, value: string | number | undefined): string[] => {
42
+ if (value === undefined) return [];
43
+ if (typeof value === 'number') {
44
+ return Number.isSafeInteger(value) && value >= 0 ? [] : [`${name} must be a non-negative id`];
45
+ }
46
+ return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\d+$/.test(value)
47
+ ? []
48
+ : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];
49
+ };
50
+
51
+ export const directoryProblems = (props: HostDirectoryProps): string[] => {
52
+ const found = pathProblems(props.path);
53
+ const mode = props.mode ?? DEFAULT_DIR_MODE;
54
+ if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');
55
+ found.push(...idProblems('owner', props.owner), ...idProblems('group', props.group));
56
+ return found;
57
+ };
58
+
59
+ const numericId = (value: string | number): number | undefined =>
60
+ typeof value === 'number' ? value : /^\d+$/.test(value) ? Number(value) : undefined;
61
+
62
+ type Desired = { readonly mode: number; readonly uid?: number; readonly gid?: number };
63
+
64
+ const desired = async (runner: HostRunner, props: HostDirectoryProps): Promise<Desired> => {
65
+ const found = directoryProblems(props);
66
+ if (found.length > 0) throw refuse(props.path, found.join('; '));
67
+ let uid: number | undefined;
68
+ let gid: number | undefined;
69
+ if (props.owner !== undefined) {
70
+ uid = numericId(props.owner) ?? (await runner.lookupUser(String(props.owner)))?.uid;
71
+ if (uid === undefined) throw refuse(props.path, `no user ${String(props.owner)} on this host`);
72
+ }
73
+ if (props.group !== undefined) {
74
+ gid = numericId(props.group) ?? (await runner.lookupGroup(String(props.group)));
75
+ if (gid === undefined) throw refuse(props.path, `no group ${String(props.group)} on this host`);
76
+ }
77
+ return {
78
+ mode: props.mode ?? DEFAULT_DIR_MODE,
79
+ ...(uid === undefined ? {} : { uid }),
80
+ ...(gid === undefined ? {} : { gid }),
81
+ };
82
+ };
83
+
84
+ export const readDirectory = async (
85
+ runner: HostRunner,
86
+ path: string,
87
+ ): Promise<HostDirectoryAttributes | undefined> => {
88
+ const stat = await runner.stat(path);
89
+ if (stat === undefined || stat.kind !== 'directory') return undefined;
90
+ return { gid: stat.gid, mode: stat.mode, path, uid: stat.uid };
91
+ };
92
+
93
+ const octal = (mode: number) => mode.toString(8).padStart(3, '0');
94
+
95
+ /** One program, checked: anything but exit 0 is an Error naming the argv and the host's words. */
96
+ const must = async (runner: HostRunner, path: string, argv: readonly string[]): Promise<void> => {
97
+ const result = await runner.exec(argv);
98
+ if (result.exitCode !== 0) {
99
+ throw refuse(
100
+ path,
101
+ `${argv.join(' ')} -> ${String(result.exitCode)}: ${result.stderr.trim().slice(0, 300)}`,
102
+ );
103
+ }
104
+ };
105
+
106
+ /**
107
+ * Create it when absent, then bring mode and ownership to the declaration and read back.
108
+ * ⚠️ `mkdir -m` sets the mode with chmod(2) after mkdir(2), so it is exact — plain `mkdir` would
109
+ * mask the declared bits through the deploying shell's umask and the drift would return forever.
110
+ */
111
+ export const reconcileDirectory = async (
112
+ runner: HostRunner,
113
+ props: HostDirectoryProps,
114
+ output?: HostDirectoryAttributes,
115
+ adopt = false,
116
+ ): Promise<HostDirectoryAttributes> => {
117
+ const want = await desired(runner, props);
118
+ if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {
119
+ throw refuse(
120
+ props.path,
121
+ `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy through ` +
122
+ 'a root destination or a privileged HostRunner. This provider never calls sudo itself.',
123
+ );
124
+ }
125
+ const stat = await runner.stat(props.path);
126
+ if (stat !== undefined && stat.kind !== 'directory') {
127
+ throw refuse(props.path, `is a ${stat.kind}, not a directory`);
128
+ }
129
+ /**
130
+ * ⛔ A DIRECTORY THIS RESOURCE DOES NOT OWN IS NEVER RE-MODED. Where the engine's probe never
131
+ * looked — a replace's new path, a create whose props were still an Output — an existing
132
+ * directory with a different mode or owner is someone else's. ★ Adoption takes it over.
133
+ */
134
+ if (output === undefined && stat !== undefined && !adopt) {
135
+ const drifted = stat.mode !== want.mode || (want.uid !== undefined && stat.uid !== want.uid);
136
+ if (drifted) {
137
+ throw refuse(
138
+ props.path,
139
+ 'already exists with another mode or owner and is not this resource. Remove it, or ' +
140
+ 'declare it as a new resource and deploy with --adopt.',
141
+ );
142
+ }
143
+ }
144
+ if (stat === undefined) {
145
+ const parent = props.path.slice(0, props.path.lastIndexOf('/')) || '/';
146
+ const above = await runner.stat(parent);
147
+ if (above === undefined || above.kind !== 'directory') {
148
+ throw refuse(props.path, `its parent ${parent} does not exist; declare that directory too`);
149
+ }
150
+ await must(runner, props.path, ['mkdir', '-m', octal(want.mode), '--', props.path]);
151
+ } else if (stat.mode !== want.mode) {
152
+ await must(runner, props.path, ['chmod', octal(want.mode), '--', props.path]);
153
+ }
154
+ /**
155
+ * ★ Only when it is actually wrong. A `chown` that changes nothing is still a write on the host
156
+ * and a line in the deploy log, and "a converged resource is not touched" is the rule this
157
+ * whole family is judged by.
158
+ */
159
+ const owner = ownerMatches(
160
+ { gid: stat?.gid ?? -1, mode: 0, path: props.path, uid: stat?.uid ?? -1 },
161
+ want,
162
+ )
163
+ ? undefined
164
+ : ownerArg(want);
165
+ if (owner !== undefined) await must(runner, props.path, ['chown', owner, '--', props.path]);
166
+ const after = await readDirectory(runner, props.path);
167
+ // ⚠️ READ BACK: a runner that ignored the mode shows up here, not as a forever-`update`.
168
+ if (after === undefined || after.mode !== want.mode || !ownerMatches(after, want)) {
169
+ throw refuse(props.path, 'the directory on the host does not match the declaration');
170
+ }
171
+ return after;
172
+ };
173
+
174
+ /** ⛔ Never `<uid>:` — GNU chown reads a trailing colon as that user's LOGIN group, undeclared. */
175
+ const ownerArg = (want: Desired): string | undefined => {
176
+ if (want.uid === undefined) return want.gid === undefined ? undefined : `:${String(want.gid)}`;
177
+ return want.gid === undefined ? String(want.uid) : `${String(want.uid)}:${String(want.gid)}`;
178
+ };
179
+
180
+ const ownerMatches = (live: HostDirectoryAttributes, want: Desired): boolean =>
181
+ (want.uid === undefined || live.uid === want.uid) &&
182
+ (want.gid === undefined || live.gid === want.gid);
183
+
184
+ /**
185
+ * ★ A plan that will write asks the runner first (`checkWrite`, when it has one) — a directory is
186
+ * created and chmod'd through `exec`, but a runner that refuses to touch the path at all should
187
+ * say so at plan time rather than mid-apply.
188
+ */
189
+ export const diffDirectory = async (
190
+ runner: HostRunner,
191
+ news: HostDirectoryProps,
192
+ output: HostDirectoryAttributes,
193
+ ): Promise<Diff> => {
194
+ const want = await desired(runner, news);
195
+ if (news.path !== output.path) return { action: 'replace' };
196
+ const live = await readDirectory(runner, news.path);
197
+ // ⛔ BOTH: the live directory catches a hand chmod, the stored attributes catch a deploy that
198
+ // died after the mkdir and before its commit.
199
+ const converged =
200
+ live !== undefined &&
201
+ live.mode === want.mode &&
202
+ ownerMatches(live, want) &&
203
+ output.mode === want.mode &&
204
+ ownerMatches(output, want);
205
+ if (converged) return { action: 'noop' };
206
+ await runner.checkWrite?.(news.path, want);
207
+ return { action: 'update' };
208
+ };
209
+
210
+ /** Remove it. ⛔ `rmdir`: a directory that still holds anything is a refusal, never a recursion. */
211
+ export const deleteDirectory = async (
212
+ runner: HostRunner,
213
+ output: HostDirectoryAttributes,
214
+ ): Promise<void> => {
215
+ const stat = await runner.stat(output.path);
216
+ if (stat === undefined) return;
217
+ if (stat.kind !== 'directory')
218
+ throw refuse(output.path, `is now a ${stat.kind}; not removing it`);
219
+ await must(runner, output.path, ['rmdir', '--', output.path]);
220
+ };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * HostDirectory against the fake host: the resource that exists because every file resource refuses
3
+ * to create a parent.
4
+ */
5
+ import { describe, expect, test } from 'bun:test';
6
+ import {
7
+ deleteDirectory,
8
+ diffDirectory,
9
+ directoryProblems,
10
+ readDirectory,
11
+ reconcileDirectory,
12
+ } from './directory-lifecycle.ts';
13
+ import { fakeLinuxHost } from './fake-linux-host.ts';
14
+
15
+ const host = () =>
16
+ fakeLinuxHost({
17
+ dirs: { '/opt/app': 0 },
18
+ groups: { app: 60 },
19
+ users: { app: { gid: 60, home: '/opt/app', uid: 900 } },
20
+ });
21
+
22
+ describe('create', () => {
23
+ test('makes the directory with the declared mode and owner, then reads it back', async () => {
24
+ const fake = host();
25
+ const attrs = await reconcileDirectory(fake.runner, {
26
+ group: 'app',
27
+ mode: 0o750,
28
+ owner: 'app',
29
+ path: '/opt/app/etc',
30
+ });
31
+ expect(attrs).toEqual({ gid: 60, mode: 0o750, path: '/opt/app/etc', uid: 900 });
32
+ // ⚠️ `mkdir -m` carries the mode, so a umask never masks a declared bit.
33
+ expect(fake.calls.some((call) => call[0] === 'mkdir' && call.includes('750'))).toBe(true);
34
+ });
35
+
36
+ test('a missing parent is a refusal that names it, never an mkdir -p', async () => {
37
+ const fake = host();
38
+ await expect(reconcileDirectory(fake.runner, { path: '/opt/app/one/two' })).rejects.toThrow(
39
+ /its parent \/opt\/app\/one does not exist/,
40
+ );
41
+ expect(fake.calls.some((call) => call.includes('-p'))).toBe(false);
42
+ });
43
+
44
+ test('a path that is a file is refused rather than replaced', async () => {
45
+ const fake = host();
46
+ await fake.runner.writeFileAtomic('/opt/app/thing', new Uint8Array(), { mode: 0o644 });
47
+ await expect(reconcileDirectory(fake.runner, { path: '/opt/app/thing' })).rejects.toThrow(
48
+ /is a file, not a directory/,
49
+ );
50
+ });
51
+ });
52
+
53
+ describe('drift', () => {
54
+ test('a hand chmod is an update, and reconcile puts it back', async () => {
55
+ const fake = host();
56
+ const output = await reconcileDirectory(fake.runner, { mode: 0o750, path: '/opt/app/etc' });
57
+ expect(await diffDirectory(fake.runner, { mode: 0o750, path: '/opt/app/etc' }, output)).toEqual(
58
+ {
59
+ action: 'noop',
60
+ },
61
+ );
62
+ const live = fake.modes.get('/opt/app/etc');
63
+ if (live !== undefined) live.mode = 0o777;
64
+ expect(await diffDirectory(fake.runner, { mode: 0o750, path: '/opt/app/etc' }, output)).toEqual(
65
+ {
66
+ action: 'update',
67
+ },
68
+ );
69
+ const fixed = await reconcileDirectory(
70
+ fake.runner,
71
+ { mode: 0o750, path: '/opt/app/etc' },
72
+ output,
73
+ );
74
+ expect(fixed.mode).toBe(0o750);
75
+ });
76
+
77
+ test('a directory that is not this resource is refused unless adoption is on', async () => {
78
+ const fake = host();
79
+ await reconcileDirectory(fake.runner, { mode: 0o700, path: '/opt/app/etc' });
80
+ await expect(
81
+ reconcileDirectory(fake.runner, { mode: 0o755, path: '/opt/app/etc' }),
82
+ ).rejects.toThrow(/deploy with --adopt/);
83
+ const adopted = await reconcileDirectory(
84
+ fake.runner,
85
+ { mode: 0o755, path: '/opt/app/etc' },
86
+ undefined,
87
+ true,
88
+ );
89
+ expect(adopted.mode).toBe(0o755);
90
+ });
91
+
92
+ test('a directory that already matches is not touched — not even a no-op chown', async () => {
93
+ const fake = host();
94
+ const props = { group: 'app', mode: 0o755, owner: 'app', path: '/opt/app/etc' };
95
+ const output = await reconcileDirectory(fake.runner, props);
96
+ const before = fake.calls.length;
97
+ await reconcileDirectory(fake.runner, props, output);
98
+ const after = fake.calls.slice(before).map((call) => call[0]);
99
+ expect(after).not.toContain('mkdir');
100
+ expect(after).not.toContain('chmod');
101
+ // ⛔ A chown that changes nothing is still a write on the host and a line in the deploy log.
102
+ expect(after).not.toContain('chown');
103
+ });
104
+ });
105
+
106
+ describe('delete', () => {
107
+ test('is rmdir: a directory that still holds a file is a refusal', async () => {
108
+ const fake = host();
109
+ const attrs = await reconcileDirectory(fake.runner, { path: '/opt/app/etc' });
110
+ await fake.runner.writeFileAtomic('/opt/app/etc/kept.conf', new Uint8Array(), { mode: 0o644 });
111
+ await expect(deleteDirectory(fake.runner, attrs)).rejects.toThrow(/Directory not empty/);
112
+ expect(fake.calls.some((call) => call.includes('-r') || call.includes('-rf'))).toBe(false);
113
+ });
114
+
115
+ test('an empty directory goes, and a second delete is a no-op', async () => {
116
+ const fake = host();
117
+ const attrs = await reconcileDirectory(fake.runner, { path: '/opt/app/etc' });
118
+ await deleteDirectory(fake.runner, attrs);
119
+ expect(await readDirectory(fake.runner, '/opt/app/etc')).toBeUndefined();
120
+ await deleteDirectory(fake.runner, attrs);
121
+ });
122
+ });
123
+
124
+ describe('validation', () => {
125
+ test('refuses a relative or unnormalised path and an impossible mode', () => {
126
+ expect(directoryProblems({ path: 'opt/app' })).toContain('path must be absolute');
127
+ expect(directoryProblems({ path: '/opt/../etc' }).length).toBeGreaterThan(0);
128
+ expect(directoryProblems({ mode: 0o10000, path: '/opt/app' })).toContain(
129
+ 'mode must be 0–0o7777',
130
+ );
131
+ });
132
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `Host.Directory` — one directory on the host, declared: path, mode, owner, group.
3
+ *
4
+ * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt`: a directory already at the path reads as `Unowned`, and
5
+ * where the plan never asked, reconcile honours the same flag (docs/ownership.md).
6
+ * ★ IT IS NOT LINUX-ONLY. `mkdir`, `chmod`, `chown` and `rmdir` are the same argv on macOS, so this
7
+ * resource works through any HostRunner; it lives in the linux subpath because the gap it closes
8
+ * was found there (a first deploy into a new tree, which every file resource refuses to create).
9
+ */
10
+ import { Resource } from 'alchemy';
11
+ import { Unowned } from 'alchemy/AdoptPolicy';
12
+ import { isResolved } from 'alchemy/Diff';
13
+ import * as Provider from 'alchemy/Provider';
14
+ import * as Effect from 'effect/Effect';
15
+ import { lift, resolvedString } from '../launchd/host-effect.ts';
16
+ import { HostRunnerService } from '../launchd/runner.ts';
17
+ import { adoptsAtApply } from '../ownership/adopt.ts';
18
+ import { noteUnfinished } from '../ownership/resume.ts';
19
+ import type { HostDirectoryAttributes, HostDirectoryProps } from './directory-lifecycle.ts';
20
+ import {
21
+ deleteDirectory,
22
+ diffDirectory,
23
+ readDirectory,
24
+ reconcileDirectory,
25
+ } from './directory-lifecycle.ts';
26
+
27
+ export type { HostDirectoryAttributes, HostDirectoryProps } from './directory-lifecycle.ts';
28
+
29
+ export interface HostDirectory extends Resource<
30
+ 'Host.Directory',
31
+ HostDirectoryProps,
32
+ HostDirectoryAttributes
33
+ > {}
34
+
35
+ export const HostDirectory = Resource<HostDirectory>('Host.Directory');
36
+
37
+ export const HostDirectoryProvider = () =>
38
+ Provider.effect(
39
+ HostDirectory,
40
+ Effect.gen(function* () {
41
+ const runner = yield* HostRunnerService;
42
+ return HostDirectory.Provider.of({
43
+ list: () => Effect.succeed([]),
44
+
45
+ read: ({ olds, output }) =>
46
+ lift(async () => {
47
+ const found = await readDirectory(runner, olds.path);
48
+ if (found === undefined) return undefined;
49
+ return output === undefined ? Unowned(found) : found;
50
+ }),
51
+
52
+ diff: ({ instanceId, news, output }) => {
53
+ if (output === undefined) return noteUnfinished(instanceId);
54
+ if (isResolved(news)) return lift(() => diffDirectory(runner, news, output));
55
+ // ⛔ A new path is a replace even while the rest is unresolved: the engine's default
56
+ // update would create the new directory and leave the old one behind forever.
57
+ const path = resolvedString(news, 'path');
58
+ return Effect.succeed(
59
+ path !== undefined && path !== output.path ? { action: 'replace' as const } : undefined,
60
+ );
61
+ },
62
+
63
+ reconcile: ({ fqn, instanceId, news, output }) =>
64
+ Effect.flatMap(adoptsAtApply({ fqn, instanceId, output }), (adopt) =>
65
+ lift(() => reconcileDirectory(runner, news, output, adopt)),
66
+ ),
67
+
68
+ delete: ({ output }) => lift(() => deleteDirectory(runner, output)),
69
+ });
70
+ }),
71
+ );