@homeflare/alchemy 0.37.1 → 0.37.3

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 (195) hide show
  1. package/dist/proxmox/backup-job-config.d.ts +95 -0
  2. package/dist/proxmox/backup-job-config.d.ts.map +1 -0
  3. package/dist/proxmox/backup-job-distilled.d.ts +46 -0
  4. package/dist/proxmox/backup-job-distilled.d.ts.map +1 -0
  5. package/dist/proxmox/backup-job.d.ts +9 -26
  6. package/dist/proxmox/backup-job.d.ts.map +1 -1
  7. package/dist/proxmox/ceph-fs-distilled.d.ts +17 -10
  8. package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -1
  9. package/dist/proxmox/ceph-fs-wire.d.ts +5 -22
  10. package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
  11. package/dist/proxmox/ceph-fs.d.ts +3 -3
  12. package/dist/proxmox/distilled-pbs.d.ts +12 -0
  13. package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
  14. package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
  15. package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
  16. package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
  17. package/dist/proxmox/fake-pve.d.ts +1 -1
  18. package/dist/proxmox/fake-pve.d.ts.map +1 -1
  19. package/dist/proxmox/group-wire.d.ts +2 -2
  20. package/dist/proxmox/group-wire.d.ts.map +1 -1
  21. package/dist/proxmox/index.js +1382 -506
  22. package/dist/proxmox/index.js.map +51 -27
  23. package/dist/proxmox/live-core-fixtures.d.ts +40 -0
  24. package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
  25. package/dist/proxmox/metric-server-config.d.ts +49 -0
  26. package/dist/proxmox/metric-server-config.d.ts.map +1 -0
  27. package/dist/proxmox/metric-server-distilled.d.ts +43 -0
  28. package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
  29. package/dist/proxmox/metric-server.d.ts +2 -10
  30. package/dist/proxmox/metric-server.d.ts.map +1 -1
  31. package/dist/proxmox/node-network-wire.d.ts +1 -1
  32. package/dist/proxmox/node-network-wire.d.ts.map +1 -1
  33. package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
  34. package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
  35. package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
  36. package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
  37. package/dist/proxmox/notification-matcher.d.ts +5 -5
  38. package/dist/proxmox/notification-matcher.d.ts.map +1 -1
  39. package/dist/proxmox/notification-sdk-input.d.ts +22 -0
  40. package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
  41. package/dist/proxmox/notification-target-distilled.d.ts +12 -0
  42. package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
  43. package/dist/proxmox/notification-target-form.d.ts +2 -0
  44. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  45. package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
  46. package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
  47. package/dist/proxmox/notification-target-spec.d.ts +5 -0
  48. package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
  49. package/dist/proxmox/notification-target.d.ts +4 -5
  50. package/dist/proxmox/notification-target.d.ts.map +1 -1
  51. package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
  52. package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
  53. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
  54. package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
  55. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
  56. package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
  57. package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
  58. package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
  59. package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
  60. package/dist/proxmox/pbs-datastore.d.ts +5 -6
  61. package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
  62. package/dist/proxmox/pbs-notification-input.d.ts +18 -0
  63. package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
  64. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
  65. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
  66. package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
  67. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
  68. package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
  69. package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
  70. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
  71. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  72. package/dist/proxmox/pbs-notification-target.d.ts +1 -1
  73. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
  74. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
  75. package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
  76. package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
  77. package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
  78. package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
  79. package/dist/proxmox/pbs-prune-job.d.ts +9 -8
  80. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
  81. package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
  82. package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
  83. package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
  84. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
  85. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
  86. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
  87. package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
  88. package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
  89. package/dist/proxmox/pbs-sync-job.d.ts +9 -39
  90. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
  91. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
  92. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
  93. package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
  94. package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
  95. package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
  96. package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
  97. package/dist/proxmox/pbs-verify-job.d.ts +12 -12
  98. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
  99. package/dist/proxmox/pool.d.ts +10 -1
  100. package/dist/proxmox/pool.d.ts.map +1 -1
  101. package/dist/proxmox/storage-wire.d.ts +2 -2
  102. package/dist/proxmox/storage-wire.d.ts.map +1 -1
  103. package/dist/proxmox/user-wire.d.ts +2 -2
  104. package/dist/proxmox/user-wire.d.ts.map +1 -1
  105. package/dist/proxmox/zfs-pool-wire.d.ts +1 -1
  106. package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -1
  107. package/docs/changelog/README.md +22 -0
  108. package/docs/changelog/archive-01.md +176 -0
  109. package/docs/changelog/archive-02.md +180 -0
  110. package/docs/changelog/archive-03.md +179 -0
  111. package/docs/changelog/archive-04.md +178 -0
  112. package/docs/changelog/archive-05.md +177 -0
  113. package/docs/changelog/archive-06.md +180 -0
  114. package/docs/changelog/archive-07.md +180 -0
  115. package/docs/changelog/archive-08.md +178 -0
  116. package/docs/changelog/archive-09.md +181 -0
  117. package/docs/changelog/archive-10.md +182 -0
  118. package/docs/changelog/archive-11.md +182 -0
  119. package/docs/changelog/archive-12.md +183 -0
  120. package/docs/changelog/archive-13.md +180 -0
  121. package/docs/changelog/archive-14.md +178 -0
  122. package/docs/changelog/archive-15.md +181 -0
  123. package/docs/changelog/archive-16.md +182 -0
  124. package/docs/changelog/archive-17.md +183 -0
  125. package/docs/changelog/archive-18.md +55 -0
  126. package/docs/pbs-distilled-jobs.md +31 -0
  127. package/docs/pbs-notifications.md +8 -2
  128. package/docs/proxmox-notification-sdk.md +28 -0
  129. package/docs/proxmox-sdk-migration.md +60 -0
  130. package/docs/proxmox.md +9 -3
  131. package/docs/upstream-conformance-families.md +6 -2
  132. package/package.json +3 -3
  133. package/src/proxmox/adopt-noop.test.ts +4 -5
  134. package/src/proxmox/backup-job-config.ts +95 -0
  135. package/src/proxmox/backup-job-distilled.ts +86 -0
  136. package/src/proxmox/backup-job.ts +78 -116
  137. package/src/proxmox/ceph-fs-delete.test.ts +5 -8
  138. package/src/proxmox/ceph-fs-destroy-sdk.test.ts +98 -0
  139. package/src/proxmox/ceph-fs-distilled.ts +41 -13
  140. package/src/proxmox/ceph-fs-wire.ts +5 -93
  141. package/src/proxmox/ceph-fs.ts +7 -7
  142. package/src/proxmox/constraints.test.ts +9 -8
  143. package/src/proxmox/distilled-pbs.test.ts +87 -0
  144. package/src/proxmox/distilled-pbs.ts +50 -0
  145. package/src/proxmox/fake-pbs-jobs.ts +99 -0
  146. package/src/proxmox/fake-pbs-notify.ts +4 -1
  147. package/src/proxmox/fake-pve.ts +3 -2
  148. package/src/proxmox/live-core-fixtures.ts +126 -0
  149. package/src/proxmox/live-core-lifecycle.test.ts +115 -0
  150. package/src/proxmox/live-core-wire.test.ts +190 -0
  151. package/src/proxmox/metric-server-config.ts +87 -0
  152. package/src/proxmox/metric-server-distilled.ts +81 -0
  153. package/src/proxmox/metric-server.ts +77 -88
  154. package/src/proxmox/notification-distilled.test.ts +162 -0
  155. package/src/proxmox/notification-matcher-distilled.ts +60 -0
  156. package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
  157. package/src/proxmox/notification-matcher.ts +9 -26
  158. package/src/proxmox/notification-replacement.test.ts +190 -0
  159. package/src/proxmox/notification-sdk-input.ts +39 -0
  160. package/src/proxmox/notification-target-distilled.ts +160 -0
  161. package/src/proxmox/notification-target-form.ts +2 -0
  162. package/src/proxmox/notification-target-lifecycle.ts +75 -0
  163. package/src/proxmox/notification-target-spec.ts +68 -0
  164. package/src/proxmox/notification-target.ts +6 -68
  165. package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
  166. package/src/proxmox/pbs-datastore-form.ts +18 -49
  167. package/src/proxmox/pbs-datastore-guard.ts +7 -4
  168. package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
  169. package/src/proxmox/pbs-datastore-wire.ts +94 -0
  170. package/src/proxmox/pbs-datastore.ts +7 -133
  171. package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
  172. package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
  173. package/src/proxmox/pbs-notification-errors.test.ts +93 -0
  174. package/src/proxmox/pbs-notification-input.ts +25 -0
  175. package/src/proxmox/pbs-notification-mail.test.ts +64 -0
  176. package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
  177. package/src/proxmox/pbs-notification-matcher.ts +70 -17
  178. package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
  179. package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
  180. package/src/proxmox/pbs-notification-target.ts +1 -1
  181. package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
  182. package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
  183. package/src/proxmox/pbs-prune-job-spec.ts +53 -0
  184. package/src/proxmox/pbs-prune-job-wire.ts +68 -0
  185. package/src/proxmox/pbs-prune-job.ts +10 -58
  186. package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
  187. package/src/proxmox/pbs-sync-job-form.ts +7 -6
  188. package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
  189. package/src/proxmox/pbs-sync-job-wire.ts +71 -0
  190. package/src/proxmox/pbs-sync-job.ts +10 -41
  191. package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
  192. package/src/proxmox/pbs-verify-job-spec.ts +76 -0
  193. package/src/proxmox/pbs-verify-job-wire.ts +58 -0
  194. package/src/proxmox/pbs-verify-job.ts +13 -85
  195. package/src/proxmox/pool.ts +92 -10
@@ -1440,89 +1440,23 @@ var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect8.su
1440
1440
  })));
1441
1441
  // src/proxmox/backup-job.ts
1442
1442
  import { Resource as Resource3 } from "alchemy";
1443
- import * as Provider3 from "alchemy/Provider";
1444
- import * as Effect12 from "effect/Effect";
1445
-
1446
- // src/proxmox/resource.ts
1447
1443
  import { isResolved as isResolved3 } from "alchemy/Diff";
1448
- import * as Effect11 from "effect/Effect";
1449
-
1450
- // src/proxmox/client.ts
1451
- import * as Effect9 from "effect/Effect";
1452
- import * as HttpClient5 from "effect/unstable/http/HttpClient";
1453
- import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
1454
- class PveError extends Error {
1455
- status;
1456
- method;
1457
- path;
1458
- constructor(status, method, path, detail) {
1459
- super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
1460
- this.status = status;
1461
- this.method = method;
1462
- this.path = path;
1463
- this.name = "PveError";
1464
- }
1465
- }
1466
- var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
1467
- var encode = (form) => {
1468
- const params = new URLSearchParams;
1469
- for (const [key, value] of Object.entries(form)) {
1470
- if (typeof value === "string")
1471
- params.append(key, value);
1472
- else
1473
- for (const item of value)
1474
- params.append(key, item);
1475
- }
1476
- return params.toString();
1477
- };
1478
- var buildRequest = (method, apiBase, path, credential, target, form) => {
1479
- const url = `${apiBase}/${path}`;
1480
- return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
1481
- };
1482
- var executeOne = (target, credential, method, path, form, apiBase) => Effect9.gen(function* () {
1483
- const client = yield* HttpClient5.HttpClient;
1484
- const request = buildRequest(method, apiBase, path, credential, target, form);
1485
- return yield* client.execute(request).pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
1486
- });
1487
- var readResponse = (response, method, path) => Effect9.gen(function* () {
1488
- if (response.status < 200 || response.status >= 300) {
1489
- const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
1490
- return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
1491
- }
1492
- const body = yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
1493
- return body.data;
1494
- });
1495
- var pveWith = (target, credential, method, path, form) => Effect9.gen(function* () {
1496
- const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect9.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
1497
- return yield* readResponse(response, method, path);
1498
- });
1499
- var pveEnvelopeWith = (target, credential, method, path) => Effect9.gen(function* () {
1500
- const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect9.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
1501
- if (response.status < 200 || response.status >= 300) {
1502
- const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
1503
- return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
1504
- }
1505
- return yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
1506
- });
1507
- var pve = (target, role, method, path, form) => Effect9.gen(function* () {
1508
- const credential = yield* leased(target, role);
1509
- return yield* pveWith(target, credential, method, path, form);
1510
- });
1444
+ import * as cluster2 from "@distilled.cloud/proxmox/cluster";
1511
1445
 
1512
1446
  // src/proxmox/resource-guard.ts
