@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.
- package/dist/proxmox/backup-job-config.d.ts +95 -0
- package/dist/proxmox/backup-job-config.d.ts.map +1 -0
- package/dist/proxmox/backup-job-distilled.d.ts +46 -0
- package/dist/proxmox/backup-job-distilled.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +9 -26
- package/dist/proxmox/backup-job.d.ts.map +1 -1
- package/dist/proxmox/distilled-pbs.d.ts +12 -0
- package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
- package/dist/proxmox/fake-pve.d.ts +1 -1
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/index.js +1501 -607
- package/dist/proxmox/index.js.map +48 -24
- package/dist/proxmox/live-core-fixtures.d.ts +40 -0
- package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
- package/dist/proxmox/metric-server-config.d.ts +49 -0
- package/dist/proxmox/metric-server-config.d.ts.map +1 -0
- package/dist/proxmox/metric-server-distilled.d.ts +43 -0
- package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +2 -10
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
- package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher.d.ts +5 -5
- package/dist/proxmox/notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/notification-sdk-input.d.ts +22 -0
- package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
- package/dist/proxmox/notification-target-distilled.d.ts +12 -0
- package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +2 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
- package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-target-spec.d.ts +5 -0
- package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +4 -5
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
- package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +5 -6
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-input.d.ts +18 -0
- package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
- package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts +1 -1
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +9 -8
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
- package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +9 -39
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +12 -12
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
- package/dist/proxmox/pool.d.ts +10 -1
- package/dist/proxmox/pool.d.ts.map +1 -1
- package/docs/pbs-distilled-jobs.md +31 -0
- package/docs/pbs-notifications.md +8 -2
- package/docs/proxmox-notification-sdk.md +28 -0
- package/docs/proxmox-sdk-migration.md +60 -0
- package/docs/upstream-conformance-families.md +6 -2
- package/package.json +1 -1
- package/src/proxmox/adopt-noop.test.ts +4 -5
- package/src/proxmox/backup-job-config.ts +95 -0
- package/src/proxmox/backup-job-distilled.ts +86 -0
- package/src/proxmox/backup-job.ts +78 -116
- package/src/proxmox/constraints.test.ts +9 -8
- package/src/proxmox/distilled-pbs.test.ts +87 -0
- package/src/proxmox/distilled-pbs.ts +50 -0
- package/src/proxmox/fake-pbs-jobs.ts +99 -0
- package/src/proxmox/fake-pbs-notify.ts +4 -1
- package/src/proxmox/fake-pve.ts +3 -2
- package/src/proxmox/live-core-fixtures.ts +126 -0
- package/src/proxmox/live-core-lifecycle.test.ts +115 -0
- package/src/proxmox/live-core-wire.test.ts +190 -0
- package/src/proxmox/metric-server-config.ts +87 -0
- package/src/proxmox/metric-server-distilled.ts +81 -0
- package/src/proxmox/metric-server.ts +77 -88
- package/src/proxmox/notification-distilled.test.ts +162 -0
- package/src/proxmox/notification-matcher-distilled.ts +60 -0
- package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
- package/src/proxmox/notification-matcher.ts +9 -26
- package/src/proxmox/notification-replacement.test.ts +190 -0
- package/src/proxmox/notification-sdk-input.ts +39 -0
- package/src/proxmox/notification-target-distilled.ts +160 -0
- package/src/proxmox/notification-target-form.ts +2 -0
- package/src/proxmox/notification-target-lifecycle.ts +75 -0
- package/src/proxmox/notification-target-spec.ts +68 -0
- package/src/proxmox/notification-target.ts +6 -68
- package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
- package/src/proxmox/pbs-datastore-form.ts +18 -49
- package/src/proxmox/pbs-datastore-guard.ts +7 -4
- package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
- package/src/proxmox/pbs-datastore-wire.ts +94 -0
- package/src/proxmox/pbs-datastore.ts +7 -133
- package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
- package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
- package/src/proxmox/pbs-notification-errors.test.ts +93 -0
- package/src/proxmox/pbs-notification-input.ts +25 -0
- package/src/proxmox/pbs-notification-mail.test.ts +64 -0
- package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
- package/src/proxmox/pbs-notification-matcher.ts +70 -17
- package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
- package/src/proxmox/pbs-notification-target.ts +1 -1
- package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
- package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-prune-job-spec.ts +53 -0
- package/src/proxmox/pbs-prune-job-wire.ts +68 -0
- package/src/proxmox/pbs-prune-job.ts +10 -58
- package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
- package/src/proxmox/pbs-sync-job-form.ts +7 -6
- package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
- package/src/proxmox/pbs-sync-job-wire.ts +71 -0
- package/src/proxmox/pbs-sync-job.ts +10 -41
- package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-verify-job-spec.ts +76 -0
- package/src/proxmox/pbs-verify-job-wire.ts +58 -0
- package/src/proxmox/pbs-verify-job.ts +13 -85
- 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()`
|
|
39
|
-
*
|
|
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
|
|
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`)
|
|
161
|
-
*
|
|
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.
|
|
170
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
20
|
-
*
|
|
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
|
|
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 {
|
|
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`
|
|
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.
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* rather than refused.
|
|
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;
|
|
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"}
|
package/dist/proxmox/pool.d.ts
CHANGED
|
@@ -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 {
|
|
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;
|
|
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
|
-
⚠️
|
|
161
|
-
|
|
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. **
|
|
79
|
-
|
|
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.
|
|
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.
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
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 (
|
|
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>;
|