@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
@@ -35,8 +35,8 @@
35
35
  * `/api2/json/config/sync` is not even evidence that the endpoint exists. THE FIRST PLAN
36
36
  * AGAINST A LIVE PBS IS THE MEASUREMENT. Read it; do not deploy it unseen.
37
37
  * ⚠️ AN ERROR BODY IS NOT WRAPPED: the 401 body is the bare text `authentication failed`, not
38
- * JSON. `pve()` checks `response.ok` before it parses, so a `pbs()` copied from it is safe —
39
- * provided the check stays in that order.
38
+ * JSON. The original `pve()` checked status before decoding success data. The distilled PBS
39
+ * protocol preserves this distinction and classifies error text through SDK patches.
40
40
  *
41
41
  * ⚠️ `Proxmox.BackupJob` AND `Pbs.SyncJob` ARE NOT THE SAME FAMILY AND THE NAMES NEARLY COLLIDE.
42
42
  * The first is a PVE vzdump schedule; this is a Proxmox Backup SERVER sync job, on a different
@@ -54,6 +54,8 @@
54
54
  */
55
55
  import { Resource } from 'alchemy';
56
56
  import * as Provider from 'alchemy/Provider';
57
+ import type { SyncJobAttributes } from './pbs-sync-job-attributes.ts';
58
+ export type { SyncJobAttributes } from './pbs-sync-job-attributes.ts';
57
59
  import type { PveRequirements } from './resource.ts';
58
60
  import type { WithPbsTarget } from './resource.ts';
59
61
  /**
@@ -155,10 +157,10 @@ export interface SyncJobProps extends WithPbsTarget {
155
157
  'transfer-last'?: number;
156
158
  /**
157
159
  * ⛔ NOT DECLARABLE, AND A COMPILE ERROR RATHER THAN A PROP THAT HALF WORKS — the storage.ts
158
- * `password` idiom, for two independent reasons, either of which alone would be enough.
160
+ * `password` idiom. Two barriers originally applied; the second remains unresolved.
159
161
  * 1. THE WIRE FORM IS AN ARRAY. PBS takes `group-filter` as a repeated parameter
160
- * (`group-filter=type:ct&group-filter=regex:^web`), and `pve()`'s form is
161
- * `Record<string, string>` — one value per key — so a list cannot be expressed at all.
162
+ * (`group-filter=type:ct&group-filter=regex:^web`). The old single-value form could not
163
+ * express it; the distilled PBS protocol now supports repeated keys.
162
164
  * 2. NARROWING A FILTER MAY BE A DELETE. Whether `remove-vanished` skips the groups a filter
163
165
  * excludes is NOT something I could measure without a credential. If it does not, then
164
166
  * editing this field on a job with `remove-vanished` set removes every local group the new
@@ -166,8 +168,8 @@ export interface SyncJobProps extends WithPbsTarget {
166
168
  * half-working prop.
167
169
  * ★ IT IS STILL REPORTED in the attributes, so a plan shows the filters a hand-made job carries
168
170
  * and this resource never strips them: undeclared is unmanaged, and PBS's update assigns only
169
- * what it was sent. To make it declarable, fix both halves — a form type that can repeat a
170
- * key, and a MEASURED answer to (2) — in that order.
171
+ * what it was sent. Making it declarable still needs a MEASURED answer to (2), even though
172
+ * the SDK now supplies the necessary repeated-key transport.
171
173
  */
172
174
  'group-filter'?: never;
173
175
  /**
@@ -192,38 +194,6 @@ export interface SyncJobProps extends WithPbsTarget {
192
194
  */
193
195
  'verified-only'?: boolean;
194
196
  }
195
- export interface SyncJobAttributes {
196
- /** From the props — it is the path this object was read by. */
197
- id: string;
198
- store: string;
199
- /** `''` is the root namespace, which is also how PBS spells it. */
200
- ns: string;
201
- /** `''` means a LOCAL sync — datastore to datastore on this PBS. */
202
- remote: string;
203
- 'remote-store': string;
204
- 'remote-ns': string;
205
- /** `''` means the job runs only when somebody starts it. */
206
- schedule: string;
207
- comment: string;
208
- 'remove-vanished': boolean;
209
- /** ⚠️ Absent on the wire means `false`, PBS's own default. */
210
- 'verified-only': boolean;
211
- /** `''` means no owner in the config, i.e. `root@pam` at runtime. An identifier, never a secret. */
212
- owner: string;
213
- /** Bytes per second, or `UNSET` (-1). See `bytes()`. */
214
- 'rate-in': number;
215
- /** `UNSET` (-1) means unset, which means full recursion. `0` is a real value meaning none. */
216
- 'max-depth': number;
217
- /** `UNSET` (-1) means all snapshots. */
218
- 'transfer-last': number;
219
- /**
220
- * ⚠️ REPORTED, NEVER SENT, NEVER COMPARED — the live filter list joined for display only. See the
221
- * ⛔ on the prop for why it is not declarable.
222
- */
223
- 'group-filter': string;
224
- /** `pull` or `push`. Reported, never compared — create-only here. */
225
- 'sync-direction': string;
226
- }
227
197
  export interface PbsSyncJob extends Resource<'Pbs.SyncJob', SyncJobProps, SyncJobAttributes, never, PveRequirements> {
228
198
  }
