@homeflare/alchemy 0.15.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 (512) 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/proxmox/api-token-form.d.ts.map +1 -1
  67. package/dist/proxmox/apply-endpoints.d.ts +30 -0
  68. package/dist/proxmox/apply-endpoints.d.ts.map +1 -0
  69. package/dist/proxmox/ceph-daemon-form.d.ts +29 -1
  70. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
  71. package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
  72. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  73. package/dist/proxmox/ceph-fs.d.ts.map +1 -1
  74. package/dist/proxmox/ceph-osd-write.d.ts +19 -0
  75. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  76. package/dist/proxmox/ceph-pool.d.ts.map +1 -1
  77. package/dist/proxmox/constraints.d.ts +28 -5
  78. package/dist/proxmox/constraints.d.ts.map +1 -1
  79. package/dist/proxmox/generated/constraints/index.d.ts +1 -1
  80. package/dist/proxmox/generated/constraints/index.d.ts.map +1 -1
  81. package/dist/proxmox/generated/constraints/pbs-config.d.ts +6 -1
  82. package/dist/proxmox/generated/constraints/pbs-config.d.ts.map +1 -1
  83. package/dist/proxmox/generated/constraints/pve-access.d.ts +6 -1
  84. package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -1
  85. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +23 -0
  86. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts.map +1 -0
  87. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +23 -0
  88. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts.map +1 -0
  89. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +23 -0
  90. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts.map +1 -0
  91. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +23 -0
  92. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts.map +1 -0
  93. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +23 -0
  94. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts.map +1 -0
  95. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +23 -0
  96. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts.map +1 -0
  97. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +23 -0
  98. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts.map +1 -0
  99. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +23 -0
  100. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts.map +1 -0
  101. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +23 -0
  102. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts.map +1 -0
  103. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +23 -0
  104. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts.map +1 -0
  105. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +23 -0
  106. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts.map +1 -0
  107. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +23 -0
  108. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts.map +1 -0
  109. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +23 -0
  110. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -0
  111. package/dist/proxmox/generated/constraints/pve-pools.d.ts +6 -1
  112. package/dist/proxmox/generated/constraints/pve-pools.d.ts.map +1 -1
  113. package/dist/proxmox/generated/constraints/pve-storage.d.ts +6 -1
  114. package/dist/proxmox/generated/constraints/pve-storage.d.ts.map +1 -1
  115. package/dist/proxmox/generated/pbs/access-users.d.ts +137 -0
  116. package/dist/proxmox/generated/pbs/access-users.d.ts.map +1 -0
  117. package/dist/proxmox/generated/pbs/access.d.ts +171 -0
  118. package/dist/proxmox/generated/pbs/access.d.ts.map +1 -0
  119. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts +252 -0
  120. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts.map +1 -0
  121. package/dist/proxmox/generated/pbs/admin-metrics.d.ts +147 -0
  122. package/dist/proxmox/generated/pbs/admin-metrics.d.ts.map +1 -0
  123. package/dist/proxmox/generated/pbs/admin.d.ts +217 -0
  124. package/dist/proxmox/generated/pbs/admin.d.ts.map +1 -0
  125. package/dist/proxmox/generated/pbs/backup.d.ts +114 -0
  126. package/dist/proxmox/generated/pbs/backup.d.ts.map +1 -0
  127. package/dist/proxmox/generated/pbs/config-access-pam.d.ts +218 -0
  128. package/dist/proxmox/generated/pbs/config-access-pam.d.ts.map +1 -0
  129. package/dist/proxmox/generated/pbs/config-access.d.ts +203 -0
  130. package/dist/proxmox/generated/pbs/config-access.d.ts.map +1 -0
  131. package/dist/proxmox/generated/pbs/config-datastore.d.ts +195 -0
  132. package/dist/proxmox/generated/pbs/config-datastore.d.ts.map +1 -0
  133. package/dist/proxmox/generated/pbs/config-media-pool.d.ts +207 -0
  134. package/dist/proxmox/generated/pbs/config-media-pool.d.ts.map +1 -0
  135. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts +159 -0
  136. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts.map +1 -0
  137. package/dist/proxmox/generated/pbs/config-notifications.d.ts +199 -0
  138. package/dist/proxmox/generated/pbs/config-notifications.d.ts.map +1 -0
  139. package/dist/proxmox/generated/pbs/config-remote.d.ts +175 -0
  140. package/dist/proxmox/generated/pbs/config-remote.d.ts.map +1 -0
  141. package/dist/proxmox/generated/pbs/config-sync.d.ts +145 -0
  142. package/dist/proxmox/generated/pbs/config-sync.d.ts.map +1 -0
  143. package/dist/proxmox/generated/pbs/config-tape.d.ts +200 -0
  144. package/dist/proxmox/generated/pbs/config-tape.d.ts.map +1 -0
  145. package/dist/proxmox/generated/pbs/config-verify.d.ts +77 -0
  146. package/dist/proxmox/generated/pbs/config-verify.d.ts.map +1 -0
  147. package/dist/proxmox/generated/pbs/config.d.ts +119 -0
  148. package/dist/proxmox/generated/pbs/config.d.ts.map +1 -0
  149. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts +178 -0
  150. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts.map +1 -0
  151. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts +205 -0
  152. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts.map +1 -0
  153. package/dist/proxmox/generated/pbs/nodes-node.d.ts +166 -0
  154. package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -0
  155. package/dist/proxmox/generated/pbs/nodes.d.ts +171 -0
  156. package/dist/proxmox/generated/pbs/nodes.d.ts.map +1 -0
  157. package/dist/proxmox/generated/pbs/ping.d.ts +18 -0
  158. package/dist/proxmox/generated/pbs/ping.d.ts.map +1 -0
  159. package/dist/proxmox/generated/pbs/pull.d.ts +37 -0
  160. package/dist/proxmox/generated/pbs/pull.d.ts.map +1 -0
  161. package/dist/proxmox/generated/pbs/push.d.ts +36 -0
  162. package/dist/proxmox/generated/pbs/push.d.ts.map +1 -0
  163. package/dist/proxmox/generated/pbs/reader.d.ts +41 -0
  164. package/dist/proxmox/generated/pbs/reader.d.ts.map +1 -0
  165. package/dist/proxmox/generated/pbs/root.d.ts +16 -0
  166. package/dist/proxmox/generated/pbs/root.d.ts.map +1 -0
  167. package/dist/proxmox/generated/pbs/status.d.ts +54 -0
  168. package/dist/proxmox/generated/pbs/status.d.ts.map +1 -0
  169. package/dist/proxmox/generated/pbs/tape-drive.d.ts +181 -0
  170. package/dist/proxmox/generated/pbs/tape-drive.d.ts.map +1 -0
  171. package/dist/proxmox/generated/pbs/tape-media.d.ts +126 -0
  172. package/dist/proxmox/generated/pbs/tape-media.d.ts.map +1 -0
  173. package/dist/proxmox/generated/pbs/tape.d.ts +90 -0
  174. package/dist/proxmox/generated/pbs/tape.d.ts.map +1 -0
  175. package/dist/proxmox/generated/pbs/version.d.ts +20 -0
  176. package/dist/proxmox/generated/pbs/version.d.ts.map +1 -0
  177. package/dist/proxmox/generated/pbs.d.ts +44 -662
  178. package/dist/proxmox/generated/pbs.d.ts.map +1 -1
  179. package/dist/proxmox/generated/pve/access-roles.d.ts +164 -0
  180. package/dist/proxmox/generated/pve/access-roles.d.ts.map +1 -0
  181. package/dist/proxmox/generated/pve/access-users.d.ts +152 -0
  182. package/dist/proxmox/generated/pve/access-users.d.ts.map +1 -0
  183. package/dist/proxmox/generated/pve/access.d.ts +221 -0
  184. package/dist/proxmox/generated/pve/access.d.ts.map +1 -0
  185. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts +45 -0
  186. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts.map +1 -0
  187. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts +47 -0
  188. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts.map +1 -0
  189. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts +240 -0
  190. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts.map +1 -0
  191. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts +200 -0
  192. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts.map +1 -0
  193. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts +109 -0
  194. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts.map +1 -0
  195. package/dist/proxmox/generated/pve/cluster-firewall.d.ts +225 -0
  196. package/dist/proxmox/generated/pve/cluster-firewall.d.ts.map +1 -0
  197. package/dist/proxmox/generated/pve/cluster-ha.d.ts +199 -0
  198. package/dist/proxmox/generated/pve/cluster-ha.d.ts.map +1 -0
  199. package/dist/proxmox/generated/pve/cluster-jobs.d.ts +183 -0
  200. package/dist/proxmox/generated/pve/cluster-jobs.d.ts.map +1 -0
  201. package/dist/proxmox/generated/pve/cluster-metrics.d.ts +110 -0
  202. package/dist/proxmox/generated/pve/cluster-metrics.d.ts.map +1 -0
  203. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts +224 -0
  204. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts.map +1 -0
  205. package/dist/proxmox/generated/pve/cluster-notifications.d.ts +91 -0
  206. package/dist/proxmox/generated/pve/cluster-notifications.d.ts.map +1 -0
  207. package/dist/proxmox/generated/pve/cluster-options.d.ts +85 -0
  208. package/dist/proxmox/generated/pve/cluster-options.d.ts.map +1 -0
  209. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts +48 -0
  210. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts.map +1 -0
  211. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts +45 -0
  212. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts.map +1 -0
  213. package/dist/proxmox/generated/pve/cluster-replication.d.ts +130 -0
  214. package/dist/proxmox/generated/pve/cluster-replication.d.ts.map +1 -0
  215. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +220 -0
  216. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -0
  217. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts +229 -0
  218. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts.map +1 -0
  219. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts +229 -0
  220. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts.map +1 -0
  221. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts +51 -0
  222. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts.map +1 -0
  223. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts +225 -0
  224. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts.map +1 -0
  225. package/dist/proxmox/generated/pve/cluster-sdn.d.ts +202 -0
  226. package/dist/proxmox/generated/pve/cluster-sdn.d.ts.map +1 -0
  227. package/dist/proxmox/generated/pve/cluster.d.ts +69 -0
  228. package/dist/proxmox/generated/pve/cluster.d.ts.map +1 -0
  229. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts +167 -0
  230. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts.map +1 -0
  231. package/dist/proxmox/generated/pve/nodes-node-config.d.ts +43 -0
  232. package/dist/proxmox/generated/pve/nodes-node-config.d.ts.map +1 -0
  233. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts +225 -0
  234. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts.map +1 -0
  235. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts +145 -0
  236. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts.map +1 -0
  237. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts +195 -0
  238. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts.map +1 -0
  239. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts +191 -0
  240. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts.map +1 -0
  241. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts +33 -0
  242. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts.map +1 -0
  243. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts +56 -0
  244. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts.map +1 -0
  245. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts +28 -0
  246. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts.map +1 -0
  247. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts +186 -0
  248. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts.map +1 -0
  249. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts +191 -0
  250. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts.map +1 -0
  251. package/dist/proxmox/generated/pve/nodes-node-network.d.ts +150 -0
  252. package/dist/proxmox/generated/pve/nodes-node-network.d.ts.map +1 -0
  253. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts +301 -0
  254. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts.map +1 -0
  255. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts +170 -0
  256. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts.map +1 -0
  257. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts +144 -0
  258. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts.map +1 -0
  259. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts +34 -0
  260. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts.map +1 -0
  261. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts +137 -0
  262. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts.map +1 -0
  263. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts +179 -0
  264. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts.map +1 -0
  265. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts +143 -0
  266. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts.map +1 -0
  267. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts +158 -0
  268. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts.map +1 -0
  269. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts +187 -0
  270. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts.map +1 -0
  271. package/dist/proxmox/generated/pve/nodes-node-services.d.ts +115 -0
  272. package/dist/proxmox/generated/pve/nodes-node-services.d.ts.map +1 -0
  273. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts +202 -0
  274. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts.map +1 -0
  275. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts +40 -0
  276. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts.map +1 -0
  277. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts +173 -0
  278. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts.map +1 -0
  279. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts +97 -0
  280. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts.map +1 -0
  281. package/dist/proxmox/generated/pve/nodes-node.d.ts +207 -0
  282. package/dist/proxmox/generated/pve/nodes-node.d.ts.map +1 -0
  283. package/dist/proxmox/generated/pve/nodes.d.ts +236 -0
  284. package/dist/proxmox/generated/pve/nodes.d.ts.map +1 -0
  285. package/dist/proxmox/generated/pve/pools.d.ts +82 -0
  286. package/dist/proxmox/generated/pve/pools.d.ts.map +1 -0
  287. package/dist/proxmox/generated/pve/storage.d.ts +159 -0
  288. package/dist/proxmox/generated/pve/storage.d.ts.map +1 -0
  289. package/dist/proxmox/generated/pve/version.d.ts +21 -0
  290. package/dist/proxmox/generated/pve/version.d.ts.map +1 -0
  291. package/dist/proxmox/generated/pve.d.ts +69 -4766
  292. package/dist/proxmox/generated/pve.d.ts.map +1 -1
  293. package/dist/proxmox/index.js +996 -372
  294. package/dist/proxmox/index.js.map +48 -32
  295. package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
  296. package/dist/proxmox/metric-server-form.d.ts +11 -0
  297. package/dist/proxmox/metric-server-form.d.ts.map +1 -1
  298. package/dist/proxmox/metric-server.d.ts.map +1 -1
  299. package/dist/proxmox/network-apply.d.ts.map +1 -1
  300. package/dist/proxmox/node-network-form.d.ts +15 -0
  301. package/dist/proxmox/node-network-form.d.ts.map +1 -1
  302. package/dist/proxmox/node-network.d.ts.map +1 -1
  303. package/dist/proxmox/notification-target-endpoint.d.ts +26 -0
  304. package/dist/proxmox/notification-target-endpoint.d.ts.map +1 -0
  305. package/dist/proxmox/notification-target-form.d.ts +7 -0
  306. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  307. package/dist/proxmox/notification-target.d.ts.map +1 -1
  308. package/dist/proxmox/pbs-notification-target-endpoint.d.ts +29 -0
  309. package/dist/proxmox/pbs-notification-target-endpoint.d.ts.map +1 -0
  310. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  311. package/dist/proxmox/qemu.d.ts +10 -0
  312. package/dist/proxmox/qemu.d.ts.map +1 -1
  313. package/dist/proxmox/resource-guard.d.ts +15 -0
  314. package/dist/proxmox/resource-guard.d.ts.map +1 -0
  315. package/dist/proxmox/resource-spec.d.ts +15 -4
  316. package/dist/proxmox/resource-spec.d.ts.map +1 -1
  317. package/dist/proxmox/resource.d.ts +8 -0
  318. package/dist/proxmox/resource.d.ts.map +1 -1
  319. package/dist/proxmox/sdn-apply.d.ts.map +1 -1
  320. package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
  321. package/dist/proxmox/zfs-pool-write.d.ts +11 -0
  322. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
  323. package/dist/proxmox/zfs-pool.d.ts.map +1 -1
  324. package/docs/linux-host-measured.md +56 -0
  325. package/docs/linux-host.md +186 -0
  326. package/docs/netbox.md +103 -0
  327. package/package.json +9 -1
  328. package/src/linux/directory-lifecycle.ts +220 -0
  329. package/src/linux/directory.test.ts +132 -0
  330. package/src/linux/directory.ts +71 -0
  331. package/src/linux/fake-linux-host.ts +197 -0
  332. package/src/linux/fake-systemd.ts +103 -0
  333. package/src/linux/index.ts +32 -0
  334. package/src/linux/providers.ts +26 -0
  335. package/src/linux/region.test.ts +89 -0
  336. package/src/linux/region.ts +118 -0
  337. package/src/linux/remote-file-form.ts +96 -0
  338. package/src/linux/remote-file-lifecycle.test.ts +191 -0
  339. package/src/linux/remote-file-lifecycle.ts +161 -0
  340. package/src/linux/remote-file-plan.ts +154 -0
  341. package/src/linux/remote-file.ts +88 -0
  342. package/src/linux/ssh-command.test.ts +140 -0
  343. package/src/linux/ssh-command.ts +99 -0
  344. package/src/linux/ssh-runner.ts +188 -0
  345. package/src/linux/ssh-scripts.ts +105 -0
  346. package/src/linux/systemctl.ts +145 -0
  347. package/src/linux/unit-form.ts +183 -0
  348. package/src/linux/unit-handlers.ts +73 -0
  349. package/src/linux/unit-lifecycle.test.ts +220 -0
  350. package/src/linux/unit-lifecycle.ts +193 -0
  351. package/src/linux/unit-settle.ts +110 -0
  352. package/src/linux/unit.ts +81 -0
  353. package/src/netbox/client.test.ts +67 -0
  354. package/src/netbox/client.ts +146 -0
  355. package/src/netbox/constraint-guard.ts +57 -0
  356. package/src/netbox/constraints.test.ts +114 -0
  357. package/src/netbox/constraints.ts +152 -0
  358. package/src/netbox/generated/constraints/index.ts +23 -0
  359. package/src/netbox/generated/constraints/netbox-ipam.ts +32 -0
  360. package/src/netbox/index.ts +34 -0
  361. package/src/netbox/prefix-form.test.ts +127 -0
  362. package/src/netbox/prefix-form.ts +84 -0
  363. package/src/netbox/prefix.ts +136 -0
  364. package/src/netbox/resource.ts +208 -0
  365. package/src/netbox/values.test.ts +66 -0
  366. package/src/netbox/values.ts +40 -0
  367. package/src/proxmox/api-token-form.ts +5 -0
  368. package/src/proxmox/apply-endpoints.ts +34 -0
  369. package/src/proxmox/ceph-daemon-form.ts +56 -2
  370. package/src/proxmox/ceph-daemon.ts +10 -21
  371. package/src/proxmox/ceph-flag.ts +1 -0
  372. package/src/proxmox/ceph-fs.ts +1 -0
  373. package/src/proxmox/ceph-osd-write.ts +30 -4
  374. package/src/proxmox/ceph-pool.ts +10 -0
  375. package/src/proxmox/constraints-dialect.test.ts +165 -0
  376. package/src/proxmox/constraints-forms.test.ts +220 -0
  377. package/src/proxmox/constraints-host-forms.test.ts +132 -0
  378. package/src/proxmox/constraints-live.test.ts +155 -0
  379. package/src/proxmox/constraints-node-forms.test.ts +188 -0
  380. package/src/proxmox/constraints-required.test.ts +172 -0
  381. package/src/proxmox/constraints.test.ts +8 -63
  382. package/src/proxmox/constraints.ts +31 -6
  383. package/src/proxmox/generated/constraints/index.ts +27 -3
  384. package/src/proxmox/generated/constraints/pbs-config.ts +63 -1
  385. package/src/proxmox/generated/constraints/pve-access.ts +15 -3
  386. package/src/proxmox/generated/constraints/pve-cluster-backup.ts +74 -0
  387. package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +27 -0
  388. package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +35 -0
  389. package/src/proxmox/generated/constraints/pve-cluster-ha.ts +55 -0
  390. package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +59 -0
  391. package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +80 -0
  392. package/src/proxmox/generated/constraints/pve-cluster-replication.ts +43 -0
  393. package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +90 -0
  394. package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +57 -0
  395. package/src/proxmox/generated/constraints/pve-nodes-disks.ts +31 -0
  396. package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +82 -0
  397. package/src/proxmox/generated/constraints/pve-nodes-network.ts +77 -0
  398. package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +125 -0
  399. package/src/proxmox/generated/constraints/pve-pools.ts +6 -1
  400. package/src/proxmox/generated/constraints/pve-storage.ts +8 -3
  401. package/src/proxmox/generated/pbs/access-users.ts +137 -0
  402. package/src/proxmox/generated/pbs/access.ts +232 -0
  403. package/src/proxmox/generated/pbs/admin-datastore-store.ts +238 -0
  404. package/src/proxmox/generated/pbs/admin-metrics.ts +147 -0
  405. package/src/proxmox/generated/pbs/admin.ts +225 -0
  406. package/src/proxmox/generated/pbs/backup.ts +121 -0
  407. package/src/proxmox/generated/pbs/config-access-pam.ts +228 -0
  408. package/src/proxmox/generated/pbs/config-access.ts +228 -0
  409. package/src/proxmox/generated/pbs/config-datastore.ts +217 -0
  410. package/src/proxmox/generated/pbs/config-media-pool.ts +232 -0
  411. package/src/proxmox/generated/pbs/config-notifications-matchers.ts +187 -0
  412. package/src/proxmox/generated/pbs/config-notifications.ts +221 -0
  413. package/src/proxmox/generated/pbs/config-remote.ts +192 -0
  414. package/src/proxmox/generated/pbs/config-sync.ts +167 -0
  415. package/src/proxmox/generated/pbs/config-tape.ts +226 -0
  416. package/src/proxmox/generated/pbs/config-verify.ts +87 -0
  417. package/src/proxmox/generated/pbs/config.ts +135 -0
  418. package/src/proxmox/generated/pbs/nodes-node-dns.ts +229 -0
  419. package/src/proxmox/generated/pbs/nodes-node-services.ts +181 -0
  420. package/src/proxmox/generated/pbs/nodes-node.ts +244 -0
  421. package/src/proxmox/generated/pbs/nodes.ts +160 -0
  422. package/src/proxmox/generated/pbs/ping.ts +16 -0
  423. package/src/proxmox/generated/pbs/pull.ts +37 -0
  424. package/src/proxmox/generated/pbs/push.ts +36 -0
  425. package/src/proxmox/generated/pbs/reader.ts +41 -0
  426. package/src/proxmox/generated/pbs/root.ts +16 -0
  427. package/src/proxmox/generated/pbs/status.ts +50 -0
  428. package/src/proxmox/generated/pbs/tape-drive.ts +220 -0
  429. package/src/proxmox/generated/pbs/tape-media.ts +133 -0
  430. package/src/proxmox/generated/pbs/tape.ts +92 -0
  431. package/src/proxmox/generated/pbs/version.ts +16 -0
  432. package/src/proxmox/generated/pbs.ts +44 -787
  433. package/src/proxmox/generated/pve/access-roles.ts +168 -0
  434. package/src/proxmox/generated/pve/access-users.ts +155 -0
  435. package/src/proxmox/generated/pve/access.ts +217 -0
  436. package/src/proxmox/generated/pve/cluster-acme-plugins-id.ts +367 -0
  437. package/src/proxmox/generated/pve/cluster-acme-plugins.ts +366 -0
  438. package/src/proxmox/generated/pve/cluster-acme-tos.ts +232 -0
  439. package/src/proxmox/generated/pve/cluster-bulk-action.ts +227 -0
  440. package/src/proxmox/generated/pve/cluster-firewall-refs.ts +110 -0
  441. package/src/proxmox/generated/pve/cluster-firewall.ts +236 -0
  442. package/src/proxmox/generated/pve/cluster-ha.ts +203 -0
  443. package/src/proxmox/generated/pve/cluster-jobs.ts +190 -0
  444. package/src/proxmox/generated/pve/cluster-metrics.ts +116 -0
  445. package/src/proxmox/generated/pve/cluster-notifications-endpoints.ts +244 -0
  446. package/src/proxmox/generated/pve/cluster-notifications.ts +99 -0
  447. package/src/proxmox/generated/pve/cluster-options.ts +189 -0
  448. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.ts +272 -0
  449. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models.ts +268 -0
  450. package/src/proxmox/generated/pve/cluster-replication.ts +127 -0
  451. package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +218 -0
  452. package/src/proxmox/generated/pve/cluster-sdn-ipams.ts +229 -0
  453. package/src/proxmox/generated/pve/cluster-sdn-vnets-vnet.ts +245 -0
  454. package/src/proxmox/generated/pve/cluster-sdn-vnets.ts +49 -0
  455. package/src/proxmox/generated/pve/cluster-sdn-zones.ts +224 -0
  456. package/src/proxmox/generated/pve/cluster-sdn.ts +201 -0
  457. package/src/proxmox/generated/pve/cluster.ts +71 -0
  458. package/src/proxmox/generated/pve/nodes-node-ceph.ts +167 -0
  459. package/src/proxmox/generated/pve/nodes-node-config.ts +56 -0
  460. package/src/proxmox/generated/pve/nodes-node-disks.ts +237 -0
  461. package/src/proxmox/generated/pve/nodes-node-firewall.ts +239 -0
  462. package/src/proxmox/generated/pve/nodes-node-hardware.ts +215 -0
  463. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.ts +232 -0
  464. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.ts +1059 -0
  465. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.ts +49 -0
  466. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-resize.ts +285 -0
  467. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.ts +184 -0
  468. package/src/proxmox/generated/pve/nodes-node-lxc-vmid.ts +240 -0
  469. package/src/proxmox/generated/pve/nodes-node-network.ts +234 -0
  470. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-config.ts +441 -0
  471. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.ts +215 -0
  472. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.ts +145 -0
  473. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.ts +664 -0
  474. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.ts +188 -0
  475. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-status.ts +233 -0
  476. package/src/proxmox/generated/pve/nodes-node-qemu-vmid.ts +187 -0
  477. package/src/proxmox/generated/pve/nodes-node-qemu.ts +204 -0
  478. package/src/proxmox/generated/pve/nodes-node-replication.ts +187 -0
  479. package/src/proxmox/generated/pve/nodes-node-services.ts +221 -0
  480. package/src/proxmox/generated/pve/nodes-node-storage-storage.ts +226 -0
  481. package/src/proxmox/generated/pve/nodes-node-storage.ts +40 -0
  482. package/src/proxmox/generated/pve/nodes-node-subscription.ts +168 -0
  483. package/src/proxmox/generated/pve/nodes-node-vzdump.ts +96 -0
  484. package/src/proxmox/generated/pve/nodes-node.ts +211 -0
  485. package/src/proxmox/generated/pve/nodes.ts +235 -0
  486. package/src/proxmox/generated/pve/pools.ts +78 -0
  487. package/src/proxmox/generated/pve/storage.ts +213 -0
  488. package/src/proxmox/generated/pve/version.ts +21 -0
  489. package/src/proxmox/generated/pve.ts +69 -7403
  490. package/src/proxmox/lxc-interior.test.ts +17 -1
  491. package/src/proxmox/lxc-lifecycle.ts +48 -9
  492. package/src/proxmox/metric-server-form.ts +10 -0
  493. package/src/proxmox/metric-server.ts +11 -2
  494. package/src/proxmox/network-apply.ts +4 -0
  495. package/src/proxmox/node-network-form.ts +19 -0
  496. package/src/proxmox/node-network.ts +9 -3
  497. package/src/proxmox/notification-target-endpoint.ts +46 -0
  498. package/src/proxmox/notification-target-form.ts +11 -0
  499. package/src/proxmox/notification-target.ts +5 -2
  500. package/src/proxmox/pbs-notification-target-endpoint.ts +45 -0
  501. package/src/proxmox/pbs-notification-target-lifecycle.ts +16 -14
  502. package/src/proxmox/qemu.ts +9 -1
  503. package/src/proxmox/resource-guard.ts +61 -0
  504. package/src/proxmox/resource-spec.ts +16 -4
  505. package/src/proxmox/resource.ts +27 -27
  506. package/src/proxmox/sdn-apply.ts +4 -0
  507. package/src/proxmox/sdn-subnet.ts +5 -0
  508. package/src/proxmox/zfs-pool-write.ts +12 -0
  509. package/src/proxmox/zfs-pool.ts +2 -1
  510. package/dist/proxmox/generated/constraints/pve-cluster.d.ts +0 -18
  511. package/dist/proxmox/generated/constraints/pve-cluster.d.ts.map +0 -1
  512. package/src/proxmox/generated/constraints/pve-cluster.ts +0 -174
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `Netbox.Prefix` — one IP prefix and the decision recorded against it.
3
+ *
4
+ * ★ WHY THIS OBJECT CLASS IS THE FIRST ONE, AND NOT `Device`, `VLAN` OR `Site`. Three reasons,
5
+ * each measurable rather than aesthetic:
6
+ *
7
+ * 1. **It is the only interesting NetBox object with no foreign-key prerequisite.** MEASURED in
8
+ * the 4.7.0 schema: `WritablePrefixRequest.required` is exactly `["prefix"]`. A `VLAN` needs
9
+ * `vid` AND `name` and belongs to a group or site; a `Device` needs a role, a type, a site
10
+ * and usually a manufacturer — four more Resources before the first one can be declared.
11
+ * A scaffold that cannot declare anything until five families exist proves nothing.
12
+ * 2. **It is what the estate's NetBox is FOR.** The house's own IPAM agent describes its job as
13
+ * reconciling "what the network IS against what NetBox says it was DECIDED to be". A prefix
14
+ * is the atom of that decision. The estate has prefixes whose status is currently recorded
15
+ * only in an SSH config comment and a retirement doc — a management VLAN, a Thunderbolt
16
+ * point-to-point, and three ranges that are retired and still resolve in people's heads.
17
+ * 3. **`status: 'deprecated'` makes retirement declarable.** That is the move this unlocks:
18
+ * a retired range stops being folklore and becomes a line in a stack file with a reviewable
19
+ * diff. No other NetBox class turns an estate decision into one field this cleanly.
20
+ *
21
+ * ⚠️ ADOPT-FRIENDLY BY CONSTRUCTION. `locate` filters on the exact `prefix`; `identifies` then
22
+ * picks the declared VRF, because NetBox allows the same CIDR in two VRFs and in the global
23
+ * table. An instance that already holds `10.0.0.0/24` is BOUND, never duplicated — and ⛔ an
24
+ * adopt never clears prose it did not declare (prefix-form.ts).
25
+ *
26
+ * ⛔ THIS RESOURCE DOES NOT REMOVE PREFIXES IT DOES NOT DECLARE, and `defaultRemovalPolicy` is
27
+ * `retain` (resource.ts). Deleting a prefix in NetBox reparents its children and detaches its
28
+ * IP assignments; the record of why it existed survives only in the change log.
29
+ *
30
+ * ⛔ NO CREDENTIAL IS A PROP. `NETBOX_URL` and `NETBOX_TOKEN` are read from the environment at
31
+ * call time — the house convention, and the only shape that keeps a token out of Alchemy state.
32
+ */
33
+ import { Resource } from 'alchemy';
34
+ import * as Provider from 'alchemy/Provider';
35
+ import * as Effect from 'effect/Effect';
36
+ import { prefixBody, prefixMatches } from './prefix-form.ts';
37
+ import { type NetboxRequirements, netboxHandlers } from './resource.ts';
38
+ import { choice, fk, text } from './values.ts';
39
+
40
+ /**
41
+ * ⚠️ THE FOUR VALUES ARE THE VENDOR'S, NOT OURS — copied from the generated table, which is
42
+ * generated from NetBox's own enum. The table is what ENFORCES them; this type is what makes
43
+ * the mistake visible in an editor.
44
+ */
45
+ export type PrefixStatus = 'container' | 'active' | 'reserved' | 'deprecated';
46
+
47
+ export interface PrefixProps {
48
+ /** CIDR, exactly as NetBox stores it — `10.0.0.0/24`. The stable key for locate. */
49
+ prefix: string;
50
+ /**
51
+ * VRF id, or omitted for the global table.
52
+ *
53
+ * ⛔ PART OF THE IDENTITY, NOT A SETTING. NetBox's uniqueness on a prefix is per-VRF, so a
54
+ * locate that ignored it could match two rows — which `soleMatch` refuses rather than
55
+ * guessing between.
56
+ */
57
+ vrf?: number;
58
+ status?: PrefixStatus;
59
+ /** ⚠️ `maxLength: 200` in the schema; the generated table enforces it at plan time. */
60
+ description?: string;
61
+ /** Free text. ⚠️ NetBox states no length limit, so nothing here checks one. */
62
+ comments?: string;
63
+ tenant?: number;
64
+ vlan?: number;
65
+ /** All addresses inside are usable. */
66
+ isPool?: boolean;
67
+ markUtilized?: boolean;
68
+ }
69
+
70
+ export interface PrefixAttributes {
71
+ prefixId: number;
72
+ prefix: string;
73
+ status: string;
74
+ description: string;
75
+ comments: string;
76
+ vrf: number | undefined;
77
+ tenant: number | undefined;
78
+ vlan: number | undefined;
79
+ isPool: boolean;
80
+ markUtilized: boolean;
81
+ }
82
+
83
+ export interface NetboxPrefix extends Resource<
84
+ 'Netbox.Prefix',
85
+ PrefixProps,
86
+ PrefixAttributes,
87
+ never,
88
+ NetboxRequirements
89
+ > {}
90
+
91
+ export const NetboxPrefix = Resource<NetboxPrefix>('Netbox.Prefix', {
92
+ defaultRemovalPolicy: 'retain',
93
+ });
94
+
95
+ const handlers = netboxHandlers<PrefixProps, PrefixAttributes>({
96
+ attributes: (live, props) => {
97
+ const id = live['id'];
98
+ if (typeof id !== 'number') return undefined;
99
+ return {
100
+ comments: text(live['comments']),
101
+ description: text(live['description']),
102
+ isPool: live['is_pool'] === true,
103
+ markUtilized: live['mark_utilized'] === true,
104
+ prefix: text(live['prefix']) || props.prefix,
105
+ prefixId: id,
106
+ status: choice(live['status']),
107
+ tenant: fk(live['tenant']),
108
+ vlan: fk(live['vlan']),
109
+ vrf: fk(live['vrf']),
110
+ };
111
+ },
112
+ collection: 'ipam/prefixes',
113
+ createBody: prefixBody,
114
+ describe: (props) => `ipam/prefixes ${props.prefix}`,
115
+ endpoint: {
116
+ create: 'netbox:POST /api/ipam/prefixes/',
117
+ update: 'netbox:PATCH /api/ipam/prefixes/{id}/',
118
+ },
119
+ /**
120
+ * 🔴 THE VRF IS **NOT** IN THIS FILTER, AND THE REASON IS A GUESS THAT WAS CAUGHT. This read
121
+ * `vrf_id=null` for the global table — NetBox does define `FILTERS_NULL_CHOICE_VALUE = 'null'`
122
+ * and does honour it on filters that opt in. ⛔ MEASURED IN THE VENDOR'S OWN SOURCE at
123
+ * v4.7.0: `PrefixFilterSet.vrf_id` is a plain `django_filters.ModelMultipleChoiceFilter` with
124
+ * no `null_value`, so `'null'` is validated against the VRF queryset, fails, and NetBox
125
+ * answers 400 under strict filtering — on EVERY plan for EVERY global prefix.
126
+ * ★ So the CIDR narrows server-side with a filter the document plainly declares, and `identifies`
127
+ * picks the VRF in this process. One extra page of candidates, no vendor semantics guessed.
128
+ */
129
+ identifies: (live, props) => fk(live['vrf']) === props.vrf,
130
+ locate: (props) => [['prefix', props.prefix]],
131
+ matches: prefixMatches,
132
+ updateBody: prefixBody,
133
+ });
134
+
135
+ export const NetboxPrefixProvider = () =>
136
+ Provider.effect(NetboxPrefix, Effect.succeed(NetboxPrefix.Provider.of(handlers)));
@@ -0,0 +1,208 @@
1
+ /**
2
+ * One shape for every NetBox object — the same four operations, different path and JSON body.
3
+ *
4
+ * ⛔ THE ALTERNATIVE IS COPYING THE SAME HANDLERS PER RESOURCE. Prefixes, VLANs, devices, tenants
5
+ * and interfaces are all create / read / update / delete over `/api` with JSON bodies and a
6
+ * numeric primary key. Hand-rolling the fourth copy is where someone drops the read-back or the
7
+ * constraint guard and the plan lies.
8
+ *
9
+ * ★ ADOPT IS THE DEFAULT POSTURE, NOT A FLAG. `reconcile` LOCATES before it writes: an object the
10
+ * estate already has is bound, not duplicated. NetBox is a record that predates this code by
11
+ * years; a provider that created a second `10.20.10.0/24` on first deploy would be worse than
12
+ * no provider.
13
+ *
14
+ * ★ `defaultRemovalPolicy: 'retain'` ON EVERY FAMILY. NetBox holds the estate's record of what
15
+ * the network was DECIDED to be, and deleting a row deletes history — child prefixes reparent,
16
+ * IP assignments detach, and the change log is the only trace left. `retain` means the engine
17
+ * skips `delete` when a resource is orphaned unless the caller opts into
18
+ * `.pipe(RemovalPolicy.destroy())`. The `delete` handler is FULLY IMPLEMENTED anyway: a stub
19
+ * that silently does nothing lies to whoever reads the plan.
20
+ */
21
+ import { isResolved } from 'alchemy/Diff';
22
+ import type { Input } from 'alchemy/Input';
23
+ import * as Effect from 'effect/Effect';
24
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
25
+ import { NetboxError, type NetboxList, type NetboxRow, netbox, soleMatch } from './client.ts';
26
+ import { guardBody } from './constraint-guard.ts';
27
+ import type { EndpointKey, NetboxBody } from './constraints.ts';
28
+
29
+ /**
30
+ * ★ WHAT EVERY OPERATION HERE NEEDS FROM THE RUNTIME, AND NOTHING MORE. The token comes from
31
+ * `NETBOX_TOKEN` in the environment, so there is no process to spawn and no `bao` to shell out
32
+ * to. Declaring a service you never use is not harmless — it makes a stack provide a layer for
33
+ * nothing, and it hides the one service you DO need.
34
+ */
35
+ export type NetboxRequirements = HttpClient.HttpClient;
36
+
37
+ export interface NetboxSpec<Props extends object, Attributes> {
38
+ /** `ipam/prefixes` — where a new one is POSTed and where `locate` filters. */
39
+ readonly collection: string;
40
+ /**
41
+ * The query that narrows the candidates, as `URLSearchParams` pairs.
42
+ *
43
+ * ⛔ ONLY FILTERS THE VENDOR DOCUMENT DECLARES, AND ONLY IN THE FORM IT DECLARES THEM. A
44
+ * filter NetBox rejects answers 400, which `absentOn404` correctly does NOT fold to "absent" —
45
+ * so a guessed filter shape is a hard failure on every plan, not a soft one.
46
+ * ★ IT DOES NOT HAVE TO BE UNIQUE. `identifies` finishes the job in this process, where the
47
+ * rule is readable and testable offline.
48
+ */
49
+ readonly locate: (props: Props) => readonly (readonly [string, string])[];
50
+ /**
51
+ * Which of the returned rows IS this object.
52
+ *
53
+ * 🔴 THIS EXISTS BECAUSE THE OBVIOUS ALTERNATIVE WAS A GUESS. `Netbox.Prefix` first narrowed
54
+ * server-side with `vrf_id=null`, the sentinel NetBox uses elsewhere for "no foreign key".
55
+ * ⛔ MEASURED IN THE VENDOR'S OWN SOURCE at v4.7.0: `PrefixFilterSet.vrf_id` is a plain
56
+ * `django_filters.ModelMultipleChoiceFilter` with NO `null_value`, so `'null'` is validated
57
+ * against the VRF queryset, fails, and NetBox answers 400 under strict filtering. The
58
+ * sentinel is real (`FILTERS_NULL_CHOICE_VALUE = 'null'`) but it is opt-in per filter, and
59
+ * this one did not opt in.
60
+ * ★ SO THE DISCRIMINATOR MOVED INTO THIS PROCESS. It costs one page of candidates and buys a
61
+ * rule that can be read, tested without a server, and cannot be wrong about a vendor's
62
+ * filter semantics. Omit it when `locate` really is unique on its own.
63
+ */
64
+ readonly identifies?: (live: NetboxRow, props: Props) => boolean;
65
+ readonly attributes: (live: NetboxRow, props: Props) => Attributes | undefined;
66
+ readonly createBody: (props: Props) => NetboxBody;
67
+ /** ⚠️ PARTIAL BY DESIGN: NetBox updates are PATCH, so presence is not checked on this body. */
68
+ readonly updateBody?: (props: Props) => NetboxBody;
69
+ readonly matches: (attributes: Attributes, props: Props) => boolean;
70
+ /** The generated constraint table to check each body against, by endpoint key. */
71
+ readonly endpoint: { readonly create: EndpointKey; readonly update?: EndpointKey };
72
+ /** For the message `soleMatch` raises and for read-back failures. */
73
+ readonly describe: (props: Props) => string;
74
+ }
75
+
76
+ /** ⚠️ Only 404 folds to "absent". A 403 or a 502 is a real failure — folding it would plan a create over an existing object. */
77
+ const absentOn404 = <A>(io: Effect.Effect<A, NetboxError, NetboxRequirements>) =>
78
+ io.pipe(
79
+ Effect.catchIf(
80
+ (cause): cause is NetboxError => cause instanceof NetboxError && cause.status === 404,
81
+ () => Effect.succeed(undefined as A),
82
+ ),
83
+ );
84
+
85
+ /**
86
+ * ⚠️ NOT `limit=1`, AND NOT `limit=2` EITHER. One row is all a caller wants, but the page has to
87
+ * be wide enough for `identifies` to see every candidate `locate` matched — a prefix that
88
+ * exists in several VRFs comes back several times, and a page of two would hide the third.
89
+ * ★ TWENTY, AND THE NUMBER IS AN ASSERTION: a natural key matching more than twenty rows is not
90
+ * a natural key. `count` is checked against it, so the failure is "your filter is too broad"
91
+ * rather than a silent truncation.
92
+ */
93
+ const LOCATE_PAGE = 20;
94
+
95
+ const query = (pairs: readonly (readonly [string, string])[]): string =>
96
+ new URLSearchParams([
97
+ ...pairs.map(([k, v]) => [k, v] as [string, string]),
98
+ ['limit', String(LOCATE_PAGE)],
99
+ ]).toString();
100
+
101
+ export const netboxOperations = <Props extends object, Attributes>(
102
+ spec: NetboxSpec<Props, Attributes>,
103
+ ) => {
104
+ const fetchLive = (props: Props) =>
105
+ absentOn404(
106
+ netbox<NetboxList<NetboxRow>>('GET', `${spec.collection}/?${query(spec.locate(props))}`),
107
+ ).pipe(
108
+ Effect.map((list) => {
109
+ if (list !== undefined && list.count > LOCATE_PAGE) {
110
+ throw new Error(
111
+ `${spec.describe(props)}: the locate filter matched ${String(list.count)} rows, more ` +
112
+ `than the ${String(LOCATE_PAGE)}-row page this reads. That is not a natural key — ` +
113
+ 'narrow the filter rather than paging through candidates.',
114
+ );
115
+ }
116
+ const rows = list?.results ?? [];
117
+ const narrow = spec.identifies;
118
+ return soleMatch(
119
+ narrow === undefined ? rows : rows.filter((row) => narrow(row, props)),
120
+ spec.describe(props),
121
+ );
122
+ }),
123
+ );
124
+
125
+ /** ⛔ The numeric `id` is the wire path. NetBox has no update-by-natural-key. */
126
+ const objectPath = (live: NetboxRow): string => `${spec.collection}/${String(live['id'])}`;
127
+
128
+ const read = (props: Props) =>
129
+ fetchLive(props).pipe(
130
+ Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))),
131
+ );
132
+
133
+ return {
134
+ read,
135
+
136
+ diff: (news: Input<Props>, output: Attributes | undefined) =>
137
+ Effect.gen(function* () {
138
+ if (output === undefined || !isResolved(news)) return undefined;
139
+ const live = yield* read(news);
140
+ if (live === undefined) return { action: 'update' } as const;
141
+ if (spec.matches(live, news)) return { action: 'noop' } as const;
142
+ return spec.updateBody === undefined
143
+ ? ({ action: 'replace' } as const)
144
+ : ({ action: 'update' } as const);
145
+ }),
146
+
147
+ reconcile: (news: Props) =>
148
+ Effect.gen(function* () {
149
+ const before = yield* fetchLive(news);
150
+ if (before === undefined) {
151
+ const body = spec.createBody(news);
152
+ // ⛔ GUARD BEFORE THE REQUEST, WITH PRESENCE ON. This is the only place a missing
153
+ // required property can still be caught locally.
154
+ yield* guardBody(spec.endpoint.create, body, true);
155
+ yield* netbox('POST', `${spec.collection}/`, body);
156
+ } else if (spec.updateBody !== undefined) {
157
+ const mapped = spec.attributes(before, news);
158
+ // ⚠️ ADOPTION IS THE `mapped === undefined` CASE TOO: a row this spec cannot map is not
159
+ // a row to PATCH blindly.
160
+ if (mapped !== undefined && !spec.matches(mapped, news)) {
161
+ const body = spec.updateBody(news);
162
+ yield* guardBody(spec.endpoint.update, body, false);
163
+ if (Object.keys(body).length > 0) {
164
+ yield* netbox('PATCH', `${objectPath(before)}/`, body);
165
+ }
166
+ }
167
+ }
168
+ const after = yield* read(news);
169
+ if (after === undefined) {
170
+ return yield* Effect.die(
171
+ new Error(
172
+ `${spec.describe(news)}: the write returned no error but the object is still ` +
173
+ 'absent. NetBox returns the object it wrote — read back rather than trusting the ' +
174
+ 'status code.',
175
+ ),
176
+ );
177
+ }
178
+ return after;
179
+ }),
180
+
181
+ destroy: (olds: Props) =>
182
+ Effect.gen(function* () {
183
+ const live = yield* fetchLive(olds);
184
+ if (live === undefined) return;
185
+ yield* netbox('DELETE', `${objectPath(live)}/`);
186
+ }),
187
+ };
188
+ };
189
+
190
+ /**
191
+ * The five provider handlers for a spec'd NetBox object, wired once.
192
+ *
193
+ * ⚠️ `list` answers empty. NetBox index endpoints return the whole estate — tens of thousands of
194
+ * rows on a real instance — so adoption stays explicit rather than sweeping.
195
+ */
196
+ export const netboxHandlers = <Props extends object, Attributes extends object>(
197
+ spec: NetboxSpec<Props, Attributes>,
198
+ ) => {
199
+ const ops = netboxOperations(spec);
200
+ return {
201
+ list: () => Effect.succeed([]),
202
+ read: ({ olds }: { olds: Props }) => ops.read(olds),
203
+ diff: ({ news, output }: { news: Input<Props>; output: Attributes | undefined }) =>
204
+ ops.diff(news, output),
205
+ reconcile: ({ news }: { news: Props }) => ops.reconcile(news),
206
+ delete: ({ olds }: { olds: Props }) => ops.destroy(olds),
207
+ };
208
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Read-versus-write asymmetry — the bug that makes a resource never converge.
3
+ *
4
+ * 🔴 THE SHAPE IS DIFFERENT IN EACH DIRECTION AND NOTHING IN THE TYPES SAYS SO. Measured in the
5
+ * NetBox 4.7.0 document: a prefix is WRITTEN with `status: "active"` and READ BACK as
6
+ * `status: {value: "active", label: "Active"}`; a foreign key is written as `4` and read back
7
+ * as `{id: 4, url: …, display: …}`. A `matches` that compared those directly would report drift
8
+ * on every plan, for every object, forever — and each "update" would PATCH the same value back.
9
+ */
10
+ import { describe, expect, test } from 'bun:test';
11
+ import { bool, choice, fk, text } from './values.ts';
12
+
13
+ describe('a choice field reads back as an object', () => {
14
+ test('the object form reduces to the string a declaration writes', () => {
15
+ expect(choice({ label: 'Active', value: 'active' })).toBe('active');
16
+ });
17
+
18
+ /** ⚠️ Both directions must work: a fixture or a brief serialisation gives the bare string. */
19
+ test('the bare string passes through unchanged', () => {
20
+ expect(choice('deprecated')).toBe('deprecated');
21
+ });
22
+
23
+ test('anything else is empty rather than undefined, so a comparison stays total', () => {
24
+ expect(choice(undefined)).toBe('');
25
+ expect(choice(null)).toBe('');
26
+ expect(choice({ label: 'Active' })).toBe('');
27
+ });
28
+ });
29
+
30
+ describe('a foreign key reads back as a nested object', () => {
31
+ test('the nested object reduces to its id', () => {
32
+ expect(
33
+ fk({ display: 'HomeFlare', id: 4, url: 'https://example.invalid/api/tenancy/tenants/4/' }),
34
+ ).toBe(4);
35
+ });
36
+
37
+ test('the bare integer passes through', () => {
38
+ expect(fk(7)).toBe(7);
39
+ });
40
+
41
+ /**
42
+ * ⛔ `undefined`, NOT `0`. A cleared foreign key and "tenant 0" are different facts, and zero
43
+ * is falsy — a `matches` written against it would treat an unset tenant as a match for every
44
+ * declaration that omitted one.
45
+ */
46
+ test('an absent or unrecognisable key is undefined', () => {
47
+ expect(fk(null)).toBeUndefined();
48
+ expect(fk(undefined)).toBeUndefined();
49
+ expect(fk({ display: 'no id here' })).toBeUndefined();
50
+ });
51
+ });
52
+
53
+ describe('text and booleans', () => {
54
+ /** ⚠️ NetBox stores an empty string for absent text, never null — so `''` is the settled value. */
55
+ test('absent text settles to the empty string', () => {
56
+ expect(text(undefined)).toBe('');
57
+ expect(text(null)).toBe('');
58
+ expect(text('a note')).toBe('a note');
59
+ });
60
+
61
+ test('only a real true is true', () => {
62
+ expect(bool(true)).toBe(true);
63
+ expect(bool('true')).toBe(false);
64
+ expect(bool(1)).toBe(false);
65
+ });
66
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Reading a NetBox row into settled values.
3
+ *
4
+ * ⛔ A NETBOX READ AND A NETBOX WRITE ARE NOT THE SAME SHAPE, AND THIS IS WHERE THAT IS HANDLED.
5
+ * MEASURED in the 4.7.0 document: `WritablePrefixRequest.status` is a bare string enum
6
+ * (`"active"`), while `Prefix.status` on the way back is an OBJECT — `{value, label}`. A `diff`
7
+ * that compared the declared string against the live object would see drift on every plan, for
8
+ * every prefix, forever, and each "update" would PATCH the same value back.
9
+ * ⚠️ The same asymmetry holds for every choice field in NetBox (`dcim` device `status`,
10
+ * `ipam` IP `role`, …). Anything read through `choice` is safe against it.
11
+ */
12
+
13
+ /** ⚠️ `''` for absent, not `undefined` — NetBox stores an empty string, never null, for text. */
14
+ export const text = (value: unknown): string => (typeof value === 'string' ? value : '');
15
+
16
+ export const bool = (value: unknown): boolean => value === true;
17
+
18
+ /** A `{value, label}` choice object, reduced to the string a declaration writes. */
19
+ export const choice = (value: unknown): string => {
20
+ if (typeof value === 'string') return value;
21
+ if (typeof value === 'object' && value !== null && 'value' in value) {
22
+ return text((value as { value: unknown }).value);
23
+ }
24
+ return '';
25
+ };
26
+
27
+ /**
28
+ * A foreign key, as the integer a declaration writes.
29
+ *
30
+ * ⚠️ IT ARRIVES AS A NESTED OBJECT ON READ AND AN INTEGER ON WRITE — `{id, url, display, …}`
31
+ * versus `4`. Comparing the two directly is the same never-converges bug as `choice`.
32
+ */
33
+ export const fk = (value: unknown): number | undefined => {
34
+ if (typeof value === 'number') return value;
35
+ if (typeof value === 'object' && value !== null && 'id' in value) {
36
+ const id = (value as { id: unknown }).id;
37
+ return typeof id === 'number' ? id : undefined;
38
+ }
39
+ return undefined;
40
+ };
@@ -103,6 +103,11 @@ export const apiTokenSpec: PveSpec<ApiTokenProps, ApiTokenAttributes> = {
103
103
  */
104
104
  collection: (props) => `access/users/${props.userid}/token/${props.tokenid}`,
105
105
  createForm: shape,
106
+ /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */
107
+ endpoint: {
108
+ create: 'pve:POST /access/users/{userid}/token/{tokenid}',
109
+ update: 'pve:PUT /access/users/{userid}/token/{tokenid}',
110
+ },
106
111
  /**
107
112
  * ⚠️ EXACTLY THE THREE FIELDS A PUT CAN PUT BACK, WHICH IS ALSO EXACTLY WHAT THE GET REPORTS.
108
113
  * MEASURED, by replaying these two functions over the live GET bodies of all four token
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The two applies, and the vendor endpoints they publish through.
3
+ *
4
+ * ★ AN ACTION ENDPOINT IS STILL WIRED, EVEN THOUGH ITS TABLE IS EMPTY, AND THE REASON IS THE
5
+ * GENERATOR RATHER THAN THE VALIDATOR. `PUT /nodes/{node}/network` and `PUT /cluster/sdn` take
6
+ * no body at all — there is nothing for a length or a range to be wrong about — but
7
+ * `codegen/constraints.ts` RESOLVES every key named in this package against the vendor's own
8
+ * schema and STOPS when one is absent. So the key buys the thing these two families most need:
9
+ * a PVE that moves, renames or withdraws the apply fails `bun run check` instead of failing an
10
+ * `ifreload -a` on three nodes at once.
11
+ * ⚠️ AND THE LOOKUP IS NOT FREE EITHER. `constraintsFor` throws when a key has no table, so a
12
+ * checkout whose generated tables were never refreshed refuses the apply with the command to
13
+ * run rather than quietly doing nothing — the same defect `constraint-guard.ts` names.
14
+ *
15
+ * ⛔ NEITHER OF THESE IS A CREATE, AND NEITHER FAMILY HAS A `PveSpec`. Both write their own five
16
+ * handlers (an apply is not an object — see the ⛔ in each file), so they reach the shared check
17
+ * through `guardForm` by name, exactly as `pbs-datastore-endpoint.ts` does.
18
+ */
19
+ import type * as Effect from 'effect/Effect';
20
+ import { guardForm } from './constraint-guard.ts';
21
+ import type { EndpointKey } from './constraints.ts';
22
+
23
+ /** ⚠️ `{node}` is a path segment, so this really does take no form keys at all. */
24
+ export const NETWORK_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /nodes/{node}/network';
25
+
26
+ /**
27
+ * ⛔ CLUSTER-WIDE. One PUT publishes every staged zone, vnet and subnet on every node; there is no
28
+ * per-object apply and no pre-apply veto. `sdn-apply.ts` is the whole argument.
29
+ */
30
+ export const SDN_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /cluster/sdn';
31
+
32
+ export const guardNetworkApply: Effect.Effect<void> = guardForm(NETWORK_APPLY_ENDPOINT, {}, true);
33
+
34
+ export const guardSdnApply: Effect.Effect<void> = guardForm(SDN_APPLY_ENDPOINT, {}, true);
@@ -28,8 +28,30 @@
28
28
  * the task in the PVE UI before concluding the create failed.
29
29
  *
30
30
  */
31
- import type { CephDaemonProps } from './ceph-daemon.ts';
32
- import { flag } from './values.ts';
31
+ import type { CephDaemonAttributes, CephDaemonKind, CephDaemonProps } from './ceph-daemon.ts';
32
+ import type { EndpointPair } from './resource-spec.ts';
33
+ import { bool, flag, num, text } from './values.ts';
34
+
35
+ /**
36
+ * The vendor endpoint each kind is created at, as the generated tables key it.
37
+ *
38
+ * ⛔ ONE PER KIND, NOT A `{type}` PATH. PVE registers `mds`, `mgr` and `mon` as three separate
39
+ * nodes with three separate parameter schemas — `hotstandby` belongs to the first and
40
+ * `mon-address` to the third — so a single key would enforce one kind's rules on all three.
41
+ * That is why `PveSpec['endpoint']` admits a function of props at all.
42
+ * ⛔ THE STRINGS MUST STAY LITERALS. `codegen/constraints.ts` finds the endpoints to table by
43
+ * SCANNING THIS PACKAGE'S TEXT, so a key assembled from `props.kind` would be tabled by
44
+ * nothing and `constraintsFor` would throw on the first deploy that reached it.
45
+ * ⚠️ THE PARAMETER NAMES DIFFER AND ARE THE VENDOR'S OWN — `{name}`, `{id}`, `{monid}`. The
46
+ * generator resolves each against the schema, so a tidied-up spelling stops the build.
47
+ * ⚠️ NO `update` ON ANY OF THEM: none of the three has a PUT, which is why the spec declares no
48
+ * `updateForm` either.
49
+ */
50
+ export const DAEMON_ENDPOINTS: Readonly<Record<CephDaemonKind, EndpointPair>> = {
51
+ mds: { create: 'pve:POST /nodes/{node}/ceph/mds/{name}' },
52
+ mgr: { create: 'pve:POST /nodes/{node}/ceph/mgr/{id}' },
53
+ mon: { create: 'pve:POST /nodes/{node}/ceph/mon/{monid}' },
54
+ };
33
55
 
34
56
  /**
35
57
  * PVE defaults the id to the nodename for all three kinds, and that default is resolved HERE
@@ -82,3 +104,35 @@ export const findRow = (live: unknown, props: CephDaemonProps) =>
82
104
  (Array.isArray(live) ? live : [])
83
105
  .filter((row): row is Record<string, unknown> => typeof row === 'object' && row !== null)
84
106
  .find((row) => row['name'] === daemonId(props));
107
+
108
+ /**
109
+ * One live row as attributes, or `undefined` — "no daemon of this kind carries this name".
110
+ *
111
+ * ★ HERE RATHER THAN IN ceph-daemon.ts FOR THE REASON THAT FILE'S SIBLING EXISTS AT ALL: the
112
+ * 250-line cap, and a seam that is real. It reads the row `findRow` picked out of an answer its
113
+ * caller already fetched, three lines above, so the lookup and the mapping now live together
114
+ * instead of a function call apart.
115
+ *
116
+ * ⚠️ `undefined` WHEN NO ROW CARRIES THIS NAME: that is how the factory learns to create.
117
+ */
118
+ export const daemonAttributes = (
119
+ live: Record<string, unknown>,
120
+ props: CephDaemonProps,
121
+ ): CephDaemonAttributes | undefined => {
122
+ const row = findRow(live, props);
123
+ if (row === undefined) return undefined;
124
+ return {
125
+ addr: text(row['addr']),
126
+ fsName: text(row['fs_name']),
127
+ host: text(row['host']),
128
+ kind: props.kind,
129
+ name: daemonId(props),
130
+ node: props.node,
131
+ quorum: bool(row['quorum']),
132
+ rank: num(row['rank'], -1),
133
+ service: bool(row['service']),
134
+ standbyReplay: bool(row['standby_replay']),
135
+ state: text(row['state']),
136
+ version: text(row['ceph_version_short']),
137
+ };
138
+ };
@@ -49,10 +49,15 @@
49
49
  import { Resource } from 'alchemy';
50
50
  import * as Provider from 'alchemy/Provider';
51
51
  import * as Effect from 'effect/Effect';
52
- import { collectionPath, createForm, daemonId, daemonPath, findRow } from './ceph-daemon-form.ts';
52
+ import {
53
+ DAEMON_ENDPOINTS,
54
+ collectionPath,
55
+ createForm,
56
+ daemonAttributes,
57
+ daemonPath,
58
+ } from './ceph-daemon-form.ts';
53
59
  import { pve } from './client.ts';
54
60
  import { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';
55
- import { bool, num, text } from './values.ts';
56
61
 
57
62
  /**
58
63
  * Which Ceph daemon this is. The discriminant picks the collection, the create parameters and
@@ -176,28 +181,12 @@ export const ProxmoxCephDaemon = Resource<ProxmoxCephDaemon>('Proxmox.CephDaemon
176
181
  });
177
182
 
178
183
  const handlers = pveHandlers<CephDaemonProps, CephDaemonAttributes>({
179
- /** ⚠️ `undefined` when no row carries this name: that is how the factory learns to create. */
180
- attributes: (live, props) => {
181
- const row = findRow(live, props);
182
- if (row === undefined) return undefined;
183
- return {
184
- addr: text(row['addr']),
185
- fsName: text(row['fs_name']),
186
- host: text(row['host']),
187
- kind: props.kind,
188
- name: daemonId(props),
189
- node: props.node,
190
- quorum: bool(row['quorum']),
191
- rank: num(row['rank'], -1),
192
- service: bool(row['service']),
193
- standbyReplay: bool(row['standby_replay']),
194
- state: text(row['state']),
195
- version: text(row['ceph_version_short']),
196
- };
197
- },
184
+ attributes: daemonAttributes,
198
185
  /** ⛔ The ID path, not the collection — POST is registered on `{id}`, exactly like a metric server. */
199
186
  collection: daemonPath,
200
187
  createForm,
188
+ /** ⚠️ A function, because the kind picks the endpoint — see `DAEMON_ENDPOINTS`. */
189
+ endpoint: (props: CephDaemonProps) => DAEMON_ENDPOINTS[props.kind],
201
190
  /**
202
191
  * ⛔ TOTAL, AND THIS IS THE REASON THE FILE EXISTS RATHER THAN A SHORTCUT PAST WRITING IT. Every
203
192
  * field a declaration can carry is CREATE-ONLY and unreadable — `mon-address` comes back as a
@@ -181,6 +181,7 @@ const handlers = pveHandlers<CephFlagProps, CephFlagAttributes>({
181
181
  * only body `cluster/ceph/flags` has ever documented. Nothing sends it today.
182
182
  */
183
183
  createForm: (props) => ({ [props.flag]: flag(props.value) ?? '0' }),
184
+ endpoint: { update: 'pve:PUT /cluster/ceph/flags/{flag}' }, // ⛔ No create: see above.
184
185
  /**
185
186
  * ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE
186
187
  * point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`
@@ -148,6 +148,7 @@ const ops = pveOperations<CephFsProps, CephFsAttributes>({
148
148
  /** ⚠️ The `{name}` path — longer than `path()`, deliberately. See the second ⛔ in the header. */
149
149
  collection: objectPath,
150
150
  createForm,
151
+ endpoint: { create: 'pve:POST /nodes/{node}/ceph/fs/{name}' }, // ⚠️ POSTed on the object.
151
152
  /**
152
153
  * ⛔ NOTHING IS COMPARED, AND THAT IS THE MOST DELIBERATE LINE IN THIS FILE. A CephFS has no
153
154
  * readable, writable field: the index returns only the name and the pools behind it, and every