@homeflare/alchemy 0.43.0 → 0.45.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 (612) 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/credential-errors.d.ts +131 -0
  17. package/dist/litellm/credential-errors.d.ts.map +1 -0
  18. package/dist/litellm/credential-form.d.ts +79 -0
  19. package/dist/litellm/credential-form.d.ts.map +1 -0
  20. package/dist/litellm/credential-operations.d.ts +84 -0
  21. package/dist/litellm/credential-operations.d.ts.map +1 -0
  22. package/dist/litellm/credential-rewrite.d.ts +6 -0
  23. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  24. package/dist/litellm/credential-types.d.ts +87 -0
  25. package/dist/litellm/credential-types.d.ts.map +1 -0
  26. package/dist/litellm/credential-values.d.ts +53 -0
  27. package/dist/litellm/credential-values.d.ts.map +1 -0
  28. package/dist/litellm/credential.d.ts +83 -0
  29. package/dist/litellm/credential.d.ts.map +1 -0
  30. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  31. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  32. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  33. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  34. package/dist/litellm/fake-keys.d.ts +53 -0
  35. package/dist/litellm/fake-keys.d.ts.map +1 -0
  36. package/dist/litellm/fake-litellm.d.ts +4 -1
  37. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  38. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  39. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  41. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  42. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  43. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  44. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  45. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  46. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  47. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-registry-base.d.ts +42 -0
  49. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  50. package/dist/litellm/fake-stack.d.ts +8 -1
  51. package/dist/litellm/fake-stack.d.ts.map +1 -1
  52. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  53. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  55. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  57. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  58. package/dist/litellm/index.d.ts +18 -0
  59. package/dist/litellm/index.d.ts.map +1 -1
  60. package/dist/litellm/index.js +3078 -2
  61. package/dist/litellm/index.js.map +65 -4
  62. package/dist/litellm/key-errors.d.ts +169 -0
  63. package/dist/litellm/key-errors.d.ts.map +1 -0
  64. package/dist/litellm/key-form.d.ts +119 -0
  65. package/dist/litellm/key-form.d.ts.map +1 -0
  66. package/dist/litellm/key-harness.d.ts +41 -0
  67. package/dist/litellm/key-harness.d.ts.map +1 -0
  68. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  69. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  70. package/dist/litellm/key-metadata.d.ts +22 -0
  71. package/dist/litellm/key-metadata.d.ts.map +1 -0
  72. package/dist/litellm/key-operations.d.ts +63 -0
  73. package/dist/litellm/key-operations.d.ts.map +1 -0
  74. package/dist/litellm/key-secret.d.ts +33 -0
  75. package/dist/litellm/key-secret.d.ts.map +1 -0
  76. package/dist/litellm/key.d.ts +78 -0
  77. package/dist/litellm/key.d.ts.map +1 -0
  78. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  79. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  80. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  81. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  82. package/dist/litellm/mcp-server-form.d.ts +56 -0
  83. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  84. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  85. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  87. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  89. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-types.d.ts +118 -0
  91. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-url.d.ts +11 -0
  93. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server.d.ts +95 -0
  95. package/dist/litellm/mcp-server.d.ts.map +1 -0
  96. package/dist/litellm/model-claim.d.ts +24 -0
  97. package/dist/litellm/model-claim.d.ts.map +1 -0
  98. package/dist/litellm/model-credential.d.ts +12 -0
  99. package/dist/litellm/model-credential.d.ts.map +1 -0
  100. package/dist/litellm/model-errors.d.ts +119 -0
  101. package/dist/litellm/model-errors.d.ts.map +1 -0
  102. package/dist/litellm/model-form.d.ts +78 -0
  103. package/dist/litellm/model-form.d.ts.map +1 -0
  104. package/dist/litellm/model-locate.d.ts +13 -0
  105. package/dist/litellm/model-locate.d.ts.map +1 -0
  106. package/dist/litellm/model-operations.d.ts +59 -0
  107. package/dist/litellm/model-operations.d.ts.map +1 -0
  108. package/dist/litellm/model-types.d.ts +91 -0
  109. package/dist/litellm/model-types.d.ts.map +1 -0
  110. package/dist/litellm/model.d.ts +88 -0
  111. package/dist/litellm/model.d.ts.map +1 -0
  112. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  113. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  114. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  115. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  116. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  117. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  118. package/dist/litellm/policy-attachment.d.ts +61 -0
  119. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  120. package/dist/litellm/policy-form.d.ts +22 -0
  121. package/dist/litellm/policy-form.d.ts.map +1 -0
  122. package/dist/litellm/policy-operations.d.ts +42 -0
  123. package/dist/litellm/policy-operations.d.ts.map +1 -0
  124. package/dist/litellm/policy-types.d.ts +57 -0
  125. package/dist/litellm/policy-types.d.ts.map +1 -0
  126. package/dist/litellm/policy.d.ts +65 -0
  127. package/dist/litellm/policy.d.ts.map +1 -0
  128. package/dist/litellm/providers.d.ts +1 -1
  129. package/dist/litellm/providers.d.ts.map +1 -1
  130. package/dist/litellm/registry-errors.d.ts +62 -0
  131. package/dist/litellm/registry-errors.d.ts.map +1 -0
  132. package/dist/litellm/registry-support.d.ts +42 -0
  133. package/dist/litellm/registry-support.d.ts.map +1 -0
  134. package/dist/litellm/team-form.d.ts +26 -0
  135. package/dist/litellm/team-form.d.ts.map +1 -0
  136. package/dist/litellm/team-members.d.ts +10 -0
  137. package/dist/litellm/team-members.d.ts.map +1 -0
  138. package/dist/litellm/team-operations.d.ts +38 -0
  139. package/dist/litellm/team-operations.d.ts.map +1 -0
  140. package/dist/litellm/team-permission.d.ts +35 -0
  141. package/dist/litellm/team-permission.d.ts.map +1 -0
  142. package/dist/litellm/team-types.d.ts +101 -0
  143. package/dist/litellm/team-types.d.ts.map +1 -0
  144. package/dist/litellm/team.d.ts +67 -0
  145. package/dist/litellm/team.d.ts.map +1 -0
  146. package/dist/litellm/tool-policy-form.d.ts +21 -0
  147. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  148. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  149. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  150. package/dist/litellm/tool-policy-types.d.ts +46 -0
  151. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  152. package/dist/litellm/tool-policy.d.ts +62 -0
  153. package/dist/litellm/tool-policy.d.ts.map +1 -0
  154. package/dist/litellm/toolset-form.d.ts +20 -0
  155. package/dist/litellm/toolset-form.d.ts.map +1 -0
  156. package/dist/litellm/toolset-operations.d.ts +39 -0
  157. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  158. package/dist/litellm/toolset-types.d.ts +46 -0
  159. package/dist/litellm/toolset-types.d.ts.map +1 -0
  160. package/dist/litellm/toolset.d.ts +55 -0
  161. package/dist/litellm/toolset.d.ts.map +1 -0
  162. package/dist/openbao/index.js +35 -1
  163. package/dist/openbao/index.js.map +3 -3
  164. package/dist/ownership/rows.d.ts +11 -0
  165. package/dist/ownership/rows.d.ts.map +1 -1
  166. package/dist/postgres/connection.d.ts +14 -2
  167. package/dist/postgres/connection.d.ts.map +1 -1
  168. package/dist/postgres/database-sql.d.ts +27 -8
  169. package/dist/postgres/database-sql.d.ts.map +1 -1
  170. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  171. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  172. package/dist/postgres/fake-grants-read.d.ts +61 -0
  173. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  174. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  175. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  176. package/dist/postgres/fake-grants-write.d.ts +29 -0
  177. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  178. package/dist/postgres/fake-role-sql.d.ts +41 -0
  179. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  180. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  181. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  182. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  183. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  184. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  185. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  186. package/dist/postgres/fake-sql.d.ts +42 -0
  187. package/dist/postgres/fake-sql.d.ts.map +1 -1
  188. package/dist/postgres/grants-attrs.d.ts +115 -0
  189. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  190. package/dist/postgres/grants-declare.d.ts +88 -0
  191. package/dist/postgres/grants-declare.d.ts.map +1 -0
  192. package/dist/postgres/grants-diff.d.ts +25 -0
  193. package/dist/postgres/grants-diff.d.ts.map +1 -0
  194. package/dist/postgres/grants-errors.d.ts +137 -0
  195. package/dist/postgres/grants-errors.d.ts.map +1 -0
  196. package/dist/postgres/grants-existence.d.ts +14 -0
  197. package/dist/postgres/grants-existence.d.ts.map +1 -0
  198. package/dist/postgres/grants-ops.d.ts +65 -0
  199. package/dist/postgres/grants-ops.d.ts.map +1 -0
  200. package/dist/postgres/grants-ownership.d.ts +18 -0
  201. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  202. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  203. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  204. package/dist/postgres/grants-plan.d.ts +65 -0
  205. package/dist/postgres/grants-plan.d.ts.map +1 -0
  206. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  207. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  208. package/dist/postgres/grants-read.d.ts +77 -0
  209. package/dist/postgres/grants-read.d.ts.map +1 -0
  210. package/dist/postgres/grants-refuse.d.ts +25 -0
  211. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  212. package/dist/postgres/grants-sql.d.ts +42 -0
  213. package/dist/postgres/grants-sql.d.ts.map +1 -0
  214. package/dist/postgres/grants-words.d.ts +29 -0
  215. package/dist/postgres/grants-words.d.ts.map +1 -0
  216. package/dist/postgres/grants.d.ts +37 -0
  217. package/dist/postgres/grants.d.ts.map +1 -0
  218. package/dist/postgres/index.d.ts +31 -6
  219. package/dist/postgres/index.d.ts.map +1 -1
  220. package/dist/postgres/index.js +1998 -37
  221. package/dist/postgres/index.js.map +46 -7
  222. package/dist/postgres/installed-classifier.d.ts +13 -0
  223. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  224. package/dist/postgres/providers.d.ts +16 -8
  225. package/dist/postgres/providers.d.ts.map +1 -1
  226. package/dist/postgres/psql-executor.d.ts +4 -0
  227. package/dist/postgres/psql-executor.d.ts.map +1 -1
  228. package/dist/postgres/role-attrs.d.ts +112 -0
  229. package/dist/postgres/role-attrs.d.ts.map +1 -0
  230. package/dist/postgres/role-diff.d.ts +22 -0
  231. package/dist/postgres/role-diff.d.ts.map +1 -0
  232. package/dist/postgres/role-errors.d.ts +132 -0
  233. package/dist/postgres/role-errors.d.ts.map +1 -0
  234. package/dist/postgres/role-membership-sql.d.ts +78 -0
  235. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  236. package/dist/postgres/role-provider.d.ts +11 -0
  237. package/dist/postgres/role-provider.d.ts.map +1 -0
  238. package/dist/postgres/role-scram.d.ts +8 -0
  239. package/dist/postgres/role-scram.d.ts.map +1 -0
  240. package/dist/postgres/role-secrets.d.ts +38 -0
  241. package/dist/postgres/role-secrets.d.ts.map +1 -0
  242. package/dist/postgres/role-sql.d.ts +68 -0
  243. package/dist/postgres/role-sql.d.ts.map +1 -0
  244. package/dist/postgres/role.d.ts +57 -0
  245. package/dist/postgres/role.d.ts.map +1 -0
  246. package/dist/postgres/schema-assert.d.ts +50 -0
  247. package/dist/postgres/schema-assert.d.ts.map +1 -0
  248. package/dist/postgres/schema-attrs.d.ts +51 -0
  249. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  250. package/dist/postgres/schema-delete.d.ts +28 -0
  251. package/dist/postgres/schema-delete.d.ts.map +1 -0
  252. package/dist/postgres/schema-diff.d.ts +14 -0
  253. package/dist/postgres/schema-diff.d.ts.map +1 -0
  254. package/dist/postgres/schema-errors.d.ts +139 -0
  255. package/dist/postgres/schema-errors.d.ts.map +1 -0
  256. package/dist/postgres/schema-identity.d.ts +8 -0
  257. package/dist/postgres/schema-identity.d.ts.map +1 -0
  258. package/dist/postgres/schema-reconcile.d.ts +12 -0
  259. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  260. package/dist/postgres/schema-sql.d.ts +45 -0
  261. package/dist/postgres/schema-sql.d.ts.map +1 -0
  262. package/dist/postgres/schema-test-kit.d.ts +67 -0
  263. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  264. package/dist/postgres/schema.d.ts +54 -0
  265. package/dist/postgres/schema.d.ts.map +1 -0
  266. package/dist/proxmox/index.js +35 -1
  267. package/dist/proxmox/index.js.map +3 -3
  268. package/dist/release/fake-engine.d.ts +10 -0
  269. package/dist/release/fake-engine.d.ts.map +1 -1
  270. package/dist/release/index.js +35 -1
  271. package/dist/release/index.js.map +3 -3
  272. package/dist/valkey/acl-attrs.d.ts +94 -0
  273. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  274. package/dist/valkey/acl-compare.d.ts +28 -0
  275. package/dist/valkey/acl-compare.d.ts.map +1 -0
  276. package/dist/valkey/acl-connection.d.ts +6 -0
  277. package/dist/valkey/acl-connection.d.ts.map +1 -0
  278. package/dist/valkey/acl-form.d.ts +62 -0
  279. package/dist/valkey/acl-form.d.ts.map +1 -0
  280. package/dist/valkey/acl-managed.d.ts +4 -0
  281. package/dist/valkey/acl-managed.d.ts.map +1 -0
  282. package/dist/valkey/acl-ops.d.ts +37 -0
  283. package/dist/valkey/acl-ops.d.ts.map +1 -0
  284. package/dist/valkey/acl-profiles.d.ts +53 -0
  285. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  286. package/dist/valkey/acl-reconcile.d.ts +26 -0
  287. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  288. package/dist/valkey/acl.d.ts +24 -0
  289. package/dist/valkey/acl.d.ts.map +1 -0
  290. package/dist/valkey/connection-service.d.ts +22 -0
  291. package/dist/valkey/connection-service.d.ts.map +1 -0
  292. package/dist/valkey/connection.d.ts +59 -0
  293. package/dist/valkey/connection.d.ts.map +1 -0
  294. package/dist/valkey/errors.d.ts +165 -0
  295. package/dist/valkey/errors.d.ts.map +1 -0
  296. package/dist/valkey/fake-acl.d.ts +29 -0
  297. package/dist/valkey/fake-acl.d.ts.map +1 -0
  298. package/dist/valkey/fake-valkey.d.ts +35 -0
  299. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  300. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  301. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  302. package/dist/valkey/index.d.ts +18 -0
  303. package/dist/valkey/index.d.ts.map +1 -0
  304. package/dist/valkey/index.js +964 -0
  305. package/dist/valkey/index.js.map +27 -0
  306. package/dist/valkey/instance-attrs.d.ts +46 -0
  307. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  308. package/dist/valkey/instance-form.d.ts +22 -0
  309. package/dist/valkey/instance-form.d.ts.map +1 -0
  310. package/dist/valkey/instance.d.ts +48 -0
  311. package/dist/valkey/instance.d.ts.map +1 -0
  312. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  313. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  314. package/dist/valkey/providers.d.ts +16 -0
  315. package/dist/valkey/providers.d.ts.map +1 -0
  316. package/dist/valkey/resp.d.ts +38 -0
  317. package/dist/valkey/resp.d.ts.map +1 -0
  318. package/dist/valkey/transport-error.d.ts +36 -0
  319. package/dist/valkey/transport-error.d.ts.map +1 -0
  320. package/dist/valkey/transport.d.ts +50 -0
  321. package/dist/valkey/transport.d.ts.map +1 -0
  322. package/docs/litellm-access-group.md +105 -0
  323. package/docs/litellm-credential.md +60 -0
  324. package/docs/litellm-key-metadata.md +56 -0
  325. package/docs/litellm-key.md +194 -0
  326. package/docs/litellm-mcp.md +187 -0
  327. package/docs/litellm-model.md +111 -0
  328. package/docs/litellm-policy-attachment.md +86 -0
  329. package/docs/litellm-policy.md +103 -0
  330. package/docs/litellm-team.md +117 -0
  331. package/docs/litellm-tool-policy.md +90 -0
  332. package/docs/litellm-toolset.md +92 -0
  333. package/docs/litellm.md +16 -9
  334. package/docs/peers.md +15 -6
  335. package/docs/postgres-grants-example.md +55 -0
  336. package/docs/postgres-grants.md +157 -0
  337. package/docs/postgres-role.md +126 -0
  338. package/docs/postgres-schema.md +139 -0
  339. package/docs/postgres.md +20 -3
  340. package/docs/valkey-transport.md +29 -0
  341. package/docs/valkey.md +187 -0
  342. package/package.json +6 -2
  343. package/src/ceph/ceph-argv.test.ts +1 -1
  344. package/src/launchd/sudo-allowlist.test.ts +2 -2
  345. package/src/linux/sudo-allowlist.test.ts +3 -3
  346. package/src/linux/sudo-runner.test.ts +2 -2
  347. package/src/litellm/access-group-form.ts +105 -0
  348. package/src/litellm/access-group-operations.ts +92 -0
  349. package/src/litellm/access-group-types.ts +49 -0
  350. package/src/litellm/access-group.test.ts +197 -0
  351. package/src/litellm/access-group.ts +151 -0
  352. package/src/litellm/credential-errors.ts +183 -0
  353. package/src/litellm/credential-form.test.ts +151 -0
  354. package/src/litellm/credential-form.ts +211 -0
  355. package/src/litellm/credential-guards.test.ts +241 -0
  356. package/src/litellm/credential-operations.ts +208 -0
  357. package/src/litellm/credential-recovery.test.ts +93 -0
  358. package/src/litellm/credential-rewrite.test.ts +164 -0
  359. package/src/litellm/credential-rewrite.ts +50 -0
  360. package/src/litellm/credential-types.ts +117 -0
  361. package/src/litellm/credential-values.ts +94 -0
  362. package/src/litellm/credential.test.ts +218 -0
  363. package/src/litellm/credential.ts +227 -0
  364. package/src/litellm/fake-access-group-litellm.ts +127 -0
  365. package/src/litellm/fake-credential-litellm.ts +216 -0
  366. package/src/litellm/fake-keys.ts +190 -0
  367. package/src/litellm/fake-litellm.ts +19 -9
  368. package/src/litellm/fake-mcp-litellm.ts +214 -0
  369. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  370. package/src/litellm/fake-model-litellm.ts +233 -0
  371. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  372. package/src/litellm/fake-policy-litellm.ts +186 -0
  373. package/src/litellm/fake-registry-base.ts +69 -0
  374. package/src/litellm/fake-stack.ts +17 -1
  375. package/src/litellm/fake-team-litellm.ts +229 -0
  376. package/src/litellm/fake-tool-litellm.ts +88 -0
  377. package/src/litellm/fake-toolset-litellm.ts +110 -0
  378. package/src/litellm/index.ts +140 -0
  379. package/src/litellm/key-delete.test.ts +212 -0
  380. package/src/litellm/key-errors.ts +241 -0
  381. package/src/litellm/key-form.ts +200 -0
  382. package/src/litellm/key-harness.ts +121 -0
  383. package/src/litellm/key-metadata-errors.ts +49 -0
  384. package/src/litellm/key-metadata.test.ts +156 -0
  385. package/src/litellm/key-metadata.ts +74 -0
  386. package/src/litellm/key-operations.ts +184 -0
  387. package/src/litellm/key-rotation.test.ts +110 -0
  388. package/src/litellm/key-scope.test.ts +135 -0
  389. package/src/litellm/key-secret.test.ts +248 -0
  390. package/src/litellm/key-secret.ts +137 -0
  391. package/src/litellm/key-transport.test.ts +166 -0
  392. package/src/litellm/key.test.ts +231 -0
  393. package/src/litellm/key.ts +213 -0
  394. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  395. package/src/litellm/mcp-server-alias.test.ts +184 -0
  396. package/src/litellm/mcp-server-credential.ts +85 -0
  397. package/src/litellm/mcp-server-errors.ts +141 -0
  398. package/src/litellm/mcp-server-exports.test.ts +15 -0
  399. package/src/litellm/mcp-server-form.test.ts +241 -0
  400. package/src/litellm/mcp-server-form.ts +233 -0
  401. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  402. package/src/litellm/mcp-server-locate.ts +66 -0
  403. package/src/litellm/mcp-server-operations.ts +111 -0
  404. package/src/litellm/mcp-server-registry.test.ts +146 -0
  405. package/src/litellm/mcp-server-retype.test.ts +114 -0
  406. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  407. package/src/litellm/mcp-server-shadow.ts +45 -0
  408. package/src/litellm/mcp-server-tools.test.ts +85 -0
  409. package/src/litellm/mcp-server-types.ts +143 -0
  410. package/src/litellm/mcp-server-url.ts +66 -0
  411. package/src/litellm/mcp-server.test.ts +213 -0
  412. package/src/litellm/mcp-server.ts +225 -0
  413. package/src/litellm/model-claim.ts +69 -0
  414. package/src/litellm/model-credential.ts +45 -0
  415. package/src/litellm/model-errors.ts +168 -0
  416. package/src/litellm/model-foreign-row.test.ts +173 -0
  417. package/src/litellm/model-form.test.ts +228 -0
  418. package/src/litellm/model-form.ts +223 -0
  419. package/src/litellm/model-locate.test.ts +128 -0
  420. package/src/litellm/model-locate.ts +67 -0
  421. package/src/litellm/model-operations.test.ts +93 -0
  422. package/src/litellm/model-operations.ts +146 -0
  423. package/src/litellm/model-params-preserve.test.ts +119 -0
  424. package/src/litellm/model-recovery.test.ts +57 -0
  425. package/src/litellm/model-types.ts +112 -0
  426. package/src/litellm/model.test.ts +222 -0
  427. package/src/litellm/model.ts +237 -0
  428. package/src/litellm/policy-attachment-form.ts +103 -0
  429. package/src/litellm/policy-attachment-operations.ts +74 -0
  430. package/src/litellm/policy-attachment-types.ts +48 -0
  431. package/src/litellm/policy-attachment.test.ts +198 -0
  432. package/src/litellm/policy-attachment.ts +178 -0
  433. package/src/litellm/policy-form.ts +122 -0
  434. package/src/litellm/policy-operations.ts +112 -0
  435. package/src/litellm/policy-types.ts +58 -0
  436. package/src/litellm/policy.test.ts +214 -0
  437. package/src/litellm/policy.ts +146 -0
  438. package/src/litellm/providers.ts +24 -3
  439. package/src/litellm/registry-errors.ts +88 -0
  440. package/src/litellm/registry-exports.test.ts +66 -0
  441. package/src/litellm/registry-support.ts +109 -0
  442. package/src/litellm/team-form.ts +140 -0
  443. package/src/litellm/team-members.test.ts +150 -0
  444. package/src/litellm/team-members.ts +58 -0
  445. package/src/litellm/team-operations.ts +90 -0
  446. package/src/litellm/team-permission.ts +159 -0
  447. package/src/litellm/team-read.test.ts +89 -0
  448. package/src/litellm/team-types.ts +106 -0
  449. package/src/litellm/team.test.ts +206 -0
  450. package/src/litellm/team.ts +159 -0
  451. package/src/litellm/tool-policy-form.ts +94 -0
  452. package/src/litellm/tool-policy-operations.ts +58 -0
  453. package/src/litellm/tool-policy-types.ts +48 -0
  454. package/src/litellm/tool-policy.test.ts +172 -0
  455. package/src/litellm/tool-policy.ts +125 -0
  456. package/src/litellm/toolset-form.ts +104 -0
  457. package/src/litellm/toolset-operations.ts +90 -0
  458. package/src/litellm/toolset-types.ts +47 -0
  459. package/src/litellm/toolset.test.ts +198 -0
  460. package/src/litellm/toolset.ts +151 -0
  461. package/src/ownership/ownership.test.ts +33 -1
  462. package/src/ownership/rows.ts +53 -0
  463. package/src/postgres/connection.ts +42 -7
  464. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  465. package/src/postgres/database-refusals.test.ts +2 -2
  466. package/src/postgres/database-sql.ts +36 -9
  467. package/src/postgres/fake-grants-parse.ts +189 -0
  468. package/src/postgres/fake-grants-read.ts +250 -0
  469. package/src/postgres/fake-grants-sql.ts +207 -0
  470. package/src/postgres/fake-grants-write.ts +90 -0
  471. package/src/postgres/fake-role-sql.ts +240 -0
  472. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  473. package/src/postgres/fake-schema-sql.ts +146 -0
  474. package/src/postgres/fake-sql-parse.ts +86 -0
  475. package/src/postgres/fake-sql-quote.ts +36 -0
  476. package/src/postgres/fake-sql.ts +166 -46
  477. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  478. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  479. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  480. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  481. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  482. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  483. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  484. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  485. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  486. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  487. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  488. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  489. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  490. package/src/postgres/grants-acl.test.ts +61 -0
  491. package/src/postgres/grants-attrs.ts +185 -0
  492. package/src/postgres/grants-database.test.ts +158 -0
  493. package/src/postgres/grants-declare.ts +172 -0
  494. package/src/postgres/grants-delete-proof.test.ts +154 -0
  495. package/src/postgres/grants-diff.ts +101 -0
  496. package/src/postgres/grants-errors.ts +215 -0
  497. package/src/postgres/grants-existence-guards.test.ts +113 -0
  498. package/src/postgres/grants-existence.ts +101 -0
  499. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  500. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  501. package/src/postgres/grants-lifecycle.test.ts +181 -0
  502. package/src/postgres/grants-ops.ts +229 -0
  503. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  504. package/src/postgres/grants-ownership.ts +49 -0
  505. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  506. package/src/postgres/grants-plan-diff.test.ts +204 -0
  507. package/src/postgres/grants-plan-restore.ts +43 -0
  508. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  509. package/src/postgres/grants-plan.test.ts +148 -0
  510. package/src/postgres/grants-plan.ts +245 -0
  511. package/src/postgres/grants-provenance.test.ts +134 -0
  512. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  513. package/src/postgres/grants-read-absent.test.ts +91 -0
  514. package/src/postgres/grants-read-defaults.ts +51 -0
  515. package/src/postgres/grants-read.ts +250 -0
  516. package/src/postgres/grants-refusals.test.ts +136 -0
  517. package/src/postgres/grants-refuse.ts +119 -0
  518. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  519. package/src/postgres/grants-repair-plan.test.ts +201 -0
  520. package/src/postgres/grants-sql.test.ts +128 -0
  521. package/src/postgres/grants-sql.ts +144 -0
  522. package/src/postgres/grants-words.ts +34 -0
  523. package/src/postgres/grants.ts +110 -0
  524. package/src/postgres/index.ts +129 -5
  525. package/src/postgres/installed-classifier.ts +36 -0
  526. package/src/postgres/providers.ts +33 -9
  527. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  528. package/src/postgres/psql-executor.test.ts +63 -2
  529. package/src/postgres/psql-executor.ts +74 -34
  530. package/src/postgres/role-adopt.test.ts +98 -0
  531. package/src/postgres/role-attrs.ts +137 -0
  532. package/src/postgres/role-diff.ts +93 -0
  533. package/src/postgres/role-errors.ts +206 -0
  534. package/src/postgres/role-guard.test.ts +60 -0
  535. package/src/postgres/role-identity.test.ts +197 -0
  536. package/src/postgres/role-lifecycle.test.ts +228 -0
  537. package/src/postgres/role-membership-options.test.ts +73 -0
  538. package/src/postgres/role-membership-repair.test.ts +153 -0
  539. package/src/postgres/role-membership-sql.ts +223 -0
  540. package/src/postgres/role-password-ascii.test.ts +53 -0
  541. package/src/postgres/role-password-transports.test.ts +123 -0
  542. package/src/postgres/role-password.test.ts +158 -0
  543. package/src/postgres/role-privilege.test.ts +107 -0
  544. package/src/postgres/role-provenance.test.ts +200 -0
  545. package/src/postgres/role-provider.ts +105 -0
  546. package/src/postgres/role-refusals.test.ts +225 -0
  547. package/src/postgres/role-scram.test.ts +38 -0
  548. package/src/postgres/role-scram.ts +40 -0
  549. package/src/postgres/role-secrets.ts +61 -0
  550. package/src/postgres/role-sql.ts +213 -0
  551. package/src/postgres/role-valid-until.test.ts +68 -0
  552. package/src/postgres/role.ts +226 -0
  553. package/src/postgres/schema-assert.ts +139 -0
  554. package/src/postgres/schema-attrs.ts +71 -0
  555. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  556. package/src/postgres/schema-delete.ts +96 -0
  557. package/src/postgres/schema-diff.ts +44 -0
  558. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  559. package/src/postgres/schema-errors.ts +209 -0
  560. package/src/postgres/schema-handlers.test.ts +193 -0
  561. package/src/postgres/schema-identity.ts +39 -0
  562. package/src/postgres/schema-lifecycle.test.ts +246 -0
  563. package/src/postgres/schema-race.test.ts +70 -0
  564. package/src/postgres/schema-reconcile.ts +100 -0
  565. package/src/postgres/schema-refusals.test.ts +144 -0
  566. package/src/postgres/schema-socket.test.ts +162 -0
  567. package/src/postgres/schema-sql.ts +130 -0
  568. package/src/postgres/schema-takeover.test.ts +149 -0
  569. package/src/postgres/schema-test-kit.ts +85 -0
  570. package/src/postgres/schema.ts +130 -0
  571. package/src/release/binary-alias.test.ts +3 -3
  572. package/src/release/binary-claims.test.ts +3 -3
  573. package/src/release/binary-directory-umask.test.ts +65 -0
  574. package/src/release/fake-engine.ts +15 -0
  575. package/src/valkey/acl-attrs.ts +98 -0
  576. package/src/valkey/acl-compare.ts +57 -0
  577. package/src/valkey/acl-connection.ts +13 -0
  578. package/src/valkey/acl-delete.test.ts +127 -0
  579. package/src/valkey/acl-form.ts +239 -0
  580. package/src/valkey/acl-managed.ts +13 -0
  581. package/src/valkey/acl-ops.ts +122 -0
  582. package/src/valkey/acl-ownership.test.ts +118 -0
  583. package/src/valkey/acl-prefix.test.ts +38 -0
  584. package/src/valkey/acl-profiles.ts +81 -0
  585. package/src/valkey/acl-reconcile.ts +180 -0
  586. package/src/valkey/acl-reserved.test.ts +69 -0
  587. package/src/valkey/acl-revocation.test.ts +94 -0
  588. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  589. package/src/valkey/acl-validation.test.ts +54 -0
  590. package/src/valkey/acl.test.ts +215 -0
  591. package/src/valkey/acl.ts +169 -0
  592. package/src/valkey/connection-service.ts +46 -0
  593. package/src/valkey/connection.ts +180 -0
  594. package/src/valkey/errors.ts +239 -0
  595. package/src/valkey/fake-acl.ts +128 -0
  596. package/src/valkey/fake-valkey.ts +224 -0
  597. package/src/valkey/handler-test-fixture.ts +43 -0
  598. package/src/valkey/handlers.test.ts +205 -0
  599. package/src/valkey/index.ts +54 -0
  600. package/src/valkey/instance-attrs.ts +48 -0
  601. package/src/valkey/instance-form.ts +104 -0
  602. package/src/valkey/instance.test.ts +108 -0
  603. package/src/valkey/instance.ts +160 -0
  604. package/src/valkey/provider-socket-fixture.ts +71 -0
  605. package/src/valkey/providers.test.ts +129 -0
  606. package/src/valkey/providers.ts +21 -0
  607. package/src/valkey/resp.ts +212 -0
  608. package/src/valkey/transport-bounds.test.ts +150 -0
  609. package/src/valkey/transport-error.ts +37 -0
  610. package/src/valkey/transport.test.ts +158 -0
  611. package/src/valkey/transport.ts +109 -0
  612. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -7,19 +7,19 @@
