@homeflare/alchemy 0.37.1 → 0.37.2
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/ceph-fs-distilled.d.ts +17 -10
- package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -22
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts +3 -3
- package/dist/proxmox/group-wire.d.ts +2 -2
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +295 -313
- package/dist/proxmox/index.js.map +5 -5
- package/dist/proxmox/node-network-wire.d.ts +1 -1
- package/dist/proxmox/node-network-wire.d.ts.map +1 -1
- package/dist/proxmox/storage-wire.d.ts +2 -2
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +2 -2
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -1
- package/docs/changelog/README.md +22 -0
- package/docs/changelog/archive-01.md +176 -0
- package/docs/changelog/archive-02.md +180 -0
- package/docs/changelog/archive-03.md +179 -0
- package/docs/changelog/archive-04.md +178 -0
- package/docs/changelog/archive-05.md +177 -0
- package/docs/changelog/archive-06.md +180 -0
- package/docs/changelog/archive-07.md +180 -0
- package/docs/changelog/archive-08.md +178 -0
- package/docs/changelog/archive-09.md +181 -0
- package/docs/changelog/archive-10.md +182 -0
- package/docs/changelog/archive-11.md +182 -0
- package/docs/changelog/archive-12.md +183 -0
- package/docs/changelog/archive-13.md +180 -0
- package/docs/changelog/archive-14.md +178 -0
- package/docs/changelog/archive-15.md +181 -0
- package/docs/changelog/archive-16.md +182 -0
- package/docs/changelog/archive-17.md +183 -0
- package/docs/changelog/archive-18.md +55 -0
- package/docs/proxmox.md +9 -3
- package/package.json +3 -3
- package/src/proxmox/ceph-fs-delete.test.ts +5 -8
- package/src/proxmox/ceph-fs-destroy-sdk.test.ts +98 -0
- package/src/proxmox/ceph-fs-distilled.ts +41 -13
- package/src/proxmox/ceph-fs-wire.ts +5 -93
- package/src/proxmox/ceph-fs.ts +7 -7
|
@@ -63,7 +63,7 @@ export interface NodeNetworkAttributes {
|
|
|
63
63
|
* read as "this interface is not there" — `diff` calls this directly and propagates every one
|
|
64
64
|
* of them loudly.
|
|
65
65
|
*/
|
|
66
|
-
export declare const readInterfaceOrFail: (props: NodeNetworkProps) => Effect.Effect<typeof UNREADABLE | NodeNetworkAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
66
|
+
export declare const readInterfaceOrFail: (props: NodeNetworkProps) => Effect.Effect<typeof UNREADABLE | NodeNetworkAttributes | 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
67
67
|
/**
|
|
68
68
|
* ⛔ FOLDS A GENUINE READ FAILURE TO `undefined` TOO, AND ONLY `read`'s adoption/recovery branch
|
|
69
69
|
* AND `reconcile` MAY USE IT — FOUND BY ADVERSARIAL REVIEW, 2026-09-24: a first draft used
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-network-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-wire.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EACL,KAAK,gBAAgB,EAOtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4DD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,UAAW,gBAAgB,
|
|
1
|
+
{"version":3,"file":"node-network-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-wire.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EACL,KAAK,gBAAgB,EAOtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4DD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,UAAW,gBAAgB,ylCAmBxD,CAAC;AAEJ;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,UAAW,gBAAgB,sIACmB,CAAC;AAEzE,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,qBAAqB,GAAG,UAAU,GAAG,SAAS,sCAC3C,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAcnB,CAAC"}
|
|
@@ -40,7 +40,7 @@ export declare const same: <T>(declared: T | undefined, live: T) => boolean;
|
|
|
40
40
|
* `readUser`/`readGroup` do, so a brand-new declaration can still be created; `readStorageOrFail`
|
|
41
41
|
* (used by `diff`) does not.
|
|
42
42
|
*/
|
|
43
|
-
export declare const readStorageOrFail: (props: StorageProps) => Effect.Effect<typeof UNREADABLE | StorageAttributes, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | storage.StorageNotFound | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
43
|
+
export declare const readStorageOrFail: (props: StorageProps) => Effect.Effect<typeof UNREADABLE | StorageAttributes, 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | storage.StorageNotFound | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
44
44
|
/**
|
|
45
45
|
* ⛔ ONLY `StorageNotFound` MEANS ABSENT. The initial distilled transport swap preserved a
|
|
46
46
|
* catch-all fold because PVE reports missing objects as HTTP 500. SDK PR 265 now types that
|
|
@@ -49,7 +49,7 @@ export declare const readStorageOrFail: (props: StorageProps) => Effect.Effect<t
|
|
|
49
49
|
* ★ `readStorageOrFail` remains the strict path used for an already-confirmed state row. This
|
|
50
50
|
* change narrows cold reads/reconcile without changing that existing drift/diff contract.
|
|
51
51
|
*/
|
|
52
|
-
export declare const readStorage: (props: StorageProps) => Effect.Effect<typeof UNREADABLE | StorageAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
52
|
+
export declare const readStorage: (props: StorageProps) => Effect.Effect<typeof UNREADABLE | StorageAttributes | 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
53
53
|
/** `read`/`reconcile` return `Attributes | undefined`; only `diff` tells `UNREADABLE` apart. */
|
|
54
54
|
export declare const dropUnreadable: (live: StorageAttributes | Unreadable | undefined) => StorageAttributes | undefined;
|
|
55
55
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/storage-wire.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAmBD,eAAO,MAAM,QAAQ,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YACN,CAAC;AAE9D,eAAO,MAAM,IAAI,GAAI,CAAC,YAAY,CAAC,GAAG,SAAS,QAAQ,CAAC,YACX,CAAC;AAwB9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,UAAW,YAAY,
|
|
1
|
+
{"version":3,"file":"storage-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/storage-wire.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAmBD,eAAO,MAAM,QAAQ,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YACN,CAAC;AAE9D,eAAO,MAAM,IAAI,GAAI,CAAC,YAAY,CAAC,GAAG,SAAS,QAAQ,CAAC,YACX,CAAC;AAwB9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,UAAW,YAAY,mmCAGyC,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,UAAW,YAAY,qlCAG5C,CAAC;AAEJ,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,iBAAiB,GAAG,UAAU,GAAG,SAAS,kCACvC,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,OAAO,eAAgB,iBAAiB,SAAS,YAAY,YAOjC,CAAC"}
|
|
@@ -29,7 +29,7 @@ export declare const matches: (attributes: UserAttributes, props: UserProps) =>
|
|
|
29
29
|
* `PveClusterExhausted`, then silently folded to "absent" by the old `orElseSucceed`, forcing a
|
|
30
30
|
* false `update` on every account.
|
|
31
31
|
*/
|
|
32
|
-
export declare const readUserOrFail: (props: UserProps) => Effect.Effect<typeof UNREADABLE | UserAttributes, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | access.UserNotFound, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
32
|
+
export declare const readUserOrFail: (props: UserProps) => Effect.Effect<typeof UNREADABLE | UserAttributes, 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity | access.UserNotFound, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
33
33
|
/**
|
|
34
34
|
* ⛔ ONLY `UserNotFound` MEANS ABSENT. The initial distilled transport swap preserved a
|
|
35
35
|
* catch-all fold because PVE reports missing objects as HTTP 500. SDK PR 265 now types that
|
|
@@ -38,7 +38,7 @@ export declare const readUserOrFail: (props: UserProps) => Effect.Effect<typeof
|
|
|
38
38
|
* ★ `readUserOrFail` remains the strict path used for an already-confirmed state row. This
|
|
39
39
|
* change narrows cold reads/reconcile without changing that existing drift/diff contract.
|
|
40
40
|
*/
|
|
41
|
-
export declare const readUser: (props: UserProps) => Effect.Effect<typeof UNREADABLE | UserAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
41
|
+
export declare const readUser: (props: UserProps) => Effect.Effect<typeof UNREADABLE | UserAttributes | 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
42
42
|
/** `read`/`reconcile` return `Attributes | undefined`; only `diff` tells `UNREADABLE` apart. */
|
|
43
43
|
export declare const dropUnreadable: (live: UserAttributes | Unreadable | undefined) => UserAttributes | undefined;
|
|
44
44
|
//# sourceMappingURL=user-wire.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/user-wire.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,iCAAiC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,WAAW,2BAA2B,CAAC;AACpD,eAAO,MAAM,WAAW,mCAAmC,CAAC;AA2D5D,eAAO,MAAM,UAAU,UAAW,SAAS,KAAG,MAAM,CAAC,uBAGnD,CAAC;AAEH,eAAO,MAAM,UAAU,UAAW,SAAS,KAAG,MAAM,CAAC,oBAGnD,CAAC;AAoBH;;;GAGG;AACH,eAAO,MAAM,OAAO,eAAgB,cAAc,SAAS,SAAS,YAOF,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,UAAW,SAAS,
|
|
1
|
+
{"version":3,"file":"user-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/user-wire.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,iCAAiC,CAAC;AAC1D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,WAAW,2BAA2B,CAAC;AACpD,eAAO,MAAM,WAAW,mCAAmC,CAAC;AA2D5D,eAAO,MAAM,UAAU,UAAW,SAAS,KAAG,MAAM,CAAC,uBAGnD,CAAC;AAEH,eAAO,MAAM,UAAU,UAAW,SAAS,KAAG,MAAM,CAAC,oBAGnD,CAAC;AAoBH;;;GAGG;AACH,eAAO,MAAM,OAAO,eAAgB,cAAc,SAAS,SAAS,YAOF,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,UAAW,SAAS,4lCAG+C,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,UAAW,SAAS,klCACqD,CAAC;AAE/F,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,cAAc,GAAG,UAAU,GAAG,SAAS,+BACpC,CAAC"}
|
|
@@ -51,7 +51,7 @@ export declare const leafDevices: (children: unknown) => string[];
|
|
|
51
51
|
* `SETTLE_ATTEMPTS` (zfs-pool-form.ts's own ⚠️ on that); `readPoolOrFail` (used by `diff`) does
|
|
52
52
|
* not fold it, so a genuine transient failure there propagates and fails the plan loudly instead.
|
|
53
53
|
*/
|
|
54
|
-
export declare const readPoolOrFail: (props: ZfsPoolProps) => Effect.Effect<typeof UNREADABLE | ZfsPoolAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
54
|
+
export declare const readPoolOrFail: (props: ZfsPoolProps) => Effect.Effect<typeof UNREADABLE | ZfsPoolAttributes | 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/caddy").NotFound | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
55
55
|
/**
|
|
56
56
|
* ⛔ FOLDS A GENUINE READ FAILURE TO `undefined` TOO, AND ONLY `createPool`'s settle-poll AND
|
|
57
57
|
* `read`/`reconcile` MAY USE IT — user.ts's `readUser` has the full reasoning. Safe here because
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zfs-pool-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-wire.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,aAAc,OAAO,KAAG,MAAM,EAO9C,CAAC;AAmBT;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,UAAW,YAAY
|
|
1
|
+
{"version":3,"file":"zfs-pool-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-wire.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,aAAc,OAAO,KAAG,MAAM,EAO9C,CAAC;AAmBT;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,UAAW,YAAY,qlCAe/C,CAAC;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,UAAW,YAAY,kIACuB,CAAC;AAEpE,sGAAsG;AACtG,eAAO,MAAM,cAAc,SAAU,iBAAiB,GAAG,UAAU,GAAG,SAAS,kCACvC,CAAC;AAEzC;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Alchemy changelog archive
|
|
2
|
+
|
|
3
|
+
Release history before 0.37.1, preserved in order. See the [current changelog](../../CHANGELOG.md).
|
|
4
|
+
|
|
5
|
+
- [0.37.0](./archive-01.md)
|
|
6
|
+
- [0.37.0 to 0.35.0](./archive-02.md)
|
|
7
|
+
- [0.35.0 to 0.34.0](./archive-03.md)
|
|
8
|
+
- [0.34.0 to 0.33.0](./archive-04.md)
|
|
9
|
+
- [0.33.0 to 0.32.0](./archive-05.md)
|
|
10
|
+
- [0.32.0 to 0.31.1](./archive-06.md)
|
|
11
|
+
- [0.31.1 to 0.29.1](./archive-07.md)
|
|
12
|
+
- [0.29.1 to 0.29.0](./archive-08.md)
|
|
13
|
+
- [0.29.0 to 0.27.3](./archive-09.md)
|
|
14
|
+
- [0.27.3 to 0.24.0](./archive-10.md)
|
|
15
|
+
- [0.24.0 to 0.20.0](./archive-11.md)
|
|
16
|
+
- [0.20.0 to 0.16.1](./archive-12.md)
|
|
17
|
+
- [0.16.1 to 0.15.0](./archive-13.md)
|
|
18
|
+
- [0.15.0 to 0.14.0](./archive-14.md)
|
|
19
|
+
- [0.14.0 to 0.10.0](./archive-15.md)
|
|
20
|
+
- [0.10.0 to 0.6.0](./archive-16.md)
|
|
21
|
+
- [0.6.0 to 0.1.1](./archive-17.md)
|
|
22
|
+
- [0.1.1 to 0.1.0](./archive-18.md)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Alchemy changelog archive 1
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
## 0.37.0
|
|
6
|
+
|
|
7
|
+
### Minor Changes
|
|
8
|
+
|
|
9
|
+
- [#262](https://github.com/taslabs-net/homeflare-kit/pull/262) [`ca1a558`](https://github.com/taslabs-net/homeflare-kit/commit/ca1a558db9101a8381dff57ba80f4aafcd01ae61) Thanks [@taslabs-net](https://github.com/taslabs-net)! - New `argocd/*` family — `Argocd.Repository`, `Argocd.RepoCreds` and `Argocd.Cluster`, on
|
|
10
|
+
`@distilled.cloud/argocd@1.0.0-rc.12` (measured published, matching the kit's own `effect`/core
|
|
11
|
+
pins — added as a direct peer dependency, the same shape as `forgejo`/`discord`). Built ahead of
|
|
12
|
+
need, per Tim's "we are using talos on pve once we get things settled ... i thought we are using
|
|
13
|
+
argocd, i wanted it on the radar" and "even if we don't use it yet but we know we will" — **no
|
|
14
|
+
Talos-on-PVE cluster and no Argo CD instance exist on the estate yet**; every test runs against a
|
|
15
|
+
fake, never a real server.
|
|
16
|
+
|
|
17
|
+
**Upstream check first (decision 49).** `alchemy-run/alchemy` v2.0.0-beta.79 ships no ArgoCD
|
|
18
|
+
provider at all — the only two `argocd` hits in the whole repo are an unrelated AWS EKS add-on
|
|
19
|
+
capability name in a JSDoc example. So there is nothing upstream to duplicate.
|
|
20
|
+
|
|
21
|
+
**The REST-vs-CRD split is the actual design decision, and it is why `Application`/`AppProject`
|
|
22
|
+
are deliberately NOT in this family.** Both are real Kubernetes CRDs (`argoproj.io/v1alpha1`,
|
|
23
|
+
their own versioned OpenAPI schema, listable with plain `kubectl`) — the upstream-correct owner is
|
|
24
|
+
Alchemy's own `Kubernetes.Manifest` (`src/Kubernetes/Manifest.ts`, which already documents a
|
|
25
|
+
"Custom resource (CRD)" example), not a house REST wrapper around the same object. Repository,
|
|
26
|
+
RepoCreds and Cluster are different: Argo CD's own storage for them is a `Secret` labeled with its
|
|
27
|
+
own private convention (`argocd.argoproj.io/secret-type: repository|repo-creds|cluster`) — not a
|
|
28
|
+
published Kubernetes API type, so the REST SDK is the only correct owner. Full reasoning, citations
|
|
29
|
+
and worked `Kubernetes.Manifest` examples for `Application`/`AppProject`:
|
|
30
|
+
`packages/alchemy/docs/argocd.md` and `docs/argocd-kubernetes.md`.
|
|
31
|
+
|
|
32
|
+
**A real, measured SDK-gap check that came back clean, unlike Discord's.** No `patches/` directory
|
|
33
|
+
exists for `packages/argocd` in distilled (homeflare/base) — every generated operation's own
|
|
34
|
+
declared `errors` array is just `[UnknownArgocdError]`. That looks like Discord's documented gap
|
|
35
|
+
(`docs/discord.md#sdk-gaps`) at first read. It is not: `protocol.ts` types every operation's shared
|
|
36
|
+
error channel from `API_ERRORS` (the full `HTTP_STATUS_MAP` set, not the narrower
|
|
37
|
+
`DEFAULT_ERRORS` Discord's channel is built from), and `core/protocol-rest.ts#decode` maps any
|
|
38
|
+
4xx status through `HTTP_STATUS_MAP` unconditionally after the per-op typed matchers, regardless
|
|
39
|
+
of what the operation itself declared. `catchTag('NotFound', ...)` works correctly today — proven
|
|
40
|
+
against the real protocol and a fake 404/401/403 in `repository.test.ts`, `repo-creds.test.ts` and
|
|
41
|
+
`cluster.test.ts`, not asserted by reading the source alone. No distilled patch was needed.
|
|
42
|
+
|
|
43
|
+
**Secrets never in state.** Every credential field (a repo's password/SSH key/bearer token/TLS
|
|
44
|
+
client key, a cluster's password/bearer token/TLS client key) is a prop typed `FromEnv`
|
|
45
|
+
(`../secrets/write-only.ts`), resolved from the deploying process's own environment at call time
|
|
46
|
+
and refused with a typed domain error when unset — never a literal value on a stack file, an
|
|
47
|
+
attribute, or in Alchemy's state store. No drift detection on those fields, the same
|
|
48
|
+
simplification `forgejo/org-actions-secrets.ts` already makes for the identical problem (Argo CD's
|
|
49
|
+
`Get`/`List` never returns them back): written once on create, rotated by destroy-then-create.
|
|
50
|
+
|
|
51
|
+
**Shape.** One shared engine (`resource.ts`) — a near-verbatim copy of `forgejo/resource.ts`, since
|
|
52
|
+
both SDKs are plain bearer-REST with a genuine typed `NotFound`. `reconcile` is a single upsert
|
|
53
|
+
call (every `Create*` operation this family calls takes `upsert: true` as a query parameter,
|
|
54
|
+
converging create-or-update in one request — no separate `Update*` path to wire). No `adopt(true)`/
|
|
55
|
+
`Unowned` wrapping, mirroring forgejo's own choice rather than discord's/netbox's marker.
|
|
56
|
+
|
|
57
|
+
**Tests**: 20 cases across the three resources, covering a live decode, a 404 folding to absent, a
|
|
58
|
+
401/403 propagating (never folded), idempotent delete (no DELETE call against an already-absent
|
|
59
|
+
object), and the write-only credential refusal/resolution path — all against `fake-argocd.ts`, a
|
|
60
|
+
loopback fake exercising the real distilled protocol, never a real Argo CD server.
|
|
61
|
+
|
|
62
|
+
### Patch Changes
|
|
63
|
+
|
|
64
|
+
- [#259](https://github.com/taslabs-net/homeflare-kit/pull/259) [`4302d28`](https://github.com/taslabs-net/homeflare-kit/commit/4302d28be200a04aa7b367a8a579134f864cd414) Thanks [@taslabs-net](https://github.com/taslabs-net)! - The `proxmox/*` family's Ceph sub-area, second of two PRs for 2c (decision 43's walk-down): migrates
|
|
65
|
+
`Proxmox.CephDaemon` (mon/mgr/mds) and `Proxmox.CephOsd` fully onto `@distilled.cloud/proxmox`.
|
|
66
|
+
Neither family had a forked-worker settle loop or an envelope-stripping dependency, so unlike
|
|
67
|
+
`Proxmox.NetworkApply`/`Proxmox.CephFs`'s delete, there is no protocol-level reason to leave either
|
|
68
|
+
on `client.ts` — both move completely.
|
|
69
|
+
|
|
70
|
+
**`Proxmox.CephDaemon`** replaces the generic `pveOperations`/`pveHandlers` factory (which cannot
|
|
71
|
+
run a distilled typed operation) with hand-written `read`/`diff`/`reconcile`/`delete`, matching the
|
|
72
|
+
factory's own pre-migration shape exactly: `matches` was always `() => true` and there is no PUT
|
|
73
|
+
for any of the three kinds, so a live daemon is always `noop` and drift is always `update`, never a
|
|
74
|
+
diff no write could satisfy. `nodes.updateNodeCephMon`/`Mgr`/`Mds` are distilled's generator
|
|
75
|
+
misnaming the create a POST "update" (checked against each one's `T.Http` annotation before
|
|
76
|
+
trusting the name); `nodes.listNodeCephMon`/`Mgr`/`Mds` and `nodes.deleteNodeCephMon`/`Mgr`/`Mds`
|
|
77
|
+
are the read and delete. One field rename: mon's `mon-address` maps to distilled's `mon_address`
|
|
78
|
+
(`T.Body("mon-address")`, confirmed against the schema) — the same one-key pattern
|
|
79
|
+
node-network-form.ts's `RENAMED` table carries for three fields.
|
|
80
|
+
|
|
81
|
+
**`Proxmox.CephOsd`** keeps its own pre-migration read behaviour exactly: `readOsd` (ceph-osd-tree.ts)
|
|
82
|
+
still has NO `Effect.orElseSucceed` — the tree endpoint is a collection that always exists while
|
|
83
|
+
Ceph is installed, so a read failure is never "the OSD is gone" and must propagate, never fold. This
|
|
84
|
+
is the one migrated Ceph family that does NOT match the single-fold shape `Proxmox.CephPool`/
|
|
85
|
+
`Proxmox.CephDaemon` carry, because it never had it before this PR either. `nodes.getNodeCephOsd`
|
|
86
|
+
(the tree), `nodes.createNodeCephOsd` and `nodes.deleteNodeCephOsd` map 1:1 to the existing forms.
|
|
87
|
+
|
|
88
|
+
**Both families keep every existing refusal byte-for-byte.** `Proxmox.CephOsd`'s create and delete
|
|
89
|
+
remain unreachable through this package's own credential — PVE registers `createosd`/`destroyosd`
|
|
90
|
+
with NO permissions block at all (root@pam only), confirmed again against distilled's own generated
|
|
91
|
+
schema, which changes nothing about that restriction. `Proxmox.CephDaemon`'s destroy still runs
|
|
92
|
+
against the daemon's own id path, never the collection `read` uses, avoiding the 501-that-looks-
|
|
93
|
+
like-a-permission-problem the pre-migration header already warned about.
|
|
94
|
+
|
|
95
|
+
**One pre-existing, out-of-scope finding, carried forward exactly rather than fixed here:**
|
|
96
|
+
`Proxmox.CephOsd`'s `destroyOsd` sends its `cleanup` flag as a request BODY on a DELETE, on
|
|
97
|
+
`client.ts` before this PR and on distilled after it alike — `client.ts`'s own `buildRequest` puts
|
|
98
|
+
`form` in the body for every HTTP method, and distilled's generated `DeleteNodeCephOsdRequest`
|
|
99
|
+
carries no `T.Query()` annotation on `cleanup` either, so it defaults to the same place. PVE's own
|
|
100
|
+
server (measured from `AnyEvent.pm`, the same fact `Proxmox.CephFs`'s delete was kept off distilled
|
|
101
|
+
for) never reads a body on DELETE — so `cleanup=1` was ALREADY silently ignored by the live cluster
|
|
102
|
+
before this migration. This is not a new regression, so it is not a reason to keep `destroyOsd` off
|
|
103
|
+
distilled the way `Proxmox.CephFs`'s delete was — but it is a real bug, unrelated to this PR, flagged
|
|
104
|
+
separately for its own fix (move `cleanup` into the query string, the way `destroyPath` in
|
|
105
|
+
ceph-fs-wire.ts already does for CephFs's own flags). It has zero live impact today either way,
|
|
106
|
+
since the whole DELETE 403s for this package's credential regardless of `cleanup`.
|
|
107
|
+
|
|
108
|
+
**One adversarial-review finding, fixed.** The hand-written `reconcile` returned early on
|
|
109
|
+
`if (live !== undefined) return live;` BEFORE calling `guardWrite` — the pre-migration factory
|
|
110
|
+
(`pveOperations.reconcile`, resource.ts) ran its create/update guard unconditionally right after
|
|
111
|
+
the read, because `reconcile` also runs for an ADOPTED row with no fresh `diff` first (Plan.ts
|
|
112
|
+
forces it after the probe even when `diff` said noop). Inert today, since `diff` guards every
|
|
113
|
+
normal plan — but a real gap for any caller that invokes `reconcile` directly (a resumed apply
|
|
114
|
+
from persisted state, a verify/adopt harness). Fixed to match `node-network.ts`'s own `reconcile`:
|
|
115
|
+
`guardWrite` now runs immediately after the read, unconditionally. `reconcileDaemon` is exported
|
|
116
|
+
so a test can call it directly, bypassing `diff` the same way the gap would have been reached.
|
|
117
|
+
|
|
118
|
+
**A related, separate finding, NOT fixed here — recorded to `decisions.md`'s open items.** Proving
|
|
119
|
+
the guard fix with an actual refused value turned up that `constraints.ts`'s `violations()` never
|
|
120
|
+
checks a bare `format` rule: `mon-address`'s own vendor entry is `{"format":"ip-list",
|
|
121
|
+
"type":"string"}` with no `pattern`, and checked directly, `formViolations` returns `[]` for a
|
|
122
|
+
malformed address. `mds`/`mgr`'s create endpoints carry EMPTY constraint tables too. About 105
|
|
123
|
+
parameters across this whole package declare a `format`; none are enforced. This is a real,
|
|
124
|
+
pre-existing gap unrelated to distilled or this migration — the new test instead proves the
|
|
125
|
+
STRUCTURAL property (`guardWrite`, and the `createForm` it must evaluate, still run on the
|
|
126
|
+
adopted-row path, via a field getter that only `createForm` reads on that path), confirmed to fail
|
|
127
|
+
without the ordering fix by temporarily reverting it and re-running.
|
|
128
|
+
|
|
129
|
+
**New tests**, proven against a deliberately broken implementation before landing:
|
|
130
|
+
`ceph-daemon-write.test.ts` (a new mon POSTs `mon-address` as `mon_address` and reads the collection
|
|
131
|
+
back; a new mds POSTs `hotstandby`; `RemovalPolicy.destroy()` then undeclaring a mgr sends exactly
|
|
132
|
+
one DELETE — confirmed to fail without the real `deleteDaemon` call by temporarily stubbing it out)
|
|
133
|
+
and `ceph-osd-write.test.ts` (a new OSD POSTs `dev` and `crush-device-class` correctly and reads the
|
|
134
|
+
new leaf back by id — this test can never run against the real cluster, since the write it drives is
|
|
135
|
+
root-only, but it is the only way to catch a wire-translation bug a real deploy would never reach far
|
|
136
|
+
enough to expose).
|
|
137
|
+
|
|
138
|
+
**Expected after this releases and the consumer bumps:** no live plan change for either family — C1's
|
|
139
|
+
read-role lease could always read the live Ceph daemons and OSDs cleanly, so this is a transport
|
|
140
|
+
migration on tested paths, not a behaviour change.
|
|
141
|
+
|
|
142
|
+
- [#257](https://github.com/taslabs-net/homeflare-kit/pull/257) [`5e1e1b7`](https://github.com/taslabs-net/homeflare-kit/commit/5e1e1b7dfd51cff7664c74284287e44e9caa9f3c) Thanks [@taslabs-net](https://github.com/taslabs-net)! - The `proxmox/*` family's Ceph sub-area, first PR of two (decision 43's serial proxmox walk-down,
|
|
143
|
+
2c — the four Ceph families, split pool+fs first, daemon+osd after): migrates `Proxmox.CephPool`
|
|
144
|
+
fully onto `@distilled.cloud/proxmox`, and migrates `Proxmox.CephFs`'s READ and CREATE onto it
|
|
145
|
+
while its DELETE stays on `client.ts` for a measured protocol reason.
|
|
146
|
+
|
|
147
|
+
**Every existing refusal and guard survives byte-for-byte — this is a transport swap, not a
|
|
148
|
+
redesign.** Both families keep the exact single-fold read shape they always had
|
|
149
|
+
(`Effect.orElseSucceed` after every failure, no `output`-branching): that is NOT the newer
|
|
150
|
+
dual-path pattern `Proxmox.NodeNetwork`/`Proxmox.ZfsPool`/`Proxmox.Storage` carry, and it is not
|
|
151
|
+
upgraded to it here — this PR's own rule is behaviour surviving unchanged, not gaining the newer
|
|
152
|
+
pattern along the way. Both `ceph-pool-wire.ts` and `ceph-fs-distilled.ts` say so in their own
|
|
153
|
+
headers, including the same-day latent gap this leaves (a refused OpenBao mint still folds to
|
|
154
|
+
"absent" on both families, the cries-wolf class already fixed elsewhere) — noted, not fixed, since
|
|
155
|
+
fixing it is a behaviour change this PR's rules do not ask for.
|
|
156
|
+
|
|
157
|
+
**`Proxmox.CephPool`: the PG-merge guard, and the absence signal, both measured live.** `GET
|
|
158
|
+
/nodes/{node}/ceph/pool/{name}/status` on a name with no pool answers a generic HTTP 500 (measured
|
|
159
|
+
against TB4 `n2`, 2026-09-24, read-role, read-only probe:
|
|
160
|
+
`{"data":null,"message":"error with 'osd pool get': mon_cmd failed - unrecognized pool
|
|
161
|
+
'<name>'\n"}`) — not a parsed field like `Proxmox.NodeNetwork`'s 400, so `confirmAbsent`
|
|
162
|
+
(ceph-pool-settle.ts) still asks the SECOND question — does the index list this name — before a
|
|
163
|
+
create is allowed to run, exactly as before. `confirmAbsent` now calls distilled's
|
|
164
|
+
`listNodeCephPool`; the settle loop (waiting out a forked worker) never used PVE's task-status
|
|
165
|
+
endpoint at all — it re-polls the pool's own status, so it needed no `Task.awaitTask` and carries
|
|
166
|
+
no NEW protocol risk. `createNodeCephPool`/`putNodeCephPool`/`deleteNodeCephPool` map 1:1 to the
|
|
167
|
+
existing form (no field renames this family needed).
|
|
168
|
+
|
|
169
|
+
**`Proxmox.CephFs`: reads and create move; delete does not, for a measured reason.** Distilled
|
|
170
|
+
types `DeleteNodeCephFsRequest`'s `remove_pools`/`remove_storages` as request-BODY fields
|
|
171
|
+
(`T.Body`) — checked directly against its generated schema. This family's own header already
|
|
172
|
+
carries the measured fact (from PVE's own `AnyEvent.pm`) that PVE's server reads a request body
|
|
173
|
+
only on PUT/POST; a DELETE's body is silently discarded, no error raised anywhere. Sending those
|
|
174
|
+
two safety flags through distilled's generated delete op as-is would silently drop them — the same
|
|
175
|
+
class of protocol gap `Proxmox.NetworkApply` was kept off distilled for. `destroyFs` therefore
|
|
176
|
+
stays on `client.ts`, unmigrated, in `ceph-fs-wire.ts`.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Alchemy changelog archive 2
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
The read (the directory index, `nodes.listNodeCephFs`) and the create (`nodes.updateNodeCephFs` —
|
|
6
|
+
distilled's generator MISNAMES the create a "update"; checked directly against its `T.Http`
|
|
7
|
+
annotation, it is genuinely `POST /nodes/{node}/ceph/fs/{name}`, PVE's only write on that path,
|
|
8
|
+
harmless once named for what it is) both move to a new `ceph-fs-distilled.ts`. The create's forked-
|
|
9
|
+
worker wait is its OWN function there, polling the RAW `nodes.getNodeTaskStatus` operation directly
|
|
10
|
+
rather than `@distilled.cloud/proxmox`'s own `Task.awaitTask` — checked against `task.ts` before
|
|
11
|
+
deciding: `Task.awaitTask` propagates a poll failure as a genuine typed error instead of treating
|
|
12
|
+
it as "not settled yet", which would turn a missing `Sys.Audit` grant or a node mid-restart into a
|
|
13
|
+
failed create instead of a patient one, the same failure mode `Proxmox.NetworkApply`'s own header
|
|
14
|
+
already names. The raw operation carries none of that behaviour — it is a plain typed GET — so
|
|
15
|
+
wrapping it in the SAME tolerant retry loop `ceph-fs-wire.ts`'s own (unmigrated, delete-only) poll
|
|
16
|
+
already uses reproduces the exact policy: same cadence (`POLL_SECONDS`/`POLL_ATTEMPTS`, one
|
|
17
|
+
constant, imported not retyped), same "an unreadable status is not a failure" tolerance.
|
|
18
|
+
|
|
19
|
+
**Live measurement note.** This session's own OpenBao policy has no Proxmox grant; the live probes
|
|
20
|
+
above ran through `homeflare-proxmox`'s `scripts/with-mini-bao.ts` (the `macmini-agent` AppRole's
|
|
21
|
+
`read` role for TB4), from a detached worktree, read-only, per the coordinator's direction — not
|
|
22
|
+
through this repo's own credentials, and nothing here reads one.
|
|
23
|
+
|
|
24
|
+
**One distilled generator quirk recorded to the shared `distilled-sdk-path.md` memory**, since it
|
|
25
|
+
is an upstream-distilled fact rather than a kit one: `DeleteNodeCephFsRequest` types
|
|
26
|
+
`remove_pools`/`remove_storages` as `T.Body` on a DELETE, which PVE's own server never reads a
|
|
27
|
+
body on — the same class of finding as `Proxmox.NetworkApply`'s two, worth the same upstream
|
|
28
|
+
attention if `@distilled.cloud/proxmox` is ever patched again.
|
|
29
|
+
|
|
30
|
+
**New tests**, every one proven against a deliberately weakened check before landing:
|
|
31
|
+
`ceph-pool-write.test.ts` (a genuinely new pool creates once the index is confirmed empty; the
|
|
32
|
+
PG-merge guard refuses a create when the index lists the name but the status read still fails —
|
|
33
|
+
confirmed this fails without the guard by temporarily removing the `confirmAbsent` call and
|
|
34
|
+
re-running; `RemovalPolicy.destroy()` then undeclaring sends exactly one DELETE) and
|
|
35
|
+
`ceph-fs-write.test.ts` (a genuinely new filesystem POSTs, polls a forked task that answers
|
|
36
|
+
`running` before `stopped`/`OK` — proving the poll loop actually loops — and reads the index back;
|
|
37
|
+
a task that finishes with a real Ceph error dies with the task-log pointer rather than a false
|
|
38
|
+
create).
|
|
39
|
+
|
|
40
|
+
**`ceph-fs-delete.test.ts` (new, added after the adversarial review's finding).** The review's one
|
|
41
|
+
substantive finding: `Proxmox.CephFs`'s `delete` handler is the single place in this PR that
|
|
42
|
+
composes a distilled call (`readFs`, before and after) with the unmigrated hand-client one
|
|
43
|
+
(`destroyFs`) inside one handler — the exact boundary this PR's whole design rests on — and that
|
|
44
|
+
composition had zero test coverage anywhere in the repo, before or after the rest of this PR
|
|
45
|
+
(each half was proven separately, never together, and the destroy is destructive and hard to
|
|
46
|
+
reverse). Added two tests: a genuine `RemovalPolicy.destroy()` then undeclare, confirmed to fail
|
|
47
|
+
(the read-back reports the filesystem still present) by temporarily removing the `destroyFs` call
|
|
48
|
+
and re-running; and a filesystem removed by hand between the adopt and the undeclare, proving
|
|
49
|
+
`delete` is a silent no-op rather than a false DELETE against an object already gone.
|
|
50
|
+
|
|
51
|
+
**Expected after this releases and the consumer bumps:** no live plan change for either family —
|
|
52
|
+
C1's read-role lease could always read the live Ceph objects cleanly, so this is a transport
|
|
53
|
+
migration on tested paths, not a behaviour change.
|
|
54
|
+
|
|
55
|
+
- [#261](https://github.com/taslabs-net/homeflare-kit/pull/261) [`ee3f452`](https://github.com/taslabs-net/homeflare-kit/commit/ee3f4526d8fa2991837a8d7931b2fb37df921eda) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `@distilled.cloud/litellm` (interim, via `packages/distilled-litellm`): reverts `credentials.ts`
|
|
56
|
+
to `Effect.orDie` on a missing or misspelled `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY`.
|
|
57
|
+
|
|
58
|
+
Q6 of the 2026-09-24 walk-down (decision 49 "upstream wins"): distilled's own convention for a
|
|
59
|
+
missing credential is `Effect.orDie`, not a typed `ConfigError` — 73 of 80 `packages/*/src/credentials.ts`
|
|
60
|
+
end this way at `homeflare/base`, 78 of 80 at `origin/main`. A worktree commit (`4ad19154`) had
|
|
61
|
+
made LiteLLM's `Credentials` the one exception, declaring `Effect.Effect<Config, ConfigError>` and
|
|
62
|
+
dropping the `orDie`. Distilled is not silent on this convention, so the divergence was the house
|
|
63
|
+
going stricter than distilled's own practice on a point it already has an answer for — the thing
|
|
64
|
+
decision 49 says to stop doing. Reverted in the litellm worktree (`homeflare/litellm`
|
|
65
|
+
`cbd6bbb890066e57fb0224b96c27fca91d89e0a6`, a `git revert` of `4ad19154`, confirmed byte-identical
|
|
66
|
+
to `4ad19154`'s parent), gated on `typecheck:ci`/`specs:check`/`format:check`, then byte-copied
|
|
67
|
+
into `packages/distilled-litellm/src/credentials.ts` (confirmed with `diff -rq` against the
|
|
68
|
+
worktree's `src/`).
|
|
69
|
+
|
|
70
|
+
**The typed-error idea is not discarded**, just not spread further: it is written up as a HELD
|
|
71
|
+
upstream proposal in [`litellm.md`](../litellm.md#credentials) — distilled's
|
|
72
|
+
own `LitellmOpError` already declares `ConfigError` and threads a real credentials failure into an
|
|
73
|
+
operation's error channel, so the plumbing for a typed refusal genuinely exists; nobody has raised
|
|
74
|
+
it to `alchemy-run/distilled`, and this PR doesn't either.
|
|
75
|
+
|
|
76
|
+
**`packages/alchemy/src/litellm/operations.ts`'s `mutate` helper needed no functional change.**
|
|
77
|
+
Its `| ConfigError` widening was always redundant with the operation-level `LitellmOpError` union
|
|
78
|
+
(which declares `ConfigError` unconditionally, regardless of `Credentials`'s own type) — confirmed
|
|
79
|
+
by re-reading `protocol.ts` — so it still typechecks and the widening is still a real no-op for
|
|
80
|
+
callers. Its comment is corrected: it no longer claims resolving `Credentials` can itself fail
|
|
81
|
+
typed, since post-revert it cannot.
|
|
82
|
+
|
|
83
|
+
**Verified**: `bun run build:interim-packages`, `tsc --noEmit` for the whole `@homeflare/alchemy`
|
|
84
|
+
package, `bun test src/litellm` (28/28), and `packages/distilled-litellm`'s own `types` and
|
|
85
|
+
`smoke` — all clean.
|
|
86
|
+
|
|
87
|
+
No live plan change: this only affects what happens when the two env vars are missing or
|
|
88
|
+
misspelled, which was never a state this family's tests exercised as "succeeds."
|
|
89
|
+
|
|
90
|
+
- [#266](https://github.com/taslabs-net/homeflare-kit/pull/266) [`c6f3f9a`](https://github.com/taslabs-net/homeflare-kit/commit/c6f3f9a33fa2ed72c5d73586fa74090582330cd0) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Make `Cloudflare.R2BucketLock` deletion succeed when its bucket is already gone.
|
|
91
|
+
Read, reconcile and delete now propagate distilled's typed SDK failures instead of
|
|
92
|
+
turning them into `Effect.orDie` defects. Error identities such as `InvalidRoute`,
|
|
93
|
+
`TooManyRequests` and reconcile's `NoSuchBucket` remain available to callers.
|
|
94
|
+
|
|
95
|
+
This follows the lifecycle pattern in upstream `Cloudflare/R2/BucketSippy.ts` at
|
|
96
|
+
`alchemy@2.0.0-beta.79` (`473c3959`): catch the missing bucket only where it means
|
|
97
|
+
absence or successful deletion, and propagate other SDK errors without a blanket remap.
|
|
98
|
+
Walked against Cloudflare API v4 via `@distilled.cloud/cloudflare@1.0.0-rc.12`,
|
|
99
|
+
the version pinned by Alchemy beta.79. No props, attributes or SDK pins change.
|
|
100
|
+
|
|
101
|
+
Fake-transport tests cover missing-bucket reads and deletes, missing-bucket reconcile
|
|
102
|
+
failures, other 404 errors, and rate-limited reads/deletes with retries disabled.
|
|
103
|
+
|
|
104
|
+
## 0.36.0
|
|
105
|
+
|
|
106
|
+
### Minor Changes
|
|
107
|
+
|
|
108
|
+
- [#255](https://github.com/taslabs-net/homeflare-kit/pull/255) [`74310b5`](https://github.com/taslabs-net/homeflare-kit/commit/74310b5fcbd6ab1ddbebe607c2693c34d664bf30) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `grafana/*` ships `Grafana.NotificationPolicy` — third and last of the stacked PRs bringing Grafana
|
|
109
|
+
alerting provisioning under Alchemy (decision 40), completing the family alongside
|
|
110
|
+
`Grafana.ContactPoint`/`Grafana.MuteTiming`/`Grafana.MessageTemplate` (kit PR 250) and
|
|
111
|
+
`Grafana.AlertRuleGroup` (kit PR 254). Full detail, including what was measured against the SDK's
|
|
112
|
+
generated types versus Grafana's own docs (no live call was made in this PR):
|
|
113
|
+
`docs/grafana-notification-policy.md`.
|
|
114
|
+
|
|
115
|
+
**A singleton — adopt and update only.** Measured: `RouteGetPolicyTreeError` has no `NotFound`
|
|
116
|
+
case, so `fetchLive` never returns `undefined` — this resource has no create or delete in the sense
|
|
117
|
+
every sibling resource has, only ever `PUT`-replaces the one tree an instance has. `spec.create`
|
|
118
|
+
exists only because `GrafanaSpec`'s shared type requires it; it is never actually reached.
|
|
119
|
+
|
|
120
|
+
**`routeResetPolicyTree` needs TWO explicit gates, not one.** `defaultRemovalPolicy: 'retain'`
|
|
121
|
+
means Alchemy's engine never calls `destroy` at all unless a stack opts in with
|
|
122
|
+
`.pipe(RemovalPolicy.destroy())` — the convention every multi-object-cascade resource in this
|
|
123
|
+
family already uses. On top of that, `destroy` itself refuses to reset unless the resource's own
|
|
124
|
+
`allowReset: true` prop is set — even a stack that opted in at the Alchemy level still needs this.
|
|
125
|
+
Without it, `destroy` logs a warning and retains the tree. Two gates because `routeResetPolicyTree`
|
|
126
|
+
is uniquely destructive here: it wipes every route, receiver reference and matcher back to
|
|
127
|
+
Grafana's bare default for the WHOLE instance, not one object. A test proves `destroy` sends no
|
|
128
|
+
`DELETE` without `allowReset`, and that a foreign-provenance tree still refuses destroy even WITH
|
|
129
|
+
it.
|
|
130
|
+
|
|
131
|
+
**The tree is opaque `Record<string, unknown>`**, same reasoning as `Grafana.MuteTiming`'s
|
|
132
|
+
`timeIntervals` — Grafana's matcher-expression types (`Matchers`/`ObjectMatchers`/`MatchRegexps`)
|
|
133
|
+
are complex nested unions this family does not hand-model, and the SDK's own unmodeled-key
|
|
134
|
+
passthrough (measured for `Grafana.MuteTiming`, mute-timing.ts's file header) means an opaque
|
|
135
|
+
pass-through round-trips correctly regardless.
|
|
136
|
+
|
|
137
|
+
**Route order is significant**, same as `Grafana.AlertRuleGroup`'s rules — Alertmanager routes an
|
|
138
|
+
alert to the first matching route in array order, proven by a test that reverses the same two
|
|
139
|
+
routes and asserts a mismatch. **Foreign-provenance refusal** reuses `alerting-provenance.ts`
|
|
140
|
+
unchanged, including the missing-means-foreign fix an adversarial review of kit PR 250 established.
|
|
141
|
+
**Grafana-injected defaults** are tolerated the `subset-match.ts` way (the same mechanism
|
|
142
|
+
`Grafana.Dashboard`/`Grafana.ContactPoint` already rely on), not hand-normalized — not measured
|
|
143
|
+
against a live instance, since none exists in this house to read.
|
|
144
|
+
|
|
145
|
+
**Dropped-route warning**, mirroring kit PR 254's `Grafana.AlertRuleGroup` fix for the identical
|
|
146
|
+
risk: a whole-tree `PUT` replaces every route, so a nested route added out-of-band is removed with
|
|
147
|
+
no error. `notification-policy-drop-warning.ts`'s `warnOnDroppedRoutes` logs every dropped route by
|
|
148
|
+
receiver and dotted-index path, from both a local reimplementation of `diff` and from `update`
|
|
149
|
+
itself — never a refusal, since the tree stays the unit by design.
|
|
150
|
+
|
|
151
|
+
⛔ **Fixed after an adversarial review of this PR: the identity the warning used collided across
|
|
152
|
+
the WHOLE TREE, not just among siblings.** The first version keyed every node by its own-fields
|
|
153
|
+
JSON alone in a `Set`; a duplicate route added anywhere in the tree matched an unrelated declared
|
|
154
|
+
node with the same fields and the drop went unwarned, even though the `PUT` still removes it —
|
|
155
|
+
worse than the "structural, siblings-only" limitation the original comment claimed. `FlatRoute.key`
|
|
156
|
+
(`notification-policy-model.ts`) is now the chain of ancestor own-fields from the root down to a
|
|
157
|
+
node (`\0`-joined) — order-insensitive among siblings, but distinct across different parents — and
|
|
158
|
+
`warnOnDroppedRoutes` counts each key as a MULTISET (live count vs. declared count) rather than
|
|
159
|
+
checking `Set` membership, so N identical live occurrences under one parent are dropped only past
|
|
160
|
+
however many the declaration itself repeats. Four tests pin this: a duplicate added live warns; a
|
|
161
|
+
plain sibling reorder doesn't; an identical route under a different, undeclared parent still warns;
|
|
162
|
+
and two declared duplicates against two live duplicates warns nothing.
|
|
163
|
+
|
|
164
|
+
Tests (`notification-policy.test.ts` + `-model.test.ts` + `-refusals.test.ts` +
|
|
165
|
+
`-drop-warning.test.ts`, split four ways to stay under the house's 250-line file cap, sharing
|
|
166
|
+
fixtures from `notification-policy-fixtures.ts`) use the family's existing `fake-grafana.ts`
|
|
167
|
+
harness and prove: a GET never carries a body; reconcile never sends a create-shaped write; a
|
|
168
|
+
foreign-provenance tree (explicit `"file"` or MISSING) refuses update while an explicit `""` is
|
|
169
|
+
writable; `destroy` never resets without `allowReset: true`; reordering routes shows `update`; and
|
|
170
|
+
the dropped-route warning's multiset identity behaves as described above.
|
|
171
|
+
|
|
172
|
+
## 0.35.0
|
|
173
|
+
|
|
174
|
+
### Minor Changes
|
|
175
|
+
|
|
176
|
+
- [#254](https://github.com/taslabs-net/homeflare-kit/pull/254) [`e6788af`](https://github.com/taslabs-net/homeflare-kit/commit/e6788af70a34495af64d9bc0a4fce112e59bf561) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `grafana/*` ships `Grafana.AlertRuleGroup` — second of three stacked PRs bringing Grafana alerting
|
|
177
|
+
provisioning under Alchemy (decision 40), on the same `@distilled.cloud/grafana@0.2.0` operations
|
|
178
|
+
as `Grafana.ContactPoint`/`Grafana.MuteTiming`/`Grafana.MessageTemplate`. `Grafana.NotificationPolicy`
|
|
179
|
+
follows in one more PR. Full detail, including what was measured against the SDK's generated types
|
|
180
|
+
versus Grafana's own docs (no live call was made in this PR): `docs/grafana-alerting-rules.md`.
|