@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,37 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/linux/directory.ts", "../src/launchd/host-effect.ts", "../src/launchd/runner.ts", "../src/ownership/adopt.ts", "../src/ownership/resume.ts", "../src/ownership/rows.ts", "../src/ownership/whole.ts", "../src/launchd/secret-tripwire.ts", "../src/launchd/host-file-form.ts", "../src/linux/directory-lifecycle.ts", "../src/linux/providers.ts", "../src/linux/remote-file.ts", "../src/launchd/job-form.ts", "../src/launchd/plist.ts", "../src/linux/region.ts", "../src/linux/remote-file-form.ts", "../src/linux/remote-file-plan.ts", "../src/linux/remote-file-lifecycle.ts", "../src/linux/unit.ts", "../src/linux/unit-handlers.ts", "../src/linux/unit-form.ts", "../src/linux/systemctl.ts", "../src/linux/unit-settle.ts", "../src/linux/unit-lifecycle.ts", "../src/linux/ssh-runner.ts", "../src/launchd/host-lookup.ts", "../src/linux/ssh-command.ts", "../src/linux/ssh-scripts.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * `Host.Directory` — one directory on the host, declared: path, mode, owner, group.\n *\n * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt`: a directory already at the path reads as `Unowned`, and\n * where the plan never asked, reconcile honours the same flag (docs/ownership.md).\n * ★ IT IS NOT LINUX-ONLY. `mkdir`, `chmod`, `chown` and `rmdir` are the same argv on macOS, so this\n * resource works through any HostRunner; it lives in the linux subpath because the gap it closes\n * was found there (a first deploy into a new tree, which every file resource refuses to create).\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { lift, resolvedString } from '../launchd/host-effect.ts';\nimport { HostRunnerService } from '../launchd/runner.ts';\nimport { adoptsAtApply } from '../ownership/adopt.ts';\nimport { noteUnfinished } from '../ownership/resume.ts';\nimport type { HostDirectoryAttributes, HostDirectoryProps } from './directory-lifecycle.ts';\nimport {\n deleteDirectory,\n diffDirectory,\n readDirectory,\n reconcileDirectory,\n} from './directory-lifecycle.ts';\n\nexport type { HostDirectoryAttributes, HostDirectoryProps } from './directory-lifecycle.ts';\n\nexport interface HostDirectory extends Resource<\n 'Host.Directory',\n HostDirectoryProps,\n HostDirectoryAttributes\n> {}\n\nexport const HostDirectory = Resource<HostDirectory>('Host.Directory');\n\nexport const HostDirectoryProvider = () =>\n Provider.effect(\n HostDirectory,\n Effect.gen(function* () {\n const runner = yield* HostRunnerService;\n return HostDirectory.Provider.of({\n list: () => Effect.succeed([]),\n\n read: ({ olds, output }) =>\n lift(async () => {\n const found = await readDirectory(runner, olds.path);\n if (found === undefined) return undefined;\n return output === undefined ? Unowned(found) : found;\n }),\n\n diff: ({ instanceId, news, output }) => {\n if (output === undefined) return noteUnfinished(instanceId);\n if (isResolved(news)) return lift(() => diffDirectory(runner, news, output));\n // ⛔ A new path is a replace even while the rest is unresolved: the engine's default\n // update would create the new directory and leave the old one behind forever.\n const path = resolvedString(news, 'path');\n return Effect.succeed(\n path !== undefined && path !== output.path ? { action: 'replace' as const } : undefined,\n );\n },\n\n reconcile: ({ fqn, instanceId, news, output }) =>\n Effect.flatMap(adoptsAtApply({ fqn, instanceId, output }), (adopt) =>\n lift(() => reconcileDirectory(runner, news, output, adopt)),\n ),\n\n delete: ({ output }) => lift(() => deleteDirectory(runner, output)),\n });\n }),\n );\n",
6
+ "/**\n * The one place a HostRunner's promises become Effects — plus the one probe of an unresolved\n * `news` both providers' diffs share. (The engine's adopt setting lives in ownership/adopt.ts.)\n *\n * ★ The lifecycle files are plain async functions over a HostRunner (see runner.ts for why the\n * runner is promise-based), so each provider handler is a single `lift(() => step(runner, …))`.\n * ⚠️ A thrown Error stays that Error — its message is the refusal a deploy prints. Anything else\n * thrown is wrapped, so the failure channel is always an Error with a readable message.\n */\nimport * as Effect from 'effect/Effect';\n\nexport const lift = <A>(step: () => Promise<A>): Effect.Effect<A, Error> =>\n Effect.tryPromise({\n catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),\n try: step,\n });\n\n/**\n * A string prop of a diff's `news`, when that one prop is already resolved; `undefined` while it\n * is still an Output (a function-typed proxy), an Effect or a Config.\n * ★ For the identity props a diff must see even when the rest of `news` is unresolved — a plain\n * string is resolved by definition, so `typeof` is the whole test.\n */\nexport const resolvedString = (news: unknown, key: string): string | undefined => {\n if (typeof news !== 'object' || news === null) return undefined;\n const value = (news as Record<string, unknown>)[key];\n return typeof value === 'string' ? value : undefined;\n};\n",
7
+ "/**\n * The one seam between the launchd providers and the host: every file read, file write and\n * `launchctl` call goes through a HostRunner, and nothing in this directory touches the host any\n * other way.\n *\n * ★ WHY A SEAM AND NOT DIRECT node:fs CALLS. launchd has no SDK — the provider renders a plist and\n * drives `launchctl` — so the only way to test the lifecycle without a real launchd (and without\n * writing /Library) is to swap the host out. fake-runner.ts is an in-memory host with a scripted\n * launchd; local-runner.ts is the real one. The same seam is where a consumer plugs in a runner\n * that reaches a host some other way.\n *\n * ★ PROMISES, NOT EFFECTS, ON PURPOSE. A consumer writing their own runner implements plain async\n * functions; the providers lift them into Effect at the call site (host-effect.ts).\n */\nimport * as Context from 'effect/Context';\nimport * as Layer from 'effect/Layer';\n\n/** One finished process. ⛔ `stdout` may carry anything the job's environment holds — never log it. */\nexport type ExecResult = {\n readonly exitCode: number;\n readonly stdout: string;\n readonly stderr: string;\n};\n\n/**\n * What `lstat` says about a path. ⚠️ lstat, not stat: a symlink is reported as a symlink, because\n * writing through one would change a file this resource never named.\n */\nexport type FileStat = {\n readonly kind: 'file' | 'directory' | 'symlink' | 'other';\n /** Permission bits only (`& 0o7777`), never the file-type bits. */\n readonly mode: number;\n readonly uid: number;\n readonly gid: number;\n readonly size: number;\n};\n\nexport type WriteOptions = {\n /** Permission bits for the final file, applied BEFORE it becomes visible at `path`. */\n readonly mode: number;\n /** Numeric owner; omitted means whoever the runner writes as. */\n readonly uid?: number;\n /** Numeric group; omitted means the directory's default. */\n readonly gid?: number;\n};\n\nexport type HostUser = { readonly uid: number; readonly gid: number; readonly home: string };\n\nexport interface HostRunner {\n /**\n * ⛔ DECLARED, NEVER INFERRED. `true` only when this runner can do every root-needing call the\n * providers make of it: every call already runs as root (a runner that talks to a root helper,\n * say), or the runner elevates exactly those calls (sudoRunner). The providers refuse\n * system-domain writes unless `effectiveUid()` is 0 or this is `true`, so claiming it falsely\n * turns that refusal into an EACCES halfway through a deploy.\n * ⛔ THE KIT NEVER ELEVATES SILENTLY. localRunner() never calls `sudo`: a deploy that prompts for\n * a password mid-plan, or quietly borrows a cached sudo ticket, is exactly the \"no silent sudo\"\n * failure. sudoRunner() does call `sudo -n`, but only when a stack passes it explicitly, only\n * for the argv shapes in sudo-allowlist.ts, and logging each one (decided 2026-09-21).\n */\n readonly privileged: boolean;\n /** The uid file writes and launchctl calls run as. */\n effectiveUid(): number;\n /** Run a program with an argv — ⛔ never through a shell. */\n exec(argv: readonly string[]): Promise<ExecResult>;\n /** File bytes, or `undefined` when nothing is at `path`. */\n readFile(path: string): Promise<Uint8Array | undefined>;\n /** lstat, or `undefined` when nothing is at `path`. */\n stat(path: string): Promise<FileStat | undefined>;\n /**\n * Write `bytes` so that `path` holds either the old file or the new one, never a torn mix:\n * a temporary file in the same directory, mode and owner set on it, then rename(2) over `path`.\n * ⚠️ The parent directory must exist; creating it would invent an owner and mode nobody declared.\n */\n writeFileAtomic(path: string, bytes: Uint8Array, options: WriteOptions): Promise<void>;\n /**\n * PLAN TIME: refuse, reading only and writing nothing, a write this runner would refuse at\n * apply — so the plan shows it before any resource is applied. Optional: a runner without it\n * refuses at the write. ★ sudoRunner implements it with the checks it makes before `sudo -n`;\n * ⛔ it never elevates, so a plan still never calls sudo.\n */\n checkWrite?(path: string, options: WriteOptions): Promise<void>;\n /** Remove a file. Idempotent: nothing at `path` is success. */\n removeFile(path: string): Promise<void>;\n /** A user by name or numeric id, or `undefined` when the host has none. */\n lookupUser(nameOrId: string): Promise<HostUser | undefined>;\n /** A group's gid by name or numeric id, or `undefined` when the host has none. */\n lookupGroup(nameOrId: string): Promise<number | undefined>;\n /** Wait between launchctl polls. ★ Injected so a test never sleeps for real. */\n sleep(ms: number): Promise<void>;\n}\n\n/** The Effect service every launchd provider reads its runner from. */\nexport class HostRunnerService extends Context.Service<HostRunnerService, HostRunner>()(\n 'homeflare/launchd/HostRunner',\n) {}\n\n/** Provide a runner to the providers: `Layer.provide(hostRunnerLayer(localRunner()))`. */\nexport const hostRunnerLayer = (runner: HostRunner): Layer.Layer<HostRunnerService> =>\n Layer.succeed(HostRunnerService, runner);\n\n/** True when this runner may write root-owned paths and drive the system domain. */\nexport const canActAsRoot = (runner: HostRunner): boolean =>\n runner.privileged || runner.effectiveUid() === 0;\n",
8
+ "/**\n * Whether a live object this stack holds no state for may be written — the apply-time half of the\n * house rule that NOTHING IS ADOPTED WITHOUT `--adopt` (decided 2026-09-21 for HostFile, LaunchdJob,\n * CaddyConfig and every Bao.* family). probe.ts is the plan-time half.\n *\n * ★ FAMILY-NEUTRAL ON PURPOSE. The engine questions here — what `--adopt` resolves to for one FQN,\n * whether this apply resumes the resource's own interrupted create — are the same for a vault\n * role, a launchd plist and a Caddy config, so they live once, outside every subpath's directory.\n */\nimport { AdoptPolicy } from 'alchemy/AdoptPolicy';\nimport { AlchemyContext } from 'alchemy/AlchemyContext';\nimport { Stack } from 'alchemy/Stack';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport { resumes, unfinished } from './resume.ts';\nimport { forgetRefusedCreate, isCreate } from './rows.ts';\n\n/**\n * Whether the resource at `fqn` may be adopted, resolved exactly as the planner resolves it (alchemy\n * beta.79 Plan.ts: `resource.Adopt ?? shouldAdopt`): a resource-scoped `adopt(…)`, else the\n * `AdoptPolicy` service, else `AlchemyContext.adopt`, else off.\n * ★ FOR A CREATE THE ENGINE NEVER PROBED. Alchemy skips the adoption probe while `news` holds an\n * Output, so an ownership check that must still honour `--adopt` can only run at apply. All three\n * reach the apply: Alchemist runs plan AND apply under the session context, which provides the\n * services from the CLI flag and the Stack the resources registered on (Alchemist/Session.ts,\n * routes/stack.ts; Apply.ts reads the same Stack).\n * ⛔ THE RESOURCE-SCOPED SETTING WINS, BOTH WAYS. The engine captures `adopt(…)` on the resource at\n * registration (Resource.ts `Adopt`, into `Stack.resources[fqn]`) and never hands it to a\n * provider. Reading only the deploy-wide flag let `--adopt` take over a resource declared\n * `.pipe(adopt(false))` — which the planner refuses — and refused one declared `adopt(true)`.\n * ⚠️ `resources` carries an `@internal` comment in Alchemy's source, but it is in the published types\n * and the planner reads `Adopt` from it. caddy/adopt-scope.test.ts registers through the real\n * `Resource`, so an upgrade that stops recording it there fails the suite.\n */\nexport const adoptEnabled = (fqn: string): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n const stack = yield* Effect.serviceOption(Stack);\n const scoped = Option.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;\n if (scoped !== undefined) return scoped;\n const policy = yield* Effect.serviceOption(AdoptPolicy);\n if (Option.isSome(policy)) return policy.value;\n const context = yield* Effect.serviceOption(AlchemyContext);\n return Option.isSome(context) ? context.value.adopt : false;\n });\n\n/** The three fields of a `reconcile` (or `read`) call that say whose object this is. */\nexport type Owner = {\n readonly fqn: string;\n readonly instanceId: string;\n /** The attributes state holds; `undefined` on a create. */\n readonly output: unknown;\n};\n\n/**\n * Whether `--adopt` may speak for this apply's generation: a CREATE (rows.ts isCreate), or an\n * UNFINISHED generation whose row this plan's diff saw with no attributes (resume.ts) — the\n * resume Alchemy itself offers `--adopt` for (\"Cannot resume creating … Re-run with --adopt\").\n * ⛔ NOT A FRESH REPLACE'S NEW GENERATION. The planner never probes it and never offers it adoption,\n * so the identity it moves to was never asked about under `--adopt`: 🔴 MEASURED 2026-09-21\n * (launchd/adopt-apply.test.ts), a HostFile whose path changed onto a file it did not own\n * overwrote that file under a deploy-wide `--adopt` meant for something else. A Bao rename onto a\n * taken identity already fails the plan, `--adopt` or not (rename.ts judgeMove); this keeps the\n * apply to the same answer.\n */\nconst adoptable = (owner: Owner): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n if (yield* isCreate(owner.fqn, owner.instanceId)) return true;\n return yield* unfinished(owner.instanceId);\n });\n\n/** Whether a stateless `reconcile` may take over a live object: adoption on, for an adoptable one. */\nexport const adoptsAtApply = (owner: Owner): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n if (!(yield* adoptEnabled(owner.fqn))) return false;\n return yield* adoptable(owner);\n });\n\n/** A lifecycle helper's hook for refuseTakeover, bound to one call's Owner: `(what) => …`. */\nexport type Claim = (what: string) => Effect.Effect<void>;\n\n/**\n * `reconcile`'s check, called with the live object it found BEFORE any write: dies unless that\n * object may be written. It may when state already vouches for it (`output`), when this apply\n * resumes the resource's own interrupted create or replace and its diff proved the object that\n * generation's (resume.ts), or when adoption is on for a create or an unfinished generation\n * (adoptable).\n * ⛔ WHY IT EXISTS AT ALL: THE PROBE DOES NOT GUARD EVERY CREATE. Alchemy skips it while `news` holds\n * an Output (Plan.ts), a new generation of a replace is never probed, and an object can appear\n * between plan and apply. Each of those used to be a silent takeover: written over, then recorded\n * as ours, and deleted with the stack under `destroy`.\n * ⚠️ A CHECK, THEN A WRITE — NOT A LOCK. Two creates of one name in the same deploy run\n * concurrently, both read nothing, and both write (measured with the fakes, 2026-09-21). Nothing\n * between a read and a write can close that; declare each name once.\n * ⚠️ AN IDENTICAL OBJECT IS STILL A TAKEOVER (decided 2026-09-21). Nothing would be written, but the\n * state row would claim it, and a later delete would remove what another owner still relies on.\n * ⛔ A REFUSED CREATE FORGETS ITS `creating` ROW (rows.ts forgetRefusedCreate), or the next plan's\n * recovery read would adopt the very object this refused.\n */\nexport const refuseTakeover = (owner: Owner, what: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n if (owner.output !== undefined) return;\n if (yield* resumes(owner.instanceId)) return;\n const may = yield* adoptable(owner);\n if (may && (yield* adoptEnabled(owner.fqn))) return;\n yield* forgetRefusedCreate(owner.fqn, owner.instanceId);\n const remedy = may\n ? 'Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first.'\n : 'It is the new identity of a replace, which --adopt does not cover: remove it first.';\n return yield* Effect.die(\n new Error(\n `${what}: already exists, and this stack holds no state for it — the plan could not ask ` +\n 'first (a prop was still an Output, a replace moved onto it, or it appeared after the ' +\n `plan). Nothing was written. ${remedy}`,\n ),\n );\n });\n\n/** refuseTakeover bound to one call's Owner, for a lifecycle helper that finds the live object. */\nexport const claimFor =\n (owner: Owner): Claim =>\n (what) =>\n refuseTakeover(owner, what);\n",
9
+ "/**\n * Whether this apply RESUMES a create or replace the engine already started for this resource AND\n * the live object it will find is proven that generation's own — the one case in which a stateless\n * `reconcile` may write over a live object without `--adopt`.\n *\n * ★ HOW THE ENGINE TELLS US. Alchemy calls `diff` whenever a state row exists, and hands it `output`\n * = that row's attributes (alchemy beta.79 Plan.ts). A row with NO attributes is exactly an\n * unfinished generation: `creating` (a create interrupted after its write, before its commit),\n * `replacing` (the same for a replace), or `deleting` re-driven as a create. A fresh create is\n * never diffed at all. So the diff of such a row decides, and the apply that follows (the same\n * instance id: Apply.ts reuses the row's) finds the note.\n * ⛔ A ROW PROVES NOTHING BY ITSELF (red team, 2026-09-21). Apply commits it BEFORE reconcile runs, so\n * a deploy killed before the claim — an upstream that failed, a Ctrl-C — leaves a row whose create\n * never asked whose object sat at its identity. 🔴 MEASURED (openbao/adopt-holes.test.ts): with the\n * name an Output, the next deploy \"resumed\" that create and wrote over another owner's role, in\n * every family that had a note on the row alone. So the note is left only when the family's own\n * `read`, asked as the recovery read (probe.ts ownedRead: a recorded instance, a row carrying the\n * whole declaration, and a live object that matches it), answers \"ours\". For a `creating` row\n * that is the question Plan.ts already asked; for a `replacing` row it is the only time it is asked.\n * ⚠️ WHY NOT THE STATE STORE ALONE. At reconcile both look the same: Apply commits the fresh create's\n * `creating` row before calling reconcile, so a row with this instance id is always there.\n * ★ THE CHANNEL IS ALCHEMY'S OWN: `Artifacts`, the per-FQN bag the engine shares between one plan\n * and its apply (Artifacts.ts: \"diff computes … create / update reads the same artifact\").\n * Deploy.ts and the Alchemist session both hold one store across the pair.\n * ⚠️ IT FAILS CLOSED. No bag, a bag the plan never wrote, or an object the read cannot prove ours\n * reads as a fresh create, and adopt.ts then asks for `--adopt` — the safe direction.\n * ★ AN UNPROVEN RESUME IS STILL MARKED UNFINISHED (`noteUnfinished`), so `--adopt` can take it over\n * the way Alchemy's own \"Cannot resume creating … Re-run with --adopt\" offers for a create — while\n * a FRESH replace's new generation, which no diff ever saw without attributes, stays unadoptable.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { Artifacts } from 'alchemy/Artifacts';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nconst PROVEN = 'homeflare/ownership/resumes';\nconst UNFINISHED = 'homeflare/ownership/unfinished';\n\nconst note = (key: string, instanceId: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n const bag = yield* Effect.serviceOption(Artifacts);\n if (Option.isSome(bag)) yield* bag.value.set(key, instanceId);\n });\n\nconst noted = (key: string, instanceId: string): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n const bag = yield* Effect.serviceOption(Artifacts);\n if (Option.isNone(bag)) return false;\n return (yield* bag.value.get<string>(key)) === instanceId;\n });\n\n/**\n * `diff`'s note for a row with no attributes: `instanceId` is an unfinished generation of this\n * resource's own, which `--adopt` may resume when nothing proves it (adopt.ts adoptable). Returns\n * `undefined`, a diff's \"defer to the engine\".\n */\nexport const noteUnfinished = (instanceId: string): Effect.Effect<undefined> =>\n Effect.as(note(UNFINISHED, instanceId), undefined);\n\n/**\n * Note `instanceId` unfinished, and a proven resume when `recovered` — the family's `read` of the\n * row's props, asked with no attributes — answers a plain object: ours. `Unowned`, nothing, or a\n * read that fails or dies proves nothing. ⚠️ Never fails the plan: an unproven resume is refused\n * at apply instead, unless `--adopt`.\n */\nexport const noteResume = <R>(\n instanceId: string,\n recovered: Effect.Effect<unknown, unknown, R>,\n): Effect.Effect<void, never, R> =>\n Effect.gen(function* () {\n yield* noteUnfinished(instanceId);\n const unproven = () => Effect.succeed(undefined);\n const found = yield* recovered.pipe(Effect.catch(unproven), Effect.catchDefect(unproven));\n if (found === undefined || found === null || Unowned.is(found)) return;\n yield* note(PROVEN, instanceId);\n });\n\n/** Whether the plan for this apply noted `instanceId` as a proven resumed create or replace. */\nexport const resumes = (instanceId: string): Effect.Effect<boolean> => noted(PROVEN, instanceId);\n\n/** Whether the plan for this apply saw `instanceId` as an unfinished generation (proven or not). */\nexport const unfinished = (instanceId: string): Effect.Effect<boolean> =>\n noted(UNFINISHED, instanceId);\n\n/** The fields of a `read` or `diff` call provingResumes uses; a family's own input carries more. */\ntype Ask = {\n readonly id: string;\n readonly fqn: string;\n readonly instanceId: string;\n readonly olds: unknown;\n readonly output: unknown;\n};\ntype Hook = Effect.Effect<unknown, unknown, unknown>;\n\n/** The two hooks provingResumes wraps, as Alchemy's ProviderService declares them. */\ninterface Resumable {\n read?(input: Ask): Hook;\n diff?(input: Ask): Hook;\n}\n\n/**\n * A provider whose `diff`, for a row with no attributes, first asks the provider's own `read` whether\n * the live object is that unfinished generation's (noteResume), then runs as written. Every Bao.*\n * family is built through it, so their diffs answer such a row with a plain `undefined`.\n */\nexport const provingResumes = <S extends Resumable>(service: S): S => {\n const { diff, read } = service;\n if (diff === undefined || read === undefined) return service;\n const proving: Resumable['diff'] = (input) =>\n Effect.gen(function* () {\n if (input.output === undefined) {\n yield* noteResume(input.instanceId, read({ ...input, output: undefined }));\n }\n return yield* diff(input);\n });\n // ⚠️ The one cast: `diff` keeps the family's own signature — it is the family's diff, run after.\n return { ...service, diff: proving } as S;\n};\n",
10
+ "/**\n * The questions ownership/ asks of Alchemy's state store: does a row record this instance, and with\n * its whole declaration (probe.ts); is this apply a create rather than a replace's new generation\n * (adopt.ts); and — when reconcile refuses a create — forget the row Apply committed for it\n * (adopt.ts). Alchemy's own AWS.EC2.SecurityGroup provider reads the store the same way: the Stack\n * service names the stack and stage, and `State` yields the store.\n *\n * ⚠️ WITHOUT A STACK OR STATE IN CONTEXT, or with a store that errors, a row reads as \"not\n * recorded\" — the object is then `Unowned`, the safe answer — and nothing is forgotten, which\n * leaves the one gap forgetRefusedCreate exists to close. `alchemy deploy` and the Alchemist\n * session provide both.\n */\nimport { Stack } from 'alchemy/Stack';\nimport { State, type StateService, isActionState } from 'alchemy/State/State';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport { wholeDeclaration } from './whole.ts';\n\ntype Where = { readonly stack: string; readonly stage: string };\n\n/** The store, where this stack lives, and what it declares — or undefined outside a stack. */\nconst storeOf = Effect.gen(function* () {\n const stack = yield* Effect.serviceOption(Stack);\n const state = yield* Effect.serviceOption(State);\n if (Option.isNone(stack) || Option.isNone(state)) return undefined;\n const store: StateService = yield* state.value;\n const where: Where = { stack: stack.value.name, stage: stack.value.stage };\n return { resources: stack.value.resources, store, where };\n});\n\nconst rowAt = (store: StateService, where: Where, fqn: string) =>\n store.get({ ...where, fqn }).pipe(Effect.orElseSucceed(() => undefined));\n\n/** One generation of a persisted row, as far as ownership/ reads it. */\nexport type Generation = {\n readonly instanceId?: unknown;\n readonly status?: unknown;\n readonly props?: unknown;\n readonly attr?: unknown;\n readonly old?: unknown;\n /** Set by Apply on the `updating` row of an adoption, until that update commits (Apply.ts). */\n readonly adopting?: unknown;\n};\n\n/** The generation of a persisted row that carries `instanceId`: the row itself, or one in `old`. */\nconst generationOf = (row: unknown, instanceId: string): Generation | undefined => {\n let at = row;\n while (typeof at === 'object' && at !== null && !isActionState(at as never)) {\n const generation = at as Generation;\n if (generation.instanceId === instanceId) return generation;\n at = generation.old;\n }\n return undefined;\n};\n\n/** The generation `instanceId` names — at the FQN, or at a former FQN `renamedFrom` names. */\nexport const recordedGeneration = (\n fqn: string,\n instanceId: string,\n): Effect.Effect<Generation | undefined> =>\n Effect.gen(function* () {\n const found = yield* storeOf;\n if (found === undefined) return undefined;\n for (const at of [fqn, ...(found.resources[fqn]?.FormerFqns ?? [])]) {\n const generation = generationOf(yield* rowAt(found.store, found.where, at), instanceId);\n if (generation !== undefined) return generation;\n }\n return undefined;\n });\n\n/**\n * What the state store says of `instanceId` for this resource (at its FQN, or at a former FQN its\n * `renamedFrom` names — the planner migrates that row before it recovers it):\n * · `absent` — no row holds it. ★ This is what separates the probe from the recovery read: the\n * probe's instance id is minted for the probe (Plan.ts `generateInstanceId()`), 128 random bits\n * no row can hold.\n * · `partial` — a row holds it, but its props lack part of the declaration: a prop stripped at\n * commit because it was still an Output (whole.ts). Those props cannot prove an object ours.\n * · `whole` — a row holds it with every prop the declaration names.\n */\nexport type Recorded = 'absent' | 'partial' | 'whole';\n\nexport const recordedInstance = (fqn: string, instanceId: string): Effect.Effect<Recorded> =>\n Effect.gen(function* () {\n const generation = yield* recordedGeneration(fqn, instanceId);\n if (generation === undefined) return 'absent';\n return (yield* wholeDeclaration(fqn, generation.props)) ? 'whole' : 'partial';\n });\n\n/**\n * Whether this apply's `instanceId` is a CREATE: the row Apply committed for it is `creating`.\n * ⛔ A REPLACE'S NEW GENERATION IS NOT ONE. Apply commits it as `replacing`, and the planner never\n * offers it adoption — the probe runs only for a resource with no state — so `--adopt` must not\n * let it write over whatever sits at its new identity (adopt.ts adoptsAtApply).\n */\nexport const isCreate = (fqn: string, instanceId: string): Effect.Effect<boolean> =>\n Effect.map(\n recordedGeneration(fqn, instanceId),\n (generation) => generation?.status === 'creating',\n );\n\n/**\n * ⛔ A REFUSED CREATE MUST NOT LEAVE A ROW THAT CLAIMS THE OBJECT IT REFUSED. Apply commits a\n * `creating` row for this instance before it calls reconcile, and a failed reconcile leaves it\n * there. The next plan's recovery read would then find our instance id and an object that can\n * match its props — a prop that was an Output is stripped from the row, and an omitted prop is\n * \"not managed\" — and adopt exactly what this apply refused. 🔴 MEASURED 2026-09-21: Bao.Mount\n * and Bao.AuthMethod did (openbao/adopt-core.test.ts). So the row goes, and state reads as if\n * the create never started: the next plan probes, and the probe answers `Unowned`.\n * ⚠️ ONLY a `creating` row of THIS instance with no attributes — the one Apply just wrote. A\n * `replacing` row carries the previous generation in `old` and is never touched.\n */\nexport const forgetRefusedCreate = (fqn: string, instanceId: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n const found = yield* storeOf;\n if (found === undefined) return;\n const row = yield* rowAt(found.store, found.where, fqn);\n const mine = row as { status?: unknown; instanceId?: unknown; attr?: unknown } | undefined;\n if (mine?.status !== 'creating' || mine.instanceId !== instanceId || mine.attr !== undefined) {\n return;\n }\n yield* found.store.delete({ ...found.where, fqn }).pipe(Effect.ignore);\n });\n",
11
+ "/**\n * Whether a state row's props say everything the declaration says — the test that separates a row\n * whose props can prove an object ours from one that cannot (probe.ts, rows.ts).\n *\n * ★ WHY A ROW CAN BE MISSING PART OF ITS DECLARATION. Apply commits the `creating` (or `replacing`)\n * row BEFORE reconcile runs, and strips every prop still an Output at that moment (alchemy\n * beta.79 Apply.ts `commit` → Diff.ts `stripUnresolved`); a JSON store then drops the key. So a\n * row written while an upstream was still being created carries a HOLE where that prop was.\n * ⛔ A HOLE IS \"UNKNOWN\", NOT \"UNMANAGED\". Most families read an absent prop as \"not managed\", so a\n * recovery read comparing the live object with holed props proves nothing: 🔴 MEASURED 2026-09-21\n * (openbao/adopt-holes.test.ts), a Bao.Mount whose `defaultLeaseTtl` was an Output, killed before\n * its reconcile ran, adopted — and tuned — another owner's mount on the next deploy; with the\n * NAME an Output, every role family read an empty name, found nothing, and the resumed create\n * wrote over another owner's role. Neither deploy had ever asked whose object it was.\n * ★ THE DECLARATION IS WHAT TELLS A HOLE FROM AN OMISSION. It still holds the Output (or the literal)\n * the row lacks, while an optional prop left out is absent from both.\n * ⚠️ CONSERVATIVE BY DESIGN: a prop added to the declaration after the crash also reads as a hole.\n * That costs one `--adopt`, never a takeover.\n */\nimport { Stack } from 'alchemy/Stack';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nconst absent = (value: unknown): boolean => value === undefined || value === null;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\n/**\n * Whether `recorded` carries a value everywhere `declared` does. Plain objects and arrays are\n * walked; any other declared value — a literal, an Output, an Effect — needs a value in the row.\n */\nexport const carries = (declared: unknown, recorded: unknown): boolean => {\n if (absent(declared)) return true;\n if (absent(recorded)) return false;\n if (Array.isArray(declared)) {\n if (!Array.isArray(recorded)) return false;\n return declared.every((each, index) => carries(each, recorded[index]));\n }\n if (isPlainObject(declared)) {\n if (!isPlainObject(recorded)) return false;\n return Object.entries(declared).every(([key, each]) => carries(each, recorded[key]));\n }\n return true;\n};\n\n/**\n * Whether `props` — one generation's, from the state store — carry the whole declaration of the\n * resource at `fqn`. ⛔ FALSE WITH NOTHING TO COMPARE: no Stack in context, or a resource no longer\n * declared (an orphan's delete). That object then reads as `Unowned` and Apply leaves it in place\n * with a note, rather than deleting what it cannot prove it made.\n */\nexport const wholeDeclaration = (fqn: string, props: unknown): Effect.Effect<boolean> =>\n Effect.map(Effect.serviceOption(Stack), (stack) => {\n const declared = Option.isSome(stack) ? stack.value.resources[fqn] : undefined;\n return declared !== undefined && carries(declared.Props, props);\n });\n",
12
+ "/**\n * A tripwire for the obvious ways a secret ends up in Alchemy state through these providers.\n *\n * ⛔ WHY IT EXISTS. Alchemy persists every prop UNENCRYPTED in its state store — the same reason\n * the OpenBao families are metadata-only (openbao/policy.ts). A LaunchdJob's `environment` and\n * `programArguments`, and a HostFile's `content`, are props. A token declared there is a token\n * copied into the state store, into every plan diff, and into the plist or file on disk.\n * ★ THE ALTERNATIVE, NAMED IN EVERY REFUSAL: let a secret renderer (openbao-agent, or any other)\n * write the secret to a root-owned file, and declare only its PATH — `FOO_TOKEN_FILE=/path`,\n * `--token-file /path`. The job reads the file at start; the state holds a path.\n *\n * ⚠️ A TRIPWIRE, NOT A SCANNER. It catches the names and shapes people actually type. A token in\n * an innocently named variable passes; nothing here can make a secret safe to declare.\n */\nimport type { PlistDict, PlistValue } from './plist.ts';\n\nconst ALTERNATIVE =\n 'props are stored unencrypted in Alchemy state. Have a secret renderer (e.g. openbao-agent) ' +\n 'write it to a root-owned file and declare the file path instead (FOO_FILE=/path, --foo-file /path).';\n\n/** Names that end in a word meaning \"this is the secret itself\". `*_FILE`, `*_PATH` never match. */\nconst SECRET_NAME =\n /(?:^|_)(?:TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|API_?KEY|ACCESS_KEY|PRIVATE_KEY|SECRET_KEY|CREDENTIALS?)$/i;\n\n/** `--token=…` / `-password=…` style flags carrying their value inline. */\nconst INLINE_FLAG = /^--?[a-z0-9-]*(?:token|password|passwd|passphrase|secret|api-?key)=./i;\n\n/** A flag whose NEXT argv element is the secret: `--token abc`. */\nconst BARE_FLAG = /^--?(?:[a-z0-9]+-)*(?:token|password|passwd|passphrase|secret|api-?key)$/i;\n\nexport const PRIVATE_KEY = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/;\n\n/** Paths of every string leaf in a plist value holding a private key. */\nconst keyLeaves = (value: PlistValue, path: string): string[] => {\n if (typeof value === 'string') return PRIVATE_KEY.test(value) ? [path] : [];\n if (Array.isArray(value)) {\n return (value as readonly PlistValue[]).flatMap((item, index) =>\n keyLeaves(item, `${path}[${String(index)}]`),\n );\n }\n if (typeof value === 'object' && value !== null) {\n return Object.entries(value).flatMap(([key, item]) => keyLeaves(item, `${path}.${key}`));\n }\n return [];\n};\n\n/**\n * Refusals for a job's environment, argv and extraKeys. Empty means nothing tripped.\n * ★ extraKeys too: it is a prop like the others, so it lands in state just the same.\n */\nexport const jobSecretProblems = (\n environment: Readonly<Record<string, string>> | undefined,\n argv: readonly string[],\n extraKeys?: PlistDict,\n): string[] => {\n const found: string[] = [];\n for (const [name, value] of Object.entries(environment ?? {})) {\n if (SECRET_NAME.test(name))\n found.push(`environment ${name} looks like a secret: ${ALTERNATIVE}`);\n else if (PRIVATE_KEY.test(value))\n found.push(`environment ${name} holds a private key: ${ALTERNATIVE}`);\n }\n for (const [index, arg] of argv.entries()) {\n // ⛔ Never echo the argument itself: if it IS a secret, the refusal would print it.\n if (INLINE_FLAG.test(arg)) {\n found.push(`programArguments[${String(index)}] passes a secret inline: ${ALTERNATIVE}`);\n } else if (BARE_FLAG.test(arg) && index + 1 < argv.length) {\n found.push(`programArguments[${String(index + 1)}] follows a secret flag: ${ALTERNATIVE}`);\n } else if (PRIVATE_KEY.test(arg)) {\n found.push(`programArguments[${String(index)}] holds a private key: ${ALTERNATIVE}`);\n }\n }\n for (const path of keyLeaves(extraKeys ?? {}, 'extraKeys')) {\n found.push(`${path} holds a private key: ${ALTERNATIVE}`);\n }\n return found;\n};\n\n/** Refusals for a HostFile's content. */\nexport const fileSecretProblems = (content: string): string[] =>\n PRIVATE_KEY.test(content) ? [`content holds a private key: ${ALTERNATIVE}`] : [];\n",
13
+ "/**\n * HostFile's props, attributes and validation — the pure half of the provider.\n *\n * ⛔ `content` IS NEVER A SECRET. Alchemy stores props unencrypted, so the content of every HostFile\n * sits in the state store and in every plan diff. A secret file stays RENDERED BY A SECRET\n * RENDERER (openbao-agent templates, on this estate) that fetches it at run time; the stack\n * declares only non-secret config, and at most the PATH the renderer writes to.\n * secret-tripwire.ts refuses the obvious case (a PEM private key); nothing can catch all of them.\n */\nimport { fileSecretProblems } from './secret-tripwire.ts';\n\nexport interface HostFileProps {\n /** Absolute path. The parent directory must already exist. */\n path: string;\n /** UTF-8 text. ⛔ Never a secret — see the file header. */\n content: string;\n /** Permission bits. @default 0o644 */\n mode?: number;\n /** User name or numeric uid. Omitted: whoever the runner writes as (a rewrite keeps no owner). */\n owner?: string | number;\n /** Group name or numeric gid. Omitted: the directory's default group. */\n group?: string | number;\n}\n\nexport interface HostFileAttributes {\n path: string;\n /** SHA-256 of the bytes on disk. */\n sha256: string;\n mode: number;\n uid: number;\n gid: number;\n size: number;\n}\n\nexport const DEFAULT_MODE = 0o644;\n\nconst identityProblems = (name: string, value: string | number | undefined): string[] => {\n if (value === undefined) return [];\n if (typeof value === 'number') {\n return Number.isSafeInteger(value) && value >= 0\n ? []\n : [`${name} must be a non-negative integer id`];\n }\n return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\\d+$/.test(value)\n ? []\n : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];\n};\n\n/**\n * An absolute, normalised path, or the reasons it is not. ★ Shared with sudo-allowlist.ts, so a\n * path the sudo runner may touch as root obeys exactly the rule a declared file does.\n */\nexport const pathProblems = (path: string): string[] => {\n const found: string[] = [];\n if (!path.startsWith('/')) found.push('path must be absolute');\n // ★ One spelling per file: `a/./b`, `a//b` and `a/../b` would each be a second resource for the\n // same inode, and `..` is how a derived path walks out of the directory it was meant for.\n if (\n path.endsWith('/') ||\n path\n .split('/')\n .slice(1)\n .some((part) => part === '' || part === '.' || part === '..')\n ) {\n found.push('path must be normalised (no trailing slash, no empty, \".\" or \"..\" segments)');\n }\n if (path.includes('\\x00')) found.push('path contains NUL');\n return found;\n};\n\nexport const fileProblems = (props: HostFileProps): string[] => {\n const found = pathProblems(props.path);\n const mode = props.mode ?? DEFAULT_MODE;\n if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');\n found.push(...identityProblems('owner', props.owner));\n found.push(...identityProblems('group', props.group));\n found.push(...fileSecretProblems(props.content));\n return found;\n};\n",
14
+ "/**\n * HostDirectory's props, validation and lifecycle over a HostRunner.\n *\n * ★ WHY IT EXISTS AT ALL. `Host.File` and `Remote.File` both refuse to create a parent directory,\n * on purpose — creating one would invent an owner and a mode nobody declared. The consequence is\n * that a first deploy into a new tree fails on its first file. This resource is the declaration\n * that was missing, not a convenience flag on the file.\n * ⛔ ONE DIRECTORY, NEVER A CHAIN. A missing parent is a refusal that names it, so a tree is a\n * chain of declarations whose modes and owners are all visible in the plan. `mkdir -p` would\n * create the intermediate levels at whatever umask the deploy happened to run with.\n * ⛔ DELETE IS `rmdir`, NEVER RECURSIVE. A directory that still holds files is a refusal: removing\n * a declaration must not be able to remove data the declaration never mentioned.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport { pathProblems } from '../launchd/host-file-form.ts';\nimport { type HostRunner, canActAsRoot } from '../launchd/runner.ts';\n\nexport interface HostDirectoryProps {\n /** Absolute path. ⚠️ Its parent must already exist — declare it as another HostDirectory. */\n path: string;\n /** Permission bits. @default 0o755 */\n mode?: number;\n /** User name or numeric uid. Omitted: whoever the runner writes as. */\n owner?: string | number;\n /** Group name or numeric gid. Omitted: the parent's default group. */\n group?: string | number;\n}\n\nexport interface HostDirectoryAttributes {\n path: string;\n mode: number;\n uid: number;\n gid: number;\n}\n\nexport const DEFAULT_DIR_MODE = 0o755;\n\nexport const refuse = (path: string, message: string): Error =>\n new Error(`Host.Directory ${path}: ${message}`);\n\nconst idProblems = (name: string, value: string | number | undefined): string[] => {\n if (value === undefined) return [];\n if (typeof value === 'number') {\n return Number.isSafeInteger(value) && value >= 0 ? [] : [`${name} must be a non-negative id`];\n }\n return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\\d+$/.test(value)\n ? []\n : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];\n};\n\nexport const directoryProblems = (props: HostDirectoryProps): string[] => {\n const found = pathProblems(props.path);\n const mode = props.mode ?? DEFAULT_DIR_MODE;\n if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');\n found.push(...idProblems('owner', props.owner), ...idProblems('group', props.group));\n return found;\n};\n\nconst numericId = (value: string | number): number | undefined =>\n typeof value === 'number' ? value : /^\\d+$/.test(value) ? Number(value) : undefined;\n\ntype Desired = { readonly mode: number; readonly uid?: number; readonly gid?: number };\n\nconst desired = async (runner: HostRunner, props: HostDirectoryProps): Promise<Desired> => {\n const found = directoryProblems(props);\n if (found.length > 0) throw refuse(props.path, found.join('; '));\n let uid: number | undefined;\n let gid: number | undefined;\n if (props.owner !== undefined) {\n uid = numericId(props.owner) ?? (await runner.lookupUser(String(props.owner)))?.uid;\n if (uid === undefined) throw refuse(props.path, `no user ${String(props.owner)} on this host`);\n }\n if (props.group !== undefined) {\n gid = numericId(props.group) ?? (await runner.lookupGroup(String(props.group)));\n if (gid === undefined) throw refuse(props.path, `no group ${String(props.group)} on this host`);\n }\n return {\n mode: props.mode ?? DEFAULT_DIR_MODE,\n ...(uid === undefined ? {} : { uid }),\n ...(gid === undefined ? {} : { gid }),\n };\n};\n\nexport const readDirectory = async (\n runner: HostRunner,\n path: string,\n): Promise<HostDirectoryAttributes | undefined> => {\n const stat = await runner.stat(path);\n if (stat === undefined || stat.kind !== 'directory') return undefined;\n return { gid: stat.gid, mode: stat.mode, path, uid: stat.uid };\n};\n\nconst octal = (mode: number) => mode.toString(8).padStart(3, '0');\n\n/** One program, checked: anything but exit 0 is an Error naming the argv and the host's words. */\nconst must = async (runner: HostRunner, path: string, argv: readonly string[]): Promise<void> => {\n const result = await runner.exec(argv);\n if (result.exitCode !== 0) {\n throw refuse(\n path,\n `${argv.join(' ')} -> ${String(result.exitCode)}: ${result.stderr.trim().slice(0, 300)}`,\n );\n }\n};\n\n/**\n * Create it when absent, then bring mode and ownership to the declaration and read back.\n * ⚠️ `mkdir -m` sets the mode with chmod(2) after mkdir(2), so it is exact — plain `mkdir` would\n * mask the declared bits through the deploying shell's umask and the drift would return forever.\n */\nexport const reconcileDirectory = async (\n runner: HostRunner,\n props: HostDirectoryProps,\n output?: HostDirectoryAttributes,\n adopt = false,\n): Promise<HostDirectoryAttributes> => {\n const want = await desired(runner, props);\n if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {\n throw refuse(\n props.path,\n `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy through ` +\n 'a root destination or a privileged HostRunner. This provider never calls sudo itself.',\n );\n }\n const stat = await runner.stat(props.path);\n if (stat !== undefined && stat.kind !== 'directory') {\n throw refuse(props.path, `is a ${stat.kind}, not a directory`);\n }\n /**\n * ⛔ A DIRECTORY THIS RESOURCE DOES NOT OWN IS NEVER RE-MODED. Where the engine's probe never\n * looked — a replace's new path, a create whose props were still an Output — an existing\n * directory with a different mode or owner is someone else's. ★ Adoption takes it over.\n */\n if (output === undefined && stat !== undefined && !adopt) {\n const drifted = stat.mode !== want.mode || (want.uid !== undefined && stat.uid !== want.uid);\n if (drifted) {\n throw refuse(\n props.path,\n 'already exists with another mode or owner and is not this resource. Remove it, or ' +\n 'declare it as a new resource and deploy with --adopt.',\n );\n }\n }\n if (stat === undefined) {\n const parent = props.path.slice(0, props.path.lastIndexOf('/')) || '/';\n const above = await runner.stat(parent);\n if (above === undefined || above.kind !== 'directory') {\n throw refuse(props.path, `its parent ${parent} does not exist; declare that directory too`);\n }\n await must(runner, props.path, ['mkdir', '-m', octal(want.mode), '--', props.path]);\n } else if (stat.mode !== want.mode) {\n await must(runner, props.path, ['chmod', octal(want.mode), '--', props.path]);\n }\n /**\n * ★ Only when it is actually wrong. A `chown` that changes nothing is still a write on the host\n * and a line in the deploy log, and \"a converged resource is not touched\" is the rule this\n * whole family is judged by.\n */\n const owner = ownerMatches(\n { gid: stat?.gid ?? -1, mode: 0, path: props.path, uid: stat?.uid ?? -1 },\n want,\n )\n ? undefined\n : ownerArg(want);\n if (owner !== undefined) await must(runner, props.path, ['chown', owner, '--', props.path]);\n const after = await readDirectory(runner, props.path);\n // ⚠️ READ BACK: a runner that ignored the mode shows up here, not as a forever-`update`.\n if (after === undefined || after.mode !== want.mode || !ownerMatches(after, want)) {\n throw refuse(props.path, 'the directory on the host does not match the declaration');\n }\n return after;\n};\n\n/** ⛔ Never `<uid>:` — GNU chown reads a trailing colon as that user's LOGIN group, undeclared. */\nconst ownerArg = (want: Desired): string | undefined => {\n if (want.uid === undefined) return want.gid === undefined ? undefined : `:${String(want.gid)}`;\n return want.gid === undefined ? String(want.uid) : `${String(want.uid)}:${String(want.gid)}`;\n};\n\nconst ownerMatches = (live: HostDirectoryAttributes, want: Desired): boolean =>\n (want.uid === undefined || live.uid === want.uid) &&\n (want.gid === undefined || live.gid === want.gid);\n\n/**\n * ★ A plan that will write asks the runner first (`checkWrite`, when it has one) — a directory is\n * created and chmod'd through `exec`, but a runner that refuses to touch the path at all should\n * say so at plan time rather than mid-apply.\n */\nexport const diffDirectory = async (\n runner: HostRunner,\n news: HostDirectoryProps,\n output: HostDirectoryAttributes,\n): Promise<Diff> => {\n const want = await desired(runner, news);\n if (news.path !== output.path) return { action: 'replace' };\n const live = await readDirectory(runner, news.path);\n // ⛔ BOTH: the live directory catches a hand chmod, the stored attributes catch a deploy that\n // died after the mkdir and before its commit.\n const converged =\n live !== undefined &&\n live.mode === want.mode &&\n ownerMatches(live, want) &&\n output.mode === want.mode &&\n ownerMatches(output, want);\n if (converged) return { action: 'noop' };\n await runner.checkWrite?.(news.path, want);\n return { action: 'update' };\n};\n\n/** Remove it. ⛔ `rmdir`: a directory that still holds anything is a refusal, never a recursion. */\nexport const deleteDirectory = async (\n runner: HostRunner,\n output: HostDirectoryAttributes,\n): Promise<void> => {\n const stat = await runner.stat(output.path);\n if (stat === undefined) return;\n if (stat.kind !== 'directory')\n throw refuse(output.path, `is now a ${stat.kind}; not removing it`);\n await must(runner, output.path, ['rmdir', '--', output.path]);\n};\n",
15
+ "/**\n * Every provider in this subpath with ONE HostRunner, as a single layer for a host stack.\n *\n * const runner = await sshRunner({ host: 'the-host' });\n * Layer.mergeAll(linuxProviders(runner), …the stack's other providers)\n *\n * ★ ONE RUNNER FOR ALL OF THEM, so a directory, the config file inside it and the unit that reads\n * that file are written by the same identity on the same host — a stack cannot create the\n * directory on one host and start the unit on another by accident.\n * ⛔ THERE IS NO DEFAULT. launchdProviders() defaults to localRunner() because a Mac stack runs on\n * the Mac it declares; a Linux host is somewhere else, and a silent default to \"this machine\"\n * would write the estate's `/etc/systemd/system` onto the laptop running the deploy.\n */\nimport * as Layer from 'effect/Layer';\nimport { type HostRunner, hostRunnerLayer } from '../launchd/runner.ts';\nimport { HostDirectoryProvider } from './directory.ts';\nimport { RemoteFileProvider } from './remote-file.ts';\nimport { SystemdTimerProvider, SystemdUnitProvider } from './unit.ts';\n\nexport const linuxProviders = (runner: HostRunner) =>\n Layer.mergeAll(\n HostDirectoryProvider(),\n RemoteFileProvider(),\n SystemdTimerProvider(),\n SystemdUnitProvider(),\n ).pipe(Layer.provide(hostRunnerLayer(runner)));\n",
16
+ "/**\n * `Remote.File` — one text file on a Linux host, declared: content, mode, owner, group — or one\n * MANAGED BLOCK inside a file this resource does not own.\n *\n * - create / update — atomic write (stage beside the target, chown, chmod, rename), skipped when\n * the host already says what we declare; then read back and compared.\n * - read — lstat plus the SHA-256 of the file and of the part this resource owns.\n * - diff — the declared digest against the stored AND the on-disk one, plus mode and owner.\n * - replace — only when `path` changes; create-before-delete.\n * - delete — removes the file, or in region mode only the block.\n *\n * ⛔ NEVER A SECRET — see remote-file-form.ts.\n * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt` (docs/ownership.md): a file already at the path, or a\n * block already carrying the markers, reads as `Unowned`.\n * ★ IT IS A SEPARATE RESOURCE FROM `Host.File`, not a flag on it: the two have different attributes\n * (a region name that a delete cannot work without) and different promises about the rest of the\n * file, and folding them together would make every existing Mac HostFile's state shape change.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { lift, resolvedString } from '../launchd/host-effect.ts';\nimport { HostRunnerService } from '../launchd/runner.ts';\nimport { adoptsAtApply } from '../ownership/adopt.ts';\nimport { noteUnfinished } from '../ownership/resume.ts';\nimport type { RemoteFileAttributes, RemoteFileProps } from './remote-file-form.ts';\nimport {\n deleteFile,\n diffFile,\n readFileAttributes,\n reconcileFile,\n} from './remote-file-lifecycle.ts';\n\nexport type { RemoteFileAttributes, RemoteFileProps } from './remote-file-form.ts';\n\nexport interface RemoteFile extends Resource<\n 'Remote.File',\n RemoteFileProps,\n RemoteFileAttributes\n> {}\n\nexport const RemoteFile = Resource<RemoteFile>('Remote.File');\n\nexport const RemoteFileProvider = () =>\n Provider.effect(\n RemoteFile,\n Effect.gen(function* () {\n const runner = yield* HostRunnerService;\n return RemoteFile.Provider.of({\n /** ⛔ A host's files are not a list of things this stack owns. */\n list: () => Effect.succeed([]),\n\n read: ({ olds, output }) =>\n lift(async () => {\n const found = await readFileAttributes(runner, olds.path, olds.region);\n if (found === undefined) return undefined;\n // ⛔ In region mode an existing file is NOT this resource: only the block is. A file\n // with no such block reads as nothing, so the create writes the block instead of\n // demanding --adopt for a file it was never claiming.\n if (olds.region !== undefined && found.contentSha256 === '') return undefined;\n return output === undefined ? Unowned(found) : found;\n }),\n\n diff: ({ instanceId, news, output }) => {\n // ★ An unfinished generation of our own: `--adopt` may resume it (ownership/resume.ts).\n if (output === undefined) return noteUnfinished(instanceId);\n if (isResolved(news)) return lift(() => diffFile(runner, news, output));\n // ⛔ A new path is a replace even while `content` is unresolved (an Output templated in):\n // the engine's default would be an update, which writes the new path and never removes\n // the old one.\n const path = resolvedString(news, 'path');\n return Effect.succeed(\n path !== undefined && path !== output.path ? { action: 'replace' as const } : undefined,\n );\n },\n\n // ★ `--adopt` reaches the apply too: the probe never ran for a create with an Output prop.\n reconcile: ({ fqn, instanceId, news, output }) =>\n Effect.flatMap(adoptsAtApply({ fqn, instanceId, output }), (adopt) =>\n lift(() => reconcileFile(runner, news, output, adopt)),\n ),\n\n delete: ({ output }) => lift(() => deleteFile(runner, output)),\n });\n }),\n );\n",
17
+ "/**\n * LaunchdJob's props, attributes and the pure half of the provider: domain parsing, the plist path,\n * and the props → plist mapping whose SHA-256 is the whole diff.\n *\n * ★ KEY NAMES AND MEANINGS FROM launchd.plist(5), checked against the 148 nix-darwin daemons on a\n * macOS 27.2 host on 2026-09-21 (read-only, from /nix/store): Label, ProgramArguments, RunAtLoad,\n * KeepAlive (boolean or {SuccessfulExit}), ThrottleInterval, StandardOutPath/StandardErrorPath,\n * EnvironmentVariables, UserName, StartInterval and StartCalendarInterval are the keys in real\n * use. Everything rarer goes through `extraKeys`.\n */\nimport { createHash } from 'node:crypto';\nimport { type PlistDict, type PlistValue, renderPlist } from './plist.ts';\n\n/** `system` (LaunchDaemons, root) or one user's login session, `gui/<uid>` (LaunchAgents). */\nexport type LaunchdDomain = 'system' | `gui/${number}`;\n\n/** One StartCalendarInterval entry. An omitted field is a wildcard, as in cron. */\nexport type CalendarInterval = {\n readonly minute?: number;\n readonly hour?: number;\n readonly day?: number;\n /** 0 and 7 are both Sunday. */\n readonly weekday?: number;\n readonly month?: number;\n};\n\n/** `true` = always restart; the object form restarts only on the named conditions. */\nexport type KeepAlive = boolean | { readonly successfulExit?: boolean; readonly crashed?: boolean };\n\nexport interface LaunchdJobProps {\n /** Reverse-DNS label. Unique per domain; also the plist's file name. */\n label: string;\n domain: LaunchdDomain;\n /** argv; the first element must be an absolute path. */\n programArguments: readonly string[];\n /**\n * ⛔ NON-SECRET VALUES ONLY. Props are stored unencrypted in Alchemy state, and these also land in\n * the plist on disk and in `launchctl print`. Pass a secret as a FILE PATH that a secret renderer\n * (openbao-agent) maintains — secret-tripwire.ts refuses the obvious mistakes.\n */\n environment?: Readonly<Record<string, string>>;\n runAtLoad?: boolean;\n keepAlive?: KeepAlive;\n /** Seconds between starts. */\n startInterval?: number;\n startCalendarInterval?: CalendarInterval | readonly CalendarInterval[];\n /** Minimum seconds between (re)starts; launchd's default is 10. */\n throttleInterval?: number;\n standardOutPath?: string;\n standardErrorPath?: string;\n workingDirectory?: string;\n /** Run as this user. ⚠️ System domain only — launchd ignores it for agents, so it is refused. */\n userName?: string;\n /** Run as this group. ⚠️ System domain only, like `userName`. */\n groupName?: string;\n /**\n * Any other launchd.plist(5) key, verbatim (`ProcessType`, `SoftResourceLimits`, …).\n * ⛔ A key the typed props own is refused, so one job can never say two things.\n */\n extraKeys?: PlistDict;\n}\n\nexport interface LaunchdJobAttributes {\n label: string;\n domain: LaunchdDomain;\n /** `<domain>/<label>` — what `launchctl print` / `bootout` take. */\n serviceTarget: string;\n plistPath: string;\n /** SHA-256 of the plist bytes last bootstrapped (reconcile) or found on disk (read). */\n plistSha256: string;\n /** Bootstrapped into the domain — not necessarily running. */\n loaded: boolean;\n /** launchd's own word: `running`, `not running`, … */\n state?: string;\n pid?: number;\n lastExitCode?: number;\n}\n\nexport type ParsedDomain =\n | { readonly kind: 'system' }\n | { readonly kind: 'gui'; readonly uid: number };\n\nexport const parseDomain = (domain: string): ParsedDomain | undefined => {\n if (domain === 'system') return { kind: 'system' };\n const match = /^gui\\/(\\d+)$/.exec(domain);\n return match?.[1] === undefined ? undefined : { kind: 'gui', uid: Number(match[1]) };\n};\n\nexport const serviceTarget = (label: string, domain: LaunchdDomain): string => `${domain}/${label}`;\n\n/**\n * Where launchd expects the job's plist. ★ Derived, never declared: a daemon's plist outside\n * /Library/LaunchDaemons is not bootstrapped at boot, so a free-form path would be a job that\n * silently stops existing after the next restart.\n */\nexport const plistPathFor = (\n label: string,\n domain: ParsedDomain,\n home: string | undefined,\n): string => {\n if (domain.kind === 'system') return `/Library/LaunchDaemons/${label}.plist`;\n // ⛔ No `~` fallback: the provider may run as root, whose `~` is not the agent owner's home.\n if (home === undefined || !home.startsWith('/')) {\n throw new Error(`gui/${String(domain.uid)}: no home directory for that uid on this host`);\n }\n return `${home}/Library/LaunchAgents/${label}.plist`;\n};\n\n/** Plist keys the typed props own — `extraKeys` may not name them. `Program` is refused too. */\nexport const OWNED_KEYS: readonly string[] = [\n 'EnvironmentVariables',\n 'GroupName',\n 'KeepAlive',\n 'Label',\n 'Program',\n 'ProgramArguments',\n 'RunAtLoad',\n 'StandardErrorPath',\n 'StandardOutPath',\n 'StartCalendarInterval',\n 'StartInterval',\n 'ThrottleInterval',\n 'UserName',\n 'WorkingDirectory',\n];\n\nconst calendarDict = (entry: CalendarInterval): PlistDict => {\n const dict: Record<string, PlistValue> = {};\n if (entry.minute !== undefined) dict['Minute'] = entry.minute;\n if (entry.hour !== undefined) dict['Hour'] = entry.hour;\n if (entry.day !== undefined) dict['Day'] = entry.day;\n if (entry.weekday !== undefined) dict['Weekday'] = entry.weekday;\n if (entry.month !== undefined) dict['Month'] = entry.month;\n return dict;\n};\n\nconst keepAliveValue = (keepAlive: KeepAlive): PlistValue => {\n if (typeof keepAlive === 'boolean') return keepAlive;\n const dict: Record<string, PlistValue> = {};\n if (keepAlive.successfulExit !== undefined) dict['SuccessfulExit'] = keepAlive.successfulExit;\n if (keepAlive.crashed !== undefined) dict['Crashed'] = keepAlive.crashed;\n return dict;\n};\n\n/** The plist dict for a job. ⚠️ Assumes jobProblems() came back empty. */\nexport const jobPlistDict = (props: LaunchdJobProps): PlistDict => {\n const dict: Record<string, PlistValue> = { ...props.extraKeys };\n dict['Label'] = props.label;\n dict['ProgramArguments'] = [...props.programArguments];\n const env = props.environment ?? {};\n if (Object.keys(env).length > 0) dict['EnvironmentVariables'] = { ...env };\n if (props.runAtLoad !== undefined) dict['RunAtLoad'] = props.runAtLoad;\n if (props.keepAlive !== undefined) dict['KeepAlive'] = keepAliveValue(props.keepAlive);\n if (props.startInterval !== undefined) dict['StartInterval'] = props.startInterval;\n const calendar = props.startCalendarInterval;\n if (calendar !== undefined) {\n dict['StartCalendarInterval'] = Array.isArray(calendar)\n ? (calendar as readonly CalendarInterval[]).map(calendarDict)\n : calendarDict(calendar as CalendarInterval);\n }\n if (props.throttleInterval !== undefined) dict['ThrottleInterval'] = props.throttleInterval;\n if (props.standardOutPath !== undefined) dict['StandardOutPath'] = props.standardOutPath;\n if (props.standardErrorPath !== undefined) dict['StandardErrorPath'] = props.standardErrorPath;\n if (props.workingDirectory !== undefined) dict['WorkingDirectory'] = props.workingDirectory;\n if (props.userName !== undefined) dict['UserName'] = props.userName;\n if (props.groupName !== undefined) dict['GroupName'] = props.groupName;\n return dict;\n};\n\nexport const sha256Hex = (bytes: Uint8Array | string): string =>\n createHash('sha256').update(bytes).digest('hex');\n\n/** The rendered plist and its digest — the only thing LaunchdJob compares. */\nexport const renderJob = (\n props: LaunchdJobProps,\n): { text: string; bytes: Uint8Array; sha256: string } => {\n const text = renderPlist(jobPlistDict(props));\n const bytes = new TextEncoder().encode(text);\n return { bytes, sha256: sha256Hex(bytes), text };\n};\n",
18
+ "/**\n * An XML property list serializer — the subset launchd.plist(5) reads, and nothing else.\n *\n * ★ WHY NOT AN npm LIBRARY. Nothing in this workspace depends on one (checked 2026-09-21: no\n * `plist`, `xmlbuilder` or `@xmldom/xmldom` anywhere under node_modules), and the common one pulls\n * an XML DOM in for PARSING, which a provider that only RENDERS never does. The subset a launchd\n * job needs is five node types, so this is ~100 lines plus exhaustive tests (plist.test.ts), which\n * also round-trip every case through Apple's own `plutil` when the tests run on macOS.\n *\n * ⛔ THE OUTPUT MUST BE DETERMINISTIC. LaunchdJob diffs by the SHA-256 of these bytes, so two\n * renders of one declaration must be byte-identical: dict keys are sorted, indentation is fixed,\n * and nothing depends on insertion order. A non-deterministic render is a forever-`update`.\n * ⚠️ AND A CHANGE TO THIS FILE'S OUTPUT RESTARTS EVERY JOB. A new header, indent or escape changes\n * every digest, so the next deploy after the kit upgrade updates — restarts — every declared\n * job at once. Treat any byte change here as a breaking change and say so in its changeset.\n *\n * ⛔ REFUSE, NEVER COERCE. `undefined`, `null`, a fraction, NaN, a class instance or a character XML\n * 1.0 cannot carry is a thrown PlistError naming the path, not a silently dropped key. A dropped\n * `KeepAlive` is a daemon that never restarts, and nothing downstream would say why.\n */\n\n/** Every value launchd reads. ⚠️ No `<real>`, `<date>` or `<data>`: no launchd key needs one. */\nexport type PlistValue =\n | string\n | number\n | boolean\n | readonly PlistValue[]\n | { readonly [key: string]: PlistValue };\n\nexport type PlistDict = { readonly [key: string]: PlistValue };\n\nexport class PlistError extends Error {\n constructor(\n readonly path: string,\n detail: string,\n ) {\n super(`plist ${path === '' ? '(root)' : path}: ${detail}`);\n this.name = 'PlistError';\n }\n}\n\nconst HEADER =\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n' +\n '<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" ' +\n '\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\\n' +\n '<plist version=\"1.0\">\\n';\n\n/** ⚠️ A cycle, or a runaway generator, would otherwise recurse until the stack dies. */\nconst MAX_DEPTH = 32;\n\n/**\n * ⛔ CHARACTERS XML 1.0 CANNOT CARRY AT ALL — not even escaped (XML 1.0 §2.2 `Char`). C0 controls\n * other than tab, LF and CR; U+FFFE / U+FFFF; and an unpaired surrogate, which has no UTF-8 form.\n */\n// ★ The `u` flag makes a VALID surrogate pair one code point above U+FFFF, outside this class, so\n// only an unpaired half matches the \\uD800-\\uDFFF range.\n// oxlint-disable-next-line no-control-regex -- matching control characters is the point\nconst FORBIDDEN = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\uD800-\\uDFFF\\uFFFE\\uFFFF]/u;\n\n/**\n * Escape text content.\n * ⚠️ CR IS A CHARACTER REFERENCE, NOT A LITERAL. An XML parser normalises a literal CR (and CRLF)\n * to LF before the application sees it (XML 1.0 §2.11), so `a\\r\\nb` would come back as `a\\nb` and\n * the argument launchd passes would differ from the one declared. `&#13;` survives normalisation.\n */\nexport const escapeText = (text: string, path: string): string => {\n const bad = FORBIDDEN.exec(text);\n if (bad !== null) {\n const code = bad[0].codePointAt(0) ?? 0;\n throw new PlistError(\n path,\n `U+${code.toString(16).toUpperCase().padStart(4, '0')} cannot appear in XML 1.0`,\n );\n }\n return text\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\\r', '&#13;');\n};\n\nconst isPlainObject = (value: object): boolean => {\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n};\n\nconst renderValue = (value: unknown, path: string, depth: number, out: string[]): void => {\n const pad = '\\t'.repeat(depth);\n if (depth > MAX_DEPTH) throw new PlistError(path, `nested deeper than ${String(MAX_DEPTH)}`);\n if (typeof value === 'string') {\n out.push(`${pad}<string>${escapeText(value, path)}</string>`);\n return;\n }\n if (typeof value === 'boolean') {\n out.push(`${pad}<${value ? 'true' : 'false'}/>`);\n return;\n }\n if (typeof value === 'number') {\n // ⛔ launchd reads every numeric key as an integer; 1.5 would be a silently different job.\n if (!Number.isSafeInteger(value)) {\n throw new PlistError(path, `${String(value)} is not a safe integer (no <real> support)`);\n }\n out.push(`${pad}<integer>${String(value === 0 ? 0 : value)}</integer>`);\n return;\n }\n if (Array.isArray(value)) {\n if (value.length === 0) {\n out.push(`${pad}<array/>`);\n return;\n }\n out.push(`${pad}<array>`);\n for (const [index, item] of (value as readonly unknown[]).entries()) {\n renderValue(item, `${path}[${String(index)}]`, depth + 1, out);\n }\n out.push(`${pad}</array>`);\n return;\n }\n if (typeof value === 'object' && value !== null && isPlainObject(value)) {\n renderDict(value as Record<string, unknown>, path, depth, out);\n return;\n }\n const kind = value === null ? 'null' : typeof value;\n throw new PlistError(path, `${kind} has no plist form — omit the key instead`);\n};\n\nconst renderDict = (\n dict: Record<string, unknown>,\n path: string,\n depth: number,\n out: string[],\n): void => {\n const pad = '\\t'.repeat(depth);\n // ★ Code-unit order, not localeCompare: the same bytes on every machine and every locale.\n const keys = Object.keys(dict).sort();\n if (keys.length === 0) {\n out.push(`${pad}<dict/>`);\n return;\n }\n out.push(`${pad}<dict>`);\n for (const key of keys) {\n const child = path === '' ? key : `${path}.${key}`;\n out.push(`${pad}\\t<key>${escapeText(key, child)}</key>`);\n renderValue(dict[key], child, depth + 1, out);\n }\n out.push(`${pad}</dict>`);\n};\n\n/** Render a top-level dict as a complete XML plist document, ending in a newline. */\nexport const renderPlist = (dict: PlistDict): string => {\n if (typeof dict !== 'object' || dict === null || Array.isArray(dict) || !isPlainObject(dict)) {\n throw new PlistError('', 'the top level of a launchd plist must be a dict');\n }\n const out: string[] = [];\n renderDict(dict as Record<string, unknown>, '', 0, out);\n return `${HEADER}${out.join('\\n')}\\n</plist>\\n`;\n};\n",
19
+ "/**\n * A MANAGED REGION: a block a resource owns inside a file it does not own.\n *\n * ★ WHY THIS EXISTS. Four files on this estate are owned by something else and still need one\n * declared block: a packet-filter anchor line inside the OS vendor's own ruleset, a host table,\n * a generated Nix module, and an operator's ssh config. Declaring the WHOLE file would mean\n * taking it from its owner — which puts it back, or refuses its next activation. So the resource\n * owns the lines between two markers and promises that every other byte is untouched.\n * ⛔ THE REST OF THE FILE IS BYTE-IDENTICAL. Not \"semantically the same\", not re-indented, not\n * re-terminated: the bytes before the BEGIN line and after the END line are copied through. That\n * is the property region.test.ts asserts, because it is the whole reason the other owner tolerates\n * this.\n * ⛔ AMBIGUITY IS A REFUSAL, NEVER A GUESS. Two BEGIN markers with the same name, an END before its\n * BEGIN, or a BEGIN with no END all mean someone edited inside the block. Picking one would\n * silently delete their edit, so each is an Error naming the line.\n * ★ THE MARKER IS A COMMENT IN THE HOST FILE'S OWN LANGUAGE, which is why the comment token is a\n * prop: `#` covers the four consumers above, and a file whose comments are `//` or `;` says so.\n */\n\nexport type RegionSpec = {\n /** Distinguishes this block from any other managed block in the same file. */\n readonly name: string;\n /** The file's comment token. @default '#' */\n readonly comment?: string;\n};\n\nexport const DEFAULT_COMMENT = '#';\n\nconst tokens = (spec: RegionSpec) => {\n const comment = spec.comment ?? DEFAULT_COMMENT;\n return {\n begin: `${comment} BEGIN ${spec.name}`,\n end: `${comment} END ${spec.name}`,\n };\n};\n\nexport const regionProblems = (spec: RegionSpec): string[] => {\n const found: string[] = [];\n const comment = spec.comment ?? DEFAULT_COMMENT;\n if (!/^\\S{1,4}$/.test(comment) || comment.includes('\\u0000')) {\n found.push('region comment must be 1–4 non-space characters');\n }\n // ⛔ A marker must be one line and must be findable verbatim; a name with a newline would split\n // it, and leading or trailing space would not match what we later search for.\n if (!/^[A-Za-z0-9][A-Za-z0-9 ._:@/-]{0,79}$/.test(spec.name)) {\n found.push('region name must be 1–80 printable characters and start alphanumeric');\n }\n return found;\n};\n\nconst refuse = (message: string): Error => new Error(`managed region: ${message}`);\n\nconst duplicate = (what: string, at: readonly number[], line: string): Error =>\n refuse(\n `${what} for ${JSON.stringify(line)} (lines ${at.map((index) => index + 1).join(', ')}). ` +\n 'Remove the duplicate; this resource will not choose between them.',\n );\n\n/** Where the block sits: the line indices of its markers, or `undefined` when it is not there. */\nconst locate = (lines: readonly string[], spec: RegionSpec) => {\n const { begin, end } = tokens(spec);\n const begins = lines.flatMap((line, index) => (line === begin ? [index] : []));\n const ends = lines.flatMap((line, index) => (line === end ? [index] : []));\n if (begins.length > 1) throw duplicate('two BEGIN markers', begins, begin);\n if (ends.length > 1) throw duplicate('two END markers', ends, end);\n const [from] = begins;\n const [to] = ends;\n if (from === undefined && to === undefined) return undefined;\n if (from === undefined || to === undefined) {\n throw refuse(\n `${JSON.stringify(spec.name)} has a ${from === undefined ? 'END' : 'BEGIN'} marker and no ` +\n 'match. Repair the file by hand — a half-marked block means an edit inside it.',\n );\n }\n if (to < from) throw refuse(`${JSON.stringify(spec.name)} has its END marker before its BEGIN`);\n return { from, to };\n};\n\n/**\n * The file's bytes with the block set to `body`, appended at the end when it is not there yet.\n * ⚠️ A file that does not end in a newline gets one before the block is appended — the ONLY byte\n * this function adds outside its own block, and without it the last existing line and the BEGIN\n * marker would become one line.\n */\nexport const spliceRegion = (file: string, spec: RegionSpec, body: string): string => {\n const { begin, end } = tokens(spec);\n // ★ A body is stored as whole lines: exactly one newline after the last one, none doubled.\n const block = [begin, ...(body === '' ? [] : body.replace(/\\n$/, '').split('\\n')), end];\n const lines = file.split('\\n');\n const found = locate(lines, spec);\n if (found !== undefined) {\n return [...lines.slice(0, found.from), ...block, ...lines.slice(found.to + 1)].join('\\n');\n }\n const prefix = file === '' || file.endsWith('\\n') ? file : `${file}\\n`;\n return `${prefix}${block.join('\\n')}\\n`;\n};\n\n/** The block's body, or `undefined` when the file has no such block. */\nexport const readRegion = (file: string, spec: RegionSpec): string | undefined => {\n const lines = file.split('\\n');\n const found = locate(lines, spec);\n if (found === undefined) return undefined;\n const body = lines.slice(found.from + 1, found.to);\n return body.length === 0 ? '' : `${body.join('\\n')}\\n`;\n};\n\n/**\n * The file with the block and both markers removed — what a delete leaves behind.\n * ★ Byte-for-byte the file as it was before the block was appended, with one exception already\n * named on `spliceRegion`: a file that had no final newline when the block was added keeps the\n * newline that adding it required. Nothing else is added or dropped.\n */\nexport const removeRegion = (file: string, spec: RegionSpec): string => {\n const lines = file.split('\\n');\n const found = locate(lines, spec);\n if (found === undefined) return file;\n return [...lines.slice(0, found.from), ...lines.slice(found.to + 1)].join('\\n');\n};\n",
20
+ "/**\n * RemoteFile's props, attributes and validation — the pure half of the provider.\n *\n * ⛔ `content` IS NEVER A SECRET, for the same reason it is not on HostFile: Alchemy stores props\n * unencrypted, so every byte here sits in the state store and in every plan diff. A secret file\n * stays rendered at run time by a secret renderer; a stack declares at most the PATH it writes to.\n * secret-tripwire.ts refuses the obvious case and catches nothing subtle.\n * ★ TWO MODES, ONE RESOURCE. Without `region` this owns the whole file, exactly as HostFile does on\n * a Mac. With `region` it owns only the lines between two markers and promises every other byte\n * is untouched (region.ts) — which is the only way to declare a block inside a file another tool,\n * a vendor package or a person owns.\n */\nimport { pathProblems } from '../launchd/host-file-form.ts';\nimport { fileSecretProblems } from '../launchd/secret-tripwire.ts';\nimport { type RegionSpec, regionProblems } from './region.ts';\n\nexport interface RemoteFileProps {\n /** Absolute path on the host. ⚠️ The parent directory must exist — declare a HostDirectory. */\n path: string;\n /** UTF-8 text: the whole file, or the region's body. ⛔ Never a secret — see the file header. */\n content: string;\n /**\n * Own a block inside a file this resource does not own, instead of the whole file.\n * ⛔ In region mode `mode`, `owner` and `group` apply ONLY to a file this resource creates: an\n * existing file keeps the mode and ownership its owner gave it, because changing them would be\n * a second, undeclared claim on a file we only borrowed four lines of.\n */\n region?: RegionSpec;\n /**\n * Create the file when it is absent. Region mode only; whole-file mode always creates.\n * ⛔ Default `false`: a region declared against a path that does not exist is far more often a\n * typo (a wrong `/etc` path) than an intention, and creating it would hide the typo.\n */\n create?: boolean;\n /** Permission bits. @default 0o644 */\n mode?: number;\n /** User name or numeric uid. Omitted: whoever the runner writes as. */\n owner?: string | number;\n /** Group name or numeric gid. Omitted: the directory's default group. */\n group?: string | number;\n}\n\nexport interface RemoteFileAttributes {\n path: string;\n /** SHA-256 of the WHOLE file on disk, in both modes. */\n sha256: string;\n /** SHA-256 of the part this resource owns — in region mode, of the region's body alone. */\n contentSha256: string;\n /**\n * The block this resource owns, when it owns only a block.\n * ⛔ IT IS IN THE ATTRIBUTES ON PURPOSE. Alchemy's `delete` handler is given the attributes and\n * nothing else, and without the marker name a delete could not find the block — it could only\n * delete the whole file, which belongs to someone else.\n */\n region?: RegionSpec;\n mode: number;\n uid: number;\n gid: number;\n size: number;\n}\n\nexport const DEFAULT_MODE = 0o644;\n\nconst identityProblems = (name: string, value: string | number | undefined): string[] => {\n if (value === undefined) return [];\n if (typeof value === 'number') {\n return Number.isSafeInteger(value) && value >= 0\n ? []\n : [`${name} must be a non-negative integer id`];\n }\n return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\\d+$/.test(value)\n ? []\n : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];\n};\n\nexport const fileProblems = (props: RemoteFileProps): string[] => {\n const found = pathProblems(props.path);\n const mode = props.mode ?? DEFAULT_MODE;\n if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');\n found.push(...identityProblems('owner', props.owner));\n found.push(...identityProblems('group', props.group));\n found.push(...fileSecretProblems(props.content));\n if (props.region !== undefined) {\n found.push(...regionProblems(props.region));\n // ⛔ Say it at plan time rather than let a deploy silently ignore the prop: in region mode\n // these describe a file we do not own, and they are honoured only for a `create`.\n if (props.create !== true && (props.mode !== undefined || props.owner !== undefined)) {\n found.push(\n 'mode and owner apply only to a file this resource creates; drop them, or set create: true',\n );\n }\n } else if (props.create === true) {\n found.push('create is for region mode only; a whole-file declaration always creates the file');\n }\n return found;\n};\n",
21
+ "/**\n * What RemoteFile would write, worked out from what is on the host right now — the half of the\n * lifecycle that reads and decides, with no write in it.\n *\n * ⛔ IN REGION MODE THE LIVE FILE IS AN INPUT, not merely something to compare against: the declared\n * block is spliced into the bytes that are there, so every surrounding line survives verbatim.\n * That is why this cannot be a pure function of the props the way a whole-file render is.\n */\nimport { sha256Hex } from '../launchd/job-form.ts';\nimport type { FileStat, HostRunner, WriteOptions } from '../launchd/runner.ts';\nimport { type RegionSpec, readRegion, spliceRegion } from './region.ts';\nimport {\n DEFAULT_MODE,\n type RemoteFileAttributes,\n type RemoteFileProps,\n fileProblems,\n} from './remote-file-form.ts';\n\nexport const decoder = new TextDecoder();\nexport const encoder = new TextEncoder();\n\nexport const refuse = (path: string, message: string): Error =>\n new Error(`Remote.File ${path}: ${message}`);\n\nconst numericId = (value: string | number): number | undefined =>\n typeof value === 'number' ? value : /^\\d+$/.test(value) ? Number(value) : undefined;\n\nexport type Identity = { readonly uid?: number; readonly gid?: number };\n\n/** Validate, then resolve owner and group names to ids through the runner. */\nexport const identity = async (runner: HostRunner, props: RemoteFileProps): Promise<Identity> => {\n const found = fileProblems(props);\n if (found.length > 0) throw refuse(props.path, found.join('; '));\n let uid: number | undefined;\n let gid: number | undefined;\n if (props.owner !== undefined) {\n uid = numericId(props.owner) ?? (await runner.lookupUser(String(props.owner)))?.uid;\n if (uid === undefined) throw refuse(props.path, `no user ${String(props.owner)} on this host`);\n }\n if (props.group !== undefined) {\n gid = numericId(props.group) ?? (await runner.lookupGroup(String(props.group)));\n if (gid === undefined) throw refuse(props.path, `no group ${String(props.group)} on this host`);\n }\n return { ...(uid === undefined ? {} : { uid }), ...(gid === undefined ? {} : { gid }) };\n};\n\nexport const digestOf = (text: string): string => sha256Hex(encoder.encode(text));\n\n/** The digest of the part this resource owns, or `undefined` when that part is not there. */\nexport const ownedDigest = (\n file: string | undefined,\n region: RegionSpec | undefined,\n): string | undefined => {\n if (file === undefined) return undefined;\n if (region === undefined) return digestOf(file);\n const body = readRegion(file, region);\n return body === undefined ? undefined : digestOf(body);\n};\n\n/** The file as it is now, plus the digest of the part this resource owns. */\nexport const readFileAttributes = async (\n runner: HostRunner,\n path: string,\n region: RegionSpec | undefined,\n): Promise<RemoteFileAttributes | undefined> => {\n const stat = await runner.stat(path);\n if (stat === undefined) return undefined;\n const bytes = stat.kind === 'file' ? await runner.readFile(path) : undefined;\n const text = bytes === undefined ? undefined : decoder.decode(bytes);\n return {\n contentSha256: ownedDigest(text, region) ?? '',\n gid: stat.gid,\n mode: stat.mode,\n path,\n ...(region === undefined ? {} : { region }),\n sha256: bytes === undefined ? '' : sha256Hex(bytes),\n size: stat.size,\n uid: stat.uid,\n };\n};\n\nconst matches = (stat: FileStat | undefined, write: WriteOptions): boolean =>\n stat !== undefined &&\n stat.mode === write.mode &&\n (write.uid === undefined || stat.uid === write.uid) &&\n (write.gid === undefined || stat.gid === write.gid);\n\nexport type Plan = {\n /** The whole file's new bytes, in both modes. */\n readonly bytes: Uint8Array;\n readonly write: WriteOptions;\n /** The bytes on disk right now, or `undefined` when nothing is there. */\n readonly before: string | undefined;\n /** Nothing to do: the file already says what we declare, with the mode and owner we declare. */\n readonly converged: boolean;\n};\n\n/**\n * ⛔ A SYMLINK OR A DIRECTORY IS REFUSED, NOT REPLACED. The write is a rename(2), which over a\n * symlink replaces the LINK — quietly taking the path from whatever put it there (a config\n * manager's `/etc` entries are symlinks, and so is `/etc/resolv.conf` on most Linux hosts).\n */\nexport const planWrite = async (\n runner: HostRunner,\n props: RemoteFileProps,\n want: Identity,\n): Promise<Plan> => {\n const stat = await runner.stat(props.path);\n if (stat !== undefined && stat.kind !== 'file') {\n throw refuse(\n props.path,\n `is a ${stat.kind}; remove it from whatever owns it before declaring it here`,\n );\n }\n const current = stat === undefined ? undefined : await runner.readFile(props.path);\n const before = current === undefined ? undefined : decoder.decode(current);\n const declaredMode = props.mode ?? DEFAULT_MODE;\n if (props.region === undefined) {\n const write: WriteOptions = { mode: declaredMode, ...want };\n return {\n before,\n bytes: encoder.encode(props.content),\n converged: before === props.content && matches(stat, write),\n write,\n };\n }\n if (before === undefined) {\n if (props.create !== true) {\n throw refuse(\n props.path,\n 'does not exist, and a managed region does not create the file it lives in. Check the ' +\n 'path, declare the file itself, or pass create: true.',\n );\n }\n return {\n before,\n bytes: encoder.encode(spliceRegion('', props.region, props.content)),\n converged: false,\n write: { mode: declaredMode, ...want },\n };\n }\n const next = spliceRegion(before, props.region, props.content);\n /**\n * ⛔ THE FILE'S OWN MODE AND OWNER ARE COPIED BACK, never re-declared. We borrowed a block; the\n * rename would otherwise hand a file someone else owns a mode and an owner this stack chose,\n * which is a second claim nobody made.\n */\n return {\n before,\n bytes: encoder.encode(next),\n converged: next === before,\n write: { gid: stat?.gid ?? 0, mode: stat?.mode ?? declaredMode, uid: stat?.uid ?? 0 },\n };\n};\n",
22
+ "/**\n * RemoteFile's diff / reconcile / delete over a HostRunner, so the whole lifecycle runs against a\n * fake host in tests with no ssh and no real write. What it would write is worked out next door\n * (remote-file-plan.ts); this file is the part that decides to act.\n *\n * ⛔ IN REGION MODE, DRIFT MEANS THE REGION AND NOTHING ELSE. Every byte outside the markers belongs\n * to another owner, so a change there must never plan an update — otherwise the two owners fight\n * on every deploy and this resource loses the right to be in that file at all.\n * ⚠️ THE WRITE REPLACES THE INODE, in both modes. A process holding the old file keeps reading it\n * until it reopens, and a hard link to the path stops tracking. That is the price of never\n * leaving a torn file, and it is the trade the launchd family already makes.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport { sha256Hex } from '../launchd/job-form.ts';\nimport { type HostRunner, canActAsRoot } from '../launchd/runner.ts';\nimport { removeRegion } from './region.ts';\nimport {\n DEFAULT_MODE,\n type RemoteFileAttributes,\n type RemoteFileProps,\n} from './remote-file-form.ts';\nimport {\n decoder,\n digestOf,\n encoder,\n identity,\n ownedDigest,\n planWrite,\n readFileAttributes,\n refuse,\n} from './remote-file-plan.ts';\n\nexport { readFileAttributes } from './remote-file-plan.ts';\n\n/**\n * ★ A PLAN THAT WILL WRITE ASKS THE RUNNER FIRST (`checkWrite`, when it has one), so a runner's own\n * refusals fail the plan instead of arriving halfway through the apply.\n */\nexport const diffFile = async (\n runner: HostRunner,\n news: RemoteFileProps,\n output: RemoteFileAttributes,\n): Promise<Diff> => {\n const want = await identity(runner, news);\n if (news.path !== output.path) {\n // ★ Create-before-delete: two paths hold two files at once, so nothing forces deleteFirst.\n await runner.checkWrite?.(news.path, { mode: news.mode ?? DEFAULT_MODE, ...want });\n return { action: 'replace' };\n }\n const planned = await planWrite(runner, news, want);\n // ⛔ BOTH digests: the live one catches a hand edit, the STORED one catches a write that landed\n // and whose deploy then died before committing. Either alone reports `noop` over a wrong file.\n if (planned.converged && output.contentSha256 === digestOf(news.content)) {\n return { action: 'noop' };\n }\n await runner.checkWrite?.(news.path, planned.write);\n return { action: 'update' };\n};\n\n/**\n * `adopt` is whether adoption is on AND this apply is a create or an unfinished generation of our\n * own (ownership/adopt.ts). It lets such a generation take over what is already at the path — the\n * takeover the plan's probe would have allowed, had it run.\n * ★ IN REGION MODE THE FILE BEING THERE IS NOT A TAKEOVER: it belongs to someone else by\n * definition. What this resource claims is the block, so only a block already present with other\n * content is the thing adoption is asked about.\n */\nexport const reconcileFile = async (\n runner: HostRunner,\n props: RemoteFileProps,\n output?: RemoteFileAttributes,\n adopt = false,\n): Promise<RemoteFileAttributes> => {\n const want = await identity(runner, props);\n // ⛔ NO SILENT SUDO: handing a file to another user is root's call (chown(2)), so say so up front.\n if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {\n throw refuse(\n props.path,\n `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy through ` +\n 'a root destination or a privileged HostRunner. This provider never calls sudo itself.',\n );\n }\n const moved = output !== undefined && output.path !== props.path;\n const prior = moved ? undefined : output;\n const planned = await planWrite(runner, props, want);\n const live = ownedDigest(planned.before, props.region);\n const declared = digestOf(props.content);\n /**\n * ⛔ SOMETHING WE DO NOT OWN IS NEVER OVERWRITTEN. With no prior state the engine's probe has\n * already refused whatever it found — except where it never looked: a replace's new path, and a\n * create whose props still held an Output at plan time. A path typo there would otherwise\n * overwrite a system file. ★ With adoption on, a create takes it over, as the probe would have.\n */\n if (\n prior === undefined &&\n live !== undefined &&\n live !== declared &&\n !(adopt && output === undefined)\n ) {\n throw refuse(\n props.path,\n `${props.region === undefined ? 'already exists' : 'already carries that managed region'}` +\n ' and is not this resource. Remove it, or declare it as a new resource and deploy --adopt.',\n );\n }\n if (!planned.converged) await runner.writeFileAtomic(props.path, planned.bytes, planned.write);\n /**\n * ⚠️ READ BACK, never echo the declaration: a umask, an ACL or a runner that ignored `uid` shows\n * up here as a refusal instead of as a forever-`update`.\n */\n const after = await readFileAttributes(runner, props.path, props.region);\n if (\n after === undefined ||\n after.sha256 !== sha256Hex(planned.bytes) ||\n after.contentSha256 !== declared\n ) {\n /**\n * ⚠️ Roll back a CREATE only: left behind, the next plan's recovery read finds a file with no\n * state, reports it Unowned, and every later deploy demands --adopt for our own file.\n * ⛔ NEVER IN REGION MODE — the file was someone else's before this deploy and must survive it.\n */\n if (planned.before === undefined && props.region === undefined) {\n await runner.removeFile(props.path).catch(() => undefined);\n }\n throw refuse(\n props.path,\n 'the write returned but the file on disk does not match the declaration',\n );\n }\n // ★ Create-before-delete, as the replace would have been: the old path goes once the new one is\n // written and verified.\n if (moved && output !== undefined) await deleteFile(runner, output);\n return after;\n};\n\n/**\n * Whole-file mode removes the file. ⛔ REGION MODE REMOVES ONLY THE BLOCK, leaving every other byte\n * identical — removing the file would delete another owner's configuration. Idempotent both ways.\n */\nexport const deleteFile = async (\n runner: HostRunner,\n output: RemoteFileAttributes,\n): Promise<void> => {\n const stat = await runner.stat(output.path);\n if (stat === undefined) return;\n if (stat.kind !== 'file') throw refuse(output.path, `is now a ${stat.kind}; not touching it`);\n if (output.region === undefined) {\n await runner.removeFile(output.path);\n return;\n }\n const bytes = await runner.readFile(output.path);\n if (bytes === undefined) return;\n const before = decoder.decode(bytes);\n const next = removeRegion(before, output.region);\n if (next === before) return;\n await runner.writeFileAtomic(output.path, encoder.encode(next), {\n gid: stat.gid,\n mode: stat.mode,\n uid: stat.uid,\n });\n};\n",
23
+ "/**\n * `Systemd.Unit` and `Systemd.Timer` — one systemd unit file and systemd's view of it, declared.\n *\n * - create / update — write the unit file atomically, `daemon-reload`, enable or disable, then\n * start, stop, or RESTART ONLY when something actually changed (unit-lifecycle.ts settle).\n * - read — the unit file's SHA-256 plus `systemctl show` (load state, active state, enablement).\n * - diff — the rendered digest against the stored AND the on-disk one, the declared enablement and\n * running state against systemd's, and systemd's own `NeedDaemonReload`.\n * - replace — only when the unit's name or directory changes, and DELETE FIRST: two unit files for\n * one name cannot both be the one systemd reads.\n * - delete — stop, disable, remove the file, reload.\n *\n * ⛔ A DEPLOY NEVER MASS-RESTARTS — see the ⛔ at the top of unit-lifecycle.ts. It is the property\n * that lets this family reach a host holding a vault, a clock or a UPS handler.\n * ⛔ THE UNIT FILE IS NEVER A SECRET (unit-form.ts): it is 0644 and its props sit in state.\n * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt`. A unit already on the host reads as `Unowned`; adopting\n * one that already matches the declaration does not restart it.\n * ★ TWO RESOURCES, ONE LIFECYCLE. A timer is a unit whose file ends `.timer` and carries a\n * `[Timer]` section; splitting the TYPE rather than the code means a plan says which of the two\n * a row is, and a `.service` declared as a timer is a refusal instead of a puzzle.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { HostRunnerService } from '../launchd/runner.ts';\nimport type { SystemdUnitAttributes, SystemdUnitProps } from './unit-form.ts';\nimport { deleteHandler, diffHandler, readHandler, reconcileHandler } from './unit-handlers.ts';\n\nexport type { SystemdUnitAttributes, SystemdUnitProps, UnitSection } from './unit-form.ts';\n\nexport interface SystemdUnit extends Resource<\n 'Systemd.Unit',\n SystemdUnitProps,\n SystemdUnitAttributes\n> {}\n\nexport interface SystemdTimer extends Resource<\n 'Systemd.Timer',\n SystemdUnitProps,\n SystemdUnitAttributes\n> {}\n\nexport const SystemdUnit = Resource<SystemdUnit>('Systemd.Unit');\nexport const SystemdTimer = Resource<SystemdTimer>('Systemd.Timer');\n\nexport const SystemdUnitProvider = () =>\n Provider.effect(\n SystemdUnit,\n Effect.gen(function* () {\n const runner = yield* HostRunnerService;\n return SystemdUnit.Provider.of({\n /**\n * ⛔ systemd's unit list is not a list of things this stack owns — it is every vendor,\n * package and hand-written unit on the host. Returning them would invite a nuke to stop\n * services nothing here declared.\n */\n list: () => Effect.succeed([]),\n read: ({ olds, output }) => readHandler(runner, olds, output),\n diff: ({ instanceId, news, output }) => diffHandler(runner, instanceId, news, output),\n reconcile: (args) => reconcileHandler(runner, args),\n delete: ({ output }) => deleteHandler(runner, output),\n });\n }),\n );\n\nexport const SystemdTimerProvider = () =>\n Provider.effect(\n SystemdTimer,\n Effect.gen(function* () {\n const runner = yield* HostRunnerService;\n return SystemdTimer.Provider.of({\n list: () => Effect.succeed([]),\n read: ({ olds, output }) => readHandler(runner, olds, output),\n // ★ `'timer'` is the whole difference: the same lifecycle, refusing a name that is not one.\n diff: ({ instanceId, news, output }) =>\n diffHandler(runner, instanceId, news, output, 'timer'),\n reconcile: (args) => reconcileHandler(runner, args, 'timer'),\n delete: ({ output }) => deleteHandler(runner, output),\n });\n }),\n );\n",
24
+ "/**\n * The four provider handlers `Systemd.Unit` and `Systemd.Timer` share, as plain functions over a\n * HostRunner — so the two Resources differ only in their TYPE and the unit suffix they insist on.\n *\n * ★ WHY NOT ONE SHARED HANDLER OBJECT. `Provider.of` is typed per Resource, so an object built once\n * and passed to both would have to be widened or cast; functions with their arguments spelled out\n * keep both call sites fully checked and still say each rule once.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport type { Diff } from 'alchemy/Diff';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport { lift, resolvedString } from '../launchd/host-effect.ts';\nimport type { HostRunner } from '../launchd/runner.ts';\nimport { adoptsAtApply } from '../ownership/adopt.ts';\nimport { noteUnfinished } from '../ownership/resume.ts';\nimport type { SystemdUnitAttributes, SystemdUnitProps } from './unit-form.ts';\nimport { deleteUnit, diffUnit, readUnit, reconcileUnit } from './unit-lifecycle.ts';\n\nexport const readHandler = (\n runner: HostRunner,\n olds: SystemdUnitProps,\n output: SystemdUnitAttributes | undefined,\n): Effect.Effect<SystemdUnitAttributes | undefined, Error> =>\n lift(async () => {\n const found = await readUnit(runner, olds);\n if (found === undefined) return undefined;\n return output === undefined ? Unowned(found) : found;\n });\n\nexport const diffHandler = (\n runner: HostRunner,\n instanceId: string,\n news: Input<SystemdUnitProps>,\n output: SystemdUnitAttributes | undefined,\n expect?: string,\n): Effect.Effect<Diff | undefined, Error> => {\n // ★ An unfinished generation of our own: `--adopt` may resume it (ownership/resume.ts).\n if (output === undefined) return noteUnfinished(instanceId);\n if (isResolved(news)) return lift(() => diffUnit(runner, news, output, expect));\n /**\n * ⛔ A RENAME MUST BE SEEN EVEN WHILE OTHER PROPS ARE UNRESOLVED. The engine's default for a prop\n * change is `update`, and an update under a new name writes the new unit file and leaves the old\n * unit running with nothing in state to ever remove it. `content` is exactly what is unresolved\n * when a rendered config file's digest is templated into the same deploy.\n */\n const name = resolvedString(news, 'name');\n return Effect.succeed(\n name !== undefined && name !== output.name\n ? { action: 'replace' as const, deleteFirst: true }\n : undefined,\n );\n};\n\nexport const reconcileHandler = (\n runner: HostRunner,\n args: {\n fqn: string;\n instanceId: string;\n news: SystemdUnitProps;\n output: SystemdUnitAttributes | undefined;\n },\n expect?: string,\n): Effect.Effect<SystemdUnitAttributes, Error> =>\n Effect.flatMap(adoptsAtApply(args), (adopt) =>\n lift(() => reconcileUnit(runner, args.news, args.output, adopt, expect)),\n );\n\nexport const deleteHandler = (\n runner: HostRunner,\n output: SystemdUnitAttributes,\n): Effect.Effect<void, Error> => lift(() => deleteUnit(runner, output));\n",
25
+ "/**\n * Systemd.Unit / Systemd.Timer props, attributes, the unit-file renderer and the validation — the\n * pure half of the family.\n *\n * ⛔ THE DIRECTIVE SET IS SYSTEMD'S, NOT THIS FILE'S. There is no machine-readable schema of\n * `systemd.unit(5)` / `systemd.service(5)` directives to generate from, so this renders SECTIONS\n * AND LINES VERBATIM and claims to know nothing about what the keys mean. What it validates is\n * only what an INI file must be true of — a section name, a key shape, no embedded newline —\n * plus the two structural rules systemd's own output proves (see `unitProblems`). ★ That is the\n * honest boundary: invent no vendor shapes, and let `systemd-analyze verify` be the deep check a\n * host stack runs on the host.\n * ⛔ `content` IS NEVER A SECRET. Props sit unencrypted in Alchemy state and the unit file is\n * world-readable at 0644; a unit that needs a secret takes `EnvironmentFile=` pointing at a file\n * a secret renderer maintains.\n */\nimport { sha256Hex } from '../launchd/job-form.ts';\n\nexport type UnitSection = {\n /** `Unit`, `Service`, `Timer`, `Install`, … — verbatim, including its capitalisation. */\n readonly name: string;\n /**\n * The section's lines, in order. A repeated key is allowed and meaningful in systemd\n * (`ExecStartPre=` twice runs twice), so this is a list of pairs, never an object.\n */\n readonly lines: readonly (readonly [key: string, value: string])[];\n};\n\nexport type SystemdUnitProps = {\n /** Unit name WITH its suffix, e.g. `thing.service`. The unit file's name on disk. */\n name: string;\n /** The whole unit file, verbatim. Mutually exclusive with `sections`. */\n content?: string;\n /** The unit file as sections, rendered in the order given. Mutually exclusive with `content`. */\n sections?: readonly UnitSection[];\n /** Directory the unit file goes in. @default '/etc/systemd/system' */\n directory?: string;\n /** `systemctl enable` / `disable`. @default true */\n enabled?: boolean;\n /** `systemctl start` / `stop`. @default true */\n started?: boolean;\n /**\n * Digests of things this unit READS that live outside its unit file — a rendered config file's\n * `sha256` attribute, most often. A change here restarts the unit; nothing else does.\n * ⚠️ A hand edit of that file is NOT noticed: the digest in state still matches what the stack\n * declared, so the unit reads converged. That limit is the same one the Mac side records.\n */\n restartOn?: readonly string[];\n};\n\nexport type SystemdUnitAttributes = {\n name: string;\n unitPath: string;\n /** SHA-256 of the unit file this resource wrote. */\n unitSha256: string;\n /** SHA-256 of `restartOn`, so a config change is a diff. */\n configSha256: string;\n loadState: string;\n activeState: string;\n unitFileState?: string;\n enabled: boolean;\n active: boolean;\n};\n\n/** ★ systemd's admin drop-in directory, ahead of the vendor's in its search order (measured). */\nexport const DEFAULT_UNIT_DIRECTORY = '/etc/systemd/system';\n\n/**\n * Directories systemd looks in for a unit file it can enable.\n * ★ MEASURED 2026-09-22 with `systemd-analyze unit-paths` on Debian 13 / systemd 257, minus the\n * per-user and generator directories, which are not places a stack writes. `/lib/systemd/system`\n * is the same directory before the /usr merge, where the measured host has only the merged name.\n * ⛔ WHY IT IS ENFORCED. `systemctl enable <name>` looks the unit up BY NAME; a unit file outside\n * these directories is invisible to that lookup, so an enabled unit declared into, say, `/opt`\n * writes its file, reloads, and then fails at `enable` with the file already on disk.\n */\nexport const UNIT_SEARCH_DIRECTORIES = [\n '/etc/systemd/system',\n '/run/systemd/system',\n '/usr/local/lib/systemd/system',\n '/usr/lib/systemd/system',\n '/lib/systemd/system',\n] as const;\n\n/** ⛔ The unit file's mode and owner are systemd's rule, not a choice: readable, never writable. */\nexport const UNIT_WRITE = { gid: 0, mode: 0o644, uid: 0 } as const;\n\nexport const digestOf = (text: string): string => sha256Hex(new TextEncoder().encode(text));\n\n/** The digests of everything outside the unit file that should restart it, as one value. */\nexport const configDigest = (restartOn: readonly string[] | undefined): string =>\n digestOf((restartOn ?? []).join('\\n'));\n\n/** Sections → an INI unit file. ★ Keys and values pass through untouched; see the file header. */\nexport const renderUnit = (sections: readonly UnitSection[]): string =>\n `${sections\n .map((section) =>\n [`[${section.name}]`, ...section.lines.map(([key, value]) => `${key}=${value}`)].join('\\n'),\n )\n .join('\\n\\n')}\\n`;\n\nexport const unitText = (props: SystemdUnitProps): string =>\n props.content ?? renderUnit(props.sections ?? []);\n\nexport const unitPathFor = (props: SystemdUnitProps): string =>\n `${props.directory ?? DEFAULT_UNIT_DIRECTORY}/${props.name}`;\n\nconst NAME = /^[A-Za-z0-9][A-Za-z0-9_.@:-]*\\.([a-z]+)$/;\n/** systemd's own unit types. ★ Fixed vocabulary from `systemd.unit(5)`, not a guess at directives. */\nconst TYPES = new Set([\n 'automount',\n 'device',\n 'mount',\n 'path',\n 'scope',\n 'service',\n 'slice',\n 'socket',\n 'swap',\n 'target',\n 'timer',\n]);\n\nconst INSTALL_KEYS = new Set(['WantedBy', 'RequiredBy', 'UpsertedBy', 'Also', 'Alias']);\n\n/**\n * Every refusal at once, so one plan shows the whole list.\n * ⛔ THE TWO STRUCTURAL RULES, both proven by systemd's own output rather than assumed:\n * - `enabled: true` needs an `[Install]` section. `systemctl enable` has nothing to link without\n * one, and systemd's name for a unit file that has none is `UnitFileState=static` — measured on\n * a live host, where the journal daemon reads `static` and the ssh daemon reads `enabled`.\n * - A `.timer` needs a `[Timer]` section, and a timer's `[Install]` is what arms it at boot.\n */\nexport const unitProblems = (props: SystemdUnitProps, expect?: string): string[] => {\n const found: string[] = [];\n const type = NAME.exec(props.name)?.[1];\n if (type === undefined || !TYPES.has(type)) {\n found.push(\n `name must be <unit>.<type> with a systemd unit type, got ${JSON.stringify(props.name)}`,\n );\n } else if (expect !== undefined && type !== expect) {\n found.push(`this resource declares a .${expect} unit; ${props.name} is a .${type}`);\n }\n if ((props.content === undefined) === (props.sections === undefined)) {\n found.push('declare exactly one of content or sections');\n }\n const directory = props.directory ?? DEFAULT_UNIT_DIRECTORY;\n if (!directory.startsWith('/') || directory.endsWith('/') || directory.includes('/..')) {\n found.push('directory must be an absolute, normalised path with no trailing slash');\n }\n found.push(...textProblems(unitText(props)));\n if (props.enabled !== false && !UNIT_SEARCH_DIRECTORIES.includes(directory as never)) {\n found.push(\n `an enabled unit must live where systemd looks for it by name (${UNIT_SEARCH_DIRECTORIES.join(', ')}); ` +\n `${directory} is not one of them. Move it, or declare enabled: false.`,\n );\n }\n if (props.enabled !== false && !hasInstall(props)) {\n found.push(\n 'enabled units need an [Install] section with WantedBy/RequiredBy/Also/Alias; without one ' +\n 'systemd calls the unit \"static\" and `systemctl enable` has nothing to link. Add one, or ' +\n 'declare enabled: false and let something else pull it in.',\n );\n }\n if (type === 'timer' && !unitText(props).includes('[Timer]')) {\n found.push('a .timer needs a [Timer] section');\n }\n return found;\n};\n\nconst hasInstall = (props: SystemdUnitProps): boolean => {\n const text = unitText(props);\n if (!text.includes('[Install]')) return false;\n const after = text.slice(text.indexOf('[Install]'));\n return [...INSTALL_KEYS].some((key) => new RegExp(`^${key}=`, 'm').test(after));\n};\n\nconst textProblems = (text: string): string[] => {\n const found: string[] = [];\n if (text.trim() === '') found.push('the unit file is empty');\n if (text.includes('\\u0000')) found.push('the unit file contains NUL');\n if (!/^\\s*\\[[A-Za-z]+]/.test(text)) found.push('a unit file must begin with a [Section]');\n return found;\n};\n",
26
+ "/**\n * The `systemctl` subcommands the systemd family uses, over a HostRunner, plus the parser for the\n * one that returns data.\n *\n * ★ MEASURED 2026-09-22 on Debian 13, systemd 257 (257.13-1~deb13u1), read-only — nothing was\n * enabled, started or reloaded to write this:\n *\n * - `systemctl show -p <list> <unit>` EXITS 0 FOR A UNIT THAT DOES NOT EXIST, and answers with\n * the properties it would have had:\n * Id=hf-nonexistent-xyz.service LoadState=not-found\n * ActiveState=inactive SubState=dead\n * FragmentPath= UnitFileState=\n * ⛔ SO THE EXIT CODE IS NOT THE ANSWER — `LoadState` is. A provider that read exit 0 as \"the\n * unit is there\" would adopt a unit that does not exist and then never write its file.\n * - The properties come back in systemd's own order, not the order asked for, so the output is a\n * map and never positional.\n * - `systemctl is-enabled <unknown>` exits **4** and prints `not-found`; `is-active <unknown>`\n * exits **4** and prints `inactive`. Both are answers, not errors.\n * - `UnitFileState=static` is exactly systemd's name for a unit file with no `[Install]` section\n * (measured: the journal daemon is `static`, the ssh daemon is `enabled`).\n * - `/etc/systemd/system` is `root:root 0755` — the admin drop-in directory, ahead of the\n * vendor's `/usr/lib/systemd/system` in systemd's own search order.\n * ⚠️ REASONED, NOT MEASURED: the write subcommands. `daemon-reload`, `enable`, `disable`, `start`,\n * `stop` and `restart` were never run for this file, because running them would change a live\n * host. Each is therefore checked the same way: exit 0 or an Error carrying systemd's stderr, and\n * the state is read back with `show` afterwards rather than assumed.\n * ⛔ NEVER PUT `show` STDOUT WITH NO PROPERTY FILTER IN AN ERROR: the unfiltered property set\n * carries the unit's whole environment block.\n */\nimport type { HostRunner } from '../launchd/runner.ts';\n\nexport const SYSTEMCTL = 'systemctl';\n\n/** The properties every read asks for. ⛔ Never `Environment` — see the header. */\nexport const SHOW_PROPERTIES = [\n 'Id',\n 'LoadState',\n 'ActiveState',\n 'SubState',\n 'UnitFileState',\n 'FragmentPath',\n 'NeedDaemonReload',\n] as const;\n\nexport class SystemctlError extends Error {\n constructor(command: string, exitCode: number, stderr: string) {\n super(`systemctl ${command} -> ${String(exitCode)}: ${stderr.trim().slice(0, 300)}`);\n this.name = 'SystemctlError';\n }\n}\n\nexport type UnitStatus = {\n /** `loaded`, `not-found`, `masked`, `error`, `bad-setting`. ⛔ The authority on existence. */\n readonly loadState: string;\n /** systemd knows this unit: it has a unit file or is loaded. */\n readonly known: boolean;\n readonly activeState: string;\n readonly subState?: string;\n /** `enabled`, `disabled`, `static`, `masked`, … Empty for a unit with no unit file. */\n readonly unitFileState?: string;\n /** The unit file systemd would read. Empty when there is none. */\n readonly fragmentPath?: string;\n /** systemd's own view that its on-disk unit files are newer than what it has loaded. */\n readonly needDaemonReload: boolean;\n};\n\n/** `key=value` lines into a map. ⚠️ First wins: systemd never repeats a key, a truncated read might. */\nexport const parseShow = (stdout: string): Map<string, string> => {\n const fields = new Map<string, string>();\n for (const line of stdout.split('\\n')) {\n const at = line.indexOf('=');\n if (at <= 0) continue;\n const key = line.slice(0, at);\n if (!fields.has(key)) fields.set(key, line.slice(at + 1));\n }\n return fields;\n};\n\nexport const statusOf = (stdout: string): UnitStatus => {\n const fields = parseShow(stdout);\n const loadState = fields.get('LoadState') ?? 'not-found';\n const unitFileState = fields.get('UnitFileState') ?? '';\n const fragmentPath = fields.get('FragmentPath') ?? '';\n const subState = fields.get('SubState') ?? '';\n return {\n activeState: fields.get('ActiveState') ?? 'inactive',\n // ⛔ MEASURED: `show` answers for a unit that does not exist, so \"known\" is LoadState plus\n // whether systemd found a unit file at all — never the exit code.\n known: loadState !== 'not-found' || unitFileState !== '',\n loadState,\n needDaemonReload: fields.get('NeedDaemonReload') === 'yes',\n ...(subState === '' ? {} : { subState }),\n ...(unitFileState === '' ? {} : { unitFileState }),\n ...(fragmentPath === '' ? {} : { fragmentPath }),\n };\n};\n\nexport const showUnit = async (runner: HostRunner, name: string): Promise<UnitStatus> => {\n const result = await runner.exec([\n SYSTEMCTL,\n 'show',\n '--no-pager',\n '-p',\n SHOW_PROPERTIES.join(','),\n '--',\n name,\n ]);\n // ⚠️ A non-zero `show` means systemctl itself failed (no bus, a malformed name) — not a missing\n // unit, which exits 0. Failing closed here is what keeps a broken connection from reading as\n // \"the unit is gone\", which would make the next deploy write its file and restart it.\n if (result.exitCode !== 0)\n throw new SystemctlError(`show ${name}`, result.exitCode, result.stderr);\n return statusOf(result.stdout);\n};\n\n/** One write subcommand, checked. ⚠️ stderr only; `show` output never reaches an error. */\nconst run = async (runner: HostRunner, args: readonly string[]): Promise<void> => {\n const result = await runner.exec([SYSTEMCTL, ...args]);\n if (result.exitCode !== 0) {\n throw new SystemctlError(args.join(' '), result.exitCode, result.stderr);\n }\n};\n\n/** ⛔ After every unit-file write or removal, and before enable/start reads it. */\nexport const daemonReload = (runner: HostRunner): Promise<void> => run(runner, ['daemon-reload']);\n\nexport const enableUnit = (runner: HostRunner, name: string): Promise<void> =>\n run(runner, ['enable', '--', name]);\n\nexport const disableUnit = (runner: HostRunner, name: string): Promise<void> =>\n run(runner, ['disable', '--', name]);\n\nexport const startUnit = (runner: HostRunner, name: string): Promise<void> =>\n run(runner, ['start', '--', name]);\n\nexport const stopUnit = (runner: HostRunner, name: string): Promise<void> =>\n run(runner, ['stop', '--', name]);\n\n/**\n * ⛔ `restart`, NOT `reload-or-restart`. The reloading form asks the unit to re-read its config in\n * place when it declares `ExecReload=`, which means \"did the change take effect?\" has two\n * different answers depending on the unit — and a deploy cannot report a half-applied change.\n */\nexport const restartUnit = (runner: HostRunner, name: string): Promise<void> =>\n run(runner, ['restart', '--', name]);\n",
27
+ "/**\n * The step that decides whether anything on the host actually moves: enablement, running state and\n * — only for a real change — a restart. Split from unit-lifecycle.ts because it is the rule the\n * whole family exists to keep, and it should be readable on its own.\n *\n * ⛔ THE RESTART CONDITION IS THE WHOLE CONTRACT. `changed` is set by the caller from the four\n * things that earn one (unit-lifecycle.ts reconcileUnit); `configChanged` is a digest the\n * DECLARATION itself listed. Nothing else restarts a unit — not a re-run, not an unrelated\n * resource in the same deploy, not `--adopt`.\n */\nimport type { HostRunner } from '../launchd/runner.ts';\nimport {\n type SystemdUnitAttributes,\n type SystemdUnitProps,\n configDigest,\n unitPathFor,\n} from './unit-form.ts';\nimport {\n type UnitStatus,\n daemonReload,\n disableUnit,\n enableUnit,\n restartUnit,\n showUnit,\n startUnit,\n stopUnit,\n} from './systemctl.ts';\n\nexport const refuse = (name: string, message: string): Error =>\n new Error(`Systemd.Unit ${name}: ${message}`);\n\nexport const attributesOf = (\n props: SystemdUnitProps,\n unitSha256: string,\n status: UnitStatus,\n): SystemdUnitAttributes => ({\n active: status.activeState === 'active' || status.activeState === 'activating',\n activeState: status.activeState,\n configSha256: configDigest(props.restartOn),\n enabled: status.unitFileState === 'enabled' || status.unitFileState === 'enabled-runtime',\n loadState: status.loadState,\n name: props.name,\n unitPath: unitPathFor(props),\n unitSha256,\n ...(status.unitFileState === undefined ? {} : { unitFileState: status.unitFileState }),\n});\n\nexport type Settle = {\n readonly before: Uint8Array | undefined;\n /** The unit file or systemd's view of it changed in a way that earns a restart. */\n readonly changed: boolean;\n readonly configChanged: boolean;\n readonly desired: string;\n readonly path: string;\n readonly status: UnitStatus;\n};\n\n/**\n * Bring enablement and running state to the declaration, restarting ONLY for a real change.\n * ⛔ THE RESTART CONDITION IS THE WHOLE CONTRACT: the unit file changed, or a digest the caller\n * listed changed. Nothing else — not a re-run, not an unrelated resource, not `--adopt`.\n */\nexport const settle = async (\n runner: HostRunner,\n props: SystemdUnitProps,\n step: Settle,\n): Promise<SystemdUnitAttributes> => {\n const wantEnabled = props.enabled !== false;\n const wantStarted = props.started !== false;\n const isEnabled =\n step.status.unitFileState === 'enabled' || step.status.unitFileState === 'enabled-runtime';\n const undo = async (detail: string): Promise<never> => {\n // ⚠️ A create that never came up leaves nothing behind; an update keeps the new file and the\n // old stored digest, so the next deploy retries.\n if (step.before === undefined) {\n await runner.removeFile(step.path).catch(() => undefined);\n await daemonReload(runner).catch(() => undefined);\n }\n throw refuse(props.name, `${detail} The unit is NOT running.`);\n };\n try {\n if (wantEnabled && (!isEnabled || step.changed)) await enableUnit(runner, props.name);\n if (!wantEnabled && isEnabled) await disableUnit(runner, props.name);\n const active = step.status.activeState === 'active' || step.status.activeState === 'activating';\n if (!wantStarted) {\n if (active) await stopUnit(runner, props.name);\n } else if (!active) await startUnit(runner, props.name);\n else if (step.changed || step.configChanged) await restartUnit(runner, props.name);\n } catch (cause) {\n return undo(cause instanceof Error ? cause.message : String(cause));\n }\n /**\n * ⚠️ READ BACK, never echo the declaration. Returning `enabled: wantEnabled` because we called\n * `enable` would put a CLAIM in state instead of a reading, and the next plan would compare\n * the declaration against itself — so a link systemd did not actually make would never show up\n * as drift, in either direction.\n */\n const after = await showUnit(runner, props.name);\n if (wantStarted && after.activeState !== 'active' && after.activeState !== 'activating') {\n return undo(`systemctl reported success but the unit is ${after.activeState}.`);\n }\n const settled = attributesOf(props, step.desired, after);\n if (wantEnabled && !settled.enabled) {\n return undo(\n `systemctl enable reported success but systemd calls the unit ` +\n `${JSON.stringify(after.unitFileState ?? '')}, not enabled.`,\n );\n }\n return settled;\n};\n",
28
+ "/**\n * Systemd.Unit's read / diff / reconcile / delete as plain async functions over a HostRunner, so\n * the whole lifecycle runs against a fake host with no real systemd.\n *\n * ⛔ WHAT A DEPLOY DOES NOT DO — the rule this family was built around. It NEVER mass-restarts.\n * A unit is restarted only when THIS resource's unit file changed, or when a digest the\n * declaration itself listed in `restartOn` changed. A unit that is already running the content\n * we declare is left strictly alone: no restart, no reload, not even a `start`. An estate rebuild\n * must be able to reach a host that holds a vault, a UPS handler or a clock without bouncing\n * them, and \"the deploy ran\" must never be a reason anything went down.\n * ★ THE ORDER IS WRITE, RELOAD, ENABLE, THEN START OR RESTART. systemd reads the unit file at\n * `daemon-reload`, so writing it while the old unit runs changes nothing yet, and a write that\n * fails leaves the old unit running rather than leaving nothing running.\n * ⚠️ A FAILED START LEAVES THE UNIT DOWN. On an update the new unit file stays and state keeps the\n * previous digest, so the next deploy retries. On a CREATE the file this deploy wrote is removed\n * and systemd reloaded again: left behind, the next plan's recovery read finds a unit with no\n * state, reports it `Unowned`, and every later deploy demands `--adopt` for our own file.\n * ⛔ A MASKED UNIT IS SOMEONE'S DECISION, NOT DRIFT. `systemctl mask` survives reboots and makes\n * start fail; unmasking here would silently overrule whoever masked it.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport { type HostRunner, canActAsRoot } from '../launchd/runner.ts';\nimport {\n type SystemdUnitAttributes,\n type SystemdUnitProps,\n UNIT_WRITE,\n configDigest,\n digestOf,\n unitPathFor,\n unitProblems,\n unitText,\n} from './unit-form.ts';\nimport { type UnitStatus, daemonReload, disableUnit, showUnit, stopUnit } from './systemctl.ts';\nimport { attributesOf, refuse, settle } from './unit-settle.ts';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** Throw every refusal at once, so one plan shows the whole list. */\nexport const assertValid = (props: SystemdUnitProps, expect?: string): void => {\n const found = unitProblems(props, expect);\n if (found.length > 0) throw refuse(props.name, found.join('; '));\n};\n\n/** What is on the host now: the unit file's digest and systemd's view. `undefined` when neither. */\nexport const readUnit = async (\n runner: HostRunner,\n props: SystemdUnitProps,\n): Promise<SystemdUnitAttributes | undefined> => {\n const bytes = await runner.readFile(unitPathFor(props));\n const status = await showUnit(runner, props.name);\n if (bytes === undefined && !status.known) return undefined;\n return attributesOf(props, bytes === undefined ? '' : digestOf(decoder.decode(bytes)), status);\n};\n\n/** ⛔ Writing a unit file and driving systemd are root's, so refuse up front rather than mid-apply. */\nexport const assertMayWrite = (runner: HostRunner, name: string): void => {\n if (canActAsRoot(runner)) return;\n throw refuse(\n name,\n 'a systemd unit is root-owned and systemctl needs root. Point the runner at a destination ' +\n 'whose ssh user is root, or provide a privileged HostRunner. This provider never calls sudo.',\n );\n};\n\nconst assertUsable = (props: SystemdUnitProps, status: UnitStatus): void => {\n if (status.loadState === 'masked' || status.unitFileState === 'masked') {\n throw refuse(\n props.name,\n 'is masked. If that is stale, `systemctl unmask` it deliberately, then redeploy. Nothing ' +\n 'was written.',\n );\n }\n};\n\nexport const diffUnit = async (\n runner: HostRunner,\n news: SystemdUnitProps,\n output: SystemdUnitAttributes,\n expect?: string,\n): Promise<Diff> => {\n assertValid(news, expect);\n // ★ The unit's name or its directory is its identity on the host: a change is a replace, and\n // delete-first, because two unit files for one name cannot both be the one systemd reads.\n if (news.name !== output.name || unitPathFor(news) !== output.unitPath) {\n assertMayWrite(runner, news.name);\n return { action: 'replace', deleteFirst: true };\n }\n const desired = digestOf(unitText(news));\n const update = async (): Promise<Diff> => {\n assertMayWrite(runner, news.name);\n await runner.checkWrite?.(unitPathFor(news), UNIT_WRITE);\n return { action: 'update' };\n };\n // ⛔ BOTH digests: the stored one catches a start that failed after the write landed, the live\n // one catches a hand edit. Either alone reports `noop` over a unit that is not what we say.\n if (desired !== output.unitSha256) return update();\n if (configDigest(news.restartOn) !== output.configSha256) return update();\n const live = await readUnit(runner, news);\n if (live === undefined || live.unitSha256 !== desired) return update();\n if (live.enabled !== (news.enabled !== false)) return update();\n if (live.active !== (news.started !== false)) return update();\n // ⚠️ systemd's own word for \"the file on disk is newer than what I loaded\" — a write that landed\n // without its reload. It is drift that neither digest can see.\n const status = await showUnit(runner, news.name);\n return status.needDaemonReload ? update() : { action: 'noop' };\n};\n\n/**\n * `adopt` is whether adoption is on AND this apply is a create or an unfinished generation of our\n * own (ownership/adopt.ts): it lets such a generation take over a unit already on the host.\n * ★ AN ADOPTED UNIT THAT ALREADY MATCHES IS NOT RESTARTED. That is the whole point of adopting one:\n * the estate's live services come under declaration without a single bounce.\n */\nexport const reconcileUnit = async (\n runner: HostRunner,\n props: SystemdUnitProps,\n output: SystemdUnitAttributes | undefined,\n adopt = false,\n expect?: string,\n): Promise<SystemdUnitAttributes> => {\n assertValid(props, expect);\n assertMayWrite(runner, props.name);\n const path = unitPathFor(props);\n const status = await showUnit(runner, props.name);\n assertUsable(props, status);\n const text = unitText(props);\n const desired = digestOf(text);\n const before = await runner.readFile(path);\n const liveSha = before === undefined ? undefined : digestOf(decoder.decode(before));\n const moved = output !== undefined && (output.name !== props.name || output.unitPath !== path);\n const prior = moved ? undefined : output;\n /**\n * ⛔ A UNIT THIS RESOURCE DOES NOT OWN IS NEVER OVERWRITTEN. Where the engine's probe never\n * looked — a replace's new name, a create whose props were still an Output at plan time — a\n * unit file already there with other content belongs to someone else, and writing ours over it\n * would take their service. ★ A byte-identical file is this declaration's own leftover.\n */\n if (\n prior === undefined &&\n !(adopt && output === undefined) &&\n liveSha !== undefined &&\n liveSha !== desired\n ) {\n throw refuse(\n props.name,\n `${path} is already on this host and is not this resource. Remove it, or declare it as a ` +\n 'new resource and deploy with --adopt.',\n );\n }\n if (moved && output !== undefined) await deleteUnit(runner, output);\n const wrote = liveSha !== desired;\n /**\n * ⛔ THE THREE THINGS THAT EARN A RESTART, and nothing else:\n * - the unit file's bytes changed (`wrote`);\n * - state holds an older digest than the file does — a previous deploy's write landed and its\n * reload or restart never ran, so systemd is still running the old content;\n * - systemd itself says its loaded unit is older than the file (`NeedDaemonReload`, measured);\n * - a digest the declaration listed in `restartOn` changed.\n * ★ An adopted, converged unit hits none of them, which is why adoption never bounces a service.\n */\n const stale = prior !== undefined && prior.unitSha256 !== desired;\n const configChanged = prior !== undefined && prior.configSha256 !== configDigest(props.restartOn);\n if (wrote) await runner.writeFileAtomic(path, encoder.encode(text), UNIT_WRITE);\n if (wrote || status.needDaemonReload) await daemonReload(runner);\n const changed = wrote || stale || status.needDaemonReload;\n return settle(runner, props, { before, changed, configChanged, desired, path, status });\n};\n\n/**\n * Stop, disable, remove the unit file, reload. Idempotent.\n * ⛔ DELETING THE DECLARATION STOPS THE SERVICE. That is what removing it from the stack means, and\n * it is why a unit that must outlive its declaration is adopted rather than deleted.\n */\nexport const deleteUnit = async (\n runner: HostRunner,\n output: SystemdUnitAttributes,\n): Promise<void> => {\n assertMayWrite(runner, output.name);\n const status = await showUnit(runner, output.name);\n if (status.activeState === 'active' || status.activeState === 'activating') {\n await stopUnit(runner, output.name);\n }\n if (status.unitFileState === 'enabled' || status.unitFileState === 'enabled-runtime') {\n await disableUnit(runner, output.name);\n }\n const stat = await runner.stat(output.unitPath);\n if (stat !== undefined) {\n if (stat.kind !== 'file') throw refuse(output.name, `${output.unitPath} is a ${stat.kind}`);\n await runner.removeFile(output.unitPath);\n }\n await daemonReload(runner);\n};\n",
29
+ "/**\n * `sshRunner()` — the estate's Linux hosts behind the SAME HostRunner seam the launchd family\n * already drives a Mac through (launchd/runner.ts). Every provider in this directory reaches its\n * host only through it, so the whole Linux lifecycle runs against a fake in tests.\n *\n * ★ WHY THE SAME SEAM AND NOT A SECOND ONE. HostRunner is nine promise-returning methods with no\n * macOS in them; what was Mac-specific was launchctl and the plist, not the seam. Reusing it\n * means HostFile, the ownership rules and `checkWrite` already work on Linux, and a stack can\n * hand the same runner to a file, a directory and a unit.\n * ⛔ NO PASSWORDS AND NO SILENT SUDO. Every connection is `BatchMode=yes` (ssh-command.ts), and\n * `privileged` is false: this runner never calls sudo. A root-owned path therefore needs a\n * destination whose ssh user IS root — which is how the estate already reaches its hypervisors —\n * or a privileged runner of the caller's own. See docs/linux-host.md; the Linux twin of\n * sudoRunner is deliberately a later, separate change with its own allowlist.\n * ⛔ FAIL CLOSED. A connection that drops, a host key that changed, a shell that cannot parse the\n * script: none of them produces a \"not found\". Without the framing marker the result is an\n * Error, never data (ssh-command.ts parseFramed).\n * ★ EXERCISED READ-ONLY AGAINST A LIVE HOST, 2026-09-22 (Debian 13, systemd 257, ssh user uid\n * 1001): `stat` of a file, of a missing path and of a symlink (reported `symlink`, so the lstat\n * contract holds over the wire); `readFile` of a file and of a missing path; `getent` user and\n * group; a failing `exec`; and an argument of `a b '$HOME` + backtick + ` ;rm` arriving verbatim,\n * which is the quoting proven rather than argued. Nothing was written.\n * ⚠️ ONE CONNECTION PER CALL. No multiplexing is configured here: a deploy over a slow link pays a\n * handshake per read. Pass `sshArgs: ['-o', 'ControlMaster=auto', …]` to opt into your own\n * ControlPersist socket — the kit does not create one behind your back, because a stale socket\n * outliving the deploy is a surprise nobody declared.\n */\nimport { spawn } from 'node:child_process';\nimport { randomBytes } from 'node:crypto';\nimport { groupQuery, parseGroup, parseUser, userQuery } from '../launchd/host-lookup.ts';\nimport type { ExecResult, HostRunner } from '../launchd/runner.ts';\nimport { ABSENT, frameScript, parseFramed, sshArgv } from './ssh-command.ts';\nimport {\n execScript,\n parseStat,\n readScript,\n removeScript,\n statScript,\n writeScript,\n} from './ssh-scripts.ts';\n\nexport type SshRunnerOptions = {\n /** The destination exactly as the operator's own ssh config names it (`Host` block or user@host). */\n readonly host: string;\n /** Extra ssh options, after the fixed safety set and before the destination. */\n readonly sshArgs?: readonly string[];\n /** Kill a connection after this long. @default 120_000 */\n readonly execTimeoutMs?: number;\n /** ssh's own `ConnectTimeout`. @default 10 */\n readonly connectTimeoutSec?: number;\n};\n\ntype Raw = { exitCode: number; stdout: string; stderr: string; signal: string | null };\n\nconst spawnSsh = (argv: readonly string[], timeoutMs: number, stdin?: Uint8Array): Promise<Raw> =>\n new Promise((resolve, reject) => {\n const [program, ...args] = argv as [string, ...string[]];\n const child = spawn(program, args, {\n shell: false,\n stdio: [stdin === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'],\n timeout: timeoutMs,\n });\n const out: Buffer[] = [];\n const err: Buffer[] = [];\n child.stdout?.on('data', (chunk: Buffer) => out.push(chunk));\n child.stderr?.on('data', (chunk: Buffer) => err.push(chunk));\n child.on('error', (cause) => {\n reject(new Error(`ssh: ${cause.message}`));\n });\n if (stdin !== undefined && child.stdin !== null) {\n // ⚠️ EPIPE is normal here: the remote `cat` can die before we finish writing, and the real\n // failure is the exit code we are about to read — not a crash in this process.\n child.stdin.on('error', () => undefined);\n child.stdin.end(stdin);\n }\n child.on('close', (exitCode, signal) => {\n resolve({\n exitCode: exitCode ?? -1,\n signal,\n stderr: Buffer.concat(err).toString('utf8'),\n stdout: Buffer.concat(out).toString('utf8'),\n });\n });\n });\n\n/** What the process on the far end did — or an Error, when we cannot say that it ran at all. */\nexport const runRemote = async (\n options: Required<Pick<SshRunnerOptions, 'host' | 'connectTimeoutSec' | 'execTimeoutMs'>> &\n Pick<SshRunnerOptions, 'sshArgs'>,\n nonce: string,\n script: string,\n stdin?: Uint8Array,\n): Promise<ExecResult> => {\n const argv = sshArgv(options.host, frameScript(script, nonce), {\n connectTimeoutSec: options.connectTimeoutSec,\n ...(options.sshArgs === undefined ? {} : { sshArgs: options.sshArgs }),\n });\n const raw = await spawnSsh(argv, options.execTimeoutMs, stdin);\n const framed = parseFramed(raw.stderr, nonce);\n if (framed === undefined) {\n // ⛔ The command may or may not have run. Saying \"absent\" here is how a dropped link becomes a\n // deleted config, so it is an error with ssh's own words (truncated, never the stdout).\n throw new Error(\n `ssh ${options.host}: the remote command did not report a status (ssh exit ` +\n `${String(raw.exitCode)}${raw.signal === null ? '' : `, killed by ${raw.signal}`}): ` +\n `${raw.stderr.trim().slice(0, 300)}`,\n );\n }\n return { exitCode: framed.exitCode, stderr: framed.stderr, stdout: raw.stdout };\n};\n\nconst decodeBase64 = (text: string): Uint8Array =>\n new Uint8Array(Buffer.from(text.replaceAll('\\n', ''), 'base64'));\n\nconst failed = (host: string, what: string, result: ExecResult): Error =>\n new Error(\n `ssh ${host}: ${what} -> ${String(result.exitCode)}: ${result.stderr.trim().slice(0, 300)}`,\n );\n\n/**\n * Connect once to learn who we are, then hand back the runner.\n * ⛔ THE PROBE IS THE FAIL-CLOSED GATE. `effectiveUid()` is synchronous on HostRunner, so the uid\n * must be known before the first provider call; and `uname -s` refuses a host whose `stat -c`,\n * `base64` and `mv -f` are not the Linux spellings this runner sends. A host that cannot answer\n * both is a thrown Error at construction, not a stack that half-deploys.\n */\nexport const sshRunner = async (options: SshRunnerOptions): Promise<HostRunner> => {\n const settings = {\n connectTimeoutSec: options.connectTimeoutSec ?? 10,\n execTimeoutMs: options.execTimeoutMs ?? 120_000,\n host: options.host,\n ...(options.sshArgs === undefined ? {} : { sshArgs: options.sshArgs }),\n };\n const nonce = randomBytes(8).toString('hex');\n const run = (script: string, stdin?: Uint8Array) => runRemote(settings, nonce, script, stdin);\n const probe = await run('uname -s; id -u');\n if (probe.exitCode !== 0) throw failed(options.host, 'uname -s; id -u', probe);\n const [system, uid] = probe.stdout.trim().split('\\n');\n if (system !== 'Linux' || uid === undefined || !/^\\d+$/.test(uid)) {\n throw new Error(\n `ssh ${options.host}: expected a Linux host, got ${JSON.stringify(probe.stdout.trim())}. ` +\n 'sshRunner() sends GNU/BusyBox argv (stat -c, base64, mv -f); for a Mac use localRunner().',\n );\n }\n const euid = Number(uid);\n return {\n effectiveUid: () => euid,\n exec: (argv) => run(execScript(argv)),\n lookupGroup: async (nameOrId) => {\n const result = await run(execScript(groupQuery('linux', nameOrId)));\n return result.exitCode === 0 ? parseGroup('linux', result.stdout) : undefined;\n },\n lookupUser: async (nameOrId) => {\n const result = await run(execScript(userQuery('linux', nameOrId)));\n return result.exitCode === 0 ? parseUser('linux', result.stdout) : undefined;\n },\n privileged: false,\n readFile: async (path) => {\n const result = await run(readScript(path));\n if (result.exitCode === ABSENT) return undefined;\n if (result.exitCode !== 0) throw failed(options.host, `read ${path}`, result);\n return decodeBase64(result.stdout);\n },\n removeFile: async (path) => {\n const result = await run(removeScript(path));\n if (result.exitCode !== 0) throw failed(options.host, `remove ${path}`, result);\n },\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n stat: async (path) => {\n const result = await run(statScript(path));\n if (result.exitCode === ABSENT) return undefined;\n if (result.exitCode !== 0) throw failed(options.host, `stat ${path}`, result);\n const parsed = parseStat(result.stdout);\n // ⛔ Unparseable is an error, not \"nothing there\": a `stat` that printed something we do not\n // understand is a host this runner does not know, and guessing would write over a file.\n if (parsed === undefined) {\n throw new Error(`ssh ${options.host}: stat ${path} printed an unexpected shape`);\n }\n return parsed;\n },\n writeFileAtomic: async (path, bytes, write) => {\n const at = path.lastIndexOf('/');\n const temp = `${path.slice(0, at)}/.${path.slice(at + 1)}.${randomBytes(6).toString('hex')}.tmp`;\n const result = await run(writeScript(path, temp, write), bytes);\n if (result.exitCode !== 0) throw failed(options.host, `write ${path}`, result);\n },\n };\n};\n",
30
+ "/**\n * User and group lookups, as argv plus a parser — split out so the parsing is tested against the\n * real output shapes without running anything.\n *\n * ★ macOS FIRST, getent ELSEWHERE. macOS has no `getent`; its directory service answers through\n * `dscacheutil`. Shape MEASURED 2026-09-21 on macOS 27.2 (read-only query):\n *\n * name: someone\n * password: ********\n * uid: 501\n * gid: 20\n * dir: /Users/someone\n * shell: /bin/zsh\n *\n * An unknown uid printed NOTHING and exited 0 — so \"no record\" is an empty parse, never an\n * exit code. `getent` exits 2 for an unknown key; that also parses as no record.\n * ⛔ NEVER PARSE OR KEEP THE `password:` LINE. It is a placeholder on macOS, but a parser that\n * copies every field would carry a hash from any host where it is not.\n */\nimport type { HostUser } from './runner.ts';\n\nconst NUMERIC = /^\\d+$/;\n\n/** argv for looking up a user, by name or numeric id. */\nexport const userQuery = (platform: string, nameOrId: string): readonly string[] =>\n platform === 'darwin'\n ? [\n '/usr/bin/dscacheutil',\n '-q',\n 'user',\n '-a',\n NUMERIC.test(nameOrId) ? 'uid' : 'name',\n nameOrId,\n ]\n : ['getent', 'passwd', nameOrId];\n\n/** argv for looking up a group, by name or numeric id. */\nexport const groupQuery = (platform: string, nameOrId: string): readonly string[] =>\n platform === 'darwin'\n ? [\n '/usr/bin/dscacheutil',\n '-q',\n 'group',\n '-a',\n NUMERIC.test(nameOrId) ? 'gid' : 'name',\n nameOrId,\n ]\n : ['getent', 'group', nameOrId];\n\n/** The first `key: value` record in dscacheutil output, minus any password field. */\nconst firstRecord = (stdout: string): Map<string, string> => {\n const fields = new Map<string, string>();\n for (const line of stdout.split('\\n')) {\n if (line.trim() === '') {\n if (fields.size > 0) break; // ⚠️ records are blank-line separated; keep the first only\n continue;\n }\n const at = line.indexOf(': ');\n if (at <= 0) continue;\n const key = line.slice(0, at);\n if (key === 'password') continue;\n fields.set(key, line.slice(at + 2).trim());\n }\n return fields;\n};\n\nconst toId = (text: string | undefined): number | undefined =>\n text !== undefined && NUMERIC.test(text) ? Number(text) : undefined;\n\nexport const parseUser = (platform: string, stdout: string): HostUser | undefined => {\n if (platform === 'darwin') {\n const record = firstRecord(stdout);\n const uid = toId(record.get('uid'));\n const gid = toId(record.get('gid'));\n const home = record.get('dir');\n return uid === undefined || gid === undefined || home === undefined\n ? undefined\n : { gid, home, uid };\n }\n // getent passwd: name:x:uid:gid:gecos:home:shell\n const fields = (stdout.split('\\n')[0] ?? '').split(':');\n const uid = toId(fields[2]);\n const gid = toId(fields[3]);\n const home = fields[5];\n return uid === undefined || gid === undefined || home === undefined || home === ''\n ? undefined\n : { gid, home, uid };\n};\n\nexport const parseGroup = (platform: string, stdout: string): number | undefined => {\n if (platform === 'darwin') return toId(firstRecord(stdout).get('gid'));\n // getent group: name:x:gid:members\n return toId((stdout.split('\\n')[0] ?? '').split(':')[2]);\n};\n",
31
+ "/**\n * The wire format the ssh runner speaks: how an argv becomes one remote `sh` script, and how that\n * script's real exit code comes back. Pure functions, so every quoting and framing rule is tested\n * without opening a connection.\n *\n * ⛔ `ssh host <words>` IS NOT AN EXECVE. ssh joins its trailing arguments with spaces and the\n * REMOTE LOGIN SHELL parses the result, so an unquoted path with a space, a `$`, a backtick or a\n * `;` is re-parsed as shell syntax on the other side. Everything therefore goes through\n * `shellQuote`, and the remote command is always `/bin/sh -c '<script>'` — a POSIX shell named\n * explicitly, so a csh or fish login shell only ever execs it.\n * ⛔ AN EXIT CODE FROM ssh IS AMBIGUOUS. ssh exits 255 for its own failures (connection refused,\n * host key changed, auth) and otherwise passes the remote exit code through — and a remote\n * command may itself exit 255. A runner that read the code alone would report \"the file is not\n * there\" for a dropped connection, which is the exact \"never infer success from an empty result\"\n * failure. So every script ends by printing its own status on stderr behind a PER-RUNNER NONCE,\n * and a result without that line is a transport failure, never data.\n * ⚠️ THE NONCE IS NOT DECORATION. Without it a remote program that happens to print the marker on\n * stderr could claim any exit code it liked; with a fresh random nonce per runner, the only thing\n * that can produce the line is the script we sent.\n */\n\n/** A script whose work found nothing at the path. ★ Chosen from the 64–113 range sh leaves free. */\nexport const ABSENT = 66;\n\n/**\n * One argument, safe for a POSIX shell. ⛔ Single quotes, because they quote EVERYTHING; the\n * `'\\''` dance is how a literal quote gets through.\n */\nexport const shellQuote = (arg: string): string => {\n if (arg.includes('\\x00')) throw new Error('ssh: an argument contains NUL');\n return `'${arg.replaceAll(\"'\", `'\\\\''`)}'`;\n};\n\n/** An argv as one shell command line. */\nexport const quoteArgv = (argv: readonly string[]): string => argv.map(shellQuote).join(' ');\n\nconst marker = (nonce: string) => `__HF_RC_${nonce}=`;\n\n/**\n * Wrap a script so its status comes back unambiguously.\n *\n * ⛔ THE SCRIPT RUNS IN A SUBSHELL, AND THAT IS NOT COSMETIC. Several of the scripts end a branch\n * with `exit <code>` to say \"nothing was there\"; at the top level that exit leaves the shell\n * immediately and the marker line NEVER PRINTS — so the caller sees a framed result missing its\n * frame and, correctly, calls it a transport failure. 🔴 MEASURED 2026-09-22 against a live\n * Debian 13 host: `stat` of a missing path came back as \"the remote command did not report a\n * status (ssh exit 66)\" instead of \"nothing is at this path\". Inside `( … )` the exit leaves only\n * the subshell, `$?` carries the code out, and the frame always runs.\n * ⚠️ The leading `\\n` matters too: a program whose last stderr line has no newline would otherwise\n * glue itself to the marker and the marker would not be found.\n */\nexport const frameScript = (script: string, nonce: string): string =>\n `(\\n${script}\\n)\\n__hf=$?\\nprintf '\\\\n${marker(nonce)}%s\\\\n' \"$__hf\" >&2\\nexit \"$__hf\"`;\n\nexport type Framed = { readonly exitCode: number; readonly stderr: string };\n\n/**\n * The remote exit code and the stderr that belongs to the command, or `undefined` when the marker\n * is absent — which means the script never ran to its end: ssh failed, the connection dropped, or\n * the remote shell could not parse what we sent. ⛔ The caller must treat `undefined` as an error,\n * never as an answer.\n */\nexport const parseFramed = (stderr: string, nonce: string): Framed | undefined => {\n const at = stderr.lastIndexOf(`\\n${marker(nonce)}`);\n if (at < 0) return undefined;\n const digits = /^(\\d+)\\s*$/.exec(stderr.slice(at + marker(nonce).length + 1));\n if (digits?.[1] === undefined) return undefined;\n return { exitCode: Number(digits[1]), stderr: stderr.slice(0, at) };\n};\n\n/**\n * The fixed ssh options, in front of anything a caller adds.\n * ⛔ `BatchMode=yes` — a deploy that stops to ask for a passphrase is the failure \"no password\n * prompts\" names, and with BatchMode ssh fails at once instead.\n * ⛔ NOTHING HERE WEAKENS HOST VERIFICATION. No `StrictHostKeyChecking`, no `UserKnownHostsFile`:\n * the operator's own ssh config and known_hosts decide who the host is, and a changed key stays\n * a refusal. A runner that turned that off would make a man-in-the-middle a deploy target.\n * ★ `-T`: no pseudo-terminal, so the remote shell's stdout stays the bytes the program wrote.\n */\nexport const sshArgv = (\n host: string,\n script: string,\n options: { readonly connectTimeoutSec: number; readonly sshArgs?: readonly string[] },\n): readonly string[] => {\n if (host === '' || host.startsWith('-')) {\n throw new Error(`ssh: ${JSON.stringify(host)} is not a usable destination`);\n }\n return [\n 'ssh',\n '-T',\n '-o',\n 'BatchMode=yes',\n '-o',\n `ConnectTimeout=${String(options.connectTimeoutSec)}`,\n ...(options.sshArgs ?? []),\n host,\n `/bin/sh -c ${shellQuote(script)}`,\n ];\n};\n",
32
+ "/**\n * The four remote scripts a HostRunner needs, and the parser for the one that returns data.\n * Pure: every shape here is asserted in ssh-scripts.test.ts without a connection.\n *\n * ★ MEASURED 2026-09-22 on Debian 13 (systemd 257, kernel 7.0.14-pve), read-only over ssh:\n * $ stat -c '%f %a %u %g %s' /etc/hostname → 81a4 644 0 0 4 (exit 0)\n * $ stat -c '%f %a %u %g %s' /nonexistent → \"cannot statx\" (exit 1)\n * `%f` is the RAW mode in hex, so the file type is in it (0x8000 regular, 0x4000 directory,\n * 0xa000 symlink) and `%a` is the permission bits alone. ⛔ GNU `stat` does NOT follow symlinks\n * without `-L`, which is what makes this an lstat — the FileStat contract (runner.ts) requires it,\n * because writing through a symlink changes a file the resource never named.\n * ⚠️ COREUTILS, NOT POSIX. `stat -c`, `base64` and `mv -f` are GNU/BusyBox spellings, not POSIX\n * ones; this runner is for Linux hosts and refuses anything else at the probe (ssh-runner.ts).\n */\nimport { ABSENT, quoteArgv, shellQuote } from './ssh-command.ts';\nimport type { FileStat, WriteOptions } from '../launchd/runner.ts';\n\n/** ★ `-e` is false for a dangling symlink and `-L` is true for it, so both tests together mean\n * \"something is at this path\" — the question stat(2) answers and `-e` alone does not. */\nconst IF_PRESENT = (path: string) => `if [ -e ${path} ] || [ -L ${path} ]; then`;\n\nexport const statScript = (path: string): string => {\n const quoted = shellQuote(path);\n return `${IF_PRESENT(quoted)} stat -c '%f %a %u %g %s' -- ${quoted}; else exit ${String(ABSENT)}; fi`;\n};\n\n/** ★ base64 on the wire: stdout then carries no byte that a shell, a pty or a locale can alter. */\nexport const readScript = (path: string): string => {\n const quoted = shellQuote(path);\n return `${IF_PRESENT(quoted)} base64 -- ${quoted}; else exit ${String(ABSENT)}; fi`;\n};\n\n/** ⛔ `rm -f` is idempotent for an absent path and still FAILS for a directory, which is what keeps\n * HostRunner.removeFile from quietly removing something that is not a file. */\nexport const removeScript = (path: string): string => `rm -f -- ${shellQuote(path)}`;\n\nexport const execScript = (argv: readonly string[]): string => quoteArgv(argv);\n\n/**\n * Write bytes (fed on stdin) so `path` holds the old file or the new one and never a torn mix.\n *\n * ⛔ NOT `install`. GNU coreutils `install` opens the destination and writes THROUGH it — unlike\n * macOS's, which stages and renames — so a deploy interrupted mid-write would leave a truncated\n * config where a whole one used to be. Staging beside the target and `mv` (rename(2), same\n * directory so never EXDEV) is the only atomic spelling available over a shell.\n * ⛔ `set -C` and `umask 077`: the staged file is created O_EXCL, so a symlink or a file planted at\n * the temp name is a refusal rather than a write through it, and it is unreadable by anyone else\n * in the moment before its mode is set.\n * ⚠️ chown BEFORE chmod: chown by root clears setuid/setgid, so the other order silently drops a\n * declared 4755 — the same ordering localRunner keeps for the same reason.\n * ⚠️ Each step removes the staged file before it fails, so a failed write leaves no litter beside\n * a file someone else owns.\n */\nexport const writeScript = (path: string, temp: string, options: WriteOptions): string => {\n const target = shellQuote(path);\n const staged = shellQuote(temp);\n const bail = `{ rm -f -- ${staged}; exit`;\n /**\n * ⛔ NEVER `chown <uid>:` — GNU chown reads a trailing colon as \"and that user's LOGIN GROUP\",\n * which is a gid nobody declared. Each half is spelled only when it was asked for.\n */\n const owner =\n options.uid === undefined\n ? options.gid === undefined\n ? undefined\n : `:${String(options.gid)}`\n : options.gid === undefined\n ? String(options.uid)\n : `${String(options.uid)}:${String(options.gid)}`;\n const chown = owner === undefined ? [] : [`chown ${owner} -- ${staged} || ${bail} 72; }`];\n return [\n 'set -C',\n 'umask 077',\n `cat > ${staged} || ${bail} 71; }`,\n ...chown,\n `chmod ${options.mode.toString(8).padStart(3, '0')} -- ${staged} || ${bail} 73; }`,\n `mv -f -- ${staged} ${target} || ${bail} 74; }`,\n ].join('\\n');\n};\n\nconst TYPE_BITS = 0xf000;\nconst KINDS = new Map<number, FileStat['kind']>([\n [0x8000, 'file'],\n [0x4000, 'directory'],\n [0xa000, 'symlink'],\n]);\n\n/** `stat -c '%f %a %u %g %s'` output → a FileStat, or `undefined` when it is not that shape. */\nexport const parseStat = (stdout: string): FileStat | undefined => {\n const fields = stdout.trim().split(/\\s+/);\n if (fields.length !== 5) return undefined;\n const [rawMode, perms, uid, gid, size] = fields as [string, string, string, string, string];\n const raw = Number.parseInt(rawMode, 16);\n const mode = Number.parseInt(perms, 8);\n const numbers = [raw, mode, Number(uid), Number(gid), Number(size)];\n if (numbers.some((value) => !Number.isSafeInteger(value) || value < 0)) return undefined;\n return {\n gid: Number(gid),\n kind: KINDS.get(raw & TYPE_BITS) ?? 'other',\n // ⚠️ `%a` alone, never `%f & 0o7777`: they agree, and the permission field is the documented one.\n mode,\n size: Number(size),\n uid: Number(uid),\n };\n};\n"
33
+ ],
34
+ "mappings": ";AASA;AACA,oBAAS;AACT;AACA;AACA;;;ACJA;AAEO,IAAM,OAAO,CAAI,SACf,kBAAW;AAAA,EAChB,OAAO,CAAC,UAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,EAC3E,KAAK;AACP,CAAC;AAQI,IAAM,iBAAiB,CAAC,MAAe,QAAoC;AAAA,EAChF,IAAI,OAAO,SAAS,YAAY,SAAS;AAAA,IAAM;AAAA,EAC/C,MAAM,QAAS,KAAiC;AAAA,EAChD,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA;;;ACZ7C;AACA;AAAA;AA8EO,MAAM,0BAAkC,gBAAuC,EACpF,8BACF,EAAE;AAAC;AAGI,IAAM,kBAAkB,CAAC,WACxB,cAAQ,mBAAmB,MAAM;AAGlC,IAAM,eAAe,CAAC,WAC3B,OAAO,cAAc,OAAO,aAAa,MAAM;;;AC9FjD;AACA;AACA,kBAAS;AACT;AACA;;;ACiBA;AACA;AACA;AACA;AAEA,IAAM,SAAS;AACf,IAAM,aAAa;AAEnB,IAAM,OAAO,CAAC,KAAa,eAClB,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,MAAM,OAAc,sBAAc,SAAS;AAAA,EACjD,IAAW,cAAO,GAAG;AAAA,IAAG,OAAO,IAAI,MAAM,IAAI,KAAK,UAAU;AAAA,CAC7D;AAEH,IAAM,QAAQ,CAAC,KAAa,eACnB,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,MAAM,OAAc,sBAAc,SAAS;AAAA,EACjD,IAAW,cAAO,GAAG;AAAA,IAAG,OAAO;AAAA,EAC/B,QAAQ,OAAO,IAAI,MAAM,IAAY,GAAG,OAAO;AAAA,CAChD;AAOI,IAAM,iBAAiB,CAAC,eACtB,WAAG,KAAK,YAAY,UAAU,GAAG,SAAS;AAQ5C,IAAM,aAAa,CACxB,YACA,cAEO,YAAI,UAAU,GAAG;AAAA,EACtB,OAAO,eAAe,UAAU;AAAA,EAChC,MAAM,WAAW,MAAa,gBAAQ,SAAS;AAAA,EAC/C,MAAM,QAAQ,OAAO,UAAU,KAAY,cAAM,QAAQ,GAAU,oBAAY,QAAQ,CAAC;AAAA,EACxF,IAAI,UAAU,aAAa,UAAU,QAAQ,QAAQ,GAAG,KAAK;AAAA,IAAG;AAAA,EAChE,OAAO,KAAK,QAAQ,UAAU;AAAA,CAC/B;AAGI,IAAM,UAAU,CAAC,eAA+C,MAAM,QAAQ,UAAU;AAGxF,IAAM,aAAa,CAAC,eACzB,MAAM,YAAY,UAAU;AAuBvB,IAAM,iBAAiB,CAAsB,YAAkB;AAAA,EACpE,QAAQ,MAAM,SAAS;AAAA,EACvB,IAAI,SAAS,aAAa,SAAS;AAAA,IAAW,OAAO;AAAA,EACrD,MAAM,UAA6B,CAAC,UAC3B,YAAI,UAAU,GAAG;AAAA,IACtB,IAAI,MAAM,WAAW,WAAW;AAAA,MAC9B,OAAO,WAAW,MAAM,YAAY,KAAK,KAAK,OAAO,QAAQ,UAAU,CAAC,CAAC;AAAA,IAC3E;AAAA,IACA,OAAO,OAAO,KAAK,KAAK;AAAA,GACzB;AAAA,EAEH,OAAO,KAAK,SAAS,MAAM,QAAQ;AAAA;;;ACxGrC,kBAAS;AACT;AACA;AACA;;;ACIA;AACA;AACA;AAEA,IAAM,SAAS,CAAC,UAA4B,UAAU,aAAa,UAAU;AAE7E,IAAM,gBAAgB,CAAC,UAAqD;AAAA,EAC1E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK;AAAA,IAAG,OAAO;AAAA,EAChF,MAAM,QAAiB,OAAO,eAAe,KAAK;AAAA,EAClD,OAAO,UAAU,OAAO,aAAa,UAAU;AAAA;AAO1C,IAAM,UAAU,CAAC,UAAmB,aAA+B;AAAA,EACxE,IAAI,OAAO,QAAQ;AAAA,IAAG,OAAO;AAAA,EAC7B,IAAI,OAAO,QAAQ;AAAA,IAAG,OAAO;AAAA,EAC7B,IAAI,MAAM,QAAQ,QAAQ,GAAG;AAAA,IAC3B,IAAI,CAAC,MAAM,QAAQ,QAAQ;AAAA,MAAG,OAAO;AAAA,IACrC,OAAO,SAAS,MAAM,CAAC,MAAM,UAAU,QAAQ,MAAM,SAAS,MAAM,CAAC;AAAA,EACvE;AAAA,EACA,IAAI,cAAc,QAAQ,GAAG;AAAA,IAC3B,IAAI,CAAC,cAAc,QAAQ;AAAA,MAAG,OAAO;AAAA,IACrC,OAAO,OAAO,QAAQ,QAAQ,EAAE,MAAM,EAAE,KAAK,UAAU,QAAQ,MAAM,SAAS,IAAI,CAAC;AAAA,EACrF;AAAA,EACA,OAAO;AAAA;AASF,IAAM,mBAAmB,CAAC,KAAa,UACrC,YAAW,sBAAc,KAAK,GAAG,CAAC,UAAU;AAAA,EACjD,MAAM,WAAkB,eAAO,KAAK,IAAI,MAAM,MAAM,UAAU,OAAO;AAAA,EACrE,OAAO,aAAa,aAAa,QAAQ,SAAS,OAAO,KAAK;AAAA,CAC/D;;;ADtCH,IAAM,UAAiB,YAAI,UAAU,GAAG;AAAA,EACtC,MAAM,QAAQ,OAAc,sBAAc,MAAK;AAAA,EAC/C,MAAM,QAAQ,OAAc,sBAAc,KAAK;AAAA,EAC/C,IAAW,eAAO,KAAK,KAAY,eAAO,KAAK;AAAA,IAAG;AAAA,EAClD,MAAM,QAAsB,OAAO,MAAM;AAAA,EACzC,MAAM,QAAe,EAAE,OAAO,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM;AAAA,EACzE,OAAO,EAAE,WAAW,MAAM,MAAM,WAAW,OAAO,MAAM;AAAA,CACzD;AAED,IAAM,QAAQ,CAAC,OAAqB,OAAc,QAChD,MAAM,IAAI,KAAK,OAAO,IAAI,CAAC,EAAE,KAAY,sBAAc,MAAG;AAAA,EAAG;AAAA,CAAS,CAAC;AAczE,IAAM,eAAe,CAAC,KAAc,eAA+C;AAAA,EACjF,IAAI,KAAK;AAAA,EACT,OAAO,OAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,cAAc,EAAW,GAAG;AAAA,IAC3E,MAAM,aAAa;AAAA,IACnB,IAAI,WAAW,eAAe;AAAA,MAAY,OAAO;AAAA,IACjD,KAAK,WAAW;AAAA,EAClB;AAAA,EACA;AAAA;AAIK,IAAM,qBAAqB,CAChC,KACA,eAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAO;AAAA,EACrB,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,WAAW,MAAM,CAAC,KAAK,GAAI,MAAM,UAAU,MAAM,cAAc,CAAC,CAAE,GAAG;AAAA,IACnE,MAAM,aAAa,aAAa,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,EAAE,GAAG,UAAU;AAAA,IACtF,IAAI,eAAe;AAAA,MAAW,OAAO;AAAA,EACvC;AAAA,EACA;AAAA,CACD;AAcI,IAAM,mBAAmB,CAAC,KAAa,eACrC,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,aAAa,OAAO,mBAAmB,KAAK,UAAU;AAAA,EAC5D,IAAI,eAAe;AAAA,IAAW,OAAO;AAAA,EACrC,QAAQ,OAAO,iBAAiB,KAAK,WAAW,KAAK,KAAK,UAAU;AAAA,CACrE;AAQI,IAAM,WAAW,CAAC,KAAa,eAC7B,YACL,mBAAmB,KAAK,UAAU,GAClC,CAAC,eAAe,YAAY,WAAW,UACzC;AAaK,IAAM,sBAAsB,CAAC,KAAa,eACxC,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAO;AAAA,EACrB,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,MAAM,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,GAAG;AAAA,EACtD,MAAM,OAAO;AAAA,EACb,IAAI,MAAM,WAAW,cAAc,KAAK,eAAe,cAAc,KAAK,SAAS,WAAW;AAAA,IAC5F;AAAA,EACF;AAAA,EACA,OAAO,MAAM,MAAM,OAAO,KAAK,MAAM,OAAO,IAAI,CAAC,EAAE,KAAY,cAAM;AAAA,CACtE;;;AFxFI,IAAM,eAAe,CAAC,QACpB,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAc,sBAAc,MAAK;AAAA,EAC/C,MAAM,SAAgB,eAAO,KAAK,IAAI,MAAM,MAAM,UAAU,MAAM,QAAQ;AAAA,EAC1E,IAAI,WAAW;AAAA,IAAW,OAAO;AAAA,EACjC,MAAM,SAAS,OAAc,sBAAc,WAAW;AAAA,EACtD,IAAW,eAAO,MAAM;AAAA,IAAG,OAAO,OAAO;AAAA,EACzC,MAAM,UAAU,OAAc,sBAAc,cAAc;AAAA,EAC1D,OAAc,eAAO,OAAO,IAAI,QAAQ,MAAM,QAAQ;AAAA,CACvD;AAqBH,IAAM,YAAY,CAAC,UACV,YAAI,UAAU,GAAG;AAAA,EACtB,IAAI,OAAO,SAAS,MAAM,KAAK,MAAM,UAAU;AAAA,IAAG,OAAO;AAAA,EACzD,OAAO,OAAO,WAAW,MAAM,UAAU;AAAA,CAC1C;AAGI,IAAM,gBAAgB,CAAC,UACrB,YAAI,UAAU,GAAG;AAAA,EACtB,IAAI,EAAE,OAAO,aAAa,MAAM,GAAG;AAAA,IAAI,OAAO;AAAA,EAC9C,OAAO,OAAO,UAAU,KAAK;AAAA,CAC9B;AAuBI,IAAM,iBAAiB,CAAC,OAAc,SACpC,YAAI,UAAU,GAAG;AAAA,EACtB,IAAI,MAAM,WAAW;AAAA,IAAW;AAAA,EAChC,IAAI,OAAO,QAAQ,MAAM,UAAU;AAAA,IAAG;AAAA,EACtC,MAAM,MAAM,OAAO,UAAU,KAAK;AAAA,EAClC,IAAI,QAAQ,OAAO,aAAa,MAAM,GAAG;AAAA,IAAI;AAAA,EAC7C,OAAO,oBAAoB,MAAM,KAAK,MAAM,UAAU;AAAA,EACtD,MAAM,SAAS,MACX,mGACA;AAAA,EACJ,OAAO,OAAc,YACnB,IAAI,MACF,GAAG,yFACD,0FACA,+BAA+B,QACnC,CACF;AAAA,CACD;AAGI,IAAM,WACX,CAAC,UACD,CAAC,SACC,eAAe,OAAO,IAAI;;;AIzG9B,IAAM,cACJ,gGACA;AAGF,IAAM,cACJ;AAGF,IAAM,cAAc;AAGpB,IAAM,YAAY;AAEX,IAAM,cAAc;AAG3B,IAAM,YAAY,CAAC,OAAmB,SAA2B;AAAA,EAC/D,IAAI,OAAO,UAAU;AAAA,IAAU,OAAO,YAAY,KAAK,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AAAA,EAC1E,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,IACxB,OAAQ,MAAgC,QAAQ,CAAC,MAAM,UACrD,UAAU,MAAM,GAAG,QAAQ,OAAO,KAAK,IAAI,CAC7C;AAAA,EACF;AAAA,EACA,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAAA,IAC/C,OAAO,OAAO,QAAQ,KAAK,EAAE,QAAQ,EAAE,KAAK,UAAU,UAAU,MAAM,GAAG,QAAQ,KAAK,CAAC;AAAA,EACzF;AAAA,EACA,OAAO,CAAC;AAAA;AAOH,IAAM,oBAAoB,CAC/B,aACA,MACA,cACa;AAAA,EACb,MAAM,QAAkB,CAAC;AAAA,EACzB,YAAY,MAAM,UAAU,OAAO,QAAQ,eAAe,CAAC,CAAC,GAAG;AAAA,IAC7D,IAAI,YAAY,KAAK,IAAI;AAAA,MACvB,MAAM,KAAK,eAAe,6BAA6B,aAAa;AAAA,IACjE,SAAI,YAAY,KAAK,KAAK;AAAA,MAC7B,MAAM,KAAK,eAAe,6BAA6B,aAAa;AAAA,EACxE;AAAA,EACA,YAAY,OAAO,QAAQ,KAAK,QAAQ,GAAG;AAAA,IAEzC,IAAI,YAAY,KAAK,GAAG,GAAG;AAAA,MACzB,MAAM,KAAK,oBAAoB,OAAO,KAAK,8BAA8B,aAAa;AAAA,IACxF,EAAO,SAAI,UAAU,KAAK,GAAG,KAAK,QAAQ,IAAI,KAAK,QAAQ;AAAA,MACzD,MAAM,KAAK,oBAAoB,OAAO,QAAQ,CAAC,6BAA6B,aAAa;AAAA,IAC3F,EAAO,SAAI,YAAY,KAAK,GAAG,GAAG;AAAA,MAChC,MAAM,KAAK,oBAAoB,OAAO,KAAK,2BAA2B,aAAa;AAAA,IACrF;AAAA,EACF;AAAA,EACA,WAAW,QAAQ,UAAU,aAAa,CAAC,GAAG,WAAW,GAAG;AAAA,IAC1D,MAAM,KAAK,GAAG,6BAA6B,aAAa;AAAA,EAC1D;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,qBAAqB,CAAC,YACjC,YAAY,KAAK,OAAO,IAAI,CAAC,gCAAgC,aAAa,IAAI,CAAC;;;AC9C1E,IAAM,eAAe;AAE5B,IAAM,mBAAmB,CAAC,MAAc,UAAiD;AAAA,EACvF,IAAI,UAAU;AAAA,IAAW,OAAO,CAAC;AAAA,EACjC,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,OAAO,OAAO,cAAc,KAAK,KAAK,SAAS,IAC3C,CAAC,IACD,CAAC,GAAG,wCAAwC;AAAA,EAClD;AAAA,EACA,OAAO,6BAA6B,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,IACjE,CAAC,IACD,CAAC,GAAG,QAAQ,KAAK,UAAU,KAAK,qCAAqC;AAAA;AAOpE,IAAM,eAAe,CAAC,SAA2B;AAAA,EACtD,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,CAAC,KAAK,WAAW,GAAG;AAAA,IAAG,MAAM,KAAK,uBAAuB;AAAA,EAG7D,IACE,KAAK,SAAS,GAAG,KACjB,KACG,MAAM,GAAG,EACT,MAAM,CAAC,EACP,KAAK,CAAC,SAAS,SAAS,MAAM,SAAS,OAAO,SAAS,IAAI,GAC9D;AAAA,IACA,MAAM,KAAK,6EAA6E;AAAA,EAC1F;AAAA,EACA,IAAI,KAAK,SAAS,MAAM;AAAA,IAAG,MAAM,KAAK,mBAAmB;AAAA,EACzD,OAAO;AAAA;AAGF,IAAM,eAAe,CAAC,UAAmC;AAAA,EAC9D,MAAM,QAAQ,aAAa,MAAM,IAAI;AAAA,EACrC,MAAM,OAAO,MAAM,QAAQ;AAAA,EAC3B,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,OAAO;AAAA,IAAQ,MAAM,KAAK,uBAAuB;AAAA,EAChG,MAAM,KAAK,GAAG,iBAAiB,SAAS,MAAM,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,GAAG,iBAAiB,SAAS,MAAM,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,GAAG,mBAAmB,MAAM,OAAO,CAAC;AAAA,EAC/C,OAAO;AAAA;;;AC1CF,IAAM,mBAAmB;AAEzB,IAAM,SAAS,CAAC,MAAc,YACnC,IAAI,MAAM,kBAAkB,SAAS,SAAS;AAEhD,IAAM,aAAa,CAAC,MAAc,UAAiD;AAAA,EACjF,IAAI,UAAU;AAAA,IAAW,OAAO,CAAC;AAAA,EACjC,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,OAAO,OAAO,cAAc,KAAK,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,gCAAgC;AAAA,EAC9F;AAAA,EACA,OAAO,6BAA6B,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,IACjE,CAAC,IACD,CAAC,GAAG,QAAQ,KAAK,UAAU,KAAK,qCAAqC;AAAA;AAGpE,IAAM,oBAAoB,CAAC,UAAwC;AAAA,EACxE,MAAM,QAAQ,aAAa,MAAM,IAAI;AAAA,EACrC,MAAM,OAAO,MAAM,QAAQ;AAAA,EAC3B,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,OAAO;AAAA,IAAQ,MAAM,KAAK,uBAAuB;AAAA,EAChG,MAAM,KAAK,GAAG,WAAW,SAAS,MAAM,KAAK,GAAG,GAAG,WAAW,SAAS,MAAM,KAAK,CAAC;AAAA,EACnF,OAAO;AAAA;AAGT,IAAM,YAAY,CAAC,UACjB,OAAO,UAAU,WAAW,QAAQ,QAAQ,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI;AAI5E,IAAM,UAAU,OAAO,QAAoB,UAAgD;AAAA,EACzF,MAAM,QAAQ,kBAAkB,KAAK;AAAA,EACrC,IAAI,MAAM,SAAS;AAAA,IAAG,MAAM,OAAO,MAAM,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,EAC/D,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI,MAAM,UAAU,WAAW;AAAA,IAC7B,MAAM,UAAU,MAAM,KAAK,MAAM,MAAM,OAAO,WAAW,OAAO,MAAM,KAAK,CAAC,IAAI;AAAA,IAChF,IAAI,QAAQ;AAAA,MAAW,MAAM,OAAO,MAAM,MAAM,WAAW,OAAO,MAAM,KAAK,gBAAgB;AAAA,EAC/F;AAAA,EACA,IAAI,MAAM,UAAU,WAAW;AAAA,IAC7B,MAAM,UAAU,MAAM,KAAK,KAAM,MAAM,OAAO,YAAY,OAAO,MAAM,KAAK,CAAC;AAAA,IAC7E,IAAI,QAAQ;AAAA,MAAW,MAAM,OAAO,MAAM,MAAM,YAAY,OAAO,MAAM,KAAK,gBAAgB;AAAA,EAChG;AAAA,EACA,OAAO;AAAA,IACL,MAAM,MAAM,QAAQ;AAAA,OAChB,QAAQ,YAAY,CAAC,IAAI,EAAE,IAAI;AAAA,OAC/B,QAAQ,YAAY,CAAC,IAAI,EAAE,IAAI;AAAA,EACrC;AAAA;AAGK,IAAM,gBAAgB,OAC3B,QACA,SACiD;AAAA,EACjD,MAAM,OAAO,MAAM,OAAO,KAAK,IAAI;AAAA,EACnC,IAAI,SAAS,aAAa,KAAK,SAAS;AAAA,IAAa;AAAA,EACrD,OAAO,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,MAAM,KAAK,KAAK,IAAI;AAAA;AAG/D,IAAM,QAAQ,CAAC,SAAiB,KAAK,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AAGhE,IAAM,OAAO,OAAO,QAAoB,MAAc,SAA2C;AAAA,EAC/F,MAAM,SAAS,MAAM,OAAO,KAAK,IAAI;AAAA,EACrC,IAAI,OAAO,aAAa,GAAG;AAAA,IACzB,MAAM,OACJ,MACA,GAAG,KAAK,KAAK,GAAG,QAAQ,OAAO,OAAO,QAAQ,MAAM,OAAO,OAAO,KAAK,EAAE,MAAM,GAAG,GAAG,GACvF;AAAA,EACF;AAAA;AAQK,IAAM,qBAAqB,OAChC,QACA,OACA,QACA,QAAQ,UAC6B;AAAA,EACrC,MAAM,OAAO,MAAM,QAAQ,QAAQ,KAAK;AAAA,EACxC,IAAI,KAAK,QAAQ,aAAa,KAAK,QAAQ,OAAO,aAAa,KAAK,CAAC,aAAa,MAAM,GAAG;AAAA,IACzF,MAAM,OACJ,MAAM,MACN,SAAS,OAAO,MAAM,KAAK,sEACzB,uFACJ;AAAA,EACF;AAAA,EACA,MAAM,OAAO,MAAM,OAAO,KAAK,MAAM,IAAI;AAAA,EACzC,IAAI,SAAS,aAAa,KAAK,SAAS,aAAa;AAAA,IACnD,MAAM,OAAO,MAAM,MAAM,QAAQ,KAAK,uBAAuB;AAAA,EAC/D;AAAA,EAMA,IAAI,WAAW,aAAa,SAAS,aAAa,CAAC,OAAO;AAAA,IACxD,MAAM,UAAU,KAAK,SAAS,KAAK,QAAS,KAAK,QAAQ,aAAa,KAAK,QAAQ,KAAK;AAAA,IACxF,IAAI,SAAS;AAAA,MACX,MAAM,OACJ,MAAM,MACN,uFACE,uDACJ;AAAA,IACF;AAAA,EACF;AAAA,EACA,IAAI,SAAS,WAAW;AAAA,IACtB,MAAM,SAAS,MAAM,KAAK,MAAM,GAAG,MAAM,KAAK,YAAY,GAAG,CAAC,KAAK;AAAA,IACnE,MAAM,QAAQ,MAAM,OAAO,KAAK,MAAM;AAAA,IACtC,IAAI,UAAU,aAAa,MAAM,SAAS,aAAa;AAAA,MACrD,MAAM,OAAO,MAAM,MAAM,cAAc,mDAAmD;AAAA,IAC5F;AAAA,IACA,MAAM,KAAK,QAAQ,MAAM,MAAM,CAAC,SAAS,MAAM,MAAM,KAAK,IAAI,GAAG,MAAM,MAAM,IAAI,CAAC;AAAA,EACpF,EAAO,SAAI,KAAK,SAAS,KAAK,MAAM;AAAA,IAClC,MAAM,KAAK,QAAQ,MAAM,MAAM,CAAC,SAAS,MAAM,KAAK,IAAI,GAAG,MAAM,MAAM,IAAI,CAAC;AAAA,EAC9E;AAAA,EAMA,MAAM,QAAQ,aACZ,EAAE,KAAK,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO,GAAG,GACxE,IACF,IACI,YACA,SAAS,IAAI;AAAA,EACjB,IAAI,UAAU;AAAA,IAAW,MAAM,KAAK,QAAQ,MAAM,MAAM,CAAC,SAAS,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EAC1F,MAAM,QAAQ,MAAM,cAAc,QAAQ,MAAM,IAAI;AAAA,EAEpD,IAAI,UAAU,aAAa,MAAM,SAAS,KAAK,QAAQ,CAAC,aAAa,OAAO,IAAI,GAAG;AAAA,IACjF,MAAM,OAAO,MAAM,MAAM,0DAA0D;AAAA,EACrF;AAAA,EACA,OAAO;AAAA;AAIT,IAAM,WAAW,CAAC,SAAsC;AAAA,EACtD,IAAI,KAAK,QAAQ;AAAA,IAAW,OAAO,KAAK,QAAQ,YAAY,YAAY,IAAI,OAAO,KAAK,GAAG;AAAA,EAC3F,OAAO,KAAK,QAAQ,YAAY,OAAO,KAAK,GAAG,IAAI,GAAG,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,GAAG;AAAA;AAG3F,IAAM,eAAe,CAAC,MAA+B,UAClD,KAAK,QAAQ,aAAa,KAAK,QAAQ,KAAK,SAC5C,KAAK,QAAQ,aAAa,KAAK,QAAQ,KAAK;AAOxC,IAAM,gBAAgB,OAC3B,QACA,MACA,WACkB;AAAA,EAClB,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;AAAA,EACvC,IAAI,KAAK,SAAS,OAAO;AAAA,IAAM,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC1D,MAAM,OAAO,MAAM,cAAc,QAAQ,KAAK,IAAI;AAAA,EAGlD,MAAM,YACJ,SAAS,aACT,KAAK,SAAS,KAAK,QACnB,aAAa,MAAM,IAAI,KACvB,OAAO,SAAS,KAAK,QACrB,aAAa,QAAQ,IAAI;AAAA,EAC3B,IAAI;AAAA,IAAW,OAAO,EAAE,QAAQ,OAAO;AAAA,EACvC,MAAM,OAAO,aAAa,KAAK,MAAM,IAAI;AAAA,EACzC,OAAO,EAAE,QAAQ,SAAS;AAAA;AAIrB,IAAM,kBAAkB,OAC7B,QACA,WACkB;AAAA,EAClB,MAAM,OAAO,MAAM,OAAO,KAAK,OAAO,IAAI;AAAA,EAC1C,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,IAAI,KAAK,SAAS;AAAA,IAChB,MAAM,OAAO,OAAO,MAAM,YAAY,KAAK,uBAAuB;AAAA,EACpE,MAAM,KAAK,QAAQ,OAAO,MAAM,CAAC,SAAS,MAAM,OAAO,IAAI,CAAC;AAAA;;;ATxLvD,IAAM,gBAAgB,SAAwB,gBAAgB;AAE9D,IAAM,wBAAwB,MAC1B,gBACP,eACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAO;AAAA,EACtB,OAAO,cAAc,SAAS,GAAG;AAAA,IAC/B,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAE7B,MAAM,GAAG,MAAM,aACb,KAAK,YAAY;AAAA,MACf,MAAM,QAAQ,MAAM,cAAc,QAAQ,KAAK,IAAI;AAAA,MACnD,IAAI,UAAU;AAAA,QAAW;AAAA,MACzB,OAAO,WAAW,YAAY,SAAQ,KAAK,IAAI;AAAA,KAChD;AAAA,IAEH,MAAM,GAAG,YAAY,MAAM,aAAa;AAAA,MACtC,IAAI,WAAW;AAAA,QAAW,OAAO,eAAe,UAAU;AAAA,MAC1D,IAAI,WAAW,IAAI;AAAA,QAAG,OAAO,KAAK,MAAM,cAAc,QAAQ,MAAM,MAAM,CAAC;AAAA,MAG3E,MAAM,OAAO,eAAe,MAAM,MAAM;AAAA,MACxC,OAAc,gBACZ,SAAS,aAAa,SAAS,OAAO,OAAO,EAAE,QAAQ,UAAmB,IAAI,SAChF;AAAA;AAAA,IAGF,WAAW,GAAG,KAAK,YAAY,MAAM,aAC5B,gBAAQ,cAAc,EAAE,KAAK,YAAY,OAAO,CAAC,GAAG,CAAC,UAC1D,KAAK,MAAM,mBAAmB,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAC5D;AAAA,IAEF,QAAQ,GAAG,aAAa,KAAK,MAAM,gBAAgB,QAAQ,MAAM,CAAC;AAAA,EACpE,CAAC;AAAA,CACF,CACH;;AUzDF;;;ACKA,qBAAS;AACT,oBAAS;AACT,uBAAS;AACT;AACA;;;ACZA;;;ACqBO,MAAM,mBAAmB,MAAM;AAAA,EAEzB;AAAA,EADX,WAAW,CACA,MACT,QACA;AAAA,IACA,MAAM,SAAS,SAAS,KAAK,WAAW,SAAS,QAAQ;AAAA,IAHhD;AAAA,IAIT,KAAK,OAAO;AAAA;AAEhB;AAEA,IAAM,SACJ;AAAA,IACA,0DACA;AAAA,IACA;AAAA;AAGF,IAAM,YAAY;AASlB,IAAM,YAAY;AAQX,IAAM,aAAa,CAAC,MAAc,SAAyB;AAAA,EAChE,MAAM,MAAM,UAAU,KAAK,IAAI;AAAA,EAC/B,IAAI,QAAQ,MAAM;AAAA,IAChB,MAAM,OAAO,IAAI,GAAG,YAAY,CAAC,KAAK;AAAA,IACtC,MAAM,IAAI,WACR,MACA,KAAK,KAAK,SAAS,EAAE,EAAE,YAAY,EAAE,SAAS,GAAG,GAAG,4BACtD;AAAA,EACF;AAAA,EACA,OAAO,KACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,MAAM,OAAO;AAAA;AAG7B,IAAM,iBAAgB,CAAC,UAA2B;AAAA,EAChD,MAAM,QAAQ,OAAO,eAAe,KAAK;AAAA,EACzC,OAAO,UAAU,OAAO,aAAa,UAAU;AAAA;AAGjD,IAAM,cAAc,CAAC,OAAgB,MAAc,OAAe,QAAwB;AAAA,EACxF,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,EAC7B,IAAI,QAAQ;AAAA,IAAW,MAAM,IAAI,WAAW,MAAM,sBAAsB,OAAO,SAAS,GAAG;AAAA,EAC3F,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,IAAI,KAAK,GAAG,cAAc,WAAW,OAAO,IAAI,YAAY;AAAA,IAC5D;AAAA,EACF;AAAA,EACA,IAAI,OAAO,UAAU,WAAW;AAAA,IAC9B,IAAI,KAAK,GAAG,OAAO,QAAQ,SAAS,WAAW;AAAA,IAC/C;AAAA,EACF;AAAA,EACA,IAAI,OAAO,UAAU,UAAU;AAAA,IAE7B,IAAI,CAAC,OAAO,cAAc,KAAK,GAAG;AAAA,MAChC,MAAM,IAAI,WAAW,MAAM,GAAG,OAAO,KAAK,6CAA6C;AAAA,IACzF;AAAA,IACA,IAAI,KAAK,GAAG,eAAe,OAAO,UAAU,IAAI,IAAI,KAAK,aAAa;AAAA,IACtE;AAAA,EACF;AAAA,EACA,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,IACxB,IAAI,MAAM,WAAW,GAAG;AAAA,MACtB,IAAI,KAAK,GAAG,aAAa;AAAA,MACzB;AAAA,IACF;AAAA,IACA,IAAI,KAAK,GAAG,YAAY;AAAA,IACxB,YAAY,OAAO,SAAU,MAA6B,QAAQ,GAAG;AAAA,MACnE,YAAY,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,QAAQ,GAAG,GAAG;AAAA,IAC/D;AAAA,IACA,IAAI,KAAK,GAAG,aAAa;AAAA,IACzB;AAAA,EACF;AAAA,EACA,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,eAAc,KAAK,GAAG;AAAA,IACvE,WAAW,OAAkC,MAAM,OAAO,GAAG;AAAA,IAC7D;AAAA,EACF;AAAA,EACA,MAAM,OAAO,UAAU,OAAO,SAAS,OAAO;AAAA,EAC9C,MAAM,IAAI,WAAW,MAAM,GAAG,+CAA+C;AAAA;AAG/E,IAAM,aAAa,CACjB,MACA,MACA,OACA,QACS;AAAA,EACT,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,EAE7B,MAAM,OAAO,OAAO,KAAK,IAAI,EAAE,KAAK;AAAA,EACpC,IAAI,KAAK,WAAW,GAAG;AAAA,IACrB,IAAI,KAAK,GAAG,YAAY;AAAA,IACxB;AAAA,EACF;AAAA,EACA,IAAI,KAAK,GAAG,WAAW;AAAA,EACvB,WAAW,OAAO,MAAM;AAAA,IACtB,MAAM,QAAQ,SAAS,KAAK,MAAM,GAAG,QAAQ;AAAA,IAC7C,IAAI,KAAK,GAAG,YAAa,WAAW,KAAK,KAAK,SAAS;AAAA,IACvD,YAAY,KAAK,MAAM,OAAO,QAAQ,GAAG,GAAG;AAAA,EAC9C;AAAA,EACA,IAAI,KAAK,GAAG,YAAY;AAAA;AAInB,IAAM,cAAc,CAAC,SAA4B;AAAA,EACtD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,KAAK,CAAC,eAAc,IAAI,GAAG;AAAA,IAC5F,MAAM,IAAI,WAAW,IAAI,iDAAiD;AAAA,EAC5E;AAAA,EACA,MAAM,MAAgB,CAAC;AAAA,EACvB,WAAW,MAAiC,IAAI,GAAG,GAAG;AAAA,EACtD,OAAO,GAAG,SAAS,IAAI,KAAK;AAAA,CAAI;AAAA;AAAA;AAAA;;;ADxE3B,IAAM,cAAc,CAAC,WAA6C;AAAA,EACvE,IAAI,WAAW;AAAA,IAAU,OAAO,EAAE,MAAM,SAAS;AAAA,EACjD,MAAM,QAAQ,eAAe,KAAK,MAAM;AAAA,EACxC,OAAO,QAAQ,OAAO,YAAY,YAAY,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE,EAAE;AAAA;AAG9E,IAAM,gBAAgB,CAAC,OAAe,WAAkC,GAAG,UAAU;AAOrF,IAAM,eAAe,CAC1B,OACA,QACA,SACW;AAAA,EACX,IAAI,OAAO,SAAS;AAAA,IAAU,OAAO,0BAA0B;AAAA,EAE/D,IAAI,SAAS,aAAa,CAAC,KAAK,WAAW,GAAG,GAAG;AAAA,IAC/C,MAAM,IAAI,MAAM,OAAO,OAAO,OAAO,GAAG,gDAAgD;AAAA,EAC1F;AAAA,EACA,OAAO,GAAG,6BAA6B;AAAA;AAIlC,IAAM,aAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,eAAe,CAAC,UAAuC;AAAA,EAC3D,MAAM,OAAmC,CAAC;AAAA,EAC1C,IAAI,MAAM,WAAW;AAAA,IAAW,KAAK,YAAY,MAAM;AAAA,EACvD,IAAI,MAAM,SAAS;AAAA,IAAW,KAAK,UAAU,MAAM;AAAA,EACnD,IAAI,MAAM,QAAQ;AAAA,IAAW,KAAK,SAAS,MAAM;AAAA,EACjD,IAAI,MAAM,YAAY;AAAA,IAAW,KAAK,aAAa,MAAM;AAAA,EACzD,IAAI,MAAM,UAAU;AAAA,IAAW,KAAK,WAAW,MAAM;AAAA,EACrD,OAAO;AAAA;AAGT,IAAM,iBAAiB,CAAC,cAAqC;AAAA,EAC3D,IAAI,OAAO,cAAc;AAAA,IAAW,OAAO;AAAA,EAC3C,MAAM,OAAmC,CAAC;AAAA,EAC1C,IAAI,UAAU,mBAAmB;AAAA,IAAW,KAAK,oBAAoB,UAAU;AAAA,EAC/E,IAAI,UAAU,YAAY;AAAA,IAAW,KAAK,aAAa,UAAU;AAAA,EACjE,OAAO;AAAA;AAIF,IAAM,eAAe,CAAC,UAAsC;AAAA,EACjE,MAAM,OAAmC,KAAK,MAAM,UAAU;AAAA,EAC9D,KAAK,WAAW,MAAM;AAAA,EACtB,KAAK,sBAAsB,CAAC,GAAG,MAAM,gBAAgB;AAAA,EACrD,MAAM,MAAM,MAAM,eAAe,CAAC;AAAA,EAClC,IAAI,OAAO,KAAK,GAAG,EAAE,SAAS;AAAA,IAAG,KAAK,0BAA0B,KAAK,IAAI;AAAA,EACzE,IAAI,MAAM,cAAc;AAAA,IAAW,KAAK,eAAe,MAAM;AAAA,EAC7D,IAAI,MAAM,cAAc;AAAA,IAAW,KAAK,eAAe,eAAe,MAAM,SAAS;AAAA,EACrF,IAAI,MAAM,kBAAkB;AAAA,IAAW,KAAK,mBAAmB,MAAM;AAAA,EACrE,MAAM,WAAW,MAAM;AAAA,EACvB,IAAI,aAAa,WAAW;AAAA,IAC1B,KAAK,2BAA2B,MAAM,QAAQ,QAAQ,IACjD,SAAyC,IAAI,YAAY,IAC1D,aAAa,QAA4B;AAAA,EAC/C;AAAA,EACA,IAAI,MAAM,qBAAqB;AAAA,IAAW,KAAK,sBAAsB,MAAM;AAAA,EAC3E,IAAI,MAAM,oBAAoB;AAAA,IAAW,KAAK,qBAAqB,MAAM;AAAA,EACzE,IAAI,MAAM,sBAAsB;AAAA,IAAW,KAAK,uBAAuB,MAAM;AAAA,EAC7E,IAAI,MAAM,qBAAqB;AAAA,IAAW,KAAK,sBAAsB,MAAM;AAAA,EAC3E,IAAI,MAAM,aAAa;AAAA,IAAW,KAAK,cAAc,MAAM;AAAA,EAC3D,IAAI,MAAM,cAAc;AAAA,IAAW,KAAK,eAAe,MAAM;AAAA,EAC7D,OAAO;AAAA;AAGF,IAAM,YAAY,CAAC,UACxB,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAG1C,IAAM,YAAY,CACvB,UACwD;AAAA,EACxD,MAAM,OAAO,YAAY,aAAa,KAAK,CAAC;AAAA,EAC5C,MAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,EAC3C,OAAO,EAAE,OAAO,QAAQ,UAAU,KAAK,GAAG,KAAK;AAAA;;;AExJ1C,IAAM,kBAAkB;AAE/B,IAAM,SAAS,CAAC,SAAqB;AAAA,EACnC,MAAM,UAAU,KAAK,WAAW;AAAA,EAChC,OAAO;AAAA,IACL,OAAO,GAAG,iBAAiB,KAAK;AAAA,IAChC,KAAK,GAAG,eAAe,KAAK;AAAA,EAC9B;AAAA;AAGK,IAAM,iBAAiB,CAAC,SAA+B;AAAA,EAC5D,MAAM,QAAkB,CAAC;AAAA,EACzB,MAAM,UAAU,KAAK,WAAW;AAAA,EAChC,IAAI,CAAC,YAAY,KAAK,OAAO,KAAK,QAAQ,SAAS,MAAQ,GAAG;AAAA,IAC5D,MAAM,KAAK,iDAAiD;AAAA,EAC9D;AAAA,EAGA,IAAI,CAAC,wCAAwC,KAAK,KAAK,IAAI,GAAG;AAAA,IAC5D,MAAM,KAAK,sEAAsE;AAAA,EACnF;AAAA,EACA,OAAO;AAAA;AAGT,IAAM,UAAS,CAAC,YAA2B,IAAI,MAAM,mBAAmB,SAAS;AAEjF,IAAM,YAAY,CAAC,MAAc,IAAuB,SACtD,QACE,GAAG,YAAY,KAAK,UAAU,IAAI,YAAY,GAAG,IAAI,CAAC,UAAU,QAAQ,CAAC,EAAE,KAAK,IAAI,SAClF,mEACJ;AAGF,IAAM,SAAS,CAAC,OAA0B,SAAqB;AAAA,EAC7D,QAAQ,OAAO,QAAQ,OAAO,IAAI;AAAA,EAClC,MAAM,SAAS,MAAM,QAAQ,CAAC,MAAM,UAAW,SAAS,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAE;AAAA,EAC7E,MAAM,OAAO,MAAM,QAAQ,CAAC,MAAM,UAAW,SAAS,MAAM,CAAC,KAAK,IAAI,CAAC,CAAE;AAAA,EACzE,IAAI,OAAO,SAAS;AAAA,IAAG,MAAM,UAAU,qBAAqB,QAAQ,KAAK;AAAA,EACzE,IAAI,KAAK,SAAS;AAAA,IAAG,MAAM,UAAU,mBAAmB,MAAM,GAAG;AAAA,EACjE,OAAO,QAAQ;AAAA,EACf,OAAO,MAAM;AAAA,EACb,IAAI,SAAS,aAAa,OAAO;AAAA,IAAW;AAAA,EAC5C,IAAI,SAAS,aAAa,OAAO,WAAW;AAAA,IAC1C,MAAM,QACJ,GAAG,KAAK,UAAU,KAAK,IAAI,WAAW,SAAS,YAAY,QAAQ,2BACjE,+EACJ;AAAA,EACF;AAAA,EACA,IAAI,KAAK;AAAA,IAAM,MAAM,QAAO,GAAG,KAAK,UAAU,KAAK,IAAI,uCAAuC;AAAA,EAC9F,OAAO,EAAE,MAAM,GAAG;AAAA;AASb,IAAM,eAAe,CAAC,MAAc,MAAkB,SAAyB;AAAA,EACpF,QAAQ,OAAO,QAAQ,OAAO,IAAI;AAAA,EAElC,MAAM,QAAQ,CAAC,OAAO,GAAI,SAAS,KAAK,CAAC,IAAI,KAAK,QAAQ,OAAO,EAAE,EAAE,MAAM;AAAA,CAAI,GAAI,GAAG;AAAA,EACtF,MAAM,QAAQ,KAAK,MAAM;AAAA,CAAI;AAAA,EAC7B,MAAM,QAAQ,OAAO,OAAO,IAAI;AAAA,EAChC,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,MAAM,IAAI,GAAG,GAAG,OAAO,GAAG,MAAM,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK;AAAA,CAAI;AAAA,EAC1F;AAAA,EACA,MAAM,SAAS,SAAS,MAAM,KAAK,SAAS;AAAA,CAAI,IAAI,OAAO,GAAG;AAAA;AAAA,EAC9D,OAAO,GAAG,SAAS,MAAM,KAAK;AAAA,CAAI;AAAA;AAAA;AAI7B,IAAM,aAAa,CAAC,MAAc,SAAyC;AAAA,EAChF,MAAM,QAAQ,KAAK,MAAM;AAAA,CAAI;AAAA,EAC7B,MAAM,QAAQ,OAAO,OAAO,IAAI;AAAA,EAChC,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,MAAM,OAAO,MAAM,MAAM,MAAM,OAAO,GAAG,MAAM,EAAE;AAAA,EACjD,OAAO,KAAK,WAAW,IAAI,KAAK,GAAG,KAAK,KAAK;AAAA,CAAI;AAAA;AAAA;AAS5C,IAAM,eAAe,CAAC,MAAc,SAA6B;AAAA,EACtE,MAAM,QAAQ,KAAK,MAAM;AAAA,CAAI;AAAA,EAC7B,MAAM,QAAQ,OAAO,OAAO,IAAI;AAAA,EAChC,IAAI,UAAU;AAAA,IAAW,OAAO;AAAA,EAChC,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK;AAAA,CAAI;AAAA;;;ACvDzE,IAAM,gBAAe;AAE5B,IAAM,oBAAmB,CAAC,MAAc,UAAiD;AAAA,EACvF,IAAI,UAAU;AAAA,IAAW,OAAO,CAAC;AAAA,EACjC,IAAI,OAAO,UAAU,UAAU;AAAA,IAC7B,OAAO,OAAO,cAAc,KAAK,KAAK,SAAS,IAC3C,CAAC,IACD,CAAC,GAAG,wCAAwC;AAAA,EAClD;AAAA,EACA,OAAO,6BAA6B,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,IACjE,CAAC,IACD,CAAC,GAAG,QAAQ,KAAK,UAAU,KAAK,qCAAqC;AAAA;AAGpE,IAAM,gBAAe,CAAC,UAAqC;AAAA,EAChE,MAAM,QAAQ,aAAa,MAAM,IAAI;AAAA,EACrC,MAAM,OAAO,MAAM,QAAQ;AAAA,EAC3B,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,OAAO;AAAA,IAAQ,MAAM,KAAK,uBAAuB;AAAA,EAChG,MAAM,KAAK,GAAG,kBAAiB,SAAS,MAAM,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,GAAG,kBAAiB,SAAS,MAAM,KAAK,CAAC;AAAA,EACpD,MAAM,KAAK,GAAG,mBAAmB,MAAM,OAAO,CAAC;AAAA,EAC/C,IAAI,MAAM,WAAW,WAAW;AAAA,IAC9B,MAAM,KAAK,GAAG,eAAe,MAAM,MAAM,CAAC;AAAA,IAG1C,IAAI,MAAM,WAAW,SAAS,MAAM,SAAS,aAAa,MAAM,UAAU,YAAY;AAAA,MACpF,MAAM,KACJ,2FACF;AAAA,IACF;AAAA,EACF,EAAO,SAAI,MAAM,WAAW,MAAM;AAAA,IAChC,MAAM,KAAK,kFAAkF;AAAA,EAC/F;AAAA,EACA,OAAO;AAAA;;;AC5EF,IAAM,UAAU,IAAI;AACpB,IAAM,UAAU,IAAI;AAEpB,IAAM,UAAS,CAAC,MAAc,YACnC,IAAI,MAAM,eAAe,SAAS,SAAS;AAE7C,IAAM,aAAY,CAAC,UACjB,OAAO,UAAU,WAAW,QAAQ,QAAQ,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI;AAKrE,IAAM,WAAW,OAAO,QAAoB,UAA8C;AAAA,EAC/F,MAAM,QAAQ,cAAa,KAAK;AAAA,EAChC,IAAI,MAAM,SAAS;AAAA,IAAG,MAAM,QAAO,MAAM,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,EAC/D,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI,MAAM,UAAU,WAAW;AAAA,IAC7B,MAAM,WAAU,MAAM,KAAK,MAAM,MAAM,OAAO,WAAW,OAAO,MAAM,KAAK,CAAC,IAAI;AAAA,IAChF,IAAI,QAAQ;AAAA,MAAW,MAAM,QAAO,MAAM,MAAM,WAAW,OAAO,MAAM,KAAK,gBAAgB;AAAA,EAC/F;AAAA,EACA,IAAI,MAAM,UAAU,WAAW;AAAA,IAC7B,MAAM,WAAU,MAAM,KAAK,KAAM,MAAM,OAAO,YAAY,OAAO,MAAM,KAAK,CAAC;AAAA,IAC7E,IAAI,QAAQ;AAAA,MAAW,MAAM,QAAO,MAAM,MAAM,YAAY,OAAO,MAAM,KAAK,gBAAgB;AAAA,EAChG;AAAA,EACA,OAAO,KAAM,QAAQ,YAAY,CAAC,IAAI,EAAE,IAAI,MAAQ,QAAQ,YAAY,CAAC,IAAI,EAAE,IAAI,EAAG;AAAA;AAGjF,IAAM,WAAW,CAAC,SAAyB,UAAU,QAAQ,OAAO,IAAI,CAAC;AAGzE,IAAM,cAAc,CACzB,MACA,WACuB;AAAA,EACvB,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,IAAI,WAAW;AAAA,IAAW,OAAO,SAAS,IAAI;AAAA,EAC9C,MAAM,OAAO,WAAW,MAAM,MAAM;AAAA,EACpC,OAAO,SAAS,YAAY,YAAY,SAAS,IAAI;AAAA;AAIhD,IAAM,qBAAqB,OAChC,QACA,MACA,WAC8C;AAAA,EAC9C,MAAM,OAAO,MAAM,OAAO,KAAK,IAAI;AAAA,EACnC,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,MAAM,QAAQ,KAAK,SAAS,SAAS,MAAM,OAAO,SAAS,IAAI,IAAI;AAAA,EACnE,MAAM,OAAO,UAAU,YAAY,YAAY,QAAQ,OAAO,KAAK;AAAA,EACnE,OAAO;AAAA,IACL,eAAe,YAAY,MAAM,MAAM,KAAK;AAAA,IAC5C,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,IACX;AAAA,OACI,WAAW,YAAY,CAAC,IAAI,EAAE,OAAO;AAAA,IACzC,QAAQ,UAAU,YAAY,KAAK,UAAU,KAAK;AAAA,IAClD,MAAM,KAAK;AAAA,IACX,KAAK,KAAK;AAAA,EACZ;AAAA;AAGF,IAAM,UAAU,CAAC,MAA4B,UAC3C,SAAS,aACT,KAAK,SAAS,MAAM,SACnB,MAAM,QAAQ,aAAa,KAAK,QAAQ,MAAM,SAC9C,MAAM,QAAQ,aAAa,KAAK,QAAQ,MAAM;AAiB1C,IAAM,YAAY,OACvB,QACA,OACA,SACkB;AAAA,EAClB,MAAM,OAAO,MAAM,OAAO,KAAK,MAAM,IAAI;AAAA,EACzC,IAAI,SAAS,aAAa,KAAK,SAAS,QAAQ;AAAA,IAC9C,MAAM,QACJ,MAAM,MACN,QAAQ,KAAK,gEACf;AAAA,EACF;AAAA,EACA,MAAM,UAAU,SAAS,YAAY,YAAY,MAAM,OAAO,SAAS,MAAM,IAAI;AAAA,EACjF,MAAM,SAAS,YAAY,YAAY,YAAY,QAAQ,OAAO,OAAO;AAAA,EACzE,MAAM,eAAe,MAAM,QAAQ;AAAA,EACnC,IAAI,MAAM,WAAW,WAAW;AAAA,IAC9B,MAAM,QAAsB,EAAE,MAAM,iBAAiB,KAAK;AAAA,IAC1D,OAAO;AAAA,MACL;AAAA,MACA,OAAO,QAAQ,OAAO,MAAM,OAAO;AAAA,MACnC,WAAW,WAAW,MAAM,WAAW,QAAQ,MAAM,KAAK;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EACA,IAAI,WAAW,WAAW;AAAA,IACxB,IAAI,MAAM,WAAW,MAAM;AAAA,MACzB,MAAM,QACJ,MAAM,MACN,0FACE,sDACJ;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL;AAAA,MACA,OAAO,QAAQ,OAAO,aAAa,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AAAA,MACnE,WAAW;AAAA,MACX,OAAO,EAAE,MAAM,iBAAiB,KAAK;AAAA,IACvC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,aAAa,QAAQ,MAAM,QAAQ,MAAM,OAAO;AAAA,EAM7D,OAAO;AAAA,IACL;AAAA,IACA,OAAO,QAAQ,OAAO,IAAI;AAAA,IAC1B,WAAW,SAAS;AAAA,IACpB,OAAO,EAAE,KAAK,MAAM,OAAO,GAAG,MAAM,MAAM,QAAQ,cAAc,KAAK,MAAM,OAAO,EAAE;AAAA,EACtF;AAAA;;;AClHK,IAAM,WAAW,OACtB,QACA,MACA,WACkB;AAAA,EAClB,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI;AAAA,EACxC,IAAI,KAAK,SAAS,OAAO,MAAM;AAAA,IAE7B,MAAM,OAAO,aAAa,KAAK,MAAM,EAAE,MAAM,KAAK,QAAQ,kBAAiB,KAAK,CAAC;AAAA,IACjF,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC7B;AAAA,EACA,MAAM,UAAU,MAAM,UAAU,QAAQ,MAAM,IAAI;AAAA,EAGlD,IAAI,QAAQ,aAAa,OAAO,kBAAkB,SAAS,KAAK,OAAO,GAAG;AAAA,IACxE,OAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAAA,EACA,MAAM,OAAO,aAAa,KAAK,MAAM,QAAQ,KAAK;AAAA,EAClD,OAAO,EAAE,QAAQ,SAAS;AAAA;AAWrB,IAAM,gBAAgB,OAC3B,QACA,OACA,QACA,QAAQ,UAC0B;AAAA,EAClC,MAAM,OAAO,MAAM,SAAS,QAAQ,KAAK;AAAA,EAEzC,IAAI,KAAK,QAAQ,aAAa,KAAK,QAAQ,OAAO,aAAa,KAAK,CAAC,aAAa,MAAM,GAAG;AAAA,IACzF,MAAM,QACJ,MAAM,MACN,SAAS,OAAO,MAAM,KAAK,sEACzB,uFACJ;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,WAAW,aAAa,OAAO,SAAS,MAAM;AAAA,EAC5D,MAAM,QAAQ,QAAQ,YAAY;AAAA,EAClC,MAAM,UAAU,MAAM,UAAU,QAAQ,OAAO,IAAI;AAAA,EACnD,MAAM,OAAO,YAAY,QAAQ,QAAQ,MAAM,MAAM;AAAA,EACrD,MAAM,WAAW,SAAS,MAAM,OAAO;AAAA,EAOvC,IACE,UAAU,aACV,SAAS,aACT,SAAS,YACT,EAAE,SAAS,WAAW,YACtB;AAAA,IACA,MAAM,QACJ,MAAM,MACN,GAAG,MAAM,WAAW,YAAY,mBAAmB,0CACjD,2FACJ;AAAA,EACF;AAAA,EACA,IAAI,CAAC,QAAQ;AAAA,IAAW,MAAM,OAAO,gBAAgB,MAAM,MAAM,QAAQ,OAAO,QAAQ,KAAK;AAAA,EAK7F,MAAM,QAAQ,MAAM,mBAAmB,QAAQ,MAAM,MAAM,MAAM,MAAM;AAAA,EACvE,IACE,UAAU,aACV,MAAM,WAAW,UAAU,QAAQ,KAAK,KACxC,MAAM,kBAAkB,UACxB;AAAA,IAMA,IAAI,QAAQ,WAAW,aAAa,MAAM,WAAW,WAAW;AAAA,MAC9D,MAAM,OAAO,WAAW,MAAM,IAAI,EAAE,MAAM,MAAG;AAAA,QAAG;AAAA,OAAS;AAAA,IAC3D;AAAA,IACA,MAAM,QACJ,MAAM,MACN,wEACF;AAAA,EACF;AAAA,EAGA,IAAI,SAAS,WAAW;AAAA,IAAW,MAAM,WAAW,QAAQ,MAAM;AAAA,EAClE,OAAO;AAAA;AAOF,IAAM,aAAa,OACxB,QACA,WACkB;AAAA,EAClB,MAAM,OAAO,MAAM,OAAO,KAAK,OAAO,IAAI;AAAA,EAC1C,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,IAAI,KAAK,SAAS;AAAA,IAAQ,MAAM,QAAO,OAAO,MAAM,YAAY,KAAK,uBAAuB;AAAA,EAC5F,IAAI,OAAO,WAAW,WAAW;AAAA,IAC/B,MAAM,OAAO,WAAW,OAAO,IAAI;AAAA,IACnC;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,OAAO,SAAS,OAAO,IAAI;AAAA,EAC/C,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,MAAM,SAAS,QAAQ,OAAO,KAAK;AAAA,EACnC,MAAM,OAAO,aAAa,QAAQ,OAAO,MAAM;AAAA,EAC/C,IAAI,SAAS;AAAA,IAAQ;AAAA,EACrB,MAAM,OAAO,gBAAgB,OAAO,MAAM,QAAQ,OAAO,IAAI,GAAG;AAAA,IAC9D,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,IACX,KAAK,KAAK;AAAA,EACZ,CAAC;AAAA;;;ANpHI,IAAM,aAAa,UAAqB,aAAa;AAErD,IAAM,qBAAqB,MACvB,iBACP,YACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAO;AAAA,EACtB,OAAO,WAAW,SAAS,GAAG;AAAA,IAE5B,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAE7B,MAAM,GAAG,MAAM,aACb,KAAK,YAAY;AAAA,MACf,MAAM,QAAQ,MAAM,mBAAmB,QAAQ,KAAK,MAAM,KAAK,MAAM;AAAA,MACrE,IAAI,UAAU;AAAA,QAAW;AAAA,MAIzB,IAAI,KAAK,WAAW,aAAa,MAAM,kBAAkB;AAAA,QAAI;AAAA,MAC7D,OAAO,WAAW,YAAY,SAAQ,KAAK,IAAI;AAAA,KAChD;AAAA,IAEH,MAAM,GAAG,YAAY,MAAM,aAAa;AAAA,MAEtC,IAAI,WAAW;AAAA,QAAW,OAAO,eAAe,UAAU;AAAA,MAC1D,IAAI,YAAW,IAAI;AAAA,QAAG,OAAO,KAAK,MAAM,SAAS,QAAQ,MAAM,MAAM,CAAC;AAAA,MAItE,MAAM,OAAO,eAAe,MAAM,MAAM;AAAA,MACxC,OAAc,gBACZ,SAAS,aAAa,SAAS,OAAO,OAAO,EAAE,QAAQ,UAAmB,IAAI,SAChF;AAAA;AAAA,IAIF,WAAW,GAAG,KAAK,YAAY,MAAM,aAC5B,gBAAQ,cAAc,EAAE,KAAK,YAAY,OAAO,CAAC,GAAG,CAAC,UAC1D,KAAK,MAAM,cAAc,QAAQ,MAAM,QAAQ,KAAK,CAAC,CACvD;AAAA,IAEF,QAAQ,GAAG,aAAa,KAAK,MAAM,WAAW,QAAQ,MAAM,CAAC;AAAA,EAC/D,CAAC;AAAA,CACF,CACH;;;AOlEF,qBAAS;AACT;AACA;;;ACfA,oBAAS;AAET,uBAAS;AAET;;;ACoDO,IAAM,yBAAyB;AAW/B,IAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,aAAa,EAAE,KAAK,GAAG,MAAM,KAAO,KAAK,EAAE;AAEjD,IAAM,YAAW,CAAC,SAAyB,UAAU,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAGnF,IAAM,eAAe,CAAC,cAC3B,WAAU,aAAa,CAAC,GAAG,KAAK;AAAA,CAAI,CAAC;AAGhC,IAAM,aAAa,CAAC,aACzB,GAAG,SACA,IAAI,CAAC,YACJ,CAAC,IAAI,QAAQ,SAAS,GAAG,QAAQ,MAAM,IAAI,EAAE,KAAK,WAAW,GAAG,OAAO,OAAO,CAAC,EAAE,KAAK;AAAA,CAAI,CAC5F,EACC,KAAK;AAAA;AAAA,CAAM;AAAA;AAET,IAAM,WAAW,CAAC,UACvB,MAAM,WAAW,WAAW,MAAM,YAAY,CAAC,CAAC;AAE3C,IAAM,cAAc,CAAC,UAC1B,GAAG,MAAM,aAAa,0BAA0B,MAAM;AAExD,IAAM,OAAO;AAEb,IAAM,QAAQ,IAAI,IAAI;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,eAAe,IAAI,IAAI,CAAC,YAAY,cAAc,cAAc,QAAQ,OAAO,CAAC;AAU/E,IAAM,eAAe,CAAC,OAAyB,WAA8B;AAAA,EAClF,MAAM,QAAkB,CAAC;AAAA,EACzB,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI,IAAI;AAAA,EACrC,IAAI,SAAS,aAAa,CAAC,MAAM,IAAI,IAAI,GAAG;AAAA,IAC1C,MAAM,KACJ,4DAA4D,KAAK,UAAU,MAAM,IAAI,GACvF;AAAA,EACF,EAAO,SAAI,WAAW,aAAa,SAAS,QAAQ;AAAA,IAClD,MAAM,KAAK,6BAA6B,gBAAgB,MAAM,cAAc,MAAM;AAAA,EACpF;AAAA,EACA,IAAK,MAAM,YAAY,eAAgB,MAAM,aAAa,YAAY;AAAA,IACpE,MAAM,KAAK,4CAA4C;AAAA,EACzD;AAAA,EACA,MAAM,YAAY,MAAM,aAAa;AAAA,EACrC,IAAI,CAAC,UAAU,WAAW,GAAG,KAAK,UAAU,SAAS,GAAG,KAAK,UAAU,SAAS,KAAK,GAAG;AAAA,IACtF,MAAM,KAAK,uEAAuE;AAAA,EACpF;AAAA,EACA,MAAM,KAAK,GAAG,aAAa,SAAS,KAAK,CAAC,CAAC;AAAA,EAC3C,IAAI,MAAM,YAAY,SAAS,CAAC,wBAAwB,SAAS,SAAkB,GAAG;AAAA,IACpF,MAAM,KACJ,iEAAiE,wBAAwB,KAAK,IAAI,SAChG,GAAG,mEACP;AAAA,EACF;AAAA,EACA,IAAI,MAAM,YAAY,SAAS,CAAC,WAAW,KAAK,GAAG;AAAA,IACjD,MAAM,KACJ,8FACE,6FACA,2DACJ;AAAA,EACF;AAAA,EACA,IAAI,SAAS,WAAW,CAAC,SAAS,KAAK,EAAE,SAAS,SAAS,GAAG;AAAA,IAC5D,MAAM,KAAK,kCAAkC;AAAA,EAC/C;AAAA,EACA,OAAO;AAAA;AAGT,IAAM,aAAa,CAAC,UAAqC;AAAA,EACvD,MAAM,OAAO,SAAS,KAAK;AAAA,EAC3B,IAAI,CAAC,KAAK,SAAS,WAAW;AAAA,IAAG,OAAO;AAAA,EACxC,MAAM,QAAQ,KAAK,MAAM,KAAK,QAAQ,WAAW,CAAC;AAAA,EAClD,OAAO,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAO,IAAI,QAAQ,GAAG,EAAE,KAAK,KAAK,CAAC;AAAA;AAGhF,IAAM,eAAe,CAAC,SAA2B;AAAA,EAC/C,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,KAAK,KAAK,MAAM;AAAA,IAAI,MAAM,KAAK,wBAAwB;AAAA,EAC3D,IAAI,KAAK,SAAS,MAAQ;AAAA,IAAG,MAAM,KAAK,4BAA4B;AAAA,EACpE,IAAI,CAAC,mBAAmB,KAAK,IAAI;AAAA,IAAG,MAAM,KAAK,yCAAyC;AAAA,EACxF,OAAO;AAAA;;;ACtJF,IAAM,YAAY;AAGlB,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAAA;AAEO,MAAM,uBAAuB,MAAM;AAAA,EACxC,WAAW,CAAC,SAAiB,UAAkB,QAAgB;AAAA,IAC7D,MAAM,aAAa,cAAc,OAAO,QAAQ,MAAM,OAAO,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG;AAAA,IACnF,KAAK,OAAO;AAAA;AAEhB;AAkBO,IAAM,YAAY,CAAC,WAAwC;AAAA,EAChE,MAAM,SAAS,IAAI;AAAA,EACnB,WAAW,QAAQ,OAAO,MAAM;AAAA,CAAI,GAAG;AAAA,IACrC,MAAM,KAAK,KAAK,QAAQ,GAAG;AAAA,IAC3B,IAAI,MAAM;AAAA,MAAG;AAAA,IACb,MAAM,MAAM,KAAK,MAAM,GAAG,EAAE;AAAA,IAC5B,IAAI,CAAC,OAAO,IAAI,GAAG;AAAA,MAAG,OAAO,IAAI,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,EAC1D;AAAA,EACA,OAAO;AAAA;AAGF,IAAM,WAAW,CAAC,WAA+B;AAAA,EACtD,MAAM,SAAS,UAAU,MAAM;AAAA,EAC/B,MAAM,YAAY,OAAO,IAAI,WAAW,KAAK;AAAA,EAC7C,MAAM,gBAAgB,OAAO,IAAI,eAAe,KAAK;AAAA,EACrD,MAAM,eAAe,OAAO,IAAI,cAAc,KAAK;AAAA,EACnD,MAAM,WAAW,OAAO,IAAI,UAAU,KAAK;AAAA,EAC3C,OAAO;AAAA,IACL,aAAa,OAAO,IAAI,aAAa,KAAK;AAAA,IAG1C,OAAO,cAAc,eAAe,kBAAkB;AAAA,IACtD;AAAA,IACA,kBAAkB,OAAO,IAAI,kBAAkB,MAAM;AAAA,OACjD,aAAa,KAAK,CAAC,IAAI,EAAE,SAAS;AAAA,OAClC,kBAAkB,KAAK,CAAC,IAAI,EAAE,cAAc;AAAA,OAC5C,iBAAiB,KAAK,CAAC,IAAI,EAAE,aAAa;AAAA,EAChD;AAAA;AAGK,IAAM,WAAW,OAAO,QAAoB,SAAsC;AAAA,EACvF,MAAM,SAAS,MAAM,OAAO,KAAK;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,KAAK,GAAG;AAAA,IACxB;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EAID,IAAI,OAAO,aAAa;AAAA,IACtB,MAAM,IAAI,eAAe,QAAQ,QAAQ,OAAO,UAAU,OAAO,MAAM;AAAA,EACzE,OAAO,SAAS,OAAO,MAAM;AAAA;AAI/B,IAAM,MAAM,OAAO,QAAoB,SAA2C;AAAA,EAChF,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;AAAA,EACrD,IAAI,OAAO,aAAa,GAAG;AAAA,IACzB,MAAM,IAAI,eAAe,KAAK,KAAK,GAAG,GAAG,OAAO,UAAU,OAAO,MAAM;AAAA,EACzE;AAAA;AAIK,IAAM,eAAe,CAAC,WAAsC,IAAI,QAAQ,CAAC,eAAe,CAAC;AAEzF,IAAM,aAAa,CAAC,QAAoB,SAC7C,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,CAAC;AAE7B,IAAM,cAAc,CAAC,QAAoB,SAC9C,IAAI,QAAQ,CAAC,WAAW,MAAM,IAAI,CAAC;AAE9B,IAAM,YAAY,CAAC,QAAoB,SAC5C,IAAI,QAAQ,CAAC,SAAS,MAAM,IAAI,CAAC;AAE5B,IAAM,WAAW,CAAC,QAAoB,SAC3C,IAAI,QAAQ,CAAC,QAAQ,MAAM,IAAI,CAAC;AAO3B,IAAM,cAAc,CAAC,QAAoB,SAC9C,IAAI,QAAQ,CAAC,WAAW,MAAM,IAAI,CAAC;;;ACpH9B,IAAM,UAAS,CAAC,MAAc,YACnC,IAAI,MAAM,gBAAgB,SAAS,SAAS;AAEvC,IAAM,eAAe,CAC1B,OACA,YACA,YAC2B;AAAA,EAC3B,QAAQ,OAAO,gBAAgB,YAAY,OAAO,gBAAgB;AAAA,EAClE,aAAa,OAAO;AAAA,EACpB,cAAc,aAAa,MAAM,SAAS;AAAA,EAC1C,SAAS,OAAO,kBAAkB,aAAa,OAAO,kBAAkB;AAAA,EACxE,WAAW,OAAO;AAAA,EAClB,MAAM,MAAM;AAAA,EACZ,UAAU,YAAY,KAAK;AAAA,EAC3B;AAAA,KACI,OAAO,kBAAkB,YAAY,CAAC,IAAI,EAAE,eAAe,OAAO,cAAc;AACtF;AAiBO,IAAM,SAAS,OACpB,QACA,OACA,SACmC;AAAA,EACnC,MAAM,cAAc,MAAM,YAAY;AAAA,EACtC,MAAM,cAAc,MAAM,YAAY;AAAA,EACtC,MAAM,YACJ,KAAK,OAAO,kBAAkB,aAAa,KAAK,OAAO,kBAAkB;AAAA,EAC3E,MAAM,OAAO,OAAO,WAAmC;AAAA,IAGrD,IAAI,KAAK,WAAW,WAAW;AAAA,MAC7B,MAAM,OAAO,WAAW,KAAK,IAAI,EAAE,MAAM,MAAG;AAAA,QAAG;AAAA,OAAS;AAAA,MACxD,MAAM,aAAa,MAAM,EAAE,MAAM,MAAG;AAAA,QAAG;AAAA,OAAS;AAAA,IAClD;AAAA,IACA,MAAM,QAAO,MAAM,MAAM,GAAG,iCAAiC;AAAA;AAAA,EAE/D,IAAI;AAAA,IACF,IAAI,gBAAgB,CAAC,aAAa,KAAK;AAAA,MAAU,MAAM,WAAW,QAAQ,MAAM,IAAI;AAAA,IACpF,IAAI,CAAC,eAAe;AAAA,MAAW,MAAM,YAAY,QAAQ,MAAM,IAAI;AAAA,IACnE,MAAM,SAAS,KAAK,OAAO,gBAAgB,YAAY,KAAK,OAAO,gBAAgB;AAAA,IACnF,IAAI,CAAC,aAAa;AAAA,MAChB,IAAI;AAAA,QAAQ,MAAM,SAAS,QAAQ,MAAM,IAAI;AAAA,IAC/C,EAAO,SAAI,CAAC;AAAA,MAAQ,MAAM,UAAU,QAAQ,MAAM,IAAI;AAAA,IACjD,SAAI,KAAK,WAAW,KAAK;AAAA,MAAe,MAAM,YAAY,QAAQ,MAAM,IAAI;AAAA,IACjF,OAAO,OAAO;AAAA,IACd,OAAO,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA;AAAA,EAQpE,MAAM,QAAQ,MAAM,SAAS,QAAQ,MAAM,IAAI;AAAA,EAC/C,IAAI,eAAe,MAAM,gBAAgB,YAAY,MAAM,gBAAgB,cAAc;AAAA,IACvF,OAAO,KAAK,8CAA8C,MAAM,cAAc;AAAA,EAChF;AAAA,EACA,MAAM,UAAU,aAAa,OAAO,KAAK,SAAS,KAAK;AAAA,EACvD,IAAI,eAAe,CAAC,QAAQ,SAAS;AAAA,IACnC,OAAO,KACL,kEACE,GAAG,KAAK,UAAU,MAAM,iBAAiB,EAAE,iBAC/C;AAAA,EACF;AAAA,EACA,OAAO;AAAA;;;ACzET,IAAM,WAAU,IAAI;AACpB,IAAM,WAAU,IAAI;AAGb,IAAM,cAAc,CAAC,OAAyB,WAA0B;AAAA,EAC7E,MAAM,QAAQ,aAAa,OAAO,MAAM;AAAA,EACxC,IAAI,MAAM,SAAS;AAAA,IAAG,MAAM,QAAO,MAAM,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA;AAI1D,IAAM,WAAW,OACtB,QACA,UAC+C;AAAA,EAC/C,MAAM,QAAQ,MAAM,OAAO,SAAS,YAAY,KAAK,CAAC;AAAA,EACtD,MAAM,SAAS,MAAM,SAAS,QAAQ,MAAM,IAAI;AAAA,EAChD,IAAI,UAAU,aAAa,CAAC,OAAO;AAAA,IAAO;AAAA,EAC1C,OAAO,aAAa,OAAO,UAAU,YAAY,KAAK,UAAS,SAAQ,OAAO,KAAK,CAAC,GAAG,MAAM;AAAA;AAIxF,IAAM,iBAAiB,CAAC,QAAoB,SAAuB;AAAA,EACxE,IAAI,aAAa,MAAM;AAAA,IAAG;AAAA,EAC1B,MAAM,QACJ,MACA,8FACE,6FACJ;AAAA;AAGF,IAAM,eAAe,CAAC,OAAyB,WAA6B;AAAA,EAC1E,IAAI,OAAO,cAAc,YAAY,OAAO,kBAAkB,UAAU;AAAA,IACtE,MAAM,QACJ,MAAM,MACN,6FACE,cACJ;AAAA,EACF;AAAA;AAGK,IAAM,WAAW,OACtB,QACA,MACA,QACA,WACkB;AAAA,EAClB,YAAY,MAAM,MAAM;AAAA,EAGxB,IAAI,KAAK,SAAS,OAAO,QAAQ,YAAY,IAAI,MAAM,OAAO,UAAU;AAAA,IACtE,eAAe,QAAQ,KAAK,IAAI;AAAA,IAChC,OAAO,EAAE,QAAQ,WAAW,aAAa,KAAK;AAAA,EAChD;AAAA,EACA,MAAM,WAAU,UAAS,SAAS,IAAI,CAAC;AAAA,EACvC,MAAM,SAAS,YAA2B;AAAA,IACxC,eAAe,QAAQ,KAAK,IAAI;AAAA,IAChC,MAAM,OAAO,aAAa,YAAY,IAAI,GAAG,UAAU;AAAA,IACvD,OAAO,EAAE,QAAQ,SAAS;AAAA;AAAA,EAI5B,IAAI,aAAY,OAAO;AAAA,IAAY,OAAO,OAAO;AAAA,EACjD,IAAI,aAAa,KAAK,SAAS,MAAM,OAAO;AAAA,IAAc,OAAO,OAAO;AAAA,EACxE,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI;AAAA,EACxC,IAAI,SAAS,aAAa,KAAK,eAAe;AAAA,IAAS,OAAO,OAAO;AAAA,EACrE,IAAI,KAAK,aAAa,KAAK,YAAY;AAAA,IAAQ,OAAO,OAAO;AAAA,EAC7D,IAAI,KAAK,YAAY,KAAK,YAAY;AAAA,IAAQ,OAAO,OAAO;AAAA,EAG5D,MAAM,SAAS,MAAM,SAAS,QAAQ,KAAK,IAAI;AAAA,EAC/C,OAAO,OAAO,mBAAmB,OAAO,IAAI,EAAE,QAAQ,OAAO;AAAA;AASxD,IAAM,gBAAgB,OAC3B,QACA,OACA,QACA,QAAQ,OACR,WACmC;AAAA,EACnC,YAAY,OAAO,MAAM;AAAA,EACzB,eAAe,QAAQ,MAAM,IAAI;AAAA,EACjC,MAAM,OAAO,YAAY,KAAK;AAAA,EAC9B,MAAM,SAAS,MAAM,SAAS,QAAQ,MAAM,IAAI;AAAA,EAChD,aAAa,OAAO,MAAM;AAAA,EAC1B,MAAM,OAAO,SAAS,KAAK;AAAA,EAC3B,MAAM,WAAU,UAAS,IAAI;AAAA,EAC7B,MAAM,SAAS,MAAM,OAAO,SAAS,IAAI;AAAA,EACzC,MAAM,UAAU,WAAW,YAAY,YAAY,UAAS,SAAQ,OAAO,MAAM,CAAC;AAAA,EAClF,MAAM,QAAQ,WAAW,cAAc,OAAO,SAAS,MAAM,QAAQ,OAAO,aAAa;AAAA,EACzF,MAAM,QAAQ,QAAQ,YAAY;AAAA,EAOlC,IACE,UAAU,aACV,EAAE,SAAS,WAAW,cACtB,YAAY,aACZ,YAAY,UACZ;AAAA,IACA,MAAM,QACJ,MAAM,MACN,GAAG,0FACD,uCACJ;AAAA,EACF;AAAA,EACA,IAAI,SAAS,WAAW;AAAA,IAAW,MAAM,WAAW,QAAQ,MAAM;AAAA,EAClE,MAAM,QAAQ,YAAY;AAAA,EAU1B,MAAM,QAAQ,UAAU,aAAa,MAAM,eAAe;AAAA,EAC1D,MAAM,gBAAgB,UAAU,aAAa,MAAM,iBAAiB,aAAa,MAAM,SAAS;AAAA,EAChG,IAAI;AAAA,IAAO,MAAM,OAAO,gBAAgB,MAAM,SAAQ,OAAO,IAAI,GAAG,UAAU;AAAA,EAC9E,IAAI,SAAS,OAAO;AAAA,IAAkB,MAAM,aAAa,MAAM;AAAA,EAC/D,MAAM,UAAU,SAAS,SAAS,OAAO;AAAA,EACzC,OAAO,OAAO,QAAQ,OAAO,EAAE,QAAQ,SAAS,eAAe,mBAAS,MAAM,OAAO,CAAC;AAAA;AAQjF,IAAM,aAAa,OACxB,QACA,WACkB;AAAA,EAClB,eAAe,QAAQ,OAAO,IAAI;AAAA,EAClC,MAAM,SAAS,MAAM,SAAS,QAAQ,OAAO,IAAI;AAAA,EACjD,IAAI,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,cAAc;AAAA,IAC1E,MAAM,SAAS,QAAQ,OAAO,IAAI;AAAA,EACpC;AAAA,EACA,IAAI,OAAO,kBAAkB,aAAa,OAAO,kBAAkB,mBAAmB;AAAA,IACpF,MAAM,YAAY,QAAQ,OAAO,IAAI;AAAA,EACvC;AAAA,EACA,MAAM,OAAO,MAAM,OAAO,KAAK,OAAO,QAAQ;AAAA,EAC9C,IAAI,SAAS,WAAW;AAAA,IACtB,IAAI,KAAK,SAAS;AAAA,MAAQ,MAAM,QAAO,OAAO,MAAM,GAAG,OAAO,iBAAiB,KAAK,MAAM;AAAA,IAC1F,MAAM,OAAO,WAAW,OAAO,QAAQ;AAAA,EACzC;AAAA,EACA,MAAM,aAAa,MAAM;AAAA;;;AJ3KpB,IAAM,cAAc,CACzB,QACA,MACA,WAEA,KAAK,YAAY;AAAA,EACf,MAAM,QAAQ,MAAM,SAAS,QAAQ,IAAI;AAAA,EACzC,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,OAAO,WAAW,YAAY,SAAQ,KAAK,IAAI;AAAA,CAChD;AAEI,IAAM,cAAc,CACzB,QACA,YACA,MACA,QACA,WAC2C;AAAA,EAE3C,IAAI,WAAW;AAAA,IAAW,OAAO,eAAe,UAAU;AAAA,EAC1D,IAAI,YAAW,IAAI;AAAA,IAAG,OAAO,KAAK,MAAM,SAAS,QAAQ,MAAM,QAAQ,MAAM,CAAC;AAAA,EAO9E,MAAM,OAAO,eAAe,MAAM,MAAM;AAAA,EACxC,OAAc,gBACZ,SAAS,aAAa,SAAS,OAAO,OAClC,EAAE,QAAQ,WAAoB,aAAa,KAAK,IAChD,SACN;AAAA;AAGK,IAAM,mBAAmB,CAC9B,QACA,MAMA,WAEO,gBAAQ,cAAc,IAAI,GAAG,CAAC,UACnC,KAAK,MAAM,cAAc,QAAQ,KAAK,MAAM,KAAK,QAAQ,OAAO,MAAM,CAAC,CACzE;AAEK,IAAM,gBAAgB,CAC3B,QACA,WAC+B,KAAK,MAAM,WAAW,QAAQ,MAAM,CAAC;;;AD9B/D,IAAM,cAAc,UAAsB,cAAc;AACxD,IAAM,eAAe,UAAuB,eAAe;AAE3D,IAAM,sBAAsB,MACxB,iBACP,aACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAO;AAAA,EACtB,OAAO,YAAY,SAAS,GAAG;AAAA,IAM7B,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,GAAG,MAAM,aAAa,YAAY,QAAQ,MAAM,MAAM;AAAA,IAC5D,MAAM,GAAG,YAAY,MAAM,aAAa,YAAY,QAAQ,YAAY,MAAM,MAAM;AAAA,IACpF,WAAW,CAAC,SAAS,iBAAiB,QAAQ,IAAI;AAAA,IAClD,QAAQ,GAAG,aAAa,cAAc,QAAQ,MAAM;AAAA,EACtD,CAAC;AAAA,CACF,CACH;AAEK,IAAM,uBAAuB,MACzB,iBACP,cACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAO;AAAA,EACtB,OAAO,aAAa,SAAS,GAAG;AAAA,IAC9B,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,GAAG,MAAM,aAAa,YAAY,QAAQ,MAAM,MAAM;AAAA,IAE5D,MAAM,GAAG,YAAY,MAAM,aACzB,YAAY,QAAQ,YAAY,MAAM,QAAQ,OAAO;AAAA,IACvD,WAAW,CAAC,SAAS,iBAAiB,QAAQ,MAAM,OAAO;AAAA,IAC3D,QAAQ,GAAG,aAAa,cAAc,QAAQ,MAAM;AAAA,EACtD,CAAC;AAAA,CACF,CACH;;;AR7DK,IAAM,iBAAiB,CAAC,WACvB,gBACJ,sBAAsB,GACtB,mBAAmB,GACnB,qBAAqB,GACrB,oBAAoB,CACtB,EAAE,KAAW,eAAQ,gBAAgB,MAAM,CAAC,CAAC;;AcE/C;AACA;;;ACPA,IAAM,UAAU;AAGT,IAAM,YAAY,CAAC,UAAkB,aAC1C,aAAa,WACT;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AAAA,EACjC;AACF,IACA,CAAC,UAAU,UAAU,QAAQ;AAG5B,IAAM,aAAa,CAAC,UAAkB,aAC3C,aAAa,WACT;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AAAA,EACjC;AACF,IACA,CAAC,UAAU,SAAS,QAAQ;AAGlC,IAAM,cAAc,CAAC,WAAwC;AAAA,EAC3D,MAAM,SAAS,IAAI;AAAA,EACnB,WAAW,QAAQ,OAAO,MAAM;AAAA,CAAI,GAAG;AAAA,IACrC,IAAI,KAAK,KAAK,MAAM,IAAI;AAAA,MACtB,IAAI,OAAO,OAAO;AAAA,QAAG;AAAA,MACrB;AAAA,IACF;AAAA,IACA,MAAM,KAAK,KAAK,QAAQ,IAAI;AAAA,IAC5B,IAAI,MAAM;AAAA,MAAG;AAAA,IACb,MAAM,MAAM,KAAK,MAAM,GAAG,EAAE;AAAA,IAC5B,IAAI,QAAQ;AAAA,MAAY;AAAA,IACxB,OAAO,IAAI,KAAK,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC;AAAA,EAC3C;AAAA,EACA,OAAO;AAAA;AAGT,IAAM,OAAO,CAAC,SACZ,SAAS,aAAa,QAAQ,KAAK,IAAI,IAAI,OAAO,IAAI,IAAI;AAErD,IAAM,YAAY,CAAC,UAAkB,WAAyC;AAAA,EACnF,IAAI,aAAa,UAAU;AAAA,IACzB,MAAM,SAAS,YAAY,MAAM;AAAA,IACjC,MAAM,OAAM,KAAK,OAAO,IAAI,KAAK,CAAC;AAAA,IAClC,MAAM,OAAM,KAAK,OAAO,IAAI,KAAK,CAAC;AAAA,IAClC,MAAM,QAAO,OAAO,IAAI,KAAK;AAAA,IAC7B,OAAO,SAAQ,aAAa,SAAQ,aAAa,UAAS,YACtD,YACA,EAAE,WAAK,aAAM,UAAI;AAAA,EACvB;AAAA,EAEA,MAAM,UAAU,OAAO,MAAM;AAAA,CAAI,EAAE,MAAM,IAAI,MAAM,GAAG;AAAA,EACtD,MAAM,MAAM,KAAK,OAAO,EAAE;AAAA,EAC1B,MAAM,MAAM,KAAK,OAAO,EAAE;AAAA,EAC1B,MAAM,OAAO,OAAO;AAAA,EACpB,OAAO,QAAQ,aAAa,QAAQ,aAAa,SAAS,aAAa,SAAS,KAC5E,YACA,EAAE,KAAK,MAAM,IAAI;AAAA;AAGhB,IAAM,aAAa,CAAC,UAAkB,WAAuC;AAAA,EAClF,IAAI,aAAa;AAAA,IAAU,OAAO,KAAK,YAAY,MAAM,EAAE,IAAI,KAAK,CAAC;AAAA,EAErE,OAAO,MAAM,OAAO,MAAM;AAAA,CAAI,EAAE,MAAM,IAAI,MAAM,GAAG,EAAE,EAAE;AAAA;;;ACtElD,IAAM,SAAS;AAMf,IAAM,aAAa,CAAC,QAAwB;AAAA,EACjD,IAAI,IAAI,SAAS,MAAM;AAAA,IAAG,MAAM,IAAI,MAAM,+BAA+B;AAAA,EACzE,OAAO,IAAI,IAAI,WAAW,KAAK,OAAO;AAAA;AAIjC,IAAM,YAAY,CAAC,SAAoC,KAAK,IAAI,UAAU,EAAE,KAAK,GAAG;AAE3F,IAAM,SAAS,CAAC,UAAkB,WAAW;AAetC,IAAM,cAAc,CAAC,QAAgB,UAC1C;AAAA,EAAM;AAAA;AAAA;AAAA,aAAkC,OAAO,KAAK;AAAA;AAU/C,IAAM,cAAc,CAAC,QAAgB,UAAsC;AAAA,EAChF,MAAM,KAAK,OAAO,YAAY;AAAA,EAAK,OAAO,KAAK,GAAG;AAAA,EAClD,IAAI,KAAK;AAAA,IAAG;AAAA,EACZ,MAAM,SAAS,aAAa,KAAK,OAAO,MAAM,KAAK,OAAO,KAAK,EAAE,SAAS,CAAC,CAAC;AAAA,EAC5E,IAAI,SAAS,OAAO;AAAA,IAAW;AAAA,EAC/B,OAAO,EAAE,UAAU,OAAO,OAAO,EAAE,GAAG,QAAQ,OAAO,MAAM,GAAG,EAAE,EAAE;AAAA;AAY7D,IAAM,UAAU,CACrB,MACA,QACA,YACsB;AAAA,EACtB,IAAI,SAAS,MAAM,KAAK,WAAW,GAAG,GAAG;AAAA,IACvC,MAAM,IAAI,MAAM,QAAQ,KAAK,UAAU,IAAI,+BAA+B;AAAA,EAC5E;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,OAAO,QAAQ,iBAAiB;AAAA,IAClD,GAAI,QAAQ,WAAW,CAAC;AAAA,IACxB;AAAA,IACA,cAAc,WAAW,MAAM;AAAA,EACjC;AAAA;;;AC9EF,IAAM,aAAa,CAAC,SAAiB,WAAW,kBAAkB;AAE3D,IAAM,aAAa,CAAC,SAAyB;AAAA,EAClD,MAAM,SAAS,WAAW,IAAI;AAAA,EAC9B,OAAO,GAAG,WAAW,MAAM,iCAAiC,qBAAqB,OAAO,MAAM;AAAA;AAIzF,IAAM,aAAa,CAAC,SAAyB;AAAA,EAClD,MAAM,SAAS,WAAW,IAAI;AAAA,EAC9B,OAAO,GAAG,WAAW,MAAM,eAAe,qBAAqB,OAAO,MAAM;AAAA;AAKvE,IAAM,eAAe,CAAC,SAAyB,YAAY,WAAW,IAAI;AAE1E,IAAM,aAAa,CAAC,SAAoC,UAAU,IAAI;AAiBtE,IAAM,cAAc,CAAC,MAAc,MAAc,YAAkC;AAAA,EACxF,MAAM,SAAS,WAAW,IAAI;AAAA,EAC9B,MAAM,SAAS,WAAW,IAAI;AAAA,EAC9B,MAAM,OAAO,cAAc;AAAA,EAK3B,MAAM,QACJ,QAAQ,QAAQ,YACZ,QAAQ,QAAQ,YACd,YACA,IAAI,OAAO,QAAQ,GAAG,MACxB,QAAQ,QAAQ,YACd,OAAO,QAAQ,GAAG,IAClB,GAAG,OAAO,QAAQ,GAAG,KAAK,OAAO,QAAQ,GAAG;AAAA,EACpD,MAAM,QAAQ,UAAU,YAAY,CAAC,IAAI,CAAC,SAAS,YAAY,aAAa,YAAY;AAAA,EACxF,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,IACtB,GAAG;AAAA,IACH,SAAS,QAAQ,KAAK,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG,QAAQ,aAAa;AAAA,IACtE,YAAY,UAAU,aAAa;AAAA,EACrC,EAAE,KAAK;AAAA,CAAI;AAAA;AAGb,IAAM,YAAY;AAClB,IAAM,QAAQ,IAAI,IAA8B;AAAA,EAC9C,CAAC,OAAQ,MAAM;AAAA,EACf,CAAC,OAAQ,WAAW;AAAA,EACpB,CAAC,OAAQ,SAAS;AACpB,CAAC;AAGM,IAAM,YAAY,CAAC,WAAyC;AAAA,EACjE,MAAM,SAAS,OAAO,KAAK,EAAE,MAAM,KAAK;AAAA,EACxC,IAAI,OAAO,WAAW;AAAA,IAAG;AAAA,EACzB,OAAO,SAAS,OAAO,KAAK,KAAK,QAAQ;AAAA,EACzC,MAAM,MAAM,OAAO,SAAS,SAAS,EAAE;AAAA,EACvC,MAAM,OAAO,OAAO,SAAS,OAAO,CAAC;AAAA,EACrC,MAAM,UAAU,CAAC,KAAK,MAAM,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,OAAO,IAAI,CAAC;AAAA,EAClE,IAAI,QAAQ,KAAK,CAAC,UAAU,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,CAAC;AAAA,IAAG;AAAA,EACxE,OAAO;AAAA,IACL,KAAK,OAAO,GAAG;AAAA,IACf,MAAM,MAAM,IAAI,MAAM,SAAS,KAAK;AAAA,IAEpC;AAAA,IACA,MAAM,OAAO,IAAI;AAAA,IACjB,KAAK,OAAO,GAAG;AAAA,EACjB;AAAA;;;AHjDF,IAAM,WAAW,CAAC,MAAyB,WAAmB,UAC5D,IAAI,QAAQ,CAAC,SAAS,WAAW;AAAA,EAC/B,OAAO,YAAY,QAAQ;AAAA,EAC3B,MAAM,QAAQ,MAAM,SAAS,MAAM;AAAA,IACjC,OAAO;AAAA,IACP,OAAO,CAAC,UAAU,YAAY,WAAW,QAAQ,QAAQ,MAAM;AAAA,IAC/D,SAAS;AAAA,EACX,CAAC;AAAA,EACD,MAAM,MAAgB,CAAC;AAAA,EACvB,MAAM,MAAgB,CAAC;AAAA,EACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB,IAAI,KAAK,KAAK,CAAC;AAAA,EAC3D,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB,IAAI,KAAK,KAAK,CAAC;AAAA,EAC3D,MAAM,GAAG,SAAS,CAAC,UAAU;AAAA,IAC3B,OAAO,IAAI,MAAM,QAAQ,MAAM,SAAS,CAAC;AAAA,GAC1C;AAAA,EACD,IAAI,UAAU,aAAa,MAAM,UAAU,MAAM;AAAA,IAG/C,MAAM,MAAM,GAAG,SAAS,MAAG;AAAA,MAAG;AAAA,KAAS;AAAA,IACvC,MAAM,MAAM,IAAI,KAAK;AAAA,EACvB;AAAA,EACA,MAAM,GAAG,SAAS,CAAC,UAAU,WAAW;AAAA,IACtC,QAAQ;AAAA,MACN,UAAU,YAAY;AAAA,MACtB;AAAA,MACA,QAAQ,OAAO,OAAO,GAAG,EAAE,SAAS,MAAM;AAAA,MAC1C,QAAQ,OAAO,OAAO,GAAG,EAAE,SAAS,MAAM;AAAA,IAC5C,CAAC;AAAA,GACF;AAAA,CACF;AAGI,IAAM,YAAY,OACvB,SAEA,OACA,QACA,UACwB;AAAA,EACxB,MAAM,OAAO,QAAQ,QAAQ,MAAM,YAAY,QAAQ,KAAK,GAAG;AAAA,IAC7D,mBAAmB,QAAQ;AAAA,OACvB,QAAQ,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;AAAA,EACtE,CAAC;AAAA,EACD,MAAM,MAAM,MAAM,SAAS,MAAM,QAAQ,eAAe,KAAK;AAAA,EAC7D,MAAM,SAAS,YAAY,IAAI,QAAQ,KAAK;AAAA,EAC5C,IAAI,WAAW,WAAW;AAAA,IAGxB,MAAM,IAAI,MACR,OAAO,QAAQ,gEACb,GAAG,OAAO,IAAI,QAAQ,IAAI,IAAI,WAAW,OAAO,KAAK,eAAe,IAAI,gBACxE,GAAG,IAAI,OAAO,KAAK,EAAE,MAAM,GAAG,GAAG,GACrC;AAAA,EACF;AAAA,EACA,OAAO,EAAE,UAAU,OAAO,UAAU,QAAQ,OAAO,QAAQ,QAAQ,IAAI,OAAO;AAAA;AAGhF,IAAM,eAAe,CAAC,SACpB,IAAI,WAAW,OAAO,KAAK,KAAK,WAAW;AAAA,GAAM,EAAE,GAAG,QAAQ,CAAC;AAEjE,IAAM,SAAS,CAAC,MAAc,MAAc,WAC1C,IAAI,MACF,OAAO,SAAS,WAAW,OAAO,OAAO,QAAQ,MAAM,OAAO,OAAO,KAAK,EAAE,MAAM,GAAG,GAAG,GAC1F;AASK,IAAM,YAAY,OAAO,YAAmD;AAAA,EACjF,MAAM,WAAW;AAAA,IACf,mBAAmB,QAAQ,qBAAqB;AAAA,IAChD,eAAe,QAAQ,iBAAiB;AAAA,IACxC,MAAM,QAAQ;AAAA,OACV,QAAQ,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;AAAA,EACtE;AAAA,EACA,MAAM,QAAQ,YAAY,CAAC,EAAE,SAAS,KAAK;AAAA,EAC3C,MAAM,OAAM,CAAC,QAAgB,UAAuB,UAAU,UAAU,OAAO,QAAQ,KAAK;AAAA,EAC5F,MAAM,QAAQ,MAAM,KAAI,iBAAiB;AAAA,EACzC,IAAI,MAAM,aAAa;AAAA,IAAG,MAAM,OAAO,QAAQ,MAAM,mBAAmB,KAAK;AAAA,EAC7E,OAAO,QAAQ,OAAO,MAAM,OAAO,KAAK,EAAE,MAAM;AAAA,CAAI;AAAA,EACpD,IAAI,WAAW,WAAW,QAAQ,aAAa,CAAC,QAAQ,KAAK,GAAG,GAAG;AAAA,IACjE,MAAM,IAAI,MACR,OAAO,QAAQ,oCAAoC,KAAK,UAAU,MAAM,OAAO,KAAK,CAAC,QACnF,2FACJ;AAAA,EACF;AAAA,EACA,MAAM,OAAO,OAAO,GAAG;AAAA,EACvB,OAAO;AAAA,IACL,cAAc,MAAM;AAAA,IACpB,MAAM,CAAC,SAAS,KAAI,WAAW,IAAI,CAAC;AAAA,IACpC,aAAa,OAAO,aAAa;AAAA,MAC/B,MAAM,SAAS,MAAM,KAAI,WAAW,WAAW,SAAS,QAAQ,CAAC,CAAC;AAAA,MAClE,OAAO,OAAO,aAAa,IAAI,WAAW,SAAS,OAAO,MAAM,IAAI;AAAA;AAAA,IAEtE,YAAY,OAAO,aAAa;AAAA,MAC9B,MAAM,SAAS,MAAM,KAAI,WAAW,UAAU,SAAS,QAAQ,CAAC,CAAC;AAAA,MACjE,OAAO,OAAO,aAAa,IAAI,UAAU,SAAS,OAAO,MAAM,IAAI;AAAA;AAAA,IAErE,YAAY;AAAA,IACZ,UAAU,OAAO,SAAS;AAAA,MACxB,MAAM,SAAS,MAAM,KAAI,WAAW,IAAI,CAAC;AAAA,MACzC,IAAI,OAAO,aAAa;AAAA,QAAQ;AAAA,MAChC,IAAI,OAAO,aAAa;AAAA,QAAG,MAAM,OAAO,QAAQ,MAAM,QAAQ,QAAQ,MAAM;AAAA,MAC5E,OAAO,aAAa,OAAO,MAAM;AAAA;AAAA,IAEnC,YAAY,OAAO,SAAS;AAAA,MAC1B,MAAM,SAAS,MAAM,KAAI,aAAa,IAAI,CAAC;AAAA,MAC3C,IAAI,OAAO,aAAa;AAAA,QAAG,MAAM,OAAO,QAAQ,MAAM,UAAU,QAAQ,MAAM;AAAA;AAAA,IAEhF,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,IAC/D,MAAM,OAAO,SAAS;AAAA,MACpB,MAAM,SAAS,MAAM,KAAI,WAAW,IAAI,CAAC;AAAA,MACzC,IAAI,OAAO,aAAa;AAAA,QAAQ;AAAA,MAChC,IAAI,OAAO,aAAa;AAAA,QAAG,MAAM,OAAO,QAAQ,MAAM,QAAQ,QAAQ,MAAM;AAAA,MAC5E,MAAM,SAAS,UAAU,OAAO,MAAM;AAAA,MAGtC,IAAI,WAAW,WAAW;AAAA,QACxB,MAAM,IAAI,MAAM,OAAO,QAAQ,cAAc,kCAAkC;AAAA,MACjF;AAAA,MACA,OAAO;AAAA;AAAA,IAET,iBAAiB,OAAO,MAAM,OAAO,UAAU;AAAA,MAC7C,MAAM,KAAK,KAAK,YAAY,GAAG;AAAA,MAC/B,MAAM,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE,MAAM,KAAK,MAAM,KAAK,CAAC,KAAK,YAAY,CAAC,EAAE,SAAS,KAAK;AAAA,MACzF,MAAM,SAAS,MAAM,KAAI,YAAY,MAAM,MAAM,KAAK,GAAG,KAAK;AAAA,MAC9D,IAAI,OAAO,aAAa;AAAA,QAAG,MAAM,OAAO,QAAQ,MAAM,SAAS,QAAQ,MAAM;AAAA;AAAA,EAEjF;AAAA;",
35
+ "debugId": "36C2172DF713B75564756E2164756E21",
36
+ "names": []
37
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Every provider in this subpath with ONE HostRunner, as a single layer for a host stack.
3
+ *
4
+ * const runner = await sshRunner({ host: 'the-host' });
5
+ * Layer.mergeAll(linuxProviders(runner), …the stack's other providers)
6
+ *
7
+ * ★ ONE RUNNER FOR ALL OF THEM, so a directory, the config file inside it and the unit that reads
8
+ * that file are written by the same identity on the same host — a stack cannot create the
9
+ * directory on one host and start the unit on another by accident.
10
+ * ⛔ THERE IS NO DEFAULT. launchdProviders() defaults to localRunner() because a Mac stack runs on
11
+ * the Mac it declares; a Linux host is somewhere else, and a silent default to "this machine"
12
+ * would write the estate's `/etc/systemd/system` onto the laptop running the deploy.
13
+ */
14
+ import * as Layer from 'effect/Layer';
15
+ import { type HostRunner } from '../launchd/runner.ts';
16
+ export declare const linuxProviders: (runner: HostRunner) => Layer.Layer<import("alchemy").Provider<import("./directory.ts").HostDirectory> | import("alchemy").Provider<import("./remote-file.ts").RemoteFile> | import("alchemy").Provider<import("./unit.ts").SystemdTimer> | import("alchemy").Provider<import("./unit.ts").SystemdUnit>, never, never>;
17
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/linux/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,sBAAsB,CAAC;AAKxE,eAAO,MAAM,cAAc,WAAY,UAAU,mSAMD,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A MANAGED REGION: a block a resource owns inside a file it does not own.
3
+ *
4
+ * ★ WHY THIS EXISTS. Four files on this estate are owned by something else and still need one
5
+ * declared block: a packet-filter anchor line inside the OS vendor's own ruleset, a host table,
6
+ * a generated Nix module, and an operator's ssh config. Declaring the WHOLE file would mean
7
+ * taking it from its owner — which puts it back, or refuses its next activation. So the resource
8
+ * owns the lines between two markers and promises that every other byte is untouched.
9
+ * ⛔ THE REST OF THE FILE IS BYTE-IDENTICAL. Not "semantically the same", not re-indented, not
10
+ * re-terminated: the bytes before the BEGIN line and after the END line are copied through. That
11
+ * is the property region.test.ts asserts, because it is the whole reason the other owner tolerates
12
+ * this.
13
+ * ⛔ AMBIGUITY IS A REFUSAL, NEVER A GUESS. Two BEGIN markers with the same name, an END before its
14
+ * BEGIN, or a BEGIN with no END all mean someone edited inside the block. Picking one would
15
+ * silently delete their edit, so each is an Error naming the line.
16
+ * ★ THE MARKER IS A COMMENT IN THE HOST FILE'S OWN LANGUAGE, which is why the comment token is a
17
+ * prop: `#` covers the four consumers above, and a file whose comments are `//` or `;` says so.
18
+ */
19
+ export type RegionSpec = {
20
+ /** Distinguishes this block from any other managed block in the same file. */
21
+ readonly name: string;
22
+ /** The file's comment token. @default '#' */
23
+ readonly comment?: string;
24
+ };
25
+ export declare const DEFAULT_COMMENT = "#";
26
+ export declare const regionProblems: (spec: RegionSpec) => string[];
27
+ /**
28
+ * The file's bytes with the block set to `body`, appended at the end when it is not there yet.
29
+ * ⚠️ A file that does not end in a newline gets one before the block is appended — the ONLY byte
30
+ * this function adds outside its own block, and without it the last existing line and the BEGIN
31
+ * marker would become one line.
32
+ */
33
+ export declare const spliceRegion: (file: string, spec: RegionSpec, body: string) => string;
34
+ /** The block's body, or `undefined` when the file has no such block. */
35
+ export declare const readRegion: (file: string, spec: RegionSpec) => string | undefined;
36
+ /**
37
+ * The file with the block and both markers removed — what a delete leaves behind.
38
+ * ★ Byte-for-byte the file as it was before the block was appended, with one exception already
39
+ * named on `spliceRegion`: a file that had no final newline when the block was added keeps the
40
+ * newline that adding it required. Nothing else is added or dropped.
41
+ */
42
+ export declare const removeRegion: (file: string, spec: RegionSpec) => string;
43
+ //# sourceMappingURL=region.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"region.d.ts","sourceRoot":"","sources":["../../src/linux/region.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,UAAU,GAAG;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,eAAe,MAAM,CAAC;AAUnC,eAAO,MAAM,cAAc,SAAU,UAAU,KAAG,MAAM,EAYvD,CAAC;AA8BF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,QAAQ,UAAU,QAAQ,MAAM,KAAG,MAW3E,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,UAAU,SAAU,MAAM,QAAQ,UAAU,KAAG,MAAM,GAAG,SAMpE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,QAAQ,UAAU,KAAG,MAK7D,CAAC"}