@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
@@ -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');
@@ -0,0 +1,93 @@
1
+ /** A failed rewrite must describe the completed DELETE and recover on the next real deploy. */
2
+ import { expect, test } from 'bun:test';
3
+ import * as Effect from 'effect/Effect';
4
+ import { type CredentialProps, LiteLLMCredential, credentialHandlers } from './credential.ts';
5
+ import { startFakeCredentialLitellm } from './fake-credential-litellm.ts';
6
+ import { fakeStack, writesOf } from './fake-stack.ts';
7
+ import { FAKE_BASE } from './fake-litellm.ts';
8
+ import { FAKE_KEY, MASTER_KEY, failureOf, newFake, runAgainst, withEnv } from './key-harness.ts';
9
+
10
+ const variable = 'FAKE_RECOVERY_KEY';
11
+ const props: CredentialProps = {
12
+ credentialName: 'recovery',
13
+ credentialInfo: { note: 'search' },
14
+ credentialValues: { api_key: { fromEnv: variable } },
15
+ };
16
+ const original = { ...props, credentialInfo: { ...props.credentialInfo, extra: 'remove' } };
17
+ const declare = (news: CredentialProps) =>
18
+ Effect.gen(function* () {
19
+ yield* LiteLLMCredential('Cred', news);
20
+ });
21
+
22
+ for (const mode of ['transport', 'forbidden', 'validation'] as const) {
23
+ test(`a rewrite POST ${mode} failure reports DELETE and the next deploy recreates`, async () => {
24
+ const fake = startFakeCredentialLitellm({ masterKey: MASTER_KEY });
25
+ let failPost = false;
26
+ let postsFailed = 0;
27
+ const fetch = (async (input, init) => {
28
+ const request =
29
+ input instanceof Request ? new Request(input, init) : new Request(String(input), init);
30
+ if (
31
+ failPost &&
32
+ request.method === 'POST' &&
33
+ new URL(request.url).pathname === '/credentials'
34
+ ) {
35
+ postsFailed += 1;
36
+ if (mode === 'transport') throw new TypeError('connection dropped');
37
+ return new Response(JSON.stringify({ detail: 'replacement rejected' }), {
38
+ status: mode === 'forbidden' ? 403 : 422,
39
+ headers: { 'content-type': 'application/json' },
40
+ });
41
+ }
42
+ return fake.fetch(request);
43
+ }) as typeof globalThis.fetch;
44
+ const engine = fakeStack({ apiKey: MASTER_KEY, baseUrl: FAKE_BASE }, fetch, 'Recovery', {
45
+ noRetry: true,
46
+ });
47
+ await withEnv({ [variable]: FAKE_KEY }, async () => {
48
+ await engine.deploy(declare(original));
49
+ failPost = true;
50
+ const message = await failureOf(engine.deploy(declare(props)));
51
+ expect(message).toContain('DELETE already ran');
52
+ expect(message).toContain('next deploy recreates');
53
+ expect(message.includes(FAKE_KEY)).toBe(false);
54
+ expect(postsFailed).toBe(1);
55
+ expect(fake.rows()).toHaveLength(0);
56
+ expect(writesOf(fake.requests())).toEqual([
57
+ 'POST /credentials',
58
+ 'DELETE /credentials/recovery',
59
+ ]);
60
+ failPost = false;
61
+ await engine.deploy(declare(props));
62
+ expect(fake.rows()).toHaveLength(1);
63
+ expect(fake.rows()[0]?.credential_info).toEqual(props.credentialInfo);
64
+ expect(await engine.deploy(declare(props))).toEqual({ Cred: 'noop' });
65
+ expect(engine.snapshot().includes(FAKE_KEY)).toBe(false);
66
+
67
+ // The same failure is catchable in the error channel, with no request or cause retained.
68
+ await engine.deploy(declare(original));
69
+ failPost = true;
70
+ const error = await runAgainst(
71
+ { ...newFake(), fetch },
72
+ credentialHandlers
73
+ .reconcile({
74
+ fqn: 'Cred',
75
+ instanceId: 'i-1',
76
+ news: props,
77
+ output: {
78
+ credentialName: props.credentialName,
79
+ credentialInfo: original.credentialInfo,
80
+ valuesSeal: '',
81
+ },
82
+ })
83
+ .pipe(
84
+ Effect.catchTag('LitellmCredentialRewriteError', (failure) => Effect.succeed(failure)),
85
+ ),
86
+ );
87
+ expect('_tag' in error && error._tag).toBe('LitellmCredentialRewriteError');
88
+ expect(JSON.stringify(error).includes(FAKE_KEY)).toBe(false);
89
+ expect('cause' in error).toBe(false);
90
+ expect('request' in error).toBe(false);
91
+ });
92
+ });
93
+ }
@@ -0,0 +1,164 @@
1
+ /** Removal needs both LiteLLM stores to converge; preflight must finish before DELETE. */
2
+ import { afterEach, beforeEach, expect, test } from 'bun:test';
3
+ import { credentials } from '@distilled.cloud/litellm/Credentials';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
6
+ import { type CredentialProps, LiteLLMCredential, credentialHandlers } from './credential.ts';
7
+ import { credentialRow, startFakeCredentialLitellm } from './fake-credential-litellm.ts';
8
+ import { FAKE_BASE } from './fake-litellm.ts';
9
+ import { fakeStack, writesOf } from './fake-stack.ts';
10
+
11
+ const variable = 'FAKE_REWRITE_KEY';
12
+ const creds = { apiKey: 'FAKE-master', baseUrl: FAKE_BASE };
13
+ const props: CredentialProps = {
14
+ credentialName: 'FAKE_api',
15
+ credentialInfo: { note: 'search' },
16
+ credentialValues: { api_key: { fromEnv: variable } },
17
+ };
18
+ const declare = (news: CredentialProps) =>
19
+ Effect.gen(function* () {
20
+ yield* LiteLLMCredential('Cred', news);
21
+ });
22
+ const extraValues = {
23
+ ...props,
24
+ credentialValues: {
25
+ ...props.credentialValues,
26
+ api_base: { fromEnv: variable },
27
+ },
28
+ };
29
+ const extraInfo = { ...props, credentialInfo: { ...props.credentialInfo, team: 'a' } };
30
+ const setup = () => {
31
+ const fake = startFakeCredentialLitellm({ masterKey: creds.apiKey });
32
+ return { fake, engine: fakeStack(creds, fake.fetch) };
33
+ };
34
+ beforeEach(() => {
35
+ process.env[variable] = 'FAKE-rewrite-value';
36
+ });
37
+ afterEach(() => {
38
+ delete process.env[variable];
39
+ delete process.env.DISTILLED_DEBUG_HTTP;
40
+ });
41
+
42
+ test('dropping a value key rewrites the row, removes it from DB and memory, then noops', async () => {
43
+ const { fake, engine } = setup();
44
+ await engine.deploy(declare(extraValues));
45
+ const before = fake.requests().length;
46
+ expect(await engine.deploy(declare(props))).toEqual({ Cred: 'update' });
47
+ expect(writesOf(fake.requests().slice(before))).toEqual([
48
+ 'DELETE /credentials/FAKE_api',
49
+ 'POST /credentials',
50
+ ]);
51
+ expect(Object.keys(fake.rows()[0]?.credential_values as object)).toEqual(['api_key']);
52
+ expect(await engine.deploy(declare(props))).toEqual({ Cred: 'noop' });
53
+ expect(engine.snapshot().includes('FAKE-rewrite-value')).toBe(false);
54
+ });
55
+
56
+ for (const [kind, original] of [
57
+ ['value', extraValues],
58
+ ['info', extraInfo],
59
+ ] as const) {
60
+ test(`dropping a ${kind} key with a missing value refuses before DELETE`, async () => {
61
+ const { fake, engine } = setup();
62
+ await engine.deploy(declare(original));
63
+ const before = fake.requests().length;
64
+ delete process.env[variable];
65
+ await expect(engine.deploy(declare(props))).rejects.toThrow(variable);
66
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
67
+ expect(fake.rows()).toHaveLength(1);
68
+ });
69
+
70
+ test(`dropping a ${kind} key under debug logging refuses before any reconcile request`, async () => {
71
+ const { fake, engine } = setup();
72
+ await engine.deploy(declare(original));
73
+ // The refusal is before any observation or write, so even stale state cannot bypass it.
74
+ const before = fake.requests().length;
75
+ process.env.DISTILLED_DEBUG_HTTP = '1';
76
+ const error = await Effect.runPromise(
77
+ credentialHandlers
78
+ .reconcile({
79
+ fqn: 'Cred',
80
+ instanceId: 'i-1',
81
+ news: props,
82
+ output: {
83
+ credentialName: props.credentialName,
84
+ credentialInfo: original.credentialInfo ?? {},
85
+ valuesSeal: '',
86
+ },
87
+ })
88
+ .pipe(
89
+ Effect.catchTag('LitellmCredentialDebugLoggingError', (failure) =>
90
+ Effect.succeed(failure._tag),
91
+ ),
92
+ Effect.provide(FetchHttpClient.layer),
93
+ Effect.provideService(FetchHttpClient.Fetch, fake.fetch),
94
+ Effect.provide(credentials(creds)),
95
+ ),
96
+ );
97
+ expect(error).toBe('LitellmCredentialDebugLoggingError');
98
+ expect(fake.requests()).toHaveLength(before);
99
+ expect(fake.rows()).toHaveLength(1);
100
+ });
101
+ }
102
+
103
+ test('adoption deliberately removes undeclared info keys, including withheld sensitive names', async () => {
104
+ const fake = startFakeCredentialLitellm({
105
+ masterKey: creds.apiKey,
106
+ seed: [
107
+ credentialRow({
108
+ credential_name: props.credentialName,
109
+ credential_info: { note: 'search', foreign: 'a', secret: 'FAKE-withheld' },
110
+ credential_values: { api_key: 'FAKE-existing' },
111
+ }),
112
+ ],
113
+ });
114
+ const engine = fakeStack(creds, fake.fetch);
115
+ await engine.deploy(declare(props), { adopt: true });
116
+ expect(writesOf(fake.requests())).toEqual(['DELETE /credentials/FAKE_api', 'POST /credentials']);
117
+ expect(Object.keys(fake.rows()[0]?.credential_info as object)).toEqual(['note']);
118
+ expect(engine.snapshot().includes('FAKE-withheld')).toBe(false);
119
+ expect(await engine.deploy(declare(props))).toEqual({ Cred: 'noop' });
120
+ });
121
+
122
+ test('PATCH records a complete intended info map and the required body name', async () => {
123
+ const { fake, engine } = setup();
124
+ await engine.deploy(declare(extraInfo));
125
+ const news = { ...extraInfo, credentialInfo: { note: 'changed', team: 'a' } };
126
+ await engine.deploy(declare(news));
127
+ expect(fake.bodies()[1]?.credential_name).toBe(props.credentialName);
128
+ expect(fake.bodies()[1]?.credential_info).toEqual(news.credentialInfo);
129
+ expect(fake.rows()[0]?.credential_info).toEqual(news.credentialInfo);
130
+ expect(await engine.deploy(declare(news))).toEqual({ Cred: 'noop' });
131
+ });
132
+
133
+ test('the fake rejects a PATCH with no body credential_name, matching UpdateCredentialItem', async () => {
134
+ const { fake, engine } = setup();
135
+ await engine.deploy(declare(props));
136
+ const response = await fake.fetch(`${FAKE_BASE}/credentials/FAKE_api`, {
137
+ method: 'PATCH',
138
+ headers: { authorization: `Bearer ${creds.apiKey}`, 'content-type': 'application/json' },
139
+ body: JSON.stringify({ credential_info: { note: 'changed' } }),
140
+ });
141
+ expect(response.status).toBe(422);
142
+ expect(fake.bodies()).toHaveLength(2);
143
+ expect(fake.rows()[0]?.credential_info).toEqual(props.credentialInfo);
144
+ });
145
+
146
+ test('a debug-enabled plan refuses before the read response can print unmasked values', async () => {
147
+ const { fake, engine } = setup();
148
+ await engine.deploy(declare(extraValues));
149
+ const before = fake.requests().length;
150
+ const printed: string[] = [];
151
+ const original = console.error;
152
+ console.error = (...args: unknown[]) => {
153
+ printed.push(args.join(' '));
154
+ };
155
+ try {
156
+ process.env.DISTILLED_DEBUG_HTTP = '1';
157
+ await expect(engine.deploy(declare(props))).rejects.toThrow('DISTILLED_DEBUG_HTTP');
158
+ } finally {
159
+ console.error = original;
160
+ }
161
+ expect(printed.join('\n').includes('FAKE-rewrite-value')).toBe(false);
162
+ expect(fake.requests()).toHaveLength(before);
163
+ expect(fake.rows()).toHaveLength(1);
164
+ });
@@ -0,0 +1,50 @@
1
+ /** Whole-row removal needs a diagnostic that remembers DELETE succeeded before POST failed. */
2
+ import type { API_ERRORS } from '@distilled.cloud/litellm/Errors';
3
+ import type * as credentials from '@distilled.cloud/litellm/credential_management';
4
+ import * as Effect from 'effect/Effect';
5
+ import {
6
+ LitellmCredentialRewriteError,
7
+ type LitellmCredentialTransportError,
8
+ } from './credential-errors.ts';
9
+ import { createCredential, deleteCredential } from './credential-operations.ts';
10
+ import type { LitellmOpContext } from './operations.ts';
11
+
12
+ export const rewriteCredential = (body: credentials.CreateCredentialCredentialsPostRequest) =>
13
+ Effect.gen(function* () {
14
+ const credentialName = body.credential_name;
15
+ yield* deleteCredential(credentialName);
16
+ // The SDK's status fallback can emit every API_ERRORS member, including statuses absent
17
+ // from this operation's generated union. Keep only its tag, never a body/message/cause.
18
+ const post: Effect.Effect<
19
+ void,
20
+ | credentials.CreateCredentialCredentialsPostError
21
+ | LitellmCredentialTransportError
22
+ | InstanceType<(typeof API_ERRORS)[number]>,
23
+ LitellmOpContext
24
+ > = createCredential(body);
25
+ yield* post.pipe(
26
+ Effect.catchTag(
27
+ [
28
+ 'BadGateway',
29
+ 'BadRequest',
30
+ 'ConfigError',
31
+ 'Conflict',
32
+ 'Forbidden',
33
+ 'GatewayTimeout',
34
+ 'HttpClientError',
35
+ 'InternalServerError',
36
+ 'LitellmCredentialTransportError',
37
+ 'LitellmParseError',
38
+ 'Locked',
39
+ 'NotFound',
40
+ 'ServiceUnavailable',
41
+ 'TooManyRequests',
42
+ 'Unauthorized',
43
+ 'UnknownLitellmError',
44
+ 'UnprocessableEntity',
45
+ ],
46
+ (error) =>
47
+ Effect.fail(new LitellmCredentialRewriteError({ credentialName, reason: error._tag })),
48
+ ),
49
+ );
50
+ });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * What `LiteLLM.Credential` declares (props) and what is remembered about it (attributes).
3
+ *
4
+ * ⛔ NO CREDENTIAL VALUE IS EVER A PROP OR AN ATTRIBUTE (S25). Alchemy persists props AND
5
+ * attributes unencrypted (`StateEncoding.ts` writes a `Redacted` value's inner string beside
6
+ * the tag — `secrets/write-only.ts`, measured on alchemy 2.0.0-beta.79), and LiteLLM stores a
7
+ * credential's values ENCRYPTED in its database while the reads answer them MASKED, so a value
8
+ * the declaration holds is `{ fromEnv: 'NAME' }` — NAME lands in state, and the value is read
9
+ * from the DEPLOYING process's environment only when a write is being made
10
+ * (`credential-values.ts`, the same shape `McpServerProps.authValue` uses). Unlike a model
11
+ * deployment's `os.environ/NAME` reference (the PROXY resolves that in its own environment),
12
+ * the credential table stores LITERAL values — measured in the live 1.103.0 container
13
+ * (`credential_endpoints/endpoints.py`: `create_credential` encrypts the body's
14
+ * `credential_values` before the insert, `CredentialsRepository.create`) — so the proxy could
15
+ * not resolve a reference here even if one were sent.
16
+ * ⛔ THE LIVE ROW'S MASKED VALUES ARE NEVER MIRRORED INTO ATTRIBUTES. Both reads answer
17
+ * `credential_values` with every SENSITIVE-keyed value masked (the vendor's own rule,
18
+ * `_get_masked_values` in `litellm_logging.py`: a key matching `authorization`, `token`, `key`,
19
+ * `secret`, `vertex_credentials`, `credentials`, `password` or `passwd` — substring,
20
+ * case-insensitive — answers `v[:2] + "****" + v[-2:]` for a string longer than four
21
+ * characters, `*****` otherwise; non-string values and non-sensitive keys answer in full).
22
+ * A masked fragment (`sk****45`) is a PARTIAL SECRET, so `toAttributes` copies no
23
+ * `credential_values` entry at all (`credential-form.ts`); what was last written is remembered
24
+ * as the digest of the values this process RESOLVED, `valuesSeal` — never the values.
25
+ * ⚠️ `credential_info` IS THE COMPLETE INTENDED MAP, INCLUDING ON ADOPTION. Measured in
26
+ * LiteLLM 1.103.0 `credential_endpoints/endpoints.py:314-319`: a nonempty PATCH normally resets
27
+ * the DB info map (except when it contains a literal `credential_info` key); an empty patch
28
+ * leaves it alone. Memory only merges (:385-387), retaining removed keys until restart.
29
+ * Therefore removing any live info key requires a rewrite; every PATCH sends the full map.
30
+ * Sensitive info keys are refused at plan time because the vendor returns info UNMASKED.
31
+ * Reads retain their NAMES only, so removal can converge without persisting their values.
32
+ */
33
+ import type { FromEnv } from '../secrets/write-only.ts';
34
+
35
+ /**
36
+ * The credential's name — its identity in the vendor's table (a unique column, enforced by the
37
+ * database, `schema.prisma`'s `Credentials` model). ADOPTED: the live row whose `credential_name`
38
+ * equals it. ⛔ A RENAMED ROW IS A DIFFERENT CREDENTIAL: the name is the only address the vendor's
39
+ * routes have, so a changed `credentialName` plans a REPLACE — the old row survives under the
40
+ * resource's default `retain` policy, never renamed in place.
41
+ */
42
+ export interface CredentialProps {
43
+ readonly credentialName: string;
44
+
45
+ /**
46
+ * Free-form metadata stored beside the values (`credential_info` in the vendor's row), compared
47
+ * as the complete intended map with canonical-JSON deep equality (omitted means empty). ⛔ Refused for a key matching the vendor's
48
+ * own sensitive-key list: those belong in `credentialValues`, where LiteLLM stores them
49
+ * encrypted and answers them masked — in `credential_info` they would be stored in clear.
50
+ */
51
+ readonly credentialInfo?: Readonly<Record<string, unknown>>;
52
+
53
+ /**
54
+ * The credential's values, each declared as `{ fromEnv: 'NAME' }` naming the environment
55
+ * variable the DEPLOYING process reads at call time. AT LEAST ONE is required: LiteLLM's create
56
+ * model refuses a body whose `credential_values` and `model_id` are both empty
57
+ * (`models/credentials.py`'s `CreateCredentialItem` validator → 422), and `model_id` — which
58
+ * would copy a deployment's values — is not modelled here, so a value-less declaration has
59
+ * nothing to write. A value that never resolves in a plan-only environment never drifts
60
+ * (credential-values.ts); one that resolves is SENT as the literal the proxy stores encrypted.
61
+ */
62
+ readonly credentialValues: Readonly<Record<string, FromEnv>>;
63
+ }
64
+
65
+ export interface CredentialAttributes {
66
+ readonly credentialName: string;
67
+
68
+ /**
69
+ * The row's `credential_info` as the read answers it, minus any SENSITIVE-KEYED entry — those
70
+ * would mirror a clear secret into Alchemy's state and are skipped (`toAttributes`). The digest
71
+ * comparison removes undeclared live keys, including on adoption (credential-form.ts).
72
+ */
73
+ readonly credentialInfo: Readonly<Record<string, unknown>>;
74
+
75
+ /**
76
+ * `scrypt:<salt>:<digest>` of the values the LAST WRITE resolved — the only way a plan notices
77
+ * a rotated credential, because the live row answers masked fragments that can never be
78
+ * compared (`secrets/write-only.ts`). An ADOPTED row starts `''`, which reads as `stale`.
79
+ */
80
+ readonly valuesSeal: string;
81
+
82
+ /** Names only, never masked fragments. Optional for states written by older providers. */
83
+ readonly valueKeys?: readonly string[];
84
+
85
+ /** All info key names, including withheld sensitive entries; optional for older states. */
86
+ readonly infoKeys?: readonly string[];
87
+ }
88
+
89
+ /**
90
+ * Whether a value is not a string with something in it. Takes `unknown`: a declaration can say
91
+ * anything. (Same rule as model-types.ts and registry-support.ts.)
92
+ */
93
+ export const isBlank = (value: unknown): boolean =>
94
+ typeof value !== 'string' || value.trim() === '';
95
+
96
+ /**
97
+ * The vendor's OWN sensitive-key list, read from the live 1.103.0 container
98
+ * (`litellm_logging.py::_get_masked_values`): a key containing any of these substrings
99
+ * (case-insensitive) is masked in every `credential_values` read. Credential-form.ts refuses
100
+ * them in `credentialInfo` for the same reason the vendor masks them in values.
101
+ */
102
+ const SENSITIVE_KEY_FRAGMENTS: readonly string[] = [
103
+ 'authorization',
104
+ 'token',
105
+ 'key',
106
+ 'secret',
107
+ 'vertex_credentials',
108
+ 'credentials',
109
+ 'password',
110
+ 'passwd',
111
+ ];
112
+
113
+ /** Whether a key is one the vendor itself treats as sensitive. */
114
+ export const isSensitiveKey = (key: string): boolean => {
115
+ const lower = key.toLowerCase();
116
+ return SENSITIVE_KEY_FRAGMENTS.some((fragment) => lower.includes(fragment));
117
+ };