@homeflare/alchemy 0.37.6 → 0.37.7

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 (80) hide show
  1. package/dist/proxmox/firewall-alias-config.d.ts +36 -0
  2. package/dist/proxmox/firewall-alias-config.d.ts.map +1 -0
  3. package/dist/proxmox/firewall-alias-form.d.ts +3 -1
  4. package/dist/proxmox/firewall-alias-form.d.ts.map +1 -1
  5. package/dist/proxmox/firewall-alias-lifecycle.d.ts +43 -0
  6. package/dist/proxmox/firewall-alias-lifecycle.d.ts.map +1 -0
  7. package/dist/proxmox/firewall-alias.d.ts +2 -8
  8. package/dist/proxmox/firewall-alias.d.ts.map +1 -1
  9. package/dist/proxmox/index.js +795 -526
  10. package/dist/proxmox/index.js.map +23 -10
  11. package/dist/proxmox/lxc-errors.d.ts +12 -0
  12. package/dist/proxmox/lxc-errors.d.ts.map +1 -0
  13. package/dist/proxmox/lxc-lifecycle.d.ts +9 -31
  14. package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
  15. package/dist/proxmox/lxc-read.d.ts +4 -0
  16. package/dist/proxmox/lxc-read.d.ts.map +1 -0
  17. package/dist/proxmox/lxc-task.d.ts +15 -0
  18. package/dist/proxmox/lxc-task.d.ts.map +1 -0
  19. package/dist/proxmox/network-apply-read.d.ts +7 -14
  20. package/dist/proxmox/network-apply-read.d.ts.map +1 -1
  21. package/dist/proxmox/network-apply-run.d.ts +7 -0
  22. package/dist/proxmox/network-apply-run.d.ts.map +1 -0
  23. package/dist/proxmox/network-apply.d.ts +2 -3
  24. package/dist/proxmox/network-apply.d.ts.map +1 -1
  25. package/dist/proxmox/qemu-errors.d.ts +5 -0
  26. package/dist/proxmox/qemu-errors.d.ts.map +1 -0
  27. package/dist/proxmox/qemu-form.d.ts +22 -0
  28. package/dist/proxmox/qemu-form.d.ts.map +1 -0
  29. package/dist/proxmox/qemu-lifecycle.d.ts +26 -0
  30. package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -0
  31. package/dist/proxmox/qemu-read.d.ts +5 -0
  32. package/dist/proxmox/qemu-read.d.ts.map +1 -0
  33. package/dist/proxmox/qemu-task.d.ts +9 -0
  34. package/dist/proxmox/qemu-task.d.ts.map +1 -0
  35. package/dist/proxmox/qemu.d.ts +9 -14
  36. package/dist/proxmox/qemu.d.ts.map +1 -1
  37. package/dist/proxmox/replication-firewall-fixtures.d.ts +30 -0
  38. package/dist/proxmox/replication-firewall-fixtures.d.ts.map +1 -0
  39. package/dist/proxmox/replication-job-config.d.ts +56 -0
  40. package/dist/proxmox/replication-job-config.d.ts.map +1 -0
  41. package/dist/proxmox/replication-job-lifecycle.d.ts +62 -0
  42. package/dist/proxmox/replication-job-lifecycle.d.ts.map +1 -0
  43. package/dist/proxmox/replication-job.d.ts +5 -6
  44. package/dist/proxmox/replication-job.d.ts.map +1 -1
  45. package/dist/release/archive.d.ts.map +1 -1
  46. package/dist/release/index.js +3 -2
  47. package/dist/release/index.js.map +3 -3
  48. package/docs/proxmox-lxc-distilled.md +56 -0
  49. package/docs/proxmox-replication-firewall-distilled.md +53 -0
  50. package/docs/proxmox-sdk-migration.md +23 -18
  51. package/package.json +2 -2
  52. package/src/proxmox/firewall-alias-config.ts +39 -0
  53. package/src/proxmox/firewall-alias-form.ts +1 -1
  54. package/src/proxmox/firewall-alias-lifecycle.ts +100 -0
  55. package/src/proxmox/firewall-alias.ts +4 -67
  56. package/src/proxmox/lxc-create.test.ts +4 -1
  57. package/src/proxmox/lxc-distilled.test.ts +239 -0
  58. package/src/proxmox/lxc-errors.ts +9 -0
  59. package/src/proxmox/lxc-lifecycle.ts +31 -113
  60. package/src/proxmox/lxc-read.test.ts +29 -6
  61. package/src/proxmox/lxc-read.ts +88 -0
  62. package/src/proxmox/lxc-task.ts +71 -0
  63. package/src/proxmox/network-apply-distilled.test.ts +76 -0
  64. package/src/proxmox/network-apply-read.ts +18 -39
  65. package/src/proxmox/network-apply-run.ts +97 -0
  66. package/src/proxmox/network-apply.ts +5 -94
  67. package/src/proxmox/qemu-errors.ts +7 -0
  68. package/src/proxmox/qemu-form.ts +15 -0
  69. package/src/proxmox/qemu-lifecycle.test.ts +171 -0
  70. package/src/proxmox/qemu-lifecycle.ts +134 -0
  71. package/src/proxmox/qemu-read.ts +73 -0
  72. package/src/proxmox/qemu-task.ts +64 -0
  73. package/src/proxmox/qemu.ts +11 -44
  74. package/src/proxmox/replication-firewall-fixtures.ts +71 -0
  75. package/src/proxmox/replication-firewall-lifecycle.test.ts +109 -0
  76. package/src/proxmox/replication-firewall-wire.test.ts +173 -0
  77. package/src/proxmox/replication-job-config.ts +79 -0
  78. package/src/proxmox/replication-job-lifecycle.ts +113 -0
  79. package/src/proxmox/replication-job.ts +7 -84
  80. package/src/release/archive.ts +4 -1
