@homeflare/alchemy 0.42.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (648) hide show
  1. package/README.md +10 -1
  2. package/dist/caddy/index.js +35 -1
  3. package/dist/caddy/index.js.map +3 -3
  4. package/dist/launchd/index.js +35 -1
  5. package/dist/launchd/index.js.map +3 -3
  6. package/dist/linux/index.js +35 -1
  7. package/dist/linux/index.js.map +3 -3
  8. package/dist/litellm/access-group-form.d.ts +26 -0
  9. package/dist/litellm/access-group-form.d.ts.map +1 -0
  10. package/dist/litellm/access-group-operations.d.ts +37 -0
  11. package/dist/litellm/access-group-operations.d.ts.map +1 -0
  12. package/dist/litellm/access-group-types.d.ts +49 -0
  13. package/dist/litellm/access-group-types.d.ts.map +1 -0
  14. package/dist/litellm/access-group.d.ts +55 -0
  15. package/dist/litellm/access-group.d.ts.map +1 -0
  16. package/dist/litellm/budget-errors.d.ts +36 -0
  17. package/dist/litellm/budget-errors.d.ts.map +1 -0
  18. package/dist/litellm/budget-form.d.ts +50 -0
  19. package/dist/litellm/budget-form.d.ts.map +1 -0
  20. package/dist/litellm/budget-operations.d.ts +20 -0
  21. package/dist/litellm/budget-operations.d.ts.map +1 -0
  22. package/dist/litellm/budget.d.ts +61 -0
  23. package/dist/litellm/budget.d.ts.map +1 -0
  24. package/dist/litellm/credential-errors.d.ts +131 -0
  25. package/dist/litellm/credential-errors.d.ts.map +1 -0
  26. package/dist/litellm/credential-form.d.ts +79 -0
  27. package/dist/litellm/credential-form.d.ts.map +1 -0
  28. package/dist/litellm/credential-operations.d.ts +84 -0
  29. package/dist/litellm/credential-operations.d.ts.map +1 -0
  30. package/dist/litellm/credential-rewrite.d.ts +6 -0
  31. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  32. package/dist/litellm/credential-types.d.ts +87 -0
  33. package/dist/litellm/credential-types.d.ts.map +1 -0
  34. package/dist/litellm/credential-values.d.ts +53 -0
  35. package/dist/litellm/credential-values.d.ts.map +1 -0
  36. package/dist/litellm/credential.d.ts +83 -0
  37. package/dist/litellm/credential.d.ts.map +1 -0
  38. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  39. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  41. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  42. package/dist/litellm/fake-keys.d.ts +53 -0
  43. package/dist/litellm/fake-keys.d.ts.map +1 -0
  44. package/dist/litellm/fake-litellm.d.ts +9 -1
  45. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  46. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  47. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  49. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  50. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  51. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  52. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  53. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  55. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-registry-base.d.ts +42 -0
  57. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  58. package/dist/litellm/fake-stack.d.ts +8 -1
  59. package/dist/litellm/fake-stack.d.ts.map +1 -1
  60. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  61. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  62. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  63. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  64. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  65. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  66. package/dist/litellm/index.d.ts +20 -0
  67. package/dist/litellm/index.d.ts.map +1 -1
  68. package/dist/litellm/index.js +3201 -2
  69. package/dist/litellm/index.js.map +70 -5
  70. package/dist/litellm/key-errors.d.ts +169 -0
  71. package/dist/litellm/key-errors.d.ts.map +1 -0
  72. package/dist/litellm/key-form.d.ts +119 -0
  73. package/dist/litellm/key-form.d.ts.map +1 -0
  74. package/dist/litellm/key-harness.d.ts +41 -0
  75. package/dist/litellm/key-harness.d.ts.map +1 -0
  76. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  77. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  78. package/dist/litellm/key-metadata.d.ts +22 -0
  79. package/dist/litellm/key-metadata.d.ts.map +1 -0
  80. package/dist/litellm/key-operations.d.ts +63 -0
  81. package/dist/litellm/key-operations.d.ts.map +1 -0
  82. package/dist/litellm/key-secret.d.ts +33 -0
  83. package/dist/litellm/key-secret.d.ts.map +1 -0
  84. package/dist/litellm/key.d.ts +78 -0
  85. package/dist/litellm/key.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  87. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  89. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-form.d.ts +56 -0
  91. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  93. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  95. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  96. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  97. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  98. package/dist/litellm/mcp-server-types.d.ts +118 -0
  99. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  100. package/dist/litellm/mcp-server-url.d.ts +11 -0
  101. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  102. package/dist/litellm/mcp-server.d.ts +95 -0
  103. package/dist/litellm/mcp-server.d.ts.map +1 -0
  104. package/dist/litellm/model-claim.d.ts +24 -0
  105. package/dist/litellm/model-claim.d.ts.map +1 -0
  106. package/dist/litellm/model-credential.d.ts +12 -0
  107. package/dist/litellm/model-credential.d.ts.map +1 -0
  108. package/dist/litellm/model-errors.d.ts +119 -0
  109. package/dist/litellm/model-errors.d.ts.map +1 -0
  110. package/dist/litellm/model-form.d.ts +78 -0
  111. package/dist/litellm/model-form.d.ts.map +1 -0
  112. package/dist/litellm/model-locate.d.ts +13 -0
  113. package/dist/litellm/model-locate.d.ts.map +1 -0
  114. package/dist/litellm/model-operations.d.ts +59 -0
  115. package/dist/litellm/model-operations.d.ts.map +1 -0
  116. package/dist/litellm/model-types.d.ts +91 -0
  117. package/dist/litellm/model-types.d.ts.map +1 -0
  118. package/dist/litellm/model.d.ts +88 -0
  119. package/dist/litellm/model.d.ts.map +1 -0
  120. package/dist/litellm/operations.d.ts +22 -0
  121. package/dist/litellm/operations.d.ts.map +1 -1
  122. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  123. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  124. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  125. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  126. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  127. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  128. package/dist/litellm/policy-attachment.d.ts +61 -0
  129. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  130. package/dist/litellm/policy-form.d.ts +22 -0
  131. package/dist/litellm/policy-form.d.ts.map +1 -0
  132. package/dist/litellm/policy-operations.d.ts +42 -0
  133. package/dist/litellm/policy-operations.d.ts.map +1 -0
  134. package/dist/litellm/policy-types.d.ts +57 -0
  135. package/dist/litellm/policy-types.d.ts.map +1 -0
  136. package/dist/litellm/policy.d.ts +65 -0
  137. package/dist/litellm/policy.d.ts.map +1 -0
  138. package/dist/litellm/providers.d.ts +1 -1
  139. package/dist/litellm/providers.d.ts.map +1 -1
  140. package/dist/litellm/registry-errors.d.ts +62 -0
  141. package/dist/litellm/registry-errors.d.ts.map +1 -0
  142. package/dist/litellm/registry-support.d.ts +42 -0
  143. package/dist/litellm/registry-support.d.ts.map +1 -0
  144. package/dist/litellm/team-form.d.ts +26 -0
  145. package/dist/litellm/team-form.d.ts.map +1 -0
  146. package/dist/litellm/team-members.d.ts +10 -0
  147. package/dist/litellm/team-members.d.ts.map +1 -0
  148. package/dist/litellm/team-operations.d.ts +38 -0
  149. package/dist/litellm/team-operations.d.ts.map +1 -0
  150. package/dist/litellm/team-permission.d.ts +35 -0
  151. package/dist/litellm/team-permission.d.ts.map +1 -0
  152. package/dist/litellm/team-types.d.ts +101 -0
  153. package/dist/litellm/team-types.d.ts.map +1 -0
  154. package/dist/litellm/team.d.ts +67 -0
  155. package/dist/litellm/team.d.ts.map +1 -0
  156. package/dist/litellm/tool-policy-form.d.ts +21 -0
  157. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  158. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  159. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  160. package/dist/litellm/tool-policy-types.d.ts +46 -0
  161. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  162. package/dist/litellm/tool-policy.d.ts +62 -0
  163. package/dist/litellm/tool-policy.d.ts.map +1 -0
  164. package/dist/litellm/toolset-form.d.ts +20 -0
  165. package/dist/litellm/toolset-form.d.ts.map +1 -0
  166. package/dist/litellm/toolset-operations.d.ts +39 -0
  167. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  168. package/dist/litellm/toolset-types.d.ts +46 -0
  169. package/dist/litellm/toolset-types.d.ts.map +1 -0
  170. package/dist/litellm/toolset.d.ts +55 -0
  171. package/dist/litellm/toolset.d.ts.map +1 -0
  172. package/dist/openbao/auth-role-form.d.ts +9 -0
  173. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  174. package/dist/openbao/auth-role-wire.d.ts.map +1 -1
  175. package/dist/openbao/fake-engines-roles.d.ts.map +1 -1
  176. package/dist/openbao/index.js +43 -6
  177. package/dist/openbao/index.js.map +5 -5
  178. package/dist/ownership/rows.d.ts +11 -0
  179. package/dist/ownership/rows.d.ts.map +1 -1
  180. package/dist/postgres/connection.d.ts +32 -2
  181. package/dist/postgres/connection.d.ts.map +1 -1
  182. package/dist/postgres/database-sql.d.ts +28 -9
  183. package/dist/postgres/database-sql.d.ts.map +1 -1
  184. package/dist/postgres/database.d.ts +1 -1
  185. package/dist/postgres/database.d.ts.map +1 -1
  186. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  187. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  188. package/dist/postgres/fake-grants-read.d.ts +61 -0
  189. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  190. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  191. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  192. package/dist/postgres/fake-grants-write.d.ts +29 -0
  193. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  194. package/dist/postgres/fake-role-sql.d.ts +40 -0
  195. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  196. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  197. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  198. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  199. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  200. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  201. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  202. package/dist/postgres/fake-sql.d.ts +42 -0
  203. package/dist/postgres/fake-sql.d.ts.map +1 -1
  204. package/dist/postgres/grants-attrs.d.ts +115 -0
  205. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  206. package/dist/postgres/grants-declare.d.ts +88 -0
  207. package/dist/postgres/grants-declare.d.ts.map +1 -0
  208. package/dist/postgres/grants-diff.d.ts +25 -0
  209. package/dist/postgres/grants-diff.d.ts.map +1 -0
  210. package/dist/postgres/grants-errors.d.ts +137 -0
  211. package/dist/postgres/grants-errors.d.ts.map +1 -0
  212. package/dist/postgres/grants-existence.d.ts +14 -0
  213. package/dist/postgres/grants-existence.d.ts.map +1 -0
  214. package/dist/postgres/grants-ops.d.ts +65 -0
  215. package/dist/postgres/grants-ops.d.ts.map +1 -0
  216. package/dist/postgres/grants-ownership.d.ts +18 -0
  217. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  218. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  219. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  220. package/dist/postgres/grants-plan.d.ts +65 -0
  221. package/dist/postgres/grants-plan.d.ts.map +1 -0
  222. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  223. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  224. package/dist/postgres/grants-read.d.ts +77 -0
  225. package/dist/postgres/grants-read.d.ts.map +1 -0
  226. package/dist/postgres/grants-refuse.d.ts +25 -0
  227. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  228. package/dist/postgres/grants-sql.d.ts +42 -0
  229. package/dist/postgres/grants-sql.d.ts.map +1 -0
  230. package/dist/postgres/grants-words.d.ts +29 -0
  231. package/dist/postgres/grants-words.d.ts.map +1 -0
  232. package/dist/postgres/grants.d.ts +37 -0
  233. package/dist/postgres/grants.d.ts.map +1 -0
  234. package/dist/postgres/index.d.ts +35 -8
  235. package/dist/postgres/index.d.ts.map +1 -1
  236. package/dist/postgres/index.js +2063 -39
  237. package/dist/postgres/index.js.map +47 -7
  238. package/dist/postgres/installed-classifier.d.ts +13 -0
  239. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  240. package/dist/postgres/providers.d.ts +17 -7
  241. package/dist/postgres/providers.d.ts.map +1 -1
  242. package/dist/postgres/psql-executor.d.ts +25 -0
  243. package/dist/postgres/psql-executor.d.ts.map +1 -0
  244. package/dist/postgres/role-attrs.d.ts +109 -0
  245. package/dist/postgres/role-attrs.d.ts.map +1 -0
  246. package/dist/postgres/role-diff.d.ts +22 -0
  247. package/dist/postgres/role-diff.d.ts.map +1 -0
  248. package/dist/postgres/role-errors.d.ts +126 -0
  249. package/dist/postgres/role-errors.d.ts.map +1 -0
  250. package/dist/postgres/role-membership-sql.d.ts +75 -0
  251. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  252. package/dist/postgres/role-provider.d.ts +11 -0
  253. package/dist/postgres/role-provider.d.ts.map +1 -0
  254. package/dist/postgres/role-scram.d.ts +6 -0
  255. package/dist/postgres/role-scram.d.ts.map +1 -0
  256. package/dist/postgres/role-secrets.d.ts +30 -0
  257. package/dist/postgres/role-secrets.d.ts.map +1 -0
  258. package/dist/postgres/role-sql.d.ts +68 -0
  259. package/dist/postgres/role-sql.d.ts.map +1 -0
  260. package/dist/postgres/role.d.ts +57 -0
  261. package/dist/postgres/role.d.ts.map +1 -0
  262. package/dist/postgres/schema-assert.d.ts +50 -0
  263. package/dist/postgres/schema-assert.d.ts.map +1 -0
  264. package/dist/postgres/schema-attrs.d.ts +51 -0
  265. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  266. package/dist/postgres/schema-delete.d.ts +28 -0
  267. package/dist/postgres/schema-delete.d.ts.map +1 -0
  268. package/dist/postgres/schema-diff.d.ts +14 -0
  269. package/dist/postgres/schema-diff.d.ts.map +1 -0
  270. package/dist/postgres/schema-errors.d.ts +139 -0
  271. package/dist/postgres/schema-errors.d.ts.map +1 -0
  272. package/dist/postgres/schema-identity.d.ts +8 -0
  273. package/dist/postgres/schema-identity.d.ts.map +1 -0
  274. package/dist/postgres/schema-reconcile.d.ts +12 -0
  275. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  276. package/dist/postgres/schema-sql.d.ts +45 -0
  277. package/dist/postgres/schema-sql.d.ts.map +1 -0
  278. package/dist/postgres/schema-test-kit.d.ts +67 -0
  279. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  280. package/dist/postgres/schema.d.ts +54 -0
  281. package/dist/postgres/schema.d.ts.map +1 -0
  282. package/dist/proxmox/ceph-flag-types.d.ts +57 -0
  283. package/dist/proxmox/ceph-flag-types.d.ts.map +1 -0
  284. package/dist/proxmox/ceph-flag-wire.d.ts +77 -0
  285. package/dist/proxmox/ceph-flag-wire.d.ts.map +1 -0
  286. package/dist/proxmox/ceph-flag.d.ts +15 -50
  287. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  288. package/dist/proxmox/ceph-osd-write.d.ts +10 -13
  289. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  290. package/dist/proxmox/index.js +806 -740
  291. package/dist/proxmox/index.js.map +9 -8
  292. package/dist/release/fake-engine.d.ts +10 -0
  293. package/dist/release/fake-engine.d.ts.map +1 -1
  294. package/dist/release/index.js +35 -1
  295. package/dist/release/index.js.map +3 -3
  296. package/dist/valkey/acl-attrs.d.ts +94 -0
  297. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  298. package/dist/valkey/acl-compare.d.ts +28 -0
  299. package/dist/valkey/acl-compare.d.ts.map +1 -0
  300. package/dist/valkey/acl-connection.d.ts +6 -0
  301. package/dist/valkey/acl-connection.d.ts.map +1 -0
  302. package/dist/valkey/acl-form.d.ts +62 -0
  303. package/dist/valkey/acl-form.d.ts.map +1 -0
  304. package/dist/valkey/acl-managed.d.ts +4 -0
  305. package/dist/valkey/acl-managed.d.ts.map +1 -0
  306. package/dist/valkey/acl-ops.d.ts +37 -0
  307. package/dist/valkey/acl-ops.d.ts.map +1 -0
  308. package/dist/valkey/acl-profiles.d.ts +53 -0
  309. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  310. package/dist/valkey/acl-reconcile.d.ts +26 -0
  311. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  312. package/dist/valkey/acl.d.ts +24 -0
  313. package/dist/valkey/acl.d.ts.map +1 -0
  314. package/dist/valkey/connection-service.d.ts +22 -0
  315. package/dist/valkey/connection-service.d.ts.map +1 -0
  316. package/dist/valkey/connection.d.ts +59 -0
  317. package/dist/valkey/connection.d.ts.map +1 -0
  318. package/dist/valkey/errors.d.ts +165 -0
  319. package/dist/valkey/errors.d.ts.map +1 -0
  320. package/dist/valkey/fake-acl.d.ts +29 -0
  321. package/dist/valkey/fake-acl.d.ts.map +1 -0
  322. package/dist/valkey/fake-valkey.d.ts +35 -0
  323. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  324. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  325. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  326. package/dist/valkey/index.d.ts +18 -0
  327. package/dist/valkey/index.d.ts.map +1 -0
  328. package/dist/valkey/index.js +964 -0
  329. package/dist/valkey/index.js.map +27 -0
  330. package/dist/valkey/instance-attrs.d.ts +46 -0
  331. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  332. package/dist/valkey/instance-form.d.ts +22 -0
  333. package/dist/valkey/instance-form.d.ts.map +1 -0
  334. package/dist/valkey/instance.d.ts +48 -0
  335. package/dist/valkey/instance.d.ts.map +1 -0
  336. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  337. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  338. package/dist/valkey/providers.d.ts +16 -0
  339. package/dist/valkey/providers.d.ts.map +1 -0
  340. package/dist/valkey/resp.d.ts +38 -0
  341. package/dist/valkey/resp.d.ts.map +1 -0
  342. package/dist/valkey/transport-error.d.ts +36 -0
  343. package/dist/valkey/transport-error.d.ts.map +1 -0
  344. package/dist/valkey/transport.d.ts +50 -0
  345. package/dist/valkey/transport.d.ts.map +1 -0
  346. package/docs/litellm-access-group.md +105 -0
  347. package/docs/litellm-budget.md +55 -0
  348. package/docs/litellm-credential.md +60 -0
  349. package/docs/litellm-key-metadata.md +56 -0
  350. package/docs/litellm-key.md +194 -0
  351. package/docs/litellm-mcp.md +187 -0
  352. package/docs/litellm-model.md +111 -0
  353. package/docs/litellm-policy-attachment.md +86 -0
  354. package/docs/litellm-policy.md +103 -0
  355. package/docs/litellm-team.md +117 -0
  356. package/docs/litellm-tool-policy.md +90 -0
  357. package/docs/litellm-toolset.md +92 -0
  358. package/docs/litellm.md +16 -8
  359. package/docs/peers.md +15 -6
  360. package/docs/postgres-grants-example.md +55 -0
  361. package/docs/postgres-grants.md +157 -0
  362. package/docs/postgres-role.md +114 -0
  363. package/docs/postgres-schema.md +139 -0
  364. package/docs/postgres.md +39 -0
  365. package/docs/proxmox-sdk-migration.md +45 -0
  366. package/docs/valkey-transport.md +29 -0
  367. package/docs/valkey.md +187 -0
  368. package/package.json +7 -3
  369. package/src/litellm/access-group-form.ts +105 -0
  370. package/src/litellm/access-group-operations.ts +92 -0
  371. package/src/litellm/access-group-types.ts +49 -0
  372. package/src/litellm/access-group.test.ts +197 -0
  373. package/src/litellm/access-group.ts +151 -0
  374. package/src/litellm/budget-errors.ts +38 -0
  375. package/src/litellm/budget-form.ts +94 -0
  376. package/src/litellm/budget-operations.ts +51 -0
  377. package/src/litellm/budget.test.ts +105 -0
  378. package/src/litellm/budget.ts +127 -0
  379. package/src/litellm/credential-errors.ts +183 -0
  380. package/src/litellm/credential-form.test.ts +151 -0
  381. package/src/litellm/credential-form.ts +211 -0
  382. package/src/litellm/credential-guards.test.ts +241 -0
  383. package/src/litellm/credential-operations.ts +208 -0
  384. package/src/litellm/credential-recovery.test.ts +93 -0
  385. package/src/litellm/credential-rewrite.test.ts +164 -0
  386. package/src/litellm/credential-rewrite.ts +50 -0
  387. package/src/litellm/credential-types.ts +117 -0
  388. package/src/litellm/credential-values.ts +94 -0
  389. package/src/litellm/credential.test.ts +218 -0
  390. package/src/litellm/credential.ts +227 -0
  391. package/src/litellm/fake-access-group-litellm.ts +127 -0
  392. package/src/litellm/fake-credential-litellm.ts +216 -0
  393. package/src/litellm/fake-keys.ts +190 -0
  394. package/src/litellm/fake-litellm.ts +66 -7
  395. package/src/litellm/fake-mcp-litellm.ts +214 -0
  396. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  397. package/src/litellm/fake-model-litellm.ts +233 -0
  398. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  399. package/src/litellm/fake-policy-litellm.ts +186 -0
  400. package/src/litellm/fake-registry-base.ts +69 -0
  401. package/src/litellm/fake-stack.ts +17 -1
  402. package/src/litellm/fake-team-litellm.ts +229 -0
  403. package/src/litellm/fake-tool-litellm.ts +88 -0
  404. package/src/litellm/fake-toolset-litellm.ts +110 -0
  405. package/src/litellm/index.ts +153 -0
  406. package/src/litellm/key-delete.test.ts +212 -0
  407. package/src/litellm/key-errors.ts +241 -0
  408. package/src/litellm/key-form.ts +200 -0
  409. package/src/litellm/key-harness.ts +121 -0
  410. package/src/litellm/key-metadata-errors.ts +49 -0
  411. package/src/litellm/key-metadata.test.ts +156 -0
  412. package/src/litellm/key-metadata.ts +74 -0
  413. package/src/litellm/key-operations.ts +184 -0
  414. package/src/litellm/key-rotation.test.ts +110 -0
  415. package/src/litellm/key-scope.test.ts +135 -0
  416. package/src/litellm/key-secret.test.ts +248 -0
  417. package/src/litellm/key-secret.ts +137 -0
  418. package/src/litellm/key-transport.test.ts +166 -0
  419. package/src/litellm/key.test.ts +231 -0
  420. package/src/litellm/key.ts +213 -0
  421. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  422. package/src/litellm/mcp-server-alias.test.ts +184 -0
  423. package/src/litellm/mcp-server-credential.ts +85 -0
  424. package/src/litellm/mcp-server-errors.ts +141 -0
  425. package/src/litellm/mcp-server-exports.test.ts +15 -0
  426. package/src/litellm/mcp-server-form.test.ts +241 -0
  427. package/src/litellm/mcp-server-form.ts +233 -0
  428. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  429. package/src/litellm/mcp-server-locate.ts +66 -0
  430. package/src/litellm/mcp-server-operations.ts +111 -0
  431. package/src/litellm/mcp-server-registry.test.ts +146 -0
  432. package/src/litellm/mcp-server-retype.test.ts +114 -0
  433. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  434. package/src/litellm/mcp-server-shadow.ts +45 -0
  435. package/src/litellm/mcp-server-tools.test.ts +85 -0
  436. package/src/litellm/mcp-server-types.ts +143 -0
  437. package/src/litellm/mcp-server-url.ts +66 -0
  438. package/src/litellm/mcp-server.test.ts +213 -0
  439. package/src/litellm/mcp-server.ts +225 -0
  440. package/src/litellm/model-claim.ts +69 -0
  441. package/src/litellm/model-credential.ts +45 -0
  442. package/src/litellm/model-errors.ts +168 -0
  443. package/src/litellm/model-foreign-row.test.ts +173 -0
  444. package/src/litellm/model-form.test.ts +228 -0
  445. package/src/litellm/model-form.ts +223 -0
  446. package/src/litellm/model-locate.test.ts +128 -0
  447. package/src/litellm/model-locate.ts +67 -0
  448. package/src/litellm/model-operations.test.ts +93 -0
  449. package/src/litellm/model-operations.ts +146 -0
  450. package/src/litellm/model-params-preserve.test.ts +119 -0
  451. package/src/litellm/model-recovery.test.ts +57 -0
  452. package/src/litellm/model-types.ts +112 -0
  453. package/src/litellm/model.test.ts +222 -0
  454. package/src/litellm/model.ts +237 -0
  455. package/src/litellm/operations.ts +1 -1
  456. package/src/litellm/policy-attachment-form.ts +103 -0
  457. package/src/litellm/policy-attachment-operations.ts +74 -0
  458. package/src/litellm/policy-attachment-types.ts +48 -0
  459. package/src/litellm/policy-attachment.test.ts +198 -0
  460. package/src/litellm/policy-attachment.ts +178 -0
  461. package/src/litellm/policy-form.ts +122 -0
  462. package/src/litellm/policy-operations.ts +112 -0
  463. package/src/litellm/policy-types.ts +58 -0
  464. package/src/litellm/policy.test.ts +214 -0
  465. package/src/litellm/policy.ts +146 -0
  466. package/src/litellm/providers.ts +25 -1
  467. package/src/litellm/registry-errors.ts +88 -0
  468. package/src/litellm/registry-exports.test.ts +66 -0
  469. package/src/litellm/registry-support.ts +109 -0
  470. package/src/litellm/team-form.ts +140 -0
  471. package/src/litellm/team-members.test.ts +150 -0
  472. package/src/litellm/team-members.ts +58 -0
  473. package/src/litellm/team-operations.ts +90 -0
  474. package/src/litellm/team-permission.ts +159 -0
  475. package/src/litellm/team-read.test.ts +89 -0
  476. package/src/litellm/team-types.ts +106 -0
  477. package/src/litellm/team.test.ts +206 -0
  478. package/src/litellm/team.ts +159 -0
  479. package/src/litellm/tool-policy-form.ts +94 -0
  480. package/src/litellm/tool-policy-operations.ts +58 -0
  481. package/src/litellm/tool-policy-types.ts +48 -0
  482. package/src/litellm/tool-policy.test.ts +172 -0
  483. package/src/litellm/tool-policy.ts +125 -0
  484. package/src/litellm/toolset-form.ts +104 -0
  485. package/src/litellm/toolset-operations.ts +90 -0
  486. package/src/litellm/toolset-types.ts +47 -0
  487. package/src/litellm/toolset.test.ts +198 -0
  488. package/src/litellm/toolset.ts +151 -0
  489. package/src/openbao/auth-role-form.test.ts +37 -0
  490. package/src/openbao/auth-role-form.ts +12 -1
  491. package/src/openbao/auth-role-wire.test.ts +1 -0
  492. package/src/openbao/auth-role-wire.ts +13 -1
  493. package/src/openbao/distilled-retry.test.ts +1 -0
  494. package/src/openbao/fake-engines-roles.ts +7 -1
  495. package/src/ownership/ownership.test.ts +33 -1
  496. package/src/ownership/rows.ts +53 -0
  497. package/src/postgres/connection.ts +72 -3
  498. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  499. package/src/postgres/database-refusals.test.ts +2 -2
  500. package/src/postgres/database-sql.ts +40 -12
  501. package/src/postgres/database.ts +3 -2
  502. package/src/postgres/fake-grants-parse.ts +189 -0
  503. package/src/postgres/fake-grants-read.ts +250 -0
  504. package/src/postgres/fake-grants-sql.ts +207 -0
  505. package/src/postgres/fake-grants-write.ts +90 -0
  506. package/src/postgres/fake-role-sql.ts +220 -0
  507. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  508. package/src/postgres/fake-schema-sql.ts +146 -0
  509. package/src/postgres/fake-sql-parse.ts +86 -0
  510. package/src/postgres/fake-sql-quote.ts +36 -0
  511. package/src/postgres/fake-sql.ts +161 -46
  512. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  513. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  514. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  515. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  516. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  517. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  518. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  519. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  520. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  521. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  522. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  523. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  524. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  525. package/src/postgres/grants-acl.test.ts +61 -0
  526. package/src/postgres/grants-attrs.ts +185 -0
  527. package/src/postgres/grants-database.test.ts +158 -0
  528. package/src/postgres/grants-declare.ts +172 -0
  529. package/src/postgres/grants-delete-proof.test.ts +154 -0
  530. package/src/postgres/grants-diff.ts +101 -0
  531. package/src/postgres/grants-errors.ts +215 -0
  532. package/src/postgres/grants-existence-guards.test.ts +113 -0
  533. package/src/postgres/grants-existence.ts +101 -0
  534. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  535. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  536. package/src/postgres/grants-lifecycle.test.ts +181 -0
  537. package/src/postgres/grants-ops.ts +229 -0
  538. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  539. package/src/postgres/grants-ownership.ts +49 -0
  540. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  541. package/src/postgres/grants-plan-diff.test.ts +204 -0
  542. package/src/postgres/grants-plan-restore.ts +43 -0
  543. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  544. package/src/postgres/grants-plan.test.ts +148 -0
  545. package/src/postgres/grants-plan.ts +245 -0
  546. package/src/postgres/grants-provenance.test.ts +134 -0
  547. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  548. package/src/postgres/grants-read-absent.test.ts +91 -0
  549. package/src/postgres/grants-read-defaults.ts +51 -0
  550. package/src/postgres/grants-read.ts +250 -0
  551. package/src/postgres/grants-refusals.test.ts +136 -0
  552. package/src/postgres/grants-refuse.ts +119 -0
  553. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  554. package/src/postgres/grants-repair-plan.test.ts +201 -0
  555. package/src/postgres/grants-sql.test.ts +128 -0
  556. package/src/postgres/grants-sql.ts +144 -0
  557. package/src/postgres/grants-words.ts +34 -0
  558. package/src/postgres/grants.ts +110 -0
  559. package/src/postgres/index.ts +137 -7
  560. package/src/postgres/installed-classifier.ts +36 -0
  561. package/src/postgres/providers.ts +44 -7
  562. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  563. package/src/postgres/psql-executor.test.ts +167 -0
  564. package/src/postgres/psql-executor.ts +170 -0
  565. package/src/postgres/role-adopt.test.ts +98 -0
  566. package/src/postgres/role-attrs.ts +134 -0
  567. package/src/postgres/role-diff.ts +89 -0
  568. package/src/postgres/role-errors.ts +198 -0
  569. package/src/postgres/role-guard.test.ts +60 -0
  570. package/src/postgres/role-identity.test.ts +185 -0
  571. package/src/postgres/role-lifecycle.test.ts +228 -0
  572. package/src/postgres/role-membership-sql.ts +198 -0
  573. package/src/postgres/role-password.test.ts +158 -0
  574. package/src/postgres/role-privilege.test.ts +80 -0
  575. package/src/postgres/role-provenance.test.ts +200 -0
  576. package/src/postgres/role-provider.ts +104 -0
  577. package/src/postgres/role-refusals.test.ts +250 -0
  578. package/src/postgres/role-scram.test.ts +38 -0
  579. package/src/postgres/role-scram.ts +38 -0
  580. package/src/postgres/role-secrets.ts +46 -0
  581. package/src/postgres/role-sql.ts +211 -0
  582. package/src/postgres/role-valid-until.test.ts +68 -0
  583. package/src/postgres/role.ts +218 -0
  584. package/src/postgres/schema-assert.ts +139 -0
  585. package/src/postgres/schema-attrs.ts +71 -0
  586. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  587. package/src/postgres/schema-delete.ts +96 -0
  588. package/src/postgres/schema-diff.ts +44 -0
  589. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  590. package/src/postgres/schema-errors.ts +209 -0
  591. package/src/postgres/schema-handlers.test.ts +193 -0
  592. package/src/postgres/schema-identity.ts +39 -0
  593. package/src/postgres/schema-lifecycle.test.ts +246 -0
  594. package/src/postgres/schema-race.test.ts +70 -0
  595. package/src/postgres/schema-reconcile.ts +100 -0
  596. package/src/postgres/schema-refusals.test.ts +144 -0
  597. package/src/postgres/schema-socket.test.ts +161 -0
  598. package/src/postgres/schema-sql.ts +130 -0
  599. package/src/postgres/schema-takeover.test.ts +149 -0
  600. package/src/postgres/schema-test-kit.ts +85 -0
  601. package/src/postgres/schema.ts +130 -0
  602. package/src/proxmox/ceph-flag-types.ts +70 -0
  603. package/src/proxmox/ceph-flag-wire.test.ts +72 -0
  604. package/src/proxmox/ceph-flag-wire.ts +123 -0
  605. package/src/proxmox/ceph-flag.ts +77 -130
  606. package/src/proxmox/ceph-osd-write.ts +10 -13
  607. package/src/release/binary-alias.test.ts +3 -3
  608. package/src/release/binary-claims.test.ts +3 -3
  609. package/src/release/binary-directory-umask.test.ts +65 -0
  610. package/src/release/fake-engine.ts +15 -0
  611. package/src/valkey/acl-attrs.ts +98 -0
  612. package/src/valkey/acl-compare.ts +57 -0
  613. package/src/valkey/acl-connection.ts +13 -0
  614. package/src/valkey/acl-delete.test.ts +127 -0
  615. package/src/valkey/acl-form.ts +239 -0
  616. package/src/valkey/acl-managed.ts +13 -0
  617. package/src/valkey/acl-ops.ts +122 -0
  618. package/src/valkey/acl-ownership.test.ts +118 -0
  619. package/src/valkey/acl-prefix.test.ts +38 -0
  620. package/src/valkey/acl-profiles.ts +81 -0
  621. package/src/valkey/acl-reconcile.ts +180 -0
  622. package/src/valkey/acl-reserved.test.ts +69 -0
  623. package/src/valkey/acl-revocation.test.ts +94 -0
  624. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  625. package/src/valkey/acl-validation.test.ts +54 -0
  626. package/src/valkey/acl.test.ts +215 -0
  627. package/src/valkey/acl.ts +169 -0
  628. package/src/valkey/connection-service.ts +46 -0
  629. package/src/valkey/connection.ts +180 -0
  630. package/src/valkey/errors.ts +239 -0
  631. package/src/valkey/fake-acl.ts +128 -0
  632. package/src/valkey/fake-valkey.ts +224 -0
  633. package/src/valkey/handler-test-fixture.ts +43 -0
  634. package/src/valkey/handlers.test.ts +205 -0
  635. package/src/valkey/index.ts +54 -0
  636. package/src/valkey/instance-attrs.ts +48 -0
  637. package/src/valkey/instance-form.ts +104 -0
  638. package/src/valkey/instance.test.ts +108 -0
  639. package/src/valkey/instance.ts +160 -0
  640. package/src/valkey/provider-socket-fixture.ts +71 -0
  641. package/src/valkey/providers.test.ts +129 -0
  642. package/src/valkey/providers.ts +21 -0
  643. package/src/valkey/resp.ts +212 -0
  644. package/src/valkey/transport-bounds.test.ts +150 -0
  645. package/src/valkey/transport-error.ts +37 -0
  646. package/src/valkey/transport.test.ts +158 -0
  647. package/src/valkey/transport.ts +109 -0
  648. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * `LiteLLM.Toolset` through Alchemy's real Plan and Apply over the fake proxy
