@homeflare/alchemy 0.36.0 → 0.37.1

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 (116) hide show
  1. package/README.md +14 -0
  2. package/dist/argocd/cluster.d.ts +102 -0
  3. package/dist/argocd/cluster.d.ts.map +1 -0
  4. package/dist/argocd/credentials.d.ts +37 -0
  5. package/dist/argocd/credentials.d.ts.map +1 -0
  6. package/dist/argocd/fake-argocd.d.ts +42 -0
  7. package/dist/argocd/fake-argocd.d.ts.map +1 -0
  8. package/dist/argocd/git-credentials.d.ts +29 -0
  9. package/dist/argocd/git-credentials.d.ts.map +1 -0
  10. package/dist/argocd/index.d.ts +19 -0
  11. package/dist/argocd/index.d.ts.map +1 -0
  12. package/dist/argocd/index.js +306 -0
  13. package/dist/argocd/index.js.map +17 -0
  14. package/dist/argocd/providers.d.ts +13 -0
  15. package/dist/argocd/providers.d.ts.map +1 -0
  16. package/dist/argocd/repo-creds.d.ts +67 -0
  17. package/dist/argocd/repo-creds.d.ts.map +1 -0
  18. package/dist/argocd/repository.d.ts +87 -0
  19. package/dist/argocd/repository.d.ts.map +1 -0
  20. package/dist/argocd/resource.d.ts +90 -0
  21. package/dist/argocd/resource.d.ts.map +1 -0
  22. package/dist/cloudflare/index.js +4 -4
  23. package/dist/cloudflare/index.js.map +3 -3
  24. package/dist/cloudflare/r2-bucket-lock.d.ts +14 -5
  25. package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -1
  26. package/dist/litellm/index.js.map +2 -2
  27. package/dist/litellm/operations.d.ts.map +1 -1
  28. package/dist/proxmox/ceph-daemon-form.d.ts +4 -5
  29. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
  30. package/dist/proxmox/ceph-daemon-wire.d.ts +31 -0
  31. package/dist/proxmox/ceph-daemon-wire.d.ts.map +1 -0
  32. package/dist/proxmox/ceph-daemon.d.ts +40 -6
  33. package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
  34. package/dist/proxmox/ceph-fs-distilled.d.ts +33 -0
  35. package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -0
  36. package/dist/proxmox/ceph-fs-wire.d.ts +20 -11
  37. package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
  38. package/dist/proxmox/ceph-fs.d.ts +30 -6
  39. package/dist/proxmox/ceph-fs.d.ts.map +1 -1
  40. package/dist/proxmox/ceph-osd-tree.d.ts +12 -3
  41. package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -1
  42. package/dist/proxmox/ceph-osd-write.d.ts +17 -2
  43. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  44. package/dist/proxmox/ceph-osd.d.ts +6 -0
  45. package/dist/proxmox/ceph-osd.d.ts.map +1 -1
  46. package/dist/proxmox/ceph-pool-settle.d.ts +19 -12
  47. package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -1
  48. package/dist/proxmox/ceph-pool-wire.d.ts +37 -0
  49. package/dist/proxmox/ceph-pool-wire.d.ts.map +1 -0
  50. package/dist/proxmox/ceph-pool.d.ts +10 -5
  51. package/dist/proxmox/ceph-pool.d.ts.map +1 -1
  52. package/dist/proxmox/group-wire.d.ts +8 -13
  53. package/dist/proxmox/group-wire.d.ts.map +1 -1
  54. package/dist/proxmox/index.js +605 -483
  55. package/dist/proxmox/index.js.map +20 -17
  56. package/dist/proxmox/storage-wire.d.ts +15 -11
  57. package/dist/proxmox/storage-wire.d.ts.map +1 -1
  58. package/dist/proxmox/unreadable-read.d.ts +7 -7
  59. package/dist/proxmox/user-wire.d.ts +8 -11
  60. package/dist/proxmox/user-wire.d.ts.map +1 -1
  61. package/dist/proxmox/user.d.ts +5 -5
  62. package/docs/argocd-kubernetes.md +100 -0
  63. package/docs/argocd.md +139 -0
  64. package/docs/credentials.md +9 -0
  65. package/docs/distilled-interim.md +15 -3
  66. package/docs/litellm.md +17 -0
  67. package/docs/peers.md +7 -0
  68. package/docs/provider-standard.md +24 -6
  69. package/docs/proxmox.md +20 -0
  70. package/docs/unifi.md +45 -0
  71. package/docs/upstream-conformance-bun.md +67 -0
  72. package/docs/upstream-conformance-conforms.md +55 -0
  73. package/docs/upstream-conformance-families.md +95 -0
  74. package/docs/upstream-conformance-grafana.md +106 -0
  75. package/docs/upstream-conformance.md +20 -298
  76. package/package.json +8 -3
  77. package/src/argocd/cluster.test.ts +120 -0
  78. package/src/argocd/cluster.ts +222 -0
  79. package/src/argocd/credentials.ts +38 -0
  80. package/src/argocd/fake-argocd.ts +60 -0
  81. package/src/argocd/git-credentials.ts +71 -0
  82. package/src/argocd/index.ts +40 -0
  83. package/src/argocd/providers.ts +17 -0
  84. package/src/argocd/repo-creds.test.ts +123 -0
  85. package/src/argocd/repo-creds.ts +115 -0
  86. package/src/argocd/repository.test.ts +167 -0
  87. package/src/argocd/repository.ts +165 -0
  88. package/src/argocd/resource.ts +150 -0
  89. package/src/cloudflare/r2-bucket-lock.test.ts +54 -18
  90. package/src/cloudflare/r2-bucket-lock.ts +25 -18
  91. package/src/litellm/operations.ts +8 -4
  92. package/src/proxmox/ceph-daemon-form.ts +7 -8
  93. package/src/proxmox/ceph-daemon-wire.ts +112 -0
  94. package/src/proxmox/ceph-daemon-write.test.ts +158 -0
  95. package/src/proxmox/ceph-daemon.ts +70 -53
  96. package/src/proxmox/ceph-fs-delete.test.ts +81 -0
  97. package/src/proxmox/ceph-fs-distilled.ts +100 -0
  98. package/src/proxmox/ceph-fs-wire.ts +17 -31
  99. package/src/proxmox/ceph-fs-write.test.ts +67 -0
  100. package/src/proxmox/ceph-fs.ts +68 -73
  101. package/src/proxmox/ceph-osd-tree.ts +14 -5
  102. package/src/proxmox/ceph-osd-write.test.ts +61 -0
  103. package/src/proxmox/ceph-osd-write.ts +33 -6
  104. package/src/proxmox/ceph-osd.ts +6 -0
  105. package/src/proxmox/ceph-pool-settle.ts +24 -17
  106. package/src/proxmox/ceph-pool-wire.ts +101 -0
  107. package/src/proxmox/ceph-pool-write.test.ts +146 -0
  108. package/src/proxmox/ceph-pool.ts +87 -98
  109. package/src/proxmox/group-wire.ts +7 -12
  110. package/src/proxmox/read-failure-propagation.test.ts +5 -5
  111. package/src/proxmox/storage-wire.ts +9 -10
  112. package/src/proxmox/typed-absence.test.ts +146 -0
  113. package/src/proxmox/unreadable-read.ts +7 -7
  114. package/src/proxmox/user-wire.ts +7 -10
  115. package/src/proxmox/user.ts +5 -5
  116. package/src/proxmox/distilled-task-await.test.ts +0 -124
@@ -1635,14 +1635,15 @@ var handlers = pveHandlers({
1635
1635
  var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect12.succeed(ProxmoxBackupJob.Provider.of(handlers)));
1636
1636
  // src/proxmox/ceph-daemon.ts
1637
1637
  import { Resource as Resource4 } from "alchemy";
1638
+ import { isResolved as isResolved4 } from "alchemy/Diff";
1638
1639
  import * as Provider4 from "alchemy/Provider";
1639
- import * as Effect13 from "effect/Effect";
1640
+ import * as Effect14 from "effect/Effect";
1640
1641
 
1641
1642
  // src/proxmox/ceph-daemon-form.ts
1642
1643
  var DAEMON_ENDPOINTS = {
1643
- mds: { create: "pve:POST /nodes/{node}/ceph/mds/{name}" },
1644
- mgr: { create: "pve:POST /nodes/{node}/ceph/mgr/{id}" },
1645
- mon: { create: "pve:POST /nodes/{node}/ceph/mon/{monid}" }
1644
+ mds: "pve:POST /nodes/{node}/ceph/mds/{name}",
1645
+ mgr: "pve:POST /nodes/{node}/ceph/mgr/{id}",
1646
+ mon: "pve:POST /nodes/{node}/ceph/mon/{monid}"
1646
1647
  };
1647
1648
  var daemonId = (props) => props.name ?? props.node;
1648
1649
  var collectionPath = (props) => `nodes/${props.node}/ceph/${props.kind}`;
@@ -1675,28 +1676,95 @@ var daemonAttributes = (live, props) => {
1675
1676
  };
1676
1677
  };
1677
1678
 
1679
+ // src/proxmox/ceph-daemon-wire.ts
1680
+ import * as nodes from "@distilled.cloud/proxmox/nodes";
1681
+ import * as Effect13 from "effect/Effect";
1682
+ var listRows = (props) => {
1683
+ const node = props.node;
1684
+ switch (props.kind) {
1685
+ case "mon":
1686
+ return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(Effect13.map((rows) => rows));
1687
+ case "mgr":
1688
+ return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(Effect13.map((rows) => rows));
1689
+ case "mds":
1690
+ return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(Effect13.map((rows) => rows));
1691
+ }
1692
+ };
1693
+ var readDaemon = (props) => listRows(props).pipe(Effect13.map((rows) => daemonAttributes(rows, props)));
1694
+ var createDaemon = (props) => {
1695
+ const node = props.node;
1696
+ const id = daemonId(props);
1697
+ switch (props.kind) {
1698
+ case "mon":
1699
+ return runPve(props.target, "provision", true, nodes.updateNodeCephMon({
1700
+ ...props["mon-address"] === undefined ? {} : { mon_address: props["mon-address"] },
1701
+ monid: id,
1702
+ node
1703
+ }));
1704
+ case "mgr":
1705
+ return runPve(props.target, "provision", true, nodes.updateNodeCephMgr({ id, node }));
1706
+ case "mds":
1707
+ return runPve(props.target, "provision", true, nodes.updateNodeCephMds({
1708
+ ...props.hotstandby === undefined ? {} : { hotstandby: props.hotstandby ? "1" : "0" },
1709
+ name: id,
1710
+ node
1711
+ }));
1712
+ }
1713
+ };
1714
+ var deleteDaemon = (props) => {
1715
+ const node = props.node;
1716
+ const id = daemonId(props);
1717
+ switch (props.kind) {
1718
+ case "mon":
1719
+ return runPve(props.target, "provision", true, nodes.deleteNodeCephMon({ monid: id, node }));
1720
+ case "mgr":
1721
+ return runPve(props.target, "provision", true, nodes.deleteNodeCephMgr({ id, node }));
1722
+ case "mds":
1723
+ return runPve(props.target, "provision", true, nodes.deleteNodeCephMds({ name: id, node }));
1724
+ }
1725
+ };
1726
+ var notCreated = (props) => new Error(`${daemonPath(props)}: no ${props.kind} named ${daemonId(props)} in GET nodes/` + `${props.node}/ceph/${props.kind} after the create task finished. POST answers a UPID, ` + "not a result -- watch the task in the PVE UI (ceph-daemon-form.ts).");
1727
+
1678
1728
  // src/proxmox/ceph-daemon.ts
1679
1729
  var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
1680
1730
  defaultRemovalPolicy: "retain"
1681
1731
  });
1682
- var handlers2 = pveHandlers({
1683
- attributes: daemonAttributes,
1684
- collection: daemonPath,
1685
- createForm,
1686
- endpoint: (props) => DAEMON_ENDPOINTS[props.kind],
1687
- matches: () => true,
1688
- path: collectionPath
1689
- });
1690
- var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect13.succeed(ProxmoxCephDaemon.Provider.of({
1691
- ...handlers2,
1692
- delete: ({ olds }) => pve(olds.target, "provision", "DELETE", daemonPath(olds))
1732
+ var reconcileDaemon = Effect14.fn(function* ({ news }) {
1733
+ const live = yield* readDaemon(news);
1734
+ yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), live === undefined);
1735
+ if (live !== undefined)
1736
+ return live;
1737
+ yield* createDaemon(news);
1738
+ const after = yield* readDaemon(news);
1739
+ if (after === undefined)
1740
+ return yield* Effect14.die(notCreated(news));
1741
+ return after;
1742
+ });
1743
+ var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect14.succeed(ProxmoxCephDaemon.Provider.of({
1744
+ list: () => Effect14.succeed([]),
1745
+ read: ({ olds }) => readDaemon(olds),
1746
+ diff: Effect14.fn(function* ({ news, output }) {
1747
+ if (!isResolved4(news))
1748
+ return;
1749
+ yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), output === undefined);
1750
+ if (output === undefined)
1751
+ return;
1752
+ const live = yield* readDaemon(news);
1753
+ if (live === undefined) {
1754
+ yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), true);
1755
+ return { action: "update" };
1756
+ }
1757
+ return { action: "noop" };
1758
+ }),
1759
+ reconcile: ({ news }) => reconcileDaemon({ news }),
1760
+ delete: ({ olds }) => deleteDaemon(olds)
1693
1761
  })));
1694
1762
  // src/proxmox/ceph-flag.ts
1695
1763
  import { Resource as Resource5 } from "alchemy";
1696
1764
  import * as Provider5 from "alchemy/Provider";
1697
- import * as Effect14 from "effect/Effect";
1765
+ import * as Effect15 from "effect/Effect";
1698
1766
  var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