@@ -0,0 +1,12 @@
1
+ import type { PveTarget } from './credentials.ts';
2
+ /** A lifecycle refusal. Post-write refusals carry the task diagnostic, never imply rollback. */
3
+ export declare class LxcRefusedError extends Error {
4
+ readonly name = "LxcRefusedError";
5
+ }
6
+ /** Where one guest lives. Its identity, and nothing else. */
7
+ export type LxcWhere = {
8
+ readonly target: PveTarget;
9
+ readonly node: string;
10
+ readonly vmid: number;
11
+ };
12
+ //# sourceMappingURL=lxc-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lxc-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/lxc-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,gGAAgG;AAChG,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,IAAI,qBAAqB;CAC5C;AAED,6DAA6D;AAC7D,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -1,48 +1,26 @@
1
1
  /**
2
2
  * The calls a `Proxmox.Lxc` makes: read one config, create, update in place, grow, delete.
3
3
  *
4
- * ⛔ A READ FAILURE IS A FAILURE HERE, NOT "ABSENT" — THE ONE PLACE THIS PACKAGE DEPARTS FROM
5
- * resource.ts. `pveOperations.read` folds every error into `undefined`, which for a guest means a
6
- * 403 or a node that did not answer plans a CREATE over a running container. PVE answers a
7
- * missing guest with HTTP 500, `Configuration file 'nodes/<node>/lxc/<vmid>.conf' does not
8
- * exist` (pve-guest-common `AbstractConfig::load_config`), so only a 500 may mean absent — and
9
- * even then only after the cluster says so, below. Anything else fails the plan.
10
- *
11
- * ⛔ "ABSENT" IS DECIDED BY THE WHOLE CLUSTER, NOT BY THE ERROR TEXT. A config lives under its
12
- * node, so a guest migrated to another node — or a QEMU VM holding the same cluster-wide vmid —
13
- * also reads as missing at the declared path. `GET /cluster/resources?type=vm` settles it: the
14
- * vmid nowhere is absent; the vmid anywhere else FAILS the read with where it is; the vmid on
15
- * the declared node fails with PVE's own error. ★ Keyed on the cluster rather than on matching
16
- * the message, so a PVE that words it differently still cannot turn into a create.
4
+ * ★ Discovery lives in lxc-read.ts; it proves precise absence with one read credential.
5
+ * Writes use named distilled operations, and lxc-task.ts preserves task ownership and bounds.
17
6
  *
18
7
  * ⚠️ CREATE, RESIZE AND DELETE ARE ASYNCHRONOUS; A CONFIG PUT IS NOT. The first three answer a
19
8
  * UPID the moment PVE forks the worker (`NodesNodeLxcPostReturn`, `…ResizePutReturn`,
20
9
  * `…VmidDeleteReturn` are all `string`), so each waits for its task's exitstatus. `PUT …/config`
21
10
  * answers `null` after writing (`…ConfigPutReturn`), so it needs no wait.
22
- * ⛔ EACH TASK IS POLLED WITH THE CREDENTIAL THAT STARTED IT — see `awaitTask`: only the owner
11
+ * ⛔ EACH TASK IS POLLED WITH THE CREDENTIAL THAT STARTED IT — see `lxcTask`: only the owner
23
12
  * skips `Sys.Audit`. A fresh `provision` mint per write gives the full 300s lease a create needs;
24
13
  * a cached lease can be handed out with 60s left (lease-cache.ts).
25
14
  */
26
15
  import * as Effect from 'effect/Effect';
27
- import type { PveTarget } from './credentials.ts';
16
+ import { LxcRefusedError, type LxcWhere } from './lxc-errors.ts';
28
17
  import { type LxcChange } from './lxc-judge.ts';
29
18
  import type { LxcAttributes, LxcProps } from './lxc-props.ts';
30
- /** A plan-time or apply-time refusal. Nothing was written when one of these is raised. */
31
- export declare class LxcRefusedError extends Error {
32
- readonly name = "LxcRefusedError";
33
- }
34
- /** Where one guest lives. Its identity, and nothing else. */
35
- export type LxcWhere = {
36
- readonly target: PveTarget;
37
- readonly node: string;
38
- readonly vmid: number;
39
- };
40
- export declare const guestPath: (where: LxcWhere) => string;
41
- /** The live config, or undefined when no guest holds this vmid anywhere in the cluster. */
42
- export declare const readLive: (where: LxcWhere) => Effect.Effect<Record<string, unknown> | undefined, Error | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
19
+ export { LxcRefusedError, type LxcWhere } from './lxc-errors.ts';
20
+ export { readLive } from './lxc-read.ts';
43
21
  export declare const attributesOf: (where: LxcWhere, live: Record<string, unknown>) => LxcAttributes;
44
22
  /** POST the create and wait for the template to unpack. ⛔ Refusals are the caller's to check. */
