@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.
- package/README.md +10 -1
- package/dist/caddy/index.js +35 -1
- package/dist/caddy/index.js.map +3 -3
- package/dist/launchd/index.js +35 -1
- package/dist/launchd/index.js.map +3 -3
- package/dist/linux/index.js +35 -1
- package/dist/linux/index.js.map +3 -3
- package/dist/litellm/access-group-form.d.ts +26 -0
- package/dist/litellm/access-group-form.d.ts.map +1 -0
- package/dist/litellm/access-group-operations.d.ts +37 -0
- package/dist/litellm/access-group-operations.d.ts.map +1 -0
- package/dist/litellm/access-group-types.d.ts +49 -0
- package/dist/litellm/access-group-types.d.ts.map +1 -0
- package/dist/litellm/access-group.d.ts +55 -0
- package/dist/litellm/access-group.d.ts.map +1 -0
- package/dist/litellm/credential-errors.d.ts +131 -0
- package/dist/litellm/credential-errors.d.ts.map +1 -0
- package/dist/litellm/credential-form.d.ts +79 -0
- package/dist/litellm/credential-form.d.ts.map +1 -0
- package/dist/litellm/credential-operations.d.ts +84 -0
- package/dist/litellm/credential-operations.d.ts.map +1 -0
- package/dist/litellm/credential-rewrite.d.ts +6 -0
- package/dist/litellm/credential-rewrite.d.ts.map +1 -0
- package/dist/litellm/credential-types.d.ts +87 -0
- package/dist/litellm/credential-types.d.ts.map +1 -0
- package/dist/litellm/credential-values.d.ts +53 -0
- package/dist/litellm/credential-values.d.ts.map +1 -0
- package/dist/litellm/credential.d.ts +83 -0
- package/dist/litellm/credential.d.ts.map +1 -0
- package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
- package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-credential-litellm.d.ts +54 -0
- package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-keys.d.ts +53 -0
- package/dist/litellm/fake-keys.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +4 -1
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
- package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm.d.ts +48 -0
- package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-litellm.d.ts +32 -0
- package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-registry-base.d.ts +42 -0
- package/dist/litellm/fake-registry-base.d.ts.map +1 -0
- package/dist/litellm/fake-stack.d.ts +8 -1
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/fake-team-litellm.d.ts +47 -0
- package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-tool-litellm.d.ts +28 -0
- package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
- package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
- package/dist/litellm/index.d.ts +18 -0
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +3078 -2
- package/dist/litellm/index.js.map +65 -4
- package/dist/litellm/key-errors.d.ts +169 -0
- package/dist/litellm/key-errors.d.ts.map +1 -0
- package/dist/litellm/key-form.d.ts +119 -0
- package/dist/litellm/key-form.d.ts.map +1 -0
- package/dist/litellm/key-harness.d.ts +41 -0
- package/dist/litellm/key-harness.d.ts.map +1 -0
- package/dist/litellm/key-metadata-errors.d.ts +30 -0
- package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
- package/dist/litellm/key-metadata.d.ts +22 -0
- package/dist/litellm/key-metadata.d.ts.map +1 -0
- package/dist/litellm/key-operations.d.ts +63 -0
- package/dist/litellm/key-operations.d.ts.map +1 -0
- package/dist/litellm/key-secret.d.ts +33 -0
- package/dist/litellm/key-secret.d.ts.map +1 -0
- package/dist/litellm/key.d.ts +78 -0
- package/dist/litellm/key.d.ts.map +1 -0
- package/dist/litellm/mcp-server-credential.d.ts +50 -0
- package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
- package/dist/litellm/mcp-server-errors.d.ts +101 -0
- package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
- package/dist/litellm/mcp-server-form.d.ts +56 -0
- package/dist/litellm/mcp-server-form.d.ts.map +1 -0
- package/dist/litellm/mcp-server-locate.d.ts +26 -0
- package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
- package/dist/litellm/mcp-server-operations.d.ts +63 -0
- package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
- package/dist/litellm/mcp-server-shadow.d.ts +24 -0
- package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
- package/dist/litellm/mcp-server-types.d.ts +118 -0
- package/dist/litellm/mcp-server-types.d.ts.map +1 -0
- package/dist/litellm/mcp-server-url.d.ts +11 -0
- package/dist/litellm/mcp-server-url.d.ts.map +1 -0
- package/dist/litellm/mcp-server.d.ts +95 -0
- package/dist/litellm/mcp-server.d.ts.map +1 -0
- package/dist/litellm/model-claim.d.ts +24 -0
- package/dist/litellm/model-claim.d.ts.map +1 -0
- package/dist/litellm/model-credential.d.ts +12 -0
- package/dist/litellm/model-credential.d.ts.map +1 -0
- package/dist/litellm/model-errors.d.ts +119 -0
- package/dist/litellm/model-errors.d.ts.map +1 -0
- package/dist/litellm/model-form.d.ts +78 -0
- package/dist/litellm/model-form.d.ts.map +1 -0
- package/dist/litellm/model-locate.d.ts +13 -0
- package/dist/litellm/model-locate.d.ts.map +1 -0
- package/dist/litellm/model-operations.d.ts +59 -0
- package/dist/litellm/model-operations.d.ts.map +1 -0
- package/dist/litellm/model-types.d.ts +91 -0
- package/dist/litellm/model-types.d.ts.map +1 -0
- package/dist/litellm/model.d.ts +88 -0
- package/dist/litellm/model.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-form.d.ts +19 -0
- package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-operations.d.ts +31 -0
- package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-types.d.ts +48 -0
- package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
- package/dist/litellm/policy-attachment.d.ts +61 -0
- package/dist/litellm/policy-attachment.d.ts.map +1 -0
- package/dist/litellm/policy-form.d.ts +22 -0
- package/dist/litellm/policy-form.d.ts.map +1 -0
- package/dist/litellm/policy-operations.d.ts +42 -0
- package/dist/litellm/policy-operations.d.ts.map +1 -0
- package/dist/litellm/policy-types.d.ts +57 -0
- package/dist/litellm/policy-types.d.ts.map +1 -0
- package/dist/litellm/policy.d.ts +65 -0
- package/dist/litellm/policy.d.ts.map +1 -0
- package/dist/litellm/providers.d.ts +1 -1
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/litellm/registry-errors.d.ts +62 -0
- package/dist/litellm/registry-errors.d.ts.map +1 -0
- package/dist/litellm/registry-support.d.ts +42 -0
- package/dist/litellm/registry-support.d.ts.map +1 -0
- package/dist/litellm/team-form.d.ts +26 -0
- package/dist/litellm/team-form.d.ts.map +1 -0
- package/dist/litellm/team-members.d.ts +10 -0
- package/dist/litellm/team-members.d.ts.map +1 -0
- package/dist/litellm/team-operations.d.ts +38 -0
- package/dist/litellm/team-operations.d.ts.map +1 -0
- package/dist/litellm/team-permission.d.ts +35 -0
- package/dist/litellm/team-permission.d.ts.map +1 -0
- package/dist/litellm/team-types.d.ts +101 -0
- package/dist/litellm/team-types.d.ts.map +1 -0
- package/dist/litellm/team.d.ts +67 -0
- package/dist/litellm/team.d.ts.map +1 -0
- package/dist/litellm/tool-policy-form.d.ts +21 -0
- package/dist/litellm/tool-policy-form.d.ts.map +1 -0
- package/dist/litellm/tool-policy-operations.d.ts +30 -0
- package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
- package/dist/litellm/tool-policy-types.d.ts +46 -0
- package/dist/litellm/tool-policy-types.d.ts.map +1 -0
- package/dist/litellm/tool-policy.d.ts +62 -0
- package/dist/litellm/tool-policy.d.ts.map +1 -0
- package/dist/litellm/toolset-form.d.ts +20 -0
- package/dist/litellm/toolset-form.d.ts.map +1 -0
- package/dist/litellm/toolset-operations.d.ts +39 -0
- package/dist/litellm/toolset-operations.d.ts.map +1 -0
- package/dist/litellm/toolset-types.d.ts +46 -0
- package/dist/litellm/toolset-types.d.ts.map +1 -0
- package/dist/litellm/toolset.d.ts +55 -0
- package/dist/litellm/toolset.d.ts.map +1 -0
- package/dist/openbao/index.js +35 -1
- package/dist/openbao/index.js.map +3 -3
- package/dist/ownership/rows.d.ts +11 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/postgres/connection.d.ts +14 -2
- package/dist/postgres/connection.d.ts.map +1 -1
- package/dist/postgres/database-sql.d.ts +27 -8
- package/dist/postgres/database-sql.d.ts.map +1 -1
- package/dist/postgres/fake-grants-parse.d.ts +37 -0
- package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
- package/dist/postgres/fake-grants-read.d.ts +61 -0
- package/dist/postgres/fake-grants-read.d.ts.map +1 -0
- package/dist/postgres/fake-grants-sql.d.ts +46 -0
- package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
- package/dist/postgres/fake-grants-write.d.ts +29 -0
- package/dist/postgres/fake-grants-write.d.ts.map +1 -0
- package/dist/postgres/fake-role-sql.d.ts +41 -0
- package/dist/postgres/fake-role-sql.d.ts.map +1 -0
- package/dist/postgres/fake-schema-sql.d.ts +42 -0
- package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
- package/dist/postgres/fake-sql-parse.d.ts +23 -0
- package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
- package/dist/postgres/fake-sql-quote.d.ts +12 -0
- package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
- package/dist/postgres/fake-sql.d.ts +42 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -1
- package/dist/postgres/grants-attrs.d.ts +115 -0
- package/dist/postgres/grants-attrs.d.ts.map +1 -0
- package/dist/postgres/grants-declare.d.ts +88 -0
- package/dist/postgres/grants-declare.d.ts.map +1 -0
- package/dist/postgres/grants-diff.d.ts +25 -0
- package/dist/postgres/grants-diff.d.ts.map +1 -0
- package/dist/postgres/grants-errors.d.ts +137 -0
- package/dist/postgres/grants-errors.d.ts.map +1 -0
- package/dist/postgres/grants-existence.d.ts +14 -0
- package/dist/postgres/grants-existence.d.ts.map +1 -0
- package/dist/postgres/grants-ops.d.ts +65 -0
- package/dist/postgres/grants-ops.d.ts.map +1 -0
- package/dist/postgres/grants-ownership.d.ts +18 -0
- package/dist/postgres/grants-ownership.d.ts.map +1 -0
- package/dist/postgres/grants-plan-restore.d.ts +22 -0
- package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
- package/dist/postgres/grants-plan.d.ts +65 -0
- package/dist/postgres/grants-plan.d.ts.map +1 -0
- package/dist/postgres/grants-read-defaults.d.ts +17 -0
- package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
- package/dist/postgres/grants-read.d.ts +77 -0
- package/dist/postgres/grants-read.d.ts.map +1 -0
- package/dist/postgres/grants-refuse.d.ts +25 -0
- package/dist/postgres/grants-refuse.d.ts.map +1 -0
- package/dist/postgres/grants-sql.d.ts +42 -0
- package/dist/postgres/grants-sql.d.ts.map +1 -0
- package/dist/postgres/grants-words.d.ts +29 -0
- package/dist/postgres/grants-words.d.ts.map +1 -0
- package/dist/postgres/grants.d.ts +37 -0
- package/dist/postgres/grants.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +31 -6
- package/dist/postgres/index.d.ts.map +1 -1
- package/dist/postgres/index.js +1998 -37
- package/dist/postgres/index.js.map +46 -7
- package/dist/postgres/installed-classifier.d.ts +13 -0
- package/dist/postgres/installed-classifier.d.ts.map +1 -0
- package/dist/postgres/providers.d.ts +16 -8
- package/dist/postgres/providers.d.ts.map +1 -1
- package/dist/postgres/psql-executor.d.ts +4 -0
- package/dist/postgres/psql-executor.d.ts.map +1 -1
- package/dist/postgres/role-attrs.d.ts +112 -0
- package/dist/postgres/role-attrs.d.ts.map +1 -0
- package/dist/postgres/role-diff.d.ts +22 -0
- package/dist/postgres/role-diff.d.ts.map +1 -0
- package/dist/postgres/role-errors.d.ts +132 -0
- package/dist/postgres/role-errors.d.ts.map +1 -0
- package/dist/postgres/role-membership-sql.d.ts +78 -0
- package/dist/postgres/role-membership-sql.d.ts.map +1 -0
- package/dist/postgres/role-provider.d.ts +11 -0
- package/dist/postgres/role-provider.d.ts.map +1 -0
- package/dist/postgres/role-scram.d.ts +8 -0
- package/dist/postgres/role-scram.d.ts.map +1 -0
- package/dist/postgres/role-secrets.d.ts +38 -0
- package/dist/postgres/role-secrets.d.ts.map +1 -0
- package/dist/postgres/role-sql.d.ts +68 -0
- package/dist/postgres/role-sql.d.ts.map +1 -0
- package/dist/postgres/role.d.ts +57 -0
- package/dist/postgres/role.d.ts.map +1 -0
- package/dist/postgres/schema-assert.d.ts +50 -0
- package/dist/postgres/schema-assert.d.ts.map +1 -0
- package/dist/postgres/schema-attrs.d.ts +51 -0
- package/dist/postgres/schema-attrs.d.ts.map +1 -0
- package/dist/postgres/schema-delete.d.ts +28 -0
- package/dist/postgres/schema-delete.d.ts.map +1 -0
- package/dist/postgres/schema-diff.d.ts +14 -0
- package/dist/postgres/schema-diff.d.ts.map +1 -0
- package/dist/postgres/schema-errors.d.ts +139 -0
- package/dist/postgres/schema-errors.d.ts.map +1 -0
- package/dist/postgres/schema-identity.d.ts +8 -0
- package/dist/postgres/schema-identity.d.ts.map +1 -0
- package/dist/postgres/schema-reconcile.d.ts +12 -0
- package/dist/postgres/schema-reconcile.d.ts.map +1 -0
- package/dist/postgres/schema-sql.d.ts +45 -0
- package/dist/postgres/schema-sql.d.ts.map +1 -0
- package/dist/postgres/schema-test-kit.d.ts +67 -0
- package/dist/postgres/schema-test-kit.d.ts.map +1 -0
- package/dist/postgres/schema.d.ts +54 -0
- package/dist/postgres/schema.d.ts.map +1 -0
- package/dist/proxmox/index.js +35 -1
- package/dist/proxmox/index.js.map +3 -3
- package/dist/release/fake-engine.d.ts +10 -0
- package/dist/release/fake-engine.d.ts.map +1 -1
- package/dist/release/index.js +35 -1
- package/dist/release/index.js.map +3 -3
- package/dist/valkey/acl-attrs.d.ts +94 -0
- package/dist/valkey/acl-attrs.d.ts.map +1 -0
- package/dist/valkey/acl-compare.d.ts +28 -0
- package/dist/valkey/acl-compare.d.ts.map +1 -0
- package/dist/valkey/acl-connection.d.ts +6 -0
- package/dist/valkey/acl-connection.d.ts.map +1 -0
- package/dist/valkey/acl-form.d.ts +62 -0
- package/dist/valkey/acl-form.d.ts.map +1 -0
- package/dist/valkey/acl-managed.d.ts +4 -0
- package/dist/valkey/acl-managed.d.ts.map +1 -0
- package/dist/valkey/acl-ops.d.ts +37 -0
- package/dist/valkey/acl-ops.d.ts.map +1 -0
- package/dist/valkey/acl-profiles.d.ts +53 -0
- package/dist/valkey/acl-profiles.d.ts.map +1 -0
- package/dist/valkey/acl-reconcile.d.ts +26 -0
- package/dist/valkey/acl-reconcile.d.ts.map +1 -0
- package/dist/valkey/acl.d.ts +24 -0
- package/dist/valkey/acl.d.ts.map +1 -0
- package/dist/valkey/connection-service.d.ts +22 -0
- package/dist/valkey/connection-service.d.ts.map +1 -0
- package/dist/valkey/connection.d.ts +59 -0
- package/dist/valkey/connection.d.ts.map +1 -0
- package/dist/valkey/errors.d.ts +165 -0
- package/dist/valkey/errors.d.ts.map +1 -0
- package/dist/valkey/fake-acl.d.ts +29 -0
- package/dist/valkey/fake-acl.d.ts.map +1 -0
- package/dist/valkey/fake-valkey.d.ts +35 -0
- package/dist/valkey/fake-valkey.d.ts.map +1 -0
- package/dist/valkey/handler-test-fixture.d.ts +20 -0
- package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
- package/dist/valkey/index.d.ts +18 -0
- package/dist/valkey/index.d.ts.map +1 -0
- package/dist/valkey/index.js +964 -0
- package/dist/valkey/index.js.map +27 -0
- package/dist/valkey/instance-attrs.d.ts +46 -0
- package/dist/valkey/instance-attrs.d.ts.map +1 -0
- package/dist/valkey/instance-form.d.ts +22 -0
- package/dist/valkey/instance-form.d.ts.map +1 -0
- package/dist/valkey/instance.d.ts +48 -0
- package/dist/valkey/instance.d.ts.map +1 -0
- package/dist/valkey/provider-socket-fixture.d.ts +8 -0
- package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
- package/dist/valkey/providers.d.ts +16 -0
- package/dist/valkey/providers.d.ts.map +1 -0
- package/dist/valkey/resp.d.ts +38 -0
- package/dist/valkey/resp.d.ts.map +1 -0
- package/dist/valkey/transport-error.d.ts +36 -0
- package/dist/valkey/transport-error.d.ts.map +1 -0
- package/dist/valkey/transport.d.ts +50 -0
- package/dist/valkey/transport.d.ts.map +1 -0
- package/docs/litellm-access-group.md +105 -0
- package/docs/litellm-credential.md +60 -0
- package/docs/litellm-key-metadata.md +56 -0
- package/docs/litellm-key.md +194 -0
- package/docs/litellm-mcp.md +187 -0
- package/docs/litellm-model.md +111 -0
- package/docs/litellm-policy-attachment.md +86 -0
- package/docs/litellm-policy.md +103 -0
- package/docs/litellm-team.md +117 -0
- package/docs/litellm-tool-policy.md +90 -0
- package/docs/litellm-toolset.md +92 -0
- package/docs/litellm.md +16 -9
- package/docs/peers.md +15 -6
- package/docs/postgres-grants-example.md +55 -0
- package/docs/postgres-grants.md +157 -0
- package/docs/postgres-role.md +126 -0
- package/docs/postgres-schema.md +139 -0
- package/docs/postgres.md +20 -3
- package/docs/valkey-transport.md +29 -0
- package/docs/valkey.md +187 -0
- package/package.json +6 -2
- package/src/ceph/ceph-argv.test.ts +1 -1
- package/src/launchd/sudo-allowlist.test.ts +2 -2
- package/src/linux/sudo-allowlist.test.ts +3 -3
- package/src/linux/sudo-runner.test.ts +2 -2
- package/src/litellm/access-group-form.ts +105 -0
- package/src/litellm/access-group-operations.ts +92 -0
- package/src/litellm/access-group-types.ts +49 -0
- package/src/litellm/access-group.test.ts +197 -0
- package/src/litellm/access-group.ts +151 -0
- package/src/litellm/credential-errors.ts +183 -0
- package/src/litellm/credential-form.test.ts +151 -0
- package/src/litellm/credential-form.ts +211 -0
- package/src/litellm/credential-guards.test.ts +241 -0
- package/src/litellm/credential-operations.ts +208 -0
- package/src/litellm/credential-recovery.test.ts +93 -0
- package/src/litellm/credential-rewrite.test.ts +164 -0
- package/src/litellm/credential-rewrite.ts +50 -0
- package/src/litellm/credential-types.ts +117 -0
- package/src/litellm/credential-values.ts +94 -0
- package/src/litellm/credential.test.ts +218 -0
- package/src/litellm/credential.ts +227 -0
- package/src/litellm/fake-access-group-litellm.ts +127 -0
- package/src/litellm/fake-credential-litellm.ts +216 -0
- package/src/litellm/fake-keys.ts +190 -0
- package/src/litellm/fake-litellm.ts +19 -9
- package/src/litellm/fake-mcp-litellm.ts +214 -0
- package/src/litellm/fake-model-litellm-post-update.ts +103 -0
- package/src/litellm/fake-model-litellm.ts +233 -0
- package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
- package/src/litellm/fake-policy-litellm.ts +186 -0
- package/src/litellm/fake-registry-base.ts +69 -0
- package/src/litellm/fake-stack.ts +17 -1
- package/src/litellm/fake-team-litellm.ts +229 -0
- package/src/litellm/fake-tool-litellm.ts +88 -0
- package/src/litellm/fake-toolset-litellm.ts +110 -0
- package/src/litellm/index.ts +140 -0
- package/src/litellm/key-delete.test.ts +212 -0
- package/src/litellm/key-errors.ts +241 -0
- package/src/litellm/key-form.ts +200 -0
- package/src/litellm/key-harness.ts +121 -0
- package/src/litellm/key-metadata-errors.ts +49 -0
- package/src/litellm/key-metadata.test.ts +156 -0
- package/src/litellm/key-metadata.ts +74 -0
- package/src/litellm/key-operations.ts +184 -0
- package/src/litellm/key-rotation.test.ts +110 -0
- package/src/litellm/key-scope.test.ts +135 -0
- package/src/litellm/key-secret.test.ts +248 -0
- package/src/litellm/key-secret.ts +137 -0
- package/src/litellm/key-transport.test.ts +166 -0
- package/src/litellm/key.test.ts +231 -0
- package/src/litellm/key.ts +213 -0
- package/src/litellm/mcp-server-adopt.test.ts +86 -0
- package/src/litellm/mcp-server-alias.test.ts +184 -0
- package/src/litellm/mcp-server-credential.ts +85 -0
- package/src/litellm/mcp-server-errors.ts +141 -0
- package/src/litellm/mcp-server-exports.test.ts +15 -0
- package/src/litellm/mcp-server-form.test.ts +241 -0
- package/src/litellm/mcp-server-form.ts +233 -0
- package/src/litellm/mcp-server-handlers.test.ts +150 -0
- package/src/litellm/mcp-server-locate.ts +66 -0
- package/src/litellm/mcp-server-operations.ts +111 -0
- package/src/litellm/mcp-server-registry.test.ts +146 -0
- package/src/litellm/mcp-server-retype.test.ts +114 -0
- package/src/litellm/mcp-server-shadow.test.ts +91 -0
- package/src/litellm/mcp-server-shadow.ts +45 -0
- package/src/litellm/mcp-server-tools.test.ts +85 -0
- package/src/litellm/mcp-server-types.ts +143 -0
- package/src/litellm/mcp-server-url.ts +66 -0
- package/src/litellm/mcp-server.test.ts +213 -0
- package/src/litellm/mcp-server.ts +225 -0
- package/src/litellm/model-claim.ts +69 -0
- package/src/litellm/model-credential.ts +45 -0
- package/src/litellm/model-errors.ts +168 -0
- package/src/litellm/model-foreign-row.test.ts +173 -0
- package/src/litellm/model-form.test.ts +228 -0
- package/src/litellm/model-form.ts +223 -0
- package/src/litellm/model-locate.test.ts +128 -0
- package/src/litellm/model-locate.ts +67 -0
- package/src/litellm/model-operations.test.ts +93 -0
- package/src/litellm/model-operations.ts +146 -0
- package/src/litellm/model-params-preserve.test.ts +119 -0
- package/src/litellm/model-recovery.test.ts +57 -0
- package/src/litellm/model-types.ts +112 -0
- package/src/litellm/model.test.ts +222 -0
- package/src/litellm/model.ts +237 -0
- package/src/litellm/policy-attachment-form.ts +103 -0
- package/src/litellm/policy-attachment-operations.ts +74 -0
- package/src/litellm/policy-attachment-types.ts +48 -0
- package/src/litellm/policy-attachment.test.ts +198 -0
- package/src/litellm/policy-attachment.ts +178 -0
- package/src/litellm/policy-form.ts +122 -0
- package/src/litellm/policy-operations.ts +112 -0
- package/src/litellm/policy-types.ts +58 -0
- package/src/litellm/policy.test.ts +214 -0
- package/src/litellm/policy.ts +146 -0
- package/src/litellm/providers.ts +24 -3
- package/src/litellm/registry-errors.ts +88 -0
- package/src/litellm/registry-exports.test.ts +66 -0
- package/src/litellm/registry-support.ts +109 -0
- package/src/litellm/team-form.ts +140 -0
- package/src/litellm/team-members.test.ts +150 -0
- package/src/litellm/team-members.ts +58 -0
- package/src/litellm/team-operations.ts +90 -0
- package/src/litellm/team-permission.ts +159 -0
- package/src/litellm/team-read.test.ts +89 -0
- package/src/litellm/team-types.ts +106 -0
- package/src/litellm/team.test.ts +206 -0
- package/src/litellm/team.ts +159 -0
- package/src/litellm/tool-policy-form.ts +94 -0
- package/src/litellm/tool-policy-operations.ts +58 -0
- package/src/litellm/tool-policy-types.ts +48 -0
- package/src/litellm/tool-policy.test.ts +172 -0
- package/src/litellm/tool-policy.ts +125 -0
- package/src/litellm/toolset-form.ts +104 -0
- package/src/litellm/toolset-operations.ts +90 -0
- package/src/litellm/toolset-types.ts +47 -0
- package/src/litellm/toolset.test.ts +198 -0
- package/src/litellm/toolset.ts +151 -0
- package/src/ownership/ownership.test.ts +33 -1
- package/src/ownership/rows.ts +53 -0
- package/src/postgres/connection.ts +42 -7
- package/src/postgres/database-bigint-serialization.test.ts +1 -0
- package/src/postgres/database-refusals.test.ts +2 -2
- package/src/postgres/database-sql.ts +36 -9
- package/src/postgres/fake-grants-parse.ts +189 -0
- package/src/postgres/fake-grants-read.ts +250 -0
- package/src/postgres/fake-grants-sql.ts +207 -0
- package/src/postgres/fake-grants-write.ts +90 -0
- package/src/postgres/fake-role-sql.ts +240 -0
- package/src/postgres/fake-schema-semantics.test.ts +36 -0
- package/src/postgres/fake-schema-sql.ts +146 -0
- package/src/postgres/fake-sql-parse.ts +86 -0
- package/src/postgres/fake-sql-quote.ts +36 -0
- package/src/postgres/fake-sql.ts +166 -46
- package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
- package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
- package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
- package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
- package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
- package/src/postgres/grants-acl.test.ts +61 -0
- package/src/postgres/grants-attrs.ts +185 -0
- package/src/postgres/grants-database.test.ts +158 -0
- package/src/postgres/grants-declare.ts +172 -0
- package/src/postgres/grants-delete-proof.test.ts +154 -0
- package/src/postgres/grants-diff.ts +101 -0
- package/src/postgres/grants-errors.ts +215 -0
- package/src/postgres/grants-existence-guards.test.ts +113 -0
- package/src/postgres/grants-existence.ts +101 -0
- package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
- package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
- package/src/postgres/grants-lifecycle.test.ts +181 -0
- package/src/postgres/grants-ops.ts +229 -0
- package/src/postgres/grants-owner-revoke.test.ts +121 -0
- package/src/postgres/grants-ownership.ts +49 -0
- package/src/postgres/grants-plan-collateral.test.ts +202 -0
- package/src/postgres/grants-plan-diff.test.ts +204 -0
- package/src/postgres/grants-plan-restore.ts +43 -0
- package/src/postgres/grants-plan-revocations.test.ts +69 -0
- package/src/postgres/grants-plan.test.ts +148 -0
- package/src/postgres/grants-plan.ts +245 -0
- package/src/postgres/grants-provenance.test.ts +134 -0
- package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
- package/src/postgres/grants-read-absent.test.ts +91 -0
- package/src/postgres/grants-read-defaults.ts +51 -0
- package/src/postgres/grants-read.ts +250 -0
- package/src/postgres/grants-refusals.test.ts +136 -0
- package/src/postgres/grants-refuse.ts +119 -0
- package/src/postgres/grants-relkind-wire.test.ts +111 -0
- package/src/postgres/grants-repair-plan.test.ts +201 -0
- package/src/postgres/grants-sql.test.ts +128 -0
- package/src/postgres/grants-sql.ts +144 -0
- package/src/postgres/grants-words.ts +34 -0
- package/src/postgres/grants.ts +110 -0
- package/src/postgres/index.ts +129 -5
- package/src/postgres/installed-classifier.ts +36 -0
- package/src/postgres/providers.ts +33 -9
- package/src/postgres/psql-executor-escaping.test.ts +67 -0
- package/src/postgres/psql-executor.test.ts +63 -2
- package/src/postgres/psql-executor.ts +74 -34
- package/src/postgres/role-adopt.test.ts +98 -0
- package/src/postgres/role-attrs.ts +137 -0
- package/src/postgres/role-diff.ts +93 -0
- package/src/postgres/role-errors.ts +206 -0
- package/src/postgres/role-guard.test.ts +60 -0
- package/src/postgres/role-identity.test.ts +197 -0
- package/src/postgres/role-lifecycle.test.ts +228 -0
- package/src/postgres/role-membership-options.test.ts +73 -0
- package/src/postgres/role-membership-repair.test.ts +153 -0
- package/src/postgres/role-membership-sql.ts +223 -0
- package/src/postgres/role-password-ascii.test.ts +53 -0
- package/src/postgres/role-password-transports.test.ts +123 -0
- package/src/postgres/role-password.test.ts +158 -0
- package/src/postgres/role-privilege.test.ts +107 -0
- package/src/postgres/role-provenance.test.ts +200 -0
- package/src/postgres/role-provider.ts +105 -0
- package/src/postgres/role-refusals.test.ts +225 -0
- package/src/postgres/role-scram.test.ts +38 -0
- package/src/postgres/role-scram.ts +40 -0
- package/src/postgres/role-secrets.ts +61 -0
- package/src/postgres/role-sql.ts +213 -0
- package/src/postgres/role-valid-until.test.ts +68 -0
- package/src/postgres/role.ts +226 -0
- package/src/postgres/schema-assert.ts +139 -0
- package/src/postgres/schema-attrs.ts +71 -0
- package/src/postgres/schema-delete-proofs.test.ts +192 -0
- package/src/postgres/schema-delete.ts +96 -0
- package/src/postgres/schema-diff.ts +44 -0
- package/src/postgres/schema-drop-2bp01.test.ts +146 -0
- package/src/postgres/schema-errors.ts +209 -0
- package/src/postgres/schema-handlers.test.ts +193 -0
- package/src/postgres/schema-identity.ts +39 -0
- package/src/postgres/schema-lifecycle.test.ts +246 -0
- package/src/postgres/schema-race.test.ts +70 -0
- package/src/postgres/schema-reconcile.ts +100 -0
- package/src/postgres/schema-refusals.test.ts +144 -0
- package/src/postgres/schema-socket.test.ts +162 -0
- package/src/postgres/schema-sql.ts +130 -0
- package/src/postgres/schema-takeover.test.ts +149 -0
- package/src/postgres/schema-test-kit.ts +85 -0
- package/src/postgres/schema.ts +130 -0
- package/src/release/binary-alias.test.ts +3 -3
- package/src/release/binary-claims.test.ts +3 -3
- package/src/release/binary-directory-umask.test.ts +65 -0
- package/src/release/fake-engine.ts +15 -0
- package/src/valkey/acl-attrs.ts +98 -0
- package/src/valkey/acl-compare.ts +57 -0
- package/src/valkey/acl-connection.ts +13 -0
- package/src/valkey/acl-delete.test.ts +127 -0
- package/src/valkey/acl-form.ts +239 -0
- package/src/valkey/acl-managed.ts +13 -0
- package/src/valkey/acl-ops.ts +122 -0
- package/src/valkey/acl-ownership.test.ts +118 -0
- package/src/valkey/acl-prefix.test.ts +38 -0
- package/src/valkey/acl-profiles.ts +81 -0
- package/src/valkey/acl-reconcile.ts +180 -0
- package/src/valkey/acl-reserved.test.ts +69 -0
- package/src/valkey/acl-revocation.test.ts +94 -0
- package/src/valkey/acl-seat-prefix.test.ts +111 -0
- package/src/valkey/acl-validation.test.ts +54 -0
- package/src/valkey/acl.test.ts +215 -0
- package/src/valkey/acl.ts +169 -0
- package/src/valkey/connection-service.ts +46 -0
- package/src/valkey/connection.ts +180 -0
- package/src/valkey/errors.ts +239 -0
- package/src/valkey/fake-acl.ts +128 -0
- package/src/valkey/fake-valkey.ts +224 -0
- package/src/valkey/handler-test-fixture.ts +43 -0
- package/src/valkey/handlers.test.ts +205 -0
- package/src/valkey/index.ts +54 -0
- package/src/valkey/instance-attrs.ts +48 -0
- package/src/valkey/instance-form.ts +104 -0
- package/src/valkey/instance.test.ts +108 -0
- package/src/valkey/instance.ts +160 -0
- package/src/valkey/provider-socket-fixture.ts +71 -0
- package/src/valkey/providers.test.ts +129 -0
- package/src/valkey/providers.ts +21 -0
- package/src/valkey/resp.ts +212 -0
- package/src/valkey/transport-bounds.test.ts +150 -0
- package/src/valkey/transport-error.ts +37 -0
- package/src/valkey/transport.test.ts +158 -0
- package/src/valkey/transport.ts +109 -0
- package/src/valkey/upstream-bounds.test.ts +14 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/litellm/pass-through-endpoint.ts", "../src/litellm/operations.ts", "../src/litellm/pass-through-errors.ts", "../src/litellm/pass-through-form.ts", "../src/litellm/budget.ts", "../src/litellm/budget-errors.ts", "../src/litellm/budget-form.ts", "../src/litellm/budget-operations.ts", "../src/litellm/providers.ts"],
|
|
3
|
+
"sources": ["../src/litellm/pass-through-endpoint.ts", "../src/litellm/operations.ts", "../src/litellm/pass-through-errors.ts", "../src/litellm/pass-through-form.ts", "../src/litellm/budget.ts", "../src/litellm/budget-errors.ts", "../src/litellm/budget-form.ts", "../src/litellm/budget-operations.ts", "../src/litellm/credential.ts", "../src/ownership/adopt.ts", "../src/ownership/resume.ts", "../src/ownership/rows.ts", "../src/ownership/whole.ts", "../src/litellm/credential-errors.ts", "../src/litellm/credential-form.ts", "../src/litellm/credential-types.ts", "../src/litellm/credential-operations.ts", "../src/litellm/credential-rewrite.ts", "../src/litellm/credential-values.ts", "../src/secrets/write-only.ts", "../src/litellm/mcp-server.ts", "../src/litellm/mcp-server-errors.ts", "../src/litellm/mcp-server-credential.ts", "../src/litellm/mcp-server-types.ts", "../src/litellm/mcp-server-form.ts", "../src/litellm/mcp-server-url.ts", "../src/litellm/mcp-server-locate.ts", "../src/litellm/mcp-server-operations.ts", "../src/litellm/mcp-server-shadow.ts", "../src/litellm/access-group.ts", "../src/litellm/registry-support.ts", "../src/litellm/registry-errors.ts", "../src/litellm/access-group-form.ts", "../src/litellm/access-group-operations.ts", "../src/litellm/policy-attachment.ts", "../src/litellm/policy-attachment-form.ts", "../src/litellm/policy-attachment-operations.ts", "../src/litellm/policy.ts", "../src/litellm/policy-form.ts", "../src/litellm/policy-operations.ts", "../src/litellm/team.ts", "../src/litellm/team-members.ts", "../src/litellm/team-permission.ts", "../src/litellm/team-form.ts", "../src/litellm/team-operations.ts", "../src/litellm/tool-policy.ts", "../src/litellm/tool-policy-types.ts", "../src/litellm/tool-policy-form.ts", "../src/litellm/tool-policy-operations.ts", "../src/litellm/toolset.ts", "../src/litellm/toolset-form.ts", "../src/litellm/toolset-operations.ts", "../src/litellm/model.ts", "../src/litellm/model-errors.ts", "../src/litellm/model-claim.ts", "../src/ownership/adopting.ts", "../src/ownership/probe.ts", "../src/litellm/model-types.ts", "../src/litellm/model-credential.ts", "../src/litellm/model-form.ts", "../src/litellm/model-locate.ts", "../src/litellm/model-operations.ts", "../src/litellm/key.ts", "../src/litellm/key-errors.ts", "../src/litellm/key-form.ts", "../src/litellm/key-metadata.ts", "../src/litellm/key-metadata-errors.ts", "../src/litellm/key-operations.ts", "../src/litellm/key-secret.ts", "../src/litellm/providers.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * `LiteLLM.PassThroughEndpoint` — one row in LiteLLM's `general_settings.pass_through_endpoints`.\n *\n * ★ ADOPT-FRIENDLY BY id, NOT BY PATH. `output?.id` is used as the object's live id once the\n * engine has one — from our own prior create (the deterministic physical name) or from an\n * adopted foreign row — and `read` computes a FRESH physical name only for a resource the\n * engine has never resolved before. This is S7/S9's \"output is a cache of stable identifiers,\n * not proof the object exists\" and S13's \"an adopted object keeps its live name as an explicit\n * prop\", applied to an id LiteLLM lets a client choose (`_types.py:2192` at v1.100.0: create\n * takes a caller-supplied `id`, else `uuid4()`).\n *\n * ⛔ A PATH HELD BY AN `is_from_config` ROW IS REFUSED, NOT ADOPTED. A DB row on the same path\n * overrides it at runtime (`pass_through_endpoints.py` merges DB after config, at the tag) — so\n * creating one would silently take over a route the operator declared in `config.yaml`, the\n * exact \"DB row silently overrides the file\" the task names. `is_from_config` rows are also\n * never updated or deleted by this provider, for the same reason.\n * ⛔ A DB ROW WITH NO `id` (path-only, `_types.py`'s \"backwards compatibility\" case) CANNOT BE\n * ADDRESSED by the id-keyed update/delete calls this provider uses, so it is refused rather than\n * silently adopted into an id-based lifecycle it cannot actually manage — see docs/litellm.md.\n * ⛔ NO CREDENTIAL IS A PROP (S25). `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` are read at call\n * time by `@distilled.cloud/litellm`'s own `CredentialsFromEnv` (providers.ts) — the same two\n * variable names the retired hand-rolled `credentials.ts` read.\n * ⛔ CREDENTIALS ARE NOT A RESOURCE REQUIREMENT. `LitellmOpContext` (`Credentials | HttpClient`)\n * belongs to the handlers at Apply, on `litellmProviders`'s layer. The 5th `Resource` parameter\n * used to be `LitellmRequirements | Stack | Stage`, so a stack body required `Credentials`, and\n * `Alchemy.Stack` rejects that (`\"LitellmCredentials\"` is not `\"Stack\"`). Measured 2026-09-24.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Provider from 'alchemy/Provider';\nimport type * as misc from '@distilled.cloud/litellm/misc';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n createPassThroughEndpoint,\n deletePassThroughEndpoint,\n listPassThroughEndpoints,\n updatePassThroughEndpoint,\n} from './operations.ts';\nimport {\n LitellmConfigPathConflictError,\n LitellmLiteralSecretHeaderError,\n LitellmUnaddressableRowError,\n} from './pass-through-errors.ts';\nimport {\n type PassThroughEndpointAttributes,\n type PassThroughEndpointProps,\n createBody,\n literalSecretHeaders,\n matches,\n needsReplace,\n updateBody,\n} from './pass-through-form.ts';\n\nexport type { PassThroughEndpointAttributes, PassThroughEndpointProps };\nexport {\n LitellmConfigPathConflictError,\n LitellmLiteralSecretHeaderError,\n LitellmUnaddressableRowError,\n type PassThroughEndpointError,\n} from './pass-through-errors.ts';\n\nexport interface LiteLLMPassThroughEndpoint extends Resource<\n 'LiteLLM.PassThroughEndpoint',\n PassThroughEndpointProps,\n PassThroughEndpointAttributes\n> {}\n\nexport const LiteLLMPassThroughEndpoint = Resource<LiteLLMPassThroughEndpoint>(\n 'LiteLLM.PassThroughEndpoint',\n);\n\nexport const isLiteLLMPassThroughEndpoint = (value: unknown): value is LiteLLMPassThroughEndpoint =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.PassThroughEndpoint';\n\nconst toAttributes = (\n row: misc.PassThroughGenericEndpoint,\n fallbackId: string,\n): PassThroughEndpointAttributes => ({\n ...row,\n id: row.id ?? fallbackId,\n});\n\nconst physicalIdOf = (logicalId: string, instanceId: string) =>\n createPhysicalName({ id: logicalId, instanceId, lowercase: true, maxLength: 64 });\n\n/**\n * Find this resource's live row (by id) and, when absent, the row that already holds its `path`\n * (a config-file row to refuse, or a DB row LiteLLM did not give an id — also refused, see the\n * file header — or a genuine foreign DB row to hand back `Unowned`).\n */\nconst locate = (\n rows: readonly misc.PassThroughGenericEndpoint[],\n objectId: string,\n path: string,\n): {\n readonly mine: misc.PassThroughGenericEndpoint | undefined;\n readonly conflict: misc.PassThroughGenericEndpoint | undefined;\n} => {\n const mine = rows.find((row) => row.id === objectId);\n const conflict = mine === undefined ? rows.find((row) => row.path === path) : undefined;\n return { conflict, mine };\n};\n\n/** The typed refusal for a `path` conflict that cannot ever be adopted (config row, or no id). */\nconst conflictRefusal = (conflict: misc.PassThroughGenericEndpoint, path: string) =>\n conflict.is_from_config === true\n ? new LitellmConfigPathConflictError({ path })\n : new LitellmUnaddressableRowError({ path });\n\n/** `read`'s answer for a path conflict: `Unowned` when it is a real, addressable DB row — a typed refusal otherwise. */\nconst readConflict = (conflict: misc.PassThroughGenericEndpoint, path: string) =>\n conflict.is_from_config !== true && conflict.id !== undefined && conflict.id !== null\n ? Effect.succeed(Unowned(toAttributes(conflict, conflict.id)))\n : Effect.fail(conflictRefusal(conflict, path));\n\nconst refuseLiteralSecrets = (headers: Readonly<Record<string, unknown>> | undefined) => {\n const bad = literalSecretHeaders(headers);\n return bad.length === 0\n ? Effect.void\n : Effect.fail(new LitellmLiteralSecretHeaderError({ headers: bad }));\n};\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel — a lifecycle test calls these directly against the fake. */\nexport const passThroughHandlers = {\n read: ({\n id,\n instanceId,\n olds,\n output,\n }: {\n id: string;\n instanceId: string;\n olds: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n const objectId = output?.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const { conflict, mine } = locate(rows, objectId, olds.path);\n if (mine !== undefined) return toAttributes(mine, objectId);\n if (conflict === undefined) return undefined;\n return yield* readConflict(conflict, olds.path);\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<PassThroughEndpointProps>;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuseLiteralSecrets(news.headers);\n if (output === undefined) return undefined;\n // ⛔ deleteFirst: TRUE — a create-first replace mints a new instanceId/id before the old row\n // on `path` is gone, so the new generation's `reconcile` would refuse it as a foreign\n // conflict (Apply.ts). Same fix this kit's other unique-identity providers use.\n if (needsReplace(output, news)) return { action: 'replace', deleteFirst: true } as const;\n return matches(output, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({\n id,\n instanceId,\n news,\n output,\n }: {\n id: string;\n instanceId: string;\n news: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n yield* refuseLiteralSecrets(news.headers);\n const objectId = output?.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const { conflict, mine } = locate(rows, objectId, news.path);\n\n if (mine === undefined) {\n // ⚠️ A CONFLICT HERE — reconcile, not read — means ownership shifted between plan and\n // apply (a config row or a foreign DB row landed on this path after `read` cleared it).\n // `read` gates a genuine adopt by returning `Unowned`; reconcile itself never adopts,\n // so ANY conflict here fails rather than risking a second row on one path.\n if (conflict !== undefined) return yield* Effect.fail(conflictRefusal(conflict, news.path));\n yield* createPassThroughEndpoint(createBody(news, objectId));\n } else if (mine.is_from_config === true) {\n // ⛔ NEVER UPDATE OR DELETE AN `is_from_config` ROW, even one this resource's own\n // deterministic id happens to match (a config.yaml author could in principle choose the\n // same id `createPhysicalName` would). Refuse rather than silently editing the file's row.\n return yield* Effect.fail(new LitellmConfigPathConflictError({ path: news.path }));\n } else if (needsReplace(toAttributes(mine, objectId), news)) {\n yield* deletePassThroughEndpoint(objectId);\n yield* createPassThroughEndpoint(createBody(news, objectId));\n } else if (!matches(toAttributes(mine, objectId), news)) {\n const body = updateBody(news);\n if (Object.keys(body).length > 0) yield* updatePassThroughEndpoint(objectId, body);\n }\n\n const after = (yield* listPassThroughEndpoints()).find((row) => row.id === objectId);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `LiteLLM.PassThroughEndpoint ${news.path}: the write returned no error but id ` +\n `${objectId} is still absent from GET /config/pass_through_endpoint — read back ` +\n 'rather than trusting the call that just returned.',\n ),\n );\n }\n return toAttributes(after, objectId);\n }),\n\n delete: ({\n id,\n instanceId,\n olds,\n output,\n }: {\n id: string;\n instanceId: string;\n olds: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes;\n }) =>\n Effect.gen(function* () {\n const objectId = output.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const live = rows.find((row) => row.id === objectId);\n // ⛔ NEVER DELETE AN `is_from_config` ROW, and a DB row this resource never created (a path\n // match with a different id) is not this resource's to remove either.\n if (live === undefined || live.is_from_config === true || live.path !== olds.path) return;\n yield* deletePassThroughEndpoint(objectId);\n }),\n\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMPassThroughEndpointProvider = () =>\n Provider.effect(\n LiteLLMPassThroughEndpoint,\n Effect.succeed(LiteLLMPassThroughEndpoint.Provider.of(passThroughHandlers)),\n );\n",
|
|
6
6
|
"/**\n * The four LiteLLM `/config/pass_through_endpoint*` calls this package writes to and reads from —\n * now through `@distilled.cloud/litellm`'s typed operations (S23), not a hand-rolled `HttpClient`\n * client. `client.ts` is retired; nothing else in this package imported it.\n *\n * ⛔ STILL WRAPPED IN A PER-BASE-URL SEMAPHORE, UNCHANGED FROM `client.ts`. LiteLLM stores every\n * pass-through endpoint in ONE `general_settings.pass_through_endpoints` list\n * (`pass_through_endpoints.py` at tag v1.100.0, mirrored in the SDK's own `misc.ts` docstrings),\n * so create/update/delete are each a read-modify-write of the WHOLE list through\n * `update_config_general_settings`. Two of this package's own calls racing each other — two\n * endpoints created in the same deploy — would each read the list, add their own row and write\n * it back, and the second write would silently drop the first row. The SDK has no opinion on\n * this: it is a quirk of how THIS vendor stores the resource, not a distilled protocol concern,\n * so it stays here, one layer above the typed calls. One permit per proxy serialises this\n * package's own writes; it cannot serialise against the LiteLLM UI or a second deploy process,\n * which is why `pass-through-endpoint.ts`'s `reconcile` reads back after writing rather than\n * trusting the call that just returned (docs/litellm.md).\n * ⛔ NO STATUS SNIFFING (S21). Every failure the SDK's own typed operations declare — `BadRequest`\n * (400), `NotFound` (404 on update only), `UnprocessableEntity` (422) plus the shared\n * `Unauthorized`/`TooManyRequests`/`InternalServerError`/`BadGateway`/`ServiceUnavailable`/\n * `GatewayTimeout` every operation carries — is a distilled patch's doing\n * (`@distilled.cloud/litellm`'s own `patches/misc/*.json`, applied at generation time), never a\n * status check here or in `pass-through-endpoint.ts`. Deleting an id LiteLLM does not have\n * answers `BadRequest` (measured against the tag's `pass_through_endpoint_delete` — the id is\n * looked up in the list and a miss raises `HTTPException(400)`, not 404) — but so does a\n * genuinely-live row when the caller isn't PROXY_ADMIN, or the DB is disconnected. Telling those\n * apart by re-listing, not by the `BadRequest`'s message text, is `pass-through-endpoint.ts`'s\n * `deletePassThroughEndpoint`'s own job (S21 — a status-and-a-real-read decision, never body\n * text) — this file only forwards the typed error.\n */\nimport * as misc from '@distilled.cloud/litellm/misc';\nimport { Credentials } from '@distilled.cloud/litellm/Credentials';\nimport type { LitellmOpContext } from '@distilled.cloud/litellm/Protocol';\nimport type { ConfigError } from '@distilled.cloud/litellm/Errors';\nimport * as Effect from 'effect/Effect';\nimport * as Semaphore from 'effect/Semaphore';\nimport * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';\n\nexport type { LitellmOpContext } from '@distilled.cloud/litellm/Protocol';\nexport type LitellmRequirements = LitellmOpContext;\nexport type {\n PassThroughEndpointResponse,\n PassThroughGenericEndpoint,\n PassThroughGuardrailSettings,\n} from '@distilled.cloud/litellm/misc';\n\nconst semaphores = new Map<string, Semaphore.Semaphore>();\n\n/** One semaphore per base URL, created on first use. Never `Hash.structure` (S18) — a plain Map key. */\nconst semaphoreFor = (baseUrl: string): Effect.Effect<Semaphore.Semaphore> =>\n Effect.sync(() => {\n const existing = semaphores.get(baseUrl);\n if (existing !== undefined) return existing;\n const created = Semaphore.makeUnsafe(1);\n semaphores.set(baseUrl, created);\n return created;\n });\n\n/**\n * Serialises a write against this call's own base URL — see the file header.\n *\n * ⚠️ `Credentials` itself ends in `Effect.orDie` on a missing/misspelled env var (Q6, decision\n * 49 \"upstream wins\", 2026-09-24, matching distilled's own convention — 73 of 80 packages'\n * `credentials.ts` do the same at `homeflare/base`), so resolving it here can no longer fail\n * typed. The `| ConfigError` this function still adds to its return type stays only because\n * `@distilled.cloud/litellm`'s own `LitellmOpError` (protocol.ts) unconditionally declares\n * `ConfigError` as a member of every generated operation's error union regardless of\n * `Credentials`'s own type — so `E` (the wrapped operation's error) already contains it, and\n * this widening is a genuine no-op for callers, not dead type-state left over from the revert.\n */\n/**\n * ⛔ THE STACK'S AMBIENT `HttpClient` MAY BE CADDY'S. `caddyProviders()` provideMerges a client\n * that dials the admin listener and ignores the request host (`caddy-http-client.ts`). Measured\n * 2026-09-25: that client answers `GET /config/pass_through_endpoint` with HTTP 200 `null`\n * (Caddy's empty-config body). The decoded value then has no `endpoints`, and `locate` throws\n * `rows.find` of undefined. FetchHttpClient uses the URL the LiteLLM protocol built. An outer\n * `Fetch` service still wins inside that client, which is how the fake proxy stays the test\n * transport.\n */\nexport const throughFetch = <A, E, R>(io: Effect.Effect<A, E, R>) =>\n io.pipe(Effect.provide(FetchHttpClient.layer));\n\nconst mutate = <A, E>(\n io: Effect.Effect<A, E, LitellmOpContext>,\n): Effect.Effect<A, E | ConfigError, LitellmOpContext> =>\n Effect.gen(function* () {\n const resolve = yield* Credentials;\n const creds = yield* resolve;\n const semaphore = yield* semaphoreFor(creds.apiBaseUrl);\n return yield* semaphore.withPermits(1)(throughFetch(io));\n });\n\nconst endpointsOf = (\n response: misc.PassThroughEndpointResponse,\n): Effect.Effect<readonly misc.PassThroughGenericEndpoint[]> => {\n const rows = response.endpoints;\n if (!Array.isArray(rows)) {\n return Effect.die(\n new Error(\n 'GET /config/pass_through_endpoint decoded no endpoints array. The call reached something other than the LiteLLM proxy.',\n ),\n );\n }\n return Effect.succeed(rows);\n};\n\n/** GET /config/pass_through_endpoint — every row, config-file and DB alike. Not under the semaphore: reads don't race the field. */\nexport const listPassThroughEndpoints = (): Effect.Effect<\n readonly misc.PassThroughGenericEndpoint[],\n misc.GetPassThroughEndpointsConfigPassThroughEndpointGetError,\n LitellmOpContext\n> =>\n throughFetch(misc.getPassThroughEndpointsConfigPassThroughEndpointGet({})).pipe(\n Effect.flatMap(endpointsOf),\n );\n\n/** POST /config/pass_through_endpoint. `body` carries `id` — see pass-through-form.ts. */\nexport const createPassThroughEndpoint = (\n body: misc.PassThroughGenericEndpoint,\n): Effect.Effect<\n void,\n misc.CreatePassThroughEndpointsConfigPassThroughEndpointPostError,\n LitellmOpContext\n> => mutate(misc.createPassThroughEndpointsConfigPassThroughEndpointPost(body).pipe(Effect.asVoid));\n\n/**\n * POST /config/pass_through_endpoint/{endpoint_id}. Merges with `exclude_none` — see\n * pass-through-form.ts. `body` is `pass-through-form.ts`'s own `UpdateBody`, not a blanket\n * `Partial<>` — `path`/`target` are the two fields the SDK's request type still requires\n * non-optionally on an update.\n */\nexport const updatePassThroughEndpoint = (\n endpointId: string,\n body: Partial<Omit<misc.PassThroughGenericEndpoint, 'path' | 'target'>> &\n Pick<misc.PassThroughGenericEndpoint, 'path' | 'target'>,\n): Effect.Effect<\n void,\n misc.UpdatePassThroughEndpointsConfigPassThroughEndpointEndpointIdPostError,\n LitellmOpContext\n> =>\n mutate(\n misc\n .updatePassThroughEndpointsConfigPassThroughEndpointEndpointIdPost({\n ...body,\n endpoint_id: endpointId,\n })\n .pipe(Effect.asVoid),\n );\n\n/**\n * DELETE /config/pass_through_endpoint?endpoint_id=… — idempotent BY THIS FUNCTION, not by the\n * vendor. ⚠️ Measured at v1.100.0: deleting an id the list does not have raises `HTTPException(400)`\n * from `pass_through_endpoint_delete`, which the SDK decodes as a typed `BadRequest` — but the SAME\n * route also raises the SAME `BadRequest` for `not_allowed_access` (a non-PROXY_ADMIN caller) or a\n * disconnected DB, on a row that never got deleted. A `BadRequest` alone cannot tell the two apart,\n * so THIS FUNCTION DOESN'T GUESS FROM THE TAG ALONE: on `BadRequest` it re-lists (a GET — no\n * PROXY_ADMIN required) and only treats the delete as already-done if `endpointId` is genuinely\n * absent; otherwise the delete failed and this re-fails with the ORIGINAL error, not a message\n * match (S21 — a status-and-a-real-read decision, never body text).\n */\nexport const deletePassThroughEndpoint = (\n endpointId: string,\n): Effect.Effect<\n void,\n misc.DeletePassThroughEndpointsConfigPassThroughEndpointDeleteError,\n LitellmOpContext\n> =>\n mutate(\n misc\n .deletePassThroughEndpointsConfigPassThroughEndpointDelete({ endpoint_id: endpointId })\n .pipe(\n Effect.asVoid,\n Effect.catchTag('BadRequest', (original) =>\n Effect.gen(function* () {\n const rows = yield* listPassThroughEndpoints();\n if (rows.some((row) => row.id === endpointId)) return yield* Effect.fail(original);\n }),\n ),\n ),\n );\n",
|
|
@@ -10,9 +10,70 @@
|
|
|
10
10
|
"/**\n * The typed refusals `LiteLLM.Budget` raises on top of `@distilled.cloud/litellm`'s own SDK errors.\n */\nimport type * as budgets from '@distilled.cloud/litellm/budget_management';\nimport * as Data from 'effect/Data';\n\n/** A `maxBudget` (a HARD cap: LiteLLM answers 429 once it is spent) declared with no reason. */\nexport class LitellmBudgetHardCapUnjustifiedError extends Data.TaggedError(\n 'LitellmBudgetHardCapUnjustifiedError',\n)<{\n readonly budgetId: string | undefined;\n}> {}\n\n/**\n * The write returned no error but the row still carries a limit the declaration does not.\n * `/budget/update` may merge with `exclude_none` (the way `/config/pass_through_endpoint` does),\n * in which case a limit can never be cleared by an update. Refused, never claimed as done.\n */\nexport class LitellmBudgetFieldNotClearedError extends Data.TaggedError(\n 'LitellmBudgetFieldNotClearedError',\n)<{\n readonly budgetId: string;\n readonly fields: readonly string[];\n}> {}\n\n/** `/budget/info` answered something that is not a list of budget rows. */\nexport class LitellmBudgetUnreadableError extends Data.TaggedError('LitellmBudgetUnreadableError')<{\n readonly budgetId: string;\n}> {}\n\nexport type BudgetError =\n | budgets.PostInfoBudgetBudgetInfoError\n | budgets.PostNewBudgetBudgetNewError\n | budgets.UpdateBudgetBudgetUpdatePostError\n | budgets.DeleteBudgetBudgetDeletePostError\n | LitellmBudgetHardCapUnjustifiedError\n | LitellmBudgetFieldNotClearedError\n | LitellmBudgetUnreadableError;\n",
|
|
11
11
|
"/**\n * Props, wire bodies and comparison for `LiteLLM.Budget`, testable without a server.\n *\n * ⛔ NO SILENT `max_budget`. LiteLLM's own field doc: \"Requests will fail if this budget (in USD)\n * is exceeded\" — a hard cap. On 2026-09-28 an undeclared $60/day `max_budget` on\n * `hf-tier-interactive` 429'd the whole claude2 fleet. So `maxBudget` is never defaulted and is\n * refused unless `maxBudgetReason` says why; `softBudget` is the monitoring field (it \"will NOT\n * fail if this is exceeded\", it fires the alert).\n * ⚠️ NOT MODELLED: `max_parallel_requests`, `model_max_budget`, `budget_reset_at`. The row read\n * back (`BudgetListItem`, generated at LiteLLM 1.100.0) has none of them, so a declaration of\n * them could never be diffed. Add them when a read that returns them is measured.\n */\nimport type * as budgets from '@distilled.cloud/litellm/budget_management';\n\nexport interface BudgetProps {\n /** Live budget id. Set it to adopt an existing row (`hf-tier-agent`); default is a deterministic physical name. */\n readonly budgetId?: string;\n /** Alert threshold in USD. Requests keep flowing when exceeded. The monitoring field. */\n readonly softBudget?: number;\n /** HARD cap in USD: requests fail (429) once spent. Refused unless `maxBudgetReason` is set. */\n readonly maxBudget?: number;\n /** Why a hard cap is wanted. Required with `maxBudget`; recorded in the declaration only. */\n readonly maxBudgetReason?: string;\n /** Reset period, e.g. `'1d'`, `'28d'`. */\n readonly budgetDuration?: string;\n /** Requests per minute. */\n readonly rpmLimit?: number;\n /** Tokens per minute. */\n readonly tpmLimit?: number;\n}\n\nexport interface BudgetAttributes {\n readonly budgetId: string;\n readonly softBudget: number | null;\n readonly maxBudget: number | null;\n readonly budgetDuration: string | null;\n readonly rpmLimit: number | null;\n readonly tpmLimit: number | null;\n}\n\n/** Declared field → wire field. The single list every comparison and body walks. */\nconst FIELDS = [\n ['softBudget', 'soft_budget'],\n ['maxBudget', 'max_budget'],\n ['budgetDuration', 'budget_duration'],\n ['rpmLimit', 'rpm_limit'],\n ['tpmLimit', 'tpm_limit'],\n] as const;\n\nexport const isHardCapUnjustified = (props: BudgetProps): boolean =>\n props.maxBudget !== undefined && (props.maxBudgetReason ?? '').trim() === '';\n\n/** Create body: only what is declared. */\nexport const createBody = (\n props: BudgetProps,\n budgetId: string,\n): budgets.PostNewBudgetBudgetNewRequest => {\n const body: Record<string, unknown> = { budget_id: budgetId };\n for (const [prop, wire] of FIELDS) if (props[prop] !== undefined) body[wire] = props[prop];\n return body as budgets.PostNewBudgetBudgetNewRequest;\n};\n\n/** Update body: declared fields, plus an explicit `null` for a live limit the declaration dropped. */\nexport const updateBody = (\n props: BudgetProps,\n live: BudgetAttributes,\n): budgets.UpdateBudgetBudgetUpdatePostRequest => {\n const body: Record<string, unknown> = { budget_id: live.budgetId };\n for (const [prop, wire] of FIELDS) {\n if (props[prop] !== undefined) body[wire] = props[prop];\n else if (live[prop] !== null) body[wire] = null;\n }\n return body as budgets.UpdateBudgetBudgetUpdatePostRequest;\n};\n\n/** Wire names of the fields where live and declared disagree. */\nexport const differing = (live: BudgetAttributes, props: BudgetProps): readonly string[] =>\n FIELDS.filter(([prop]) => live[prop] !== (props[prop] ?? null)).map(([, wire]) => wire);\n\nconst numberOrNull = (value: unknown): number | null =>\n value === null || value === undefined || value === '' ? null : Number(value);\n\n/**\n * One row of `/budget/info`. `tpm_limit`/`rpm_limit` are `BigInt?` in LiteLLM's schema and come\n * back as decimal strings (the generated `BudgetListItem` says so), hence `Number()`.\n */\nexport const toAttributes = (row: Record<string, unknown>): BudgetAttributes => ({\n budgetDuration: typeof row['budget_duration'] === 'string' ? row['budget_duration'] : null,\n budgetId: String(row['budget_id']),\n maxBudget: numberOrNull(row['max_budget']),\n rpmLimit: numberOrNull(row['rpm_limit']),\n softBudget: numberOrNull(row['soft_budget']),\n tpmLimit: numberOrNull(row['tpm_limit']),\n});\n",
|
|
12
12
|
"/**\n * The four LiteLLM `/budget/*` calls `LiteLLM.Budget` uses, through the SDK's typed operations.\n *\n * ★ READS GO THROUGH `/budget/list` (the whole table, a bare array), not `/budget/info`. Both\n * answer `body: unknown` in the generated schema, and what `/budget/info` does for an id that is\n * not there is unmeasured; a list answers absence unambiguously.\n * ⚠️ Unlike pass-through endpoints, each budget is its own DB row, so no semaphore.\n * ⚠️ DELETE is idempotent BY THIS FUNCTION: a `BadRequest` re-lists and is swallowed only when the\n * id is genuinely absent, never on message text (pass-through `operations.ts`' rule).\n */\nimport * as budgets from '@distilled.cloud/litellm/budget_management';\nimport * as Effect from 'effect/Effect';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\nimport { type BudgetAttributes, toAttributes } from './budget-form.ts';\nimport { LitellmBudgetUnreadableError } from './budget-errors.ts';\n\nexport const listBudgets = (): Effect.Effect<\n readonly BudgetAttributes[],\n budgets.ListBudgetBudgetListGetError | LitellmBudgetUnreadableError,\n LitellmOpContext\n> =>\n throughFetch(budgets.listBudgetBudgetListGet({})).pipe(\n // ⚠️ MEASURED with the fake: the SDK types this `{ body: unknown }` but hands a bare JSON array\n // back as the array itself, so accept both shapes and refuse anything else.\n Effect.flatMap((response) => {\n const rows: unknown = Array.isArray(response)\n ? response\n : (response as { body?: unknown }).body;\n return Array.isArray(rows)\n ? Effect.succeed(rows.map((row) => toAttributes(row as Record<string, unknown>)))\n : Effect.fail(new LitellmBudgetUnreadableError({ budgetId: '*' }));\n }),\n );\n\nexport const createBudget = (body: budgets.PostNewBudgetBudgetNewRequest) =>\n throughFetch(budgets.postNewBudgetBudgetNew(body)).pipe(Effect.asVoid);\n\nexport const updateBudget = (body: budgets.UpdateBudgetBudgetUpdatePostRequest) =>\n throughFetch(budgets.updateBudgetBudgetUpdatePost(body)).pipe(Effect.asVoid);\n\nexport const deleteBudget = (id: string) =>\n throughFetch(budgets.deleteBudgetBudgetDeletePost({ id })).pipe(\n Effect.asVoid,\n Effect.catchTag('BadRequest', (original) =>\n listBudgets().pipe(\n Effect.flatMap((rows) =>\n rows.some((row) => row.budgetId === id) ? Effect.fail(original) : Effect.void,\n ),\n ),\n ),\n );\n",
|
|
13
|
-
"/**\n * The LiteLLM provider, with its credential source, as one layer for a stack.\n *\n * Layer.mergeAll(litellmProviders(), …the stack's other providers)\n *\n * ★ THE DEFAULT READS `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` AT CALL TIME, through\n * `@distilled.cloud/litellm`'s own `CredentialsFromEnv` — the same two variable names the\n * retired hand-rolled `credentials.ts` read — never at layer build. Pass another\n * `Layer.Layer<Credentials>` (see the SDK's own `credentials({ apiKey, baseUrl })` in a test) to\n * source them another way.\n * ⚠️ `HttpClient` is NOT provided here. It is ambient in every Alchemy runtime the same way it is\n * for `netboxProviders`-style families in this kit; a stack that already deploys anything over\n * HTTP already has one. Do not add a second client to this layer.\n * ⚠️ `Layer.provideMerge`, NEVER PLAIN `Layer.provide`, for the credentials layer. The handlers\n * still need `Credentials` when the engine calls `read`/`diff`/`reconcile`. `Provider.effect`\n * puts that need on the layer. Plain `provide` satisfies it only while the `Provider` value is\n * built, then hides `Credentials` from those later calls. Measured on Caddy, 2026-09-23:\n * `caddy/providers.ts`. `provideMerge` feeds credentials in and keeps them in the layer the\n * engine runs the handlers with.\n */\nimport { type Credentials, CredentialsFromEnv } from '@distilled.cloud/litellm/Credentials';\nimport * as Layer from 'effect/Layer';\nimport { LiteLLMBudgetProvider } from './budget.ts';\nimport { LiteLLMPassThroughEndpointProvider } from './pass-through-endpoint.ts';\n\nexport const litellmProviders = (creds: Layer.Layer<Credentials> = CredentialsFromEnv) =>\n Layer.mergeAll(LiteLLMPassThroughEndpointProvider(), LiteLLMBudgetProvider()).pipe(\n Layer.provideMerge(creds),\n );\n"
|
|
13
|
+
"/**\n * `LiteLLM.Credential` — one row of LiteLLM's reusable credential table (`/credentials/*`), a\n * named bag of values the proxy stores encrypted and can bind to a deployment by `model_id`.\n *\n * ★ ADOPT BY NAME. `credentialName` finds the live row whose `credential_name` equals it; a live\n * row with no state is `Unowned`, so it needs `--adopt`. The name is the vendor's only address\n * (the table's unique column, `schema.prisma`), so a row is located by the by-name read and\n * never guessed at.\n * ★ `defaultRemovalPolicy: 'retain'` — deleting a credential a deployment references would break\n * that deployment's next call. Opt in with `.pipe(RemovalPolicy.destroy())`.\n * ⛔ THE VALUES ARE `{ fromEnv: 'NAME' }`, NEVER A VALUE (S25): LiteLLM STORES these literals\n * (encrypted at rest), so the deploying process must hold them — credential-types.ts carries the\n * full reasoning, and a `model` deployment's `os.environ/NAME` reference is the opposite case.\n * A plan compares the live row's `credential_info` per declared key and the values through a\n * digest of what was resolved (`valuesSeal`); it never compares the masked read, and a value\n * whose variable is unset never drifts a plan-only environment (credential-values.ts).\n * ⚠️ A RENAME IS A REPLACE: the name is the row's identity and its only address, so a changed\n * `credentialName` creates the new row and the old one survives under 'retain' — never renamed\n * in place.\n * ⚠️ A CHANGED ROW IS PATCHED, NOT REWRITTEN — PATCH merges values and normally replaces DB\n * info, but only merges in-memory info (1.103.0 credential_endpoints/endpoints.py:312-319,384-387).\n * A failed PATCH leaves the row behind (review finding 3: DELETE + POST left NO row when POST\n * failed). Dropping a value key OR an info key still requires DELETE + POST; both stores must\n * converge. All refusals run before the first write. Every write reads back to prove removal.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { refuseTakeover } from '../ownership/adopt.ts';\nimport {\n LitellmCredentialAbsentAfterWriteError,\n LitellmCredentialInvalidError,\n LitellmCredentialNotConvergedError,\n} from './credential-errors.ts';\nimport {\n createBody,\n differing,\n firstProblem,\n literalValues,\n patchBody,\n removedInfoKeys,\n removedValueKeys,\n} from './credential-form.ts';\nimport {\n createCredential,\n deleteCredential,\n readCredential,\n refuseDebugLogging,\n updateCredential,\n} from './credential-operations.ts';\nimport type { CredentialAttributes, CredentialProps } from './credential-types.ts';\nimport { rewriteCredential } from './credential-rewrite.ts';\nimport { requireValues, resolveValues, sealValues, valuesState } from './credential-values.ts';\n\nexport type { CredentialAttributes, CredentialProps };\nexport type { CredentialError } from './credential-errors.ts';\n\nexport interface LiteLLMCredential extends Resource<\n 'LiteLLM.Credential',\n CredentialProps,\n CredentialAttributes\n> {}\n\nexport const LiteLLMCredential = Resource<LiteLLMCredential>('LiteLLM.Credential', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMCredential = (value: unknown): value is LiteLLMCredential =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Credential';\n\nconst refuse = (props: CredentialProps) => {\n const problem = firstProblem(props);\n return problem === undefined\n ? Effect.void\n : Effect.fail(\n new LitellmCredentialInvalidError({\n problem,\n credentialName: String(props.credentialName),\n }),\n );\n};\n\ntype Args<P> = {\n fqn: string;\n instanceId: string;\n output: CredentialAttributes | undefined;\n} & P;\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const credentialHandlers = {\n /**\n * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW\n * CREDENTIAL GETS — so the refusals run here too, as a defect (mcp-server.ts's own rule: the\n * cold-start probe must fail the plan, and a recovery read must degrade to \"nothing\n * recovered\", which only a defect does).\n */\n read: ({ olds, output }: Args<{ olds: CredentialProps }>) =>\n Effect.gen(function* () {\n // SDK debug logging prints responses too; non-sensitive value keys can be unmasked.\n yield* refuseDebugLogging(olds.credentialName);\n const problem = output === undefined ? firstProblem(olds) : undefined;\n if (problem !== undefined) {\n return yield* Effect.die(\n new LitellmCredentialInvalidError({\n problem,\n credentialName: String(olds.credentialName),\n }),\n );\n }\n const found = yield* readCredential(olds.credentialName);\n if (found === undefined) return undefined;\n // A row cannot supply a digest of values it hides; state can.\n return output === undefined ? Unowned(found) : { ...found, valuesSeal: output.valuesSeal };\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<CredentialProps>;\n output: CredentialAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuse(news);\n if (output === undefined) return undefined;\n // The name is identity: a different declared name is a different row.\n if (news.credentialName !== output.credentialName) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n // ⚠️ An unresolved value never drifts a plan (credential-values.ts's `unknown`): the gate\n // is \"not stale\", the same shape mcp-server.ts uses, and the write below still demands\n // the value when a write is actually being made.\n const state = valuesState(news, output.valuesSeal);\n // PATCH cannot remove value keys or in-memory info keys. Names alone prove drift.\n const removed = [...removedInfoKeys(output, news), ...removedValueKeys(output, news)];\n return differing(output, news).length === 0 && removed.length === 0 && state !== 'stale'\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ fqn, instanceId, news, output }: Args<{ news: CredentialProps }>) =>\n Effect.gen(function* () {\n yield* refuse(news);\n yield* refuseDebugLogging(news.credentialName);\n const resolved = resolveValues(news);\n const before = yield* readCredential(news.credentialName);\n let sealed = output?.valuesSeal ?? '';\n\n if (before === undefined) {\n // ⛔ A CREATE IS A WHOLE-ROW WRITE, so it must hold every declared value.\n yield* requireValues(news, resolved);\n yield* createCredential(createBody(news, resolved));\n sealed = sealValues(resolved.values);\n } else {\n // ⛔ A LIVE ROW IS ONLY WRITTEN WHEN THIS STACK MAY OWN IT. A rename onto a name the proxy\n // already holds runs here with `output: undefined` (a fresh replace's new generation) and\n // `before` set to the FOREIGN row — the planner never probed the new identity, so this is\n // the apply-time check key.ts's own reconcile makes, and without it a deploy-wide\n // `--adopt` meant for something else would overwrite a row another owner relies on.\n yield* refuseTakeover(\n { fqn, instanceId, output },\n `LiteLLM.Credential ${news.credentialName}`,\n );\n // ⛔ PATCH FOR VALUE/INFO CHANGES, REWRITE TO DROP KEYS IN BOTH STORES. Unlike the\n // old unconditional rewrite (finding 3), failed PATCH leaves the row behind. A rewrite\n // still needs every declared value BEFORE DELETE so a partial environment cannot erase it.\n const removed = [...removedInfoKeys(before, news), ...removedValueKeys(before, news)];\n const infoDrift = differing(before, news).length > 0;\n const valuesStale = valuesState(news, sealed) === 'stale';\n if (removed.length > 0) {\n yield* requireValues(news, resolved);\n yield* rewriteCredential(createBody(news, resolved));\n sealed = sealValues(resolved.values);\n } else if (infoDrift || valuesStale) {\n if (valuesStale) yield* requireValues(news, resolved);\n yield* updateCredential(\n patchBody(news, valuesStale ? literalValues(resolved) : undefined),\n );\n if (valuesStale) sealed = sealValues(resolved.values);\n }\n }\n\n // ⛔ THE READ-BACK IS A SINGLE READ, ON PURPOSE, AND THE VENDOR CLOSES THE RACE. The by-name\n // read walks the proxy's IN-MEMORY list, which a write that just returned has already\n // reloaded: `create_credential` awaits `upsert_credentials` and the PATCH awaits its\n // in-memory sync before answering (endpoints.py, 1.103.0), so a row written by THIS proxy\n // is in memory by the time its write answers. A retry loop would only paper over a\n // multi-proxy read that hit a peer which had not reloaded — and then the honest answer is\n // the failure below, not a slow pretend. This is the same single-read stance key.ts takes.\n const after = yield* readCredential(news.credentialName);\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmCredentialAbsentAfterWriteError({ credentialName: news.credentialName }),\n );\n }\n const left = [\n ...differing(after, news),\n ...removedInfoKeys(after, news).map((key) => `credential_info.${key}`),\n ...removedValueKeys(after, news).map((key) => `credential_values.${key}`),\n ];\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmCredentialNotConvergedError({\n fields: left,\n credentialName: news.credentialName,\n }),\n );\n }\n return { ...after, valuesSeal: sealed };\n }),\n\n delete: ({ output }: { output: CredentialAttributes }) => deleteCredential(output.credentialName),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMCredentialProvider = () =>\n Provider.effect(\n LiteLLMCredential,\n Effect.succeed(LiteLLMCredential.Provider.of(credentialHandlers)),\n );\n",
|
|
14
|
+
"/**\n * Whether a live object this stack holds no state for may be written — the apply-time half of the\n * house rule that NOTHING IS ADOPTED WITHOUT `--adopt` (decided 2026-09-21 for HostFile, LaunchdJob,\n * CaddyConfig and every Bao.* family). probe.ts is the plan-time half.\n *\n * ★ FAMILY-NEUTRAL ON PURPOSE. The engine questions here — what `--adopt` resolves to for one FQN,\n * whether this apply resumes the resource's own interrupted create — are the same for a vault\n * role, a launchd plist and a Caddy config, so they live once, outside every subpath's directory.\n */\nimport { AdoptPolicy } from 'alchemy/AdoptPolicy';\nimport { AlchemyContext } from 'alchemy/AlchemyContext';\nimport { Stack } from 'alchemy/Stack';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport { resumes, unfinished } from './resume.ts';\nimport { forgetRefusedCreate, isCreate } from './rows.ts';\n\n/**\n * Whether the resource at `fqn` may be adopted, resolved exactly as the planner resolves it (alchemy\n * beta.79 Plan.ts: `resource.Adopt ?? shouldAdopt`): a resource-scoped `adopt(…)`, else the\n * `AdoptPolicy` service, else `AlchemyContext.adopt`, else off.\n * ★ FOR A CREATE THE ENGINE NEVER PROBED. Alchemy skips the adoption probe while `news` holds an\n * Output, so an ownership check that must still honour `--adopt` can only run at apply. All three\n * reach the apply: Alchemist runs plan AND apply under the session context, which provides the\n * services from the CLI flag and the Stack the resources registered on (Alchemist/Session.ts,\n * routes/stack.ts; Apply.ts reads the same Stack).\n * ⛔ THE RESOURCE-SCOPED SETTING WINS, BOTH WAYS. The engine captures `adopt(…)` on the resource at\n * registration (Resource.ts `Adopt`, into `Stack.resources[fqn]`) and never hands it to a\n * provider. Reading only the deploy-wide flag let `--adopt` take over a resource declared\n * `.pipe(adopt(false))` — which the planner refuses — and refused one declared `adopt(true)`.\n * ⚠️ `resources` carries an `@internal` comment in Alchemy's source, but it is in the published types\n * and the planner reads `Adopt` from it. caddy/adopt-scope.test.ts registers through the real\n * `Resource`, so an upgrade that stops recording it there fails the suite.\n */\nexport const adoptEnabled = (fqn: string): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n const stack = yield* Effect.serviceOption(Stack);\n const scoped = Option.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;\n if (scoped !== undefined) return scoped;\n const policy = yield* Effect.serviceOption(AdoptPolicy);\n if (Option.isSome(policy)) return policy.value;\n const context = yield* Effect.serviceOption(AlchemyContext);\n return Option.isSome(context) ? context.value.adopt : false;\n });\n\n/** The three fields of a `reconcile` (or `read`) call that say whose object this is. */\nexport type Owner = {\n readonly fqn: string;\n readonly instanceId: string;\n /** The attributes state holds; `undefined` on a create. */\n readonly output: unknown;\n};\n\n/**\n * Whether `--adopt` may speak for this apply's generation: a CREATE (rows.ts isCreate), or an\n * UNFINISHED generation whose row this plan's diff saw with no attributes (resume.ts) — the\n * resume Alchemy itself offers `--adopt` for (\"Cannot resume creating … Re-run with --adopt\").\n * ⛔ NOT A FRESH REPLACE'S NEW GENERATION. The planner never probes it and never offers it adoption,\n * so the identity it moves to was never asked about under `--adopt`: 🔴 MEASURED 2026-09-21\n * (launchd/adopt-apply.test.ts), a HostFile whose path changed onto a file it did not own\n * overwrote that file under a deploy-wide `--adopt` meant for something else. A Bao rename onto a\n * taken identity already fails the plan, `--adopt` or not (rename.ts judgeMove); this keeps the\n * apply to the same answer.\n */\nconst adoptable = (owner: Owner): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n if (yield* isCreate(owner.fqn, owner.instanceId)) return true;\n return yield* unfinished(owner.instanceId);\n });\n\n/** Whether a stateless `reconcile` may take over a live object: adoption on, for an adoptable one. */\nexport const adoptsAtApply = (owner: Owner): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n if (!(yield* adoptEnabled(owner.fqn))) return false;\n return yield* adoptable(owner);\n });\n\n/** A lifecycle helper's hook for refuseTakeover, bound to one call's Owner: `(what) => …`. */\nexport type Claim = (what: string) => Effect.Effect<void>;\n\n/**\n * `reconcile`'s check, called with the live object it found BEFORE any write: dies unless that\n * object may be written. It may when state already vouches for it (`output`), when this apply\n * resumes the resource's own interrupted create or replace and its diff proved the object that\n * generation's (resume.ts), or when adoption is on for a create or an unfinished generation\n * (adoptable).\n * ⛔ WHY IT EXISTS AT ALL: THE PROBE DOES NOT GUARD EVERY CREATE. Alchemy skips it while `news` holds\n * an Output (Plan.ts), a new generation of a replace is never probed, and an object can appear\n * between plan and apply. Each of those used to be a silent takeover: written over, then recorded\n * as ours, and deleted with the stack under `destroy`.\n * ⚠️ A CHECK, THEN A WRITE — NOT A LOCK. Two creates of one name in the same deploy run\n * concurrently, both read nothing, and both write (measured with the fakes, 2026-09-21). Nothing\n * between a read and a write can close that; declare each name once.\n * ⚠️ AN IDENTICAL OBJECT IS STILL A TAKEOVER (decided 2026-09-21). Nothing would be written, but the\n * state row would claim it, and a later delete would remove what another owner still relies on.\n * ⛔ A REFUSED CREATE FORGETS ITS `creating` ROW (rows.ts forgetRefusedCreate), or the next plan's\n * recovery read would adopt the very object this refused.\n */\nexport const refuseTakeover = (owner: Owner, what: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n if (owner.output !== undefined) return;\n if (yield* resumes(owner.instanceId)) return;\n const may = yield* adoptable(owner);\n if (may && (yield* adoptEnabled(owner.fqn))) return;\n yield* forgetRefusedCreate(owner.fqn, owner.instanceId);\n const remedy = may\n ? 'Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first.'\n : 'It is the new identity of a replace, which --adopt does not cover: remove it first.';\n return yield* Effect.die(\n new Error(\n `${what}: already exists, and this stack holds no state for it — the plan could not ask ` +\n 'first (a prop was still an Output, a replace moved onto it, or it appeared after the ' +\n `plan). Nothing was written. ${remedy}`,\n ),\n );\n });\n\n/** refuseTakeover bound to one call's Owner, for a lifecycle helper that finds the live object. */\nexport const claimFor =\n (owner: Owner): Claim =>\n (what) =>\n refuseTakeover(owner, what);\n",
|
|
15
|
+
"/**\n * Whether this apply RESUMES a create or replace the engine already started for this resource AND\n * the live object it will find is proven that generation's own — the one case in which a stateless\n * `reconcile` may write over a live object without `--adopt`.\n *\n * ★ HOW THE ENGINE TELLS US. Alchemy calls `diff` whenever a state row exists, and hands it `output`\n * = that row's attributes (alchemy beta.79 Plan.ts). A row with NO attributes is exactly an\n * unfinished generation: `creating` (a create interrupted after its write, before its commit),\n * `replacing` (the same for a replace), or `deleting` re-driven as a create. A fresh create is\n * never diffed at all. So the diff of such a row decides, and the apply that follows (the same\n * instance id: Apply.ts reuses the row's) finds the note.\n * ⛔ A ROW PROVES NOTHING BY ITSELF (red team, 2026-09-21). Apply commits it BEFORE reconcile runs, so\n * a deploy killed before the claim — an upstream that failed, a Ctrl-C — leaves a row whose create\n * never asked whose object sat at its identity. 🔴 MEASURED (openbao/adopt-holes.test.ts): with the\n * name an Output, the next deploy \"resumed\" that create and wrote over another owner's role, in\n * every family that had a note on the row alone. So the note is left only when the family's own\n * `read`, asked as the recovery read (probe.ts ownedRead: a recorded instance, a row carrying the\n * whole declaration, and a live object that matches it), answers \"ours\". For a `creating` row\n * that is the question Plan.ts already asked; for a `replacing` row it is the only time it is asked.\n * ⚠️ WHY NOT THE STATE STORE ALONE. At reconcile both look the same: Apply commits the fresh create's\n * `creating` row before calling reconcile, so a row with this instance id is always there.\n * ★ THE CHANNEL IS ALCHEMY'S OWN: `Artifacts`, the per-FQN bag the engine shares between one plan\n * and its apply (Artifacts.ts: \"diff computes … create / update reads the same artifact\").\n * Deploy.ts and the Alchemist session both hold one store across the pair.\n * ⚠️ IT FAILS CLOSED. No bag, a bag the plan never wrote, or an object the read cannot prove ours\n * reads as a fresh create, and adopt.ts then asks for `--adopt` — the safe direction.\n * ★ AN UNPROVEN RESUME IS STILL MARKED UNFINISHED (`noteUnfinished`), so `--adopt` can take it over\n * the way Alchemy's own \"Cannot resume creating … Re-run with --adopt\" offers for a create — while\n * a FRESH replace's new generation, which no diff ever saw without attributes, stays unadoptable.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { Artifacts } from 'alchemy/Artifacts';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nconst PROVEN = 'homeflare/ownership/resumes';\nconst UNFINISHED = 'homeflare/ownership/unfinished';\n\nconst note = (key: string, instanceId: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n const bag = yield* Effect.serviceOption(Artifacts);\n if (Option.isSome(bag)) yield* bag.value.set(key, instanceId);\n });\n\nconst noted = (key: string, instanceId: string): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n const bag = yield* Effect.serviceOption(Artifacts);\n if (Option.isNone(bag)) return false;\n return (yield* bag.value.get<string>(key)) === instanceId;\n });\n\n/**\n * `diff`'s note for a row with no attributes: `instanceId` is an unfinished generation of this\n * resource's own, which `--adopt` may resume when nothing proves it (adopt.ts adoptable). Returns\n * `undefined`, a diff's \"defer to the engine\".\n */\nexport const noteUnfinished = (instanceId: string): Effect.Effect<undefined> =>\n Effect.as(note(UNFINISHED, instanceId), undefined);\n\n/**\n * Note `instanceId` unfinished, and a proven resume when `recovered` — the family's `read` of the\n * row's props, asked with no attributes — answers a plain object: ours. `Unowned`, nothing, or a\n * read that fails or dies proves nothing. ⚠️ Never fails the plan: an unproven resume is refused\n * at apply instead, unless `--adopt`.\n */\nexport const noteResume = <R>(\n instanceId: string,\n recovered: Effect.Effect<unknown, unknown, R>,\n): Effect.Effect<void, never, R> =>\n Effect.gen(function* () {\n yield* noteUnfinished(instanceId);\n const unproven = () => Effect.succeed(undefined);\n const found = yield* recovered.pipe(Effect.catch(unproven), Effect.catchDefect(unproven));\n if (found === undefined || found === null || Unowned.is(found)) return;\n yield* note(PROVEN, instanceId);\n });\n\n/** Whether the plan for this apply noted `instanceId` as a proven resumed create or replace. */\nexport const resumes = (instanceId: string): Effect.Effect<boolean> => noted(PROVEN, instanceId);\n\n/** Whether the plan for this apply saw `instanceId` as an unfinished generation (proven or not). */\nexport const unfinished = (instanceId: string): Effect.Effect<boolean> =>\n noted(UNFINISHED, instanceId);\n\n/** The fields of a `read` or `diff` call provingResumes uses; a family's own input carries more. */\ntype Ask = {\n readonly id: string;\n readonly fqn: string;\n readonly instanceId: string;\n readonly olds: unknown;\n readonly output: unknown;\n};\ntype Hook = Effect.Effect<unknown, unknown, unknown>;\n\n/** The two hooks provingResumes wraps, as Alchemy's ProviderService declares them. */\ninterface Resumable {\n read?(input: Ask): Hook;\n diff?(input: Ask): Hook;\n}\n\n/**\n * A provider whose `diff`, for a row with no attributes, first asks the provider's own `read` whether\n * the live object is that unfinished generation's (noteResume), then runs as written. Every Bao.*\n * family is built through it, so their diffs answer such a row with a plain `undefined`.\n */\nexport const provingResumes = <S extends Resumable>(service: S): S => {\n const { diff, read } = service;\n if (diff === undefined || read === undefined) return service;\n const proving: Resumable['diff'] = (input) =>\n Effect.gen(function* () {\n if (input.output === undefined) {\n yield* noteResume(input.instanceId, read({ ...input, output: undefined }));\n }\n return yield* diff(input);\n });\n // ⚠️ The one cast: `diff` keeps the family's own signature — it is the family's diff, run after.\n return { ...service, diff: proving } as S;\n};\n",
|
|
16
|
+
"/**\n * The questions ownership/ asks of Alchemy's state store: does a row record this instance, and with\n * its whole declaration (probe.ts); is this apply a create rather than a replace's new generation\n * (adopt.ts); and — when reconcile refuses a create — forget the row Apply committed for it\n * (adopt.ts). Alchemy's own AWS.EC2.SecurityGroup provider reads the store the same way: the Stack\n * service names the stack and stage, and `State` yields the store.\n *\n * ⚠️ WITHOUT A STACK OR STATE IN CONTEXT, or with a store that errors, a row reads as \"not\n * recorded\" — the object is then `Unowned`, the safe answer — and nothing is forgotten, which\n * leaves the one gap forgetRefusedCreate exists to close. `alchemy deploy` and the Alchemist\n * session provide both.\n */\nimport { Stack } from 'alchemy/Stack';\nimport { State, type StateService, isActionState } from 'alchemy/State/State';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport { wholeDeclaration } from './whole.ts';\n\ntype Where = { readonly stack: string; readonly stage: string };\n\n/** The store, where this stack lives, and what it declares — or undefined outside a stack. */\nconst storeOf = Effect.gen(function* () {\n const stack = yield* Effect.serviceOption(Stack);\n const state = yield* Effect.serviceOption(State);\n if (Option.isNone(stack) || Option.isNone(state)) return undefined;\n const store: StateService = yield* state.value;\n const where: Where = { stack: stack.value.name, stage: stack.value.stage };\n return { resources: stack.value.resources, store, where };\n});\n\nconst rowAt = (store: StateService, where: Where, fqn: string) =>\n store.get({ ...where, fqn }).pipe(Effect.orElseSucceed(() => undefined));\n\n/** One generation of a persisted row, as far as ownership/ reads it. */\nexport type Generation = {\n readonly instanceId?: unknown;\n readonly status?: unknown;\n readonly props?: unknown;\n readonly attr?: unknown;\n readonly old?: unknown;\n /** Set by Apply on the `updating` row of an adoption, until that update commits (Apply.ts). */\n readonly adopting?: unknown;\n};\n\n/** The generation of a persisted row that carries `instanceId`: the row itself, or one in `old`. */\nconst generationOf = (row: unknown, instanceId: string): Generation | undefined => {\n let at = row;\n while (typeof at === 'object' && at !== null && !isActionState(at as never)) {\n const generation = at as Generation;\n if (generation.instanceId === instanceId) return generation;\n at = generation.old;\n }\n return undefined;\n};\n\n/** The generation `instanceId` names — at the FQN, or at a former FQN `renamedFrom` names. */\nexport const recordedGeneration = (\n fqn: string,\n instanceId: string,\n): Effect.Effect<Generation | undefined> =>\n Effect.gen(function* () {\n const found = yield* storeOf;\n if (found === undefined) return undefined;\n for (const at of [fqn, ...(found.resources[fqn]?.FormerFqns ?? [])]) {\n const generation = generationOf(yield* rowAt(found.store, found.where, at), instanceId);\n if (generation !== undefined) return generation;\n }\n return undefined;\n });\n\n/**\n * What the state store says of `instanceId` for this resource (at its FQN, or at a former FQN its\n * `renamedFrom` names — the planner migrates that row before it recovers it):\n * · `absent` — no row holds it. ★ This is what separates the probe from the recovery read: the\n * probe's instance id is minted for the probe (Plan.ts `generateInstanceId()`), 128 random bits\n * no row can hold.\n * · `partial` — a row holds it, but its props lack part of the declaration: a prop stripped at\n * commit because it was still an Output (whole.ts). Those props cannot prove an object ours.\n * · `whole` — a row holds it with every prop the declaration names.\n */\nexport type Recorded = 'absent' | 'partial' | 'whole';\n\nexport const recordedInstance = (fqn: string, instanceId: string): Effect.Effect<Recorded> =>\n Effect.gen(function* () {\n const generation = yield* recordedGeneration(fqn, instanceId);\n if (generation === undefined) return 'absent';\n return (yield* wholeDeclaration(fqn, generation.props)) ? 'whole' : 'partial';\n });\n\n/**\n * Whether this apply's `instanceId` is a CREATE: the row Apply committed for it is `creating`.\n * ⛔ A REPLACE'S NEW GENERATION IS NOT ONE. Apply commits it as `replacing`, and the planner never\n * offers it adoption — the probe runs only for a resource with no state — so `--adopt` must not\n * let it write over whatever sits at its new identity (adopt.ts adoptsAtApply).\n */\nexport const isCreate = (fqn: string, instanceId: string): Effect.Effect<boolean> =>\n Effect.map(\n recordedGeneration(fqn, instanceId),\n (generation) => generation?.status === 'creating',\n );\n\nconst idOf = (attr: unknown): string | undefined => {\n if (typeof attr !== 'object' || attr === null) return undefined;\n const id = (attr as { readonly id?: unknown }).id;\n return typeof id === 'string' && id !== '' ? id : undefined;\n};\n\n/**\n * `attr.id` on every generation OLDER than `instanceId` in this FQN's state chain (and a former\n * FQN `renamedFrom` names). Walks `old` the way {@link generationOf} does, and skips the current\n * generation itself.\n * ⛔ THOSE IDS ARE NOT THIS GENERATION'S. A `replacing` row's reconcile runs with no attributes, so\n * a live row found by name can be the previous generation's own deployment. Recording that id on\n * the new generation makes both the same id, and `destroy()` then deletes the old generation by\n * it (Apply.ts, the retain check near 2168 and the provider delete that follows when the policy\n * is not `retain`). A same-instance `old` snapshot (an `updating` row) is not one of these.\n */\nexport const olderGenerationIds = (\n fqn: string,\n instanceId: string,\n): Effect.Effect<readonly string[]> =>\n Effect.gen(function* () {\n const found = yield* storeOf;\n if (found === undefined) return [];\n const ids: string[] = [];\n for (const at of [fqn, ...(found.resources[fqn]?.FormerFqns ?? [])]) {\n collectOlder(yield* rowAt(found.store, found.where, at), instanceId, ids);\n }\n return ids;\n });\n\n/**\n * Once `instanceId` has been seen, collect `attr.id` from generations with a DIFFERENT instance\n * id. An `updating` row's `old` is the same instance's pre-update snapshot (Apply.ts), not a\n * replacement generation — that snapshot's id is the row this apply is already writing.\n */\nconst collectOlder = (row: unknown, instanceId: string, ids: string[]): void => {\n let at = row;\n let seen = false;\n while (typeof at === 'object' && at !== null && !isActionState(at as never)) {\n const generation = at as Generation;\n if (!seen) {\n seen = generation.instanceId === instanceId;\n at = generation.old;\n continue;\n }\n if (generation.instanceId !== instanceId) {\n const id = idOf(generation.attr);\n if (id !== undefined) ids.push(id);\n }\n at = generation.old;\n }\n};\n\n/**\n * ⛔ A REFUSED CREATE MUST NOT LEAVE A ROW THAT CLAIMS THE OBJECT IT REFUSED. Apply commits a\n * `creating` row for this instance before it calls reconcile, and a failed reconcile leaves it\n * there. The next plan's recovery read would then find our instance id and an object that can\n * match its props — a prop that was an Output is stripped from the row, and an omitted prop is\n * \"not managed\" — and adopt exactly what this apply refused. 🔴 MEASURED 2026-09-21: Bao.Mount\n * and Bao.AuthMethod did (openbao/adopt-core.test.ts). So the row goes, and state reads as if\n * the create never started: the next plan probes, and the probe answers `Unowned`.\n * ⚠️ ONLY a `creating` row of THIS instance with no attributes — the one Apply just wrote. A\n * `replacing` row carries the previous generation in `old` and is never touched.\n */\nexport const forgetRefusedCreate = (fqn: string, instanceId: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n const found = yield* storeOf;\n if (found === undefined) return;\n const row = yield* rowAt(found.store, found.where, fqn);\n const mine = row as { status?: unknown; instanceId?: unknown; attr?: unknown } | undefined;\n if (mine?.status !== 'creating' || mine.instanceId !== instanceId || mine.attr !== undefined) {\n return;\n }\n yield* found.store.delete({ ...found.where, fqn }).pipe(Effect.ignore);\n });\n",
|
|
17
|
+
"/**\n * Whether a state row's props say everything the declaration says — the test that separates a row\n * whose props can prove an object ours from one that cannot (probe.ts, rows.ts).\n *\n * ★ WHY A ROW CAN BE MISSING PART OF ITS DECLARATION. Apply commits the `creating` (or `replacing`)\n * row BEFORE reconcile runs, and strips every prop still an Output at that moment (alchemy\n * beta.79 Apply.ts `commit` → Diff.ts `stripUnresolved`); a JSON store then drops the key. So a\n * row written while an upstream was still being created carries a HOLE where that prop was.\n * ⛔ A HOLE IS \"UNKNOWN\", NOT \"UNMANAGED\". Most families read an absent prop as \"not managed\", so a\n * recovery read comparing the live object with holed props proves nothing: 🔴 MEASURED 2026-09-21\n * (openbao/adopt-holes.test.ts), a Bao.Mount whose `defaultLeaseTtl` was an Output, killed before\n * its reconcile ran, adopted — and tuned — another owner's mount on the next deploy; with the\n * NAME an Output, every role family read an empty name, found nothing, and the resumed create\n * wrote over another owner's role. Neither deploy had ever asked whose object it was.\n * ★ THE DECLARATION IS WHAT TELLS A HOLE FROM AN OMISSION. It still holds the Output (or the literal)\n * the row lacks, while an optional prop left out is absent from both.\n * ⚠️ CONSERVATIVE BY DESIGN: a prop added to the declaration after the crash also reads as a hole.\n * That costs one `--adopt`, never a takeover.\n */\nimport { Stack } from 'alchemy/Stack';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nconst absent = (value: unknown): boolean => value === undefined || value === null;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\n/**\n * Whether `recorded` carries a value everywhere `declared` does. Plain objects and arrays are\n * walked; any other declared value — a literal, an Output, an Effect — needs a value in the row.\n */\nexport const carries = (declared: unknown, recorded: unknown): boolean => {\n if (absent(declared)) return true;\n if (absent(recorded)) return false;\n if (Array.isArray(declared)) {\n if (!Array.isArray(recorded)) return false;\n return declared.every((each, index) => carries(each, recorded[index]));\n }\n if (isPlainObject(declared)) {\n if (!isPlainObject(recorded)) return false;\n return Object.entries(declared).every(([key, each]) => carries(each, recorded[key]));\n }\n return true;\n};\n\n/**\n * Whether `props` — one generation's, from the state store — carry the whole declaration of the\n * resource at `fqn`. ⛔ FALSE WITH NOTHING TO COMPARE: no Stack in context, or a resource no longer\n * declared (an orphan's delete). That object then reads as `Unowned` and Apply leaves it in place\n * with a note, rather than deleting what it cannot prove it made.\n */\nexport const wholeDeclaration = (fqn: string, props: unknown): Effect.Effect<boolean> =>\n Effect.map(Effect.serviceOption(Stack), (stack) => {\n const declared = Option.isSome(stack) ? stack.value.resources[fqn] : undefined;\n return declared !== undefined && carries(declared.Props, props);\n });\n",
|
|
18
|
+
"/**\n * The typed refusals `LiteLLM.Credential` raises on top of `@distilled.cloud/litellm`'s own SDK\n * errors.\n *\n * ⛔ NO MESSAGE HERE MAY CARRY A CREDENTIAL VALUE: only the credential's NAME, field names, the\n * NAMES of environment variables, and SDK error text — exactly like mcp-server-errors.ts.\n * ⚠️ THE SDK'S TYPED UNION IS NARROW ON PURPOSE, and the runtime is wider: the credential routes\n * declare only `UnprocessableEntity` (422) in `credential_management.ts`, so a create on an\n * existing name — which the live 1.103.0 container answers `409` (`endpoints.py`'s\n * `_credential_exists_detail`) — decodes at run time as the core `Conflict` class (`Errors.ts`\n * re-exports it, but it is not in the operation's TS union) and PROPAGATES as such, never\n * silently swallowed (S21). The read's and delete's `404` is TYPED as the SDK's\n * `CredentialNotFound` (a distilled patch matched on the vendor's \"Credential not found\"\n * message, `endpoints.py`), so credential-operations.ts `catchTag`s it instead of `instanceof`\n * `NotFound`: a 404 from a front proxy, Access, or a wrong base path stays a `NotFound` and is\n * never read as absence. `CredentialError` carries the typed members of the four operations the\n * resource calls (create, by-name read, PATCH, delete), plus the refusals below.\n */\nimport type * as credentials from '@distilled.cloud/litellm/credential_management';\nimport * as Data from 'effect/Data';\nimport type { HttpClientErrorReason } from 'effect/unstable/http/HttpClientError';\n\n/** A declaration the resource refuses before any request: a combination nobody meant. */\nexport class LitellmCredentialInvalidError extends Data.TaggedError(\n 'LitellmCredentialInvalidError',\n)<{\n readonly credentialName: string;\n readonly problem: string;\n}> {\n override get message(): string {\n return `LiteLLM.Credential \"${this.credentialName}\": ${this.problem}.`;\n }\n}\n\n/**\n * A create's `POST /credentials` or an update's `PATCH /credentials/{name}` failed on the wire (or\n * its answer could not be read), so nobody knows whether the credential is what was asked for.\n *\n * ⛔ THIS IS WHAT STANDS IN FOR AN `HttpClientError`, WHICH CARRIES ITS REQUEST, AND THE REQUEST\n * BODY OF A CREATE OR UPDATE IS `{\"credential_name\": …, \"credential_values\": {\"api_key\": \"…\"}}`.\n * Measured 2026-09-30: on a dropped connection `JSON.stringify(error)`, `Bun.inspect(error)` and\n * Effect's JSON logger all printed the value, as did a failed deploy over it — the same leak\n * key-operations.ts's `LitellmKeyTransportError` closes. A defect reading a broken answer\n * (`protocol-rest.ts` reads it `Effect.orDie`) carries the same request. So `reason` keeps the\n * `HttpClientError` reason's TAG (a string) and nothing else: no request, no cause, no\n * description. The credential NAME stays — it is not secret.\n */\nexport class LitellmCredentialTransportError extends Data.TaggedError(\n 'LitellmCredentialTransportError',\n)<{\n readonly credentialName: string;\n readonly reason: HttpClientErrorReason['_tag'] | 'Defect';\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": a /credentials write failed ` +\n `(${this.reason}) before an answer could be read, so the credential MAY have been written. ` +\n 'This error keeps neither the request nor its cause: the request body holds the credential ' +\n 'values. Look the name up in LiteLLM. A row that landed is taken over by the next deploy ' +\n 'with --adopt; one that did not is created by it.'\n );\n }\n}\n\n/**\n * The SDK prints request and response bodies to stderr while `DISTILLED_DEBUG_HTTP` is set, and a\n * `POST /credentials` carries the values in its body — refused before any request, exactly like\n * `key-secret.ts`'s `refuseDebugLogging` for `/key/generate`.\n */\nexport class LitellmCredentialDebugLoggingError extends Data.TaggedError(\n 'LitellmCredentialDebugLoggingError',\n)<{\n readonly credentialName: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": DISTILLED_DEBUG_HTTP is set, so the SDK ` +\n \"would print the credential values to stderr in /credentials' request and response. Unset \" +\n 'it and run again. Nothing was written.'\n );\n }\n}\n\n/**\n * A write had to send the credential values and the deploying process does not hold one or more\n * of the declared variables. The names are listed, never a value.\n */\nexport class LitellmCredentialEnvUnsetError extends Data.TaggedError(\n 'LitellmCredentialEnvUnsetError',\n)<{\n readonly credentialName: string;\n readonly variables: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": ` +\n `the deploying process does not set ${this.variables.join(', ')}.`\n );\n }\n}\n\n/**\n * A credentials read answered something that is not a row of the credential table (or named a\n * different row), so adopt-by-name cannot trust it. The by-name read is this resource's only\n * read, so a row it cannot recognise is refused rather than guessed at.\n */\nexport class LitellmCredentialUnreadableError extends Data.TaggedError(\n 'LitellmCredentialUnreadableError',\n)<{\n readonly reason: string;\n}> {\n override get message(): string {\n return `LiteLLM.Credential: a credentials read is unreadable (${this.reason}).`;\n }\n}\n\n/**\n * A create or update returned no error, yet the by-name read does not answer the credential — the\n * proxy failed to record it in memory or in the DB. Refused instead of reported as done.\n */\nexport class LitellmCredentialAbsentAfterWriteError extends Data.TaggedError(\n 'LitellmCredentialAbsentAfterWriteError',\n)<{\n readonly credentialName: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": the write returned no error but the ` +\n 'credential is absent from the by-name read.'\n );\n }\n}\n\n/**\n * The write returned no error but the credential still differs from the declaration. The update\n * path this resource uses is a PATCH merge (credential.ts, credential-operations.ts) — or a\n * whole-row rewrite to drop a key — so a residual difference is refused here instead of being\n * reported as converged.\n */\nexport class LitellmCredentialNotConvergedError extends Data.TaggedError(\n 'LitellmCredentialNotConvergedError',\n)<{\n readonly credentialName: string;\n readonly fields: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": after the write it still differs from the ` +\n `declaration in ${this.fields.join(', ')}.`\n );\n }\n}\n\n/** A rewrite deleted the old row before its POST failed. Keep only the failure tag. */\nexport class LitellmCredentialRewriteError extends Data.TaggedError(\n 'LitellmCredentialRewriteError',\n)<{\n readonly credentialName: string;\n readonly reason: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Credential \"${this.credentialName}\": DELETE already ran, but the replacement ` +\n `POST failed (${this.reason}). The next deploy recreates the row if absent; keep all ` +\n 'declared credential variables set. If the POST landed despite the failure, the next ' +\n 'deploy reads and reconciles that row.'\n );\n }\n}\n\nexport type CredentialError =\n | credentials.CreateCredentialCredentialsPostError\n | credentials.GetCredentialByNameCredentialsByNameCredentialNameGetError\n | credentials.UpdateCredentialCredentialsCredentialNamePatchError\n | credentials.DeleteCredentialCredentialsCredentialNameDeleteError\n | LitellmCredentialInvalidError\n | LitellmCredentialTransportError\n | LitellmCredentialRewriteError\n | LitellmCredentialDebugLoggingError\n | LitellmCredentialEnvUnsetError\n | LitellmCredentialUnreadableError\n | LitellmCredentialAbsentAfterWriteError\n | LitellmCredentialNotConvergedError;\n",
|
|
19
|
+
"/**\n * Refusals, wire bodies, the read shape and the comparison for `LiteLLM.Credential`, testable\n * without a server. Value handling is credential-values.ts's.\n *\n * ⛔ WHAT IS COMPARED, WITH THE VENDOR'S MASKING IN MIND. Both reads answer `credential_values`\n * with SENSITIVE-keyed values masked (2 + `****` + 2, `*****` for a short string —\n * `litellm_logging.py::_get_masked_values`, measured in the live 1.103.0 container) and\n * `credential_info` in full:\n * - `credential_info` is the complete intended map, including on adoption. Nonempty PATCH\n * normally replaces DB info (`credential_endpoints/endpoints.py:314-319`, 1.103.0), while\n * memory merges (:385-387). Empty info leaves DB unchanged. Dropping a live info key therefore\n * requires a rewrite to converge both stores (`removedInfoKeys`). Every PATCH sends full info.\n * - the values are compared only through the seal of the values this process RESOLVED\n * (`valuesSeal`, credential-values.ts) — never against the masked fragments, which are\n * partial secrets and are never copied into state.\n * ★ A CHANGED ROW IS PATCHED, NOT REWRITTEN. The vendor's PATCH merges VALUES by key\n * (`update_db_credential`:312), so updating a value or an `info` entry is a PATCH carrying the\n * declared `credential_info` and (only when the values are stale) the resolved\n * `credential_values`. A PATCH that fails on the wire leaves the row it was merging into — the\n * fix for the review's finding 3, where a DELETE + POST rewrite left NO row when the POST failed\n * after the DELETE. PATCH cannot REMOVE a value key or an in-memory info key, so dropping either\n * is still a whole-row rewrite (credential.ts decides). Names from the read prove removal. The\n * PATCH body carries `credential_name` IN THE BODY as a second member (`credential_name_body`,\n * wire-named `credential_name`) because the vendor's `UpdateCredentialItem` requires it and a\n * Smithy member has one binding — the path label can't also be the body field; the distilled\n * patch for it is `patches/credential_management/update_credential_credentials__credential_name__patch.json`.\n */\nimport type * as credentials from '@distilled.cloud/litellm/credential_management';\nimport * as Redacted from 'effect/Redacted';\nimport {\n type CredentialAttributes,\n type CredentialProps,\n isBlank,\n isSensitiveKey,\n} from './credential-types.ts';\nimport type { ResolvedValues } from './credential-values.ts';\n\n/** The first reason a declaration is refused, or `undefined`. Pure: no environment, no server. */\nexport const firstProblem = (props: CredentialProps): string | undefined => {\n if (isBlank(props.credentialName) || props.credentialName !== props.credentialName.trim()) {\n return '`credentialName` must be a non-empty string without leading or trailing whitespace';\n }\n if (\n props.credentialInfo !== undefined &&\n (typeof props.credentialInfo !== 'object' || props.credentialInfo === null)\n ) {\n return '`credentialInfo` must be an object of metadata';\n }\n if (props.credentialInfo !== undefined) {\n for (const key of Object.keys(props.credentialInfo)) {\n if (isSensitiveKey(key)) {\n return (\n `\\`credentialInfo\\` must not declare \"${key}\": LiteLLM returns \\`credential_info\\` ` +\n 'unmasked, so declare it in `credentialValues`, which LiteLLM stores encrypted'\n );\n }\n }\n }\n if (Object.keys(props.credentialValues).length === 0) {\n return (\n '`credentialValues` needs at least one entry: LiteLLM refuses a credential with neither ' +\n '`credential_values` nor a model_id, and `model_id` is not modelled here'\n );\n }\n for (const key of Object.keys(props.credentialValues)) {\n if (isBlank(key) || key !== key.trim()) {\n return '`credentialValues` keys must be non-empty strings without padding';\n }\n const entry = props.credentialValues[key];\n if (typeof entry !== 'object' || entry === null || isBlank(entry.fromEnv)) {\n return `\\`credentialValues[\"${key}\"]\\` takes \\`{ fromEnv: 'NAME' }\\`, never a value: the value would land in Alchemy's unencrypted state`;\n }\n }\n return undefined;\n};\n\n/**\n * Wire names of the fields where the live row and the declaration disagree, for a\n * `NotConvergedError` and for the drift decision. The NAME is identity — a changed name plans a\n * REPLACE in credential.ts and is never compared here. Values are never compared against the\n * live row at all: the seal decides (credential-values.ts).\n */\nexport const differing = (\n live: CredentialAttributes,\n props: CredentialProps,\n): readonly string[] => {\n const out: string[] = [];\n if (props.credentialInfo !== undefined) {\n for (const [key, declared] of Object.entries(props.credentialInfo)) {\n if (!isSensitiveKey(key) && !jsonEquals(live.credentialInfo[key], declared)) {\n out.push(`credential_info.${key}`);\n }\n }\n }\n return out;\n};\n\n/** Canonical-JSON deep equality, order-insensitive for objects. */\nconst jsonEquals = (left: unknown, right: unknown): boolean =>\n JSON.stringify(canonicalise(left)) === JSON.stringify(canonicalise(right));\n\nconst canonicalise = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(canonicalise);\n if (typeof value === 'object' && value !== null) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = canonicalise((value as Record<string, unknown>)[key]);\n }\n return out;\n }\n return value;\n};\n\n/**\n * One row of a credentials read, as attributes. ⛔ COPIES NO `credential_values` ENTRY: a masked\n * fragment (`sk****45`) is a partial secret, and a sensitive key the read masks would land half a\n * secret in Alchemy's state. The `credential_info` mirror drops SENSITIVE-KEYED entries (they are\n * returned unmasked and would mirror a clear secret into state). The digest is the caller's\n * (credential.ts fills `valuesSeal` from state; a row cannot supply one).\n */\nexport const toAttributes = (row: Record<string, unknown>): CredentialAttributes => {\n const info = (row['credential_info'] ?? {}) as Record<string, unknown>;\n const mirrored: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(info)) {\n if (!isSensitiveKey(key)) mirrored[key] = value;\n }\n return {\n credentialInfo: mirrored,\n credentialName: String(row['credential_name'] ?? ''),\n valueKeys: Object.keys((row['credential_values'] ?? {}) as Record<string, unknown>).sort(),\n infoKeys: Object.keys(info).sort(),\n valuesSeal: '',\n };\n};\n\n/** The rows a credentials read carries, `CredentialItem`-shaped. */\nexport const isCredentialRow = (row: unknown): row is Record<string, unknown> =>\n typeof row === 'object' &&\n row !== null &&\n typeof (row as Record<string, unknown>)['credential_name'] === 'string';\n\n/**\n * The literal values a create or a values-stale PATCH sends, unwrapped from their `Redacted`\n * exactly once. ⛔ EVERY DECLARED ENTRY IS SENT: the whole-row create and the values-stale PATCH\n * both demand every declared value (`requireValues`), so there is no partial send that could\n * leave a stale value behind under a declared key.\n */\nexport const literalValues = (resolved: ResolvedValues): Readonly<Record<string, unknown>> =>\n Object.fromEntries(\n Object.entries(resolved.values).map(([key, value]) => [key, Redacted.value(value)]),\n );\n\n/** The body `POST /credentials` carries — the name IN THE BODY (measured with a stub fetch). */\nexport const createBody = (\n props: CredentialProps,\n resolved: ResolvedValues,\n): credentials.CreateCredentialCredentialsPostRequest => {\n const body: Record<string, unknown> = {\n credential_info: { ...props.credentialInfo },\n credential_name: props.credentialName,\n credential_values: { ...literalValues(resolved) },\n };\n return body as unknown as credentials.CreateCredentialCredentialsPostRequest;\n};\n\n/**\n * The body `PATCH /credentials/{name}` carries — the declared `credential_info` always (the\n * vendor's `UpdateCredentialItem` REQUIRES it, and the DB normally replaces the info map), the\n * resolved `credential_values` only when the caller passes them (a values-stale write; an\n * info-only drift sends none, so it never demands a value the environment does not hold).\n *\n * ⛔ `credential_name` APPEARS TWICE, ON PURPOSE: `credential_name` is the path label (its URI\n * placeholder is the row's address), and `credential_name_body` is the SAME value as a body\n * field wire-named `credential_name` — the vendor's `UpdateCredentialItem` requires the name in\n * the body, a Smithy member can carry only ONE binding, and the kit never renames in place (a\n * changed name is a REPLACE, credential.ts), so the two are always equal here.\n */\nexport const patchBody = (\n props: CredentialProps,\n values: Readonly<Record<string, unknown>> | undefined,\n): credentials.UpdateCredentialCredentialsCredentialNamePatchRequest => {\n const body: Record<string, unknown> = {\n credential_info: { ...props.credentialInfo },\n credential_name: props.credentialName,\n credential_name_body: props.credentialName,\n };\n if (values !== undefined) body.credential_values = { ...values };\n return body as unknown as credentials.UpdateCredentialCredentialsCredentialNamePatchRequest;\n};\n\n/**\n * All live info keys absent from the intended map, including on adoption. DB replacement alone\n * cannot clear memory, so these require a rewrite. Only names of sensitive info are remembered.\n */\nexport const removedInfoKeys = (\n prior: CredentialAttributes | undefined,\n props: CredentialProps,\n): readonly string[] => {\n if (prior === undefined) return [];\n const declared = props.credentialInfo ?? {};\n return (prior.infoKeys ?? Object.keys(prior.credentialInfo)).filter(\n (key) => !Object.hasOwn(declared, key),\n );\n};\n\n/** Values merge in BOTH stores, so dropped names from the masked read require a rewrite. */\nexport const removedValueKeys = (\n live: CredentialAttributes | undefined,\n props: CredentialProps,\n): readonly string[] =>\n (live?.valueKeys ?? []).filter((key) => !Object.hasOwn(props.credentialValues, key));\n",
|
|
20
|
+
"/**\n * What `LiteLLM.Credential` declares (props) and what is remembered about it (attributes).\n *\n * ⛔ NO CREDENTIAL VALUE IS EVER A PROP OR AN ATTRIBUTE (S25). Alchemy persists props AND\n * attributes unencrypted (`StateEncoding.ts` writes a `Redacted` value's inner string beside\n * the tag — `secrets/write-only.ts`, measured on alchemy 2.0.0-beta.79), and LiteLLM stores a\n * credential's values ENCRYPTED in its database while the reads answer them MASKED, so a value\n * the declaration holds is `{ fromEnv: 'NAME' }` — NAME lands in state, and the value is read\n * from the DEPLOYING process's environment only when a write is being made\n * (`credential-values.ts`, the same shape `McpServerProps.authValue` uses). Unlike a model\n * deployment's `os.environ/NAME` reference (the PROXY resolves that in its own environment),\n * the credential table stores LITERAL values — measured in the live 1.103.0 container\n * (`credential_endpoints/endpoints.py`: `create_credential` encrypts the body's\n * `credential_values` before the insert, `CredentialsRepository.create`) — so the proxy could\n * not resolve a reference here even if one were sent.\n * ⛔ THE LIVE ROW'S MASKED VALUES ARE NEVER MIRRORED INTO ATTRIBUTES. Both reads answer\n * `credential_values` with every SENSITIVE-keyed value masked (the vendor's own rule,\n * `_get_masked_values` in `litellm_logging.py`: a key matching `authorization`, `token`, `key`,\n * `secret`, `vertex_credentials`, `credentials`, `password` or `passwd` — substring,\n * case-insensitive — answers `v[:2] + \"****\" + v[-2:]` for a string longer than four\n * characters, `*****` otherwise; non-string values and non-sensitive keys answer in full).\n * A masked fragment (`sk****45`) is a PARTIAL SECRET, so `toAttributes` copies no\n * `credential_values` entry at all (`credential-form.ts`); what was last written is remembered\n * as the digest of the values this process RESOLVED, `valuesSeal` — never the values.\n * ⚠️ `credential_info` IS THE COMPLETE INTENDED MAP, INCLUDING ON ADOPTION. Measured in\n * LiteLLM 1.103.0 `credential_endpoints/endpoints.py:314-319`: a nonempty PATCH normally resets\n * the DB info map (except when it contains a literal `credential_info` key); an empty patch\n * leaves it alone. Memory only merges (:385-387), retaining removed keys until restart.\n * Therefore removing any live info key requires a rewrite; every PATCH sends the full map.\n * Sensitive info keys are refused at plan time because the vendor returns info UNMASKED.\n * Reads retain their NAMES only, so removal can converge without persisting their values.\n */\nimport type { FromEnv } from '../secrets/write-only.ts';\n\n/**\n * The credential's name — its identity in the vendor's table (a unique column, enforced by the\n * database, `schema.prisma`'s `Credentials` model). ADOPTED: the live row whose `credential_name`\n * equals it. ⛔ A RENAMED ROW IS A DIFFERENT CREDENTIAL: the name is the only address the vendor's\n * routes have, so a changed `credentialName` plans a REPLACE — the old row survives under the\n * resource's default `retain` policy, never renamed in place.\n */\nexport interface CredentialProps {\n readonly credentialName: string;\n\n /**\n * Free-form metadata stored beside the values (`credential_info` in the vendor's row), compared\n * as the complete intended map with canonical-JSON deep equality (omitted means empty). ⛔ Refused for a key matching the vendor's\n * own sensitive-key list: those belong in `credentialValues`, where LiteLLM stores them\n * encrypted and answers them masked — in `credential_info` they would be stored in clear.\n */\n readonly credentialInfo?: Readonly<Record<string, unknown>>;\n\n /**\n * The credential's values, each declared as `{ fromEnv: 'NAME' }` naming the environment\n * variable the DEPLOYING process reads at call time. AT LEAST ONE is required: LiteLLM's create\n * model refuses a body whose `credential_values` and `model_id` are both empty\n * (`models/credentials.py`'s `CreateCredentialItem` validator → 422), and `model_id` — which\n * would copy a deployment's values — is not modelled here, so a value-less declaration has\n * nothing to write. A value that never resolves in a plan-only environment never drifts\n * (credential-values.ts); one that resolves is SENT as the literal the proxy stores encrypted.\n */\n readonly credentialValues: Readonly<Record<string, FromEnv>>;\n}\n\nexport interface CredentialAttributes {\n readonly credentialName: string;\n\n /**\n * The row's `credential_info` as the read answers it, minus any SENSITIVE-KEYED entry — those\n * would mirror a clear secret into Alchemy's state and are skipped (`toAttributes`). The digest\n * comparison removes undeclared live keys, including on adoption (credential-form.ts).\n */\n readonly credentialInfo: Readonly<Record<string, unknown>>;\n\n /**\n * `scrypt:<salt>:<digest>` of the values the LAST WRITE resolved — the only way a plan notices\n * a rotated credential, because the live row answers masked fragments that can never be\n * compared (`secrets/write-only.ts`). An ADOPTED row starts `''`, which reads as `stale`.\n */\n readonly valuesSeal: string;\n\n /** Names only, never masked fragments. Optional for states written by older providers. */\n readonly valueKeys?: readonly string[];\n\n /** All info key names, including withheld sensitive entries; optional for older states. */\n readonly infoKeys?: readonly string[];\n}\n\n/**\n * Whether a value is not a string with something in it. Takes `unknown`: a declaration can say\n * anything. (Same rule as model-types.ts and registry-support.ts.)\n */\nexport const isBlank = (value: unknown): boolean =>\n typeof value !== 'string' || value.trim() === '';\n\n/**\n * The vendor's OWN sensitive-key list, read from the live 1.103.0 container\n * (`litellm_logging.py::_get_masked_values`): a key containing any of these substrings\n * (case-insensitive) is masked in every `credential_values` read. Credential-form.ts refuses\n * them in `credentialInfo` for the same reason the vendor masks them in values.\n */\nconst SENSITIVE_KEY_FRAGMENTS: readonly string[] = [\n 'authorization',\n 'token',\n 'key',\n 'secret',\n 'vertex_credentials',\n 'credentials',\n 'password',\n 'passwd',\n];\n\n/** Whether a key is one the vendor itself treats as sensitive. */\nexport const isSensitiveKey = (key: string): boolean => {\n const lower = key.toLowerCase();\n return SENSITIVE_KEY_FRAGMENTS.some((fragment) => lower.includes(fragment));\n};\n",
|
|
21
|
+
"/**\n * The `@distilled.cloud/litellm/credential_management` calls `LiteLLM.Credential` uses\n * (LiteLLM 1.103.0; names verified in the generated source, never guessed):\n *\n * read `getCredentialByNameCredentialsByNameCredentialNameGet` GET /credentials/by_name/{name}\n * create `createCredentialCredentialsPost` POST /credentials\n * update `updateCredentialCredentialsCredentialNamePatch` PATCH /credentials/{name}\n * delete `deleteCredentialCredentialsCredentialNameDelete` DELETE /credentials/{name}\n *\n * ★ THE UPDATE IS A PATCH, NOT A REWRITE. The vendor's PATCH merges value keys; nonempty info\n * normally replaces the DB map but merges in memory (`endpoints.py:312-319,384-387`, 1.103.0).\n * The body carries `credential_name` as a second member\n * (`credential_name_body`, wire-named `credential_name`) because the vendor's\n * `UpdateCredentialItem` requires it in the body and a Smithy member has one binding — the\n * distilled patch `patches/credential_management/update_credential_credentials__credential_name__patch.json`.\n * A PATCH that fails on the wire leaves the row it was merging into, where the old DELETE + POST\n * rewrite left NO row when the POST failed after the DELETE (review finding 3). credential.ts\n * still rewrites to drop value or info keys, since PATCH cannot remove them from both stores.\n * ⛔ NO MESSAGE SNIFFING (S21). The read's and delete's `404` is typed as the SDK's\n * `CredentialNotFound` (a distilled patch matched on the vendor's \"Credential not found\" message,\n * `endpoints.py`), so absence is that TAG, `catchTag`ed — never `instanceof`, never error text. A\n * 404 from a front proxy, Access, or a wrong base path decodes as core's `NotFound` and stays an\n * error, never absence. A create on a name the DB already has answers `409` (`endpoints.py`'s\n * unique-violation handling), decoded as core `Conflict`, and is PROPAGATED: a row another writer\n * raced in is not this declaration's row. The PATCH's own 404 (message `Credential not found in\n * DB.`) is NOT `CredentialNotFound`: it stays core `NotFound`, an error — a row deleted between\n * the read and the PATCH must not silently read as absence.\n * ⚠️ THE READ AND THE DELETE ASK DIFFERENT STORES (measured, `endpoints.py`): the by-name read\n * walks the proxy's IN-MEMORY `credential_list` (a row the proxy has not loaded answers 404),\n * while the delete consults the DATABASE (`delete_by_name`) and removes the in-memory entry\n * after. A row present in the DB but not loaded in memory reads as ABSENT, and a create for it\n * answers `409` — the resource reports that failure loudly instead of pretending to converge.\n * ⚠️ Unlike pass-through endpoints, each credential is its own DB row: no semaphore.\n */\nimport * as credentials from '@distilled.cloud/litellm/credential_management';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { isHttpClientError } from 'effect/unstable/http/HttpClientError';\nimport { isCredentialRow, toAttributes } from './credential-form.ts';\nimport type { CredentialAttributes } from './credential-types.ts';\nimport {\n LitellmCredentialDebugLoggingError,\n LitellmCredentialTransportError,\n LitellmCredentialUnreadableError,\n} from './credential-errors.ts';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\n\n/**\n * One credential by name, or `undefined` when the proxy does not hold it in memory. ⛔ ANY FAILURE\n * OTHER THAN `CredentialNotFound` PROPAGATES: a 401, a 422 or a dead network says nothing about\n * whether the row exists, and must never be read as absence.\n */\nexport const readCredential = (\n credentialName: string,\n): Effect.Effect<\n CredentialAttributes | undefined,\n | credentials.GetCredentialByNameCredentialsByNameCredentialNameGetError\n | LitellmCredentialUnreadableError,\n LitellmOpContext\n> =>\n throughFetch(\n credentials.getCredentialByNameCredentialsByNameCredentialNameGet({\n credential_name: credentialName,\n }),\n ).pipe(\n Effect.catchTag('CredentialNotFound', () => Effect.succeed(undefined)),\n Effect.flatMap((response) => {\n if (response === undefined) return Effect.succeed(undefined);\n if (!isCredentialRow(response)) {\n return Effect.fail(\n new LitellmCredentialUnreadableError({\n reason: 'the by-name read did not answer a credential row',\n }),\n );\n }\n const answer = String(response['credential_name']);\n // The name is re-checked on the answer: a proxy that answered some other row must never be\n // adopted as this one's.\n return answer === credentialName\n ? Effect.succeed(toAttributes(response))\n : Effect.fail(\n new LitellmCredentialUnreadableError({\n reason: 'the by-name read answered a different credential name',\n }),\n );\n }),\n );\n\n/**\n * POST /credentials. The answer carries no row (`{\"success\": true, ...}`), so the read back is the\n * proof.\n *\n * ⛔ THE REQUEST BODY CARRIES THE VALUES, AND A FAILED CALL CAN CARRY ITS REQUEST — exactly the\n * `{\"key\":\"sk-…\"}` leak `generateKey` (key-operations.ts) closes, and it is closed the same way:\n * a transport failure is an `HttpClientError` in the error channel, a body that fails mid-read is\n * the SAME error as a DEFECT (`Effect.orDie` in core's `protocol-rest.ts`). Both become\n * `LitellmCredentialTransportError`, which keeps the NAME and the reason's tag and nothing else.\n * ⛔ RECONCILE REFUSES WHILE `DISTILLED_DEBUG_HTTP` IS SET, before any request: the SDK prints\n * the first 400 characters of every request body (core `protocol-http.ts`), which would put the values on\n * stderr — the same refusal `key-secret.ts` makes for `/key/generate`.\n */\nexport const createCredential = (\n body: credentials.CreateCredentialCredentialsPostRequest,\n): Effect.Effect<\n void,\n credentials.CreateCredentialCredentialsPostError | LitellmCredentialTransportError,\n LitellmOpContext\n> => {\n const credentialName = body.credential_name ?? '';\n return Effect.gen(function* () {\n yield* throughFetch(credentials.createCredentialCredentialsPost(body)).pipe(\n Effect.asVoid,\n Effect.catchTag('HttpClientError', (error) =>\n Effect.fail(\n new LitellmCredentialTransportError({ credentialName, reason: error.reason._tag }),\n ),\n ),\n Effect.catchDefect((defect) =>\n carriesRequest(defect)\n ? Effect.fail(\n new LitellmCredentialTransportError({\n credentialName,\n reason: isHttpClientError(defect) ? defect.reason._tag : 'Defect',\n }),\n )\n : Effect.die(defect),\n ),\n );\n });\n};\n\n/**\n * PATCH /credentials/{name}. Sends the full intended `credential_info` (DB replacement, memory\n * merge) and merges resolved `credential_values` when present. The answer carries no row\n * (`{\"success\": true, ...}`), so the read back is the proof.\n *\n * ⛔ THE SAME TWO GUARDS AS `createCredential`: the body carries the values, so a transport failure\n * becomes a `LitellmCredentialTransportError` that keeps the NAME and the reason's tag and\n * nothing else, and reconcile refuses while `DISTILLED_DEBUG_HTTP` is set (the SDK would print\n * the values). A PATCH that fails on the wire leaves the row in place — the point of using it\n * over a DELETE + POST rewrite.\n */\nexport const updateCredential = (\n body: credentials.UpdateCredentialCredentialsCredentialNamePatchRequest,\n): Effect.Effect<\n void,\n credentials.UpdateCredentialCredentialsCredentialNamePatchError | LitellmCredentialTransportError,\n LitellmOpContext\n> => {\n const credentialName = body.credential_name ?? '';\n return Effect.gen(function* () {\n yield* throughFetch(credentials.updateCredentialCredentialsCredentialNamePatch(body)).pipe(\n Effect.asVoid,\n Effect.catchTag('HttpClientError', (error) =>\n Effect.fail(\n new LitellmCredentialTransportError({ credentialName, reason: error.reason._tag }),\n ),\n ),\n Effect.catchDefect((defect) =>\n carriesRequest(defect)\n ? Effect.fail(\n new LitellmCredentialTransportError({\n credentialName,\n reason: isHttpClientError(defect) ? defect.reason._tag : 'Defect',\n }),\n )\n : Effect.die(defect),\n ),\n );\n });\n};\n\n/**\n * DELETE /credentials/{name}, idempotent BY THIS FUNCTION: the vendor answers 404 for a name its\n * database does not have, which this treats as already-absent — the DB is the authoritative store\n * the delete consults (the read consults memory, see the header). Any other failure propagates.\n */\nexport const deleteCredential = (\n credentialName: string,\n): Effect.Effect<\n void,\n credentials.DeleteCredentialCredentialsCredentialNameDeleteError,\n LitellmOpContext\n> =>\n throughFetch(\n credentials.deleteCredentialCredentialsCredentialNameDelete({\n credential_name: credentialName,\n }),\n ).pipe(\n Effect.asVoid,\n Effect.catchTag('CredentialNotFound', () => Effect.void),\n );\n\n/**\n * Whether `DISTILLED_DEBUG_HTTP` is set, refused once at the top of reconcile, before even a\n * DELETE can run (the following POST would carry values). Reads `process.env` because that is\n * what the SDK's `protocol-http.ts` reads.\n */\nexport const refuseDebugLogging = (\n credentialName: string,\n): Effect.Effect<void, LitellmCredentialDebugLoggingError> =>\n (globalThis.process?.env?.['DISTILLED_DEBUG_HTTP'] ?? '') !== ''\n ? Effect.fail(new LitellmCredentialDebugLoggingError({ credentialName }))\n : Effect.void;\n\n/** An `HttpClientError` (or a defect that is one) carries its request — and thus the values. */\nconst carriesRequest = (value: unknown): boolean =>\n isHttpClientError(value) || Predicate.hasProperty(value, 'request');\n",
|
|
22
|
+
"/** Whole-row removal needs a diagnostic that remembers DELETE succeeded before POST failed. */\nimport type { API_ERRORS } from '@distilled.cloud/litellm/Errors';\nimport type * as credentials from '@distilled.cloud/litellm/credential_management';\nimport * as Effect from 'effect/Effect';\nimport {\n LitellmCredentialRewriteError,\n type LitellmCredentialTransportError,\n} from './credential-errors.ts';\nimport { createCredential, deleteCredential } from './credential-operations.ts';\nimport type { LitellmOpContext } from './operations.ts';\n\nexport const rewriteCredential = (body: credentials.CreateCredentialCredentialsPostRequest) =>\n Effect.gen(function* () {\n const credentialName = body.credential_name;\n yield* deleteCredential(credentialName);\n // The SDK's status fallback can emit every API_ERRORS member, including statuses absent\n // from this operation's generated union. Keep only its tag, never a body/message/cause.\n const post: Effect.Effect<\n void,\n | credentials.CreateCredentialCredentialsPostError\n | LitellmCredentialTransportError\n | InstanceType<(typeof API_ERRORS)[number]>,\n LitellmOpContext\n > = createCredential(body);\n yield* post.pipe(\n Effect.catchTag(\n [\n 'BadGateway',\n 'BadRequest',\n 'ConfigError',\n 'Conflict',\n 'Forbidden',\n 'GatewayTimeout',\n 'HttpClientError',\n 'InternalServerError',\n 'LitellmCredentialTransportError',\n 'LitellmParseError',\n 'Locked',\n 'NotFound',\n 'ServiceUnavailable',\n 'TooManyRequests',\n 'Unauthorized',\n 'UnknownLitellmError',\n 'UnprocessableEntity',\n ],\n (error) =>\n Effect.fail(new LitellmCredentialRewriteError({ credentialName, reason: error._tag })),\n ),\n );\n });\n",
|
|
23
|
+
"/**\n * The values of a `LiteLLM.Credential` — resolved from the deploying process's environment at\n * call time, held as `Redacted`, and compared by seal.\n *\n * ★ THIS IS THE CREDENTIAL LITELLM STORES, so the deploying process holds the value: the wire\n * body carries the LITERAL (the proxy encrypts it into its database, measured in the live\n * 1.103.0 container — `credential_endpoints/endpoints.py`'s `encrypt_credential_values`), which\n * is exactly why the prop is only a NAME (`credential-types.ts`). Inside this process the value\n * is a `Redacted<string>`, so a stray log or error dump prints `<redacted>`; it is unwrapped in\n * exactly one place, the wire body (credential-form.ts). Never a prop or an attribute.\n * ⚠️ LITELLM DOES NOT HAND THE VALUES BACK TO COMPARE WITH: both reads answer them MASKED\n * (2 + `****` + 2, `*****` for a short string — `litellm_logging.py::_get_masked_values`), and a\n * masked fragment is a partial secret that must never be compared or stored. So what was last\n * written is remembered as a SEAL, and a plan asks \"are the values in my environment the ones I\n * wrote\". Four answers, mcp-server-credential.ts's own list:\n * - `none` — no values are declared, nothing to compare.\n * - `unknown` — one or more declared variables is unset here, so a plan cannot tell. Never\n * drift: a plan-only environment must not report every credentialed row as\n * changed. A WRITE still demands them (`requireValues`).\n * - `stale` — every variable is set and there is no seal (an adopted row, or one written\n * outside this stack) or the seal disagrees. The declaration is authoritative,\n * so it is written.\n * - `match` — the seal was made from exactly these resolved values.\n * ⚠️ ALL-OR-NOTHING RESOLUTION: a write sends `credential_values` only when EVERY declared\n * variable resolves — the values-stale PATCH and the whole-row create both demand every value\n * (`requireValues`, credential.ts), because sending the resolved half would store a row whose\n * unset half was silently blanked. When any variable is missing the state is `unknown`: an\n * info-only PATCH can proceed, but a required rewrite refuses before DELETE.\n * ⚠️ A SEAL OF A GUESSABLE SECRET IS STILL GUESSABLE (secrets/write-only.ts). Use a random token.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Redacted from 'effect/Redacted';\nimport { type Environment, resolveAll, seal, sealMatches } from '../secrets/write-only.ts';\nimport { LitellmCredentialEnvUnsetError } from './credential-errors.ts';\nimport type { CredentialProps } from './credential-types.ts';\n\n/** What the environment holds for the declared values. `missing` names the unset variables. */\nexport interface ResolvedValues {\n readonly values: Readonly<Record<string, Redacted.Redacted<string>>>;\n readonly missing: readonly string[];\n}\n\nexport type CredentialValuesState = 'none' | 'unknown' | 'stale' | 'match';\n\n/** ⛔ Reads the environment at CALL time, never at module load (docs/credentials.md). */\nexport const resolveValues = (\n props: CredentialProps,\n env: Environment = process.env,\n): ResolvedValues => {\n const { values, missing } = resolveAll(props.credentialValues, env);\n return {\n missing,\n values: Object.fromEntries(\n Object.entries(values).map(([key, value]) => [key, Redacted.make(value)]),\n ),\n };\n};\n\nconst sealable = (values: Readonly<Record<string, Redacted.Redacted<string>>>) =>\n Object.fromEntries(Object.entries(values).map(([key, value]) => [key, Redacted.value(value)]));\n\n/** `scrypt:<salt>:<digest>` of the resolved values, for `valuesSeal`. ⛔ Never a value. */\nexport const sealValues = (values: Readonly<Record<string, Redacted.Redacted<string>>>): string =>\n seal(sealable(values));\n\nexport const valuesState = (\n props: CredentialProps,\n sealed: string,\n resolved: ResolvedValues = resolveValues(props),\n): CredentialValuesState => {\n const declared = Object.keys(props.credentialValues);\n if (declared.length === 0) return 'none';\n if (resolved.missing.length > 0) return 'unknown';\n if (sealed === '') return 'stale';\n return sealMatches(sealed, sealable(resolved.values)) ? 'match' : 'stale';\n};\n\n/**\n * Fails naming the missing variables when this write has to send the values and the deploying\n * process does not hold them all. Creates, rewrites and value-stale PATCHes need every value;\n * an info-only PATCH leaves values alone and does not need this guard (credential.ts).\n */\nexport const requireValues = (\n props: CredentialProps,\n resolved: ResolvedValues,\n): Effect.Effect<void, LitellmCredentialEnvUnsetError> =>\n resolved.missing.length === 0\n ? Effect.void\n : Effect.fail(\n new LitellmCredentialEnvUnsetError({\n credentialName: props.credentialName,\n variables: resolved.missing,\n }),\n );\n",
|
|
24
|
+
"/**\n * Values a declaration may SEND and must never STORE — a webhook secret, a header carrying a\n * credential, an smtp password — and the one form in which a plan may still remember them.\n *\n * ★ FAMILY-NEUTRAL ON PURPOSE, MOVED HERE FROM `proxmox/` 2026-09-22. The rule it encodes is not\n * about Proxmox: Alchemy's state store is the same unencrypted store for every provider, so the\n * Cloudflare MCP server entry's `auth_credentials` needs exactly this shape. A second copy under\n * `cloudflare/` would be two seal formats for one rule; a cross-family import would make\n * Cloudflare depend on Proxmox. Neither; it lives on its own.\n *\n * ⛔ A SECRET PROP IS A SECRET IN THE STATE STORE, WHATEVER TYPE IT HAS. Alchemy persists props as\n * well as attributes — `delete` is handed `olds`, which can only have come from the store — and\n * it does not encrypt either: `StateEncoding.ts` TAGS a `Redacted` value and writes the inner\n * value beside the tag. So `Redacted` hides a secret from a log line and from nothing else.\n * notification-target.ts (PVE) answered that by making no secret declarable at all, and paid\n * for it: an authenticated smtp target or a signed webhook could not be created from code.\n * ★ SO THE PROP HOLDS A NAME, NOT A VALUE. `{ fromEnv: 'PBS_HOOK_TOKEN' }` names the variable the\n * DEPLOYING process reads at call time — the shape forgejo/org-actions-secrets.ts already uses.\n * The name is what lands in state; the value exists only in that process's memory and on the\n * wire to the server.\n *\n * ⚠️ A PLAN STILL NEEDS TO KNOW WHETHER A VALUE CHANGED, AND IT CANNOT ASK THE SERVER. PBS returns a\n * webhook secret's NAME and never its value, and never returns an smtp password at all. So what\n * was last written is remembered as a SEAL: a salted scrypt digest, which lets the next plan\n * check \"is the value in my environment the one I wrote\" without the store holding anything a\n * reader could send to PBS.\n * ⚠️ A DIGEST OF A GUESSABLE SECRET IS STILL GUESSABLE. The salt defeats precomputed tables and\n * scrypt makes each guess cost memory and time; neither makes a dictionary word safe. The\n * remedy is a secret worth the name — a random token — not a faster hash.\n */\nimport { Buffer } from 'node:buffer';\nimport { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';\n\n/** A write-only value, by the NAME of the environment variable holding it. Never the value. */\nexport interface FromEnv {\n readonly fromEnv: string;\n}\n\n/** Where a value is looked up. Defaults to `process.env`, read at call time, never at load. */\nexport type Environment = Readonly<Record<string, string | undefined>>;\n\n/**\n * Each `name → FromEnv` resolved, or the variable names that could not be.\n *\n * ⚠️ AN EMPTY VARIABLE IS A MISSING ONE. A secret renderer that failed writes `NAME=` rather than\n * omitting the line, and sending `''` would store an empty secret on the server — a webhook that\n * authenticates with nothing, reported as a successful deploy.\n * ⚠️ THE VALUE IS NOT TRIMMED. A token with a trailing newline is a broken header either way, and\n * trimming here would make the seal disagree with a value written by any other tool.\n */\nexport const resolveAll = (\n refs: Readonly<Record<string, FromEnv>>,\n env: Environment = process.env,\n): { readonly values: Readonly<Record<string, string>>; readonly missing: readonly string[] } => {\n const values: Record<string, string> = {};\n const missing: string[] = [];\n for (const [key, ref] of Object.entries(refs)) {\n const value = env[ref.fromEnv];\n if (value === undefined || value === '') missing.push(ref.fromEnv);\n else values[key] = value;\n }\n return { missing: [...new Set(missing)].sort(), values };\n};\n\n/**\n * ★ PARAMETERS ARE NODE'S OWN SCRYPT DEFAULTS (N 2^14, r 8, p 1) — 16 MiB and tens of\n * milliseconds per digest. Plan runs it a handful of times per resource, which is noise, and a\n * guesser runs it once per guess, which is the point.\n */\nconst COST = { N: 16_384, maxmem: 64 * 1024 * 1024, p: 1, r: 8 } as const;\nconst PREFIX = 'scrypt';\n\n/** Key order must not change a digest, so entries are sorted and serialised as JSON pairs. */\nconst canonical = (values: Readonly<Record<string, string>>): string =>\n JSON.stringify(\n Object.keys(values)\n .sort()\n .map((key) => [key, values[key]]),\n );\n\nconst digest = (salt: Buffer, values: Readonly<Record<string, string>>): Buffer =>\n scryptSync(canonical(values), salt, 32, COST);\n\n/**\n * `scrypt:<salt>:<digest>`, or `''` when there is nothing to seal.\n *\n * ★ A RANDOM SALT BY DEFAULT, FOR WHAT THIS PROVIDER WROTE. Pass a fixed one only for a value the\n * server returns anyway (a header), where a fresh salt per read would churn the state row on\n * every plan without protecting anything the server does not already show its auditors.\n */\nexport const seal = (values: Readonly<Record<string, string>>, salt?: string): string => {\n if (Object.keys(values).length === 0) return '';\n const bytes = salt === undefined ? randomBytes(16) : Buffer.from(salt, 'utf8');\n return `${PREFIX}:${bytes.toString('base64url')}:${digest(bytes, values).toString('base64url')}`;\n};\n\n/**\n * Whether `values` are the ones `sealed` was made from.\n *\n * ⚠️ A MALFORMED OR EMPTY SEAL ANSWERS FALSE, never throws: callers decide what \"no evidence\"\n * means, and every caller here treats it as \"cannot tell\" rather than as drift.\n */\nexport const sealMatches = (sealed: string, values: Readonly<Record<string, string>>): boolean => {\n if (sealed === '') return Object.keys(values).length === 0;\n const [prefix, salt, stored, extra] = sealed.split(':');\n if (prefix !== PREFIX || salt === undefined || stored === undefined || extra !== undefined) {\n return false;\n }\n const expected = Buffer.from(stored, 'base64url');\n const actual = digest(Buffer.from(salt, 'base64url'), values);\n return expected.length === actual.length && timingSafeEqual(expected, actual);\n};\n",
|
|
25
|
+
"/**\n * `LiteLLM.MCPServer` — one row of LiteLLM's `LiteLLM_MCPServerTable` (`/v1/mcp/server`), an\n * upstream MCP server the proxy's gateway exposes to keys and teams.\n *\n * ★ ADOPT BY NAME. `serverName` finds the live row whose `server_name` matches; declare `serverId`\n * to pin one instead. A live row with no state is `Unowned`, so it needs `--adopt`. Two rows with\n * the same name are refused, never guessed at (`LitellmMcpServerAmbiguousNameError`).\n * ★ `defaultRemovalPolicy: 'retain'` — a server's removal takes its tools from every key, team and\n * seat that reaches it. Opt in with `.pipe(RemovalPolicy.destroy())`.\n * ⛔ THE CREDENTIAL IS `{ fromEnv: 'NAME' }`, NEVER A VALUE (S25): mcp-server-types.ts says why a\n * `Redacted` prop would still be stored in plaintext. Reading a server never reveals auth material\n * either — `toAttributes` copies no credential — and a rotated credential is noticed through a\n * seal (mcp-server-credential.ts).\n * ⛔ NO LITELLM CREDENTIAL IS A PROP either: `litellmProviders`' layer supplies `Credentials`.\n * ⛔ THE LIST IS LITELLM'S IN-MEMORY REGISTRY, NOT THE TABLE (mcp-server-operations.ts): a committed row\n * can be missing from it. So a row the list lacks is read by id from the table (`findLive`), the read\n * back after a write falls back to the same read, and a delete always sends its DELETE.\n * ⛔ AN UPDATE NEVER LETS LITELLM DEFAULT THE ALIAS (mcp-server-form.ts): the alias is the tool prefix.\n * ⚠️ A `description` that the row's `mcp_info` would hide is refused before any write (mcp-server-shadow.ts).\n * ⚠️ UNMEASURED AT 1.103.0, each guarded by a read back: whether a create honours a supplied\n * `server_id`, and whether an empty list or a `false` lands on an edit (that the edit is a partial\n * update is read from the source, `mcp-server-form.ts`). Nothing here assumes them; the returned\n * row is what is recorded.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n LitellmMcpServerAbsentAfterWriteError,\n LitellmMcpServerInvalidError,\n LitellmMcpServerNotConvergedError,\n} from './mcp-server-errors.ts';\nimport {\n credentialState,\n requireCredential,\n resolveCredential,\n sealCredential,\n} from './mcp-server-credential.ts';\nimport {\n createBody,\n differing,\n firstProblem,\n toAttributes,\n updateBody,\n} from './mcp-server-form.ts';\nimport { findLive, wantedId } from './mcp-server-locate.ts';\nimport {\n createMcpServer,\n deleteMcpServer,\n listMcpServers,\n readMcpServer,\n updateMcpServer,\n} from './mcp-server-operations.ts';\nimport { refuseShadowedDescription } from './mcp-server-shadow.ts';\nimport {\n type McpServerAttributes,\n type McpServerProps,\n isStaticAuthType,\n} from './mcp-server-types.ts';\n\nexport type { McpServerAttributes, McpServerProps };\nexport type { McpServerError } from './mcp-server-errors.ts';\n\nexport interface LiteLLMMCPServer extends Resource<\n 'LiteLLM.MCPServer',\n McpServerProps,\n McpServerAttributes\n> {}\n\nexport const LiteLLMMCPServer = Resource<LiteLLMMCPServer>('LiteLLM.MCPServer', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMMCPServer = (value: unknown): value is LiteLLMMCPServer =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.MCPServer';\n\nconst refuse = (props: McpServerProps) => {\n const problem = firstProblem(props);\n return problem === undefined\n ? Effect.void\n : Effect.fail(\n new LitellmMcpServerInvalidError({ problem, serverName: String(props.serverName) }),\n );\n};\n\ntype Args<P> = {\n id: string;\n instanceId: string;\n output: McpServerAttributes | undefined;\n} & P;\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const mcpServerHandlers = {\n /**\n * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW SERVER\n * GETS — so the refusals run here too. Measured on beta.79 (`Plan.ts` lines 1303-1316, the\n * `oldState === undefined` branch): a declaration with no state row is `read` with `olds: news`\n * and never `diff`ed, and Apply then commits its props as `creating` BEFORE `reconcile` runs. A\n * URL that carries a token would already be in the unencrypted store when reconcile refused it.\n * ★ A DEFECT (`Effect.die`), NOT A TYPED FAILURE, and that is what keeps it from wedging a stage:\n * the same call shape is Alchemy's recovery read of an interrupted create (`Plan.ts` lines\n * 1428-1447), with the STORED props, and only a defect is caught there (`Effect.catchDefect`\n * degrades it to \"nothing recovered\"). A typed failure would block the plan on props that a\n * later, stricter release now refuses. The cold-start probe has no such catch, so there the\n * defect fails the plan, as intended. `pbs-notification-target-lifecycle.ts` reasons the same.\n */\n read: ({ id, instanceId, olds, output }: Args<{ olds: McpServerProps }>) =>\n Effect.gen(function* () {\n const problem = output === undefined ? firstProblem(olds) : undefined;\n if (problem !== undefined) {\n return yield* Effect.die(\n new LitellmMcpServerInvalidError({ problem, serverName: String(olds.serverName) }),\n );\n }\n // ★ THE LIST, THEN THE TABLE BY ID: a row the registry lacks is still a live row\n // (mcp-server-locate.ts). `instanceId` is what recovery of an interrupted create needs to\n // rebuild the deterministic id that create asked for.\n const found = yield* findLive(id, instanceId, olds, output);\n if (found === undefined) return undefined;\n const live = toAttributes(found);\n return output === undefined\n ? Unowned(live)\n : { ...live, credentialSeal: output.credentialSeal };\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<McpServerProps>;\n output: McpServerAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuse(news);\n if (output === undefined) return undefined;\n // The id is identity: a different declared id is a different row.\n if (news.serverId !== undefined && news.serverId !== output.serverId) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n return differing(output, news).length === 0 &&\n credentialState(news, output.credentialSeal) !== 'stale'\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ id, instanceId, news, output }: Args<{ news: McpServerProps }>) =>\n Effect.gen(function* () {\n yield* refuse(news);\n const before = yield* findLive(id, instanceId, news, output);\n const credential = resolveCredential(news);\n let sealed = output?.credentialSeal ?? '';\n let serverId: string;\n\n if (before === undefined) {\n // A static credential is part of the create: without it the row would exist unusable.\n yield* requireCredential(news, credential);\n const wanted = yield* wantedId(id, instanceId, news, output);\n const created = yield* createMcpServer(createBody(news, wanted, credential.value));\n // ★ THE ROW LITELLM ANSWERS IS THE TRUTH: a proxy that ignores a supplied `server_id` is\n // recorded under the id it actually used, not the one that was asked for.\n serverId = typeof created?.server_id === 'string' ? created.server_id : wanted;\n sealed = credential.value === undefined ? '' : sealCredential(credential.value);\n } else {\n serverId = before.server_id;\n const live = toAttributes(before);\n const state = credentialState(news, sealed, credential);\n if (differing(live, news).length > 0 || state === 'stale') {\n // ⛔ A CHANGED AUTH TYPE DEMANDS THE CREDENTIAL, before anything is written: LiteLLM wipes\n // the stored one on an auth-class change that sends none (mcp-server-credential.ts).\n if (live.authType !== news.authType) yield* requireCredential(news, credential);\n // ⛔ Same rule for a description `mcp_info` would hide: before anything is written.\n if (differing(live, news).includes('description')) {\n yield* refuseShadowedDescription(news, serverId);\n }\n // ★ Otherwise sent whenever the process has it, demanded only when the seal says stale.\n const send = isStaticAuthType(news.authType) ? credential.value : undefined;\n const leavingStatic =\n !isStaticAuthType(news.authType) && (sealed !== '' || isStaticAuthType(live.authType));\n yield* updateMcpServer(updateBody(news, serverId, send, leavingStatic, live));\n if (send !== undefined) sealed = sealCredential(send);\n else if (leavingStatic) sealed = '';\n }\n }\n\n // ★ THE LIST FIRST, THEN THE TABLE: after a create the registry refresh can fail (LiteLLM only\n // logs it), and a committed row that the list does not show yet is \"created, not yet visible\",\n // never absent. The by-id read also registers it, which heals the list.\n const found =\n (yield* listMcpServers()).find((row) => row.server_id === serverId) ??\n (yield* readMcpServer(serverId));\n if (found === undefined) {\n return yield* Effect.fail(\n new LitellmMcpServerAbsentAfterWriteError({ serverId, serverName: news.serverName }),\n );\n }\n const after = toAttributes(found);\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmMcpServerNotConvergedError({\n fields: left,\n serverId,\n serverName: news.serverName,\n }),\n );\n }\n return { ...after, credentialSeal: sealed };\n }),\n\n delete: ({ output }: { output: McpServerAttributes }) => deleteMcpServer(output.serverId),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMMCPServerProvider = () =>\n Provider.effect(\n LiteLLMMCPServer,\n Effect.succeed(LiteLLMMCPServer.Provider.of(mcpServerHandlers)),\n );\n",
|
|
26
|
+
"/**\n * The typed refusals `LiteLLM.MCPServer` raises on top of `@distilled.cloud/litellm`'s own SDK errors.\n *\n * ⛔ NO MESSAGE HERE MAY CARRY A CREDENTIAL. The only things named are the server's name, ids the\n * proxy already lists, field names, and the NAME of an environment variable — never a value, and\n * never a URL (a URL can hold a token; `mcp-server-form.ts` refuses those and redacts the read).\n */\nimport type * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport * as Data from 'effect/Data';\n\n/** A declaration the resource refuses before any request: a combination nobody meant. */\nexport class LitellmMcpServerInvalidError extends Data.TaggedError('LitellmMcpServerInvalidError')<{\n readonly serverName: string;\n readonly problem: string;\n}> {\n override get message(): string {\n return `LiteLLM.MCPServer \"${this.serverName}\": ${this.problem}.`;\n }\n}\n\n/**\n * A write must send the credential and the deploying process does not have it. An empty variable\n * counts as unset (secrets/write-only.ts: a failed renderer writes `NAME=`, and sending `''` would\n * store an empty credential and report a successful deploy).\n */\nexport class LitellmMcpServerCredentialEnvUnsetError extends Data.TaggedError(\n 'LitellmMcpServerCredentialEnvUnsetError',\n)<{\n readonly serverName: string;\n readonly variable: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.MCPServer \"${this.serverName}\": this write must send its credential, and ` +\n `${this.variable} is unset or empty in the deploying environment. Export it and deploy again.`\n );\n }\n}\n\n/**\n * More than one live row carries the declared `server_name`, so adopt-by-name cannot say which one\n * is meant. LiteLLM does not make the name unique (unmeasured on 1.103.0). Declare `serverId` to\n * pick one — nothing is adopted, updated or deleted on a guess.\n */\nexport class LitellmMcpServerAmbiguousNameError extends Data.TaggedError(\n 'LitellmMcpServerAmbiguousNameError',\n)<{\n readonly serverName: string;\n readonly serverIds: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.MCPServer \"${this.serverName}\": ${this.serverIds.length} live rows carry that ` +\n `server_name (${this.serverIds.join(', ')}). Declare \\`serverId\\` to choose one.`\n );\n }\n}\n\n/** `GET /v1/mcp/server` answered something that is not a list of servers. */\nexport class LitellmMcpServerUnreadableError extends Data.TaggedError(\n 'LitellmMcpServerUnreadableError',\n)<{\n readonly reason: string;\n}> {\n override get message(): string {\n return `LiteLLM.MCPServer: the server list is unreadable (${this.reason}).`;\n }\n}\n\n/**\n * A create or edit returned no error, yet the row is absent on the read back: neither in the list\n * NOR in a database-backed read by id (the list alone is the registry, which can lag a commit).\n */\nexport class LitellmMcpServerAbsentAfterWriteError extends Data.TaggedError(\n 'LitellmMcpServerAbsentAfterWriteError',\n)<{\n readonly serverName: string;\n readonly serverId: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.MCPServer \"${this.serverName}\": the write of ${this.serverId} returned no error ` +\n 'but the row is absent from the list and from the database read.'\n );\n }\n}\n\n/**\n * The write returned no error but the row still differs from the declaration. The edit route may\n * merge with `exclude_none` or a truthiness check (an empty list or a `false` never lands); the\n * read back says so instead of reporting a converged row. Unmeasured on 1.103.0.\n */\nexport class LitellmMcpServerNotConvergedError extends Data.TaggedError(\n 'LitellmMcpServerNotConvergedError',\n)<{\n readonly serverName: string;\n readonly serverId: string;\n readonly fields: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.MCPServer \"${this.serverName}\": after the write, ${this.serverId} still differs ` +\n `from the declaration in ${this.fields.join(', ')}.`\n );\n }\n}\n\n/**\n * The declared `description` cannot be applied: the live row's `mcp_info` has a `description` key\n * (even a null one), and LiteLLM answers that in preference to the column, which is all this\n * resource writes (`mcp_info` is not modelled). Raised BEFORE any write, so the row is untouched.\n * Nothing here names the text itself: a description is free text a person wrote.\n */\nexport class LitellmMcpServerDescriptionShadowedError extends Data.TaggedError(\n 'LitellmMcpServerDescriptionShadowedError',\n)<{\n readonly serverName: string;\n readonly serverId: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.MCPServer \"${this.serverName}\": ${this.serverId} has a \\`description\\` in \\`mcp_info\\`, ` +\n 'which LiteLLM shows instead of the column this resource writes, and it differs from the ' +\n 'declaration. Edit it in LiteLLM, or remove `description` from the declaration.'\n );\n }\n}\n\nexport type McpServerError =\n | mcp.AddMcpServerV1McpServerPostError\n | mcp.EditMcpServerV1McpServerPutError\n | mcp.FetchAllMcpServersV1McpServerGetError\n | mcp.FetchMcpServerV1McpServerServerIdGetError\n | mcp.RemoveMcpServerV1McpServerServerIdDeleteError\n | LitellmMcpServerInvalidError\n | LitellmMcpServerCredentialEnvUnsetError\n | LitellmMcpServerAmbiguousNameError\n | LitellmMcpServerUnreadableError\n | LitellmMcpServerAbsentAfterWriteError\n | LitellmMcpServerNotConvergedError\n | LitellmMcpServerDescriptionShadowedError;\n",
|
|
27
|
+
"/**\n * The credential of a `LiteLLM.MCPServer`: resolved from the deploying process's environment,\n * held as `Redacted`, sealed, and compared by seal.\n *\n * ★ `Redacted` IS THE IN-MEMORY WRAPPER, NOT THE STORAGE FORMAT. Inside this process the value is a\n * `Redacted<string>`, so a stray `console.log`, `JSON.stringify` or error dump prints\n * `<redacted>`. It is unwrapped in exactly one place: the wire body (`mcp-server-form.ts`). It is\n * never a prop or an attribute — see mcp-server-types.ts for why `Redacted` cannot be either.\n * ⚠️ LITELLM DOES NOT HAND A CREDENTIAL BACK TO COMPARE WITH (UNMEASURED at 1.103.0; the resource\n * never relies on it). So what was last written is remembered as a SEAL, and a plan asks \"is the\n * value in my environment the one I wrote\". Four answers:\n * - `none` — no credential is declared, nothing to compare.\n * - `unknown` — the variable is unset here, so a plan cannot tell. Never drift: a plan-only\n * environment must not report every credentialed server as changed. (A write that\n * CHANGES `auth_type` is the exception: it demands the value, `requireCredential`.)\n * - `stale` — the variable is set and there is no seal (an adopted row, or one written outside\n * this stack) or the seal disagrees. The declaration is authoritative, so it is written.\n * - `match` — the seal was made from this value.\n * ⚠️ A SEAL OF A GUESSABLE SECRET IS STILL GUESSABLE (secrets/write-only.ts). Use a random token.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Redacted from 'effect/Redacted';\nimport { type Environment, resolveAll, seal, sealMatches } from '../secrets/write-only.ts';\nimport { LitellmMcpServerCredentialEnvUnsetError } from './mcp-server-errors.ts';\nimport { type McpServerProps, isStaticAuthType } from './mcp-server-types.ts';\n\n/** What the environment holds for the declared credential. `value` is undefined when unset or empty. */\nexport interface ResolvedCredential {\n /** The variable's NAME, for a message. `undefined` when no credential is declared. */\n readonly variable: string | undefined;\n readonly value: Redacted.Redacted<string> | undefined;\n}\n\nexport type CredentialState = 'none' | 'unknown' | 'stale' | 'match';\n\n/** ⛔ Reads the environment at CALL time, never at module load (docs/credentials.md). */\nexport const resolveCredential = (\n props: McpServerProps,\n env: Environment = process.env,\n): ResolvedCredential => {\n if (props.authValue === undefined) return { value: undefined, variable: undefined };\n const { values } = resolveAll({ authValue: props.authValue }, env);\n const raw = values['authValue'];\n return {\n value: raw === undefined ? undefined : Redacted.make(raw),\n variable: props.authValue.fromEnv,\n };\n};\n\nconst sealable = (value: Redacted.Redacted<string>) => ({ authValue: Redacted.value(value) });\n\n/** `scrypt:<salt>:<digest>` of a credential, for `credentialSeal`. ⛔ Never the value. */\nexport const sealCredential = (value: Redacted.Redacted<string>): string => seal(sealable(value));\n\nexport const credentialState = (\n props: McpServerProps,\n sealed: string,\n resolved: ResolvedCredential = resolveCredential(props),\n): CredentialState => {\n if (props.authValue === undefined || !isStaticAuthType(props.authType)) return 'none';\n if (resolved.value === undefined) return 'unknown';\n if (sealed === '') return 'stale';\n return sealMatches(sealed, sealable(resolved.value)) ? 'match' : 'stale';\n};\n\n/**\n * Fails naming the variable when this write has to send a static credential and the deploying\n * process does not hold it. Passes for a type that carries none.\n *\n * ⛔ A WRITE THAT CHANGES `auth_type` MUST SEND THE CREDENTIAL, exactly like a create. Measured on\n * the live 1.103.0 container (`mcp_server/db.py` lines 1013-1014, `_credential_auth_class`): an\n * edit whose auth class differs from the stored one and that sends no `credentials` key WIPES the\n * stored credential, and each static type is its own class. Sent without the credential, the row\n * would end with none while the seal still matched the old value, so a later deploy with the\n * variable set would be a no-op and the server would stay unauthenticated until the secret rotated.\n */\nexport const requireCredential = (props: McpServerProps, credential: ResolvedCredential) =>\n isStaticAuthType(props.authType) && credential.value === undefined\n ? Effect.fail(\n new LitellmMcpServerCredentialEnvUnsetError({\n serverName: props.serverName,\n variable: credential.variable ?? '(undeclared)',\n }),\n )\n : Effect.void;\n",
|
|
28
|
+
"/**\n * What a `LiteLLM.MCPServer` declares (props) and what is remembered about it (attributes).\n *\n * ⛔ NEITHER TYPE HOLDS A CREDENTIAL (S25). Alchemy persists props AND attributes in its state\n * store, and it does not encrypt them: `StateEncoding.ts` TAGS a `Redacted` value and writes the\n * inner value beside the tag (measured on alchemy 2.0.0-beta.79, `State/StateEncoding.ts` line\n * 87-89). So a `Redacted<string>` prop would still land in state as plaintext. The credential is\n * declared as `{ fromEnv: 'NAME' }`; the NAME is what is stored, and the value lives only in the\n * deploying process (wrapped in `Redacted` there, `mcp-server-credential.ts`) and on the wire.\n * ★ WHAT LITELLM STORES IS NEVER MIRRORED BACK. The list route carries `credentials`,\n * `static_headers`, `env` and `env_vars`; `toAttributes` (mcp-server-form.ts) copies none of them.\n * ⚠️ NOT MODELLED, on purpose: `stdio` (it runs a command on the proxy host), the OAuth client\n * registration fields, `static_headers`/`extra_headers`, `mcp_info`, tool renames, BYOK and\n * the AWS / token-exchange auth types (each needs credential fields this resource does not\n * model). A row that uses them can still be adopted; what is not declared is left alone.\n * ⚠️ NO `teams`. The create and edit requests of `@distilled.cloud/litellm` 0.3.0 (LiteLLM 1.103.0)\n * have no team field; `teams` exists only on the row a read returns. Team access is granted on\n * the team side (its `object_permission`), which is a different resource.\n */\nimport type { FromEnv } from '../secrets/write-only.ts';\n\n/** `stdio` is refused by the type: it would run a declared command on the proxy host. */\nexport type McpTransport = 'sse' | 'http';\n\n/**\n * Auth types that carry ONE static credential (`credentials.auth_value` on the wire). Which header\n * each one becomes is LiteLLM's, not this package's (unmeasured here).\n */\nexport type McpStaticAuthType = 'api_key' | 'bearer_token' | 'basic' | 'authorization' | 'token';\n\n/** `oauth2` is a per-user flow LiteLLM runs itself: no static credential is declared for it. */\nexport type McpAuthType = 'none' | 'oauth2' | McpStaticAuthType;\n\nexport interface McpServerProps {\n /**\n * The server's name, and the way an existing row is ADOPTED: the live row whose `server_name`\n * equals it. Changing it is an update of the same row, never a replace. The update re-sends the\n * live alias, so the tool prefix stays. Declare `alias` to change the prefix.\n */\n readonly serverName: string;\n /**\n * Pins the live row (adopts by id) or chooses the id a create asks for. Without it, a live row\n * is found by `serverName` and a new one gets a deterministic physical name. ⛔ A DIFFERENT\n * DECLARED ID IS A REPLACE (create-first, so the old row is only removed by an opt-in\n * `RemovalPolicy.destroy()`).\n */\n readonly serverId?: string;\n /**\n * The tool prefix LiteLLM shows (`<alias>-<tool>`). Compared only when declared, and an\n * undeclared live alias is KEPT: an update that sends a `serverName` with no alias would make\n * LiteLLM rewrite the alias to the name, so the live one is re-sent (mcp-server-form.ts).\n * Declare this to change the prefix; a changed `serverName` does not. ⛔ Must match\n * `^[A-Za-z0-9._]{1,128}$` (same rule as `serverName`): LiteLLM 1.103 runs `validate_tool_name`\n * on it, so a space, `/`, `:`, `@` or more than 128 characters is a 400 only at apply.\n */\n readonly alias?: string;\n /**\n * The upstream MCP endpoint, `http` or `https`. ⛔ Refused if it carries userinfo, a fragment or\n * a secret-looking query parameter (`?token=…`): the URL is a prop, so it lands in state. Declare\n * the credential as `authValue` instead.\n */\n readonly url: string;\n /** `sse` or `http`. Required: LiteLLM's own default is not this resource's to fix silently. */\n readonly transport: McpTransport;\n /** Required: a default would silently choose \"no authentication\". */\n readonly authType: McpAuthType;\n /**\n * The static credential for a `McpStaticAuthType`, by the NAME of the environment variable that\n * holds it. Required for those types and refused for the others.\n */\n readonly authValue?: FromEnv;\n /**\n * Compared only when declared. ⚠️ A blank one (`''` or whitespace) is refused: LiteLLM copies the\n * column into `mcp_info` only when the value is truthy, so it would never converge. LiteLLM's\n * list answers `mcp_info.description` in preference to this column, and this resource writes only\n * the column. A row whose `mcp_info` has a `description` key (even a null one) that differs from\n * the declaration is refused (`LitellmMcpServerDescriptionShadowedError`) rather than written and\n * failed on the read back.\n */\n readonly description?: string;\n /**\n * Whether EVERY virtual key may call this server. Always compared, default `false`: it is an\n * access grant, so an adopted row that has it on is corrected, never left open silently.\n */\n readonly allowAllKeys?: boolean;\n /**\n * The tools a caller may use, compared as a set. ⛔ COMPARED AND SENT ONLY WHEN DECLARED: omitted\n * leaves the live whitelist exactly as it is. ⚠️ `[]` is NOT \"closed\" — at 1.103.0 an empty list\n * turns the whitelist off (every tool is returned) unless the row's `mcp_info` carries the enforce\n * flag, which this resource does not model. Declare `[]` only to mean \"no restriction\".\n */\n readonly allowedTools?: readonly string[];\n /** Access groups this server belongs to. Always compared (as a set). */\n readonly mcpAccessGroups?: readonly string[];\n}\n\nexport interface McpServerAttributes {\n readonly serverId: string;\n readonly serverName: string | null;\n readonly alias: string | null;\n /** ⛔ REDACTED on the way in (userinfo and secret-looking query values become `REDACTED`). */\n readonly url: string | null;\n readonly transport: string;\n /** `none` when the live row carries no auth type. */\n readonly authType: string;\n readonly description: string | null;\n readonly allowAllKeys: boolean;\n readonly allowedTools: readonly string[];\n readonly mcpAccessGroups: readonly string[];\n /**\n * `scrypt:<salt>:<digest>` of the credential last written, or `''`. ⛔ Never the value. LiteLLM\n * does not return a credential to compare with, so this is how a plan notices a rotated one\n * (secrets/write-only.ts). It is carried from the previous state, never read from the row.\n */\n readonly credentialSeal: string;\n}\n\n/** Whether a value is not a string with something in it. Takes `unknown`: a declaration can say anything. */\nexport const isBlank = (value: unknown): boolean =>\n typeof value !== 'string' || value.trim() === '';\n\nconst STATIC: ReadonlySet<string> = new Set<McpStaticAuthType>([\n 'api_key',\n 'bearer_token',\n 'basic',\n 'authorization',\n 'token',\n]);\n\n/** Whether an auth type carries one static credential. Takes a string: a live row can say anything. */\nexport const isStaticAuthType = (authType: string): boolean => STATIC.has(authType);\n\nexport const AUTH_TYPES: readonly McpAuthType[] = [\n 'none',\n 'oauth2',\n 'api_key',\n 'bearer_token',\n 'basic',\n 'authorization',\n 'token',\n];\n\nexport const TRANSPORTS: readonly McpTransport[] = ['sse', 'http'];\n",
|
|
29
|
+
"/**\n * Refusals, wire bodies, the read shape and the comparison for `LiteLLM.MCPServer`, testable\n * without a server. The URL rules are in `mcp-server-url.ts`.\n *\n * ⛔ TWO ACCESS GRANTS ARE ALWAYS COMPARED, WITH A CLOSED DEFAULT. `allow_all_keys` (default\n * false) and `mcp_access_groups` (default empty) decide which keys can reach a server, so a live\n * value that differs from the declaration is corrected rather than left as it is.\n * ⛔ `allowed_tools` IS NOT ONE OF THEM: it is compared and sent ONLY WHEN DECLARED, like `alias`.\n * An empty list is the OPEN state, not a closed one. Measured on the live 1.103.0 container:\n * `server_applies_tool_allowlist` (`mcp_server/utils.py`) is \"the `mcp_info` enforce flag, or a\n * non-empty `allowed_tools`\", and `filter_tools_by_allowed_tools` (`server.py` lines 1692-1698)\n * returns every tool when it is false. This resource does not model `mcp_info`, so a default of\n * `[]` would switch a live whitelist OFF the moment a row is adopted without restating it, and an\n * adopted row is planned `adopted` (Alchemy's adoption branch), so no `update` would show. An\n * undeclared list is left exactly as the proxy holds it.\n * ★ Descriptive fields (`alias`, `description`) are also compared only when declared, so adopting a\n * row never clears text a person wrote. That holds on the wire too, for `alias` only because an\n * update RE-SENDS the live alias whenever it sends a `serverName` (`identityOnUpdate` below).\n * ⚠️ EVERY UPDATE SENDS THE FULL MANAGED SET, NOT ONLY THE FIELDS THAT DIFFER, bar the two name\n * fields, which `identityOnUpdate` sends selectively because LiteLLM defaults an alias. The edit\n * route is a PARTIAL update at 1.103.0 (measured by reading the live container's\n * `mcp_server/db.py`, the update function's `exclude_unset=True`: fields the caller did not send\n * keep their stored value; no live call was made), so a field this resource does not send is left\n * alone. Sending the whole managed set is still correct, and reconcile reads back and fails loudly\n * if a declared field did not land (`LitellmMcpServerNotConvergedError`). Whether a `false` or an\n * empty list is written rather than skipped stays UNMEASURED, which is what the read back guards.\n */\nimport type * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport * as Redacted from 'effect/Redacted';\nimport { redactUrl, urlProblem } from './mcp-server-url.ts';\nimport {\n AUTH_TYPES,\n type McpServerAttributes,\n type McpServerProps,\n TRANSPORTS,\n isBlank,\n isStaticAuthType,\n} from './mcp-server-types.ts';\n\nconst listProblem = (name: string, list: readonly unknown[] | undefined): string | undefined =>\n list !== undefined && list.some((entry) => isBlank(entry))\n ? `\\`${name}\\` must hold non-empty strings`\n : undefined;\n\n/**\n * ⛔ LITELLM 1.103 RUNS THE MCP SDK'S `validate_tool_name` ON `server_name` AND THE NORMALISED\n * ALIAS, and that 400 happens only at apply. The names it accepts are `^[A-Za-z0-9._]{1,128}$`:\n * a space, `/`, `:`, `@`, or more than 128 characters is refused here so nothing is written to\n * Alchemy's state first. `-` is out for the same reason and one more: it is\n * `MCP_TOOL_PREFIX_SEPARATOR` (default `-`, unset in the live container as measured 2026-09-29),\n * and a tool is `<alias>-<tool>`. A proxy that set another separator would be over-refused:\n * rare, and loud. `normalize_server_name` would also rewrite a space to `_`, so a name outside\n * this pattern would never equal the row it produced.\n */\nconst TOOL_NAME = /^[A-Za-z0-9._]{1,128}$/;\n\nconst toolNameProblem = (field: string, value: string): string | undefined =>\n TOOL_NAME.test(value)\n ? undefined\n : `\\`${field}\\` must match ^[A-Za-z0-9._]{1,128}$: LiteLLM 1.103 rejects a space, \"/\", \":\", \"@\", \"-\" or more than 128 characters only at apply`;\n\n/** A declared alias is a tool name too. Omitted is fine: the live alias is kept. */\nconst aliasProblem = (alias: string | undefined): string | undefined => {\n if (alias === undefined) return undefined;\n if (isBlank(alias)) return '`alias` must be a non-empty string when declared';\n return toolNameProblem('alias', alias);\n};\n\n/** The first reason a declaration is refused, or `undefined`. Pure: no environment, no server. */\nexport const firstProblem = (props: McpServerProps): string | undefined => {\n if (isBlank(props.serverName) || props.serverName !== props.serverName.trim()) {\n return '`serverName` must be a non-empty string without leading or trailing whitespace';\n }\n const named = toolNameProblem('serverName', props.serverName) ?? aliasProblem(props.alias);\n if (named !== undefined) return named;\n // ⛔ A blank description never converges. LiteLLM copies the column into `mcp_info` only when\n // the value is truthy (`build_mcp_server_from_table`), and the list then answers\n // `mcp_info.description`. `''` is written and read back as absent, so every deploy would fail\n // the read back. Omitted is still \"leave the live text alone\".\n if (props.description !== undefined && isBlank(props.description)) {\n 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';\n }\n if (props.serverId !== undefined && isBlank(props.serverId)) {\n return '`serverId` must be a non-empty string when declared';\n }\n const url = urlProblem(props.url);\n if (url !== undefined) return url;\n if (!TRANSPORTS.includes(props.transport)) {\n return '`transport` must be \"sse\" or \"http\" (`stdio` runs a command on the proxy host and is not modelled)';\n }\n if (!AUTH_TYPES.includes(props.authType)) {\n return `\\`authType\\` must be one of ${AUTH_TYPES.join(', ')}; the others need credential fields this resource does not model`;\n }\n const declared = props.authValue !== undefined;\n if (isStaticAuthType(props.authType) && !declared) {\n return `\\`authType: \"${props.authType}\"\\` needs \\`authValue: { fromEnv: … }\\``;\n }\n if (!isStaticAuthType(props.authType) && declared) {\n return `\\`authValue\\` is the static credential for api_key, bearer_token, basic, authorization and token; this server is \"${props.authType}\"`;\n }\n if (declared && isBlank(props.authValue?.fromEnv)) {\n return '`authValue.fromEnv` must name an environment variable';\n }\n return (\n listProblem('allowedTools', props.allowedTools) ??\n listProblem('mcpAccessGroups', props.mcpAccessGroups)\n );\n};\n\n/**\n * One row of `GET /v1/mcp/server`, or of the by-id read, as attributes.\n *\n * ⚠️ THE LIST'S `description` IS `mcp_info.description` WHEN THAT EXISTS, else the column (measured\n * on the live 1.103.0 container: `_build_mcp_server_table` and, on registration,\n * `build_mcp_server_from_table`, which copies the column into `mcp_info` only when `mcp_info`\n * has none). This resource writes only the column, so a declared description cannot be read back\n * from a row whose `mcp_info` holds another: reconcile refuses that before it writes.\n *\n * ⛔ `credentials`, `static_headers`, `env`, `env_vars` and every other field are deliberately NOT\n * copied: what LiteLLM stores as a credential stays on the proxy. The `credentialSeal` is filled\n * in by the caller from the previous state; a row cannot supply one.\n */\nexport const toAttributes = (row: mcp.LiteLLMMCPServerTable): McpServerAttributes => ({\n alias: row.alias ?? null,\n allowAllKeys: row.allow_all_keys ?? false,\n allowedTools: [...(row.allowed_tools ?? [])],\n authType: row.auth_type ?? 'none',\n credentialSeal: '',\n description: row.description ?? null,\n mcpAccessGroups: [...(row.mcp_access_groups ?? [])],\n serverId: row.server_id,\n serverName: row.server_name ?? null,\n transport: row.transport,\n url: row.url === undefined || row.url === null ? null : redactUrl(row.url),\n});\n\nconst sameSet = (live: readonly string[], declared: readonly string[]): boolean =>\n new Set(live).size === new Set(declared).size && declared.every((entry) => live.includes(entry));\n\n/** Wire names of the fields where the live row and the declaration disagree. */\nexport const differing = (live: McpServerAttributes, props: McpServerProps): readonly string[] => {\n const out: string[] = [];\n if (live.serverName !== props.serverName) out.push('server_name');\n if (live.url !== props.url) out.push('url');\n if (live.transport !== props.transport) out.push('transport');\n if (live.authType !== props.authType) out.push('auth_type');\n if (live.allowAllKeys !== (props.allowAllKeys ?? false)) out.push('allow_all_keys');\n if (props.allowedTools !== undefined && !sameSet(live.allowedTools, props.allowedTools)) {\n out.push('allowed_tools');\n }\n if (!sameSet(live.mcpAccessGroups, props.mcpAccessGroups ?? [])) out.push('mcp_access_groups');\n if (props.alias !== undefined && live.alias !== props.alias) out.push('alias');\n if (props.description !== undefined && live.description !== props.description) {\n out.push('description');\n }\n return out;\n};\n\n/** The managed set both writes send, WITHOUT the name fields. ⚠️ See the file header on why an update sends all of it. */\nconst managed = (props: McpServerProps) => ({\n allow_all_keys: props.allowAllKeys ?? false,\n ...(props.allowedTools === undefined ? {} : { allowed_tools: [...props.allowedTools] }),\n auth_type: props.authType,\n ...(props.description === undefined ? {} : { description: props.description }),\n mcp_access_groups: [...(props.mcpAccessGroups ?? [])],\n transport: props.transport,\n url: props.url,\n});\n\n/** A create names the row, and sends an alias only when one is declared: LiteLLM defaults it to the name. */\nconst identityOnCreate = (props: McpServerProps) => ({\n ...(props.alias === undefined ? {} : { alias: props.alias }),\n server_name: props.serverName,\n});\n\n/**\n * ⛔ AN UPDATE MUST NOT LET LITELLM DEFAULT THE ALIAS. Measured 2026-09-29 by running the live\n * 1.103.0 container's own `validate_and_normalize_mcp_server_payload` and `_prepare_mcp_server_data`\n * on an edit body (no database, no network): a body with a `server_name` and no `alias` comes out\n * with `alias = normalize(server_name)` and the alias IS WRITTEN, because the route captures the\n * set of fields the caller sent BEFORE the normalisation and `_prepare_mcp_server_data` drops an\n * alias only when it is still None. The alias is the tool prefix (`get_server_prefix`), so a row\n * with alias `search` and name `estate_web` would have every tool renamed `estate_web-*` for\n * every key and seat, while `differing` (which skips an undeclared alias) called it converged.\n * A body with neither field sends no alias. So:\n * - `server_name` is sent only when it CHANGES, and\n * - an alias is sent when declared, or (on a rename) as the live one, so a rename keeps the prefix.\n * A live row with no alias that is renamed gets the new name as its alias: LiteLLM's own default.\n */\nconst identityOnUpdate = (props: McpServerProps, live: McpServerAttributes) => {\n const renamed = live.serverName !== props.serverName;\n const alias = props.alias ?? (renamed && live.alias !== null ? live.alias : undefined);\n return {\n ...(alias === undefined ? {} : { alias }),\n ...(renamed ? { server_name: props.serverName } : {}),\n };\n};\n\n/**\n * ⛔ THE ONE PLACE THE CREDENTIAL IS UNWRAPPED, and it goes straight onto the wire body. `undefined`\n * sends no `credentials` key at all, which leaves whatever LiteLLM already holds.\n */\nconst credentialsOf = (credential: Redacted.Redacted<string> | undefined) =>\n credential === undefined ? {} : { credentials: { auth_value: Redacted.value(credential) } };\n\nexport const createBody = (\n props: McpServerProps,\n serverId: string,\n credential: Redacted.Redacted<string> | undefined,\n): mcp.AddMcpServerV1McpServerPostRequest => ({\n ...managed(props),\n ...identityOnCreate(props),\n ...credentialsOf(credential),\n server_id: serverId,\n});\n\n/**\n * `clearCredentials` sends an explicit `credentials: null`, for a server moving off a static auth\n * type. Whether LiteLLM clears its stored credential on `null` or ignores it (an `exclude_none`\n * merge) is UNMEASURED at 1.103.0; either way nothing is sent that could set a wrong one.\n */\nexport const updateBody = (\n props: McpServerProps,\n serverId: string,\n credential: Redacted.Redacted<string> | undefined,\n clearCredentials: boolean,\n live: McpServerAttributes,\n): mcp.EditMcpServerV1McpServerPutRequest => ({\n ...managed(props),\n ...identityOnUpdate(props, live),\n ...(clearCredentials ? { credentials: null } : credentialsOf(credential)),\n server_id: serverId,\n});\n",
|
|
30
|
+
"/**\n * The URL half of `LiteLLM.MCPServer`: what a declared URL may not carry, and what a live one is\n * scrubbed of before it reaches Alchemy's state.\n *\n * ⛔ THE URL IS A PROP, SO IT LANDS IN AN UNENCRYPTED STATE STORE. A secret in it (userinfo, a\n * `?token=`) would be persisted, so a declaration is refused for carrying one and a live row's\n * URL is redacted on the way in. The credential is `authValue`, never the URL.\n */\nimport { isBlank } from './mcp-server-types.ts';\n\n/** A query parameter whose NAME says it carries a secret. */\nconst SECRET_PARAM = /token|secret|passw|api.?key|auth|credential|signature|^key$|^sig$/i;\n\n/** Why a URL may not be declared, or `undefined`. ⛔ Never quotes the URL: it may hold the secret. */\nexport const urlProblem = (raw: unknown): string | undefined => {\n if (isBlank(raw)) return '`url` must be a non-empty string';\n let url: URL;\n try {\n url = new URL(raw as string);\n } catch {\n return '`url` must be a full http(s) URL including the scheme';\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '`url` must be http or https';\n if (url.username !== '' || url.password !== '') {\n return '`url` must not carry userinfo; declare the credential as `authValue`';\n }\n if (url.hash !== '') return '`url` must not carry a fragment';\n for (const name of url.searchParams.keys()) {\n if (SECRET_PARAM.test(name)) {\n return `\\`url\\` carries the query parameter \"${name}\", which looks like a credential; declare it as \\`authValue\\` instead, because the URL is stored in Alchemy's state`;\n }\n }\n return undefined;\n};\n\n/**\n * `userinfo` and secret-looking query values replaced with `REDACTED`, so a live URL that carries a\n * token never reaches Alchemy's unencrypted state.\n * ★ A URL THAT NEEDS NO REDACTION COMES BACK BYTE-FOR-BYTE, never re-serialised: `URL#toString`\n * adds a trailing slash to a bare origin, and a declared `https://host` would then differ from\n * its own live row on every plan. A URL that does not parse is returned as it is.\n */\nexport const redactUrl = (raw: string): string => {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return raw;\n }\n let changed = false;\n if (url.username !== '') {\n url.username = 'REDACTED';\n changed = true;\n }\n if (url.password !== '') {\n url.password = 'REDACTED';\n changed = true;\n }\n // ⚠️ A Set first: `set` mutates the live iterator's list, and a repeated name would be skipped.\n for (const name of new Set(url.searchParams.keys())) {\n if (!SECRET_PARAM.test(name)) continue;\n url.searchParams.set(name, 'REDACTED');\n changed = true;\n }\n return changed ? url.toString() : raw;\n};\n",
|
|
31
|
+
"/**\n * Which live row a `LiteLLM.MCPServer` declaration means, and which id a new row would ask for.\n *\n * ⛔ THE LIST DECIDES FIRST, THE DATABASE SECOND. `GET /v1/mcp/server` is LiteLLM's in-memory\n * registry, not the table (mcp-server-operations.ts says how a committed row can be missing from\n * it). A row the list does not show is therefore looked for BY ID in the database, with the id\n * this declaration would use. Without that second read, a row created a moment ago whose\n * registry refresh failed would be POSTed again: the id is deterministic, LiteLLM answers 400\n * \"already exists\", and the resource would be wedged on a row it can neither see nor adopt.\n * ⚠️ ADOPT-BY-NAME NEEDS THE LIST: a name cannot be read from the table by this route family. A row\n * the registry lacks is adoptable only by declaring its `serverId`.\n */\nimport type * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Effect from 'effect/Effect';\nimport { LitellmMcpServerAmbiguousNameError } from './mcp-server-errors.ts';\nimport { listMcpServers, readMcpServer } from './mcp-server-operations.ts';\nimport type { McpServerAttributes, McpServerProps } from './mcp-server-types.ts';\n\n/**\n * The live row among the LISTED ones, or `undefined`. A pinned id (from state, else declared) wins;\n * otherwise the name decides, and an ambiguous name is refused.\n */\nexport const locate = (\n rows: readonly mcp.LiteLLMMCPServerTable[],\n props: McpServerProps,\n output: McpServerAttributes | undefined,\n) => {\n const pinned = output?.serverId ?? props.serverId;\n if (pinned !== undefined) return Effect.succeed(rows.find((row) => row.server_id === pinned));\n const named = rows.filter((row) => row.server_name === props.serverName);\n return named.length > 1\n ? Effect.fail(\n new LitellmMcpServerAmbiguousNameError({\n serverIds: named.map((row) => row.server_id),\n serverName: props.serverName,\n }),\n )\n : Effect.succeed(named[0]);\n};\n\n/** The id a create asks for: the recorded one, else the declared one, else a deterministic name. */\nexport const wantedId = (\n id: string,\n instanceId: string,\n props: McpServerProps,\n output: McpServerAttributes | undefined,\n) =>\n output?.serverId !== undefined\n ? Effect.succeed(output.serverId)\n : props.serverId !== undefined\n ? Effect.succeed(props.serverId)\n : createPhysicalName({ id, instanceId, lowercase: true, maxLength: 64 });\n\n/** The live row this declaration means, from the list, else from the table by the id it would use. */\nexport const findLive = (\n id: string,\n instanceId: string,\n props: McpServerProps,\n output: McpServerAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const listed = yield* locate(yield* listMcpServers(), props, output);\n if (listed !== undefined) return listed;\n return yield* readMcpServer(yield* wantedId(id, instanceId, props, output));\n });\n",
|
|
32
|
+
"/**\n * The five LiteLLM `/v1/mcp/server*` calls `LiteLLM.MCPServer` uses, through the SDK's typed\n * operations in `@distilled.cloud/litellm/mcp_management` (LiteLLM 1.103.0):\n *\n * list `fetchAllMcpServersV1McpServerGet` GET /v1/mcp/server\n * read `fetchMcpServerV1McpServerServerIdGet` GET /v1/mcp/server/{server_id}\n * create `addMcpServerV1McpServerPost` POST /v1/mcp/server\n * update `editMcpServerV1McpServerPut` PUT /v1/mcp/server\n * delete `removeMcpServerV1McpServerServerIdDelete` DELETE /v1/mcp/server/{server_id}\n *\n * ⛔ THE LIST IS NOT THE TABLE. Measured 2026-09-29 by reading the live 1.103.0 container:\n * `GET /v1/mcp/server` answers `global_mcp_server_manager`'s IN-MEMORY REGISTRY\n * (`_resolve_accessible_mcp_servers`), not `LiteLLM_MCPServerTable`. `reload_servers_from_database`\n * loads only rows whose approval status is null, active or approved, and skips a row whose build\n * throws; the registry is narrowed again by the caller's key (`object_permission.mcp_servers`); and\n * `add_mcp_server` only LOGS a failed registry refresh after the row is committed. So a row that\n * is absent from the list can be live in the database. The list is right for adopt-by-name, and\n * wrong as proof of absence. The database is read by ID (`readMcpServer`): `fetch_mcp_server`\n * asks `get_mcp_server` (the table) first and answers 404 only when the table, the registry and\n * a name lookup all miss.\n * ⚠️ THE BY-ID READ HAS SIDE EFFECTS, so it is used only where the list cannot decide: it registers\n * the row in the registry (`add_server`) and runs a health check against the upstream server.\n * ⛔ NO MESSAGE SNIFFING. `mcp_management` declares only `UnprocessableEntity` beyond the shared\n * errors — the distilled `patches/` that type `BadRequest`/`NotFound` cover five other admin tags\n * (budget, key, team, internal_user, model), not this one. A 404 from these routes still decodes\n * at run time as the SDK's `NotFound` class (core's `HTTP_STATUS_MAP`), invisible to the type\n * checker; `readMcpServer` tests that class with `instanceof` and nothing else, and a REAL READ\n * decides (`deleteMcpServer`), the way `budget-operations.ts` and pass-through `operations.ts` do.\n * The typed fix is a distilled patch for this tag, which belongs in the distilled clone.\n * ⚠️ Unlike pass-through endpoints, each server is its own DB row: no semaphore.\n */\nimport * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport { NotFound } from '@distilled.cloud/litellm/Errors';\nimport * as Effect from 'effect/Effect';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\nimport { LitellmMcpServerUnreadableError } from './mcp-server-errors.ts';\n\nconst hasServerId = (row: unknown): boolean =>\n typeof row === 'object' &&\n row !== null &&\n typeof (row as { server_id?: unknown }).server_id === 'string';\n\n/** ⚠️ The REGISTRY as the caller may see it, not the table: see the file header. */\nexport const listMcpServers = (): Effect.Effect<\n readonly mcp.LiteLLMMCPServerTable[],\n mcp.FetchAllMcpServersV1McpServerGetError | LitellmMcpServerUnreadableError,\n LitellmOpContext\n> =>\n throughFetch(mcp.fetchAllMcpServersV1McpServerGet({})).pipe(\n // ⚠️ The SDK types the answer `{ body: LiteLLMMCPServerTable[] }`, but a top-level JSON array is\n // handed back as the array itself (measured through the fake, as for `/budget/list`). Accept\n // both shapes and refuse anything else.\n Effect.flatMap((response) => {\n const rows: unknown = Array.isArray(response)\n ? response\n : (response as { body?: unknown }).body;\n return Array.isArray(rows) && rows.every(hasServerId)\n ? Effect.succeed(rows as readonly mcp.LiteLLMMCPServerTable[])\n : Effect.fail(\n new LitellmMcpServerUnreadableError({\n reason: Array.isArray(rows) ? 'a row has no server_id' : 'not an array',\n }),\n );\n }),\n );\n\n/**\n * One row read BY ID, DATABASE FIRST, or `undefined` when the proxy has no such row.\n *\n * ★ THE ROW MUST CARRY THE ID ASKED FOR. When the table has no row, `fetch_mcp_server` falls back to\n * the registry and then to a lookup by NAME or alias, so an id that happens to be another server's\n * name answers THAT server. A row with another id is \"no such row\" here.\n * ⛔ ANY FAILURE OTHER THAN `NotFound` PROPAGATES: a 401, a 500 or a dead network says nothing about\n * whether the row exists, and must never be read as absence.\n */\nexport const readMcpServer = (serverId: string) =>\n throughFetch(mcp.fetchMcpServerV1McpServerServerIdGet({ server_id: serverId })).pipe(\n Effect.map((row) => (row.server_id === serverId ? row : undefined)),\n Effect.catch((error) =>\n error instanceof NotFound ? Effect.succeed(undefined) : Effect.fail(error),\n ),\n );\n\n/** Answers the created row, which carries the `server_id` LiteLLM actually used. */\nexport const createMcpServer = (body: mcp.AddMcpServerV1McpServerPostRequest) =>\n throughFetch(mcp.addMcpServerV1McpServerPost(body));\n\nexport const updateMcpServer = (body: mcp.EditMcpServerV1McpServerPutRequest) =>\n throughFetch(mcp.editMcpServerV1McpServerPut(body)).pipe(Effect.asVoid);\n\n/**\n * Idempotent BY THIS FUNCTION, not by the vendor. The DELETE is ALWAYS SENT: it acts on the table\n * (`delete_mcp_server` answers 404 for a missing row), so a row the list does not show, because the\n * registry dropped it, is still removed. A list that skipped the call for an unlisted id would drop\n * Alchemy's state for a row that stays live in the database.\n *\n * If the DELETE fails, the failure is swallowed only when a DATABASE-BACKED read by id says the row is\n * absent (`readMcpServer`: a concurrent removal, or a row already gone). A row that is still there,\n * or a read that cannot say, re-raises the ORIGINAL error — so a delete refused for lack of rights,\n * which leaves the row live, is never reported as done.\n */\nexport const deleteMcpServer = (serverId: string) =>\n throughFetch(mcp.removeMcpServerV1McpServerServerIdDelete({ server_id: serverId })).pipe(\n Effect.asVoid,\n Effect.catch((original) =>\n readMcpServer(serverId).pipe(\n Effect.catch(() => Effect.fail(original)),\n Effect.flatMap((row) => (row === undefined ? Effect.void : Effect.fail(original))),\n ),\n ),\n );\n",
|
|
33
|
+
"/**\n * The one description this resource cannot write: a row whose `mcp_info` already has a\n * `description` key.\n *\n * ⛔ MEASURED 2026-09-29 on the live 1.103.0 container and its database (read only): the list route\n * builds each row's `description` from `mcp_info.get(\"description\")` (`_build_mcp_server_table`),\n * and registering a row copies the `description` COLUMN into `mcp_info` only when the KEY IS ABSENT\n * from `mcp_info` (`build_mcp_server_from_table`, `\"description\" not in mcp_info`). This resource\n * writes the column and models no `mcp_info`, so on a row whose `mcp_info` has the key, a declared\n * description lands in the column and is never read back, and every deploy would fail\n * `LitellmMcpServerNotConvergedError` after the write. Live: 2 of the 9 rows in\n * `LiteLLM_MCPServerTable` have the key: `linear` (a string, with the same length as its column)\n * and `Memos_CF` (a JSON null, with a NULL column). A key holding null shadows the column too.\n * ★ THE ROW IS READ BY ID FROM THE TABLE, because the list's `mcp_info` already has the column\n * folded in: a description in it could be either, and only the table row tells them apart.\n * ⚠️ Not `mcp_info` merging: a sent `mcp_info` REPLACES the stored one, and the list's copy carries\n * fields the proxy adds on the way out (`is_public`), which would be persisted.\n */\nimport * as Effect from 'effect/Effect';\nimport { LitellmMcpServerDescriptionShadowedError } from './mcp-server-errors.ts';\nimport { readMcpServer } from './mcp-server-operations.ts';\nimport type { McpServerProps } from './mcp-server-types.ts';\n\n/** Fails BEFORE any write when the declared description would be hidden by the row's `mcp_info`. */\nexport const refuseShadowedDescription = (props: McpServerProps, serverId: string) =>\n props.description === undefined\n ? Effect.void\n : readMcpServer(serverId).pipe(\n Effect.flatMap((row) => {\n const info = row?.mcp_info;\n const shadowed =\n info !== undefined &&\n info !== null &&\n 'description' in info &&\n info['description'] !== props.description;\n return shadowed\n ? Effect.fail(\n new LitellmMcpServerDescriptionShadowedError({\n serverId,\n serverName: props.serverName,\n }),\n )\n : Effect.void;\n }),\n );\n",
|
|
34
|
+
"/**\n * `LiteLLM.AccessGroup` — one row of LiteLLM's unified access group table\n * (`LiteLLM_AccessGroupTable`, `/v1/unified_access_group`): a named bundle of models and MCP servers\n * that teams and keys are attached to. Types and what is left out: access-group-types.ts.\n *\n * ★ ADOPT BY NAME. `access_group_name` is unique in the table, so `accessGroupName` finds the one\n * live row that carries it. A live row with no state is `Unowned`, so it needs `--adopt`.\n * ★ `defaultRemovalPolicy: 'retain'` — deleting a group detaches it from every team and key that\n * holds it (`delete_access_group` rewrites their `access_group_ids`), and those callers lose the\n * models and servers it granted. Opt in with `.pipe(RemovalPolicy.destroy())`.\n * ★ THE GRANTS ARE THE DECLARATION. `modelNames` and `mcpServerIds` are always compared (an\n * undeclared list means \"grants nothing\"), so an adopted group that grants more is corrected.\n * ⚠️ NO LICENCE GATE ON THESE ROUTES: `access_group_endpoints.py` has no `premium_user` check\n * (grep, 1.103.0), and docs.litellm.ai/docs/proxy/access_groups makes no Enterprise statement.\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { createBody, differing, firstProblem, updateBody } from './access-group-form.ts';\nimport {\n createAccessGroup,\n deleteAccessGroup,\n listAccessGroups,\n updateAccessGroup,\n} from './access-group-operations.ts';\nimport type { AccessGroupAttributes, AccessGroupProps } from './access-group-types.ts';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryNotConvergedError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\n\nexport type { AccessGroupAttributes, AccessGroupProps };\nexport type { RegistryError as AccessGroupError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.AccessGroup';\n\nexport interface LiteLLMAccessGroup extends Resource<\n 'LiteLLM.AccessGroup',\n AccessGroupProps,\n AccessGroupAttributes\n> {}\n\nexport const LiteLLMAccessGroup = Resource<LiteLLMAccessGroup>('LiteLLM.AccessGroup', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMAccessGroup = (value: unknown): value is LiteLLMAccessGroup =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.AccessGroup';\n\n/**\n * The live row this declaration means. A RECORDED id wins and is matched by id alone: a row that\n * vanished and was recreated by hand under the same name is not silently taken over (the create\n * would then fail on the unique name, loudly). With no state, the name decides.\n */\nconst locate = (\n rows: readonly AccessGroupAttributes[],\n props: AccessGroupProps,\n output: AccessGroupAttributes | undefined,\n) =>\n output !== undefined\n ? rows.find((row) => row.accessGroupId === output.accessGroupId)\n : rows.find((row) => row.accessGroupName === props.accessGroupName);\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const accessGroupHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({ olds, output }: { olds: AccessGroupProps; output: AccessGroupAttributes | undefined }) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.accessGroupName), firstProblem(olds));\n }\n const live = locate(yield* listAccessGroups(), olds, output);\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : live;\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<AccessGroupProps>;\n output: AccessGroupAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.accessGroupName), firstProblem(news));\n if (output === undefined) return undefined;\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({\n news,\n output,\n }: {\n news: AccessGroupProps;\n output: AccessGroupAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.accessGroupName), firstProblem(news));\n const before = locate(yield* listAccessGroups(), news, output);\n if (before === undefined) yield* createAccessGroup(createBody(news));\n else if (differing(before, news).length > 0) {\n yield* updateAccessGroup(before.accessGroupId, updateBody(news, before));\n }\n\n const rows = yield* listAccessGroups();\n const after =\n before === undefined\n ? rows.find((row) => row.accessGroupName === news.accessGroupName)\n : rows.find((row) => row.accessGroupId === before.accessGroupId);\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({\n name: news.accessGroupName,\n resource: RESOURCE,\n }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.accessGroupName,\n resource: RESOURCE,\n }),\n );\n }\n return after;\n }),\n\n delete: ({ output }: { output: AccessGroupAttributes }) =>\n deleteAccessGroup(output.accessGroupId),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMAccessGroupProvider = () =>\n Provider.effect(\n LiteLLMAccessGroup,\n Effect.succeed(LiteLLMAccessGroup.Provider.of(accessGroupHandlers)),\n );\n",
|
|
35
|
+
"/**\n * Small pure helpers the LiteLLM registry resources share (`LiteLLM.Team`, `AccessGroup`,\n * `Toolset`, `Policy`, `PolicyAttachment`, `ToolPolicy`). Nothing here talks to a proxy.\n *\n * ★ WHY ONE FILE AND NOT ONE COPY PER RESOURCE: every one of these answers the same three\n * questions about a `body: unknown` reply — \"what is the payload\", \"is this list a set of rows\",\n * \"is this failure a missing row\" — and they must answer them the same way, or two resources\n * disagree about what absence looks like.\n * ⛔ NO MESSAGE OR STATUS SNIFFING (S21). `isNotFound` is a CLASS test against the SDK's own\n * `NotFound`, never a `.status` or message match. These routes do not DECLARE 404 (only the\n * `team_management` create/update/delete routes do, by distilled patch), but core's shared status\n * map still decodes a 404 to that class at run time, invisible to the type checker\n * (`mcp-server-operations.ts` says the same about its own by-id read). The typed fix is a distilled\n * patch for each tag, which belongs in the distilled clone — never a catch by text here.\n * ⚠️ WHICH CLASS A 404 DECODES TO DEPENDS ON THE TAG. Where the SDK DECLARES the status (a distilled\n * patch: the `team_management` create, update and delete routes), the runtime error is that service\n * module's OWN `NotFound`, a different class from the core one exported by `Errors`, and only\n * `Effect.catchTag('NotFound')` sees it (`deleteTeam`). Where it is undeclared (`/team/info`,\n * toolset and tool reads) it is core's, and this `instanceof` sees it. A later patch that declares\n * 404 for one of those tags would move it to the other class and fail the tests here, which run every\n * call through the real SDK decode, rather than silently reading \"not found\" as a failure.\n */\nimport { NotFound } from '@distilled.cloud/litellm/Errors';\nimport * as Effect from 'effect/Effect';\nimport { LitellmRegistryInvalidError } from './registry-errors.ts';\n\n/** Whether a failure is the SDK's `NotFound` class. A 401, a 500 or a dead network is never absence. */\nexport const isNotFound = (error: unknown): boolean => error instanceof NotFound;\n\n/**\n * ⚠️ The SDK types most of these answers `{ body: unknown }`, but hands a JSON array or object back as\n * it came (measured through the fakes, as for `/budget/list`). Accept both shapes.\n */\nexport const bodyOf = (response: unknown): unknown =>\n typeof response === 'object' &&\n response !== null &&\n !Array.isArray(response) &&\n 'body' in response\n ? (response as { readonly body: unknown }).body\n : response;\n\nexport type Row = Readonly<Record<string, unknown>>;\n\n/** The value as a plain object, else `undefined`. An array is not a row. */\nexport const asRow = (value: unknown): Row | undefined =>\n typeof value === 'object' && value !== null && !Array.isArray(value) ? (value as Row) : undefined;\n\n/** The strings of a live list, in order. Anything else (`null`, a number) is skipped, never trusted. */\nexport const stringsOf = (value: unknown): readonly string[] =>\n Array.isArray(value) ? value.filter((each): each is string => typeof each === 'string') : [];\n\n/** `null` for anything that is not a string: a live column can hold `null`, and so can a proxy's JSON. */\nexport const stringOrNull = (value: unknown): string | null =>\n typeof value === 'string' ? value : null;\n\n/** `null` for anything that is not a finite number. Numeric strings (Prisma BigInt) are converted. */\nexport const numberOrNull = (value: unknown): number | null => {\n if (value === null || value === undefined || value === '') return null;\n const parsed = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\n/** Sorted, de-duplicated. Every list a resource compares is a SET, so this is its canonical form. */\nexport const canonical = (values: readonly string[]): readonly string[] =>\n [...new Set(values)].sort();\n\nexport const sameSet = (left: readonly string[], right: readonly string[]): boolean => {\n const a = canonical(left);\n const b = canonical(right);\n return a.length === b.length && a.every((each, index) => each === b[index]);\n};\n\n/** Whether a value is not a string with something in it. Takes `unknown`: a declaration can say anything. */\nexport const isBlank = (value: unknown): boolean =>\n typeof value !== 'string' || value.trim() === '';\n\n/** The first blank or padded entry of a declared list, or `undefined`. Padding is refused too: it is a typo. */\nexport const firstBadEntry = (values: readonly unknown[]): string | undefined => {\n for (const each of values) {\n if (isBlank(each) || (each as string) !== (each as string).trim()) return String(each);\n }\n return undefined;\n};\n\n/** A list that names one entry twice. Refused rather than silently collapsed: the twin is a typo. */\nexport const firstDuplicate = (values: readonly string[]): string | undefined =>\n values.find((each, index) => values.indexOf(each) !== index);\n\n/**\n * A refusal as a TYPED FAILURE. Used in `diff` and `reconcile`, where a failure blocks that one\n * resource with a message.\n */\nexport const failIfInvalid = (resource: string, name: string, problem: string | undefined) =>\n problem === undefined\n ? Effect.void\n : Effect.fail(new LitellmRegistryInvalidError({ name, problem, resource }));\n\n/**\n * The same refusal as a DEFECT, for `read` with no `output` — Alchemy's adoption probe and the ONLY\n * plan-time hook a brand-new declaration gets (`mcp-server.ts` measured this on beta.79, `Plan.ts`\n * lines 1303-1316). ★ A defect and not a typed failure because the same call shape is Alchemy's\n * recovery read of an interrupted create with the STORED props, where only a defect is caught\n * (`Effect.catchDefect`); a typed failure would wedge a stage on props a later, stricter release now\n * refuses. The cold-start probe has no such catch, so there the defect fails the plan, as intended.\n */\nexport const dieIfInvalid = (resource: string, name: string, problem: string | undefined) =>\n problem === undefined\n ? Effect.void\n : Effect.die(new LitellmRegistryInvalidError({ name, problem, resource }));\n",
|
|
36
|
+
"/**\n * The typed refusals the LiteLLM registry resources (`LiteLLM.Team`, `AccessGroup`, `Toolset`,\n * `Policy`, `PolicyAttachment`, `ToolPolicy`) raise on top of `@distilled.cloud/litellm`'s own SDK\n * errors. One small set, tagged with the resource it came from, so a stack's error handling does not\n * grow six near-identical classes.\n *\n * ⛔ NO MESSAGE HERE MAY CARRY A CREDENTIAL. What is named: the resource type, the declared name or\n * id, ids the proxy already lists, and field names. Never a description (free text a person wrote)\n * and never a request body.\n */\nimport * as Data from 'effect/Data';\n\n/** A declaration the resource refuses before any request: a combination nobody meant. */\nexport class LitellmRegistryInvalidError extends Data.TaggedError('LitellmRegistryInvalidError')<{\n readonly resource: string;\n readonly name: string;\n readonly problem: string;\n}> {\n override get message(): string {\n return `${this.resource} \"${this.name}\": ${this.problem}.`;\n }\n}\n\n/** A list or read answered something that is not the rows this resource expects. */\nexport class LitellmRegistryUnreadableError extends Data.TaggedError(\n 'LitellmRegistryUnreadableError',\n)<{\n readonly resource: string;\n readonly reason: string;\n}> {\n override get message(): string {\n return `${this.resource}: the proxy's answer is unreadable (${this.reason}).`;\n }\n}\n\n/**\n * More than one live row could be the declared one, so adopting by name cannot say which. Nothing is\n * adopted, updated or deleted on a guess: declare the id to choose one.\n */\nexport class LitellmRegistryAmbiguousError extends Data.TaggedError(\n 'LitellmRegistryAmbiguousError',\n)<{\n readonly resource: string;\n readonly name: string;\n readonly ids: readonly string[];\n}> {\n override get message(): string {\n return (\n `${this.resource} \"${this.name}\": ${this.ids.length} live rows match (${this.ids.join(', ')}). ` +\n 'Declare the id to choose one.'\n );\n }\n}\n\n/** A write returned no error, yet the row is not there on the read back. */\nexport class LitellmRegistryAbsentAfterWriteError extends Data.TaggedError(\n 'LitellmRegistryAbsentAfterWriteError',\n)<{\n readonly resource: string;\n readonly name: string;\n}> {\n override get message(): string {\n return `${this.resource} \"${this.name}\": the write returned no error but the row is absent on the read back.`;\n }\n}\n\n/**\n * A write returned no error, yet the row still differs from the declaration. The route may merge with\n * `exclude_none` or a truthiness check; the read back says so instead of reporting a converged row.\n */\nexport class LitellmRegistryNotConvergedError extends Data.TaggedError(\n 'LitellmRegistryNotConvergedError',\n)<{\n readonly resource: string;\n readonly name: string;\n readonly fields: readonly string[];\n}> {\n override get message(): string {\n return `${this.resource} \"${this.name}\": after the write, the row still differs in ${this.fields.join(', ')}.`;\n }\n}\n\nexport type RegistryError =\n | LitellmRegistryInvalidError\n | LitellmRegistryUnreadableError\n | LitellmRegistryAmbiguousError\n | LitellmRegistryAbsentAfterWriteError\n | LitellmRegistryNotConvergedError;\n",
|
|
37
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.AccessGroup`, testable without a server.\n *\n * ★ THE UPDATE IS A PARTIAL ONE, so it sends only what differs. `update_access_group` builds its\n * write from `data.model_dump(exclude_unset=True)` (`access_group_endpoints.py`, 1.103.0), and turns\n * a `null` list into `[]`. Nothing here relies on an omitted field being kept beyond that rule.\n * ★ THE ROW READ BACK IS THE WHOLE TABLE, through `GET /v1/unified_access_group`. That route has no\n * filter and no by-name form, and it answers absence unambiguously, which a by-id `GET` (a 404 the\n * SDK does not declare for this tag) does not.\n */\nimport type * as ag from '@distilled.cloud/litellm/access_groups';\nimport {\n asRow,\n canonical,\n firstBadEntry,\n firstDuplicate,\n isBlank,\n sameSet,\n stringOrNull,\n stringsOf,\n} from './registry-support.ts';\nimport type { AccessGroupAttributes, AccessGroupProps } from './access-group-types.ts';\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: AccessGroupProps): string | undefined => {\n if (isBlank(props.accessGroupName) || props.accessGroupName !== props.accessGroupName.trim()) {\n return '`accessGroupName` must be a non-blank name with no leading or trailing space';\n }\n if (props.description !== undefined && isBlank(props.description)) {\n return '`description` is blank; leave it out to keep the live text';\n }\n for (const [field, values] of [\n ['modelNames', props.modelNames ?? []],\n ['mcpServerIds', props.mcpServerIds ?? []],\n ] as const) {\n const bad = firstBadEntry(values);\n if (bad !== undefined)\n return `\\`${field}\\` has a blank or padded entry (${JSON.stringify(bad)})`;\n const twice = firstDuplicate(values);\n if (twice !== undefined) return `\\`${field}\\` lists ${JSON.stringify(twice)} twice`;\n }\n return undefined;\n};\n\n/** One row of the list. `undefined` is \"not a row\": the caller refuses the whole answer. */\nexport const toAttributes = (value: unknown): AccessGroupAttributes | undefined => {\n const row = asRow(value);\n if (row === undefined) return undefined;\n const accessGroupId = row['access_group_id'];\n const accessGroupName = row['access_group_name'];\n if (typeof accessGroupId !== 'string' || typeof accessGroupName !== 'string') return undefined;\n return {\n accessGroupId,\n accessGroupName,\n description: stringOrNull(row['description']),\n mcpServerIds: canonical(stringsOf(row['access_mcp_server_ids'])),\n modelNames: canonical(stringsOf(row['access_model_names'])),\n };\n};\n\n/**\n * Wire names of the fields where live and declared disagree. The description counts only when\n * declared; the two grant lists always count, an undeclared one meaning \"grants nothing\".\n */\nexport const differing = (\n live: AccessGroupAttributes,\n props: AccessGroupProps,\n): readonly string[] => {\n const fields: string[] = [];\n if (live.accessGroupName !== props.accessGroupName) fields.push('access_group_name');\n if (props.description !== undefined && live.description !== props.description) {\n fields.push('description');\n }\n if (!sameSet(live.modelNames, props.modelNames ?? [])) fields.push('access_model_names');\n if (!sameSet(live.mcpServerIds, props.mcpServerIds ?? [])) fields.push('access_mcp_server_ids');\n return fields;\n};\n\n/** Create body: the name and both grant lists always (a create with none would be an empty group). */\nexport const createBody = (\n props: AccessGroupProps,\n): ag.CreateAccessGroupV1UnifiedAccessGroupPostRequest => ({\n access_group_name: props.accessGroupName,\n access_mcp_server_ids: [...(props.mcpServerIds ?? [])],\n access_model_names: [...(props.modelNames ?? [])],\n ...(props.description === undefined ? {} : { description: props.description }),\n});\n\n/** Update body: only the fields that differ, so nothing the declaration does not manage is sent. */\nexport const updateBody = (\n props: AccessGroupProps,\n live: AccessGroupAttributes,\n): Omit<ag.UpdateAccessGroupV1UnifiedAccessGroupAccessGroupIdPutRequest, 'access_group_id'> => {\n const changed = new Set(differing(live, props));\n return {\n ...(changed.has('access_group_name') ? { access_group_name: props.accessGroupName } : {}),\n ...(changed.has('description') ? { description: props.description ?? null } : {}),\n ...(changed.has('access_model_names')\n ? { access_model_names: [...(props.modelNames ?? [])] }\n : {}),\n ...(changed.has('access_mcp_server_ids')\n ? { access_mcp_server_ids: [...(props.mcpServerIds ?? [])] }\n : {}),\n };\n};\n",
|
|
38
|
+
"/**\n * The four LiteLLM `/v1/unified_access_group*` calls `LiteLLM.AccessGroup` uses, through the SDK's\n * typed operations in `@distilled.cloud/litellm/access_groups` (LiteLLM 1.103.0):\n *\n * list `listAccessGroupsV1UnifiedAccessGroupGet` GET /v1/unified_access_group\n * create `createAccessGroupV1UnifiedAccessGroupPost` POST /v1/unified_access_group\n * update `updateAccessGroupV1UnifiedAccessGroupAccessGroupIdPut` PUT /v1/unified_access_group/{id}\n * delete `deleteAccessGroupV1UnifiedAccessGroupAccessGroupIdDelete` DELETE /v1/unified_access_group/{id}\n *\n * `/v1/unified_access_group` is registered as an alias of `/v1/access_group` on the same handlers\n * (`access_group_endpoints.py`, the \"Alias routes\" block), so the two answer identically.\n * The by-id `GET` is not used: a missing id is a 404 this tag does not declare, and the list decides\n * absence without depending on it.\n *\n * ⛔ WRITES NEED PROXY_ADMIN (`_require_proxy_admin`, 403 otherwise) and READS need admin or admin-view\n * (`_require_admin_view`). A key that can only read sees every group and can change none.\n * ⛔ NO MESSAGE SNIFFING. Nothing here reads a status or message. `create` of a name that exists is a\n * 409 LiteLLM raises itself; the SDK does not declare it for this tag, so it reaches the caller as\n * the SDK's own error, unchanged. `create` runs only after a list showed the name absent, so a 409\n * means a concurrent create, which is a failure to report, not to paper over.\n * ★ DELETE IS IDEMPOTENT BY A REAL READ (the budget and pass-through rule): a failed DELETE is\n * swallowed only when the list no longer has the id. A 403 for lack of rights leaves the row\n * live, so it re-raises the ORIGINAL error.\n * ⚠️ DELETING A GROUP DETACHES IT FROM EVERY TEAM AND KEY (`delete_access_group` rewrites their\n * `access_group_ids`), which is why the resource defaults to `retain`.\n */\nimport * as ag from '@distilled.cloud/litellm/access_groups';\nimport * as Effect from 'effect/Effect';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\nimport { LitellmRegistryUnreadableError } from './registry-errors.ts';\nimport { bodyOf } from './registry-support.ts';\nimport { type AccessGroupAttributes } from './access-group-types.ts';\nimport { toAttributes } from './access-group-form.ts';\n\nconst RESOURCE = 'LiteLLM.AccessGroup';\n\n/** Every group. The list is the table (`AccessGroupRepository(...).table.find_many`), not a registry. */\nexport const listAccessGroups = (): Effect.Effect<\n readonly AccessGroupAttributes[],\n ag.ListAccessGroupsV1UnifiedAccessGroupGetError | LitellmRegistryUnreadableError,\n LitellmOpContext\n> =>\n throughFetch(ag.listAccessGroupsV1UnifiedAccessGroupGet({})).pipe(\n Effect.flatMap((response) => {\n const rows: unknown = bodyOf(response);\n if (!Array.isArray(rows)) {\n return Effect.fail(\n new LitellmRegistryUnreadableError({ reason: 'not an array', resource: RESOURCE }),\n );\n }\n const parsed = rows.map(toAttributes);\n return parsed.every((row) => row !== undefined)\n ? Effect.succeed(parsed as readonly AccessGroupAttributes[])\n : Effect.fail(\n new LitellmRegistryUnreadableError({\n reason: 'a row has no id or name',\n resource: RESOURCE,\n }),\n );\n }),\n );\n\nexport const createAccessGroup = (body: ag.CreateAccessGroupV1UnifiedAccessGroupPostRequest) =>\n throughFetch(ag.createAccessGroupV1UnifiedAccessGroupPost(body)).pipe(Effect.asVoid);\n\nexport const updateAccessGroup = (\n accessGroupId: string,\n body: Omit<ag.UpdateAccessGroupV1UnifiedAccessGroupAccessGroupIdPutRequest, 'access_group_id'>,\n) =>\n throughFetch(\n ag.updateAccessGroupV1UnifiedAccessGroupAccessGroupIdPut({\n ...body,\n access_group_id: accessGroupId,\n }),\n ).pipe(Effect.asVoid);\n\nexport const deleteAccessGroup = (accessGroupId: string) =>\n throughFetch(\n ag.deleteAccessGroupV1UnifiedAccessGroupAccessGroupIdDelete({ access_group_id: accessGroupId }),\n ).pipe(\n Effect.asVoid,\n Effect.catch((original) =>\n listAccessGroups().pipe(\n Effect.catch(() => Effect.fail(original)),\n Effect.flatMap((rows) =>\n rows.some((row) => row.accessGroupId === accessGroupId)\n ? Effect.fail(original)\n : Effect.void,\n ),\n ),\n ),\n );\n",
|
|
39
|
+
"/**\n * `LiteLLM.PolicyAttachment` — one row of LiteLLM's policy attachment table\n * (`/policies/attachments`): WHERE a policy applies, by team alias, key alias, model and tag. Props,\n * the global-by-default trap and the config-file exclusion: policy-attachment-types.ts.\n *\n * ⛔ IMMUTABLE, SO EVERY CHANGE IS A CREATE-FIRST REPLACE. There is no update route. The engine creates\n * the new attachment and then deletes the old, so both apply for a moment; guardrails are additive,\n * so that can only add enforcement.\n * ★ THE REMOVAL POLICY IS THE DEFAULT (`destroy`), DELIBERATELY NOT `retain` (the other registry\n * resources retain). Under `retain` a replace would leave the OLD attachment live beside the new one\n * for good: a scope the declaration no longer names would keep the policy attached. An attachment\n * holds no data, so removing it loses nothing but the scoping the declaration just dropped.\n * ★ ADOPT BY CONTENT. A live attachment that matches every field, with no state, is `Unowned` and needs\n * `--adopt`. Two identical live rows are refused, never guessed at.\n * ⚠️ NO LICENCE GATE ON THESE ROUTES: none in `policy_endpoints.py` or `policy_engine/*.py` (1.103.0).\n * The guardrail-policies docs page says team/key-based attachment needs Enterprise\n * (docs.litellm.ai/docs/proxy/guardrails/guardrail_policies); whether a CE proxy enforces a\n * team-scoped attachment is UNVERIFIED.\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { createBody, differing, firstProblem } from './policy-attachment-form.ts';\nimport {\n createAttachment,\n deleteAttachment,\n listAttachments,\n} from './policy-attachment-operations.ts';\nimport type {\n PolicyAttachmentAttributes,\n PolicyAttachmentProps,\n} from './policy-attachment-types.ts';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryAmbiguousError,\n LitellmRegistryNotConvergedError,\n LitellmRegistryUnreadableError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\n\nexport type { PolicyAttachmentAttributes, PolicyAttachmentProps };\nexport type { RegistryError as PolicyAttachmentError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.PolicyAttachment';\n\nexport interface LiteLLMPolicyAttachment extends Resource<\n 'LiteLLM.PolicyAttachment',\n PolicyAttachmentProps,\n PolicyAttachmentAttributes\n> {}\n\nexport const LiteLLMPolicyAttachment = Resource<LiteLLMPolicyAttachment>(\n 'LiteLLM.PolicyAttachment',\n);\n\nexport const isLiteLLMPolicyAttachment = (value: unknown): value is LiteLLMPolicyAttachment =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.PolicyAttachment';\n\n/**\n * The live row this declaration means: a RECORDED id is matched by id alone (a row that vanished is\n * `undefined`); with no state, the rows equal to the declaration decide, and a twin is refused.\n */\nconst locate = (\n rows: readonly PolicyAttachmentAttributes[],\n props: PolicyAttachmentProps,\n output: PolicyAttachmentAttributes | undefined,\n) => {\n if (output !== undefined) {\n return Effect.succeed(rows.find((row) => row.attachmentId === output.attachmentId));\n }\n const equal = rows.filter((row) => differing(row, props).length === 0);\n return equal.length > 1\n ? Effect.fail(\n new LitellmRegistryAmbiguousError({\n ids: equal.map((row) => row.attachmentId),\n name: props.policyName,\n resource: RESOURCE,\n }),\n )\n : Effect.succeed(equal[0]);\n};\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const policyAttachmentHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({\n olds,\n output,\n }: {\n olds: PolicyAttachmentProps;\n output: PolicyAttachmentAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.policyName), firstProblem(olds));\n }\n const live = yield* locate(yield* listAttachments(), olds, output);\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : live;\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<PolicyAttachmentProps>;\n output: PolicyAttachmentAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.policyName), firstProblem(news));\n if (output === undefined) return undefined;\n // ⛔ no update route: any difference is a new attachment, created before the old one goes\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'replace', deleteFirst: false } as const);\n }),\n\n reconcile: ({\n news,\n output,\n }: {\n news: PolicyAttachmentProps;\n output: PolicyAttachmentAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.policyName), firstProblem(news));\n const before = yield* locate(yield* listAttachments(), news, output);\n // an attachment that already IS the declaration is kept, whether it is ours or being adopted\n if (before !== undefined && differing(before, news).length === 0) return before;\n\n const created = yield* createAttachment(createBody(news));\n if (created === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryUnreadableError({\n reason: 'the create answered something that is not an attachment',\n resource: RESOURCE,\n }),\n );\n }\n const after = (yield* listAttachments()).find(\n (row) => row.attachmentId === created.attachmentId,\n );\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.policyName, resource: RESOURCE }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.policyName,\n resource: RESOURCE,\n }),\n );\n }\n return after;\n }),\n\n delete: ({ output }: { output: PolicyAttachmentAttributes }) =>\n deleteAttachment(output.attachmentId),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMPolicyAttachmentProvider = () =>\n Provider.effect(\n LiteLLMPolicyAttachment,\n Effect.succeed(LiteLLMPolicyAttachment.Provider.of(policyAttachmentHandlers)),\n );\n",
|
|
40
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.PolicyAttachment`, testable without a server.\n *\n * ★ TWO ATTACHMENTS ARE \"THE SAME\" WHEN EVERY FIELD IS: the policy, the scope, the four selector SETS\n * and the priority. That is the only identity an attachment has (policy-attachment-types.ts), and\n * it is what adoption and the read back both use.\n */\nimport type * as policies from '@distilled.cloud/litellm/policy_engine';\nimport {\n asRow,\n canonical,\n firstBadEntry,\n firstDuplicate,\n isBlank,\n numberOrNull,\n sameSet,\n stringOrNull,\n stringsOf,\n} from './registry-support.ts';\nimport type {\n PolicyAttachmentAttributes,\n PolicyAttachmentProps,\n} from './policy-attachment-types.ts';\n\nconst SELECTORS = ['teams', 'keys', 'models', 'tags'] as const;\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: PolicyAttachmentProps): string | undefined => {\n if (isBlank(props.policyName) || props.policyName !== props.policyName.trim()) {\n return '`policyName` must be a non-blank name with no leading or trailing space';\n }\n if (props.scope !== undefined && props.scope !== '*') return \"`scope` can only be '*'\";\n for (const field of SELECTORS) {\n const values = props[field] ?? [];\n const bad = firstBadEntry(values);\n if (bad !== undefined)\n return `\\`${field}\\` has a blank or padded entry (${JSON.stringify(bad)})`;\n const twice = firstDuplicate(values);\n if (twice !== undefined) return `\\`${field}\\` lists ${JSON.stringify(twice)} twice`;\n }\n if (props.scope === undefined && SELECTORS.every((field) => (props[field] ?? []).length === 0)) {\n return \"no `scope`, `teams`, `keys`, `models` or `tags`: LiteLLM treats an attachment with no selector as GLOBAL, so declare `scope: '*'` if that is meant\";\n }\n if (\n props.priority !== undefined &&\n !(\n Number.isInteger(props.priority) &&\n props.priority >= -2147483648 &&\n props.priority <= 2147483647\n )\n ) {\n return '`priority` must be a 32-bit integer';\n }\n return undefined;\n};\n\n/** One attachment row. `undefined` is \"not an attachment\", and so is a config-file one. */\nexport const toAttributes = (value: unknown): PolicyAttachmentAttributes | undefined => {\n const row = asRow(value);\n if (row === undefined) return undefined;\n const attachmentId = row['attachment_id'];\n const policyName = row['policy_name'];\n if (typeof attachmentId !== 'string' || typeof policyName !== 'string') return undefined;\n return {\n attachmentId,\n keys: canonical(stringsOf(row['keys'])),\n models: canonical(stringsOf(row['models'])),\n policyName,\n priority: numberOrNull(row['priority']),\n scope: stringOrNull(row['scope']),\n tags: canonical(stringsOf(row['tags'])),\n teams: canonical(stringsOf(row['teams'])),\n };\n};\n\n/** Whether a live row is defined in config.yaml (its synthetic id is `config-<n>`): out of reach of the API. */\nexport const isConfigRow = (value: unknown): boolean =>\n asRow(value)?.['definition_location'] === 'config';\n\n/** Wire names of the fields where live and declared disagree. Empty means \"the same attachment\". */\nexport const differing = (\n live: PolicyAttachmentAttributes,\n props: PolicyAttachmentProps,\n): readonly string[] => {\n const fields: string[] = [];\n if (live.policyName !== props.policyName) fields.push('policy_name');\n if ((live.scope ?? null) !== (props.scope ?? null)) fields.push('scope');\n for (const field of SELECTORS) if (!sameSet(live[field], props[field] ?? [])) fields.push(field);\n if (live.priority !== (props.priority ?? null)) fields.push('priority');\n return fields;\n};\n\nexport const createBody = (\n props: PolicyAttachmentProps,\n): policies.CreatePolicyAttachmentPoliciesAttachmentsPostRequest => ({\n policy_name: props.policyName,\n ...(props.scope === undefined ? {} : { scope: props.scope }),\n ...(props.teams === undefined ? {} : { teams: [...props.teams] }),\n ...(props.keys === undefined ? {} : { keys: [...props.keys] }),\n ...(props.models === undefined ? {} : { models: [...props.models] }),\n ...(props.tags === undefined ? {} : { tags: [...props.tags] }),\n ...(props.priority === undefined ? {} : { priority: props.priority }),\n});\n",
|
|
41
|
+
"/**\n * The three LiteLLM `/policies/attachments*` calls `LiteLLM.PolicyAttachment` uses, through the SDK's\n * typed operations in `@distilled.cloud/litellm/policy_engine` (LiteLLM 1.103.0):\n *\n * list `listPolicyAttachmentsPoliciesAttachmentsListGet` GET /policies/attachments/list\n * create `createPolicyAttachmentPoliciesAttachmentsPost` POST /policies/attachments\n * delete `deletePolicyAttachmentPoliciesAttachmentsAttachmentIdDelete` DELETE /policies/attachments/{id}\n *\n * ★ THERE IS NO UPDATE OPERATION, because the API has none (policy-attachment-types.ts). The by-id\n * `GET` is not used either: the list carries every field and answers absence without a 404.\n * ★ THE LIST MERGES IN CONFIG-FILE ATTACHMENTS (`definition_location: \"config\"`, ids `config-<n>`),\n * which the API cannot delete. `listAttachments` returns database rows only.\n * ⚠️ MOST FAILURES ARE A PLAIN 500 (`policy_endpoints.py` wraps everything in `except Exception` and\n * answers `HTTPException(500, detail=str(e))`), and the SDK retries 5xx with backoff. The two\n * answers that are not: a policy with no production version is a 404, and a concrete team, key or model\n * selector that resolves to nothing is a 400 (`PolicyValidator.find_invalid_scope_entries`), so a\n * `teams` entry must exist BEFORE its attachment: declare it as `team.teamAlias`. Wildcard patterns\n * are let through, and may match nothing today.\n * ★ DELETE IS IDEMPOTENT BY A REAL READ: a failed DELETE (a missing id is a 404) is swallowed only when\n * the list no longer has the id; otherwise the ORIGINAL error is re-raised.\n */\nimport * as policies from '@distilled.cloud/litellm/policy_engine';\nimport * as Effect from 'effect/Effect';\nimport { throughFetch } from './operations.ts';\nimport { isConfigRow, toAttributes } from './policy-attachment-form.ts';\nimport type { PolicyAttachmentAttributes } from './policy-attachment-types.ts';\nimport { LitellmRegistryUnreadableError } from './registry-errors.ts';\nimport { asRow, bodyOf } from './registry-support.ts';\n\nconst RESOURCE = 'LiteLLM.PolicyAttachment';\n\nconst unreadable = (reason: string) =>\n new LitellmRegistryUnreadableError({ reason, resource: RESOURCE });\n\n/** Every DATABASE attachment. Config-file ones are dropped here, once. */\nexport const listAttachments = () =>\n throughFetch(policies.listPolicyAttachmentsPoliciesAttachmentsListGet({})).pipe(\n Effect.flatMap((response) => {\n const rows = asRow(bodyOf(response))?.['attachments'];\n if (rows === undefined) return Effect.succeed<readonly PolicyAttachmentAttributes[]>([]);\n if (!Array.isArray(rows)) return Effect.fail(unreadable('attachments is not an array'));\n const parsed = rows.filter((row) => !isConfigRow(row)).map(toAttributes);\n return parsed.every((each) => each !== undefined)\n ? Effect.succeed(parsed as readonly PolicyAttachmentAttributes[])\n : Effect.fail(unreadable('an attachment has no id or policy name'));\n }),\n );\n\n/** The created row, which carries the id LiteLLM issued. `undefined` when the answer is not one. */\nexport const createAttachment = (\n body: policies.CreatePolicyAttachmentPoliciesAttachmentsPostRequest,\n) =>\n throughFetch(policies.createPolicyAttachmentPoliciesAttachmentsPost(body)).pipe(\n Effect.map((response) => toAttributes(bodyOf(response))),\n );\n\nexport const deleteAttachment = (attachmentId: string) =>\n throughFetch(\n policies.deletePolicyAttachmentPoliciesAttachmentsAttachmentIdDelete({\n attachment_id: attachmentId,\n }),\n ).pipe(\n Effect.asVoid,\n Effect.catch((original) =>\n listAttachments().pipe(\n Effect.catch(() => Effect.fail(original)),\n Effect.flatMap((rows) =>\n rows.some((row) => row.attachmentId === attachmentId)\n ? Effect.fail(original)\n : Effect.void,\n ),\n ),\n ),\n );\n",
|
|
42
|
+
"/**\n * `LiteLLM.Policy` — the PRODUCTION version of one named policy in LiteLLM's policy engine\n * (`LiteLLM_PolicyTable`, `/policies`): a set of guardrails to add and remove, with an optional model\n * condition. Props, the versioning and what is left out: policy-types.ts.\n *\n * ★ A CHANGE IS A NEW VERSION, PROMOTED. Only a draft is editable and only production is enforced, so\n * an update clones production to a draft, edits it, publishes it and promotes it; the old production\n * stays as a `published` version. A run that stops midway leaves a draft or a published copy, both\n * inert; the next run starts from production again and never touches a version it did not make.\n * ★ ADOPT BY NAME. A live production version with no state is `Unowned`, so it needs `--adopt`.\n * ⛔ A POLICY THAT EXISTS ONLY IN config.yaml IS NEVER SHADOWED BY ACCIDENT: a create refuses when the\n * name belongs to a config policy, because a production DB policy would override it at runtime.\n * ⛔ NO PRODUCTION VERSION IS NEVER GUESSED AT: a name whose versions are all `draft` or `published`\n * (someone demoted it) is refused with the count, not promoted.\n * ★ `defaultRemovalPolicy: 'retain'` — deleting removes EVERY version (the history) and stops the\n * guardrails applying wherever an attachment pointed at it. Opt in with `RemovalPolicy.destroy()`.\n * ⚠️ NO LICENCE GATE ON THESE ROUTES: `policy_endpoints.py` and `policy_engine/*.py` have no\n * `premium_user` check (grep, 1.103.0). The docs page says team/key-based attachment needs Enterprise\n * (docs.litellm.ai/docs/proxy/guardrails/guardrail_policies); whether a CE proxy enforces one is\n * UNVERIFIED. The `policies` metadata of a team or key IS gated (`_premium_user_check`).\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { createBody, differing, firstProblem, toAttributes, updateBody } from './policy-form.ts';\nimport {\n configPolicyNames,\n createDraft,\n createPolicy,\n deleteAllVersions,\n editDraft,\n listPolicyVersions,\n setVersionStatus,\n} from './policy-operations.ts';\nimport type { PolicyAttributes, PolicyProps, PolicyVersion } from './policy-types.ts';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryInvalidError,\n LitellmRegistryNotConvergedError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\n\nexport type { PolicyAttributes, PolicyProps };\nexport type { RegistryError as PolicyError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.Policy';\n\nexport interface LiteLLMPolicy extends Resource<'LiteLLM.Policy', PolicyProps, PolicyAttributes> {}\n\nexport const LiteLLMPolicy = Resource<LiteLLMPolicy>('LiteLLM.Policy', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMPolicy = (value: unknown): value is LiteLLMPolicy =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Policy';\n\nconst production = (versions: readonly PolicyVersion[]) =>\n versions.find((version) => version.versionStatus === 'production');\n\nconst invalid = (name: string, problem: string) =>\n Effect.fail(new LitellmRegistryInvalidError({ name, problem, resource: RESOURCE }));\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const policyHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({ olds, output }: { olds: PolicyProps; output: PolicyAttributes | undefined }) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.policyName), firstProblem(olds));\n }\n const live = production(yield* listPolicyVersions(output?.policyName ?? olds.policyName));\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(toAttributes(live)) : toAttributes(live);\n }),\n\n diff: ({ news, output }: { news: Input<PolicyProps>; output: PolicyAttributes | undefined }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.policyName), firstProblem(news));\n if (output === undefined) return undefined;\n // The name is identity: a different name is a different policy.\n if (news.policyName !== output.policyName) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ news }: { news: PolicyProps }) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.policyName), firstProblem(news));\n const versions = yield* listPolicyVersions(news.policyName);\n const live = production(versions);\n if (versions.length === 0) {\n if ((yield* configPolicyNames()).includes(news.policyName)) {\n return yield* invalid(\n news.policyName,\n 'a policy of this name is defined in config.yaml, and a production DB policy would override it at runtime; rename this one or remove the config policy first',\n );\n }\n yield* createPolicy(createBody(news));\n } else if (live === undefined) {\n return yield* invalid(\n news.policyName,\n `${versions.length} version(s) exist and none is production, so the policy is inactive; promote one in LiteLLM (or delete its versions) instead of letting this guess`,\n );\n } else if (differing(live, news).length > 0) {\n const draft = yield* createDraft(news.policyName);\n yield* editDraft(draft.policyId, updateBody(news, live));\n yield* setVersionStatus(draft.policyId, 'published');\n yield* setVersionStatus(draft.policyId, 'production');\n }\n\n const after = production(yield* listPolicyVersions(news.policyName));\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.policyName, resource: RESOURCE }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.policyName,\n resource: RESOURCE,\n }),\n );\n }\n return toAttributes(after);\n }),\n\n delete: ({ output }: { output: PolicyAttributes }) => deleteAllVersions(output.policyName),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMPolicyProvider = () =>\n Provider.effect(LiteLLMPolicy, Effect.succeed(LiteLLMPolicy.Provider.of(policyHandlers)));\n",
|
|
43
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.Policy`, testable without a server.\n *\n * ★ THE EDIT BODY CARRIES ONLY WHAT DIFFERS AND IS NEVER A `null`: `update_policy_in_db` skips every\n * field that is `None` (policy-types.ts), so a `null` would be a silent no-op the read back would\n * then have to catch.\n */\nimport type * as policies from '@distilled.cloud/litellm/policy_engine';\nimport {\n asRow,\n canonical,\n firstBadEntry,\n firstDuplicate,\n isBlank,\n numberOrNull,\n sameSet,\n stringOrNull,\n stringsOf,\n} from './registry-support.ts';\nimport type { PolicyAttributes, PolicyProps, PolicyVersion } from './policy-types.ts';\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: PolicyProps): string | undefined => {\n if (isBlank(props.policyName) || props.policyName !== props.policyName.trim()) {\n return '`policyName` must be a non-blank name with no leading or trailing space';\n }\n for (const [field, value] of [\n ['description', props.description],\n ['inherit', props.inherit],\n ['conditionModel', props.conditionModel],\n ] as const) {\n if (value !== undefined && isBlank(value)) {\n return `\\`${field}\\` is blank; leave it out to keep the live value (a policy field cannot be cleared)`;\n }\n }\n if (props.inherit === props.policyName) return '`inherit` names the policy itself';\n for (const [field, values] of [\n ['guardrailsAdd', props.guardrailsAdd ?? []],\n ['guardrailsRemove', props.guardrailsRemove ?? []],\n ] as const) {\n const bad = firstBadEntry(values);\n if (bad !== undefined)\n return `\\`${field}\\` has a blank or padded entry (${JSON.stringify(bad)})`;\n const twice = firstDuplicate(values);\n if (twice !== undefined) return `\\`${field}\\` lists ${JSON.stringify(twice)} twice`;\n }\n const both = (props.guardrailsAdd ?? []).find((name) =>\n (props.guardrailsRemove ?? []).includes(name),\n );\n if (both !== undefined) return `guardrail ${JSON.stringify(both)} is both added and removed`;\n return undefined;\n};\n\n/** One policy version. `undefined` is \"not a policy row\": the caller refuses the whole answer. */\nexport const toVersion = (value: unknown): PolicyVersion | undefined => {\n const row = asRow(value);\n if (row === undefined) return undefined;\n const policyId = row['policy_id'];\n const policyName = row['policy_name'];\n if (typeof policyId !== 'string' || typeof policyName !== 'string') return undefined;\n const condition = asRow(row['condition']);\n return {\n conditionModel: stringOrNull(condition?.['model']),\n description: stringOrNull(row['description']),\n guardrailsAdd: canonical(stringsOf(row['guardrails_add'])),\n guardrailsRemove: canonical(stringsOf(row['guardrails_remove'])),\n inherit: stringOrNull(row['inherit']),\n policyId,\n policyName,\n versionNumber: numberOrNull(row['version_number']) ?? 1,\n versionStatus: typeof row['version_status'] === 'string' ? row['version_status'] : 'production',\n };\n};\n\n/** The attributes of a version: without the status, which is never recorded. */\nexport const toAttributes = (version: PolicyVersion): PolicyAttributes => {\n const { versionStatus: _status, ...attributes } = version;\n return attributes;\n};\n\n/** Wire names of the fields where live and declared disagree. */\nexport const differing = (live: PolicyAttributes, props: PolicyProps): readonly string[] => {\n const fields: string[] = [];\n if (props.description !== undefined && live.description !== props.description) {\n fields.push('description');\n }\n if (props.inherit !== undefined && live.inherit !== props.inherit) fields.push('inherit');\n if (!sameSet(live.guardrailsAdd, props.guardrailsAdd ?? [])) fields.push('guardrails_add');\n if (!sameSet(live.guardrailsRemove, props.guardrailsRemove ?? []))\n fields.push('guardrails_remove');\n if (props.conditionModel !== undefined && live.conditionModel !== props.conditionModel) {\n fields.push('condition');\n }\n return fields;\n};\n\n/** Create body (version 1, which LiteLLM makes production): every declared field. */\nexport const createBody = (props: PolicyProps): policies.CreatePolicyPoliciesPostRequest => ({\n guardrails_add: [...(props.guardrailsAdd ?? [])],\n guardrails_remove: [...(props.guardrailsRemove ?? [])],\n policy_name: props.policyName,\n ...(props.description === undefined ? {} : { description: props.description }),\n ...(props.inherit === undefined ? {} : { inherit: props.inherit }),\n ...(props.conditionModel === undefined ? {} : { condition: { model: props.conditionModel } }),\n});\n\n/** Draft edit body: only the fields that differ from what the draft was cloned from. */\nexport const updateBody = (\n props: PolicyProps,\n live: PolicyAttributes,\n): Omit<policies.UpdatePolicyPoliciesPolicyIdPutRequest, 'policy_id'> => {\n const changed = new Set(differing(live, props));\n return {\n ...(changed.has('description') ? { description: props.description ?? null } : {}),\n ...(changed.has('inherit') ? { inherit: props.inherit ?? null } : {}),\n ...(changed.has('guardrails_add') ? { guardrails_add: [...(props.guardrailsAdd ?? [])] } : {}),\n ...(changed.has('guardrails_remove')\n ? { guardrails_remove: [...(props.guardrailsRemove ?? [])] }\n : {}),\n ...(changed.has('condition') ? { condition: { model: props.conditionModel ?? null } } : {}),\n };\n};\n",
|
|
44
|
+
"/**\n * The seven LiteLLM `/policies*` calls `LiteLLM.Policy` uses, through the SDK's typed operations in\n * `@distilled.cloud/litellm/policy_engine` (LiteLLM 1.103.0):\n *\n * versions `listPolicyVersionsPoliciesNamePolicyNameVersionsGet` GET /policies/name/{policy_name}/versions\n * config `listPoliciesPoliciesListGet` GET /policies/list?version_status=production\n * create `createPolicyPoliciesPost` POST /policies\n * draft `createPolicyVersionPoliciesNamePolicyNameVersionsPost` POST /policies/name/{policy_name}/versions\n * edit `updatePolicyPoliciesPolicyIdPut` PUT /policies/{policy_id}\n * status `updatePolicyVersionStatusPoliciesPolicyIdStatusPut` PUT /policies/{policy_id}/status\n * delete `deleteAllPolicyVersionsPoliciesNamePolicyNameAllVersionsDelete` DELETE /policies/name/{policy_name}/all-versions\n *\n * ⛔ THE VERSION LIST IS THE TABLE AND ANSWERS ABSENCE WITH AN EMPTY LIST. `get_versions_by_policy_name`\n * is a `find_many` by name (`policy_registry.py`, 1.103.0), so a name with no rows is `versions: []`\n * with a 200, never a 404. Nothing here needs the SDK's undeclared `NotFound`.\n * ⛔ A CONFIG-FILE POLICY IS NOT IN THE TABLE. `/policies/list` merges DB rows with config.yaml\n * policies (`definition_location: \"config\"`), and a production DB policy of the same name OVERRIDES the\n * config one at runtime. `configPolicyNames` is how a create refuses to do that by accident.\n * ⚠️ MOST FAILURES HERE ARE A PLAIN 500. The handlers wrap everything in `except Exception` and answer\n * `HTTPException(500, detail=str(e))`, except a duplicate name (400) and a few 404/400 cases matched\n * by message text. The SDK retries 5xx with backoff, so a genuine failure is slow to surface; nothing\n * here reads the text either way (S21).\n * ⛔ DELETE REMOVES EVERY VERSION (`delete_many` by name), not just production, and answers 200 whether\n * or not any row existed, so it is idempotent as it stands. It is why the resource defaults to `retain`.\n */\nimport * as policies from '@distilled.cloud/litellm/policy_engine';\nimport * as Effect from 'effect/Effect';\nimport { throughFetch } from './operations.ts';\nimport { toVersion } from './policy-form.ts';\nimport type { PolicyVersion } from './policy-types.ts';\nimport { LitellmRegistryUnreadableError } from './registry-errors.ts';\nimport { asRow, bodyOf } from './registry-support.ts';\n\nconst RESOURCE = 'LiteLLM.Policy';\n\nconst unreadable = (reason: string) =>\n new LitellmRegistryUnreadableError({ reason, resource: RESOURCE });\n\n/** Every version of one policy name, newest first as LiteLLM orders them. `[]` when there is none. */\nexport const listPolicyVersions = (policyName: string) =>\n throughFetch(\n policies.listPolicyVersionsPoliciesNamePolicyNameVersionsGet({ policy_name: policyName }),\n ).pipe(\n Effect.flatMap((response) => {\n const versions = asRow(bodyOf(response))?.['versions'];\n if (versions === undefined) return Effect.succeed<readonly PolicyVersion[]>([]);\n if (!Array.isArray(versions)) return Effect.fail(unreadable('versions is not an array'));\n const parsed = versions.map(toVersion);\n return parsed.every((each) => each !== undefined)\n ? Effect.succeed(parsed as readonly PolicyVersion[])\n : Effect.fail(unreadable('a version has no id or name'));\n }),\n );\n\n/** The names of the policies defined in config.yaml (their `definition_location` is `config`). */\nexport const configPolicyNames = () =>\n throughFetch(policies.listPoliciesPoliciesListGet({ version_status: 'production' })).pipe(\n Effect.flatMap((response) => {\n const rows = asRow(bodyOf(response))?.['policies'];\n if (!Array.isArray(rows)) return Effect.fail(unreadable('policies is not an array'));\n return Effect.succeed(\n rows.flatMap((row) => {\n const each = asRow(row);\n return each?.['definition_location'] === 'config' &&\n typeof each['policy_name'] === 'string'\n ? [each['policy_name']]\n : [];\n }),\n );\n }),\n );\n\n/** Creates version 1 AS PRODUCTION. */\nexport const createPolicy = (body: policies.CreatePolicyPoliciesPostRequest) =>\n throughFetch(policies.createPolicyPoliciesPost(body)).pipe(Effect.asVoid);\n\n/** A new DRAFT cloned from production. Answers the draft, whose id every later call needs. */\nexport const createDraft = (policyName: string) =>\n throughFetch(\n policies.createPolicyVersionPoliciesNamePolicyNameVersionsPost({ policy_name: policyName }),\n ).pipe(\n Effect.flatMap((response) => {\n const draft = toVersion(bodyOf(response));\n return draft === undefined\n ? Effect.fail(unreadable('the new draft is not a policy'))\n : Effect.succeed(draft);\n }),\n );\n\nexport const editDraft = (\n policyId: string,\n body: Omit<policies.UpdatePolicyPoliciesPolicyIdPutRequest, 'policy_id'>,\n) =>\n throughFetch(policies.updatePolicyPoliciesPolicyIdPut({ ...body, policy_id: policyId })).pipe(\n Effect.asVoid,\n );\n\n/** `draft → published` or `published → production` (which demotes the old production to published). */\nexport const setVersionStatus = (policyId: string, status: 'published' | 'production') =>\n throughFetch(\n policies.updatePolicyVersionStatusPoliciesPolicyIdStatusPut({\n policy_id: policyId,\n version_status: status,\n }),\n ).pipe(Effect.asVoid);\n\nexport const deleteAllVersions = (policyName: string) =>\n throughFetch(\n policies.deleteAllPolicyVersionsPoliciesNamePolicyNameAllVersionsDelete({\n policy_name: policyName,\n }),\n ).pipe(Effect.asVoid);\n",
|
|
45
|
+
"/**\n * `LiteLLM.Team` — one row of LiteLLM's team table (`/team/*`): the models, MCP grants and roster that\n * bound every key of a team. Props, the MCP ceiling and everything left out: team-types.ts.\n *\n * ★ ADOPT BY ID. `teamId` names the live team; without it a deterministic physical name is used. A\n * team with that id and no state is `Unowned`, so it needs `--adopt`. (`team_alias` is not unique, so\n * it cannot identify a team.)\n * ★ `defaultRemovalPolicy: 'retain'` — deleting a team DELETES EVERY KEY OF THE TEAM (team-operations.ts).\n * Opt in with `.pipe(RemovalPolicy.destroy())`, knowing that.\n * ★ A DECLARED FIELD IS THE TRUTH, AN UNDECLARED ONE IS LEFT ALONE. `models`, `blocked`,\n * `accessGroupIds`, each object-permission field and the roster are compared and sent only when\n * declared; the update is a partial one, and the object permission a merge by field (team-permission.ts),\n * so an adopted team keeps everything this resource does not manage.\n * ⛔ THE ROSTER IS ADDITIVE ONLY (team-members.ts): removing a member would delete that user's keys.\n * ⚠️ NO LICENCE GATE ON THE FIELDS IT MANAGES except a member's `admin` role (Enterprise, 400 without a\n * licence: `team_endpoints.py` lines 2713-2723, 3806-3810). Every other Enterprise team field is one\n * this resource never sends (team-types.ts lists them).\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryNotConvergedError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\nimport { createBody, differing, firstProblem, updateBody } from './team-form.ts';\nimport { membersToAdd, roleChanges } from './team-members.ts';\nimport {\n addMembers,\n createTeam,\n deleteTeam,\n readTeam,\n setMemberRole,\n updateTeam,\n} from './team-operations.ts';\nimport type { TeamAttributes, TeamProps } from './team-types.ts';\n\nexport type { TeamAttributes, TeamProps };\nexport type {\n TeamMember,\n TeamMemberAttributes,\n TeamObjectPermission,\n TeamObjectPermissionAttributes,\n} from './team-types.ts';\nexport type { RegistryError as TeamError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.Team';\n\nexport interface LiteLLMTeam extends Resource<'LiteLLM.Team', TeamProps, TeamAttributes> {}\n\nexport const LiteLLMTeam = Resource<LiteLLMTeam>('LiteLLM.Team', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMTeam = (value: unknown): value is LiteLLMTeam =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Team';\n\n/** The team id: the recorded one, else the declared one, else a deterministic physical name. */\nconst idOf = (\n id: string,\n instanceId: string,\n props: TeamProps,\n output: TeamAttributes | undefined,\n) =>\n output?.teamId !== undefined\n ? Effect.succeed(output.teamId)\n : props.teamId !== undefined\n ? Effect.succeed(props.teamId)\n : createPhysicalName({ id, instanceId, lowercase: true, maxLength: 64 });\n\ntype Args<P> = { id: string; instanceId: string; output: TeamAttributes | undefined } & P;\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const teamHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({ id, instanceId, olds, output }: Args<{ olds: TeamProps }>) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.teamAlias), firstProblem(olds));\n }\n const live = yield* readTeam(yield* idOf(id, instanceId, olds, output));\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : live;\n }),\n\n diff: ({ news, output }: { news: Input<TeamProps>; output: TeamAttributes | undefined }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.teamAlias), firstProblem(news));\n if (output === undefined) return undefined;\n // The id is identity: a different declared id is a different team.\n if (news.teamId !== undefined && news.teamId !== output.teamId) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ id, instanceId, news, output }: Args<{ news: TeamProps }>) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.teamAlias), firstProblem(news));\n const teamId = yield* idOf(id, instanceId, news, output);\n const before = yield* readTeam(teamId);\n if (before === undefined) yield* createTeam(createBody(news, teamId));\n else {\n const body = updateBody(news, before);\n if (body !== undefined) yield* updateTeam(body);\n }\n\n // The roster, additively (team-members.ts). Read after the team write so a fresh team is seen.\n if (news.members !== undefined) {\n const live = yield* readTeam(teamId);\n if (live === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.teamAlias, resource: RESOURCE }),\n );\n }\n const missing = membersToAdd(live.members, news.members);\n if (missing.length > 0) yield* addMembers(teamId, missing);\n for (const member of roleChanges(live.members, news.members)) {\n yield* setMemberRole(teamId, member);\n }\n }\n\n const after = yield* readTeam(teamId);\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.teamAlias, resource: RESOURCE }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.teamAlias,\n resource: RESOURCE,\n }),\n );\n }\n return after;\n }),\n\n delete: ({ output }: { output: TeamAttributes }) => deleteTeam(output.teamId),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMTeamProvider = () =>\n Provider.effect(LiteLLMTeam, Effect.succeed(LiteLLMTeam.Provider.of(teamHandlers)));\n",
|
|
46
|
+
"/**\n * The member roster half of `LiteLLM.Team`: reading it, and what a declared roster still needs.\n *\n * ⛔ ADDITIVE ONLY, AND THAT IS A SAFETY RULE, NOT A SHORTCUT. `/team/member_delete` does not only take a\n * user off the roster: it also DELETES every virtual key that user created for the team\n * (`litellm_verificationtoken.delete_many` where `user_id` and `team_id`, `team_endpoints.py`\n * lines 3701-3748, 1.103.0). A seat's key would vanish with a roster edit. So a live member the declaration does not\n * list is never removed and never an error; `delete` on the team is the only removal, and it too\n * deletes the team's keys (team.ts).\n * ★ A ROLE IS CHANGED THROUGH `/team/member_update`, which rewrites the roster entry under the team's\n * advisory lock. `admin` is Enterprise on both routes (`team_endpoints.py` lines 2713-2723 and\n * 3806-3810): a proxy without a licence answers 400, the deploy fails with it, and the roster stays\n * as it was.\n * ⚠️ A NEW USER ID CREATES A USER ROW (`add_new_member`), and only PROXY_ADMIN may name an id that has no\n * user yet (`_validate_member_user_id_provisioning`, 403 otherwise).\n */\nimport { asRow } from './registry-support.ts';\nimport type { TeamMember, TeamMemberAttributes } from './team-types.ts';\n\n/** The roster of a `team_info` row. An entry with no `user_id` (an email-only invite) is skipped. */\nexport const toMemberAttributes = (value: unknown): readonly TeamMemberAttributes[] => {\n if (!Array.isArray(value)) return [];\n const members: TeamMemberAttributes[] = [];\n for (const entry of value) {\n const row = asRow(entry);\n if (typeof row?.['user_id'] === 'string' && row['user_id'] !== '') {\n members.push({\n role: typeof row['role'] === 'string' ? row['role'] : 'user',\n userId: row['user_id'],\n });\n }\n }\n return members.sort((a, b) => a.userId.localeCompare(b.userId));\n};\n\nconst roleOf = (member: TeamMember): string => member.role ?? 'user';\n\n/** Declared members that are not on the live roster at all. */\nexport const membersToAdd = (\n live: readonly TeamMemberAttributes[],\n declared: readonly TeamMember[] | undefined,\n): readonly TeamMember[] =>\n (declared ?? []).filter((member) => !live.some((each) => each.userId === member.userId));\n\n/** Declared members that are on the roster with another role. */\nexport const roleChanges = (\n live: readonly TeamMemberAttributes[],\n declared: readonly TeamMember[] | undefined,\n): readonly TeamMember[] =>\n (declared ?? []).filter((member) =>\n live.some((each) => each.userId === member.userId && each.role !== roleOf(member)),\n );\n\n/** Whether the declared roster is fully present. Extra live members do not count. */\nexport const rosterPending = (\n live: readonly TeamMemberAttributes[],\n declared: readonly TeamMember[] | undefined,\n): boolean => membersToAdd(live, declared).length + roleChanges(live, declared).length > 0;\n",
|
|
47
|
+
"/**\n * The `object_permission` half of `LiteLLM.Team`: reading it, comparing it, and the body that writes it.\n *\n * ★ THE WRITE IS A MERGE BY TOP-LEVEL FIELD (measured, `object_permission_utils.py` lines 114-118,\n * 1.103.0, read from the live container): `prepare_object_permission_upsert` builds\n * `{**existing_fields, **new_object_permission}`, so a field the request leaves out KEEPS its stored\n * value, and a field sent as `[]` (or `{}`) REPLACES it with an empty one. That is what lets this\n * resource manage five fields and leave the other seven of an adopted team alone. docs.litellm.ai/docs/\n * mcp_grant_access says the same: \"unset fields are left unchanged on update\".\n * ★ NULL AND `[]` ARE THE SAME \"NOTHING GRANTED\". A team that never had a grant reads `null` or `[]`\n * (`LiteLLM_ObjectPermissionTable` defaults), so both normalise to an empty list before comparing.\n * ⚠️ `mcp_tool_permissions` CAN COME BACK AS A JSON STRING (it is stored through `safe_dumps` \"for\n * GraphQL compatibility\", and `_resolve_team_allowed_mcp_servers` itself handles both), so the read\n * accepts an object or a string.\n * ⚠️ THE SERVER REFUSES, AND THIS FILE DOES NOT REPEAT IT: a name or alias that matches several MCP\n * servers cannot key `mcp_tool_permissions` (400, `reject_ambiguous_mcp_tool_permission_keys`), and a\n * non-admin caller cannot grant `all-proxy-mcpservers` (403). The one refusal made here is that\n * sentinel, because it silently grants every server on the proxy and is not something a declaration\n * should carry by accident.\n */\nimport type * as team from '@distilled.cloud/litellm/team_management';\nimport {\n asRow,\n canonical,\n firstBadEntry,\n firstDuplicate,\n isBlank,\n sameSet,\n stringsOf,\n} from './registry-support.ts';\nimport type { TeamObjectPermission, TeamObjectPermissionAttributes } from './team-types.ts';\n\n/** The grant that reaches every MCP server on the proxy (`SpecialMCPServerName.all_proxy_servers`). */\nconst ALL_PROXY_SERVERS = 'all-proxy-mcpservers';\n\nconst LISTS = [\n ['mcpServers', 'mcp_servers'],\n ['mcpAccessGroups', 'mcp_access_groups'],\n ['mcpToolsets', 'mcp_toolsets'],\n ['blockedTools', 'blocked_tools'],\n] as const;\n\nconst parseToolPermissions = (value: unknown): Readonly<Record<string, readonly string[]>> => {\n const parsed: unknown = typeof value === 'string' ? safeJson(value) : value;\n const row = asRow(parsed);\n if (row === undefined) return {};\n return Object.fromEntries(\n Object.keys(row)\n .sort()\n .map((server) => [server, canonical(stringsOf(row[server]))]),\n );\n};\n\nconst safeJson = (text: string): unknown => {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n};\n\n/** A live `object_permission` (or `null`) as this resource compares it. */\nexport const toPermissionAttributes = (value: unknown): TeamObjectPermissionAttributes => {\n const row = asRow(value);\n return {\n blockedTools: canonical(stringsOf(row?.['blocked_tools'])),\n mcpAccessGroups: canonical(stringsOf(row?.['mcp_access_groups'])),\n mcpServers: canonical(stringsOf(row?.['mcp_servers'])),\n mcpToolPermissions: parseToolPermissions(row?.['mcp_tool_permissions']),\n mcpToolsets: canonical(stringsOf(row?.['mcp_toolsets'])),\n };\n};\n\nconst sameToolPermissions = (\n live: Readonly<Record<string, readonly string[]>>,\n declared: Readonly<Record<string, readonly string[]>>,\n): boolean => {\n const servers = Object.keys(declared).sort();\n return (\n sameSet(Object.keys(live), servers) &&\n servers.every((server) => sameSet(live[server] ?? [], declared[server] ?? []))\n );\n};\n\n/** Wire names of the DECLARED fields where live and declared disagree. An undeclared field never counts. */\nexport const permissionDiffers = (\n live: TeamObjectPermissionAttributes,\n declared: TeamObjectPermission | undefined,\n): readonly string[] => {\n if (declared === undefined) return [];\n const fields: string[] = [];\n for (const [prop, wire] of LISTS) {\n const wanted = declared[prop];\n if (wanted !== undefined && !sameSet(live[prop], wanted)) fields.push(wire);\n }\n if (\n declared.mcpToolPermissions !== undefined &&\n !sameToolPermissions(live.mcpToolPermissions, declared.mcpToolPermissions)\n ) {\n fields.push('mcp_tool_permissions');\n }\n return fields;\n};\n\n/**\n * The `object_permission` body: the declared fields named in `only` (all of them when `only` is\n * absent, for a create). ⛔ Never a `null`: a merge would read it as \"clear\", and the declaration said\n * a list.\n */\nexport const permissionBody = (\n declared: TeamObjectPermission,\n only?: ReadonlySet<string>,\n): team.LiteLLMObjectPermissionBase => {\n const body: Record<string, unknown> = {};\n for (const [prop, wire] of LISTS) {\n const wanted = declared[prop];\n if (wanted !== undefined && (only === undefined || only.has(wire))) body[wire] = [...wanted];\n }\n if (\n declared.mcpToolPermissions !== undefined &&\n (only === undefined || only.has('mcp_tool_permissions'))\n ) {\n body['mcp_tool_permissions'] = Object.fromEntries(\n Object.entries(declared.mcpToolPermissions).map(([server, tools]) => [server, [...tools]]),\n );\n }\n return body as team.LiteLLMObjectPermissionBase;\n};\n\n/** The first reason a declared object permission is refused, or `undefined`. */\nexport const permissionProblem = (\n declared: TeamObjectPermission | undefined,\n): string | undefined => {\n if (declared === undefined) return undefined;\n for (const [prop] of LISTS) {\n const values = declared[prop] ?? [];\n const bad = firstBadEntry(values);\n if (bad !== undefined)\n return `\\`objectPermission.${prop}\\` has a blank or padded entry (${JSON.stringify(bad)})`;\n const twice = firstDuplicate(values);\n if (twice !== undefined)\n return `\\`objectPermission.${prop}\\` lists ${JSON.stringify(twice)} twice`;\n }\n if ((declared.mcpServers ?? []).includes(ALL_PROXY_SERVERS)) {\n return `\\`objectPermission.mcpServers\\` carries \\`${ALL_PROXY_SERVERS}\\`, which grants EVERY MCP server on the proxy; list the servers instead`;\n }\n for (const [server, tools] of Object.entries(declared.mcpToolPermissions ?? {})) {\n if (isBlank(server) || server !== server.trim()) {\n return '`objectPermission.mcpToolPermissions` has a blank or padded server key';\n }\n const bad = firstBadEntry(tools);\n if (bad !== undefined)\n return `\\`objectPermission.mcpToolPermissions\\` lists a blank or padded tool for ${server}`;\n const twice = firstDuplicate(tools);\n if (twice !== undefined)\n return `\\`objectPermission.mcpToolPermissions\\` lists ${JSON.stringify(twice)} twice for ${server}`;\n }\n return undefined;\n};\n",
|
|
48
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.Team`, testable without a server. The object\n * permission is team-permission.ts, the roster team-members.ts.\n *\n * ★ THE UPDATE IS A PARTIAL ONE (`/team/update` builds its write from `data.json(exclude_unset=True)`,\n * `team_endpoints.py` line 2418, 1.103.0), so it sends the team id and ONLY what differs. Nothing\n * here sends `metadata`, a budget, a limit or any Enterprise field.\n * ★ THE READ IS `/team/info`, and it returns the team's OWN columns: access-group grants are resolved\n * into separate `access_group_*` fields (`_resolve_team_access_group_resources`), never merged into\n * `models` or `object_permission`, so a group cannot make a team look as if it declared a model.\n */\nimport type * as team from '@distilled.cloud/litellm/team_management';\nimport {\n asRow,\n canonical,\n firstBadEntry,\n firstDuplicate,\n isBlank,\n sameSet,\n stringsOf,\n} from './registry-support.ts';\nimport { rosterPending, toMemberAttributes } from './team-members.ts';\nimport {\n permissionBody,\n permissionDiffers,\n permissionProblem,\n toPermissionAttributes,\n} from './team-permission.ts';\nimport type { TeamAttributes, TeamProps } from './team-types.ts';\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: TeamProps): string | undefined => {\n if (isBlank(props.teamAlias) || props.teamAlias !== props.teamAlias.trim()) {\n return '`teamAlias` must be a non-blank name with no leading or trailing space';\n }\n if (\n props.teamId !== undefined &&\n (isBlank(props.teamId) || props.teamId !== props.teamId.trim())\n ) {\n return '`teamId` must be a non-blank id with no leading or trailing space';\n }\n for (const [field, values] of [\n ['models', props.models ?? []],\n ['accessGroupIds', props.accessGroupIds ?? []],\n ] as const) {\n const bad = firstBadEntry(values);\n if (bad !== undefined)\n return `\\`${field}\\` has a blank or padded entry (${JSON.stringify(bad)})`;\n const twice = firstDuplicate(values);\n if (twice !== undefined) return `\\`${field}\\` lists ${JSON.stringify(twice)} twice`;\n }\n const ids = (props.members ?? []).map((member) => member.userId);\n const badId = firstBadEntry(ids);\n if (badId !== undefined)\n return `\\`members\\` has a blank or padded userId (${JSON.stringify(badId)})`;\n const twiceId = firstDuplicate(ids);\n if (twiceId !== undefined) return `\\`members\\` lists ${JSON.stringify(twiceId)} twice`;\n for (const member of props.members ?? []) {\n if (member.role !== undefined && member.role !== 'user' && member.role !== 'admin') {\n return `member ${member.userId} has a role other than user or admin`;\n }\n }\n return permissionProblem(props.objectPermission);\n};\n\n/** One `team_info` row. `undefined` is \"not a team\": the caller refuses the whole answer. */\nexport const toAttributes = (value: unknown): TeamAttributes | undefined => {\n const row = asRow(asRow(value)?.['team_info']);\n if (row === undefined) return undefined;\n const teamId = row['team_id'];\n if (typeof teamId !== 'string') return undefined;\n return {\n accessGroupIds: canonical(stringsOf(row['access_group_ids'])),\n blocked: row['blocked'] === true,\n members: toMemberAttributes(row['members_with_roles']),\n models: canonical(stringsOf(row['models'])),\n objectPermission: toPermissionAttributes(row['object_permission']),\n teamAlias: typeof row['team_alias'] === 'string' ? row['team_alias'] : '',\n teamId,\n };\n};\n\n/** The team's own fields (never the roster) where live and declared disagree, by wire name. */\nexport const teamFieldsDiffering = (live: TeamAttributes, props: TeamProps): readonly string[] => {\n const fields: string[] = [];\n if (live.teamAlias !== props.teamAlias) fields.push('team_alias');\n if (props.models !== undefined && !sameSet(live.models, props.models)) fields.push('models');\n if (props.blocked !== undefined && live.blocked !== props.blocked) fields.push('blocked');\n if (props.accessGroupIds !== undefined && !sameSet(live.accessGroupIds, props.accessGroupIds)) {\n fields.push('access_group_ids');\n }\n for (const wire of permissionDiffers(live.objectPermission, props.objectPermission)) {\n fields.push(`object_permission.${wire}`);\n }\n return fields;\n};\n\n/** Everything that differs, the roster included. Empty means converged. */\nexport const differing = (live: TeamAttributes, props: TeamProps): readonly string[] => [\n ...teamFieldsDiffering(live, props),\n ...(rosterPending(live.members, props.members) ? ['members'] : []),\n];\n\n/** Create body: every declared team field. The roster is added afterwards, through `member_add`. */\nexport const createBody = (props: TeamProps, teamId: string): team.PostNewTeamTeamNewRequest => ({\n team_alias: props.teamAlias,\n team_id: teamId,\n ...(props.models === undefined ? {} : { models: [...props.models] }),\n ...(props.blocked === undefined ? {} : { blocked: props.blocked }),\n ...(props.accessGroupIds === undefined ? {} : { access_group_ids: [...props.accessGroupIds] }),\n ...(props.objectPermission === undefined\n ? {}\n : { object_permission: permissionBody(props.objectPermission) }),\n});\n\n/** Update body: the team id and only the fields that differ. `undefined` when nothing but the roster does. */\nexport const updateBody = (\n props: TeamProps,\n live: TeamAttributes,\n): team.UpdateTeamTeamUpdatePostRequest | undefined => {\n const changed = new Set(teamFieldsDiffering(live, props));\n if (changed.size === 0) return undefined;\n const permission = new Set(\n [...changed]\n .filter((field) => field.startsWith('object_permission.'))\n .map((field) => field.slice(18)),\n );\n return {\n team_id: live.teamId,\n ...(changed.has('team_alias') ? { team_alias: props.teamAlias } : {}),\n ...(changed.has('models') ? { models: [...(props.models ?? [])] } : {}),\n ...(changed.has('blocked') ? { blocked: props.blocked ?? false } : {}),\n ...(changed.has('access_group_ids')\n ? { access_group_ids: [...(props.accessGroupIds ?? [])] }\n : {}),\n ...(props.objectPermission !== undefined && permission.size > 0\n ? { object_permission: permissionBody(props.objectPermission, permission) }\n : {}),\n };\n};\n",
|
|
49
|
+
"/**\n * The seven LiteLLM `/team/*` calls `LiteLLM.Team` uses, through the SDK's typed operations in\n * `@distilled.cloud/litellm/team_management` (LiteLLM 1.103.0):\n *\n * read `getTeamInfoTeamInfo` GET /team/info?team_id=\n * list `listTeamTeamListGet` GET /team/list (only to confirm a delete)\n * create `postNewTeamTeamNew` POST /team/new\n * update `updateTeamTeamUpdatePost` POST /team/update\n * add `teamMemberAddTeamMemberAddPost` POST /team/member_add\n * role `teamMemberUpdateTeamMemberUpdatePost` POST /team/member_update\n * delete `deleteTeamTeamDeletePost` POST /team/delete\n *\n * ⛔ A \"NOT FOUND\" FROM `/team/info` IS NOT PROOF OF ABSENCE. Its lookup sits in `try … except Exception:\n * raise HTTPException(404)` (`team_endpoints.py`, 1.103.0), so a database error answers exactly like a\n * missing team, and `/team/delete` does the same. The SDK declares no 404 for `/team/info`; a 404\n * decodes at run time as its `NotFound` class, which `readTeam` tests by CLASS (registry-support.ts,\n * never a message). A wrong \"absent\" can only end in a `create`, which a duplicate team id turns into a\n * 400 that fails the deploy; nothing is written on a guess.\n * ⛔ DELETE DELETES THE TEAM'S KEYS (`delete_team`, `team_endpoints.py` line 4258: \"delete team and associated team\n * keys\", and `prisma_client.delete_data(team_id_list=…, table_name=\"key\")` at line 4374). It is the single most destructive\n * call in this family, and the reason the resource defaults to `retain`.\n * ★ DELETE IS IDEMPOTENT ONLY AFTER A REAL READ THAT CANNOT LIE. The SDK types `NotFound` for\n * `/team/delete` (a distilled patch), so it is caught with `catchTag`; but the route's own 404 also\n * covers a database error, so success is reported only when `/team/list` (a `find_many`, whose\n * failures are not swallowed) no longer has the id. A team still listed re-raises the ORIGINAL error.\n */\nimport * as team from '@distilled.cloud/litellm/team_management';\nimport * as Effect from 'effect/Effect';\nimport { throughFetch } from './operations.ts';\nimport { asRow, bodyOf, isNotFound } from './registry-support.ts';\nimport { toAttributes } from './team-form.ts';\nimport type { TeamMember } from './team-types.ts';\n\n/** One team BY ID, or `undefined` when the proxy has no such team (see the file header). */\nexport const readTeam = (teamId: string) =>\n throughFetch(team.getTeamInfoTeamInfo({ team_id: teamId })).pipe(\n Effect.map((response) => {\n const row = toAttributes(bodyOf(response));\n return row?.teamId === teamId ? row : undefined;\n }),\n Effect.catch((error) => (isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error))),\n );\n\nexport const createTeam = (body: team.PostNewTeamTeamNewRequest) =>\n throughFetch(team.postNewTeamTeamNew(body)).pipe(Effect.asVoid);\n\nexport const updateTeam = (body: team.UpdateTeamTeamUpdatePostRequest) =>\n throughFetch(team.updateTeamTeamUpdatePost(body)).pipe(Effect.asVoid);\n\n/** One call adds every missing member; LiteLLM skips a user already on the roster. */\nexport const addMembers = (teamId: string, members: readonly TeamMember[]) =>\n throughFetch(\n team.teamMemberAddTeamMemberAddPost({\n member: members.map((member) => ({ role: member.role ?? 'user', user_id: member.userId })),\n team_id: teamId,\n }),\n ).pipe(Effect.asVoid);\n\nexport const setMemberRole = (teamId: string, member: TeamMember) =>\n throughFetch(\n team.teamMemberUpdateTeamMemberUpdatePost({\n role: member.role ?? 'user',\n team_id: teamId,\n user_id: member.userId,\n }),\n ).pipe(Effect.asVoid);\n\n/** Every team id on the proxy. ⚠️ HEAVY: `/team/list` also loads each team's keys. Used only on the delete path. */\nconst listTeamIds = () =>\n throughFetch(team.listTeamTeamListGet({})).pipe(\n Effect.map((response) => {\n const rows: unknown = bodyOf(response);\n return Array.isArray(rows)\n ? rows.flatMap((row) => {\n const id = asRow(row)?.['team_id'];\n return typeof id === 'string' ? [id] : [];\n })\n : [];\n }),\n );\n\nexport const deleteTeam = (teamId: string) =>\n throughFetch(team.deleteTeamTeamDeletePost({ team_ids: [teamId] })).pipe(\n Effect.asVoid,\n Effect.catchTag('NotFound', (original) =>\n listTeamIds().pipe(\n Effect.flatMap((ids) => (ids.includes(teamId) ? Effect.fail(original) : Effect.void)),\n ),\n ),\n );\n",
|
|
50
|
+
"/**\n * `LiteLLM.ToolPolicy` — the input and output trust policy of one tool in LiteLLM's tool registry\n * (`LiteLLM_ToolTable`, `POST /v1/tool/policy`). Props, the enforcement precondition and what is left\n * out: tool-policy-types.ts.\n *\n * ⛔ INERT WITHOUT THE `tool_policy` GUARDRAIL in the proxy's own config (tool-policy-types.ts).\n * ★ ADOPT BY NAME. LiteLLM discovers tools itself, so a tool it has already seen has a live row with no\n * state: `Unowned`, which needs `--adopt` (a row at the defaults is adopted the same way). A tool\n * never seen has no row, and the write creates it.\n * ★ `defaultRemovalPolicy: 'retain'` — there is no delete route. `delete` RESETS the managed fields to\n * `untrusted` (tool-policy-operations.ts), which for an `input_policy` of `blocked` re-opens a tool.\n * Opt in with `.pipe(RemovalPolicy.destroy())`.\n * ⚠️ NO LICENCE GATE ON THESE ROUTES: `tool_management_endpoints.py` has no `premium_user` check\n * (1.103.0). Whether the `tool_policy` guardrail itself is licence-gated is UNVERIFIED (its module\n * has no `premium` reference either).\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryNotConvergedError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\nimport { differing, firstProblem, updateBody } from './tool-policy-form.ts';\nimport { readTool, resetToolPolicy, writeToolPolicy } from './tool-policy-operations.ts';\nimport type { ToolPolicyAttributes, ToolPolicyProps } from './tool-policy-types.ts';\n\nexport type { ToolPolicyAttributes, ToolPolicyProps };\nexport type { ToolInputPolicy, ToolOutputPolicy } from './tool-policy-types.ts';\nexport type { RegistryError as ToolPolicyError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.ToolPolicy';\n\nexport interface LiteLLMToolPolicy extends Resource<\n 'LiteLLM.ToolPolicy',\n ToolPolicyProps,\n ToolPolicyAttributes\n> {}\n\nexport const LiteLLMToolPolicy = Resource<LiteLLMToolPolicy>('LiteLLM.ToolPolicy', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMToolPolicy = (value: unknown): value is LiteLLMToolPolicy =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.ToolPolicy';\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const toolPolicyHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({ olds, output }: { olds: ToolPolicyProps; output: ToolPolicyAttributes | undefined }) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.toolName), firstProblem(olds));\n }\n const live = yield* readTool(output?.toolName ?? olds.toolName);\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : live;\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<ToolPolicyProps>;\n output: ToolPolicyAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.toolName), firstProblem(news));\n if (output === undefined) return undefined;\n // The name is identity: a different tool is a different row.\n if (news.toolName !== output.toolName) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ news }: { news: ToolPolicyProps }) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.toolName), firstProblem(news));\n // ★ THE TOOL THIS DECLARATION NAMES IS READ BY ITS NAME, never by a recorded one: a create-first\n // replace hands reconcile no output, and the name is the whole identity.\n const before = yield* readTool(news.toolName);\n if (before === undefined || differing(before, news).length > 0) {\n yield* writeToolPolicy(updateBody(news, before));\n }\n const after = yield* readTool(news.toolName);\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.toolName, resource: RESOURCE }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.toolName,\n resource: RESOURCE,\n }),\n );\n }\n return after;\n }),\n\n /** ⛔ A RESET, not a removal: see tool-policy-operations.ts. */\n delete: ({ olds }: { olds: ToolPolicyProps }) => resetToolPolicy(olds),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and a discovered tool row has no owner mark. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMToolPolicyProvider = () =>\n Provider.effect(\n LiteLLMToolPolicy,\n Effect.succeed(LiteLLMToolPolicy.Provider.of(toolPolicyHandlers)),\n );\n",
|
|
51
|
+
"/**\n * What a `LiteLLM.ToolPolicy` declares (props) and what is remembered about it (attributes).\n *\n * ★ A TOOL POLICY IS THE TRUST SETTING OF ONE TOOL in `LiteLLM_ToolTable` (`/v1/tool/policy`): an\n * input policy (`untrusted`, `trusted`, `blocked`) and an output policy (`untrusted`, `trusted`).\n * LiteLLM DISCOVERS tools as requests use them and gives each `untrusted`/`untrusted`\n * (`TOOL_POLICY_OPTIONS`, `tool_management_endpoints.py`, 1.103.0); this resource sets them.\n * ⛔ IT DOES NOTHING UNTIL THE `tool_policy` GUARDRAIL IS CONFIGURED. The rows are data; only\n * `guardrails/guardrail_hooks/tool_policy/tool_policy_guardrail.py` reads them, and it is a guardrail\n * the proxy's own config must enable (`guardrails: - guardrail_name: tool_policy, litellm_params:\n * { guardrail: tool_policy, mode: post_call }`, its module docstring). Declaring a policy without it\n * is an inert row, not a control. Measured 2026-09-30: the rendered CT100 config has no\n * `tool_policy` guardrail (count 0), so on that proxy today these rows are not enforced.\n * ⚠️ NOT MODELLED: the team and key OVERRIDES of `POST /v1/tool/policy` (`team_id`, `key_hash`). An\n * override is stored as an entry of that team's or key's `object_permission.blocked_tools`\n * (`add_tool_to_object_permission_blocked`), the same list `LiteLLM.Team` manages through\n * `objectPermission.blockedTools`; two resources writing one list would fight. Block a tool for a\n * team there, and for a key on the key.\n */\nexport type ToolInputPolicy = 'untrusted' | 'trusted' | 'blocked';\nexport type ToolOutputPolicy = 'untrusted' | 'trusted';\n\nexport interface ToolPolicyProps {\n /**\n * The tool's name as LiteLLM records it (for an MCP tool, `<server prefix>-<tool>`). Changing it is\n * a DIFFERENT tool, so it is a replace. Refused if it is one of the names the route table shadows\n * (`list`, `spend`, `policy/options`) or ends in `/detail` or `/logs`: the read is\n * `GET /v1/tool/{tool_name:path}`, which those routes are registered before.\n */\n readonly toolName: string;\n /** Compared and sent only when declared. Declare at least one of the two. */\n readonly inputPolicy?: ToolInputPolicy;\n /** Compared and sent only when declared. Declare at least one of the two. */\n readonly outputPolicy?: ToolOutputPolicy;\n}\n\nexport interface ToolPolicyAttributes {\n readonly toolName: string;\n readonly toolId: string;\n /** What the row carries, as a string: a later LiteLLM may add a value this package does not know. */\n readonly inputPolicy: string;\n readonly outputPolicy: string;\n}\n\n/** What LiteLLM gives a newly discovered tool, and what `delete` resets a managed field to. */\nexport const DEFAULT_TOOL_POLICY = 'untrusted';\nexport const INPUT_POLICIES: readonly ToolInputPolicy[] = ['untrusted', 'trusted', 'blocked'];\nexport const OUTPUT_POLICIES: readonly ToolOutputPolicy[] = ['untrusted', 'trusted'];\n",
|
|
52
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.ToolPolicy`, testable without a server.\n *\n * ★ THE WRITE IS AN UPSERT. `update_tool_policy` (`db/tool_registry_writer.py`, 1.103.0) upserts on\n * `tool_name`: it CREATES the row when the tool has not been seen (a sent field, else `untrusted`),\n * and on an existing row updates only the fields that are sent. So there is no separate create.\n */\nimport type * as tools from '@distilled.cloud/litellm/tools';\nimport { asRow, isBlank } from './registry-support.ts';\nimport {\n INPUT_POLICIES,\n OUTPUT_POLICIES,\n type ToolPolicyAttributes,\n type ToolPolicyProps,\n} from './tool-policy-types.ts';\n\n/** Names the route table hands to another route before `GET /v1/tool/{tool_name:path}` sees them. */\nconst shadowed = (name: string): boolean =>\n name === 'list' ||\n name === 'spend' ||\n name === 'policy/options' ||\n ['/detail', '/logs'].some((suffix) => name.endsWith(suffix));\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: ToolPolicyProps): string | undefined => {\n if (isBlank(props.toolName) || props.toolName !== props.toolName.trim()) {\n return '`toolName` must be a non-blank name with no leading or trailing space';\n }\n if (shadowed(props.toolName)) {\n return '`toolName` is one the /v1/tool route table answers itself (list, spend, policy/options, …/detail, …/logs), so it could never be read back';\n }\n if (props.inputPolicy === undefined && props.outputPolicy === undefined) {\n return 'declare `inputPolicy`, `outputPolicy` or both (LiteLLM answers 400 to neither)';\n }\n if (props.inputPolicy !== undefined && !INPUT_POLICIES.includes(props.inputPolicy)) {\n return `\\`inputPolicy\\` must be one of ${INPUT_POLICIES.join(', ')}`;\n }\n if (props.outputPolicy !== undefined && !OUTPUT_POLICIES.includes(props.outputPolicy)) {\n return `\\`outputPolicy\\` must be one of ${OUTPUT_POLICIES.join(', ')}`;\n }\n return undefined;\n};\n\n/** One tool row. `undefined` is \"not a tool row\". A missing policy reads as LiteLLM's default. */\nexport const toAttributes = (value: unknown): ToolPolicyAttributes | undefined => {\n const row = asRow(value);\n if (row === undefined) return undefined;\n const toolId = row['tool_id'];\n const toolName = row['tool_name'];\n if (typeof toolId !== 'string' || typeof toolName !== 'string') return undefined;\n const policy = (field: string): string =>\n typeof row[field] === 'string' && row[field] !== '' ? row[field] : 'untrusted';\n return {\n inputPolicy: policy('input_policy'),\n outputPolicy: policy('output_policy'),\n toolId,\n toolName,\n };\n};\n\n/** Wire names of the DECLARED fields where live and declared disagree. */\nexport const differing = (\n live: ToolPolicyAttributes,\n props: ToolPolicyProps,\n): readonly string[] => {\n const fields: string[] = [];\n if (props.inputPolicy !== undefined && live.inputPolicy !== props.inputPolicy) {\n fields.push('input_policy');\n }\n if (props.outputPolicy !== undefined && live.outputPolicy !== props.outputPolicy) {\n fields.push('output_policy');\n }\n return fields;\n};\n\n/**\n * The write: the declared fields that differ (all declared ones when the tool row is new). ⚠️ NEVER a\n * `team_id` or `key_hash` — those turn the call into an override of another object's `blocked_tools`.\n */\nexport const updateBody = (\n props: ToolPolicyProps,\n live: ToolPolicyAttributes | undefined,\n): tools.UpdateToolPolicyV1ToolPolicyPostRequest => {\n const changed = live === undefined ? undefined : new Set(differing(live, props));\n return {\n tool_name: props.toolName,\n ...(props.inputPolicy !== undefined && (changed === undefined || changed.has('input_policy'))\n ? { input_policy: props.inputPolicy }\n : {}),\n ...(props.outputPolicy !== undefined && (changed === undefined || changed.has('output_policy'))\n ? { output_policy: props.outputPolicy }\n : {}),\n };\n};\n",
|
|
53
|
+
"/**\n * The two LiteLLM `/v1/tool*` calls `LiteLLM.ToolPolicy` uses, through the SDK's typed operations in\n * `@distilled.cloud/litellm/tools` (LiteLLM 1.103.0):\n *\n * read `getToolV1ToolToolNameGet` GET /v1/tool/{tool_name:path}\n * write `updateToolPolicyV1ToolPolicyPost` POST /v1/tool/policy (an UPSERT on tool_name)\n *\n * ⛔ A READ THAT SAYS \"NOT FOUND\" IS NOT PROOF OF ABSENCE. `db_get_tool` (`db/tool_registry_writer.py`)\n * wraps its query in `except Exception: log; return None`, and the route turns `None` into a 404, so a\n * database error reads exactly like a tool that was never seen. Nothing here depends on it: the write\n * is an upsert, so a wrong \"absent\" ends in a write that succeeds or fails loudly, and the read back\n * after it is what the deploy reports.\n * ⛔ THERE IS NO DELETE ROUTE for a tool row or for its global policy: `DELETE /v1/tool/{name}/overrides`\n * removes only a team or key override. So this resource's delete RESETS the fields it managed to\n * LiteLLM's default (`untrusted`), it does not remove a row. It is a write, so it runs only when the\n * stack opts in (`retain` is the default), and only for a field that is not already the default.\n * ⛔ NO MESSAGE SNIFFING: absence is the SDK's `NotFound` CLASS (registry-support.ts) and nothing else.\n */\nimport * as tools from '@distilled.cloud/litellm/tools';\nimport * as Effect from 'effect/Effect';\nimport { throughFetch } from './operations.ts';\nimport { bodyOf, isNotFound } from './registry-support.ts';\nimport { toAttributes } from './tool-policy-form.ts';\nimport { DEFAULT_TOOL_POLICY, type ToolPolicyProps } from './tool-policy-types.ts';\n\n/** One tool by name, or `undefined` when the proxy has no such row (see the file header). */\nexport const readTool = (toolName: string) =>\n throughFetch(tools.getToolV1ToolToolNameGet({ tool_name: toolName })).pipe(\n Effect.map((response) => {\n const row = toAttributes(bodyOf(response));\n return row?.toolName === toolName ? row : undefined;\n }),\n Effect.catch((error) => (isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error))),\n );\n\nexport const writeToolPolicy = (body: tools.UpdateToolPolicyV1ToolPolicyPostRequest) =>\n throughFetch(tools.updateToolPolicyV1ToolPolicyPost(body)).pipe(Effect.asVoid);\n\n/**\n * The delete: reset each managed field that is not already the default, and nothing else. A tool the\n * proxy does not know, or one already at the defaults, sends no request (an empty body would be a 400).\n */\nexport const resetToolPolicy = (props: ToolPolicyProps) =>\n readTool(props.toolName).pipe(\n Effect.flatMap((live) => {\n if (live === undefined) return Effect.void;\n const inputStale =\n props.inputPolicy !== undefined && live.inputPolicy !== DEFAULT_TOOL_POLICY;\n const outputStale =\n props.outputPolicy !== undefined && live.outputPolicy !== DEFAULT_TOOL_POLICY;\n if (!inputStale && !outputStale) return Effect.void;\n return writeToolPolicy({\n tool_name: props.toolName,\n ...(inputStale ? { input_policy: DEFAULT_TOOL_POLICY } : {}),\n ...(outputStale ? { output_policy: DEFAULT_TOOL_POLICY } : {}),\n });\n }),\n );\n",
|
|
54
|
+
"/**\n * `LiteLLM.Toolset` — one row of LiteLLM's MCP toolset table (`/v1/mcp/toolset`): a named selection of\n * `{ server_id, tool_name }` pairs a key or team can be granted instead of whole servers. Props and\n * what is left out: toolset-types.ts.\n *\n * ★ ADOPT BY NAME. `toolset_name` is unique, so `toolsetName` finds the one live row (from the LIST,\n * which is the only by-name lookup the API has, and which cannot prove absence: toolset-operations.ts).\n * A live row with no state is `Unowned`, so it needs `--adopt`.\n * ★ THE ID IS THE PROXY'S and is what state remembers; every later read is BY ID, from the table.\n * ★ `defaultRemovalPolicy: 'retain'` — deleting a toolset does not detach it: teams and keys that hold\n * its id keep a dangling grant, and every caller reaching MCP through it loses the tools.\n * ⚠️ NO LICENCE GATE ON THESE ROUTES: `mcp_management_endpoints.py`'s toolset block has no\n * `premium_user` check (1.103.0), and docs.litellm.ai/docs/mcp_toolsets makes no Enterprise statement.\n * Writes need PROXY_ADMIN (403 otherwise).\n * ⛔ NO LITELLM CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n LitellmRegistryAbsentAfterWriteError,\n LitellmRegistryAmbiguousError,\n LitellmRegistryNotConvergedError,\n LitellmRegistryUnreadableError,\n} from './registry-errors.ts';\nimport { dieIfInvalid, failIfInvalid } from './registry-support.ts';\nimport { createBody, differing, firstProblem, updateBody } from './toolset-form.ts';\nimport {\n createToolset,\n deleteToolset,\n listToolsets,\n readToolset,\n updateToolset,\n} from './toolset-operations.ts';\nimport type { ToolsetAttributes, ToolsetProps } from './toolset-types.ts';\n\nexport type { ToolsetAttributes, ToolsetProps };\nexport type { RegistryError as ToolsetError } from './registry-errors.ts';\n\nconst RESOURCE = 'LiteLLM.Toolset';\n\nexport interface LiteLLMToolset extends Resource<\n 'LiteLLM.Toolset',\n ToolsetProps,\n ToolsetAttributes\n> {}\n\nexport const LiteLLMToolset = Resource<LiteLLMToolset>('LiteLLM.Toolset', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMToolset = (value: unknown): value is LiteLLMToolset =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Toolset';\n\n/**\n * The live row this declaration means: BY ID from the table when state has one (a vanished row is\n * `undefined`, never taken over by name), else by name from the list. Two rows sharing a name would\n * mean the unique index is gone; refused, never guessed.\n */\nconst locate = (props: ToolsetProps, output: ToolsetAttributes | undefined) =>\n Effect.gen(function* () {\n if (output !== undefined) return yield* readToolset(output.toolsetId);\n const named = (yield* listToolsets()).filter((row) => row.toolsetName === props.toolsetName);\n if (named.length > 1) {\n return yield* Effect.fail(\n new LitellmRegistryAmbiguousError({\n ids: named.map((row) => row.toolsetId),\n name: props.toolsetName,\n resource: RESOURCE,\n }),\n );\n }\n return named[0];\n });\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const toolsetHandlers = {\n /** ⛔ With no `output` this is the adoption probe, so the refusals run here too (registry-support.ts). */\n read: ({ olds, output }: { olds: ToolsetProps; output: ToolsetAttributes | undefined }) =>\n Effect.gen(function* () {\n if (output === undefined) {\n yield* dieIfInvalid(RESOURCE, String(olds.toolsetName), firstProblem(olds));\n }\n const live = yield* locate(olds, output);\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : live;\n }),\n\n diff: ({ news, output }: { news: Input<ToolsetProps>; output: ToolsetAttributes | undefined }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* failIfInvalid(RESOURCE, String(news.toolsetName), firstProblem(news));\n if (output === undefined) return undefined;\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ news, output }: { news: ToolsetProps; output: ToolsetAttributes | undefined }) =>\n Effect.gen(function* () {\n yield* failIfInvalid(RESOURCE, String(news.toolsetName), firstProblem(news));\n const before = yield* locate(news, output);\n let toolsetId: string;\n if (before === undefined) {\n const created = yield* createToolset(createBody(news));\n if (created === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryUnreadableError({\n reason: 'the create answered something that is not a toolset',\n resource: RESOURCE,\n }),\n );\n }\n toolsetId = created.toolsetId;\n } else {\n toolsetId = before.toolsetId;\n if (differing(before, news).length > 0) yield* updateToolset(updateBody(news, before));\n }\n\n // ★ BY ID FROM THE TABLE, never the list: the list is the caller-narrowed, error-swallowing view.\n const after = yield* readToolset(toolsetId);\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmRegistryAbsentAfterWriteError({ name: news.toolsetName, resource: RESOURCE }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmRegistryNotConvergedError({\n fields: left,\n name: news.toolsetName,\n resource: RESOURCE,\n }),\n );\n }\n return after;\n }),\n\n delete: ({ output }: { output: ToolsetAttributes }) => deleteToolset(output.toolsetId),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMToolsetProvider = () =>\n Provider.effect(LiteLLMToolset, Effect.succeed(LiteLLMToolset.Provider.of(toolsetHandlers)));\n",
|
|
55
|
+
"/**\n * Wire bodies, comparison and refusals for `LiteLLM.Toolset`, testable without a server.\n *\n * ★ THE EDIT IS A PARTIAL ONE (`update_mcp_toolset`, 1.103.0, read from the live container): an absent\n * field keeps its stored value, a `null` clears it, EXCEPT `toolset_name` and `tools`, where `null`\n * is a no-op (\"a toolset always has a name and a tool list\"). So `tools` is sent only as a list, and\n * emptying a selection is an explicit `[]`. This resource never sends a `null` name or list.\n */\nimport type * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport { asRow, firstBadEntry, isBlank, stringOrNull } from './registry-support.ts';\nimport type { ToolsetAttributes, ToolsetProps, ToolsetTool } from './toolset-types.ts';\n\nconst pairKey = (tool: ToolsetTool): string => JSON.stringify([tool.serverId, tool.toolName]);\n\n/** Sorted by server, then tool. A set has no order, so this is the form two reads compare in. */\nconst canonicalTools = (tools: readonly ToolsetTool[]): readonly ToolsetTool[] =>\n [...new Map(tools.map((tool) => [pairKey(tool), tool])).values()].sort((a, b) =>\n pairKey(a).localeCompare(pairKey(b)),\n );\n\nconst sameTools = (left: readonly ToolsetTool[], right: readonly ToolsetTool[]): boolean => {\n const a = canonicalTools(left).map(pairKey);\n const b = canonicalTools(right).map(pairKey);\n return a.length === b.length && a.every((each, index) => each === b[index]);\n};\n\n/** The first reason a declaration is refused, or `undefined`. Runs before any request. */\nexport const firstProblem = (props: ToolsetProps): string | undefined => {\n if (isBlank(props.toolsetName) || props.toolsetName !== props.toolsetName.trim()) {\n return '`toolsetName` must be a non-blank name with no leading or trailing space';\n }\n if (props.toolsetName.includes('/')) {\n return '`toolsetName` contains `/`: a toolset is served at /toolset/{toolset_name}/mcp, so that name can never be addressed';\n }\n if (props.description !== undefined && isBlank(props.description)) {\n return '`description` is blank; leave it out to keep the live text';\n }\n if (!Array.isArray(props.tools)) return '`tools` must be a list (an empty one is `[]`)';\n const bad =\n firstBadEntry(props.tools.map((tool) => tool.serverId)) ??\n firstBadEntry(props.tools.map((tool) => tool.toolName));\n if (bad !== undefined)\n return `a tool has a blank or padded serverId or toolName (${JSON.stringify(bad)})`;\n const keys = props.tools.map(pairKey);\n const twice = keys.find((key, index) => keys.indexOf(key) !== index);\n if (twice !== undefined) return `\\`tools\\` lists ${twice} twice`;\n return undefined;\n};\n\n/** One toolset row. `undefined` is \"not a toolset\": the caller refuses the whole answer. */\nexport const toAttributes = (value: unknown): ToolsetAttributes | undefined => {\n const row = asRow(value);\n if (row === undefined) return undefined;\n const toolsetId = row['toolset_id'];\n const toolsetName = row['toolset_name'];\n if (typeof toolsetId !== 'string' || typeof toolsetName !== 'string') return undefined;\n const tools: ToolsetTool[] = [];\n for (const entry of Array.isArray(row['tools']) ? row['tools'] : []) {\n const pair = asRow(entry);\n if (typeof pair?.['server_id'] === 'string' && typeof pair['tool_name'] === 'string') {\n tools.push({ serverId: pair['server_id'], toolName: pair['tool_name'] });\n }\n }\n return {\n description: stringOrNull(row['description']),\n toolsetId,\n toolsetName,\n tools: canonicalTools(tools),\n };\n};\n\n/** Wire names of the fields where live and declared disagree. */\nexport const differing = (live: ToolsetAttributes, props: ToolsetProps): readonly string[] => {\n const fields: string[] = [];\n if (live.toolsetName !== props.toolsetName) fields.push('toolset_name');\n if (props.description !== undefined && live.description !== props.description) {\n fields.push('description');\n }\n if (!sameTools(live.tools, props.tools)) fields.push('tools');\n return fields;\n};\n\nconst wireTools = (tools: readonly ToolsetTool[]): mcp.MCPToolsetTool[] =>\n tools.map((tool) => ({ server_id: tool.serverId, tool_name: tool.toolName }));\n\nexport const createBody = (props: ToolsetProps): mcp.AddMcpToolsetV1McpToolsetPostRequest => ({\n toolset_name: props.toolsetName,\n tools: wireTools(props.tools),\n ...(props.description === undefined ? {} : { description: props.description }),\n});\n\n/** Only what differs, and never a `null` name or list (LiteLLM ignores those). */\nexport const updateBody = (\n props: ToolsetProps,\n live: ToolsetAttributes,\n): mcp.EditMcpToolsetV1McpToolsetPutRequest => {\n const changed = new Set(differing(live, props));\n return {\n toolset_id: live.toolsetId,\n ...(changed.has('toolset_name') ? { toolset_name: props.toolsetName } : {}),\n ...(changed.has('description') ? { description: props.description ?? null } : {}),\n ...(changed.has('tools') ? { tools: wireTools(props.tools) } : {}),\n };\n};\n",
|
|
56
|
+
"/**\n * The five LiteLLM `/v1/mcp/toolset*` calls `LiteLLM.Toolset` uses, through the SDK's typed operations\n * in `@distilled.cloud/litellm/mcp_management` (LiteLLM 1.103.0):\n *\n * list `fetchMcpToolsetsV1McpToolsetGet` GET /v1/mcp/toolset\n * read `fetchMcpToolsetV1McpToolsetToolsetIdGet` GET /v1/mcp/toolset/{toolset_id}\n * create `addMcpToolsetV1McpToolsetPost` POST /v1/mcp/toolset (201)\n * update `editMcpToolsetV1McpToolsetPut` PUT /v1/mcp/toolset\n * delete `removeMcpToolsetV1McpToolsetToolsetIdDelete` DELETE /v1/mcp/toolset/{toolset_id} (202, no body)\n *\n * ⛔ THE LIST CANNOT PROVE ABSENCE, THE BY-ID READ CAN (`mcp_management_endpoints.py` and\n * `toolset_db.py`, read from the live 1.103.0 container). `list_mcp_toolsets` wraps its query in\n * `except Exception: … return []`, so a database error answers an EMPTY LIST with a 200; and the\n * route narrows the answer to the caller's granted toolsets unless the caller is an admin. The by-id\n * route reads the table (`get_mcp_toolset`, a `find_unique`) and answers 404 only for a missing row.\n * So the list is used ONLY to adopt by name, and everything else reads by id. A wrong \"absent\" from\n * the list can only end in a create, which a unique name turns into a 409 that fails the deploy.\n * ⛔ NO MESSAGE SNIFFING. `readToolset` tests the SDK's `NotFound` CLASS (registry-support.ts) and\n * nothing else; a 401, a 403 or a dead network propagates, and is never read as absence.\n * ★ DELETE IS IDEMPOTENT BY A REAL READ: a failed DELETE is swallowed only when a by-id read says the\n * row is gone. A 403 (only PROXY_ADMIN may delete) leaves it live, so it re-raises the original.\n */\nimport * as mcp from '@distilled.cloud/litellm/mcp_management';\nimport * as Effect from 'effect/Effect';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\nimport { LitellmRegistryUnreadableError } from './registry-errors.ts';\nimport { bodyOf, isNotFound } from './registry-support.ts';\nimport { toAttributes } from './toolset-form.ts';\nimport type { ToolsetAttributes } from './toolset-types.ts';\n\nconst RESOURCE = 'LiteLLM.Toolset';\n\n/** ⚠️ NOT PROOF OF ABSENCE: see the file header. Adopt-by-name only. */\nexport const listToolsets = (): Effect.Effect<\n readonly ToolsetAttributes[],\n mcp.FetchMcpToolsetsV1McpToolsetGetError | LitellmRegistryUnreadableError,\n LitellmOpContext\n> =>\n throughFetch(mcp.fetchMcpToolsetsV1McpToolsetGet({})).pipe(\n Effect.flatMap((response) => {\n const rows: unknown = bodyOf(response);\n if (!Array.isArray(rows)) {\n return Effect.fail(\n new LitellmRegistryUnreadableError({ reason: 'not an array', resource: RESOURCE }),\n );\n }\n const parsed = rows.map(toAttributes);\n return parsed.every((row) => row !== undefined)\n ? Effect.succeed(parsed as readonly ToolsetAttributes[])\n : Effect.fail(\n new LitellmRegistryUnreadableError({\n reason: 'a row has no id or name',\n resource: RESOURCE,\n }),\n );\n }),\n );\n\n/**\n * One toolset BY ID, from the table, or `undefined` when the proxy has no such row. The row must carry\n * the id asked for; anything else the proxy answers is \"no such row\".\n */\nexport const readToolset = (toolsetId: string) =>\n throughFetch(mcp.fetchMcpToolsetV1McpToolsetToolsetIdGet({ toolset_id: toolsetId })).pipe(\n Effect.map((response) => {\n const row = toAttributes(bodyOf(response));\n return row?.toolsetId === toolsetId ? row : undefined;\n }),\n Effect.catch((error) => (isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error))),\n );\n\n/** The created row, which carries the id LiteLLM issued. `undefined` when the answer is not a toolset. */\nexport const createToolset = (body: mcp.AddMcpToolsetV1McpToolsetPostRequest) =>\n throughFetch(mcp.addMcpToolsetV1McpToolsetPost(body)).pipe(\n Effect.map((response) => toAttributes(bodyOf(response))),\n );\n\nexport const updateToolset = (body: mcp.EditMcpToolsetV1McpToolsetPutRequest) =>\n throughFetch(mcp.editMcpToolsetV1McpToolsetPut(body)).pipe(Effect.asVoid);\n\nexport const deleteToolset = (toolsetId: string) =>\n throughFetch(mcp.removeMcpToolsetV1McpToolsetToolsetIdDelete({ toolset_id: toolsetId })).pipe(\n Effect.asVoid,\n Effect.catch((original) =>\n readToolset(toolsetId).pipe(\n Effect.catch(() => Effect.fail(original)),\n Effect.flatMap((row) => (row === undefined ? Effect.void : Effect.fail(original))),\n ),\n ),\n );\n",
|
|
57
|
+
"/**\n * `LiteLLM.Model` — one deployment row of LiteLLM's model registry (`/model/info`), an upstream\n * model the proxy's router serves under a group name. A group name can carry SEVERAL deployments\n * (that is LiteLLM's load balancing); this resource owns exactly one row.\n *\n * ★ ADOPT BY NAME. `modelName` finds the live row whose group name matches; declare `id` to pin one\n * instead. A live row with no state is `Unowned`, so it needs `--adopt`. A name matching more\n * than one listed row is refused, never guessed at (`LitellmModelAmbiguousNameError`).\n * ★ `defaultRemovalPolicy: 'retain'` — a deployment's removal takes it out of its routing group for\n * every key that reaches it. Opt in with `.pipe(RemovalPolicy.destroy())`.\n * ⛔ THE CREDENTIAL IS `{ fromEnv: 'NAME' }`, NEVER A VALUE (S25): it is sent in LiteLLM's own\n * reference form `os.environ/NAME` and the proxy resolves it in its own environment — nothing\n * here reads `process.env` (model-credential.ts), so a value could never be sealed truthfully.\n * A row's stored params come back DECRYPTED at v1.103.0 but with `api_key` STRIPPED (measured),\n * so a plan compares the visible fields plus a digest of the DECLARED values (`paramsSeal`) —\n * never the values themselves, which belong in the proxy's DB only (model-form.ts).\n * ⚠️ A RENAME IS A NEW GROUP when the id is not pinned: the old row survives under 'retain', the\n * new group gets a fresh deterministic id. With a pinned id the SAME row is renamed — the caller\n * chose the row (model-types.ts).\n * ⚠️ UNMEASURED AT 1.103.0, guarded by a read back: whether `/model/new` honours a supplied\n * `model_info.id`. The answer is an untyped body, so the id ASKED FOR is the one tracked\n * (model-operations.ts); a proxy that issued its own id fails loudly\n * (`LitellmModelAbsentAfterWriteError`) instead of recording a row it cannot identify.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { noteUnfinished } from '../ownership/resume.ts';\nimport {\n LitellmModelAbsentAfterWriteError,\n LitellmModelConfigFileRowError,\n LitellmModelInvalidError,\n LitellmModelNotConvergedError,\n} from './model-errors.ts';\nimport { claimStatelessRow } from './model-claim.ts';\nimport { createBody, declaredDigest, differing, firstProblem, patchBody } from './model-form.ts';\nimport { sealState } from './model-credential.ts';\nimport { findLive, wantedId } from './model-locate.ts';\nimport {\n createModel,\n deleteModel,\n listModels,\n patchModel,\n readModelRow,\n} from './model-operations.ts';\nimport type { ModelAttributes, ModelProps } from './model-types.ts';\n\nexport type { ModelAttributes, ModelProps };\nexport type { ModelError } from './model-errors.ts';\n\nexport interface LiteLLMModel extends Resource<'LiteLLM.Model', ModelProps, ModelAttributes> {}\n\nexport const LiteLLMModel = Resource<LiteLLMModel>('LiteLLM.Model', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isLiteLLMModel = (value: unknown): value is LiteLLMModel =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Model';\n\nconst refuse = (props: ModelProps) => {\n const problem = firstProblem(props);\n return problem === undefined\n ? Effect.void\n : Effect.fail(new LitellmModelInvalidError({ problem, modelName: String(props.modelName) }));\n};\n\ntype Args<P> = {\n fqn: string;\n id: string;\n instanceId: string;\n output: ModelAttributes | undefined;\n} & P;\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const modelHandlers = {\n /**\n * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW MODEL\n * GETS — so the refusals run here too, as a defect (mcp-server.ts: the cold-start probe must\n * fail the plan, and a recovery read must degrade to \"nothing recovered\", which only a defect\n * does). The v1.103.0 read answers params decrypted with `api_key` stripped, so this refusal\n * is the declaration's (`firstProblem`), before Apply commits props into unencrypted state.\n */\n read: ({ id, instanceId, olds, output }: Args<{ olds: ModelProps }>) =>\n Effect.gen(function* () {\n const problem = output === undefined ? firstProblem(olds) : undefined;\n if (problem !== undefined) {\n return yield* Effect.die(\n new LitellmModelInvalidError({ problem, modelName: String(olds.modelName) }),\n );\n }\n // ★ THE LIST FIRST: one GET answers a name, an id and absence (model-locate.ts).\n const found = yield* findLive(id, instanceId, olds, output);\n if (found === undefined) return undefined;\n // ★ AN INTERRUPTED UNPINNED CREATE. The recovery read reuses the `creating` row's instance\n // id, which is the one `POST /model/new` was given, so the live id matches the physical\n // name. The probe's instance id is minted fresh (Plan.ts `generateInstanceId`) and cannot\n // match a row this declaration created. A declared id can collide, so it stays `Unowned`.\n if (output === undefined && olds.id === undefined) {\n const physical = yield* createPhysicalName({\n id,\n instanceId,\n lowercase: true,\n maxLength: 64,\n });\n if (found.id === physical) return found;\n }\n // The row cannot supply a digest of what was declared; state can (model-form.ts).\n return output === undefined ? Unowned(found) : { ...found, paramsSeal: output.paramsSeal };\n }),\n\n diff: ({\n instanceId,\n news,\n output,\n }: {\n instanceId: string;\n news: Input<ModelProps>;\n output: ModelAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuse(news);\n // ★ No attributes: an unfinished generation of our own. `--adopt` may resume it\n // (ownership/resume.ts). A fresh replace's new instance is never diffed this way.\n if (output === undefined) return yield* noteUnfinished(instanceId);\n // The id is identity: a different declared id is a different row.\n if (news.id !== undefined && news.id !== output.id) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n // A rename without a pinned id is a new group (model.ts header): old row retained.\n if (news.modelName !== output.modelName && news.id === undefined) {\n return { action: 'replace', deleteFirst: false } as const;\n }\n // Visible drift, or a stale seal, is an update: the read strips `api_key`, so the\n // seal is the only signal a declared credential moved (model-credential.ts).\n // Reconcile then decides the write: a bare matching adopt records its seal\n // locally, and a declaration that manages an invisible param PATCHes once to\n // converge it.\n return differing(output, news).length === 0 && sealState(news, output.paramsSeal) === 'match'\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({ fqn, id, instanceId, news, output }: Args<{ news: ModelProps }>) =>\n Effect.gen(function* () {\n yield* refuse(news);\n const before = yield* findLive(id, instanceId, news, output);\n let sealed = output?.paramsSeal ?? '';\n let modelId: string;\n\n if (before === undefined) {\n const wanted = yield* wantedId(id, instanceId, news, output);\n yield* createModel(createBody(news, wanted));\n // The id ASKED FOR is the one tracked; the read back refuses to claim a row the\n // proxy filed elsewhere (model.ts header's unmeasured note, model-operations.ts).\n modelId = wanted;\n sealed = declaredDigest(news);\n } else {\n // ⛔ CONFIG-FILE ROWS ARE ALWAYS REFUSED. The DB API cannot manage a row the proxy\n // serves from its config file (`model_info.db_model: false`), regardless of whether\n // this stack already holds state for it.\n if (before.dbModel === false) {\n return yield* Effect.fail(\n new LitellmModelConfigFileRowError({ id: before.id, modelName: news.modelName }),\n );\n }\n // ⛔ NOTHING IS WRITTEN OVER A ROW THIS STACK HOLDS NO STATE FOR, unless the gate says\n // so (model-claim.ts): a deterministic id is ours, an older generation's id is refused\n // with a state-level recovery, and anything else needs `--adopt` on a create or an\n // unfinished generation. `output` may be the live attributes the plan stripped from\n // `Unowned(found)` (`paramsSeal` is empty until a deploy commits a seal), so the gate\n // runs both when output is undefined and when it is only a probe result.\n const stateless = output === undefined || output.paramsSeal === '';\n if (stateless) {\n yield* claimStatelessRow({\n beforeId: before.id,\n fqn,\n instanceId,\n news,\n output,\n wanted: yield* wantedId(id, instanceId, news, output),\n });\n }\n modelId = before.id;\n const drifted = differing(before, news);\n const sealStale = sealState(news, sealed) === 'stale';\n // ⛔ A STAMPING ADOPT, AND ONE MORE REASON TO WRITE. A live row starts `paramsSeal: ''`\n // (model-form.ts), and the PATCH merge rewrites the managed `litellm_params`: a bare\n // adopt of an already-matching row records the digest locally and writes nothing. A\n // declaration that manages an invisible param (`apiKey` / `apiBase` — never on the\n // read) must still write once: a matching-looking row can carry a DIFFERENT stored\n // reference, and a local seal over it would freeze that divergence forever. A seal\n // that EXISTS and no longer matches is a declaration this resource already owns —\n // that one writes too, with nulls for the five non-`None` defaults (model-form.ts).\n const sealNeedsStamp =\n sealStale && (sealed !== '' || news.apiKey !== undefined || news.apiBase !== undefined);\n if (drifted.length > 0 || sealNeedsStamp) {\n yield* patchModel(patchBody(news, before));\n }\n if (drifted.length > 0 || sealStale) sealed = declaredDigest(news);\n }\n\n // ★ THE LIST FIRST, THEN BY ID: a row the list does not show yet (create commits, list\n // refreshes lazily) is still a live row if the by-id read answers it.\n const after =\n (yield* listModels()).find((row) => row.id === modelId) ?? (yield* readModelRow(modelId));\n if (after === undefined) {\n return yield* Effect.fail(\n new LitellmModelAbsentAfterWriteError({ id: modelId, modelName: news.modelName }),\n );\n }\n const left = differing(after, news);\n if (left.length > 0) {\n return yield* Effect.fail(\n new LitellmModelNotConvergedError({\n fields: left,\n id: modelId,\n modelName: news.modelName,\n }),\n );\n }\n return { ...after, paramsSeal: sealed };\n }),\n\n delete: ({ output }: { output: ModelAttributes }) => deleteModel(output.id),\n\n /** ⛔ EMPTY: adoption is an explicit act (`--adopt`), and no ownership mark exists to filter by. */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMModelProvider = () =>\n Provider.effect(LiteLLMModel, Effect.succeed(LiteLLMModel.Provider.of(modelHandlers)));\n",
|
|
58
|
+
"/**\n * The typed refusals `LiteLLM.Model` raises on top of `@distilled.cloud/litellm`'s own SDK errors.\n *\n * ⛔ NO MESSAGE HERE MAY CARRY A CREDENTIAL: only names, ids, field names and the NAME of an\n * environment variable, exactly like mcp-server-errors.ts.\n */\nimport type { BadRequest, NotFound } from '@distilled.cloud/litellm/Errors';\nimport type * as models from '@distilled.cloud/litellm/model_management';\nimport * as Data from 'effect/Data';\n\n/** A declaration the resource refuses before any request: a combination nobody meant. */\nexport class LitellmModelInvalidError extends Data.TaggedError('LitellmModelInvalidError')<{\n readonly modelName: string;\n readonly problem: string;\n}> {\n override get message(): string {\n return `LiteLLM.Model \"${this.modelName}\": ${this.problem}.`;\n }\n}\n\n/**\n * More than one live deployment carries the declared `model_name`, so adopt-by-name cannot say\n * which one is meant — LiteLLM runs several deployments under one group name on purpose. Declare\n * `id` to pick one; nothing is adopted, updated or deleted on a guess.\n */\nexport class LitellmModelAmbiguousNameError extends Data.TaggedError(\n 'LitellmModelAmbiguousNameError',\n)<{\n readonly modelName: string;\n readonly ids: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Model \"${this.modelName}\": ${this.ids.length} live deployments carry that name ` +\n `(${this.ids.join(', ')}). Declare \\`id\\` to choose one.`\n );\n }\n}\n\n/** `GET /model/info` answered something that is not a list of deployments. */\nexport class LitellmModelUnreadableError extends Data.TaggedError('LitellmModelUnreadableError')<{\n readonly reason: string;\n}> {\n override get message(): string {\n return `LiteLLM.Model: the deployment list is unreadable (${this.reason}).`;\n }\n}\n\n/**\n * A create or update returned no error, yet no read answers a deployment with the id it was written\n * under: not in the list, and not by the id it would carry.\n */\nexport class LitellmModelAbsentAfterWriteError extends Data.TaggedError(\n 'LitellmModelAbsentAfterWriteError',\n)<{\n readonly modelName: string;\n readonly id: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Model \"${this.modelName}\": the write of ${this.id} returned no error but the ` +\n 'deployment is absent from the list and from the by-id read.'\n );\n }\n}\n\n/**\n * The write returned no error but the deployment still differs from the declaration. The PATCH\n * merge writes the keys it was sent (`update_db_model` at v1.103.0); a field the read-back still\n * shows different is refused here instead of being reported as converged.\n */\nexport class LitellmModelNotConvergedError extends Data.TaggedError(\n 'LitellmModelNotConvergedError',\n)<{\n readonly modelName: string;\n readonly id: string;\n readonly fields: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Model \"${this.modelName}\": after the write, ${this.id} still differs from the ` +\n `declaration in ${this.fields.join(', ')}.`\n );\n }\n}\n\n/**\n * A reconcile with no state found a live row under the declared name whose id is not the one this\n * declaration would create, and nothing vouchsafes it: writing over it would take over another\n * owner's deployment. `replace` says whether this generation is a replace's new identity (a changed\n * declared id), which `--adopt` does not cover — the object must be removed first.\n */\nexport class LitellmModelForeignRowError extends Data.TaggedError('LitellmModelForeignRowError')<{\n readonly modelName: string;\n readonly id: string;\n readonly replace: boolean;\n}> {\n override get message(): string {\n const remedy = this.replace\n ? 'It is the new identity of a replace, which --adopt does not cover: remove it first.'\n : 'Deploy with --adopt to take it over, or remove it first.';\n return (\n `LiteLLM.Model \"${this.modelName}\": the live row ${this.id} already exists and this stack ` +\n `holds no state for it. Nothing was written. ${remedy}`\n );\n }\n}\n\n/**\n * The row a reconcile found is served from the proxy's config file (`model_info.db_model: false`),\n * not from the database, so the DB API cannot manage it. Remove it from the config file first.\n */\n/**\n * The live row a stateless reconcile found is an OLDER generation of this FQN's own state chain\n * (`attr.id` walked through `old`). It is still the serving deployment: the replacement that\n * followed it never committed. Taking the row over would store that id on the new generation, and\n * `destroy()` then deletes the old generation by the same id.\n * ⛔ THE RECOVERY IS STATE, NOT THE ROW. `alchemy state rm` deletes the state record and leaves the\n * deployment; deleting the live row takes the serving model out of its group.\n */\nexport class LitellmModelPriorGenerationError extends Data.TaggedError(\n 'LitellmModelPriorGenerationError',\n)<{\n readonly modelName: string;\n readonly id: string;\n readonly fqn: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Model \"${this.modelName}\": the live row ${this.id} is an older generation still ` +\n `recorded on ${this.fqn}. Nothing was written. Taking it over would store that id on the ` +\n 'in-flight replacement, and destroy() then deletes the old generation by the same id. ' +\n \"Drop this resource's state row (alchemy state rm, which leaves the deployment) and \" +\n 'deploy again with --adopt. Do not delete the live row.'\n );\n }\n}\n\nexport class LitellmModelConfigFileRowError extends Data.TaggedError(\n 'LitellmModelConfigFileRowError',\n)<{\n readonly modelName: string;\n readonly id: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Model \"${this.modelName}\": the live row ${this.id} is served from the proxy's ` +\n 'config file (db_model: false), not from the database, so the DB API cannot manage it. ' +\n 'Remove it from the config file first.'\n );\n }\n}\n\nexport type ModelError =\n | models.AddNewModelModelNewPostError\n | models.DeleteModelModelDeletePostError\n | models.GetModelInfoV1ModelInfoError\n | models.PatchModelModelModelIdUpdatePatchError\n | BadRequest\n | NotFound\n | LitellmModelInvalidError\n | LitellmModelAmbiguousNameError\n | LitellmModelUnreadableError\n | LitellmModelAbsentAfterWriteError\n | LitellmModelNotConvergedError\n | LitellmModelForeignRowError\n | LitellmModelPriorGenerationError\n | LitellmModelConfigFileRowError;\n",
|
|
59
|
+
"/**\n * Whether a stateless `LiteLLM.Model` reconcile may write the live row it found.\n *\n * ⛔ A ROW WITH NO SEAL IS NOT YET THIS GENERATION'S. `output` is undefined on a create and on a\n * `replacing` row Apply committed before reconcile (Apply.ts), and a probe result carries\n * `paramsSeal: ''`. Either way the live row may belong to someone else.\n * ★ TWO ROWS THAT LOOK THE SAME AND ARE NOT. A deterministic id this declaration would create is\n * ours by construction. An older generation's `attr.id` (ownership/rows.ts) is our own serving\n * deployment from before this replacement — recording it here makes destroy() delete it. A\n * declared id needs `--adopt` on a create or an unfinished generation; an unpinned row with any\n * other id is adopted only on a create (ownership/adopt.ts).\n */\nimport * as Effect from 'effect/Effect';\nimport { adoptEnabled, adoptsAtApply } from '../ownership/adopt.ts';\nimport { adopting } from '../ownership/adopting.ts';\nimport { isCreate, olderGenerationIds } from '../ownership/rows.ts';\nimport { unfinished } from '../ownership/resume.ts';\nimport { LitellmModelForeignRowError, LitellmModelPriorGenerationError } from './model-errors.ts';\nimport type { ModelAttributes, ModelProps } from './model-types.ts';\n\nexport const claimStatelessRow = (args: {\n readonly fqn: string;\n readonly instanceId: string;\n readonly output: ModelAttributes | undefined;\n readonly news: ModelProps;\n readonly beforeId: string;\n readonly wanted: string;\n}): Effect.Effect<void, LitellmModelPriorGenerationError | LitellmModelForeignRowError> =>\n Effect.gen(function* () {\n const older = yield* olderGenerationIds(args.fqn, args.instanceId);\n // ⛔ NOT OURS, EVEN WITH --adopt. The id is the previous generation's. Accepting it stores\n // that id on this generation; destroy() GC then deletes the old generation by the same id\n // (Apply.ts ~2168). The way back is `alchemy state rm`, then a deploy with --adopt.\n if (older.includes(args.beforeId)) {\n return yield* Effect.fail(\n new LitellmModelPriorGenerationError({\n fqn: args.fqn,\n id: args.beforeId,\n modelName: args.news.modelName,\n }),\n );\n }\n // ⛔ ONLY A DETERMINISTIC ID IS OURS BY CONSTRUCTION. With no declared id, `wanted` is the\n // physical name this declaration would create. A row found by name with any other id is not\n // the one this generation posted — a resumed unpinned replace must not `--adopt` it.\n // A DECLARED id is a name a human chose, so a matching row goes through the adoption gate,\n // which `--adopt` may pass for a create or an unfinished generation (the read-back that\n // died after `POST /model/new`).\n const unpinned = args.news.id === undefined;\n const ours = unpinned && args.beforeId === args.wanted;\n const owner = { fqn: args.fqn, instanceId: args.instanceId, output: args.output };\n const isAdoption = yield* adopting(owner, () => Effect.succeed(false));\n const creating = yield* isCreate(args.fqn, args.instanceId);\n const mayAdopt = unpinned\n ? creating && (yield* adoptEnabled(args.fqn))\n : yield* adoptsAtApply(owner);\n if (!ours && !isAdoption && !mayAdopt) {\n // A declared id's unfinished generation is the one `--adopt` resumes. An unpinned row whose\n // id is not the physical name is a different deployment, replace or not.\n const replace = unpinned ? !creating : !creating && !(yield* unfinished(args.instanceId));\n return yield* Effect.fail(\n new LitellmModelForeignRowError({\n id: args.beforeId,\n modelName: args.news.modelName,\n replace,\n }),\n );\n }\n });\n",
|
|
60
|
+
"/**\n * Whether a `diff` or `reconcile` that holds attributes is looking at an ADOPTION — a live object\n * that reached this stack's state by being FOUND, not by this resource's own create — so a family\n * whose adoption must never change what it takes over (`Proxmox.Lxc`) can refuse any difference\n * instead of writing it. adopt.ts and probe.ts decide WHETHER an object may be claimed; this says,\n * once it is claimed, whether the claim is an adoption.\n *\n * ★ STATE ALREADY SAYS WHICH, AT BOTH HALVES OF A DEPLOY (alchemy 2.0.0-beta.79):\n * · PLAN, A COLD ADOPTION. The probe mints an instance id no row can hold (Plan.ts\n * `generateInstanceId()`), and `diff` then runs with that id and the attributes the probe\n * read. So no row holds the id.\n * · APPLY, THE SAME ADOPTION. Apply commits an `updating` row with `adopting: true` under that id\n * BEFORE it calls reconcile, and the flag stays until the update commits (Apply.ts). An\n * adoption whose apply was interrupted keeps it, so the next plan's `diff` sees it too.\n * · A RECOVERED CREATE. A `creating` row with no attributes, whose live object the recovery read\n * found. It is this resource's own create when the row proves it (probe.ts `provenOurs`, the\n * same proof the read asked). Otherwise the plan went on only because `--adopt` said so — an\n * adoption like any other.\n * · Any other row with attributes is state vouching for the object: not an adoption.\n * ⚠️ OUTSIDE A STACK — no Stack or State in context, or a store that errors (rows.ts) — no row\n * holds anything, so every call is an adoption: the strict answer, never the permissive one. A\n * family then refuses an ordinary update's write as if it were an adoption's. `alchemy deploy`\n * and the Alchemist session provide both services.\n */\nimport * as Effect from 'effect/Effect';\nimport type { Owner } from './adopt.ts';\nimport { provenOurs } from './probe.ts';\nimport { recordedGeneration } from './rows.ts';\n\n/**\n * Whether `owner`'s call is an adoption (header). `settled` is the family's \"live matches these\n * props\", asked only for a recovered create, with the props of the row that recorded it.\n * ⛔ A CREATE IS NEVER ONE HERE. `output` undefined is reconcile's create path, which has its own\n * rule for a live object it finds (adopt.ts `refuseTakeover`).\n */\nexport const adopting = <E, R>(\n owner: Owner,\n settled: (recorded: unknown) => Effect.Effect<boolean, E, R>,\n): Effect.Effect<boolean, never, R> =>\n Effect.gen(function* () {\n if (owner.output === undefined) return false;\n const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);\n if (generation === undefined || generation.adopting === true) return true;\n if (generation.attr !== undefined && generation.attr !== null) return false;\n return !(yield* provenOurs(owner, settled(generation.props)));\n });\n",
|
|
61
|
+
"/**\n * `read` with no attributes — the plan-time half of \"nothing is adopted without `--adopt`\"\n * (adopt.ts has the apply-time half). Alchemy asks it in two situations and passes `output:\n * undefined` in both (alchemy beta.79 Plan.ts):\n * · THE ADOPTION PROBE — no state row at all; `olds` is the declaration, `instanceId` fresh.\n * · THE RECOVERY READ — a `creating` row whose create was interrupted after its write and before\n * its commit; `olds` is that row's props, `instanceId` the row's. (Apply.ts asks the same for a\n * row it must delete that never recorded attributes.)\n * The answer routes the plan: `undefined` → create; plain attributes → ours; `Unowned(…)` → the\n * plan fails with OwnedBySomeoneElse unless `--adopt` / `adopt(true)` (AdoptPolicy.ts).\n *\n * ⛔ THE PROBE ALWAYS ANSWERS `Unowned` FOR A LIVE OBJECT, EVEN ONE IDENTICAL TO THE DECLARATION\n * (decided 2026-09-21). Identical is not ours: a name another stack, a person or an old script put\n * there reads the same, and once state claims it, a delete under `destroy` removes it from its\n * real owner. 🔴 Measured before the change (openbao/rename-adoption.test.ts): a new logical id\n * for a live Bao name adopted it silently, and the old id's delete then removed it, green.\n * ★ THE RECOVERY READ STILL ADOPTS WHAT THE INTERRUPTED CREATE MADE — the instance is ours by the\n * state row, and the object is proven ours when it matches that row's props (`settled`). One that\n * does not match may have lost a race to someone else: `Unowned`, as Plan.ts itself asks.\n * ⛔ ONLY WHEN THE ROW'S PROPS ARE THE WHOLE DECLARATION (whole.ts). A row written while a prop was\n * still an Output has a hole there, and \"matches\" against a hole proves nothing — nor had that\n * deploy's plan ever asked whose object it was, because Alchemy skips the probe for an Output.\n * Such a create resumes with `--adopt`.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport * as Effect from 'effect/Effect';\nimport type { Owner } from './adopt.ts';\nimport { recordedInstance } from './rows.ts';\n\n/**\n * Whether the state store proves the live object this instance's own: a row records `ask.instanceId`\n * with its whole declaration (whole.ts) AND `settled` — the family's own \"live matches these props\"\n * — says so. The one proof ownership/ accepts for a live object state holds no attributes for; the\n * recovery read (`ownedRead`) and an adoption check (adopting.ts) both ask it.\n * ⛔ `settled` NEVER FAILS THE CALLER. The recovery read runs with the INTERRUPTED deploy's props, so\n * a declaration that cannot be evaluated any more (a fragments directory since moved, a group\n * name since renamed) would fail every later plan, the fix included. It reads as \"not proven\n * ours\" instead, with the reason logged.\n */\nexport const provenOurs = <E, R>(\n ask: { readonly fqn: string; readonly instanceId: string },\n settled: Effect.Effect<boolean, E, R>,\n): Effect.Effect<boolean, never, R> =>\n Effect.gen(function* () {\n const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);\n if (recorded === 'absent') return false;\n if (recorded === 'partial') {\n yield* Effect.logWarning(\n `${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` +\n 'was still an Output when it was written), so the live object is not proven ours',\n );\n return false;\n }\n const unproven = (reason: unknown) =>\n Effect.as(\n Effect.logWarning(\n `${ask.fqn}: the interrupted create's object could not be compared with its props ` +\n `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`,\n ),\n false,\n );\n return yield* settled.pipe(Effect.catch(unproven), Effect.catchDefect(unproven));\n });\n\n/**\n * A family's `read` answer. With attributes in state it is `found` as is. Without, it is the probe\n * or the recovery read (header): `found` is ours only when `provenOurs` says so, else `Unowned`,\n * which `--adopt` resolves.\n */\nexport const ownedRead = <A extends object, E, R>(\n ask: Owner,\n found: A | undefined,\n settled: Effect.Effect<boolean, E, R>,\n): Effect.Effect<A | undefined, never, R> =>\n Effect.gen(function* () {\n if (found === undefined || ask.output !== undefined) return found;\n return (yield* provenOurs(ask, settled)) ? found : Unowned(found);\n });\n",
|
|
62
|
+
"/**\n * What a `LiteLLM.Model` declares (props) and what is remembered about it (attributes).\n *\n * ⛔ NO CREDENTIAL VALUE IS A PROP OR AN ATTRIBUTE (S25). Alchemy persists props AND attributes\n * unencrypted (`StateEncoding.ts` writes a `Redacted` value's inner string beside its tag,\n * measured on alchemy 2.0.0-beta.79), so `apiKey` is `{ fromEnv: 'NAME' }`: the NAME is stored,\n * the value lives only in the deploying process and on the wire, and it is sent in LiteLLM's own\n * reference form `os.environ/NAME` — so the PROXY reads it from ITS environment at call time and\n * the proxy's own DB row never carries a literal either. A `Redacted` prop would still land in\n * state as plaintext (mcp-server-types.ts's reasoning, same store).\n * ⛔ THE LIVE ROW'S `litellm_params` ARE NEVER MIRRORED INTO ATTRIBUTES. At v1.103.0 the read\n * answers the stored params DECRYPTED but STRIPS `api_key` (measured on a live 1.103.0 proxy),\n * and anything mirrored would land in state as plaintext — Alchemy persists attributes\n * unencrypted. So a read compares only the visible fields the proxy returns, and this\n * resource's own attributes carry a DIGEST of the DECLARED values (`paramsSeal`), never the\n * values: that is how a plan notices a declared credential moved when the read cannot show it.\n * ⚠️ NOT MODELLED, on purpose: the rest of `litellm_params` (rpm/tpm, timeouts, fallbacks,\n * wildcard routing params, `mock_response`, vertex/aws credential blobs, …), cost fields on\n * `model_info` beyond what the read returns, `teams`, and the config-file-only rows\n * (`db_model: false`) — a reconcile that finds one REFUSES it (model-errors.ts): the DB\n * API cannot manage what the proxy serves from its config file.\n */\nimport type { FromEnv } from '../secrets/write-only.ts';\n\n/**\n * The LiteLLM-SDK path of the deployment: a provider prefix, then the upstream model, such as\n * `xai/grok-4.7` or `cloudflare/@cf/meta/llama-3.1-8b-instruct`. Required: LiteLLM needs it to\n * pick the provider handler, and an unprefixed name is ambiguous.\n */\nexport type ModelLiteLLMPath = string;\n\nexport interface ModelProps {\n /**\n * The group name callers use (`/chat/completions` `model: …`), one group = one or more\n * deployments. This is also how an existing row is ADOPTED: the live row whose `model_name`\n * equals it. ⛔ Renaming a group changes nothing upstream — LiteLLM keys deployments by id —\n * so a changed `modelName` with no pinned `id` is a REPLACE: the new group gets a fresh id, and\n * the old row survives under the default `RemovalPolicy.retain()` (opt in to `destroy()` to\n * delete it). A pinned `id` is that row: the same deployment is renamed in place\n * (`patchBody` sends `model_name`), and keys routing to the old name miss.\n */\n readonly modelName: string;\n /**\n * Pins the live row (adopts by id) or chooses the id a create asks for. Without it, a live row\n * is found by `modelName` and a new one gets a deterministic physical name. A group name is\n * shared by several deployments on a live proxy: one id selects exactly one.\n */\n readonly id?: string;\n /** The LiteLLM-SDK path, provider prefix included (`xai/grok-4.7`, `cloudflare/@cf/...`). */\n readonly model: string;\n /**\n * The upstream base URL, when the deployment needs one. ⛔ Refused if it carries userinfo, a\n * fragment or a secret-looking query parameter (`?token=…`): the URL is a prop, so it lands in\n * state. Declare the credential as `apiKey` instead.\n */\n readonly apiBase?: string;\n /**\n * The upstream credential, by the NAME of the environment variable that holds it — sent to the\n * proxy as LiteLLM's own reference form `os.environ/NAME`, never a literal value. Omitted\n * leaves the live credential exactly as the proxy holds it (an adopted row is never blanked).\n */\n readonly apiKey?: FromEnv;\n /** The deployment's capabilities, `mode` in LiteLLM's model_info — `chat`, `embedding`, …. */\n readonly mode?: string;\n /** The upstream model this one masquerades as, for cost/prompt mapping. */\n readonly baseModel?: string;\n /** Access groups this deployment belongs to. Always compared (as a set), default empty. */\n readonly accessGroups?: readonly string[];\n}\n\nexport interface ModelAttributes {\n readonly id: string;\n readonly modelName: string;\n /** Whether the row is served from the database (`model_info.db_model`); false = config-file row. */\n readonly dbModel: boolean;\n /** The provider-prefixed path as the live row carries it (null when the read omits it). */\n readonly model: string | null;\n readonly mode: string | null;\n readonly baseModel: string | null;\n readonly accessGroups: readonly string[];\n /**\n * Digest of the DECLARED params this write would send (`scrypt:<salt>:<digest>`), or `''`.\n * ⛔ Never the values, never a digest of the LIVE row. This is how a plan notices that the\n * environment variable behind `apiKey` rotated: the read strips `api_key`, so the seal is the\n * only signal a declared credential moved (model-form.ts's `declaredDigest`).\n */\n readonly paramsSeal: string;\n}\n\n/** Whether a value is not a string with something in it. Takes `unknown`: a declaration can say anything. */\nexport const isBlank = (value: unknown): boolean =>\n typeof value !== 'string' || value.trim() === '';\n\nexport const MODES: readonly string[] = [\n 'chat',\n 'completion',\n 'embedding',\n 'audio_transcription',\n 'audio_speech',\n 'image_generation',\n 'image_editing',\n 'moderation',\n 'rerank',\n 'search',\n 'realtime',\n 'batch',\n 'video',\n 'ocr',\n];\n\n/** Whether `model` carries a provider prefix, LiteLLM's `litellm.utils.get_llm_provider` rule. */\nexport const hasProviderPrefix = (model: string): boolean => /^[a-z0-9_-]+\\//i.test(model);\n",
|
|
63
|
+
"/**\n * The seal of a `LiteLLM.Model`'s declared `litellm_params` — the one way a plan notices a changed\n * `model`, `apiBase` or `apiKey` when the live row hides them.\n *\n * ★ REFERENCE-ONLY, AND THEREFORE PURE. The credential this resource declares is LiteLLM's own\n * `os.environ/NAME` reference: the PROXY resolves the variable in ITS environment at inference\n * time, and neither the row it writes nor the wire body ever carries the value. So this module\n * never reads `process.env` — there is no value here to read, and a digest of a value that lives\n * on another host would be false drift on every plan whose deploying environment differed.\n * ⛔ THE SEAL COVERS THE DECLARED VALUES ONLY. At v1.103.0 the read answers the stored params\n * DECRYPTED but STRIPS `api_key` (model-types.ts), and a credential that did come back would\n * only be a plaintext in state — so the live row never contributes to this comparison:\n * attributes carry the digest of what was DECLARED, `paramsSeal`, and a plan that changes any\n * of `model`, `apiBase` or the variable behind `apiKey` sees the digest change and updates.\n * What was last written is remembered exactly; a value the proxy strips stays quiet because\n * the comparison never involves it.\n * ⛔ ADOPTED OR FOREIGN ROWS START `paramsSeal: ''` (a row cannot supply a digest of values it\n * hides), which reads as `stale`. A bare adopt of a visibly matching row records the digest\n * locally with no write; a stale seal on a declaration that manages a param the read never\n * shows (`api_key`, `api_base`) forces one stamping PATCH, so a different stored reference is\n * never sealed over. After that the seal is stable. `seals/write-only.ts` carries the scrypt\n * rules; see also mcp-server-credential.ts for the credential that LiteLLM STORES (this one is\n * only referenced).\n */\nimport { seal, sealMatches } from '../secrets/write-only.ts';\nimport type { ModelProps } from './model-types.ts';\n\n/**\n * The declared `litellm_params` as the reference form names them — the input to the seal. `model`\n * and `api_base` are what the proxy routes on; `api_key` is the reference LiteLLM resolves in its\n * own environment. No value, ever: there is no lookup and no environment here.\n */\nexport const declaredValues = (props: ModelProps): Record<string, string> => {\n const values: Record<string, string> = { model: props.model };\n if (props.apiBase !== undefined) values['api_base'] = props.apiBase;\n if (props.apiKey !== undefined) values['api_key'] = `os.environ/${props.apiKey.fromEnv}`;\n return values;\n};\n\n/** `scrypt:<salt>:<digest>` of declared values, for `paramsSeal`. ⛔ Never a secret value. */\nexport const sealFromValues = (values: Record<string, string>): string => seal(values);\n\n/** Whether the declaration's values are the ones `sealed` was made from. */\nexport const sealState = (props: ModelProps, sealed: string): 'match' | 'stale' =>\n sealMatches(sealed, declaredValues(props)) ? 'match' : 'stale';\n",
|
|
64
|
+
"/**\n * Refusals, wire bodies, the read shape and the comparison for `LiteLLM.Model`, testable without\n * a server. Credential handling is model-credential.ts's; the URL rules are mcp-server-url.ts's.\n *\n * ⛔ WHAT IS COMPARED, WITH ENCRYPTION IN MIND. LiteLLM stores `litellm_params` encrypted when the\n * proxy runs with a database master key (`prisma_client.py`, `encrypt_value`, tag v1.103.0) and\n * answers a row whose `litellm_params` are decrypted, with `api_key` stripped (measured at\n * v1.103.0) — `/v2/model/info`'s docstring still says api keys and api_base are left out. So:\n * - `model_name`, `model_info.id`, `mode`, `base_model` and `access_groups` are compared from\n * the fields the read returns.\n * - `model`, `api_base` and `api_key` are compared through a DIGEST of the DECLARED values\n * (`declaredDigest`), carried in attributes as `paramsSeal`, never a digest of the live row.\n * A plan therefore reports drift when the declaration changes and stays quiet when the proxy\n * hides a field it was told before — the false-drift answer the encrypted row leaves no other\n * way to. A field the proxy DOES return (`model`) is additionally compared when the read\n * carries it; the seal is what decides when the read says nothing.\n * ⛔ A LIVE `api_key` IS NEVER OVERWRITTEN BY AN ADOPTED ROW'S NOTHING. `apiKey` omitted on an\n * adopted row sends no `api_key` and the live reference is kept; `differing` reports no drift\n * for it. Declaring `apiKey` is what moves the row onto `os.environ/NAME`.\n * ⛔ EVERYTHING AN EXISTING ROW NEEDS GOES TO ONE PATCH `/model/{model_id}/update`. At the tag\n * the SDK was generated from (OpenAPI 1.103.0), POST `/model/update`'s `update_model` REBUILDS\n * `litellm_params` from the parsed request model and REPLACES the column: a key the model does\n * not declare (`extra_headers`, `weight`, `order`) is DELETED from the stored row, and a\n * non-`None` pydantic default (`false` on the five flags in `LEAVE_ALONE_PARAMS`) overwrites\n * it — measured on a throwaway 1.103.0 proxy, and the reason a params POST is never sent. The\n * PATCH route's `update_db_model` instead MERGES: sent keys land, the rest of the row is\n * preserved, and a JSON `null` keeps the stored value. So one PATCH carries the managed\n * `litellm_params` (with `null` for those five flags), the `model_name` when it changed, and\n * the `model_info` fields — `access_groups`, `mode`, `base_model`, and the row's own `id`, so\n * an adopt-by-id re-affirms the row it pinned. `model_name` is sent only when it changed,\n * because a same-name write would collide with another deployment in the group (model.ts).\n */\nimport type * as models from '@distilled.cloud/litellm/model_management';\nimport {\n type ModelAttributes,\n type ModelProps,\n hasProviderPrefix,\n isBlank,\n} from './model-types.ts';\nimport { redactUrl, urlProblem } from './mcp-server-url.ts';\nimport { declaredValues, sealFromValues } from './model-credential.ts';\n\n/** The first reason a declaration is refused, or `undefined`. Pure: no environment, no server. */\nexport const firstProblem = (props: ModelProps): string | undefined => {\n if (isBlank(props.modelName) || props.modelName !== props.modelName.trim()) {\n return '`modelName` must be a non-empty string without leading or trailing whitespace';\n }\n if (props.id !== undefined && isBlank(props.id)) {\n return '`id` must be a non-empty string when declared';\n }\n if (isBlank(props.model)) return '`model` must be a non-empty string';\n if (!hasProviderPrefix(props.model)) {\n return `\\`model\\` must carry a provider prefix, e.g. \"xai/${props.model}\": LiteLLM routes by it`;\n }\n if (\n props.model.toLowerCase().startsWith('openai/') &&\n props.modelName.toLowerCase().startsWith('grok')\n ) {\n return '`model` must not use the \"openai/\" prefix on a grok group: Grok is xAI, declare \"xai/…\"';\n }\n if (props.apiBase !== undefined) {\n const url = urlProblem(props.apiBase);\n if (url !== undefined) return url;\n }\n if (\n props.apiKey !== undefined &&\n (typeof props.apiKey !== 'object' || typeof props.apiKey.fromEnv !== 'string')\n ) {\n return \"`apiKey` takes `{ fromEnv: 'NAME' }`, never a value: the value would land in Alchemy's unencrypted state\";\n }\n if (props.apiKey !== undefined && isBlank(props.apiKey.fromEnv)) {\n return '`apiKey.fromEnv` must name an environment variable';\n }\n return undefined;\n};\n\n/** The digest of the DECLARED values, for `paramsSeal`. Pure: no environment, no lookup. */\nexport const declaredDigest = (props: ModelProps): string => sealFromValues(declaredValues(props));\n\nconst sameSet = (live: readonly string[], declared: readonly string[]): boolean =>\n new Set(live).size === new Set(declared).size && declared.every((entry) => live.includes(entry));\n\n/**\n * Wire names of the fields where the live row and the declaration disagree.\n *\n * `model` is compared only when the read carries it (an encrypted row omits it); the seal decides\n * otherwise, and model.ts's diff turns a changed seal into an update. `model_name` mismatch on a\n * pinned id is identity drift — replace in model.ts, update here only when the name changed and\n * the id stayed.\n */\nexport const differing = (live: ModelAttributes, props: ModelProps): readonly string[] => {\n const out: string[] = [];\n if (live.modelName !== props.modelName) out.push('model_name');\n if (live.model !== null && live.model !== props.model) {\n out.push('model');\n }\n if (props.mode !== undefined && live.mode !== props.mode) out.push('mode');\n if (props.baseModel !== undefined && live.baseModel !== props.baseModel) out.push('base_model');\n if (!sameSet(live.accessGroups, props.accessGroups ?? [])) out.push('access_groups');\n return out;\n};\n\n/**\n * One row of `/model/info`, as attributes. ⛔ Copies no `litellm_params` field but `model`, and\n * only when the read returns an OBJECT: the fields a row hides (`api_key` is stripped at\n * v1.103.0) are never mistaken for declared values — `api_base` and any `api_key` are never\n * copied at all. `dbModel` records `model_info.db_model` (false = served from the proxy's config\n * file), defaulting to true when the read omits it. The digest is the caller's (model.ts fills\n * `paramsSeal` from state, a row cannot supply one).\n */\nexport const toAttributes = (row: Record<string, unknown>): ModelAttributes => {\n const info = (row['model_info'] ?? {}) as Record<string, unknown>;\n const params = row['litellm_params'];\n const carriedModel =\n typeof params === 'object' && params !== null\n ? (params as Record<string, unknown>)['model']\n : undefined;\n const id = row['id'] ?? info['id'];\n return {\n accessGroups: Array.isArray(info['access_groups'])\n ? (info['access_groups'] as string[]).map(String)\n : [],\n baseModel: typeof info['base_model'] === 'string' ? info['base_model'] : null,\n dbModel: info['db_model'] === false ? false : true,\n id: String(id),\n mode: typeof info['mode'] === 'string' ? info['mode'] : null,\n model: typeof carriedModel === 'string' ? redactUrl(carriedModel) : null,\n modelName: String(row['model_name'] ?? ''),\n paramsSeal: '',\n };\n};\n\n/**\n * `litellm_params` fields whose pydantic default is `False`, not `None` (`litellm/types/router.py`\n * at v1.103.0). A params write parses the request model, which fills every unset field, so an\n * omitted key is written `false`; JSON `null` is the `None` that keeps the stored value. Create\n * does not send them: a new row should take the vendor default.\n */\nconst LEAVE_ALONE_PARAMS = [\n 'allow_client_keepalive_override',\n 'merge_reasoning_content_in_choices',\n 'use_in_pass_through',\n 'use_litellm_proxy',\n 'use_xai_oauth',\n] as const;\n\n/** The managed `litellm_params` a create sends — with the credential in reference form. */\nconst managedParams = (props: ModelProps) => {\n const params: Record<string, unknown> = { model: props.model };\n if (props.apiBase !== undefined) params['api_base'] = props.apiBase;\n if (props.apiKey !== undefined) params['api_key'] = `os.environ/${props.apiKey.fromEnv}`;\n return params;\n};\n\n/**\n * The same set for a PATCH, plus `null` for every non-`None` default this resource does not own.\n * The PATCH merge keeps the stored value for `None`, so the nulls are what stop a params write\n * clobbering those five flags.\n */\nconst patchParams = (props: ModelProps): Record<string, unknown> => ({\n ...managedParams(props),\n ...Object.fromEntries(LEAVE_ALONE_PARAMS.map((key) => [key, null])),\n});\n\n/**\n * ⚠️ `Record`, then a widening cast in the callers, ON PURPOSE: the generated 1.103.0 request\n * schema declares neither `access_groups` nor `mode` on `model_info`, while the vendor routes\n * on both (`model/types.py`'s `access_groups`, the read's own docstring) and the read ANSWERS\n * both. `modelInfoOf` returns the plain object and the wire probe's runtime encode carries\n * unknown keys through (measured: the fake receives `model_info.id` and `access_groups` the\n * typed struct does not declare).\n */\nconst modelInfoOf = (props: ModelProps): Record<string, unknown> => ({\n ...(props.baseModel === undefined ? {} : { base_model: props.baseModel }),\n ...(props.mode === undefined ? {} : { mode: props.mode }),\n // ★ ALWAYS SENT: `accessGroups` is compared with a default empty set (model-types.ts), so an\n // omitted list is a declared \"no groups\" — a partial update must be able to clear them, the\n // way a budget's soft-only declaration clears max_budget. `mode`/`base_model` are compared\n // only when declared, so their omission really does leave the live value alone.\n access_groups: [...(props.accessGroups ?? [])],\n id: null,\n});\n\n/** A create names the group and pins the id it asked for on `model_info` of the POST body. */\nexport const createBody = (\n props: ModelProps,\n modelId: string,\n): models.AddNewModelModelNewPostRequest => {\n const body: Record<string, unknown> = {\n litellm_params: managedParams(props),\n model_info: { ...modelInfoOf(props), id: modelId },\n model_name: props.modelName,\n };\n return body as unknown as models.AddNewModelModelNewPostRequest;\n};\n\n/**\n * PATCH `/model/{model_id}/update`: everything an existing row needs, in the ONE write a\n * converge fires (model.ts). `update_db_model` MERGES (v1.103.0, measured), so:\n * - `litellm_params` carries the managed keys (with `null` for the five non-`None` defaults\n * this resource does not own); the merge keeps every stored key the declaration does not\n * own (`extra_headers`, `weight`, `order`), which is exactly what POST `/model/update`\n * would DELETE by rebuilding the column.\n * - `model_info` carries `access_groups` (always present: an omitted list is \"no groups\" and\n * must be able to clear), `mode` and `base_model` only when declared, and the row's own\n * `id`, so an adopt-by-id re-affirms the row it pinned.\n * - `model_name` only when it changed: a same-name write would collide with a sibling\n * deployment in the group (model.ts). Not a seal stamp: a matching row records `paramsSeal`\n * locally (model.ts).\n *\n * ⚠️ `Record`, then a widening cast, for the same reason as {@link modelInfoOf}: the generated\n * 1.103.0 `LitellmTypesRouterModelInfo` declares neither `access_groups` nor `mode`, while the\n * vendor model allows both (`extra=\"allow\"`) and the read answers both.\n */\nexport const patchBody = (props: ModelProps, live: ModelAttributes): Record<string, unknown> => {\n const renamed = live.modelName !== props.modelName;\n return {\n model_id: live.id,\n litellm_params: patchParams(props),\n model_info: { ...modelInfoOf(props), id: live.id },\n ...(renamed ? { model_name: props.modelName } : {}),\n };\n};\n",
|
|
65
|
+
"/**\n * Which live row a `LiteLLM.Model` declaration means, and which id a new row would ask for.\n *\n * ★ THE LIST DECIDES FIRST. `GET /model/info` answers every deployment row in one call, so a\n * group name, an id and absence are all decided from it (model-operations.ts). The by-id read\n * is the same route with the `litellm_model_id` filter, kept only for the write-back on a row\n * the list may not show yet — after a create, the read-back falls back to it exactly like\n * `mcp-server-locate.ts` does for the registry.\n * ⚠️ ADOPT-BY-NAME NEEDS THE LIST: a name cannot be read from the table by this route family. A\n * row the list lacks is adoptable only by declaring its `id`.\n * ⛔ A NAME IS NOT UNIQUE (unlike an MCPServer): LiteLLM runs several deployments under one group\n * name on purpose, so a name that matches more than one listed row is refused\n * (`LitellmModelAmbiguousNameError`), never guessed at.\n */\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Effect from 'effect/Effect';\nimport { LitellmModelAmbiguousNameError } from './model-errors.ts';\nimport { listModels, readModelRow } from './model-operations.ts';\nimport type { ModelAttributes, ModelProps } from './model-types.ts';\n\n/**\n * The live row among the LISTED ones, or `undefined`. A pinned id (from state, else declared) wins;\n * otherwise the name decides, and an ambiguous name is refused.\n */\nexport const locate = (\n rows: readonly ModelAttributes[],\n props: ModelProps,\n output: ModelAttributes | undefined,\n) => {\n const pinned = output?.id ?? props.id;\n if (pinned !== undefined) return Effect.succeed(rows.find((row) => row.id === pinned));\n const named = rows.filter((row) => row.modelName === props.modelName);\n return named.length > 1\n ? Effect.fail(\n new LitellmModelAmbiguousNameError({\n ids: named.map((row) => row.id),\n modelName: props.modelName,\n }),\n )\n : Effect.succeed(named[0]);\n};\n\n/** The id a create asks for: the recorded one, else the declared one, else a deterministic name. */\nexport const wantedId = (\n id: string,\n instanceId: string,\n props: ModelProps,\n output: ModelAttributes | undefined,\n) =>\n output?.id !== undefined\n ? Effect.succeed(output.id)\n : props.id !== undefined\n ? Effect.succeed(props.id)\n : createPhysicalName({ id, instanceId, lowercase: true, maxLength: 64 });\n\n/** The live row this declaration means, from the list, else from the table by the id it would use. */\nexport const findLive = (\n id: string,\n instanceId: string,\n props: ModelProps,\n output: ModelAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const listed = yield* locate(yield* listModels(), props, output);\n if (listed !== undefined) return listed;\n return yield* readModelRow(yield* wantedId(id, instanceId, props, output));\n });\n",
|
|
66
|
+
"/**\n * The five LiteLLM `/model/*` calls `LiteLLM.Model` uses, through the SDK's typed operations in\n * `@distilled.cloud/litellm/model_management` (LiteLLM 1.103.0; names verified in the generated\n * source, never guessed):\n *\n * list `getModelInfoV1ModelInfo` GET /model/info\n * read `getModelInfoV1ModelInfo` GET /model/info?litellm_model_id={id}\n * create `addNewModelModelNewPost` POST /model/new\n * patch `patchModelModelModelIdUpdatePatch` PATCH /model/{model_id}/update\n * delete `deleteModelModelDeletePost` POST /model/delete\n *\n * The SDK is generated from LiteLLM OpenAPI **1.103.0** (`model_management.ts`'s header). At that\n * tag, and at v1.100.0, `model_info_v1` raises HTTP 400 when the router has no such deployment\n * (`proxy_server.py`, \"Model id = … not found on litellm proxy\"), not 404. The same 400 is also\n * \"no rights\" or a bad filter. The PATCH route's `update_db_model` MERGES: `litellm_params`,\n * `model_name` and `model_info` (`access_groups`, `mode`, `base_model`) all land without\n * rebuilding the column, which is the one write `LiteLLM.Model` converges with (model-form.ts).\n * POST `/model/update` REBUILDS `litellm_params` and deletes stored keys the request model does\n * not declare, so this resource never sends it.\n *\n * ⛔ LIST-FIRST READS. One GET of the whole list answers a group, an id and absence unambiguously,\n * the way `budget-operations.ts` reads. The by-id read is the same route with the\n * `litellm_model_id` filter, used only where the list cannot decide (a write-back on a row the\n * list may not show yet).\n * ⛔ NO MESSAGE SNIFFING (S21). The by-id read's 400 is undeclared on the generated operation, so\n * it decodes at run time as the shared `BadRequest` (`HTTP_STATUS_MAP`). `catchTag` matches the\n * tag — the same match `deleteModel` uses — so a later declared class with that tag is still\n * seen. A 400 is absence only when a re-list also lacks the id. A 404 is the same absence.\n * Any other failure propagates.\n * ⛔ THE ROW'S PARAMS ARE NEVER READ INTO STATE: `toAttributes` (model-form.ts) copies `model`\n * only when `litellm_params` is an object, and never `api_base` or `api_key` — the v1.103.0\n * read answers params DECRYPTED but STRIPS `api_key` (measured). What was declared is\n * remembered as a digest instead.\n * ⚠️ Unlike pass-through endpoints, each deployment is its own DB row: no semaphore.\n */\nimport * as models from '@distilled.cloud/litellm/model_management';\nimport type { BadRequest, NotFound } from '@distilled.cloud/litellm/Errors';\nimport * as Effect from 'effect/Effect';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\nimport { toAttributes } from './model-form.ts';\nimport type { ModelAttributes } from './model-types.ts';\nimport { LitellmModelUnreadableError } from './model-errors.ts';\n\nconst hasRowId = (row: unknown): boolean => {\n if (typeof row !== 'object' || row === null) return false;\n const record = row as Record<string, unknown>;\n const info =\n typeof record['model_info'] === 'object' && record['model_info'] !== null\n ? (record['model_info'] as Record<string, unknown>)\n : {};\n return typeof record['id'] === 'string' || typeof info['id'] === 'string';\n};\n\n/**\n * The rows the answer carries. ⚠️ MEASURED with the fake: the SDK types\n * `GetModelInfoV1ModelInfoResponse` as `{ body: unknown }` but hands the decoded JSON back\n * directly (a bare array, or the proxy's `{ data: [...] }` wrapper), so accept all three shapes\n * and refuse anything else — the same rule budget-operations.ts / mcp-server-operations.ts keep.\n */\nconst rowsOf = (\n response: unknown,\n): Effect.Effect<readonly ModelAttributes[], LitellmModelUnreadableError> => {\n const rows: unknown = Array.isArray(response)\n ? response\n : Array.isArray((response as { data?: unknown } | null)?.data)\n ? (response as { data: unknown[] }).data\n : (response as { body?: unknown } | null)?.body;\n return Array.isArray(rows) && rows.every(hasRowId)\n ? Effect.succeed((rows as Record<string, unknown>[]).map(toAttributes))\n : Effect.fail(\n new LitellmModelUnreadableError({\n reason: Array.isArray(rows) ? 'a row has no model id' : 'not a list of deployments',\n }),\n );\n};\n\n/** Every deployment row, config-file ones included. This is the read that locates and diffs. */\nexport const listModels = (): Effect.Effect<\n readonly ModelAttributes[],\n models.GetModelInfoV1ModelInfoError | LitellmModelUnreadableError,\n LitellmOpContext\n> => throughFetch(models.getModelInfoV1ModelInfo({})).pipe(Effect.flatMap(rowsOf));\n\n/**\n * One row by id, or `undefined` when the proxy has no such deployment.\n *\n * ⛔ A 400 IS NOT ABSENCE BY ITSELF. v1.103.0 `model_info_v1` raises 400 for a missing id, and the\n * same status is \"no rights\" or a bad filter. Re-list, and treat the id as absent only when the\n * list does not contain it (`deleteModel`'s rule). A 401, a 500 or a dead network says nothing\n * about whether the row exists and must never be read as absence. The id is re-checked on a\n * successful answer: a proxy that ignored the filter must not hand back some other row.\n * ⚠️ 400 and 404 are undeclared on this GET, so they are not in its error union, but the status\n * map still produces those tags. Widening here is what lets `catchTag` see them.\n */\ntype ModelInfoFailure = models.GetModelInfoV1ModelInfoError | BadRequest | NotFound;\n\nexport const readModelRow = (modelId: string) =>\n throughFetch(models.getModelInfoV1ModelInfo({ litellm_model_id: modelId })).pipe(\n Effect.mapError((error): ModelInfoFailure => error),\n Effect.catchTag('NotFound', () => Effect.succeed(undefined)),\n Effect.catchTag('BadRequest', (error) =>\n listModels().pipe(\n Effect.flatMap((rows) =>\n rows.some((row) => row.id === modelId) ? Effect.fail(error) : Effect.succeed(undefined),\n ),\n ),\n ),\n Effect.flatMap((response) =>\n response === undefined\n ? Effect.succeed(undefined)\n : rowsOf(response).pipe(Effect.map((rows) => rows.find((row) => row.id === modelId))),\n ),\n );\n\n/** POST /model/new. The answer carries no usable row (the fake answers `{}`), so the id asked for is the one tracked. */\nexport const createModel = (body: models.AddNewModelModelNewPostRequest) =>\n throughFetch(models.addNewModelModelNewPost(body)).pipe(Effect.asVoid);\n\n/**\n * PATCH `/model/{model_id}/update`. The ONE write a converge of an existing row fires: its merge\n * carries `litellm_params`, `model_name` and `model_info` together (model-form.ts).\n */\nexport const patchModel = (body: Record<string, unknown>) =>\n throughFetch(\n models.patchModelModelModelIdUpdatePatch(\n body as unknown as models.PatchModelModelModelIdUpdatePatchRequest,\n ),\n ).pipe(Effect.asVoid);\n\n/**\n * Idempotent BY THIS FUNCTION, not by the vendor. A `BadRequest` re-lists and is swallowed only\n * when the id is genuinely absent — the same delete can also be refused for lack of rights or a\n * disconnected DB, which leaves the row live and must never be reported as done\n * (budget-operations.ts's rule; never the message text, S21).\n */\nexport const deleteModel = (modelId: string) =>\n throughFetch(models.deleteModelModelDeletePost({ id: modelId })).pipe(\n Effect.asVoid,\n Effect.catchTag('BadRequest', (original) =>\n listModels().pipe(\n Effect.flatMap((rows) =>\n rows.some((row) => row.id === modelId) ? Effect.fail(original) : Effect.void,\n ),\n ),\n ),\n );\n",
|
|
67
|
+
"/**\n * `LiteLLM.Key` — one virtual key of LiteLLM's `LiteLLM_VerificationToken` (`/key/*`), bound to a\n * `LiteLLM.Budget` tier, with its value kept out of Alchemy state.\n *\n * ★ ADOPT BY ALIAS. `keyAlias` is the identity: `/key/update` and `/key/delete` find a key by it\n * (their docstrings), and `/key/list` filters by it, so an existing key is taken over by declaring\n * its alias, with no value in hand. A live key with no state is `Unowned` and needs `--adopt`; the\n * apply re-checks it (`refuseTakeover`) for a create the planner could not probe — which is EVERY\n * key declared with `budgetId: tier.budgetId`, an Output until the tier exists.\n * ⛔ THE VALUE IS WRITE-ONLY (key-secret.ts): `key: { fromEnv }` names the variable, the value goes\n * over the wire once in `/key/generate`, and no attribute, log line or error carries it. `diff`\n * checks a set variable against the live token in memory; an unset variable skips the check.\n * `read` never touches the environment, and no value or hash is added to state.\n * ★ `defaultRemovalPolicy: 'retain'` — deleting a key breaks whatever holds it (a seat's every call\n * 401s). Dropping the declaration leaves the key live; `.pipe(RemovalPolicy.destroy())` opts in.\n * ⛔ A RENAME IS REFUSED, not replaced. The new key would need the same value, and `/key/generate`\n * rejects a second key with a value it already holds; under `retain` the old row would still be\n * there. Declare the new alias as a new resource and destroy the old one.\n * ★ A DECLARATION MANAGES ONLY WHAT IT NAMES (key-form.ts): an omitted prop is never compared or sent,\n * and `metadata` is a merge (key-metadata.ts), so an adopted key's scope, budget and guardrails are\n * never cleared by leaving them out.\n * ⛔ NO CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { refuseTakeover } from '../ownership/adopt.ts';\nimport {\n LitellmKeyAbsentAfterWriteError,\n LitellmKeyAliasChangedError,\n LitellmKeyFieldNotAppliedError,\n LitellmKeyValueNotHonouredError,\n} from './key-errors.ts';\nimport {\n type KeyAttributes,\n type KeyProps,\n createBody,\n differing,\n updateBody,\n} from './key-form.ts';\nimport { callbackSlotsIn } from './key-metadata.ts';\nimport {\n LitellmKeyCallbackMetadataDeclaredError,\n LitellmKeyCallbackMetadataLiveError,\n} from './key-metadata-errors.ts';\nimport { deleteKey, findKey, generateKey, readKeyToken, updateKey } from './key-operations.ts';\nimport {\n echoes,\n hasKeyValue,\n refuseDebugLogging,\n resolveKeyValue,\n verifyKeyValue,\n} from './key-secret.ts';\n\nexport type { KeyAttributes, KeyProps };\nexport type { KeyError } from './key-errors.ts';\n\nexport interface LiteLLMKey extends Resource<'LiteLLM.Key', KeyProps, KeyAttributes> {}\n\nexport const LiteLLMKey = Resource<LiteLLMKey>('LiteLLM.Key', { defaultRemovalPolicy: 'retain' });\n\nexport const isLiteLLMKey = (value: unknown): value is LiteLLMKey =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Key';\n\n/** The row with the memo the row cannot carry: what this provider last wrote for `duration`. */\nconst remembering = (live: KeyAttributes, output: KeyAttributes | undefined): KeyAttributes => ({\n ...live,\n duration: output?.duration ?? null,\n});\n\n/**\n * What state remembers of `duration` after a write: the declaration when it names one, else what it\n * remembered (`before`, `undefined` for a key just created). The row carries only `expires`.\n */\nconst remembered = (news: KeyProps, before: KeyAttributes | undefined): string | null =>\n news.duration !== undefined ? news.duration : (before?.duration ?? null);\n\n/**\n * The `metadata` refusals that need no write to know (key-metadata.ts): a declaration that names a\n * callback slot, and — given the live row — a metadata write onto a key that holds one.\n */\nconst refuseMetadata = (news: KeyProps, live: KeyAttributes | undefined) =>\n Effect.gen(function* () {\n const declared = callbackSlotsIn(news.metadata);\n if (declared.length > 0) {\n return yield* new LitellmKeyCallbackMetadataDeclaredError({\n keyAlias: news.keyAlias,\n slots: declared,\n });\n }\n if (\n live !== undefined &&\n live.withheld.length > 0 &&\n differing(live, news).includes('metadata')\n ) {\n return yield* new LitellmKeyCallbackMetadataLiveError({\n keyAlias: news.keyAlias,\n slots: live.withheld,\n });\n }\n });\n\n/**\n * Create the key, then prove LiteLLM stored the value it was sent.\n * ⛔ A PROXY THAT MINTS ITS OWN VALUE INSTEAD leaves a row nobody can authenticate with, reported as\n * a success. `/key/generate` echoes the key (`Redacted`, never logged); a different one is deleted\n * again and refused. UNVERIFIED that any proxy does this — the 1.103.0 docstring says the value is\n * honoured — but a key nobody holds is not a failure to discover later.\n */\nconst createKey = (news: KeyProps) =>\n Effect.gen(function* () {\n yield* refuseDebugLogging(news.keyAlias);\n const secret = yield* resolveKeyValue(news.keyAlias, news.key);\n const created = yield* generateKey(createBody(news, secret));\n if (echoes(created.key, secret)) return;\n const removed = yield* deleteKey(news.keyAlias).pipe(\n Effect.as(true),\n Effect.orElseSucceed(() => false),\n );\n return yield* new LitellmKeyValueNotHonouredError({ keyAlias: news.keyAlias, removed });\n });\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */\nexport const keyHandlers = {\n read: ({ olds, output }: { olds: KeyProps; output: KeyAttributes | undefined }) =>\n Effect.gen(function* () {\n const live = yield* findKey(output?.keyAlias ?? olds.keyAlias);\n if (live === undefined) return undefined;\n return output === undefined ? Unowned(live) : remembering(live, output);\n }),\n\n diff: ({ news, output }: { news: Input<KeyProps>; output: KeyAttributes | undefined }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuseMetadata(news, output);\n if (output === undefined) return undefined;\n if (news.keyAlias !== output.keyAlias) {\n return yield* new LitellmKeyAliasChangedError({ from: output.keyAlias, to: news.keyAlias });\n }\n // beta.79 Provider.ts:274-289 permits effectful diff, and Plan.ts:1503-1527 calls it\n // even for unchanged props. Refuse an immutable value mismatch before a false noop.\n if (hasKeyValue(news.key)) {\n const token = yield* readKeyToken(news.keyAlias);\n // A dashboard deletion leaves stale output: recreate even without a settings change.\n if (token === undefined) return { action: 'update' } as const;\n yield* verifyKeyValue(news.keyAlias, news.key, token);\n }\n return differing(output, news).length === 0\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({\n fqn,\n instanceId,\n news,\n output,\n }: {\n fqn: string;\n instanceId: string;\n news: KeyProps;\n output: KeyAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n const alias = news.keyAlias;\n yield* refuseMetadata(news, undefined);\n const found = yield* findKey(alias);\n const before = found === undefined ? undefined : remembering(found, output);\n if (before === undefined) {\n yield* createKey(news);\n } else {\n yield* refuseTakeover({ fqn, instanceId, output }, `LiteLLM.Key ${alias}`);\n // ⚠️ AN ADOPTED KEY'S DECLARED VALUE IS CHECKED AGAINST THE ROW (key-secret.ts's header):\n // only when `key: { fromEnv }` names a variable, only as a sha256 in memory, and nothing\n // is written about a mismatch (`/key/update` cannot change a key's value). No `key` declared\n // means no extra read: `readKeyToken` is a `/key/list`, and an omitted value is never held.\n if (hasKeyValue(news.key)) {\n yield* verifyKeyValue(alias, news.key, (yield* readKeyToken(alias)) ?? null);\n }\n yield* refuseMetadata(news, before);\n if (differing(before, news).length > 0) yield* updateKey(updateBody(news, before));\n }\n\n const after = yield* findKey(alias);\n if (after === undefined)\n return yield* new LitellmKeyAbsentAfterWriteError({ keyAlias: alias });\n // ★ THE MEMO IS THE DECLARATION when it names a `duration` (else what state already remembered):\n // a write that reached here carried `duration` whenever the expiry differed, so what is left\n // to check is the row itself (and whether it HAS an expiry).\n const settled = { ...after, duration: remembered(news, before) };\n const left = differing(settled, news);\n if (left.length > 0) {\n return yield* new LitellmKeyFieldNotAppliedError({ fields: left, keyAlias: alias });\n }\n return settled;\n }),\n\n delete: ({ output }: { output: KeyAttributes }) => deleteKey(output.keyAlias),\n\n /**\n * ⚠️ ENUMERATES NOTHING, ON PURPOSE. `nuke` walks `list` and deletes what it finds; a proxy's key\n * table holds every live credential the estate's services authenticate with, plus dashboard\n * session keys nobody declared — none of which this stack made.\n */\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMKeyProvider = () =>\n Provider.effect(LiteLLMKey, Effect.succeed(LiteLLMKey.Provider.of(keyHandlers)));\n",
|
|
68
|
+
"/**\n * The typed refusals `LiteLLM.Key` raises on top of `@distilled.cloud/litellm`'s own SDK errors.\n *\n * ⛔ NOT ONE OF THESE CARRIES A KEY VALUE. They name the alias, the environment VARIABLE and the\n * rule broken, never what the variable held: an error is logged, retried and pasted into a PR.\n * The SDK's own errors are the other half: `generateKey` (key-operations.ts) turns the one that\n * holds the request, and so the value, into `LitellmKeyTransportError` below.\n */\nimport type * as keys from '@distilled.cloud/litellm/key_management';\nimport * as Data from 'effect/Data';\nimport type { HttpClientErrorReason } from 'effect/unstable/http/HttpClientError';\nimport type {\n LitellmKeyCallbackMetadataDeclaredError,\n LitellmKeyCallbackMetadataLiveError,\n} from './key-metadata-errors.ts';\n\n/** A create is needed and the declaration names no `key` — LiteLLM would mint a value nobody holds. */\nexport class LitellmKeyValueRequiredError extends Data.TaggedError('LitellmKeyValueRequiredError')<{\n readonly keyAlias: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}' does not exist and declares no \\`key\\`. Creating it would ` +\n 'mint a value nobody holds. Declare `key: { fromEnv: \"NAME\" }`, or adopt an existing key.'\n );\n }\n}\n\n/**\n * distilled prints request and response BODIES to stderr while `DISTILLED_DEBUG_HTTP` is set, and\n * `/key/generate` carries the key in both. Refused before any request, as `fetchMeshNodeToken` does.\n */\nexport class LitellmKeyDebugLoggingError extends Data.TaggedError('LitellmKeyDebugLoggingError')<{\n readonly keyAlias: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': DISTILLED_DEBUG_HTTP is set, so the SDK would print the ` +\n \"key to stderr in /key/generate's request and response. Unset it and run again. Nothing was written.\"\n );\n }\n}\n\n/**\n * `/key/generate` failed on the wire (or its answer could not be read), so nobody knows whether the\n * key exists.\n *\n * ⛔ THIS IS WHAT STANDS IN FOR AN `HttpClientError`, WHICH CARRIES ITS REQUEST, AND THE REQUEST BODY\n * OF `/key/generate` IS `{\"key\":\"sk-…\"}`. Measured 2026-09-29 with a synthetic value: on a dropped\n * connection `JSON.stringify(error)`, `Bun.inspect(error, { depth: 10 })` and Effect's JSON logger\n * all printed the key, and so did the failure of a deploy over it. A defect from reading a broken\n * answer (`protocol-rest.ts` reads it with `Effect.orDie`) carries the same request. So `reason` is\n * the `HttpClientError` reason's TAG (a string) and nothing else is kept: no request, no cause, no\n * description (a cause is free text from `fetch`).\n */\nexport class LitellmKeyTransportError extends Data.TaggedError('LitellmKeyTransportError')<{\n readonly keyAlias: string;\n readonly reason: HttpClientErrorReason['_tag'] | 'Defect';\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the /key/generate request failed (${this.reason}) before an ` +\n 'answer could be read, so the key MAY have been created. This error keeps neither the request ' +\n \"nor its cause: the request body holds the key's value. Look for the alias in LiteLLM. A key \" +\n 'that landed is taken over by the next deploy with --adopt; one that did not is created by it.'\n );\n }\n}\n\n/** An empty alias: `/key/list` would read `key_alias=` as no filter, and nothing could find the key again. */\nexport class LitellmKeyAliasEmptyError extends Data.TaggedError('LitellmKeyAliasEmptyError')<{}> {\n override get message(): string {\n return \"LiteLLM.Key: keyAlias is empty. The alias is the key's identity. Nothing was written.\";\n }\n}\n\n/** `key: { fromEnv }` names a variable the deploying process does not have (or has empty). */\nexport class LitellmKeyValueMissingError extends Data.TaggedError('LitellmKeyValueMissingError')<{\n readonly keyAlias: string;\n readonly variable: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the environment variable ${this.variable} is unset or empty ` +\n 'in the deploying process. Nothing was written.'\n );\n }\n}\n\n/** The variable's value breaks the rule `/key/generate` states for a user-defined key. */\nexport class LitellmKeyValueMalformedError extends Data.TaggedError(\n 'LitellmKeyValueMalformedError',\n)<{\n readonly keyAlias: string;\n readonly variable: string;\n readonly rule: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the value of ${this.variable} is not usable — ${this.rule}. ` +\n 'Nothing was written.'\n );\n }\n}\n\n/**\n * `/key/generate` answered with a different value than the one sent, so the row exists under a\n * key nobody holds. `removed` says whether the row was deleted again on the way out.\n */\nexport class LitellmKeyValueNotHonouredError extends Data.TaggedError(\n 'LitellmKeyValueNotHonouredError',\n)<{\n readonly keyAlias: string;\n readonly removed: boolean;\n}> {\n override get message(): string {\n const outcome = this.removed\n ? 'The row was deleted again.'\n : 'The row could NOT be deleted again: remove it by hand.';\n return (\n `LiteLLM.Key '${this.keyAlias}': /key/generate answered with a different value than the one ` +\n `sent, so the key would authenticate nobody. ${outcome}`\n );\n }\n}\n\n/**\n * An ADOPTED key was declared with a `key` whose value is not the key the live row holds. The\n * value is compared only as a sha256 in memory against the row's `token` (`hash_token`, the\n * vendor's own store) — never in state, never in an error — and a mismatch is refused, because a\n * seat that reads the same variable would hold a key that authenticates nothing while the plan\n * reported success. `/key/update` cannot change a key's value (`prepare_key_update_data` pops\n * `key`), so there is nothing to write: the operator must fix the variable or drop the `key`.\n */\nexport class LitellmKeyValueMismatchError extends Data.TaggedError('LitellmKeyValueMismatchError')<{\n readonly keyAlias: string;\n readonly variable: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the value of ${this.variable} is not the key this alias ` +\n 'holds. Nothing was written. /key/update cannot change a key value, so fix the variable or ' +\n 'drop the `key` and manage the existing key without one.'\n );\n }\n}\n\n/**\n * The alias is the identity: `/key/update` and `/key/delete` find a key by it, and this resource\n * never holds the value that could find it any other way. A different alias is a different key.\n */\nexport class LitellmKeyAliasChangedError extends Data.TaggedError('LitellmKeyAliasChangedError')<{\n readonly from: string;\n readonly to: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key alias changed from '${this.from}' to '${this.to}'. The alias is the key's ` +\n 'identity and a rename would need the same value under a new row: declare the new alias as ' +\n 'a new resource and remove the old one with RemovalPolicy.destroy(). Nothing was written.'\n );\n }\n}\n\n/** More than one live key carries the alias, so nothing can say which one is declared. */\nexport class LitellmKeyAmbiguousAliasError extends Data.TaggedError(\n 'LitellmKeyAmbiguousAliasError',\n)<{\n readonly keyAlias: string;\n readonly count: number;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': ${this.count} live keys carry this alias, so none can be ` +\n 'called the declared one. Resolve the duplicates in LiteLLM first. Nothing was written.'\n );\n }\n}\n\n/** `/key/list` answered something that is not a list of key rows. */\nexport class LitellmKeyUnreadableError extends Data.TaggedError('LitellmKeyUnreadableError')<{\n readonly keyAlias: string;\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': /key/list did not answer full key rows (a hash list means ` +\n 'return_full_object was ignored). Nothing was written.'\n );\n }\n}\n\n/** A write returned no error but the alias is not listed afterwards. */\nexport class LitellmKeyAbsentAfterWriteError extends Data.TaggedError(\n 'LitellmKeyAbsentAfterWriteError',\n)<{\n readonly keyAlias: string;\n}> {\n override get message(): string {\n return `LiteLLM.Key '${this.keyAlias}': the write returned no error but the alias is not listed.`;\n }\n}\n\n/**\n * The write returned no error but the row still differs from the declaration. `/key/update` is a\n * merge patch (its docstring), so this is a proxy that did not apply a field — refused, never\n * reported as done.\n */\nexport class LitellmKeyFieldNotAppliedError extends Data.TaggedError(\n 'LitellmKeyFieldNotAppliedError',\n)<{\n readonly keyAlias: string;\n readonly fields: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the write returned no error but ${this.fields.join(', ')} ` +\n 'still differ from the declaration.'\n );\n }\n}\n\nexport type KeyError =\n | keys.ListKeysKeyListGetError\n | keys.GenerateKeyFnKeyGeneratePostError\n | keys.UpdateKeyFnKeyUpdatePostError\n | keys.DeleteKeyFnKeyDeletePostError\n | LitellmKeyDebugLoggingError\n | LitellmKeyTransportError\n | LitellmKeyAliasEmptyError\n | LitellmKeyValueRequiredError\n | LitellmKeyValueMissingError\n | LitellmKeyValueMalformedError\n | LitellmKeyValueNotHonouredError\n | LitellmKeyValueMismatchError\n | LitellmKeyAliasChangedError\n | LitellmKeyAmbiguousAliasError\n | LitellmKeyCallbackMetadataDeclaredError\n | LitellmKeyCallbackMetadataLiveError\n | LitellmKeyUnreadableError\n | LitellmKeyAbsentAfterWriteError\n | LitellmKeyFieldNotAppliedError;\n",
|
|
69
|
+
"/**\n * Props, attributes, wire bodies and comparison for `LiteLLM.Key`, testable without a server.\n *\n * ⛔ THERE IS NO SECRET IN THIS FILE'S OUTPUT. `KeyAttributes` has no `key`, no `token` and no hash:\n * `/key/list` rows carry `token` (LiteLLM's hash of the key) and `toAttributes` drops it, because\n * attributes are persisted unencrypted (key-secret.ts). The value is unwrapped in `createBody`, at\n * the last moment before the wire, and compared with a live row only as a sha256 in memory\n * (`verifyKeyValue`, key-secret.ts) against the raw row's `token` — the hash itself never reaches\n * state. The callback slots of `metadata` are dropped too (key-metadata.ts).\n * ★ THE ROW IS READ THROUGH `/key/list?key_alias=…&return_full_object=true`. Measured 2026-09-28\n * (litellm-key-access): a bare list and `get_key` come back with `models: []` for keys whose real\n * allowlist lives in the full object. The generated `UserAPIKeyAuth` row declares every column\n * compared here (`budget_id`, `models`, `allowed_routes`, `team_id`, `metadata`, `expires`).\n * ⛔ A DECLARATION MANAGES ONLY WHAT IT NAMES. An omitted `budgetId`, `models`, `allowedRoutes`,\n * `teamId` or `duration` is neither compared nor sent, so a live value stays. Reading omission as\n * \"clear it\" FAILS OPEN on a credential: `[]` is \"all models\" and \"no route restriction\" to\n * LiteLLM, and no budget drops the tier's limits. On an adopted key (whose plan says `adopted`, never\n * what the apply will write, and which `--adopt` given for another resource takes over too) that\n * would widen a live key without the plan ever showing an `update`. Clearing is an explicit\n * declaration: `models: []`, `allowedRoutes: []`, `budgetId: null`, `teamId: null`, `duration: null`.\n * `/key/update` is a merge patch for the COLUMNS (its docstring), so an unsent one is left as it is.\n * ⛔ `metadata` IS A MERGE, NOT A COLUMN OF THE DECLARER'S: LiteLLM keeps guardrails, per-model limits\n * and more inside it, and `/key/update` replaces it wholesale. See key-metadata.ts.\n * ⚠️ NOT MODELLED, so a declaration of them could never be diffed: `max_budget`, `soft_budget`,\n * `object_permission`, `blocked`, `spend`, and the top-level rate limits. Bind a `LiteLLM.Budget`\n * for limits. The ones that live in `metadata` (guardrails, tags, `model_rpm_limit`, …) are left\n * alone by the merge, and can be declared as `metadata` keys.\n */\nimport type * as keys from '@distilled.cloud/litellm/key_management';\nimport * as Redacted from 'effect/Redacted';\nimport type { FromEnv } from '../secrets/write-only.ts';\nimport { type Metadata, mergedMetadata, metadataDiffers, splitMetadata } from './key-metadata.ts';\n\nexport interface KeyProps {\n /** The alias LiteLLM knows the key by: its identity, unique per proxy. Declare an existing key's alias to adopt it. */\n readonly keyAlias: string;\n /**\n * The key's VALUE, by the NAME of the environment variable holding it (`{ fromEnv: 'SEAT_KEY' }`).\n * Write-only and create-only: required to create the key, never stored. On an EXISTING key a\n * declared value is compared with the row in memory only (sha256 against the row's `token`), and a\n * mismatch is refused — `/key/update` cannot change a key's value. Omit it to manage an existing\n * key's settings without ever holding its value.\n */\n readonly key?: FromEnv;\n /**\n * The `LiteLLM.Budget` tier the key binds to (`budget.budgetId`, so the tier is created first).\n * Omitted = not managed, a live binding stays. `null` = unbind, which drops the tier's limits.\n */\n readonly budgetId?: string | null;\n /**\n * Model names the key may call. Omitted = not managed, a live scope stays (a NEW key then starts\n * with LiteLLM's default). `[]` = ALL models, LiteLLM's reading: declared on a scoped key it WIDENS it.\n */\n readonly models?: readonly string[];\n /**\n * Routes the key may call, exact or wildcard (`/chat/completions`, `/v1/*`). Omitted = not\n * managed. `[]` = no route restriction: declared on a restricted key it WIDENS it.\n */\n readonly allowedRoutes?: readonly string[];\n /** The team the key belongs to. Omitted = not managed. `null` = detach. */\n readonly teamId?: string | null;\n /**\n * The `metadata` keys this declaration manages, a MERGE (key-metadata.ts): other live keys are\n * carried, never removed, and a key is cleared by declaring it `null`. Stored in state, so no\n * secrets. `logging`, `callback_settings` and `secret_manager_settings` are refused.\n */\n readonly metadata?: Metadata;\n /**\n * Validity from creation: `'30s'`, `'30m'`, `'30h'`, `'30d'`. Omitted = not managed (a live expiry\n * stays; a new key never expires). `null` = never expires, which clears a live expiry.\n */\n readonly duration?: string | null;\n}\n\nexport interface KeyAttributes {\n readonly keyAlias: string;\n readonly budgetId: string | null;\n readonly models: readonly string[];\n readonly allowedRoutes: readonly string[];\n readonly teamId: string | null;\n /** The row's `metadata` WITHOUT the callback slots (`withheld`): those carry secret keys and state is unencrypted. */\n readonly metadata: Metadata;\n /**\n * The `duration` this provider last WROTE. The row carries only `expires`, an absolute time, so\n * the declared duration cannot be read back; state remembers it. `null` for an adopted row.\n */\n readonly duration: string | null;\n /** When the key stops authenticating, as the row says. Observed, never declared. */\n readonly expires: string | null;\n /**\n * The NAMES of the `metadata` callback slots the row has and this resource never reads. A metadata\n * write is refused while any is present: `/key/update` replaces the column and could not carry them.\n */\n readonly withheld: readonly string[];\n}\n\n/** Order-insensitive: LiteLLM does not promise the order a list comes back in. */\nconst sameSet = (a: readonly string[], b: readonly string[]): boolean => {\n const left = [...a].sort();\n const right = [...b].sort();\n return left.length === right.length && left.every((value, i) => value === right[i]);\n};\n\n/**\n * Whether the expiry differs from the DECLARED `duration` (`null` = never expires): the remembered\n * duration is not the declared one, or the row has (no) expiry where the declaration wants (none) —\n * someone cleared or set it by hand.\n * ⚠️ AN ADOPTED ROW REMEMBERS NOTHING, so declaring a `duration` on one re-arms its expiry once,\n * from that deploy; after it the duration is remembered and a plan is quiet.\n */\nconst expiryDiffers = (live: KeyAttributes, wanted: string | null): boolean =>\n live.duration !== wanted || (wanted === null) !== (live.expires === null);\n\n/**\n * Wire names of the fields the declaration NAMES where the live row disagrees. An omitted prop is\n * never here (the header's \"manages only what it names\"), so nothing omitted is ever written.\n */\nexport const differing = (live: KeyAttributes, props: KeyProps): readonly string[] => {\n const out: string[] = [];\n if (props.budgetId !== undefined && live.budgetId !== props.budgetId) out.push('budget_id');\n if (props.models !== undefined && !sameSet(live.models, props.models)) out.push('models');\n if (props.allowedRoutes !== undefined && !sameSet(live.allowedRoutes, props.allowedRoutes)) {\n out.push('allowed_routes');\n }\n if (props.teamId !== undefined && live.teamId !== props.teamId) out.push('team_id');\n if (metadataDiffers(live.metadata, props.metadata)) out.push('metadata');\n if (props.duration !== undefined && expiryDiffers(live, props.duration)) out.push('duration');\n return out;\n};\n\n/**\n * Create body: only what is declared, plus the value.\n * ⛔ `Redacted.value` IS CALLED HERE, TO SEND, AND IN key-secret.ts's `echoes`, TO COMPARE — nowhere else.\n */\nexport const createBody = (\n props: KeyProps,\n secret: Redacted.Redacted<string>,\n): keys.GenerateKeyFnKeyGeneratePostRequest => {\n const body: keys.GenerateKeyFnKeyGeneratePostRequest = {\n key: Redacted.value(secret),\n key_alias: props.keyAlias,\n };\n // ★ A DECLARED `null` (unbind, detach, never expires) IS NOT SENT: a new key has nothing to clear.\n if (typeof props.budgetId === 'string') body.budget_id = props.budgetId;\n if (props.models !== undefined) body.models = [...props.models];\n if (props.allowedRoutes !== undefined) body.allowed_routes = [...props.allowedRoutes];\n if (typeof props.teamId === 'string') body.team_id = props.teamId;\n if (props.metadata !== undefined) body.metadata = { ...props.metadata };\n if (typeof props.duration === 'string') body.duration = props.duration;\n return body;\n};\n\n/**\n * Update body: the alias (which finds the key, `key` omitted) and ONLY the declared fields that\n * differ, each as the declaration wants it — a declared `null` on `budget_id`/`team_id`/`duration`\n * as an explicit `null` (a key column, which \"an explicit null clears\"), a declared `[]` as `[]`.\n * ★ `metadata` CARRIES EVERY LIVE KEY with the declared ones on top, because `/key/update` replaces\n * the column (key-metadata.ts). The caller has refused a key with `withheld` slots by now.\n * ⛔ NEVER CARRIES `key`: an update must not be able to change which value authenticates.\n */\nexport const updateBody = (\n props: KeyProps,\n live: KeyAttributes,\n): keys.UpdateKeyFnKeyUpdatePostRequest => {\n const body: keys.UpdateKeyFnKeyUpdatePostRequest = { key_alias: live.keyAlias };\n for (const field of differing(live, props)) {\n if (field === 'budget_id') body.budget_id = props.budgetId ?? null;\n if (field === 'models') body.models = [...(props.models ?? [])];\n if (field === 'allowed_routes') body.allowed_routes = [...(props.allowedRoutes ?? [])];\n if (field === 'team_id') body.team_id = props.teamId ?? null;\n if (field === 'metadata') body.metadata = { ...mergedMetadata(live.metadata, props.metadata) };\n if (field === 'duration') body.duration = props.duration ?? null;\n }\n return body;\n};\n\nconst strings = (value: unknown): readonly string[] =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n\nconst stringOrNull = (value: unknown): string | null => (typeof value === 'string' ? value : null);\n\n/**\n * One row of `/key/list?return_full_object=true`. The row's `token` (LiteLLM's hash of the key), the\n * `metadata` callback slots (only their names stay, as `withheld`), and every other column not named\n * in `KeyAttributes`, are dropped here on purpose.\n */\nexport const toAttributes = (row: Record<string, unknown>): KeyAttributes => {\n const { metadata, withheld } = splitMetadata(row['metadata']);\n return {\n allowedRoutes: strings(row['allowed_routes']),\n budgetId: stringOrNull(row['budget_id']),\n duration: null,\n expires: stringOrNull(row['expires']),\n keyAlias: String(row['key_alias']),\n metadata,\n models: strings(row['models']),\n teamId: stringOrNull(row['team_id']),\n withheld,\n };\n};\n",
|
|
70
|
+
"/**\n * `LiteLLM.Key`'s `metadata` column: what is compared, what is sent, and what is never held.\n * Pure, so it is testable without a server.\n *\n * ⛔ `metadata` IS NOT ONLY THE DECLARER'S. LiteLLM 1.103 keeps a key's guardrails, policies, tags,\n * prompts, per-model RPM/TPM caps, `enforced_params`, `allowed_passthrough_routes`,\n * `end_user_budget_id` and more INSIDE it: they are `LiteLLM_ManagementEndpoint_MetadataFields`\n * and `..._Premium` (`proxy/_types.py:4821-4853`), folded in on create by\n * `metadata_json_with_limits` (`key_management_endpoints.py:4429`) and on update by\n * `prepare_metadata_fields` (:2312). And `/key/update` REPLACES the column with the `metadata` it is\n * sent: only `service_account_id`, the one reserved field, survives an omission (:2323-2336). A\n * body carrying just the declared keys therefore strips every one of those from a live credential,\n * and the deploy reports success. Measured over the fake, which replaces the column the same way\n * (key.test.ts, \"metadata is a merge\").\n * ★ SO `metadata` IS A MERGE, AT THE TOP-LEVEL KEY. A declaration COMPARES and WRITES only the keys it\n * names; a live key it does not name is CARRIED into the update body unchanged, never dropped. The\n * alternative, a list of the keys LiteLLM owns to carry and clear the rest, was not taken: the list\n * is per-version (a key LiteLLM adds next release would be stripped until it is updated here),\n * and this way fails safe. The cost: a key cannot be removed by leaving it out. Declare it `null`.\n * ⛔ THREE SLOTS ARE NEVER HELD, READ OR WRITTEN: `logging`, `callback_settings` and\n * `secret_manager_settings` (`common_utils/callback_utils.py:51`, `_CALLBACK_CONFIG_SLOTS`). They\n * carry `callback_vars`, an observability platform's secret keys. LiteLLM encrypts them at rest\n * (`encrypt_callback_vars`, :716) but a legacy row's are plaintext (\"pass through unchanged\",\n * :725-731), and Alchemy persists attributes unencrypted. So a row's slots are dropped from\n * `KeyAttributes.metadata` and only their NAMES are kept (`withheld`); a declaration that names\n * one is refused, since it would be stored encrypted and so never read back equal (a deploy that\n * failed on every run); and a metadata write on a key that HAS one is refused, since this resource\n * cannot carry what it never read. Configure key logging in LiteLLM itself.\n * ⚠️ TOP-LEVEL KEYS ONLY, as LiteLLM reads them: `_transform_callback_vars` looks at `metadata[\"logging\"]`\n * and no deeper.\n */\nimport { deepEqual } from 'alchemy/Diff';\n\nexport type Metadata = Readonly<Record<string, unknown>>;\n\n/** The slots that carry callback credentials: never in attributes, never declared, never written. */\nexport const CALLBACK_SLOTS = ['callback_settings', 'logging', 'secret_manager_settings'] as const;\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** The callback slots `metadata` names, sorted (`CALLBACK_SLOTS` is). */\nexport const callbackSlotsIn = (metadata: Metadata | undefined): readonly string[] =>\n metadata === undefined ? [] : CALLBACK_SLOTS.filter((slot) => Object.hasOwn(metadata, slot));\n\n/**\n * A row's `metadata` as attributes hold it: everything but the callback slots, whose NAMES are\n * returned beside it (`withheld`) so a write knows it could not carry them.\n */\nexport const splitMetadata = (\n raw: unknown,\n): { readonly metadata: Record<string, unknown>; readonly withheld: readonly string[] } => {\n if (!isObject(raw)) return { metadata: {}, withheld: [] };\n const withheld = callbackSlotsIn(raw);\n return {\n metadata: Object.fromEntries(Object.entries(raw).filter(([name]) => !withheld.includes(name))),\n withheld,\n };\n};\n\n/**\n * Whether a key the declaration names differs from the live row. An absent live key differs from any\n * declared value, `null` included (`deepEqual` tells `undefined` from `null`), so `{ seat: null }`\n * writes a `null` once and is quiet after.\n */\nexport const metadataDiffers = (live: Metadata, declared: Metadata | undefined): boolean =>\n declared !== undefined &&\n Object.entries(declared).some(([name, value]) => !deepEqual(live[name], value));\n\n/** The body's `metadata`: every live key carried, the declared ones on top. */\nexport const mergedMetadata = (live: Metadata, declared: Metadata | undefined): Metadata => ({\n ...live,\n ...declared,\n});\n",
|
|
71
|
+
"/**\n * The typed refusals for `LiteLLM.Key`'s `metadata` slots that carry callback credentials\n * (`logging`, `callback_settings`, `secret_manager_settings`; key-metadata.ts has the reasons).\n * Split from key-errors.ts, which is at its size cap.\n *\n * ⛔ NEITHER CARRIES A VALUE. They name the alias and the slot, never what the slot held.\n */\nimport * as Data from 'effect/Data';\n\n/**\n * The declaration names a callback slot. LiteLLM stores its `callback_vars` ENCRYPTED, so the row\n * would never read back equal (every deploy would fail with `LitellmKeyFieldNotAppliedError`), and\n * Alchemy state is unencrypted.\n */\nexport class LitellmKeyCallbackMetadataDeclaredError extends Data.TaggedError(\n 'LitellmKeyCallbackMetadataDeclaredError',\n)<{\n readonly keyAlias: string;\n readonly slots: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': metadata declares ${this.slots.join(', ')}, which carry ` +\n 'callback credentials. LiteLLM stores them encrypted, so they would never read back equal, ' +\n \"and Alchemy state is unencrypted. Configure the key's logging in LiteLLM itself. Nothing was written.\"\n );\n }\n}\n\n/**\n * The live key holds callback credentials in its `metadata` and the declaration wants a metadata\n * key changed. `/key/update` REPLACES the column, and this resource never read those slots, so it\n * could not send them back: writing would strip the key's logging. Refused before the write.\n */\nexport class LitellmKeyCallbackMetadataLiveError extends Data.TaggedError(\n 'LitellmKeyCallbackMetadataLiveError',\n)<{\n readonly keyAlias: string;\n readonly slots: readonly string[];\n}> {\n override get message(): string {\n return (\n `LiteLLM.Key '${this.keyAlias}': the live key holds ${this.slots.join(', ')} in its metadata ` +\n '(callback credentials this resource never reads), and /key/update replaces the whole ' +\n 'metadata, so writing the declared metadata would strip them. Change that metadata in LiteLLM, ' +\n 'or stop declaring metadata for this key. Nothing was written.'\n );\n }\n}\n",
|
|
72
|
+
"/**\n * The four LiteLLM `/key/*` calls `LiteLLM.Key` uses, through the SDK's typed operations.\n *\n * ★ EVERY OPERATION HERE EXISTS IN `@distilled.cloud/litellm/key_management` (generated at LiteLLM\n * 1.103.0) AND IS CALLED WITH ITS OWN REQUEST TYPE — none is guessed, none is hand-rolled:\n * `listKeysKeyListGet` GET /key/list read, by `key_alias`, `return_full_object`\n * `generateKeyFnKeyGeneratePost` POST /key/generate create, with the user-defined `key`\n * `updateKeyFnKeyUpdatePost` POST /key/update merge patch, found by `key_alias`\n * `deleteKeyFnKeyDeletePost` POST /key/delete by `key_aliases`\n * `/key/info` and `/v2/key/info` are NOT used: they take the key or its hash, and this resource\n * holds neither for an adopted key. Listing by alias needs no value.\n * ⚠️ NO SEMAPHORE, unlike pass-through endpoints: every key is its own DB row (budget-operations.ts).\n * ⚠️ THE SDK'S DEFAULT RETRY APPLIES TO `/key/generate` TOO. A create whose response was lost and is\n * then retried surfaces the proxy's duplicate-alias 400 rather than a second key; the next deploy\n * reads the row. UNVERIFIED against a live proxy — the retry policy is core's, not this file's.\n * ⚠️ DELETE IS IDEMPOTENT BY THIS FUNCTION, not by the vendor. MEASURED in the litellm 1.103.0 wheel\n * (`key_management_endpoints.py`): `/key/delete` for an alias it does not hold answers 404 \"No keys\n * found\" (`delete_verification_tokens`, :4915-4918), and 403 for a caller that may not delete the\n * key (:4933-4936). The SDK types both as resource-specific tags, `KeyNotFound` and\n * `KeyDeleteForbidden` (a distilled patch, `patches/key_management/delete_key_fn_key_delete_post.json`,\n * each matched on the status AND a phrase of the vendor's message), so `catchTag` sees them: the\n * MESSAGE MATCH LIVES IN THE SDK, never here (S21). An absent alias is settled by the LIST, which\n * answers absence unambiguously, and a `KeyNotFound` (another caller won the race, or a retry of a\n * delete whose answer was lost) re-lists and is swallowed only if the key has gone since. Every\n * other failure, `KeyDeleteForbidden` included, propagates typed: a 404 that is not the vendor's\n * is core's `NotFound`, not absence. operations.ts's `deletePassThroughEndpoint` and\n * budget-operations.ts decide the same way, on their own tag.\n */\nimport * as keys from '@distilled.cloud/litellm/key_management';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport { isHttpClientError } from 'effect/unstable/http/HttpClientError';\nimport {\n LitellmKeyAliasEmptyError,\n LitellmKeyAmbiguousAliasError,\n LitellmKeyTransportError,\n LitellmKeyUnreadableError,\n} from './key-errors.ts';\nimport { type KeyAttributes, toAttributes } from './key-form.ts';\nimport { type LitellmOpContext, throughFetch } from './operations.ts';\n\n/**\n * The one live key with this alias, or `undefined`. Two rows are an error, never a pick: the\n * server filters by an EXACT alias by default, and this filters again so a proxy that matched a\n * substring cannot hand back someone else's key.\n */\nexport const findKey = (\n keyAlias: string,\n): Effect.Effect<\n KeyAttributes | undefined,\n | keys.ListKeysKeyListGetError\n | LitellmKeyUnreadableError\n | LitellmKeyAmbiguousAliasError\n | LitellmKeyAliasEmptyError,\n LitellmOpContext\n> =>\n keyAlias.trim() === '' ? Effect.fail(new LitellmKeyAliasEmptyError()) : findKeyByAlias(keyAlias);\n\nconst findKeyByAlias = (keyAlias: string) =>\n findKeyRowByAlias(keyAlias).pipe(\n Effect.map((row) => (row === undefined ? undefined : toAttributes(row))),\n );\n\n/**\n * The one live key with this alias, as the raw `/key/list` row — the shape `toAttributes` reads\n * and drops the `token` from. `findKey` maps it to `KeyAttributes`; `readKeyToken` reads the raw\n * row because the row's `token` (the vendor's sha256 of the key) is what key-secret.ts's\n * `verifyKeyValue` compares a declared value against, and it must not pass through `toAttributes`\n * (which would put the hash in state).\n */\nconst findKeyRowByAlias = (keyAlias: string) =>\n throughFetch(keys.listKeysKeyListGet({ key_alias: keyAlias, return_full_object: true })).pipe(\n Effect.flatMap((response) =>\n Effect.gen(function* () {\n const rows: readonly unknown[] = response.keys ?? [];\n // ⚠️ A STRING ROW IS A HASH: the proxy ignored `return_full_object`, and a hash carries none\n // of the columns compared. Refused rather than read as \"no key\".\n if (rows.some((row) => typeof row !== 'object' || row === null)) {\n return yield* new LitellmKeyUnreadableError({ keyAlias });\n }\n const mine = (rows as readonly Record<string, unknown>[]).filter(\n (row) => row['key_alias'] === keyAlias,\n );\n if (mine.length > 1) {\n return yield* new LitellmKeyAmbiguousAliasError({ count: mine.length, keyAlias });\n }\n return mine[0];\n }),\n ),\n );\n\n/**\n * The row's `token` (the vendor's sha256 of the key), `null` when the row carries none, or\n * `undefined` when the alias is absent. Absence must plan recreation, not a value mismatch.\n * Read only for the in-memory mismatch check (`verifyKeyValue`); the hash is never persisted.\n */\nexport const readKeyToken = (\n keyAlias: string,\n): Effect.Effect<\n string | null | undefined,\n | keys.ListKeysKeyListGetError\n | LitellmKeyUnreadableError\n | LitellmKeyAmbiguousAliasError\n | LitellmKeyAliasEmptyError,\n LitellmOpContext\n> =>\n keyAlias.trim() === ''\n ? Effect.fail(new LitellmKeyAliasEmptyError())\n : findKeyRowByAlias(keyAlias).pipe(\n Effect.map((row) =>\n row === undefined ? undefined : typeof row['token'] === 'string' ? row['token'] : null,\n ),\n );\n\n/**\n * Whether `value` holds the request it answers. An `HttpClientError` does, through `reason.request`\n * (its `request` getter reads it), and its body is the JSON the call sent.\n */\nconst carriesRequest = (value: unknown): boolean =>\n isHttpClientError(value) || Predicate.hasProperty(value, 'request');\n\n/**\n * `/key/generate`. The response's `key` is `Redacted` (the SDK's `SensitiveValue`); never log the response.\n * ⛔ THE REQUEST BODY IS `{\"key\":\"sk-…\"}`, AND A FAILED CALL CAN CARRY ITS REQUEST. Two ways, both\n * measured 2026-09-29 with a synthetic key (key-transport.test.ts): a transport failure is an\n * `HttpClientError` in the error channel, and a body that fails mid-read is the SAME error as a\n * DEFECT (`Effect.orDie` in `@distilled.cloud/core`'s `protocol-rest.ts`). Either would put the\n * plaintext in front of anything that serialises, inspects or logs the failure — while the SDK's\n * own `HttpClientError` is not something this resource can leave typed as it is. Both become\n * `LitellmKeyTransportError`, which keeps the alias and the reason's tag and nothing else.\n * The SDK's status errors (`BadRequest`, `Forbidden`, …) carry only the proxy's message text and\n * stay as they are, so a caller can still `catchTag` them.\n */\nexport const generateKey = (body: keys.GenerateKeyFnKeyGeneratePostRequest) => {\n const keyAlias = body.key_alias ?? '';\n return throughFetch(keys.generateKeyFnKeyGeneratePost(body)).pipe(\n Effect.catchTag('HttpClientError', (error) =>\n Effect.fail(new LitellmKeyTransportError({ keyAlias, reason: error.reason._tag })),\n ),\n Effect.catchDefect((defect) =>\n carriesRequest(defect)\n ? Effect.fail(\n new LitellmKeyTransportError({\n keyAlias,\n reason: isHttpClientError(defect) ? defect.reason._tag : 'Defect',\n }),\n )\n : Effect.die(defect),\n ),\n );\n};\n\n/** `/key/update`, found by the body's `key_alias`. */\nexport const updateKey = (body: keys.UpdateKeyFnKeyUpdatePostRequest) =>\n throughFetch(keys.updateKeyFnKeyUpdatePost(body)).pipe(Effect.asVoid);\n\n/** `/key/delete` by alias, when the list shows the key; a no-op when it does not. */\nexport const deleteKey = (\n keyAlias: string,\n): Effect.Effect<\n void,\n | keys.DeleteKeyFnKeyDeletePostError\n | keys.ListKeysKeyListGetError\n | LitellmKeyUnreadableError\n | LitellmKeyAmbiguousAliasError\n | LitellmKeyAliasEmptyError,\n LitellmOpContext\n> =>\n findKey(keyAlias).pipe(\n Effect.flatMap((live) =>\n live === undefined\n ? Effect.void\n : throughFetch(keys.deleteKeyFnKeyDeletePost({ key_aliases: [keyAlias] })).pipe(\n Effect.asVoid,\n Effect.catchTag('KeyNotFound', (original) =>\n findKey(keyAlias).pipe(\n Effect.flatMap((still) =>\n still === undefined ? Effect.void : Effect.fail(original),\n ),\n ),\n ),\n ),\n ),\n );\n",
|
|
73
|
+
"/**\n * Where `LiteLLM.Key`'s secret comes from, and the only place it is unwrapped.\n *\n * ⛔ THE KEY VALUE IS WRITE-ONLY, THE SAME MECHANISM EVERY OTHER KIT SECRET USES\n * (`../secrets/write-only.ts`: PBS webhook secrets, the Cloudflare MCP entry, Forgejo org secrets).\n * The prop holds the NAME of an environment variable, `{ fromEnv: 'SEAT_KEY' }`; the deploying\n * process reads it at call time; the value goes over the wire once, in `/key/generate`'s body.\n * Alchemy persists props AND attributes unencrypted — a `Redacted` is TAGGED, not encrypted\n * (`State/StateEncoding.ts` writes the inner value beside the tag) — so neither may hold it.\n * S25 of the provider standard says the same; `MeshNode` (docs/mesh-node.md) is the other family\n * that keeps a generated secret out of state.\n * ★ WHY NOT LET LITELLM MINT IT AND RETURN IT. `/key/generate` does return the new key, and the SDK\n * decodes it `Redacted` (`T.SensitiveValue`). But a resource has exactly two places to put it, an\n * attribute (state, in the clear) or nowhere; and LiteLLM never shows the plaintext again, only a\n * hash. A key minted and dropped is a row nobody can use. So the value is minted OUTSIDE — a random\n * `sk-…` in the vault, the same one the seat reads — and handed to LiteLLM, which the docstring\n * for `key` supports: \"User defined key value. Must start with 'sk-' and be at least 16\n * characters long.\"\n * ⚠️ AN ADOPTED KEY'S VALUE IS COMPARED WITH THE LIVE ROW, IN MEMORY ONLY. LiteLLM keeps the sha256\n * hex of the key (`hash_token`, measured in the 1.103.0 wheel's `proxy/utils.py`), and `/key/list`\n * with `return_full_object` returns it as the row's `token`. When a declaration names `key:\n * { fromEnv }` for a key that already exists, the resolved value is sha256'd and compared with that\n * hash (never with the value, and the hash is never persisted — key-form.ts's `toAttributes` still\n * drops `token`). A mismatch is refused (`LitellmKeyValueMismatchError`): a seat reading the same\n * variable would hold a key that authenticates nothing while the plan reported success. There is\n * nothing to WRITE about a mismatch — `/key/update` drops `key` from its body\n * (`prepare_key_update_data` pops it, `key_management_endpoints.py:2469`), so it cannot change a\n * key's value, and `/key/regenerate` is an Enterprise feature (its docstring, :5673) — so the\n * operator fixes the variable or drops the `key`. A changed variable changes nothing on an\n * existing key: rotate by declaring a new alias. Plan and reconcile both check a set variable;\n * an unset or empty variable skips verification so a deployer need not hold the seat key.\n * ⚠️ A PROXY MAY REFUSE THE WHOLE APPROACH: with the dashboard's `disable_custom_api_keys` setting\n * on, `/key/generate` answers 403 to ANY user-defined key (\"Keys must be auto-generated\",\n * `_check_custom_key_allowed`, :485-496). That surfaces as the SDK's typed `Forbidden`. UNVERIFIED\n * whether the estate's proxy has it on; adopting existing keys is unaffected.\n */\nimport { createHash } from 'node:crypto';\nimport * as Effect from 'effect/Effect';\nimport * as Redacted from 'effect/Redacted';\nimport { type FromEnv, resolveAll } from '../secrets/write-only.ts';\nimport {\n LitellmKeyDebugLoggingError,\n LitellmKeyValueMalformedError,\n LitellmKeyValueMismatchError,\n LitellmKeyValueMissingError,\n LitellmKeyValueRequiredError,\n} from './key-errors.ts';\n\n/** From `/key/generate`'s own docstring for the `key` field (distilled-litellm, LiteLLM 1.103.0). */\nconst PREFIX = 'sk-';\nconst MIN_LENGTH = 16;\n/**\n * ⚠️ NO WHITESPACE is the house's own addition, not LiteLLM's: a secret renderer's trailing newline\n * would be sent as part of the value, and a seat that trims what it reads would then hold a\n * different key from the one LiteLLM stored (`resolveAll` deliberately does not trim).\n */\nconst RULE = `a user-defined key must start with '${PREFIX}', be at least ${MIN_LENGTH} characters and contain no whitespace`;\n\n/**\n * The declared value, `Redacted`, or a typed refusal naming the alias, the variable and the rule.\n * Called only when a create is actually about to happen, never by `diff` or `read`, so a plan\n * (which needs no secret) runs without the variable.\n * ⚠️ AN EMPTY VARIABLE IS A MISSING ONE (`resolveAll`): a secret renderer that failed writes\n * `NAME=` and sending `''` would create a key that authenticates with nothing.\n */\nexport const resolveKeyValue = (\n keyAlias: string,\n ref: FromEnv | undefined,\n): Effect.Effect<\n Redacted.Redacted<string>,\n LitellmKeyValueRequiredError | LitellmKeyValueMissingError | LitellmKeyValueMalformedError\n> =>\n Effect.gen(function* () {\n if (ref === undefined) return yield* new LitellmKeyValueRequiredError({ keyAlias });\n const variable = ref.fromEnv;\n const value = resolveAll({ key: ref }).values['key'];\n if (value === undefined) return yield* new LitellmKeyValueMissingError({ keyAlias, variable });\n if (!value.startsWith(PREFIX) || value.length < MIN_LENGTH || /\\s/.test(value)) {\n return yield* new LitellmKeyValueMalformedError({ keyAlias, rule: RULE, variable });\n }\n return Redacted.make(value);\n });\n\n/** Whether `/key/generate` echoed the value it was sent (the SDK hands it back `Redacted`). */\nexport const echoes = (\n returned: string | Redacted.Redacted<string>,\n sent: Redacted.Redacted<string>,\n): boolean =>\n (Redacted.isRedacted(returned) ? Redacted.value(returned) : returned) === Redacted.value(sent);\n\n/**\n * The sha256 hex of a key value, the same digest LiteLLM stores as `hash_token` (`proxy/utils.py`).\n * ★ MATCHES THE VENDOR'S HASH, not a house format: the row's `token` is the vendor's own store, so\n * the digest must be reproducible from the value with the vendor's algorithm, not a salt (a salt\n * would make every comparison fail). Nothing here is persisted — the value is in memory only.\n */\nconst sha256 = (value: string): string => createHash('sha256').update(value).digest('hex');\n\n/**\n * Whether an ADOPTED key's declared `key: { fromEnv }` value is the key the live row holds.\n *\n * ⚠️ COMPARED AS A SHA256 AGAINST THE ROW'S `token`, never value-against-value, and the hash is\n * never written anywhere: a `Redacted` value unwrapped only long enough to digest it, then gone.\n * The row's `token` (`hash_token`) is the vendor's own sha256 hex of the key.\n */\nexport const verifyKeyValue = (\n keyAlias: string,\n ref: FromEnv | undefined,\n token: string | null,\n): Effect.Effect<void, LitellmKeyValueMismatchError> =>\n Effect.gen(function* () {\n if (ref === undefined) return;\n const variable = ref.fromEnv;\n const value = resolveAll({ key: ref }).values['key'];\n if (value === undefined) return;\n if (token === null || sha256(value) !== token) {\n return yield* new LitellmKeyValueMismatchError({ keyAlias, variable });\n }\n });\n\n/**\n * ⛔ THE SDK PRINTS BODIES WHILE `DISTILLED_DEBUG_HTTP` IS SET (`@distilled.cloud/core`\n * `protocol-http.ts`: the first 400 characters of every request body; `protocol-rest.ts`: of every\n * response), and `/key/generate` carries the key in both. Measured by reading the pinned source\n * 2026-09-29; `mesh-node-token.ts` refuses for the same reason. Only a create needs it: no other\n * call in this resource carries the value. Reads `process.env` because that is what distilled reads.\n */\nexport const refuseDebugLogging = (\n keyAlias: string,\n): Effect.Effect<void, LitellmKeyDebugLoggingError> =>\n (globalThis.process?.env?.['DISTILLED_DEBUG_HTTP'] ?? '') !== ''\n ? Effect.fail(new LitellmKeyDebugLoggingError({ keyAlias }))\n : Effect.void;\n\n/** Whether verification can run; unset/empty variables skip the check, never a create. */\nexport const hasKeyValue = (ref: FromEnv | undefined): boolean =>\n ref !== undefined && resolveAll({ key: ref }).values['key'] !== undefined;\n",
|
|
74
|
+
"/**\n * The LiteLLM provider, with its credential source, as one layer for a stack.\n *\n * Layer.mergeAll(litellmProviders(), …the stack's other providers)\n *\n * ★ THE DEFAULT READS `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` AT CALL TIME, through\n * `@distilled.cloud/litellm`'s own `CredentialsFromEnv` — the same two variable names the\n * retired hand-rolled `credentials.ts` read — never at layer build. Pass another\n * `Layer.Layer<Credentials>` (see the SDK's own `credentials({ apiKey, baseUrl })` in a test) to\n * source them another way.\n * ⚠️ `HttpClient` is NOT provided here. It is ambient in every Alchemy runtime the same way it is\n * for `netboxProviders`-style families in this kit; a stack that already deploys anything over\n * HTTP already has one. Do not add a second client to this layer.\n * ⚠️ `Layer.provideMerge`, NEVER PLAIN `Layer.provide`, for the credentials layer. The handlers\n * still need `Credentials` when the engine calls `read`/`diff`/`reconcile`. `Provider.effect`\n * puts that need on the layer. Plain `provide` satisfies it only while the `Provider` value is\n * built, then hides `Credentials` from those later calls. Measured on Caddy, 2026-09-23:\n * `caddy/providers.ts`. `provideMerge` feeds credentials in and keeps them in the layer the\n * engine runs the handlers with.\n */\nimport { type Credentials, CredentialsFromEnv } from '@distilled.cloud/litellm/Credentials';\nimport * as Layer from 'effect/Layer';\nimport { LiteLLMAccessGroupProvider } from './access-group.ts';\nimport { LiteLLMBudgetProvider } from './budget.ts';\nimport { LiteLLMCredentialProvider } from './credential.ts';\nimport { LiteLLMMCPServerProvider } from './mcp-server.ts';\nimport { LiteLLMKeyProvider } from './key.ts';\nimport { LiteLLMModelProvider } from './model.ts';\nimport { LiteLLMPassThroughEndpointProvider } from './pass-through-endpoint.ts';\nimport { LiteLLMPolicyAttachmentProvider } from './policy-attachment.ts';\nimport { LiteLLMPolicyProvider } from './policy.ts';\nimport { LiteLLMTeamProvider } from './team.ts';\nimport { LiteLLMToolPolicyProvider } from './tool-policy.ts';\nimport { LiteLLMToolsetProvider } from './toolset.ts';\n\nexport const litellmProviders = (creds: Layer.Layer<Credentials> = CredentialsFromEnv) =>\n Layer.mergeAll(\n LiteLLMPassThroughEndpointProvider(),\n LiteLLMBudgetProvider(),\n LiteLLMCredentialProvider(),\n LiteLLMMCPServerProvider(),\n LiteLLMAccessGroupProvider(),\n LiteLLMToolsetProvider(),\n LiteLLMToolPolicyProvider(),\n LiteLLMPolicyProvider(),\n LiteLLMPolicyAttachmentProvider(),\n LiteLLMTeamProvider(),\n LiteLLMKeyProvider(),\n LiteLLMModelProvider(),\n ).pipe(Layer.provideMerge(creds));\n"
|
|
14
75
|
],
|
|
15
|
-
"mappings": "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",
|
|
16
|
-
"debugId": "
|
|
76
|
+
"mappings": "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",
|
|
77
|
+
"debugId": "2E47E5AC1835F31A64756E2164756E21",
|
|
17
78
|
"names": []
|
|
18
79
|
}
|