1699
- var handlers3 = pveHandlers({
1767
+ var handlers2 = pveHandlers({
1700
1768
  attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
1701
1769
  collection: () => "cluster/ceph/flags",
1702
1770
  createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
@@ -1705,25 +1773,29 @@ var handlers3 = pveHandlers({
1705
1773
  path: (props) => `cluster/ceph/flags/${props.flag}`,
1706
1774
  updateForm: (props) => ({ value: flag(props.value) ?? "0" })
1707
1775
  });
1708
- var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect14.succeed(ProxmoxCephFlag.Provider.of({
1709
- ...handlers3,
1710
- delete: () => Effect14.void,
1711
- reconcile: Effect14.fn(function* ({ news }) {
1712
- const after = yield* handlers3.reconcile({ news });
1776
+ var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect15.succeed(ProxmoxCephFlag.Provider.of({
1777
+ ...handlers2,
1778
+ delete: () => Effect15.void,
1779
+ reconcile: Effect15.fn(function* ({ news }) {
1780
+ const after = yield* handlers2.reconcile({ news });
1713
1781
  if (after.value !== news.value) {
1714
- return yield* Effect14.die(new Error(`cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` + `reports the flag ${after.value ? "set" : "clear"} rather than ` + `${news.value ? "set" : "clear"}. This endpoint's success is documented as a ` + "null body, so the status code proves nothing -- check `ceph osd dump | " + "head -1` and the cluster log. `pause` in particular is two ceph flags read " + "as one; see the header."));
1782
+ return yield* Effect15.die(new Error(`cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` + `reports the flag ${after.value ? "set" : "clear"} rather than ` + `${news.value ? "set" : "clear"}. This endpoint's success is documented as a ` + "null body, so the status code proves nothing -- check `ceph osd dump | " + "head -1` and the cluster log. `pause` in particular is two ceph flags read " + "as one; see the header."));
1715
1783
  }
1716
1784
  return after;
1717
1785
  })
1718
1786
  })));
1719
1787
  // src/proxmox/ceph-fs.ts
1720
1788
  import { Resource as Resource6 } from "alchemy";
1721
- import { isResolved as isResolved4 } from "alchemy/Diff";
1789
+ import { isResolved as isResolved5 } from "alchemy/Diff";
1722
1790
  import * as Provider6 from "alchemy/Provider";
1723
- import * as Effect16 from "effect/Effect";
1791
+ import * as Effect18 from "effect/Effect";
1792
+
1793
+ // src/proxmox/ceph-fs-distilled.ts
1794
+ import * as nodes2 from "@distilled.cloud/proxmox/nodes";
1795
+ import * as Effect17 from "effect/Effect";
1724
1796
 
1725
1797
  // src/proxmox/ceph-fs-wire.ts
1726
- import * as Effect15 from "effect/Effect";
1798
+ import * as Effect16 from "effect/Effect";
1727
1799
  var readRow = (live, props) => {
1728
1800
  const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
1729
1801
  if (row === undefined)
@@ -1739,6 +1811,7 @@ var readRow = (live, props) => {
1739
1811
  var POLL_SECONDS = 2;
1740
1812
  var POLL_ATTEMPTS = 45;
1741
1813
  var objectPath = (props) => `nodes/${props.node}/ceph/fs/${props.name}`;
1814
+ var CEPH_FS_CREATE = "pve:POST /nodes/{node}/ceph/fs/{name}";
1742
1815
  var field = (name, value) => value === undefined ? {} : { [name]: value };
1743
1816
  var createForm2 = (props) => ({
1744
1817
  ...field("add-storage", flag(props["add-storage"])),
@@ -1751,86 +1824,111 @@ var destroyPath = (props) => {
1751
1824
  ].filter((part) => part !== undefined);
1752
1825
  return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
1753
1826
  };
1754
- var settle = (target, node, upid, what) => Effect15.gen(function* () {
1827
+ var settle = (target, node, upid, what) => Effect16.gen(function* () {
1755
1828
  for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
1756
- const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect15.orElseSucceed(() => {
1829
+ const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect16.orElseSucceed(() => {
1757
1830
  return;
1758
1831
  }));
1759
1832
  if (status?.status === "stopped") {
1760
1833
  const exit = text(status.exitstatus);
1761
1834
  if (exit === "OK")
1762
1835
  return;
1763
- return yield* Effect15.die(new Error(`${what}: PVE task ${upid} finished with "${exit === "" ? "no exit status" : exit}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${node}/tasks/${upid}/log\`.`));
1836
+ return yield* Effect16.die(new Error(`${what}: PVE task ${upid} finished with "${exit === "" ? "no exit status" : exit}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${node}/tasks/${upid}/log\`.`));
1764
1837
  }
1765
- yield* Effect15.sleep(POLL_SECONDS * 1000);
1838
+ yield* Effect16.sleep(POLL_SECONDS * 1000);
1766
1839
  }
1767
- return yield* Effect15.die(new Error(`${what}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its ` + "status could not be read. Check the task log, and check that the `read` role holds " + `Sys.Audit on /nodes/${node} -- each call mints a new token, so the poller is never the ` + "task owner and is answered 403 rather than told to wait."));
1840
+ return yield* Effect16.die(new Error(`${what}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its ` + "status could not be read. Check the task log, and check that the `read` role holds " + `Sys.Audit on /nodes/${node} -- each call mints a new token, so the poller is never the ` + "task owner and is answered 403 rather than told to wait."));
1768
1841
  });
1769
- var createFs = (props) => Effect15.gen(function* () {
1770
- const upid = text(yield* pve(props.target, "provision", "POST", objectPath(props), createForm2(props)));
1771
- if (upid === "") {
1772
- return yield* Effect15.die(new Error(`${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` + '{"data":...} and can report success on a call that did nothing.'));
1773
- }
1774
- yield* settle(props.target, props.node, upid, objectPath(props));
1775
- });
1776
- var destroyFs = (props) => Effect15.gen(function* () {
1842
+ var destroyFs = (props) => Effect16.gen(function* () {
1777
1843
  const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
1778
1844
  if (upid !== "")
1779
1845
  yield* settle(props.target, props.node, upid, destroyPath(props));
1780
1846
  });
1781
- var notCreated = (props) => new Error(`${objectPath(props)}: the create task finished but the filesystem is not in ` + `GET nodes/${props.node}/ceph/fs. Check the task log -- a PVE worker can exit having ` + "rolled back the pools it had just made.");
1847
+ var notCreated2 = (props) => new Error(`${objectPath(props)}: the create task finished but the filesystem is not in ` + `GET nodes/${props.node}/ceph/fs. Check the task log -- a PVE worker can exit having ` + "rolled back the pools it had just made.");
1782
1848
  var notDestroyed = (props) => new Error(`${objectPath(props)}: the destroy task finished but the filesystem is still in ` + `GET nodes/${props.node}/ceph/fs. PVE refuses to destroy a CephFS while a PVE storage entry ` + "of type cephfs still references it and is not disabled.");
1783
1849
 
1850
+ // src/proxmox/ceph-fs-distilled.ts
1851
+ var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect17.map((rows) => readRow(rows, props)));
1852
+ var toDistilledCreate = (props) => ({
1853
+ ...createForm2(props),
1854
+ name: props.name,
1855
+ node: props.node
1856
+ });
1857
+ var settleCreate = (props, upid) => Effect17.gen(function* () {
1858
+ for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
1859
+ const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
1860
+ return;
1861
+ }));
1862
+ if (status?.status === "stopped") {
1863
+ if (status.exitstatus === "OK")
1864
+ return;
1865
+ return yield* Effect17.die(new Error(`${objectPath(props)}: PVE task ${upid} finished with "${status.exitstatus ?? "no exit status"}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${props.node}/tasks/${upid}/log\`.`));
1866
+ }
1867
+ yield* Effect17.sleep(POLL_SECONDS * 1000);
1868
+ }
1869
+ return yield* Effect17.die(new Error(`${objectPath(props)}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its status could not be read. Check the ` + "task log, and check that the `read` role holds Sys.Audit on " + `/nodes/${props.node} -- each call mints a new token, so the poller is never the task ` + "owner and is answered 403 rather than told to wait."));
1870
+ });
1871
+ var createFs = (props) => Effect17.gen(function* () {
1872
+ const upid = yield* runPve(props.target, "provision", true, nodes2.updateNodeCephFs(toDistilledCreate(props)));
1873
+ if (upid === "") {
1874
+ return yield* Effect17.die(new Error(`${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` + '{"data":...} and can report success on a call that did nothing.'));
1875
+ }
1876
+ yield* settleCreate(props, upid);
1877
+ });
1878
+
1784
1879
  // src/proxmox/ceph-fs.ts
1785
1880
  var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
1786
1881
  defaultRemovalPolicy: "retain"
1787
1882
  });
1788
- var ops = pveOperations({
1789
- attributes: readRow,
1790
- collection: objectPath,
1791
- createForm: createForm2,
1792
- endpoint: { create: "pve:POST /nodes/{node}/ceph/fs/{name}" },
1793
- matches: () => true,
1794
- path: (props) => `nodes/${props.node}/ceph/fs`
1795
- });
1796
- var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect16.succeed(ProxmoxCephFs.Provider.of({
1797
- list: () => Effect16.succeed([]),
1798
- read: Effect16.fn(function* ({ olds }) {
1799
- return yield* ops.read(olds);
1883
+ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succeed(ProxmoxCephFs.Provider.of({
1884
+ list: () => Effect18.succeed([]),
1885
+ read: Effect18.fn(function* ({ olds }) {
1886
+ return yield* readFs(olds);
1800
1887
  }),
1801
- diff: Effect16.fn(function* ({ news, output }) {
1802
- if (output !== undefined && isResolved4(news) && news.name !== output.name) {
1888
+ diff: Effect18.fn(function* ({ news, output }) {
1889
+ if (!isResolved5(news))
1890
+ return;
1891
+ yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
1892
+ if (output !== undefined && news.name !== output.name) {
1803
1893
  return { action: "replace" };
1804
1894
  }
1805
- return yield* ops.diff(news, output);
1895
+ if (output === undefined)
1896
+ return;
1897
+ const live = yield* readFs(news);
1898
+ if (live === undefined) {
1899
+ yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), true);
1900
+ return { action: "update" };
1901
+ }
1902
+ return { action: "noop" };
1806
1903
  }),
1807
- reconcile: Effect16.fn(function* ({ news }) {
1808
- const live = yield* ops.read(news);
1904
+ reconcile: Effect18.fn(function* ({ news }) {
1905
+ const live = yield* readFs(news);
1809
1906
  if (live !== undefined)
1810
1907
  return live;
1811
1908
  yield* createFs(news);
1812
- const after = yield* ops.read(news);
1909
+ const after = yield* readFs(news);
1813
1910
  if (after === undefined)
1814
- return yield* Effect16.die(notCreated(news));
1911
+ return yield* Effect18.die(notCreated2(news));
1815
1912
  return after;
1816
1913
  }),
1817
- delete: Effect16.fn(function* ({ olds }) {
1818
- if ((yield* ops.read(olds)) === undefined)
1914
+ delete: Effect18.fn(function* ({ olds }) {
1915
+ if ((yield* readFs(olds)) === undefined)
1819
1916
  return;
1820
1917
  yield* destroyFs(olds);
1821
- const after = yield* ops.read(olds);
1918
+ const after = yield* readFs(olds);
1822
1919
  if (after !== undefined)
1823
- return yield* Effect16.die(notDestroyed(olds));
1920
+ return yield* Effect18.die(notDestroyed(olds));
1824
1921
  })
1825
1922
  })));
1826
1923
  // src/proxmox/ceph-osd.ts
1827
1924
  import { Resource as Resource7 } from "alchemy";
1828
- import { isResolved as isResolved5 } from "alchemy/Diff";
1925
+ import { isResolved as isResolved6 } from "alchemy/Diff";
1829
1926
  import * as Provider7 from "alchemy/Provider";
1830
- import * as Effect19 from "effect/Effect";
1927
+ import * as Effect21 from "effect/Effect";
1831
1928
 
1832
1929
  // src/proxmox/ceph-osd-tree.ts
1833
- import * as Effect17 from "effect/Effect";
1930
+ import * as nodes3 from "@distilled.cloud/proxmox/nodes";
1931
+ import * as Effect19 from "effect/Effect";
1834
1932
  var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
1835
1933
  var osdLeaves = (tree) => {
1836
1934
  const root = tree["root"];
@@ -1856,33 +1954,42 @@ var osdAttributes = (leaf, props) => ({
1856
1954
  status: text(leaf["status"]),
1857
1955
  total_space: num(leaf["total_space"], 0)
1858
1956
  });
1859
- var readOsd = (props) => pve(props.target, "read", "GET", `nodes/${props.node}/ceph/osd`).pipe(Effect17.map((t) => {
1860
- const leaf = t === undefined ? undefined : findOsd(t, props.osdid);
1957
+ var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect19.map((t) => {
1958
+ const leaf = findOsd(t, props.osdid);
1861
1959
  return leaf === undefined ? undefined : osdAttributes(leaf, props);
1862
1960
  }));
1863
1961
 
1864
1962
  // src/proxmox/ceph-osd-write.ts
1865
- import * as Effect18 from "effect/Effect";
1963
+ import * as nodes4 from "@distilled.cloud/proxmox/nodes";
1964
+ import * as Effect20 from "effect/Effect";
1866
1965
  var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
1867
1966
  var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
1868
1967
  var createOsdForm = (props, dev) => ({
1869
1968
  dev,
1870
1969
  ...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
1871
1970
  });
1872
- var createOsd = Effect18.fn(function* (news) {
1971
+ var createOsd = Effect20.fn(function* (news) {
1873
1972
  if (news.dev === undefined) {
1874
- return yield* Effect18.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
1973
+ return yield* Effect20.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
1875
1974
  }
1876
1975
  const form = createOsdForm(news, news.dev);
1877
1976
  yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
1878
- yield* pve(news.target, "provision", "POST", `nodes/${news.node}/ceph/osd`, form);
1977
+ yield* runPve(news.target, "provision", true, nodes4.createNodeCephOsd({
1978
+ dev: news.dev,
1979
+ node: news.node,
1980
+ ...news.device_class === undefined ? {} : { crush_device_class: news.device_class }
1981
+ }));
1879
1982
  const created = yield* readOsd(news);
1880
1983
  if (created === undefined) {
1881
- return yield* Effect18.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
1984
+ return yield* Effect20.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
1882
1985
  }
1883
1986
  return created;
1884
1987
  });
1885
- var destroyOsd = (olds) => pve(olds.target, "provision", "DELETE", `nodes/${olds.node}/ceph/osd/${String(olds.osdid)}`, olds.cleanup === true ? { cleanup: "1" } : {});
1988
+ var destroyOsd = (olds) => runPve(olds.target, "provision", true, nodes4.deleteNodeCephOsd({
1989
+ node: olds.node,
1990
+ osdid: String(olds.osdid),
1991
+ ...olds.cleanup === true ? { cleanup: "1" } : {}
1992
+ }));
1886
1993
 
1887
1994
  // src/proxmox/ceph-osd.ts
1888
1995
  var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
@@ -1890,16 +1997,16 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
1890
1997
  });
1891
1998
  var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
1892
1999
  var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
1893
- var handlers4 = {
1894
- list: () => Effect19.succeed([]),
1895
- read: Effect19.fn(function* ({ olds }) {
2000
+ var handlers3 = {
2001
+ list: () => Effect21.succeed([]),
2002
+ read: Effect21.fn(function* ({ olds }) {
1896
2003
  return yield* readOsd(olds);
1897
2004
  }),
1898
- diff: Effect19.fn(function* ({
2005
+ diff: Effect21.fn(function* ({
1899
2006
  news,
1900
2007
  output
1901
2008
  }) {
1902
- if (output === undefined || !isResolved5(news))
2009
+ if (output === undefined || !isResolved6(news))
1903
2010
  return;
1904
2011
  const live = yield* readOsd(news);
1905
2012
  if (live === undefined)
@@ -1908,29 +2015,30 @@ var handlers4 = {
1908
2015
  return { action: "update" };
1909
2016
  return settled(live, news) ? { action: "noop" } : { action: "update" };
1910
2017
  }),
1911
- reconcile: Effect19.fn(function* ({ news }) {
2018
+ reconcile: Effect21.fn(function* ({ news }) {
1912
2019
  const live = yield* readOsd(news);
1913
2020
  if (live === undefined) {
1914
2021
  return yield* createOsd(news);
1915
2022
  }
1916
2023
  if (!settled(live, news)) {
1917
- return yield* Effect19.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
2024
+ return yield* Effect21.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
1918
2025
  }
1919
2026
  return live;
1920
2027
  }),
1921
- delete: Effect19.fn(function* ({ olds }) {
2028
+ delete: Effect21.fn(function* ({ olds }) {
1922
2029
  yield* destroyOsd(olds);
1923
2030
  })
1924
2031
  };
1925
- var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect19.succeed(ProxmoxCephOsd.Provider.of(handlers4)));
2032
+ var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers3)));
1926
2033
  // src/proxmox/ceph-pool.ts
1927
2034
  import { Resource as Resource8 } from "alchemy";
2035
+ import { isResolved as isResolved7 } from "alchemy/Diff";
1928
2036
  import * as Provider8 from "alchemy/Provider";
1929
- import * as Effect21 from "effect/Effect";
2037
+ import * as nodes7 from "@distilled.cloud/proxmox/nodes";
2038
+ import * as Effect24 from "effect/Effect";
1930
2039
 
1931
2040
  // src/proxmox/ceph-pool-form.ts
1932
2041
  var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
1933
- var collection = (props) => `nodes/${props.node}/ceph/pool`;
1934
2042
  var UNSET = -1;
1935
2043
  var applications = (value) => csv(Array.isArray(value) ? value.map((entry) => String(entry)) : []);
1936
2044
  var same = (declared, live, frozen = false) => declared === undefined || frozen || declared === live;
@@ -1955,92 +2063,112 @@ var createBody = (props) => ({
1955
2063
  });
1956
2064
 
1957
2065
  // src/proxmox/ceph-pool-settle.ts
1958
- import * as Effect20 from "effect/Effect";
1959
- var confirmAbsent = (props, collection2, describe3) => pve(props.target, "read", "GET", collection2).pipe(Effect20.flatMap((rows) => (rows ?? []).some((row) => row.pool_name === props.name) ? Effect20.die(new Error(`${describe3}: the cluster lists this pool but its status could not be read, ` + "so this deploy will NOT create over it. Check that the node is up and that the " + "read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply " + "pg_num to a live pool and start a PG merge.")) : Effect20.void));
1960
- var settle2 = (props, read, done) => Effect20.gen(function* () {
2066
+ import * as nodes5 from "@distilled.cloud/proxmox/nodes";
2067
+ import * as Effect22 from "effect/Effect";
2068
+ var confirmAbsent = (props, describe3) => runPve(props.target, "read", false, nodes5.listNodeCephPool({ node: props.node })).pipe(Effect22.flatMap((rows) => rows.some((row) => row.pool_name === props.name) ? Effect22.die(new Error(`${describe3}: the cluster lists this pool but its status could not be read, ` + "so this deploy will NOT create over it. Check that the node is up and that the " + "read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply " + "pg_num to a live pool and start a PG merge.")) : Effect22.void));
2069
+ var settle2 = (props, read, done) => Effect22.gen(function* () {
1961
2070
  for (let attempt = 0;attempt < 30; attempt += 1) {
1962
2071
  const live = yield* read(props);
1963
2072
  if (done(live))
1964
2073
  return live;
1965
- yield* Effect20.sleep("2 seconds");
2074
+ yield* Effect22.sleep("2 seconds");
1966
2075
  }
1967
2076
  return yield* read(props);
1968
2077
  });
1969
2078
 
2079
+ // src/proxmox/ceph-pool-wire.ts
2080
+ import * as nodes6 from "@distilled.cloud/proxmox/nodes";
2081
+ import * as Effect23 from "effect/Effect";
2082
+ var attributesOf3 = (live, props) => {
2083
+ const row = live;
2084
+ if (typeof row["name"] !== "string")
2085
+ return;
2086
+ return {
2087
+ applications: applications(row["application_list"]),
2088
+ crush_rule: text(row["crush_rule"]),
2089
+ id: int(row["id"], UNSET),
2090
+ min_size: int(row["min_size"], UNSET),
2091
+ name: props.name,
2092
+ node: props.node,
2093
+ nodelete: bool(row["nodelete"]),
2094
+ nopgchange: bool(row["nopgchange"]),
2095
+ nosizechange: bool(row["nosizechange"]),
2096
+ pg_autoscale_mode: text(row["pg_autoscale_mode"]),
2097
+ pg_num: int(row["pg_num"], UNSET),
2098
+ pg_num_min: int(row["pg_num_min"], UNSET),
2099
+ size: int(row["size"], UNSET),
2100
+ target_size: int(row["target_size"], UNSET),
2101
+ target_size_ratio: num(row["target_size_ratio"], UNSET)
2102
+ };
2103
+ };
2104
+ var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(Effect23.map((live) => attributesOf3(live, props)), Effect23.catchTag("CephPoolNotFound", () => Effect23.succeed(undefined)));
2105
+ var CEPH_POOL_CREATE = "pve:POST /nodes/{node}/ceph/pool";
2106
+ var CEPH_POOL_UPDATE = "pve:PUT /nodes/{node}/ceph/pool/{name}";
2107
+ var toDistilledCreate2 = (props) => ({
2108
+ ...createBody(props),
2109
+ node: props.node
2110
+ });
2111
+ var toDistilledUpdate = (props) => ({
2112
+ ...updateBody(props),
2113
+ name: props.name,
2114
+ node: props.node
2115
+ });
2116
+
1970
2117
  // src/proxmox/ceph-pool.ts
1971
2118
  var ProxmoxCephPool = Resource8("Proxmox.CephPool", {
1972
2119
  defaultRemovalPolicy: "retain"
1973
2120
  });
1974
- var spec = {
1975
- attributes: (live, props) => {
1976
- if (typeof live["name"] !== "string")
2121
+ var matches3 = (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size);
2122
+ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.succeed(ProxmoxCephPool.Provider.of({
2123
+ list: () => Effect24.succeed([]),
2124
+ read: ({ olds }) => readPoolStatus(olds),
2125
+ diff: Effect24.fn(function* ({ news, output }) {
2126
+ if (!isResolved7(news))
1977
2127
  return;
1978
- return {
1979
- applications: applications(live["application_list"]),
1980
- crush_rule: text(live["crush_rule"]),
1981
- id: int(live["id"], UNSET),
1982
- min_size: int(live["min_size"], UNSET),
1983
- name: props.name,
1984
- node: props.node,
1985
- nodelete: bool(live["nodelete"]),
1986
- nopgchange: bool(live["nopgchange"]),
1987
- nosizechange: bool(live["nosizechange"]),
1988
- pg_autoscale_mode: text(live["pg_autoscale_mode"]),
1989
- pg_num: int(live["pg_num"], UNSET),
1990
- pg_num_min: int(live["pg_num_min"], UNSET),
1991
- size: int(live["size"], UNSET),
1992
- target_size: int(live["target_size"], UNSET),
1993
- target_size_ratio: num(live["target_size_ratio"], UNSET)
1994
- };
1995
- },
1996
- collection,
1997
- createForm: createBody,
1998
- endpoint: {
1999
- create: "pve:POST /nodes/{node}/ceph/pool",
2000
- update: "pve:PUT /nodes/{node}/ceph/pool/{name}"
2001
- },
2002
- matches: (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size),
2003
- path: (props) => `${object(props)}/status?verbose=1`,
2004
- updateForm: updateBody
2005
- };
2006
- var ops2 = pveOperations(spec);
2007
- var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect21.succeed(ProxmoxCephPool.Provider.of({
2008
- list: () => Effect21.succeed([]),
2009
- read: ({ olds }) => ops2.read(olds),
2010
- diff: ({ news, output }) => ops2.diff(news, output),
2011
- reconcile: Effect21.fn(function* ({ news }) {
2012
- const live = yield* ops2.read(news);
2128
+ yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
2129
+ yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
2130
+ if (output === undefined)
2131
+ return;
2132
+ const live = yield* readPoolStatus(news);
2133
+ if (live === undefined) {
2134
+ yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
2135
+ return { action: "update" };
2136
+ }
2137
+ return matches3(live, news) ? { action: "noop" } : { action: "update" };
2138
+ }),
2139
+ reconcile: Effect24.fn(function* ({ news }) {
2140
+ const live = yield* readPoolStatus(news);
2013
2141
  let upid;
2014
2142
  if (live === undefined) {
2015
- yield* ops2.guardCreate(news, true);
2016
- yield* confirmAbsent(news, spec.collection(news), object(news));
2017
- upid = yield* pve(news.target, "provision", "POST", collection(news), createBody(news));
2143
+ yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
2144
+ yield* confirmAbsent(news, object(news));
2145
+ upid = yield* runPve(news.target, "provision", true, nodes7.createNodeCephPool(toDistilledCreate2(news)));
2018
2146
  } else {
2019
- const form = formToSend(spec.matches, live, news, updateBody(news));
2147
+ const form = formToSend(matches3, live, news, updateBody(news));
2020
2148
  if (form !== undefined) {
2021
- yield* ops2.guardUpdate(news);
2022
- upid = yield* pve(news.target, "provision", "PUT", object(news), form);
2149
+ yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
2150
+ upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
2023
2151
  }
2024
2152
  }
2025
- const after = yield* settle2(news, ops2.read, (row) => row !== undefined && spec.matches(row, news));
2026
- if (after === undefined || !spec.matches(after, news)) {
2027
- return yield* Effect21.die(new Error(`${object(news)}: the write returned no error but the pool still does not match ` + `the declaration after 60s. PVE answered with task ${upid ?? "(none)"} -- read ` + "its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph " + "refused."));
2153
+ const after = yield* settle2(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
2154
+ if (after === undefined || !matches3(after, news)) {
2155
+ return yield* Effect24.die(new Error(`${object(news)}: the write returned no error but the pool still does not match ` + `the declaration after 60s. PVE answered with task ${upid ?? "(none)"} -- read ` + "its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph " + "refused."));
2028
2156
  }
2029
2157
  return after;
2030
2158
  }),
2031
- delete: Effect21.fn(function* ({ olds }) {
2032
- yield* pve(olds.target, "provision", "DELETE", object(olds));
2033
- const left = yield* settle2(olds, ops2.read, (row) => row === undefined);
2159
+ delete: Effect24.fn(function* ({ olds }) {
2160
+ yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
2161
+ const left = yield* settle2(olds, readPoolStatus, (row) => row === undefined);
2034
2162
  if (left !== undefined) {
2035
- return yield* Effect21.die(new Error(`${object(olds)}: the DELETE returned no error but the pool is still there after ` + "60s. Either a storage on it still holds RBD images, or the mons are running " + "with mon_allow_pool_delete false."));
2163
+ return yield* Effect24.die(new Error(`${object(olds)}: the DELETE returned no error but the pool is still there after ` + "60s. Either a storage on it still holds RBD images, or the mons are running " + "with mon_allow_pool_delete false."));
2036
2164
  }
2037
2165
  })
2038
2166
  })));
2039
2167
  // src/proxmox/firewall-alias.ts
2040
2168
  import { Resource as Resource9 } from "alchemy";
2041
- import { isResolved as isResolved6 } from "alchemy/Diff";
2169
+ import { isResolved as isResolved8 } from "alchemy/Diff";
2042
2170
  import * as Provider9 from "alchemy/Provider";
2043
- import * as Effect22 from "effect/Effect";
2171
+ import * as Effect25 from "effect/Effect";
2044
2172
 
2045
2173
  // src/proxmox/firewall-alias-form.ts
2046
2174
  var fold = (name) => name.trim().toLowerCase();
@@ -2060,7 +2188,7 @@ var body = (props) => {
2060
2188
 
2061
2189
  // src/proxmox/firewall-alias.ts
2062
2190
  var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
2063
- var handlers5 = pveHandlers({
2191
+ var handlers4 = pveHandlers({
2064
2192
  attributes: (live, props) => {
2065
2193
  const address = cidr(live["cidr"]);
2066
2194
  return address === "" ? undefined : {
@@ -2080,25 +2208,25 @@ var handlers5 = pveHandlers({
2080
2208
  path: (props) => `cluster/firewall/aliases/${props.name}`,
2081
2209
  updateForm: body
2082
2210
  });
2083
- var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect22.succeed(ProxmoxFirewallAlias.Provider.of({
2084
- ...handlers5,
2085
- diff: Effect22.fn(function* ({ news, output }) {
2086
- if (output !== undefined && isResolved6(news) && fold(news.name) !== fold(output.name)) {
2211
+ var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
2212
+ ...handlers4,
2213
+ diff: Effect25.fn(function* ({ news, output }) {
2214
+ if (output !== undefined && isResolved8(news) && fold(news.name) !== fold(output.name)) {
2087
2215
  return { action: "replace" };
2088
2216
  }
2089
- return yield* handlers5.diff({ news, output });
2217
+ return yield* handlers4.diff({ news, output });
2090
2218
  })
2091
2219
  })));
2092
2220
  // src/proxmox/group.ts
2093
2221
  import { Resource as Resource10 } from "alchemy";
2094
- import { isResolved as isResolved7 } from "alchemy/Diff";
2222
+ import { isResolved as isResolved9 } from "alchemy/Diff";
2095
2223
  import * as Provider10 from "alchemy/Provider";
2096
2224
  import * as access4 from "@distilled.cloud/proxmox/access";
2097
- import * as Effect24 from "effect/Effect";
2225
+ import * as Effect27 from "effect/Effect";
2098
2226
 
2099
2227
  // src/proxmox/group-wire.ts
2100
2228
  import * as access3 from "@distilled.cloud/proxmox/access";
2101
- import * as Effect23 from "effect/Effect";
2229
+ import * as Effect26 from "effect/Effect";
2102
2230
  var GROUP_CREATE = "pve:POST /access/groups";
2103
2231
  var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
2104
2232
  var storedComment = (comment2) => {
@@ -2117,29 +2245,27 @@ var updateForm = (props) => ({
2117
2245
  comment: storedComment(props.comment),
2118
2246
  groupid: props.groupid
2119
2247
  });
2120
- var attributesOf3 = (live, props) => ({
2248
+ var attributesOf4 = (live, props) => ({
2121
2249
  comment: text(live.comment),
2122
2250
  groupid: props.groupid,
2123
2251
  members: memberList(live.members)
2124
2252
  });
2125
- var matches3 = (attributes, props) => attributes.comment === storedComment(props.comment);
2126
- var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect23.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf3(live, props)));
2127
- var readGroup = (props) => readGroupOrFail(props).pipe(Effect23.orElseSucceed(() => {
2128
- return;
2129
- }));
2253
+ var matches4 = (attributes, props) => attributes.comment === storedComment(props.comment);
2254
+ var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect26.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf4(live, props)));
2255
+ var readGroup = (props) => readGroupOrFail(props).pipe(Effect26.catchTag("GroupNotFound", () => Effect26.succeed(undefined)));
2130
2256
  var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
2131
2257
 
2132
2258
  // src/proxmox/group.ts
2133
2259
  var ProxmoxGroup = Resource10("Proxmox.Group", {
2134
2260
  defaultRemovalPolicy: "retain"
2135
2261
  });
2136
- var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect24.succeed(ProxmoxGroup.Provider.of({
2137
- list: () => Effect24.succeed([]),
2138
- read: Effect24.fn(function* ({ olds, output }) {
2262
+ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succeed(ProxmoxGroup.Provider.of({
2263
+ list: () => Effect27.succeed([]),
2264
+ read: Effect27.fn(function* ({ olds, output }) {
2139
2265
  return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
2140
2266
  }),
2141
- diff: Effect24.fn(function* ({ news, output }) {
2142
- if (!isResolved7(news))
2267
+ diff: Effect27.fn(function* ({ news, output }) {
2268
+ if (!isResolved9(news))
2143
2269
  return;
2144
2270
  yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
2145
2271
  yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
@@ -2154,31 +2280,31 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect24.succee
2154
2280
  yield* guardWrite2(GROUP_CREATE, createForm3(news), true);
2155
2281
  return { action: "update" };
2156
2282
  }
2157
- return matches3(live, news) ? { action: "noop" } : { action: "update" };
2283
+ return matches4(live, news) ? { action: "noop" } : { action: "update" };
2158
2284
  }),
2159
- reconcile: Effect24.fn(function* ({ news }) {
2285
+ reconcile: Effect27.fn(function* ({ news }) {
2160
2286
  const before = dropUnreadable3(yield* readGroup(news));
2161
2287
  yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
2162
2288
  yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
2163
2289
  if (before === undefined) {
2164
2290
  yield* runPve(news.target, "provision", true, access4.createAccessGroup(createForm3(news)));
2165
- } else if (!matches3(before, news)) {
2291
+ } else if (!matches4(before, news)) {
2166
2292
  yield* runPve(news.target, "provision", true, access4.putAccessGroup(updateForm(news)));
2167
2293
  }
2168
2294
  const after = dropUnreadable3(yield* readGroup(news));
2169
2295
  if (after === undefined) {
2170
- return yield* Effect24.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
2296
+ return yield* Effect27.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
2171
2297
  }
2172
2298
  return after;
2173
2299
  }),
2174
- delete: Effect24.fn(function* ({ olds }) {
2300
+ delete: Effect27.fn(function* ({ olds }) {
2175
2301
  yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
2176
2302
  })
2177
2303
  })));
2178
2304
  // src/proxmox/ha-resource.ts
2179
2305
  import { Resource as Resource11 } from "alchemy";
2180
2306
  import * as Provider11 from "alchemy/Provider";
2181
- import * as Effect25 from "effect/Effect";
2307
+ import * as Effect28 from "effect/Effect";
2182
2308
  var ProxmoxHaResource = Resource11("Proxmox.HaResource");
2183
2309
  var kind = (sid) => sid.split(":")[0] ?? "";
2184
2310
  var shape3 = (props) => ({
@@ -2187,7 +2313,7 @@ var shape3 = (props) => ({
2187
2313
  max_restart: String(props.max_restart ?? 1),
2188
2314
  state: props.state ?? "started"
2189
2315
  });
2190
- var handlers6 = pveHandlers({
2316
+ var handlers5 = pveHandlers({
2191
2317
  attributes: (live, props) => ({
2192
2318
  comment: typeof live["comment"] === "string" ? live["comment"] : "",
2193
2319
  max_relocate: num(live["max_relocate"], 1),
@@ -2206,11 +2332,11 @@ var handlers6 = pveHandlers({
2206
2332
  path: (props) => `cluster/ha/resources/${props.sid}`,
2207
2333
  updateForm: shape3
2208
2334
  });
2209
- var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect25.succeed(ProxmoxHaResource.Provider.of(handlers6)));
2335
+ var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers5)));
2210
2336
  // src/proxmox/ha-rule.ts
2211
2337
  import { Resource as Resource12 } from "alchemy";
2212
2338
  import * as Provider12 from "alchemy/Provider";
2213
- import * as Effect26 from "effect/Effect";
2339
+ import * as Effect29 from "effect/Effect";
2214
2340
 
2215
2341
  // src/proxmox/ha-rule-form.ts
2216
2342
  var nodeList = (value) => {
@@ -2258,7 +2384,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
2258
2384
 
2259
2385
  // src/proxmox/ha-rule.ts
2260
2386
  var ProxmoxHaRule = Resource12("Proxmox.HaRule");
2261
- var handlers7 = pveHandlers({
2387
+ var handlers6 = pveHandlers({
2262
2388
  attributes: (live, props) => {
2263
2389
  const liveType = text(live["type"]);
2264
2390
  if (liveType !== "" && liveType !== props.type)
@@ -2287,43 +2413,43 @@ var handlers7 = pveHandlers({
2287
2413
  return withClears(fields, clear);
2288
2414
  }
2289
2415
  });
2290
- var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect26.succeed(ProxmoxHaRule.Provider.of(handlers7)));
2416
+ var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers6)));
2291
2417
  // src/proxmox/lxc.ts
2292
2418
  import { Resource as Resource13 } from "alchemy";
2293
- import { isResolved as isResolved8 } from "alchemy/Diff";
2419
+ import { isResolved as isResolved10 } from "alchemy/Diff";
2294
2420
  import * as Provider13 from "alchemy/Provider";
2295
- import * as Effect36 from "effect/Effect";
2421
+ import * as Effect39 from "effect/Effect";
2296
2422
 
2297
2423
  // src/ownership/adopt.ts
2298
2424
  import { AdoptPolicy } from "alchemy/AdoptPolicy";
2299
2425
  import { AlchemyContext } from "alchemy/AlchemyContext";
2300
2426
  import { Stack as Stack3 } from "alchemy/Stack";
2301
- import * as Effect30 from "effect/Effect";
2427
+ import * as Effect33 from "effect/Effect";
2302
2428
  import * as Option5 from "effect/Option";
2303
2429
 
2304
2430
  // src/ownership/resume.ts
2305
2431
  import { Unowned } from "alchemy/AdoptPolicy";
2306
2432
  import { Artifacts } from "alchemy/Artifacts";
2307
- import * as Effect27 from "effect/Effect";
2433
+ import * as Effect30 from "effect/Effect";
2308
2434
  import * as Option2 from "effect/Option";
2309
2435
  var PROVEN = "homeflare/ownership/resumes";
2310
2436
  var UNFINISHED = "homeflare/ownership/unfinished";
2311
- var note = (key, instanceId) => Effect27.gen(function* () {
2312
- const bag = yield* Effect27.serviceOption(Artifacts);
2437
+ var note = (key, instanceId) => Effect30.gen(function* () {
2438
+ const bag = yield* Effect30.serviceOption(Artifacts);
2313
2439
  if (Option2.isSome(bag))
2314
2440
  yield* bag.value.set(key, instanceId);
2315
2441
  });
2316
- var noted = (key, instanceId) => Effect27.gen(function* () {
2317
- const bag = yield* Effect27.serviceOption(Artifacts);
2442
+ var noted = (key, instanceId) => Effect30.gen(function* () {
2443
+ const bag = yield* Effect30.serviceOption(Artifacts);
2318
2444
  if (Option2.isNone(bag))
2319
2445
  return false;
2320
2446
  return (yield* bag.value.get(key)) === instanceId;
2321
2447
  });
2322
- var noteUnfinished = (instanceId) => Effect27.as(note(UNFINISHED, instanceId), undefined);
2323
- var noteResume = (instanceId, recovered) => Effect27.gen(function* () {
2448
+ var noteUnfinished = (instanceId) => Effect30.as(note(UNFINISHED, instanceId), undefined);
2449
+ var noteResume = (instanceId, recovered) => Effect30.gen(function* () {
2324
2450
  yield* noteUnfinished(instanceId);
2325
- const unproven = () => Effect27.succeed(undefined);
2326
- const found = yield* recovered.pipe(Effect27.catch(unproven), Effect27.catchDefect(unproven));
2451
+ const unproven = () => Effect30.succeed(undefined);
2452
+ const found = yield* recovered.pipe(Effect30.catch(unproven), Effect30.catchDefect(unproven));
2327
2453
  if (found === undefined || found === null || Unowned.is(found))
2328
2454
  return;
2329
2455
  yield* note(PROVEN, instanceId);
@@ -2334,7 +2460,7 @@ var provingResumes = (service) => {
2334
2460
  const { diff, read } = service;
2335
2461
  if (diff === undefined || read === undefined)
2336
2462
  return service;
2337
- const proving = (input) => Effect27.gen(function* () {
2463
+ const proving = (input) => Effect30.gen(function* () {
2338
2464
  if (input.output === undefined) {
2339
2465
  yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
2340
2466
  }
@@ -2346,12 +2472,12 @@ var provingResumes = (service) => {
2346
2472
  // src/ownership/rows.ts
2347
2473
  import { Stack as Stack2 } from "alchemy/Stack";
2348
2474
  import { State, isActionState } from "alchemy/State/State";
2349
- import * as Effect29 from "effect/Effect";
2475
+ import * as Effect32 from "effect/Effect";
2350
2476
  import * as Option4 from "effect/Option";
2351
2477
 
2352
2478
  // src/ownership/whole.ts
2353
2479
  import { Stack } from "alchemy/Stack";
2354
- import * as Effect28 from "effect/Effect";
2480
+ import * as Effect31 from "effect/Effect";
2355
2481
  import * as Option3 from "effect/Option";
2356
2482
  var absent = (value) => value === undefined || value === null;
2357
2483
  var isPlainObject = (value) => {
@@ -2377,22 +2503,22 @@ var carries = (declared, recorded) => {
2377
2503
  }
2378
2504
  return true;
2379
2505
  };
2380
- var wholeDeclaration = (fqn, props) => Effect28.map(Effect28.serviceOption(Stack), (stack) => {
2506
+ var wholeDeclaration = (fqn, props) => Effect31.map(Effect31.serviceOption(Stack), (stack) => {
2381
2507
  const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
2382
2508
  return declared !== undefined && carries(declared.Props, props);
2383
2509
  });
2384
2510
 
2385
2511
  // src/ownership/rows.ts
2386
- var storeOf = Effect29.gen(function* () {
2387
- const stack = yield* Effect29.serviceOption(Stack2);
2388
- const state = yield* Effect29.serviceOption(State);
2512
+ var storeOf = Effect32.gen(function* () {
2513
+ const stack = yield* Effect32.serviceOption(Stack2);
2514
+ const state = yield* Effect32.serviceOption(State);
2389
2515
  if (Option4.isNone(stack) || Option4.isNone(state))
2390
2516
  return;
2391
2517
  const store = yield* state.value;
2392
2518
  const where = { stack: stack.value.name, stage: stack.value.stage };
2393
2519
  return { resources: stack.value.resources, store, where };
2394
2520
  });
2395
- var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect29.orElseSucceed(() => {
2521
+ var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect32.orElseSucceed(() => {
2396
2522
  return;
2397
2523
  }));
2398
2524
  var generationOf = (row, instanceId) => {
@@ -2405,7 +2531,7 @@ var generationOf = (row, instanceId) => {
2405
2531
  }
2406
2532
  return;
2407
2533
  };
2408
- var recordedGeneration = (fqn, instanceId) => Effect29.gen(function* () {
2534
+ var recordedGeneration = (fqn, instanceId) => Effect32.gen(function* () {
2409
2535
  const found = yield* storeOf;
2410
2536
  if (found === undefined)
2411
2537
  return;
@@ -2416,14 +2542,14 @@ var recordedGeneration = (fqn, instanceId) => Effect29.gen(function* () {
2416
2542
  }
2417
2543
  return;
2418
2544
  });
2419
- var recordedInstance = (fqn, instanceId) => Effect29.gen(function* () {
2545
+ var recordedInstance = (fqn, instanceId) => Effect32.gen(function* () {
2420
2546
  const generation = yield* recordedGeneration(fqn, instanceId);
2421
2547
  if (generation === undefined)
2422
2548
  return "absent";
2423
2549
  return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
2424
2550
  });
2425
- var isCreate = (fqn, instanceId) => Effect29.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
2426
- var forgetRefusedCreate = (fqn, instanceId) => Effect29.gen(function* () {
2551
+ var isCreate = (fqn, instanceId) => Effect32.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
2552
+ var forgetRefusedCreate = (fqn, instanceId) => Effect32.gen(function* () {
2427
2553
  const found = yield* storeOf;
2428
2554
  if (found === undefined)
2429
2555
  return;
@@ -2432,32 +2558,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect29.gen(function* () {
2432
2558
  if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
2433
2559
  return;
2434
2560
  }
2435
- yield* found.store.delete({ ...found.where, fqn }).pipe(Effect29.ignore);
2561
+ yield* found.store.delete({ ...found.where, fqn }).pipe(Effect32.ignore);
2436
2562
  });
2437
2563
 
2438
2564
  // src/ownership/adopt.ts
2439
- var adoptEnabled = (fqn) => Effect30.gen(function* () {
2440
- const stack = yield* Effect30.serviceOption(Stack3);
2565
+ var adoptEnabled = (fqn) => Effect33.gen(function* () {
2566
+ const stack = yield* Effect33.serviceOption(Stack3);
2441
2567
  const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
2442
2568
  if (scoped !== undefined)
2443
2569
  return scoped;
2444
- const policy = yield* Effect30.serviceOption(AdoptPolicy);
2570
+ const policy = yield* Effect33.serviceOption(AdoptPolicy);
2445
2571
  if (Option5.isSome(policy))
2446
2572
  return policy.value;
2447
- const context = yield* Effect30.serviceOption(AlchemyContext);
2573
+ const context = yield* Effect33.serviceOption(AlchemyContext);
2448
2574
  return Option5.isSome(context) ? context.value.adopt : false;
2449
2575
  });
2450
- var adoptable = (owner) => Effect30.gen(function* () {
2576
+ var adoptable = (owner) => Effect33.gen(function* () {
2451
2577
  if (yield* isCreate(owner.fqn, owner.instanceId))
2452
2578
  return true;
2453
2579
  return yield* unfinished(owner.instanceId);
2454
2580
  });
2455
- var adoptsAtApply = (owner) => Effect30.gen(function* () {
2581
+ var adoptsAtApply = (owner) => Effect33.gen(function* () {
2456
2582
  if (!(yield* adoptEnabled(owner.fqn)))
2457
2583
  return false;
2458
2584
  return yield* adoptable(owner);
2459
2585
  });
2460
- var refuseTakeover = (owner, what) => Effect30.gen(function* () {
2586
+ var refuseTakeover = (owner, what) => Effect33.gen(function* () {
2461
2587
  if (owner.output !== undefined)
2462
2588
  return;
2463
2589
  if (yield* resumes(owner.instanceId))
@@ -2467,25 +2593,25 @@ var refuseTakeover = (owner, what) => Effect30.gen(function* () {
2467
2593
  return;
2468
2594
  yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
2469
2595
  const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
2470
- return yield* Effect30.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
2596
+ return yield* Effect33.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
2471
2597
  });
2472
2598
  var claimFor = (owner) => (what) => refuseTakeover(owner, what);
2473
2599
 
2474
2600
  // src/ownership/probe.ts
2475
2601
  import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
2476
- import * as Effect31 from "effect/Effect";
2477
- var provenOurs = (ask, settled2) => Effect31.gen(function* () {
2602
+ import * as Effect34 from "effect/Effect";
2603
+ var provenOurs = (ask, settled2) => Effect34.gen(function* () {
2478
2604
  const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
2479
2605
  if (recorded === "absent")
2480
2606
  return false;
2481
2607
  if (recorded === "partial") {
2482
- yield* Effect31.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
2608
+ yield* Effect34.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
2483
2609
  return false;
2484
2610
  }
2485
- const unproven = (reason) => Effect31.as(Effect31.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
2486
- return yield* settled2.pipe(Effect31.catch(unproven), Effect31.catchDefect(unproven));
2611
+ const unproven = (reason) => Effect34.as(Effect34.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
2612
+ return yield* settled2.pipe(Effect34.catch(unproven), Effect34.catchDefect(unproven));
2487
2613
  });
2488
- var ownedRead = (ask, found, settled2) => Effect31.gen(function* () {
2614
+ var ownedRead = (ask, found, settled2) => Effect34.gen(function* () {
2489
2615
  if (found === undefined || ask.output !== undefined)
2490
2616
  return found;
2491
2617
  return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
@@ -2543,8 +2669,8 @@ var optionMap = (value, family, defaultKey) => {
2543
2669
  return out;
2544
2670
  };
2545
2671
  var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
2546
- var sortedForm = (map9) => [...map9].map(([key, value]) => `${key}=${value}`).sort().join(",");
2547
- var printMap = (map9, lead = []) => [...lead.filter((key) => map9.has(key)), ...[...map9.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map9.get(key) ?? ""}`).join(",");
2672
+ var sortedForm = (map12) => [...map12].map(([key, value]) => `${key}=${value}`).sort().join(",");
2673
+ var printMap = (map12, lead = []) => [...lead.filter((key) => map12.has(key)), ...[...map12.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map12.get(key) ?? ""}`).join(",");
2548
2674
  var netMaps = (declared, live) => {
2549
2675
  const want = optionMap(declared, "net");
2550
2676
  const have = optionMap(live, "net");
@@ -2690,12 +2816,12 @@ var sizeBytes = (text2) => {
2690
2816
  };
2691
2817
  var parseVolume = (text2) => {
2692
2818
  const [first] = pairs(text2, "volume");
2693
- const spec2 = first?.[0] === "volume" ? first[1] : "";
2819
+ const spec = first?.[0] === "volume" ? first[1] : "";
2694
2820
  const options = optionMap(text2, "volume", "volume");
2695
2821
  options.delete("volume");
2696
2822
  const sizeText = options.get("size");
2697
2823
  options.delete("size");
2698
- const fresh = NEW_DISK.exec(spec2);
2824
+ const fresh = NEW_DISK.exec(spec);
2699
2825
  if (fresh !== null) {
2700
2826
  const gib = `${fresh[2] ?? "0"}G`;
2701
2827
  return {
@@ -2708,18 +2834,18 @@ var parseVolume = (text2) => {
2708
2834
  };
2709
2835
  }
2710
2836
  const size = sizeText === undefined ? undefined : sizeBytes(sizeText);
2711
- if (spec2.startsWith("/")) {
2712
- const kind2 = spec2.startsWith("/dev/") ? "device" : "bind";
2713
- return { kind: kind2, options, size, sizeText, source: spec2, volname: "" };
2837
+ if (spec.startsWith("/")) {
2838
+ const kind2 = spec.startsWith("/dev/") ? "device" : "bind";
2839
+ return { kind: kind2, options, size, sizeText, source: spec, volname: "" };
2714
2840
  }
2715
- const at = spec2.indexOf(":");
2841
+ const at = spec.indexOf(":");
2716
2842
  return {
2717
2843
  kind: "volume",
2718
2844
  options,
2719
2845
  size,
2720
2846
  sizeText,
2721
- source: at === -1 ? spec2 : spec2.slice(0, at),
2722
- volname: at === -1 ? "" : spec2.slice(at + 1)
2847
+ source: at === -1 ? spec : spec.slice(0, at),
2848
+ volname: at === -1 ? "" : spec.slice(at + 1)
2723
2849
  };
2724
2850
  };
2725
2851
  var isHostPath = (volume) => volume.kind === "bind" || volume.kind === "device";
@@ -2940,11 +3066,11 @@ var identityRefusals = (news, olds, output) => {
2940
3066
  };
2941
3067
 
2942
3068
  // src/proxmox/lxc-adoption.ts
2943
- import * as Effect35 from "effect/Effect";
3069
+ import * as Effect38 from "effect/Effect";
2944
3070
 
2945
3071
  // src/ownership/adopting.ts
2946
- import * as Effect32 from "effect/Effect";
2947
- var adopting = (owner, settled2) => Effect32.gen(function* () {
3072
+ import * as Effect35 from "effect/Effect";
3073
+ var adopting = (owner, settled2) => Effect35.gen(function* () {
2948
3074
  if (owner.output === undefined)
2949
3075
  return false;
2950
3076
  const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
@@ -2956,11 +3082,11 @@ var adopting = (owner, settled2) => Effect32.gen(function* () {
2956
3082
  });
2957
3083
 
2958
3084
  // src/proxmox/lxc-lifecycle.ts
2959
- import * as Effect34 from "effect/Effect";
3085
+ import * as Effect37 from "effect/Effect";
2960
3086
 
2961
3087
  // src/proxmox/network-apply-read.ts
2962
- import * as Effect33 from "effect/Effect";
2963
- var stagedDiff = (target, node) => Effect33.gen(function* () {
3088
+ import * as Effect36 from "effect/Effect";
3089
+ var stagedDiff = (target, node) => Effect36.gen(function* () {
2964
3090
  const path = `nodes/${node}/network`;
2965
3091
  const credential = yield* leased(target, "read");
2966
3092
  const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
@@ -2973,8 +3099,8 @@ var pendingLines = (diff) => {
2973
3099
  `).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
2974
3100
  return Math.max(changed, 1);
2975
3101
  };
2976
- var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect33.map(pendingLines));
2977
- var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect33.map((rows) => {
3102
+ var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
3103
+ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
2978
3104
  const all = rows ?? [];
2979
3105
  const cluster = all.find((row) => row.type === "cluster");
2980
3106
  return {
@@ -2982,21 +3108,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
2982
3108
  quorate: cluster === undefined ? true : bool(cluster.quorate)
2983
3109
  };
2984
3110
  }));
2985
- var degradedReason = (target) => clusterHealth(target).pipe(Effect33.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
3111
+ var degradedReason = (target) => clusterHealth(target).pipe(Effect36.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
2986
3112
  var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
2987
3113
  var STILL_RUNNING = "still running when this provider stopped waiting";
2988
3114
  var ATTEMPTS = 30;
2989
- var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect33.gen(function* () {
3115
+ var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect36.gen(function* () {
2990
3116
  const path = `nodes/${node}/tasks/${upid}/status`;
2991
3117
  for (let attempt = 0;attempt < attempts; attempt += 1) {
2992
- const status = yield* pveWith(target, credential, "GET", path).pipe(Effect33.orElseSucceed(() => {
3118
+ const status = yield* pveWith(target, credential, "GET", path).pipe(Effect36.orElseSucceed(() => {
2993
3119
  return;
2994
3120
  }));
2995
3121
  if (status === undefined)
2996
3122
  return UNREACHABLE;
2997
3123
  if (status.status === "stopped")
2998
3124
  return text(status.exitstatus, "stopped with no exitstatus");
2999
- yield* Effect33.sleep("1 second");
3125
+ yield* Effect36.sleep("1 second");
3000
3126
  }
3001
3127
  return STILL_RUNNING;
3002
3128
  });
@@ -3014,43 +3140,43 @@ var LXC_ENDPOINTS = {
3014
3140
  var CREATE_POLLS = 180;
3015
3141
  var TASK_POLLS = 60;
3016
3142
  var isServerError = (error) => error instanceof PveError && error.status === 500;
3017
- var readLive = (where) => Effect34.gen(function* () {
3018
- const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect34.map((data) => ({ data, error: undefined })), Effect34.catchIf(isServerError, (error) => Effect34.succeed({ data: undefined, error })));
3143
+ var readLive = (where) => Effect37.gen(function* () {
3144
+ const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect37.map((data) => ({ data, error: undefined })), Effect37.catchIf(isServerError, (error) => Effect37.succeed({ data: undefined, error })));
3019
3145
  if (answer.data !== undefined && answer.data !== null)
3020
3146
  return answer.data;
3021
3147
  const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
3022
3148
  const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
3023
3149
  if (found !== undefined && found.node === where.node && found.type === "lxc") {
3024
- return yield* Effect34.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
3150
+ return yield* Effect37.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
3025
3151
  }
3026
3152
  if (found !== undefined) {
3027
- return yield* Effect34.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
3153
+ return yield* Effect37.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
3028
3154
  }
3029
3155
  return;
3030
3156
  });
3031
- var attributesOf4 = (where, live) => ({
3157
+ var attributesOf5 = (where, live) => ({
3032
3158
  config: storedConfig(live),
3033
3159
  node: where.node,
3034
3160
  rawKeys: rawKeysOf(live),
3035
3161
  vmid: where.vmid
3036
3162
  });
3037
- var task = (where, method, path, what, polls, form) => Effect34.gen(function* () {
3163
+ var task = (where, method, path, what, polls, form) => Effect37.gen(function* () {
3038
3164
  const credential = yield* mint(where.target, "provision");
3039
3165
  const upid = text(yield* pveWith(where.target, credential, method, path, form));
3040
3166
  if (upid === "") {
3041
- return yield* Effect34.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
3167
+ return yield* Effect37.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
3042
3168
  }
3043
3169
  const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
3044
3170
  if (exit !== "OK") {
3045
- return yield* Effect34.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
3171
+ return yield* Effect37.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
3046
3172
  }
3047
3173
  });
3048
- var createGuest = (props) => Effect34.gen(function* () {
3174
+ var createGuest = (props) => Effect37.gen(function* () {
3049
3175
  const form = createForm4(props);
3050
3176
  yield* guardForm(LXC_ENDPOINTS.create, form, true);
3051
3177
  return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
3052
3178
  });
3053
- var updateGuest = (props, change, digest) => Effect34.gen(function* () {
3179
+ var updateGuest = (props, change, digest) => Effect37.gen(function* () {
3054
3180
  if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
3055
3181
  const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
3056
3182
  yield* guardForm(LXC_ENDPOINTS.update, form, false);
@@ -3062,47 +3188,47 @@ var updateGuest = (props, change, digest) => Effect34.gen(function* () {
3062
3188
  yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
3063
3189
  }
3064
3190
  });
3065
- var destroyGuest = (where, declared) => Effect34.gen(function* () {
3191
+ var destroyGuest = (where, declared) => Effect37.gen(function* () {
3066
3192
  const live = yield* readLive(where);
3067
3193
  if (live === undefined)
3068
3194
  return;
3069
3195
  const drift = judge(declared, live).drift;
3070
3196
  if (drift.length > 0) {
3071
- return yield* Effect34.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
3197
+ return yield* Effect37.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
3072
3198
  }
3073
3199
  yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
3074
3200
  });
3075
3201
 
3076
3202
  // src/proxmox/lxc-adoption.ts
3077
3203
  var adoptionRefusal = (props, drift) => `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(", ")}. An ` + "adoption never changes a guest, so nothing is written. Declare what is live for these keys " + "(the plan names keys, never values) and plan again; change them after the adoption, as an " + "update.";
3078
- var refuseAdoptedDrift = (owner, props, live, change) => Effect35.gen(function* () {
3204
+ var refuseAdoptedDrift = (owner, props, live, change) => Effect38.gen(function* () {
3079
3205
  if (change.drift.length === 0)
3080
3206
  return;
3081
- const settled2 = (recorded) => Effect35.sync(() => judge(recorded, live).drift.length === 0);
3207
+ const settled2 = (recorded) => Effect38.sync(() => judge(recorded, live).drift.length === 0);
3082
3208
  if (!(yield* adopting(owner, settled2)))
3083
3209
  return;
3084
- return yield* Effect35.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
3210
+ return yield* Effect38.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
3085
3211
  });
3086
3212
 
3087
3213
  // src/proxmox/lxc.ts
3088
3214
  var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
3089
- var refused = (reasons) => Effect36.fail(new LxcRefusedError(reasons.join(`
3215
+ var refused = (reasons) => Effect39.fail(new LxcRefusedError(reasons.join(`
3090
3216
  `)));
3091
- var diff = (owner, news, olds) => Effect36.gen(function* () {
3217
+ var diff = (owner, news, olds) => Effect39.gen(function* () {
3092
3218
  const { output } = owner;
3093
3219
  const identity2 = identityRefusals(news, olds, output);
3094
3220
  if (identity2.length > 0)
3095
3221
  return yield* refused(identity2);
3096
3222
  if (output === undefined)
3097
3223
  return;
3098
- if (!isResolved8(news))
3224
+ if (!isResolved10(news))
3099
3225
  return;
3100
3226
  const live = yield* readLive(news);
3101
3227
  if (live === undefined) {
3102
3228
  const refusals = createRefusals(news);
3103
3229
  if (refusals.length > 0)
3104
3230
  return yield* refused(refusals);
3105
- yield* Effect36.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
3231
+ yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
3106
3232
  return { action: "update" };
3107
3233
  }
3108
3234
  const change = judge(news, live);
@@ -3113,10 +3239,10 @@ var diff = (owner, news, olds) => Effect36.gen(function* () {
3113
3239
  if (change.drift.length === 0 && !moved)
3114
3240
  return { action: "noop" };
3115
3241
  const what = change.drift.length === 0 ? `the guest is on ${news.node} now, not ${output.node} -- a deploy re-records it, ` + "writing nothing." : `live config differs from the declaration in ${change.drift.join(", ")} -- a ` + "deploy writes these.";
3116
- yield* Effect36.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
3242
+ yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
3117
3243
  return { action: "update" };
3118
3244
  });
3119
- var reconcile = (owner, news) => Effect36.gen(function* () {
3245
+ var reconcile = (owner, news) => Effect39.gen(function* () {
3120
3246
  const { output } = owner;
3121
3247
  const identity2 = identityRefusals(news, news, output);
3122
3248
  if (identity2.length > 0)
@@ -3155,22 +3281,22 @@ var reconcile = (owner, news) => Effect36.gen(function* () {
3155
3281
  `CT ${String(news.vmid)}: after the write, ${left.join(", ")} still differ from the ` + "declaration. PVE may have normalised the value (tags are lower-cased by default) -- " + "declare what it stores."
3156
3282
  ]);
3157
3283
  }
3158
- return attributesOf4(news, after);
3284
+ return attributesOf5(news, after);
3159
3285
  });
3160
3286
  var whereOf = (olds, output) => ({
3161
3287
  node: output?.node ?? olds.node,
3162
3288
  target: olds.target,
3163
3289
  vmid: output?.vmid ?? olds.vmid
3164
3290
  });
3165
- var read = (owner, olds) => Effect36.gen(function* () {
3291
+ var read = (owner, olds) => Effect39.gen(function* () {
3166
3292
  const where = whereOf(olds, owner.output);
3167
3293
  const live = yield* readLive(where);
3168
3294
  if (live === undefined)
3169
3295
  return;
3170
- const settled2 = Effect36.sync(() => judge(olds, live).drift.length === 0);
3171
- return yield* ownedRead(owner, attributesOf4(where, live), settled2);
3296
+ const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
3297
+ return yield* ownedRead(owner, attributesOf5(where, live), settled2);
3172
3298
  });
3173
- var handlers8 = {
3299
+ var handlers7 = {
3174
3300
  delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
3175
3301
  diff: ({
3176
3302
  fqn,
@@ -3179,7 +3305,7 @@ var handlers8 = {
3179
3305
  olds,
3180
3306
  output
3181
3307
  }) => diff({ fqn, instanceId, output }, news, olds),
3182
- list: () => Effect36.succeed([]),
3308
+ list: () => Effect39.succeed([]),
3183
3309
  read: ({
3184
3310
  fqn,
3185
3311
  instanceId,
@@ -3193,11 +3319,11 @@ var handlers8 = {
3193
3319
  output
3194
3320
  }) => reconcile({ fqn, instanceId, output }, news)
3195
3321
  };
3196
- var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect36.succeed(ProxmoxLxc.Provider.of(handlers8)).pipe(Effect36.map(provingResumes)));
3322
+ var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers7)).pipe(Effect39.map(provingResumes)));
3197
3323
  // src/proxmox/metric-server.ts
3198
3324
  import { Resource as Resource14 } from "alchemy";
3199
3325
  import * as Provider14 from "alchemy/Provider";
3200
- import * as Effect37 from "effect/Effect";
3326
+ import * as Effect40 from "effect/Effect";
3201
3327
 
3202
3328
  // src/proxmox/metric-server-form.ts
3203
3329
  var UNSET2 = -1;
@@ -3277,7 +3403,7 @@ var matchesType = (attributes, props) => {
3277
3403
  return socket && attributes.path === (props.path ?? "");
3278
3404
  return socket && attributes.influxdbproto === (props.influxdbproto ?? "") && attributes.organization === (props.organization ?? "") && attributes.bucket === (props.bucket ?? "") && attributes["api-path-prefix"] === (props["api-path-prefix"] ?? "") && attributes["max-body-size"] === (props["max-body-size"] ?? UNSET2) && attributes["verify-certificate"] === (props["verify-certificate"] !== false);
3279
3405
  };
3280
- var handlers9 = pveHandlers({
3406
+ var handlers8 = pveHandlers({
3281
3407
  attributes: (live, props) => {
3282
3408
  const liveType = text(live["type"]);
3283
3409
  if (liveType !== "" && liveType !== props.type)
@@ -3310,12 +3436,12 @@ var handlers9 = pveHandlers({
3310
3436
  path: (props) => `cluster/metrics/server/${props.id}`,
3311
3437
  updateForm: update
3312
3438
  });
3313
- var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect37.succeed(ProxmoxMetricServer.Provider.of(handlers9)));
3439
+ var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect40.succeed(ProxmoxMetricServer.Provider.of(handlers8)));
3314
3440
  // src/proxmox/network-apply.ts
3315
3441
  import { Resource as Resource15 } from "alchemy";
3316
- import { isResolved as isResolved9 } from "alchemy/Diff";
3442
+ import { isResolved as isResolved11 } from "alchemy/Diff";
3317
3443
  import * as Provider15 from "alchemy/Provider";
3318
- import * as Effect38 from "effect/Effect";
3444
+ import * as Effect41 from "effect/Effect";
3319
3445
 
3320
3446
  // src/proxmox/apply-endpoints.ts
3321
3447
  var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
@@ -3325,15 +3451,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
3325
3451
 
3326
3452
  // src/proxmox/network-apply.ts
3327
3453
  var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
3328
- var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect38.map((pending) => ({ node: props.node, pending })));
3329
- var diff2 = (news) => Effect38.gen(function* () {
3330
- if (!isResolved9(news))
3454
+ var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect41.map((pending) => ({ node: props.node, pending })));
3455
+ var diff2 = (news) => Effect41.gen(function* () {
3456
+ if (!isResolved11(news))
3331
3457
  return;
3332
3458
  const { pending } = yield* read2(news);
3333
3459
  return pending === 0 ? { action: "noop" } : { action: "update" };
3334
3460
  });
3335
- var refuse = (node, why) => Effect38.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3336
- var apply = (props) => Effect38.gen(function* () {
3461
+ var refuse = (node, why) => Effect41.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3462
+ var apply = (props) => Effect41.gen(function* () {
3337
3463
  const node = props.node;
3338
3464
  const staged = yield* pendingCount(props.target, node);
3339
3465
  if (staged === 0)
@@ -3362,20 +3488,20 @@ var apply = (props) => Effect38.gen(function* () {
3362
3488
  }
3363
3489
  return { node, pending: left };
3364
3490
  });
3365
- var handlers10 = {
3366
- delete: () => Effect38.void,
3491
+ var handlers9 = {
3492
+ delete: () => Effect41.void,
3367
3493
  diff: ({ news }) => diff2(news),
3368
- list: () => Effect38.succeed([]),
3494
+ list: () => Effect41.succeed([]),
3369
3495
  read: ({ olds }) => read2(olds),
3370
3496
  reconcile: ({ news }) => apply(news)
3371
3497
  };
3372
- var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect38.succeed(ProxmoxNetworkApply.Provider.of(handlers10)));
3498
+ var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect41.succeed(ProxmoxNetworkApply.Provider.of(handlers9)));
3373
3499
  // src/proxmox/node-network.ts
3374
3500
  import { Resource as Resource16 } from "alchemy";
3375
- import { isResolved as isResolved10 } from "alchemy/Diff";
3501
+ import { isResolved as isResolved12 } from "alchemy/Diff";
3376
3502
  import * as Provider16 from "alchemy/Provider";
3377
- import * as nodes2 from "@distilled.cloud/proxmox/nodes";
3378
- import * as Effect40 from "effect/Effect";
3503
+ import * as nodes9 from "@distilled.cloud/proxmox/nodes";
3504
+ import * as Effect43 from "effect/Effect";
3379
3505
 
3380
3506
  // src/proxmox/node-network-form.ts
3381
3507
  var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
@@ -3417,9 +3543,9 @@ var updateForm2 = (props) => {
3417
3543
  };
3418
3544
 
3419
3545
  // src/proxmox/node-network-wire.ts
3420
- import * as nodes from "@distilled.cloud/proxmox/nodes";
3421
- import * as Effect39 from "effect/Effect";
3422
- var attributesOf5 = (live, props) => {
3546
+ import * as nodes8 from "@distilled.cloud/proxmox/nodes";
3547
+ import * as Effect42 from "effect/Effect";
3548
+ var attributesOf6 = (live, props) => {
3423
3549
  const data = live;
3424
3550
  const liveType = text(data["type"]);
3425
3551
  if (liveType !== "" && liveType !== props.type)
@@ -3452,16 +3578,16 @@ var attributesOf5 = (live, props) => {
3452
3578
  "vlan-raw-device": text(data["vlan-raw-device"])
3453
3579
  };
3454
3580
  };
3455
- var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect39.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect39.succeed(undefined) : Effect39.fail(error)))).pipe(Effect39.map((live) => {
3581
+ var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect42.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect42.succeed(undefined) : Effect42.fail(error)))).pipe(Effect42.map((live) => {
3456
3582
  if (live === UNREADABLE)
3457
3583
  return UNREADABLE;
3458
- return live === undefined ? undefined : attributesOf5(live, props);
3584
+ return live === undefined ? undefined : attributesOf6(live, props);
3459
3585
  }));
3460
- var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect39.orElseSucceed(() => {
3586
+ var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect42.orElseSucceed(() => {
3461
3587
  return;
3462
3588
  }));
3463
3589
  var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
3464
- var matches4 = (attributes, props) => attributes.cidr === (props.cidr ?? "") && same2(props.gateway, attributes.gateway) && same2(props.autostart, attributes.autostart) && same2(props.mtu, attributes.mtu) && sameComment(props.comments, attributes.comments) && sameList(props.bridge_ports, attributes.bridge_ports) && sameList(props.bridge_vids, attributes.bridge_vids) && same2(props.bridge_vlan_aware, attributes.bridge_vlan_aware) && sameList(props.slaves, attributes.slaves) && same2(props.bond_mode, attributes.bond_mode) && same2(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) && same2(props["bond-primary"], attributes["bond-primary"]) && same2(props["vlan-id"], attributes["vlan-id"]) && same2(props["vlan-raw-device"], attributes["vlan-raw-device"]);
3590
+ var matches5 = (attributes, props) => attributes.cidr === (props.cidr ?? "") && same2(props.gateway, attributes.gateway) && same2(props.autostart, attributes.autostart) && same2(props.mtu, attributes.mtu) && sameComment(props.comments, attributes.comments) && sameList(props.bridge_ports, attributes.bridge_ports) && sameList(props.bridge_vids, attributes.bridge_vids) && same2(props.bridge_vlan_aware, attributes.bridge_vlan_aware) && sameList(props.slaves, attributes.slaves) && same2(props.bond_mode, attributes.bond_mode) && same2(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) && same2(props["bond-primary"], attributes["bond-primary"]) && same2(props["vlan-id"], attributes["vlan-id"]) && same2(props["vlan-raw-device"], attributes["vlan-raw-device"]);
3465
3591
 
3466
3592
  // src/proxmox/node-network.ts
3467
3593
  var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
@@ -3473,22 +3599,22 @@ var RENAMED = {
3473
3599
  "vlan-raw-device": "vlan_raw_device"
3474
3600
  };
3475
3601
  var toDistilled = (form) => Object.fromEntries(Object.entries(form).map(([key, value]) => [RENAMED[key] ?? key, value]));
3476
- var toDistilledCreate = (props) => ({
3602
+ var toDistilledCreate3 = (props) => ({
3477
3603
  ...toDistilled(createForm5(props)),
3478
3604
  node: props.node
3479
3605
  });
3480
- var toDistilledUpdate = (props) => ({
3606
+ var toDistilledUpdate2 = (props) => ({
3481
3607
  ...toDistilled(updateForm2(props)),
3482
3608
  iface: props.iface,
3483
3609
  node: props.node
3484
3610
  });
3485
- var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect40.succeed(ProxmoxNodeNetwork.Provider.of({
3486
- list: () => Effect40.succeed([]),
3487
- read: Effect40.fn(function* ({ olds, output }) {
3611
+ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect43.succeed(ProxmoxNodeNetwork.Provider.of({
3612
+ list: () => Effect43.succeed([]),
3613
+ read: Effect43.fn(function* ({ olds, output }) {
3488
3614
  return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
3489
3615
  }),
3490
- diff: Effect40.fn(function* ({ news, output }) {
3491
- if (!isResolved10(news))
3616
+ diff: Effect43.fn(function* ({ news, output }) {
3617
+ if (!isResolved12(news))
3492
3618
  return;
3493
3619
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
3494
3620
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
@@ -3503,31 +3629,31 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
3503
3629
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), true);
3504
3630
  return { action: "update" };
3505
3631
  }
3506
- return matches4(live, news) ? { action: "noop" } : { action: "update" };
3632
+ return matches5(live, news) ? { action: "noop" } : { action: "update" };
3507
3633
  }),
3508
- reconcile: Effect40.fn(function* ({ news }) {
3634
+ reconcile: Effect43.fn(function* ({ news }) {
3509
3635
  const before = dropUnreadable4(yield* readInterface(news));
3510
3636
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
3511
3637
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
3512
3638
  if (before === undefined) {
3513
- yield* runPve(news.target, "provision", true, nodes2.createNodeNetwork(toDistilledCreate(news)));
3514
- } else if (!matches4(before, news)) {
3515
- yield* runPve(news.target, "provision", true, nodes2.putNodeNetwork2(toDistilledUpdate(news)));
3639
+ yield* runPve(news.target, "provision", true, nodes9.createNodeNetwork(toDistilledCreate3(news)));
3640
+ } else if (!matches5(before, news)) {
3641
+ yield* runPve(news.target, "provision", true, nodes9.putNodeNetwork2(toDistilledUpdate2(news)));
3516
3642
  }
3517
3643
  const after = dropUnreadable4(yield* readInterface(news));
3518
3644
  if (after === undefined) {
3519
- return yield* Effect40.die(new Error(`nodes/${news.node}/network/${news.iface}: wrote with no error, but the ` + 'interface is still absent -- PVE wraps every answer in {"data":...} and can ' + "report success on a call that did nothing, so read back rather than trust it."));
3645
+ return yield* Effect43.die(new Error(`nodes/${news.node}/network/${news.iface}: wrote with no error, but the ` + 'interface is still absent -- PVE wraps every answer in {"data":...} and can ' + "report success on a call that did nothing, so read back rather than trust it."));
3520
3646
  }
3521
3647
  return after;
3522
3648
  }),
3523
- delete: Effect40.fn(function* ({ olds }) {
3524
- yield* runPve(olds.target, "provision", true, nodes2.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
3649
+ delete: Effect43.fn(function* ({ olds }) {
3650
+ yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
3525
3651
  })
3526
3652
  })));
3527
3653
  // src/proxmox/notification-matcher.ts
3528
3654
  import { Resource as Resource17 } from "alchemy";
3529
3655
  import * as Provider17 from "alchemy/Provider";
3530
- import * as Effect41 from "effect/Effect";
3656
+ import * as Effect44 from "effect/Effect";
3531
3657
 
3532
3658
  // src/proxmox/notification-matcher-form.ts
3533
3659
  var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
@@ -3605,7 +3731,7 @@ var matcherUpdateForm = (fields) => {
3605
3731
 
3606
3732
  // src/proxmox/notification-matcher.ts
3607
3733
  var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
3608
- var handlers11 = pveHandlers({
3734
+ var handlers10 = pveHandlers({
3609
3735
  attributes: (live, props) => matcherAttributes(live, props.name),
3610
3736
  collection: () => "cluster/notifications/matchers",
3611
3737
  createForm: matcherCreateForm,
@@ -3617,11 +3743,11 @@ var handlers11 = pveHandlers({
3617
3743
  path: (props) => `cluster/notifications/matchers/${props.name}`,
3618
3744
  updateForm: matcherUpdateForm
3619
3745
  });
3620
- var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect41.succeed(ProxmoxNotificationMatcher.Provider.of(handlers11)));
3746
+ var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect44.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
3621
3747
  // src/proxmox/notification-target.ts
3622
3748
  import { Resource as Resource18 } from "alchemy";
3623
3749
  import * as Provider18 from "alchemy/Provider";
3624
- import * as Effect42 from "effect/Effect";
3750
+ import * as Effect45 from "effect/Effect";
3625
3751
 
3626
3752
  // src/proxmox/notification-target-endpoint.ts
3627
3753
  var ENDPOINTS = {
@@ -3677,7 +3803,7 @@ var createShape = (props) => ({
3677
3803
 
3678
3804
  // src/proxmox/notification-target.ts
3679
3805
  var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
3680
- var handlers12 = pveHandlers({
3806
+ var handlers11 = pveHandlers({
3681
3807
  attributes: (live, props) => ({
3682
3808
  author: text(live["author"]),
3683
3809
  body: text(live["body"]),
@@ -3703,15 +3829,15 @@ var handlers12 = pveHandlers({
3703
3829
  path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
3704
3830
  updateForm: shape4
3705
3831
  });
3706
- var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect42.succeed(ProxmoxNotificationTarget.Provider.of(handlers12)));
3832
+ var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect45.succeed(ProxmoxNotificationTarget.Provider.of(handlers11)));
3707
3833
  // src/proxmox/pbs-datastore.ts
3708
3834
  import { Resource as Resource19 } from "alchemy";
3709
- import { isResolved as isResolved11 } from "alchemy/Diff";
3835
+ import { isResolved as isResolved13 } from "alchemy/Diff";
3710
3836
  import * as Provider19 from "alchemy/Provider";
3711
- import * as Effect46 from "effect/Effect";
3837
+ import * as Effect49 from "effect/Effect";
3712
3838
 
3713
3839
  // src/proxmox/pbs-datastore-form.ts
3714
- import * as Effect43 from "effect/Effect";
3840
+ import * as Effect46 from "effect/Effect";
3715
3841
  var attributes = (live, props) => ({
3716
3842
  backend: propertyString(live["backend"]),
3717
3843
  comment: text(live["comment"]),
@@ -3734,7 +3860,7 @@ var attributes = (live, props) => ({
3734
3860
  "verify-new": bool(live["verify-new"])
3735
3861
  });
3736
3862
  var same4 = (declared, live) => declared === undefined || declared === live;
3737
- var matches5 = (live, props) => same4(props.comment, live.comment) && same4(props["gc-schedule"]?.trim(), live["gc-schedule"]) && same4(props["prune-schedule"]?.trim(), live["prune-schedule"]) && same4(props["keep-last"], live["keep-last"]) && same4(props["keep-hourly"], live["keep-hourly"]) && same4(props["keep-daily"], live["keep-daily"]) && same4(props["keep-weekly"], live["keep-weekly"]) && same4(props["keep-monthly"], live["keep-monthly"]) && same4(props["keep-yearly"], live["keep-yearly"]) && same4(props["notify-user"], live["notify-user"]) && same4(props["counter-reset-schedule"]?.trim(), live["counter-reset-schedule"]) && (props["notification-thresholds"] === undefined || propertyString(props["notification-thresholds"]) === live["notification-thresholds"]) && same4(props["verify-new"], live["verify-new"]) && (props.notify === undefined || propertyString(props.notify) === live.notify) && (props.tuning === undefined || propertyString(props.tuning) === live.tuning) && (props["maintenance-mode"] === undefined || propertyString(props["maintenance-mode"], "type") === live["maintenance-mode"]);
3863
+ var matches6 = (live, props) => same4(props.comment, live.comment) && same4(props["gc-schedule"]?.trim(), live["gc-schedule"]) && same4(props["prune-schedule"]?.trim(), live["prune-schedule"]) && same4(props["keep-last"], live["keep-last"]) && same4(props["keep-hourly"], live["keep-hourly"]) && same4(props["keep-daily"], live["keep-daily"]) && same4(props["keep-weekly"], live["keep-weekly"]) && same4(props["keep-monthly"], live["keep-monthly"]) && same4(props["keep-yearly"], live["keep-yearly"]) && same4(props["notify-user"], live["notify-user"]) && same4(props["counter-reset-schedule"]?.trim(), live["counter-reset-schedule"]) && (props["notification-thresholds"] === undefined || propertyString(props["notification-thresholds"]) === live["notification-thresholds"]) && same4(props["verify-new"], live["verify-new"]) && (props.notify === undefined || propertyString(props.notify) === live.notify) && (props.tuning === undefined || propertyString(props.tuning) === live.tuning) && (props["maintenance-mode"] === undefined || propertyString(props["maintenance-mode"], "type") === live["maintenance-mode"]);
3738
3864
  var set2 = (key, value) => value === undefined ? {} : { [key]: value };
3739
3865
  var count = (value) => value === undefined ? undefined : String(value);
3740
3866
  var mutable = (props) => ({
@@ -3763,35 +3889,35 @@ var createForm6 = (props) => ({
3763
3889
  });
3764
3890
  var updateForm3 = (props) => mutable(props);
3765
3891
  var object2 = (props) => `config/datastore/${props.name}`;
3766
- var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect43.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect43.orElseSucceed(() => {
3892
+ var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect46.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect46.orElseSucceed(() => {
3767
3893
  return;
3768
3894
  }));
3769
3895
 
3770
3896
  // src/proxmox/pbs-datastore-guard.ts
3771
- import * as Effect44 from "effect/Effect";
3897
+ import * as Effect47 from "effect/Effect";
3772
3898
  var samePath = (left, right) => {
3773
3899
  const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
3774
3900
  return trim(left) === trim(right);
3775
3901
  };
3776
- var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect44.void : Effect44.die(new Error(`${object2(props)}: declared path ${props.path} but PBS holds ${live.path}. \`path\` ` + "is create-only, so this cannot be reconciled and will NOT be planned as a silent " + "noop: backups would keep landing in the old directory behind a green plan. Correct " + "the declaration, or remove the datastore and declare a new one -- the contents live, " + "because `destroy-data` is never sent."));
3777
- var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect44.void : Effect44.die(new Error(`${object2(props)}: declared backend ${props.backend} but PBS holds ` + `${live.backend === "" ? "(none — a local datastore)" : live.backend}. \`backend\` ` + "is create-only: PBS has no update for it, so this cannot be reconciled and will NOT " + "be planned as a silent noop. Correct the declaration to match what is really there, " + "or remove the datastore and declare a new one -- the contents live, because " + "`destroy-data` is never sent."));
3778
- var settle3 = (read3, done) => Effect44.gen(function* () {
3902
+ var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect47.void : Effect47.die(new Error(`${object2(props)}: declared path ${props.path} but PBS holds ${live.path}. \`path\` ` + "is create-only, so this cannot be reconciled and will NOT be planned as a silent " + "noop: backups would keep landing in the old directory behind a green plan. Correct " + "the declaration, or remove the datastore and declare a new one -- the contents live, " + "because `destroy-data` is never sent."));
3903
+ var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect47.void : Effect47.die(new Error(`${object2(props)}: declared backend ${props.backend} but PBS holds ` + `${live.backend === "" ? "(none — a local datastore)" : live.backend}. \`backend\` ` + "is create-only: PBS has no update for it, so this cannot be reconciled and will NOT " + "be planned as a silent noop. Correct the declaration to match what is really there, " + "or remove the datastore and declare a new one -- the contents live, because " + "`destroy-data` is never sent."));
3904
+ var settle3 = (read3, done) => Effect47.gen(function* () {
3779
3905
  for (let attempt = 0;attempt < 60; attempt += 1) {
3780
3906
  const live = yield* read3;
3781
3907
  if (done(live))
3782
3908
  return live;
3783
- yield* Effect44.sleep("2 seconds");
3909
+ yield* Effect47.sleep("2 seconds");
3784
3910
  }
3785
3911
  return yield* read3;
3786
3912
  });
3787
3913
 
3788
3914
  // src/proxmox/pbs-datastore-endpoint.ts
3789
- import * as Effect45 from "effect/Effect";
3915
+ import * as Effect48 from "effect/Effect";
3790
3916
  var DATASTORE_ENDPOINT = {
3791
3917
  create: "pbs:POST /config/datastore",
3792
3918
  update: "pbs:PUT /config/datastore/{name}"
3793
3919
  };
3794
- var guardDatastoreForms = (props) => Effect45.gen(function* () {
3920
+ var guardDatastoreForms = (props) => Effect48.gen(function* () {
3795
3921
  yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
3796
3922
  yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
3797
3923
  });
@@ -3800,11 +3926,11 @@ var guardDatastoreForms = (props) => Effect45.gen(function* () {
3800
3926
  var PbsDatastore = Resource19("Pbs.Datastore", {
3801
3927
  defaultRemovalPolicy: "retain"
3802
3928
  });
3803
- var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succeed(PbsDatastore.Provider.of({
3804
- list: () => Effect46.succeed([]),
3929
+ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succeed(PbsDatastore.Provider.of({
3930
+ list: () => Effect49.succeed([]),
3805
3931
  read: ({ olds }) => readOne(olds),
3806
- diff: ({ news, output }) => Effect46.gen(function* () {
3807
- if (!isResolved11(news))
3932
+ diff: ({ news, output }) => Effect49.gen(function* () {
3933
+ if (!isResolved13(news))
3808
3934
  return;
3809
3935
  yield* guardDatastoreForms(news);
3810
3936
  if (output === undefined)
@@ -3814,9 +3940,9 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
3814
3940
  return { action: "update" };
3815
3941
  yield* guardPath(live, news);
3816
3942
  yield* guardBackend(live, news);
3817
- return matches5(live, news) ? { action: "noop" } : { action: "update" };
3943
+ return matches6(live, news) ? { action: "noop" } : { action: "update" };
3818
3944
  }),
3819
- reconcile: Effect46.fn(function* ({ news }) {
3945
+ reconcile: Effect49.fn(function* ({ news }) {
3820
3946
  yield* guardDatastoreForms(news);
3821
3947
  const live = yield* readOne(news);
3822
3948
  let upid;
@@ -3825,31 +3951,31 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect46.succee
3825
3951
  } else {
3826
3952
  yield* guardPath(live, news);
3827
3953
  yield* guardBackend(live, news);
3828
- const form = formToSend(matches5, live, news, updateForm3(news));
3954
+ const form = formToSend(matches6, live, news, updateForm3(news));
3829
3955
  if (form !== undefined) {
3830
3956
  yield* pve(news.target, "provision", "PUT", object2(news), form);
3831
3957
  }
3832
3958
  }
3833
- const after = yield* settle3(readOne(news), (row) => row !== undefined && matches5(row, news));
3834
- if (after === undefined || !matches5(after, news)) {
3835
- return yield* Effect46.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + `match the declaration after 120s. PBS answered with task ${upid ?? "(none)"} ` + "-- read it with `proxmox-backup-manager task log <upid>`. A slow disk is a " + "plausible cause (see `settle`); a create over an existing chunk store is not, " + "because PBS refuses that outright rather than timing out."));
3959
+ const after = yield* settle3(readOne(news), (row) => row !== undefined && matches6(row, news));
3960
+ if (after === undefined || !matches6(after, news)) {
3961
+ return yield* Effect49.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + `match the declaration after 120s. PBS answered with task ${upid ?? "(none)"} ` + "-- read it with `proxmox-backup-manager task log <upid>`. A slow disk is a " + "plausible cause (see `settle`); a create over an existing chunk store is not, " + "because PBS refuses that outright rather than timing out."));
3836
3962
  }
3837
3963
  return after;
3838
3964
  }),
3839
- delete: Effect46.fn(function* ({ olds }) {
3965
+ delete: Effect49.fn(function* ({ olds }) {
3840
3966
  yield* pve(olds.target, "provision", "DELETE", object2(olds));
3841
3967
  const left = yield* settle3(readOne(olds), (row) => row === undefined);
3842
3968
  if (left !== undefined) {
3843
- return yield* Effect46.die(new Error(`${object2(olds)}: the DELETE returned no error but the datastore is still in ` + "datastore.cfg after 120s. Either a backup, verify or GC task still holds it, " + "or the provision role lacks Datastore.Allocate on /datastore/" + `${olds.name}. Read the task log with \`proxmox-backup-manager task log <upid>\`.`));
3969
+ return yield* Effect49.die(new Error(`${object2(olds)}: the DELETE returned no error but the datastore is still in ` + "datastore.cfg after 120s. Either a backup, verify or GC task still holds it, " + "or the provision role lacks Datastore.Allocate on /datastore/" + `${olds.name}. Read the task log with \`proxmox-backup-manager task log <upid>\`.`));
3844
3970
  }
3845
3971
  })
3846
3972
  })));
3847
3973
  // src/proxmox/pbs-notification-matcher.ts
3848
3974
  import { Resource as Resource20 } from "alchemy";
3849
3975
  import * as Provider20 from "alchemy/Provider";
3850
- import * as Effect47 from "effect/Effect";
3976
+ import * as Effect50 from "effect/Effect";
3851
3977
  var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
3852
- var handlers13 = pveHandlers({
3978
+ var handlers12 = pveHandlers({
3853
3979
  attributes: (live, props) => matcherAttributes(live, props.name),
3854
3980
  collection: () => "config/notifications/matchers",
3855
3981
  createForm: matcherCreateForm,
@@ -3861,15 +3987,15 @@ var handlers13 = pveHandlers({
3861
3987
  path: (props) => `config/notifications/matchers/${props.name}`,
3862
3988
  updateForm: matcherUpdateForm
3863
3989
  });
3864
- var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect47.succeed(PbsNotificationMatcher.Provider.of(handlers13)));
3990
+ var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect50.succeed(PbsNotificationMatcher.Provider.of(handlers12)));
3865
3991
  // src/proxmox/pbs-notification-target.ts
3866
3992
  import { Resource as Resource21 } from "alchemy";
3867
3993
  import * as Provider21 from "alchemy/Provider";
3868
- import * as Effect49 from "effect/Effect";
3994
+ import * as Effect52 from "effect/Effect";
3869
3995
 
3870
3996
  // src/proxmox/pbs-notification-target-lifecycle.ts
3871
- import { isResolved as isResolved12 } from "alchemy/Diff";
3872
- import * as Effect48 from "effect/Effect";
3997
+ import { isResolved as isResolved14 } from "alchemy/Diff";
3998
+ import * as Effect51 from "effect/Effect";
3873
3999
 
3874
4000
  // src/proxmox/pbs-notification-target-endpoint.ts
3875
4001
  var ENDPOINTS2 = {
@@ -4146,12 +4272,12 @@ var targetForm = (props, groups, carry, mode) => {
4146
4272
 
4147
4273
  // src/proxmox/pbs-notification-target-lifecycle.ts
4148
4274
  var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
4149
- var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect48.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect48.orElseSucceed(() => {
4275
+ var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect51.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect51.orElseSucceed(() => {
4150
4276
  return;
4151
4277
  }));
4152
4278
  var refuse2 = (props) => {
4153
4279
  const reasons = refusals(props);
4154
- return reasons.length === 0 ? Effect48.void : Effect48.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4280
+ return reasons.length === 0 ? Effect51.void : Effect51.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4155
4281
  };
4156
4282
  var toCarry = (live, props, groups, sealed) => {
4157
4283
  const header = headerState(live, props, groups) === "stale";
@@ -4163,17 +4289,17 @@ var requireValues = (props, groups, carry) => {
4163
4289
  ...carry.header ? groups.header.missing : [],
4164
4290
  ...carry.sealed ? groups.sealed.missing : []
4165
4291
  ];
4166
- return missing.length === 0 ? Effect48.void : Effect48.die(new Error(`${path(props)}: this write must send write-only values, and ${missing.join(", ")} ` + "is unset or empty in the deploying environment. Export it and deploy again."));
4292
+ return missing.length === 0 ? Effect51.void : Effect51.die(new Error(`${path(props)}: this write must send write-only values, and ${missing.join(", ")} ` + "is unset or empty in the deploying environment. Export it and deploy again."));
4167
4293
  };
4168
- var handlers14 = {
4169
- list: () => Effect48.succeed([]),
4170
- read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect48.void).pipe(Effect48.andThen(readLive2(olds)), Effect48.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
4294
+ var handlers13 = {
4295
+ list: () => Effect51.succeed([]),
4296
+ read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect51.void).pipe(Effect51.andThen(readLive2(olds)), Effect51.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
4171
4297
  diff: ({
4172
4298
  news,
4173
4299
  olds,
4174
4300
  output
4175
- }) => Effect48.gen(function* () {
4176
- if (output === undefined || !isResolved12(news))
4301
+ }) => Effect51.gen(function* () {
4302
+ if (output === undefined || !isResolved14(news))
4177
4303
  return;
4178
4304
  if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
4179
4305
  return { action: "replace", deleteFirst: olds.name === news.name };
@@ -4184,7 +4310,7 @@ var handlers14 = {
4184
4310
  return { action: "update" };
4185
4311
  return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
4186
4312
  }),
4187
- reconcile: ({ news, output }) => Effect48.gen(function* () {
4313
+ reconcile: ({ news, output }) => Effect51.gen(function* () {
4188
4314
  yield* refuse2(news);
4189
4315
  const groups = resolveGroups(news);
4190
4316
  const live = yield* readLive2(news);
@@ -4192,10 +4318,10 @@ var handlers14 = {
4192
4318
  if (live === undefined) {
4193
4319
  const carry = { header: true, sealed: true };
4194
4320
  yield* requireValues(news, groups, carry);
4195
- const collection2 = `config/notifications/endpoints/${news.type}`;
4321
+ const collection = `config/notifications/endpoints/${news.type}`;
4196
4322
  const form = targetForm(news, groups, carry, "create");
4197
4323
  yield* guardForm(pbsTargetEndpoint(news).create, form, true);
4198
- yield* pve(news.target, "provision", "POST", collection2, form);
4324
+ yield* pve(news.target, "provision", "POST", collection, form);
4199
4325
  sealed = seal(groups.sealed.values);
4200
4326
  } else {
4201
4327
  const carry = toCarry(live, news, groups, sealed);
@@ -4210,7 +4336,7 @@ var handlers14 = {
4210
4336
  }
4211
4337
  const after = yield* readLive2(news);
4212
4338
  if (after === undefined) {
4213
- return yield* Effect48.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
4339
+ return yield* Effect51.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
4214
4340
  }
4215
4341
  return { ...after, sealed };
4216
4342
  }),
@@ -4219,11 +4345,11 @@ var handlers14 = {
4219
4345
 
4220
4346
  // src/proxmox/pbs-notification-target.ts
4221
4347
  var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
4222
- var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect49.succeed(PbsNotificationTarget.Provider.of(handlers14)));
4348
+ var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect52.succeed(PbsNotificationTarget.Provider.of(handlers13)));
4223
4349
  // src/proxmox/pbs-prune-job.ts
4224
4350
  import { Resource as Resource22 } from "alchemy";
4225
4351
  import * as Provider22 from "alchemy/Provider";
4226
- import * as Effect50 from "effect/Effect";
4352
+ import * as Effect53 from "effect/Effect";
4227
4353
 
4228
4354
  // src/proxmox/pbs-prune-job-form.ts
4229
4355
  var KEEP_KEYS = [
@@ -4281,7 +4407,7 @@ var updateBody2 = (props) => {
4281
4407
  // src/proxmox/pbs-prune-job.ts
4282
4408
  var PbsPruneJob = Resource22("Pbs.PruneJob");
4283
4409
  var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
4284
- var handlers15 = pveHandlers({
4410
+ var handlers14 = pveHandlers({
4285
4411
  attributes: (live, props) => ({
4286
4412
  comment: text(live["comment"], ""),
4287
4413
  disable: bool(live["disable"], false),
@@ -4299,11 +4425,11 @@ var handlers15 = pveHandlers({
4299
4425
  path: (props) => `config/prune/${props.id}`,
4300
4426
  updateForm: updateBody2
4301
4427
  });
4302
- var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect50.succeed(PbsPruneJob.Provider.of(handlers15)));
4428
+ var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect53.succeed(PbsPruneJob.Provider.of(handlers14)));
4303
4429
  // src/proxmox/pbs-sync-job.ts
4304
4430
  import { Resource as Resource23 } from "alchemy";
4305
4431
  import * as Provider23 from "alchemy/Provider";
4306
- import * as Effect51 from "effect/Effect";
4432
+ import * as Effect54 from "effect/Effect";
4307
4433
 
4308
4434
  // src/proxmox/pbs-sync-job-form.ts
4309
4435
  var UNSET4 = -1;
@@ -4351,7 +4477,7 @@ var createBody4 = (props) => ({
4351
4477
  ...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
4352
4478
  });
4353
4479
  var updateBody3 = (props) => shape6(props);
4354
- var handlers16 = pveHandlers({
4480
+ var handlers15 = pveHandlers({
4355
4481
  attributes: (live, props) => ({
4356
4482
  comment: text(live["comment"], ""),
4357
4483
  "group-filter": filters(live["group-filter"]),
@@ -4380,11 +4506,11 @@ var handlers16 = pveHandlers({
4380
4506
 
4381
4507
  // src/proxmox/pbs-sync-job.ts
4382
4508
  var PbsSyncJob = Resource23("Pbs.SyncJob");
4383
- var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect51.succeed(PbsSyncJob.Provider.of(handlers16)));
4509
+ var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect54.succeed(PbsSyncJob.Provider.of(handlers15)));
4384
4510
  // src/proxmox/pbs-verify-job.ts
4385
4511
  import { Resource as Resource24 } from "alchemy";
4386
4512
  import * as Provider24 from "alchemy/Provider";
4387
- import * as Effect52 from "effect/Effect";
4513
+ import * as Effect55 from "effect/Effect";
4388
4514
 
4389
4515
  // src/proxmox/pbs-verify-job-form.ts
4390
4516
  var rechecks = (ignoreVerified, outdatedAfter) => {
@@ -4406,7 +4532,7 @@ var shape7 = (props) => ({
4406
4532
 
4407
4533
  // src/proxmox/pbs-verify-job.ts
4408
4534
  var PbsVerifyJob = Resource24("Pbs.VerifyJob");
4409
- var handlers17 = pveHandlers({
4535
+ var handlers16 = pveHandlers({
4410
4536
  attributes: (live, props) => {
4411
4537
  const ignoreVerified = bool(live["ignore-verified"], true);
4412
4538
  const outdatedAfter = int(live["outdated-after"], -1);
@@ -4429,11 +4555,11 @@ var handlers17 = pveHandlers({
4429
4555
  path: (props) => `config/verify/${props.id}`,
4430
4556
  updateForm: shape7
4431
4557
  });
4432
- var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect52.succeed(PbsVerifyJob.Provider.of(handlers17)));
4558
+ var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect55.succeed(PbsVerifyJob.Provider.of(handlers16)));
4433
4559
  // src/proxmox/pool.ts
4434
4560
  import { Resource as Resource25 } from "alchemy";
4435
4561
  import * as Provider25 from "alchemy/Provider";
4436
- import * as Effect53 from "effect/Effect";
4562
+ import * as Effect56 from "effect/Effect";
4437
4563
  var ProxmoxPool = Resource25("Proxmox.Pool");
4438
4564
  var poolCreateForm = (props) => ({
4439
4565
  comment: props.comment ?? "",
@@ -4442,7 +4568,7 @@ var poolCreateForm = (props) => ({
4442
4568
  var poolUpdateForm = (props) => ({
4443
4569
  comment: props.comment ?? ""
4444
4570
  });
4445
- var handlers18 = pveHandlers({
4571
+ var handlers17 = pveHandlers({
4446
4572
  attributes: (live, props) => {
4447
4573
  const row = live;
4448
4574
  return {
@@ -4458,7 +4584,7 @@ var handlers18 = pveHandlers({
4458
4584
  path: (props) => `pools/${props.poolid}`,
4459
4585
  updateForm: poolUpdateForm
4460
4586
  });
4461
- var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect53.succeed(ProxmoxPool.Provider.of(handlers18)));
4587
+ var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect56.succeed(ProxmoxPool.Provider.of(handlers17)));
4462
4588
  // src/proxmox/provision-baseline.ts
4463
4589
  var PROVISION_PRIVILEGES = Object.freeze([
4464
4590
  "Datastore.Allocate",
@@ -4651,14 +4777,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
4651
4777
  // src/proxmox/provision-declare.ts
4652
4778
  import { adopt } from "alchemy/AdoptPolicy";
4653
4779
  import * as RemovalPolicy from "alchemy/RemovalPolicy";
4654
- import * as Effect57 from "effect/Effect";
4780
+ import * as Effect60 from "effect/Effect";
4655
4781
 
4656
4782
  // src/proxmox/role.ts
4657
4783
  import { Resource as Resource26 } from "alchemy";
4658
- import { isResolved as isResolved13 } from "alchemy/Diff";
4784
+ import { isResolved as isResolved15 } from "alchemy/Diff";
4659
4785
  import * as Provider26 from "alchemy/Provider";
4660
4786
  import * as access5 from "@distilled.cloud/proxmox/access";
4661
- import * as Effect54 from "effect/Effect";
4787
+ import * as Effect57 from "effect/Effect";
4662
4788
 
4663
4789
  // src/proxmox/role-wire.ts
4664
4790
  var ROLE_CREATE = "pve:POST /access/roles";
@@ -4673,28 +4799,28 @@ var updateForm4 = (props) => ({
4673
4799
  roleid: props.roleid
4674
4800
  });
4675
4801
  var find2 = (rows, roleid) => rows.find((row) => row.roleid === roleid);
4676
- var attributesOf6 = (row, props) => ({
4802
+ var attributesOf7 = (row, props) => ({
4677
4803
  privs: canonical2(row.privs === undefined ? [] : row.privs.split(",")),
4678
4804
  roleid: props.roleid
4679
4805
  });
4680
- var matches6 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
4806
+ var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
4681
4807
 
4682
4808
  // src/proxmox/role.ts
4683
4809
  var ProxmoxRole = Resource26("Proxmox.Role");
4684
- var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect54.map((rows) => {
4810
+ var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect57.map((rows) => {
4685
4811
  if (rows === UNREADABLE)
4686
4812
  return UNREADABLE;
4687
4813
  const row = find2(rows, props.roleid);
4688
- return row === undefined ? undefined : attributesOf6(row, props);
4814
+ return row === undefined ? undefined : attributesOf7(row, props);
4689
4815
  }));
4690
4816
  var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
4691
- var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect54.succeed(ProxmoxRole.Provider.of({
4692
- list: () => Effect54.succeed([]),
4693
- read: Effect54.fn(function* ({ olds }) {
4817
+ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(ProxmoxRole.Provider.of({
4818
+ list: () => Effect57.succeed([]),
4819
+ read: Effect57.fn(function* ({ olds }) {
4694
4820
  return dropUnreadable5(yield* readRole(olds));
4695
4821
  }),
4696
- diff: Effect54.fn(function* ({ news, output }) {
4697
- if (!isResolved13(news))
4822
+ diff: Effect57.fn(function* ({ news, output }) {
4823
+ if (!isResolved15(news))
4698
4824
  return;
4699
4825
  yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
4700
4826
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
@@ -4709,38 +4835,38 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect54.succeed(
4709
4835
  yield* guardWrite2(ROLE_CREATE, createForm7(news), true);
4710
4836
  return { action: "update" };
4711
4837
  }
4712
- return matches6(live, news) ? { action: "noop" } : { action: "update" };
4838
+ return matches7(live, news) ? { action: "noop" } : { action: "update" };
4713
4839
  }),
4714
- reconcile: Effect54.fn(function* ({ news }) {
4840
+ reconcile: Effect57.fn(function* ({ news }) {
4715
4841
  const before = dropUnreadable5(yield* readRole(news));
4716
4842
  yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
4717
4843
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
4718
4844
  if (before === undefined) {
4719
4845
  yield* runPve(news.target, "provision", true, access5.createAccessRole(createForm7(news)));
4720
- } else if (!matches6(before, news)) {
4846
+ } else if (!matches7(before, news)) {
4721
4847
  yield* runPve(news.target, "provision", true, access5.putAccessRole(updateForm4(news)));
4722
4848
  }
4723
4849
  const after = dropUnreadable5(yield* readRole(news));
4724
4850
  if (after === undefined) {
4725
- return yield* Effect54.die(new Error(`access/roles/${news.roleid}: the write returned no error but the role is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
4851
+ return yield* Effect57.die(new Error(`access/roles/${news.roleid}: the write returned no error but the role is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
4726
4852
  }
4727
4853
  return after;
4728
4854
  }),
4729
- delete: Effect54.fn(function* ({ olds }) {
4855
+ delete: Effect57.fn(function* ({ olds }) {
4730
4856
  yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
4731
4857
  })
4732
4858
  })));
4733
4859
 
4734
4860
  // src/proxmox/user.ts
4735
4861
  import { Resource as Resource27 } from "alchemy";
4736
- import { isResolved as isResolved14 } from "alchemy/Diff";
4862
+ import { isResolved as isResolved16 } from "alchemy/Diff";
4737
4863
  import * as Provider27 from "alchemy/Provider";
4738
4864
  import * as access7 from "@distilled.cloud/proxmox/access";
4739
- import * as Effect56 from "effect/Effect";
4865
+ import * as Effect59 from "effect/Effect";
4740
4866
 
4741
4867
  // src/proxmox/user-wire.ts
4742
4868
  import * as access6 from "@distilled.cloud/proxmox/access";
4743
- import * as Effect55 from "effect/Effect";
4869
+ import * as Effect58 from "effect/Effect";
4744
4870
  var USER_CREATE = "pve:POST /access/users";
4745
4871
  var USER_UPDATE = "pve:PUT /access/users/{userid}";
4746
4872
  var int2 = (value) => {
@@ -4770,7 +4896,7 @@ var updateForm5 = (props) => ({
4770
4896
  ...shape8(props),
4771
4897
  userid: props.userid
4772
4898
  });
4773
- var attributesOf7 = (live, props) => ({
4899
+ var attributesOf8 = (live, props) => ({
4774
4900
  comment: text(live.comment),
4775
4901
  email: text(live.email),
4776
4902
  enable: bool(live.enable, true),
@@ -4781,22 +4907,20 @@ var attributesOf7 = (live, props) => ({
4781
4907
  tokens: tokenNames(live.tokens),
4782
4908
  userid: props.userid
4783
4909
  });
4784
- var matches7 = (attributes2, props) => attributes2.comment === (props.comment ?? "") && attributes2.email === (props.email ?? "") && attributes2.enable === (props.enable !== false) && attributes2.expire === (props.expire ?? 0) && attributes2.firstname === (props.firstname ?? "") && attributes2.lastname === (props.lastname ?? "") && attributes2.groups.join(",") === groupSet(props.groups).join(",");
4785
- var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect55.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf7(live, props)));
4786
- var readUser = (props) => readUserOrFail(props).pipe(Effect55.orElseSucceed(() => {
4787
- return;
4788
- }));
4910
+ var matches8 = (attributes2, props) => attributes2.comment === (props.comment ?? "") && attributes2.email === (props.email ?? "") && attributes2.enable === (props.enable !== false) && attributes2.expire === (props.expire ?? 0) && attributes2.firstname === (props.firstname ?? "") && attributes2.lastname === (props.lastname ?? "") && attributes2.groups.join(",") === groupSet(props.groups).join(",");
4911
+ var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect58.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
4912
+ var readUser = (props) => readUserOrFail(props).pipe(Effect58.catchTag("UserNotFound", () => Effect58.succeed(undefined)));
4789
4913
  var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
4790
4914
 
4791
4915
  // src/proxmox/user.ts
4792
4916
  var ProxmoxUser = Resource27("Proxmox.User");
4793
- var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect56.succeed(ProxmoxUser.Provider.of({
4794
- list: () => Effect56.succeed([]),
4795
- read: Effect56.fn(function* ({ olds, output }) {
4917
+ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(ProxmoxUser.Provider.of({
4918
+ list: () => Effect59.succeed([]),
4919
+ read: Effect59.fn(function* ({ olds, output }) {
4796
4920
  return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
4797
4921
  }),
4798
- diff: Effect56.fn(function* ({ news, output }) {
4799
- if (!isResolved14(news))
4922
+ diff: Effect59.fn(function* ({ news, output }) {
4923
+ if (!isResolved16(news))
4800
4924
  return;
4801
4925
  yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
4802
4926
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
@@ -4811,31 +4935,31 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect56.succeed(
4811
4935
  yield* guardWrite2(USER_CREATE, createForm8(news), true);
4812
4936
  return { action: "update" };
4813
4937
  }
4814
- return matches7(live, news) ? { action: "noop" } : { action: "update" };
4938
+ return matches8(live, news) ? { action: "noop" } : { action: "update" };
4815
4939
  }),
4816
- reconcile: Effect56.fn(function* ({ news }) {
4940
+ reconcile: Effect59.fn(function* ({ news }) {
4817
4941
  const before = dropUnreadable6(yield* readUser(news));
4818
4942
  yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
4819
4943
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
4820
4944
  if (before === undefined) {
4821
4945
  yield* runPve(news.target, "provision", true, access7.createAccessUser(createForm8(news)));
4822
- } else if (!matches7(before, news)) {
4946
+ } else if (!matches8(before, news)) {
4823
4947
  yield* runPve(news.target, "provision", true, access7.putAccessUser(updateForm5(news)));
4824
4948
  }
4825
4949
  const after = dropUnreadable6(yield* readUser(news));
4826
4950
  if (after === undefined) {
4827
- return yield* Effect56.die(new Error(`access/users/${news.userid}: the write returned no error but the account is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
4951
+ return yield* Effect59.die(new Error(`access/users/${news.userid}: the write returned no error but the account is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
4828
4952
  }
4829
4953
  return after;
4830
4954
  }),
4831
- delete: Effect56.fn(function* ({ olds }) {
4955
+ delete: Effect59.fn(function* ({ olds }) {
4832
4956
  yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
4833
4957
  })
4834
4958
  })));
4835
4959
 
4836
4960
  // src/proxmox/provision-declare.ts
4837
- var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect57.gen(function* () {
4838
- const baseline = yield* Effect57.try({
4961
+ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect60.gen(function* () {
4962
+ const baseline = yield* Effect60.try({
4839
4963
  catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
4840
4964
  try: () => provisionBaseline(names2)
4841
4965
  });
@@ -4849,13 +4973,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4849
4973
  target
4850
4974
  }));
4851
4975
  const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
4852
- const users = yield* Effect57.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
4976
+ const users = yield* Effect60.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
4853
4977
  comment: user.comment,
4854
4978
  groups: [...user.groups],
4855
4979
  target,
4856
4980
  userid: user.userid
4857
4981
  }))));
4858
- const grants = yield* Effect57.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
4982
+ const grants = yield* Effect60.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
4859
4983
  path: "/",
4860
4984
  propagate: true,
4861
4985
  roleid: grant.roleid,
@@ -4868,7 +4992,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4868
4992
  // src/proxmox/qemu.ts
4869
4993
  import { Resource as Resource28 } from "alchemy";
4870
4994
  import * as Provider28 from "alchemy/Provider";
4871
- import * as Effect58 from "effect/Effect";
4995
+ import * as Effect61 from "effect/Effect";
4872
4996
  var ProxmoxVm = Resource28("Proxmox.Vm");
4873
4997
  var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
4874
4998
  var shape9 = (props) => ({
@@ -4879,7 +5003,7 @@ var shape9 = (props) => ({
4879
5003
  sockets: String(props.sockets ?? 1),
4880
5004
  ...props.net0 === undefined ? {} : { net0: props.net0 }
4881
5005
  });
4882
- var handlers19 = pveHandlers({
5006
+ var handlers18 = pveHandlers({
4883
5007
  attributes: (live, props) => ({
4884
5008
  cores: num(live["cores"], 1),
4885
5009
  memory: num(live["memory"], 512),
@@ -4899,11 +5023,11 @@ var handlers19 = pveHandlers({
4899
5023
  path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
4900
5024
  updateForm: shape9
4901
5025
  });
4902
- var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect58.succeed(ProxmoxVm.Provider.of(handlers19)));
5026
+ var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect61.succeed(ProxmoxVm.Provider.of(handlers18)));
4903
5027
  // src/proxmox/replication-job.ts
4904
5028
  import { Resource as Resource29 } from "alchemy";
4905
5029
  import * as Provider29 from "alchemy/Provider";
4906
- import * as Effect59 from "effect/Effect";
5030
+ import * as Effect62 from "effect/Effect";
4907
5031
 
4908
5032
  // src/proxmox/replication-job-form.ts
4909
5033
  var DEFAULT_SCHEDULE = "*/15";
@@ -4933,7 +5057,7 @@ var rateOf = (value) => {
4933
5057
  const parsed = Number.parseFloat(text(value));
4934
5058
  return Number.isFinite(parsed) ? parsed : 0;
4935
5059
  };
4936
- var handlers20 = pveHandlers({
5060
+ var handlers19 = pveHandlers({
4937
5061
  attributes: (live, props) => ({
4938
5062
  comment: text(live["comment"], ""),
4939
5063
  disable: bool(live["disable"], false),
@@ -4957,15 +5081,15 @@ var handlers20 = pveHandlers({
4957
5081
  path: (props) => `cluster/replication/${jobId(props)}`,
4958
5082
  updateForm: updateBody4
4959
5083
  });
4960
- var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect59.succeed(ProxmoxReplicationJob.Provider.of(handlers20)));
5084
+ var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect62.succeed(ProxmoxReplicationJob.Provider.of(handlers19)));
4961
5085
  // src/proxmox/sdn-apply.ts
4962
5086
  import { Resource as Resource30 } from "alchemy";
4963
- import { isResolved as isResolved15 } from "alchemy/Diff";
5087
+ import { isResolved as isResolved17 } from "alchemy/Diff";
4964
5088
  import * as Provider30 from "alchemy/Provider";
4965
- import * as Effect61 from "effect/Effect";
5089
+ import * as Effect64 from "effect/Effect";
4966
5090
 
4967
5091
  // src/proxmox/sdn-apply-read.ts
4968
- import * as Effect60 from "effect/Effect";
5092
+ import * as Effect63 from "effect/Effect";
4969
5093
  var MARKED = [
4970
5094
  "cluster/sdn/zones",
4971
5095
  "cluster/sdn/vnets",
@@ -4974,7 +5098,7 @@ var MARKED = [
4974
5098
  "cluster/sdn/route-maps"
4975
5099
  ];
4976
5100
  var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
4977
- var markedPending = (target) => Effect60.all(MARKED.map((collection2) => pve(target, "read", "GET", `${collection2}?pending=1`).pipe(Effect60.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect60.catchIf(subsystemAbsent, () => Effect60.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect60.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
5101
+ var markedPending = (target) => Effect63.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect63.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect63.catchIf(subsystemAbsent, () => Effect63.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect63.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
4978
5102
  var fabricStaged = (staged, running) => {
4979
5103
  if (staged === undefined && running === undefined)
4980
5104
  return 0;
@@ -4982,9 +5106,9 @@ var fabricStaged = (staged, running) => {
4982
5106
  return "inconsistent";
4983
5107
  return staged === running ? 0 : 1;
4984
5108
  };
4985
- var fabricPending = (target) => Effect60.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect60.map(canonical3), Effect60.catchIf(subsystemAbsent, () => Effect60.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect60.flatMap(([staged, running]) => {
5109
+ var fabricPending = (target) => Effect63.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect63.map(canonical3), Effect63.catchIf(subsystemAbsent, () => Effect63.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect63.flatMap(([staged, running]) => {
4986
5110
  const verdict = fabricStaged(staged, running);
4987
- return verdict === "inconsistent" ? Effect60.die(new Error("cluster/sdn/fabrics/all answered 501 for one of ?pending=1 and ?running=1 and not " + "the other. Refusing to read that as a staged fabric change.")) : Effect60.succeed(verdict);
5111
+ return verdict === "inconsistent" ? Effect63.die(new Error("cluster/sdn/fabrics/all answered 501 for one of ?pending=1 and ?running=1 and not " + "the other. Refusing to read that as a staged fabric change.")) : Effect63.succeed(verdict);
4988
5112
  }));
4989
5113
  var canonical3 = (value) => JSON.stringify(sortDeep(value));
4990
5114
  var sortDeep = (value) => {
@@ -4997,23 +5121,23 @@ var sortDeep = (value) => {
4997
5121
  }
4998
5122
  return value;
4999
5123
  };
5000
- var sdnPendingCount = (target) => Effect60.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect60.map(([marked, fabric]) => marked + fabric));
5124
+ var sdnPendingCount = (target) => Effect63.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect63.map(([marked, fabric]) => marked + fabric));
5001
5125
 
5002
5126
  // src/proxmox/sdn-apply.ts
5003
5127
  var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
5004
- var read3 = (props) => sdnPendingCount(props.target).pipe(Effect61.map((pending) => ({ pending })));
5005
- var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect61.succeed(ProxmoxSdnApply.Provider.of({
5006
- list: () => Effect61.succeed([]),
5007
- read: Effect61.fn(function* ({ olds }) {
5128
+ var read3 = (props) => sdnPendingCount(props.target).pipe(Effect64.map((pending) => ({ pending })));
5129
+ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect64.succeed(ProxmoxSdnApply.Provider.of({
5130
+ list: () => Effect64.succeed([]),
5131
+ read: Effect64.fn(function* ({ olds }) {
5008
5132
  return yield* read3(olds);
5009
5133
  }),
5010
- diff: Effect61.fn(function* ({ news }) {
5011
- if (!isResolved15(news))
5134
+ diff: Effect64.fn(function* ({ news }) {
5135
+ if (!isResolved17(news))
5012
5136
  return;
5013
5137
  const { pending } = yield* read3(news);
5014
5138
  return pending === 0 ? { action: "noop" } : { action: "update" };
5015
5139
  }),
5016
- reconcile: Effect61.fn(function* ({ news }) {
5140
+ reconcile: Effect64.fn(function* ({ news }) {
5017
5141
  const staged = yield* read3(news);
5018
5142
  if (staged.pending === 0)
5019
5143
  return staged;
@@ -5021,16 +5145,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect61.
5021
5145
  yield* pve(news.target, "provision", "PUT", "cluster/sdn");
5022
5146
  const after = yield* read3(news);
5023
5147
  if (after.pending !== 0) {
5024
- return yield* Effect61.die(new Error(`PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` + "are still staged. The running config does not match the declared one -- check " + "`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused."));
5148
+ return yield* Effect64.die(new Error(`PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` + "are still staged. The running config does not match the declared one -- check " + "`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused."));
5025
5149
  }
5026
5150
  return after;
5027
5151
  }),
5028
- delete: () => Effect61.void
5152
+ delete: () => Effect64.void
5029
5153
  })));
5030
5154
  // src/proxmox/sdn-subnet.ts
5031
5155
  import { Resource as Resource31 } from "alchemy";
5032
5156
  import * as Provider31 from "alchemy/Provider";
5033
- import * as Effect62 from "effect/Effect";
5157
+ import * as Effect65 from "effect/Effect";
5034
5158
 
5035
5159
  // src/proxmox/sdn-subnet-form.ts
5036
5160
  var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
@@ -5061,7 +5185,7 @@ var updateForm6 = (props) => {
5061
5185
 
5062
5186
  // src/proxmox/sdn-subnet.ts
5063
5187
  var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
5064
- var handlers21 = pveHandlers({
5188
+ var handlers20 = pveHandlers({
5065
5189
  attributes: (live, props) => {
5066
5190
  const liveVnet = text(live["vnet"]);
5067
5191
  if (liveVnet !== "" && liveVnet !== props.vnet)
@@ -5090,11 +5214,11 @@ var handlers21 = pveHandlers({
5090
5214
  path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
5091
5215
  updateForm: updateForm6
5092
5216
  });
5093
- var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect62.succeed(ProxmoxSdnSubnet.Provider.of(handlers21)));
5217
+ var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect65.succeed(ProxmoxSdnSubnet.Provider.of(handlers20)));
5094
5218
  // src/proxmox/sdn-vnet.ts
5095
5219
  import { Resource as Resource32 } from "alchemy";
5096
5220
  import * as Provider32 from "alchemy/Provider";
5097
- import * as Effect63 from "effect/Effect";
5221
+ import * as Effect66 from "effect/Effect";
5098
5222
  var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
5099
5223
  var tagOf = (value) => typeof value === "number" ? value : undefined;
5100
5224
  var shape11 = (props) => {
@@ -5114,7 +5238,7 @@ var updateForm7 = (props) => {
5114
5238
  ];
5115
5239
  return withClears(shape11(props), unset2);
5116
5240
  };
5117
- var handlers22 = pveHandlers({
5241
+ var handlers21 = pveHandlers({
5118
5242
  readRole: "provision",
5119
5243
  attributes: (live, props) => ({
5120
5244
  alias: typeof live["alias"] === "string" ? live["alias"] : "",
@@ -5132,11 +5256,11 @@ var handlers22 = pveHandlers({
5132
5256
  path: (props) => `cluster/sdn/vnets/${props.vnet}`,
5133
5257
  updateForm: updateForm7
5134
5258
  });
5135
- var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect63.succeed(ProxmoxSdnVnet.Provider.of(handlers22)));
5259
+ var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect66.succeed(ProxmoxSdnVnet.Provider.of(handlers21)));
5136
5260
  // src/proxmox/sdn-zone.ts
5137
5261
  import { Resource as Resource33 } from "alchemy";
5138
5262
  import * as Provider33 from "alchemy/Provider";
5139
- import * as Effect64 from "effect/Effect";
5263
+ import * as Effect67 from "effect/Effect";
5140
5264
  var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
5141
5265
  var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
5142
5266
  var body6 = (props) => ({
@@ -5157,7 +5281,7 @@ var clearable = {
5157
5281
  vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
5158
5282
  vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
5159
5283
  };
5160
- var handlers23 = pveHandlers({
5284
+ var handlers22 = pveHandlers({
5161
5285
  readRole: "provision",
5162
5286
  attributes: (live, props) => {
5163
5287
  const liveType = text(live["type"]);
@@ -5189,13 +5313,13 @@ var handlers23 = pveHandlers({
5189
5313
  return withClears(fields, clear);
5190
5314
  }
5191
5315
  });
5192
- var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect64.succeed(ProxmoxSdnZone.Provider.of(handlers23)));
5316
+ var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect67.succeed(ProxmoxSdnZone.Provider.of(handlers22)));
5193
5317
  // src/proxmox/storage.ts
5194
5318
  import { Resource as Resource34 } from "alchemy";
5195
- import { isResolved as isResolved16 } from "alchemy/Diff";
5319
+ import { isResolved as isResolved18 } from "alchemy/Diff";
5196
5320
  import * as Provider34 from "alchemy/Provider";
5197
5321
  import * as storage2 from "@distilled.cloud/proxmox/storage";
5198
- import * as Effect66 from "effect/Effect";
5322
+ import * as Effect69 from "effect/Effect";
5199
5323
 
5200
5324
  // src/proxmox/storage-form.ts
5201
5325
  var STORAGE_CREATE = "pve:POST /storage";
@@ -5222,17 +5346,17 @@ var updateForm8 = (props) => ({
5222
5346
  ...mutable2(props),
5223
5347
  storage: props.storage
5224
5348
  });
5225
- var toDistilledCreate2 = (props) => underscored(createForm11(props));
5226
- var toDistilledUpdate2 = (props) => underscored(updateForm8(props));
5349
+ var toDistilledCreate4 = (props) => underscored(createForm11(props));
5350
+ var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
5227
5351
 
5228
5352
  // src/proxmox/storage-wire.ts
5229
5353
  import * as storage from "@distilled.cloud/proxmox/storage";
5230
- import * as Effect65 from "effect/Effect";
5354
+ import * as Effect68 from "effect/Effect";
5231
5355
  var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
5232
5356
  var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
5233
5357
  var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
5234
5358
  var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
5235
- var attributesOf8 = (live, props) => {
5359
+ var attributesOf9 = (live, props) => {
5236
5360
  const data = live;
5237
5361
  return {
5238
5362
  comment: str2(data["comment"]),
@@ -5246,24 +5370,22 @@ var attributesOf8 = (live, props) => {
5246
5370
  type: str2(data["type"], props.type)
5247
5371
  };
5248
5372
  };
5249
- var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect65.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
5250
- var readStorage = (props) => readStorageOrFail(props).pipe(Effect65.orElseSucceed(() => {
5251
- return;
5252
- }));
5373
+ var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect68.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
5374
+ var readStorage = (props) => readStorageOrFail(props).pipe(Effect68.catchTag("StorageNotFound", () => Effect68.succeed(undefined)));
5253
5375
  var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
5254
- var matches8 = (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) && same7(props.disable, attributes2.disable) && same7(props.shared, attributes2.shared) && same7(props.preallocation, attributes2.preallocation) && same7(props.comment, attributes2.comment);
5376
+ var matches9 = (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) && same7(props.disable, attributes2.disable) && same7(props.shared, attributes2.shared) && same7(props.preallocation, attributes2.preallocation) && same7(props.comment, attributes2.comment);
5255
5377
 
5256
5378
  // src/proxmox/storage.ts
5257
5379
  var ProxmoxStorage = Resource34("Proxmox.Storage", {
5258
5380
  defaultRemovalPolicy: "retain"
5259
5381
  });
5260
- var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect66.succeed(ProxmoxStorage.Provider.of({
5261
- list: () => Effect66.succeed([]),
5262
- read: Effect66.fn(function* ({ olds, output }) {
5382
+ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.succeed(ProxmoxStorage.Provider.of({
5383
+ list: () => Effect69.succeed([]),
5384
+ read: Effect69.fn(function* ({ olds, output }) {
5263
5385
  return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
5264
5386
  }),
5265
- diff: Effect66.fn(function* ({ news, output }) {
5266
- if (!isResolved16(news))
5387
+ diff: Effect69.fn(function* ({ news, output }) {
5388
+ if (!isResolved18(news))
5267
5389
  return;
5268
5390
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
5269
5391
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
@@ -5278,61 +5400,61 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect66.su
5278
5400
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), true);
5279
5401
  return { action: "update" };
5280
5402
  }
5281
- return matches8(live, news) ? { action: "noop" } : { action: "update" };
5403
+ return matches9(live, news) ? { action: "noop" } : { action: "update" };
5282
5404
  }),
5283
- reconcile: Effect66.fn(function* ({ news }) {
5405
+ reconcile: Effect69.fn(function* ({ news }) {
5284
5406
  const before = dropUnreadable7(yield* readStorage(news));
5285
5407
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
5286
5408
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
5287
5409
  if (before === undefined) {
5288
- yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate2(news)));
5289
- } else if (!matches8(before, news)) {
5290
- yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate2(news)));
5410
+ yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate4(news)));
5411
+ } else if (!matches9(before, news)) {
5412
+ yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate3(news)));
5291
5413
  }
5292
5414
  const after = dropUnreadable7(yield* readStorage(news));
5293
5415
  if (after === undefined) {
5294
- return yield* Effect66.die(new Error(`storage/${news.storage}: the write returned no error but the storage is still ` + 'absent. PVE wraps every answer in {"data":...} and can report success on a ' + "call that did nothing -- read back rather than trusting the status code."));
5416
+ return yield* Effect69.die(new Error(`storage/${news.storage}: the write returned no error but the storage is still ` + 'absent. PVE wraps every answer in {"data":...} and can report success on a ' + "call that did nothing -- read back rather than trusting the status code."));
5295
5417
  }
5296
5418
  return after;
5297
5419
  }),
5298
- delete: Effect66.fn(function* ({ olds }) {
5420
+ delete: Effect69.fn(function* ({ olds }) {
5299
5421
  yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
5300
5422
  })
5301
5423
  })));
5302
5424
  // src/proxmox/zfs-pool.ts
5303
5425
  import { Resource as Resource35 } from "alchemy";
5304
- import { isResolved as isResolved17 } from "alchemy/Diff";
5426
+ import { isResolved as isResolved19 } from "alchemy/Diff";
5305
5427
  import * as Provider35 from "alchemy/Provider";
5306
- import * as Effect69 from "effect/Effect";
5428
+ import * as Effect72 from "effect/Effect";
5307
5429
 
5308
5430
  // src/proxmox/zfs-pool-wire.ts
5309
- import * as nodes3 from "@distilled.cloud/proxmox/nodes";
5310
- import * as Effect67 from "effect/Effect";
5431
+ import * as nodes10 from "@distilled.cloud/proxmox/nodes";
5432
+ import * as Effect70 from "effect/Effect";
5311
5433
  var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
5312
5434
  const vdev = entry;
5313
5435
  const nested = leafDevices(vdev.children);
5314
5436
  return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== "");
5315
5437
  }) : [];
5316
- var attributesOf9 = (live, props) => ({
5438
+ var attributesOf10 = (live, props) => ({
5317
5439
  devices: leafDevices(live.children).join(","),
5318
5440
  errors: text(live.errors, "unknown"),
5319
5441
  name: props.name,
5320
5442
  node: props.node,
5321
5443
  state: text(live.state, "unknown")
5322
5444
  });
5323
- var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes3.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect67.map((live) => {
5445
+ var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect70.map((live) => {
5324
5446
  if (live === UNREADABLE)
5325
5447
  return UNREADABLE;
5326
- return text(live.name) === "" ? undefined : attributesOf9(live, props);
5448
+ return text(live.name) === "" ? undefined : attributesOf10(live, props);
5327
5449
  }));
5328
- var readPool = (props) => readPoolOrFail(props).pipe(Effect67.orElseSucceed(() => {
5450
+ var readPool = (props) => readPoolOrFail(props).pipe(Effect70.orElseSucceed(() => {
5329
5451
  return;
5330
5452
  }));
5331
5453
  var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
5332
5454
 
5333
5455
  // src/proxmox/zfs-pool-form.ts
5334
- import * as Effect68 from "effect/Effect";
5335
- import * as nodes4 from "@distilled.cloud/proxmox/nodes";
5456
+ import * as Effect71 from "effect/Effect";
5457
+ import * as nodes11 from "@distilled.cloud/proxmox/nodes";
5336
5458
  var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
5337
5459
  var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
5338
5460
  var createForm12 = (props) => ({
@@ -5343,7 +5465,7 @@ var createForm12 = (props) => ({
5343
5465
  ...props.compression === undefined ? {} : { compression: props.compression },
5344
5466
  ...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
5345
5467
  });
5346
- var toDistilledCreate3 = (props) => {
5468
+ var toDistilledCreate5 = (props) => {
5347
5469
  const form = createForm12(props);
5348
5470
  return {
5349
5471
  ...form,
@@ -5352,41 +5474,41 @@ var toDistilledCreate3 = (props) => {
5352
5474
  };
5353
5475
  };
5354
5476
  var SETTLE_ATTEMPTS = 30;
5355
- var createPool = (props, read4) => Effect68.gen(function* () {
5477
+ var createPool = (props, read4) => Effect71.gen(function* () {
5356
5478
  const existing = yield* read4(props);
5357
5479
  if (existing !== undefined)
5358
5480
  return existing;
5359
5481
  if (props.devices === undefined || props.raidlevel === undefined) {
5360
- return yield* Effect68.die(new Error(`${props.node}/${props.name}: this is an adopt-only declaration (no \`devices\` or ` + "`raidlevel`) and the pool is not there to adopt. An adopt-only Proxmox.ZfsPool never " + "creates -- declare both fields for a pool this resource should build, or point this " + "declaration at a pool that already exists on this node."));
5482
+ return yield* Effect71.die(new Error(`${props.node}/${props.name}: this is an adopt-only declaration (no \`devices\` or ` + "`raidlevel`) and the pool is not there to adopt. An adopt-only Proxmox.ZfsPool never " + "creates -- declare both fields for a pool this resource should build, or point this " + "declaration at a pool that already exists on this node."));
5361
5483
  }
5362
5484
  yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
5363
- const upid = yield* runPve(props.target, "provision", true, nodes4.createNodeDiskZfs(toDistilledCreate3(props)));
5364
- const settle4 = (attempts) => Effect68.gen(function* () {
5485
+ const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
5486
+ const settle4 = (attempts) => Effect71.gen(function* () {
5365
5487
  const live = yield* read4(props);
5366
5488
  if (live !== undefined || attempts <= 0)
5367
5489
  return live;
5368
- yield* Effect68.sleep("2 seconds");
5490
+ yield* Effect71.sleep("2 seconds");
5369
5491
  return yield* settle4(attempts - 1);
5370
5492
  });
5371
5493
  const after = yield* settle4(SETTLE_ATTEMPTS);
5372
5494
  if (after === undefined) {
5373
- return yield* Effect68.die(new Error(`nodes/${props.node}/disks/zfs/${props.name}: POST returned ${text(upid, "(no UPID)")} ` + "but the pool has not appeared. That POST only forks a `zfscreate` worker, so the task " + `holds the real error -- read it with \`pvesh get /nodes/${props.node}/tasks/<upid>/log\`. ` + "NOTHING WAS RETRIED AND NOTHING WAS CLEANED UP: if the worker reached `zpool create`, " + "the declared devices have already been written to. DO NOT RE-RUN THIS DEPLOY BEFORE " + 'READING THAT LOG: `read` cannot tell "the worker is still running" from "the pool is ' + 'genuinely gone", so a re-run this soon can POST a SECOND `zpool create` at the SAME ' + "devices while the first one is still mid-write -- confirm the task finished (one way " + "or the other) before trying again."));
5495
+ return yield* Effect71.die(new Error(`nodes/${props.node}/disks/zfs/${props.name}: POST returned ${text(upid, "(no UPID)")} ` + "but the pool has not appeared. That POST only forks a `zfscreate` worker, so the task " + `holds the real error -- read it with \`pvesh get /nodes/${props.node}/tasks/<upid>/log\`. ` + "NOTHING WAS RETRIED AND NOTHING WAS CLEANED UP: if the worker reached `zpool create`, " + "the declared devices have already been written to. DO NOT RE-RUN THIS DEPLOY BEFORE " + 'READING THAT LOG: `read` cannot tell "the worker is still running" from "the pool is ' + 'genuinely gone", so a re-run this soon can POST a SECOND `zpool create` at the SAME ' + "devices while the first one is still mid-write -- confirm the task finished (one way " + "or the other) before trying again."));
5374
5496
  }
5375
5497
  return after;
5376
5498
  });
5377
- var destroyPool = (props) => runPve(props.target, "provision", true, nodes4.deleteNodeDiskZfs({ name: props.name, node: props.node }));
5499
+ var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.deleteNodeDiskZfs({ name: props.name, node: props.node }));
5378
5500
 
5379
5501
  // src/proxmox/zfs-pool.ts
5380
5502
  var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
5381
5503
  defaultRemovalPolicy: "retain"
5382
5504
  });
5383
- var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect69.succeed(ProxmoxZfsPool.Provider.of({
5384
- list: () => Effect69.succeed([]),
5385
- read: Effect69.fn(function* ({ olds, output }) {
5505
+ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.succeed(ProxmoxZfsPool.Provider.of({
5506
+ list: () => Effect72.succeed([]),
5507
+ read: Effect72.fn(function* ({ olds, output }) {
5386
5508
  return dropUnreadable8(yield* output === undefined ? readPool(olds) : readPoolOrFail(olds));
5387
5509
  }),
5388
- diff: Effect69.fn(function* ({ news, output }) {
5389
- if (!isResolved17(news))
5510
+ diff: Effect72.fn(function* ({ news, output }) {
5511
+ if (!isResolved19(news))
5390
5512
  return;
5391
5513
  yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
5392
5514
  if (output === undefined)
@@ -5402,10 +5524,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect69.su
5402
5524
  }
5403
5525
  return { action: "noop" };
5404
5526
  }),
5405
- reconcile: Effect69.fn(function* ({ news }) {
5406
- return yield* createPool(news, (props) => readPool(props).pipe(Effect69.map(dropUnreadable8)));
5527
+ reconcile: Effect72.fn(function* ({ news }) {
5528
+ return yield* createPool(news, (props) => readPool(props).pipe(Effect72.map(dropUnreadable8)));
5407
5529
  }),
5408
- delete: Effect69.fn(function* ({ olds }) {
5530
+ delete: Effect72.fn(function* ({ olds }) {
5409
5531
  yield* destroyPool(olds);
5410
5532
  })
5411
5533
  })));
@@ -5485,5 +5607,5 @@ export {
5485
5607
  violations
5486
5608
  };
5487
5609
 
5488
- //# debugId=AF866CF2F0A2718F64756E2164756E21
5610
+ //# debugId=34DF7A874403817964756E2164756E21
5489
5611
  //# sourceMappingURL=index.js.map