@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,211 @@
1
+ /**
2
+ * Refusals, wire bodies, the read shape and the comparison for `LiteLLM.Credential`, testable
3
+ * without a server. Value handling is credential-values.ts's.
4
+ *
5
+ * ⛔ WHAT IS COMPARED, WITH THE VENDOR'S MASKING IN MIND. Both reads answer `credential_values`
6
+ * with SENSITIVE-keyed values masked (2 + `****` + 2, `*****` for a short string —
7
+ * `litellm_logging.py::_get_masked_values`, measured in the live 1.103.0 container) and
8
+ * `credential_info` in full:
9
+ * - `credential_info` is the complete intended map, including on adoption. Nonempty PATCH
10
+ * normally replaces DB info (`credential_endpoints/endpoints.py:314-319`, 1.103.0), while
11
+ * memory merges (:385-387). Empty info leaves DB unchanged. Dropping a live info key therefore
12
+ * requires a rewrite to converge both stores (`removedInfoKeys`). Every PATCH sends full info.
13
+ * - the values are compared only through the seal of the values this process RESOLVED
14
+ * (`valuesSeal`, credential-values.ts) — never against the masked fragments, which are
15
+ * partial secrets and are never copied into state.
16
+ * ★ A CHANGED ROW IS PATCHED, NOT REWRITTEN. The vendor's PATCH merges VALUES by key
17
+ * (`update_db_credential`:312), so updating a value or an `info` entry is a PATCH carrying the
18
+ * declared `credential_info` and (only when the values are stale) the resolved
19
+ * `credential_values`. A PATCH that fails on the wire leaves the row it was merging into — the
20
+ * fix for the review's finding 3, where a DELETE + POST rewrite left NO row when the POST failed
21
+ * after the DELETE. PATCH cannot REMOVE a value key or an in-memory info key, so dropping either
22
+ * is still a whole-row rewrite (credential.ts decides). Names from the read prove removal. The
23
+ * PATCH body carries `credential_name` IN THE BODY as a second member (`credential_name_body`,
24
+ * wire-named `credential_name`) because the vendor's `UpdateCredentialItem` requires it and a
25
+ * Smithy member has one binding — the path label can't also be the body field; the distilled
26
+ * patch for it is `patches/credential_management/update_credential_credentials__credential_name__patch.json`.
27
+ */
28
+ import type * as credentials from '@distilled.cloud/litellm/credential_management';
29
+ import * as Redacted from 'effect/Redacted';
30
+ import {
31
+ type CredentialAttributes,
32
+ type CredentialProps,
33
+ isBlank,
34
+ isSensitiveKey,
35
+ } from './credential-types.ts';
36
+ import type { ResolvedValues } from './credential-values.ts';
37
+
38
+ /** The first reason a declaration is refused, or `undefined`. Pure: no environment, no server. */
39
+ export const firstProblem = (props: CredentialProps): string | undefined => {
40
+ if (isBlank(props.credentialName) || props.credentialName !== props.credentialName.trim()) {
41
+ return '`credentialName` must be a non-empty string without leading or trailing whitespace';
42
+ }
43
+ if (
44
+ props.credentialInfo !== undefined &&
45
+ (typeof props.credentialInfo !== 'object' || props.credentialInfo === null)
46
+ ) {
47
+ return '`credentialInfo` must be an object of metadata';
48
+ }
49
+ if (props.credentialInfo !== undefined) {
50
+ for (const key of Object.keys(props.credentialInfo)) {
51
+ if (isSensitiveKey(key)) {
52
+ return (
53
+ `\`credentialInfo\` must not declare "${key}": LiteLLM returns \`credential_info\` ` +
54
+ 'unmasked, so declare it in `credentialValues`, which LiteLLM stores encrypted'
55
+ );
56
+ }
57
+ }
58
+ }
59
+ if (Object.keys(props.credentialValues).length === 0) {
60
+ return (
61
+ '`credentialValues` needs at least one entry: LiteLLM refuses a credential with neither ' +
62
+ '`credential_values` nor a model_id, and `model_id` is not modelled here'
63
+ );
64
+ }
65
+ for (const key of Object.keys(props.credentialValues)) {
66
+ if (isBlank(key) || key !== key.trim()) {
67
+ return '`credentialValues` keys must be non-empty strings without padding';
68
+ }
69
+ const entry = props.credentialValues[key];
70
+ if (typeof entry !== 'object' || entry === null || isBlank(entry.fromEnv)) {
71
+ return `\`credentialValues["${key}"]\` takes \`{ fromEnv: 'NAME' }\`, never a value: the value would land in Alchemy's unencrypted state`;
72
+ }
73
+ }
74
+ return undefined;
75
+ };
76
+
77
+ /**
78
+ * Wire names of the fields where the live row and the declaration disagree, for a
79
+ * `NotConvergedError` and for the drift decision. The NAME is identity — a changed name plans a
80
+ * REPLACE in credential.ts and is never compared here. Values are never compared against the
81
+ * live row at all: the seal decides (credential-values.ts).
82
+ */
83
+ export const differing = (
84
+ live: CredentialAttributes,
85
+ props: CredentialProps,
86
+ ): readonly string[] => {
87
+ const out: string[] = [];
88
+ if (props.credentialInfo !== undefined) {
89
+ for (const [key, declared] of Object.entries(props.credentialInfo)) {
90
+ if (!isSensitiveKey(key) && !jsonEquals(live.credentialInfo[key], declared)) {
91
+ out.push(`credential_info.${key}`);
92
+ }
93
+ }
94
+ }
95
+ return out;
96
+ };
97
+
98
+ /** Canonical-JSON deep equality, order-insensitive for objects. */
99
+ const jsonEquals = (left: unknown, right: unknown): boolean =>
100
+ JSON.stringify(canonicalise(left)) === JSON.stringify(canonicalise(right));
101
+
102
+ const canonicalise = (value: unknown): unknown => {
103
+ if (Array.isArray(value)) return value.map(canonicalise);
104
+ if (typeof value === 'object' && value !== null) {
105
+ const out: Record<string, unknown> = {};
106
+ for (const key of Object.keys(value as Record<string, unknown>).sort()) {
107
+ out[key] = canonicalise((value as Record<string, unknown>)[key]);
108
+ }
109
+ return out;
110
+ }
111
+ return value;
112
+ };
113
+
114
+ /**
115
+ * One row of a credentials read, as attributes. ⛔ COPIES NO `credential_values` ENTRY: a masked
116
+ * fragment (`sk****45`) is a partial secret, and a sensitive key the read masks would land half a
117
+ * secret in Alchemy's state. The `credential_info` mirror drops SENSITIVE-KEYED entries (they are
118
+ * returned unmasked and would mirror a clear secret into state). The digest is the caller's
119
+ * (credential.ts fills `valuesSeal` from state; a row cannot supply one).
120
+ */
121
+ export const toAttributes = (row: Record<string, unknown>): CredentialAttributes => {
122
+ const info = (row['credential_info'] ?? {}) as Record<string, unknown>;
123
+ const mirrored: Record<string, unknown> = {};
124
+ for (const [key, value] of Object.entries(info)) {
125
+ if (!isSensitiveKey(key)) mirrored[key] = value;
126
+ }
127
+ return {
128
+ credentialInfo: mirrored,
129
+ credentialName: String(row['credential_name'] ?? ''),
130
+ valueKeys: Object.keys((row['credential_values'] ?? {}) as Record<string, unknown>).sort(),
131
+ infoKeys: Object.keys(info).sort(),
132
+ valuesSeal: '',
133
+ };
134
+ };
135
+
136
+ /** The rows a credentials read carries, `CredentialItem`-shaped. */
137
+ export const isCredentialRow = (row: unknown): row is Record<string, unknown> =>
138
+ typeof row === 'object' &&
139
+ row !== null &&
140
+ typeof (row as Record<string, unknown>)['credential_name'] === 'string';
141
+
142
+ /**
143
+ * The literal values a create or a values-stale PATCH sends, unwrapped from their `Redacted`
144
+ * exactly once. ⛔ EVERY DECLARED ENTRY IS SENT: the whole-row create and the values-stale PATCH
145
+ * both demand every declared value (`requireValues`), so there is no partial send that could
146
+ * leave a stale value behind under a declared key.
147
+ */
148
+ export const literalValues = (resolved: ResolvedValues): Readonly<Record<string, unknown>> =>
149
+ Object.fromEntries(
150
+ Object.entries(resolved.values).map(([key, value]) => [key, Redacted.value(value)]),
151
+ );
152
+
153
+ /** The body `POST /credentials` carries — the name IN THE BODY (measured with a stub fetch). */
154
+ export const createBody = (
155
+ props: CredentialProps,
156
+ resolved: ResolvedValues,
157
+ ): credentials.CreateCredentialCredentialsPostRequest => {
158
+ const body: Record<string, unknown> = {
159
+ credential_info: { ...props.credentialInfo },
160
+ credential_name: props.credentialName,
161
+ credential_values: { ...literalValues(resolved) },
162
+ };
163
+ return body as unknown as credentials.CreateCredentialCredentialsPostRequest;
164
+ };
165
+
166
+ /**
167
+ * The body `PATCH /credentials/{name}` carries — the declared `credential_info` always (the
168
+ * vendor's `UpdateCredentialItem` REQUIRES it, and the DB normally replaces the info map), the
169
+ * resolved `credential_values` only when the caller passes them (a values-stale write; an
170
+ * info-only drift sends none, so it never demands a value the environment does not hold).
171
+ *
172
+ * ⛔ `credential_name` APPEARS TWICE, ON PURPOSE: `credential_name` is the path label (its URI
173
+ * placeholder is the row's address), and `credential_name_body` is the SAME value as a body
174
+ * field wire-named `credential_name` — the vendor's `UpdateCredentialItem` requires the name in
175
+ * the body, a Smithy member can carry only ONE binding, and the kit never renames in place (a
176
+ * changed name is a REPLACE, credential.ts), so the two are always equal here.
177
+ */
178
+ export const patchBody = (
179
+ props: CredentialProps,
180
+ values: Readonly<Record<string, unknown>> | undefined,
181
+ ): credentials.UpdateCredentialCredentialsCredentialNamePatchRequest => {
182
+ const body: Record<string, unknown> = {
183
+ credential_info: { ...props.credentialInfo },
184
+ credential_name: props.credentialName,
185
+ credential_name_body: props.credentialName,
186
+ };
187
+ if (values !== undefined) body.credential_values = { ...values };
188
+ return body as unknown as credentials.UpdateCredentialCredentialsCredentialNamePatchRequest;
189
+ };
190
+
191
+ /**
192
+ * All live info keys absent from the intended map, including on adoption. DB replacement alone
193
+ * cannot clear memory, so these require a rewrite. Only names of sensitive info are remembered.
194
+ */
195
+ export const removedInfoKeys = (
196
+ prior: CredentialAttributes | undefined,
197
+ props: CredentialProps,
198
+ ): readonly string[] => {
199
+ if (prior === undefined) return [];
200
+ const declared = props.credentialInfo ?? {};
201
+ return (prior.infoKeys ?? Object.keys(prior.credentialInfo)).filter(
202
+ (key) => !Object.hasOwn(declared, key),
203
+ );
204
+ };
205
+
206
+ /** Values merge in BOTH stores, so dropped names from the masked read require a rewrite. */
207
+ export const removedValueKeys = (
208
+ live: CredentialAttributes | undefined,
209
+ props: CredentialProps,
210
+ ): readonly string[] =>
211
+ (live?.valueKeys ?? []).filter((key) => !Object.hasOwn(props.credentialValues, key));
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The guards `LiteLLM.Credential` adds on top of the typed SDK calls, tested at the handler level
3
+ * (a value is in the request body of `POST /credentials`, so a create that dies on the wire, or one
4
+ * made while the SDK's debug printing is on, would put it where nobody should see it) and through
5
+ * Alchemy's real Plan and Apply (a rename onto a row another owner holds must be refused, not
6
+ * overwritten).
7
+ *
8
+ * ★ EVERY VALUE IS `FAKE-*`, and a test that asserts a value is absent needs a value it can search
9
+ * for: `FAKE-key-one` is the credential value the declaration resolves from `process.env`.
10
+ */
11
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
12
+ import { credentials } from '@distilled.cloud/litellm/Credentials';
13
+ import { Retry } from '@distilled.cloud/litellm/Retry';
14
+ import * as Effect from 'effect/Effect';
15
+ import * as Layer from 'effect/Layer';
16
+ import * as Logger from 'effect/Logger';
17
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
18
+ import { credentialHandlers } from './credential.ts';
19
+ import type { CredentialProps } from './credential-types.ts';
20
+ import { resolveValues, sealValues } from './credential-values.ts';
21
+ import { credentialRow, startFakeCredentialLitellm } from './fake-credential-litellm.ts';
22
+ import { FAKE_BASE } from './fake-litellm.ts';
23
+ import { fakeStack, writesOf } from './fake-stack.ts';
24
+ import { LiteLLMCredential } from './credential.ts';
25
+
26
+ const KEY = 'sk-test-master';
27
+ const VARIABLE = 'FAKE_CREDENTIAL_GUARD_KEY';
28
+ const VALUE = 'FAKE-key-one';
29
+
30
+ const props: CredentialProps = {
31
+ credentialInfo: { note: 'docs search' },
32
+ credentialName: 'FAKE_api',
33
+ credentialValues: { api_key: { fromEnv: VARIABLE } },
34
+ };
35
+
36
+ beforeEach(() => {
37
+ process.env[VARIABLE] = VALUE;
38
+ });
39
+ afterEach(() => {
40
+ delete process.env[VARIABLE];
41
+ delete process.env.DISTILLED_DEBUG_HTTP;
42
+ });
43
+
44
+ type Fake = ReturnType<typeof startFakeCredentialLitellm>;
45
+
46
+ /** One reconcile effect at the handler level, against the fake, with the SDK's retry OFF. */
47
+ const reconcileEffect = (
48
+ fake: Fake,
49
+ news: CredentialProps,
50
+ output: Parameters<typeof credentialHandlers.reconcile>[0]['output'] = undefined,
51
+ ) =>
52
+ credentialHandlers
53
+ .reconcile({ fqn: 'Cred', instanceId: 'i-1', news, output })
54
+ .pipe(
55
+ Effect.provideService(Retry, { while: () => false }),
56
+ Effect.provide(FetchHttpClient.layer),
57
+ Effect.provide(Layer.succeed(FetchHttpClient.Fetch, fake.fetch)),
58
+ Effect.provide(credentials({ apiKey: KEY, baseUrl: FAKE_BASE })),
59
+ ) as Effect.Effect<unknown, unknown, never>;
60
+
61
+ /** Runs the reconcile to a value, or the failure it raised. */
62
+ const runReconcile = (fake: Fake, news: CredentialProps): Promise<unknown> =>
63
+ Effect.runPromise(Effect.flip(reconcileEffect(fake, news)));
64
+
65
+ /** A fake whose `POST /credentials` drops the connection before any answer. */
66
+ const droppedCreate = (fake: Fake): Fake => ({
67
+ ...fake,
68
+ fetch: (async (input: string | URL | Request, init?: RequestInit) => {
69
+ const request = input instanceof Request ? input : new Request(String(input), init);
70
+ if (new URL(request.url).pathname === '/credentials' && request.method === 'POST') {
71
+ throw new TypeError('fetch failed');
72
+ }
73
+ return fake.fetch(input, init);
74
+ }) as typeof globalThis.fetch,
75
+ });
76
+
77
+ /** A fake whose `PATCH /credentials/{name}` drops the connection before any answer. */
78
+ const droppedPatch = (fake: Fake): Fake => ({
79
+ ...fake,
80
+ fetch: (async (input: string | URL | Request, init?: RequestInit) => {
81
+ const request = input instanceof Request ? input : new Request(String(input), init);
82
+ if (request.method === 'PATCH' && new URL(request.url).pathname.startsWith('/credentials/')) {
83
+ const body = (await request.json()) as Record<string, unknown>;
84
+ expect(Object.keys((body.credential_values ?? {}) as object)).toEqual(['api_key']);
85
+ throw new TypeError('fetch failed');
86
+ }
87
+ return fake.fetch(input, init);
88
+ }) as typeof globalThis.fetch,
89
+ });
90
+
91
+ /** What Effect's JSON logger prints for the value. */
92
+ const jsonLogged = async (value: unknown): Promise<string> => {
93
+ const lines: string[] = [];
94
+ const capture = Logger.make((options) => void lines.push(Logger.formatJson.log(options)));
95
+ await Effect.runPromise(
96
+ Effect.logError('create failed', value).pipe(Effect.provide(Logger.layer([capture]))),
97
+ );
98
+ return lines.join('\n');
99
+ };
100
+
101
+ /** Every reading of `value` a caller could take: serialise, inspect, print, log. */
102
+ const everyReadingOf = async (value: unknown): Promise<string> => {
103
+ const readings = [Bun.inspect(value, { depth: 12 }), String(value), await jsonLogged(value)];
104
+ try {
105
+ readings.push(JSON.stringify(value));
106
+ } catch {
107
+ /* a value JSON cannot serialise leaks nothing that way */
108
+ }
109
+ if (typeof value === 'object' && value !== null && 'message' in value) {
110
+ readings.push(String(value.message), 'stack' in value ? String(value.stack) : '');
111
+ }
112
+ return readings.join('\n');
113
+ };
114
+
115
+ describe('a rename onto a row another owner holds', () => {
116
+ test('is refused at apply, the foreign row untouched, nothing written', async () => {
117
+ const fake = startFakeCredentialLitellm({
118
+ masterKey: KEY,
119
+ seed: [
120
+ credentialRow({
121
+ credential_info: { note: 'another owner' },
122
+ credential_name: 'FAKE_api2',
123
+ credential_values: { api_key: 'FAKE-elsewhere' },
124
+ }),
125
+ ],
126
+ });
127
+ const engine = fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
128
+ const declare = (p: CredentialProps) =>
129
+ Effect.gen(function* () {
130
+ yield* LiteLLMCredential('Cred', p);
131
+ });
132
+ // Own `FAKE_api` first, then rename onto the seeded `FAKE_api2`.
133
+ await engine.deploy(declare(props));
134
+ const before = fake.requests().length;
135
+ await expect(engine.deploy(declare({ ...props, credentialName: 'FAKE_api2' }))).rejects.toThrow(
136
+ /already exists/,
137
+ );
138
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
139
+ expect(fake.rows().find((row) => row['credential_name'] === 'FAKE_api2')).toMatchObject({
140
+ credential_info: { note: 'another owner' },
141
+ credential_values: { api_key: 'FAKE-elsewhere' },
142
+ });
143
+ });
144
+
145
+ test('is refused even under --adopt (a replace is never probed for adoption)', async () => {
146
+ const fake = startFakeCredentialLitellm({
147
+ masterKey: KEY,
148
+ seed: [credentialRow({ credential_name: 'FAKE_api2', credential_values: { api_key: 'x' } })],
149
+ });
150
+ const engine = fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
151
+ const declare = (p: CredentialProps) =>
152
+ Effect.gen(function* () {
153
+ yield* LiteLLMCredential('Cred', p);
154
+ });
155
+ await engine.deploy(declare(props));
156
+ const before = fake.requests().length;
157
+ await expect(
158
+ engine.deploy(declare({ ...props, credentialName: 'FAKE_api2' }), { adopt: true }),
159
+ ).rejects.toThrow(/--adopt does not cover/);
160
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
161
+ });
162
+ });
163
+
164
+ describe('a create that dies on the wire', () => {
165
+ test('fails typed, names the credential, and holds no request and no value', async () => {
166
+ const fake = droppedCreate(startFakeCredentialLitellm({ masterKey: KEY }));
167
+ const error = await runReconcile(fake, props);
168
+ expect(error).toMatchObject({
169
+ _tag: 'LitellmCredentialTransportError',
170
+ credentialName: 'FAKE_api',
171
+ reason: 'TransportError',
172
+ });
173
+ expect(Object.keys(error as object)).not.toContain('request');
174
+ expect(Object.keys(error as object)).not.toContain('cause');
175
+ expect(await everyReadingOf(error)).not.toContain(VALUE);
176
+ });
177
+ });
178
+
179
+ describe('an update that dies on the wire', () => {
180
+ test('leaves the row in place, fails typed, holds no value', async () => {
181
+ // The owned row has a stale seal: this failing PATCH MUST carry credential values.
182
+ const fake = droppedPatch(
183
+ startFakeCredentialLitellm({
184
+ masterKey: KEY,
185
+ seed: [
186
+ credentialRow({
187
+ credential_info: { note: 'docs search' },
188
+ credential_name: 'FAKE_api',
189
+ credential_values: { api_key: VALUE },
190
+ }),
191
+ ],
192
+ }),
193
+ );
194
+ const sealed = sealValues(resolveValues(props).values);
195
+ process.env[VARIABLE] = 'FAKE-key-rotated';
196
+ const error = await Effect.runPromise(
197
+ Effect.flip(
198
+ reconcileEffect(fake, props, {
199
+ credentialInfo: { note: 'docs search' },
200
+ credentialName: 'FAKE_api',
201
+ valuesSeal: sealed,
202
+ }),
203
+ ),
204
+ );
205
+ expect(error).toMatchObject({
206
+ _tag: 'LitellmCredentialTransportError',
207
+ credentialName: 'FAKE_api',
208
+ reason: 'TransportError',
209
+ });
210
+ expect(Object.keys(error as object)).not.toContain('request');
211
+ expect(await everyReadingOf(error)).not.toContain('FAKE-key-rotated');
212
+ // ★ finding 3: a PATCH that dies leaves the row it was merging into — the old DELETE + POST
213
+ // rewrite left NO row when the POST failed after the DELETE.
214
+ expect(fake.rows()).toHaveLength(1);
215
+ expect(fake.rows()[0]?.['credential_info']).toEqual({ note: 'docs search' });
216
+ expect(fake.rows()[0]?.['credential_values']).toEqual({ api_key: VALUE });
217
+ });
218
+ });
219
+
220
+ describe('a create while the SDK prints bodies', () => {
221
+ test('is refused before any request, naming the credential', async () => {
222
+ process.env.DISTILLED_DEBUG_HTTP = '1';
223
+ const fake = startFakeCredentialLitellm({ masterKey: KEY });
224
+ const error = await runReconcile(fake, props);
225
+ expect(error).toMatchObject({
226
+ _tag: 'LitellmCredentialDebugLoggingError',
227
+ credentialName: 'FAKE_api',
228
+ });
229
+ // Refuse at the top of reconcile: even a read can expose unmasked non-sensitive values.
230
+ expect(writesOf(fake.requests())).toEqual([]);
231
+ });
232
+ });
233
+
234
+ describe('an absent credential is caught as absence, never a failure', () => {
235
+ test('reconcile of a create with no live row converges (the read 404 is CredentialNotFound)', async () => {
236
+ const fake = startFakeCredentialLitellm({ masterKey: KEY });
237
+ const result = await Effect.runPromise(reconcileEffect(fake, props));
238
+ expect(result).toMatchObject({ credentialName: 'FAKE_api' });
239
+ expect(writesOf(fake.requests())).toEqual(['POST /credentials']);
240
+ });
241
+ });
@@ -0,0 +1,208 @@
1
+ /**
2
+ * The `@distilled.cloud/litellm/credential_management` calls `LiteLLM.Credential` uses
3
+ * (LiteLLM 1.103.0; names verified in the generated source, never guessed):
4
+ *
5
+ * read `getCredentialByNameCredentialsByNameCredentialNameGet` GET /credentials/by_name/{name}
6
+ * create `createCredentialCredentialsPost` POST /credentials
7
+ * update `updateCredentialCredentialsCredentialNamePatch` PATCH /credentials/{name}
8
+ * delete `deleteCredentialCredentialsCredentialNameDelete` DELETE /credentials/{name}
9
+ *
10
+ * ★ THE UPDATE IS A PATCH, NOT A REWRITE. The vendor's PATCH merges value keys; nonempty info
11
+ * normally replaces the DB map but merges in memory (`endpoints.py:312-319,384-387`, 1.103.0).
12
+ * The body carries `credential_name` as a second member
13
+ * (`credential_name_body`, wire-named `credential_name`) because the vendor's
14
+ * `UpdateCredentialItem` requires it in the body and a Smithy member has one binding — the
15
+ * distilled patch `patches/credential_management/update_credential_credentials__credential_name__patch.json`.
16
+ * A PATCH that fails on the wire leaves the row it was merging into, where the old DELETE + POST
17
+ * rewrite left NO row when the POST failed after the DELETE (review finding 3). credential.ts
18
+ * still rewrites to drop value or info keys, since PATCH cannot remove them from both stores.
19
+ * ⛔ NO MESSAGE SNIFFING (S21). The read's and delete's `404` is typed as the SDK's
20
+ * `CredentialNotFound` (a distilled patch matched on the vendor's "Credential not found" message,
21
+ * `endpoints.py`), so absence is that TAG, `catchTag`ed — never `instanceof`, never error text. A
22
+ * 404 from a front proxy, Access, or a wrong base path decodes as core's `NotFound` and stays an
23
+ * error, never absence. A create on a name the DB already has answers `409` (`endpoints.py`'s
24
+ * unique-violation handling), decoded as core `Conflict`, and is PROPAGATED: a row another writer
25
+ * raced in is not this declaration's row. The PATCH's own 404 (message `Credential not found in
26
+ * DB.`) is NOT `CredentialNotFound`: it stays core `NotFound`, an error — a row deleted between
27
+ * the read and the PATCH must not silently read as absence.
28
+ * ⚠️ THE READ AND THE DELETE ASK DIFFERENT STORES (measured, `endpoints.py`): the by-name read
29
+ * walks the proxy's IN-MEMORY `credential_list` (a row the proxy has not loaded answers 404),
30
+ * while the delete consults the DATABASE (`delete_by_name`) and removes the in-memory entry
31
+ * after. A row present in the DB but not loaded in memory reads as ABSENT, and a create for it
32
+ * answers `409` — the resource reports that failure loudly instead of pretending to converge.
33
+ * ⚠️ Unlike pass-through endpoints, each credential is its own DB row: no semaphore.
34
+ */
35
+ import * as credentials from '@distilled.cloud/litellm/credential_management';
36
+ import * as Effect from 'effect/Effect';
37
+ import * as Predicate from 'effect/Predicate';
38
+ import { isHttpClientError } from 'effect/unstable/http/HttpClientError';
39
+ import { isCredentialRow, toAttributes } from './credential-form.ts';
40
+ import type { CredentialAttributes } from './credential-types.ts';
41
+ import {
42
+ LitellmCredentialDebugLoggingError,
43
+ LitellmCredentialTransportError,
44
+ LitellmCredentialUnreadableError,
45
+ } from './credential-errors.ts';
46
+ import { type LitellmOpContext, throughFetch } from './operations.ts';
47
+
48
+ /**
49
+ * One credential by name, or `undefined` when the proxy does not hold it in memory. ⛔ ANY FAILURE
50
+ * OTHER THAN `CredentialNotFound` PROPAGATES: a 401, a 422 or a dead network says nothing about
51
+ * whether the row exists, and must never be read as absence.
52
+ */
53
+ export const readCredential = (
54
+ credentialName: string,
55
+ ): Effect.Effect<
56
+ CredentialAttributes | undefined,
57
+ | credentials.GetCredentialByNameCredentialsByNameCredentialNameGetError
58
+ | LitellmCredentialUnreadableError,
59
+ LitellmOpContext
60
+ > =>
61
+ throughFetch(
62
+ credentials.getCredentialByNameCredentialsByNameCredentialNameGet({
63
+ credential_name: credentialName,
64
+ }),
65
+ ).pipe(
66
+ Effect.catchTag('CredentialNotFound', () => Effect.succeed(undefined)),
67
+ Effect.flatMap((response) => {
68
+ if (response === undefined) return Effect.succeed(undefined);
69
+ if (!isCredentialRow(response)) {
70
+ return Effect.fail(
71
+ new LitellmCredentialUnreadableError({
72
+ reason: 'the by-name read did not answer a credential row',
73
+ }),
74
+ );
75
+ }
76
+ const answer = String(response['credential_name']);
77
+ // The name is re-checked on the answer: a proxy that answered some other row must never be
78
+ // adopted as this one's.
79
+ return answer === credentialName
80
+ ? Effect.succeed(toAttributes(response))
81
+ : Effect.fail(
82
+ new LitellmCredentialUnreadableError({
83
+ reason: 'the by-name read answered a different credential name',
84
+ }),
85
+ );
86
+ }),
87
+ );
88
+
89
+ /**
90
+ * POST /credentials. The answer carries no row (`{"success": true, ...}`), so the read back is the
91
+ * proof.
92
+ *
93
+ * ⛔ THE REQUEST BODY CARRIES THE VALUES, AND A FAILED CALL CAN CARRY ITS REQUEST — exactly the
94
+ * `{"key":"sk-…"}` leak `generateKey` (key-operations.ts) closes, and it is closed the same way:
95
+ * a transport failure is an `HttpClientError` in the error channel, a body that fails mid-read is
96
+ * the SAME error as a DEFECT (`Effect.orDie` in core's `protocol-rest.ts`). Both become
97
+ * `LitellmCredentialTransportError`, which keeps the NAME and the reason's tag and nothing else.
98
+ * ⛔ RECONCILE REFUSES WHILE `DISTILLED_DEBUG_HTTP` IS SET, before any request: the SDK prints
99
+ * the first 400 characters of every request body (core `protocol-http.ts`), which would put the values on
100
+ * stderr — the same refusal `key-secret.ts` makes for `/key/generate`.
101
+ */
102
+ export const createCredential = (
103
+ body: credentials.CreateCredentialCredentialsPostRequest,
104
+ ): Effect.Effect<
105
+ void,
106
+ credentials.CreateCredentialCredentialsPostError | LitellmCredentialTransportError,
107
+ LitellmOpContext
108
+ > => {
109
+ const credentialName = body.credential_name ?? '';
110
+ return Effect.gen(function* () {
111
+ yield* throughFetch(credentials.createCredentialCredentialsPost(body)).pipe(
112
+ Effect.asVoid,
113
+ Effect.catchTag('HttpClientError', (error) =>
114
+ Effect.fail(
115
+ new LitellmCredentialTransportError({ credentialName, reason: error.reason._tag }),
116
+ ),
117
+ ),
118
+ Effect.catchDefect((defect) =>
119
+ carriesRequest(defect)
120
+ ? Effect.fail(
121
+ new LitellmCredentialTransportError({
122
+ credentialName,
123
+ reason: isHttpClientError(defect) ? defect.reason._tag : 'Defect',
124
+ }),
125
+ )
126
+ : Effect.die(defect),
127
+ ),
128
+ );
129
+ });
130
+ };
131
+
132
+ /**
133
+ * PATCH /credentials/{name}. Sends the full intended `credential_info` (DB replacement, memory
134
+ * merge) and merges resolved `credential_values` when present. The answer carries no row
135
+ * (`{"success": true, ...}`), so the read back is the proof.
136
+ *
137
+ * ⛔ THE SAME TWO GUARDS AS `createCredential`: the body carries the values, so a transport failure
138
+ * becomes a `LitellmCredentialTransportError` that keeps the NAME and the reason's tag and
139
+ * nothing else, and reconcile refuses while `DISTILLED_DEBUG_HTTP` is set (the SDK would print
140
+ * the values). A PATCH that fails on the wire leaves the row in place — the point of using it
141
+ * over a DELETE + POST rewrite.
142
+ */
143
+ export const updateCredential = (
144
+ body: credentials.UpdateCredentialCredentialsCredentialNamePatchRequest,
145
+ ): Effect.Effect<
146
+ void,
147
+ credentials.UpdateCredentialCredentialsCredentialNamePatchError | LitellmCredentialTransportError,
148
+ LitellmOpContext
149
+ > => {
150
+ const credentialName = body.credential_name ?? '';
151
+ return Effect.gen(function* () {
152
+ yield* throughFetch(credentials.updateCredentialCredentialsCredentialNamePatch(body)).pipe(
153
+ Effect.asVoid,
154
+ Effect.catchTag('HttpClientError', (error) =>
155
+ Effect.fail(
156
+ new LitellmCredentialTransportError({ credentialName, reason: error.reason._tag }),
157
+ ),
158
+ ),
159
+ Effect.catchDefect((defect) =>
160
+ carriesRequest(defect)
161
+ ? Effect.fail(
162
+ new LitellmCredentialTransportError({
163
+ credentialName,
164
+ reason: isHttpClientError(defect) ? defect.reason._tag : 'Defect',
165
+ }),
166
+ )
167
+ : Effect.die(defect),
168
+ ),
169
+ );
170
+ });
171
+ };
172
+
173
+ /**
174
+ * DELETE /credentials/{name}, idempotent BY THIS FUNCTION: the vendor answers 404 for a name its
175
+ * database does not have, which this treats as already-absent — the DB is the authoritative store
176
+ * the delete consults (the read consults memory, see the header). Any other failure propagates.
177
+ */
178
+ export const deleteCredential = (
179
+ credentialName: string,
180
+ ): Effect.Effect<
181
+ void,
182
+ credentials.DeleteCredentialCredentialsCredentialNameDeleteError,
183
+ LitellmOpContext
184
+ > =>
185
+ throughFetch(
186
+ credentials.deleteCredentialCredentialsCredentialNameDelete({
187
+ credential_name: credentialName,
188
+ }),
189
+ ).pipe(
190
+ Effect.asVoid,
191
+ Effect.catchTag('CredentialNotFound', () => Effect.void),
192
+ );
193
+
194
+ /**
195
+ * Whether `DISTILLED_DEBUG_HTTP` is set, refused once at the top of reconcile, before even a
196
+ * DELETE can run (the following POST would carry values). Reads `process.env` because that is
197
+ * what the SDK's `protocol-http.ts` reads.
198
+ */
199
+ export const refuseDebugLogging = (
200
+ credentialName: string,
201
+ ): Effect.Effect<void, LitellmCredentialDebugLoggingError> =>
202
+ (globalThis.process?.env?.['DISTILLED_DEBUG_HTTP'] ?? '') !== ''
203
+ ? Effect.fail(new LitellmCredentialDebugLoggingError({ credentialName }))
204
+ : Effect.void;
205
+
206
+ /** An `HttpClientError` (or a defect that is one) carries its request — and thus the values. */
207
+ const carriesRequest = (value: unknown): boolean =>
208
+ isHttpClientError(value) || Predicate.hasProperty(value, 'request');