7
7
  * `replace`, reported success, and left no binary on disk; the next deploy put it back.
8
8
  */
9
9
  import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
10
- import { mkdir, mkdtemp, rm, stat, symlink } from 'node:fs/promises';
10
+ import { mkdtemp, rm, stat, symlink } from 'node:fs/promises';
11
11
  import { tmpdir } from 'node:os';
12
12
  import { join } from 'node:path';
13
13
  import { type HostRunner } from '../launchd/runner.ts';
14
14
  import { localRunner } from '../launchd/local-runner.ts';
15
15
  import { reconcileBinary } from './binary-lifecycle.ts';
16
- import { VMALERT_TEXT, realEngine } from './fake-engine.ts';
16
+ import { VMALERT_TEXT, makeBinaryDirectory, realEngine } from './fake-engine.ts';
17
17
  import { VMUTILS_URL, fakeTransport, syntheticRelease, vmalertProps } from './fake-release.ts';
18
18
 
19
19
  let tmp = '';
20
20
  beforeEach(async () => {
21
21
  tmp = await mkdtemp(join(tmpdir(), 'hf-alias-'));
22
- await mkdir(join(tmp, 'real', 'vmutils-1.151.0'), { recursive: true });
22
+ await makeBinaryDirectory(join(tmp, 'real', 'vmutils-1.151.0'));
23
23
  await symlink(join(tmp, 'real'), join(tmp, 'link'));
24
24
  });
