@homeflare/alchemy 0.42.0 → 0.44.0

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 (648) hide show
  1. package/README.md +10 -1
  2. package/dist/caddy/index.js +35 -1
  3. package/dist/caddy/index.js.map +3 -3
  4. package/dist/launchd/index.js +35 -1
  5. package/dist/launchd/index.js.map +3 -3
  6. package/dist/linux/index.js +35 -1
  7. package/dist/linux/index.js.map +3 -3
  8. package/dist/litellm/access-group-form.d.ts +26 -0
  9. package/dist/litellm/access-group-form.d.ts.map +1 -0
  10. package/dist/litellm/access-group-operations.d.ts +37 -0
  11. package/dist/litellm/access-group-operations.d.ts.map +1 -0
  12. package/dist/litellm/access-group-types.d.ts +49 -0
  13. package/dist/litellm/access-group-types.d.ts.map +1 -0
  14. package/dist/litellm/access-group.d.ts +55 -0
  15. package/dist/litellm/access-group.d.ts.map +1 -0
  16. package/dist/litellm/budget-errors.d.ts +36 -0
  17. package/dist/litellm/budget-errors.d.ts.map +1 -0
  18. package/dist/litellm/budget-form.d.ts +50 -0
  19. package/dist/litellm/budget-form.d.ts.map +1 -0
  20. package/dist/litellm/budget-operations.d.ts +20 -0
  21. package/dist/litellm/budget-operations.d.ts.map +1 -0
  22. package/dist/litellm/budget.d.ts +61 -0
  23. package/dist/litellm/budget.d.ts.map +1 -0
  24. package/dist/litellm/credential-errors.d.ts +131 -0
  25. package/dist/litellm/credential-errors.d.ts.map +1 -0
  26. package/dist/litellm/credential-form.d.ts +79 -0
  27. package/dist/litellm/credential-form.d.ts.map +1 -0
  28. package/dist/litellm/credential-operations.d.ts +84 -0
  29. package/dist/litellm/credential-operations.d.ts.map +1 -0
  30. package/dist/litellm/credential-rewrite.d.ts +6 -0
  31. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  32. package/dist/litellm/credential-types.d.ts +87 -0
  33. package/dist/litellm/credential-types.d.ts.map +1 -0
  34. package/dist/litellm/credential-values.d.ts +53 -0
  35. package/dist/litellm/credential-values.d.ts.map +1 -0
  36. package/dist/litellm/credential.d.ts +83 -0
  37. package/dist/litellm/credential.d.ts.map +1 -0
  38. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  39. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  41. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  42. package/dist/litellm/fake-keys.d.ts +53 -0
  43. package/dist/litellm/fake-keys.d.ts.map +1 -0
  44. package/dist/litellm/fake-litellm.d.ts +9 -1
  45. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  46. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  47. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  49. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  50. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  51. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  52. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  53. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  55. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-registry-base.d.ts +42 -0
  57. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  58. package/dist/litellm/fake-stack.d.ts +8 -1
  59. package/dist/litellm/fake-stack.d.ts.map +1 -1
  60. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  61. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  62. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  63. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  64. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  65. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  66. package/dist/litellm/index.d.ts +20 -0
  67. package/dist/litellm/index.d.ts.map +1 -1
  68. package/dist/litellm/index.js +3201 -2
  69. package/dist/litellm/index.js.map +70 -5
  70. package/dist/litellm/key-errors.d.ts +169 -0
  71. package/dist/litellm/key-errors.d.ts.map +1 -0
  72. package/dist/litellm/key-form.d.ts +119 -0
  73. package/dist/litellm/key-form.d.ts.map +1 -0
  74. package/dist/litellm/key-harness.d.ts +41 -0
  75. package/dist/litellm/key-harness.d.ts.map +1 -0
  76. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  77. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  78. package/dist/litellm/key-metadata.d.ts +22 -0
  79. package/dist/litellm/key-metadata.d.ts.map +1 -0
  80. package/dist/litellm/key-operations.d.ts +63 -0
  81. package/dist/litellm/key-operations.d.ts.map +1 -0
  82. package/dist/litellm/key-secret.d.ts +33 -0
  83. package/dist/litellm/key-secret.d.ts.map +1 -0
  84. package/dist/litellm/key.d.ts +78 -0
  85. package/dist/litellm/key.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  87. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  89. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-form.d.ts +56 -0
  91. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  93. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  95. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  96. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  97. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  98. package/dist/litellm/mcp-server-types.d.ts +118 -0
  99. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  100. package/dist/litellm/mcp-server-url.d.ts +11 -0
  101. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  102. package/dist/litellm/mcp-server.d.ts +95 -0
  103. package/dist/litellm/mcp-server.d.ts.map +1 -0
  104. package/dist/litellm/model-claim.d.ts +24 -0
  105. package/dist/litellm/model-claim.d.ts.map +1 -0
  106. package/dist/litellm/model-credential.d.ts +12 -0
  107. package/dist/litellm/model-credential.d.ts.map +1 -0
  108. package/dist/litellm/model-errors.d.ts +119 -0
  109. package/dist/litellm/model-errors.d.ts.map +1 -0
  110. package/dist/litellm/model-form.d.ts +78 -0
  111. package/dist/litellm/model-form.d.ts.map +1 -0
  112. package/dist/litellm/model-locate.d.ts +13 -0
  113. package/dist/litellm/model-locate.d.ts.map +1 -0
  114. package/dist/litellm/model-operations.d.ts +59 -0
  115. package/dist/litellm/model-operations.d.ts.map +1 -0
  116. package/dist/litellm/model-types.d.ts +91 -0
  117. package/dist/litellm/model-types.d.ts.map +1 -0
  118. package/dist/litellm/model.d.ts +88 -0
  119. package/dist/litellm/model.d.ts.map +1 -0
  120. package/dist/litellm/operations.d.ts +22 -0
  121. package/dist/litellm/operations.d.ts.map +1 -1
  122. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  123. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  124. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  125. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  126. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  127. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  128. package/dist/litellm/policy-attachment.d.ts +61 -0
  129. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  130. package/dist/litellm/policy-form.d.ts +22 -0
  131. package/dist/litellm/policy-form.d.ts.map +1 -0
  132. package/dist/litellm/policy-operations.d.ts +42 -0
  133. package/dist/litellm/policy-operations.d.ts.map +1 -0
  134. package/dist/litellm/policy-types.d.ts +57 -0
  135. package/dist/litellm/policy-types.d.ts.map +1 -0
  136. package/dist/litellm/policy.d.ts +65 -0
  137. package/dist/litellm/policy.d.ts.map +1 -0
  138. package/dist/litellm/providers.d.ts +1 -1
  139. package/dist/litellm/providers.d.ts.map +1 -1
  140. package/dist/litellm/registry-errors.d.ts +62 -0
  141. package/dist/litellm/registry-errors.d.ts.map +1 -0
  142. package/dist/litellm/registry-support.d.ts +42 -0
  143. package/dist/litellm/registry-support.d.ts.map +1 -0
  144. package/dist/litellm/team-form.d.ts +26 -0
  145. package/dist/litellm/team-form.d.ts.map +1 -0
  146. package/dist/litellm/team-members.d.ts +10 -0
  147. package/dist/litellm/team-members.d.ts.map +1 -0
  148. package/dist/litellm/team-operations.d.ts +38 -0
  149. package/dist/litellm/team-operations.d.ts.map +1 -0
  150. package/dist/litellm/team-permission.d.ts +35 -0
  151. package/dist/litellm/team-permission.d.ts.map +1 -0
  152. package/dist/litellm/team-types.d.ts +101 -0
  153. package/dist/litellm/team-types.d.ts.map +1 -0
  154. package/dist/litellm/team.d.ts +67 -0
  155. package/dist/litellm/team.d.ts.map +1 -0
  156. package/dist/litellm/tool-policy-form.d.ts +21 -0
  157. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  158. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  159. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  160. package/dist/litellm/tool-policy-types.d.ts +46 -0
  161. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  162. package/dist/litellm/tool-policy.d.ts +62 -0
  163. package/dist/litellm/tool-policy.d.ts.map +1 -0
  164. package/dist/litellm/toolset-form.d.ts +20 -0
  165. package/dist/litellm/toolset-form.d.ts.map +1 -0
  166. package/dist/litellm/toolset-operations.d.ts +39 -0
  167. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  168. package/dist/litellm/toolset-types.d.ts +46 -0
  169. package/dist/litellm/toolset-types.d.ts.map +1 -0
  170. package/dist/litellm/toolset.d.ts +55 -0
  171. package/dist/litellm/toolset.d.ts.map +1 -0
  172. package/dist/openbao/auth-role-form.d.ts +9 -0
  173. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  174. package/dist/openbao/auth-role-wire.d.ts.map +1 -1
  175. package/dist/openbao/fake-engines-roles.d.ts.map +1 -1
  176. package/dist/openbao/index.js +43 -6
  177. package/dist/openbao/index.js.map +5 -5
  178. package/dist/ownership/rows.d.ts +11 -0
  179. package/dist/ownership/rows.d.ts.map +1 -1
  180. package/dist/postgres/connection.d.ts +32 -2
  181. package/dist/postgres/connection.d.ts.map +1 -1
  182. package/dist/postgres/database-sql.d.ts +28 -9
  183. package/dist/postgres/database-sql.d.ts.map +1 -1
  184. package/dist/postgres/database.d.ts +1 -1
  185. package/dist/postgres/database.d.ts.map +1 -1
  186. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  187. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  188. package/dist/postgres/fake-grants-read.d.ts +61 -0
  189. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  190. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  191. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  192. package/dist/postgres/fake-grants-write.d.ts +29 -0
  193. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  194. package/dist/postgres/fake-role-sql.d.ts +40 -0
  195. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  196. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  197. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  198. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  199. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  200. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  201. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  202. package/dist/postgres/fake-sql.d.ts +42 -0
  203. package/dist/postgres/fake-sql.d.ts.map +1 -1
  204. package/dist/postgres/grants-attrs.d.ts +115 -0
  205. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  206. package/dist/postgres/grants-declare.d.ts +88 -0
  207. package/dist/postgres/grants-declare.d.ts.map +1 -0
  208. package/dist/postgres/grants-diff.d.ts +25 -0
  209. package/dist/postgres/grants-diff.d.ts.map +1 -0
  210. package/dist/postgres/grants-errors.d.ts +137 -0
  211. package/dist/postgres/grants-errors.d.ts.map +1 -0
  212. package/dist/postgres/grants-existence.d.ts +14 -0
  213. package/dist/postgres/grants-existence.d.ts.map +1 -0
  214. package/dist/postgres/grants-ops.d.ts +65 -0
  215. package/dist/postgres/grants-ops.d.ts.map +1 -0
  216. package/dist/postgres/grants-ownership.d.ts +18 -0
  217. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  218. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  219. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  220. package/dist/postgres/grants-plan.d.ts +65 -0
  221. package/dist/postgres/grants-plan.d.ts.map +1 -0
  222. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  223. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  224. package/dist/postgres/grants-read.d.ts +77 -0
  225. package/dist/postgres/grants-read.d.ts.map +1 -0
  226. package/dist/postgres/grants-refuse.d.ts +25 -0
  227. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  228. package/dist/postgres/grants-sql.d.ts +42 -0
  229. package/dist/postgres/grants-sql.d.ts.map +1 -0
  230. package/dist/postgres/grants-words.d.ts +29 -0
  231. package/dist/postgres/grants-words.d.ts.map +1 -0
  232. package/dist/postgres/grants.d.ts +37 -0
  233. package/dist/postgres/grants.d.ts.map +1 -0
  234. package/dist/postgres/index.d.ts +35 -8
  235. package/dist/postgres/index.d.ts.map +1 -1
  236. package/dist/postgres/index.js +2063 -39
  237. package/dist/postgres/index.js.map +47 -7
  238. package/dist/postgres/installed-classifier.d.ts +13 -0
  239. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  240. package/dist/postgres/providers.d.ts +17 -7
  241. package/dist/postgres/providers.d.ts.map +1 -1
  242. package/dist/postgres/psql-executor.d.ts +25 -0
  243. package/dist/postgres/psql-executor.d.ts.map +1 -0
  244. package/dist/postgres/role-attrs.d.ts +109 -0
  245. package/dist/postgres/role-attrs.d.ts.map +1 -0
  246. package/dist/postgres/role-diff.d.ts +22 -0
  247. package/dist/postgres/role-diff.d.ts.map +1 -0
  248. package/dist/postgres/role-errors.d.ts +126 -0
  249. package/dist/postgres/role-errors.d.ts.map +1 -0
  250. package/dist/postgres/role-membership-sql.d.ts +75 -0
  251. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  252. package/dist/postgres/role-provider.d.ts +11 -0
  253. package/dist/postgres/role-provider.d.ts.map +1 -0
  254. package/dist/postgres/role-scram.d.ts +6 -0
  255. package/dist/postgres/role-scram.d.ts.map +1 -0
  256. package/dist/postgres/role-secrets.d.ts +30 -0
  257. package/dist/postgres/role-secrets.d.ts.map +1 -0
  258. package/dist/postgres/role-sql.d.ts +68 -0
  259. package/dist/postgres/role-sql.d.ts.map +1 -0
  260. package/dist/postgres/role.d.ts +57 -0
  261. package/dist/postgres/role.d.ts.map +1 -0
  262. package/dist/postgres/schema-assert.d.ts +50 -0
  263. package/dist/postgres/schema-assert.d.ts.map +1 -0
  264. package/dist/postgres/schema-attrs.d.ts +51 -0
  265. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  266. package/dist/postgres/schema-delete.d.ts +28 -0
  267. package/dist/postgres/schema-delete.d.ts.map +1 -0
  268. package/dist/postgres/schema-diff.d.ts +14 -0
  269. package/dist/postgres/schema-diff.d.ts.map +1 -0
  270. package/dist/postgres/schema-errors.d.ts +139 -0
  271. package/dist/postgres/schema-errors.d.ts.map +1 -0
  272. package/dist/postgres/schema-identity.d.ts +8 -0
  273. package/dist/postgres/schema-identity.d.ts.map +1 -0
  274. package/dist/postgres/schema-reconcile.d.ts +12 -0
  275. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  276. package/dist/postgres/schema-sql.d.ts +45 -0
  277. package/dist/postgres/schema-sql.d.ts.map +1 -0
  278. package/dist/postgres/schema-test-kit.d.ts +67 -0
  279. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  280. package/dist/postgres/schema.d.ts +54 -0
  281. package/dist/postgres/schema.d.ts.map +1 -0
  282. package/dist/proxmox/ceph-flag-types.d.ts +57 -0
  283. package/dist/proxmox/ceph-flag-types.d.ts.map +1 -0
  284. package/dist/proxmox/ceph-flag-wire.d.ts +77 -0
  285. package/dist/proxmox/ceph-flag-wire.d.ts.map +1 -0
  286. package/dist/proxmox/ceph-flag.d.ts +15 -50
  287. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  288. package/dist/proxmox/ceph-osd-write.d.ts +10 -13
  289. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  290. package/dist/proxmox/index.js +806 -740
  291. package/dist/proxmox/index.js.map +9 -8
  292. package/dist/release/fake-engine.d.ts +10 -0
  293. package/dist/release/fake-engine.d.ts.map +1 -1
  294. package/dist/release/index.js +35 -1
  295. package/dist/release/index.js.map +3 -3
  296. package/dist/valkey/acl-attrs.d.ts +94 -0
  297. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  298. package/dist/valkey/acl-compare.d.ts +28 -0
  299. package/dist/valkey/acl-compare.d.ts.map +1 -0
  300. package/dist/valkey/acl-connection.d.ts +6 -0
  301. package/dist/valkey/acl-connection.d.ts.map +1 -0
  302. package/dist/valkey/acl-form.d.ts +62 -0
  303. package/dist/valkey/acl-form.d.ts.map +1 -0
  304. package/dist/valkey/acl-managed.d.ts +4 -0
  305. package/dist/valkey/acl-managed.d.ts.map +1 -0
  306. package/dist/valkey/acl-ops.d.ts +37 -0
  307. package/dist/valkey/acl-ops.d.ts.map +1 -0
  308. package/dist/valkey/acl-profiles.d.ts +53 -0
  309. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  310. package/dist/valkey/acl-reconcile.d.ts +26 -0
  311. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  312. package/dist/valkey/acl.d.ts +24 -0
  313. package/dist/valkey/acl.d.ts.map +1 -0
  314. package/dist/valkey/connection-service.d.ts +22 -0
  315. package/dist/valkey/connection-service.d.ts.map +1 -0
  316. package/dist/valkey/connection.d.ts +59 -0
  317. package/dist/valkey/connection.d.ts.map +1 -0
  318. package/dist/valkey/errors.d.ts +165 -0
  319. package/dist/valkey/errors.d.ts.map +1 -0
  320. package/dist/valkey/fake-acl.d.ts +29 -0
  321. package/dist/valkey/fake-acl.d.ts.map +1 -0
  322. package/dist/valkey/fake-valkey.d.ts +35 -0
  323. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  324. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  325. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  326. package/dist/valkey/index.d.ts +18 -0
  327. package/dist/valkey/index.d.ts.map +1 -0
  328. package/dist/valkey/index.js +964 -0
  329. package/dist/valkey/index.js.map +27 -0
  330. package/dist/valkey/instance-attrs.d.ts +46 -0
  331. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  332. package/dist/valkey/instance-form.d.ts +22 -0
  333. package/dist/valkey/instance-form.d.ts.map +1 -0
  334. package/dist/valkey/instance.d.ts +48 -0
  335. package/dist/valkey/instance.d.ts.map +1 -0
  336. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  337. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  338. package/dist/valkey/providers.d.ts +16 -0
  339. package/dist/valkey/providers.d.ts.map +1 -0
  340. package/dist/valkey/resp.d.ts +38 -0
  341. package/dist/valkey/resp.d.ts.map +1 -0
  342. package/dist/valkey/transport-error.d.ts +36 -0
  343. package/dist/valkey/transport-error.d.ts.map +1 -0
  344. package/dist/valkey/transport.d.ts +50 -0
  345. package/dist/valkey/transport.d.ts.map +1 -0
  346. package/docs/litellm-access-group.md +105 -0
  347. package/docs/litellm-budget.md +55 -0
  348. package/docs/litellm-credential.md +60 -0
  349. package/docs/litellm-key-metadata.md +56 -0
  350. package/docs/litellm-key.md +194 -0
  351. package/docs/litellm-mcp.md +187 -0
  352. package/docs/litellm-model.md +111 -0
  353. package/docs/litellm-policy-attachment.md +86 -0
  354. package/docs/litellm-policy.md +103 -0
  355. package/docs/litellm-team.md +117 -0
  356. package/docs/litellm-tool-policy.md +90 -0
  357. package/docs/litellm-toolset.md +92 -0
  358. package/docs/litellm.md +16 -8
  359. package/docs/peers.md +15 -6
  360. package/docs/postgres-grants-example.md +55 -0
  361. package/docs/postgres-grants.md +157 -0
  362. package/docs/postgres-role.md +114 -0
  363. package/docs/postgres-schema.md +139 -0
  364. package/docs/postgres.md +39 -0
  365. package/docs/proxmox-sdk-migration.md +45 -0
  366. package/docs/valkey-transport.md +29 -0
  367. package/docs/valkey.md +187 -0
  368. package/package.json +7 -3
  369. package/src/litellm/access-group-form.ts +105 -0
  370. package/src/litellm/access-group-operations.ts +92 -0
  371. package/src/litellm/access-group-types.ts +49 -0
  372. package/src/litellm/access-group.test.ts +197 -0
  373. package/src/litellm/access-group.ts +151 -0
  374. package/src/litellm/budget-errors.ts +38 -0
  375. package/src/litellm/budget-form.ts +94 -0
  376. package/src/litellm/budget-operations.ts +51 -0
  377. package/src/litellm/budget.test.ts +105 -0
  378. package/src/litellm/budget.ts +127 -0
  379. package/src/litellm/credential-errors.ts +183 -0
  380. package/src/litellm/credential-form.test.ts +151 -0
  381. package/src/litellm/credential-form.ts +211 -0
  382. package/src/litellm/credential-guards.test.ts +241 -0
  383. package/src/litellm/credential-operations.ts +208 -0
  384. package/src/litellm/credential-recovery.test.ts +93 -0
  385. package/src/litellm/credential-rewrite.test.ts +164 -0
  386. package/src/litellm/credential-rewrite.ts +50 -0
  387. package/src/litellm/credential-types.ts +117 -0
  388. package/src/litellm/credential-values.ts +94 -0
  389. package/src/litellm/credential.test.ts +218 -0
  390. package/src/litellm/credential.ts +227 -0
  391. package/src/litellm/fake-access-group-litellm.ts +127 -0
  392. package/src/litellm/fake-credential-litellm.ts +216 -0
  393. package/src/litellm/fake-keys.ts +190 -0
  394. package/src/litellm/fake-litellm.ts +66 -7
  395. package/src/litellm/fake-mcp-litellm.ts +214 -0
  396. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  397. package/src/litellm/fake-model-litellm.ts +233 -0
  398. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  399. package/src/litellm/fake-policy-litellm.ts +186 -0
  400. package/src/litellm/fake-registry-base.ts +69 -0
  401. package/src/litellm/fake-stack.ts +17 -1
  402. package/src/litellm/fake-team-litellm.ts +229 -0
  403. package/src/litellm/fake-tool-litellm.ts +88 -0
  404. package/src/litellm/fake-toolset-litellm.ts +110 -0
  405. package/src/litellm/index.ts +153 -0
  406. package/src/litellm/key-delete.test.ts +212 -0
  407. package/src/litellm/key-errors.ts +241 -0
  408. package/src/litellm/key-form.ts +200 -0
  409. package/src/litellm/key-harness.ts +121 -0
  410. package/src/litellm/key-metadata-errors.ts +49 -0
  411. package/src/litellm/key-metadata.test.ts +156 -0
  412. package/src/litellm/key-metadata.ts +74 -0
  413. package/src/litellm/key-operations.ts +184 -0
  414. package/src/litellm/key-rotation.test.ts +110 -0
  415. package/src/litellm/key-scope.test.ts +135 -0
  416. package/src/litellm/key-secret.test.ts +248 -0
  417. package/src/litellm/key-secret.ts +137 -0
  418. package/src/litellm/key-transport.test.ts +166 -0
  419. package/src/litellm/key.test.ts +231 -0
  420. package/src/litellm/key.ts +213 -0
  421. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  422. package/src/litellm/mcp-server-alias.test.ts +184 -0
  423. package/src/litellm/mcp-server-credential.ts +85 -0
  424. package/src/litellm/mcp-server-errors.ts +141 -0
  425. package/src/litellm/mcp-server-exports.test.ts +15 -0
  426. package/src/litellm/mcp-server-form.test.ts +241 -0
  427. package/src/litellm/mcp-server-form.ts +233 -0
  428. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  429. package/src/litellm/mcp-server-locate.ts +66 -0
  430. package/src/litellm/mcp-server-operations.ts +111 -0
  431. package/src/litellm/mcp-server-registry.test.ts +146 -0
  432. package/src/litellm/mcp-server-retype.test.ts +114 -0
  433. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  434. package/src/litellm/mcp-server-shadow.ts +45 -0
  435. package/src/litellm/mcp-server-tools.test.ts +85 -0
  436. package/src/litellm/mcp-server-types.ts +143 -0
  437. package/src/litellm/mcp-server-url.ts +66 -0
  438. package/src/litellm/mcp-server.test.ts +213 -0
  439. package/src/litellm/mcp-server.ts +225 -0
  440. package/src/litellm/model-claim.ts +69 -0
  441. package/src/litellm/model-credential.ts +45 -0
  442. package/src/litellm/model-errors.ts +168 -0
  443. package/src/litellm/model-foreign-row.test.ts +173 -0
  444. package/src/litellm/model-form.test.ts +228 -0
  445. package/src/litellm/model-form.ts +223 -0
  446. package/src/litellm/model-locate.test.ts +128 -0
  447. package/src/litellm/model-locate.ts +67 -0
  448. package/src/litellm/model-operations.test.ts +93 -0
  449. package/src/litellm/model-operations.ts +146 -0
  450. package/src/litellm/model-params-preserve.test.ts +119 -0
  451. package/src/litellm/model-recovery.test.ts +57 -0
  452. package/src/litellm/model-types.ts +112 -0
  453. package/src/litellm/model.test.ts +222 -0
  454. package/src/litellm/model.ts +237 -0
  455. package/src/litellm/operations.ts +1 -1
  456. package/src/litellm/policy-attachment-form.ts +103 -0
  457. package/src/litellm/policy-attachment-operations.ts +74 -0
  458. package/src/litellm/policy-attachment-types.ts +48 -0
  459. package/src/litellm/policy-attachment.test.ts +198 -0
  460. package/src/litellm/policy-attachment.ts +178 -0
  461. package/src/litellm/policy-form.ts +122 -0
  462. package/src/litellm/policy-operations.ts +112 -0
  463. package/src/litellm/policy-types.ts +58 -0
  464. package/src/litellm/policy.test.ts +214 -0
  465. package/src/litellm/policy.ts +146 -0
  466. package/src/litellm/providers.ts +25 -1
  467. package/src/litellm/registry-errors.ts +88 -0
  468. package/src/litellm/registry-exports.test.ts +66 -0
  469. package/src/litellm/registry-support.ts +109 -0
  470. package/src/litellm/team-form.ts +140 -0
  471. package/src/litellm/team-members.test.ts +150 -0
  472. package/src/litellm/team-members.ts +58 -0
  473. package/src/litellm/team-operations.ts +90 -0
  474. package/src/litellm/team-permission.ts +159 -0
  475. package/src/litellm/team-read.test.ts +89 -0
  476. package/src/litellm/team-types.ts +106 -0
  477. package/src/litellm/team.test.ts +206 -0
  478. package/src/litellm/team.ts +159 -0
  479. package/src/litellm/tool-policy-form.ts +94 -0
  480. package/src/litellm/tool-policy-operations.ts +58 -0
  481. package/src/litellm/tool-policy-types.ts +48 -0
  482. package/src/litellm/tool-policy.test.ts +172 -0
  483. package/src/litellm/tool-policy.ts +125 -0
  484. package/src/litellm/toolset-form.ts +104 -0
  485. package/src/litellm/toolset-operations.ts +90 -0
  486. package/src/litellm/toolset-types.ts +47 -0
  487. package/src/litellm/toolset.test.ts +198 -0
  488. package/src/litellm/toolset.ts +151 -0
  489. package/src/openbao/auth-role-form.test.ts +37 -0
  490. package/src/openbao/auth-role-form.ts +12 -1
  491. package/src/openbao/auth-role-wire.test.ts +1 -0
  492. package/src/openbao/auth-role-wire.ts +13 -1
  493. package/src/openbao/distilled-retry.test.ts +1 -0
  494. package/src/openbao/fake-engines-roles.ts +7 -1
  495. package/src/ownership/ownership.test.ts +33 -1
  496. package/src/ownership/rows.ts +53 -0
  497. package/src/postgres/connection.ts +72 -3
  498. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  499. package/src/postgres/database-refusals.test.ts +2 -2
  500. package/src/postgres/database-sql.ts +40 -12
  501. package/src/postgres/database.ts +3 -2
  502. package/src/postgres/fake-grants-parse.ts +189 -0
  503. package/src/postgres/fake-grants-read.ts +250 -0
  504. package/src/postgres/fake-grants-sql.ts +207 -0
  505. package/src/postgres/fake-grants-write.ts +90 -0
  506. package/src/postgres/fake-role-sql.ts +220 -0
  507. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  508. package/src/postgres/fake-schema-sql.ts +146 -0
  509. package/src/postgres/fake-sql-parse.ts +86 -0
  510. package/src/postgres/fake-sql-quote.ts +36 -0
  511. package/src/postgres/fake-sql.ts +161 -46
  512. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  513. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  514. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  515. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  516. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  517. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  518. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  519. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  520. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  521. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  522. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  523. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  524. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  525. package/src/postgres/grants-acl.test.ts +61 -0
  526. package/src/postgres/grants-attrs.ts +185 -0
  527. package/src/postgres/grants-database.test.ts +158 -0
  528. package/src/postgres/grants-declare.ts +172 -0
  529. package/src/postgres/grants-delete-proof.test.ts +154 -0
  530. package/src/postgres/grants-diff.ts +101 -0
  531. package/src/postgres/grants-errors.ts +215 -0
  532. package/src/postgres/grants-existence-guards.test.ts +113 -0
  533. package/src/postgres/grants-existence.ts +101 -0
  534. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  535. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  536. package/src/postgres/grants-lifecycle.test.ts +181 -0
  537. package/src/postgres/grants-ops.ts +229 -0
  538. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  539. package/src/postgres/grants-ownership.ts +49 -0
  540. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  541. package/src/postgres/grants-plan-diff.test.ts +204 -0
  542. package/src/postgres/grants-plan-restore.ts +43 -0
  543. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  544. package/src/postgres/grants-plan.test.ts +148 -0
  545. package/src/postgres/grants-plan.ts +245 -0
  546. package/src/postgres/grants-provenance.test.ts +134 -0
  547. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  548. package/src/postgres/grants-read-absent.test.ts +91 -0
  549. package/src/postgres/grants-read-defaults.ts +51 -0
  550. package/src/postgres/grants-read.ts +250 -0
  551. package/src/postgres/grants-refusals.test.ts +136 -0
  552. package/src/postgres/grants-refuse.ts +119 -0
  553. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  554. package/src/postgres/grants-repair-plan.test.ts +201 -0
  555. package/src/postgres/grants-sql.test.ts +128 -0
  556. package/src/postgres/grants-sql.ts +144 -0
  557. package/src/postgres/grants-words.ts +34 -0
  558. package/src/postgres/grants.ts +110 -0
  559. package/src/postgres/index.ts +137 -7
  560. package/src/postgres/installed-classifier.ts +36 -0
  561. package/src/postgres/providers.ts +44 -7
  562. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  563. package/src/postgres/psql-executor.test.ts +167 -0
  564. package/src/postgres/psql-executor.ts +170 -0
  565. package/src/postgres/role-adopt.test.ts +98 -0
  566. package/src/postgres/role-attrs.ts +134 -0
  567. package/src/postgres/role-diff.ts +89 -0
  568. package/src/postgres/role-errors.ts +198 -0
  569. package/src/postgres/role-guard.test.ts +60 -0
  570. package/src/postgres/role-identity.test.ts +185 -0
  571. package/src/postgres/role-lifecycle.test.ts +228 -0
  572. package/src/postgres/role-membership-sql.ts +198 -0
  573. package/src/postgres/role-password.test.ts +158 -0
  574. package/src/postgres/role-privilege.test.ts +80 -0
  575. package/src/postgres/role-provenance.test.ts +200 -0
  576. package/src/postgres/role-provider.ts +104 -0
  577. package/src/postgres/role-refusals.test.ts +250 -0
  578. package/src/postgres/role-scram.test.ts +38 -0
  579. package/src/postgres/role-scram.ts +38 -0
  580. package/src/postgres/role-secrets.ts +46 -0
  581. package/src/postgres/role-sql.ts +211 -0
  582. package/src/postgres/role-valid-until.test.ts +68 -0
  583. package/src/postgres/role.ts +218 -0
  584. package/src/postgres/schema-assert.ts +139 -0
  585. package/src/postgres/schema-attrs.ts +71 -0
  586. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  587. package/src/postgres/schema-delete.ts +96 -0
  588. package/src/postgres/schema-diff.ts +44 -0
  589. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  590. package/src/postgres/schema-errors.ts +209 -0
  591. package/src/postgres/schema-handlers.test.ts +193 -0
  592. package/src/postgres/schema-identity.ts +39 -0
  593. package/src/postgres/schema-lifecycle.test.ts +246 -0
  594. package/src/postgres/schema-race.test.ts +70 -0
  595. package/src/postgres/schema-reconcile.ts +100 -0
  596. package/src/postgres/schema-refusals.test.ts +144 -0
  597. package/src/postgres/schema-socket.test.ts +161 -0
  598. package/src/postgres/schema-sql.ts +130 -0
  599. package/src/postgres/schema-takeover.test.ts +149 -0
  600. package/src/postgres/schema-test-kit.ts +85 -0
  601. package/src/postgres/schema.ts +130 -0
  602. package/src/proxmox/ceph-flag-types.ts +70 -0
  603. package/src/proxmox/ceph-flag-wire.test.ts +72 -0
  604. package/src/proxmox/ceph-flag-wire.ts +123 -0
  605. package/src/proxmox/ceph-flag.ts +77 -130
  606. package/src/proxmox/ceph-osd-write.ts +10 -13
  607. package/src/release/binary-alias.test.ts +3 -3
  608. package/src/release/binary-claims.test.ts +3 -3
  609. package/src/release/binary-directory-umask.test.ts +65 -0
  610. package/src/release/fake-engine.ts +15 -0
  611. package/src/valkey/acl-attrs.ts +98 -0
  612. package/src/valkey/acl-compare.ts +57 -0
  613. package/src/valkey/acl-connection.ts +13 -0
  614. package/src/valkey/acl-delete.test.ts +127 -0
  615. package/src/valkey/acl-form.ts +239 -0
  616. package/src/valkey/acl-managed.ts +13 -0
  617. package/src/valkey/acl-ops.ts +122 -0
  618. package/src/valkey/acl-ownership.test.ts +118 -0
  619. package/src/valkey/acl-prefix.test.ts +38 -0
  620. package/src/valkey/acl-profiles.ts +81 -0
  621. package/src/valkey/acl-reconcile.ts +180 -0
  622. package/src/valkey/acl-reserved.test.ts +69 -0
  623. package/src/valkey/acl-revocation.test.ts +94 -0
  624. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  625. package/src/valkey/acl-validation.test.ts +54 -0
  626. package/src/valkey/acl.test.ts +215 -0
  627. package/src/valkey/acl.ts +169 -0
  628. package/src/valkey/connection-service.ts +46 -0
  629. package/src/valkey/connection.ts +180 -0
  630. package/src/valkey/errors.ts +239 -0
  631. package/src/valkey/fake-acl.ts +128 -0
  632. package/src/valkey/fake-valkey.ts +224 -0
  633. package/src/valkey/handler-test-fixture.ts +43 -0
  634. package/src/valkey/handlers.test.ts +205 -0
  635. package/src/valkey/index.ts +54 -0
  636. package/src/valkey/instance-attrs.ts +48 -0
  637. package/src/valkey/instance-form.ts +104 -0
  638. package/src/valkey/instance.test.ts +108 -0
  639. package/src/valkey/instance.ts +160 -0
  640. package/src/valkey/provider-socket-fixture.ts +71 -0
  641. package/src/valkey/providers.test.ts +129 -0
  642. package/src/valkey/providers.ts +21 -0
  643. package/src/valkey/resp.ts +212 -0
  644. package/src/valkey/transport-bounds.test.ts +150 -0
  645. package/src/valkey/transport-error.ts +37 -0
  646. package/src/valkey/transport.test.ts +158 -0
  647. package/src/valkey/transport.ts +109 -0
  648. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * `Postgres.Schema` — create-and-assert over one schema in a self-hosted PostgreSQL 18 cluster.