3
+ * (`fake-toolset-litellm.ts`): create, no-op, adopt by name, update, rename and delete.
4
+ *
5
+ * ★ EVERY VALUE IS `FAKE-*`.
6
+ * ⚠️ WHAT THE FAKE MODELS IS READ FROM THE 1.103.0 SOURCE, NOT MEASURED ON A LIVE PROXY (its header).
7
+ */
8
+ import { expect, test } from 'bun:test';
9
+ import { credentials } from '@distilled.cloud/litellm/Credentials';
10
+ import * as Effect from 'effect/Effect';
11
+ import * as Layer from 'effect/Layer';
12
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
13
+ import { FAKE_BASE } from './fake-litellm.ts';
14
+ import { FAKE_KEY } from './fake-registry-base.ts';
15
+ import {
16
+ type FakeToolsetLitellm,
17
+ startFakeToolsetLitellm,
18
+ toolsetRow,
19
+ } from './fake-toolset-litellm.ts';
20
+ import { fakeStack, writesOf } from './fake-stack.ts';
21
+ import { LiteLLMToolset } from './toolset.ts';
22
+ import { deleteToolset } from './toolset-operations.ts';
23
+ import type { ToolsetProps } from './toolset-types.ts';
24
+
25
+ const stack = (fake: FakeToolsetLitellm) =>
26
+ fakeStack({ apiKey: FAKE_KEY, baseUrl: FAKE_BASE }, fake.fetch);
27
+ const declare = (props: ToolsetProps, name = 'Web') =>
28
+ Effect.gen(function* () {
29
+ yield* LiteLLMToolset(name, props);
30
+ });
31
+
32
+ const web: ToolsetProps = {
33
+ description: 'FAKE web tools',
34
+ tools: [
35
+ { serverId: 'FAKE-server-a', toolName: 'search' },
36
+ { serverId: 'FAKE-server-b', toolName: 'fetch' },
37
+ ],
38
+ toolsetName: 'FAKE-web',
39
+ };
40
+
41
+ test('creates a toolset with the pairs, remembers the id the proxy issued, then writes nothing', async () => {
42
+ const fake = startFakeToolsetLitellm();
43
+ const same = stack(fake);
44
+ expect(await same.deploy(declare(web))).toEqual({ Web: 'create' });
45
+ expect(fake.toolsets()[0]).toMatchObject({
46
+ description: 'FAKE web tools',
47
+ tools: web.tools.map((tool) => ({ server_id: tool.serverId, tool_name: tool.toolName })),
48
+ toolset_name: 'FAKE-web',
49
+ });
50
+ const before = fake.requests().length;
51
+ expect(await same.deploy(declare(web))).toEqual({ Web: 'noop' });
52
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
53
+ });
54
+
55
+ test('the list is used ONLY to adopt: with state, every read is by id and the list is never asked', async () => {
56
+ const fake = startFakeToolsetLitellm();
57
+ const same = stack(fake);
58
+ await same.deploy(declare(web));
59
+ const before = fake.requests().length;
60
+ await same.deploy(declare({ ...web, description: 'FAKE changed' }));
61
+ const paths = fake
62
+ .requests()
63
+ .slice(before)
64
+ .map((each) => `${each.method} ${each.path}`);
65
+ expect(paths).not.toContain('GET /v1/mcp/toolset');
66
+ expect(paths.some((path) => path.startsWith('GET /v1/mcp/toolset/FAKE-toolset-'))).toBe(true);
67
+ });
68
+
69
+ test('a changed selection is an update that sends the id and ONLY the changed field', async () => {
70
+ const fake = startFakeToolsetLitellm();
71
+ const same = stack(fake);
72
+ await same.deploy(declare(web));
73
+ const planned = await same.deploy(
74
+ declare({ ...web, tools: [{ serverId: 'FAKE-server-a', toolName: 'search' }] }),
75
+ );
76
+ expect(planned).toEqual({ Web: 'update' });
77
+ expect(fake.bodies().at(-1)).toEqual({
78
+ tools: [{ server_id: 'FAKE-server-a', tool_name: 'search' }],
79
+ toolset_id: 'FAKE-toolset-0001',
80
+ });
81
+ });
82
+
83
+ test('an empty selection is sent as an explicit empty list, because a null would be a no-op', async () => {
84
+ const fake = startFakeToolsetLitellm();
85
+ const same = stack(fake);
86
+ await same.deploy(declare(web));
87
+ await same.deploy(declare({ ...web, tools: [] }));
88
+ expect(fake.bodies().at(-1)).toMatchObject({ tools: [] });
89
+ expect(fake.toolsets()[0]?.['tools']).toEqual([]);
90
+ });
91
+
92
+ test('a rename is an update of the SAME row', async () => {
93
+ const fake = startFakeToolsetLitellm();
94
+ const same = stack(fake);
95
+ await same.deploy(declare(web));
96
+ expect(await same.deploy(declare({ ...web, toolsetName: 'FAKE-renamed' }))).toEqual({
97
+ Web: 'update',
98
+ });
99
+ expect(fake.toolsets()).toHaveLength(1);
100
+ expect(fake.toolsets()[0]).toMatchObject({
101
+ toolset_id: 'FAKE-toolset-0001',
102
+ toolset_name: 'FAKE-renamed',
103
+ });
104
+ });
105
+
106
+ test('a live row is Unowned: refused without --adopt, adopted by name with it, and a matching row writes nothing', async () => {
107
+ const live = toolsetRow({
108
+ description: 'FAKE web tools',
109
+ tools: [
110
+ { server_id: 'FAKE-server-b', tool_name: 'fetch' },
111
+ { server_id: 'FAKE-server-a', tool_name: 'search' },
112
+ ],
113
+ toolset_id: 'FAKE-live-1',
114
+ toolset_name: 'FAKE-web',
115
+ });
116
+ const fake = startFakeToolsetLitellm({ seed: [live] });
117
+ const engine = stack(fake);
118
+ await expect(engine.deploy(declare(web))).rejects.toThrow();
119
+ expect(writesOf(fake.requests())).toEqual([]);
120
+ expect(Object.values(await engine.deploy(declare(web), { adopt: true }))).toEqual(['adopted']);
121
+ expect(writesOf(fake.requests())).toEqual([]);
122
+ });
123
+
124
+ test('adopting a toolset with MORE tools than declared corrects it, and keeps undeclared text', async () => {
125
+ const wide = toolsetRow({
126
+ description: 'written by a person',
127
+ tools: [
128
+ { server_id: 'FAKE-server-a', tool_name: 'search' },
129
+ { server_id: 'FAKE-server-b', tool_name: 'fetch' },
130
+ { server_id: 'FAKE-server-b', tool_name: 'FAKE-extra' },
131
+ ],
132
+ toolset_id: 'FAKE-live-2',
133
+ toolset_name: 'FAKE-web',
134
+ });
135
+ const fake = startFakeToolsetLitellm({ seed: [wide] });
136
+ const { description: _kept, ...withoutDescription } = web;
137
+ await stack(fake).deploy(declare(withoutDescription), { adopt: true });
138
+ expect(fake.toolsets()[0]?.['tools']).toHaveLength(2);
139
+ expect(fake.toolsets()[0]?.['description']).toBe('written by a person');
140
+ expect(fake.bodies()[0]).not.toHaveProperty('description');
141
+ });
142
+
143
+ test('a list that swallowed a database error cannot hide an existing name: the create fails on the 409', async () => {
144
+ // ⛔ measured in the source: list_mcp_toolsets answers [] on any exception
145
+ const live = toolsetRow({ toolset_id: 'FAKE-live-3', toolset_name: 'FAKE-web' });
146
+ const fake = startFakeToolsetLitellm({ listSwallowsErrors: true, seed: [live] });
147
+ await expect(stack(fake).deploy(declare(web))).rejects.toThrow();
148
+ expect(fake.toolsets()).toHaveLength(1);
149
+ });
150
+
151
+ test('a refused declaration fails the plan before a single request', async () => {
152
+ for (const props of [
153
+ { ...web, toolsetName: ' ' },
154
+ { ...web, toolsetName: 'a/b' },
155
+ { ...web, description: ' ' },
156
+ { ...web, tools: [{ serverId: '', toolName: 'search' }] },
157
+ { ...web, tools: [web.tools[0], web.tools[0]] as never },
158
+ ]) {
159
+ const fake = startFakeToolsetLitellm();
160
+ await expect(stack(fake).deploy(declare(props))).rejects.toThrow();
161
+ expect(fake.requests()).toEqual([]);
162
+ }
163
+ });
164
+
165
+ test('removing the declaration RETAINS the toolset', async () => {
166
+ const fake = startFakeToolsetLitellm();
167
+ const same = stack(fake);
168
+ await same.deploy(declare(web));
169
+ await same.deploy(Effect.void);
170
+ expect(fake.toolsets()).toHaveLength(1);
171
+ });
172
+
173
+ const run = <A, E>(fetchFn: typeof globalThis.fetch, effect: Effect.Effect<A, E, unknown>) =>
174
+ Effect.runPromise(
175
+ effect.pipe(
176
+ Effect.provide(FetchHttpClient.layer),
177
+ Effect.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)),
178
+ Effect.provide(credentials({ apiKey: FAKE_KEY, baseUrl: FAKE_BASE })),
179
+ ) as Effect.Effect<A, E, never>,
180
+ );
181
+
182
+ test('delete answers 202 with no body, and a missing id is swallowed only because a by-id read says so', async () => {
183
+ const fake = startFakeToolsetLitellm({
184
+ seed: [toolsetRow({ toolset_id: 'FAKE-live-4', toolset_name: 'FAKE-web' })],
185
+ });
186
+ await run(fake.fetch, deleteToolset('FAKE-live-4'));
187
+ expect(fake.toolsets()).toEqual([]);
188
+ await run(fake.fetch, deleteToolset('FAKE-live-4'));
189
+ });
190
+
191
+ test('a delete refused for lack of rights re-raises the ORIGINAL error and the row stays', async () => {
192
+ const fake = startFakeToolsetLitellm({
193
+ forbidWrites: true,
194
+ seed: [toolsetRow({ toolset_id: 'FAKE-live-5', toolset_name: 'FAKE-web' })],
195
+ });
196
+ await expect(run(fake.fetch, deleteToolset('FAKE-live-5'))).rejects.toThrow();
197
+ expect(fake.toolsets()).toHaveLength(1);
198
+ });
@@ -0,0 +1,151 @@
1
+ /**
2
+ * `LiteLLM.Toolset` — one row of LiteLLM's MCP toolset table (`/v1/mcp/toolset`): a named selection of
3
+ * `{ server_id, tool_name }` pairs a key or team can be granted instead of whole servers. Props and
4
+ * what is left out: toolset-types.ts.
5
+ *
6
+ * ★ ADOPT BY NAME. `toolset_name` is unique, so `toolsetName` finds the one live row (from the LIST,
7
+ * which is the only by-name lookup the API has, and which cannot prove absence: toolset-operations.ts).
8
+ * A live row with no state is `Unowned`, so it needs `--adopt`.
9
+ * ★ THE ID IS THE PROXY'S and is what state remembers; every later read is BY ID, from the table.
10
+ * ★ `defaultRemovalPolicy: 'retain'` — deleting a toolset does not detach it: teams and keys that hold
11
+ * its id keep a dangling grant, and every caller reaching MCP through it loses the tools.
12
+ * ⚠️ NO LICENCE GATE ON THESE ROUTES: `mcp_management_endpoints.py`'s toolset block has no
13
+ * `premium_user` check (1.103.0), and docs.litellm.ai/docs/mcp_toolsets makes no Enterprise statement.
14
+ * Writes need PROXY_ADMIN (403 otherwise).
15
+ * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.
16
+ */
17
+ import { Resource } from 'alchemy';
18
+ import { Unowned } from 'alchemy/AdoptPolicy';
19
+ import { isResolved } from 'alchemy/Diff';
20
+ import type { Input } from 'alchemy/Input';
21
+ import * as Provider from 'alchemy/Provider';
22
+ import * as Effect from 'effect/Effect';
23
+ import * as Predicate from 'effect/Predicate';
24
+ import {
25
+ LitellmRegistryAbsentAfterWriteError,
26
+ LitellmRegistryAmbiguousError,
27
+ LitellmRegistryNotConvergedError,
28
+ LitellmRegistryUnreadableError,
29
+ } from './registry-errors.ts';
30
+ import { dieIfInvalid, failIfInvalid } from './registry-support.ts';
31
+ import { createBody, differing, firstProblem, updateBody } from './toolset-form.ts';
32
+ import {
33
+ createToolset,
34
+ deleteToolset,
35
+ listToolsets,
36
+ readToolset,
37
+ updateToolset,
38
+ } from './toolset-operations.ts';
39
+ import type { ToolsetAttributes, ToolsetProps } from './toolset-types.ts';
40
+
41
+ export type { ToolsetAttributes, ToolsetProps };
42
+ export type { RegistryError as ToolsetError } from './registry-errors.ts';
43
+
44
+ const RESOURCE = 'LiteLLM.Toolset';
45
+
46
+ export interface LiteLLMToolset extends Resource<
47
+ 'LiteLLM.Toolset',
48
+ ToolsetProps,
49
+ ToolsetAttributes
50
+ > {}
51
+
52
+ export const LiteLLMToolset = Resource<LiteLLMToolset>('LiteLLM.Toolset', {
53
+ defaultRemovalPolicy: 'retain',
54
+ });
55
+
56
+ export const isLiteLLMToolset = (value: unknown): value is LiteLLMToolset =>
57
+ Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Toolset';
58
+
59
+ /**
60
+ * The live row this declaration means: BY ID from the table when state has one (a vanished row is
61
+ * `undefined`, never taken over by name), else by name from the list. Two rows sharing a name would
62
+ * mean the unique index is gone; refused, never guessed.
63
+ */
64
+ const locate = (props: ToolsetProps, output: ToolsetAttributes | undefined) =>
65
+ Effect.gen(function* () {
66
+ if (output !== undefined) return yield* readToolset(output.toolsetId);
67
+ const named = (yield* listToolsets()).filter((row) => row.toolsetName === props.toolsetName);
68
+ if (named.length > 1) {
69
+ return yield* Effect.fail(
70
+ new LitellmRegistryAmbiguousError({
71
+ ids: named.map((row) => row.toolsetId),
72
+ name: props.toolsetName,
73
+ resource: RESOURCE,
74
+ }),
75
+ );
76
+ }
77
+ return named[0];
78
+ });
79
+
80
+ /** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */
81
+ export const toolsetHandlers = {
82
+ /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */
83
+ read: ({ olds, output }: { olds: ToolsetProps; output: ToolsetAttributes | undefined }) =>
84
+ Effect.gen(function* () {
85
+ if (output === undefined) {
86
+ yield* dieIfInvalid(RESOURCE, String(olds.toolsetName), firstProblem(olds));
87
+ }
88
+ const live = yield* locate(olds, output);
89
+ if (live === undefined) return undefined;
90
+ return output === undefined ? Unowned(live) : live;
91
+ }),
92
+
93
+ diff: ({ news, output }: { news: Input<ToolsetProps>; output: ToolsetAttributes | undefined }) =>
94
+ Effect.gen(function* () {
95
+ if (!isResolved(news)) return undefined;
96
+ yield* failIfInvalid(RESOURCE, String(news.toolsetName), firstProblem(news));
97
+ if (output === undefined) return undefined;
98
+ return differing(output, news).length === 0
99
+ ? ({ action: 'noop' } as const)
100
+ : ({ action: 'update' } as const);
101
+ }),
102
+
103
+ reconcile: ({ news, output }: { news: ToolsetProps; output: ToolsetAttributes | undefined }) =>
104
+ Effect.gen(function* () {
105
+ yield* failIfInvalid(RESOURCE, String(news.toolsetName), firstProblem(news));
106
+ const before = yield* locate(news, output);
107
+ let toolsetId: string;
108
+ if (before === undefined) {
109
+ const created = yield* createToolset(createBody(news));
110
+ if (created === undefined) {
111
+ return yield* Effect.fail(
112
+ new LitellmRegistryUnreadableError({
113
+ reason: 'the create answered something that is not a toolset',
114
+ resource: RESOURCE,
115
+ }),
116
+ );
117
+ }
118
+ toolsetId = created.toolsetId;
119
+ } else {
120
+ toolsetId = before.toolsetId;
121
+ if (differing(before, news).length > 0) yield* updateToolset(updateBody(news, before));
122
+ }
123
+
124
+ // ★ BY ID FROM THE TABLE, never the list: the list is the caller-narrowed, error-swallowing view.
125
+ const after = yield* readToolset(toolsetId);
126
+ if (after === undefined) {
127
+ return yield* Effect.fail(
128
+ new LitellmRegistryAbsentAfterWriteError({ name: news.toolsetName, resource: RESOURCE }),
129
+ );
130
+ }
131
+ const left = differing(after, news);
132
+ if (left.length > 0) {
133
+ return yield* Effect.fail(
134
+ new LitellmRegistryNotConvergedError({
135
+ fields: left,
136
+ name: news.toolsetName,
137
+ resource: RESOURCE,
138
+ }),
139
+ );
140
+ }
141
+ return after;
142
+ }),
143
+
144
+ delete: ({ output }: { output: ToolsetAttributes }) => deleteToolset(output.toolsetId),
145
+
146
+ /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */
147
+ list: () => Effect.succeed([]),
148
+ };
149
+
150
+ export const LiteLLMToolsetProvider = () =>
151
+ Provider.effect(LiteLLMToolset, Effect.succeed(LiteLLMToolset.Provider.of(toolsetHandlers)));
@@ -43,3 +43,40 @@ describe('auth role secret_id TTL', () => {
43
43
  expect(matches(attributesOf(PROPS, { ...live, secret_id_ttl: 0 }), PROPS)).toBe(false);
44
44
  });