1513
- import * as Effect10 from "effect/Effect";
1447
+ import * as Effect9 from "effect/Effect";
1514
1448
  var specGuards = (spec) => {
1515
1449
  const endpointsOf = (props) => typeof spec.endpoint === "function" ? spec.endpoint(props) : spec.endpoint;
1516
- const guardCreate = (props, presence) => Effect10.suspend(() => {
1450
+ const guardCreate = (props, presence) => Effect9.suspend(() => {
1517
1451
  const create = endpointsOf(props)?.create;
1518
- return create === undefined ? Effect10.void : guardForm(create, spec.createForm(props), presence);
1452
+ return create === undefined ? Effect9.void : guardForm(create, spec.createForm(props), presence);
1519
1453
  });
1520
- const guardUpdate = (props) => Effect10.suspend(() => {
1454
+ const guardUpdate = (props) => Effect9.suspend(() => {
1521
1455
  const update = endpointsOf(props)?.update;
1522
1456
  if (update === undefined)
1523
- return Effect10.void;
1457
+ return Effect9.void;
1524
1458
  const form = spec.updateForm?.(props);
1525
- return form === undefined ? Effect10.void : guardForm(update, form, false);
1459
+ return form === undefined ? Effect9.void : guardForm(update, form, false);
1526
1460
  });
1527
1461
  return { guardCreate, guardUpdate };
1528
1462
  };
@@ -1530,66 +1464,7 @@ var specGuards = (spec) => {
1530
1464
  // src/proxmox/update-guard.ts
1531
1465
  var formToSend = (matches3, live, props, form) => matches3(live, props) || Object.keys(form).length === 0 ? undefined : form;
1532
1466
 
1533
- // src/proxmox/resource.ts
1534
- var pveOperations = (spec) => {
1535
- const { guardCreate, guardUpdate } = specGuards(spec);
1536
- const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect11.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect11.orElseSucceed(() => {
1537
- return;
1538
- }));
1539
- return {
1540
- guardCreate,
1541
- guardUpdate,
1542
- read,
1543
- diff: (news, output) => Effect11.gen(function* () {
1544
- if (!isResolved3(news))
1545
- return;
1546
- yield* guardCreate(news, output === undefined);
1547
- yield* guardUpdate(news);
1548
- if (output === undefined)
1549
- return;
1550
- const live = yield* read(news);
1551
- if (live === undefined) {
1552
- yield* guardCreate(news, true);
1553
- return { action: "update" };
1554
- }
1555
- if (spec.matches(live, news))
1556
- return { action: "noop" };
1557
- return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
1558
- }),
1559
- reconcile: (news) => Effect11.gen(function* () {
1560
- const live = yield* read(news);
1561
- yield* guardCreate(news, live === undefined);
1562
- yield* guardUpdate(news);
1563
- if (live === undefined) {
1564
- yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
1565
- } else if (spec.updateForm !== undefined) {
1566
- const form = formToSend(spec.matches, live, news, spec.updateForm(news));
1567
- if (form !== undefined) {
1568
- yield* pve(news.target, "provision", "PUT", spec.path(news), form);
1569
- }
1570
- }
1571
- const after = yield* read(news);
1572
- if (after === undefined) {
1573
- return yield* Effect11.die(new Error(`${spec.path(news)}: the write returned no error but the object 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."));
1574
- }
1575
- return after;
1576
- }),
1577
- destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
1578
- };
1579
- };
1580
- var pveHandlers = (spec) => {
1581
- const ops = pveOperations(spec);
1582
- return {
1583
- list: () => Effect11.succeed([]),
1584
- read: ({ olds }) => ops.read(olds),
1585
- diff: ({ news, output }) => ops.diff(news, output),
1586
- reconcile: ({ news }) => ops.reconcile(news),
1587
- delete: ({ olds }) => ops.destroy(olds)
1588
- };
1589
- };
1590
-
1591
- // src/proxmox/backup-job.ts
1592
- var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
1467
+ // src/proxmox/backup-job-config.ts
1593
1468
  var shape2 = (props) => ({
1594
1469
  all: props.all === true ? "1" : "0",
1595
1470
  enabled: props.enabled === false ? "0" : "1",
@@ -1606,7 +1481,7 @@ var shape2 = (props) => ({
1606
1481
  ...props.storage === undefined ? {} : { storage: props.storage },
1607
1482
  ...props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }
1608
1483
  });
1609
- var handlers = pveHandlers({
1484
+ var backupJobSpec = {
1610
1485
  attributes: (live, props) => ({
1611
1486
  all: bool(live["all"], false),
1612
1487
  comment: text(live["comment"], ""),
@@ -1631,13 +1506,95 @@ var handlers = pveHandlers({
1631
1506
  matches: (attributes, props) => attributes.schedule === props.schedule && attributes.enabled === (props.enabled !== false) && attributes.all === (props.all === true) && attributes.mode === (props.mode ?? "snapshot") && attributes["notification-mode"] === (props["notification-mode"] ?? "auto") && attributes["repeat-missed"] === (props["repeat-missed"] === true) && (props.storage === undefined || attributes.storage === props.storage) && (props.pool === undefined || attributes.pool === props.pool) && (props.comment === undefined || attributes.comment === props.comment) && (props.vmid === undefined || attributes.vmid === guestList(props.vmid)) && (props.exclude === undefined || attributes.exclude === guestList(props.exclude)) && (props["notes-template"] === undefined || attributes["notes-template"] === props["notes-template"]) && (props["prune-backups"] === undefined || attributes["prune-backups"] === propertyString(props["prune-backups"])) && (props.fleecing === undefined || attributes.fleecing === propertyString(props.fleecing, "enabled")),
1632
1507
  path: (props) => `cluster/backup/${props.id}`,
1633
1508
  updateForm: shape2
1634
- });
1635
- var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect12.succeed(ProxmoxBackupJob.Provider.of(handlers)));
1509
+ };
1510
+
1511
+ // src/proxmox/backup-job-distilled.ts
1512
+ import * as cluster from "@distilled.cloud/proxmox/cluster";
1513
+ import * as Effect10 from "effect/Effect";
1514
+ import * as Schema from "effect/Schema";
1515
+ import { ProxmoxParseError } from "@distilled.cloud/proxmox/Errors";
1516
+ var backupJobRequest = (props, form = backupJobSpec.updateForm(props)) => {
1517
+ const {
1518
+ "notes-template": notes_template,
1519
+ "notification-mode": notification_mode,
1520
+ "prune-backups": prune_backups,
1521
+ "repeat-missed": repeat_missed,
1522
+ ...fields
1523
+ } = form;
1524
+ return {
1525
+ ...fields,
1526
+ id: props.id,
1527
+ notification_mode,
1528
+ repeat_missed,
1529
+ ...notes_template === undefined ? {} : { notes_template },
1530
+ ...prune_backups === undefined ? {} : { prune_backups }
1531
+ };
1532
+ };
1533
+ var readBackupJob = (props) => runPve(props.target, "read", false, cluster.getClusterBackup({ id: props.id })).pipe(Effect10.flatMap((live) => Schema.decodeUnknownEffect(Schema.toType(cluster.GetClusterBackupResponse))({
1534
+ ...live,
1535
+ prune_backups: undefined,
1536
+ fleecing: undefined
1537
+ }).pipe(Effect10.mapError(() => new ProxmoxParseError({
1538
+ body: undefined,
1539
+ cause: "Backup job response does not match its vendor schema"
1540
+ })), Effect10.as(live))), Effect10.map((live) => backupJobSpec.attributes({
1541
+ ...live,
1542
+ "next-run": live.next_run,
1543
+ "notes-template": live.notes_template,
1544
+ "notification-mode": live.notification_mode,
1545
+ "prune-backups": typeof live.prune_backups === "object" && live.prune_backups !== null ? Object.fromEntries(Object.entries(live.prune_backups).map(([key, value]) => [
1546
+ key.replaceAll("_", "-"),
1547
+ value
1548
+ ])) : live.prune_backups,
1549
+ "repeat-missed": live.repeat_missed
1550
+ }, props)), Effect10.catchTag("BackupJobNotFound", () => Effect10.succeed(undefined)), Effect10.catchTag("NotFound", () => Effect10.succeed(undefined)));
1551
+ var deleteBackupJob = (props) => runPve(props.target, "provision", true, cluster.deleteClusterBackup({ id: props.id })).pipe(Effect10.catchTag("BackupJobNotFound", () => Effect10.void), Effect10.catchTag("NotFound", () => Effect10.void));
1552
+
1553
+ // src/proxmox/backup-job.ts
1554
+ import * as Provider3 from "alchemy/Provider";
1555
+ import * as Effect11 from "effect/Effect";
1556
+ var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
1557
+ var { guardCreate, guardUpdate } = specGuards(backupJobSpec);
1558
+ var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect11.succeed(ProxmoxBackupJob.Provider.of({
1559
+ list: () => Effect11.succeed([]),
1560
+ read: ({ olds }) => readBackupJob(olds),
1561
+ diff: Effect11.fn(function* ({ news, output }) {
1562
+ if (!isResolved3(news))
1563
+ return;
1564
+ yield* guardCreate(news, output === undefined);
1565
+ yield* guardUpdate(news);
1566
+ if (output === undefined)
1567
+ return;
1568
+ const live = yield* readBackupJob(news);
1569
+ if (live === undefined) {
1570
+ yield* guardCreate(news, true);
1571
+ return { action: "update" };
1572
+ }
1573
+ return { action: backupJobSpec.matches(live, news) ? "noop" : "update" };
1574
+ }),
1575
+ reconcile: Effect11.fn(function* ({ news }) {
1576
+ const live = yield* readBackupJob(news);
1577
+ yield* guardCreate(news, live === undefined);
1578
+ yield* guardUpdate(news);
1579
+ if (live === undefined) {
1580
+ yield* runPve(news.target, "provision", true, cluster2.createClusterBackup(backupJobRequest(news)));
1581
+ } else {
1582
+ const form = formToSend(backupJobSpec.matches, live, news, backupJobSpec.updateForm(news));
1583
+ if (form !== undefined)
1584
+ yield* runPve(news.target, "provision", true, cluster2.putClusterBackup(backupJobRequest(news, form)));
1585
+ }
1586
+ const after = yield* readBackupJob(news);
1587
+ if (after === undefined)
1588
+ return yield* Effect11.fail(new Error(`${backupJobSpec.path(news)}: write returned success but the resource is still absent`));
1589
+ return after;
1590
+ }),
1591
+ delete: ({ olds }) => deleteBackupJob(olds)
1592
+ })));
1636
1593
  // src/proxmox/ceph-daemon.ts
1637
1594
  import { Resource as Resource4 } from "alchemy";
1638
1595
  import { isResolved as isResolved4 } from "alchemy/Diff";
1639
1596
  import * as Provider4 from "alchemy/Provider";
1640
- import * as Effect14 from "effect/Effect";
1597
+ import * as Effect13 from "effect/Effect";
1641
1598
 
1642
1599
  // src/proxmox/ceph-daemon-form.ts
1643
1600
  var DAEMON_ENDPOINTS = {
@@ -1678,19 +1635,19 @@ var daemonAttributes = (live, props) => {
1678
1635
 
1679
1636
  // src/proxmox/ceph-daemon-wire.ts
1680
1637
  import * as nodes from "@distilled.cloud/proxmox/nodes";
1681
- import * as Effect13 from "effect/Effect";
1638
+ import * as Effect12 from "effect/Effect";
1682
1639
  var listRows = (props) => {
1683
1640
  const node = props.node;
1684
1641
  switch (props.kind) {
1685
1642
  case "mon":
1686
- return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(Effect13.map((rows) => rows));
1643
+ return runPve(props.target, "read", false, nodes.listNodeCephMon({ node })).pipe(Effect12.map((rows) => rows));
1687
1644
  case "mgr":
1688
- return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(Effect13.map((rows) => rows));
1645
+ return runPve(props.target, "read", false, nodes.listNodeCephMgr({ node })).pipe(Effect12.map((rows) => rows));
1689
1646
  case "mds":
1690
- return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(Effect13.map((rows) => rows));
1647
+ return runPve(props.target, "read", false, nodes.listNodeCephMds({ node })).pipe(Effect12.map((rows) => rows));
1691
1648
  }
1692
1649
  };
1693
- var readDaemon = (props) => listRows(props).pipe(Effect13.map((rows) => daemonAttributes(rows, props)));
1650
+ var readDaemon = (props) => listRows(props).pipe(Effect12.map((rows) => daemonAttributes(rows, props)));
1694
1651
  var createDaemon = (props) => {
1695
1652
  const node = props.node;
1696
1653
  const id = daemonId(props);
@@ -1729,7 +1686,7 @@ var notCreated = (props) => new Error(`${daemonPath(props)}: no ${props.kind} na
1729
1686
  var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
1730
1687
  defaultRemovalPolicy: "retain"
1731
1688
  });
1732
- var reconcileDaemon = Effect14.fn(function* ({ news }) {
1689
+ var reconcileDaemon = Effect13.fn(function* ({ news }) {
1733
1690
  const live = yield* readDaemon(news);
1734
1691
  yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), live === undefined);
1735
1692
  if (live !== undefined)
@@ -1737,13 +1694,13 @@ var reconcileDaemon = Effect14.fn(function* ({ news }) {
1737
1694
  yield* createDaemon(news);
1738
1695
  const after = yield* readDaemon(news);
1739
1696
  if (after === undefined)
1740
- return yield* Effect14.die(notCreated(news));
1697
+ return yield* Effect13.die(notCreated(news));
1741
1698
  return after;
1742
1699
  });
1743
- var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect14.succeed(ProxmoxCephDaemon.Provider.of({
1744
- list: () => Effect14.succeed([]),
1700
+ var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect13.succeed(ProxmoxCephDaemon.Provider.of({
1701
+ list: () => Effect13.succeed([]),
1745
1702
  read: ({ olds }) => readDaemon(olds),
1746
- diff: Effect14.fn(function* ({ news, output }) {
1703
+ diff: Effect13.fn(function* ({ news, output }) {
1747
1704
  if (!isResolved4(news))
1748
1705
  return;
1749
1706
  yield* guardWrite2(DAEMON_ENDPOINTS[news.kind], createForm(news), output === undefined);
@@ -1762,9 +1719,135 @@ var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect
1762
1719
  // src/proxmox/ceph-flag.ts
1763
1720
  import { Resource as Resource5 } from "alchemy";
1764
1721
  import * as Provider5 from "alchemy/Provider";
1722
+ import * as Effect16 from "effect/Effect";
1723
+
1724
+ // src/proxmox/resource.ts
1725
+ import { isResolved as isResolved5 } from "alchemy/Diff";
1765
1726
  import * as Effect15 from "effect/Effect";
1727
+
1728
+ // src/proxmox/client.ts
1729
+ import * as Effect14 from "effect/Effect";
1730
+ import * as HttpClient5 from "effect/unstable/http/HttpClient";
1731
+ import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
1732
+ class PveError extends Error {
1733
+ status;
1734
+ method;
1735
+ path;
1736
+ constructor(status, method, path, detail) {
1737
+ super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
1738
+ this.status = status;
1739
+ this.method = method;
1740
+ this.path = path;
1741
+ this.name = "PveError";
1742
+ }
1743
+ }
1744
+ var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
1745
+ var encode = (form) => {
1746
+ const params = new URLSearchParams;
1747
+ for (const [key, value] of Object.entries(form)) {
1748
+ if (typeof value === "string")
1749
+ params.append(key, value);
1750
+ else
1751
+ for (const item of value)
1752
+ params.append(key, item);
1753
+ }
1754
+ return params.toString();
1755
+ };
1756
+ var buildRequest = (method, apiBase, path, credential, target, form) => {
1757
+ const url = `${apiBase}/${path}`;
1758
+ return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
1759
+ };
1760
+ var executeOne = (target, credential, method, path, form, apiBase) => Effect14.gen(function* () {
1761
+ const client = yield* HttpClient5.HttpClient;
1762
+ const request = buildRequest(method, apiBase, path, credential, target, form);
1763
+ return yield* client.execute(request).pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
1764
+ });
1765
+ var readResponse = (response, method, path) => Effect14.gen(function* () {
1766
+ if (response.status < 200 || response.status >= 300) {
1767
+ const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
1768
+ return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
1769
+ }
1770
+ const body = yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
1771
+ return body.data;
1772
+ });
1773
+ var pveWith = (target, credential, method, path, form) => Effect14.gen(function* () {
1774
+ const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
1775
+ return yield* readResponse(response, method, path);
1776
+ });
1777
+ var pveEnvelopeWith = (target, credential, method, path) => Effect14.gen(function* () {
1778
+ const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
1779
+ if (response.status < 200 || response.status >= 300) {
1780
+ const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
1781
+ return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
1782
+ }
1783
+ return yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
1784
+ });
1785
+ var pve = (target, role, method, path, form) => Effect14.gen(function* () {
1786
+ const credential = yield* leased(target, role);
1787
+ return yield* pveWith(target, credential, method, path, form);
1788
+ });
1789
+
1790
+ // src/proxmox/resource.ts
1791
+ var pveOperations = (spec) => {
1792
+ const { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(spec);
1793
+ const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect15.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect15.orElseSucceed(() => {
1794
+ return;
1795
+ }));
1796
+ return {
1797
+ guardCreate: guardCreate2,
1798
+ guardUpdate: guardUpdate2,
1799
+ read,
1800
+ diff: (news, output) => Effect15.gen(function* () {
1801
+ if (!isResolved5(news))
1802
+ return;
1803
+ yield* guardCreate2(news, output === undefined);
1804
+ yield* guardUpdate2(news);
1805
+ if (output === undefined)
1806
+ return;
1807
+ const live = yield* read(news);
1808
+ if (live === undefined) {
1809
+ yield* guardCreate2(news, true);
1810
+ return { action: "update" };
1811
+ }
1812
+ if (spec.matches(live, news))
1813
+ return { action: "noop" };
1814
+ return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
1815
+ }),
1816
+ reconcile: (news) => Effect15.gen(function* () {
1817
+ const live = yield* read(news);
1818
+ yield* guardCreate2(news, live === undefined);
1819
+ yield* guardUpdate2(news);
1820
+ if (live === undefined) {
1821
+ yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
1822
+ } else if (spec.updateForm !== undefined) {
1823
+ const form = formToSend(spec.matches, live, news, spec.updateForm(news));
1824
+ if (form !== undefined) {
1825
+ yield* pve(news.target, "provision", "PUT", spec.path(news), form);
1826
+ }
1827
+ }
1828
+ const after = yield* read(news);
1829
+ if (after === undefined) {
1830
+ return yield* Effect15.die(new Error(`${spec.path(news)}: the write returned no error but the object 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."));
1831
+ }
1832
+ return after;
1833
+ }),
1834
+ destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
1835
+ };
1836
+ };
1837
+ var pveHandlers = (spec) => {
1838
+ const ops = pveOperations(spec);
1839
+ return {
1840
+ list: () => Effect15.succeed([]),
1841
+ read: ({ olds }) => ops.read(olds),
1842
+ diff: ({ news, output }) => ops.diff(news, output),
1843
+ reconcile: ({ news }) => ops.reconcile(news),
1844
+ delete: ({ olds }) => ops.destroy(olds)
1845
+ };
1846
+ };
1847
+
1848
+ // src/proxmox/ceph-flag.ts
1766
1849
  var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
1767
- var handlers2 = pveHandlers({
1850
+ var handlers = pveHandlers({
1768
1851
  attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
1769
1852
  collection: () => "cluster/ceph/flags",
1770
1853
  createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
@@ -1773,20 +1856,20 @@ var handlers2 = pveHandlers({
1773
1856
  path: (props) => `cluster/ceph/flags/${props.flag}`,
1774
1857
  updateForm: (props) => ({ value: flag(props.value) ?? "0" })
1775
1858
  });
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 });
1859
+ var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect16.succeed(ProxmoxCephFlag.Provider.of({
1860
+ ...handlers,
1861
+ delete: () => Effect16.void,
1862
+ reconcile: Effect16.fn(function* ({ news }) {
1863
+ const after = yield* handlers.reconcile({ news });
1781
1864
  if (after.value !== news.value) {
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."));
1865
+ return yield* Effect16.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."));
1783
1866
  }
1784
1867
  return after;
1785
1868
  })
1786
1869
  })));
1787
1870
  // src/proxmox/ceph-fs.ts
1788
1871
  import { Resource as Resource6 } from "alchemy";
1789
- import { isResolved as isResolved5 } from "alchemy/Diff";
1872
+ import { isResolved as isResolved6 } from "alchemy/Diff";
1790
1873
  import * as Provider6 from "alchemy/Provider";
1791
1874
  import * as Effect18 from "effect/Effect";
1792
1875
 
@@ -1795,7 +1878,6 @@ import * as nodes2 from "@distilled.cloud/proxmox/nodes";
1795
1878
  import * as Effect17 from "effect/Effect";
1796
1879
 
1797
1880
  // src/proxmox/ceph-fs-wire.ts
1798
- import * as Effect16 from "effect/Effect";
1799
1881
  var readRow = (live, props) => {
1800
1882
  const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
1801
1883
  if (row === undefined)
@@ -1817,33 +1899,6 @@ var createForm2 = (props) => ({
1817
1899
  ...field("add-storage", flag(props["add-storage"])),
1818
1900
  ...field("pg_num", props.pg_num === undefined ? undefined : String(props.pg_num))
1819
1901
  });
1820
- var destroyPath = (props) => {
1821
- const query = [
1822
- props["remove-pools"] === true ? "remove-pools=1" : undefined,
1823
- props["remove-storages"] === true ? "remove-storages=1" : undefined
1824
- ].filter((part) => part !== undefined);
1825
- return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
1826
- };
1827
- var settle = (target, node, upid, what) => Effect16.gen(function* () {
1828
- for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
1829
- const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect16.orElseSucceed(() => {
1830
- return;
1831
- }));
1832
- if (status?.status === "stopped") {
1833
- const exit = text(status.exitstatus);
1834
- if (exit === "OK")
1835
- return;
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\`.`));
1837
- }
1838
- yield* Effect16.sleep(POLL_SECONDS * 1000);
1839
- }
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."));
1841
- });
1842
- var destroyFs = (props) => Effect16.gen(function* () {
1843
- const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
1844
- if (upid !== "")
1845
- yield* settle(props.target, props.node, upid, destroyPath(props));
1846
- });
1847
1902
  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.");
1848
1903
  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.");
1849
1904
 
@@ -1854,7 +1909,7 @@ var toDistilledCreate = (props) => ({
1854
1909
  name: props.name,
1855
1910
  node: props.node
1856
1911
  });
1857
- var settleCreate = (props, upid) => Effect17.gen(function* () {
1912
+ var settleTask = (props, upid) => Effect17.gen(function* () {
1858
1913
  for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
1859
1914
  const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
1860
1915
  return;
@@ -1873,7 +1928,17 @@ var createFs = (props) => Effect17.gen(function* () {
1873
1928
  if (upid === "") {
1874
1929
  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
1930
  }
1876
- yield* settleCreate(props, upid);
1931
+ yield* settleTask(props, upid);
1932
+ });
1933
+ var destroyFs = (props) => Effect17.gen(function* () {
1934
+ const upid = yield* runPve(props.target, "provision", true, nodes2.deleteNodeCephFs({
1935
+ node: props.node,
1936
+ name: props.name,
1937
+ ...props["remove-pools"] === true ? { remove_pools: "1" } : {},
1938
+ ...props["remove-storages"] === true ? { remove_storages: "1" } : {}
1939
+ })).pipe(Effect17.catchTag("CephFsNotFound", () => Effect17.succeed("")));
1940
+ if (upid !== "")
1941
+ yield* settleTask(props, upid);
1877
1942
  });
1878
1943
 
1879
1944
  // src/proxmox/ceph-fs.ts
@@ -1886,7 +1951,7 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succe
1886
1951
  return yield* readFs(olds);
1887
1952
  }),
1888
1953
  diff: Effect18.fn(function* ({ news, output }) {
1889
- if (!isResolved5(news))
1954
+ if (!isResolved6(news))
1890
1955
  return;
1891
1956
  yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
1892
1957
  if (output !== undefined && news.name !== output.name) {
@@ -1922,7 +1987,7 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succe
1922
1987
  })));
1923
1988
  // src/proxmox/ceph-osd.ts
1924
1989
  import { Resource as Resource7 } from "alchemy";
1925
- import { isResolved as isResolved6 } from "alchemy/Diff";
1990
+ import { isResolved as isResolved7 } from "alchemy/Diff";
1926
1991
  import * as Provider7 from "alchemy/Provider";
1927
1992
  import * as Effect21 from "effect/Effect";
1928
1993
 
@@ -1997,7 +2062,7 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
1997
2062
  });
1998
2063
  var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
1999
2064
  var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
2000
- var handlers3 = {
2065
+ var handlers2 = {
2001
2066
  list: () => Effect21.succeed([]),
2002
2067
  read: Effect21.fn(function* ({ olds }) {
2003
2068
  return yield* readOsd(olds);
@@ -2006,7 +2071,7 @@ var handlers3 = {
2006
2071
  news,
2007
2072
  output
2008
2073
  }) {
2009
- if (output === undefined || !isResolved6(news))
2074
+ if (output === undefined || !isResolved7(news))
2010
2075
  return;
2011
2076
  const live = yield* readOsd(news);
2012
2077
  if (live === undefined)
@@ -2029,10 +2094,10 @@ var handlers3 = {
2029
2094
  yield* destroyOsd(olds);
2030
2095
  })
2031
2096
  };
2032
- var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers3)));
2097
+ var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers2)));
2033
2098
  // src/proxmox/ceph-pool.ts
2034
2099
  import { Resource as Resource8 } from "alchemy";
2035
- import { isResolved as isResolved7 } from "alchemy/Diff";
2100
+ import { isResolved as isResolved8 } from "alchemy/Diff";
2036
2101
  import * as Provider8 from "alchemy/Provider";
2037
2102
  import * as nodes7 from "@distilled.cloud/proxmox/nodes";
2038
2103
  import * as Effect24 from "effect/Effect";
@@ -2066,7 +2131,7 @@ var createBody = (props) => ({
2066
2131
  import * as nodes5 from "@distilled.cloud/proxmox/nodes";
2067
2132
  import * as Effect22 from "effect/Effect";
2068
2133
  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* () {
2134
+ var settle = (props, read, done) => Effect22.gen(function* () {
2070
2135
  for (let attempt = 0;attempt < 30; attempt += 1) {
2071
2136
  const live = yield* read(props);
2072
2137
  if (done(live))
@@ -2123,7 +2188,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
2123
2188
  list: () => Effect24.succeed([]),
2124
2189
  read: ({ olds }) => readPoolStatus(olds),
2125
2190
  diff: Effect24.fn(function* ({ news, output }) {
2126
- if (!isResolved7(news))
2191
+ if (!isResolved8(news))
2127
2192
  return;
2128
2193
  yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
2129
2194
  yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
@@ -2150,7 +2215,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
2150
2215
  upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
2151
2216
  }
2152
2217
  }
2153
- const after = yield* settle2(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
2218
+ const after = yield* settle(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
2154
2219
  if (after === undefined || !matches3(after, news)) {
2155
2220
  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."));
2156
2221
  }
@@ -2158,7 +2223,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
2158
2223
  }),
2159
2224
  delete: Effect24.fn(function* ({ olds }) {
2160
2225
  yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
2161
- const left = yield* settle2(olds, readPoolStatus, (row) => row === undefined);
2226
+ const left = yield* settle(olds, readPoolStatus, (row) => row === undefined);
2162
2227
  if (left !== undefined) {
2163
2228
  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."));
2164
2229
  }
@@ -2166,7 +2231,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
2166
2231
  })));
2167
2232
  // src/proxmox/firewall-alias.ts
2168
2233
  import { Resource as Resource9 } from "alchemy";
2169
- import { isResolved as isResolved8 } from "alchemy/Diff";
2234
+ import { isResolved as isResolved9 } from "alchemy/Diff";
2170
2235
  import * as Provider9 from "alchemy/Provider";
2171
2236
  import * as Effect25 from "effect/Effect";
2172
2237
 
@@ -2188,7 +2253,7 @@ var body = (props) => {
2188
2253
 
2189
2254
  // src/proxmox/firewall-alias.ts
2190
2255
  var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
2191
- var handlers4 = pveHandlers({
2256
+ var handlers3 = pveHandlers({
2192
2257
  attributes: (live, props) => {
2193
2258
  const address = cidr(live["cidr"]);
2194
2259
  return address === "" ? undefined : {
@@ -2209,17 +2274,17 @@ var handlers4 = pveHandlers({
2209
2274
  updateForm: body
2210
2275
  });
2211
2276
  var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
2212
- ...handlers4,
2277
+ ...handlers3,
2213
2278
  diff: Effect25.fn(function* ({ news, output }) {
2214
- if (output !== undefined && isResolved8(news) && fold(news.name) !== fold(output.name)) {
2279
+ if (output !== undefined && isResolved9(news) && fold(news.name) !== fold(output.name)) {
2215
2280
  return { action: "replace" };
2216
2281
  }
2217
- return yield* handlers4.diff({ news, output });
2282
+ return yield* handlers3.diff({ news, output });
2218
2283
  })
2219
2284
  })));
2220
2285
  // src/proxmox/group.ts
2221
2286
  import { Resource as Resource10 } from "alchemy";
2222
- import { isResolved as isResolved9 } from "alchemy/Diff";
2287
+ import { isResolved as isResolved10 } from "alchemy/Diff";
2223
2288
  import * as Provider10 from "alchemy/Provider";
2224
2289
  import * as access4 from "@distilled.cloud/proxmox/access";
2225
2290
  import * as Effect27 from "effect/Effect";
@@ -2265,7 +2330,7 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succee
2265
2330
  return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
2266
2331
  }),
2267
2332
  diff: Effect27.fn(function* ({ news, output }) {
2268
- if (!isResolved9(news))
2333
+ if (!isResolved10(news))
2269
2334
  return;
2270
2335
  yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
2271
2336
  yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
@@ -2313,7 +2378,7 @@ var shape3 = (props) => ({
2313
2378
  max_restart: String(props.max_restart ?? 1),
2314
2379
  state: props.state ?? "started"
2315
2380
  });
2316
- var handlers5 = pveHandlers({
2381
+ var handlers4 = pveHandlers({
2317
2382
  attributes: (live, props) => ({
2318
2383
  comment: typeof live["comment"] === "string" ? live["comment"] : "",
2319
2384
  max_relocate: num(live["max_relocate"], 1),
@@ -2332,7 +2397,7 @@ var handlers5 = pveHandlers({
2332
2397
  path: (props) => `cluster/ha/resources/${props.sid}`,
2333
2398
  updateForm: shape3
2334
2399
  });
2335
- var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers5)));
2400
+ var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers4)));
2336
2401
  // src/proxmox/ha-rule.ts
2337
2402
  import { Resource as Resource12 } from "alchemy";
2338
2403
  import * as Provider12 from "alchemy/Provider";
@@ -2384,7 +2449,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
2384
2449
 
2385
2450
  // src/proxmox/ha-rule.ts
2386
2451
  var ProxmoxHaRule = Resource12("Proxmox.HaRule");
2387
- var handlers6 = pveHandlers({
2452
+ var handlers5 = pveHandlers({
2388
2453
  attributes: (live, props) => {
2389
2454
  const liveType = text(live["type"]);
2390
2455
  if (liveType !== "" && liveType !== props.type)
@@ -2413,10 +2478,10 @@ var handlers6 = pveHandlers({
2413
2478
  return withClears(fields, clear);
2414
2479
  }
2415
2480
  });
2416
- var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers6)));
2481
+ var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers5)));
2417
2482
  // src/proxmox/lxc.ts
2418
2483
  import { Resource as Resource13 } from "alchemy";
2419
- import { isResolved as isResolved10 } from "alchemy/Diff";
2484
+ import { isResolved as isResolved11 } from "alchemy/Diff";
2420
2485
  import * as Provider13 from "alchemy/Provider";
2421
2486
  import * as Effect39 from "effect/Effect";
2422
2487
 
@@ -2669,8 +2734,8 @@ var optionMap = (value, family, defaultKey) => {
2669
2734
  return out;
2670
2735
  };
2671
2736
  var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
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(",");
2737
+ var sortedForm = (map13) => [...map13].map(([key, value]) => `${key}=${value}`).sort().join(",");
2738
+ var printMap = (map13, lead = []) => [...lead.filter((key) => map13.has(key)), ...[...map13.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map13.get(key) ?? ""}`).join(",");
2674
2739
  var netMaps = (declared, live) => {
2675
2740
  const want = optionMap(declared, "net");
2676
2741
  const have = optionMap(live, "net");
@@ -3102,10 +3167,10 @@ var pendingLines = (diff) => {
3102
3167
  var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
3103
3168
  var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
3104
3169
  const all = rows ?? [];
3105
- const cluster = all.find((row) => row.type === "cluster");
3170
+ const cluster3 = all.find((row) => row.type === "cluster");
3106
3171
  return {
3107
3172
  offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
3108
- quorate: cluster === undefined ? true : bool(cluster.quorate)
3173
+ quorate: cluster3 === undefined ? true : bool(cluster3.quorate)
3109
3174
  };
3110
3175
  }));
3111
3176
  var degradedReason = (target) => clusterHealth(target).pipe(Effect36.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
@@ -3221,7 +3286,7 @@ var diff = (owner, news, olds) => Effect39.gen(function* () {
3221
3286
  return yield* refused(identity2);
3222
3287
  if (output === undefined)
3223
3288
  return;
3224
- if (!isResolved10(news))
3289
+ if (!isResolved11(news))
3225
3290
  return;
3226
3291
  const live = yield* readLive(news);
3227
3292
  if (live === undefined) {
@@ -3296,7 +3361,7 @@ var read = (owner, olds) => Effect39.gen(function* () {
3296
3361
  const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
3297
3362
  return yield* ownedRead(owner, attributesOf5(where, live), settled2);
3298
3363
  });
3299
- var handlers7 = {
3364
+ var handlers6 = {
3300
3365
  delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
3301
3366
  diff: ({
3302
3367
  fqn,
@@ -3319,11 +3384,11 @@ var handlers7 = {
3319
3384
  output
3320
3385
  }) => reconcile({ fqn, instanceId, output }, news)
3321
3386
  };
3322
- var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers7)).pipe(Effect39.map(provingResumes)));
3387
+ var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers6)).pipe(Effect39.map(provingResumes)));
3323
3388
  // src/proxmox/metric-server.ts
3324
3389
  import { Resource as Resource14 } from "alchemy";
3325
- import * as Provider14 from "alchemy/Provider";
3326
- import * as Effect40 from "effect/Effect";
3390
+ import { isResolved as isResolved12 } from "alchemy/Diff";
3391
+ import * as cluster4 from "@distilled.cloud/proxmox/cluster";
3327
3392
 
3328
3393
  // src/proxmox/metric-server-form.ts
3329
3394
  var UNSET2 = -1;
@@ -3393,8 +3458,7 @@ var otelAttributes = (live) => ({
3393
3458
  });
3394
3459
  var otelMatches = (attributes, props) => attributes["otel-compression"] === (props["otel-compression"] ?? "") && attributes["otel-max-body-size"] === (props["otel-max-body-size"] ?? UNSET2) && attributes["otel-path"] === (props["otel-path"] ?? "") && attributes["otel-protocol"] === (props["otel-protocol"] ?? "") && attributes["otel-timeout"] === (props["otel-timeout"] ?? UNSET2) && attributes["otel-verify-ssl"] === (props["otel-verify-ssl"] !== false);
3395
3460
 
3396
- // src/proxmox/metric-server.ts
3397
- var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
3461
+ // src/proxmox/metric-server-config.ts
3398
3462
  var matchesType = (attributes, props) => {
3399
3463
  if (props.type === "opentelemetry")
3400
3464
  return otelMatches(attributes, props);
@@ -3403,7 +3467,7 @@ var matchesType = (attributes, props) => {
3403
3467
  return socket && attributes.path === (props.path ?? "");
3404
3468
  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);
3405
3469
  };
3406
- var handlers8 = pveHandlers({
3470
+ var metricServerSpec = {
3407
3471
  attributes: (live, props) => {
3408
3472
  const liveType = text(live["type"]);
3409
3473
  if (liveType !== "" && liveType !== props.type)
@@ -3435,13 +3499,96 @@ var handlers8 = pveHandlers({
3435
3499
  matches: (attributes, props) => attributes.server === props.server && attributes.port === props.port && attributes.disable === (props.disable === true) && matchesType(attributes, props),
3436
3500
  path: (props) => `cluster/metrics/server/${props.id}`,
3437
3501
  updateForm: update
3502
+ };
3503
+
3504
+ // src/proxmox/metric-server-distilled.ts
3505
+ import * as cluster3 from "@distilled.cloud/proxmox/cluster";
3506
+ import { ProxmoxParseError as ProxmoxParseError2 } from "@distilled.cloud/proxmox/Errors";
3507
+ import * as Effect40 from "effect/Effect";
3508
+ import * as Schema2 from "effect/Schema";
3509
+ var readMetricServerRow = (props) => runPve(props.target, "read", false, cluster3.getClusterMetricsServer({ id: props.id })).pipe(Effect40.flatMap((body4) => Schema2.decodeUnknownEffect(Schema2.Record(Schema2.String, Schema2.Unknown))(body4).pipe(Effect40.mapError(() => new ProxmoxParseError2({
3510
+ body: undefined,
3511
+ cause: "Metric server response must be an object"
3512
+ })))), Effect40.catchTag("MetricServerNotFound", () => Effect40.succeed(undefined)), Effect40.catchTag("NotFound", () => Effect40.succeed(undefined)));
3513
+ var readMetricServer = (props) => readMetricServerRow(props).pipe(Effect40.map((row) => row === undefined ? undefined : metricServerSpec.attributes(row, props)));
3514
+ var metricServerRequest = (props, form) => ({
3515
+ ...asForm2({
3516
+ api_path_prefix: form["api-path-prefix"],
3517
+ bucket: form["bucket"],
3518
+ delete: form["delete"],
3519
+ disable: form["disable"],
3520
+ influxdbproto: form["influxdbproto"],
3521
+ max_body_size: form["max-body-size"],
3522
+ mtu: form["mtu"],
3523
+ organization: form["organization"],
3524
+ otel_compression: form["otel-compression"],
3525
+ otel_max_body_size: form["otel-max-body-size"],
3526
+ otel_path: form["otel-path"],
3527
+ otel_protocol: form["otel-protocol"],
3528
+ otel_timeout: form["otel-timeout"],
3529
+ otel_verify_ssl: form["otel-verify-ssl"],
3530
+ path: form["path"],
3531
+ timeout: form["timeout"],
3532
+ verify_certificate: form["verify-certificate"]
3533
+ }),
3534
+ id: props.id,
3535
+ port: String(props.port),
3536
+ server: props.server
3438
3537
  });
3439
- var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect40.succeed(ProxmoxMetricServer.Provider.of(handlers8)));
3538
+ var deleteMetricServer = (props) => Effect40.gen(function* () {
3539
+ if ((yield* readMetricServerRow(props)) === undefined)
3540
+ return;
3541
+ yield* runPve(props.target, "provision", true, cluster3.deleteClusterMetricsServer({ id: props.id }));
3542
+ });
3543
+
3544
+ // src/proxmox/metric-server.ts
3545
+ import * as Provider14 from "alchemy/Provider";
3546
+ import * as Effect41 from "effect/Effect";
3547
+ var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
3548
+ var { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(metricServerSpec);
3549
+ var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect41.succeed(ProxmoxMetricServer.Provider.of({
3550
+ list: () => Effect41.succeed([]),
3551
+ read: ({ olds }) => readMetricServer(olds),
3552
+ diff: Effect41.fn(function* ({ news, output }) {
3553
+ if (!isResolved12(news))
3554
+ return;
3555
+ yield* guardCreate2(news, output === undefined);
3556
+ yield* guardUpdate2(news);
3557
+ if (output === undefined)
3558
+ return;
3559
+ const live = yield* readMetricServer(news);
3560
+ if (live === undefined) {
3561
+ yield* guardCreate2(news, true);
3562
+ return { action: "update" };
3563
+ }
3564
+ return { action: metricServerSpec.matches(live, news) ? "noop" : "update" };
3565
+ }),
3566
+ reconcile: Effect41.fn(function* ({ news }) {
3567
+ const live = yield* readMetricServer(news);
3568
+ yield* guardCreate2(news, live === undefined);
3569
+ yield* guardUpdate2(news);
3570
+ if (live === undefined) {
3571
+ yield* runPve(news.target, "provision", true, cluster4.updateClusterMetricsServer({
3572
+ ...metricServerRequest(news, metricServerSpec.createForm(news)),
3573
+ type: news.type
3574
+ }));
3575
+ } else {
3576
+ const form = formToSend(metricServerSpec.matches, live, news, metricServerSpec.updateForm(news));
3577
+ if (form !== undefined)
3578
+ yield* runPve(news.target, "provision", true, cluster4.putClusterMetricsServer(metricServerRequest(news, form)));
3579
+ }
3580
+ const after = yield* readMetricServer(news);
3581
+ if (after === undefined)
3582
+ return yield* Effect41.fail(new Error(`${metricServerSpec.path(news)}: write returned success but the resource is still absent`));
3583
+ return after;
3584
+ }),
3585
+ delete: ({ olds }) => deleteMetricServer(olds)
3586
+ })));
3440
3587
  // src/proxmox/network-apply.ts
3441
3588
  import { Resource as Resource15 } from "alchemy";
3442
- import { isResolved as isResolved11 } from "alchemy/Diff";
3589
+ import { isResolved as isResolved13 } from "alchemy/Diff";
3443
3590
  import * as Provider15 from "alchemy/Provider";
3444
- import * as Effect41 from "effect/Effect";
3591
+ import * as Effect42 from "effect/Effect";
3445
3592
 
3446
3593
  // src/proxmox/apply-endpoints.ts
3447
3594
  var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
@@ -3451,15 +3598,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
3451
3598
 
3452
3599
  // src/proxmox/network-apply.ts
3453
3600
  var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
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))
3601
+ var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect42.map((pending) => ({ node: props.node, pending })));
3602
+ var diff2 = (news) => Effect42.gen(function* () {
3603
+ if (!isResolved13(news))
3457
3604
  return;
3458
3605
  const { pending } = yield* read2(news);
3459
3606
  return pending === 0 ? { action: "noop" } : { action: "update" };
3460
3607
  });
3461
- var refuse = (node, why) => Effect41.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3462
- var apply = (props) => Effect41.gen(function* () {
3608
+ var refuse = (node, why) => Effect42.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3609
+ var apply = (props) => Effect42.gen(function* () {
3463
3610
  const node = props.node;
3464
3611
  const staged = yield* pendingCount(props.target, node);
3465
3612
  if (staged === 0)
@@ -3488,20 +3635,20 @@ var apply = (props) => Effect41.gen(function* () {
3488
3635
  }
3489
3636
  return { node, pending: left };
3490
3637
  });
3491
- var handlers9 = {
3492
- delete: () => Effect41.void,
3638
+ var handlers7 = {
3639
+ delete: () => Effect42.void,
3493
3640
  diff: ({ news }) => diff2(news),
3494
- list: () => Effect41.succeed([]),
3641
+ list: () => Effect42.succeed([]),
3495
3642
  read: ({ olds }) => read2(olds),
3496
3643
  reconcile: ({ news }) => apply(news)
3497
3644
  };
3498
- var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect41.succeed(ProxmoxNetworkApply.Provider.of(handlers9)));
3645
+ var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect42.succeed(ProxmoxNetworkApply.Provider.of(handlers7)));
3499
3646
  // src/proxmox/node-network.ts
3500
3647
  import { Resource as Resource16 } from "alchemy";
3501
- import { isResolved as isResolved12 } from "alchemy/Diff";
3648
+ import { isResolved as isResolved14 } from "alchemy/Diff";
3502
3649
  import * as Provider16 from "alchemy/Provider";
3503
3650
  import * as nodes9 from "@distilled.cloud/proxmox/nodes";
3504
- import * as Effect43 from "effect/Effect";
3651
+ import * as Effect44 from "effect/Effect";
3505
3652
 
3506
3653
  // src/proxmox/node-network-form.ts
3507
3654
  var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
@@ -3544,7 +3691,7 @@ var updateForm2 = (props) => {
3544
3691
 
3545
3692
  // src/proxmox/node-network-wire.ts
3546
3693
  import * as nodes8 from "@distilled.cloud/proxmox/nodes";
3547
- import * as Effect42 from "effect/Effect";
3694
+ import * as Effect43 from "effect/Effect";
3548
3695
  var attributesOf6 = (live, props) => {
3549
3696
  const data = live;
3550
3697
  const liveType = text(data["type"]);
@@ -3578,12 +3725,12 @@ var attributesOf6 = (live, props) => {
3578
3725
  "vlan-raw-device": text(data["vlan-raw-device"])
3579
3726
  };
3580
3727
  };
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) => {
3728
+ var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect43.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect43.succeed(undefined) : Effect43.fail(error)))).pipe(Effect43.map((live) => {
3582
3729
  if (live === UNREADABLE)
3583
3730
  return UNREADABLE;
3584
3731
  return live === undefined ? undefined : attributesOf6(live, props);
3585
3732
  }));
3586
- var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect42.orElseSucceed(() => {
3733
+ var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect43.orElseSucceed(() => {
3587
3734
  return;
3588
3735
  }));
3589
3736
  var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
@@ -3608,13 +3755,13 @@ var toDistilledUpdate2 = (props) => ({
3608
3755
  iface: props.iface,
3609
3756
  node: props.node
3610
3757
  });
3611
- var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect43.succeed(ProxmoxNodeNetwork.Provider.of({
3612
- list: () => Effect43.succeed([]),
3613
- read: Effect43.fn(function* ({ olds, output }) {
3758
+ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect44.succeed(ProxmoxNodeNetwork.Provider.of({
3759
+ list: () => Effect44.succeed([]),
3760
+ read: Effect44.fn(function* ({ olds, output }) {
3614
3761
  return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
3615
3762
  }),
3616
- diff: Effect43.fn(function* ({ news, output }) {
3617
- if (!isResolved12(news))
3763
+ diff: Effect44.fn(function* ({ news, output }) {
3764
+ if (!isResolved14(news))
3618
3765
  return;
3619
3766
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
3620
3767
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
@@ -3631,7 +3778,7 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
3631
3778
  }
3632
3779
  return matches5(live, news) ? { action: "noop" } : { action: "update" };
3633
3780
  }),
3634
- reconcile: Effect43.fn(function* ({ news }) {
3781
+ reconcile: Effect44.fn(function* ({ news }) {
3635
3782
  const before = dropUnreadable4(yield* readInterface(news));
3636
3783
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
3637
3784
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
@@ -3642,18 +3789,22 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
3642
3789
  }
3643
3790
  const after = dropUnreadable4(yield* readInterface(news));
3644
3791
  if (after === undefined) {
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."));
3792
+ return yield* Effect44.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."));
3646
3793
  }
3647
3794
  return after;
3648
3795
  }),
3649
- delete: Effect43.fn(function* ({ olds }) {
3796
+ delete: Effect44.fn(function* ({ olds }) {
3650
3797
  yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
3651
3798
  })
3652
3799
  })));
3653
3800
  // src/proxmox/notification-matcher.ts
3654
3801
  import { Resource as Resource17 } from "alchemy";
3655
3802
  import * as Provider17 from "alchemy/Provider";
3656
- import * as Effect44 from "effect/Effect";
3803
+ import * as Effect48 from "effect/Effect";
3804
+
3805
+ // src/proxmox/notification-matcher-lifecycle.ts
3806
+ import { isResolved as isResolved15 } from "alchemy/Diff";
3807
+ import * as Effect47 from "effect/Effect";
3657
3808
 
3658
3809
  // src/proxmox/notification-matcher-form.ts
3659
3810
  var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
@@ -3729,25 +3880,99 @@ var matcherUpdateForm = (fields) => {
3729
3880
  };
3730
3881
  };
3731
3882
 
3883
+ // src/proxmox/notification-matcher-distilled.ts
3884
+ import * as cluster5 from "@distilled.cloud/proxmox/cluster";
3885
+ import * as Effect46 from "effect/Effect";
3886
+
3887
+ // src/proxmox/notification-sdk-input.ts
3888
+ import { ProxmoxParseError as ProxmoxParseError3 } from "@distilled.cloud/proxmox/Errors";
3889
+ import * as Data2 from "effect/Data";
3890
+ import * as Effect45 from "effect/Effect";
3891
+ import * as Schema3 from "effect/Schema";
3892
+
3893
+ class NotificationRequestInvalid extends Data2.TaggedError("NotificationRequestInvalid") {
3894
+ }
3895
+ var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(Effect45.mapError(() => new NotificationRequestInvalid({ operation: "PVE notification write" })));
3896
+ var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(Effect45.as(body5), Effect45.mapError(() => new ProxmoxParseError3({
3897
+ body: undefined,
3898
+ cause: "PVE notification response does not match its SDK schema"
3899
+ })));
3900
+
3901
+ // src/proxmox/notification-matcher-distilled.ts
3902
+ var readMatcher = (props) => runPve(props.target, "read", false, cluster5.getClusterNotificationMatcher({ name: props.name })).pipe(Effect46.flatMap(notificationResponse(cluster5.GetClusterNotificationMatcherResponse)), Effect46.map((live) => matcherAttributes({
3903
+ ...live,
3904
+ "invert-match": live.invert_match,
3905
+ "match-calendar": live.match_calendar,
3906
+ "match-field": live.match_field,
3907
+ "match-severity": live.match_severity
3908
+ }, props.name)), Effect46.catchTag("NotFound", () => Effect46.succeed(undefined)));
3909
+ var createMatcher = (props) => Effect46.gen(function* () {
3910
+ const input = yield* notificationRequest(cluster5.CreateClusterNotificationMatcherRequest, matcherCreateForm(props));
3911
+ yield* runPve(props.target, "provision", true, cluster5.createClusterNotificationMatcher(input));
3912
+ });
3913
+ var updateMatcher = (props) => Effect46.gen(function* () {
3914
+ const input = yield* notificationRequest(cluster5.PutClusterNotificationMatcherRequest, {
3915
+ ...matcherUpdateForm(props),
3916
+ name: props.name
3917
+ });
3918
+ yield* runPve(props.target, "provision", true, cluster5.putClusterNotificationMatcher(input));
3919
+ });
3920
+ var deleteMatcher = (props) => runPve(props.target, "provision", true, cluster5.deleteClusterNotificationMatcher({ name: props.name })).pipe(Effect46.catchTag("NotFound", () => Effect46.void));
3921
+
3922
+ // src/proxmox/notification-matcher-lifecycle.ts
3923
+ var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(Effect47.andThen(guardForm("pve:PUT /cluster/notifications/matchers/{name}", matcherUpdateForm(props), false)));
3924
+ var matcherHandlers = {
3925
+ list: () => Effect47.succeed([]),
3926
+ read: ({ olds }) => Effect47.gen(function* () {
3927
+ const live = yield* readMatcher(olds);
3928
+ yield* guard(olds, live === undefined);
3929
+ return live;
3930
+ }),
3931
+ diff: ({
3932
+ news,
3933
+ olds,
3934
+ output
3935
+ }) => Effect47.gen(function* () {
3936
+ if (!isResolved15(news))
3937
+ return;
3938
+ if (olds.name !== news.name)
3939
+ return { action: "replace" };
3940
+ yield* guard(news, output === undefined);
3941
+ if (output === undefined)
3942
+ return;
3943
+ const live = yield* readMatcher(news);
3944
+ if (live === undefined) {
3945
+ yield* guard(news, true);
3946
+ return { action: "update" };
3947
+ }
3948
+ return { action: matcherMatches(live, news) ? "noop" : "update" };
3949
+ }),
3950
+ reconcile: ({ news }) => Effect47.gen(function* () {
3951
+ const live = yield* readMatcher(news);
3952
+ yield* guard(news, live === undefined);
3953
+ if (live === undefined)
3954
+ yield* createMatcher(news);
3955
+ else if (!matcherMatches(live, news))
3956
+ yield* updateMatcher(news);
3957
+ const after = yield* readMatcher(news);
3958
+ if (after === undefined)
3959
+ return yield* Effect47.die(new Error(`Notification matcher ${news.name}: write returned but read-back is absent.`));
3960
+ return after;
3961
+ }),
3962
+ delete: ({ olds }) => deleteMatcher(olds)
3963
+ };
3964
+
3732
3965
  // src/proxmox/notification-matcher.ts
3733
3966
  var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
3734
- var handlers10 = pveHandlers({
3735
- attributes: (live, props) => matcherAttributes(live, props.name),
3736
- collection: () => "cluster/notifications/matchers",
3737
- createForm: matcherCreateForm,
3738
- endpoint: {
3739
- create: "pve:POST /cluster/notifications/matchers",
3740
- update: "pve:PUT /cluster/notifications/matchers/{name}"
3741
- },
3742
- matches: matcherMatches,
3743
- path: (props) => `cluster/notifications/matchers/${props.name}`,
3744
- updateForm: matcherUpdateForm
3745
- });
3746
- var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect44.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
3967
+ var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect48.succeed(ProxmoxNotificationMatcher.Provider.of(matcherHandlers)));
3747
3968
  // src/proxmox/notification-target.ts
3748
3969
  import { Resource as Resource18 } from "alchemy";
3749
3970
  import * as Provider18 from "alchemy/Provider";
3750
- import * as Effect45 from "effect/Effect";
3971
+ import * as Effect51 from "effect/Effect";
3972
+
3973
+ // src/proxmox/notification-target-lifecycle.ts
3974
+ import { isResolved as isResolved16 } from "alchemy/Diff";
3975
+ import * as Effect50 from "effect/Effect";
3751
3976
 
3752
3977
  // src/proxmox/notification-target-endpoint.ts
3753
3978
  var ENDPOINTS = {
@@ -3801,9 +4026,8 @@ var createShape = (props) => ({
3801
4026
  name: props.name
3802
4027
  });
3803
4028
 
3804
- // src/proxmox/notification-target.ts
3805
- var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
3806
- var handlers11 = pveHandlers({
4029
+ // src/proxmox/notification-target-spec.ts
4030
+ var targetSpec = {
3807
4031
  attributes: (live, props) => ({
3808
4032
  author: text(live["author"]),
3809
4033
  body: text(live["body"]),
@@ -3828,16 +4052,155 @@ var handlers11 = pveHandlers({
3828
4052
  matches: (attributes, props) => attributes.disable === (props.disable === true) && (props.comment === undefined || attributes.comment === props.comment) && (props.server === undefined || attributes.server === props.server) && (props.port === undefined || attributes.port === props.port) && (props.mode === undefined || attributes.mode === props.mode) && (props.username === undefined || attributes.username === props.username) && (props["from-address"] === undefined || attributes["from-address"] === props["from-address"]) && (props.author === undefined || attributes.author === props.author) && (props.url === undefined || attributes.url === props.url) && (props.method === undefined || attributes.method === props.method) && (props.body === undefined || attributes.body === props.body) && (props.mailto === undefined || attributes.mailto === addressList(props.mailto)) && (props["mailto-user"] === undefined || attributes["mailto-user"] === addressList(props["mailto-user"])) && (props.header === undefined || attributes.header === headerList(props.header)),
3829
4053
  path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
3830
4054
  updateForm: shape4
3831
- });
3832
- var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect45.succeed(ProxmoxNotificationTarget.Provider.of(handlers11)));
4055
+ };
4056
+
4057
+ // src/proxmox/notification-target-distilled.ts
4058
+ import * as cluster6 from "@distilled.cloud/proxmox/cluster";
4059
+ import * as Effect49 from "effect/Effect";
4060
+ var read3 = (props) => {
4061
+ const request = { name: props.name };
4062
+ switch (props.type) {
4063
+ case "gotify":
4064
+ return cluster6.getClusterNotificationEndpointGotify(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointGotifyResponse)), Effect49.map((row) => ({ ...row })));
4065
+ case "sendmail":
4066
+ return cluster6.getClusterNotificationEndpointSendmail(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointSendmailResponse)), Effect49.map((row) => ({
4067
+ ...row,
4068
+ "from-address": row.from_address,
4069
+ "mailto-user": row.mailto_user
4070
+ })));
4071
+ case "smtp":
4072
+ return cluster6.getClusterNotificationEndpointSmtp(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointSmtpResponse)), Effect49.map((row) => ({
4073
+ ...row,
4074
+ "from-address": row.from_address,
4075
+ "mailto-user": row.mailto_user
4076
+ })));
4077
+ case "webhook":
4078
+ return cluster6.getClusterNotificationEndpointWebhook(request).pipe(Effect49.flatMap(notificationResponse(cluster6.GetClusterNotificationEndpointWebhookResponse)), Effect49.map((row) => ({ ...row })));
4079
+ }
4080
+ };
4081
+ var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(Effect49.map((row) => targetSpec.attributes(row, props)), Effect49.catchTag("NotFound", () => Effect49.succeed(undefined)));
4082
+ var fields = (props, create) => {
4083
+ const form = {
4084
+ ...create ? createShape(props) : shape4(props),
4085
+ name: props.name
4086
+ };
4087
+ const cleared = [];
4088
+ for (const [key, values] of [
4089
+ ["header", props.header === undefined ? undefined : [...props.header]],
4090
+ [
4091
+ "mailto",
4092
+ props.mailto === undefined ? undefined : addressList(props.mailto).split(",").filter(Boolean)
4093
+ ],
4094
+ [
4095
+ "mailto-user",
4096
+ props["mailto-user"] === undefined ? undefined : addressList(props["mailto-user"]).split(",").filter(Boolean)
4097
+ ]
4098
+ ]) {
4099
+ if (values === undefined)
4100
+ continue;
4101
+ if (values.length > 0)
4102
+ form[key] = values;
4103
+ else {
4104
+ delete form[key];
4105
+ if (!create)
4106
+ cleared.push(key);
4107
+ }
4108
+ }
4109
+ if (cleared.length > 0)
4110
+ form.delete = cleared;
4111
+ return form;
4112
+ };
4113
+ var validateTargetWrite = (props, create) => {
4114
+ const schema = create ? {
4115
+ sendmail: cluster6.CreateClusterNotificationEndpointSendmailRequest,
4116
+ smtp: cluster6.CreateClusterNotificationEndpointSmtpRequest,
4117
+ gotify: cluster6.CreateClusterNotificationEndpointGotifyRequest,
4118
+ webhook: cluster6.CreateClusterNotificationEndpointWebhookRequest
4119
+ }[props.type] : {
4120
+ sendmail: cluster6.PutClusterNotificationEndpointSendmailRequest,
4121
+ smtp: cluster6.PutClusterNotificationEndpointSmtpRequest,
4122
+ gotify: cluster6.PutClusterNotificationEndpointGotifyRequest,
4123
+ webhook: cluster6.PutClusterNotificationEndpointWebhookRequest
4124
+ }[props.type];
4125
+ return notificationRequest(schema, fields(props, create)).pipe(Effect49.asVoid);
4126
+ };
4127
+ var createTarget = (props) => {
4128
+ const form = fields(props, true);
4129
+ const operation = props.type === "sendmail" ? notificationRequest(cluster6.CreateClusterNotificationEndpointSendmailRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster6.CreateClusterNotificationEndpointSmtpRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster6.CreateClusterNotificationEndpointGotifyRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointGotify)) : notificationRequest(cluster6.CreateClusterNotificationEndpointWebhookRequest, form).pipe(Effect49.flatMap(cluster6.createClusterNotificationEndpointWebhook));
4130
+ return runPve(props.target, "provision", true, operation);
4131
+ };
4132
+ var updateTarget = (props) => {
4133
+ const form = fields(props, false);
4134
+ const operation = props.type === "sendmail" ? notificationRequest(cluster6.PutClusterNotificationEndpointSendmailRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster6.PutClusterNotificationEndpointSmtpRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster6.PutClusterNotificationEndpointGotifyRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointGotify)) : notificationRequest(cluster6.PutClusterNotificationEndpointWebhookRequest, form).pipe(Effect49.flatMap(cluster6.putClusterNotificationEndpointWebhook));
4135
+ return runPve(props.target, "provision", true, operation);
4136
+ };
4137
+ var deleteTarget = (props) => {
4138
+ const request = { name: props.name };
4139
+ const operation = props.type === "sendmail" ? cluster6.deleteClusterNotificationEndpointSendmail(request) : props.type === "smtp" ? cluster6.deleteClusterNotificationEndpointSmtp(request) : props.type === "gotify" ? cluster6.deleteClusterNotificationEndpointGotify(request) : cluster6.deleteClusterNotificationEndpointWebhook(request);
4140
+ return runPve(props.target, "provision", true, operation).pipe(Effect49.catchTag("NotFound", () => Effect49.void));
4141
+ };
4142
+
4143
+ // src/proxmox/notification-target-lifecycle.ts
4144
+ var { guardCreate: guardCreate3, guardUpdate: guardUpdate3 } = specGuards(targetSpec);
4145
+ var guard2 = (props, create) => guardCreate3(props, create).pipe(Effect50.andThen(guardUpdate3(props)));
4146
+ var targetHandlers = {
4147
+ list: () => Effect50.succeed([]),
4148
+ read: ({ olds }) => Effect50.gen(function* () {
4149
+ const live = yield* readTarget(olds);
4150
+ yield* guard2(olds, live === undefined);
4151
+ return live;
4152
+ }),
4153
+ diff: ({
4154
+ news,
4155
+ olds,
4156
+ output
4157
+ }) => Effect50.gen(function* () {
4158
+ if (!isResolved16(news))
4159
+ return;
4160
+ if (olds.name !== news.name || olds.type !== news.type) {
4161
+ const destination = yield* readTarget(news);
4162
+ yield* guard2(news, destination === undefined);
4163
+ yield* validateTargetWrite(news, destination === undefined);
4164
+ return { action: "replace", deleteFirst: olds.name === news.name };
4165
+ }
4166
+ yield* guard2(news, output === undefined);
4167
+ if (output === undefined)
4168
+ return;
4169
+ const live = yield* readTarget(news);
4170
+ if (live === undefined) {
4171
+ yield* guard2(news, true);
4172
+ return { action: "update" };
4173
+ }
4174
+ return { action: targetSpec.matches(live, news) ? "noop" : "update" };
4175
+ }),
4176
+ reconcile: ({ news }) => Effect50.gen(function* () {
4177
+ const live = yield* readTarget(news);
4178
+ yield* guard2(news, live === undefined);
4179
+ if (live === undefined)
4180
+ yield* createTarget(news);
4181
+ else if (!targetSpec.matches(live, news))
4182
+ yield* updateTarget(news);
4183
+ const after = yield* readTarget(news);
4184
+ if (after === undefined)
4185
+ return yield* Effect50.die(new Error(`Notification target ${news.name}: write returned but read-back is absent.`));
4186
+ return after;
4187
+ }),
4188
+ delete: ({ olds }) => deleteTarget(olds)
4189
+ };
4190
+
4191
+ // src/proxmox/notification-target.ts
4192
+ var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
4193
+ var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect51.succeed(ProxmoxNotificationTarget.Provider.of(targetHandlers)));
3833
4194
  // src/proxmox/pbs-datastore.ts
3834
4195
  import { Resource as Resource19 } from "alchemy";
3835
- import { isResolved as isResolved13 } from "alchemy/Diff";
3836
4196
  import * as Provider19 from "alchemy/Provider";
3837
- import * as Effect49 from "effect/Effect";
4197
+ import * as Effect57 from "effect/Effect";
4198
+
4199
+ // src/proxmox/pbs-datastore-lifecycle.ts
4200
+ import { isResolved as isResolved17 } from "alchemy/Diff";
4201
+ import * as Effect56 from "effect/Effect";
3838
4202
 
3839
4203
  // src/proxmox/pbs-datastore-form.ts
3840
- import * as Effect46 from "effect/Effect";
3841
4204
  var attributes = (live, props) => ({
3842
4205
  backend: propertyString(live["backend"]),
3843
4206
  comment: text(live["comment"]),
@@ -3889,48 +4252,117 @@ var createForm6 = (props) => ({
3889
4252
  });
3890
4253
  var updateForm3 = (props) => mutable(props);
3891
4254
  var object2 = (props) => `config/datastore/${props.name}`;
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(() => {
3893
- return;
3894
- }));
4255
+
4256
+ // src/proxmox/pbs-datastore-wire.ts
4257
+ import * as config from "@distilled.cloud/proxmox-backup/config";
4258
+ import * as Effect53 from "effect/Effect";
4259
+
4260
+ // src/proxmox/distilled-pbs.ts
4261
+ import { credentials } from "@distilled.cloud/proxmox-backup/Credentials";
4262
+ import * as Retry from "@distilled.cloud/proxmox-backup/Retry";
4263
+ import * as Effect52 from "effect/Effect";
4264
+ var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none, Effect52.provide(credentials({
4265
+ baseUrl: target.api,
4266
+ secret: credential.secret,
4267
+ tokenId: credential.tokenId
4268
+ })), Effect52.timeout(timeout5));
4269
+ var runPbs = (target, role, op) => Effect52.gen(function* () {
4270
+ const credential = yield* leased(target, role);
4271
+ return yield* runPbsWith(target, credential, op);
4272
+ });
4273
+
4274
+ // src/proxmox/pbs-datastore-wire.ts
4275
+ var members = (form) => ({
4276
+ ...form["comment"] === undefined ? {} : { comment: form["comment"] },
4277
+ ...form["counter-reset-schedule"] === undefined ? {} : { counter_reset_schedule: form["counter-reset-schedule"] },
4278
+ ...form["gc-schedule"] === undefined ? {} : { gc_schedule: form["gc-schedule"] },
4279
+ ...form["keep-daily"] === undefined ? {} : { keep_daily: form["keep-daily"] },
4280
+ ...form["keep-hourly"] === undefined ? {} : { keep_hourly: form["keep-hourly"] },
4281
+ ...form["keep-last"] === undefined ? {} : { keep_last: form["keep-last"] },
4282
+ ...form["keep-monthly"] === undefined ? {} : { keep_monthly: form["keep-monthly"] },
4283
+ ...form["keep-weekly"] === undefined ? {} : { keep_weekly: form["keep-weekly"] },
4284
+ ...form["keep-yearly"] === undefined ? {} : { keep_yearly: form["keep-yearly"] },
4285
+ ...form["maintenance-mode"] === undefined ? {} : { maintenance_mode: form["maintenance-mode"] },
4286
+ ...form["notification-thresholds"] === undefined ? {} : { notification_thresholds: form["notification-thresholds"] },
4287
+ ...form["notify"] === undefined ? {} : { notify: form["notify"] },
4288
+ ...form["notify-user"] === undefined ? {} : { notify_user: form["notify-user"] },
4289
+ ...form["prune-schedule"] === undefined ? {} : { prune_schedule: form["prune-schedule"] },
4290
+ ...form["tuning"] === undefined ? {} : { tuning: form["tuning"] },
4291
+ ...form["verify-new"] === undefined ? {} : { verify_new: form["verify-new"] }
4292
+ });
4293
+ var createRequest = (props) => ({
4294
+ ...members(createForm6(props)),
4295
+ name: props.name,
4296
+ path: props.path,
4297
+ ...props.backend === undefined ? {} : { backend: props.backend }
4298
+ });
4299
+ var updateRequest = (props) => ({
4300
+ ...members(updateForm3(props)),
4301
+ name: props.name
4302
+ });
4303
+ var wire = (row) => ({
4304
+ ...row,
4305
+ "counter-reset-schedule": row.counter_reset_schedule,
4306
+ "gc-schedule": row.gc_schedule,
4307
+ "keep-daily": row.keep_daily,
4308
+ "keep-hourly": row.keep_hourly,
4309
+ "keep-last": row.keep_last,
4310
+ "keep-monthly": row.keep_monthly,
4311
+ "keep-weekly": row.keep_weekly,
4312
+ "keep-yearly": row.keep_yearly,
4313
+ "maintenance-mode": row.maintenance_mode,
4314
+ "notification-thresholds": row.notification_thresholds,
4315
+ "notify-user": row.notify_user,
4316
+ "prune-schedule": row.prune_schedule,
4317
+ "verify-new": row.verify_new
4318
+ });
4319
+ var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(Effect53.map((live) => attributes(wire(live), props)), Effect53.catchTag("DatastoreNotFound", () => Effect53.succeed(undefined)));
4320
+ var createOne = (props) => runPbs(props.target, "provision", config.createConfigDatastore(createRequest(props)));
4321
+ var updateOne = (props) => runPbs(props.target, "provision", config.putConfigDatastore(updateRequest(props)));
4322
+ var deleteOne = (props) => Effect53.gen(function* () {
4323
+ if ((yield* readOne(props)) === undefined)
4324
+ return;
4325
+ return yield* runPbs(props.target, "provision", config.deleteConfigDatastore({ name: props.name }));
4326
+ });
3895
4327
 
3896
4328
  // src/proxmox/pbs-datastore-guard.ts
3897
- import * as Effect47 from "effect/Effect";
4329
+ import * as Effect54 from "effect/Effect";
3898
4330
  var samePath = (left, right) => {
3899
4331
  const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
3900
4332
  return trim(left) === trim(right);
3901
4333
  };
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* () {
4334
+ var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect54.void : Effect54.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."));
4335
+ var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect54.void : Effect54.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."));
4336
+ var settle2 = (read4, done) => Effect54.gen(function* () {
3905
4337
  for (let attempt = 0;attempt < 60; attempt += 1) {
3906
- const live = yield* read3;
4338
+ const live = yield* read4;
3907
4339
  if (done(live))
3908
4340
  return live;
3909
- yield* Effect47.sleep("2 seconds");
4341
+ yield* Effect54.sleep("2 seconds");
3910
4342
  }
3911
- return yield* read3;
4343
+ return yield* read4;
3912
4344
  });
3913
4345
 
3914
4346
  // src/proxmox/pbs-datastore-endpoint.ts
3915
- import * as Effect48 from "effect/Effect";
4347
+ import * as Effect55 from "effect/Effect";
3916
4348
  var DATASTORE_ENDPOINT = {
3917
4349
  create: "pbs:POST /config/datastore",
3918
4350
  update: "pbs:PUT /config/datastore/{name}"
3919
4351
  };
3920
- var guardDatastoreForms = (props) => Effect48.gen(function* () {
4352
+ var guardDatastoreForms = (props) => Effect55.gen(function* () {
3921
4353
  yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
3922
4354
  yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
3923
4355
  });
3924
4356
 
3925
- // src/proxmox/pbs-datastore.ts
3926
- var PbsDatastore = Resource19("Pbs.Datastore", {
3927
- defaultRemovalPolicy: "retain"
3928
- });
3929
- var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succeed(PbsDatastore.Provider.of({
3930
- list: () => Effect49.succeed([]),
4357
+ // src/proxmox/pbs-datastore-lifecycle.ts
4358
+ var handlers8 = {
4359
+ list: () => Effect56.succeed([]),
3931
4360
  read: ({ olds }) => readOne(olds),
3932
- diff: ({ news, output }) => Effect49.gen(function* () {
3933
- if (!isResolved13(news))
4361
+ diff: ({
4362
+ news,
4363
+ output
4364
+ }) => Effect56.gen(function* () {
4365
+ if (!isResolved17(news))
3934
4366
  return;
3935
4367
  yield* guardDatastoreForms(news);
3936
4368
  if (output === undefined)
@@ -3942,106 +4374,135 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect49.succee
3942
4374
  yield* guardBackend(live, news);
3943
4375
  return matches6(live, news) ? { action: "noop" } : { action: "update" };
3944
4376
  }),
3945
- reconcile: Effect49.fn(function* ({ news }) {
4377
+ reconcile: Effect56.fn(function* ({ news }) {
3946
4378
  yield* guardDatastoreForms(news);
3947
4379
  const live = yield* readOne(news);
3948
- let upid;
3949
4380
  if (live === undefined) {
3950
- upid = yield* pve(news.target, "provision", "POST", "config/datastore", createForm6(news));
4381
+ yield* createOne(news);
3951
4382
  } else {
3952
4383
  yield* guardPath(live, news);
3953
4384
  yield* guardBackend(live, news);
3954
4385
  const form = formToSend(matches6, live, news, updateForm3(news));
3955
4386
  if (form !== undefined) {
3956
- yield* pve(news.target, "provision", "PUT", object2(news), form);
4387
+ yield* updateOne(news);
3957
4388
  }
3958
4389
  }
3959
- const after = yield* settle3(readOne(news), (row) => row !== undefined && matches6(row, news));
4390
+ const after = yield* settle2(readOne(news), (row) => row !== undefined && matches6(row, news));
3960
4391
  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."));
4392
+ return yield* Effect56.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + "match the declaration after 120s. Check the PBS task list " + "-- 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."));
3962
4393
  }
3963
4394
  return after;
3964
4395
  }),
3965
- delete: Effect49.fn(function* ({ olds }) {
3966
- yield* pve(olds.target, "provision", "DELETE", object2(olds));
3967
- const left = yield* settle3(readOne(olds), (row) => row === undefined);
4396
+ delete: Effect56.fn(function* ({ olds }) {
4397
+ yield* deleteOne(olds);
4398
+ const left = yield* settle2(readOne(olds), (row) => row === undefined);
3968
4399
  if (left !== undefined) {
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>\`.`));
4400
+ return yield* Effect56.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>\`.`));
3970
4401
  }
3971
4402
  })
3972
- })));
4403
+ };
4404
+
4405
+ // src/proxmox/pbs-datastore.ts
4406
+ var PbsDatastore = Resource19("Pbs.Datastore", {
4407
+ defaultRemovalPolicy: "retain"
4408
+ });
4409
+ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect57.succeed(PbsDatastore.Provider.of(handlers8)));
3973
4410
  // src/proxmox/pbs-notification-matcher.ts
3974
4411
  import { Resource as Resource20 } from "alchemy";
3975
4412
  import * as Provider20 from "alchemy/Provider";
3976
- import * as Effect50 from "effect/Effect";
3977
- var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
3978
- var handlers12 = pveHandlers({
3979
- attributes: (live, props) => matcherAttributes(live, props.name),
3980
- collection: () => "config/notifications/matchers",
3981
- createForm: matcherCreateForm,
3982
- endpoint: {
3983
- create: "pbs:POST /config/notifications/matchers",
3984
- update: "pbs:PUT /config/notifications/matchers/{name}"
3985
- },
3986
- matches: matcherMatches,
3987
- path: (props) => `config/notifications/matchers/${props.name}`,
3988
- updateForm: matcherUpdateForm
4413
+ import { isResolved as isResolved18 } from "alchemy/Diff";
4414
+ import * as Effect60 from "effect/Effect";
4415
+
4416
+ // src/proxmox/pbs-notification-matcher-distilled.ts
4417
+ import * as config2 from "@distilled.cloud/proxmox-backup/config";
4418
+ import * as Effect59 from "effect/Effect";
4419
+
4420
+ // src/proxmox/pbs-notification-input.ts
4421
+ import * as Data3 from "effect/Data";
4422
+ import * as Effect58 from "effect/Effect";
4423
+ import * as Schema4 from "effect/Schema";
4424
+
4425
+ class PbsNotificationInputRefusal extends Data3.TaggedError("PbsNotificationInputRefusal") {
4426
+ }
4427
+ var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(Effect58.mapError(() => new PbsNotificationInputRefusal({
4428
+ message: "PBS notification fields do not match the SDK request schema; no PBS request was sent."
4429
+ })));
4430
+
4431
+ // src/proxmox/pbs-notification-matcher-distilled.ts
4432
+ var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(Effect59.map((row) => matcherAttributes({
4433
+ ...row,
4434
+ "invert-match": row.invert_match,
4435
+ "match-calendar": row.match_calendar,
4436
+ "match-field": row.match_field,
4437
+ "match-severity": row.match_severity
4438
+ }, props.name)), Effect59.catchTag("NotFound", () => Effect59.succeed(undefined)));
4439
+ var requestFields = (props, form) => ({
4440
+ ...form,
4441
+ invert_match: form["invert-match"],
4442
+ match_calendar: form["match-calendar"],
4443
+ match_field: form["match-field"],
4444
+ match_severity: form["match-severity"],
4445
+ name: props.name
3989
4446
  });
3990
- var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect50.succeed(PbsNotificationMatcher.Provider.of(handlers12)));
4447
+ var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect59.flatMap(config2.createConfigNotificationMatcher)));
4448
+ var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect59.flatMap(config2.putConfigNotificationMatcher)));
4449
+ var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(Effect59.catchTag("NotFound", () => Effect59.void));
4450
+
4451
+ // src/proxmox/pbs-notification-matcher.ts
4452
+ var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
4453
+ var guardCreate4 = (props, requirePresence) => guardForm("pbs:POST /config/notifications/matchers", matcherCreateForm(props), requirePresence);
4454
+ var guardUpdate4 = (props) => guardForm("pbs:PUT /config/notifications/matchers/{name}", matcherUpdateForm(props), false);
4455
+ var handlers9 = {
4456
+ list: () => Effect60.succeed([]),
4457
+ read: ({ olds }) => readMatcher2(olds),
4458
+ diff: ({
4459
+ news,
4460
+ olds,
4461
+ output
4462
+ }) => Effect60.gen(function* () {
4463
+ if (!isResolved18(news))
4464
+ return;
4465
+ yield* guardCreate4(news, output === undefined);
4466
+ yield* guardUpdate4(news);
4467
+ if (output === undefined)
4468
+ return;
4469
+ if (olds.name !== news.name)
4470
+ return { action: "replace" };
4471
+ const live = yield* readMatcher2(news);
4472
+ if (live === undefined) {
4473
+ yield* guardCreate4(news, true);
4474
+ return { action: "update" };
4475
+ }
4476
+ return { action: matcherMatches(live, news) ? "noop" : "update" };
4477
+ }),
4478
+ reconcile: ({ news }) => Effect60.gen(function* () {
4479
+ const live = yield* readMatcher2(news);
4480
+ yield* guardCreate4(news, live === undefined);
4481
+ yield* guardUpdate4(news);
4482
+ if (live === undefined)
4483
+ yield* createMatcher2(news, matcherCreateForm(news));
4484
+ else if (!matcherMatches(live, news))
4485
+ yield* updateMatcher2(news, matcherUpdateForm(news));
4486
+ const after = yield* readMatcher2(news);
4487
+ if (after === undefined)
4488
+ return yield* Effect60.fail(new Error(`config/notifications/matchers/${news.name}: the write returned no error but the matcher is still absent.`));
4489
+ return after;
4490
+ }),
4491
+ delete: ({ olds }) => deleteMatcher2(olds)
4492
+ };
4493
+ var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect60.succeed(PbsNotificationMatcher.Provider.of(handlers9)));
3991
4494
  // src/proxmox/pbs-notification-target.ts
3992
4495
  import { Resource as Resource21 } from "alchemy";
3993
4496
  import * as Provider21 from "alchemy/Provider";
3994
- import * as Effect52 from "effect/Effect";
4497
+ import * as Effect63 from "effect/Effect";
3995
4498
 
3996
4499
  // src/proxmox/pbs-notification-target-lifecycle.ts
3997
- import { isResolved as isResolved14 } from "alchemy/Diff";
3998
- import * as Effect51 from "effect/Effect";
3999
-
4000
- // src/proxmox/pbs-notification-target-endpoint.ts
4001
- var ENDPOINTS2 = {
4002
- sendmail: {
4003
- create: "pbs:POST /config/notifications/endpoints/sendmail",
4004
- update: "pbs:PUT /config/notifications/endpoints/sendmail/{name}"
4005
- },
4006
- smtp: {
4007
- create: "pbs:POST /config/notifications/endpoints/smtp",
4008
- update: "pbs:PUT /config/notifications/endpoints/smtp/{name}"
4009
- },
4010
- webhook: {
4011
- create: "pbs:POST /config/notifications/endpoints/webhook",
4012
- update: "pbs:PUT /config/notifications/endpoints/webhook/{name}"
4013
- }
4014
- };
4015
- var pbsTargetEndpoint = (props) => ENDPOINTS2[props.type];
4500
+ import { isResolved as isResolved19 } from "alchemy/Diff";
4501
+ import * as Effect62 from "effect/Effect";
4016
4502
 
4017
- // src/proxmox/credential-literals.ts
4018
- var READS_SECRET = /\{\{[^}]*\bsecrets\./;
4019
- var CREDENTIAL_WORD = /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;
4020
- var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
4021
- var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
4022
- var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
4023
- var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
4024
- var literalCredentialsInUrl = (url) => {
4025
- if (url === undefined)
4026
- return [];
4027
- const out = [];
4028
- const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
4029
- const at = authority.lastIndexOf("@");
4030
- if (at >= 0) {
4031
- const userinfo = authority.slice(0, at);
4032
- const colon = userinfo.indexOf(":");
4033
- if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
4034
- out.push("userinfo");
4035
- }
4036
- const params = url.replace(/^[^?#]*/, "");
4037
- for (const pair of params.split(/[?&#]/)) {
4038
- const eq = pair.indexOf("=");
4039
- const name = eq < 0 ? pair : pair.slice(0, eq);
4040
- if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
4041
- out.push(`?${name}`);
4042
- }
4043
- return out;
4044
- };
4503
+ // src/proxmox/pbs-notification-target-distilled.ts
4504
+ import * as config3 from "@distilled.cloud/proxmox-backup/config";
4505
+ import * as Effect61 from "effect/Effect";
4045
4506
 
4046
4507
  // src/proxmox/pbs-notification-target-wire.ts
4047
4508
  import { Buffer as Buffer2 } from "node:buffer";
@@ -4132,6 +4593,106 @@ var targetAttributes = (live, props) => {
4132
4593
  };
4133
4594
  };
4134
4595
 
4596
+ // src/proxmox/pbs-notification-target-distilled.ts
4597
+ var requestFields2 = (props, form) => ({
4598
+ ...form,
4599
+ from_address: form["from-address"],
4600
+ mailto_user: form["mailto-user"],
4601
+ name: props.name
4602
+ });
4603
+ var read4 = (props) => {
4604
+ switch (props.type) {
4605
+ case "sendmail":
4606
+ return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(Effect61.map((row) => ({
4607
+ ...row,
4608
+ "from-address": row.from_address,
4609
+ "mailto-user": row.mailto_user
4610
+ })));
4611
+ case "smtp":
4612
+ return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(Effect61.map((row) => ({
4613
+ ...row,
4614
+ "from-address": row.from_address,
4615
+ "mailto-user": row.mailto_user
4616
+ })));
4617
+ case "webhook":
4618
+ return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(Effect61.map((row) => ({ ...row })));
4619
+ }
4620
+ };
4621
+ var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(Effect61.map((row) => targetAttributes(row, props)), Effect61.catchTag("NotFound", () => Effect61.succeed(undefined)));
4622
+ var validateTargetWrite2 = (props, form, mode) => {
4623
+ const schema = mode === "create" ? {
4624
+ sendmail: config3.CreateConfigNotificationEndpointSendmailRequest,
4625
+ smtp: config3.CreateConfigNotificationEndpointSmtpRequest,
4626
+ webhook: config3.CreateConfigNotificationEndpointWebhookRequest
4627
+ }[props.type] : {
4628
+ sendmail: config3.PutConfigNotificationEndpointSendmailRequest,
4629
+ smtp: config3.PutConfigNotificationEndpointSmtpRequest,
4630
+ webhook: config3.PutConfigNotificationEndpointWebhookRequest
4631
+ }[props.type];
4632
+ return notificationInput(schema, requestFields2(props, form)).pipe(Effect61.asVoid);
4633
+ };
4634
+ var createTarget2 = (props, form) => {
4635
+ const fields2 = requestFields2(props, form);
4636
+ const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.CreateConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointSmtp)) : notificationInput(config3.CreateConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect61.flatMap(config3.createConfigNotificationEndpointWebhook));
4637
+ return runPbs(props.target, "provision", operation);
4638
+ };
4639
+ var updateTarget2 = (props, form) => {
4640
+ const fields2 = requestFields2(props, form);
4641
+ const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.PutConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointSmtp)) : notificationInput(config3.PutConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect61.flatMap(config3.putConfigNotificationEndpointWebhook));
4642
+ return runPbs(props.target, "provision", operation);
4643
+ };
4644
+ var deleteTarget2 = (props) => {
4645
+ const request = { name: props.name };
4646
+ const operation = props.type === "sendmail" ? config3.deleteConfigNotificationEndpointSendmail(request) : props.type === "smtp" ? config3.deleteConfigNotificationEndpointSmtp(request) : config3.deleteConfigNotificationEndpointWebhook(request);
4647
+ return runPbs(props.target, "provision", operation).pipe(Effect61.catchTag("NotFound", () => Effect61.void));
4648
+ };
4649
+
4650
+ // src/proxmox/pbs-notification-target-endpoint.ts
4651
+ var ENDPOINTS2 = {
4652
+ sendmail: {
4653
+ create: "pbs:POST /config/notifications/endpoints/sendmail",
4654
+ update: "pbs:PUT /config/notifications/endpoints/sendmail/{name}"
4655
+ },
4656
+ smtp: {
4657
+ create: "pbs:POST /config/notifications/endpoints/smtp",
4658
+ update: "pbs:PUT /config/notifications/endpoints/smtp/{name}"
4659
+ },
4660
+ webhook: {
4661
+ create: "pbs:POST /config/notifications/endpoints/webhook",
4662
+ update: "pbs:PUT /config/notifications/endpoints/webhook/{name}"
4663
+ }
4664
+ };
4665
+ var pbsTargetEndpoint = (props) => ENDPOINTS2[props.type];
4666
+
4667
+ // src/proxmox/credential-literals.ts
4668
+ var READS_SECRET = /\{\{[^}]*\bsecrets\./;
4669
+ var CREDENTIAL_WORD = /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;
4670
+ var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
4671
+ var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
4672
+ var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
4673
+ var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
4674
+ var literalCredentialsInUrl = (url) => {
4675
+ if (url === undefined)
4676
+ return [];
4677
+ const out = [];
4678
+ const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
4679
+ const at = authority.lastIndexOf("@");
4680
+ if (at >= 0) {
4681
+ const userinfo = authority.slice(0, at);
4682
+ const colon = userinfo.indexOf(":");
4683
+ if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
4684
+ out.push("userinfo");
4685
+ }
4686
+ const params = url.replace(/^[^?#]*/, "");
4687
+ for (const pair of params.split(/[?&#]/)) {
4688
+ const eq = pair.indexOf("=");
4689
+ const name = eq < 0 ? pair : pair.slice(0, eq);
4690
+ if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
4691
+ out.push(`?${name}`);
4692
+ }
4693
+ return out;
4694
+ };
4695
+
4135
4696
  // src/proxmox/pbs-notification-target-form.ts
4136
4697
  var OWNED = {
4137
4698
  author: ["sendmail", "smtp"],
@@ -4272,12 +4833,9 @@ var targetForm = (props, groups, carry, mode) => {
4272
4833
 
4273
4834
  // src/proxmox/pbs-notification-target-lifecycle.ts
4274
4835
  var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
4275
- var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect51.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect51.orElseSucceed(() => {
4276
- return;
4277
- }));
4278
4836
  var refuse2 = (props) => {
4279
4837
  const reasons = refusals(props);
4280
- return reasons.length === 0 ? Effect51.void : Effect51.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4838
+ return reasons.length === 0 ? Effect62.void : Effect62.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4281
4839
  };
4282
4840
  var toCarry = (live, props, groups, sealed) => {
4283
4841
  const header = headerState(live, props, groups) === "stale";
@@ -4289,39 +4847,48 @@ var requireValues = (props, groups, carry) => {
4289
4847
  ...carry.header ? groups.header.missing : [],
4290
4848
  ...carry.sealed ? groups.sealed.missing : []
4291
4849
  ];
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."));
4850
+ return missing.length === 0 ? Effect62.void : Effect62.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."));
4293
4851
  };
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 ?? "" })),
4852
+ var handlers10 = {
4853
+ list: () => Effect62.succeed([]),
4854
+ read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect62.void).pipe(Effect62.andThen(readTarget2(olds)), Effect62.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
4297
4855
  diff: ({
4298
4856
  news,
4299
4857
  olds,
4300
4858
  output
4301
- }) => Effect51.gen(function* () {
4302
- if (output === undefined || !isResolved14(news))
4859
+ }) => Effect62.gen(function* () {
4860
+ if (output === undefined || !isResolved19(news))
4303
4861
  return;
4862
+ yield* refuse2(news);
4304
4863
  if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
4864
+ const destination = yield* readTarget2(news);
4865
+ const groups = resolveGroups(news);
4866
+ const carry = destination === undefined ? { header: true, sealed: true } : toCarry(destination, news, groups, "");
4867
+ if (carry !== undefined) {
4868
+ yield* requireValues(news, groups, carry);
4869
+ const mode = destination === undefined ? "create" : "update";
4870
+ const form = targetForm(news, groups, carry, mode);
4871
+ yield* guardForm(pbsTargetEndpoint(news)[mode], form, destination === undefined);
4872
+ yield* validateTargetWrite2(news, form, mode);
4873
+ }
4305
4874
  return { action: "replace", deleteFirst: olds.name === news.name };
4306
4875
  }
4307
- yield* refuse2(news);
4308
- const live = yield* readLive2(news);
4876
+ const live = yield* readTarget2(news);
4309
4877
  if (live === undefined)
4310
4878
  return { action: "update" };
4311
4879
  return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
4312
4880
  }),
4313
- reconcile: ({ news, output }) => Effect51.gen(function* () {
4881
+ reconcile: ({ news, output }) => Effect62.gen(function* () {
4314
4882
  yield* refuse2(news);
4315
4883
  const groups = resolveGroups(news);
4316
- const live = yield* readLive2(news);
4884
+ const live = yield* readTarget2(news);
4317
4885
  let sealed = output?.sealed ?? "";
4318
4886
  if (live === undefined) {
4319
4887
  const carry = { header: true, sealed: true };
4320
4888
  yield* requireValues(news, groups, carry);
4321
- const collection = `config/notifications/endpoints/${news.type}`;
4322
4889
  const form = targetForm(news, groups, carry, "create");
4323
4890
  yield* guardForm(pbsTargetEndpoint(news).create, form, true);
4324
- yield* pve(news.target, "provision", "POST", collection, form);
4891
+ yield* createTarget2(news, form);
4325
4892
  sealed = seal(groups.sealed.values);
4326
4893
  } else {
4327
4894
  const carry = toCarry(live, news, groups, sealed);
@@ -4329,27 +4896,31 @@ var handlers13 = {
4329
4896
  yield* requireValues(news, groups, carry);
4330
4897
  const form = targetForm(news, groups, carry, "update");
4331
4898
  yield* guardForm(pbsTargetEndpoint(news).update, form, false);
4332
- yield* pve(news.target, "provision", "PUT", path(news), form);
4899
+ yield* updateTarget2(news, form);
4333
4900
  if (carry.sealed)
4334
4901
  sealed = seal(groups.sealed.values);
4335
4902
  }
4336
4903
  }
4337
- const after = yield* readLive2(news);
4904
+ const after = yield* readTarget2(news);
4338
4905
  if (after === undefined) {
4339
- return yield* Effect51.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
4906
+ return yield* Effect62.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
4340
4907
  }
4341
4908
  return { ...after, sealed };
4342
4909
  }),
4343
- delete: ({ olds }) => pve(olds.target, "provision", "DELETE", path(olds))
4910
+ delete: ({ olds }) => deleteTarget2(olds)
4344
4911
  };
4345
4912
 
4346
4913
  // src/proxmox/pbs-notification-target.ts
4347
4914
  var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
4348
- var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect52.succeed(PbsNotificationTarget.Provider.of(handlers13)));
4915
+ var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect63.succeed(PbsNotificationTarget.Provider.of(handlers10)));
4349
4916
  // src/proxmox/pbs-prune-job.ts
4350
4917
  import { Resource as Resource22 } from "alchemy";
4351
4918
  import * as Provider22 from "alchemy/Provider";
4352
- import * as Effect53 from "effect/Effect";
4919
+ import * as Effect66 from "effect/Effect";
4920
+
4921
+ // src/proxmox/pbs-prune-job-lifecycle.ts
4922
+ import { isResolved as isResolved20 } from "alchemy/Diff";
4923
+ import * as Effect65 from "effect/Effect";
4353
4924
 
4354
4925
  // src/proxmox/pbs-prune-job-form.ts
4355
4926
  var KEEP_KEYS = [
@@ -4404,10 +4975,9 @@ var updateBody2 = (props) => {
4404
4975
  return shape5(props);
4405
4976
  };
4406
4977
 
4407
- // src/proxmox/pbs-prune-job.ts
4408
- var PbsPruneJob = Resource22("Pbs.PruneJob");
4978
+ // src/proxmox/pbs-prune-job-spec.ts
4409
4979
  var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
4410
- var handlers14 = pveHandlers({
4980
+ var spec = {
4411
4981
  attributes: (live, props) => ({
4412
4982
  comment: text(live["comment"], ""),
4413
4983
  disable: bool(live["disable"], false),
@@ -4424,12 +4994,104 @@ var handlers14 = pveHandlers({
4424
4994
  matches: (attributes2, props) => attributes2.schedule === props.schedule && attributes2.store === props.store && same6(props.ns, attributes2.ns) && same6(props["max-depth"], attributes2["max-depth"]) && same6(props.disable, attributes2.disable) && same6(props.comment, attributes2.comment) && keepsMatch(attributes2, props),
4425
4995
  path: (props) => `config/prune/${props.id}`,
4426
4996
  updateForm: updateBody2
4997
+ };
4998
+
4999
+ // src/proxmox/pbs-prune-job-wire.ts
5000
+ import * as config4 from "@distilled.cloud/proxmox-backup/config";
5001
+ import * as Effect64 from "effect/Effect";
5002
+ var members2 = (form) => ({
5003
+ ...form["comment"] === undefined ? {} : { comment: form["comment"] },
5004
+ ...form["disable"] === undefined ? {} : { disable: form["disable"] },
5005
+ ...form["keep-daily"] === undefined ? {} : { keep_daily: form["keep-daily"] },
5006
+ ...form["keep-hourly"] === undefined ? {} : { keep_hourly: form["keep-hourly"] },
5007
+ ...form["keep-last"] === undefined ? {} : { keep_last: form["keep-last"] },
5008
+ ...form["keep-monthly"] === undefined ? {} : { keep_monthly: form["keep-monthly"] },
5009
+ ...form["keep-weekly"] === undefined ? {} : { keep_weekly: form["keep-weekly"] },
5010
+ ...form["keep-yearly"] === undefined ? {} : { keep_yearly: form["keep-yearly"] },
5011
+ ...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
5012
+ ...form["ns"] === undefined ? {} : { ns: form["ns"] },
5013
+ ...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
5014
+ ...form["store"] === undefined ? {} : { store: form["store"] }
5015
+ });
5016
+ var createRequest2 = (props) => ({
5017
+ ...members2(createBody3(props)),
5018
+ id: props.id,
5019
+ store: props.store,
5020
+ schedule: props.schedule
5021
+ });
5022
+ var updateRequest2 = (props) => ({
5023
+ ...members2(updateBody2(props)),
5024
+ id: props.id
5025
+ });
5026
+ var wire2 = (row) => ({
5027
+ ...row,
5028
+ "keep-daily": row.keep_daily,
5029
+ "keep-hourly": row.keep_hourly,
5030
+ "keep-last": row.keep_last,
5031
+ "keep-monthly": row.keep_monthly,
5032
+ "keep-weekly": row.keep_weekly,
5033
+ "keep-yearly": row.keep_yearly,
5034
+ "max-depth": row.max_depth
5035
+ });
5036
+ var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(Effect64.map((row) => spec.attributes(wire2(row), props)), Effect64.catchTag("PruneJobNotFound", () => Effect64.succeed(undefined)));
5037
+ var createOne2 = (props) => runPbs(props.target, "provision", config4.createConfigPrune(createRequest2(props)));
5038
+ var updateOne2 = (props) => runPbs(props.target, "provision", config4.putConfigPrune(updateRequest2(props)));
5039
+ var deleteOne2 = (props) => Effect64.gen(function* () {
5040
+ if ((yield* readOne2(props)) === undefined)
5041
+ return;
5042
+ return yield* runPbs(props.target, "provision", config4.deleteConfigPrune({ id: props.id }));
4427
5043
  });
4428
- var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect53.succeed(PbsPruneJob.Provider.of(handlers14)));
5044
+
5045
+ // src/proxmox/pbs-prune-job-lifecycle.ts
5046
+ var { guardCreate: guardCreate5, guardUpdate: guardUpdate5 } = specGuards(spec);
5047
+ var handlers11 = {
5048
+ list: () => Effect65.succeed([]),
5049
+ read: ({ olds }) => readOne2(olds),
5050
+ diff: ({
5051
+ news,
5052
+ output
5053
+ }) => Effect65.gen(function* () {
5054
+ if (!isResolved20(news))
5055
+ return;
5056
+ yield* guardCreate5(news, output === undefined);
5057
+ yield* guardUpdate5(news);
5058
+ if (output === undefined)
5059
+ return;
5060
+ const live = yield* readOne2(news);
5061
+ if (live === undefined) {
5062
+ yield* guardCreate5(news, true);
5063
+ return { action: "update" };
5064
+ }
5065
+ return { action: spec.matches(live, news) ? "noop" : "update" };
5066
+ }),
5067
+ reconcile: ({ news }) => Effect65.gen(function* () {
5068
+ const live = yield* readOne2(news);
5069
+ yield* guardCreate5(news, live === undefined);
5070
+ yield* guardUpdate5(news);
5071
+ if (live === undefined)
5072
+ yield* createOne2(news);
5073
+ else if (!spec.matches(live, news))
5074
+ yield* updateOne2(news);
5075
+ const after = yield* readOne2(news);
5076
+ if (after === undefined) {
5077
+ return yield* Effect65.fail(new Error(`${spec.path(news)}: write succeeded but job is absent.`));
5078
+ }
5079
+ return after;
5080
+ }),
5081
+ delete: ({ olds }) => deleteOne2(olds)
5082
+ };
5083
+
5084
+ // src/proxmox/pbs-prune-job.ts
5085
+ var PbsPruneJob = Resource22("Pbs.PruneJob");
5086
+ var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect66.succeed(PbsPruneJob.Provider.of(handlers11)));
4429
5087
  // src/proxmox/pbs-sync-job.ts
4430
5088
  import { Resource as Resource23 } from "alchemy";
4431
5089
  import * as Provider23 from "alchemy/Provider";
4432
- import * as Effect54 from "effect/Effect";
5090
+ import * as Effect69 from "effect/Effect";
5091
+
5092
+ // src/proxmox/pbs-sync-job-lifecycle.ts
5093
+ import { isResolved as isResolved21 } from "alchemy/Diff";
5094
+ import * as Effect68 from "effect/Effect";
4433
5095
 
4434
5096
  // src/proxmox/pbs-sync-job-form.ts
4435
5097
  var UNSET4 = -1;
@@ -4477,7 +5139,7 @@ var createBody4 = (props) => ({
4477
5139
  ...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
4478
5140
  });
4479
5141
  var updateBody3 = (props) => shape6(props);
4480
- var handlers15 = pveHandlers({
5142
+ var spec2 = {
4481
5143
  attributes: (live, props) => ({
4482
5144
  comment: text(live["comment"], ""),
4483
5145
  "group-filter": filters(live["group-filter"]),
@@ -4502,15 +5164,105 @@ var handlers15 = pveHandlers({
4502
5164
  matches: (attributes2, props) => attributes2["remove-vanished"] === (props["remove-vanished"] === true) && attributes2.store === props.store && attributes2["remote-store"] === props["remote-store"] && (props.remote === undefined || attributes2.remote === props.remote) && (props.ns === undefined || attributes2.ns === props.ns) && (props["remote-ns"] === undefined || attributes2["remote-ns"] === props["remote-ns"]) && (props.schedule === undefined || attributes2.schedule === props.schedule) && (props.comment === undefined || attributes2.comment === props.comment) && (props.owner === undefined || attributes2.owner === props.owner) && (props["verified-only"] === undefined || attributes2["verified-only"] === props["verified-only"]) && (props["rate-in"] === undefined || attributes2["rate-in"] === props["rate-in"]) && (props["max-depth"] === undefined || attributes2["max-depth"] === props["max-depth"]) && (props["transfer-last"] === undefined || attributes2["transfer-last"] === props["transfer-last"]),
4503
5165
  path: (props) => `config/sync/${props.id}`,
4504
5166
  updateForm: updateBody3
5167
+ };
5168
+
5169
+ // src/proxmox/pbs-sync-job-wire.ts
5170
+ import * as config5 from "@distilled.cloud/proxmox-backup/config";
5171
+ import * as Effect67 from "effect/Effect";
5172
+ var members3 = (form) => ({
5173
+ ...form["remote-store"] === undefined ? {} : { remote_store: form["remote-store"] },
5174
+ ...form["remove-vanished"] === undefined ? {} : { remove_vanished: form["remove-vanished"] },
5175
+ ...form["store"] === undefined ? {} : { store: form["store"] },
5176
+ ...form["comment"] === undefined ? {} : { comment: form["comment"] },
5177
+ ...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
5178
+ ...form["ns"] === undefined ? {} : { ns: form["ns"] },
5179
+ ...form["owner"] === undefined ? {} : { owner: form["owner"] },
5180
+ ...form["rate-in"] === undefined ? {} : { rate_in: form["rate-in"] },
5181
+ ...form["remote"] === undefined ? {} : { remote: form["remote"] },
5182
+ ...form["remote-ns"] === undefined ? {} : { remote_ns: form["remote-ns"] },
5183
+ ...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
5184
+ ...form["verified-only"] === undefined ? {} : { verified_only: form["verified-only"] },
5185
+ ...form["transfer-last"] === undefined ? {} : { transfer_last: form["transfer-last"] },
5186
+ ...form["sync-direction"] === undefined ? {} : { sync_direction: form["sync-direction"] }
5187
+ });
5188
+ var createRequest3 = (props) => ({
5189
+ ...members3(createBody4(props)),
5190
+ id: props.id,
5191
+ store: props.store,
5192
+ remote_store: props["remote-store"]
5193
+ });
5194
+ var updateRequest3 = (props) => ({
5195
+ ...members3(updateBody3(props)),
5196
+ id: props.id
5197
+ });
5198
+ var wire3 = (row) => ({
5199
+ ...row,
5200
+ "remote-store": row.remote_store,
5201
+ "remove-vanished": row.remove_vanished,
5202
+ "max-depth": row.max_depth,
5203
+ "rate-in": row.rate_in,
5204
+ "remote-ns": row.remote_ns,
5205
+ "verified-only": row.verified_only,
5206
+ "transfer-last": row.transfer_last,
5207
+ "sync-direction": row.sync_direction,
5208
+ "group-filter": row.group_filter
5209
+ });
5210
+ var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(Effect67.map((row) => spec2.attributes(wire3(row), props)), Effect67.catchTag("SyncJobNotFound", () => Effect67.succeed(undefined)));
5211
+ var createOne3 = (props) => runPbs(props.target, "provision", config5.syncConfig(createRequest3(props)));
5212
+ var updateOne3 = (props) => runPbs(props.target, "provision", config5.putConfigSync(updateRequest3(props)));
5213
+ var deleteOne3 = (props) => Effect67.gen(function* () {
5214
+ if ((yield* readOne3(props)) === undefined)
5215
+ return;
5216
+ return yield* runPbs(props.target, "provision", config5.deleteConfigSync({ id: props.id }));
4505
5217
  });
4506
5218
 
5219
+ // src/proxmox/pbs-sync-job-lifecycle.ts
5220
+ var { guardCreate: guardCreate6, guardUpdate: guardUpdate6 } = specGuards(spec2);
5221
+ var handlers12 = {
5222
+ list: () => Effect68.succeed([]),
5223
+ read: ({ olds }) => readOne3(olds),
5224
+ diff: ({ news, output }) => Effect68.gen(function* () {
5225
+ if (!isResolved21(news))
5226
+ return;
5227
+ yield* guardCreate6(news, output === undefined);
5228
+ yield* guardUpdate6(news);
5229
+ if (output === undefined)
5230
+ return;
5231
+ const live = yield* readOne3(news);
5232
+ if (live === undefined) {
5233
+ yield* guardCreate6(news, true);
5234
+ return { action: "update" };
5235
+ }
5236
+ return { action: spec2.matches(live, news) ? "noop" : "update" };
5237
+ }),
5238
+ reconcile: ({ news }) => Effect68.gen(function* () {
5239
+ const live = yield* readOne3(news);
5240
+ yield* guardCreate6(news, live === undefined);
5241
+ yield* guardUpdate6(news);
5242
+ if (live === undefined)
5243
+ yield* createOne3(news);
5244
+ else if (!spec2.matches(live, news))
5245
+ yield* updateOne3(news);
5246
+ const after = yield* readOne3(news);
5247
+ if (after === undefined) {
5248
+ return yield* Effect68.fail(new Error(`${spec2.path(news)}: write succeeded but job is absent.`));
5249
+ }
5250
+ return after;
5251
+ }),
5252
+ delete: ({ olds }) => deleteOne3(olds)
5253
+ };
5254
+
4507
5255
  // src/proxmox/pbs-sync-job.ts
4508
5256
  var PbsSyncJob = Resource23("Pbs.SyncJob");
4509
- var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect54.succeed(PbsSyncJob.Provider.of(handlers15)));
5257
+ var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect69.succeed(PbsSyncJob.Provider.of(handlers12)));
4510
5258
  // src/proxmox/pbs-verify-job.ts
4511
5259
  import { Resource as Resource24 } from "alchemy";
4512
5260
  import * as Provider24 from "alchemy/Provider";
4513
- import * as Effect55 from "effect/Effect";
5261
+ import * as Effect72 from "effect/Effect";
5262
+
5263
+ // src/proxmox/pbs-verify-job-lifecycle.ts
5264
+ import { isResolved as isResolved22 } from "alchemy/Diff";
5265
+ import * as Effect71 from "effect/Effect";
4514
5266
 
4515
5267
  // src/proxmox/pbs-verify-job-form.ts
4516
5268
  var rechecks = (ignoreVerified, outdatedAfter) => {
@@ -4530,9 +5282,8 @@ var shape7 = (props) => ({
4530
5282
  ...props["outdated-after"] === undefined ? {} : { "outdated-after": String(props["outdated-after"]) }
4531
5283
  });
4532
5284
 
4533
- // src/proxmox/pbs-verify-job.ts
4534
- var PbsVerifyJob = Resource24("Pbs.VerifyJob");
4535
- var handlers16 = pveHandlers({
5285
+ // src/proxmox/pbs-verify-job-spec.ts
5286
+ var spec3 = {
4536
5287
  attributes: (live, props) => {
4537
5288
  const ignoreVerified = bool(live["ignore-verified"], true);
4538
5289
  const outdatedAfter = int(live["outdated-after"], -1);
@@ -4554,12 +5305,94 @@ var handlers16 = pveHandlers({
4554
5305
  matches: (attributes2, props) => attributes2.store === props.store && attributes2.schedule === (props.schedule ?? "") && attributes2["ignore-verified"] === (props["ignore-verified"] !== false) && (props["outdated-after"] === undefined || attributes2["outdated-after"] === props["outdated-after"]) && (props["max-depth"] === undefined || attributes2["max-depth"] === props["max-depth"]) && (props.ns === undefined || attributes2.ns === props.ns) && (props.comment === undefined || attributes2.comment === props.comment),
4555
5306
  path: (props) => `config/verify/${props.id}`,
4556
5307
  updateForm: shape7
5308
+ };
5309
+
5310
+ // src/proxmox/pbs-verify-job-wire.ts
5311
+ import * as config6 from "@distilled.cloud/proxmox-backup/config";
5312
+ import * as Effect70 from "effect/Effect";
5313
+ var members4 = (form) => ({
5314
+ ...form["ignore-verified"] === undefined ? {} : { ignore_verified: form["ignore-verified"] },
5315
+ ...form["store"] === undefined ? {} : { store: form["store"] },
5316
+ ...form["schedule"] === undefined ? {} : { schedule: form["schedule"] },
5317
+ ...form["comment"] === undefined ? {} : { comment: form["comment"] },
5318
+ ...form["max-depth"] === undefined ? {} : { max_depth: form["max-depth"] },
5319
+ ...form["ns"] === undefined ? {} : { ns: form["ns"] },
5320
+ ...form["outdated-after"] === undefined ? {} : { outdated_after: form["outdated-after"] }
5321
+ });
5322
+ var createRequest4 = (props) => ({
5323
+ ...members4({ ...shape7(props), id: props.id }),
5324
+ id: props.id,
5325
+ store: props.store
5326
+ });
5327
+ var updateRequest4 = (props) => ({
5328
+ ...members4(shape7(props)),
5329
+ id: props.id
5330
+ });
5331
+ var wire4 = (row) => ({
5332
+ ...row,
5333
+ "ignore-verified": row.ignore_verified,
5334
+ "max-depth": row.max_depth,
5335
+ "outdated-after": row.outdated_after
5336
+ });
5337
+ var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(Effect70.map((row) => spec3.attributes(wire4(row), props)), Effect70.catchTag("VerifyJobNotFound", () => Effect70.succeed(undefined)));
5338
+ var createOne4 = (props) => runPbs(props.target, "provision", config6.verifyConfig(createRequest4(props)));
5339
+ var updateOne4 = (props) => runPbs(props.target, "provision", config6.putConfigVerify(updateRequest4(props)));
5340
+ var deleteOne4 = (props) => Effect70.gen(function* () {
5341
+ if ((yield* readOne4(props)) === undefined)
5342
+ return;
5343
+ return yield* runPbs(props.target, "provision", config6.deleteConfigVerify({ id: props.id }));
4557
5344
  });
4558
- var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect55.succeed(PbsVerifyJob.Provider.of(handlers16)));
5345
+
5346
+ // src/proxmox/pbs-verify-job-lifecycle.ts
5347
+ var { guardCreate: guardCreate7, guardUpdate: guardUpdate7 } = specGuards(spec3);
5348
+ var handlers13 = {
5349
+ list: () => Effect71.succeed([]),
5350
+ read: ({ olds }) => readOne4(olds),
5351
+ diff: ({
5352
+ news,
5353
+ output
5354
+ }) => Effect71.gen(function* () {
5355
+ if (!isResolved22(news))
5356
+ return;
5357
+ yield* guardCreate7(news, output === undefined);
5358
+ yield* guardUpdate7(news);
5359
+ if (output === undefined)
5360
+ return;
5361
+ const live = yield* readOne4(news);
5362
+ if (live === undefined) {
5363
+ yield* guardCreate7(news, true);
5364
+ return { action: "update" };
5365
+ }
5366
+ return { action: spec3.matches(live, news) ? "noop" : "update" };
5367
+ }),
5368
+ reconcile: ({ news }) => Effect71.gen(function* () {
5369
+ const live = yield* readOne4(news);
5370
+ yield* guardCreate7(news, live === undefined);
5371
+ yield* guardUpdate7(news);
5372
+ if (live === undefined)
5373
+ yield* createOne4(news);
5374
+ else if (!spec3.matches(live, news))
5375
+ yield* updateOne4(news);
5376
+ const after = yield* readOne4(news);
5377
+ if (after === undefined) {
5378
+ return yield* Effect71.fail(new Error(`${spec3.path(news)}: write succeeded but job is absent.`));
5379
+ }
5380
+ return after;
5381
+ }),
5382
+ delete: ({ olds }) => deleteOne4(olds)
5383
+ };
5384
+
5385
+ // src/proxmox/pbs-verify-job.ts
5386
+ var PbsVerifyJob = Resource24("Pbs.VerifyJob");
5387
+ var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect72.succeed(PbsVerifyJob.Provider.of(handlers13)));
4559
5388
  // src/proxmox/pool.ts
4560
5389
  import { Resource as Resource25 } from "alchemy";
5390
+ import { isResolved as isResolved23 } from "alchemy/Diff";
4561
5391
  import * as Provider25 from "alchemy/Provider";
4562
- import * as Effect56 from "effect/Effect";
5392
+ import * as pools from "@distilled.cloud/proxmox/pools";
5393
+ import { ProxmoxParseError as ProxmoxParseError4 } from "@distilled.cloud/proxmox/Errors";
5394
+ import * as Schema5 from "effect/Schema";
5395
+ import * as Effect73 from "effect/Effect";
4563
5396
  var ProxmoxPool = Resource25("Proxmox.Pool");
4564
5397
  var poolCreateForm = (props) => ({
4565
5398
  comment: props.comment ?? "",
@@ -4568,7 +5401,7 @@ var poolCreateForm = (props) => ({
4568
5401
  var poolUpdateForm = (props) => ({
4569
5402
  comment: props.comment ?? ""
4570
5403
  });
4571
- var handlers17 = pveHandlers({
5404
+ var spec4 = {
4572
5405
  attributes: (live, props) => {
4573
5406
  const row = live;
4574
5407
  return {
@@ -4583,8 +5416,51 @@ var handlers17 = pveHandlers({
4583
5416
  matches: (attributes2, props) => attributes2.comment === (props.comment ?? ""),
4584
5417
  path: (props) => `pools/${props.poolid}`,
4585
5418
  updateForm: poolUpdateForm
4586
- });
4587
- var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect56.succeed(ProxmoxPool.Provider.of(handlers17)));
5419
+ };
5420
+ var { guardCreate: guardCreate8, guardUpdate: guardUpdate8 } = specGuards(spec4);
5421
+ var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(Effect73.flatMap((live) => Schema5.decodeUnknownEffect(Schema5.toType(pools.GetPoolResponse))(live).pipe(Effect73.mapError(() => new ProxmoxParseError4({
5422
+ body: undefined,
5423
+ cause: "Pool response does not match its vendor schema"
5424
+ })), Effect73.as(live))), Effect73.map((live) => spec4.attributes({ ...live }, props)), Effect73.catchTag("PoolNotFound", () => Effect73.succeed(undefined)), Effect73.catchTag("NotFound", () => Effect73.succeed(undefined)));
5425
+ var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(Effect73.catchTag("PoolNotFound", () => Effect73.void), Effect73.catchTag("NotFound", () => Effect73.void));
5426
+ var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect73.succeed(ProxmoxPool.Provider.of({
5427
+ list: () => Effect73.succeed([]),
5428
+ read: ({ olds }) => readPool(olds),
5429
+ diff: Effect73.fn(function* ({ news, output }) {
5430
+ if (!isResolved23(news))
5431
+ return;
5432
+ yield* guardCreate8(news, output === undefined);
5433
+ yield* guardUpdate8(news);
5434
+ if (output === undefined)
5435
+ return;
5436
+ const live = yield* readPool(news);
5437
+ if (live === undefined) {
5438
+ yield* guardCreate8(news, true);
5439
+ return { action: "update" };
5440
+ }
5441
+ return { action: spec4.matches(live, news) ? "noop" : "update" };
5442
+ }),
5443
+ reconcile: Effect73.fn(function* ({ news }) {
5444
+ const live = yield* readPool(news);
5445
+ yield* guardCreate8(news, live === undefined);
5446
+ yield* guardUpdate8(news);
5447
+ if (live === undefined) {
5448
+ yield* runPve(news.target, "provision", true, pools.createPool(poolCreateForm(news)));
5449
+ } else {
5450
+ const form = formToSend(spec4.matches, live, news, poolUpdateForm(news));
5451
+ if (form !== undefined)
5452
+ yield* runPve(news.target, "provision", true, pools.putPool({
5453
+ ...form,
5454
+ poolid: news.poolid
5455
+ }));
5456
+ }
5457
+ const after = yield* readPool(news);
5458
+ if (after === undefined)
5459
+ return yield* Effect73.fail(new Error(`pools/${news.poolid}: write returned success but the pool is still absent`));
5460
+ return after;
5461
+ }),
5462
+ delete: ({ olds }) => deletePool2(olds)
5463
+ })));
4588
5464
  // src/proxmox/provision-baseline.ts
4589
5465
  var PROVISION_PRIVILEGES = Object.freeze([
4590
5466
  "Datastore.Allocate",
@@ -4736,15 +5612,15 @@ fi
4736
5612
  var userStep = (user) => {
4737
5613
  const groups = [...user.groups].sort().join(",");
4738
5614
  const want = `groups=${groups};enable=1;expire=0;email=;firstname=;lastname=;comment=${user.comment}`;
4739
- const fields = `--groups ${groups} --comment '${user.comment}'`;
5615
+ const fields2 = `--groups ${groups} --comment '${user.comment}'`;
4740
5616
  return `
4741
5617
  # User ${user.userid} (the ${user.lane} lane): in ${groups}, enabled, no password.
4742
5618
  if have=$(get /access/users/${user.userid} '${USER_STATE}'); then
4743
5619
  if [ "$have" = '${want}' ]; then echo 'user ${user.userid}: ok'
4744
5620
  else echo 'user ${user.userid}: setting groups, comment and account fields'
4745
- pveum user modify ${user.userid} ${fields} --enable 1 --expire 0 --email '' --firstname '' --lastname ''; fi
5621
+ pveum user modify ${user.userid} ${fields2} --enable 1 --expire 0 --email '' --firstname '' --lastname ''; fi
4746
5622
  else
4747
- echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields}
5623
+ echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields2}
4748
5624
  fi
4749
5625
  `;
4750
5626
  };
@@ -4777,14 +5653,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
4777
5653
  // src/proxmox/provision-declare.ts
4778
5654
  import { adopt } from "alchemy/AdoptPolicy";
4779
5655
  import * as RemovalPolicy from "alchemy/RemovalPolicy";
4780
- import * as Effect60 from "effect/Effect";
5656
+ import * as Effect77 from "effect/Effect";
4781
5657
 
4782
5658
  // src/proxmox/role.ts
4783
5659
  import { Resource as Resource26 } from "alchemy";
4784
- import { isResolved as isResolved15 } from "alchemy/Diff";
5660
+ import { isResolved as isResolved24 } from "alchemy/Diff";
4785
5661
  import * as Provider26 from "alchemy/Provider";
4786
5662
  import * as access5 from "@distilled.cloud/proxmox/access";
4787
- import * as Effect57 from "effect/Effect";
5663
+ import * as Effect74 from "effect/Effect";
4788
5664
 
4789
5665
  // src/proxmox/role-wire.ts
4790
5666
  var ROLE_CREATE = "pve:POST /access/roles";
@@ -4807,20 +5683,20 @@ var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") =
4807
5683
 
4808
5684
  // src/proxmox/role.ts
4809
5685
  var ProxmoxRole = Resource26("Proxmox.Role");
4810
- var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect57.map((rows) => {
5686
+ var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect74.map((rows) => {
4811
5687
  if (rows === UNREADABLE)
4812
5688
  return UNREADABLE;
4813
5689
  const row = find2(rows, props.roleid);
4814
5690
  return row === undefined ? undefined : attributesOf7(row, props);
4815
5691
  }));
4816
5692
  var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
4817
- var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(ProxmoxRole.Provider.of({
4818
- list: () => Effect57.succeed([]),
4819
- read: Effect57.fn(function* ({ olds }) {
5693
+ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect74.succeed(ProxmoxRole.Provider.of({
5694
+ list: () => Effect74.succeed([]),
5695
+ read: Effect74.fn(function* ({ olds }) {
4820
5696
  return dropUnreadable5(yield* readRole(olds));
4821
5697
  }),
4822
- diff: Effect57.fn(function* ({ news, output }) {
4823
- if (!isResolved15(news))
5698
+ diff: Effect74.fn(function* ({ news, output }) {
5699
+ if (!isResolved24(news))
4824
5700
  return;
4825
5701
  yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
4826
5702
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
@@ -4837,7 +5713,7 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(
4837
5713
  }
4838
5714
  return matches7(live, news) ? { action: "noop" } : { action: "update" };
4839
5715
  }),
4840
- reconcile: Effect57.fn(function* ({ news }) {
5716
+ reconcile: Effect74.fn(function* ({ news }) {
4841
5717
  const before = dropUnreadable5(yield* readRole(news));
4842
5718
  yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
4843
5719
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
@@ -4848,25 +5724,25 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect57.succeed(
4848
5724
  }
4849
5725
  const after = dropUnreadable5(yield* readRole(news));
4850
5726
  if (after === undefined) {
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."));
5727
+ return yield* Effect74.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."));
4852
5728
  }
4853
5729
  return after;
4854
5730
  }),
4855
- delete: Effect57.fn(function* ({ olds }) {
5731
+ delete: Effect74.fn(function* ({ olds }) {
4856
5732
  yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
4857
5733
  })
4858
5734
  })));
4859
5735
 
4860
5736
  // src/proxmox/user.ts
4861
5737
  import { Resource as Resource27 } from "alchemy";
4862
- import { isResolved as isResolved16 } from "alchemy/Diff";
5738
+ import { isResolved as isResolved25 } from "alchemy/Diff";
4863
5739
  import * as Provider27 from "alchemy/Provider";
4864
5740
  import * as access7 from "@distilled.cloud/proxmox/access";
4865
- import * as Effect59 from "effect/Effect";
5741
+ import * as Effect76 from "effect/Effect";
4866
5742
 
4867
5743
  // src/proxmox/user-wire.ts
4868
5744
  import * as access6 from "@distilled.cloud/proxmox/access";
4869
- import * as Effect58 from "effect/Effect";
5745
+ import * as Effect75 from "effect/Effect";
4870
5746
  var USER_CREATE = "pve:POST /access/users";
4871
5747
  var USER_UPDATE = "pve:PUT /access/users/{userid}";
4872
5748
  var int2 = (value) => {
@@ -4908,19 +5784,19 @@ var attributesOf8 = (live, props) => ({
4908
5784
  userid: props.userid
4909
5785
  });
4910
5786
  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)));
5787
+ var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect75.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
5788
+ var readUser = (props) => readUserOrFail(props).pipe(Effect75.catchTag("UserNotFound", () => Effect75.succeed(undefined)));
4913
5789
  var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
4914
5790
 
4915
5791
  // src/proxmox/user.ts
4916
5792
  var ProxmoxUser = Resource27("Proxmox.User");
4917
- var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(ProxmoxUser.Provider.of({
4918
- list: () => Effect59.succeed([]),
4919
- read: Effect59.fn(function* ({ olds, output }) {
5793
+ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect76.succeed(ProxmoxUser.Provider.of({
5794
+ list: () => Effect76.succeed([]),
5795
+ read: Effect76.fn(function* ({ olds, output }) {
4920
5796
  return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
4921
5797
  }),
4922
- diff: Effect59.fn(function* ({ news, output }) {
4923
- if (!isResolved16(news))
5798
+ diff: Effect76.fn(function* ({ news, output }) {
5799
+ if (!isResolved25(news))
4924
5800
  return;
4925
5801
  yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
4926
5802
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
@@ -4937,7 +5813,7 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(
4937
5813
  }
4938
5814
  return matches8(live, news) ? { action: "noop" } : { action: "update" };
4939
5815
  }),
4940
- reconcile: Effect59.fn(function* ({ news }) {
5816
+ reconcile: Effect76.fn(function* ({ news }) {
4941
5817
  const before = dropUnreadable6(yield* readUser(news));
4942
5818
  yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
4943
5819
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
@@ -4948,18 +5824,18 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect59.succeed(
4948
5824
  }
4949
5825
  const after = dropUnreadable6(yield* readUser(news));
4950
5826
  if (after === undefined) {
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."));
5827
+ return yield* Effect76.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."));
4952
5828
  }
4953
5829
  return after;
4954
5830
  }),
4955
- delete: Effect59.fn(function* ({ olds }) {
5831
+ delete: Effect76.fn(function* ({ olds }) {
4956
5832
  yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
4957
5833
  })
4958
5834
  })));
4959
5835
 
4960
5836
  // src/proxmox/provision-declare.ts
4961
- var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect60.gen(function* () {
4962
- const baseline = yield* Effect60.try({
5837
+ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect77.gen(function* () {
5838
+ const baseline = yield* Effect77.try({
4963
5839
  catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
4964
5840
  try: () => provisionBaseline(names2)
4965
5841
  });
@@ -4973,13 +5849,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4973
5849
  target
4974
5850
  }));
4975
5851
  const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
4976
- const users = yield* Effect60.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
5852
+ const users = yield* Effect77.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
4977
5853
  comment: user.comment,
4978
5854
  groups: [...user.groups],
4979
5855
  target,
4980
5856
  userid: user.userid
4981
5857
  }))));
4982
- const grants = yield* Effect60.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
5858
+ const grants = yield* Effect77.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
4983
5859
  path: "/",
4984
5860
  propagate: true,
4985
5861
  roleid: grant.roleid,
@@ -4992,7 +5868,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4992
5868
  // src/proxmox/qemu.ts
4993
5869
  import { Resource as Resource28 } from "alchemy";
4994
5870
  import * as Provider28 from "alchemy/Provider";
4995
- import * as Effect61 from "effect/Effect";
5871
+ import * as Effect78 from "effect/Effect";
4996
5872
  var ProxmoxVm = Resource28("Proxmox.Vm");
4997
5873
  var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
4998
5874
  var shape9 = (props) => ({
@@ -5003,7 +5879,7 @@ var shape9 = (props) => ({
5003
5879
  sockets: String(props.sockets ?? 1),
5004
5880
  ...props.net0 === undefined ? {} : { net0: props.net0 }
5005
5881
  });
5006
- var handlers18 = pveHandlers({
5882
+ var handlers14 = pveHandlers({
5007
5883
  attributes: (live, props) => ({
5008
5884
  cores: num(live["cores"], 1),
5009
5885
  memory: num(live["memory"], 512),
@@ -5023,11 +5899,11 @@ var handlers18 = pveHandlers({
5023
5899
  path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
5024
5900
  updateForm: shape9
5025
5901
  });
5026
- var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect61.succeed(ProxmoxVm.Provider.of(handlers18)));
5902
+ var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect78.succeed(ProxmoxVm.Provider.of(handlers14)));
5027
5903
  // src/proxmox/replication-job.ts
5028
5904
  import { Resource as Resource29 } from "alchemy";
5029
5905
  import * as Provider29 from "alchemy/Provider";
5030
- import * as Effect62 from "effect/Effect";
5906
+ import * as Effect79 from "effect/Effect";
5031
5907
 
5032
5908
  // src/proxmox/replication-job-form.ts
5033
5909
  var DEFAULT_SCHEDULE = "*/15";
@@ -5057,7 +5933,7 @@ var rateOf = (value) => {
5057
5933
  const parsed = Number.parseFloat(text(value));
5058
5934
  return Number.isFinite(parsed) ? parsed : 0;
5059
5935
  };
5060
- var handlers19 = pveHandlers({
5936
+ var handlers15 = pveHandlers({
5061
5937
  attributes: (live, props) => ({
5062
5938
  comment: text(live["comment"], ""),
5063
5939
  disable: bool(live["disable"], false),
@@ -5081,15 +5957,15 @@ var handlers19 = pveHandlers({
5081
5957
  path: (props) => `cluster/replication/${jobId(props)}`,
5082
5958
  updateForm: updateBody4
5083
5959
  });
5084
- var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect62.succeed(ProxmoxReplicationJob.Provider.of(handlers19)));
5960
+ var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect79.succeed(ProxmoxReplicationJob.Provider.of(handlers15)));
5085
5961
  // src/proxmox/sdn-apply.ts
5086
5962
  import { Resource as Resource30 } from "alchemy";
5087
- import { isResolved as isResolved17 } from "alchemy/Diff";
5963
+ import { isResolved as isResolved26 } from "alchemy/Diff";
5088
5964
  import * as Provider30 from "alchemy/Provider";
5089
- import * as Effect64 from "effect/Effect";
5965
+ import * as Effect81 from "effect/Effect";
5090
5966
 
5091
5967
  // src/proxmox/sdn-apply-read.ts
5092
- import * as Effect63 from "effect/Effect";
5968
+ import * as Effect80 from "effect/Effect";
5093
5969
  var MARKED = [
5094
5970
  "cluster/sdn/zones",
5095
5971
  "cluster/sdn/vnets",
@@ -5098,7 +5974,7 @@ var MARKED = [
5098
5974
  "cluster/sdn/route-maps"
5099
5975
  ];
5100
5976
  var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
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)));
5977
+ var markedPending = (target) => Effect80.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect80.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect80.catchIf(subsystemAbsent, () => Effect80.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect80.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
5102
5978
  var fabricStaged = (staged, running) => {
5103
5979
  if (staged === undefined && running === undefined)
5104
5980
  return 0;
@@ -5106,9 +5982,9 @@ var fabricStaged = (staged, running) => {
5106
5982
  return "inconsistent";
5107
5983
  return staged === running ? 0 : 1;
5108
5984
  };
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]) => {
5985
+ var fabricPending = (target) => Effect80.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect80.map(canonical3), Effect80.catchIf(subsystemAbsent, () => Effect80.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect80.flatMap(([staged, running]) => {
5110
5986
  const verdict = fabricStaged(staged, running);
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);
5987
+ return verdict === "inconsistent" ? Effect80.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.")) : Effect80.succeed(verdict);
5112
5988
  }));
5113
5989
  var canonical3 = (value) => JSON.stringify(sortDeep(value));
5114
5990
  var sortDeep = (value) => {
@@ -5121,40 +5997,40 @@ var sortDeep = (value) => {
5121
5997
  }
5122
5998
  return value;
5123
5999
  };
5124
- var sdnPendingCount = (target) => Effect63.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect63.map(([marked, fabric]) => marked + fabric));
6000
+ var sdnPendingCount = (target) => Effect80.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect80.map(([marked, fabric]) => marked + fabric));
5125
6001
 
5126
6002
  // src/proxmox/sdn-apply.ts
5127
6003
  var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
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 }) {
5132
- return yield* read3(olds);
6004
+ var read5 = (props) => sdnPendingCount(props.target).pipe(Effect81.map((pending) => ({ pending })));
6005
+ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect81.succeed(ProxmoxSdnApply.Provider.of({
6006
+ list: () => Effect81.succeed([]),
6007
+ read: Effect81.fn(function* ({ olds }) {
6008
+ return yield* read5(olds);
5133
6009
  }),
5134
- diff: Effect64.fn(function* ({ news }) {
5135
- if (!isResolved17(news))
6010
+ diff: Effect81.fn(function* ({ news }) {
6011
+ if (!isResolved26(news))
5136
6012
  return;
5137
- const { pending } = yield* read3(news);
6013
+ const { pending } = yield* read5(news);
5138
6014
  return pending === 0 ? { action: "noop" } : { action: "update" };
5139
6015
  }),
5140
- reconcile: Effect64.fn(function* ({ news }) {
5141
- const staged = yield* read3(news);
6016
+ reconcile: Effect81.fn(function* ({ news }) {
6017
+ const staged = yield* read5(news);
5142
6018
  if (staged.pending === 0)
5143
6019
  return staged;
5144
6020
  yield* guardSdnApply;
5145
6021
  yield* pve(news.target, "provision", "PUT", "cluster/sdn");
5146
- const after = yield* read3(news);
6022
+ const after = yield* read5(news);
5147
6023
  if (after.pending !== 0) {
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."));
6024
+ return yield* Effect81.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."));
5149
6025
  }
5150
6026
  return after;
5151
6027
  }),
5152
- delete: () => Effect64.void
6028
+ delete: () => Effect81.void
5153
6029
  })));
5154
6030
  // src/proxmox/sdn-subnet.ts
5155
6031
  import { Resource as Resource31 } from "alchemy";
5156
6032
  import * as Provider31 from "alchemy/Provider";
5157
- import * as Effect65 from "effect/Effect";
6033
+ import * as Effect82 from "effect/Effect";
5158
6034
 
5159
6035
  // src/proxmox/sdn-subnet-form.ts
5160
6036
  var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
@@ -5185,7 +6061,7 @@ var updateForm6 = (props) => {
5185
6061
 
5186
6062
  // src/proxmox/sdn-subnet.ts
5187
6063
  var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
5188
- var handlers20 = pveHandlers({
6064
+ var handlers16 = pveHandlers({
5189
6065
  attributes: (live, props) => {
5190
6066
  const liveVnet = text(live["vnet"]);
5191
6067
  if (liveVnet !== "" && liveVnet !== props.vnet)
@@ -5214,11 +6090,11 @@ var handlers20 = pveHandlers({
5214
6090
  path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
5215
6091
  updateForm: updateForm6
5216
6092
  });
5217
- var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect65.succeed(ProxmoxSdnSubnet.Provider.of(handlers20)));
6093
+ var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect82.succeed(ProxmoxSdnSubnet.Provider.of(handlers16)));
5218
6094
  // src/proxmox/sdn-vnet.ts
5219
6095
  import { Resource as Resource32 } from "alchemy";
5220
6096
  import * as Provider32 from "alchemy/Provider";
5221
- import * as Effect66 from "effect/Effect";
6097
+ import * as Effect83 from "effect/Effect";
5222
6098
  var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
5223
6099
  var tagOf = (value) => typeof value === "number" ? value : undefined;
5224
6100
  var shape11 = (props) => {
@@ -5238,7 +6114,7 @@ var updateForm7 = (props) => {
5238
6114
  ];
5239
6115
  return withClears(shape11(props), unset2);
5240
6116
  };
5241
- var handlers21 = pveHandlers({
6117
+ var handlers17 = pveHandlers({
5242
6118
  readRole: "provision",
5243
6119
  attributes: (live, props) => ({
5244
6120
  alias: typeof live["alias"] === "string" ? live["alias"] : "",
@@ -5256,11 +6132,11 @@ var handlers21 = pveHandlers({
5256
6132
  path: (props) => `cluster/sdn/vnets/${props.vnet}`,
5257
6133
  updateForm: updateForm7
5258
6134
  });
5259
- var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect66.succeed(ProxmoxSdnVnet.Provider.of(handlers21)));
6135
+ var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect83.succeed(ProxmoxSdnVnet.Provider.of(handlers17)));
5260
6136
  // src/proxmox/sdn-zone.ts
5261
6137
  import { Resource as Resource33 } from "alchemy";
5262
6138
  import * as Provider33 from "alchemy/Provider";
5263
- import * as Effect67 from "effect/Effect";
6139
+ import * as Effect84 from "effect/Effect";
5264
6140
  var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
5265
6141
  var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
5266
6142
  var body6 = (props) => ({
@@ -5281,7 +6157,7 @@ var clearable = {
5281
6157
  vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
5282
6158
  vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
5283
6159
  };
5284
- var handlers22 = pveHandlers({
6160
+ var handlers18 = pveHandlers({
5285
6161
  readRole: "provision",
5286
6162
  attributes: (live, props) => {
5287
6163
  const liveType = text(live["type"]);
@@ -5308,18 +6184,18 @@ var handlers22 = pveHandlers({
5308
6184
  matches: (attributes2, props) => attributes2.mtu === (props.mtu ?? 0) && attributes2.nodes === csv(props.nodes) && sameIpam(attributes2.ipam, props.ipam) && attributes2.dns === (props.dns ?? "") && attributes2.dnszone === (props.dnszone ?? "") && attributes2.peers === csv(props.peers) && attributes2["vrf-vxlan"] === (props["vrf-vxlan"] ?? 0) && attributes2.tag === (props.tag ?? 0) && attributes2.bridge === (props.bridge ?? ""),
5309
6185
  path: (props) => `cluster/sdn/zones/${props.zone}`,
5310
6186
  updateForm: (props) => {
5311
- const fields = body6(props);
5312
- const clear = clearable[props.type].filter((option) => fields[option] === undefined);
5313
- return withClears(fields, clear);
6187
+ const fields2 = body6(props);
6188
+ const clear = clearable[props.type].filter((option) => fields2[option] === undefined);
6189
+ return withClears(fields2, clear);
5314
6190
  }
5315
6191
  });
5316
- var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect67.succeed(ProxmoxSdnZone.Provider.of(handlers22)));
6192
+ var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect84.succeed(ProxmoxSdnZone.Provider.of(handlers18)));
5317
6193
  // src/proxmox/storage.ts
5318
6194
  import { Resource as Resource34 } from "alchemy";
5319
- import { isResolved as isResolved18 } from "alchemy/Diff";
6195
+ import { isResolved as isResolved27 } from "alchemy/Diff";
5320
6196
  import * as Provider34 from "alchemy/Provider";
5321
6197
  import * as storage2 from "@distilled.cloud/proxmox/storage";
5322
- import * as Effect69 from "effect/Effect";
6198
+ import * as Effect86 from "effect/Effect";
5323
6199
 
5324
6200
  // src/proxmox/storage-form.ts
5325
6201
  var STORAGE_CREATE = "pve:POST /storage";
@@ -5351,7 +6227,7 @@ var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
5351
6227
 
5352
6228
  // src/proxmox/storage-wire.ts
5353
6229
  import * as storage from "@distilled.cloud/proxmox/storage";
5354
- import * as Effect68 from "effect/Effect";
6230
+ import * as Effect85 from "effect/Effect";
5355
6231
  var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
5356
6232
  var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
5357
6233
  var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
@@ -5370,8 +6246,8 @@ var attributesOf9 = (live, props) => {
5370
6246
  type: str2(data["type"], props.type)
5371
6247
  };
5372
6248
  };
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)));
6249
+ var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect85.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
6250
+ var readStorage = (props) => readStorageOrFail(props).pipe(Effect85.catchTag("StorageNotFound", () => Effect85.succeed(undefined)));
5375
6251
  var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
5376
6252
  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);
5377
6253
 
@@ -5379,13 +6255,13 @@ var matches9 = (attributes2, props) => sameList2(props.content, attributes2.cont
5379
6255
  var ProxmoxStorage = Resource34("Proxmox.Storage", {
5380
6256
  defaultRemovalPolicy: "retain"
5381
6257
  });
5382
- var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.succeed(ProxmoxStorage.Provider.of({
5383
- list: () => Effect69.succeed([]),
5384
- read: Effect69.fn(function* ({ olds, output }) {
6258
+ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect86.succeed(ProxmoxStorage.Provider.of({
6259
+ list: () => Effect86.succeed([]),
6260
+ read: Effect86.fn(function* ({ olds, output }) {
5385
6261
  return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
5386
6262
  }),
5387
- diff: Effect69.fn(function* ({ news, output }) {
5388
- if (!isResolved18(news))
6263
+ diff: Effect86.fn(function* ({ news, output }) {
6264
+ if (!isResolved27(news))
5389
6265
  return;
5390
6266
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
5391
6267
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
@@ -5402,7 +6278,7 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.su
5402
6278
  }
5403
6279
  return matches9(live, news) ? { action: "noop" } : { action: "update" };
5404
6280
  }),
5405
- reconcile: Effect69.fn(function* ({ news }) {
6281
+ reconcile: Effect86.fn(function* ({ news }) {
5406
6282
  const before = dropUnreadable7(yield* readStorage(news));
5407
6283
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
5408
6284
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
@@ -5413,23 +6289,23 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect69.su
5413
6289
  }
5414
6290
  const after = dropUnreadable7(yield* readStorage(news));
5415
6291
  if (after === undefined) {
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."));
6292
+ return yield* Effect86.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."));
5417
6293
  }
5418
6294
  return after;
5419
6295
  }),
5420
- delete: Effect69.fn(function* ({ olds }) {
6296
+ delete: Effect86.fn(function* ({ olds }) {
5421
6297
  yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
5422
6298
  })
5423
6299
  })));
5424
6300
  // src/proxmox/zfs-pool.ts
5425
6301
  import { Resource as Resource35 } from "alchemy";
5426
- import { isResolved as isResolved19 } from "alchemy/Diff";
6302
+ import { isResolved as isResolved28 } from "alchemy/Diff";
5427
6303
  import * as Provider35 from "alchemy/Provider";
5428
- import * as Effect72 from "effect/Effect";
6304
+ import * as Effect89 from "effect/Effect";
5429
6305
 
5430
6306
  // src/proxmox/zfs-pool-wire.ts
5431
6307
  import * as nodes10 from "@distilled.cloud/proxmox/nodes";
5432
- import * as Effect70 from "effect/Effect";
6308
+ import * as Effect87 from "effect/Effect";
5433
6309
  var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
5434
6310
  const vdev = entry;
5435
6311
  const nested = leafDevices(vdev.children);
@@ -5442,18 +6318,18 @@ var attributesOf10 = (live, props) => ({
5442
6318
  node: props.node,
5443
6319
  state: text(live.state, "unknown")
5444
6320
  });
5445
- var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect70.map((live) => {
6321
+ var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect87.map((live) => {
5446
6322
  if (live === UNREADABLE)
5447
6323
  return UNREADABLE;
5448
6324
  return text(live.name) === "" ? undefined : attributesOf10(live, props);
5449
6325
  }));
5450
- var readPool = (props) => readPoolOrFail(props).pipe(Effect70.orElseSucceed(() => {
6326
+ var readPool2 = (props) => readPoolOrFail(props).pipe(Effect87.orElseSucceed(() => {
5451
6327
  return;
5452
6328
  }));
5453
6329
  var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
5454
6330
 
5455
6331
  // src/proxmox/zfs-pool-form.ts
5456
- import * as Effect71 from "effect/Effect";
6332
+ import * as Effect88 from "effect/Effect";
5457
6333
  import * as nodes11 from "@distilled.cloud/proxmox/nodes";
5458
6334
  var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
5459
6335
  var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
@@ -5474,25 +6350,25 @@ var toDistilledCreate5 = (props) => {
5474
6350
  };
5475
6351
  };
5476
6352
  var SETTLE_ATTEMPTS = 30;
5477
- var createPool = (props, read4) => Effect71.gen(function* () {
5478
- const existing = yield* read4(props);
6353
+ var createPool2 = (props, read6) => Effect88.gen(function* () {
6354
+ const existing = yield* read6(props);
5479
6355
  if (existing !== undefined)
5480
6356
  return existing;
5481
6357
  if (props.devices === undefined || props.raidlevel === undefined) {
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."));
6358
+ return yield* Effect88.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."));
5483
6359
  }
5484
6360
  yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
5485
6361
  const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
5486
- const settle4 = (attempts) => Effect71.gen(function* () {
5487
- const live = yield* read4(props);
6362
+ const settle3 = (attempts) => Effect88.gen(function* () {
6363
+ const live = yield* read6(props);
5488
6364
  if (live !== undefined || attempts <= 0)
5489
6365
  return live;
5490
- yield* Effect71.sleep("2 seconds");
5491
- return yield* settle4(attempts - 1);
6366
+ yield* Effect88.sleep("2 seconds");
6367
+ return yield* settle3(attempts - 1);
5492
6368
  });
5493
- const after = yield* settle4(SETTLE_ATTEMPTS);
6369
+ const after = yield* settle3(SETTLE_ATTEMPTS);
5494
6370
  if (after === undefined) {
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."));
6371
+ return yield* Effect88.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."));
5496
6372
  }
5497
6373
  return after;
5498
6374
  });
@@ -5502,13 +6378,13 @@ var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.del
5502
6378
  var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
5503
6379
  defaultRemovalPolicy: "retain"
5504
6380
  });
5505
- var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.succeed(ProxmoxZfsPool.Provider.of({
5506
- list: () => Effect72.succeed([]),
5507
- read: Effect72.fn(function* ({ olds, output }) {
5508
- return dropUnreadable8(yield* output === undefined ? readPool(olds) : readPoolOrFail(olds));
6381
+ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect89.succeed(ProxmoxZfsPool.Provider.of({
6382
+ list: () => Effect89.succeed([]),
6383
+ read: Effect89.fn(function* ({ olds, output }) {
6384
+ return dropUnreadable8(yield* output === undefined ? readPool2(olds) : readPoolOrFail(olds));
5509
6385
  }),
5510
- diff: Effect72.fn(function* ({ news, output }) {
5511
- if (!isResolved19(news))
6386
+ diff: Effect89.fn(function* ({ news, output }) {
6387
+ if (!isResolved28(news))
5512
6388
  return;
5513
6389
  yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
5514
6390
  if (output === undefined)
@@ -5524,10 +6400,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect72.su
5524
6400
  }
5525
6401
  return { action: "noop" };
5526
6402
  }),
5527
- reconcile: Effect72.fn(function* ({ news }) {
5528
- return yield* createPool(news, (props) => readPool(props).pipe(Effect72.map(dropUnreadable8)));
6403
+ reconcile: Effect89.fn(function* ({ news }) {
6404
+ return yield* createPool2(news, (props) => readPool2(props).pipe(Effect89.map(dropUnreadable8)));
5529
6405
  }),
5530
- delete: Effect72.fn(function* ({ olds }) {
6406
+ delete: Effect89.fn(function* ({ olds }) {
5531
6407
  yield* destroyPool(olds);
5532
6408
  })
5533
6409
  })));
@@ -5607,5 +6483,5 @@ export {
5607
6483
  violations
5608
6484
  };
5609
6485
 
5610
- //# debugId=34DF7A874403817964756E2164756E21
6486
+ //# debugId=ECA89AEF81AD3AD164756E2164756E21
5611
6487
  //# sourceMappingURL=index.js.map