45
- export declare const createGuest: (props: LxcProps) => Effect.Effect<undefined, Error | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
23
+ export declare const createGuest: (props: LxcProps) => Effect.Effect<undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
46
24
  /**
47
25
  * Apply a judged change: the config PUT first, then each resize.
48
26
  * ⚠️ IN THAT ORDER BECAUSE A MOUNT POINT'S PUT CARRIES ITS LIVE `size=`, and the resize rewrites it.
@@ -53,7 +31,7 @@ export declare const createGuest: (props: LxcProps) => Effect.Effect<undefined,
53
31
  * seen by reconcile's fresh judge instead: a declared key edited meanwhile is written back to the
54
32
  * declaration (even after a plan that warned of nothing), an undeclared one is left alone.
55
33
  */
56
- export declare const updateGuest: (props: LxcProps, change: LxcChange, digest: string) => Effect.Effect<void, Error | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
34
+ export declare const updateGuest: (props: LxcProps, change: LxcChange, digest: string) => Effect.Effect<void, Error | import("effect/unstable/http/HttpClientError").HttpClientError | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
57
35
  /**
58
36
  * ⛔ NO `force`, NO `purge`, NO STOP FIRST. PVE refuses to destroy a running or protected guest,
59
37
  * and that refusal is kept: stopping a guest so a delete can proceed is an operator's decision.
@@ -64,5 +42,5 @@ export declare const updateGuest: (props: LxcProps, change: LxcChange, digest: s
64
42
  * first; one that no longer matches is refused with the keys that differ, and deleting it (or
65
43
  * declaring it again) is the operator's call.
66
44
  */
67
- export declare const destroyGuest: (where: LxcWhere, declared: LxcProps) => Effect.Effect<undefined, Error | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
45
+ export declare const destroyGuest: (where: LxcWhere, declared: LxcProps) => Effect.Effect<undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
68
46
  //# sourceMappingURL=lxc-lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lxc-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/lxc-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK9D,0FAA0F;AAC1F,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,IAAI,qBAAqB;CAC5C;AAED,6DAA6D;AAC7D,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,eAAO,MAAM,SAAS,UAAW,QAAQ,WAAoD,CAAC;AAgC9F,2FAA2F;AAC3F,eAAO,MAAM,QAAQ,UAAW,QAAQ,6LAqCpC,CAAC;AAEL,eAAO,MAAM,YAAY,UAAW,QAAQ,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,aAK5E,CAAC;AAgCH,iGAAiG;AACjG,eAAO,MAAM,WAAW,UAAW,QAAQ,mKAmBvC,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,UAAW,QAAQ,UAAU,SAAS,UAAU,MAAM,8JAyB1E,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,UAAW,QAAQ,YAAY,QAAQ,mKAe5D,CAAC"}
1
+ {"version":3,"file":"lxc-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/lxc-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMjE,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI9D,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA0BzC,eAAO,MAAM,YAAY,UAAW,QAAQ,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,aAK5E,CAAC;AAEH,iGAAiG;AACjG,eAAO,MAAM,WAAW,UAAW,QAAQ,oOAsBvC,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,UAAW,QAAQ,UAAU,SAAS,UAAU,MAAM,+NA4B1E,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,UAAW,QAAQ,YAAY,QAAQ,oOAoB5D,CAAC"}
@@ -0,0 +1,4 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { LxcRefusedError, type LxcWhere } from './lxc-errors.ts';
3
+ export declare const readLive: (where: LxcWhere) => Effect.Effect<Record<string, unknown> | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
4
+ //# sourceMappingURL=lxc-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lxc-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/lxc-read.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGjE,eAAO,MAAM,QAAQ,UAAW,QAAQ,8PAoEpC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ProxmoxOpContext } from '@distilled.cloud/proxmox/Protocol';
2
+ import * as Effect from 'effect/Effect';
3
+ import { LxcRefusedError, type LxcWhere } from './lxc-errors.ts';
4
+ /**
5
+ * ⛔ Fresh provision mint for each write, then retain it for every task poll. PVE's task
6
+ * owner skips Sys.Audit; another mint is another token identity. This also gives a create
7
+ * the full lease rather than a cached lease with only 60 seconds left.
8
+ * ⚠️ A failed post-write poll is uncertainty, never absence or permission to retry the write.
9
+ * Preserve the existing one-second bound and the UPID diagnostic. NetworkApply keeps its
10
+ * separate raw helper; migrating this caller does not alter reload behavior.
11
+ * ★ SDK labels are percent-encoded. Installed APIServer/AnyEvent.pm uri_unescape runs before
12
+ * routing, so an encoded UPID reaches the same task as its old literal spelling.
13
+ */
14
+ export declare const lxcTask: <E>(where: LxcWhere, operation: Effect.Effect<string, E, ProxmoxOpContext>, what: string, polls: number) => Effect.Effect<undefined, E | Error | LxcRefusedError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
15
+ //# sourceMappingURL=lxc-task.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lxc-task.d.ts","sourceRoot":"","sources":["../../src/proxmox/lxc-task.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIjE;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAChB,QAAQ,aACJ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,gBAAgB,CAAC,QAC/C,MAAM,SACL,MAAM,uKA+CX,CAAC"}
@@ -17,13 +17,8 @@
17
17
  * of `data`, set only when the diff is non-empty. `client.ts`'s `pve()` returns `body.data` and
18
18
  * drops the envelope, so the one readable pending signal is invisible to every other resource in
19
19
  * this package. Without the fetch below, `Proxmox.NetworkApply` could not diff honestly at all.