45
45
  });
46
+
47
+ describe('auth role token_period', () => {
48
+ const LIVE = {
49
+ bind_secret_id: true,
50
+ secret_id_num_uses: 0,
51
+ secret_id_ttl: 7776000,
52
+ token_max_ttl: 3600,
53
+ token_period: 0,
54
+ token_policies: ['default', 'host-cert'],
55
+ token_ttl: 900,
56
+ };
57
+
58
+ it('omits token_period from the SDK write when the prop is unset', async () => {
59
+ expect((await Effect.runPromise(authRoleRequest(PROPS))).token_period).toBeUndefined();
60
+ });
61
+
62
+ it('writes 24h as the equivalent SDK seconds, never legacy period', async () => {
63
+ const props = { ...PROPS, tokenPeriod: '24h' };
64
+ const request = await Effect.runPromise(authRoleRequest(props));
65
+ expect(request.token_period).toBe(86400);
66
+ expect(request).not.toHaveProperty('period');
67
+ });
68
+
69
+ it('reads a live token_period back and matches an equivalent declaration', () => {
70
+ const props = { ...PROPS, tokenPeriod: '24h' };
71
+ const attributes = attributesOf(props, { ...LIVE, token_period: 86400 });
72
+ expect(attributes.tokenPeriod).toBe('1d');
73
+ expect(matches(attributes, props)).toBe(true);
74
+ expect(matches(attributesOf(props, { ...LIVE, token_period: 3600 }), props)).toBe(false);
75
+ });
76
+
77
+ it('leaves an unmanaged live token_period out of drift when the prop is unset', () => {
78
+ const attributes = attributesOf(PROPS, { ...LIVE, token_period: 86400 });
79
+ expect(attributes.tokenPeriod).toBe('1d');
80
+ expect(matches(attributes, PROPS)).toBe(true);
81
+ });
82
+ });
@@ -16,6 +16,14 @@ export interface BaoAuthRoleProps {
16
16
  bindSecretId?: boolean;
17
17
  /** `0` means unlimited uses — the shape reconcile.sh writes for headless roles. */
18
18
  secretIdNumUses?: number;
19
+ /**
20
+ * Token period, e.g. `24h` — set only for a periodic (forever-renewable) role, where
21
+ * `tokenTtl`/`tokenMaxTtl` are conventionally `'0'`. Omitted means this declaration does not
22
+ * manage the field: a live role's `token_period` (however it got set) is left untouched, the
23
+ * same way an omitted `bindSecretId` is. See house `openbao/approles.ts` for the role this
24
+ * unblocks — a periodic agent token that was inexpressible with these props.
25
+ */
26
+ tokenPeriod?: string;
19
27
  }