25
25
  afterEach(async () => {
@@ -7,16 +7,16 @@
7
7
  * `vmalert: noop, vmalert-logs: delete` — and the file `vmalert` still declared was gone.
8
8
  */
9
9
  import { afterEach, beforeEach, expect, test } from 'bun:test';
10
- import { mkdir, mkdtemp, rm } from 'node:fs/promises';
10
+ import { mkdtemp, rm } from 'node:fs/promises';
11
11
  import { tmpdir } from 'node:os';
12
12
  import { join } from 'node:path';
13
- import { VMALERT_TEXT, realEngine } from './fake-engine.ts';
13
+ import { VMALERT_TEXT, makeBinaryDirectory, realEngine } from './fake-engine.ts';
14
14
  import { VMUTILS_URL } from './fake-release.ts';
15
15
 
16
16
  let dir = '';
17
17
  beforeEach(async () => {
18
18
  dir = join(await mkdtemp(join(tmpdir(), 'hf-claims-')), 'vmutils-1.151.0');
19
- await mkdir(dir);
19
+ await makeBinaryDirectory(dir);
20
20
  });
21
21
  afterEach(async () => {
22
22
  await rm(join(dir, '..'), { force: true, recursive: true });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * THE FIXTURE DIRECTORIES DO NOT DEPEND ON THE UMASK (fake-engine.ts `makeBinaryDirectory`).
3
+ *
4
+ * 🔴 MEASURED 2026-10-01: under CT100's login umask 0002 six tests in binary-alias and
5
+ * binary-claims failed with "directory ... is writable by group or other (mode 0775)", because
6
+ * their fixtures made the directory with a bare `mkdir`; under 022 they passed. Only the
7
+ * fixtures were wrong — Release.Binary is right to refuse a directory its group may write, and
8
+ * the last test here pins that it still does, on the real filesystem.
9
+ *
10
+ * Each test sets the umask for itself and puts the process's own back in a `finally`; bun runs the
11
+ * tests of a file one after another, so nothing else sees the changed mask. Every directory is
12
+ * under a fresh `mkdtemp`, removed in `afterEach`.
13
+ */
14
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
15
+ import { mkdir, mkdtemp, rm, stat } from 'node:fs/promises';
16
+ import { tmpdir } from 'node:os';
17
+ import { join } from 'node:path';
18
+ import { VMALERT_TEXT, makeBinaryDirectory, realEngine } from './fake-engine.ts';
19
+
20
+ let tmp = '';
21
+ beforeEach(async () => {
22
+ tmp = await mkdtemp(join(tmpdir(), 'hf-umask-'));
23
+ });
24
+ afterEach(async () => {
25
+ await rm(tmp, { force: true, recursive: true });
26
+ });
27
+
28
+ /** Run `body` under `mask`, and give the process its own umask back whatever happens. */
29
+ const underUmask = async <T>(mask: number, body: () => Promise<T>): Promise<T> => {
30
+ const before = process.umask(mask);
31
+ try {
32
+ return await body();
33
+ } finally {
34
+ process.umask(before);
35
+ }
36
+ };
37
+ const modeOf = async (path: string): Promise<number> => (await stat(path)).mode & 0o777;
38
+ const octal = (mode: number): string => mode.toString(8).padStart(4, '0');
39
+
40
+ describe('makeBinaryDirectory', () => {
41
+ for (const mask of [0o000, 0o002, 0o022]) {
42
+ test(`makes 0755 directories under umask ${octal(mask)}, the leaf and the ones on the way`, async () => {
43
+ const leaf = join(tmp, 'real', 'vmutils-1.151.0');
44
+ await underUmask(mask, () => makeBinaryDirectory(leaf));
45
+ expect(octal(await modeOf(leaf))).toBe('0755');
46
+ expect(octal(await modeOf(join(tmp, 'real')))).toBe('0755');
47
+ });
48
+ }
49
+
50
+ test('⛔ the control: a bare mkdir under 0002 is the 0775 the resource refuses, and still is', async () => {
51
+ const dir = join(tmp, 'vmutils-1.151.0');
52
+ await underUmask(0o002, () => mkdir(dir));
53
+ expect(octal(await modeOf(dir))).toBe('0775');
54
+ await expect(realEngine().deploy({ vmalert: dir })).rejects.toThrow(
55
+ `directory ${dir} is writable by group or other (mode 0775)`,
56
+ );
57
+ });
58
+
59
+ test('the real engine installs into one made under a group-writable umask', async () => {
60
+ const dir = join(tmp, 'vmutils-1.151.0');
61
+ await underUmask(0o002, () => makeBinaryDirectory(dir));
62
+ expect(await realEngine().deploy({ vmalert: dir })).toEqual({ vmalert: 'create' });
63
+ expect(await Bun.file(join(dir, 'vmalert')).text()).toBe(VMALERT_TEXT);
64
+ });
65
+ });
@@ -6,6 +6,7 @@
6
6
  * ⛔ TEST-ONLY, like fake-release.ts: no provider imports it and it is not on the barrel. Every
7
7
  * directory a test passes must be under its own `mkdtemp`; nothing here chooses a path.
8
8
  */
9
+ import { mkdir } from 'node:fs/promises';
9
10
  import * as Effect from 'effect/Effect';
10
11
  import * as Layer from 'effect/Layer';
11
12
  import * as HttpClient from 'effect/unstable/http/HttpClient';
@@ -20,6 +21,20 @@ import { BINARY, VMALERT_REQUEST, VMUTILS_URL, syntheticRelease } from './fake-r
20
21
  /** vmalert's bytes as text, to compare with what a test reads off the disk. */
21
22
  export const VMALERT_TEXT = new TextDecoder().decode(BINARY.vmalert);
22
23
 
24
+ /**
25
+ * A directory Release.Binary accepts, whatever the process umask. The resource refuses a directory
26
+ * its group or others may write (binary-lifecycle.ts `directoryReady`), and a bare `mkdir` asks for
27
+ * 0777 and loses the umask's bits: 0775 under the 0002 a CT100 login shell has, so the suite
28
+ * passed under 022 and failed six tests under 0002 (measured 2026-10-01). A umask only ever takes
29
+ * bits away, so asking for 0755 outright can come out no wider than 0755 under any mask; the
30
+ * directories made on the way (`recursive`) get the same mode. Every test directory goes through
31
+ * here; the resource's refusal is right and stays as it is.
32
+ */
33
+ export const makeBinaryDirectory = async (path: string): Promise<string> => {
34
+ await mkdir(path, { mode: 0o755, recursive: true });
35
+ return path;
36
+ };
37
+
23
38
  /** One stack over localRunner; `deploy` takes logical id → directory; `gets` counts downloads. */
24
39
  export const realEngine = () => {
25
40
  const { catalog, vmutils } = syntheticRelease();
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `Valkey.AclFile` props attributes.
3
+ *
4
+ * ⛔ PASSWORDS BY REFERENCE, NEVER IN STATE. Each ACL user's password is a `{ fromEnv }` reference
5
+ * (see `secrets/write-only.ts`); the value exists only in the deploying process's memory and on
6
+ * the wire to the server. State stores an scrypt SEAL, so a later plan can check "is the value
7
+ * in my environment the one I wrote" without holding anything a reader could send to Valkey.
8
+ * ★ THE KEY PREFIX IS THE ISOLATION. `~<name>:*` limits a seat to its own keyspace; a seat
9
+ * gets `WRITE` only where its job writes. The deny list (`-scan -randomkey -dbsize -pubsub`)
10
+ * closes keyspace/channel enumeration, matching `homeflare-ct100/src/valkey-acl.ts`.
11
+ * ★ THE COMMAND ALLOW-LIST IS A FIXED PROFILE, NOT A PROP. `profile: 'seat'` carries the seat
12
+ * allow-list with pub/sub restricted to the user's own channel; `profile: 'service'` carries
13
+ * `+@all -@dangerous` with channels from the declaration, for one consumer that owns the whole
14
+ * instance (LiteLLM's cache user on `:6380`); `profile: 'monitor'` carries the key-less,
15
+ * channel-less `redis_exporter` rule set (ct100#117). It is deliberately no free-form rules
16
+ * prop: a declaration could widen itself, which would let a seat request `FLUSHALL` or
17
+ * `ACL SETUSER` and defeat the isolation the resource enforces.
18
+ */
19
+ import type { FromEnv } from '../secrets/write-only.ts';
20
+
21
+ /** The fixed rule set a user carries. */
22
+ export type ValkeyAclProfile = 'seat' | 'service' | 'monitor';
23
+
24
+ /** One ACL user, limited to a key prefix and a fixed command allow-list. */
25
+ export interface ValkeyAclUser {
26
+ /** The username (matches the seat name). */
27
+ readonly name: string;
28
+ /**
29
+ * The key pattern the user may touch, sent as `~<keyPrefix>`. Optional: a `seat` defaults to
30
+ * exactly `<name>:*` — CT100's template (`homeflare-ct100/src/valkey-acl.ts`) — and a
31
+ * `service` defaults to `*` (the whole instance; LiteLLM's cache user declares `hf-litellm:*`).
32
+ * A `monitor` must not carry one — it is key-less (`resetkeys`), so a declared prefix is
33
+ * refused (`ValkeyAclMonitorKeyPrefix`).
34
+ */
35
+ readonly keyPrefix?: string;
36
+ /** Channel globs without `&`. Defaults to the key prefix. Seats must keep `<name>:*`;
37
+ * monitors must have none. Services may declare a separate channel namespace. */
38
+ readonly channelPatterns?: ReadonlyArray<string>;
39
+ /** The fixed allow-list this user carries. */
40
+ readonly profile: ValkeyAclProfile;
41
+ /** The password, by reference. */
42
+ readonly password: FromEnv;
43
+ }
44
+
45
+ /** The ACL users of one instance. */
46
+ export interface ValkeyAclFileProps {
47
+ /** The instance the ACL file belongs to — matches the `Valkey.Instance` name. */
48
+ readonly instance: string;
49
+ /** The ACL users, keyed by username. */
50
+ readonly users: Readonly<Record<string, ValkeyAclUser>>;
51
+ /**
52
+ * ★ EXCLUSIVE OWNERSHIP IS OPT-IN, OFF BY DEFAULT (round-3 finding 1). `true` means this
53
+ * declaration is the ONLY writer of the instance's ACL: reconcile `ACL DELUSER`s every user
54
+ * it did not declare. `false` — the default — leaves never-managed users (CT100's key-less
55
+ * `monitor` for `redis_exporter`, an operator's debugging user) untouched and reports them
56
+ * with a warning, because reconcile cannot know what a template renderer or a human still
57
+ * needs. The measured incident: reconcile deleted CT100's `monitor` and closed the
58
+ * exporter's connection.
59
+ * Removing a previously managed user from the declaration always revokes it.
60
+ */
61
+ readonly exclusive?: boolean;
62
+ }
63
+
64
+ /** One user's live ACL fields, parsed from `ACL LIST`. Every field except `passwordSeal`
65
+ * comes from the server's own echo (measured semantics in `acl-form.ts`'s header), so `diff`
66
+ * can see exactly what `reconcile`'s read-back asserts — a user widened or disabled outside the
67
+ * stack must show up as an update, never as a silent no-op. */
68
+ export interface ValkeyAclUserAttributes {
69
+ readonly name: string;
70
+ /** The first live key pattern without its `~`, or `''` when the user has none. */
71
+ readonly keyPrefix: string;
72
+ /** Any live key pattern beyond the first, `~`-stripped. This family declares exactly one
73
+ * pattern per user, so a non-empty list means the ACL was widened outside the stack. */
74
+ readonly extraKeyPatterns: ReadonlyArray<string>;
75
+ /** The live channel patterns as the server echoes them (`&claude:*`; `&*` is all channels;
76
+ * an empty list — the bare `resetchannels` marker — means no channel access at all). */
77
+ readonly channelPatterns: ReadonlyArray<string>;
78
+ /** The profile the live rule set was recognised as — `unrecognized` when the rules match
79
+ * neither fixed profile (something outside the stack edited them). */
80
+ readonly profile: ValkeyAclProfile | 'unrecognized';
81
+ /** The live `on` flag: a user this family declared but the host disabled is drift. */
82
+ readonly on: boolean;
83
+ /** The live `nopass` flag: a passwordless user authenticates as nobody, which bypasses the
84
+ * per-seat password boundary, so it always drifts for a user this family manages. */
85
+ readonly nopass: boolean;
86
+ /** Any password token (`#<hash>`, `>pw`, `<pw`) present. Never the value. */
87
+ readonly hasPassword: boolean;
88
+ /** The scrypt seal of the password the family last wrote, threaded from state. */
89
+ readonly passwordSeal: string;
90
+ }
91
+
92
+ /** The live ACL attributes of one instance. */
93
+ export interface ValkeyAclFileAttributes {
94
+ readonly instance: string;
95
+ readonly users: Readonly<Record<string, ValkeyAclUserAttributes>>;
96
+ /** Last managed declaration, separate from observed users. Optional for earlier state. */
97
+ readonly managedUsers?: ReadonlyArray<string>;
98
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Comparison logic between a live `ACL LIST` line and the declaration: whether a user matches,
3
+ * what profile its rules were recognised as, and set equality over the token lists.
4
+ *
5
+ * Extracted from `acl-form.ts` (which owns building and parsing) to keep both files under the cap;
6
+ * tests import these comparisons directly.
7
+ */
8
+ import type { ValkeyAclProfile, ValkeyAclUser } from './acl-attrs.ts';
9
+ import type { ParsedAclUser } from './acl-form.ts';
10
+ import {
11
+ MONITOR_RULES,
12
+ SEAT_RULES,
13
+ SERVICE_RULES,
14
+ declaredChannels,
15
+ declaredKeyPatterns,
16
+ } from './acl-profiles.ts';
17
+
18
+ /** Whether two string lists carry the same members — order ignored, duplicates and extras caught
19
+ * by the length guard (the echo's order is the server's business; a wider or duplicated list is
20
+ * not). */
21
+ export const sameSet = (a: ReadonlyArray<string>, b: ReadonlyArray<string>): boolean => {
22
+ if (a.length !== b.length) return false;
23
+ const sa = new Set(a);
24
+ const sb = new Set(b);
25
+ if (sa.size !== sb.size) return false;
26
+ for (const x of sa) if (!sb.has(x)) return false;
27
+ return true;
28
+ };
29
+
30
+ /**
31
+ * Recognise a live rule set as one of the three fixed profiles. The echo is normalised first
32
+ * (`acl-form.ts`'s header): drop the `-@all` baseline the server echoes for a reset user, since
33
+ * the seat set never grants it back and a `+@all` cancels it. `unrecognized` means something
34
+ * outside the stack edited the rules — that user drifts.
35
+ */
36
+ export const inferProfile = (rules: ReadonlyArray<string>): ValkeyAclProfile | 'unrecognized' => {
37
+ const normalised = rules.filter((rule) => rule !== '-@all');
38
+ if (sameSet(normalised, SEAT_RULES)) return 'seat';
39
+ if (sameSet(normalised, SERVICE_RULES)) return 'service';
40
+ if (sameSet(rules, MONITOR_RULES)) return 'monitor';
41
+ return 'unrecognized';
42
+ };
43
+
44
+ /**
45
+ * Whether a live user line is exactly as declared — every visible disagreement is a match failure,
46
+ * not just a changed prefix (a widened rule set, a disabled flag, or a stripped password is exactly
47
+ * what reconcile exists to restore). Passwords are never compared here: the live line holds only a
48
+ * hash, and the stored seal is what `diff`/`reconcile` check for rotation.
49
+ */
50
+ export const matchesDeclared = (live: ParsedAclUser, user: ValkeyAclUser): boolean =>
51
+ live.name === user.name &&
52
+ live.on &&
53
+ !live.nopass &&
54
+ live.hasPassword &&
55
+ sameSet(live.keyPatterns, declaredKeyPatterns(user)) &&
56
+ sameSet(live.channelPatterns, declaredChannels(user)) &&
57
+ inferProfile(live.rules) === user.profile;
@@ -0,0 +1,13 @@
1
+ /** Map the whole connection operation, including socket acquisition and AUTH transport errors. */
2
+ import * as Effect from 'effect/Effect';
3
+ import { resolveValkeyConnection } from './connection-service.ts';
4
+ import { ValkeyInstanceUnreachable } from './errors.ts';
5
+ import type { ValkeySocketError } from './transport.ts';
6
+
7
+ export const aclUnreachable = (instance: string) => (_error: ValkeySocketError) =>
8
+ Effect.gen(function* () {
9
+ const config = yield* resolveValkeyConnection(instance);
10
+ return yield* Effect.fail(
11
+ new ValkeyInstanceUnreachable({ instance, host: config.host, port: config.port }),
12
+ );
13
+ });
@@ -0,0 +1,127 @@
1
+ /** Delete removes only the users the declaration managed, never what the instance merely holds. */
2
+ import { expect, test } from 'bun:test';
3
+ import * as Effect from 'effect/Effect';
4
+ import { makeValkeyAclFileHandlers } from './acl.ts';
5
+ import type { ValkeyAclFileAttributes, ValkeyAclFileProps } from './acl-attrs.ts';
6
+ import { managedUserNames } from './acl-managed.ts';
7
+ import { readWithExecutor } from './acl-ops.ts';
8
+ import { reconcileWithExecutor } from './acl-reconcile.ts';
9
+ import { type FakeValkey, makeFakeValkey } from './fake-valkey.ts';
10
+ import { connection, context, run } from './handler-test-fixture.ts';
11
+
12
+ const seat = {
13
+ name: 'seat',
14
+ profile: 'seat',
15
+ password: { fromEnv: 'TEST_VALKEY_SEAT_PW' },
16
+ } as const;
17
+ const draft = {
18
+ name: 'draft',
19
+ profile: 'seat',
20
+ password: { fromEnv: 'TEST_VALKEY_DRAFT_PW' },
21
+ } as const;
22
+ const foreign = {
23
+ name: 'operator',
24
+ profile: 'seat',
25
+ password: { fromEnv: 'TEST_VALKEY_OPERATOR_PW' },
26
+ } as const;
27
+ const props: ValkeyAclFileProps = { instance: 'scratch', users: { seat } };
28
+ const env = { TEST_VALKEY_SEAT_PW: 'FAKE-seat' };
29
+ const admin = 'user admin on >FAKE-admin ~* &* +@all';
30
+ const operator = 'user operator on >FAKE-operator ~* &* +@all';
31
+ const monitor = 'user monitor on >FAKE-monitor -@all';
32
+ const draftAcl = 'user draft on >FAKE-draft ~draft:* &draft:* +@all';
33
+ const survivors = ['admin', 'monitor', 'operator'];
34
+
35
+ const instance = (): FakeValkey =>
36
+ makeFakeValkey({ acl: { admin, operator, monitor }, passwords: { admin: 'FAKE-admin' } });
37
+ // The state the engine would hold after a real reconcile: every observed user in `users`,
38
+ // ownership recorded separately in `managedUsers`.
39
+ const stored = (fake: FakeValkey): Promise<ValkeyAclFileAttributes> =>
40
+ Effect.runPromise(reconcileWithExecutor(fake, props, undefined, env, 'admin'));
41
+ const remove = (
42
+ fake: FakeValkey,
43
+ olds: ValkeyAclFileProps,
44
+ output: ValkeyAclFileAttributes,
45
+ ): Promise<void> =>
46
+ run(makeValkeyAclFileHandlers(connection(fake)).delete({ ...context, olds, output }));
47
+ const delusers = (fake: FakeValkey) =>
48
+ fake.commands.filter(({ args }) => args[1] === 'DELUSER').map(({ args }) => args);
49
+
50
+ test('delete removes only the managed user, not an operator, monitor or admin user', async () => {
51
+ const fake = instance();
52
+ const output = await stored(fake);
53
+ expect(Object.keys(output.users).sort()).toEqual(['monitor', 'operator', 'seat']);
54
+ expect(output.managedUsers).toEqual(['seat']);
55
+ await remove(fake, props, output);
56
+ expect(delusers(fake)).toEqual([['ACL', 'DELUSER', 'seat']]);
57
+ expect([...fake.acl.keys()].sort()).toEqual(survivors);
58
+ });
59
+
60
+ test('delete on state written before managedUsers existed falls back as managedUserNames does', async () => {
61
+ const fake = instance();
62
+ const { managedUsers: _dropped, ...legacy } = await stored(fake);
63
+ expect(legacy).not.toHaveProperty('managedUsers');
64
+ expect(managedUserNames(legacy, props)).toEqual(['seat']);
65
+ await remove(fake, props, legacy);
66
+ expect(delusers(fake)).toEqual([['ACL', 'DELUSER', ...managedUserNames(legacy, props)]]);
67
+ expect([...fake.acl.keys()].sort()).toEqual(survivors);
68
+ });
69
+
70
+ test('read-without-output seals empty, so legacy state with no managedUsers and no previous answers []', async () => {
71
+ // ★ Apply.ts calls reconcile with `olds: undefined` and `output: attr` on both create (:924)
72
+ // and replacement (:1321), never a bare read. The empty-seal fallback in `managedUserNames`
73
+ // (`previous === undefined`) therefore only ever fires on state that predates `managedUsers`
74
+ // AND was last read without an output — where `[]` is the safe default, not a Valkey adoption
75
+ // bug. A read with no output mints an empty seal (acl-ops.ts `toUserAttributes`), and with no
76
+ // `previous` to lean on, that is indistinguishable from "nothing we ever managed".
77
+ const fake = makeFakeValkey({ acl: { seat: 'user seat on >FAKE-seed ~seat:* +@read' } });
78
+ const bare = await Effect.runPromise(readWithExecutor(fake, 'scratch'));
79
+ expect(bare.users.seat?.passwordSeal).toBe('');
80
+ expect(managedUserNames({ instance: 'scratch', users: bare.users }, undefined)).toEqual([]);
81
+ });
82
+
83
+ test('delete after a failed update spares users the last good reconcile never managed', async () => {
84
+ // The engine commits the NEW declaration as `olds` before reconcile runs. If reconcile then
85
+ // fails, `olds` names `draft` and a foreign live user (`operator`) while `output.managedUsers`
86
+ // still holds the last successful ownership record.
87
+ const fake = instance();
88
+ const output = await stored(fake);
89
+ const failedUpdate: ValkeyAclFileProps = {
90
+ instance: 'scratch',
91
+ users: { seat, draft, operator: foreign },
92
+ };
93
+ await remove(fake, failedUpdate, output);
94
+ expect(delusers(fake)).toEqual([['ACL', 'DELUSER', 'seat']]);
95
+ expect([...fake.acl.keys()].sort()).toEqual(survivors);
96
+ });
97
+
98
+ test('delete removes only output.managedUsers, never users listed in olds', async () => {
99
+ const fake = instance();
100
+ const output = await stored(fake);
101
+ // `olds` claims live users (`operator`) and reserved names (`admin`, `default`) that the
102
+ // declaration no longer manages. Only `output.managedUsers` may be targets.
103
+ const corruptedOlds: ValkeyAclFileProps = {
104
+ instance: 'scratch',
105
+ users: { seat, operator: foreign, admin: foreign, default: foreign },
106
+ } as unknown as ValkeyAclFileProps;
107
+ await remove(fake, corruptedOlds, { ...output, managedUsers: ['seat'] });
108
+ expect(delusers(fake)).toEqual([['ACL', 'DELUSER', 'seat']]);
109
+ expect([...fake.acl.keys()].sort()).toEqual(['admin', 'monitor', 'operator']);
110
+ });
111
+
112
+ test('delete after a partially-applied failed update spares users it never managed', async () => {
113
+ // The engine committed the new declaration as `olds` before reconcile ran. Reconcile then
114
+ // partially applied `draft` before failing; `output` still carries the last successful
115
+ // ownership record (`managedUsers: ['seat']`) but `draft` is already live.
116
+ const partial = makeFakeValkey({
117
+ acl: { admin, operator, monitor, draft: draftAcl },
118
+ passwords: { admin: 'FAKE-admin' },
119
+ });
120
+ const output = await stored(partial);
121
+ expect(Object.keys(output.users).sort()).toEqual(['draft', 'monitor', 'operator', 'seat']);
122
+ expect(output.managedUsers).toEqual(['seat']);
123
+ const failedUpdate: ValkeyAclFileProps = { instance: 'scratch', users: { seat, draft } };
124
+ await remove(partial, failedUpdate, output);
125
+ expect(delusers(partial)).toEqual([['ACL', 'DELUSER', 'seat']]);
126
+ expect([...partial.acl.keys()].sort()).toEqual(['admin', 'draft', 'monitor', 'operator']);
127
+ });