@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
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `LiteLLM.Key` through Alchemy's real Plan and Apply over the fake proxy: create, no-op, adopt by
|
|
3
|
+
* alias, update, and the bind to a `LiteLLM.Budget`. Secret handling is key-secret.test.ts; delete
|
|
4
|
+
* and read failures are key-delete.test.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import { LiteLLMBudget } from './budget.ts';
|
|
9
|
+
import { writesOf } from './fake-stack.ts';
|
|
10
|
+
import { LiteLLMKey } from './key.ts';
|
|
11
|
+
import { differing, toAttributes, updateBody } from './key-form.ts';
|
|
12
|
+
import {
|
|
13
|
+
FAKE_KEY,
|
|
14
|
+
VAR,
|
|
15
|
+
declare,
|
|
16
|
+
failureOf,
|
|
17
|
+
keyStack,
|
|
18
|
+
liveRow,
|
|
19
|
+
newFake,
|
|
20
|
+
withEnv,
|
|
21
|
+
} from './key-harness.ts';
|
|
22
|
+
|
|
23
|
+
const tier = (id: string) => ({ budget_id: id, created_at: 'x', updated_at: 'x' });
|
|
24
|
+
const seat = { key: { fromEnv: VAR }, keyAlias: 'seat-a' } as const;
|
|
25
|
+
const deploy = (stack: ReturnType<typeof keyStack>, props: Parameters<typeof declare>[0]) =>
|
|
26
|
+
withEnv({ [VAR]: FAKE_KEY }, () => stack.deploy(declare(props)));
|
|
27
|
+
const paths = (fake: ReturnType<typeof newFake>) => fake.keys.writes().map((write) => write.path);
|
|
28
|
+
|
|
29
|
+
describe('create', () => {
|
|
30
|
+
test('makes the key with every declared setting and sends the value once', async () => {
|
|
31
|
+
const fake = newFake({ budgetSeed: [tier('hf-tier-agent')] });
|
|
32
|
+
const props = {
|
|
33
|
+
...seat,
|
|
34
|
+
allowedRoutes: ['/chat/completions'],
|
|
35
|
+
budgetId: 'hf-tier-agent',
|
|
36
|
+
metadata: { seat: 'a' },
|
|
37
|
+
models: ['glm-5.3', 'gpt-oss-120b'],
|
|
38
|
+
teamId: 'team-cf',
|
|
39
|
+
};
|
|
40
|
+
expect(await deploy(keyStack(fake), props)).toEqual({ Seat: 'create' });
|
|
41
|
+
expect(fake.keys.rows()).toMatchObject([
|
|
42
|
+
{
|
|
43
|
+
allowed_routes: ['/chat/completions'],
|
|
44
|
+
budget_id: 'hf-tier-agent',
|
|
45
|
+
expires: null,
|
|
46
|
+
key_alias: 'seat-a',
|
|
47
|
+
metadata: { seat: 'a' },
|
|
48
|
+
models: ['glm-5.3', 'gpt-oss-120b'],
|
|
49
|
+
team_id: 'team-cf',
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
expect(fake.keys.received()).toEqual([FAKE_KEY]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('a second deploy of the same declaration writes nothing', async () => {
|
|
56
|
+
const fake = newFake();
|
|
57
|
+
const stack = keyStack(fake);
|
|
58
|
+
await deploy(stack, { ...seat, models: ['glm-5.3'] });
|
|
59
|
+
const before = fake.requests().length;
|
|
60
|
+
expect(await deploy(stack, { ...seat, models: ['glm-5.3'] })).toEqual({ Seat: 'noop' });
|
|
61
|
+
expect(writesOf(fake.requests().slice(before))).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('binds to a Budget declared beside it: the tier is created first', async () => {
|
|
65
|
+
const fake = newFake();
|
|
66
|
+
const planned = await withEnv({ [VAR]: FAKE_KEY }, () =>
|
|
67
|
+
keyStack(fake).deploy(
|
|
68
|
+
Effect.gen(function* () {
|
|
69
|
+
const tierRow = yield* LiteLLMBudget('Tier', { budgetId: 'hf-tier-x', softBudget: 5 });
|
|
70
|
+
// ★ AN OUTPUT IN A PROP IS A DEPENDENCY EDGE: the fake 400s a key whose `budget_id` is not
|
|
71
|
+
// a live budget, so this only succeeds if the Budget was written first.
|
|
72
|
+
yield* LiteLLMKey('Seat', { ...seat, budgetId: tierRow.budgetId });
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
expect(planned).toEqual({ Seat: 'create', Tier: 'create' });
|
|
77
|
+
expect(writesOf(fake.requests())).toEqual(['POST /budget/new', 'POST /key/generate']);
|
|
78
|
+
expect(fake.keys.rows()[0]).toMatchObject({ budget_id: 'hf-tier-x' });
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('adopt by alias', () => {
|
|
83
|
+
const live = liveRow({ budget_id: 'hf-tier-agent', models: ['old-model'] });
|
|
84
|
+
// ★ `budgetId: null` IS DECLARED: an omitted prop is left alone (key-scope.test.ts), so unbinding
|
|
85
|
+
// the live tier takes a declaration.
|
|
86
|
+
const declared = { budgetId: null, keyAlias: 'seat-a', models: ['glm-5.3'] };
|
|
87
|
+
|
|
88
|
+
test('a live key with no state is refused without --adopt, and nothing is written', async () => {
|
|
89
|
+
const fake = newFake({ budgetSeed: [tier('hf-tier-agent')], keySeed: [live] });
|
|
90
|
+
await expect(keyStack(fake).deploy(declare(declared))).rejects.toThrow();
|
|
91
|
+
expect(fake.keys.writes()).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('with --adopt it is taken over and updated, needing no key value', async () => {
|
|
95
|
+
const fake = newFake({ budgetSeed: [tier('hf-tier-agent')], keySeed: [live] });
|
|
96
|
+
const planned = await keyStack(fake).deploy(declare(declared), { adopt: true });
|
|
97
|
+
expect(Object.values(planned)).toEqual(['adopted']);
|
|
98
|
+
// No `key` in the environment, none in the body: the update finds the key by its alias alone.
|
|
99
|
+
expect(fake.keys.writes()).toEqual([
|
|
100
|
+
{ body: { budget_id: null, key_alias: 'seat-a', models: ['glm-5.3'] }, path: '/key/update' },
|
|
101
|
+
]);
|
|
102
|
+
expect(fake.keys.rows()[0]).toMatchObject({ budget_id: null, models: ['glm-5.3'] });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('a create the planner could not probe is refused at apply, not taken over', async () => {
|
|
106
|
+
// `budgetId: tierRow.budgetId` is an Output until the tier exists, so Alchemy skips its
|
|
107
|
+
// adoption probe for this key — the apply-time check is all that stands between a stray
|
|
108
|
+
// declaration and a live credential.
|
|
109
|
+
const fake = newFake({ keySeed: [liveRow({ key_alias: 'seat-a' })] });
|
|
110
|
+
const body = Effect.gen(function* () {
|
|
111
|
+
const tierRow = yield* LiteLLMBudget('Tier', { budgetId: 'hf-tier-x', softBudget: 5 });
|
|
112
|
+
yield* LiteLLMKey('Seat', { keyAlias: 'seat-a', budgetId: tierRow.budgetId });
|
|
113
|
+
});
|
|
114
|
+
const message = await failureOf(keyStack(fake).deploy(body));
|
|
115
|
+
expect(message).toContain('already exists');
|
|
116
|
+
expect(fake.keys.writes()).toEqual([]);
|
|
117
|
+
await keyStack(fake).deploy(body, { adopt: true });
|
|
118
|
+
expect(paths(fake)).toEqual(['/key/update']);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('update', () => {
|
|
123
|
+
test('changing budget_id sends only that field, and never the key', async () => {
|
|
124
|
+
const fake = newFake({ budgetSeed: [tier('tier-a'), tier('tier-b')] });
|
|
125
|
+
const stack = keyStack(fake);
|
|
126
|
+
await deploy(stack, { ...seat, budgetId: 'tier-a' });
|
|
127
|
+
expect(await deploy(stack, { ...seat, budgetId: 'tier-b' })).toEqual({ Seat: 'update' });
|
|
128
|
+
expect(fake.keys.writes().at(-1)).toEqual({
|
|
129
|
+
body: { budget_id: 'tier-b', key_alias: 'seat-a' },
|
|
130
|
+
path: '/key/update',
|
|
131
|
+
});
|
|
132
|
+
expect(fake.keys.rows()[0]).toMatchObject({ budget_id: 'tier-b' });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('declaring null and [] clears budget_id, models and routes', async () => {
|
|
136
|
+
const fake = newFake({ budgetSeed: [tier('tier-a')] });
|
|
137
|
+
const stack = keyStack(fake);
|
|
138
|
+
await deploy(stack, { ...seat, allowedRoutes: ['/v1/*'], budgetId: 'tier-a', models: ['m'] });
|
|
139
|
+
await deploy(stack, { ...seat, allowedRoutes: [], budgetId: null, models: [] });
|
|
140
|
+
expect(fake.keys.rows()[0]).toMatchObject({ allowed_routes: [], budget_id: null, models: [] });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('model order is not a difference', async () => {
|
|
144
|
+
const fake = newFake();
|
|
145
|
+
const stack = keyStack(fake);
|
|
146
|
+
await deploy(stack, { ...seat, models: ['a', 'b'] });
|
|
147
|
+
expect(await deploy(stack, { ...seat, models: ['b', 'a'] })).toEqual({ Seat: 'noop' });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('a proxy that keeps a field it was told to clear fails the apply', async () => {
|
|
151
|
+
const fake = newFake({
|
|
152
|
+
budgetSeed: [tier('hf-tier-agent')],
|
|
153
|
+
keySeed: [liveRow({ budget_id: 'hf-tier-agent' })],
|
|
154
|
+
keyUpdateIgnoresNull: true,
|
|
155
|
+
});
|
|
156
|
+
const message = await failureOf(
|
|
157
|
+
keyStack(fake).deploy(declare({ budgetId: null, keyAlias: 'seat-a' }), { adopt: true }),
|
|
158
|
+
);
|
|
159
|
+
expect(message).toContain('budget_id');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('renaming the alias is refused before any write', async () => {
|
|
163
|
+
const fake = newFake();
|
|
164
|
+
const stack = keyStack(fake);
|
|
165
|
+
await deploy(stack, seat);
|
|
166
|
+
const writes = fake.keys.writes().length;
|
|
167
|
+
const message = await failureOf(deploy(stack, { ...seat, keyAlias: 'seat-b' }));
|
|
168
|
+
expect(message).toContain('seat-b');
|
|
169
|
+
expect(fake.keys.writes()).toHaveLength(writes);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('duration', () => {
|
|
174
|
+
test('is written on create, remembered, and changed or cleared by an update', async () => {
|
|
175
|
+
const fake = newFake();
|
|
176
|
+
const stack = keyStack(fake);
|
|
177
|
+
await deploy(stack, { ...seat, duration: '30d' });
|
|
178
|
+
expect(fake.keys.rows()[0]?.['expires']).not.toBeNull();
|
|
179
|
+
expect(await deploy(stack, { ...seat, duration: '30d' })).toEqual({ Seat: 'noop' });
|
|
180
|
+
expect(await deploy(stack, { ...seat, duration: '7d' })).toEqual({ Seat: 'update' });
|
|
181
|
+
expect(fake.keys.writes().at(-1)?.body).toEqual({ duration: '7d', key_alias: 'seat-a' });
|
|
182
|
+
// `duration: null` is the declaration that clears the expiry; leaving it out does not (key-scope.test.ts).
|
|
183
|
+
expect(await deploy(stack, { ...seat, duration: null })).toEqual({ Seat: 'update' });
|
|
184
|
+
expect(fake.keys.writes().at(-1)?.body).toEqual({ duration: null, key_alias: 'seat-a' });
|
|
185
|
+
expect(fake.keys.rows()[0]?.['expires']).toBeNull();
|
|
186
|
+
expect(await deploy(stack, { ...seat, duration: null })).toEqual({ Seat: 'noop' });
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('an adopted key remembers no duration, so declaring one arms it once', async () => {
|
|
190
|
+
const fake = newFake({ keySeed: [liveRow({ expires: '2099-01-01T00:00:00.000Z' })] });
|
|
191
|
+
const stack = keyStack(fake);
|
|
192
|
+
await stack.deploy(declare({ duration: '30d', keyAlias: 'seat-a' }), { adopt: true });
|
|
193
|
+
expect(fake.keys.writes().at(-1)?.body).toEqual({ duration: '30d', key_alias: 'seat-a' });
|
|
194
|
+
expect(await stack.deploy(declare({ duration: '30d', keyAlias: 'seat-a' }))).toEqual({
|
|
195
|
+
Seat: 'noop',
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('form helpers', () => {
|
|
201
|
+
const row = liveRow({ budget_id: 'tier-a', models: ['a'], metadata: { seat: 'a' } });
|
|
202
|
+
|
|
203
|
+
test('toAttributes keeps the compared columns and drops the token hash', () => {
|
|
204
|
+
const attributes = toAttributes(row);
|
|
205
|
+
expect(attributes).toEqual({
|
|
206
|
+
allowedRoutes: [],
|
|
207
|
+
budgetId: 'tier-a',
|
|
208
|
+
duration: null,
|
|
209
|
+
expires: null,
|
|
210
|
+
keyAlias: 'seat-a',
|
|
211
|
+
metadata: { seat: 'a' },
|
|
212
|
+
models: ['a'],
|
|
213
|
+
teamId: null,
|
|
214
|
+
withheld: [],
|
|
215
|
+
});
|
|
216
|
+
expect(JSON.stringify(attributes)).not.toContain('FAKE-TOKEN');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('differing names the wire fields, and the update body carries only those', () => {
|
|
220
|
+
const live = toAttributes(row);
|
|
221
|
+
const props = {
|
|
222
|
+
budgetId: 'tier-b',
|
|
223
|
+
keyAlias: 'seat-a',
|
|
224
|
+
metadata: { seat: 'a' },
|
|
225
|
+
models: ['a'],
|
|
226
|
+
};
|
|
227
|
+
expect(differing(live, props)).toEqual(['budget_id']);
|
|
228
|
+
expect(updateBody(props, live)).toEqual({ budget_id: 'tier-b', key_alias: 'seat-a' });
|
|
229
|
+
expect(differing(live, { ...props, budgetId: 'tier-a' })).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `LiteLLM.Key` — one virtual key of LiteLLM's `LiteLLM_VerificationToken` (`/key/*`), bound to a
|
|
3
|
+
* `LiteLLM.Budget` tier, with its value kept out of Alchemy state.
|
|
4
|
+
*
|
|
5
|
+
* ★ ADOPT BY ALIAS. `keyAlias` is the identity: `/key/update` and `/key/delete` find a key by it
|
|
6
|
+
* (their docstrings), and `/key/list` filters by it, so an existing key is taken over by declaring
|
|
7
|
+
* its alias, with no value in hand. A live key with no state is `Unowned` and needs `--adopt`; the
|
|
8
|
+
* apply re-checks it (`refuseTakeover`) for a create the planner could not probe — which is EVERY
|
|
9
|
+
* key declared with `budgetId: tier.budgetId`, an Output until the tier exists.
|
|
10
|
+
* ⛔ THE VALUE IS WRITE-ONLY (key-secret.ts): `key: { fromEnv }` names the variable, the value goes
|
|
11
|
+
* over the wire once in `/key/generate`, and no attribute, log line or error carries it. `diff`
|
|
12
|
+
* checks a set variable against the live token in memory; an unset variable skips the check.
|
|
13
|
+
* `read` never touches the environment, and no value or hash is added to state.
|
|
14
|
+
* ★ `defaultRemovalPolicy: 'retain'` — deleting a key breaks whatever holds it (a seat's every call
|
|
15
|
+
* 401s). Dropping the declaration leaves the key live; `.pipe(RemovalPolicy.destroy())` opts in.
|
|
16
|
+
* ⛔ A RENAME IS REFUSED, not replaced. The new key would need the same value, and `/key/generate`
|
|
17
|
+
* rejects a second key with a value it already holds; under `retain` the old row would still be
|
|
18
|
+
* there. Declare the new alias as a new resource and destroy the old one.
|
|
19
|
+
* ★ A DECLARATION MANAGES ONLY WHAT IT NAMES (key-form.ts): an omitted prop is never compared or sent,
|
|
20
|
+
* and `metadata` is a merge (key-metadata.ts), so an adopted key's scope, budget and guardrails are
|
|
21
|
+
* never cleared by leaving them out.
|
|
22
|
+
* ⛔ NO CREDENTIAL IS A PROP; `litellmProviders`' layer supplies `Credentials`.
|
|
23
|
+
*/
|
|
24
|
+
import { Resource } from 'alchemy';
|
|
25
|
+
import { Unowned } from 'alchemy/AdoptPolicy';
|
|
26
|
+
import { isResolved } from 'alchemy/Diff';
|
|
27
|
+
import type { Input } from 'alchemy/Input';
|
|
28
|
+
import * as Provider from 'alchemy/Provider';
|
|
29
|
+
import * as Effect from 'effect/Effect';
|
|
30
|
+
import * as Predicate from 'effect/Predicate';
|
|
31
|
+
import { refuseTakeover } from '../ownership/adopt.ts';
|
|
32
|
+
import {
|
|
33
|
+
LitellmKeyAbsentAfterWriteError,
|
|
34
|
+
LitellmKeyAliasChangedError,
|
|
35
|
+
LitellmKeyFieldNotAppliedError,
|
|
36
|
+
LitellmKeyValueNotHonouredError,
|
|
37
|
+
} from './key-errors.ts';
|
|
38
|
+
import {
|
|
39
|
+
type KeyAttributes,
|
|
40
|
+
type KeyProps,
|
|
41
|
+
createBody,
|
|
42
|
+
differing,
|
|
43
|
+
updateBody,
|
|
44
|
+
} from './key-form.ts';
|
|
45
|
+
import { callbackSlotsIn } from './key-metadata.ts';
|
|
46
|
+
import {
|
|
47
|
+
LitellmKeyCallbackMetadataDeclaredError,
|
|
48
|
+
LitellmKeyCallbackMetadataLiveError,
|
|
49
|
+
} from './key-metadata-errors.ts';
|
|
50
|
+
import { deleteKey, findKey, generateKey, readKeyToken, updateKey } from './key-operations.ts';
|
|
51
|
+
import {
|
|
52
|
+
echoes,
|
|
53
|
+
hasKeyValue,
|
|
54
|
+
refuseDebugLogging,
|
|
55
|
+
resolveKeyValue,
|
|
56
|
+
verifyKeyValue,
|
|
57
|
+
} from './key-secret.ts';
|
|
58
|
+
|
|
59
|
+
export type { KeyAttributes, KeyProps };
|
|
60
|
+
export type { KeyError } from './key-errors.ts';
|
|
61
|
+
|
|
62
|
+
export interface LiteLLMKey extends Resource<'LiteLLM.Key', KeyProps, KeyAttributes> {}
|
|
63
|
+
|
|
64
|
+
export const LiteLLMKey = Resource<LiteLLMKey>('LiteLLM.Key', { defaultRemovalPolicy: 'retain' });
|
|
65
|
+
|
|
66
|
+
export const isLiteLLMKey = (value: unknown): value is LiteLLMKey =>
|
|
67
|
+
Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.Key';
|
|
68
|
+
|
|
69
|
+
/** The row with the memo the row cannot carry: what this provider last wrote for `duration`. */
|
|
70
|
+
const remembering = (live: KeyAttributes, output: KeyAttributes | undefined): KeyAttributes => ({
|
|
71
|
+
...live,
|
|
72
|
+
duration: output?.duration ?? null,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* What state remembers of `duration` after a write: the declaration when it names one, else what it
|
|
77
|
+
* remembered (`before`, `undefined` for a key just created). The row carries only `expires`.
|
|
78
|
+
*/
|
|
79
|
+
const remembered = (news: KeyProps, before: KeyAttributes | undefined): string | null =>
|
|
80
|
+
news.duration !== undefined ? news.duration : (before?.duration ?? null);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The `metadata` refusals that need no write to know (key-metadata.ts): a declaration that names a
|
|
84
|
+
* callback slot, and — given the live row — a metadata write onto a key that holds one.
|
|
85
|
+
*/
|
|
86
|
+
const refuseMetadata = (news: KeyProps, live: KeyAttributes | undefined) =>
|
|
87
|
+
Effect.gen(function* () {
|
|
88
|
+
const declared = callbackSlotsIn(news.metadata);
|
|
89
|
+
if (declared.length > 0) {
|
|
90
|
+
return yield* new LitellmKeyCallbackMetadataDeclaredError({
|
|
91
|
+
keyAlias: news.keyAlias,
|
|
92
|
+
slots: declared,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (
|
|
96
|
+
live !== undefined &&
|
|
97
|
+
live.withheld.length > 0 &&
|
|
98
|
+
differing(live, news).includes('metadata')
|
|
99
|
+
) {
|
|
100
|
+
return yield* new LitellmKeyCallbackMetadataLiveError({
|
|
101
|
+
keyAlias: news.keyAlias,
|
|
102
|
+
slots: live.withheld,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create the key, then prove LiteLLM stored the value it was sent.
|
|
109
|
+
* ⛔ A PROXY THAT MINTS ITS OWN VALUE INSTEAD leaves a row nobody can authenticate with, reported as
|
|
110
|
+
* a success. `/key/generate` echoes the key (`Redacted`, never logged); a different one is deleted
|
|
111
|
+
* again and refused. UNVERIFIED that any proxy does this — the 1.103.0 docstring says the value is
|
|
112
|
+
* honoured — but a key nobody holds is not a failure to discover later.
|
|
113
|
+
*/
|
|
114
|
+
const createKey = (news: KeyProps) =>
|
|
115
|
+
Effect.gen(function* () {
|
|
116
|
+
yield* refuseDebugLogging(news.keyAlias);
|
|
117
|
+
const secret = yield* resolveKeyValue(news.keyAlias, news.key);
|
|
118
|
+
const created = yield* generateKey(createBody(news, secret));
|
|
119
|
+
if (echoes(created.key, secret)) return;
|
|
120
|
+
const removed = yield* deleteKey(news.keyAlias).pipe(
|
|
121
|
+
Effect.as(true),
|
|
122
|
+
Effect.orElseSucceed(() => false),
|
|
123
|
+
);
|
|
124
|
+
return yield* new LitellmKeyValueNotHonouredError({ keyAlias: news.keyAlias, removed });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel. */
|
|
128
|
+
export const keyHandlers = {
|
|
129
|
+
read: ({ olds, output }: { olds: KeyProps; output: KeyAttributes | undefined }) =>
|
|
130
|
+
Effect.gen(function* () {
|
|
131
|
+
const live = yield* findKey(output?.keyAlias ?? olds.keyAlias);
|
|
132
|
+
if (live === undefined) return undefined;
|
|
133
|
+
return output === undefined ? Unowned(live) : remembering(live, output);
|
|
134
|
+
}),
|
|
135
|
+
|
|
136
|
+
diff: ({ news, output }: { news: Input<KeyProps>; output: KeyAttributes | undefined }) =>
|
|
137
|
+
Effect.gen(function* () {
|
|
138
|
+
if (!isResolved(news)) return undefined;
|
|
139
|
+
yield* refuseMetadata(news, output);
|
|
140
|
+
if (output === undefined) return undefined;
|
|
141
|
+
if (news.keyAlias !== output.keyAlias) {
|
|
142
|
+
return yield* new LitellmKeyAliasChangedError({ from: output.keyAlias, to: news.keyAlias });
|
|
143
|
+
}
|
|
144
|
+
// beta.79 Provider.ts:274-289 permits effectful diff, and Plan.ts:1503-1527 calls it
|
|
145
|
+
// even for unchanged props. Refuse an immutable value mismatch before a false noop.
|
|
146
|
+
if (hasKeyValue(news.key)) {
|
|
147
|
+
const token = yield* readKeyToken(news.keyAlias);
|
|
148
|
+
// A dashboard deletion leaves stale output: recreate even without a settings change.
|
|
149
|
+
if (token === undefined) return { action: 'update' } as const;
|
|
150
|
+
yield* verifyKeyValue(news.keyAlias, news.key, token);
|
|
151
|
+
}
|
|
152
|
+
return differing(output, news).length === 0
|
|
153
|
+
? ({ action: 'noop' } as const)
|
|
154
|
+
: ({ action: 'update' } as const);
|
|
155
|
+
}),
|
|
156
|
+
|
|
157
|
+
reconcile: ({
|
|
158
|
+
fqn,
|
|
159
|
+
instanceId,
|
|
160
|
+
news,
|
|
161
|
+
output,
|
|
162
|
+
}: {
|
|
163
|
+
fqn: string;
|
|
164
|
+
instanceId: string;
|
|
165
|
+
news: KeyProps;
|
|
166
|
+
output: KeyAttributes | undefined;
|
|
167
|
+
}) =>
|
|
168
|
+
Effect.gen(function* () {
|
|
169
|
+
const alias = news.keyAlias;
|
|
170
|
+
yield* refuseMetadata(news, undefined);
|
|
171
|
+
const found = yield* findKey(alias);
|
|
172
|
+
const before = found === undefined ? undefined : remembering(found, output);
|
|
173
|
+
if (before === undefined) {
|
|
174
|
+
yield* createKey(news);
|
|
175
|
+
} else {
|
|
176
|
+
yield* refuseTakeover({ fqn, instanceId, output }, `LiteLLM.Key ${alias}`);
|
|
177
|
+
// ⚠️ AN ADOPTED KEY'S DECLARED VALUE IS CHECKED AGAINST THE ROW (key-secret.ts's header):
|
|
178
|
+
// only when `key: { fromEnv }` names a variable, only as a sha256 in memory, and nothing
|
|
179
|
+
// is written about a mismatch (`/key/update` cannot change a key's value). No `key` declared
|
|
180
|
+
// means no extra read: `readKeyToken` is a `/key/list`, and an omitted value is never held.
|
|
181
|
+
if (hasKeyValue(news.key)) {
|
|
182
|
+
yield* verifyKeyValue(alias, news.key, (yield* readKeyToken(alias)) ?? null);
|
|
183
|
+
}
|
|
184
|
+
yield* refuseMetadata(news, before);
|
|
185
|
+
if (differing(before, news).length > 0) yield* updateKey(updateBody(news, before));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const after = yield* findKey(alias);
|
|
189
|
+
if (after === undefined)
|
|
190
|
+
return yield* new LitellmKeyAbsentAfterWriteError({ keyAlias: alias });
|
|
191
|
+
// ★ THE MEMO IS THE DECLARATION when it names a `duration` (else what state already remembered):
|
|
192
|
+
// a write that reached here carried `duration` whenever the expiry differed, so what is left
|
|
193
|
+
// to check is the row itself (and whether it HAS an expiry).
|
|
194
|
+
const settled = { ...after, duration: remembered(news, before) };
|
|
195
|
+
const left = differing(settled, news);
|
|
196
|
+
if (left.length > 0) {
|
|
197
|
+
return yield* new LitellmKeyFieldNotAppliedError({ fields: left, keyAlias: alias });
|
|
198
|
+
}
|
|
199
|
+
return settled;
|
|
200
|
+
}),
|
|
201
|
+
|
|
202
|
+
delete: ({ output }: { output: KeyAttributes }) => deleteKey(output.keyAlias),
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* ⚠️ ENUMERATES NOTHING, ON PURPOSE. `nuke` walks `list` and deletes what it finds; a proxy's key
|
|
206
|
+
* table holds every live credential the estate's services authenticate with, plus dashboard
|
|
207
|
+
* session keys nobody declared — none of which this stack made.
|
|
208
|
+
*/
|
|
209
|
+
list: () => Effect.succeed([]),
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const LiteLLMKeyProvider = () =>
|
|
213
|
+
Provider.effect(LiteLLMKey, Effect.succeed(LiteLLMKey.Provider.of(keyHandlers)));
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `LiteLLM.MCPServer`: adopting a row that already holds a credential, and removal.
|
|
3
|
+
*
|
|
4
|
+
* ★ A CREDENTIAL CANNOT BE READ BACK, so an adopted row has no seal. The declaration is
|
|
5
|
+
* authoritative: with the variable set it is written once (and sealed); with it unset the plan
|
|
6
|
+
* cannot tell, so nothing is written and nothing is demanded.
|
|
7
|
+
* ⚠️ The fake hides `credentials` on read, like the real proxy is assumed to (unmeasured).
|
|
8
|
+
*/
|
|
9
|
+
import { afterEach, beforeEach, expect, test } from 'bun:test';
|
|
10
|
+
import * as RemovalPolicy from 'alchemy/RemovalPolicy';
|
|
11
|
+
import * as Effect from 'effect/Effect';
|
|
12
|
+
import { FAKE_BASE } from './fake-litellm.ts';
|
|
13
|
+
import { type FakeMcpLitellm, serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
|
|
14
|
+
import { fakeStack, writesOf } from './fake-stack.ts';
|
|
15
|
+
import { LiteLLMMCPServer } from './mcp-server.ts';
|
|
16
|
+
import type { McpServerProps } from './mcp-server-types.ts';
|
|
17
|
+
|
|
18
|
+
const KEY = 'sk-test-master';
|
|
19
|
+
const VARIABLE = 'FAKE_MCP_ADOPT_TOKEN';
|
|
20
|
+
|
|
21
|
+
const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
|
|
22
|
+
|
|
23
|
+
const props: McpServerProps = {
|
|
24
|
+
authType: 'api_key',
|
|
25
|
+
authValue: { fromEnv: VARIABLE },
|
|
26
|
+
serverName: 'FAKE_keyed',
|
|
27
|
+
transport: 'http',
|
|
28
|
+
url: 'https://keyed.example.com/mcp',
|
|
29
|
+
};
|
|
30
|
+
const held = serverRow({
|
|
31
|
+
auth_type: 'api_key',
|
|
32
|
+
server_id: 'FAKE-uuid-9',
|
|
33
|
+
server_name: 'FAKE_keyed',
|
|
34
|
+
url: 'https://keyed.example.com/mcp',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
process.env[VARIABLE] = 'FAKE-adopt-one';
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
delete process.env[VARIABLE];
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('adopting with the variable set writes the declared credential once, then is a no-op', async () => {
|
|
45
|
+
const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [held] });
|
|
46
|
+
const engine = stack(fake);
|
|
47
|
+
const declare = Effect.asVoid(LiteLLMMCPServer('Keyed', props));
|
|
48
|
+
await engine.deploy(declare, { adopt: true });
|
|
49
|
+
expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-adopt-one' });
|
|
50
|
+
const before = fake.requests().length;
|
|
51
|
+
expect(await engine.deploy(declare, { adopt: true })).toEqual({ Keyed: 'noop' });
|
|
52
|
+
expect(writesOf(fake.requests().slice(before))).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('adopting with the variable unset writes nothing and demands nothing; a later deploy with it set writes', async () => {
|
|
56
|
+
const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [held] });
|
|
57
|
+
const engine = stack(fake);
|
|
58
|
+
const declare = Effect.asVoid(LiteLLMMCPServer('Keyed', props));
|
|
59
|
+
delete process.env[VARIABLE];
|
|
60
|
+
await engine.deploy(declare, { adopt: true });
|
|
61
|
+
expect(writesOf(fake.requests())).toEqual([]);
|
|
62
|
+
process.env[VARIABLE] = 'FAKE-adopt-two';
|
|
63
|
+
expect(await engine.deploy(declare)).toEqual({ Keyed: 'update' });
|
|
64
|
+
expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-adopt-two' });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('under RemovalPolicy.destroy() a removed declaration deletes the row; the default retains it', async () => {
|
|
68
|
+
const fake = startFakeMcpLitellm({ masterKey: KEY });
|
|
69
|
+
const engine = stack(fake);
|
|
70
|
+
const noCredential: McpServerProps = {
|
|
71
|
+
authType: 'none',
|
|
72
|
+
serverName: props.serverName,
|
|
73
|
+
transport: props.transport,
|
|
74
|
+
url: props.url,
|
|
75
|
+
};
|
|
76
|
+
await engine.deploy(
|
|
77
|
+
Effect.asVoid(LiteLLMMCPServer('Gone', noCredential).pipe(RemovalPolicy.destroy())),
|
|
78
|
+
);
|
|
79
|
+
expect(fake.servers()).toHaveLength(1);
|
|
80
|
+
expect(await engine.deploy(Effect.void)).toEqual({ Gone: 'delete' });
|
|
81
|
+
expect(fake.servers()).toEqual([]);
|
|
82
|
+
|
|
83
|
+
await engine.deploy(Effect.asVoid(LiteLLMMCPServer('Kept', noCredential)));
|
|
84
|
+
await engine.deploy(Effect.void);
|
|
85
|
+
expect(fake.servers()).toHaveLength(1);
|
|
86
|
+
});
|