@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,66 @@
1
+ /**
2
+ * The URL half of `LiteLLM.MCPServer`: what a declared URL may not carry, and what a live one is
3
+ * scrubbed of before it reaches Alchemy's state.
4
+ *
5
+ * ⛔ THE URL IS A PROP, SO IT LANDS IN AN UNENCRYPTED STATE STORE. A secret in it (userinfo, a
6
+ * `?token=`) would be persisted, so a declaration is refused for carrying one and a live row's
7
+ * URL is redacted on the way in. The credential is `authValue`, never the URL.
8
+ */
9
+ import { isBlank } from './mcp-server-types.ts';
10
+
11
+ /** A query parameter whose NAME says it carries a secret. */
12
+ const SECRET_PARAM = /token|secret|passw|api.?key|auth|credential|signature|^key$|^sig$/i;
13
+
14
+ /** Why a URL may not be declared, or `undefined`. ⛔ Never quotes the URL: it may hold the secret. */
15
+ export const urlProblem = (raw: unknown): string | undefined => {
16
+ if (isBlank(raw)) return '`url` must be a non-empty string';
17
+ let url: URL;
18
+ try {
19
+ url = new URL(raw as string);
20
+ } catch {
21
+ return '`url` must be a full http(s) URL including the scheme';
22
+ }
23
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return '`url` must be http or https';
24
+ if (url.username !== '' || url.password !== '') {
25
+ return '`url` must not carry userinfo; declare the credential as `authValue`';
26
+ }
27
+ if (url.hash !== '') return '`url` must not carry a fragment';
28
+ for (const name of url.searchParams.keys()) {
29
+ if (SECRET_PARAM.test(name)) {
30
+ return `\`url\` carries the query parameter "${name}", which looks like a credential; declare it as \`authValue\` instead, because the URL is stored in Alchemy's state`;
31
+ }
32
+ }
33
+ return undefined;
34
+ };
35
+
36
+ /**
37
+ * `userinfo` and secret-looking query values replaced with `REDACTED`, so a live URL that carries a
38
+ * token never reaches Alchemy's unencrypted state.
39
+ * ★ A URL THAT NEEDS NO REDACTION COMES BACK BYTE-FOR-BYTE, never re-serialised: `URL#toString`
40
+ * adds a trailing slash to a bare origin, and a declared `https://host` would then differ from
41
+ * its own live row on every plan. A URL that does not parse is returned as it is.
42
+ */
43
+ export const redactUrl = (raw: string): string => {
44
+ let url: URL;
45
+ try {
46
+ url = new URL(raw);
47
+ } catch {
48
+ return raw;
49
+ }
50
+ let changed = false;
51
+ if (url.username !== '') {
52
+ url.username = 'REDACTED';
53
+ changed = true;
54
+ }
55
+ if (url.password !== '') {
56
+ url.password = 'REDACTED';
57
+ changed = true;
58
+ }
59
+ // ⚠️ A Set first: `set` mutates the live iterator's list, and a repeated name would be skipped.
60
+ for (const name of new Set(url.searchParams.keys())) {
61
+ if (!SECRET_PARAM.test(name)) continue;
62
+ url.searchParams.set(name, 'REDACTED');
63
+ changed = true;
64
+ }
65
+ return changed ? url.toString() : raw;
66
+ };
@@ -0,0 +1,213 @@
1
+ /**
2
+ * `LiteLLM.MCPServer` through Alchemy's real Plan and Apply over the fake proxy
3
+ * (`fake-mcp-litellm.ts`): create, no-op, rotate, adopt by name and drift.
4
+ *
5
+ * ★ EVERY VALUE IS `FAKE-*`, and the credential reaches the resource only through an environment
6
+ * variable the test sets and removes, exactly as a deploying process would.
7
+ * ⚠️ WHAT THE FAKE MODELS IS ITS OWN CHOICE, NOT A MEASUREMENT OF LITELLM 1.103.0 (see the fake).
8
+ * A test that leans on one of those choices says so.
9
+ */
10
+ import { afterEach, beforeEach, expect, test } from 'bun:test';
11
+ import * as Effect from 'effect/Effect';
12
+ import { FAKE_BASE } from './fake-litellm.ts';
13
+ import { type FakeMcpLitellm, serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
14
+ import { fakeStack, writesOf } from './fake-stack.ts';
15
+ import { LiteLLMMCPServer } from './mcp-server.ts';
16
+ import type { McpServerProps } from './mcp-server-types.ts';
17
+
18
+ const KEY = 'sk-test-master';
19
+ const VARIABLE = 'FAKE_MCP_SERVER_TOKEN';
20
+
21
+ const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
22
+ const declare = (props: McpServerProps, name = 'Docs') =>
23
+ Effect.gen(function* () {
24
+ yield* LiteLLMMCPServer(name, props);
25
+ });
26
+
27
+ const docs: McpServerProps = {
28
+ authType: 'bearer_token',
29
+ authValue: { fromEnv: VARIABLE },
30
+ serverName: 'FAKE_docs',
31
+ transport: 'http',
32
+ url: 'https://mcp.example.com/mcp',
33
+ };
34
+
35
+ const live = serverRow({
36
+ server_id: 'FAKE-uuid-1',
37
+ server_name: 'FAKE_search',
38
+ transport: 'sse',
39
+ url: 'https://search.example.com/sse',
40
+ });
41
+ const search: McpServerProps = {
42
+ authType: 'none',
43
+ serverName: 'FAKE_search',
44
+ transport: 'sse',
45
+ url: 'https://search.example.com/sse',
46
+ };
47
+
48
+ beforeEach(() => {
49
+ process.env[VARIABLE] = 'FAKE-token-one';
50
+ });
51
+ afterEach(() => {
52
+ delete process.env[VARIABLE];
53
+ });
54
+
55
+ test('creates a server, sends the credential once, and defaults the key and group grants to closed', async () => {
56
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
57
+ expect(await stack(fake).deploy(declare(docs))).toEqual({ Docs: 'create' });
58
+ const [row] = fake.servers();
59
+ expect(row).toMatchObject({
60
+ allow_all_keys: false,
61
+ auth_type: 'bearer_token',
62
+ mcp_access_groups: [],
63
+ server_name: 'FAKE_docs',
64
+ transport: 'http',
65
+ url: 'https://mcp.example.com/mcp',
66
+ });
67
+ expect(row?.['credentials']).toEqual({ auth_value: 'FAKE-token-one' });
68
+ // ⛔ an undeclared tool list is not sent: `[]` would mean "no restriction" (mcp-server-tools.test.ts)
69
+ expect(fake.bodies()[0]).not.toHaveProperty('allowed_tools');
70
+ // ★ a deterministic physical name, not a proxy-issued uuid
71
+ expect(String(row?.['server_id'])).toMatch(/^[a-z0-9-]+$/);
72
+ });
73
+
74
+ test('a second deploy of the same declaration writes nothing', async () => {
75
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
76
+ const same = stack(fake);
77
+ await same.deploy(declare(docs));
78
+ const before = fake.requests().length;
79
+ expect(await same.deploy(declare(docs))).toEqual({ Docs: 'noop' });
80
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
81
+ });
82
+
83
+ test('a rotated credential is an update that carries the new value, then a no-op', async () => {
84
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
85
+ const same = stack(fake);
86
+ await same.deploy(declare(docs));
87
+ process.env[VARIABLE] = 'FAKE-token-two';
88
+ expect(await same.deploy(declare(docs))).toEqual({ Docs: 'update' });
89
+ expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-token-two' });
90
+ expect(await same.deploy(declare(docs))).toEqual({ Docs: 'noop' });
91
+ });
92
+
93
+ test('an unset variable is "cannot tell", never drift: an unchanged server stays a no-op', async () => {
94
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
95
+ const same = stack(fake);
96
+ await same.deploy(declare(docs));
97
+ delete process.env[VARIABLE];
98
+ const before = fake.requests().length;
99
+ expect(await same.deploy(declare(docs))).toEqual({ Docs: 'noop' });
100
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
101
+ });
102
+
103
+ test('a create whose variable is unset is refused, names the variable, and writes nothing', async () => {
104
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
105
+ delete process.env[VARIABLE];
106
+ const failure = await stack(fake)
107
+ .deploy(declare(docs))
108
+ .then(
109
+ () => undefined,
110
+ (error: unknown) => String(error),
111
+ );
112
+ expect(failure).toContain(VARIABLE);
113
+ expect(writesOf(fake.requests())).toEqual([]);
114
+ });
115
+
116
+ test('a refused declaration fails the plan before a single request, so nothing invalid is stored', async () => {
117
+ for (const props of [
118
+ { ...docs, url: 'https://mcp.example.com/mcp?token=FAKE-in-url' },
119
+ { ...docs, authValue: undefined },
120
+ { ...docs, transport: 'stdio' as never },
121
+ ]) {
122
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
123
+ await expect(stack(fake).deploy(declare(props as McpServerProps))).rejects.toThrow();
124
+ expect(fake.requests()).toEqual([]);
125
+ }
126
+ });
127
+
128
+ test('a live row is Unowned: refused without --adopt, taken by name with it, with no write', async () => {
129
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [live] });
130
+ const engine = stack(fake);
131
+ await expect(engine.deploy(declare(search, 'Search'))).rejects.toThrow();
132
+ expect(writesOf(fake.requests())).toEqual([]);
133
+ const planned = await engine.deploy(declare(search, 'Search'), { adopt: true });
134
+ expect(Object.values(planned)).toEqual(['adopted']);
135
+ expect(writesOf(fake.requests())).toEqual([]);
136
+ });
137
+
138
+ test('adopting an open server: allow_all_keys is corrected to closed, the undeclared tool list is left', async () => {
139
+ const open = serverRow({
140
+ ...live,
141
+ allow_all_keys: true,
142
+ allowed_tools: ['FAKE_tool_a'],
143
+ });
144
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [open] });
145
+ await stack(fake).deploy(declare(search, 'Search'), { adopt: true });
146
+ expect(fake.servers()[0]).toMatchObject({
147
+ allow_all_keys: false,
148
+ allowed_tools: ['FAKE_tool_a'],
149
+ });
150
+ // ★ the update sent the whole managed set, and left the id alone
151
+ expect(fake.bodies()[0]).toMatchObject({ server_id: 'FAKE-uuid-1', allow_all_keys: false });
152
+ expect(fake.bodies()[0]).not.toHaveProperty('allowed_tools');
153
+ });
154
+
155
+ test('two live rows with the declared name are refused, never guessed at', async () => {
156
+ const twin = serverRow({ ...live, server_id: 'FAKE-uuid-2' });
157
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [live, twin] });
158
+ await expect(stack(fake).deploy(declare(search, 'Search'), { adopt: true })).rejects.toThrow();
159
+ expect(writesOf(fake.requests())).toEqual([]);
160
+ });
161
+
162
+ test('a declared serverId pins one of two same-named rows', async () => {
163
+ const twin = serverRow({ ...live, server_id: 'FAKE-uuid-2' });
164
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [live, twin] });
165
+ const planned = await stack(fake).deploy(
166
+ declare({ ...search, serverId: 'FAKE-uuid-2' }, 'Search'),
167
+ { adopt: true },
168
+ );
169
+ expect(Object.values(planned)).toEqual(['adopted']);
170
+ });
171
+
172
+ test('a proxy that drops an edit it cannot apply fails the deploy instead of claiming success', async () => {
173
+ // ⚠️ the fake's own model of a truthiness check on the edit route — unmeasured at 1.103.0
174
+ const open = serverRow({ ...live, allow_all_keys: true });
175
+ const fake = startFakeMcpLitellm({ editIgnoresFalsy: true, masterKey: KEY, seed: [open] });
176
+ await expect(stack(fake).deploy(declare(search, 'Search'), { adopt: true })).rejects.toThrow();
177
+ });
178
+
179
+ test('the id the proxy issues is the one recorded: the next deploy is a no-op on it', async () => {
180
+ const fake = startFakeMcpLitellm({ issuesOwnId: true, masterKey: KEY });
181
+ const same = stack(fake);
182
+ await same.deploy(declare(search, 'Search'));
183
+ expect(fake.servers()[0]?.['server_id']).toBe('FAKE-issued-id-0001');
184
+ const before = fake.requests().length;
185
+ expect(await same.deploy(declare(search, 'Search'))).toEqual({ Search: 'noop' });
186
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
187
+ });
188
+
189
+ test('moving off a static auth type sends credentials: null and forgets the seal', async () => {
190
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
191
+ const same = stack(fake);
192
+ await same.deploy(declare(docs));
193
+ const oauth: McpServerProps = {
194
+ authType: 'oauth2',
195
+ serverName: docs.serverName,
196
+ transport: docs.transport,
197
+ url: docs.url,
198
+ };
199
+ expect(await same.deploy(declare(oauth))).toEqual({ Docs: 'update' });
200
+ expect(fake.bodies().at(-1)).toMatchObject({ auth_type: 'oauth2', credentials: null });
201
+ expect(fake.servers()[0]?.['credentials']).toBeNull();
202
+ expect(await same.deploy(declare(oauth))).toEqual({ Docs: 'noop' });
203
+ });
204
+
205
+ test('a changed declared serverId is a replace, create-first, and the old row is retained', async () => {
206
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
207
+ const same = stack(fake);
208
+ await same.deploy(declare({ ...search, serverId: 'FAKE-id-a' }, 'Search'));
209
+ const planned = await same.deploy(declare({ ...search, serverId: 'FAKE-id-b' }, 'Search'));
210
+ expect(planned).toEqual({ Search: 'replace' });
211
+ // ★ `defaultRemovalPolicy: 'retain'`: nothing removes a server unless the stack opts in
212
+ expect(fake.servers().map((row) => row['server_id'])).toEqual(['FAKE-id-a', 'FAKE-id-b']);
213
+ });
@@ -0,0 +1,225 @@
1
+ /**
2
+ * `LiteLLM.MCPServer` — one row of LiteLLM's `LiteLLM_MCPServerTable` (`/v1/mcp/server`), an
3
+ * upstream MCP server the proxy's gateway exposes to keys and teams.
4
+ *
5
+ * ★ ADOPT BY NAME. `serverName` finds the live row whose `server_name` matches; declare `serverId`
6
+ * to pin one instead. A live row with no state is `Unowned`, so it needs `--adopt`. Two rows with
7
+ * the same name are refused, never guessed at (`LitellmMcpServerAmbiguousNameError`).
8
+ * ★ `defaultRemovalPolicy: 'retain'` — a server's removal takes its tools from every key, team and
9
+ * seat that reaches it. Opt in with `.pipe(RemovalPolicy.destroy())`.
10
+ * ⛔ THE CREDENTIAL IS `{ fromEnv: 'NAME' }`, NEVER A VALUE (S25): mcp-server-types.ts says why a
11
+ * `Redacted` prop would still be stored in plaintext. Reading a server never reveals auth material
12
+ * either — `toAttributes` copies no credential — and a rotated credential is noticed through a
13
+ * seal (mcp-server-credential.ts).
14
+ * ⛔ NO LITELLM CREDENTIAL IS A PROP either: `litellmProviders`' layer supplies `Credentials`.
15
+ * ⛔ THE LIST IS LITELLM'S IN-MEMORY REGISTRY, NOT THE TABLE (mcp-server-operations.ts): a committed row
16
+ * can be missing from it. So a row the list lacks is read by id from the table (`findLive`), the read
17
+ * back after a write falls back to the same read, and a delete always sends its DELETE.
18
+ * ⛔ AN UPDATE NEVER LETS LITELLM DEFAULT THE ALIAS (mcp-server-form.ts): the alias is the tool prefix.
19
+ * ⚠️ A `description` that the row's `mcp_info` would hide is refused before any write (mcp-server-shadow.ts).
20
+ * ⚠️ UNMEASURED AT 1.103.0, each guarded by a read back: whether a create honours a supplied
21
+ * `server_id`, and whether an empty list or a `false` lands on an edit (that the edit is a partial
22
+ * update is read from the source, `mcp-server-form.ts`). Nothing here assumes them; the returned
23
+ * row is what is recorded.
24
+ */
25
+ import { Resource } from 'alchemy';
26
+ import { Unowned } from 'alchemy/AdoptPolicy';
27
+ import { isResolved } from 'alchemy/Diff';
28
+ import type { Input } from 'alchemy/Input';
29
+ import * as Provider from 'alchemy/Provider';
30
+ import * as Effect from 'effect/Effect';
31
+ import * as Predicate from 'effect/Predicate';
32
+ import {
33
+ LitellmMcpServerAbsentAfterWriteError,
34
+ LitellmMcpServerInvalidError,
35
+ LitellmMcpServerNotConvergedError,
36
+ } from './mcp-server-errors.ts';
37
+ import {
38
+ credentialState,
39
+ requireCredential,
40
+ resolveCredential,
41
+ sealCredential,
42
+ } from './mcp-server-credential.ts';
43
+ import {
44
+ createBody,
45
+ differing,
46
+ firstProblem,
47
+ toAttributes,
48
+ updateBody,
49
+ } from './mcp-server-form.ts';
50
+ import { findLive, wantedId } from './mcp-server-locate.ts';
51
+ import {
52
+ createMcpServer,
53
+ deleteMcpServer,
54
+ listMcpServers,
55
+ readMcpServer,
56
+ updateMcpServer,
57
+ } from './mcp-server-operations.ts';
58
+ import { refuseShadowedDescription } from './mcp-server-shadow.ts';
59
+ import {
60
+ type McpServerAttributes,
61
+ type McpServerProps,
62
+ isStaticAuthType,
63
+ } from './mcp-server-types.ts';
64
+
65
+ export type { McpServerAttributes, McpServerProps };
66
+ export type { McpServerError } from './mcp-server-errors.ts';
67
+
68
+ export interface LiteLLMMCPServer extends Resource<
69
+ 'LiteLLM.MCPServer',
70
+ McpServerProps,
71
+ McpServerAttributes
72
+ > {}
73
+
74
+ export const LiteLLMMCPServer = Resource<LiteLLMMCPServer>('LiteLLM.MCPServer', {
75
+ defaultRemovalPolicy: 'retain',
76
+ });
77
+
78
+ export const isLiteLLMMCPServer = (value: unknown): value is LiteLLMMCPServer =>
79
+ Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.MCPServer';
80
+
81
+ const refuse = (props: McpServerProps) => {
82
+ const problem = firstProblem(props);
83
+ return problem === undefined
84
+ ? Effect.void
85
+ : Effect.fail(
86
+ new LitellmMcpServerInvalidError({ problem, serverName: String(props.serverName) }),
87
+ );
88
+ };
89
+
90
+ type Args<P> = {
91
+ id: string;
92
+ instanceId: string;
93
+ output: McpServerAttributes | undefined;
94
+ } & P;
95
+
96
+ /** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */
97
+ export const mcpServerHandlers = {
98
+ /**
99
+ * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW SERVER
100
+ * GETS — so the refusals run here too. Measured on beta.79 (`Plan.ts` lines 1303-1316, the
101
+ * `oldState === undefined` branch): a declaration with no state row is `read` with `olds: news`
102
+ * and never `diff`ed, and Apply then commits its props as `creating` BEFORE `reconcile` runs. A
103
+ * URL that carries a token would already be in the unencrypted store when reconcile refused it.
104
+ * ★ A DEFECT (`Effect.die`), NOT A TYPED FAILURE, and that is what keeps it from wedging a stage:
105
+ * the same call shape is Alchemy's recovery read of an interrupted create (`Plan.ts` lines
106
+ * 1428-1447), with the STORED props, and only a defect is caught there (`Effect.catchDefect`
107
+ * degrades it to "nothing recovered"). A typed failure would block the plan on props that a
108
+ * later, stricter release now refuses. The cold-start probe has no such catch, so there the
109
+ * defect fails the plan, as intended. `pbs-notification-target-lifecycle.ts` reasons the same.
110
+ */
111
+ read: ({ id, instanceId, olds, output }: Args<{ olds: McpServerProps }>) =>
112
+ Effect.gen(function* () {
113
+ const problem = output === undefined ? firstProblem(olds) : undefined;
114
+ if (problem !== undefined) {
115
+ return yield* Effect.die(
116
+ new LitellmMcpServerInvalidError({ problem, serverName: String(olds.serverName) }),
117
+ );
118
+ }
119
+ // ★ THE LIST, THEN THE TABLE BY ID: a row the registry lacks is still a live row
120
+ // (mcp-server-locate.ts). `instanceId` is what recovery of an interrupted create needs to
121
+ // rebuild the deterministic id that create asked for.
122
+ const found = yield* findLive(id, instanceId, olds, output);
123
+ if (found === undefined) return undefined;
124
+ const live = toAttributes(found);
125
+ return output === undefined
126
+ ? Unowned(live)
127
+ : { ...live, credentialSeal: output.credentialSeal };
128
+ }),
129
+
130
+ diff: ({
131
+ news,
132
+ output,
133
+ }: {
134
+ news: Input<McpServerProps>;
135
+ output: McpServerAttributes | undefined;
136
+ }) =>
137
+ Effect.gen(function* () {
138
+ if (!isResolved(news)) return undefined;
139
+ yield* refuse(news);
140
+ if (output === undefined) return undefined;
141
+ // The id is identity: a different declared id is a different row.
142
+ if (news.serverId !== undefined && news.serverId !== output.serverId) {
143
+ return { action: 'replace', deleteFirst: false } as const;
144
+ }
145
+ return differing(output, news).length === 0 &&
146
+ credentialState(news, output.credentialSeal) !== 'stale'
147
+ ? ({ action: 'noop' } as const)
148
+ : ({ action: 'update' } as const);
149
+ }),
150
+
151
+ reconcile: ({ id, instanceId, news, output }: Args<{ news: McpServerProps }>) =>
152
+ Effect.gen(function* () {
153
+ yield* refuse(news);
154
+ const before = yield* findLive(id, instanceId, news, output);
155
+ const credential = resolveCredential(news);
156
+ let sealed = output?.credentialSeal ?? '';
157
+ let serverId: string;
158
+
159
+ if (before === undefined) {
160
+ // A static credential is part of the create: without it the row would exist unusable.
161
+ yield* requireCredential(news, credential);
162
+ const wanted = yield* wantedId(id, instanceId, news, output);
163
+ const created = yield* createMcpServer(createBody(news, wanted, credential.value));
164
+ // ★ THE ROW LITELLM ANSWERS IS THE TRUTH: a proxy that ignores a supplied `server_id` is
165
+ // recorded under the id it actually used, not the one that was asked for.
166
+ serverId = typeof created?.server_id === 'string' ? created.server_id : wanted;
167
+ sealed = credential.value === undefined ? '' : sealCredential(credential.value);
168
+ } else {
169
+ serverId = before.server_id;
170
+ const live = toAttributes(before);
171
+ const state = credentialState(news, sealed, credential);
172
+ if (differing(live, news).length > 0 || state === 'stale') {
173
+ // ⛔ A CHANGED AUTH TYPE DEMANDS THE CREDENTIAL, before anything is written: LiteLLM wipes
174
+ // the stored one on an auth-class change that sends none (mcp-server-credential.ts).
175
+ if (live.authType !== news.authType) yield* requireCredential(news, credential);
176
+ // ⛔ Same rule for a description `mcp_info` would hide: before anything is written.
177
+ if (differing(live, news).includes('description')) {
178
+ yield* refuseShadowedDescription(news, serverId);
179
+ }
180
+ // ★ Otherwise sent whenever the process has it, demanded only when the seal says stale.
181
+ const send = isStaticAuthType(news.authType) ? credential.value : undefined;
182
+ const leavingStatic =
183
+ !isStaticAuthType(news.authType) && (sealed !== '' || isStaticAuthType(live.authType));
184
+ yield* updateMcpServer(updateBody(news, serverId, send, leavingStatic, live));
185
+ if (send !== undefined) sealed = sealCredential(send);
186
+ else if (leavingStatic) sealed = '';
187
+ }
188
+ }
189
+
190
+ // ★ THE LIST FIRST, THEN THE TABLE: after a create the registry refresh can fail (LiteLLM only
191
+ // logs it), and a committed row that the list does not show yet is "created, not yet visible",
192
+ // never absent. The by-id read also registers it, which heals the list.
193
+ const found =
194
+ (yield* listMcpServers()).find((row) => row.server_id === serverId) ??
195
+ (yield* readMcpServer(serverId));
196
+ if (found === undefined) {
197
+ return yield* Effect.fail(
198
+ new LitellmMcpServerAbsentAfterWriteError({ serverId, serverName: news.serverName }),
199
+ );
200
+ }
201
+ const after = toAttributes(found);
202
+ const left = differing(after, news);
203
+ if (left.length > 0) {
204
+ return yield* Effect.fail(
205
+ new LitellmMcpServerNotConvergedError({
206
+ fields: left,
207
+ serverId,
208
+ serverName: news.serverName,
209
+ }),
210
+ );
211
+ }
212
+ return { ...after, credentialSeal: sealed };
213
+ }),
214
+
215
+ delete: ({ output }: { output: McpServerAttributes }) => deleteMcpServer(output.serverId),
216
+
217
+ /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */
218
+ list: () => Effect.succeed([]),
219
+ };
220
+
221
+ export const LiteLLMMCPServerProvider = () =>
222
+ Provider.effect(
223
+ LiteLLMMCPServer,
224
+ Effect.succeed(LiteLLMMCPServer.Provider.of(mcpServerHandlers)),
225
+ );
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Whether a stateless `LiteLLM.Model` reconcile may write the live row it found.
3
+ *
4
+ * ⛔ A ROW WITH NO SEAL IS NOT YET THIS GENERATION'S. `output` is undefined on a create and on a
5
+ * `replacing` row Apply committed before reconcile (Apply.ts), and a probe result carries
6
+ * `paramsSeal: ''`. Either way the live row may belong to someone else.
7
+ * ★ TWO ROWS THAT LOOK THE SAME AND ARE NOT. A deterministic id this declaration would create is
8
+ * ours by construction. An older generation's `attr.id` (ownership/rows.ts) is our own serving
9
+ * deployment from before this replacement — recording it here makes destroy() delete it. A
10
+ * declared id needs `--adopt` on a create or an unfinished generation; an unpinned row with any
11
+ * other id is adopted only on a create (ownership/adopt.ts).
12
+ */
13
+ import * as Effect from 'effect/Effect';
14
+ import { adoptEnabled, adoptsAtApply } from '../ownership/adopt.ts';
15
+ import { adopting } from '../ownership/adopting.ts';
16
+ import { isCreate, olderGenerationIds } from '../ownership/rows.ts';
17
+ import { unfinished } from '../ownership/resume.ts';
18
+ import { LitellmModelForeignRowError, LitellmModelPriorGenerationError } from './model-errors.ts';
19
+ import type { ModelAttributes, ModelProps } from './model-types.ts';
20
+
21
+ export const claimStatelessRow = (args: {
22
+ readonly fqn: string;
23
+ readonly instanceId: string;
24
+ readonly output: ModelAttributes | undefined;
25
+ readonly news: ModelProps;
26
+ readonly beforeId: string;
27
+ readonly wanted: string;
28
+ }): Effect.Effect<void, LitellmModelPriorGenerationError | LitellmModelForeignRowError> =>
29
+ Effect.gen(function* () {
30
+ const older = yield* olderGenerationIds(args.fqn, args.instanceId);
31
+ // ⛔ NOT OURS, EVEN WITH --adopt. The id is the previous generation's. Accepting it stores
32
+ // that id on this generation; destroy() GC then deletes the old generation by the same id
33
+ // (Apply.ts ~2168). The way back is `alchemy state rm`, then a deploy with --adopt.
34
+ if (older.includes(args.beforeId)) {
35
+ return yield* Effect.fail(
36
+ new LitellmModelPriorGenerationError({
37
+ fqn: args.fqn,
38
+ id: args.beforeId,
39
+ modelName: args.news.modelName,
40
+ }),
41
+ );
42
+ }
43
+ // ⛔ ONLY A DETERMINISTIC ID IS OURS BY CONSTRUCTION. With no declared id, `wanted` is the
44
+ // physical name this declaration would create. A row found by name with any other id is not
45
+ // the one this generation posted — a resumed unpinned replace must not `--adopt` it.
46
+ // A DECLARED id is a name a human chose, so a matching row goes through the adoption gate,
47
+ // which `--adopt` may pass for a create or an unfinished generation (the read-back that
48
+ // died after `POST /model/new`).
49
+ const unpinned = args.news.id === undefined;
50
+ const ours = unpinned && args.beforeId === args.wanted;
51
+ const owner = { fqn: args.fqn, instanceId: args.instanceId, output: args.output };
52
+ const isAdoption = yield* adopting(owner, () => Effect.succeed(false));
53
+ const creating = yield* isCreate(args.fqn, args.instanceId);
54
+ const mayAdopt = unpinned
55
+ ? creating && (yield* adoptEnabled(args.fqn))
56
+ : yield* adoptsAtApply(owner);
57
+ if (!ours && !isAdoption && !mayAdopt) {
58
+ // A declared id's unfinished generation is the one `--adopt` resumes. An unpinned row whose
59
+ // id is not the physical name is a different deployment, replace or not.
60
+ const replace = unpinned ? !creating : !creating && !(yield* unfinished(args.instanceId));
61
+ return yield* Effect.fail(
62
+ new LitellmModelForeignRowError({
63
+ id: args.beforeId,
64
+ modelName: args.news.modelName,
65
+ replace,
66
+ }),
67
+ );
68
+ }
69
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The seal of a `LiteLLM.Model`'s declared `litellm_params` — the one way a plan notices a changed
3
+ * `model`, `apiBase` or `apiKey` when the live row hides them.
4
+ *
5
+ * ★ REFERENCE-ONLY, AND THEREFORE PURE. The credential this resource declares is LiteLLM's own
6
+ * `os.environ/NAME` reference: the PROXY resolves the variable in ITS environment at inference
7
+ * time, and neither the row it writes nor the wire body ever carries the value. So this module
8
+ * never reads `process.env` — there is no value here to read, and a digest of a value that lives
9
+ * on another host would be false drift on every plan whose deploying environment differed.
10
+ * ⛔ THE SEAL COVERS THE DECLARED VALUES ONLY. At v1.103.0 the read answers the stored params
11
+ * DECRYPTED but STRIPS `api_key` (model-types.ts), and a credential that did come back would
12
+ * only be a plaintext in state — so the live row never contributes to this comparison:
13
+ * attributes carry the digest of what was DECLARED, `paramsSeal`, and a plan that changes any
14
+ * of `model`, `apiBase` or the variable behind `apiKey` sees the digest change and updates.
15
+ * What was last written is remembered exactly; a value the proxy strips stays quiet because
16
+ * the comparison never involves it.
17
+ * ⛔ ADOPTED OR FOREIGN ROWS START `paramsSeal: ''` (a row cannot supply a digest of values it
18
+ * hides), which reads as `stale`. A bare adopt of a visibly matching row records the digest
19
+ * locally with no write; a stale seal on a declaration that manages a param the read never
20
+ * shows (`api_key`, `api_base`) forces one stamping PATCH, so a different stored reference is
21
+ * never sealed over. After that the seal is stable. `seals/write-only.ts` carries the scrypt
22
+ * rules; see also mcp-server-credential.ts for the credential that LiteLLM STORES (this one is
23
+ * only referenced).
24
+ */
25
+ import { seal, sealMatches } from '../secrets/write-only.ts';
26
+ import type { ModelProps } from './model-types.ts';
27
+
28
+ /**
29
+ * The declared `litellm_params` as the reference form names them — the input to the seal. `model`
30
+ * and `api_base` are what the proxy routes on; `api_key` is the reference LiteLLM resolves in its
31
+ * own environment. No value, ever: there is no lookup and no environment here.
32
+ */
33
+ export const declaredValues = (props: ModelProps): Record<string, string> => {
34
+ const values: Record<string, string> = { model: props.model };
35
+ if (props.apiBase !== undefined) values['api_base'] = props.apiBase;
36
+ if (props.apiKey !== undefined) values['api_key'] = `os.environ/${props.apiKey.fromEnv}`;
37
+ return values;
38
+ };
39
+
40
+ /** `scrypt:<salt>:<digest>` of declared values, for `paramsSeal`. ⛔ Never a secret value. */
41
+ export const sealFromValues = (values: Record<string, string>): string => seal(values);
42
+
43
+ /** Whether the declaration's values are the ones `sealed` was made from. */
44
+ export const sealState = (props: ModelProps, sealed: string): 'match' | 'stale' =>
45
+ sealMatches(sealed, declaredValues(props)) ? 'match' : 'stale';