20
- * ★ `pveEnvelopeWith` in client.ts now carries the same member failover as every other call.
21
- * ⛔ WHY THIS FAMILY DID NOT MOVE TO DISTILLED WITH `Proxmox.NodeNetwork` (2026-09-24, decision
22
- * 43's walk-down): CONFIRMED against `@distilled.cloud/proxmox`'s own `protocol.ts` that its
23
- * `transformResponse` unconditionally returns `body.data ?? {}` for every operation, before a
24
- * generated call's typed output is even built — no typed call, current or future, could see
25
- * `changes` without a protocol-level change upstream. `pveEnvelopeWith` stays because nothing
26
- * else preserves the sibling this function needs.
20
+ * ★ `listNodeNetwork` keeps a string `changes` sibling. The protocol unwraps every other
21
+ * envelope. The diff string is counted here and then dropped.
27
22
  *
28
23
  * ⛔ AND THE INTERFACE ROWS CANNOT TELL YOU EITHER. MEASURED in PVE/INotify.pm `read_file`: when
29
24
  * `/etc/network/interfaces.new` exists, THAT is the file that gets parsed, so
@@ -41,6 +36,7 @@
41
36
  * there waiting for the next reload anybody triggers. The file's existence is not exposed by the
42
37
  * API in any other form, so the alternative is not a better read — it is SSH.
43
38
  */
39
+ import * as cluster from '@distilled.cloud/proxmox/cluster';
44
40
  import * as Effect from 'effect/Effect';
45
41
  import { type PveCredential, type PveTarget } from './credentials.ts';
46
42
  /** What the cluster looked like the moment it was asked. Never persisted — see network-apply.ts. */
@@ -60,7 +56,7 @@ export type ClusterHealth = {
60
56
  */
61
57
  export declare const pendingLines: (diff: string) => number;
62
58
  /** How many staged lines this node is carrying. Zero is the only settled value. */
63
- export declare const pendingCount: (target: PveTarget, node: string) => Effect.Effect<number, Error | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
59
+ export declare const pendingCount: (target: PveTarget, node: string) => Effect.Effect<number, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
64
60
  /**
65
61
  * Why the cluster is not healthy, or `undefined` when it is.
66
62
  *
@@ -75,7 +71,7 @@ export declare const pendingCount: (target: PveTarget, node: string) => Effect.E
75
71
  * pools losing min_size; reloading another node's networking while it is out is the move that
76
72
  * completes the outage. Bring it back, or apply by hand with the cluster in front of you.
77
73
  */
78
- export declare const degradedReason: (target: PveTarget) => Effect.Effect<string | undefined, Error | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
74
+ export declare const degradedReason: (target: PveTarget) => Effect.Effect<string | undefined, Error | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | cluster.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
79
75
  /**
80
76
  * ⛔ NOT AN EXITSTATUS PVE CAN RETURN, AND THAT IS THE POINT. The caller compares against `'OK'`;
81
77
  * these two say WHY the answer is not OK without needing a second branch for "we do not know".
@@ -95,11 +91,8 @@ export declare const UNREACHABLE = "the task could not be read -- the node stopp
95
91
  * is not the owner — which is why this takes a `PveCredential` and the caller keeps one lease
96
92
  * across both calls, instead of widening the provisioning role to read its own tasks.
97
93
  *
98
- * ⛔ THE OTHER REASON THIS FAMILY DID NOT MOVE TO DISTILLED (2026-09-24): CHECKED against
99
- * `@distilled.cloud/proxmox`'s own `Task.awaitTask` (`src/task.ts`) before deciding — it
100
- * propagates a poll failure as a genuine typed `GetNodeTaskStatusError` rather than collapsing
101
- * it to `UNREACHABLE` the way this function does, which would turn the ⛔ two paragraphs above
102
- * into a failed `reconcile` on exactly the poll a mid-reload connection drop is expected to hit.
94
+ * ★ A poll failure stays `UNREACHABLE`. `getNodeTaskStatus` would otherwise fail the reload
95
+ * on the connection drop this function exists to survive.
103
96
  */
104
97
  export declare const awaitTask: (target: PveTarget, credential: PveCredential, node: string, upid: string, attempts?: number) => Effect.Effect<string, never, import("effect/unstable/http/HttpClient").HttpClient>;
105
98
  //# sourceMappingURL=network-apply-read.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"network-apply-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAatE,oGAAoG;AACpG,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AA0B7E;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,WAWxC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,WAAY,SAAS,QAAQ,MAAM,8IACH,CAAC;AAiC1D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,WAAY,SAAS,0JAO7C,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,WAAW,0FACiE,CAAC;AAY1F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,SAAS,WACZ,SAAS,cACL,aAAa,QACnB,MAAM,QACN,MAAM,aAEF,MAAM,uFAgCd,CAAC"}
1
+ {"version":3,"file":"network-apply-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,OAAO,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAItE,oGAAoG;AACpG,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AA0B7E;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,WAWxC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,WAAY,SAAS,QAAQ,MAAM,+MACH,CAAC;AAiC1D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,WAAY,SAAS,4QAO7C,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,WAAW,0FACiE,CAAC;AAY1F;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,SAAS,WACZ,SAAS,cACL,aAAa,QACnB,MAAM,QACN,MAAM,aAEF,MAAM,uFA0Bd,CAAC"}
@@ -0,0 +1,7 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import type { NetworkApplyProps } from './network-apply.ts';
3
+ export declare const applyNetwork: (props: NetworkApplyProps) => Effect.Effect<{
4
+ node: string;
5
+ pending: number;
6
+ }, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
7
+ //# sourceMappingURL=network-apply-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-apply-run.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply-run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAKxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,eAAO,MAAM,YAAY,UAAW,iBAAiB;;;uLAkFjD,CAAC"}
@@ -45,12 +45,11 @@
45
45
  * GRANT — it also carries that node's DNS, hosts file, time and service configuration. Granting
46
46
  * it cluster-wide to run this resource widens the provisioning role well beyond networking.
47
47
  *
48
- * ⛔ STAYS ON `client.ts` (2026-09-24) — network-apply-read.ts's header has the two MEASURED reasons.
48
+ * Named operations: `listNodeNetwork` keeps the diff sibling, `putNodeNetwork` reloads.
49
49
  */
50
50
  import { Resource } from 'alchemy';
51
51
  import * as Provider from 'alchemy/Provider';
52
- import type { PveRequirements } from './resource.ts';
53
- import type { WithTarget } from './resource.ts';
52
+ import type { PveRequirements, WithTarget } from './resource.ts';
54
53
  export interface NetworkApplyProps extends WithTarget {
55
54
  /**
56
55
  * The node whose staged interfaces file this applies — `node-b`.
@@ -1 +1 @@
1
- {"version":3,"file":"network-apply.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAM7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAAwD,CAAC;AAiIzF,eAAO,MAAM,2BAA2B,yIACyD,CAAC"}
1
+ {"version":3,"file":"network-apply.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEjE,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAAwD,CAAC;AA2CzF,eAAO,MAAM,2BAA2B,yIACyD,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** A QEMU write or observation this provider will not turn into a create or a delete. */
2
+ export declare class QemuRefusedError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ //# sourceMappingURL=qemu-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-errors.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF"}
@@ -0,0 +1,22 @@
1
+ /** QEMU create and update forms. Path identity stays out of the body. */
2
+ import type { VmProps } from './qemu.ts';
3
+ /** Fields both create and update send. `net0` is optional and is not compared. */
4
+ export declare const shape: (props: VmProps) => {
5
+ cores: string;
6
+ memory: string;
7
+ name: string;
8
+ onboot: string;
9
+ sockets: string;
10
+ net0?: string;
11
+ };
12
+ /** ★ Exported so the constraint proof can run the REAL create form, not a retyped copy of it. */
13
+ export declare const createForm: (props: VmProps) => {
14
+ cores: string;
15
+ memory: string;
16
+ name: string;
17
+ onboot: string;
18
+ sockets: string;
19
+ net0?: string;
20
+ vmid: string;
21
+ };
22
+ //# sourceMappingURL=qemu-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-form.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,kFAAkF;AAClF,eAAO,MAAM,KAAK,UAAW,OAAO;;;;;;;CAOlC,CAAC;AAEH,iGAAiG;AACjG,eAAO,MAAM,UAAU,UAAW,OAAO;;;;;;;;CAAoD,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as nodes from '@distilled.cloud/proxmox/nodes';
3
+ import * as Effect from 'effect/Effect';
4
+ import type { VmAttributes, VmProps } from './qemu.ts';
5
+ export declare const readQemu: (props: VmProps) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
6
+ /** No purge, no skiplock, no destroy-unreferenced-disks. A missing config is already gone. */
7
+ export declare const deleteQemu: (props: VmProps) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
8
+ export declare const qemuHandlers: {
9
+ list: () => Effect.Effect<never[], never, never>;
10
+ read: ({ olds }: {
11
+ olds: VmProps;
12
+ }) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
13
+ diff: (args_0: {
14
+ news: Input<VmProps>;
15
+ output: VmAttributes | undefined;
16
+ }) => Effect.Effect<{
17
+ readonly action: "noop" | "update";
18
+ } | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
19
+ reconcile: (args_0: {
20
+ news: VmProps;
21
+ }) => Effect.Effect<VmAttributes, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
22
+ delete: ({ olds }: {
23
+ olds: VmProps;
24
+ }) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
25
+ };
26
+ //# sourceMappingURL=qemu-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-lifecycle.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAMxC,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkDvD,eAAO,MAAM,QAAQ,UAAW,OAAO,iOAGpC,CAAC;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,UAAU,UAAW,OAAO,+KAMyB,CAAC;AAEnE,eAAO,MAAM,YAAY;IACvB,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;IAClC,IAAI;cAII,KAAK,CAAC,OAAO,CAAC;gBACZ,YAAY,GAAG,SAAS;;;;IAalC,SAAS;cAAyC,OAAO;;IAgCzD,MAAM,aAAa;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;CACrC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import * as nodes from '@distilled.cloud/proxmox/nodes';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { VmProps } from './qemu.ts';
4
+ export declare const readVm: (props: VmProps) => Effect.Effect<nodes.GetNodeQemuConfigResponse | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
5
+ //# sourceMappingURL=qemu-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-read.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,eAAO,MAAM,MAAM,UAAW,OAAO,oPAuDjC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ProxmoxOpContext } from '@distilled.cloud/proxmox/Protocol';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { VmProps } from './qemu.ts';
4
+ /**
5
+ * ⛔ Fresh provision mint, then the same lease for every poll. A timed-out poll is uncertainty,
6
+ * never a reason to send the write again.
7
+ */
8
+ export declare const qemuTask: <E>(props: VmProps, operation: Effect.Effect<string, E, ProxmoxOpContext>, what: string, polls: number) => Effect.Effect<undefined, E | Error | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
9
+ //# sourceMappingURL=qemu-task.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-task.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-task.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,SACjB,OAAO,aACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,gBAAgB,CAAC,QAC/C,MAAM,SACL,MAAM,qJA6CX,CAAC"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * `Proxmox.Vm` — a QEMU virtual machine, declared.
3
3
  *
4
- * ⚠️ THE SAME FOUR OPERATIONS AS AN LXC OVER A DIFFERENT PATH, which is the point of the factory:
5
- * `nodes/{node}/qemu` to create, `nodes/{node}/qemu/{vmid}/config` to read and update. A VM and a
6
- * container differ in what they carry, not in how they are reconciled.
4
+ * ⚠️ CREATE is `POST /nodes/{node}/qemu`. GET and PUT stay on `.../qemu/{vmid}/config`.
5
+ * ⛔ DELETE is `DELETE /nodes/{node}/qemu/{vmid}` (`destroy_vm` in qemu-server Qemu.pm). The
6
+ * config route is not a destroy. A missing config file is `QemuConfigNotFound` only.
7
7
  *
8
8
  * ⛔ `vmid` IS CLUSTER-WIDE AND SHARED WITH CONTAINERS. A VM and an LXC cannot hold the same id, so
9
9
  * these two resources compete for one number space. Declaring both with vmid 101 is not a
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { Resource } from 'alchemy';
18
18
  import * as Provider from 'alchemy/Provider';
19
- import { type PveRequirements, type WithTarget } from './resource.ts';
19
+ import type { PveRequirements, WithTarget } from './resource.ts';
20
20
  export interface VmProps extends WithTarget {
21
21
  node: string;
22
22
  /** ⛔ Cluster-wide, and shared with LXC. See the ⛔ above. */
@@ -42,15 +42,10 @@ export interface VmAttributes {
42
42
  export interface ProxmoxVm extends Resource<'Proxmox.Vm', VmProps, VmAttributes, never, PveRequirements> {
43
43
  }
44
44
  export declare const ProxmoxVm: import("alchemy").ResourceClass<ProxmoxVm>;
45
- /** ★ Exported so the constraint proof can run the REAL create form, not a retyped copy of it. */
46
- export declare const createForm: (props: VmProps) => {
47
- vmid: string;
48
- cores: string;
49
- memory: string;
50
- name: string;
51
- onboot: string;
52
- sockets: string;
53
- net0?: string;
54
- };
45
+ export { createForm } from './qemu-form.ts';
46
+ /**
47
+ * ⛔ Destroy uses deleteNodeQemu (`DELETE /nodes/{node}/qemu/{vmid}`), not the config path.
48
+ * The old factory sent both reads and deletes to `.../config`.
49
+ */
55
50
  export declare const ProxmoxVmProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxVm>, never, import("effect/unstable/http/HttpClient").HttpClient>;
56
51
  //# sourceMappingURL=qemu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qemu.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF,MAAM,WAAW,OAAQ,SAAQ,UAAU;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ,CACzC,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,SAAS,4CAAoC,CAAC;AAE3D,iGAAiG;AACjG,eAAO,MAAM,UAAU,UAAW,OAAO;;;;;;;;CAAoD,CAAC;AAsC9F,eAAO,MAAM,iBAAiB,+HAC+C,CAAC"}
1
+ {"version":3,"file":"qemu.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEjE,MAAM,WAAW,OAAQ,SAAQ,UAAU;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ,CACzC,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,SAAS,4CAAoC,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,+HACmD,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { CoreCase } from './live-core-fixtures.ts';
2
+ export declare const replicationProps: {
3
+ target: import("./credentials.ts").PveTarget;
4
+ guest: number;
5
+ jobnum: number;
6
+ targetNode: string;
7
+ };
8
+ export declare const aliasProps: {
9
+ target: import("./credentials.ts").PveTarget;
10
+ name: string;
11
+ cidr: string;
12
+ comment: string;
13
+ };
14
+ export declare const replicationLive: {
15
+ id: string;
16
+ guest: number;
17
+ jobnum: number;
18
+ target: string;
19
+ type: string;
20
+ source: string;
21
+ };
22
+ export declare const aliasLive: {
23
+ name: string;
24
+ cidr: string;
25
+ ipversion: number;
26
+ };
27
+ export declare const replicationMissing: () => Response;
28
+ export declare const aliasMissing: () => Response;
29
+ export declare const replicationFirewallCases: CoreCase[];
30
+ //# sourceMappingURL=replication-firewall-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replication-firewall-fixtures.d.ts","sourceRoot":"","sources":["../../src/proxmox/replication-firewall-fixtures.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAMxD,eAAO,MAAM,gBAAgB;IAAK,MAAM;IAAe,KAAK;IAAO,MAAM;IAAK,UAAU;CAAW,CAAC;AACpG,eAAO,MAAM,UAAU;IACrB,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,OAAO;CACR,CAAC;AACF,eAAO,MAAM,eAAe;IAC1B,EAAE;IACF,KAAK;IACL,MAAM;IACN,MAAM;IACN,IAAI;IACJ,MAAM;CACP,CAAC;AACF,eAAO,MAAM,SAAS;IAAK,IAAI;IAAa,IAAI;IAAe,SAAS;CAAK,CAAC;AAC9E,eAAO,MAAM,kBAAkB,gBAC+D,CAAC;AAC/F,eAAO,MAAM,YAAY,gBAItB,CAAC;AAEJ,eAAO,MAAM,wBAAwB,EAAE,QAAQ,EAoC9C,CAAC"}
@@ -0,0 +1,56 @@
1
+ /** Existing comparison and form semantics, separated from the named SDK transport. */
2
+ import type { ReplicationJobAttributes, ReplicationJobProps } from './replication-job.ts';
3
+ import { createBody, updateBody } from './replication-job-form.ts';
4
+ export declare const replicationJobSpec: {
5
+ /**
6
+ * ⚠️ EVERY FALLBACK IS PVE'S DOCUMENTED DEFAULT, NOT `false`/`''` PICKED FOR CONVENIENCE, so the
7
+ * answer does not depend on whether a given release echoes back a key it never wrote.
8
+ * SectionConfig stores what it was handed; a job created in the UI without a schedule has no
9
+ * schedule line, and reading that absence as anything but `DEFAULT_SCHEDULE` would report
10
+ * drift on a job nobody touched and then WRITE the reading back.
11
+ * ⛔ The distilled read validates the response and requested identity before applying defaults.
12
+ * Only the precise typed vendor exception means absence. `digest` is deliberately absent
13
+ * here — it changes when ANY job changes, including one somebody else declared.
14
+ */
15
+ attributes: (live: Record<string, unknown>, props: ReplicationJobProps) => {
16
+ comment: string;
17
+ disable: boolean;
18
+ guest: number;
19
+ id: string;
20
+ jobnum: number;
21
+ rate: number;
22
+ remove_job: string;
23
+ schedule: string;
24
+ source: string;
25
+ targetNode: string;
26
+ type: string;
27
+ };
28
+ collection: () => string;
29
+ createForm: typeof createBody;
30
+ /**
31
+ * ⚠️ `targetNode` AND `source` ARE OUT OF THIS COMPARISON FOR TWO REASONS EACH, AND EITHER ALONE
32
+ * WOULD BE ENOUGH. They are create-only — `target` is a `fixed` option, `source` has no honest
33
+ * declared value — AND PVE REWRITES BOTH BEHIND YOUR BACK. MEASURED in
34
+ * `switch_replication_job_target_nolock`: when a guest migrates, PVE sets
35
+ * `$jobcfg->{target} = $new_target` and `$jobcfg->{source} = $old_target`, so the job follows
36
+ * the guest. Compared, a single migration would make every later plan report an update that the
37
+ * PUT cannot perform, forever. The cost of leaving them out is stated on the `targetNode` prop:
38
+ * a changed target plans as noop, and a migrated job is not dragged back.
39
+ * ⚠️ `guest`, `jobnum`, `type` AND `id` ARE NOT COMPARED EITHER: the first two are the id in
40
+ * integer form, `type` has one legal value, and `id` is the path — a change there is a
41
+ * different object, which reads as absent and is created.
42
+ * ⛔ `remove_job` IS COMPARED, AND IT IS THE ONE FIELD HERE THAT LOOKS LIKE A FOREVER-UPDATE AND
43
+ * IS NOT. A DELETE marks the job rather than removing it, so a re-declared id can name a job
44
+ * that is busy deleting itself, and matching that would be a noop over a vanishing object. It
45
+ * settles because `updateBody` actually clears the marker — see there.
46
+ */
47
+ /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */
48
+ endpoint: {
49
+ create: string;
50
+ update: string;
51
+ };
52
+ matches: (attributes: ReplicationJobAttributes, props: ReplicationJobProps) => boolean;
53
+ path: (props: ReplicationJobProps) => string;
54
+ updateForm: typeof updateBody;
55
+ };
56
+ //# sourceMappingURL=replication-job-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replication-job-config.d.ts","sourceRoot":"","sources":["../../src/proxmox/replication-job-config.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,EAAoB,UAAU,EAAS,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAmB5F,eAAO,MAAM,kBAAkB;IAC7B;;;;;;;;;OASG;;;;;;;;;;;;;;;;IAgBH;;;;;;;;;;;;;;;;OAgBG;IACH,wFAAwF;;QAEtF,MAAM;QACN,MAAM;;;;;CAUwD,CAAC"}
@@ -0,0 +1,62 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as cluster from '@distilled.cloud/proxmox/cluster';
3
+ import * as Effect from 'effect/Effect';
4
+ import type { ReplicationJobAttributes, ReplicationJobProps } from './replication-job.ts';
5
+ export declare const readReplicationJob: (props: ReplicationJobProps) => Effect.Effect<{
6
+ comment: string;
7
+ disable: boolean;
8
+ guest: number;
9
+ id: string;
10
+ jobnum: number;
11
+ rate: number;
12
+ remove_job: string;
13
+ schedule: string;
14
+ source: string;
15
+ targetNode: string;
16
+ type: string;
17
+ } | undefined, Error | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | cluster.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
18
+ /** DELETE without force/keep marks full background removal; it must never force immediate removal. */
19
+ export declare const deleteReplicationJob: (props: ReplicationJobProps) => Effect.Effect<void | cluster.DeleteClusterReplicationResponse, Error | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | cluster.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
20
+ export declare const replicationJobHandlers: {
21
+ list: () => Effect.Effect<never[], never, never>;
22
+ read: ({ olds }: {
23
+ olds: ReplicationJobProps;
24
+ }) => Effect.Effect<{
25
+ comment: string;
26
+ disable: boolean;
27
+ guest: number;
28
+ id: string;
29
+ jobnum: number;
30
+ rate: number;
31
+ remove_job: string;
32
+ schedule: string;
33
+ source: string;
34
+ targetNode: string;
35
+ type: string;
36
+ } | undefined, Error | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | cluster.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
37
+ diff: (args_0: {
38
+ news: Input<ReplicationJobProps>;
39
+ output: ReplicationJobAttributes | undefined;
40
+ }) => Effect.Effect<{
41
+ readonly action: "noop" | "update";
42
+ } | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
43
+ reconcile: (args_0: {
44
+ news: ReplicationJobProps;
45
+ }) => Effect.Effect<{
46
+ comment: string;
47
+ disable: boolean;
48
+ guest: number;
49
+ id: string;
50
+ jobnum: number;
51
+ rate: number;
52
+ remove_job: string;
53
+ schedule: string;
54
+ source: string;
55
+ targetNode: string;
56
+ type: string;
57
+ }, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
58
+ delete: ({ olds }: {
59
+ olds: ReplicationJobProps;
60
+ }) => Effect.Effect<void | cluster.DeleteClusterReplicationResponse, Error | import("./distilled-pve.ts").PveClusterExhausted | import("./credential-errors.ts").PveCredentialDenied | import("effect/Cause").TimeoutError | cluster.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
61
+ };
62
+ //# sourceMappingURL=replication-job-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replication-job-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/replication-job-lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,OAAO,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAc1F,eAAO,MAAM,kBAAkB,UAAW,mBAAmB;;;;;;;;;;;;oPA4B1D,CAAC;AAEJ,sGAAsG;AACtG,eAAO,MAAM,oBAAoB,UAAW,mBAAmB,ySAMO,CAAC;AAEvE,eAAO,MAAM,sBAAsB;IAEjC,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,mBAAmB,CAAA;KAAE;;;;;;;;;;;;;IAC9C,IAAI;cAII,KAAK,CAAC,mBAAmB,CAAC;gBACxB,wBAAwB,GAAG,SAAS;;;;IAa9C,SAAS;cAAyC,mBAAmB;;;;;;;;;;;;;;IA4BrE,MAAM,aAAa;QAAE,IAAI,EAAE,mBAAmB,CAAA;KAAE;CACjD,CAAC"}
@@ -56,13 +56,12 @@
56
56
  * checks `VM.Audit` on `/vms/<guest>`, `hf-read@pve` holds the built-in `PVEAuditor` at `/`
57
57
  * with propagate — both MEASURED — and `PVEAuditor` carries `VM.Audit`. This family is not a
58
58
  * fourth one that must borrow the provision lease.
59
- * ⚠️ A MISSING JOB IS A 500, NOT A 404 — "no such replication job '100-0'" — and the factory
60
- * folds any failed read into "absent", so a 403 reads as absent too. A plan insisting on
61
- * creating a job that plainly exists is a credential problem; the honest error arrives one step
62
- * later, from the create.
59
+ * ⚠️ A MISSING JOB IS A 500, NOT A 404 — "no such replication job '100-0'" — and distilled now
60
+ * exposes a precise missing-job tag. Permission failures and unrelated server errors propagate
61
+ * before any write; only the vendor missing-job signal means absent.
63
62
  *
64
63
  * ⛔ DELETE DOES NOT DELETE, AND A DESTROY IS NOT FREE. MEASURED: with neither `force` nor `keep` —
65
- * and the factory's `destroy` sends no form at all — the handler only sets `remove_job = 'full'`
64
+ * and the named SDK delete sends neither option — the handler only sets `remove_job = 'full'`
66
65
  * on the job and writes the config back. The job stays in replication.cfg until the SOURCE node's
67
66
  * `pvesr` timer next runs it, at which point it removes the local replication snapshots, removes
68
67
  * the replicated volumes on the target, and finally removes itself. So a destroy costs the
@@ -85,7 +84,7 @@
85
84
  */
86
85
  import { Resource } from 'alchemy';
87
86
  import * as Provider from 'alchemy/Provider';
88
- import { type PveRequirements, type WithTarget } from './resource.ts';
87
+ import type { PveRequirements, WithTarget } from './resource-spec.ts';
89
88
  export interface ReplicationJobProps extends WithTarget {
90
89
  /** ⛔ The guest being replicated. 100 or above — PVE reserves everything below. Identity. */
91
90
  guest: number;
@@ -1 +1 @@
1
- {"version":3,"file":"replication-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/replication-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,QAAQ,CACrD,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,qBAAqB,wDAA4D,CAAC;AA6E/F;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,2IAIvC,CAAC"}
1
+ {"version":3,"file":"replication-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/replication-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGtE,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,QAAQ,CACrD,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,qBAAqB,wDAA4D,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,2IAIvC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/release/archive.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAKvD;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,UACX,WAAW,CAAC,MAAM,CAAC,SACpB,MAAM,KACZ,OAAO,CAAC,WAAW,CAkCrB,CAAC;AAUF;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,WACV,cAAc,KACtB,OAAO,CAAC,UAAU,CAgBpB,CAAC"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/release/archive.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAKvD;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,UACX,WAAW,CAAC,MAAM,CAAC,SACpB,MAAM,KACZ,OAAO,CAAC,WAAW,CAqCrB,CAAC;AAUF;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,WACV,cAAc,KACtB,OAAO,CAAC,UAAU,CAgBpB,CAAC"}