3
+ *
4
+ * ★ A SCHEMA CARRIES NO OWNERSHIP MARK, exactly like `Postgres.Database` (H1). `read` answers
5
+ * `Unowned` for a match, so every already-live schema needs `adopt(true)` in the stack that
6
+ * declares it.
7
+ * ⛔ NOT ALTER-CAPABLE. `ALTER SCHEMA` only renames or changes owner in Postgres; neither is
8
+ * implemented. An owner or comment mismatch against the live row is a typed
9
+ * `PostgresSchemaDrift` refusal, never an `ALTER SCHEMA` — same rule `Postgres.Database`
10
+ * applies to its own asserted props. An omitted `owner` is compared to `current_user`
11
+ * (`schema-assert.ts`): a fresh create without `AUTHORIZATION` would be owned by that role.
12
+ * ⛔ THE DECLARED `database` IS PROVEN, NEVER ASSUMED. The family connection points at a
13
+ * maintenance database (a brand-new database cannot be connected to on a cold plan —
14
+ * `docs/postgres.md#measured-path`), so the handlers below open `props.database` themselves
15
+ * (`withPg`'s database override) and `reconcile`/`drop` first compare `current_database()`
16
+ * against it (`PostgresSchemaWrongDatabase`). Without that proof a seat's schema would be
17
+ * created in whatever database the single connection happened to target.
18
+ * ⛔ `delete` DROPS ONLY WHAT IT CAN PROVE IT CREATED. Before any `DROP`, the handler re-reads
19
+ * the live row (`schema-delete.ts`) and matches it against the persisted `oid` + `owner`;
20
+ * a mismatch is `PostgresSchemaDeleteForeignRefused`, never a `DROP` (`CASCADE` included) —
21
+ * the schema under that name may be another role's, recreated out of band.
22
+ * ⛔ A MISSING DECLARED DATABASE IS "SCHEMA ABSENT" IN `read` AND `delete`. Both probe
23
+ * `pg_database` over the family connection first (`database-sql.ts#databaseExists`), so a
24
+ * cold plan against a not-yet-created database plans a create (`read` answers absent)
25
+ * instead of failing untyped on connect (`ConnectionError` over psql, `UnknownError` 3D000
26
+ * over the socket), and a delete of a dropped database stays idempotent. `reconcile` still
27
+ * requires the database to exist — that ordering is the declaration's job (`docs/
28
+ * postgres-schema.md`: pass `database: db.name`).
29
+ * ⛔ `delete` DROPS ONLY WHEN SAFE. `DROP SCHEMA` refuses a non-empty schema unless
30
+ * `cascade: true` is declared (`PostgresSchemaDropNotEmptyError`); the `cascade` prop
31
+ * defaults to `false` (retain policy by default — see `defaultRemovalPolicy: 'retain'`).
32
+ */
33
+ import { Resource } from 'alchemy';
34
+ import { Unowned } from 'alchemy/AdoptPolicy';
35
+ import * as Provider from 'alchemy/Provider';
36
+ import * as Effect from 'effect/Effect';
37
+ import { type PostgresSchemaAttributes, type PostgresSchemaProps } from './schema-attrs.ts';
38
+ import { schemaNameByteRefusal } from './schema-attrs.ts';
39
+ import { diffPostgresSchema } from './schema-diff.ts';
40
+ import { selectSchema } from './schema-sql.ts';
41
+ import { databaseExists } from './database-sql.ts';
42
+ import { reconcileWithClient } from './schema-reconcile.ts';
43
+ import { assertSchemaIdentity, assertSchemaTarget } from './schema-identity.ts';
44
+ import { deleteWithClient, dropWithClient } from './schema-delete.ts';
45
+ import type { PgExecutor } from './database-sql.ts';
46
+ import { type PostgresConnection, withPg } from './connection.ts';
47
+ import { PostgresSchemaNameRefused, PostgresSchemaWrongDatabase } from './schema-errors.ts';
48
+
49
+ export interface PostgresSchema extends Resource<
50
+ 'Postgres.Schema',
51
+ PostgresSchemaProps,
52
+ PostgresSchemaAttributes,
53
+ never,
54
+ PostgresConnection
55
+ > {}
56
+
57
+ export const PostgresSchema = Resource<PostgresSchema>('Postgres.Schema', {
58
+ defaultRemovalPolicy: 'retain',
59
+ });
60
+
61
+ /** Same dual `typeof` guard as `isPostgresDatabase` (see `database.ts`): a resource constructor
62
+ * is a callable `Object.assign`ed function, not a plain object. */
63
+ export const isPostgresSchema = (value: unknown): value is PostgresSchema =>
64
+ (typeof value === 'object' || typeof value === 'function') &&
65
+ value !== null &&
66
+ (value as { Type?: unknown }).Type === 'Postgres.Schema';
67
+
68
+ /** The core of `read`: one bound `SELECT`, no ownership branding. */
69
+ export const readWithClient = (pg: PgExecutor, name: string) => selectSchema(pg, name);
70
+
71
+ export { diffPostgresSchema } from './schema-diff.ts';
72
+ export { reconcileWithClient } from './schema-reconcile.ts';
73
+ export { deleteWithClient, dropWithClient };
74
+
75
+ /** The five lifecycle handlers. Exported on its own so a test drives the real implementation. */
76
+ export const postgresSchemaHandlers = PostgresSchema.Provider.of({
77
+ // ⚠️ EMPTY, NOT A LIVE SWEEP — same reasoning as Postgres.Database's list.
78
+ list: () => Effect.succeed([]),
79
+ nuke: { skip: true },
80
+
81
+ read: ({ olds, output }) =>
82
+ Effect.gen(function* () {
83
+ const name = output?.name ?? olds.name;
84
+ const database = output?.database ?? olds.database;
85
+ // A declared database that does not exist IS a schema absent. The probe runs over the
86
+ // FAMILY connection (no override), which points at the maintenance database a cold plan
87
+ // can always reach — opening the declared database instead would fail untyped
88
+ // (`ConnectionError` over psql, `UnknownError` 3D000 over the socket).
89
+ const present = yield* withPg((pg) => databaseExists(pg, database));
90
+ if (!present) return undefined;
91
+ const live = yield* withPg((pg) => readWithClient(pg, name), database);
92
+ if (live !== undefined && live.database !== database) {
93
+ return yield* Effect.fail(
94
+ new PostgresSchemaWrongDatabase({
95
+ schema: name,
96
+ declared: database,
97
+ connected: live.database,
98
+ }),
99
+ );
100
+ }
101
+ if (live !== undefined && output !== undefined) yield* assertSchemaIdentity(live, output);
102
+ return live === undefined ? undefined : Unowned(live);
103
+ }),
104
+
105
+ diff: ({ news, output, olds }) => diffPostgresSchema(news, output, olds),
106
+
107
+ reconcile: ({ news, output }) =>
108
+ Effect.gen(function* () {
109
+ const nameRefusal = schemaNameByteRefusal(news.name);
110
+ if (nameRefusal !== undefined) {
111
+ return yield* Effect.fail(
112
+ new PostgresSchemaNameRefused({ name: news.name, ...nameRefusal }),
113
+ );
114
+ }
115
+ yield* assertSchemaTarget(news, output);
116
+ return yield* withPg((pg) => reconcileWithClient(pg, news, output), news.database);
117
+ }),
118
+
119
+ delete: ({ olds, output }) =>
120
+ Effect.gen(function* () {
121
+ // Same missing-database rule as `read`: an absent database is an absent schema, and the
122
+ // delete stays idempotent instead of failing untyped on connect.
123
+ const present = yield* withPg((pg) => databaseExists(pg, olds.database));
124
+ if (!present) return undefined;
125
+ return yield* withPg((pg) => deleteWithClient(pg, olds, output), olds.database);
126
+ }),
127
+ });
128
+
129
+ export const PostgresSchemaProvider = () =>
130
+ Provider.succeed(PostgresSchema, postgresSchemaHandlers);
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Flag names, props and attributes for `Proxmox.CephFlag`.
3
+ *
4
+ * ★ SPLIT OUT OF ceph-flag.ts FOR THE 250-LINE CAP. The comments stay with the types they
5
+ * explain; nothing here talks to the cluster.
6
+ */
7
+ import type { WithTarget } from './resource-spec.ts';
8
+
9
+ /**
10
+ * PVE's eleven flags, spelled as `PVE::Ceph::Tools::get_possible_osd_flags` spells them.
11
+ *
12
+ * ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both
13
+ * the GET and the PUT declare `additionalProperties => 0` over exactly this list.
14
+ * ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on C1:
15
+ * `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.
16
+ * PVE reads that same string and answers only about its own eleven, so the others can neither
17
+ * leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.
18
+ * ⛔ IF SOMEBODY LATER "IMPROVES" THE READ BY PARSING `osd dump` DIRECTLY, those four become
19
+ * permanently-set flags that nothing declares: a forever-diff on a brand new cluster.
20
+ */
21
+ export type CephFlagName =
22
+ | 'nobackfill'
23
+ | 'nodeep-scrub'
24
+ | 'nodown'
25
+ | 'noin'
26
+ | 'noout'
27
+ | 'norebalance'
28
+ | 'norecover'
29
+ | 'noscrub'
30
+ | 'notieragent'
31
+ | 'noup'
32
+ | 'pause';
33
+
34
+ export interface CephFlagProps extends WithTarget {
35
+ /**
36
+ * Which flag. PVE's primary key here, and the last segment of the path.
37
+ *
38
+ * ⚠️ EDITING IT IN PLACE ORPHANS THE OLD FLAG RATHER THAN MOVING ANYTHING. `diff` reads the NEW
39
+ * path and reconcile writes it, while the old flag keeps whatever this stack last put there.
40
+ * There is deliberately no replace override for it — acl.ts needs one because its `delete`
41
+ * removes a real grant, and `delete` here is inert by design, so a replace would do exactly
42
+ * what an update already does. Declare a second resource and set the old one to `value: false`
43
+ * rather than renaming this one.
44
+ * ⛔ TWO RESOURCES DECLARING THE SAME FLAG ARE ONE CLUSTER OBJECT, and Alchemy sees two ids
45
+ * rather than a collision — the acl.ts hazard exactly. Disagreeing, they take turns winning
46
+ * and BOTH plan `update` for ever; agreeing, deleting either leaves the flag where the other
47
+ * put it. One declaration per flag per cluster.
48
+ */
49
+ flag: CephFlagName;
50
+ /**
51
+ * Set the flag (`true`) or clear it (`false`).
52
+ *
53
+ * ⛔ REQUIRED, WITH NO DEFAULT — see the second ⛔ in the header, which is the whole safety
54
+ * argument for this family. It is also the only field PVE accepts on this endpoint, and it is
55
+ * not optional in the schema either: omitting it from the form is a 400, not an untouched
56
+ * flag. (The BULK endpoint is the one where omission means "leave it alone"; this is not it.)
57
+ */
58
+ value: boolean;
59
+ }
60
+
61
+ export interface CephFlagAttributes {
62
+ /**
63
+ * ⚠️ REPORTED, NEVER COMPARED. It is the path key the read was made WITH, copied back out of
64
+ * props, so comparing it against props would be true by construction — the same reasoning
65
+ * acl.ts gives for the four identity fields it also declines to diff.
66
+ */
67
+ flag: CephFlagName;
68
+ /** Whether ceph has the flag set right now. The only field `matches` looks at. */
69
+ value: boolean;
70
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pins the two Opus findings on the CephFlag transport (kit PR 319, 2026-09-28).
3
+ *
4
+ * 1. A `{"data":null}` body becomes `{}` inside distilled's `transformResponse` before the
5
+ * provider sees it. Guarding only `null`/`undefined` lets `bool({})` read a missing answer
6
+ * as "flag clear".
7
+ * 2. A refused mint must not fail `diff`. Alchemy fails the whole plan if one resource's
8
+ * `diff` fails (unreadable-read.ts).
9
+ */
10
+ import { describe, expect, test } from 'bun:test';
11
+ import * as Effect from 'effect/Effect';
12
+ import * as Layer from 'effect/Layer';
13
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
14
+ import { engineOver } from '../verify/fake-engine.ts';
15
+ import { scalarFlagOrDie } from './ceph-flag-wire.ts';
16
+ import { ProxmoxCephFlag, ProxmoxCephFlagProvider } from './ceph-flag.ts';
17
+ import { FAKE_TARGET, withoutBao } from './fake-pve.ts';
18
+
19
+ const flagProps = { flag: 'noout' as const, target: FAKE_TARGET, value: true };
20
+
21
+ describe('CephFlag GET of no data dies instead of reading clear', () => {
22
+ test('the post-transform empty object dies', async () => {
23
+ // distilled protocol.ts maps `{"data":null}` to `{}` before a RawResponseRoot decode.
24
+ await expect(Effect.runPromise(scalarFlagOrDie({}, flagProps))).rejects.toThrow(
25
+ /not a bare boolean/,
26
+ );
27
+ await expect(Effect.runPromise(scalarFlagOrDie(null, flagProps))).rejects.toThrow(
28
+ /not a bare boolean/,
29
+ );
30
+ });
31
+
32
+ test('a real scalar still decodes', async () => {
33
+ expect(await Effect.runPromise(scalarFlagOrDie(1, flagProps))).toBe(1);
34
+ expect(await Effect.runPromise(scalarFlagOrDie(false, flagProps))).toBe(false);
35
+ });
36
+ });
37
+
38
+ describe('a refused CephFlag read does not abort the plan', () => {
39
+ test('verify after a 403 mint reports noop, not a failed plan', async () => {
40
+ let refuse = false;
41
+ const stub = async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {
42
+ const request =
43
+ input instanceof Request ? new Request(input, init) : new Request(String(input), init);
44
+ const url = new URL(request.url);
45
+ if (url.pathname.includes('/creds/')) {
46
+ if (refuse) {
47
+ return Response.json({ errors: ['status 403'] }, { status: 403 });
48
+ }
49
+ return Response.json({
50
+ data: { secret: 'fake-secret-not-real', token_id: 'hf-test@pve!fake' },
51
+ lease_duration: 0,
52
+ });
53
+ }
54
+ return Response.json({ data: 1 });
55
+ };
56
+ const layer = FetchHttpClient.layer.pipe(
57
+ Layer.provideMerge(
58
+ Layer.succeed(
59
+ FetchHttpClient.Fetch,
60
+ Object.assign(stub, { preconnect: globalThis.fetch.preconnect }) as typeof fetch,
61
+ ),
62
+ ),
63
+ );
64
+ const declare = () => ProxmoxCephFlag('noout', flagProps);
65
+ await withoutBao(async () => {
66
+ const engine = engineOver(ProxmoxCephFlagProvider().pipe(Layer.provideMerge(layer)));
67
+ expect(await engine.deploy(declare())).toEqual({ noout: 'adopted' });
68
+ refuse = true;
69
+ await expect(engine.verify(declare(), { all: true })).resolves.toBeDefined();
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,123 @@
1
+ /**
2
+ * `Proxmox.CephFlag`'s distilled wire lane: the read and the single-flag write.
3
+ *
4
+ * ★ SPLIT OUT OF ceph-flag.ts FOR THE 250-LINE CAP, mirroring ceph-pool-wire.ts's split by "touches
5
+ * the cluster or not" — ceph-flag.ts keeps the header, the props/attributes shapes, `matches` and
6
+ * the provider wiring; this file is the only place that calls `runPve` for this family.
7
+ *
8
+ * ⛔ NEITHER OPERATION HERE DECLARES A TYPED ABSENCE (`errors: []` on both `getClusterCephFlag` and
9
+ * `putClusterCephFlag`, MEASURED in distilled-proxmox/src/services/cluster.ts) — UNLIKE
10
+ * ceph-pool-wire.ts's `CephPoolNotFound`. All eleven flags always exist (ceph-flag.ts's own ⛔ on
11
+ * `CephFlagName` and on the old unreachable create branch), so there is no absence case to fold
12
+ * here. `readClusterCephFlag` therefore has no `catchTag` at all and lets every failure — a
13
+ * refused credential, a timeout, cluster exhaustion — propagate to the caller exactly as it
14
+ * arrived, rather than inventing an "absent" this family does not have. Simpler than
15
+ * ceph-pool-wire.ts's case, not harder.
16
+ * ⚠️ THIS IS A BEHAVIOR CHANGE FROM THE PRE-MIGRATION FACTORY, AND A DELIBERATE ONE. The old
17
+ * `pveOperations.read` folded EVERY failure into `undefined` via `Effect.orElseSucceed`, so an
18
+ * expired lease or node-b being down looked identical to "the flag is absent" (which cannot
19
+ * happen — see ceph-flag.ts). Now such a failure propagates as itself: a caller sees the real
20
+ * error (an expired 300s lease, node-b down, ceph not configured) rather than a misleading
21
+ * "absent" that would have sent `diff`/`reconcile` down a create path this endpoint has never
22
+ * implemented. Same advice as the old comment gave, sharper because the failure is no longer
23
+ * disguised.
24
+ *
25
+ * ⛔ THE PER-FLAG PUT IS SYNCHRONOUS (`$rados->mon_command` inline, MEASURED in Ceph.pm — see
26
+ * ceph-flag.ts's header), which is what makes reading back immediately after
27
+ * `writeClusterCephFlag` fair rather than a race against a worker task, unlike ceph-pool.ts's
28
+ * forked-worker writes.
29
+ */
30
+ import * as cluster from '@distilled.cloud/proxmox/cluster';
31
+ import * as Effect from 'effect/Effect';
32
+ import type { CephFlagAttributes, CephFlagProps } from './ceph-flag.ts';
33
+ import { runPve } from './distilled-pve.ts';
34
+ import { readOrUnreadable } from './unreadable-read.ts';
35
+ import { bool, flag } from './values.ts';
36
+
37
+ /** The one endpoint this family writes, as `guardWrite` (distilled-guard.ts) keys it. */
38
+ export const CEPH_FLAG_UPDATE = 'pve:PUT /cluster/ceph/flags/{flag}';
39
+
40
+ /**
41
+ * ⚠️ `live` IS A BARE BOOLEAN HERE, NOT AN OBJECT, AND IT IS HANDED TO `bool` WHOLE ON PURPOSE.
42
+ * `get_flag` returns perl `1` or `0` under a `type => 'boolean'` return schema, so the wire is
43
+ * `{"data":0}` or `{"data":false}` depending on how the REST layer renders it — matching
44
+ * distilled's `GetClusterCephFlagResponse = unknown` (`S.Unknown.pipe(T.RawResponseRoot())`).
45
+ * `bool` accepts every one of those spellings, which is exactly why it is a shared coercion.
46
+ * ⚠️ THE SCALAR IS A MEASURED PROPERTY OF THIS PVE, NOT A PROMISE. A later version wrapping the
47
+ * answer in an object would make `bool` read a SET flag as false, and `value: false` would then
48
+ * plan `noop` over a live flag — silently. Re-measure `GET /cluster/ceph/flags/noout` after a
49
+ * major upgrade; cheaper than a second parse path.
50
+ */
51
+ const attributesOf = (live: unknown, props: CephFlagProps): CephFlagAttributes => ({
52
+ flag: props.flag,
53
+ value: bool(live),
54
+ });
55
+
56
+ /**
57
+ * ⛔ DIE ON THE POST-TRANSFORM SHAPE, NOT THE PRE-TRANSFORM ONE. `bool` treats a missing value as
58
+ * `false` because most callers hand it an optional PVE form field where that is correct — but
59
+ * here it would read an unmeasured "no answer" as "flag is clear". distilled's
60
+ * `transformResponse` maps a `{"data":null}` body to `{}` BEFORE this function sees it (a shape
61
+ * `get_flag`'s `type => 'boolean'` schema does not document and this PVE has never produced).
62
+ * Guarding only `null`/`undefined` lets that `{}` through, and `bool({})` is `false` — a plan
63
+ * of `noop` over a set brake, with no read-back guard, because `reconcile`'s guard only fires
64
+ * on a write. A success here is a scalar (`true`/`false`/`0`/`1`/`'0'`/`'1'`). Anything else,
65
+ * including the empty object the protocol substitutes for null, is the unmeasured shape.
66
+ */
67
+ export const scalarFlagOrDie = (live: unknown, props: CephFlagProps) => {
68
+ const scalar =
69
+ typeof live === 'boolean' || live === 0 || live === 1 || live === '0' || live === '1';
70
+ return scalar
71
+ ? Effect.succeed(live)
72
+ : Effect.die(
73
+ new Error(
74
+ `cluster/ceph/flags/${props.flag}: GET answered ${JSON.stringify(live)}, not a bare ` +
75
+ "boolean. This endpoint's success is a scalar; an empty object is what the protocol " +
76
+ 'substitutes for `{"data":null}`. Check `ceph osd dump | head -1` and the cluster ' +
77
+ 'log rather than trust a coerced `false`.',
78
+ ),
79
+ );
80
+ };
81
+
82
+ /**
83
+ * The live flag, as attributes.
84
+ *
85
+ * ⚠️ NO ABSENCE TAG, ON PURPOSE. Both operations declare `errors: []` (see the file header), so
86
+ * there is nothing to `catchTag` except `PveCredentialDenied`, and that one is caught by the
87
+ * caller that plans (`readClusterCephFlagForDiff`) — not here. `read` and `reconcile` must
88
+ * still see a refused mint as itself.
89
+ */
90
+ export const readClusterCephFlag = (props: CephFlagProps) =>
91
+ runPve(props.target, 'read', false, cluster.getClusterCephFlag({ flag: props.flag })).pipe(
92
+ Effect.flatMap((live) => scalarFlagOrDie(live, props)),
93
+ Effect.map((live) => attributesOf(live, props)),
94
+ );
95
+
96
+ /**
97
+ * The same read, with a refused mint turned into `UNREADABLE`.
98
+ *
99
+ * ⛔ `diff` MUST NOT FAIL ON A REFUSED READ. Alchemy runs every resource's `diff` under
100
+ * `Effect.exit` and fails the whole plan if one fails (unreadable-read.ts). A lane that cannot
101
+ * mint `read` would otherwise abort every other row, not just this flag. `read` and
102
+ * `reconcile` keep the propagating form above.
103
+ */
104
+ export const readClusterCephFlagForDiff = (props: CephFlagProps) =>
105
+ readOrUnreadable(readClusterCephFlag(props));
106
+
107
+ /**
108
+ * ⚠️ ONLY `value` GOES IN THE BODY. `flag` is already bound by the path and the PUT declares
109
+ * `additionalProperties => 0`, so a second copy in the form could only disagree with the URL.
110
+ * ⚠️ THE `?? '0'` IS UNREACHABLE, AND IS WRITTEN RATHER THAN CAST AWAY. `flag()` types its answer
111
+ * `string | undefined` because it serves OPTIONAL props, and `value` here is required, so the
112
+ * branch cannot be taken; a cast would claim a proof the type system has not made.
113
+ */
114
+ export const updateForm = (props: CephFlagProps) => ({ value: flag(props.value) ?? '0' });
115
+
116
+ /** The actual `putClusterCephFlag` call — `updateForm`'s own `value`, so the two cannot disagree. */
117
+ export const writeClusterCephFlag = (props: CephFlagProps) =>
118
+ runPve(
119
+ props.target,
120
+ 'provision',
121
+ true,
122
+ cluster.putClusterCephFlag({ flag: props.flag, value: updateForm(props).value }),
123
+ );
@@ -73,73 +73,24 @@
73
73
  * "Permission check failed (/, Sys.Modify)" on the first deploy, and widen deliberately.
74
74
  */
75
75
  import { Resource } from 'alchemy';
76
+ import { isResolved } from 'alchemy/Diff';
76
77
  import * as Provider from 'alchemy/Provider';
77
78
  import * as Effect from 'effect/Effect';
78
- import { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';
79
- import { bool, flag } from './values.ts';
79
+ import {
80
+ CEPH_FLAG_UPDATE,
81
+ readClusterCephFlag,
82
+ readClusterCephFlagForDiff,
83
+ updateForm,
84
+ writeClusterCephFlag,
85
+ } from './ceph-flag-wire.ts';
86
+ import { mintTier } from './credentials.ts';
87
+ import { UNREADABLE, unreadableWarning } from './unreadable-read.ts';
88
+ import { guardWrite } from './distilled-guard.ts';
89
+ import type { CephFlagAttributes, CephFlagProps } from './ceph-flag-types.ts';
90
+ import type { PveRequirements } from './resource-spec.ts';
91
+ import { formToSend } from './update-guard.ts';
80
92
 
81
- /**
82
- * PVE's eleven flags, spelled as `PVE::Ceph::Tools::get_possible_osd_flags` spells them.
83
- *
84
- * ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both
85
- * the GET and the PUT declare `additionalProperties => 0` over exactly this list.
86
- * ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on C1:
87
- * `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.
88
- * PVE reads that same string and answers only about its own eleven, so the others can neither
89
- * leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.
90
- * ⛔ IF SOMEBODY LATER "IMPROVES" THE READ BY PARSING `osd dump` DIRECTLY, those four become
91
- * permanently-set flags that nothing declares: a forever-diff on a brand new cluster.
92
- */
93
- export type CephFlagName =
94
- | 'nobackfill'
95
- | 'nodeep-scrub'
96
- | 'nodown'
97
- | 'noin'
98
- | 'noout'
99
- | 'norebalance'
100
- | 'norecover'
101
- | 'noscrub'
102
- | 'notieragent'
103
- | 'noup'
104
- | 'pause';
105
-
106
- export interface CephFlagProps extends WithTarget {
107
- /**
108
- * Which flag. PVE's primary key here, and the last segment of the path.
109
- *
110
- * ⚠️ EDITING IT IN PLACE ORPHANS THE OLD FLAG RATHER THAN MOVING ANYTHING. `diff` reads the NEW
111
- * path and reconcile writes it, while the old flag keeps whatever this stack last put there.
112
- * There is deliberately no replace override for it — acl.ts needs one because its `delete`
113
- * removes a real grant, and `delete` here is inert by design, so a replace would do exactly
114
- * what an update already does. Declare a second resource and set the old one to `value: false`
115
- * rather than renaming this one.
116
- * ⛔ TWO RESOURCES DECLARING THE SAME FLAG ARE ONE CLUSTER OBJECT, and Alchemy sees two ids
117
- * rather than a collision — the acl.ts hazard exactly. Disagreeing, they take turns winning
118
- * and BOTH plan `update` for ever; agreeing, deleting either leaves the flag where the other
119
- * put it. One declaration per flag per cluster.
120
- */
121
- flag: CephFlagName;
122
- /**
123
- * Set the flag (`true`) or clear it (`false`).
124
- *
125
- * ⛔ REQUIRED, WITH NO DEFAULT — see the second ⛔ in the header, which is the whole safety
126
- * argument for this family. It is also the only field PVE accepts on this endpoint, and it is
127
- * not optional in the schema either: omitting it from the form is a 400, not an untouched
128
- * flag. (The BULK endpoint is the one where omission means "leave it alone"; this is not it.)
129
- */
130
- value: boolean;
131
- }
132
-
133
- export interface CephFlagAttributes {
134
- /**
135
- * ⚠️ REPORTED, NEVER COMPARED. It is the path key the read was made WITH, copied back out of
136
- * props, so comparing it against props would be true by construction — the same reasoning
137
- * acl.ts gives for the four identity fields it also declines to diff.
138
- */
139
- flag: CephFlagName;
140
- /** Whether ceph has the flag set right now. The only field `matches` looks at. */
141
- value: boolean;
142
- }
93
+ export type { CephFlagAttributes, CephFlagName, CephFlagProps } from './ceph-flag-types.ts';
143
94
 
144
95
  export interface ProxmoxCephFlag extends Resource<
145
96
  'Proxmox.CephFlag',
@@ -151,86 +102,82 @@ export interface ProxmoxCephFlag extends Resource<
151
102
 
152
103
  export const ProxmoxCephFlag = Resource<ProxmoxCephFlag>('Proxmox.CephFlag');
153
104
 
154
- const handlers = pveHandlers<CephFlagProps, CephFlagAttributes>({
155
- /**
156
- * ⚠️ `live` IS A BARE BOOLEAN HERE, NOT AN OBJECT, AND IT IS HANDED TO `bool` WHOLE ON PURPOSE.
157
- * `get_flag` returns perl `1` or `0` under a `type => 'boolean'` return schema, so the wire is
158
- * `{"data":0}` or `{"data":false}` depending on how the REST layer renders it. `bool` accepts
159
- * every one of those spellings, which is exactly why it is a shared coercion. The factory
160
- * types this parameter `Record<string, unknown>` because every OTHER PVE read is an object;
161
- * reaching for `live['value']` here would answer undefined on every plan and diff `false`
162
- * forever against a set flag.
163
- * ⚠️ THE SCALAR IS A MEASURED PROPERTY OF THIS PVE, NOT A PROMISE. A later version wrapping
164
- * the answer in an object would make `bool` read a SET flag as false, and `value: false`
165
- * would then plan `noop` over a live flag — silently. Re-measure
166
- * `GET /cluster/ceph/flags/noout` after a major upgrade; cheaper than a second parse path.
167
- *
168
- * ⛔ IT NEVER ANSWERS undefined, WHICH MAKES THE CREATE BRANCH UNREACHABLE, AND ITS ERROR WILL
169
- * MISLEAD YOU. All eleven flags always exist; there is nothing for "absent" to mean. So a POST
170
- * to `collection` means the READ failed — an expired 300s lease, node-b down, or ceph simply not
171
- * configured on the cluster (every one of these four handlers opens with
172
- * `check_ceph_configured()`, which dies). Read the resulting "Method 'POST /cluster/ceph/flags'
173
- * not implemented" as "the read failed" and go and look at the credential, not at ceph. Same
174
- * shape as the unreachable POST in acl.ts, same advice.
175
- */
176
- attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
177
- collection: () => 'cluster/ceph/flags',
178
- /**
179
- * ⛔ UNREACHABLE — see the second ⛔ in `attributes`. It is written in the BULK endpoint's shape
180
- * (the flag name is the parameter, not `value`) rather than left empty, because that is the
181
- * only body `cluster/ceph/flags` has ever documented. Nothing sends it today.
182
- */
183
- createForm: (props) => ({ [props.flag]: flag(props.value) ?? '0' }),
184
- endpoint: { update: 'pve:PUT /cluster/ceph/flags/{flag}' }, // ⛔ No create: see above.
185
- /**
186
- * ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE
187
- * point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`
188
- * and `name` exist only on the bulk GET, which this file does not read, so neither can be
189
- * compared by accident. Nothing PVE returns here is rewritten, re-ordered or re-typed by the
190
- * cluster, because all PVE returns here is one boolean.
191
- * ★ MEASURED: on C1, `GET /cluster/ceph/flags` answers value 0 for all eleven flags, so a
192
- * declaration of `value: false` reads back false and plans `noop`.
193
- */
194
- matches: (attributes, props) => attributes.value === props.value,
195
- path: (props) => `cluster/ceph/flags/${props.flag}`,
196
- /**
197
- * ⚠️ ONLY `value` GOES IN THE BODY. `flag` is already bound by the path and the PUT declares
198
- * `additionalProperties => 0`, so a second copy in the form could only disagree with the URL.
199
- * ⚠️ THE `?? '0'` IS UNREACHABLE, AND IS WRITTEN RATHER THAN CAST AWAY. `flag()` types its
200
- * answer `string | undefined` because it serves OPTIONAL props, and `value` here is required,
201
- * so the branch cannot be taken; a cast would claim a proof the type system has not made.
202
- */
203
- updateForm: (props) => ({ value: flag(props.value) ?? '0' }),
204
- });
105
+ /**
106
+ * ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE
107
+ * point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`
108
+ * and `name` exist only on the bulk GET, which this file does not read, so neither can be
109
+ * compared by accident. Nothing PVE returns here is rewritten, re-ordered or re-typed by the
110
+ * cluster, because all PVE returns here is one boolean.
111
+ * ★ MEASURED: on C1, `GET /cluster/ceph/flags` answers value 0 for all eleven flags, so a
112
+ * declaration of `value: false` reads back false and plans `noop`.
113
+ */
114
+ const matches = (attributes: CephFlagAttributes, props: CephFlagProps) =>
115
+ attributes.value === props.value;
205
116
 
117
+ /**
118
+ * ★ MIGRATED OFF `pveHandlers`/`client.ts`'s generic `pve()` ONTO `@distilled.cloud/proxmox`'s
119
+ * typed `cluster.getClusterCephFlag`/`cluster.putClusterCephFlag` (ceph-flag-wire.ts).
120
+ * `pveHandlers` cannot run a distilled operation, so `diff`/`reconcile` are hand-written here,
121
+ * the same way ceph-pool.ts and backup-job.ts hand-write theirs for the same reason.
122
+ *
123
+ * ⛔ THIS FAMILY HAS NO CREATE KEY, AND THAT IS THE VENDOR'S DOING, NOT AN OMISSION HERE. PVE
124
+ * registers only `PUT /cluster/ceph/flags/{flag}`; `POST /cluster/ceph/flags` does not exist
125
+ * (constraints-forms.test.ts's own regression test), which is why the second ⛔ in the header
126
+ * above calls the create branch unreachable — there is no create form and no create endpoint to
127
+ * guard, only `CEPH_FLAG_UPDATE`.
128
+ */
206
129
  export const ProxmoxCephFlagProvider = () =>
207
130
  Provider.effect(
208
131
  ProxmoxCephFlag,
209
132
  Effect.succeed(
210
133
  ProxmoxCephFlag.Provider.of({
211
- /**
212
- * ⚠️ NOT THE ONE-LINE `Provider.of(handlers)` EVERY OTHER FILE HERE USES, AND THE TWO
213
- * REASONS ARE BOTH acl.ts's. PVE implements no DELETE on this path, and the factory's
214
- * read-back guard is dead whenever `attributes` cannot answer undefined. Unlike acl.ts
215
- * this SPREADS the factory's handlers instead of retyping them, so `list`, `read` and
216
- * `diff` cannot drift from the shared ones — only the two that genuinely differ are
217
- * written out below.
218
- */
219
- ...handlers,
134
+ /** ⛔ Empty, like every resource in this package — see resource.ts's own ⚠️ on why. */
135
+ list: () => Effect.succeed([]),
136
+ read: ({ olds }) => readClusterCephFlag(olds),
137
+ diff: Effect.fn(function* ({ news, output }) {
138
+ if (!isResolved(news)) return undefined;
139
+ yield* guardWrite(CEPH_FLAG_UPDATE, updateForm(news), false);
140
+ if (output === undefined) return undefined;
141
+ // ⛔ Never absent — see the second ⛔ above. A failed read other than a refused mint
142
+ // propagates instead of reaching this point. A refused mint must NOT fail `diff`:
143
+ // that aborts the whole plan (unreadable-read.ts). `read` still propagates.
144
+ const live = yield* readClusterCephFlagForDiff(news);
145
+ if (live === UNREADABLE) {
146
+ yield* unreadableWarning(
147
+ 'Proxmox.CephFlag',
148
+ news.flag,
149
+ news.target.mount,
150
+ mintTier(news.target, 'read'),
151
+ );
152
+ return { action: 'noop' } as const;
153
+ }
154
+ return { action: matches(live, news) ? 'noop' : 'update' } as const;
155
+ }),
220
156
  /** ⛔ Inert, deliberately. The third ⛔ in the header is the whole argument; read it. */
221
157
  delete: () => Effect.void,
222
158
  /**
223
- * ⚠️ THE READ-BACK GUARD, RESTORED FOR A FAMILY WHOSE "ABSENT" DOES NOT EXIST.
224
- * `ops.reconcile` refuses when the object is still missing after a write, and a flag is
225
- * never missing, so that check can never fire here — exactly the hole acl.ts patches
226
- * with `bound`. PVE answers 200 with `{"data":null}` on calls that did nothing, and this
227
- * PUT's documented return IS null, so the status code carries no evidence at all.
159
+ * ⚠️ THE READ-BACK GUARD, RESTORED FOR A FAMILY WHOSE "ABSENT" DOES NOT EXIST. A generic
160
+ * `reconcile` (resource.ts) refuses when the object is still missing after a write, and
161
+ * a flag is never missing, so that check could never fire here. PVE answers 200 with
162
+ * `{"data":null}` on calls that did nothing, and this PUT's documented return IS null,
163
+ * so the status code carries no evidence at all — only a read-back does.
228
164
  * ⚠️ THE PER-FLAG PUT IS SYNCHRONOUS (`$rados->mon_command` inline, MEASURED in Ceph.pm),
229
165
  * which is what makes this check fair rather than a race against a worker task. If it
230
166
  * ever fires spuriously the honest fix is to look at the mon, not to delete the guard.
167
+ * ⛔ `formToSend` IS WHAT MAKES ADOPTING AN ALREADY-MATCHING FLAG FREE. Alchemy's
168
+ * `adopted` action reaches `reconcile` exactly like `update` does (resource.ts's own ⛔
169
+ * on this), so without this guard adopting a flag that already reads correctly would
170
+ * still fire a PUT — pointless against ceph, and update-guard.ts's header has the
171
+ * incident where the equivalent gap on CephPool forked an unnecessary worker.
231
172
  */
232
173
  reconcile: Effect.fn(function* ({ news }) {
233
- const after = yield* handlers.reconcile({ news });
174
+ const live = yield* readClusterCephFlag(news);
175
+ const form = formToSend(matches, live, news, updateForm(news));
176
+ if (form !== undefined) {
177
+ yield* guardWrite(CEPH_FLAG_UPDATE, updateForm(news), false);
178
+ yield* writeClusterCephFlag(news);
179
+ }
180
+ const after = yield* readClusterCephFlag(news);
234
181
  if (after.value !== news.value) {
235
182
  return yield* Effect.die(
236
183
  new Error(