@homeflare/alchemy 0.37.2 → 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 (155) 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/distilled-pbs.d.ts +12 -0
  8. package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
  9. package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
  10. package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
  11. package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
  12. package/dist/proxmox/fake-pve.d.ts +1 -1
  13. package/dist/proxmox/fake-pve.d.ts.map +1 -1
  14. package/dist/proxmox/index.js +1501 -607
  15. package/dist/proxmox/index.js.map +48 -24
  16. package/dist/proxmox/live-core-fixtures.d.ts +40 -0
  17. package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
  18. package/dist/proxmox/metric-server-config.d.ts +49 -0
  19. package/dist/proxmox/metric-server-config.d.ts.map +1 -0
  20. package/dist/proxmox/metric-server-distilled.d.ts +43 -0
  21. package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
  22. package/dist/proxmox/metric-server.d.ts +2 -10
  23. package/dist/proxmox/metric-server.d.ts.map +1 -1
  24. package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
  25. package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
  26. package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
  27. package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
  28. package/dist/proxmox/notification-matcher.d.ts +5 -5
  29. package/dist/proxmox/notification-matcher.d.ts.map +1 -1
  30. package/dist/proxmox/notification-sdk-input.d.ts +22 -0
  31. package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
  32. package/dist/proxmox/notification-target-distilled.d.ts +12 -0
  33. package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
  34. package/dist/proxmox/notification-target-form.d.ts +2 -0
  35. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  36. package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
  37. package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
  38. package/dist/proxmox/notification-target-spec.d.ts +5 -0
  39. package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
  40. package/dist/proxmox/notification-target.d.ts +4 -5
  41. package/dist/proxmox/notification-target.d.ts.map +1 -1
  42. package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
  43. package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
  44. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
  45. package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
  46. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
  47. package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
  48. package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
  49. package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
  50. package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
  51. package/dist/proxmox/pbs-datastore.d.ts +5 -6
  52. package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
  53. package/dist/proxmox/pbs-notification-input.d.ts +18 -0
  54. package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
  55. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
  56. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
  57. package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
  58. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
  59. package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
  60. package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
  61. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
  62. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  63. package/dist/proxmox/pbs-notification-target.d.ts +1 -1
  64. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
  65. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
  66. package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
  67. package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
  68. package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
  69. package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
  70. package/dist/proxmox/pbs-prune-job.d.ts +9 -8
  71. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
  72. package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
  73. package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
  74. package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
  75. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
  76. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
  77. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
  78. package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
  79. package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
  80. package/dist/proxmox/pbs-sync-job.d.ts +9 -39
  81. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
  82. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
  83. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
  84. package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
  85. package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
  86. package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
  87. package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
  88. package/dist/proxmox/pbs-verify-job.d.ts +12 -12
  89. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
  90. package/dist/proxmox/pool.d.ts +10 -1
  91. package/dist/proxmox/pool.d.ts.map +1 -1
  92. package/docs/pbs-distilled-jobs.md +31 -0
  93. package/docs/pbs-notifications.md +8 -2
  94. package/docs/proxmox-notification-sdk.md +28 -0
  95. package/docs/proxmox-sdk-migration.md +60 -0
  96. package/docs/upstream-conformance-families.md +6 -2
  97. package/package.json +1 -1
  98. package/src/proxmox/adopt-noop.test.ts +4 -5
  99. package/src/proxmox/backup-job-config.ts +95 -0
  100. package/src/proxmox/backup-job-distilled.ts +86 -0
  101. package/src/proxmox/backup-job.ts +78 -116
  102. package/src/proxmox/constraints.test.ts +9 -8
  103. package/src/proxmox/distilled-pbs.test.ts +87 -0
  104. package/src/proxmox/distilled-pbs.ts +50 -0
  105. package/src/proxmox/fake-pbs-jobs.ts +99 -0
  106. package/src/proxmox/fake-pbs-notify.ts +4 -1
  107. package/src/proxmox/fake-pve.ts +3 -2
  108. package/src/proxmox/live-core-fixtures.ts +126 -0
  109. package/src/proxmox/live-core-lifecycle.test.ts +115 -0
  110. package/src/proxmox/live-core-wire.test.ts +190 -0
  111. package/src/proxmox/metric-server-config.ts +87 -0
  112. package/src/proxmox/metric-server-distilled.ts +81 -0
  113. package/src/proxmox/metric-server.ts +77 -88
  114. package/src/proxmox/notification-distilled.test.ts +162 -0
  115. package/src/proxmox/notification-matcher-distilled.ts +60 -0
  116. package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
  117. package/src/proxmox/notification-matcher.ts +9 -26
  118. package/src/proxmox/notification-replacement.test.ts +190 -0
  119. package/src/proxmox/notification-sdk-input.ts +39 -0
  120. package/src/proxmox/notification-target-distilled.ts +160 -0
  121. package/src/proxmox/notification-target-form.ts +2 -0
  122. package/src/proxmox/notification-target-lifecycle.ts +75 -0
  123. package/src/proxmox/notification-target-spec.ts +68 -0
  124. package/src/proxmox/notification-target.ts +6 -68
  125. package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
  126. package/src/proxmox/pbs-datastore-form.ts +18 -49
  127. package/src/proxmox/pbs-datastore-guard.ts +7 -4
  128. package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
  129. package/src/proxmox/pbs-datastore-wire.ts +94 -0
  130. package/src/proxmox/pbs-datastore.ts +7 -133
  131. package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
  132. package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
  133. package/src/proxmox/pbs-notification-errors.test.ts +93 -0
  134. package/src/proxmox/pbs-notification-input.ts +25 -0
  135. package/src/proxmox/pbs-notification-mail.test.ts +64 -0
  136. package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
  137. package/src/proxmox/pbs-notification-matcher.ts +70 -17
  138. package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
  139. package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
  140. package/src/proxmox/pbs-notification-target.ts +1 -1
  141. package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
  142. package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
  143. package/src/proxmox/pbs-prune-job-spec.ts +53 -0
  144. package/src/proxmox/pbs-prune-job-wire.ts +68 -0
  145. package/src/proxmox/pbs-prune-job.ts +10 -58
  146. package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
  147. package/src/proxmox/pbs-sync-job-form.ts +7 -6
  148. package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
  149. package/src/proxmox/pbs-sync-job-wire.ts +71 -0
  150. package/src/proxmox/pbs-sync-job.ts +10 -41
  151. package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
  152. package/src/proxmox/pbs-verify-job-spec.ts +76 -0
  153. package/src/proxmox/pbs-verify-job-wire.ts +58 -0
  154. package/src/proxmox/pbs-verify-job.ts +13 -85
  155. 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,26 +1856,26 @@ 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
- import * as Effect17 from "effect/Effect";
1874
+ import * as Effect18 from "effect/Effect";
1792
1875
 
1793
1876
  // src/proxmox/ceph-fs-distilled.ts
1794
1877
  import * as nodes2 from "@distilled.cloud/proxmox/nodes";
1795
- import * as Effect16 from "effect/Effect";
1878
+ import * as Effect17 from "effect/Effect";
1796
1879
 
1797
1880
  // src/proxmox/ceph-fs-wire.ts
1798
1881
  var readRow = (live, props) => {
@@ -1820,40 +1903,40 @@ var notCreated2 = (props) => new Error(`${objectPath(props)}: the create task fi
1820
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.");
1821
1904
 
1822
1905
  // src/proxmox/ceph-fs-distilled.ts
1823
- var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect16.map((rows) => readRow(rows, props)));
1906
+ var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect17.map((rows) => readRow(rows, props)));
1824
1907
  var toDistilledCreate = (props) => ({
1825
1908
  ...createForm2(props),
1826
1909
  name: props.name,
1827
1910
  node: props.node
1828
1911
  });
1829
- var settleTask = (props, upid) => Effect16.gen(function* () {
1912
+ var settleTask = (props, upid) => Effect17.gen(function* () {
1830
1913
  for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
1831
- const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect16.orElseSucceed(() => {
1914
+ const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect17.orElseSucceed(() => {
1832
1915
  return;
1833
1916
  }));
1834
1917
  if (status?.status === "stopped") {
1835
1918
  if (status.exitstatus === "OK")
1836
1919
  return;
1837
- return yield* Effect16.die(new Error(`${objectPath(props)}: PVE task ${upid} finished with "${status.exitstatus ?? "no exit status"}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${props.node}/tasks/${upid}/log\`.`));
1920
+ return yield* Effect17.die(new Error(`${objectPath(props)}: PVE task ${upid} finished with "${status.exitstatus ?? "no exit status"}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${props.node}/tasks/${upid}/log\`.`));
1838
1921
  }
1839
- yield* Effect16.sleep(POLL_SECONDS * 1000);
1922
+ yield* Effect17.sleep(POLL_SECONDS * 1000);
1840
1923
  }
1841
- return yield* Effect16.die(new Error(`${objectPath(props)}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its status could not be read. Check the ` + "task log, and check that the `read` role holds Sys.Audit on " + `/nodes/${props.node} -- each call mints a new token, so the poller is never the task ` + "owner and is answered 403 rather than told to wait."));
1924
+ return yield* Effect17.die(new Error(`${objectPath(props)}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its status could not be read. Check the ` + "task log, and check that the `read` role holds Sys.Audit on " + `/nodes/${props.node} -- each call mints a new token, so the poller is never the task ` + "owner and is answered 403 rather than told to wait."));
1842
1925
  });