229
199
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pbs-sync-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-sync-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD;;;;;;;OAOG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC;IACvB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,oGAAoG;IACpG,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,8FAA8F;IAC9F,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,gIACgD,CAAC"}
1
+ {"version":3,"file":"pbs-sync-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-sync-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD;;;;;;;OAOG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC;IACvB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,gIACgD,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { PbsVerifyJobAttributes, PbsVerifyJobProps } from './pbs-verify-job.ts';
4
+ export declare const handlers: {
5
+ list: () => Effect.Effect<never[], never, never>;
6
+ read: ({ olds }: {
7
+ olds: PbsVerifyJobProps;
8
+ }) => Effect.Effect<PbsVerifyJobAttributes | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | import("@distilled.cloud/proxmox-backup/Protocol").ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
9
+ diff: ({ news, output, }: {
10
+ news: Input<PbsVerifyJobProps>;
11
+ output: PbsVerifyJobAttributes | undefined;
12
+ }) => Effect.Effect<{
13
+ readonly action: "noop" | "update";
14
+ } | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
15
+ reconcile: ({ news }: {
16
+ news: PbsVerifyJobProps;
17
+ }) => Effect.Effect<PbsVerifyJobAttributes, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
18
+ delete: ({ olds }: {
19
+ olds: PbsVerifyJobProps;
20
+ }) => Effect.Effect<import("@distilled.cloud/proxmox-backup/config").DeleteConfigVerifyResponse | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | import("@distilled.cloud/proxmox-backup/Protocol").ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
21
+ };
22
+ //# sourceMappingURL=pbs-verify-job-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pbs-verify-job-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-verify-job-lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAMrF,eAAO,MAAM,QAAQ;IAEnB,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE;IAC5C,IAAI,sBAGD;QACD,IAAI,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,EAAE,sBAAsB,GAAG,SAAS,CAAC;KAC5C;;;IAaD,SAAS,aAAa;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE;IAgBjD,MAAM,aAAa;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE;CAC/C,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** PBS 4.2.6-1 values and vendor form guards; SDK transport is in pbs-verify-job-wire.ts. */
2
+ import type { PbsVerifyJobAttributes, PbsVerifyJobProps } from './pbs-verify-job.ts';
3
+ import type { PveSpec } from './resource-spec.ts';
4
+ /**
5
+ * ⚠️ EVERY FIELD IS READ THROUGH `values.ts` THOUGH PBS RETURNS REAL JSON TYPES. Unlike PVE's
6
+ * SectionConfig round-trip, `GET /config/verify/{id}` serialises a Rust struct, so
7
+ * `ignore-verified` arrives as a JSON boolean and `max-depth` as a JSON number. `bool` and `int`
8
+ * accept both spellings, and using them costs nothing while covering the version that does not.
9
+ * ⚠️ `digest` IS NOT AN ATTRIBUTE AND CANNOT BECOME ONE BY ACCIDENT. PBS puts it on the rpcenv, and
10
+ * the JSON formatter adds rpcenv attributes as SIBLINGS of `data` — `{"data":{…},"digest":"…"}` —
11
+ * so both the old client and the SDK expose only the resource data. Attributes deliberately
12
+ * exclude it: the digest covers verification.cfg as a FILE, so keeping it would churn state
13
+ * whenever an unrelated verify job was edited.
14
+ */
15
+ export declare const spec: PveSpec<PbsVerifyJobProps, PbsVerifyJobAttributes>;
16
+ //# sourceMappingURL=pbs-verify-job-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pbs-verify-job-spec.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-verify-job-spec.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,OAAO,KAAK,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAIlD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,EAAE,OAAO,CAAC,iBAAiB,EAAE,sBAAsB,CA0DnE,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** PBS 4.2.6-1 operations through @distilled.cloud/proxmox-backup. */
2
+ import * as config from '@distilled.cloud/proxmox-backup/config';
3
+ import * as Effect from 'effect/Effect';
4
+ import type { PbsVerifyJobProps } from './pbs-verify-job.ts';
5
+ /** ⛔ Only the SDK VerifyJobNotFound is absence; permission, transport and malformed replies propagate. */
6
+ export declare const readOne: (props: PbsVerifyJobProps) => Effect.Effect<import("./pbs-verify-job.ts").PbsVerifyJobAttributes | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | config.ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
7
+ export declare const createOne: (props: PbsVerifyJobProps) => Effect.Effect<config.VerifyConfigResponse, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | config.ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
8
+ export declare const updateOne: (props: PbsVerifyJobProps) => Effect.Effect<config.PutConfigVerifyResponse, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | config.ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
9
+ /** Deleting the schedule never deletes backups; already missing is a successful deletion. */
10
+ export declare const deleteOne: (props: PbsVerifyJobProps) => Effect.Effect<config.DeleteConfigVerifyResponse | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | config.ProxmoxBackupOpError, import("effect/unstable/http/HttpClient").HttpClient>;
11
+ //# sourceMappingURL=pbs-verify-job-wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pbs-verify-job-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-verify-job-wire.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,OAAO,KAAK,MAAM,MAAM,wCAAwC,CAAC;AACjE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAkC7D,0GAA0G;AAC1G,eAAO,MAAM,OAAO,UAAW,iBAAiB,4QAI7C,CAAC;AAEJ,eAAO,MAAM,SAAS,UAAW,iBAAiB,uOAC4B,CAAC;AAE/E,eAAO,MAAM,SAAS,UAAW,iBAAiB,0OAC+B,CAAC;AAElF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,UAAW,iBAAiB,yPAK9C,CAAC"}
@@ -9,22 +9,22 @@
9
9
  * green `Proxmox.BackupJob` makes it worse rather than better, because a vzdump job that says
10
10
  * "OK" every night is exactly the evidence people stop at.
11
11
  *
12
- * ⛔ THIS FILE CANNOT AUTHENTICATE UNTIL TWO THINGS OUTSIDE IT CHANGE, AND IT IS SHIPPED SAYING SO
13
- * RATHER THAN SHIPPED LOOKING FINISHED. Both are recorded on `PbsTarget` below:
12
+ * ⚠️ HISTORICAL BOOTSTRAP BLOCKER, 2026-09-13: TWO THINGS OUTSIDE THIS FILE WERE MISSING
13
+ * before PBS could authenticate. Both are now resolved by the PBS lane and SDK:
14
14
  * 1. `credentials.ts` has no PBS mint. MEASURED 2026-09-13 from this Mac: the agent's
15
15
  * approle is refused `sys/mounts`, `kv/infra/proxmox` and `proxmox-pbs/creds/read` alike —
16
16
  * 403 permission denied on all three — so there is no mount to name and none is invented.
17
17
  * 2. `client.ts`'s `authorization()` builds a PVE header, and PBS will not accept it. See the
18
18
  * ⛔ on `PbsTarget`.
19
- * Until both land, every call 401s, `pveOperations.read` folds that into "absent", and the plan
20
- * reports `create` for a job that is plainly there. The deploy then FAILS at the POST rather
19
+ * Before both landed, every call 401'd, `pveOperations.read` folded that into "absent", and the plan
20
+ * reported `create` for a job that was plainly there. The deploy then FAILED at the POST rather
21
21
  * than clobbering anything — an unauthenticated POST 401s, and an authenticated one is refused
22
22
  * because PBS will not take a duplicate section id — so the cost is a LYING PLAN and a broken
23
23
  * run, not a lost job. That is still reason enough not to wire it into a stack yet: a plan
24
24
  * nobody can trust is its own outage.
25
25
  *
26
- * ⚠️ THE SHAPES BELOW ARE READ FROM PUBLISHED SOURCE, NOT MEASURED ON THIS ESTATE. Everything is
27
- * taken from `pbs-api-types/src/jobs.rs`, `src/api2/config/verify.rs`, `src/server/verify_job.rs`
26
+ * ⚠️ THE ORIGINAL SHAPES WERE READ FROM PUBLISHED SOURCE, NOT MEASURED ON THIS ESTATE. Everything is
27
+ * originally taken from `pbs-api-types/src/jobs.rs`, `src/api2/config/verify.rs`, `src/server/verify_job.rs`
28
28
  * and `src/backup/verify.rs` at HEAD of the Proxmox git mirrors on 2026-09-13. What WAS measured
29
29
  * here: `pbs.example.com:8007` and `pbs.mgmt.example.com:8007` both answer HTTP 401 under
30
30
  * STRICT TLS (curl `ssl_verify_result=0`), so no `-k` is needed and `client.ts`'s plain `fetch`
@@ -34,7 +34,7 @@
34
34
  import { Resource } from 'alchemy';
35
35
  import * as Provider from 'alchemy/Provider';
36
36
  import type { PbsTarget } from './credentials.ts';
37
- import { type PveRequirements } from './resource.ts';
37
+ import type { PveRequirements } from './resource-spec.ts';
38
38
  export interface PbsVerifyJobProps {
39
39
  /** ⛔ A PBS host, never a PVE cluster. See `PbsTarget`. */
40
40
  target: PbsTarget;
@@ -128,7 +128,7 @@ export interface PbsVerifyJob extends Resource<'Pbs.VerifyJob', PbsVerifyJobProp
128
128
  */
129
129
  export declare const PbsVerifyJob: import("alchemy").ResourceClass<PbsVerifyJob>;
130
130
  /**
131
- * ⛔ `list` IS EMPTY, INHERITED FROM `pveHandlers`, AND THE REASON IS SHARPER HERE THAN ELSEWHERE.
131
+ * ⛔ `list` STAYS EXPLICITLY EMPTY, AND THE REASON IS SHARPER HERE THAN ELSEWHERE.
132
132
  * `GET /config/verify` returns every verification job the caller can see, including the
133
133
  * ones the PBS installer and a human made. Adopting those would put Alchemy one dropped line
134
134
  * away from DELETING a datastore's only verification schedule — and deleting it destroys nothing
@@ -136,10 +136,10 @@ export declare const PbsVerifyJob: import("alchemy").ResourceClass<PbsVerifyJob>
136
136
  * to say when the rot started. Adoption stays explicit: declare the existing id.
137
137
  *
138
138
  * ⚠️ NO `digest` IS SENT ON UPDATE OR DELETE, SO THIS IS LAST-WRITER-WINS. PBS accepts an optional
139
- * `digest` on both and would refuse a write made against a stale read. Sending one would need a
140
- * read-then-write window this provider does not have — `pveOperations` reads and writes in two
141
- * separate calls with two separate mints — so a concurrent edit from the PBS UI is overwritten
142
- * rather than refused. Same behaviour as every PVE family here; stated because PBS offers better.
139
+ * `digest` on both and would refuse a write made against a stale read. This migration preserves
140
+ * the existing forms, which omit it; the SDK runner reuses leased credentials but does not add
141
+ * an optimistic-lock check. A concurrent edit from the PBS UI may therefore be overwritten
142
+ * rather than refused. PBS offers that stronger guarantee as a separate provider change.
143
143
  */
144
144
  export declare const PbsVerifyJobProvider: () => import("effect/Layer").Layer<Provider.Provider<PbsVerifyJob>, never, import("effect/unstable/http/HttpClient").HttpClient>;
145
145
  //# sourceMappingURL=pbs-verify-job.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pbs-verify-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-verify-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,eAAe,CAAC;AAGlE,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,MAAM,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ,CAC5C,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,+CAA0C,CAAC;AAyEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,kIACkD,CAAC"}
1
+ {"version":3,"file":"pbs-verify-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-verify-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,MAAM,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ,CAC5C,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,+CAA0C,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,kIACkD,CAAC"}
@@ -17,8 +17,10 @@
17
17
  */
18
18
  import { Resource } from 'alchemy';
19
19
  import * as Provider from 'alchemy/Provider';
20
+ import * as pools from '@distilled.cloud/proxmox/pools';
21
+ import * as Effect from 'effect/Effect';
20
22
  import type { PoolsPoolidPutParams, PoolsPostParams } from './generated/pve.ts';
21
- import { type PveRequirements, type WithTarget } from './resource.ts';
23
+ import type { PveRequirements, WithTarget } from './resource-spec.ts';
22
24
  export interface PoolProps extends WithTarget {
23
25
  /** PVE's primary key for a pool. Changing it is a replace, not an update. */
24
26
  poolid: string;
@@ -38,5 +40,12 @@ export declare const ProxmoxPool: import("alchemy").ResourceClass<ProxmoxPool>;
38
40
  export declare const poolCreateForm: (props: PoolProps) => PoolsPostParams;
39
41
  /** PUT body — only `comment`; path carries `poolid`. Matches deprecated `/pools/{poolid}`. */
40
42
  export declare const poolUpdateForm: (props: PoolProps) => Pick<PoolsPoolidPutParams, 'comment'>;
43
+ /** Pool absence is PVE's typed 500, not a catch-all; pve-manager 9.2.11. */
44
+ export declare const readPool: (props: PoolProps) => Effect.Effect<{
45
+ comment: string;
46
+ members: number;
47
+ poolid: string;
48
+ } | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | import("@distilled.cloud/caddy").Conflict | Error | import("@distilled.cloud/caddy").Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/caddy").Locked | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/caddy").UnprocessableEntity | import("@distilled.cloud/proxmox/Errors").ClientErrors, import("effect/unstable/http/HttpClient").HttpClient>;
49
+ export declare const deletePool: (props: PoolProps) => Effect.Effect<void | pools.DeletePoolResponse, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | import("@distilled.cloud/caddy").Conflict | Error | import("@distilled.cloud/caddy").Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/caddy").Locked | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/caddy").UnprocessableEntity | import("@distilled.cloud/proxmox/Errors").ClientErrors, import("effect/unstable/http/HttpClient").HttpClient>;
41
50
  export declare const ProxmoxPoolProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxPool>, never, import("effect/unstable/http/HttpClient").HttpClient>;
42
51
  //# sourceMappingURL=pool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAEnF,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,SAAS,EACT,cAAc,EACd,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAAwC,CAAC;AAEjE,mFAAmF;AACnF,eAAO,MAAM,cAAc,UAAW,SAAS,KAAG,eAGhD,CAAC;AAEH,8FAA8F;AAC9F,eAAO,MAAM,cAAc,UAAW,SAAS,KAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAEpF,CAAC;AAoBH,eAAO,MAAM,mBAAmB,iIACiD,CAAC"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AAGxD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAW,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAK/E,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,SAAS,EACT,cAAc,EACd,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAAwC,CAAC;AAEjE,mFAAmF;AACnF,eAAO,MAAM,cAAc,UAAW,SAAS,KAAG,eAGhD,CAAC;AAEH,8FAA8F;AAC9F,eAAO,MAAM,cAAc,UAAW,SAAS,KAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAEpF,CAAC;AAsBH,4EAA4E;AAC5E,eAAO,MAAM,QAAQ,UAAW,SAAS;;;;o+BAiBtC,CAAC;AAEJ,eAAO,MAAM,UAAU,UAAW,SAAS,ygCAIxC,CAAC;AAEJ,eAAO,MAAM,mBAAmB,iIAoD7B,CAAC"}
@@ -0,0 +1,31 @@
1
+ # PBS jobs on distilled
2
+
3
+ The datastore, prune, sync and verification providers use the typed operations in
4
+ `@distilled.cloud/proxmox-backup`, generated from **proxmox-backup-server 4.2.6-1**.
5
+ `distilled-pbs.ts` supplies the existing leased read/provision credentials to the SDK's
6
+ Credentials service. The SDK owns the `PBSAPIToken` header and request serialization.
7
+
8
+ Existing props and state remain unchanged: retention windows omitted from a declaration
9
+ stay unmanaged, `remove-vanished` still defaults to false, a verification schedule may be
10
+ explicitly null, and a datastore path or backend change is refused. No automatic clearing,
11
+ new deletion flag or live job execution is introduced.
12
+
13
+ Read-only probes on 2026-09-24 found that missing configuration sections answer plain-text
14
+ HTTP 400: `no such datastore`, `no such prune`, `no such sync`, or `no such verification`.
15
+ The SDK patches those specific GET errors as `DatastoreNotFound`, `PruneJobNotFound`,
16
+ `SyncJobNotFound` and `VerifyJobNotFound`. Providers catch those tags only. Permission
17
+ errors, unrelated 400/500 responses, malformed replies and failed transports propagate.
18
+ A failed read can no longer become a speculative create or a successful deletion.
19
+
20
+ Delete first reads the declared item. A typed absent item needs no DELETE. When it exists,
21
+ the provider calls the SDK delete operation; any unclassified error during a concurrent
22
+ removal propagates. The datastore retains its bounded readback and default retain policy;
23
+ `destroy-data` is never sent.
24
+
25
+ The pinned schema declares a null result for datastore create and a UPID for delete. The
26
+ provider verifies the resulting configuration rather than inventing a create task id.
27
+
28
+ The offline lifecycle suite uses `alchemy/Test/Bun` and a strict in-memory PBS HTTP
29
+ fixture. It covers matching adoption without writes, genuine create, drift repair followed
30
+ by a no-op plan, and refused reads. Live adoption remains a separate consumer gate against
31
+ the released package; an offline fixture is not proof of the running estate.
@@ -12,6 +12,11 @@
12
12
 
13
13
  A target that no matcher names receives nothing. Declare both.
14
14
 
15
+ PBS lifecycle calls use `@distilled.cloud/proxmox-backup` (vendor schema PBS 4.2.6-1).
16
+ The runner reuses short-lived OpenBao leases and bounds each request to 20 seconds, with SDK
17
+ retries disabled. A typed `NotFound` alone means absence or an already completed delete.
18
+ Other read failures stop the plan, so a denied read cannot silently plan a create.
19
+
15
20
  ## Paging on a failed verify, sync, prune or garbage collection
16
21
 
17
22
  ```ts
@@ -157,7 +162,8 @@ built-in reverts it to the shipped rule**, and the server reports success. The a
157
162
  | PBS | `Sys.Audit` on `/system/notifications` | `Sys.Modify` on `/system/notifications` |
158
163
  | PVE | `Mapping.Audit` or `Mapping.Modify` on `/mapping/notifications` | `Mapping.Modify` on `/mapping/notifications` |
159
164
 
160
- ⚠️ A missing read privilege looks like "absent". The plan says `create` and the server answers
161
- that the object already exists. Grant the read before changing anything else. On PVE,
165
+ ⚠️ The old shared client hid missing read privileges as "absent", planning a `create` that the
166
+ server rejected as already existing. PBS now preserves the SDK refusal and fails the plan.
167
+ Grant the required read privilege before changing infrastructure. On PVE,
162
168
  `Mapping.Use` is not enough: it opens the matcher list, not the per-matcher read this package
163
169
  makes (`get_matcher` in pve-manager's `Notifications.pm`).
@@ -0,0 +1,28 @@
1
+ # PVE notification SDK migration
2
+
3
+ PVE 9.2.11 notification targets and matchers use named distilled SDK operations
4
+ for every lifecycle call. Their existing declaration forms, comparison rules,
5
+ secret omission and vendor constraint guards remain in place.
6
+
7
+ A missing matcher or target returns HTTP 404, confirmed by read-only probes on
8
+ 2026-09-24. The provider catches the SDK's typed `NotFound` for absent reads and
9
+ idempotent deletes. Authentication, permissions, transport failures and unrelated
10
+ server errors propagate. A malformed successful read fails against the SDK's
11
+ response schema; diagnostics do not retain response contents.
12
+
13
+ SDK request schemas validate the form-name translation before sending a write.
14
+ Arrays use repeated form keys. Commas within matcher rules and webhook property
15
+ strings remain part of each item. An explicitly empty recipient/header list uses
16
+ the vendor's update `delete` field, because an empty SDK array emits no keys.
17
+ Undeclared fields remain unmanaged.
18
+
19
+ A matching adoption sends no write. A changed name replaces the resource; an
20
+ endpoint changing type under the same name replaces delete-first because the
21
+ name is unique across types. Built-in deletion still reverts the vendor default,
22
+ and a gotify create still refuses its missing write-only token: this migration
23
+ does not introduce secrets into persisted props.
24
+
25
+ `notification-distilled.test.ts` exercises create, matching adoption, list clearing,
26
+ identity replacement, malformed/refused reads and subsequent no-op plans through
27
+ the upstream Alchemy harness and real SDK protocol. Live verification follows the
28
+ published release in the consuming stack; fixtures do not prove live writes.
@@ -0,0 +1,60 @@
1
+ # PVE/PBS SDK migration
2
+
3
+ 2026-09-24. Transport completion and live adoption are separate gates. The interim
4
+ SDKs follow [the distilled route](./distilled-interim.md): generated vendor schemas,
5
+ local distilled fixes first, verbatim source copies, and published npm aliases.
6
+
7
+ ## Implemented transport
8
+
9
+ Seventeen existing PVE resource families use named distilled operations throughout:
10
+ ACL, ApiToken, BackupJob, CephDaemon, CephFs, CephOsd, CephPool, Group, MetricServer,
11
+ NodeNetwork, NotificationMatcher, NotificationTarget, Pool, Role, Storage, User and
12
+ ZfsPool. All six existing PBS families now do too: Datastore, NotificationMatcher,
13
+ NotificationTarget, PruneJob, SyncJob and VerifyJob.
14
+
15
+ The latest migration preserves credential leases, safe member failover, bounded
16
+ polling, vendor constraints, write-only secret handling and existing state shapes.
17
+ The newly migrated pool, metric, job and notification providers fold only typed
18
+ missing-resource errors to absence; their other SDK failures propagate.
19
+ Protocol fixtures exercise the generated operations; Alchemy engine tests prove
20
+ matching adoption does not write and a real update settles to a no-op.
21
+
22
+ The SDK patches record PVE 9.2.11 source and PBS 4.2.6-1 schema provenance. Read-only
23
+ probes against PVE 9.2.11 and PBS 4.2.3 distinguish notification HTTP404 responses
24
+ from PBS configuration HTTP400 responses. Resource providers do not inspect status
25
+ codes or message text to make that distinction.
26
+
27
+ ## Consumer gate
28
+
29
+ These families cover the default TB4/HF-Ops/PBS adoption declarations in
30
+ `homeflare-proxmox` at commit `91b31b0`. Its opt-in vault-door Lxc is still a later
31
+ slice. This source migration alone does not establish live adoption.
32
+ After both SDKs and the provider release publish, the consumer pins those exact
33
+ versions, runs its full check and all three adoption verifiers, then compares the
34
+ read and admin plans at stage `live`. A write requires a fresh matching proof.
35
+
36
+ ## Remaining PVE families
37
+
38
+ Twelve existing families still retain some hand-client transport: Lxc, Vm (QEMU),
39
+ NetworkApply, SdnApply, SdnZone, SdnVnet, SdnSubnet, HaResource, HaRule,
40
+ ReplicationJob, FirewallAlias and CephFlag. Their previous measured-none records
41
+ are historical inventories, not a current census or permission to skip migration.
42
+
43
+ - NetworkApply needs the `changes` sibling beside the response's `data`; generic
44
+ unwrapping discards it. Fix the distilled protocol/schema boundary before moving
45
+ this read. The network diff can contain secrets and must not enter state or logs.
46
+ - Lxc task polling must keep the credential that started the task, preserve bounded
47
+ uncertainty after connection loss, and retain its cluster-wide identity checks.
48
+ - SdnApply distinguishes an unavailable subsystem from failed reads and refuses
49
+ undiffable settings. Those semantics need vendor-backed typed SDK errors.
50
+ - Qemu's guest DELETE route differs from its configuration GET/PUT route; copying
51
+ the old generic factory path would keep an existing bug.
52
+
53
+ Complete vendor coverage additionally requires a census of families not yet modeled
54
+ as resources. The 23 migrated families above are a transport milestone, not that
55
+ larger completion claim. No upstream push or pull request was made.
56
+
57
+ Existing NodeNetwork and ZfsPool adoption/reconcile reads still contain broad failure
58
+ folds. Their strict diff reads do not remove that separate conformance gap. CephFs
59
+ task polling also tolerates temporary read failures, but uses a bounded uncertainty
60
+ policy rather than treating a resource as absent.
@@ -10,6 +10,9 @@ measurements and open findings are preserved below.
10
10
  - 27 `Effect.die` sites in non-test source (defect risk, not a separate S20 ban);
11
11
  - 7 test files on `node:test`;
12
12
  - `Pbs.*` type strings (H14).
13
+ **SDK progress, 2026-09-24:** 17 PVE and all six existing PBS resource families now
14
+ call named distilled operations. The [migration record](./proxmox-sdk-migration.md)
15
+ separates that transport milestone, the published-consumer gate and remaining families.
13
16
  8. **`talos/*`.** Its `talosctl` calls go through `ChildProcessSpawner`, as upstream's Docker
14
17
  provider does, which conforms. Its file handling uses `Bun.file`, `Bun.write`,
15
18
  `Bun.YAML` and `node:fs` (S42), and it uses `Effect.promise` in 3 files (S19). The
@@ -75,8 +78,9 @@ HttpClient` client. The old status-carrying `NetboxError` and its `cause.status
75
78
  12. **Resource JSDoc is not in upstream's generator format.** Zero files use `@resource`,
76
79
  `**Example:**` or `### Section` (S31, H10). **Decision:** maintainer, because it moves
77
80
  the house's glyph rationale into `//` comments.
78
- 13. **Tests never use `alchemy/Test/Bun`.** Every lifecycle is proven against loopback
79
- fakes (S28, H12). Live suites need a place to run, and that is a maintainer decision.
81
+ 13. **Original finding: tests did not use `alchemy/Test/Bun`.** The PVE/PBS SDK migration
82
+ now exercises provider lifecycles through that upstream engine harness with fake HTTP
83
+ responses. It does not claim live write coverage; live suites still need a place to run.
80
84
  14. **`discord/*` is new (2026-09-24, task-authorized, kit PR TBD) and built directly on
81
85
  `@distilled.cloud/discord` — no hand-rolled `client.ts` ever existed to retire.**
82
86
  `Discord.ApplicationCommand` and `Discord.GuildApplicationCommand` call the SDK's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homeflare/alchemy",
3
- "version": "0.37.2",
3
+ "version": "0.37.3",
4
4
  "description": "Custom Alchemy providers for Cloudflare, Proxmox, OpenBao, Forgejo, Talos, launchd and Caddy, and a verifier that proves adoptions are no-ops.",
5
5
  "homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/alchemy#readme",
6
6
  "bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
@@ -2,10 +2,9 @@
2
2
  * Adopting an object that already matches must be read-only — measured per reconcile SHAPE, through
3
3
  * Alchemy's own Plan and Apply over a fake cluster (fake-pve.ts, ../verify/fake-engine.ts).
4
4
  *
5
- * ★ ONE ROW PER WAY A FAMILY REACHES ITS WRITE, not one per family. Every family built on
6
- * `pveHandlers` shares resource.ts's reconcile, so `Proxmox.Pool` stands for all of them; `Acl`
7
- * wraps that reconcile; `PbsDatastore` and `SdnApply` write their own. CephPool, the one that got
8
- * it wrong, has its own file. docs/adopt-verify.md carries the full per-family table.
5
+ * ★ ONE ROW PER WAY A FAMILY REACHES ITS WRITE, not one per family. Pool now has its own distilled
6
+ * lifecycle; `Acl` wraps resource.ts's shared reconcile; `PbsDatastore` and `SdnApply` write their
7
+ * own. CephPool, the one that got it wrong, has its own file. docs/adopt-verify.md carries the full per-family table.
9
8
  * ⚠️ A FIXTURE THAT DOES NOT MATCH TURNS A ROW INTO A DRIFT TEST, and the assertion on the
10
9
  * verifier's `noop` is what catches that: a wrong fixture fails loudly instead of passing for
11
10
  * the wrong reason. The Pool drift row proves the harness can see a write at all.
@@ -36,7 +35,7 @@ const cases: readonly Case[] = [
36
35
  {
37
36
  declare: () => ProxmoxPool('lab', { comment: 'x', poolid: 'lab', target: FAKE_TARGET }),
38
37
  live: { 'pools/lab': { comment: 'x', members: [] } },
39
- name: 'Proxmox.Pool (pveHandlers: every factory family)',
38
+ name: 'Proxmox.Pool (direct distilled lifecycle)',
40
39
  provider: ProxmoxPoolProvider as never,
41
40
  },
42
41
  {
@@ -0,0 +1,95 @@
1
+ /** Pure existing forms, normalization and equality, shared by plan and SDK lifecycle. */
2
+ import type { BackupJobAttributes, BackupJobProps } from './backup-job.ts';
3
+ import type { PveSpec } from './resource-spec.ts';
4
+ import { bool, guestList, propertyString, text } from './values.ts';
5
+
6
+ /**
7
+ * The form for both create and update.
8
+ *
9
+ * ⛔ A FIELD THE DECLARATION LEAVES OUT IS NOT SENT, AND THAT IS LOAD BEARING. PVE's PUT can only
10
+ * SET a key; clearing one needs `delete=<key>`, which this resource deliberately never sends.
11
+ * `updateForm` is handed the props and nothing else, so a `delete` list could only be "every
12
+ * field you did not declare" — and on a job adopted out of the UI that would silently strip
13
+ * settings this resource does not even model, `compress`, `mailto` and `bwlimit` among them, on
14
+ * the first deploy. Leaving undeclared fields alone is the smaller lie, and `matches` tells the
15
+ * same lie consistently: it does not compare what it cannot change.
16
+ *
17
+ * ⚠️ THE FIVE FIELDS PVE DOCUMENTS A DEFAULT FOR ARE ALWAYS SENT AND ALWAYS COMPARED — `enabled`
18
+ * (1), `all` (0), `mode` (snapshot), `notification-mode` (auto), `repeat-missed` (0). That is
19
+ * what makes a hand-flipped `enabled 0` show up as drift rather than as silence, and it is also
20
+ * what stops those five looping: a field compared against a default must be sent as that
21
+ * default, or every plan reports an update the PUT never performs. The cost is worth reading
22
+ * before the first deploy against an adopted job — an omitted `all` is a declaration that this
23
+ * job backs up NOTHING, and PVE accepts a job that selects nothing without a word. Read that
24
+ * first plan; do not deploy it unseen.
25
+ */
26
+ const shape = (props: BackupJobProps) => ({
27
+ all: props.all === true ? '1' : '0',
28
+ enabled: props.enabled === false ? '0' : '1',
29
+ mode: props.mode ?? 'snapshot',
30
+ 'notification-mode': props['notification-mode'] ?? 'auto',
31
+ 'repeat-missed': props['repeat-missed'] === true ? '1' : '0',
32
+ schedule: props.schedule,
33
+ ...(props.comment === undefined ? {} : { comment: props.comment }),
34
+ ...(props.exclude === undefined ? {} : { exclude: guestList(props.exclude) }),
35
+ ...(props.fleecing === undefined ? {} : { fleecing: props.fleecing }),
36
+ ...(props['notes-template'] === undefined ? {} : { 'notes-template': props['notes-template'] }),
37
+ ...(props.pool === undefined ? {} : { pool: props.pool }),
38
+ ...(props['prune-backups'] === undefined ? {} : { 'prune-backups': props['prune-backups'] }),
39
+ ...(props.storage === undefined ? {} : { storage: props.storage }),
40
+ ...(props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }),
41
+ });
42
+
43
+ export const backupJobSpec = {
44
+ /**
45
+ * ⛔ NO "IS IT REALLY THERE" GUARD, ON PURPOSE. Returning undefined for a job whose JSON is
46
+ * missing some key would be a guess about which keys PVE echoes back, and a wrong guess here
47
+ * does not read as absent-and-harmless: `reconcile` would POST, and POST creates ANOTHER job.
48
+ * Absence is decided by the API declining to answer, which `pveOperations.read` already
49
+ * handles — not by a key being missing from an answer that did arrive.
50
+ */
51
+ attributes: (live, props) => ({
52
+ all: bool(live['all'], false),
53
+ comment: text(live['comment'], ''),
54
+ enabled: bool(live['enabled'], true),
55
+ exclude: guestList(live['exclude']),
56
+ fleecing: propertyString(live['fleecing'], 'enabled'),
57
+ id: props.id,
58
+ mode: text(live['mode'], 'snapshot'),
59
+ 'next-run': typeof live['next-run'] === 'number' ? live['next-run'] : 0,
60
+ 'notes-template': text(live['notes-template'], ''),
61
+ 'notification-mode': text(live['notification-mode'], 'auto'),
62
+ pool: text(live['pool'], ''),
63
+ 'prune-backups': propertyString(live['prune-backups']),
64
+ 'repeat-missed': bool(live['repeat-missed'], false),
65
+ schedule: text(live['schedule'], ''),
66
+ storage: text(live['storage'], ''),
67
+ vmid: guestList(live['vmid']),
68
+ }),
69
+ collection: () => 'cluster/backup',
70
+ /** ⛔ `id` IS SENT AND IS NOT OPTIONAL. Everything above depends on PVE not inventing one. */
71
+ createForm: (props) => ({ ...shape(props), id: props.id }),
72
+ /** Each line reads "not declared, or equal"; the five defaulted fields have no undeclared case. */
73
+ /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */
74
+ endpoint: { create: 'pve:POST /cluster/backup', update: 'pve:PUT /cluster/backup/{id}' },
75
+ matches: (attributes, props) =>
76
+ attributes.schedule === props.schedule &&
77
+ attributes.enabled === (props.enabled !== false) &&
78
+ attributes.all === (props.all === true) &&
79
+ attributes.mode === (props.mode ?? 'snapshot') &&
80
+ attributes['notification-mode'] === (props['notification-mode'] ?? 'auto') &&
81
+ attributes['repeat-missed'] === (props['repeat-missed'] === true) &&
82
+ (props.storage === undefined || attributes.storage === props.storage) &&
83
+ (props.pool === undefined || attributes.pool === props.pool) &&
84
+ (props.comment === undefined || attributes.comment === props.comment) &&
85
+ (props.vmid === undefined || attributes.vmid === guestList(props.vmid)) &&
86
+ (props.exclude === undefined || attributes.exclude === guestList(props.exclude)) &&
87
+ (props['notes-template'] === undefined ||
88
+ attributes['notes-template'] === props['notes-template']) &&
89
+ (props['prune-backups'] === undefined ||
90
+ attributes['prune-backups'] === propertyString(props['prune-backups'])) &&
91
+ (props.fleecing === undefined ||
92
+ attributes.fleecing === propertyString(props.fleecing, 'enabled')),
93
+ path: (props) => `cluster/backup/${props.id}`,
94
+ updateForm: shape,
95
+ } satisfies PveSpec<BackupJobProps, BackupJobAttributes>;