20
28
 
21
29
  export interface BaoAuthRoleAttributes {
@@ -26,6 +34,7 @@ export interface BaoAuthRoleAttributes {
26
34
  secretIdTtl: string;
27
35
  bindSecretId: boolean;
28
36
  secretIdNumUses: number;
37
+ tokenPeriod: string;
29
38
  /** SHA-256 of the managed fields — safe to persist; see policy.ts. */
30
39
  digest: string;
31
40
  }
@@ -64,6 +73,7 @@ export const attributesOf = (
64
73
  secretIdTtl: ttlText(ttlSeconds(live['secret_id_ttl']), '0'),
65
74
  bindSecretId: bool(live['bind_secret_id'], true),
66
75
  secretIdNumUses: int(live['secret_id_num_uses'], 0),
76
+ tokenPeriod: ttlText(ttlSeconds(live['token_period']), '0'),
67
77
  };
68
78
  return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
69
79
  };
@@ -88,6 +98,7 @@ export const matches = (attributes: BaoAuthRoleAttributes, props: BaoAuthRolePro
88
98
  sameTtl(props.tokenMaxTtl, attributes.tokenMaxTtl) &&
89
99
  sameTtl(props.secretIdTtl, attributes.secretIdTtl) &&
90
100
  (props.bindSecretId === undefined || attributes.bindSecretId === props.bindSecretId) &&
91
- (props.secretIdNumUses === undefined || attributes.secretIdNumUses === props.secretIdNumUses)
101
+ (props.secretIdNumUses === undefined || attributes.secretIdNumUses === props.secretIdNumUses) &&
102
+ (props.tokenPeriod === undefined || sameTtl(props.tokenPeriod, attributes.tokenPeriod))
92
103
  );
