@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,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fake of LiteLLM's `/credentials/*` routes for tests — TEST ONLY, never imported by `index.ts`.
|
|
3
|
+
* Like `fake-model-litellm.ts` it is a `fetch` function handed to Effect's real `FetchHttpClient`
|
|
4
|
+
* through its `Fetch` reference, so a test exercises distilled's REAL path assembly, JSON encoding
|
|
5
|
+
* and status-to-error matching; only the wire responses a scenario needs are written here.
|
|
6
|
+
*
|
|
7
|
+
* ★ THE MASKING RULE IS THE VENDOR'S, READ FROM THE LIVE 1.103.0 CONTAINER
|
|
8
|
+
* (`litellm_logging.py::_get_masked_values`): a key whose lowercased form contains `token`,
|
|
9
|
+
* `key`, `secret`, `password`, `passwd`, `credentials`, `vertex_credentials` or
|
|
10
|
+
* `authorization` answers `v[:2] + "****" + v[-2:]` for a string longer than four characters,
|
|
11
|
+
* `*****` for a shorter one, the value untouched for a non-string, and a non-sensitive key
|
|
12
|
+
* answers its full value. Both the list and the by-name read apply it (the by-name read with
|
|
13
|
+
* `unmasked_length=4, number_of_asterisks=4` — the same rule).
|
|
14
|
+
* ★ THE READ AND THE DELETE ASK DIFFERENT STORES, AS THE VENDOR DOES (measured): the by-name read
|
|
15
|
+
* answers only rows loaded in memory, while the delete consults the table. `memoryOmits` models
|
|
16
|
+
* a row committed to the table but not loaded in memory, so the read answers 404 while the
|
|
17
|
+
* create for the same name answers 409 — the asymmetry credential-operations.ts documents.
|
|
18
|
+
* ★ A CREATE ON AN EXISTING NAME ANSWERS 409 (the vendor's unique-violation handling,
|
|
19
|
+
* `_credential_exists_detail`), decoded by the real SDK path as core `Conflict`. The PATCH route
|
|
20
|
+
* merges values in both stores; nonempty info normally replaces DB info but merges in memory
|
|
21
|
+
* (`credential_endpoints/endpoints.py:312-319,384-387`, 1.103.0). Empty info leaves DB unchanged. With
|
|
22
|
+
* the body's `credential_name` (the `credential_name_body` member) applied as the new name only
|
|
23
|
+
* when it differs from the path's (the kit never renames in place, so it never does).
|
|
24
|
+
* ★ `FAKE-*` VALUES ONLY. Nothing here is, or looks like, a real credential.
|
|
25
|
+
*/
|
|
26
|
+
/** One request that reached the fake. Its own type, so this file depends on no other fake. */
|
|
27
|
+
export interface FakeCredentialRequest {
|
|
28
|
+
readonly method: string;
|
|
29
|
+
readonly path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type Row = Record<string, unknown>;
|
|
33
|
+
|
|
34
|
+
export interface FakeCredentialLitellm {
|
|
35
|
+
readonly fetch: typeof globalThis.fetch;
|
|
36
|
+
/**
|
|
37
|
+
* Every stored row as the fake's table holds it — values in CLEAR, the fake's own choice: the
|
|
38
|
+
* real table encrypts them, but nothing reads the table directly, only the masked reads, so the
|
|
39
|
+
* encryption seam is invisible here and a test can assert what a write SENT.
|
|
40
|
+
*/
|
|
41
|
+
readonly rows: () => readonly Row[];
|
|
42
|
+
readonly requests: () => readonly FakeCredentialRequest[];
|
|
43
|
+
/** The JSON bodies of every `POST` and `PATCH`, in order (including rejected PATCH requests). */
|
|
44
|
+
readonly bodies: () => readonly Row[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface FakeCredentialOptions {
|
|
48
|
+
readonly masterKey?: string;
|
|
49
|
+
readonly seed?: readonly Row[];
|
|
50
|
+
/** Names committed to the table that the in-memory read omits (a row not loaded). */
|
|
51
|
+
readonly memoryOmits?: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const json = (status: number, body: unknown) =>
|
|
55
|
+
new Response(JSON.stringify(body), { headers: { 'content-type': 'application/json' }, status });
|
|
56
|
+
|
|
57
|
+
/** A minimal 1.103.0-shaped row, as the table holds it. */
|
|
58
|
+
export const credentialRow = (fields: Row): Row => ({
|
|
59
|
+
credential_info: {},
|
|
60
|
+
credential_name: '',
|
|
61
|
+
credential_values: {},
|
|
62
|
+
...fields,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const SENSITIVE_FRAGMENTS: readonly string[] = [
|
|
66
|
+
'authorization',
|
|
67
|
+
'token',
|
|
68
|
+
'key',
|
|
69
|
+
'secret',
|
|
70
|
+
'vertex_credentials',
|
|
71
|
+
'credentials',
|
|
72
|
+
'password',
|
|
73
|
+
'passwd',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/** The vendor's own masking rule, applied to one keyed map (see the header). */
|
|
77
|
+
const masked = (values: unknown): Record<string, unknown> => {
|
|
78
|
+
const out: Record<string, unknown> = {};
|
|
79
|
+
if (typeof values !== 'object' || values === null) return out;
|
|
80
|
+
for (const [key, value] of Object.entries(values as Record<string, unknown>)) {
|
|
81
|
+
const sensitive = SENSITIVE_FRAGMENTS.some((fragment) => key.toLowerCase().includes(fragment));
|
|
82
|
+
if (!sensitive) {
|
|
83
|
+
out[key] = value;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (typeof value !== 'string') {
|
|
87
|
+
out[key] = value;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
out[key] = value.length <= 4 ? '*****' : `${value.slice(0, 2)}****${value.slice(-2)}`;
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const startFakeCredentialLitellm = (
|
|
96
|
+
options: FakeCredentialOptions = {},
|
|
97
|
+
): FakeCredentialLitellm => {
|
|
98
|
+
const masterKey = options.masterKey ?? 'sk-test-master';
|
|
99
|
+
let rows: Row[] = (options.seed ?? []).map((row) => ({ ...row }));
|
|
100
|
+
let memory = rows.map((row) => ({ ...row }));
|
|
101
|
+
const requests: FakeCredentialRequest[] = [];
|
|
102
|
+
const bodies: Row[] = [];
|
|
103
|
+
const memoryOmits = new Set<string>(options.memoryOmits ?? []);
|
|
104
|
+
|
|
105
|
+
const inMemory = (name: string): Row | undefined =>
|
|
106
|
+
memory.find((row) => String(row['credential_name']) === name && !memoryOmits.has(name));
|
|
107
|
+
|
|
108
|
+
const fetch = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
109
|
+
const request =
|
|
110
|
+
input instanceof Request ? new Request(input, init) : new Request(String(input), init);
|
|
111
|
+
const url = new URL(request.url);
|
|
112
|
+
requests.push({ method: request.method, path: `${url.pathname}${url.search}` });
|
|
113
|
+
if (request.headers.get('authorization') !== `Bearer ${masterKey}`) {
|
|
114
|
+
return json(401, { detail: 'invalid api key' });
|
|
115
|
+
}
|
|
116
|
+
const path = url.pathname;
|
|
117
|
+
|
|
118
|
+
// GET /credentials/by_name/{name} — the read, in-memory only (see the header).
|
|
119
|
+
if (path.startsWith('/credentials/by_name/') && request.method === 'GET') {
|
|
120
|
+
const name = decodeURIComponent(path.slice('/credentials/by_name/'.length));
|
|
121
|
+
const found = inMemory(name);
|
|
122
|
+
return found === undefined
|
|
123
|
+
? json(404, { error: { message: `Credential not found. Got credential name: ${name}` } })
|
|
124
|
+
: json(200, {
|
|
125
|
+
...found,
|
|
126
|
+
credential_values: masked(found['credential_values']),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// POST /credentials — the create; 409 on a name the TABLE already has.
|
|
131
|
+
if (path === '/credentials' && request.method === 'POST') {
|
|
132
|
+
const body = (await request.json()) as Row;
|
|
133
|
+
bodies.push(body);
|
|
134
|
+
const name = String(body['credential_name']);
|
|
135
|
+
if (rows.some((row) => String(row['credential_name']) === name)) {
|
|
136
|
+
return json(409, { detail: `Credential '${name}' already exists.` });
|
|
137
|
+
}
|
|
138
|
+
rows = [...rows, credentialRow(body)];
|
|
139
|
+
memory = [...memory, credentialRow(body)];
|
|
140
|
+
memoryOmits.delete(name); // a create loads the row into memory; an omission is seeded-only
|
|
141
|
+
return json(200, { success: true, message: 'Credential created successfully' });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// PATCH /credentials/{name} — DB info replacement, memory merge (see the header); the body's
|
|
145
|
+
// `credential_name` (the `credential_name_body` member) renames only when it differs.
|
|
146
|
+
if (path.startsWith('/credentials/') && request.method === 'PATCH') {
|
|
147
|
+
const name = decodeURIComponent(path.slice('/credentials/'.length));
|
|
148
|
+
const body = (await request.json()) as Row;
|
|
149
|
+
bodies.push(body);
|
|
150
|
+
if (typeof body['credential_name'] !== 'string') {
|
|
151
|
+
return json(422, {
|
|
152
|
+
detail: [{ loc: ['body', 'credential_name'], msg: 'Field required', type: 'missing' }],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (!rows.some((row) => String(row['credential_name']) === name)) {
|
|
156
|
+
return json(404, { error: { message: 'Credential not found in DB.' } });
|
|
157
|
+
}
|
|
158
|
+
const bodyName = body['credential_name'];
|
|
159
|
+
const target =
|
|
160
|
+
typeof bodyName === 'string' && bodyName !== '' && bodyName !== name ? bodyName : name;
|
|
161
|
+
const info = (body['credential_info'] ?? {}) as Row;
|
|
162
|
+
const values = (body['credential_values'] ?? {}) as Row;
|
|
163
|
+
rows = rows.map((row) =>
|
|
164
|
+
String(row['credential_name']) === name
|
|
165
|
+
? {
|
|
166
|
+
...row,
|
|
167
|
+
credential_name: target,
|
|
168
|
+
credential_info:
|
|
169
|
+
Object.keys(info).length === 0
|
|
170
|
+
? row['credential_info']
|
|
171
|
+
: {
|
|
172
|
+
...('credential_info' in ((row['credential_info'] ?? {}) as Row)
|
|
173
|
+
? (row['credential_info'] as Row)
|
|
174
|
+
: {}),
|
|
175
|
+
...info,
|
|
176
|
+
},
|
|
177
|
+
credential_values: { ...((row['credential_values'] ?? {}) as Row), ...values },
|
|
178
|
+
}
|
|
179
|
+
: row,
|
|
180
|
+
);
|
|
181
|
+
memory = memory.map((row) =>
|
|
182
|
+
String(row['credential_name']) === name
|
|
183
|
+
? {
|
|
184
|
+
...row,
|
|
185
|
+
credential_name: target,
|
|
186
|
+
credential_info: { ...((row['credential_info'] ?? {}) as Row), ...info },
|
|
187
|
+
credential_values: { ...((row['credential_values'] ?? {}) as Row), ...values },
|
|
188
|
+
}
|
|
189
|
+
: row,
|
|
190
|
+
);
|
|
191
|
+
return json(200, { success: true, message: 'Credential updated successfully' });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// DELETE /credentials/{name} — DB-authoritative; 404 when the TABLE lacks the name.
|
|
195
|
+
if (path.startsWith('/credentials/') && request.method === 'DELETE') {
|
|
196
|
+
const name = decodeURIComponent(path.slice('/credentials/'.length));
|
|
197
|
+
if (!rows.some((row) => String(row['credential_name']) === name)) {
|
|
198
|
+
return json(404, {
|
|
199
|
+
error: { message: `Credential not found. Got credential name: ${name}` },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
rows = rows.filter((row) => String(row['credential_name']) !== name);
|
|
203
|
+
memory = memory.filter((row) => String(row['credential_name']) !== name);
|
|
204
|
+
return json(200, { success: true, message: 'Credential deleted successfully' });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return json(404, { error: { message: 'not found' } });
|
|
208
|
+
}) as typeof globalThis.fetch;
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
bodies: () => [...bodies],
|
|
212
|
+
fetch,
|
|
213
|
+
requests: () => [...requests],
|
|
214
|
+
rows: () => rows.map((row) => ({ ...row })),
|
|
215
|
+
};
|
|
216
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fake of LiteLLM's `/key/*` routes for `LiteLLM.Key` tests — TEST ONLY, never imported by
|
|
3
|
+
* `index.ts`. `fake-litellm.ts` mounts it, so a test still goes through distilled's REAL path
|
|
4
|
+
* assembly, JSON encode/decode and status→typed-error matching over `FetchHttpClient.Fetch`.
|
|
5
|
+
*
|
|
6
|
+
* ★ MEASURED against the litellm 1.103.0 wheel's source (`key_management_endpoints.py`), not a live
|
|
7
|
+
* proxy: a user-defined `key` must start with `sk-` and be 16+ characters, else 400 (:1477-1490); a
|
|
8
|
+
* duplicate `key_alias` is 400 on generate and update (`_enforce_unique_key_alias`, :7639-7667);
|
|
9
|
+
* `/key/update` applies `model_dump(exclude_unset=True)`, so an explicit `null` clears a column
|
|
10
|
+
* (`duration: null` sets `expires` null, :2468-2500) and omitted fields are untouched; an update
|
|
11
|
+
* that finds no alias is 404 (`_get_and_validate_existing_key`, per the distilled patch); a delete
|
|
12
|
+
* of an alias it does not hold is 404 "No keys found" (:4914-4918) and of a key the caller may not
|
|
13
|
+
* delete 403 (:4933-4936), BOTH answered as a `ProxyException` envelope, because `delete_key_fn`
|
|
14
|
+
* re-raises through `handle_exception_on_proxy` (`proxy/utils.py:7815`), whose `message` is `str(detail)`
|
|
15
|
+
* (`_types.py` `ProxyException`): `{"error": {"message": "{'error': 'No keys found'}", "code": "404", ...}}`;
|
|
16
|
+
* `/key/list` filters `key_alias` exactly unless told otherwise, and
|
|
17
|
+
* `return_full_object` answers full rows, `token` being the sha256 hex of the key (`hash_token`).
|
|
18
|
+
* ⚠️ NOT MODELLED, or only a stand-in: `disable_custom_api_keys` (a 403 for ANY user-defined key);
|
|
19
|
+
* a `budget_id` that names no budget is a foreign-key violation whose status is UNMEASURED — 400
|
|
20
|
+
* here, so a test can prove the Budget is written first.
|
|
21
|
+
* ⛔ THE PLAINTEXT KEY NEVER SITS IN A ROW. Rows carry only `token`, a sha256 of the value, the way
|
|
22
|
+
* a LiteLLM row carries a hash. The values the proxy was SENT are kept apart in `received()`, so a
|
|
23
|
+
* test can prove what reached the wire without the value ever being in the state a resource sees.
|
|
24
|
+
*/
|
|
25
|
+
type Row = Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
export interface FakeKeysOptions {
|
|
28
|
+
/** Live rows, wire-shaped like `UserAPIKeyAuth` (`key_alias`, `budget_id`, `models`, `token`, …). */
|
|
29
|
+
readonly keySeed?: readonly Row[];
|
|
30
|
+
/** `GET /key/list` answers this status and nothing else — a transient read failure. */
|
|
31
|
+
readonly keyListStatus?: number;
|
|
32
|
+
/** `POST /key/delete` answers 403 and keeps the row — a caller that may not delete. */
|
|
33
|
+
readonly keyDeleteForbidden?: boolean;
|
|
34
|
+
/** `POST /key/delete` deletes the row and STILL answers 404 — another caller got there first. */
|
|
35
|
+
readonly keyDeleteRaces?: boolean;
|
|
36
|
+
/** `/key/generate` ignores the user-defined `key` and mints its own. */
|
|
37
|
+
readonly keyIgnoresUserValue?: boolean;
|
|
38
|
+
/** `/key/update` drops explicit `null`s instead of writing them (an `exclude_none` merge). */
|
|
39
|
+
readonly keyUpdateIgnoresNull?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface FakeKeys {
|
|
43
|
+
readonly route: (request: Request, url: URL) => Promise<Response>;
|
|
44
|
+
readonly rows: () => readonly Row[];
|
|
45
|
+
/** Every JSON body a `/key/*` write carried, in order. FAKE-* material only. */
|
|
46
|
+
readonly writes: () => readonly { readonly path: string; readonly body: Row }[];
|
|
47
|
+
/** The raw key values `/key/generate` was sent — kept off the rows on purpose. */
|
|
48
|
+
readonly received: () => readonly string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const json = (status: number, body: unknown) =>
|
|
52
|
+
new Response(JSON.stringify(body), { headers: { 'content-type': 'application/json' }, status });
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* What `/key/delete` answers a failure with: a `ProxyException`, whose `message` is Python's `str()` of
|
|
56
|
+
* the `HTTPException` detail dict (`{'error': '…'}`), and whose `code` is the status as a string.
|
|
57
|
+
*/
|
|
58
|
+
const proxyException = (status: number, error: string) =>
|
|
59
|
+
json(status, {
|
|
60
|
+
error: {
|
|
61
|
+
code: String(status),
|
|
62
|
+
message: `{'error': '${error}'}`,
|
|
63
|
+
param: null,
|
|
64
|
+
type: 'internal_server_error',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const sha256 = (value: string): string =>
|
|
69
|
+
new Bun.CryptoHasher('sha256').update(value).digest('hex');
|
|
70
|
+
|
|
71
|
+
const UNITS = { d: 86_400_000, h: 3_600_000, m: 60_000, s: 1_000 } as const;
|
|
72
|
+
|
|
73
|
+
/** `'30d'` → an ISO timestamp that far ahead; `undefined` for anything else (a bad duration is refused; its status is UNMEASURED). */
|
|
74
|
+
const expiryFor = (duration: string): string | undefined => {
|
|
75
|
+
const match = /^(\d+)([smhd])$/.exec(duration);
|
|
76
|
+
if (match === null) return undefined;
|
|
77
|
+
const unit = UNITS[match[2] as keyof typeof UNITS];
|
|
78
|
+
return new Date(Date.now() + Number(match[1]) * unit).toISOString();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** The columns `/key/update` may write; each list column's `null` means "none". */
|
|
82
|
+
const LIST_COLUMNS = new Set(['models', 'allowed_routes']);
|
|
83
|
+
|
|
84
|
+
export const createFakeKeys = (
|
|
85
|
+
options: FakeKeysOptions | undefined,
|
|
86
|
+
budgetExists: (budgetId: string) => boolean,
|
|
87
|
+
): FakeKeys => {
|
|
88
|
+
let rows: Row[] = [...(options?.keySeed ?? [])];
|
|
89
|
+
const writes: { path: string; body: Row }[] = [];
|
|
90
|
+
const received: string[] = [];
|
|
91
|
+
let minted = 0;
|
|
92
|
+
|
|
93
|
+
const byAlias = (alias: unknown) => rows.findIndex((row) => row['key_alias'] === alias);
|
|
94
|
+
|
|
95
|
+
const generate = (body: Row) => {
|
|
96
|
+
const alias = body['key_alias'];
|
|
97
|
+
const supplied = body['key'];
|
|
98
|
+
if (typeof supplied === 'string') received.push(supplied);
|
|
99
|
+
if (
|
|
100
|
+
typeof supplied === 'string' &&
|
|
101
|
+
(!supplied.startsWith('sk-') || supplied.length < 16) &&
|
|
102
|
+
options?.keyIgnoresUserValue !== true
|
|
103
|
+
) {
|
|
104
|
+
return json(400, { detail: 'user-defined key must start with sk- and be 16+ characters' });
|
|
105
|
+
}
|
|
106
|
+
if (byAlias(alias) !== -1) return json(400, { detail: `key alias '${String(alias)}' exists` });
|
|
107
|
+
const budgetId = body['budget_id'];
|
|
108
|
+
if (typeof budgetId === 'string' && !budgetExists(budgetId)) {
|
|
109
|
+
return json(400, { detail: `budget ${budgetId} not found` });
|
|
110
|
+
}
|
|
111
|
+
minted += 1;
|
|
112
|
+
const value =
|
|
113
|
+
typeof supplied === 'string' && options?.keyIgnoresUserValue !== true
|
|
114
|
+
? supplied
|
|
115
|
+
: `sk-FAKE-minted-${minted}-0000000000`;
|
|
116
|
+
const token = sha256(value);
|
|
117
|
+
// ⚠️ A key VALUE already held is a unique-token violation; its status is UNMEASURED.
|
|
118
|
+
if (rows.some((row) => row['token'] === token)) return json(400, { detail: 'key exists' });
|
|
119
|
+
const duration = body['duration'];
|
|
120
|
+
const expires = typeof duration === 'string' ? expiryFor(duration) : null;
|
|
121
|
+
if (expires === undefined) return json(400, { detail: 'invalid duration' });
|
|
122
|
+
const { key: _key, duration: _duration, ...rest } = body;
|
|
123
|
+
const row = { models: [], allowed_routes: [], metadata: {}, ...rest, expires, token };
|
|
124
|
+
rows = [...rows, row];
|
|
125
|
+
return json(200, { ...row, key: value });
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const update = (body: Row) => {
|
|
129
|
+
const at = byAlias(body['key_alias']);
|
|
130
|
+
if (at === -1) return json(404, { detail: 'no key with that alias' });
|
|
131
|
+
const budgetId = body['budget_id'];
|
|
132
|
+
if (typeof budgetId === 'string' && !budgetExists(budgetId)) {
|
|
133
|
+
return json(400, { detail: `budget ${budgetId} not found` });
|
|
134
|
+
}
|
|
135
|
+
const ignoresNull = options?.keyUpdateIgnoresNull === true;
|
|
136
|
+
const patch: Row = {};
|
|
137
|
+
for (const [column, value] of Object.entries(body)) {
|
|
138
|
+
if (column === 'key' || column === 'duration') continue;
|
|
139
|
+
if (value === null && ignoresNull) continue;
|
|
140
|
+
patch[column] = value === null && LIST_COLUMNS.has(column) ? [] : value;
|
|
141
|
+
}
|
|
142
|
+
if ('duration' in body && !(body['duration'] === null && ignoresNull)) {
|
|
143
|
+
const duration = body['duration'];
|
|
144
|
+
const expires = typeof duration === 'string' ? expiryFor(duration) : null;
|
|
145
|
+
if (expires === undefined) return json(400, { detail: 'invalid duration' });
|
|
146
|
+
patch['expires'] = expires;
|
|
147
|
+
}
|
|
148
|
+
rows = rows.map((row, i) => (i === at ? { ...row, ...patch } : row));
|
|
149
|
+
return json(200, {});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const remove = (body: Row) => {
|
|
153
|
+
if (options?.keyDeleteForbidden === true) {
|
|
154
|
+
return proxyException(403, 'You are not authorized to delete this key');
|
|
155
|
+
}
|
|
156
|
+
const aliases = Array.isArray(body['key_aliases']) ? body['key_aliases'] : [];
|
|
157
|
+
const found = aliases.filter((alias) => byAlias(alias) !== -1);
|
|
158
|
+
if (found.length === 0) return proxyException(404, 'No keys found');
|
|
159
|
+
rows = rows.filter((row) => !found.includes(row['key_alias']));
|
|
160
|
+
if (options?.keyDeleteRaces === true) return proxyException(404, 'No keys found');
|
|
161
|
+
return json(200, { deleted_keys: found });
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const route = async (request: Request, url: URL): Promise<Response> => {
|
|
165
|
+
if (request.method === 'GET' && url.pathname === '/key/list') {
|
|
166
|
+
if (options?.keyListStatus !== undefined) {
|
|
167
|
+
return json(options.keyListStatus, { detail: 'upstream unavailable' });
|
|
168
|
+
}
|
|
169
|
+
const alias = url.searchParams.get('key_alias');
|
|
170
|
+
const matching = rows.filter((row) => alias === null || row['key_alias'] === alias);
|
|
171
|
+
const full = url.searchParams.get('return_full_object') === 'true';
|
|
172
|
+
const keys = full ? matching : matching.map((row) => row['token']);
|
|
173
|
+
return json(200, { keys, total_count: keys.length, current_page: 1, total_pages: 1 });
|
|
174
|
+
}
|
|
175
|
+
if (request.method !== 'POST') return json(405, { detail: `unhandled ${request.method}` });
|
|
176
|
+
const body = (await request.json()) as Row;
|
|
177
|
+
writes.push({ body, path: url.pathname });
|
|
178
|
+
if (url.pathname === '/key/generate') return generate(body);
|
|
179
|
+
if (url.pathname === '/key/update') return update(body);
|
|
180
|
+
if (url.pathname === '/key/delete') return remove(body);
|
|
181
|
+
return json(404, { detail: 'not found' });
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
received: () => [...received],
|
|
186
|
+
route,
|
|
187
|
+
rows: () => [...rows],
|
|
188
|
+
writes: () => [...writes],
|
|
189
|
+
};
|
|
190
|
+
};
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* fake now matches that measured vendor behaviour.
|
|
28
28
|
*/
|
|
29
29
|
import type { PassThroughGenericEndpoint } from '@distilled.cloud/litellm/misc';
|
|
30
|
+
import { type FakeKeys, type FakeKeysOptions, createFakeKeys } from './fake-keys.ts';
|
|
30
31
|
|
|
31
32
|
/** RFC 2606 placeholder — never a real host. Shared across tests, like `../netbox/fake-netbox.ts`. */
|
|
32
33
|
export const FAKE_BASE = 'https://litellm.example.com';
|
|
@@ -42,6 +43,8 @@ export interface FakeLitellm {
|
|
|
42
43
|
readonly requests: () => readonly FakeRequest[];
|
|
43
44
|
/** `/budget/*` rows, wire-shaped (`tpm_limit`/`rpm_limit` as decimal strings, like the real table). */
|
|
44
45
|
readonly budgets: () => readonly Record<string, unknown>[];
|
|
46
|
+
/** `/key/*` rows and the writes they took — see fake-keys.ts. */
|
|
47
|
+
readonly keys: FakeKeys;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const RACE_WINDOW_MS = 15;
|
|
@@ -50,20 +53,25 @@ const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve,
|
|
|
50
53
|
const json = (status: number, body: unknown) =>
|
|
51
54
|
new Response(JSON.stringify(body), { headers: { 'content-type': 'application/json' }, status });
|
|
52
55
|
|
|
53
|
-
export const startFakeLitellm = (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
export const startFakeLitellm = (
|
|
57
|
+
options?: {
|
|
58
|
+
readonly masterKey?: string;
|
|
59
|
+
readonly seed?: readonly PassThroughGenericEndpoint[];
|
|
60
|
+
/** Every `DELETE` 400s `not_allowed_access`, whether or not the row exists — see the file header. */
|
|
61
|
+
readonly forbidDelete?: boolean;
|
|
62
|
+
readonly budgetSeed?: readonly Record<string, unknown>[];
|
|
63
|
+
/** `/budget/update` drops `null`s instead of writing them (an `exclude_none` merge). */
|
|
64
|
+
readonly budgetExcludeNone?: boolean;
|
|
65
|
+
} & FakeKeysOptions,
|
|
66
|
+
): FakeLitellm => {
|
|
62
67
|
const masterKey = options?.masterKey ?? 'sk-test-master';
|
|
63
68
|
const forbidDelete = options?.forbidDelete ?? false;
|
|
64
69
|
let rows: PassThroughGenericEndpoint[] = [...(options?.seed ?? [])];
|
|
65
70
|
const requests: FakeRequest[] = [];
|
|
66
71
|
let budgets: Record<string, unknown>[] = [...(options?.budgetSeed ?? [])];
|
|
72
|
+
// ★ A KEY'S `budget_id` MUST NAME A LIVE BUDGET — the foreign key that makes `LiteLLM.Key` wait for
|
|
73
|
+
// the `LiteLLM.Budget` it binds to. Read at call time: `budgets` is reassigned on every write.
|
|
74
|
+
const keys = createFakeKeys(options, (id) => budgets.some((row) => row['budget_id'] === id));
|
|
67
75
|
|
|
68
76
|
/**
|
|
69
77
|
* ⚠️ Shapes from the generated 1.100.0 schema (`budget_management.ts`), not a live 1.103.0 read.
|
|
@@ -110,6 +118,7 @@ export const startFakeLitellm = (options?: {
|
|
|
110
118
|
const auth = request.headers.get('authorization');
|
|
111
119
|
if (auth !== `Bearer ${masterKey}`) return json(401, { detail: 'invalid api key' });
|
|
112
120
|
if (url.pathname.startsWith('/budget/')) return budgetRoute(request, url);
|
|
121
|
+
if (url.pathname.startsWith('/key/')) return keys.route(request, url);
|
|
113
122
|
if (
|
|
114
123
|
url.pathname !== '/config/pass_through_endpoint' &&
|
|
115
124
|
!url.pathname.startsWith('/config/pass_through_endpoint/')
|
|
@@ -164,6 +173,7 @@ export const startFakeLitellm = (options?: {
|
|
|
164
173
|
return {
|
|
165
174
|
budgets: () => [...budgets],
|
|
166
175
|
fetch,
|
|
176
|
+
keys,
|
|
167
177
|
requests: () => [...requests],
|
|
168
178
|
rows: () => [...rows],
|
|
169
179
|
};
|