@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,233 @@
1
+ /**
2
+ * Refusals, wire bodies, the read shape and the comparison for `LiteLLM.MCPServer`, testable
3
+ * without a server. The URL rules are in `mcp-server-url.ts`.
4
+ *
5
+ * ⛔ TWO ACCESS GRANTS ARE ALWAYS COMPARED, WITH A CLOSED DEFAULT. `allow_all_keys` (default
6
+ * false) and `mcp_access_groups` (default empty) decide which keys can reach a server, so a live
7
+ * value that differs from the declaration is corrected rather than left as it is.
8
+ * ⛔ `allowed_tools` IS NOT ONE OF THEM: it is compared and sent ONLY WHEN DECLARED, like `alias`.
9
+ * An empty list is the OPEN state, not a closed one. Measured on the live 1.103.0 container:
10
+ * `server_applies_tool_allowlist` (`mcp_server/utils.py`) is "the `mcp_info` enforce flag, or a
11
+ * non-empty `allowed_tools`", and `filter_tools_by_allowed_tools` (`server.py` lines 1692-1698)
12
+ * returns every tool when it is false. This resource does not model `mcp_info`, so a default of
13
+ * `[]` would switch a live whitelist OFF the moment a row is adopted without restating it, and an
14
+ * adopted row is planned `adopted` (Alchemy's adoption branch), so no `update` would show. An
15
+ * undeclared list is left exactly as the proxy holds it.
16
+ * ★ Descriptive fields (`alias`, `description`) are also compared only when declared, so adopting a
17
+ * row never clears text a person wrote. That holds on the wire too, for `alias` only because an
18
+ * update RE-SENDS the live alias whenever it sends a `serverName` (`identityOnUpdate` below).
19
+ * ⚠️ EVERY UPDATE SENDS THE FULL MANAGED SET, NOT ONLY THE FIELDS THAT DIFFER, bar the two name
20
+ * fields, which `identityOnUpdate` sends selectively because LiteLLM defaults an alias. The edit
21
+ * route is a PARTIAL update at 1.103.0 (measured by reading the live container's
22
+ * `mcp_server/db.py`, the update function's `exclude_unset=True`: fields the caller did not send
23
+ * keep their stored value; no live call was made), so a field this resource does not send is left
24
+ * alone. Sending the whole managed set is still correct, and reconcile reads back and fails loudly
25
+ * if a declared field did not land (`LitellmMcpServerNotConvergedError`). Whether a `false` or an
26
+ * empty list is written rather than skipped stays UNMEASURED, which is what the read back guards.
27
+ */
28
+ import type * as mcp from '@distilled.cloud/litellm/mcp_management';
29
+ import * as Redacted from 'effect/Redacted';
30
+ import { redactUrl, urlProblem } from './mcp-server-url.ts';
31
+ import {
32
+ AUTH_TYPES,
33
+ type McpServerAttributes,
34
+ type McpServerProps,
35
+ TRANSPORTS,
36
+ isBlank,
37
+ isStaticAuthType,
38
+ } from './mcp-server-types.ts';
39
+
40
+ const listProblem = (name: string, list: readonly unknown[] | undefined): string | undefined =>
41
+ list !== undefined && list.some((entry) => isBlank(entry))
42
+ ? `\`${name}\` must hold non-empty strings`
43
+ : undefined;
44
+
45
+ /**
46
+ * ⛔ LITELLM 1.103 RUNS THE MCP SDK'S `validate_tool_name` ON `server_name` AND THE NORMALISED
47
+ * ALIAS, and that 400 happens only at apply. The names it accepts are `^[A-Za-z0-9._]{1,128}$`:
48
+ * a space, `/`, `:`, `@`, or more than 128 characters is refused here so nothing is written to
49
+ * Alchemy's state first. `-` is out for the same reason and one more: it is
50
+ * `MCP_TOOL_PREFIX_SEPARATOR` (default `-`, unset in the live container as measured 2026-09-29),
51
+ * and a tool is `<alias>-<tool>`. A proxy that set another separator would be over-refused:
52
+ * rare, and loud. `normalize_server_name` would also rewrite a space to `_`, so a name outside
53
+ * this pattern would never equal the row it produced.
54
+ */
55
+ const TOOL_NAME = /^[A-Za-z0-9._]{1,128}$/;
56
+
57
+ const toolNameProblem = (field: string, value: string): string | undefined =>
58
+ TOOL_NAME.test(value)
59
+ ? undefined
60
+ : `\`${field}\` must match ^[A-Za-z0-9._]{1,128}$: LiteLLM 1.103 rejects a space, "/", ":", "@", "-" or more than 128 characters only at apply`;
61
+
62
+ /** A declared alias is a tool name too. Omitted is fine: the live alias is kept. */
63
+ const aliasProblem = (alias: string | undefined): string | undefined => {
64
+ if (alias === undefined) return undefined;
65
+ if (isBlank(alias)) return '`alias` must be a non-empty string when declared';
66
+ return toolNameProblem('alias', alias);
67
+ };
68
+
69
+ /** The first reason a declaration is refused, or `undefined`. Pure: no environment, no server. */
70
+ export const firstProblem = (props: McpServerProps): string | undefined => {
71
+ if (isBlank(props.serverName) || props.serverName !== props.serverName.trim()) {
72
+ return '`serverName` must be a non-empty string without leading or trailing whitespace';
73
+ }
74
+ const named = toolNameProblem('serverName', props.serverName) ?? aliasProblem(props.alias);
75
+ if (named !== undefined) return named;
76
+ // ⛔ A blank description never converges. LiteLLM copies the column into `mcp_info` only when
77
+ // the value is truthy (`build_mcp_server_from_table`), and the list then answers
78
+ // `mcp_info.description`. `''` is written and read back as absent, so every deploy would fail
79
+ // the read back. Omitted is still "leave the live text alone".
80
+ if (props.description !== undefined && isBlank(props.description)) {
81
+ return '`description` must be a non-empty string when declared: LiteLLM copies it into mcp_info only when it is truthy, so a blank one never converges';
82
+ }
83
+ if (props.serverId !== undefined && isBlank(props.serverId)) {
84
+ return '`serverId` must be a non-empty string when declared';
85
+ }
86
+ const url = urlProblem(props.url);
87
+ if (url !== undefined) return url;
88
+ if (!TRANSPORTS.includes(props.transport)) {
89
+ return '`transport` must be "sse" or "http" (`stdio` runs a command on the proxy host and is not modelled)';
90
+ }
91
+ if (!AUTH_TYPES.includes(props.authType)) {
92
+ return `\`authType\` must be one of ${AUTH_TYPES.join(', ')}; the others need credential fields this resource does not model`;
93
+ }
94
+ const declared = props.authValue !== undefined;
95
+ if (isStaticAuthType(props.authType) && !declared) {
96
+ return `\`authType: "${props.authType}"\` needs \`authValue: { fromEnv: … }\``;
97
+ }
98
+ if (!isStaticAuthType(props.authType) && declared) {
99
+ return `\`authValue\` is the static credential for api_key, bearer_token, basic, authorization and token; this server is "${props.authType}"`;
100
+ }
101
+ if (declared && isBlank(props.authValue?.fromEnv)) {
102
+ return '`authValue.fromEnv` must name an environment variable';
103
+ }
104
+ return (
105
+ listProblem('allowedTools', props.allowedTools) ??
106
+ listProblem('mcpAccessGroups', props.mcpAccessGroups)
107
+ );
108
+ };
109
+
110
+ /**
111
+ * One row of `GET /v1/mcp/server`, or of the by-id read, as attributes.
112
+ *
113
+ * ⚠️ THE LIST'S `description` IS `mcp_info.description` WHEN THAT EXISTS, else the column (measured
114
+ * on the live 1.103.0 container: `_build_mcp_server_table` and, on registration,
115
+ * `build_mcp_server_from_table`, which copies the column into `mcp_info` only when `mcp_info`
116
+ * has none). This resource writes only the column, so a declared description cannot be read back
117
+ * from a row whose `mcp_info` holds another: reconcile refuses that before it writes.
118
+ *
119
+ * ⛔ `credentials`, `static_headers`, `env`, `env_vars` and every other field are deliberately NOT
120
+ * copied: what LiteLLM stores as a credential stays on the proxy. The `credentialSeal` is filled
121
+ * in by the caller from the previous state; a row cannot supply one.
122
+ */
123
+ export const toAttributes = (row: mcp.LiteLLMMCPServerTable): McpServerAttributes => ({
124
+ alias: row.alias ?? null,
125
+ allowAllKeys: row.allow_all_keys ?? false,
126
+ allowedTools: [...(row.allowed_tools ?? [])],
127
+ authType: row.auth_type ?? 'none',
128
+ credentialSeal: '',
129
+ description: row.description ?? null,
130
+ mcpAccessGroups: [...(row.mcp_access_groups ?? [])],
131
+ serverId: row.server_id,
132
+ serverName: row.server_name ?? null,
133
+ transport: row.transport,
134
+ url: row.url === undefined || row.url === null ? null : redactUrl(row.url),
135
+ });
136
+
137
+ const sameSet = (live: readonly string[], declared: readonly string[]): boolean =>
138
+ new Set(live).size === new Set(declared).size && declared.every((entry) => live.includes(entry));
139
+
140
+ /** Wire names of the fields where the live row and the declaration disagree. */
141
+ export const differing = (live: McpServerAttributes, props: McpServerProps): readonly string[] => {
142
+ const out: string[] = [];
143
+ if (live.serverName !== props.serverName) out.push('server_name');
144
+ if (live.url !== props.url) out.push('url');
145
+ if (live.transport !== props.transport) out.push('transport');
146
+ if (live.authType !== props.authType) out.push('auth_type');
147
+ if (live.allowAllKeys !== (props.allowAllKeys ?? false)) out.push('allow_all_keys');
148
+ if (props.allowedTools !== undefined && !sameSet(live.allowedTools, props.allowedTools)) {
149
+ out.push('allowed_tools');
150
+ }
151
+ if (!sameSet(live.mcpAccessGroups, props.mcpAccessGroups ?? [])) out.push('mcp_access_groups');
152
+ if (props.alias !== undefined && live.alias !== props.alias) out.push('alias');
153
+ if (props.description !== undefined && live.description !== props.description) {
154
+ out.push('description');
155
+ }
156
+ return out;
157
+ };
158
+
159
+ /** The managed set both writes send, WITHOUT the name fields. ⚠️ See the file header on why an update sends all of it. */
160
+ const managed = (props: McpServerProps) => ({
161
+ allow_all_keys: props.allowAllKeys ?? false,
162
+ ...(props.allowedTools === undefined ? {} : { allowed_tools: [...props.allowedTools] }),
163
+ auth_type: props.authType,
164
+ ...(props.description === undefined ? {} : { description: props.description }),
165
+ mcp_access_groups: [...(props.mcpAccessGroups ?? [])],
166
+ transport: props.transport,
167
+ url: props.url,
168
+ });
169
+
170
+ /** A create names the row, and sends an alias only when one is declared: LiteLLM defaults it to the name. */
171
+ const identityOnCreate = (props: McpServerProps) => ({
172
+ ...(props.alias === undefined ? {} : { alias: props.alias }),
173
+ server_name: props.serverName,
174
+ });
175
+
176
+ /**
177
+ * ⛔ AN UPDATE MUST NOT LET LITELLM DEFAULT THE ALIAS. Measured 2026-09-29 by running the live
178
+ * 1.103.0 container's own `validate_and_normalize_mcp_server_payload` and `_prepare_mcp_server_data`
179
+ * on an edit body (no database, no network): a body with a `server_name` and no `alias` comes out
180
+ * with `alias = normalize(server_name)` and the alias IS WRITTEN, because the route captures the
181
+ * set of fields the caller sent BEFORE the normalisation and `_prepare_mcp_server_data` drops an
182
+ * alias only when it is still None. The alias is the tool prefix (`get_server_prefix`), so a row
183
+ * with alias `search` and name `estate_web` would have every tool renamed `estate_web-*` for
184
+ * every key and seat, while `differing` (which skips an undeclared alias) called it converged.
185
+ * A body with neither field sends no alias. So:
186
+ * - `server_name` is sent only when it CHANGES, and
187
+ * - an alias is sent when declared, or (on a rename) as the live one, so a rename keeps the prefix.
188
+ * A live row with no alias that is renamed gets the new name as its alias: LiteLLM's own default.
189
+ */
190
+ const identityOnUpdate = (props: McpServerProps, live: McpServerAttributes) => {
191
+ const renamed = live.serverName !== props.serverName;
192
+ const alias = props.alias ?? (renamed && live.alias !== null ? live.alias : undefined);
193
+ return {
194
+ ...(alias === undefined ? {} : { alias }),
195
+ ...(renamed ? { server_name: props.serverName } : {}),
196
+ };
197
+ };
198
+
199
+ /**
200
+ * ⛔ THE ONE PLACE THE CREDENTIAL IS UNWRAPPED, and it goes straight onto the wire body. `undefined`
201
+ * sends no `credentials` key at all, which leaves whatever LiteLLM already holds.
202
+ */
203
+ const credentialsOf = (credential: Redacted.Redacted<string> | undefined) =>
204
+ credential === undefined ? {} : { credentials: { auth_value: Redacted.value(credential) } };
205
+
206
+ export const createBody = (
207
+ props: McpServerProps,
208
+ serverId: string,
209
+ credential: Redacted.Redacted<string> | undefined,
210
+ ): mcp.AddMcpServerV1McpServerPostRequest => ({
211
+ ...managed(props),
212
+ ...identityOnCreate(props),
213
+ ...credentialsOf(credential),
214
+ server_id: serverId,
215
+ });
216
+
217
+ /**
218
+ * `clearCredentials` sends an explicit `credentials: null`, for a server moving off a static auth
219
+ * type. Whether LiteLLM clears its stored credential on `null` or ignores it (an `exclude_none`
220
+ * merge) is UNMEASURED at 1.103.0; either way nothing is sent that could set a wrong one.
221
+ */
222
+ export const updateBody = (
223
+ props: McpServerProps,
224
+ serverId: string,
225
+ credential: Redacted.Redacted<string> | undefined,
226
+ clearCredentials: boolean,
227
+ live: McpServerAttributes,
228
+ ): mcp.EditMcpServerV1McpServerPutRequest => ({
229
+ ...managed(props),
230
+ ...identityOnUpdate(props, live),
231
+ ...(clearCredentials ? { credentials: null } : credentialsOf(credential)),
232
+ server_id: serverId,
233
+ });
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `LiteLLM.MCPServer`'s handlers and operations called directly, for what the engine harness cannot
3
+ * show: that nothing a proxy returns can reach an attribute, and that delete is idempotent by a
4
+ * real read.
5
+ *
6
+ * ⚠️ CALLING A HANDLER DIRECTLY IS NOT THE SHAPE THE ENGINE USES for a create-first replace
7
+ * (`output: undefined, olds: undefined` under a fresh instance id) — that path is
8
+ * `mcp-server.test.ts`, through `fakeStack`. What is checked here does not depend on it.
9
+ * ★ EVERY VALUE IS `FAKE-*`.
10
+ */
11
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
12
+ import { credentials } from '@distilled.cloud/litellm/Credentials';
13
+ import * as Effect from 'effect/Effect';
14
+ import * as Layer from 'effect/Layer';
15
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
16
+ import { FAKE_BASE } from './fake-litellm.ts';
17
+ import { serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
18
+ import { mcpServerHandlers } from './mcp-server.ts';
19
+ import { deleteMcpServer, listMcpServers } from './mcp-server-operations.ts';
20
+ import type { McpServerProps } from './mcp-server-types.ts';
21
+
22
+ const KEY = 'sk-test-master';
23
+ const VARIABLE = 'FAKE_MCP_HANDLER_TOKEN';
24
+
25
+ /**
26
+ * ⚠️ `R` IS `unknown` BECAUSE `reconcile`'s type still names `InstanceId | Stack | Stage`, which
27
+ * `createPhysicalName` needs and a declared `serverId` never reaches; the cast below is the same
28
+ * one `pass-through-endpoint-delete-swallow.test.ts` makes.
29
+ */
30
+ const run = <A, E>(
31
+ fetchFn: typeof globalThis.fetch,
32
+ effect: Effect.Effect<A, E, unknown>,
33
+ ): Promise<A> =>
34
+ Effect.runPromise(
35
+ effect.pipe(
36
+ Effect.provide(FetchHttpClient.layer),
37
+ Effect.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)),
38
+ Effect.provide(credentials({ apiKey: KEY, baseUrl: FAKE_BASE })),
39
+ ) as Effect.Effect<A, E, never>,
40
+ );
41
+
42
+ const props: McpServerProps = {
43
+ authType: 'bearer_token',
44
+ authValue: { fromEnv: VARIABLE },
45
+ serverName: 'FAKE_docs',
46
+ transport: 'http',
47
+ url: 'https://mcp.example.com/mcp',
48
+ };
49
+
50
+ beforeEach(() => {
51
+ process.env[VARIABLE] = 'FAKE-handler-secret';
52
+ });
53
+ afterEach(() => {
54
+ delete process.env[VARIABLE];
55
+ });
56
+
57
+ describe('reading a server never reveals auth material', () => {
58
+ // ⚠️ the fake's worst case: a proxy whose list hands every stored secret back
59
+ const leaky = serverRow({
60
+ credentials: { auth_value: 'FAKE-live-secret' },
61
+ env_vars: [{ name: 'FAKE_ENV', scope: 'global', value: 'FAKE-env-secret' }],
62
+ server_id: 'FAKE-uuid-1',
63
+ server_name: 'FAKE_docs',
64
+ static_headers: { Authorization: 'FAKE-header-secret' },
65
+ url: 'https://mcp.example.com/mcp?token=FAKE-url-secret',
66
+ });
67
+
68
+ test('read of an adopted row: no credential, header, env value or url token in the attributes', async () => {
69
+ const fake = startFakeMcpLitellm({ echoCredentials: true, masterKey: KEY, seed: [leaky] });
70
+ const read = await run(
71
+ fake.fetch,
72
+ mcpServerHandlers.read({ id: 'Docs', instanceId: 'i', olds: props, output: undefined }),
73
+ );
74
+ const text = JSON.stringify(read);
75
+ for (const secret of [
76
+ 'FAKE-live-secret',
77
+ 'FAKE-env-secret',
78
+ 'FAKE-header-secret',
79
+ 'FAKE-url-secret',
80
+ ]) {
81
+ expect(text).not.toContain(secret);
82
+ }
83
+ expect(text).toContain('REDACTED');
84
+ });
85
+
86
+ test('reconcile: the attributes hold a seal, never the credential it sent', async () => {
87
+ const fake = startFakeMcpLitellm({ echoCredentials: true, masterKey: KEY });
88
+ // ⚠️ `serverId` declared: a direct call has no Stack for `createPhysicalName` to read.
89
+ const attributes = await run(
90
+ fake.fetch,
91
+ mcpServerHandlers.reconcile({
92
+ id: 'Docs',
93
+ instanceId: 'i',
94
+ news: { ...props, serverId: 'FAKE-uuid-2' },
95
+ output: undefined,
96
+ }),
97
+ );
98
+ expect(JSON.stringify(attributes)).not.toContain('FAKE-handler-secret');
99
+ expect(attributes.credentialSeal).toStartWith('scrypt:');
100
+ // ★ the credential did reach the proxy — that is the write — and only there
101
+ expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-handler-secret' });
102
+ });
103
+
104
+ test('the sealed variable name is the only thing the declaration holds', () => {
105
+ expect(JSON.stringify(props)).not.toContain('FAKE-handler-secret');
106
+ });
107
+ });
108
+
109
+ describe('list', () => {
110
+ test('answers the rows from a bare array', async () => {
111
+ const fake = startFakeMcpLitellm({
112
+ masterKey: KEY,
113
+ seed: [serverRow({ server_id: 'FAKE-1' })],
114
+ });
115
+ expect((await run(fake.fetch, listMcpServers())).map((row) => row.server_id)).toEqual([
116
+ 'FAKE-1',
117
+ ]);
118
+ });
119
+
120
+ test('refuses an answer that is not a list of servers', async () => {
121
+ const notAList = (async () =>
122
+ new Response(JSON.stringify({ detail: 'nope' }), {
123
+ headers: { 'content-type': 'application/json' },
124
+ })) as unknown as typeof globalThis.fetch;
125
+ await expect(run(notAList, listMcpServers())).rejects.toThrow();
126
+ });
127
+ });
128
+
129
+ describe('delete', () => {
130
+ const row = serverRow({ server_id: 'FAKE-uuid-1', server_name: 'FAKE_docs' });
131
+
132
+ test('removes a live row', async () => {
133
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [row] });
134
+ await run(fake.fetch, deleteMcpServer('FAKE-uuid-1'));
135
+ expect(fake.servers()).toEqual([]);
136
+ });
137
+
138
+ test('an id already gone is done, confirmed by a table read (mcp-server-registry.test.ts)', async () => {
139
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
140
+ await expect(run(fake.fetch, deleteMcpServer('FAKE-never'))).resolves.toBeUndefined();
141
+ });
142
+
143
+ test('a delete refused for another reason is not swallowed: it fails and the row is live', async () => {
144
+ // ⚠️ the fake answers 400 to every DELETE — the status of "no rights" is unmeasured on the
145
+ // real route, which is exactly why a read of the table, not the status, decides.
146
+ const fake = startFakeMcpLitellm({ forbidDelete: true, masterKey: KEY, seed: [row] });
147
+ await expect(run(fake.fetch, deleteMcpServer('FAKE-uuid-1'))).rejects.toThrow();
148
+ expect(fake.servers()).toHaveLength(1);
149
+ });
150
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Which live row a `LiteLLM.MCPServer` declaration means, and which id a new row would ask for.
3
+ *
4
+ * ⛔ THE LIST DECIDES FIRST, THE DATABASE SECOND. `GET /v1/mcp/server` is LiteLLM's in-memory
5
+ * registry, not the table (mcp-server-operations.ts says how a committed row can be missing from
6
+ * it). A row the list does not show is therefore looked for BY ID in the database, with the id
7
+ * this declaration would use. Without that second read, a row created a moment ago whose
8
+ * registry refresh failed would be POSTed again: the id is deterministic, LiteLLM answers 400
9
+ * "already exists", and the resource would be wedged on a row it can neither see nor adopt.
10
+ * ⚠️ ADOPT-BY-NAME NEEDS THE LIST: a name cannot be read from the table by this route family. A row
11
+ * the registry lacks is adoptable only by declaring its `serverId`.
12
+ */
13
+ import type * as mcp from '@distilled.cloud/litellm/mcp_management';
14
+ import { createPhysicalName } from 'alchemy/PhysicalName';
15
+ import * as Effect from 'effect/Effect';
16
+ import { LitellmMcpServerAmbiguousNameError } from './mcp-server-errors.ts';
17
+ import { listMcpServers, readMcpServer } from './mcp-server-operations.ts';
18
+ import type { McpServerAttributes, McpServerProps } from './mcp-server-types.ts';
19
+
20
+ /**
21
+ * The live row among the LISTED ones, or `undefined`. A pinned id (from state, else declared) wins;
22
+ * otherwise the name decides, and an ambiguous name is refused.
23
+ */
24
+ export const locate = (
25
+ rows: readonly mcp.LiteLLMMCPServerTable[],
26
+ props: McpServerProps,
27
+ output: McpServerAttributes | undefined,
28
+ ) => {
29
+ const pinned = output?.serverId ?? props.serverId;
30
+ if (pinned !== undefined) return Effect.succeed(rows.find((row) => row.server_id === pinned));
31
+ const named = rows.filter((row) => row.server_name === props.serverName);
32
+ return named.length > 1
33
+ ? Effect.fail(
34
+ new LitellmMcpServerAmbiguousNameError({
35
+ serverIds: named.map((row) => row.server_id),
36
+ serverName: props.serverName,
37
+ }),
38
+ )
39
+ : Effect.succeed(named[0]);
40
+ };
41
+
42
+ /** The id a create asks for: the recorded one, else the declared one, else a deterministic name. */
43
+ export const wantedId = (
44
+ id: string,
45
+ instanceId: string,
46
+ props: McpServerProps,
47
+ output: McpServerAttributes | undefined,
48
+ ) =>
49
+ output?.serverId !== undefined
50
+ ? Effect.succeed(output.serverId)
51
+ : props.serverId !== undefined
52
+ ? Effect.succeed(props.serverId)
53
+ : createPhysicalName({ id, instanceId, lowercase: true, maxLength: 64 });
54
+
55
+ /** The live row this declaration means, from the list, else from the table by the id it would use. */
56
+ export const findLive = (
57
+ id: string,
58
+ instanceId: string,
59
+ props: McpServerProps,
60
+ output: McpServerAttributes | undefined,
61
+ ) =>
62
+ Effect.gen(function* () {
63
+ const listed = yield* locate(yield* listMcpServers(), props, output);
64
+ if (listed !== undefined) return listed;
65
+ return yield* readMcpServer(yield* wantedId(id, instanceId, props, output));
66
+ });
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The five LiteLLM `/v1/mcp/server*` calls `LiteLLM.MCPServer` uses, through the SDK's typed
3
+ * operations in `@distilled.cloud/litellm/mcp_management` (LiteLLM 1.103.0):
4
+ *
5
+ * list `fetchAllMcpServersV1McpServerGet` GET /v1/mcp/server
6
+ * read `fetchMcpServerV1McpServerServerIdGet` GET /v1/mcp/server/{server_id}
7
+ * create `addMcpServerV1McpServerPost` POST /v1/mcp/server
8
+ * update `editMcpServerV1McpServerPut` PUT /v1/mcp/server
9
+ * delete `removeMcpServerV1McpServerServerIdDelete` DELETE /v1/mcp/server/{server_id}
10
+ *
11
+ * ⛔ THE LIST IS NOT THE TABLE. Measured 2026-09-29 by reading the live 1.103.0 container:
12
+ * `GET /v1/mcp/server` answers `global_mcp_server_manager`'s IN-MEMORY REGISTRY
13
+ * (`_resolve_accessible_mcp_servers`), not `LiteLLM_MCPServerTable`. `reload_servers_from_database`
14
+ * loads only rows whose approval status is null, active or approved, and skips a row whose build
15
+ * throws; the registry is narrowed again by the caller's key (`object_permission.mcp_servers`); and
16
+ * `add_mcp_server` only LOGS a failed registry refresh after the row is committed. So a row that
17
+ * is absent from the list can be live in the database. The list is right for adopt-by-name, and
18
+ * wrong as proof of absence. The database is read by ID (`readMcpServer`): `fetch_mcp_server`
19
+ * asks `get_mcp_server` (the table) first and answers 404 only when the table, the registry and
20
+ * a name lookup all miss.
21
+ * ⚠️ THE BY-ID READ HAS SIDE EFFECTS, so it is used only where the list cannot decide: it registers
22
+ * the row in the registry (`add_server`) and runs a health check against the upstream server.
23
+ * ⛔ NO MESSAGE SNIFFING. `mcp_management` declares only `UnprocessableEntity` beyond the shared
24
+ * errors — the distilled `patches/` that type `BadRequest`/`NotFound` cover five other admin tags
25
+ * (budget, key, team, internal_user, model), not this one. A 404 from these routes still decodes
26
+ * at run time as the SDK's `NotFound` class (core's `HTTP_STATUS_MAP`), invisible to the type
27
+ * checker; `readMcpServer` tests that class with `instanceof` and nothing else, and a REAL READ
28
+ * decides (`deleteMcpServer`), the way `budget-operations.ts` and pass-through `operations.ts` do.
29
+ * The typed fix is a distilled patch for this tag, which belongs in the distilled clone.
30
+ * ⚠️ Unlike pass-through endpoints, each server is its own DB row: no semaphore.
31
+ */
32
+ import * as mcp from '@distilled.cloud/litellm/mcp_management';
33
+ import { NotFound } from '@distilled.cloud/litellm/Errors';
34
+ import * as Effect from 'effect/Effect';
35
+ import { type LitellmOpContext, throughFetch } from './operations.ts';
36
+ import { LitellmMcpServerUnreadableError } from './mcp-server-errors.ts';
37
+
38
+ const hasServerId = (row: unknown): boolean =>
39
+ typeof row === 'object' &&
40
+ row !== null &&
41
+ typeof (row as { server_id?: unknown }).server_id === 'string';
42
+
43
+ /** ⚠️ The REGISTRY as the caller may see it, not the table: see the file header. */
44
+ export const listMcpServers = (): Effect.Effect<
45
+ readonly mcp.LiteLLMMCPServerTable[],
46
+ mcp.FetchAllMcpServersV1McpServerGetError | LitellmMcpServerUnreadableError,
47
+ LitellmOpContext
48
+ > =>
49
+ throughFetch(mcp.fetchAllMcpServersV1McpServerGet({})).pipe(
50
+ // ⚠️ The SDK types the answer `{ body: LiteLLMMCPServerTable[] }`, but a top-level JSON array is
51
+ // handed back as the array itself (measured through the fake, as for `/budget/list`). Accept
52
+ // both shapes and refuse anything else.
53
+ Effect.flatMap((response) => {
54
+ const rows: unknown = Array.isArray(response)
55
+ ? response
56
+ : (response as { body?: unknown }).body;
57
+ return Array.isArray(rows) && rows.every(hasServerId)
58
+ ? Effect.succeed(rows as readonly mcp.LiteLLMMCPServerTable[])
59
+ : Effect.fail(
60
+ new LitellmMcpServerUnreadableError({
61
+ reason: Array.isArray(rows) ? 'a row has no server_id' : 'not an array',
62
+ }),
63
+ );
64
+ }),
65
+ );
66
+
67
+ /**
68
+ * One row read BY ID, DATABASE FIRST, or `undefined` when the proxy has no such row.
69
+ *
70
+ * ★ THE ROW MUST CARRY THE ID ASKED FOR. When the table has no row, `fetch_mcp_server` falls back to
71
+ * the registry and then to a lookup by NAME or alias, so an id that happens to be another server's
72
+ * name answers THAT server. A row with another id is "no such row" here.
73
+ * ⛔ ANY FAILURE OTHER THAN `NotFound` PROPAGATES: a 401, a 500 or a dead network says nothing about
74
+ * whether the row exists, and must never be read as absence.
75
+ */
76
+ export const readMcpServer = (serverId: string) =>
77
+ throughFetch(mcp.fetchMcpServerV1McpServerServerIdGet({ server_id: serverId })).pipe(
78
+ Effect.map((row) => (row.server_id === serverId ? row : undefined)),
79
+ Effect.catch((error) =>
80
+ error instanceof NotFound ? Effect.succeed(undefined) : Effect.fail(error),
81
+ ),
82
+ );
83
+
84
+ /** Answers the created row, which carries the `server_id` LiteLLM actually used. */
85
+ export const createMcpServer = (body: mcp.AddMcpServerV1McpServerPostRequest) =>
86
+ throughFetch(mcp.addMcpServerV1McpServerPost(body));
87
+
88
+ export const updateMcpServer = (body: mcp.EditMcpServerV1McpServerPutRequest) =>
89
+ throughFetch(mcp.editMcpServerV1McpServerPut(body)).pipe(Effect.asVoid);
90
+
91
+ /**
92
+ * Idempotent BY THIS FUNCTION, not by the vendor. The DELETE is ALWAYS SENT: it acts on the table
93
+ * (`delete_mcp_server` answers 404 for a missing row), so a row the list does not show, because the
94
+ * registry dropped it, is still removed. A list that skipped the call for an unlisted id would drop
95
+ * Alchemy's state for a row that stays live in the database.
96
+ *
97
+ * If the DELETE fails, the failure is swallowed only when a DATABASE-BACKED read by id says the row is
98
+ * absent (`readMcpServer`: a concurrent removal, or a row already gone). A row that is still there,
99
+ * or a read that cannot say, re-raises the ORIGINAL error — so a delete refused for lack of rights,
100
+ * which leaves the row live, is never reported as done.
101
+ */
102
+ export const deleteMcpServer = (serverId: string) =>
103
+ throughFetch(mcp.removeMcpServerV1McpServerServerIdDelete({ server_id: serverId })).pipe(
104
+ Effect.asVoid,
105
+ Effect.catch((original) =>
106
+ readMcpServer(serverId).pipe(
107
+ Effect.catch(() => Effect.fail(original)),
108
+ Effect.flatMap((row) => (row === undefined ? Effect.void : Effect.fail(original))),
109
+ ),
110
+ ),
111
+ );