93
104
  };
@@ -16,6 +16,7 @@ const DATA = {
16
16
  secret_id_num_uses: 0,
17
17
  secret_id_ttl: 7776000,
18
18
  token_max_ttl: 3600,
19
+ token_period: 0,
19
20
  token_policies: ['host-cert'],
20
21
  token_ttl: 900,
21
22
  };
@@ -35,6 +35,7 @@ const completeRole = (live: AppRoleReadRoleResponse): boolean =>
35
35
  seconds(live.secret_id_ttl) &&
36
36
  seconds(live.token_ttl) &&
37
37
  seconds(live.token_max_ttl) &&
38
+ seconds(live.token_period) &&
38
39
  Array.isArray(live.token_policies) &&
39
40
  live.token_policies.every((policy) => typeof policy === 'string');
40
41
 
@@ -56,7 +57,14 @@ export const authRoleRequest = (props: BaoAuthRoleProps) =>
56
57
  const tokenTtl = parseDuration(props.tokenTtl);
57
58
  const tokenMaxTtl = parseDuration(props.tokenMaxTtl);
58
59
  const secretIdTtl = parseDuration(props.secretIdTtl);
59
- if (!seconds(tokenTtl) || !seconds(tokenMaxTtl) || !seconds(secretIdTtl)) {
60
+ const tokenPeriod =
61
+ props.tokenPeriod === undefined ? undefined : parseDuration(props.tokenPeriod);
62
+ if (
63
+ !seconds(tokenTtl) ||
64
+ !seconds(tokenMaxTtl) ||
65
+ !seconds(secretIdTtl) ||
66
+ (props.tokenPeriod !== undefined && !seconds(tokenPeriod))
67
+ ) {
60
68
  return yield* Effect.fail(
61
69
  new BaoError(0, 'POST', readPath(props.name), ['invalid role TTL duration']),
62
70
  );
@@ -64,6 +72,9 @@ export const authRoleRequest = (props: BaoAuthRoleProps) =>
64
72
  // ★ The former CLI-style body used strings for every k=v. The SDK schema uses seconds,
65
73
  // booleans and arrays; omitted knobs stay omitted on update.
66
74
  // pathRoleCreateUpdate preserves absent bind_secret_id/secret_id_num_uses on existing roles.
75
+ // ⛔ token_period, not period — the vendor doc on both fields says period is legacy and loses
76
+ // to token_period when both are set (approle.ts AppRoleWriteRoleRequest); this Resource only
77
+ // ever emits the modern field.
67
78
  const request: AppRoleWriteRoleRequest = {
68
79
  ...identity(props.name),
69
80
  token_policies: [...props.tokenPolicies],
@@ -72,6 +83,7 @@ export const authRoleRequest = (props: BaoAuthRoleProps) =>
72
83
  secret_id_ttl: secretIdTtl,
73
84
  ...(props.bindSecretId === undefined ? {} : { bind_secret_id: props.bindSecretId }),
74
85
  ...(props.secretIdNumUses === undefined ? {} : { secret_id_num_uses: props.secretIdNumUses }),
86
+ ...(tokenPeriod === undefined ? {} : { token_period: tokenPeriod }),
75
87
  };
76
88
  return request;
77
89
  });