1843
- var createFs = (props) => Effect16.gen(function* () {
1926
+ var createFs = (props) => Effect17.gen(function* () {
1844
1927
  const upid = yield* runPve(props.target, "provision", true, nodes2.updateNodeCephFs(toDistilledCreate(props)));
1845
1928
  if (upid === "") {
1846
- return yield* Effect16.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.'));
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.'));
1847
1930
  }
1848
1931
  yield* settleTask(props, upid);
1849
1932
  });
1850
- var destroyFs = (props) => Effect16.gen(function* () {
1933
+ var destroyFs = (props) => Effect17.gen(function* () {
1851
1934
  const upid = yield* runPve(props.target, "provision", true, nodes2.deleteNodeCephFs({
1852
1935
  node: props.node,
1853
1936
  name: props.name,
1854
1937
  ...props["remove-pools"] === true ? { remove_pools: "1" } : {},
1855
1938
  ...props["remove-storages"] === true ? { remove_storages: "1" } : {}
1856
- })).pipe(Effect16.catchTag("CephFsNotFound", () => Effect16.succeed("")));
1939
+ })).pipe(Effect17.catchTag("CephFsNotFound", () => Effect17.succeed("")));
1857
1940
  if (upid !== "")
1858
1941
  yield* settleTask(props, upid);
1859
1942
  });
@@ -1862,13 +1945,13 @@ var destroyFs = (props) => Effect16.gen(function* () {
1862
1945
  var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
1863
1946
  defaultRemovalPolicy: "retain"
1864
1947
  });
1865
- var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect17.succeed(ProxmoxCephFs.Provider.of({
1866
- list: () => Effect17.succeed([]),
1867
- read: Effect17.fn(function* ({ olds }) {
1948
+ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succeed(ProxmoxCephFs.Provider.of({
1949
+ list: () => Effect18.succeed([]),
1950
+ read: Effect18.fn(function* ({ olds }) {
1868
1951
  return yield* readFs(olds);
1869
1952
  }),
1870
- diff: Effect17.fn(function* ({ news, output }) {
1871
- if (!isResolved5(news))
1953
+ diff: Effect18.fn(function* ({ news, output }) {
1954
+ if (!isResolved6(news))
1872
1955
  return;
1873
1956
  yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
1874
1957
  if (output !== undefined && news.name !== output.name) {
@@ -1883,34 +1966,34 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect17.succe
1883
1966
  }
1884
1967
  return { action: "noop" };
1885
1968
  }),
1886
- reconcile: Effect17.fn(function* ({ news }) {
1969
+ reconcile: Effect18.fn(function* ({ news }) {
1887
1970
  const live = yield* readFs(news);
1888
1971
  if (live !== undefined)
1889
1972
  return live;
1890
1973
  yield* createFs(news);
1891
1974
  const after = yield* readFs(news);
1892
1975
  if (after === undefined)
1893
- return yield* Effect17.die(notCreated2(news));
1976
+ return yield* Effect18.die(notCreated2(news));
1894
1977
  return after;
1895
1978
  }),
1896
- delete: Effect17.fn(function* ({ olds }) {
1979
+ delete: Effect18.fn(function* ({ olds }) {
1897
1980
  if ((yield* readFs(olds)) === undefined)
1898
1981
  return;
1899
1982
  yield* destroyFs(olds);
1900
1983
  const after = yield* readFs(olds);
1901
1984
  if (after !== undefined)
1902
- return yield* Effect17.die(notDestroyed(olds));
1985
+ return yield* Effect18.die(notDestroyed(olds));
1903
1986
  })
1904
1987
  })));
1905
1988
  // src/proxmox/ceph-osd.ts
1906
1989
  import { Resource as Resource7 } from "alchemy";
1907
- import { isResolved as isResolved6 } from "alchemy/Diff";
1990
+ import { isResolved as isResolved7 } from "alchemy/Diff";
1908
1991
  import * as Provider7 from "alchemy/Provider";
1909
- import * as Effect20 from "effect/Effect";
1992
+ import * as Effect21 from "effect/Effect";
1910
1993
 
1911
1994
  // src/proxmox/ceph-osd-tree.ts
1912
1995
  import * as nodes3 from "@distilled.cloud/proxmox/nodes";
1913
- import * as Effect18 from "effect/Effect";
1996
+ import * as Effect19 from "effect/Effect";
1914
1997
  var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
1915
1998
  var osdLeaves = (tree) => {
1916
1999
  const root = tree["root"];
@@ -1936,23 +2019,23 @@ var osdAttributes = (leaf, props) => ({
1936
2019
  status: text(leaf["status"]),
1937
2020
  total_space: num(leaf["total_space"], 0)
1938
2021
  });
1939
- var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect18.map((t) => {
2022
+ var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect19.map((t) => {
1940
2023
  const leaf = findOsd(t, props.osdid);
1941
2024
  return leaf === undefined ? undefined : osdAttributes(leaf, props);
1942
2025
  }));
1943
2026
 
1944
2027
  // src/proxmox/ceph-osd-write.ts
1945
2028
  import * as nodes4 from "@distilled.cloud/proxmox/nodes";
1946
- import * as Effect19 from "effect/Effect";
2029
+ import * as Effect20 from "effect/Effect";
1947
2030
  var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
1948
2031
  var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
1949
2032
  var createOsdForm = (props, dev) => ({
1950
2033
  dev,
1951
2034
  ...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
1952
2035
  });
1953
- var createOsd = Effect19.fn(function* (news) {
2036
+ var createOsd = Effect20.fn(function* (news) {
1954
2037
  if (news.dev === undefined) {
1955
- return yield* Effect19.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
2038
+ return yield* Effect20.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
1956
2039
  }
1957
2040
  const form = createOsdForm(news, news.dev);
1958
2041
  yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
@@ -1963,7 +2046,7 @@ var createOsd = Effect19.fn(function* (news) {
1963
2046
  }));
1964
2047
  const created = yield* readOsd(news);
1965
2048
  if (created === undefined) {
1966
- return yield* Effect19.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
2049
+ return yield* Effect20.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
1967
2050
  }
1968
2051
  return created;
1969
2052
  });
@@ -1979,16 +2062,16 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
1979
2062
  });
1980
2063
  var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
1981
2064
  var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
1982
- var handlers3 = {
1983
- list: () => Effect20.succeed([]),
1984
- read: Effect20.fn(function* ({ olds }) {
2065
+ var handlers2 = {
2066
+ list: () => Effect21.succeed([]),
2067
+ read: Effect21.fn(function* ({ olds }) {
1985
2068
  return yield* readOsd(olds);
1986
2069
  }),
1987
- diff: Effect20.fn(function* ({
2070
+ diff: Effect21.fn(function* ({
1988
2071
  news,
1989
2072
  output
1990
2073
  }) {
1991
- if (output === undefined || !isResolved6(news))
2074
+ if (output === undefined || !isResolved7(news))
1992
2075
  return;
1993
2076
  const live = yield* readOsd(news);
1994
2077
  if (live === undefined)
@@ -1997,27 +2080,27 @@ var handlers3 = {
1997
2080
  return { action: "update" };
1998
2081
  return settled(live, news) ? { action: "noop" } : { action: "update" };
1999
2082
  }),
2000
- reconcile: Effect20.fn(function* ({ news }) {
2083
+ reconcile: Effect21.fn(function* ({ news }) {
2001
2084
  const live = yield* readOsd(news);
2002
2085
  if (live === undefined) {
2003
2086
  return yield* createOsd(news);
2004
2087
  }
2005
2088
  if (!settled(live, news)) {
2006
- return yield* Effect20.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
2089
+ return yield* Effect21.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
2007
2090
  }
2008
2091
  return live;
2009
2092
  }),
2010
- delete: Effect20.fn(function* ({ olds }) {
2093
+ delete: Effect21.fn(function* ({ olds }) {
2011
2094
  yield* destroyOsd(olds);
2012
2095
  })
2013
2096
  };
2014
- var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect20.succeed(ProxmoxCephOsd.Provider.of(handlers3)));
2097
+ var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect21.succeed(ProxmoxCephOsd.Provider.of(handlers2)));
2015
2098
  // src/proxmox/ceph-pool.ts
2016
2099
  import { Resource as Resource8 } from "alchemy";
2017
- import { isResolved as isResolved7 } from "alchemy/Diff";
2100
+ import { isResolved as isResolved8 } from "alchemy/Diff";
2018
2101
  import * as Provider8 from "alchemy/Provider";
2019
2102
  import * as nodes7 from "@distilled.cloud/proxmox/nodes";
2020
- import * as Effect23 from "effect/Effect";
2103
+ import * as Effect24 from "effect/Effect";
2021
2104
 
2022
2105
  // src/proxmox/ceph-pool-form.ts
2023
2106
  var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
@@ -2046,21 +2129,21 @@ var createBody = (props) => ({
2046
2129
 
2047
2130
  // src/proxmox/ceph-pool-settle.ts
2048
2131
  import * as nodes5 from "@distilled.cloud/proxmox/nodes";
2049
- import * as Effect21 from "effect/Effect";
2050
- var confirmAbsent = (props, describe3) => runPve(props.target, "read", false, nodes5.listNodeCephPool({ node: props.node })).pipe(Effect21.flatMap((rows) => rows.some((row) => row.pool_name === props.name) ? Effect21.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.")) : Effect21.void));
2051
- var settle = (props, read, done) => Effect21.gen(function* () {
2132
+ import * as Effect22 from "effect/Effect";
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));
2134
+ var settle = (props, read, done) => Effect22.gen(function* () {
2052
2135
  for (let attempt = 0;attempt < 30; attempt += 1) {
2053
2136
  const live = yield* read(props);
2054
2137
  if (done(live))
2055
2138
  return live;
2056
- yield* Effect21.sleep("2 seconds");
2139
+ yield* Effect22.sleep("2 seconds");
2057
2140
  }
2058
2141
  return yield* read(props);
2059
2142
  });
2060
2143
 
2061
2144
  // src/proxmox/ceph-pool-wire.ts
2062
2145
  import * as nodes6 from "@distilled.cloud/proxmox/nodes";
2063
- import * as Effect22 from "effect/Effect";
2146
+ import * as Effect23 from "effect/Effect";
2064
2147
  var attributesOf3 = (live, props) => {
2065
2148
  const row = live;
2066
2149
  if (typeof row["name"] !== "string")
@@ -2083,7 +2166,7 @@ var attributesOf3 = (live, props) => {
2083
2166
  target_size_ratio: num(row["target_size_ratio"], UNSET)
2084
2167
  };
2085
2168
  };
2086
- var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(Effect22.map((live) => attributesOf3(live, props)), Effect22.catchTag("CephPoolNotFound", () => Effect22.succeed(undefined)));
2169
+ var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(Effect23.map((live) => attributesOf3(live, props)), Effect23.catchTag("CephPoolNotFound", () => Effect23.succeed(undefined)));
2087
2170
  var CEPH_POOL_CREATE = "pve:POST /nodes/{node}/ceph/pool";
2088
2171
  var CEPH_POOL_UPDATE = "pve:PUT /nodes/{node}/ceph/pool/{name}";
2089
2172
  var toDistilledCreate2 = (props) => ({
@@ -2101,11 +2184,11 @@ var ProxmoxCephPool = Resource8("Proxmox.CephPool", {
2101
2184
  defaultRemovalPolicy: "retain"
2102
2185
  });
2103
2186
  var matches3 = (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size);
2104
- var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect23.succeed(ProxmoxCephPool.Provider.of({
2105
- list: () => Effect23.succeed([]),
2187
+ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.succeed(ProxmoxCephPool.Provider.of({
2188
+ list: () => Effect24.succeed([]),
2106
2189
  read: ({ olds }) => readPoolStatus(olds),
2107
- diff: Effect23.fn(function* ({ news, output }) {
2108
- if (!isResolved7(news))
2190
+ diff: Effect24.fn(function* ({ news, output }) {
2191
+ if (!isResolved8(news))
2109
2192
  return;
2110
2193
  yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
2111
2194
  yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
@@ -2118,7 +2201,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect23.s
2118
2201
  }
2119
2202
  return matches3(live, news) ? { action: "noop" } : { action: "update" };
2120
2203
  }),
2121
- reconcile: Effect23.fn(function* ({ news }) {
2204
+ reconcile: Effect24.fn(function* ({ news }) {
2122
2205
  const live = yield* readPoolStatus(news);
2123
2206
  let upid;
2124
2207
  if (live === undefined) {
@@ -2134,23 +2217,23 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect23.s
2134
2217
  }
2135
2218
  const after = yield* settle(news, readPoolStatus, (row) => row !== undefined && matches3(row, news));
2136
2219
  if (after === undefined || !matches3(after, news)) {
2137
- return yield* Effect23.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."));
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."));
2138
2221
  }
2139
2222
  return after;
2140
2223
  }),
2141
- delete: Effect23.fn(function* ({ olds }) {
2224
+ delete: Effect24.fn(function* ({ olds }) {
2142
2225
  yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
2143
2226
  const left = yield* settle(olds, readPoolStatus, (row) => row === undefined);
2144
2227
  if (left !== undefined) {
2145
- return yield* Effect23.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."));
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."));
2146
2229
  }
2147
2230
  })
2148
2231
  })));
2149
2232
  // src/proxmox/firewall-alias.ts
2150
2233
  import { Resource as Resource9 } from "alchemy";
2151
- import { isResolved as isResolved8 } from "alchemy/Diff";
2234
+ import { isResolved as isResolved9 } from "alchemy/Diff";
2152
2235
  import * as Provider9 from "alchemy/Provider";
2153
- import * as Effect24 from "effect/Effect";
2236
+ import * as Effect25 from "effect/Effect";
2154
2237
 
2155
2238
  // src/proxmox/firewall-alias-form.ts
2156
2239
  var fold = (name) => name.trim().toLowerCase();
@@ -2170,7 +2253,7 @@ var body = (props) => {
2170
2253
 
2171
2254
  // src/proxmox/firewall-alias.ts
2172
2255
  var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
2173
- var handlers4 = pveHandlers({
2256
+ var handlers3 = pveHandlers({
2174
2257
  attributes: (live, props) => {
2175
2258
  const address = cidr(live["cidr"]);
2176
2259
  return address === "" ? undefined : {
@@ -2190,25 +2273,25 @@ var handlers4 = pveHandlers({
2190
2273
  path: (props) => `cluster/firewall/aliases/${props.name}`,
2191
2274
  updateForm: body
2192
2275
  });
2193
- var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect24.succeed(ProxmoxFirewallAlias.Provider.of({
2194
- ...handlers4,
2195
- diff: Effect24.fn(function* ({ news, output }) {
2196
- if (output !== undefined && isResolved8(news) && fold(news.name) !== fold(output.name)) {
2276
+ var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of({
2277
+ ...handlers3,
2278
+ diff: Effect25.fn(function* ({ news, output }) {
2279
+ if (output !== undefined && isResolved9(news) && fold(news.name) !== fold(output.name)) {
2197
2280
  return { action: "replace" };
2198
2281
  }
2199
- return yield* handlers4.diff({ news, output });
2282
+ return yield* handlers3.diff({ news, output });
2200
2283
  })
2201
2284
  })));
2202
2285
  // src/proxmox/group.ts
2203
2286
  import { Resource as Resource10 } from "alchemy";
2204
- import { isResolved as isResolved9 } from "alchemy/Diff";
2287
+ import { isResolved as isResolved10 } from "alchemy/Diff";
2205
2288
  import * as Provider10 from "alchemy/Provider";
2206
2289
  import * as access4 from "@distilled.cloud/proxmox/access";
2207
- import * as Effect26 from "effect/Effect";
2290
+ import * as Effect27 from "effect/Effect";
2208
2291
 
2209
2292
  // src/proxmox/group-wire.ts
2210
2293
  import * as access3 from "@distilled.cloud/proxmox/access";
2211
- import * as Effect25 from "effect/Effect";
2294
+ import * as Effect26 from "effect/Effect";
2212
2295
  var GROUP_CREATE = "pve:POST /access/groups";
2213
2296
  var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
2214
2297
  var storedComment = (comment2) => {
@@ -2233,21 +2316,21 @@ var attributesOf4 = (live, props) => ({
2233
2316
  members: memberList(live.members)
2234
2317
  });
2235
2318
  var matches4 = (attributes, props) => attributes.comment === storedComment(props.comment);
2236
- var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect25.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf4(live, props)));
2237
- var readGroup = (props) => readGroupOrFail(props).pipe(Effect25.catchTag("GroupNotFound", () => Effect25.succeed(undefined)));
2319
+ var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect26.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf4(live, props)));
2320
+ var readGroup = (props) => readGroupOrFail(props).pipe(Effect26.catchTag("GroupNotFound", () => Effect26.succeed(undefined)));
2238
2321
  var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
2239
2322
 
2240
2323
  // src/proxmox/group.ts
2241
2324
  var ProxmoxGroup = Resource10("Proxmox.Group", {
2242
2325
  defaultRemovalPolicy: "retain"
2243
2326
  });
2244
- var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect26.succeed(ProxmoxGroup.Provider.of({
2245
- list: () => Effect26.succeed([]),
2246
- read: Effect26.fn(function* ({ olds, output }) {
2327
+ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succeed(ProxmoxGroup.Provider.of({
2328
+ list: () => Effect27.succeed([]),
2329
+ read: Effect27.fn(function* ({ olds, output }) {
2247
2330
  return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
2248
2331
  }),
2249
- diff: Effect26.fn(function* ({ news, output }) {
2250
- if (!isResolved9(news))
2332
+ diff: Effect27.fn(function* ({ news, output }) {
2333
+ if (!isResolved10(news))
2251
2334
  return;
2252
2335
  yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
2253
2336
  yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
@@ -2264,7 +2347,7 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect26.succee
2264
2347
  }
2265
2348
  return matches4(live, news) ? { action: "noop" } : { action: "update" };
2266
2349
  }),
2267
- reconcile: Effect26.fn(function* ({ news }) {
2350
+ reconcile: Effect27.fn(function* ({ news }) {
2268
2351
  const before = dropUnreadable3(yield* readGroup(news));
2269
2352
  yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
2270
2353
  yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
@@ -2275,18 +2358,18 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect26.succee
2275
2358
  }
2276
2359
  const after = dropUnreadable3(yield* readGroup(news));
2277
2360
  if (after === undefined) {
2278
- return yield* Effect26.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
2361
+ return yield* Effect27.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
2279
2362
  }
2280
2363
  return after;
2281
2364
  }),
2282
- delete: Effect26.fn(function* ({ olds }) {
2365
+ delete: Effect27.fn(function* ({ olds }) {
2283
2366
  yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
2284
2367
  })
2285
2368
  })));
2286
2369
  // src/proxmox/ha-resource.ts
2287
2370
  import { Resource as Resource11 } from "alchemy";
2288
2371
  import * as Provider11 from "alchemy/Provider";
2289
- import * as Effect27 from "effect/Effect";
2372
+ import * as Effect28 from "effect/Effect";
2290
2373
  var ProxmoxHaResource = Resource11("Proxmox.HaResource");
2291
2374
  var kind = (sid) => sid.split(":")[0] ?? "";
2292
2375
  var shape3 = (props) => ({
@@ -2295,7 +2378,7 @@ var shape3 = (props) => ({
2295
2378
  max_restart: String(props.max_restart ?? 1),
2296
2379
  state: props.state ?? "started"
2297
2380
  });
2298
- var handlers5 = pveHandlers({
2381
+ var handlers4 = pveHandlers({
2299
2382
  attributes: (live, props) => ({
2300
2383
  comment: typeof live["comment"] === "string" ? live["comment"] : "",
2301
2384
  max_relocate: num(live["max_relocate"], 1),
@@ -2314,11 +2397,11 @@ var handlers5 = pveHandlers({
2314
2397
  path: (props) => `cluster/ha/resources/${props.sid}`,
2315
2398
  updateForm: shape3
2316
2399
  });
2317
- var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect27.succeed(ProxmoxHaResource.Provider.of(handlers5)));
2400
+ var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect28.succeed(ProxmoxHaResource.Provider.of(handlers4)));
2318
2401
  // src/proxmox/ha-rule.ts
2319
2402
  import { Resource as Resource12 } from "alchemy";
2320
2403
  import * as Provider12 from "alchemy/Provider";
2321
- import * as Effect28 from "effect/Effect";
2404
+ import * as Effect29 from "effect/Effect";
2322
2405
 
2323
2406
  // src/proxmox/ha-rule-form.ts
2324
2407
  var nodeList = (value) => {
@@ -2366,7 +2449,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
2366
2449
 
2367
2450
  // src/proxmox/ha-rule.ts
2368
2451
  var ProxmoxHaRule = Resource12("Proxmox.HaRule");
2369
- var handlers6 = pveHandlers({
2452
+ var handlers5 = pveHandlers({
2370
2453
  attributes: (live, props) => {
2371
2454
  const liveType = text(live["type"]);
2372
2455
  if (liveType !== "" && liveType !== props.type)
@@ -2395,43 +2478,43 @@ var handlers6 = pveHandlers({
2395
2478
  return withClears(fields, clear);
2396
2479
  }
2397
2480
  });
2398
- var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect28.succeed(ProxmoxHaRule.Provider.of(handlers6)));
2481
+ var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect29.succeed(ProxmoxHaRule.Provider.of(handlers5)));
2399
2482
  // src/proxmox/lxc.ts
2400
2483
  import { Resource as Resource13 } from "alchemy";
2401
- import { isResolved as isResolved10 } from "alchemy/Diff";
2484
+ import { isResolved as isResolved11 } from "alchemy/Diff";
2402
2485
  import * as Provider13 from "alchemy/Provider";
2403
- import * as Effect38 from "effect/Effect";
2486
+ import * as Effect39 from "effect/Effect";
2404
2487
 
2405
2488
  // src/ownership/adopt.ts
2406
2489
  import { AdoptPolicy } from "alchemy/AdoptPolicy";
2407
2490
  import { AlchemyContext } from "alchemy/AlchemyContext";
2408
2491
  import { Stack as Stack3 } from "alchemy/Stack";
2409
- import * as Effect32 from "effect/Effect";
2492
+ import * as Effect33 from "effect/Effect";
2410
2493
  import * as Option5 from "effect/Option";
2411
2494
 
2412
2495
  // src/ownership/resume.ts
2413
2496
  import { Unowned } from "alchemy/AdoptPolicy";
2414
2497
  import { Artifacts } from "alchemy/Artifacts";
2415
- import * as Effect29 from "effect/Effect";
2498
+ import * as Effect30 from "effect/Effect";
2416
2499
  import * as Option2 from "effect/Option";
2417
2500
  var PROVEN = "homeflare/ownership/resumes";
2418
2501
  var UNFINISHED = "homeflare/ownership/unfinished";
2419
- var note = (key, instanceId) => Effect29.gen(function* () {
2420
- const bag = yield* Effect29.serviceOption(Artifacts);
2502
+ var note = (key, instanceId) => Effect30.gen(function* () {
2503
+ const bag = yield* Effect30.serviceOption(Artifacts);
2421
2504
  if (Option2.isSome(bag))
2422
2505
  yield* bag.value.set(key, instanceId);
2423
2506
  });
2424
- var noted = (key, instanceId) => Effect29.gen(function* () {
2425
- const bag = yield* Effect29.serviceOption(Artifacts);
2507
+ var noted = (key, instanceId) => Effect30.gen(function* () {
2508
+ const bag = yield* Effect30.serviceOption(Artifacts);
2426
2509
  if (Option2.isNone(bag))
2427
2510
  return false;
2428
2511
  return (yield* bag.value.get(key)) === instanceId;
2429
2512
  });
2430
- var noteUnfinished = (instanceId) => Effect29.as(note(UNFINISHED, instanceId), undefined);
2431
- var noteResume = (instanceId, recovered) => Effect29.gen(function* () {
2513
+ var noteUnfinished = (instanceId) => Effect30.as(note(UNFINISHED, instanceId), undefined);
2514
+ var noteResume = (instanceId, recovered) => Effect30.gen(function* () {
2432
2515
  yield* noteUnfinished(instanceId);
2433
- const unproven = () => Effect29.succeed(undefined);
2434
- const found = yield* recovered.pipe(Effect29.catch(unproven), Effect29.catchDefect(unproven));
2516
+ const unproven = () => Effect30.succeed(undefined);
2517
+ const found = yield* recovered.pipe(Effect30.catch(unproven), Effect30.catchDefect(unproven));
2435
2518
  if (found === undefined || found === null || Unowned.is(found))
2436
2519
  return;
2437
2520
  yield* note(PROVEN, instanceId);
@@ -2442,7 +2525,7 @@ var provingResumes = (service) => {
2442
2525
  const { diff, read } = service;
2443
2526
  if (diff === undefined || read === undefined)
2444
2527
  return service;
2445
- const proving = (input) => Effect29.gen(function* () {
2528
+ const proving = (input) => Effect30.gen(function* () {
2446
2529
  if (input.output === undefined) {
2447
2530
  yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
2448
2531
  }
@@ -2454,12 +2537,12 @@ var provingResumes = (service) => {
2454
2537
  // src/ownership/rows.ts
2455
2538
  import { Stack as Stack2 } from "alchemy/Stack";
2456
2539
  import { State, isActionState } from "alchemy/State/State";
2457
- import * as Effect31 from "effect/Effect";
2540
+ import * as Effect32 from "effect/Effect";
2458
2541
  import * as Option4 from "effect/Option";
2459
2542
 
2460
2543
  // src/ownership/whole.ts
2461
2544
  import { Stack } from "alchemy/Stack";
2462
- import * as Effect30 from "effect/Effect";
2545
+ import * as Effect31 from "effect/Effect";
2463
2546
  import * as Option3 from "effect/Option";
2464
2547
  var absent = (value) => value === undefined || value === null;
2465
2548
  var isPlainObject = (value) => {
@@ -2485,22 +2568,22 @@ var carries = (declared, recorded) => {
2485
2568
  }
2486
2569
  return true;
2487
2570
  };
2488
- var wholeDeclaration = (fqn, props) => Effect30.map(Effect30.serviceOption(Stack), (stack) => {
2571
+ var wholeDeclaration = (fqn, props) => Effect31.map(Effect31.serviceOption(Stack), (stack) => {
2489
2572
  const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
2490
2573
  return declared !== undefined && carries(declared.Props, props);
2491
2574
  });
2492
2575
 
2493
2576
  // src/ownership/rows.ts
2494
- var storeOf = Effect31.gen(function* () {
2495
- const stack = yield* Effect31.serviceOption(Stack2);
2496
- const state = yield* Effect31.serviceOption(State);
2577
+ var storeOf = Effect32.gen(function* () {
2578
+ const stack = yield* Effect32.serviceOption(Stack2);
2579
+ const state = yield* Effect32.serviceOption(State);
2497
2580
  if (Option4.isNone(stack) || Option4.isNone(state))
2498
2581
  return;
2499
2582
  const store = yield* state.value;
2500
2583
  const where = { stack: stack.value.name, stage: stack.value.stage };
2501
2584
  return { resources: stack.value.resources, store, where };
2502
2585
  });
2503
- var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect31.orElseSucceed(() => {
2586
+ var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect32.orElseSucceed(() => {
2504
2587
  return;
2505
2588
  }));
2506
2589
  var generationOf = (row, instanceId) => {
@@ -2513,7 +2596,7 @@ var generationOf = (row, instanceId) => {
2513
2596
  }
2514
2597
  return;
2515
2598
  };
2516
- var recordedGeneration = (fqn, instanceId) => Effect31.gen(function* () {
2599
+ var recordedGeneration = (fqn, instanceId) => Effect32.gen(function* () {
2517
2600
  const found = yield* storeOf;
2518
2601
  if (found === undefined)
2519
2602
  return;
@@ -2524,14 +2607,14 @@ var recordedGeneration = (fqn, instanceId) => Effect31.gen(function* () {
2524
2607
  }
2525
2608
  return;
2526
2609
  });
2527
- var recordedInstance = (fqn, instanceId) => Effect31.gen(function* () {
2610
+ var recordedInstance = (fqn, instanceId) => Effect32.gen(function* () {
2528
2611
  const generation = yield* recordedGeneration(fqn, instanceId);
2529
2612
  if (generation === undefined)
2530
2613
  return "absent";
2531
2614
  return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
2532
2615
  });
2533
- var isCreate = (fqn, instanceId) => Effect31.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
2534
- var forgetRefusedCreate = (fqn, instanceId) => Effect31.gen(function* () {
2616
+ var isCreate = (fqn, instanceId) => Effect32.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
2617
+ var forgetRefusedCreate = (fqn, instanceId) => Effect32.gen(function* () {
2535
2618
  const found = yield* storeOf;
2536
2619
  if (found === undefined)
2537
2620
  return;
@@ -2540,32 +2623,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect31.gen(function* () {
2540
2623
  if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
2541
2624
  return;
2542
2625
  }
2543
- yield* found.store.delete({ ...found.where, fqn }).pipe(Effect31.ignore);
2626
+ yield* found.store.delete({ ...found.where, fqn }).pipe(Effect32.ignore);
2544
2627
  });
2545
2628
 
2546
2629
  // src/ownership/adopt.ts
2547
- var adoptEnabled = (fqn) => Effect32.gen(function* () {
2548
- const stack = yield* Effect32.serviceOption(Stack3);
2630
+ var adoptEnabled = (fqn) => Effect33.gen(function* () {
2631
+ const stack = yield* Effect33.serviceOption(Stack3);
2549
2632
  const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
2550
2633
  if (scoped !== undefined)
2551
2634
  return scoped;
2552
- const policy = yield* Effect32.serviceOption(AdoptPolicy);
2635
+ const policy = yield* Effect33.serviceOption(AdoptPolicy);
2553
2636
  if (Option5.isSome(policy))
2554
2637
  return policy.value;
2555
- const context = yield* Effect32.serviceOption(AlchemyContext);
2638
+ const context = yield* Effect33.serviceOption(AlchemyContext);
2556
2639
  return Option5.isSome(context) ? context.value.adopt : false;
2557
2640
  });
2558
- var adoptable = (owner) => Effect32.gen(function* () {
2641
+ var adoptable = (owner) => Effect33.gen(function* () {
2559
2642
  if (yield* isCreate(owner.fqn, owner.instanceId))
2560
2643
  return true;
2561
2644
  return yield* unfinished(owner.instanceId);
2562
2645
  });
2563
- var adoptsAtApply = (owner) => Effect32.gen(function* () {
2646
+ var adoptsAtApply = (owner) => Effect33.gen(function* () {
2564
2647
  if (!(yield* adoptEnabled(owner.fqn)))
2565
2648
  return false;
2566
2649
  return yield* adoptable(owner);
2567
2650
  });
2568
- var refuseTakeover = (owner, what) => Effect32.gen(function* () {
2651
+ var refuseTakeover = (owner, what) => Effect33.gen(function* () {
2569
2652
  if (owner.output !== undefined)
2570
2653
  return;
2571
2654
  if (yield* resumes(owner.instanceId))
@@ -2575,25 +2658,25 @@ var refuseTakeover = (owner, what) => Effect32.gen(function* () {
2575
2658
  return;
2576
2659
  yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
2577
2660
  const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
2578
- return yield* Effect32.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
2661
+ return yield* Effect33.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
2579
2662
  });
2580
2663
  var claimFor = (owner) => (what) => refuseTakeover(owner, what);
2581
2664
 
2582
2665
  // src/ownership/probe.ts
2583
2666
  import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
2584
- import * as Effect33 from "effect/Effect";
2585
- var provenOurs = (ask, settled2) => Effect33.gen(function* () {
2667
+ import * as Effect34 from "effect/Effect";
2668
+ var provenOurs = (ask, settled2) => Effect34.gen(function* () {
2586
2669
  const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
2587
2670
  if (recorded === "absent")
2588
2671
  return false;
2589
2672
  if (recorded === "partial") {
2590
- yield* Effect33.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
2673
+ yield* Effect34.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
2591
2674
  return false;
2592
2675
  }
2593
- const unproven = (reason) => Effect33.as(Effect33.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
2594
- return yield* settled2.pipe(Effect33.catch(unproven), Effect33.catchDefect(unproven));
2676
+ const unproven = (reason) => Effect34.as(Effect34.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
2677
+ return yield* settled2.pipe(Effect34.catch(unproven), Effect34.catchDefect(unproven));
2595
2678
  });
2596
- var ownedRead = (ask, found, settled2) => Effect33.gen(function* () {
2679
+ var ownedRead = (ask, found, settled2) => Effect34.gen(function* () {
2597
2680
  if (found === undefined || ask.output !== undefined)
2598
2681
  return found;
2599
2682
  return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
@@ -2651,8 +2734,8 @@ var optionMap = (value, family, defaultKey) => {
2651
2734
  return out;
2652
2735
  };
2653
2736
  var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
2654
- var sortedForm = (map12) => [...map12].map(([key, value]) => `${key}=${value}`).sort().join(",");
2655
- 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(",");
2656
2739
  var netMaps = (declared, live) => {
2657
2740
  const want = optionMap(declared, "net");
2658
2741
  const have = optionMap(live, "net");
@@ -3048,11 +3131,11 @@ var identityRefusals = (news, olds, output) => {
3048
3131
  };
3049
3132
 
3050
3133
  // src/proxmox/lxc-adoption.ts
3051
- import * as Effect37 from "effect/Effect";
3134
+ import * as Effect38 from "effect/Effect";
3052
3135
 
3053
3136
  // src/ownership/adopting.ts
3054
- import * as Effect34 from "effect/Effect";
3055
- var adopting = (owner, settled2) => Effect34.gen(function* () {
3137
+ import * as Effect35 from "effect/Effect";
3138
+ var adopting = (owner, settled2) => Effect35.gen(function* () {
3056
3139
  if (owner.output === undefined)
3057
3140
  return false;
3058
3141
  const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
@@ -3064,11 +3147,11 @@ var adopting = (owner, settled2) => Effect34.gen(function* () {
3064
3147
  });
3065
3148
 
3066
3149
  // src/proxmox/lxc-lifecycle.ts
3067
- import * as Effect36 from "effect/Effect";
3150
+ import * as Effect37 from "effect/Effect";
3068
3151
 
3069
3152
  // src/proxmox/network-apply-read.ts
3070
- import * as Effect35 from "effect/Effect";
3071
- var stagedDiff = (target, node) => Effect35.gen(function* () {
3153
+ import * as Effect36 from "effect/Effect";
3154
+ var stagedDiff = (target, node) => Effect36.gen(function* () {
3072
3155
  const path = `nodes/${node}/network`;
3073
3156
  const credential = yield* leased(target, "read");
3074
3157
  const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
@@ -3081,30 +3164,30 @@ var pendingLines = (diff) => {
3081
3164
  `).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
3082
3165
  return Math.max(changed, 1);
3083
3166
  };
3084
- var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect35.map(pendingLines));
3085
- var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect35.map((rows) => {
3167
+ var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect36.map(pendingLines));
3168
+ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect36.map((rows) => {
3086
3169
  const all = rows ?? [];
3087
- const cluster = all.find((row) => row.type === "cluster");
3170
+ const cluster3 = all.find((row) => row.type === "cluster");
3088
3171
  return {
3089
3172
  offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
3090
- quorate: cluster === undefined ? true : bool(cluster.quorate)
3173
+ quorate: cluster3 === undefined ? true : bool(cluster3.quorate)
3091
3174
  };
3092
3175
  }));
3093
- var degradedReason = (target) => clusterHealth(target).pipe(Effect35.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
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(", ")}]`));
3094
3177
  var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
3095
3178
  var STILL_RUNNING = "still running when this provider stopped waiting";
3096
3179
  var ATTEMPTS = 30;
3097
- var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect35.gen(function* () {
3180
+ var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect36.gen(function* () {
3098
3181
  const path = `nodes/${node}/tasks/${upid}/status`;
3099
3182
  for (let attempt = 0;attempt < attempts; attempt += 1) {
3100
- const status = yield* pveWith(target, credential, "GET", path).pipe(Effect35.orElseSucceed(() => {
3183
+ const status = yield* pveWith(target, credential, "GET", path).pipe(Effect36.orElseSucceed(() => {
3101
3184
  return;
3102
3185
  }));
3103
3186
  if (status === undefined)
3104
3187
  return UNREACHABLE;
3105
3188
  if (status.status === "stopped")
3106
3189
  return text(status.exitstatus, "stopped with no exitstatus");
3107
- yield* Effect35.sleep("1 second");
3190
+ yield* Effect36.sleep("1 second");
3108
3191
  }
3109
3192
  return STILL_RUNNING;
3110
3193
  });
@@ -3122,17 +3205,17 @@ var LXC_ENDPOINTS = {
3122
3205
  var CREATE_POLLS = 180;
3123
3206
  var TASK_POLLS = 60;
3124
3207
  var isServerError = (error) => error instanceof PveError && error.status === 500;
3125
- var readLive = (where) => Effect36.gen(function* () {
3126
- const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect36.map((data) => ({ data, error: undefined })), Effect36.catchIf(isServerError, (error) => Effect36.succeed({ data: undefined, error })));
3208
+ var readLive = (where) => Effect37.gen(function* () {
3209
+ const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect37.map((data) => ({ data, error: undefined })), Effect37.catchIf(isServerError, (error) => Effect37.succeed({ data: undefined, error })));
3127
3210
  if (answer.data !== undefined && answer.data !== null)
3128
3211
  return answer.data;
3129
3212
  const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
3130
3213
  const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
3131
3214
  if (found !== undefined && found.node === where.node && found.type === "lxc") {
3132
- return yield* Effect36.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
3215
+ return yield* Effect37.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
3133
3216
  }
3134
3217
  if (found !== undefined) {
3135
- return yield* Effect36.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
3218
+ return yield* Effect37.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
3136
3219
  }
3137
3220
  return;
3138
3221
  });
@@ -3142,23 +3225,23 @@ var attributesOf5 = (where, live) => ({
3142
3225
  rawKeys: rawKeysOf(live),
3143
3226
  vmid: where.vmid
3144
3227
  });
3145
- var task = (where, method, path, what, polls, form) => Effect36.gen(function* () {
3228
+ var task = (where, method, path, what, polls, form) => Effect37.gen(function* () {
3146
3229
  const credential = yield* mint(where.target, "provision");
3147
3230
  const upid = text(yield* pveWith(where.target, credential, method, path, form));
3148
3231
  if (upid === "") {
3149
- return yield* Effect36.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
3232
+ return yield* Effect37.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
3150
3233
  }
3151
3234
  const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
3152
3235
  if (exit !== "OK") {
3153
- return yield* Effect36.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
3236
+ return yield* Effect37.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
3154
3237
  }
3155
3238
  });
3156
- var createGuest = (props) => Effect36.gen(function* () {
3239
+ var createGuest = (props) => Effect37.gen(function* () {
3157
3240
  const form = createForm4(props);
3158
3241
  yield* guardForm(LXC_ENDPOINTS.create, form, true);
3159
3242
  return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
3160
3243
  });
3161
- var updateGuest = (props, change, digest) => Effect36.gen(function* () {
3244
+ var updateGuest = (props, change, digest) => Effect37.gen(function* () {
3162
3245
  if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
3163
3246
  const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
3164
3247
  yield* guardForm(LXC_ENDPOINTS.update, form, false);
@@ -3170,47 +3253,47 @@ var updateGuest = (props, change, digest) => Effect36.gen(function* () {
3170
3253
  yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
3171
3254
  }
3172
3255
  });
3173
- var destroyGuest = (where, declared) => Effect36.gen(function* () {
3256
+ var destroyGuest = (where, declared) => Effect37.gen(function* () {
3174
3257
  const live = yield* readLive(where);
3175
3258
  if (live === undefined)
3176
3259
  return;
3177
3260
  const drift = judge(declared, live).drift;
3178
3261
  if (drift.length > 0) {
3179
- return yield* Effect36.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
3262
+ return yield* Effect37.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
3180
3263
  }
3181
3264
  yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
3182
3265
  });
3183
3266
 
3184
3267
  // src/proxmox/lxc-adoption.ts
3185
3268
  var adoptionRefusal = (props, drift) => `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(", ")}. An ` + "adoption never changes a guest, so nothing is written. Declare what is live for these keys " + "(the plan names keys, never values) and plan again; change them after the adoption, as an " + "update.";
3186
- var refuseAdoptedDrift = (owner, props, live, change) => Effect37.gen(function* () {
3269
+ var refuseAdoptedDrift = (owner, props, live, change) => Effect38.gen(function* () {
3187
3270
  if (change.drift.length === 0)
3188
3271
  return;
3189
- const settled2 = (recorded) => Effect37.sync(() => judge(recorded, live).drift.length === 0);
3272
+ const settled2 = (recorded) => Effect38.sync(() => judge(recorded, live).drift.length === 0);
3190
3273
  if (!(yield* adopting(owner, settled2)))
3191
3274
  return;
3192
- return yield* Effect37.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
3275
+ return yield* Effect38.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
3193
3276
  });
3194
3277
 
3195
3278
  // src/proxmox/lxc.ts
3196
3279
  var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
3197
- var refused = (reasons) => Effect38.fail(new LxcRefusedError(reasons.join(`
3280
+ var refused = (reasons) => Effect39.fail(new LxcRefusedError(reasons.join(`
3198
3281
  `)));
3199
- var diff = (owner, news, olds) => Effect38.gen(function* () {
3282
+ var diff = (owner, news, olds) => Effect39.gen(function* () {
3200
3283
  const { output } = owner;
3201
3284
  const identity2 = identityRefusals(news, olds, output);
3202
3285
  if (identity2.length > 0)
3203
3286
  return yield* refused(identity2);
3204
3287
  if (output === undefined)
3205
3288
  return;
3206
- if (!isResolved10(news))
3289
+ if (!isResolved11(news))
3207
3290
  return;
3208
3291
  const live = yield* readLive(news);
3209
3292
  if (live === undefined) {
3210
3293
  const refusals = createRefusals(news);
3211
3294
  if (refusals.length > 0)
3212
3295
  return yield* refused(refusals);
3213
- yield* Effect38.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
3296
+ yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
3214
3297
  return { action: "update" };
3215
3298
  }
3216
3299
  const change = judge(news, live);
@@ -3221,10 +3304,10 @@ var diff = (owner, news, olds) => Effect38.gen(function* () {
3221
3304
  if (change.drift.length === 0 && !moved)
3222
3305
  return { action: "noop" };
3223
3306
  const what = change.drift.length === 0 ? `the guest is on ${news.node} now, not ${output.node} -- a deploy re-records it, ` + "writing nothing." : `live config differs from the declaration in ${change.drift.join(", ")} -- a ` + "deploy writes these.";
3224
- yield* Effect38.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
3307
+ yield* Effect39.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
3225
3308
  return { action: "update" };
3226
3309
  });
3227
- var reconcile = (owner, news) => Effect38.gen(function* () {
3310
+ var reconcile = (owner, news) => Effect39.gen(function* () {
3228
3311
  const { output } = owner;
3229
3312
  const identity2 = identityRefusals(news, news, output);
3230
3313
  if (identity2.length > 0)
@@ -3270,15 +3353,15 @@ var whereOf = (olds, output) => ({
3270
3353
  target: olds.target,
3271
3354
  vmid: output?.vmid ?? olds.vmid
3272
3355
  });
3273
- var read = (owner, olds) => Effect38.gen(function* () {
3356
+ var read = (owner, olds) => Effect39.gen(function* () {
3274
3357
  const where = whereOf(olds, owner.output);
3275
3358
  const live = yield* readLive(where);
3276
3359
  if (live === undefined)
3277
3360
  return;
3278
- const settled2 = Effect38.sync(() => judge(olds, live).drift.length === 0);
3361
+ const settled2 = Effect39.sync(() => judge(olds, live).drift.length === 0);
3279
3362
  return yield* ownedRead(owner, attributesOf5(where, live), settled2);
3280
3363
  });
3281
- var handlers7 = {
3364
+ var handlers6 = {
3282
3365
  delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
3283
3366
  diff: ({
3284
3367
  fqn,
@@ -3287,7 +3370,7 @@ var handlers7 = {
3287
3370
  olds,
3288
3371
  output
3289
3372
  }) => diff({ fqn, instanceId, output }, news, olds),
3290
- list: () => Effect38.succeed([]),
3373
+ list: () => Effect39.succeed([]),
3291
3374
  read: ({
3292
3375
  fqn,
3293
3376
  instanceId,
@@ -3301,11 +3384,11 @@ var handlers7 = {
3301
3384
  output
3302
3385
  }) => reconcile({ fqn, instanceId, output }, news)
3303
3386
  };
3304
- var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect38.succeed(ProxmoxLxc.Provider.of(handlers7)).pipe(Effect38.map(provingResumes)));
3387
+ var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect39.succeed(ProxmoxLxc.Provider.of(handlers6)).pipe(Effect39.map(provingResumes)));
3305
3388
  // src/proxmox/metric-server.ts
3306
3389
  import { Resource as Resource14 } from "alchemy";
3307
- import * as Provider14 from "alchemy/Provider";
3308
- import * as Effect39 from "effect/Effect";
3390
+ import { isResolved as isResolved12 } from "alchemy/Diff";
3391
+ import * as cluster4 from "@distilled.cloud/proxmox/cluster";
3309
3392
 
3310
3393
  // src/proxmox/metric-server-form.ts
3311
3394
  var UNSET2 = -1;
@@ -3375,8 +3458,7 @@ var otelAttributes = (live) => ({
3375
3458
  });
3376
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);
3377
3460
 
3378
- // src/proxmox/metric-server.ts
3379
- var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
3461
+ // src/proxmox/metric-server-config.ts
3380
3462
  var matchesType = (attributes, props) => {
3381
3463
  if (props.type === "opentelemetry")
3382
3464
  return otelMatches(attributes, props);
@@ -3385,7 +3467,7 @@ var matchesType = (attributes, props) => {
3385
3467
  return socket && attributes.path === (props.path ?? "");
3386
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);
3387
3469
  };
3388
- var handlers8 = pveHandlers({
3470
+ var metricServerSpec = {
3389
3471
  attributes: (live, props) => {
3390
3472
  const liveType = text(live["type"]);
3391
3473
  if (liveType !== "" && liveType !== props.type)
@@ -3417,13 +3499,96 @@ var handlers8 = pveHandlers({
3417
3499
  matches: (attributes, props) => attributes.server === props.server && attributes.port === props.port && attributes.disable === (props.disable === true) && matchesType(attributes, props),
3418
3500
  path: (props) => `cluster/metrics/server/${props.id}`,
3419
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
3537
+ });
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 }));
3420
3542
  });
3421
- var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect39.succeed(ProxmoxMetricServer.Provider.of(handlers8)));
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
+ })));
3422
3587
  // src/proxmox/network-apply.ts
3423
3588
  import { Resource as Resource15 } from "alchemy";
3424
- import { isResolved as isResolved11 } from "alchemy/Diff";
3589
+ import { isResolved as isResolved13 } from "alchemy/Diff";
3425
3590
  import * as Provider15 from "alchemy/Provider";
3426
- import * as Effect40 from "effect/Effect";
3591
+ import * as Effect42 from "effect/Effect";
3427
3592
 
3428
3593
  // src/proxmox/apply-endpoints.ts
3429
3594
  var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
@@ -3433,15 +3598,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
3433
3598
 
3434
3599
  // src/proxmox/network-apply.ts
3435
3600
  var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
3436
- var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect40.map((pending) => ({ node: props.node, pending })));
3437
- var diff2 = (news) => Effect40.gen(function* () {
3438
- 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))
3439
3604
  return;
3440
3605
  const { pending } = yield* read2(news);
3441
3606
  return pending === 0 ? { action: "noop" } : { action: "update" };
3442
3607
  });
3443
- var refuse = (node, why) => Effect40.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3444
- var apply = (props) => Effect40.gen(function* () {
3608
+ var refuse = (node, why) => Effect42.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
3609
+ var apply = (props) => Effect42.gen(function* () {
3445
3610
  const node = props.node;
3446
3611
  const staged = yield* pendingCount(props.target, node);
3447
3612
  if (staged === 0)
@@ -3470,20 +3635,20 @@ var apply = (props) => Effect40.gen(function* () {
3470
3635
  }
3471
3636
  return { node, pending: left };
3472
3637
  });
3473
- var handlers9 = {
3474
- delete: () => Effect40.void,
3638
+ var handlers7 = {
3639
+ delete: () => Effect42.void,
3475
3640
  diff: ({ news }) => diff2(news),
3476
- list: () => Effect40.succeed([]),
3641
+ list: () => Effect42.succeed([]),
3477
3642
  read: ({ olds }) => read2(olds),
3478
3643
  reconcile: ({ news }) => apply(news)
3479
3644
  };
3480
- var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect40.succeed(ProxmoxNetworkApply.Provider.of(handlers9)));
3645
+ var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect42.succeed(ProxmoxNetworkApply.Provider.of(handlers7)));
3481
3646
  // src/proxmox/node-network.ts
3482
3647
  import { Resource as Resource16 } from "alchemy";
3483
- import { isResolved as isResolved12 } from "alchemy/Diff";
3648
+ import { isResolved as isResolved14 } from "alchemy/Diff";
3484
3649
  import * as Provider16 from "alchemy/Provider";
3485
3650
  import * as nodes9 from "@distilled.cloud/proxmox/nodes";
3486
- import * as Effect42 from "effect/Effect";
3651
+ import * as Effect44 from "effect/Effect";
3487
3652
 
3488
3653
  // src/proxmox/node-network-form.ts
3489
3654
  var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
@@ -3526,7 +3691,7 @@ var updateForm2 = (props) => {
3526
3691
 
3527
3692
  // src/proxmox/node-network-wire.ts
3528
3693
  import * as nodes8 from "@distilled.cloud/proxmox/nodes";
3529
- import * as Effect41 from "effect/Effect";
3694
+ import * as Effect43 from "effect/Effect";
3530
3695
  var attributesOf6 = (live, props) => {
3531
3696
  const data = live;
3532
3697
  const liveType = text(data["type"]);
@@ -3560,12 +3725,12 @@ var attributesOf6 = (live, props) => {
3560
3725
  "vlan-raw-device": text(data["vlan-raw-device"])
3561
3726
  };
3562
3727
  };
3563
- var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes8.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect41.catchTag("ParameterVerificationFailed", (error) => error.errors["iface"] === "interface does not exist" ? Effect41.succeed(undefined) : Effect41.fail(error)))).pipe(Effect41.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) => {
3564
3729
  if (live === UNREADABLE)
3565
3730
  return UNREADABLE;
3566
3731
  return live === undefined ? undefined : attributesOf6(live, props);
3567
3732
  }));
3568
- var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect41.orElseSucceed(() => {
3733
+ var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect43.orElseSucceed(() => {
3569
3734
  return;
3570
3735
  }));
3571
3736
  var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
@@ -3590,13 +3755,13 @@ var toDistilledUpdate2 = (props) => ({
3590
3755
  iface: props.iface,
3591
3756
  node: props.node
3592
3757
  });
3593
- var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect42.succeed(ProxmoxNodeNetwork.Provider.of({
3594
- list: () => Effect42.succeed([]),
3595
- read: Effect42.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 }) {
3596
3761
  return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
3597
3762
  }),
3598
- diff: Effect42.fn(function* ({ news, output }) {
3599
- if (!isResolved12(news))
3763
+ diff: Effect44.fn(function* ({ news, output }) {
3764
+ if (!isResolved14(news))
3600
3765
  return;
3601
3766
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
3602
3767
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
@@ -3613,7 +3778,7 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
3613
3778
  }
3614
3779
  return matches5(live, news) ? { action: "noop" } : { action: "update" };
3615
3780
  }),
3616
- reconcile: Effect42.fn(function* ({ news }) {
3781
+ reconcile: Effect44.fn(function* ({ news }) {
3617
3782
  const before = dropUnreadable4(yield* readInterface(news));
3618
3783
  yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
3619
3784
  yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
@@ -3624,18 +3789,22 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
3624
3789
  }
3625
3790
  const after = dropUnreadable4(yield* readInterface(news));
3626
3791
  if (after === undefined) {
3627
- return yield* Effect42.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."));
3628
3793
  }
3629
3794
  return after;
3630
3795
  }),
3631
- delete: Effect42.fn(function* ({ olds }) {
3796
+ delete: Effect44.fn(function* ({ olds }) {
3632
3797
  yield* runPve(olds.target, "provision", true, nodes9.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
3633
3798
  })
3634
3799
  })));
3635
3800
  // src/proxmox/notification-matcher.ts
3636
3801
  import { Resource as Resource17 } from "alchemy";
3637
3802
  import * as Provider17 from "alchemy/Provider";
3638
- import * as Effect43 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";
3639
3808
 
3640
3809
  // src/proxmox/notification-matcher-form.ts
3641
3810
  var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
@@ -3711,25 +3880,99 @@ var matcherUpdateForm = (fields) => {
3711
3880
  };
3712
3881
  };
3713
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
+
3714
3965
  // src/proxmox/notification-matcher.ts
3715
3966
  var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
3716
- var handlers10 = pveHandlers({
3717
- attributes: (live, props) => matcherAttributes(live, props.name),
3718
- collection: () => "cluster/notifications/matchers",
3719
- createForm: matcherCreateForm,
3720
- endpoint: {
3721
- create: "pve:POST /cluster/notifications/matchers",
3722
- update: "pve:PUT /cluster/notifications/matchers/{name}"
3723
- },
3724
- matches: matcherMatches,
3725
- path: (props) => `cluster/notifications/matchers/${props.name}`,
3726
- updateForm: matcherUpdateForm
3727
- });
3728
- var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect43.succeed(ProxmoxNotificationMatcher.Provider.of(handlers10)));
3967
+ var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect48.succeed(ProxmoxNotificationMatcher.Provider.of(matcherHandlers)));
3729
3968
  // src/proxmox/notification-target.ts
3730
3969
  import { Resource as Resource18 } from "alchemy";
3731
3970
  import * as Provider18 from "alchemy/Provider";
3732
- import * as Effect44 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";
3733
3976
 
3734
3977
  // src/proxmox/notification-target-endpoint.ts
3735
3978
  var ENDPOINTS = {
@@ -3783,9 +4026,8 @@ var createShape = (props) => ({
3783
4026
  name: props.name
3784
4027
  });
3785
4028
 
3786
- // src/proxmox/notification-target.ts
3787
- var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
3788
- var handlers11 = pveHandlers({
4029
+ // src/proxmox/notification-target-spec.ts
4030
+ var targetSpec = {
3789
4031
  attributes: (live, props) => ({
3790
4032
  author: text(live["author"]),
3791
4033
  body: text(live["body"]),
@@ -3810,16 +4052,155 @@ var handlers11 = pveHandlers({
3810
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)),
3811
4053
  path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
3812
4054
  updateForm: shape4
3813
- });
3814
- var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect44.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)));
3815
4194
  // src/proxmox/pbs-datastore.ts
3816
4195
  import { Resource as Resource19 } from "alchemy";
3817
- import { isResolved as isResolved13 } from "alchemy/Diff";
3818
4196
  import * as Provider19 from "alchemy/Provider";
3819
- import * as Effect48 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";
3820
4202
 
3821
4203
  // src/proxmox/pbs-datastore-form.ts
3822
- import * as Effect45 from "effect/Effect";
3823
4204
  var attributes = (live, props) => ({
3824
4205
  backend: propertyString(live["backend"]),
3825
4206
  comment: text(live["comment"]),
@@ -3871,48 +4252,117 @@ var createForm6 = (props) => ({
3871
4252
  });
3872
4253
  var updateForm3 = (props) => mutable(props);
3873
4254
  var object2 = (props) => `config/datastore/${props.name}`;
3874
- var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect45.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect45.orElseSucceed(() => {
3875
- return;
3876
- }));
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
+ });
3877
4327
 
3878
4328
  // src/proxmox/pbs-datastore-guard.ts
3879
- import * as Effect46 from "effect/Effect";
4329
+ import * as Effect54 from "effect/Effect";
3880
4330
  var samePath = (left, right) => {
3881
4331
  const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
3882
4332
  return trim(left) === trim(right);
3883
4333
  };
3884
- var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect46.void : Effect46.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."));
3885
- var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect46.void : Effect46.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."));
3886
- var settle2 = (read3, done) => Effect46.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* () {
3887
4337
  for (let attempt = 0;attempt < 60; attempt += 1) {
3888
- const live = yield* read3;
4338
+ const live = yield* read4;
3889
4339
  if (done(live))
3890
4340
  return live;
3891
- yield* Effect46.sleep("2 seconds");
4341
+ yield* Effect54.sleep("2 seconds");
3892
4342
  }
3893
- return yield* read3;
4343
+ return yield* read4;
3894
4344
  });
3895
4345
 
3896
4346
  // src/proxmox/pbs-datastore-endpoint.ts
3897
- import * as Effect47 from "effect/Effect";
4347
+ import * as Effect55 from "effect/Effect";
3898
4348
  var DATASTORE_ENDPOINT = {
3899
4349
  create: "pbs:POST /config/datastore",
3900
4350
  update: "pbs:PUT /config/datastore/{name}"
3901
4351
  };
3902
- var guardDatastoreForms = (props) => Effect47.gen(function* () {
4352
+ var guardDatastoreForms = (props) => Effect55.gen(function* () {
3903
4353
  yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
3904
4354
  yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
3905
4355
  });
3906
4356
 
3907
- // src/proxmox/pbs-datastore.ts
3908
- var PbsDatastore = Resource19("Pbs.Datastore", {
3909
- defaultRemovalPolicy: "retain"
3910
- });
3911
- var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect48.succeed(PbsDatastore.Provider.of({
3912
- list: () => Effect48.succeed([]),
4357
+ // src/proxmox/pbs-datastore-lifecycle.ts
4358
+ var handlers8 = {
4359
+ list: () => Effect56.succeed([]),
3913
4360
  read: ({ olds }) => readOne(olds),
3914
- diff: ({ news, output }) => Effect48.gen(function* () {
3915
- if (!isResolved13(news))
4361
+ diff: ({
4362
+ news,
4363
+ output
4364
+ }) => Effect56.gen(function* () {
4365
+ if (!isResolved17(news))
3916
4366
  return;
3917
4367
  yield* guardDatastoreForms(news);
3918
4368
  if (output === undefined)
@@ -3924,106 +4374,135 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect48.succee
3924
4374
  yield* guardBackend(live, news);
3925
4375
  return matches6(live, news) ? { action: "noop" } : { action: "update" };
3926
4376
  }),
3927
- reconcile: Effect48.fn(function* ({ news }) {
4377
+ reconcile: Effect56.fn(function* ({ news }) {
3928
4378
  yield* guardDatastoreForms(news);
3929
4379
  const live = yield* readOne(news);
3930
- let upid;
3931
4380
  if (live === undefined) {
3932
- upid = yield* pve(news.target, "provision", "POST", "config/datastore", createForm6(news));
4381
+ yield* createOne(news);
3933
4382
  } else {
3934
4383
  yield* guardPath(live, news);
3935
4384
  yield* guardBackend(live, news);
3936
4385
  const form = formToSend(matches6, live, news, updateForm3(news));
3937
4386
  if (form !== undefined) {
3938
- yield* pve(news.target, "provision", "PUT", object2(news), form);
4387
+ yield* updateOne(news);
3939
4388
  }
3940
4389
  }
3941
4390
  const after = yield* settle2(readOne(news), (row) => row !== undefined && matches6(row, news));
3942
4391
  if (after === undefined || !matches6(after, news)) {
3943
- return yield* Effect48.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."));
3944
4393
  }
3945
4394
  return after;
3946
4395
  }),
3947
- delete: Effect48.fn(function* ({ olds }) {
3948
- yield* pve(olds.target, "provision", "DELETE", object2(olds));
4396
+ delete: Effect56.fn(function* ({ olds }) {
4397
+ yield* deleteOne(olds);
3949
4398
  const left = yield* settle2(readOne(olds), (row) => row === undefined);
3950
4399
  if (left !== undefined) {
3951
- return yield* Effect48.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>\`.`));
3952
4401
  }
3953
4402
  })
3954
- })));
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)));
3955
4410
  // src/proxmox/pbs-notification-matcher.ts
3956
4411
  import { Resource as Resource20 } from "alchemy";
3957
4412
  import * as Provider20 from "alchemy/Provider";
3958
- import * as Effect49 from "effect/Effect";
3959
- var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
3960
- var handlers12 = pveHandlers({
3961
- attributes: (live, props) => matcherAttributes(live, props.name),
3962
- collection: () => "config/notifications/matchers",
3963
- createForm: matcherCreateForm,
3964
- endpoint: {
3965
- create: "pbs:POST /config/notifications/matchers",
3966
- update: "pbs:PUT /config/notifications/matchers/{name}"
3967
- },
3968
- matches: matcherMatches,
3969
- path: (props) => `config/notifications/matchers/${props.name}`,
3970
- updateForm: matcherUpdateForm
3971
- });
3972
- var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect49.succeed(PbsNotificationMatcher.Provider.of(handlers12)));
3973
- // src/proxmox/pbs-notification-target.ts
3974
- import { Resource as Resource21 } from "alchemy";
3975
- import * as Provider21 from "alchemy/Provider";
3976
- import * as Effect51 from "effect/Effect";
4413
+ import { isResolved as isResolved18 } from "alchemy/Diff";
4414
+ import * as Effect60 from "effect/Effect";
3977
4415
 
3978
- // src/proxmox/pbs-notification-target-lifecycle.ts
3979
- import { isResolved as isResolved14 } from "alchemy/Diff";
3980
- import * as Effect50 from "effect/Effect";
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";
3981
4419
 
3982
- // src/proxmox/pbs-notification-target-endpoint.ts
3983
- var ENDPOINTS2 = {
3984
- sendmail: {
3985
- create: "pbs:POST /config/notifications/endpoints/sendmail",
3986
- update: "pbs:PUT /config/notifications/endpoints/sendmail/{name}"
3987
- },
3988
- smtp: {
3989
- create: "pbs:POST /config/notifications/endpoints/smtp",
3990
- update: "pbs:PUT /config/notifications/endpoints/smtp/{name}"
3991
- },
3992
- webhook: {
3993
- create: "pbs:POST /config/notifications/endpoints/webhook",
3994
- update: "pbs:PUT /config/notifications/endpoints/webhook/{name}"
3995
- }
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
4446
+ });
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)
3996
4492
  };
3997
- var pbsTargetEndpoint = (props) => ENDPOINTS2[props.type];
4493
+ var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect60.succeed(PbsNotificationMatcher.Provider.of(handlers9)));
4494
+ // src/proxmox/pbs-notification-target.ts
4495
+ import { Resource as Resource21 } from "alchemy";
4496
+ import * as Provider21 from "alchemy/Provider";
4497
+ import * as Effect63 from "effect/Effect";
3998
4498
 
3999
- // src/proxmox/credential-literals.ts
4000
- var READS_SECRET = /\{\{[^}]*\bsecrets\./;
4001
- var CREDENTIAL_WORD = /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;
4002
- var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
4003
- var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
4004
- var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
4005
- var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
4006
- var literalCredentialsInUrl = (url) => {
4007
- if (url === undefined)
4008
- return [];
4009
- const out = [];
4010
- const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
4011
- const at = authority.lastIndexOf("@");
4012
- if (at >= 0) {
4013
- const userinfo = authority.slice(0, at);
4014
- const colon = userinfo.indexOf(":");
4015
- if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
4016
- out.push("userinfo");
4017
- }
4018
- const params = url.replace(/^[^?#]*/, "");
4019
- for (const pair of params.split(/[?&#]/)) {
4020
- const eq = pair.indexOf("=");
4021
- const name = eq < 0 ? pair : pair.slice(0, eq);
4022
- if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
4023
- out.push(`?${name}`);
4024
- }
4025
- return out;
4026
- };
4499
+ // src/proxmox/pbs-notification-target-lifecycle.ts
4500
+ import { isResolved as isResolved19 } from "alchemy/Diff";
4501
+ import * as Effect62 from "effect/Effect";
4502
+
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";
4027
4506
 
4028
4507
  // src/proxmox/pbs-notification-target-wire.ts
4029
4508
  import { Buffer as Buffer2 } from "node:buffer";
@@ -4114,6 +4593,106 @@ var targetAttributes = (live, props) => {
4114
4593
  };
4115
4594
  };
4116
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
+
4117
4696
  // src/proxmox/pbs-notification-target-form.ts
4118
4697
  var OWNED = {
4119
4698
  author: ["sendmail", "smtp"],
@@ -4254,12 +4833,9 @@ var targetForm = (props, groups, carry, mode) => {
4254
4833
 
4255
4834
  // src/proxmox/pbs-notification-target-lifecycle.ts
4256
4835
  var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
4257
- var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect50.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect50.orElseSucceed(() => {
4258
- return;
4259
- }));
4260
4836
  var refuse2 = (props) => {
4261
4837
  const reasons = refusals(props);
4262
- return reasons.length === 0 ? Effect50.void : Effect50.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4838
+ return reasons.length === 0 ? Effect62.void : Effect62.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
4263
4839
  };
4264
4840
  var toCarry = (live, props, groups, sealed) => {
4265
4841
  const header = headerState(live, props, groups) === "stale";
@@ -4271,39 +4847,48 @@ var requireValues = (props, groups, carry) => {
4271
4847
  ...carry.header ? groups.header.missing : [],
4272
4848
  ...carry.sealed ? groups.sealed.missing : []
4273
4849
  ];
4274
- return missing.length === 0 ? Effect50.void : Effect50.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."));
4275
4851
  };
4276
- var handlers13 = {
4277
- list: () => Effect50.succeed([]),
4278
- read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect50.void).pipe(Effect50.andThen(readLive2(olds)), Effect50.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 ?? "" })),
4279
4855
  diff: ({
4280
4856
  news,
4281
4857
  olds,
4282
4858
  output
4283
- }) => Effect50.gen(function* () {
4284
- if (output === undefined || !isResolved14(news))
4859
+ }) => Effect62.gen(function* () {
4860
+ if (output === undefined || !isResolved19(news))
4285
4861
  return;
4862
+ yield* refuse2(news);
4286
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
+ }
4287
4874
  return { action: "replace", deleteFirst: olds.name === news.name };
4288
4875
  }
4289
- yield* refuse2(news);
4290
- const live = yield* readLive2(news);
4876
+ const live = yield* readTarget2(news);
4291
4877
  if (live === undefined)
4292
4878
  return { action: "update" };
4293
4879
  return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
4294
4880
  }),
4295
- reconcile: ({ news, output }) => Effect50.gen(function* () {
4881
+ reconcile: ({ news, output }) => Effect62.gen(function* () {
4296
4882
  yield* refuse2(news);
4297
4883
  const groups = resolveGroups(news);
4298
- const live = yield* readLive2(news);
4884
+ const live = yield* readTarget2(news);
4299
4885
  let sealed = output?.sealed ?? "";
4300
4886
  if (live === undefined) {
4301
4887
  const carry = { header: true, sealed: true };
4302
4888
  yield* requireValues(news, groups, carry);
4303
- const collection = `config/notifications/endpoints/${news.type}`;
4304
4889
  const form = targetForm(news, groups, carry, "create");
4305
4890
  yield* guardForm(pbsTargetEndpoint(news).create, form, true);
4306
- yield* pve(news.target, "provision", "POST", collection, form);
4891
+ yield* createTarget2(news, form);
4307
4892
  sealed = seal(groups.sealed.values);
4308
4893
  } else {
4309
4894
  const carry = toCarry(live, news, groups, sealed);
@@ -4311,27 +4896,31 @@ var handlers13 = {
4311
4896
  yield* requireValues(news, groups, carry);
4312
4897
  const form = targetForm(news, groups, carry, "update");
4313
4898
  yield* guardForm(pbsTargetEndpoint(news).update, form, false);
4314
- yield* pve(news.target, "provision", "PUT", path(news), form);
4899
+ yield* updateTarget2(news, form);
4315
4900
  if (carry.sealed)
4316
4901
  sealed = seal(groups.sealed.values);
4317
4902
  }
4318
4903
  }
4319
- const after = yield* readLive2(news);
4904
+ const after = yield* readTarget2(news);
4320
4905
  if (after === undefined) {
4321
- return yield* Effect50.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.`));
4322
4907
  }
4323
4908
  return { ...after, sealed };
4324
4909
  }),
4325
- delete: ({ olds }) => pve(olds.target, "provision", "DELETE", path(olds))
4910
+ delete: ({ olds }) => deleteTarget2(olds)
4326
4911
  };
4327
4912
 
4328
4913
  // src/proxmox/pbs-notification-target.ts
4329
4914
  var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
4330
- var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect51.succeed(PbsNotificationTarget.Provider.of(handlers13)));
4915
+ var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect63.succeed(PbsNotificationTarget.Provider.of(handlers10)));
4331
4916
  // src/proxmox/pbs-prune-job.ts
4332
4917
  import { Resource as Resource22 } from "alchemy";
4333
4918
  import * as Provider22 from "alchemy/Provider";
4334
- import * as Effect52 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";
4335
4924
 
4336
4925
  // src/proxmox/pbs-prune-job-form.ts
4337
4926
  var KEEP_KEYS = [
@@ -4386,10 +4975,9 @@ var updateBody2 = (props) => {
4386
4975
  return shape5(props);
4387
4976
  };
4388
4977
 
4389
- // src/proxmox/pbs-prune-job.ts
4390
- var PbsPruneJob = Resource22("Pbs.PruneJob");
4978
+ // src/proxmox/pbs-prune-job-spec.ts
4391
4979
  var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
4392
- var handlers14 = pveHandlers({
4980
+ var spec = {
4393
4981
  attributes: (live, props) => ({
4394
4982
  comment: text(live["comment"], ""),
4395
4983
  disable: bool(live["disable"], false),
@@ -4406,12 +4994,104 @@ var handlers14 = pveHandlers({
4406
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),
4407
4995
  path: (props) => `config/prune/${props.id}`,
4408
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 }));
4409
5043
  });
4410
- var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect52.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)));
4411
5087
  // src/proxmox/pbs-sync-job.ts
4412
5088
  import { Resource as Resource23 } from "alchemy";
4413
5089
  import * as Provider23 from "alchemy/Provider";
4414
- import * as Effect53 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";
4415
5095
 
4416
5096
  // src/proxmox/pbs-sync-job-form.ts
4417
5097
  var UNSET4 = -1;
@@ -4459,7 +5139,7 @@ var createBody4 = (props) => ({
4459
5139
  ...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
4460
5140
  });
4461
5141
  var updateBody3 = (props) => shape6(props);
4462
- var handlers15 = pveHandlers({
5142
+ var spec2 = {
4463
5143
  attributes: (live, props) => ({
4464
5144
  comment: text(live["comment"], ""),
4465
5145
  "group-filter": filters(live["group-filter"]),
@@ -4484,15 +5164,105 @@ var handlers15 = pveHandlers({
4484
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"]),
4485
5165
  path: (props) => `config/sync/${props.id}`,
4486
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 }));
4487
5217
  });
4488
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
+
4489
5255
  // src/proxmox/pbs-sync-job.ts
4490
5256
  var PbsSyncJob = Resource23("Pbs.SyncJob");
4491
- var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect53.succeed(PbsSyncJob.Provider.of(handlers15)));
5257
+ var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect69.succeed(PbsSyncJob.Provider.of(handlers12)));
4492
5258
  // src/proxmox/pbs-verify-job.ts
4493
5259
  import { Resource as Resource24 } from "alchemy";
4494
5260
  import * as Provider24 from "alchemy/Provider";
4495
- import * as Effect54 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";
4496
5266
 
4497
5267
  // src/proxmox/pbs-verify-job-form.ts
4498
5268
  var rechecks = (ignoreVerified, outdatedAfter) => {
@@ -4512,9 +5282,8 @@ var shape7 = (props) => ({
4512
5282
  ...props["outdated-after"] === undefined ? {} : { "outdated-after": String(props["outdated-after"]) }
4513
5283
  });
4514
5284
 
4515
- // src/proxmox/pbs-verify-job.ts
4516
- var PbsVerifyJob = Resource24("Pbs.VerifyJob");
4517
- var handlers16 = pveHandlers({
5285
+ // src/proxmox/pbs-verify-job-spec.ts
5286
+ var spec3 = {
4518
5287
  attributes: (live, props) => {
4519
5288
  const ignoreVerified = bool(live["ignore-verified"], true);
4520
5289
  const outdatedAfter = int(live["outdated-after"], -1);
@@ -4536,12 +5305,94 @@ var handlers16 = pveHandlers({
4536
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),
4537
5306
  path: (props) => `config/verify/${props.id}`,
4538
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 }));
4539
5344
  });
4540
- var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect54.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)));
4541
5388
  // src/proxmox/pool.ts
4542
5389
  import { Resource as Resource25 } from "alchemy";
5390
+ import { isResolved as isResolved23 } from "alchemy/Diff";
4543
5391
  import * as Provider25 from "alchemy/Provider";
4544
- import * as Effect55 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";
4545
5396
  var ProxmoxPool = Resource25("Proxmox.Pool");
4546
5397
  var poolCreateForm = (props) => ({
4547
5398
  comment: props.comment ?? "",
@@ -4550,7 +5401,7 @@ var poolCreateForm = (props) => ({
4550
5401
  var poolUpdateForm = (props) => ({
4551
5402
  comment: props.comment ?? ""
4552
5403
  });
4553
- var handlers17 = pveHandlers({
5404
+ var spec4 = {
4554
5405
  attributes: (live, props) => {
4555
5406
  const row = live;
4556
5407
  return {
@@ -4565,8 +5416,51 @@ var handlers17 = pveHandlers({
4565
5416
  matches: (attributes2, props) => attributes2.comment === (props.comment ?? ""),
4566
5417
  path: (props) => `pools/${props.poolid}`,
4567
5418
  updateForm: poolUpdateForm
4568
- });
4569
- var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect55.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
+ })));
4570
5464
  // src/proxmox/provision-baseline.ts
4571
5465
  var PROVISION_PRIVILEGES = Object.freeze([
4572
5466
  "Datastore.Allocate",
@@ -4718,15 +5612,15 @@ fi
4718
5612
  var userStep = (user) => {
4719
5613
  const groups = [...user.groups].sort().join(",");
4720
5614
  const want = `groups=${groups};enable=1;expire=0;email=;firstname=;lastname=;comment=${user.comment}`;
4721
- const fields = `--groups ${groups} --comment '${user.comment}'`;
5615
+ const fields2 = `--groups ${groups} --comment '${user.comment}'`;
4722
5616
  return `
4723
5617
  # User ${user.userid} (the ${user.lane} lane): in ${groups}, enabled, no password.
4724
5618
  if have=$(get /access/users/${user.userid} '${USER_STATE}'); then
4725
5619
  if [ "$have" = '${want}' ]; then echo 'user ${user.userid}: ok'
4726
5620
  else echo 'user ${user.userid}: setting groups, comment and account fields'
4727
- 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
4728
5622
  else
4729
- echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields}
5623
+ echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields2}
4730
5624
  fi
4731
5625
  `;
4732
5626
  };
@@ -4759,14 +5653,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
4759
5653
  // src/proxmox/provision-declare.ts
4760
5654
  import { adopt } from "alchemy/AdoptPolicy";
4761
5655
  import * as RemovalPolicy from "alchemy/RemovalPolicy";
4762
- import * as Effect59 from "effect/Effect";
5656
+ import * as Effect77 from "effect/Effect";
4763
5657
 
4764
5658
  // src/proxmox/role.ts
4765
5659
  import { Resource as Resource26 } from "alchemy";
4766
- import { isResolved as isResolved15 } from "alchemy/Diff";
5660
+ import { isResolved as isResolved24 } from "alchemy/Diff";
4767
5661
  import * as Provider26 from "alchemy/Provider";
4768
5662
  import * as access5 from "@distilled.cloud/proxmox/access";
4769
- import * as Effect56 from "effect/Effect";
5663
+ import * as Effect74 from "effect/Effect";
4770
5664
 
4771
5665
  // src/proxmox/role-wire.ts
4772
5666
  var ROLE_CREATE = "pve:POST /access/roles";
@@ -4789,20 +5683,20 @@ var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") =
4789
5683
 
4790
5684
  // src/proxmox/role.ts
4791
5685
  var ProxmoxRole = Resource26("Proxmox.Role");
4792
- var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect56.map((rows) => {
5686
+ var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect74.map((rows) => {
4793
5687
  if (rows === UNREADABLE)
4794
5688
  return UNREADABLE;
4795
5689
  const row = find2(rows, props.roleid);
4796
5690
  return row === undefined ? undefined : attributesOf7(row, props);
4797
5691
  }));
4798
5692
  var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
4799
- var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect56.succeed(ProxmoxRole.Provider.of({
4800
- list: () => Effect56.succeed([]),
4801
- read: Effect56.fn(function* ({ olds }) {
5693
+ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect74.succeed(ProxmoxRole.Provider.of({
5694
+ list: () => Effect74.succeed([]),
5695
+ read: Effect74.fn(function* ({ olds }) {
4802
5696
  return dropUnreadable5(yield* readRole(olds));
4803
5697
  }),
4804
- diff: Effect56.fn(function* ({ news, output }) {
4805
- if (!isResolved15(news))
5698
+ diff: Effect74.fn(function* ({ news, output }) {
5699
+ if (!isResolved24(news))
4806
5700
  return;
4807
5701
  yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
4808
5702
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
@@ -4819,7 +5713,7 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect56.succeed(
4819
5713
  }
4820
5714
  return matches7(live, news) ? { action: "noop" } : { action: "update" };
4821
5715
  }),
4822
- reconcile: Effect56.fn(function* ({ news }) {
5716
+ reconcile: Effect74.fn(function* ({ news }) {
4823
5717
  const before = dropUnreadable5(yield* readRole(news));
4824
5718
  yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
4825
5719
  yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
@@ -4830,25 +5724,25 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect56.succeed(
4830
5724
  }
4831
5725
  const after = dropUnreadable5(yield* readRole(news));
4832
5726
  if (after === undefined) {
4833
- return yield* Effect56.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."));
4834
5728
  }
4835
5729
  return after;
4836
5730
  }),
4837
- delete: Effect56.fn(function* ({ olds }) {
5731
+ delete: Effect74.fn(function* ({ olds }) {
4838
5732
  yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
4839
5733
  })
4840
5734
  })));
4841
5735
 
4842
5736
  // src/proxmox/user.ts
4843
5737
  import { Resource as Resource27 } from "alchemy";
4844
- import { isResolved as isResolved16 } from "alchemy/Diff";
5738
+ import { isResolved as isResolved25 } from "alchemy/Diff";
4845
5739
  import * as Provider27 from "alchemy/Provider";
4846
5740
  import * as access7 from "@distilled.cloud/proxmox/access";
4847
- import * as Effect58 from "effect/Effect";
5741
+ import * as Effect76 from "effect/Effect";
4848
5742
 
4849
5743
  // src/proxmox/user-wire.ts
4850
5744
  import * as access6 from "@distilled.cloud/proxmox/access";
4851
- import * as Effect57 from "effect/Effect";
5745
+ import * as Effect75 from "effect/Effect";
4852
5746
  var USER_CREATE = "pve:POST /access/users";
4853
5747
  var USER_UPDATE = "pve:PUT /access/users/{userid}";
4854
5748
  var int2 = (value) => {
@@ -4890,19 +5784,19 @@ var attributesOf8 = (live, props) => ({
4890
5784
  userid: props.userid
4891
5785
  });
4892
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(",");
4893
- var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect57.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
4894
- var readUser = (props) => readUserOrFail(props).pipe(Effect57.catchTag("UserNotFound", () => Effect57.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)));
4895
5789
  var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
4896
5790
 
4897
5791
  // src/proxmox/user.ts
4898
5792
  var ProxmoxUser = Resource27("Proxmox.User");
4899
- var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect58.succeed(ProxmoxUser.Provider.of({
4900
- list: () => Effect58.succeed([]),
4901
- read: Effect58.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 }) {
4902
5796
  return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
4903
5797
  }),
4904
- diff: Effect58.fn(function* ({ news, output }) {
4905
- if (!isResolved16(news))
5798
+ diff: Effect76.fn(function* ({ news, output }) {
5799
+ if (!isResolved25(news))
4906
5800
  return;
4907
5801
  yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
4908
5802
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
@@ -4919,7 +5813,7 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect58.succeed(
4919
5813
  }
4920
5814
  return matches8(live, news) ? { action: "noop" } : { action: "update" };
4921
5815
  }),
4922
- reconcile: Effect58.fn(function* ({ news }) {
5816
+ reconcile: Effect76.fn(function* ({ news }) {
4923
5817
  const before = dropUnreadable6(yield* readUser(news));
4924
5818
  yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
4925
5819
  yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
@@ -4930,18 +5824,18 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect58.succeed(
4930
5824
  }
4931
5825
  const after = dropUnreadable6(yield* readUser(news));
4932
5826
  if (after === undefined) {
4933
- return yield* Effect58.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."));
4934
5828
  }
4935
5829
  return after;
4936
5830
  }),
4937
- delete: Effect58.fn(function* ({ olds }) {
5831
+ delete: Effect76.fn(function* ({ olds }) {
4938
5832
  yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
4939
5833
  })
4940
5834
  })));
4941
5835
 
4942
5836
  // src/proxmox/provision-declare.ts
4943
- var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect59.gen(function* () {
4944
- const baseline = yield* Effect59.try({
5837
+ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect77.gen(function* () {
5838
+ const baseline = yield* Effect77.try({
4945
5839
  catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
4946
5840
  try: () => provisionBaseline(names2)
4947
5841
  });
@@ -4955,13 +5849,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4955
5849
  target
4956
5850
  }));
4957
5851
  const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
4958
- const users = yield* Effect59.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`, {
4959
5853
  comment: user.comment,
4960
5854
  groups: [...user.groups],
4961
5855
  target,
4962
5856
  userid: user.userid
4963
5857
  }))));
4964
- const grants = yield* Effect59.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`, {
4965
5859
  path: "/",
4966
5860
  propagate: true,
4967
5861
  roleid: grant.roleid,
@@ -4974,7 +5868,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
4974
5868
  // src/proxmox/qemu.ts
4975
5869
  import { Resource as Resource28 } from "alchemy";
4976
5870
  import * as Provider28 from "alchemy/Provider";
4977
- import * as Effect60 from "effect/Effect";
5871
+ import * as Effect78 from "effect/Effect";
4978
5872
  var ProxmoxVm = Resource28("Proxmox.Vm");
4979
5873
  var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
4980
5874
  var shape9 = (props) => ({
@@ -4985,7 +5879,7 @@ var shape9 = (props) => ({
4985
5879
  sockets: String(props.sockets ?? 1),
4986
5880
  ...props.net0 === undefined ? {} : { net0: props.net0 }
4987
5881
  });
4988
- var handlers18 = pveHandlers({
5882
+ var handlers14 = pveHandlers({
4989
5883
  attributes: (live, props) => ({
4990
5884
  cores: num(live["cores"], 1),
4991
5885
  memory: num(live["memory"], 512),
@@ -5005,11 +5899,11 @@ var handlers18 = pveHandlers({
5005
5899
  path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
5006
5900
  updateForm: shape9
5007
5901
  });
5008
- var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect60.succeed(ProxmoxVm.Provider.of(handlers18)));
5902
+ var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect78.succeed(ProxmoxVm.Provider.of(handlers14)));
5009
5903
  // src/proxmox/replication-job.ts
5010
5904
  import { Resource as Resource29 } from "alchemy";
5011
5905
  import * as Provider29 from "alchemy/Provider";
5012
- import * as Effect61 from "effect/Effect";
5906
+ import * as Effect79 from "effect/Effect";
5013
5907
 
5014
5908
  // src/proxmox/replication-job-form.ts
5015
5909
  var DEFAULT_SCHEDULE = "*/15";
@@ -5039,7 +5933,7 @@ var rateOf = (value) => {
5039
5933
  const parsed = Number.parseFloat(text(value));
5040
5934
  return Number.isFinite(parsed) ? parsed : 0;
5041
5935
  };
5042
- var handlers19 = pveHandlers({
5936
+ var handlers15 = pveHandlers({
5043
5937
  attributes: (live, props) => ({
5044
5938
  comment: text(live["comment"], ""),
5045
5939
  disable: bool(live["disable"], false),
@@ -5063,15 +5957,15 @@ var handlers19 = pveHandlers({
5063
5957
  path: (props) => `cluster/replication/${jobId(props)}`,
5064
5958
  updateForm: updateBody4
5065
5959
  });
5066
- var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect61.succeed(ProxmoxReplicationJob.Provider.of(handlers19)));
5960
+ var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect79.succeed(ProxmoxReplicationJob.Provider.of(handlers15)));
5067
5961
  // src/proxmox/sdn-apply.ts
5068
5962
  import { Resource as Resource30 } from "alchemy";
5069
- import { isResolved as isResolved17 } from "alchemy/Diff";
5963
+ import { isResolved as isResolved26 } from "alchemy/Diff";
5070
5964
  import * as Provider30 from "alchemy/Provider";
5071
- import * as Effect63 from "effect/Effect";
5965
+ import * as Effect81 from "effect/Effect";
5072
5966
 
5073
5967
  // src/proxmox/sdn-apply-read.ts
5074
- import * as Effect62 from "effect/Effect";
5968
+ import * as Effect80 from "effect/Effect";
5075
5969
  var MARKED = [
5076
5970
  "cluster/sdn/zones",
5077
5971
  "cluster/sdn/vnets",
@@ -5080,7 +5974,7 @@ var MARKED = [
5080
5974
  "cluster/sdn/route-maps"
5081
5975
  ];
5082
5976
  var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
5083
- var markedPending = (target) => Effect62.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect62.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect62.catchIf(subsystemAbsent, () => Effect62.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect62.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)));
5084
5978
  var fabricStaged = (staged, running) => {
5085
5979
  if (staged === undefined && running === undefined)
5086
5980
  return 0;
@@ -5088,9 +5982,9 @@ var fabricStaged = (staged, running) => {
5088
5982
  return "inconsistent";
5089
5983
  return staged === running ? 0 : 1;
5090
5984
  };
5091
- var fabricPending = (target) => Effect62.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect62.map(canonical3), Effect62.catchIf(subsystemAbsent, () => Effect62.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect62.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]) => {
5092
5986
  const verdict = fabricStaged(staged, running);
5093
- return verdict === "inconsistent" ? Effect62.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.")) : Effect62.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);
5094
5988
  }));
5095
5989
  var canonical3 = (value) => JSON.stringify(sortDeep(value));
5096
5990
  var sortDeep = (value) => {
@@ -5103,40 +5997,40 @@ var sortDeep = (value) => {
5103
5997
  }
5104
5998
  return value;
5105
5999
  };
5106
- var sdnPendingCount = (target) => Effect62.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect62.map(([marked, fabric]) => marked + fabric));
6000
+ var sdnPendingCount = (target) => Effect80.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect80.map(([marked, fabric]) => marked + fabric));
5107
6001
 
5108
6002
  // src/proxmox/sdn-apply.ts
5109
6003
  var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
5110
- var read3 = (props) => sdnPendingCount(props.target).pipe(Effect63.map((pending) => ({ pending })));
5111
- var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect63.succeed(ProxmoxSdnApply.Provider.of({
5112
- list: () => Effect63.succeed([]),
5113
- read: Effect63.fn(function* ({ olds }) {
5114
- 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);
5115
6009
  }),
5116
- diff: Effect63.fn(function* ({ news }) {
5117
- if (!isResolved17(news))
6010
+ diff: Effect81.fn(function* ({ news }) {
6011
+ if (!isResolved26(news))
5118
6012
  return;
5119
- const { pending } = yield* read3(news);
6013
+ const { pending } = yield* read5(news);
5120
6014
  return pending === 0 ? { action: "noop" } : { action: "update" };
5121
6015
  }),
5122
- reconcile: Effect63.fn(function* ({ news }) {
5123
- const staged = yield* read3(news);
6016
+ reconcile: Effect81.fn(function* ({ news }) {
6017
+ const staged = yield* read5(news);
5124
6018
  if (staged.pending === 0)
5125
6019
  return staged;
5126
6020
  yield* guardSdnApply;
5127
6021
  yield* pve(news.target, "provision", "PUT", "cluster/sdn");
5128
- const after = yield* read3(news);
6022
+ const after = yield* read5(news);
5129
6023
  if (after.pending !== 0) {
5130
- return yield* Effect63.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."));
5131
6025
  }
5132
6026
  return after;
5133
6027
  }),
5134
- delete: () => Effect63.void
6028
+ delete: () => Effect81.void
5135
6029
  })));
5136
6030
  // src/proxmox/sdn-subnet.ts
5137
6031
  import { Resource as Resource31 } from "alchemy";
5138
6032
  import * as Provider31 from "alchemy/Provider";
5139
- import * as Effect64 from "effect/Effect";
6033
+ import * as Effect82 from "effect/Effect";
5140
6034
 
5141
6035
  // src/proxmox/sdn-subnet-form.ts
5142
6036
  var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
@@ -5167,7 +6061,7 @@ var updateForm6 = (props) => {
5167
6061
 
5168
6062
  // src/proxmox/sdn-subnet.ts
5169
6063
  var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
5170
- var handlers20 = pveHandlers({
6064
+ var handlers16 = pveHandlers({
5171
6065
  attributes: (live, props) => {
5172
6066
  const liveVnet = text(live["vnet"]);
5173
6067
  if (liveVnet !== "" && liveVnet !== props.vnet)
@@ -5196,11 +6090,11 @@ var handlers20 = pveHandlers({
5196
6090
  path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
5197
6091
  updateForm: updateForm6
5198
6092
  });
5199
- var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect64.succeed(ProxmoxSdnSubnet.Provider.of(handlers20)));
6093
+ var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect82.succeed(ProxmoxSdnSubnet.Provider.of(handlers16)));
5200
6094
  // src/proxmox/sdn-vnet.ts
5201
6095
  import { Resource as Resource32 } from "alchemy";
5202
6096
  import * as Provider32 from "alchemy/Provider";
5203
- import * as Effect65 from "effect/Effect";
6097
+ import * as Effect83 from "effect/Effect";
5204
6098
  var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
5205
6099
  var tagOf = (value) => typeof value === "number" ? value : undefined;
5206
6100
  var shape11 = (props) => {
@@ -5220,7 +6114,7 @@ var updateForm7 = (props) => {
5220
6114
  ];
5221
6115
  return withClears(shape11(props), unset2);
5222
6116
  };
5223
- var handlers21 = pveHandlers({
6117
+ var handlers17 = pveHandlers({
5224
6118
  readRole: "provision",
5225
6119
  attributes: (live, props) => ({
5226
6120
  alias: typeof live["alias"] === "string" ? live["alias"] : "",
@@ -5238,11 +6132,11 @@ var handlers21 = pveHandlers({
5238
6132
  path: (props) => `cluster/sdn/vnets/${props.vnet}`,
5239
6133
  updateForm: updateForm7
5240
6134
  });
5241
- var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect65.succeed(ProxmoxSdnVnet.Provider.of(handlers21)));
6135
+ var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect83.succeed(ProxmoxSdnVnet.Provider.of(handlers17)));
5242
6136
  // src/proxmox/sdn-zone.ts
5243
6137
  import { Resource as Resource33 } from "alchemy";
5244
6138
  import * as Provider33 from "alchemy/Provider";
5245
- import * as Effect66 from "effect/Effect";
6139
+ import * as Effect84 from "effect/Effect";
5246
6140
  var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
5247
6141
  var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
5248
6142
  var body6 = (props) => ({
@@ -5263,7 +6157,7 @@ var clearable = {
5263
6157
  vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
5264
6158
  vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
5265
6159
  };
5266
- var handlers22 = pveHandlers({
6160
+ var handlers18 = pveHandlers({
5267
6161
  readRole: "provision",
5268
6162
  attributes: (live, props) => {
5269
6163
  const liveType = text(live["type"]);
@@ -5290,18 +6184,18 @@ var handlers22 = pveHandlers({
5290
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 ?? ""),
5291
6185
  path: (props) => `cluster/sdn/zones/${props.zone}`,
5292
6186
  updateForm: (props) => {
5293
- const fields = body6(props);
5294
- const clear = clearable[props.type].filter((option) => fields[option] === undefined);
5295
- 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);
5296
6190
  }
5297
6191
  });
5298
- var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect66.succeed(ProxmoxSdnZone.Provider.of(handlers22)));
6192
+ var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect84.succeed(ProxmoxSdnZone.Provider.of(handlers18)));
5299
6193
  // src/proxmox/storage.ts
5300
6194
  import { Resource as Resource34 } from "alchemy";
5301
- import { isResolved as isResolved18 } from "alchemy/Diff";
6195
+ import { isResolved as isResolved27 } from "alchemy/Diff";
5302
6196
  import * as Provider34 from "alchemy/Provider";
5303
6197
  import * as storage2 from "@distilled.cloud/proxmox/storage";
5304
- import * as Effect68 from "effect/Effect";
6198
+ import * as Effect86 from "effect/Effect";
5305
6199
 
5306
6200
  // src/proxmox/storage-form.ts
5307
6201
  var STORAGE_CREATE = "pve:POST /storage";
@@ -5333,7 +6227,7 @@ var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
5333
6227
 
5334
6228
  // src/proxmox/storage-wire.ts
5335
6229
  import * as storage from "@distilled.cloud/proxmox/storage";
5336
- import * as Effect67 from "effect/Effect";
6230
+ import * as Effect85 from "effect/Effect";
5337
6231
  var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
5338
6232
  var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
5339
6233
  var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
@@ -5352,8 +6246,8 @@ var attributesOf9 = (live, props) => {
5352
6246
  type: str2(data["type"], props.type)
5353
6247
  };
5354
6248
  };
5355
- var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect67.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
5356
- var readStorage = (props) => readStorageOrFail(props).pipe(Effect67.catchTag("StorageNotFound", () => Effect67.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)));
5357
6251
  var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
5358
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);
5359
6253
 
@@ -5361,13 +6255,13 @@ var matches9 = (attributes2, props) => sameList2(props.content, attributes2.cont
5361
6255
  var ProxmoxStorage = Resource34("Proxmox.Storage", {
5362
6256
  defaultRemovalPolicy: "retain"
5363
6257
  });
5364
- var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect68.succeed(ProxmoxStorage.Provider.of({
5365
- list: () => Effect68.succeed([]),
5366
- read: Effect68.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 }) {
5367
6261
  return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
5368
6262
  }),
5369
- diff: Effect68.fn(function* ({ news, output }) {
5370
- if (!isResolved18(news))
6263
+ diff: Effect86.fn(function* ({ news, output }) {
6264
+ if (!isResolved27(news))
5371
6265
  return;
5372
6266
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
5373
6267
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
@@ -5384,7 +6278,7 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect68.su
5384
6278
  }
5385
6279
  return matches9(live, news) ? { action: "noop" } : { action: "update" };
5386
6280
  }),
5387
- reconcile: Effect68.fn(function* ({ news }) {
6281
+ reconcile: Effect86.fn(function* ({ news }) {
5388
6282
  const before = dropUnreadable7(yield* readStorage(news));
5389
6283
  yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
5390
6284
  yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
@@ -5395,23 +6289,23 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect68.su
5395
6289
  }
5396
6290
  const after = dropUnreadable7(yield* readStorage(news));
5397
6291
  if (after === undefined) {
5398
- return yield* Effect68.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."));
5399
6293
  }
5400
6294
  return after;
5401
6295
  }),
5402
- delete: Effect68.fn(function* ({ olds }) {
6296
+ delete: Effect86.fn(function* ({ olds }) {
5403
6297
  yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
5404
6298
  })
5405
6299
  })));
5406
6300
  // src/proxmox/zfs-pool.ts
5407
6301
  import { Resource as Resource35 } from "alchemy";
5408
- import { isResolved as isResolved19 } from "alchemy/Diff";
6302
+ import { isResolved as isResolved28 } from "alchemy/Diff";
5409
6303
  import * as Provider35 from "alchemy/Provider";
5410
- import * as Effect71 from "effect/Effect";
6304
+ import * as Effect89 from "effect/Effect";
5411
6305
 
5412
6306
  // src/proxmox/zfs-pool-wire.ts
5413
6307
  import * as nodes10 from "@distilled.cloud/proxmox/nodes";
5414
- import * as Effect69 from "effect/Effect";
6308
+ import * as Effect87 from "effect/Effect";
5415
6309
  var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
5416
6310
  const vdev = entry;
5417
6311
  const nested = leafDevices(vdev.children);
@@ -5424,18 +6318,18 @@ var attributesOf10 = (live, props) => ({
5424
6318
  node: props.node,
5425
6319
  state: text(live.state, "unknown")
5426
6320
  });
5427
- var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect69.map((live) => {
6321
+ var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes10.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect87.map((live) => {
5428
6322
  if (live === UNREADABLE)
5429
6323
  return UNREADABLE;
5430
6324
  return text(live.name) === "" ? undefined : attributesOf10(live, props);
5431
6325
  }));
5432
- var readPool = (props) => readPoolOrFail(props).pipe(Effect69.orElseSucceed(() => {
6326
+ var readPool2 = (props) => readPoolOrFail(props).pipe(Effect87.orElseSucceed(() => {
5433
6327
  return;
5434
6328
  }));
5435
6329
  var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
5436
6330
 
5437
6331
  // src/proxmox/zfs-pool-form.ts
5438
- import * as Effect70 from "effect/Effect";
6332
+ import * as Effect88 from "effect/Effect";
5439
6333
  import * as nodes11 from "@distilled.cloud/proxmox/nodes";
5440
6334
  var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
5441
6335
  var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
@@ -5456,25 +6350,25 @@ var toDistilledCreate5 = (props) => {
5456
6350
  };
5457
6351
  };
5458
6352
  var SETTLE_ATTEMPTS = 30;
5459
- var createPool = (props, read4) => Effect70.gen(function* () {
5460
- const existing = yield* read4(props);
6353
+ var createPool2 = (props, read6) => Effect88.gen(function* () {
6354
+ const existing = yield* read6(props);
5461
6355
  if (existing !== undefined)
5462
6356
  return existing;
5463
6357
  if (props.devices === undefined || props.raidlevel === undefined) {
5464
- return yield* Effect70.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."));
5465
6359
  }
5466
6360
  yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
5467
6361
  const upid = yield* runPve(props.target, "provision", true, nodes11.createNodeDiskZfs(toDistilledCreate5(props)));
5468
- const settle3 = (attempts) => Effect70.gen(function* () {
5469
- const live = yield* read4(props);
6362
+ const settle3 = (attempts) => Effect88.gen(function* () {
6363
+ const live = yield* read6(props);
5470
6364
  if (live !== undefined || attempts <= 0)
5471
6365
  return live;
5472
- yield* Effect70.sleep("2 seconds");
6366
+ yield* Effect88.sleep("2 seconds");
5473
6367
  return yield* settle3(attempts - 1);
5474
6368
  });
5475
6369
  const after = yield* settle3(SETTLE_ATTEMPTS);
5476
6370
  if (after === undefined) {
5477
- return yield* Effect70.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."));
5478
6372
  }
5479
6373
  return after;
5480
6374
  });
@@ -5484,13 +6378,13 @@ var destroyPool = (props) => runPve(props.target, "provision", true, nodes11.del
5484
6378
  var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
5485
6379
  defaultRemovalPolicy: "retain"
5486
6380
  });
5487
- var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect71.succeed(ProxmoxZfsPool.Provider.of({
5488
- list: () => Effect71.succeed([]),
5489
- read: Effect71.fn(function* ({ olds, output }) {
5490
- 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));
5491
6385
  }),
5492
- diff: Effect71.fn(function* ({ news, output }) {
5493
- if (!isResolved19(news))
6386
+ diff: Effect89.fn(function* ({ news, output }) {
6387
+ if (!isResolved28(news))
5494
6388
  return;
5495
6389
  yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
5496
6390
  if (output === undefined)
@@ -5506,10 +6400,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect71.su
5506
6400
  }
5507
6401
  return { action: "noop" };
5508
6402
  }),
5509
- reconcile: Effect71.fn(function* ({ news }) {
5510
- return yield* createPool(news, (props) => readPool(props).pipe(Effect71.map(dropUnreadable8)));
6403
+ reconcile: Effect89.fn(function* ({ news }) {
6404
+ return yield* createPool2(news, (props) => readPool2(props).pipe(Effect89.map(dropUnreadable8)));
5511
6405
  }),
5512
- delete: Effect71.fn(function* ({ olds }) {
6406
+ delete: Effect89.fn(function* ({ olds }) {
5513
6407
  yield* destroyPool(olds);
5514
6408
  })
5515
6409
  })));
@@ -5589,5 +6483,5 @@ export {
5589
6483
  violations
5590
6484
  };
5591
6485
 
5592
- //# debugId=7C16E956F2A3150864756E2164756E21
6486
+ //# debugId=ECA89AEF81AD3AD164756E2164756E21
5593
6487
  //# sourceMappingURL=index.js.map