@@ -25,6 +25,7 @@ const ROLE = {
25
25
  secret_id_num_uses: 0,
26
26
  secret_id_ttl: 0,
27
27
  token_max_ttl: 3600,
28
+ token_period: 0,
28
29
  token_policies: ['default'],
29
30
  token_ttl: 900,
30
31
  };
@@ -57,7 +57,13 @@ export const wireRoles = (): Store =>
57
57
  store<Stored>(
58
58
  (path, body, before) =>
59
59
  path.startsWith('auth/approle/role/')
60
- ? { bind_secret_id: true, secret_id_num_uses: 0, ...before, ...typed(body) }
60
+ ? {
61
+ bind_secret_id: true,
62
+ secret_id_num_uses: 0,
63
+ token_period: 0,
64
+ ...before,
65
+ ...typed(body),
66
+ }
61
67
  : typed(body),
62
68
  (path) => keyOf(path),
63
69
  );
@@ -12,7 +12,7 @@ import * as Effect from 'effect/Effect';
12
12
  import { refuseTakeover } from './adopt.ts';
13
13
  import { ownedRead } from './probe.ts';
14
14
  import { noteResume, noteUnfinished, resumes } from './resume.ts';
15
- import { forgetRefusedCreate, isCreate, recordedInstance } from './rows.ts';
15
+ import { forgetRefusedCreate, isCreate, olderGenerationIds, recordedInstance } from './rows.ts';
16
16
  import { row, withStore } from './store-fixture.ts';
17
17
 
18
18
  describe('recordedInstance', () => {
@@ -50,6 +50,38 @@ describe('isCreate', () => {
50
50
  });
51
51
  });
52
52
 
53
+ describe('olderGenerationIds', () => {
54
+ test('collects attr.id from every generation older than the instance, through the old chain', async () => {
55
+ const rows = {
56
+ A: {
57
+ ...row('A', 'new', { ...row('A', 'older'), attr: { id: 'older-id' } }),
58
+ attr: { id: 'new-id' },
59
+ },
60
+ };
61
+ expect(await withStore(rows, olderGenerationIds('A', 'new'))).toEqual(['older-id']);
62
+ });
63
+
64
+ test('walks a former FQN the instance moved away from (renamedFrom)', async () => {
65
+ // Z is declared renamedFrom('X'). beta.79 moves the row (preserving instanceId) and deletes
66
+ // the former FQN; while a pre-move row still sits at X, the FormerFqns walk finds it and
67
+ // collects the older generation it holds.
68
+ const rows = {
69
+ Z: row('Z', 'new'),
70
+ X: {
71
+ ...row('X', 'new', { ...row('X', 'older'), attr: { id: 'older-id' } }),
72
+ attr: { id: 'new-id' },
73
+ },
74
+ };
75
+ expect(await withStore(rows, olderGenerationIds('Z', 'new'))).toEqual(['older-id']);
76
+ });
77
+
78
+ test('the current generation itself is skipped, and an empty store answers nothing', async () => {
79
+ const rows = { A: { ...row('A', 'new'), attr: { id: 'new-id' } } };
80
+ expect(await withStore(rows, olderGenerationIds('A', 'new'))).toEqual([]);
81
+ expect(await Effect.runPromise(olderGenerationIds('A', 'new'))).toEqual([]);
82
+ });
83
+ });
84
+
53
85
  describe('forgetRefusedCreate', () => {
54
86
  test('drops only the creating row of this instance, with no attributes', async () => {
55
87
  const rows: Record<string, ResourceState> = {
@@ -99,6 +99,59 @@ export const isCreate = (fqn: string, instanceId: string): Effect.Effect<boolean
99
99
  (generation) => generation?.status === 'creating',
100
100
  );
101
101
 
102
+ const idOf = (attr: unknown): string | undefined => {
103
+ if (typeof attr !== 'object' || attr === null) return undefined;
104
+ const id = (attr as { readonly id?: unknown }).id;
105
+ return typeof id === 'string' && id !== '' ? id : undefined;
106
+ };
107
+
108
+ /**
109
+ * `attr.id` on every generation OLDER than `instanceId` in this FQN's state chain (and a former
110
+ * FQN `renamedFrom` names). Walks `old` the way {@link generationOf} does, and skips the current
111
+ * generation itself.
112
+ * ⛔ THOSE IDS ARE NOT THIS GENERATION'S. A `replacing` row's reconcile runs with no attributes, so
113
+ * a live row found by name can be the previous generation's own deployment. Recording that id on
114
+ * the new generation makes both the same id, and `destroy()` then deletes the old generation by
115
+ * it (Apply.ts, the retain check near 2168 and the provider delete that follows when the policy
116
+ * is not `retain`). A same-instance `old` snapshot (an `updating` row) is not one of these.
117
+ */
118
+ export const olderGenerationIds = (
119
+ fqn: string,
120
+ instanceId: string,
121
+ ): Effect.Effect<readonly string[]> =>
122
+ Effect.gen(function* () {
123
+ const found = yield* storeOf;
124
+ if (found === undefined) return [];
125
+ const ids: string[] = [];
126
+ for (const at of [fqn, ...(found.resources[fqn]?.FormerFqns ?? [])]) {
127
+ collectOlder(yield* rowAt(found.store, found.where, at), instanceId, ids);
128
+ }
129
+ return ids;
130
+ });
131
+
132
+ /**
133
+ * Once `instanceId` has been seen, collect `attr.id` from generations with a DIFFERENT instance
134
+ * id. An `updating` row's `old` is the same instance's pre-update snapshot (Apply.ts), not a
135
+ * replacement generation — that snapshot's id is the row this apply is already writing.
136
+ */
137
+ const collectOlder = (row: unknown, instanceId: string, ids: string[]): void => {
138
+ let at = row;
139
+ let seen = false;
140
+ while (typeof at === 'object' && at !== null && !isActionState(at as never)) {
141
+ const generation = at as Generation;
142
+ if (!seen) {
143
+ seen = generation.instanceId === instanceId;
144
+ at = generation.old;
145
+ continue;
146
+ }
147
+ if (generation.instanceId !== instanceId) {
148
+ const id = idOf(generation.attr);
149
+ if (id !== undefined) ids.push(id);
150
+ }
151
+ at = generation.old;
152
+ }
153
+ };
154
+
102
155
  /**
103
156
  * ⛔ A REFUSED CREATE MUST NOT LEAVE A ROW THAT CLAIMS THE OBJECT IT REFUSED. Apply commits a
104
157
  * `creating` row for this instance before it calls reconcile, and a failed reconcile leaves it