@homeflare/alchemy 0.27.4 → 0.29.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 +2 -0
- package/dist/caddy/admin-address.d.ts +27 -0
- package/dist/caddy/admin-address.d.ts.map +1 -0
- package/dist/caddy/admin-calls.d.ts +25 -28
- package/dist/caddy/admin-calls.d.ts.map +1 -1
- package/dist/caddy/admin.d.ts +28 -38
- package/dist/caddy/admin.d.ts.map +1 -1
- package/dist/caddy/caddy-http-client.d.ts +25 -0
- package/dist/caddy/caddy-http-client.d.ts.map +1 -0
- package/dist/caddy/config-errors.d.ts +24 -0
- package/dist/caddy/config-errors.d.ts.map +1 -0
- package/dist/caddy/config-lifecycle.d.ts +20 -49
- package/dist/caddy/config-lifecycle.d.ts.map +1 -1
- package/dist/caddy/config-reconcile.d.ts +45 -0
- package/dist/caddy/config-reconcile.d.ts.map +1 -0
- package/dist/caddy/config.d.ts +4 -3
- package/dist/caddy/config.d.ts.map +1 -1
- package/dist/caddy/fake-caddy.d.ts +10 -2
- package/dist/caddy/fake-caddy.d.ts.map +1 -1
- package/dist/caddy/index.d.ts +3 -3
- package/dist/caddy/index.d.ts.map +1 -1
- package/dist/caddy/index.js +487 -444
- package/dist/caddy/index.js.map +16 -12
- package/dist/caddy/local-admin.d.ts +20 -34
- package/dist/caddy/local-admin.d.ts.map +1 -1
- package/dist/caddy/providers.d.ts +19 -3
- package/dist/caddy/providers.d.ts.map +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -1
- package/dist/forgejo/branch-protection.d.ts +4 -4
- package/dist/forgejo/org-actions-secrets.d.ts +4 -4
- package/dist/forgejo/org-label.d.ts +4 -4
- package/dist/forgejo/org-team.d.ts +4 -4
- package/dist/forgejo/repo-webhook.d.ts +4 -4
- package/dist/forgejo/repository.d.ts +4 -4
- package/dist/forgejo/team-member.d.ts +4 -4
- package/dist/github/declare-repo-baseline.d.ts +1 -1
- package/dist/github/declare-repo-baseline.d.ts.map +1 -1
- package/dist/github/index.d.ts +9 -5
- package/dist/github/index.d.ts.map +1 -1
- package/dist/github/index.js +161 -25
- package/dist/github/index.js.map +15 -13
- package/dist/github/repo-baseline-data.d.ts +10 -1
- package/dist/github/repo-baseline-data.d.ts.map +1 -1
- package/dist/github/repo-policy.d.ts +1 -1
- package/dist/github/repo-policy.d.ts.map +1 -1
- package/dist/github/repository-ruleset-errors.d.ts +37 -3
- package/dist/github/repository-ruleset-errors.d.ts.map +1 -1
- package/dist/github/repository-ruleset-form.d.ts.map +1 -1
- package/dist/github/repository-ruleset-guards.d.ts +15 -2
- package/dist/github/repository-ruleset-guards.d.ts.map +1 -1
- package/dist/github/repository-ruleset-narrowing-guards.d.ts +39 -0
- package/dist/github/repository-ruleset-narrowing-guards.d.ts.map +1 -0
- package/dist/github/repository-ruleset-octokit.d.ts +49 -0
- package/dist/github/repository-ruleset-octokit.d.ts.map +1 -1
- package/dist/github/repository-ruleset-probe.d.ts +2 -1
- package/dist/github/repository-ruleset-probe.d.ts.map +1 -1
- package/dist/github/repository-ruleset-props.d.ts +91 -0
- package/dist/github/repository-ruleset-props.d.ts.map +1 -0
- package/dist/github/repository-ruleset-providers.d.ts +42 -0
- package/dist/github/repository-ruleset-providers.d.ts.map +1 -0
- package/dist/github/repository-ruleset-reconcile.d.ts.map +1 -1
- package/dist/github/repository-ruleset.d.ts +28 -34
- package/dist/github/repository-ruleset.d.ts.map +1 -1
- package/dist/litellm/fake-litellm.d.ts +27 -16
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-stack.d.ts +9 -4
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/index.d.ts +7 -5
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +53 -123
- package/dist/litellm/index.js.map +8 -8
- package/dist/litellm/operations.d.ts +60 -0
- package/dist/litellm/operations.d.ts.map +1 -0
- package/dist/litellm/pass-through-endpoint.d.ts +10 -28
- package/dist/litellm/pass-through-endpoint.d.ts.map +1 -1
- package/dist/litellm/pass-through-errors.d.ts +37 -0
- package/dist/litellm/pass-through-errors.d.ts.map +1 -0
- package/dist/litellm/pass-through-form.d.ts +11 -4
- package/dist/litellm/pass-through-form.d.ts.map +1 -1
- package/dist/litellm/providers.d.ts +7 -5
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/netbox/prefix.d.ts +4 -4
- package/dist/paperless/custom-field.d.ts +21 -21
- package/dist/paperless/custom-field.d.ts.map +1 -1
- package/dist/paperless/document-type.d.ts +34 -4
- package/dist/paperless/document-type.d.ts.map +1 -1
- package/dist/paperless/fake-paperless.d.ts +36 -19
- package/dist/paperless/fake-paperless.d.ts.map +1 -1
- package/dist/paperless/index.d.ts +7 -2
- package/dist/paperless/index.d.ts.map +1 -1
- package/dist/paperless/index.js +164 -228
- package/dist/paperless/index.js.map +10 -13
- package/dist/paperless/matching-locate.d.ts +42 -20
- package/dist/paperless/matching-locate.d.ts.map +1 -1
- package/dist/paperless/matching-types.d.ts +47 -18
- package/dist/paperless/matching-types.d.ts.map +1 -1
- package/dist/paperless/matching.d.ts +30 -27
- package/dist/paperless/matching.d.ts.map +1 -1
- package/dist/paperless/providers.d.ts +13 -7
- package/dist/paperless/providers.d.ts.map +1 -1
- package/dist/paperless/storage-path.d.ts +34 -4
- package/dist/paperless/storage-path.d.ts.map +1 -1
- package/dist/paperless/tag.d.ts +43 -8
- package/dist/paperless/tag.d.ts.map +1 -1
- package/dist/proxmox/acl.d.ts +30 -23
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/distilled-pve.d.ts +89 -0
- package/dist/proxmox/distilled-pve.d.ts.map +1 -0
- package/dist/proxmox/index.js +663 -609
- package/dist/proxmox/index.js.map +12 -11
- package/dist/proxmox/members.d.ts +29 -10
- package/dist/proxmox/members.d.ts.map +1 -1
- package/docs/caddy-admin.md +1 -1
- package/docs/caddy.md +3 -2
- package/docs/credentials.md +16 -0
- package/docs/litellm.md +21 -13
- package/docs/paperless.md +12 -0
- package/docs/peers.md +5 -0
- package/docs/repo-policy.md +16 -1
- package/docs/repository-ruleset.md +53 -13
- package/docs/upstream-conformance.md +126 -10
- package/package.json +9 -2
- package/src/caddy/admin-address.ts +71 -0
- package/src/caddy/admin-calls.ts +48 -100
- package/src/caddy/admin.ts +37 -41
- package/src/caddy/adopt-scope.test.ts +1 -1
- package/src/caddy/caddy-http-client.ts +229 -0
- package/src/caddy/config-adopt-provider.test.ts +139 -0
- package/src/caddy/config-adopt.test.ts +60 -126
- package/src/caddy/config-errors.ts +47 -0
- package/src/caddy/config-lifecycle.test.ts +55 -42
- package/src/caddy/config-lifecycle.ts +72 -162
- package/src/caddy/config-reconcile.ts +138 -0
- package/src/caddy/config.ts +36 -42
- package/src/caddy/fake-caddy.ts +20 -2
- package/src/caddy/index.ts +3 -8
- package/src/caddy/local-admin.test.ts +35 -21
- package/src/caddy/local-admin.ts +60 -167
- package/src/caddy/providers.test.ts +14 -6
- package/src/caddy/providers.ts +20 -4
- package/src/github/declare-repo-baseline.ts +6 -6
- package/src/github/index.ts +10 -4
- package/src/github/repo-baseline-data.test.ts +13 -0
- package/src/github/repo-baseline-data.ts +11 -2
- package/src/github/repo-policy-stack.test.ts +59 -0
- package/src/github/repo-policy.ts +3 -2
- package/src/github/repository-ruleset-errors.ts +51 -4
- package/src/github/repository-ruleset-exact-adopt-k1.test.ts +109 -0
- package/src/github/repository-ruleset-exact-adopt.test.ts +184 -0
- package/src/github/repository-ruleset-form.ts +7 -3
- package/src/github/repository-ruleset-guards.ts +11 -4
- package/src/github/repository-ruleset-narrowing-guards.ts +141 -0
- package/src/github/repository-ruleset-narrowing.test.ts +210 -0
- package/src/github/repository-ruleset-octokit.test.ts +141 -0
- package/src/github/repository-ruleset-octokit.ts +121 -30
- package/src/github/repository-ruleset-probe.ts +2 -1
- package/src/github/repository-ruleset-props.ts +95 -0
- package/src/github/repository-ruleset-providers.ts +46 -0
- package/src/github/repository-ruleset-reconcile.ts +35 -0
- package/src/github/repository-ruleset-wire.test.ts +38 -27
- package/src/github/repository-ruleset-write-refusals.test.ts +6 -26
- package/src/github/repository-ruleset.ts +53 -43
- package/src/litellm/fake-litellm.ts +83 -78
- package/src/litellm/fake-stack.ts +13 -5
- package/src/litellm/index.ts +7 -21
- package/src/litellm/operations.ts +149 -0
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +17 -20
- package/src/litellm/pass-through-endpoint-replace.test.ts +3 -4
- package/src/litellm/pass-through-endpoint.test.ts +8 -8
- package/src/litellm/pass-through-endpoint.ts +21 -31
- package/src/litellm/pass-through-errors.ts +37 -0
- package/src/litellm/pass-through-form.ts +20 -10
- package/src/litellm/providers.ts +8 -7
- package/src/paperless/custom-field.test.ts +49 -40
- package/src/paperless/custom-field.ts +57 -33
- package/src/paperless/document-type.test.ts +48 -0
- package/src/paperless/document-type.ts +51 -22
- package/src/paperless/fake-paperless.ts +63 -72
- package/src/paperless/index.ts +7 -13
- package/src/paperless/matching-locate.test.ts +102 -0
- package/src/paperless/matching-locate.ts +80 -86
- package/src/paperless/matching-types.ts +49 -16
- package/src/paperless/matching.ts +77 -90
- package/src/paperless/providers.ts +14 -10
- package/src/paperless/storage-path.test.ts +56 -0
- package/src/paperless/storage-path.ts +53 -24
- package/src/paperless/tag-identity.test.ts +127 -0
- package/src/paperless/tag.test.ts +100 -0
- package/src/paperless/tag.ts +62 -32
- package/src/proxmox/acl.test.ts +145 -0
- package/src/proxmox/acl.ts +128 -150
- package/src/proxmox/distilled-pve.test.ts +231 -0
- package/src/proxmox/distilled-pve.ts +147 -0
- package/src/proxmox/members-timeout.test.ts +161 -0
- package/src/proxmox/members.test.ts +9 -0
- package/src/proxmox/members.ts +36 -2
- package/dist/litellm/client.d.ts +0 -76
- package/dist/litellm/client.d.ts.map +0 -1
- package/dist/litellm/credentials.d.ts +0 -48
- package/dist/litellm/credentials.d.ts.map +0 -1
- package/dist/litellm/generated/pass-through.d.ts +0 -71
- package/dist/litellm/generated/pass-through.d.ts.map +0 -1
- package/dist/paperless/client.d.ts +0 -37
- package/dist/paperless/client.d.ts.map +0 -1
- package/dist/paperless/credentials.d.ts +0 -40
- package/dist/paperless/credentials.d.ts.map +0 -1
- package/dist/paperless/errors.d.ts +0 -49
- package/dist/paperless/errors.d.ts.map +0 -1
- package/src/litellm/client.ts +0 -195
- package/src/litellm/credentials.ts +0 -73
- package/src/litellm/generated/pass-through.ts +0 -73
- package/src/paperless/client.ts +0 -88
- package/src/paperless/credentials.ts +0 -69
- package/src/paperless/errors.ts +0 -62
- package/src/paperless/matching-identity.test.ts +0 -194
- package/src/paperless/matching.test.ts +0 -128
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One authenticated call to the Paperless-ngx REST API at `/api`.
|
|
3
|
-
*
|
|
4
|
-
* ⚠️ `Authorization: Token <value>`, NOT `Bearer`. Paperless-ngx's `securitySchemes.tokenAuth`
|
|
5
|
-
* (the v3.1.1 served schema) states the "Token" prefix explicitly — the same DRF
|
|
6
|
-
* `TokenAuthentication` NetBox uses (netbox/client.ts), a different vendor, same convention.
|
|
7
|
-
*
|
|
8
|
-
* ⛔ `Accept: application/json; version=10` ON EVERY CALL. Paperless-ngx versions its API with
|
|
9
|
-
* DRF's `AcceptHeaderVersioning` (`REST_FRAMEWORK.DEFAULT_VERSION = '10'`, measured against the
|
|
10
|
-
* v3.1.1 source). Omitting the header still answers — DRF falls back to `DEFAULT_VERSION` — but
|
|
11
|
-
* pinning it here means an estate upgrade that moves the default cannot silently change what
|
|
12
|
-
* this package receives without a version bump of its own noticing.
|
|
13
|
-
*
|
|
14
|
-
* ⚠️ A LIST IS WRAPPED, A DETAIL IS NOT: `{count, next, previous, results: […]}` vs. the bare
|
|
15
|
-
* object — the same DRF pagination shape NetBox uses.
|
|
16
|
-
*/
|
|
17
|
-
import * as Effect from 'effect/Effect';
|
|
18
|
-
import * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
19
|
-
import { PaperlessCredentials, type PaperlessCredentialsError } from './credentials.ts';
|
|
20
|
-
import { type PaperlessError } from './errors.ts';
|
|
21
|
-
export interface PaperlessList<T> {
|
|
22
|
-
readonly count: number;
|
|
23
|
-
readonly next: string | null;
|
|
24
|
-
readonly previous: string | null;
|
|
25
|
-
readonly results: readonly T[];
|
|
26
|
-
}
|
|
27
|
-
export type PaperlessRow = Record<string, unknown>;
|
|
28
|
-
/**
|
|
29
|
-
* ⚠️ `PaperlessCredentials` IS PART OF THIS, NOT JUST `HttpClient`. `paperless()` reads the lazy
|
|
30
|
-
* credentials service (`yield* yield* PaperlessCredentials`), so a stack that wires the
|
|
31
|
-
* providers without also providing a credentials layer should fail to TYPECHECK, not fail at
|
|
32
|
-
* the first call. `providers.ts` supplies `environmentLayer` by default for exactly this.
|
|
33
|
-
*/
|
|
34
|
-
export type PaperlessRequirements = HttpClient.HttpClient | PaperlessCredentials;
|
|
35
|
-
/** One JSON call. Returns the parsed body, or `undefined` for an empty response (DELETE). */
|
|
36
|
-
export declare const paperless: <T>(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: Record<string, unknown>) => Effect.Effect<T, PaperlessError | PaperlessCredentialsError, PaperlessRequirements>;
|
|
37
|
-
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/paperless/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,KAAK,cAAc,EAAuC,MAAM,aAAa,CAAC;AAKvF,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;CAChC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,UAAU,GAAG,oBAAoB,CAAC;AAEjF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,GAAI,CAAC,UACjB,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,QAC7C,MAAM,SACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,cAAc,GAAG,yBAAyB,EAAE,qBAAqB,CAwCjF,CAAC"}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where the Paperless URL and token come from — S24's shape (alchemy-provider-standard): a lazy
|
|
3
|
-
* `Context.Service` whose VALUE is itself an `Effect`, resolved inside each operation with
|
|
4
|
-
* `yield* yield* PaperlessCredentials`. Resolving twice, not once at layer build, is what keeps a
|
|
5
|
-
* token out of any state the engine might persist and lets a consumer swap the source (an env var
|
|
6
|
-
* today, an OpenBao mint tomorrow) without touching a resource file.
|
|
7
|
-
*
|
|
8
|
-
* ⛔ NO DEFAULT HOST, AND NO DEFAULT TOKEN. A published package that fell back to a loopback
|
|
9
|
-
* address would let a consumer who forgot the variable watch every call fail against a machine
|
|
10
|
-
* that is not theirs, instead of being told which variable is missing.
|
|
11
|
-
* ⛔ THE TOKEN IS NEVER A PROP, NEVER AN ATTRIBUTE. Alchemy persists attributes unencrypted
|
|
12
|
-
* (S25); nothing this family returns from `read`/`reconcile` may carry it.
|
|
13
|
-
*/
|
|
14
|
-
import * as Context from 'effect/Context';
|
|
15
|
-
import * as Effect from 'effect/Effect';
|
|
16
|
-
import * as Layer from 'effect/Layer';
|
|
17
|
-
declare const PaperlessCredentialsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
18
|
-
readonly _tag: "PaperlessCredentialsError";
|
|
19
|
-
} & Readonly<A>;
|
|
20
|
-
export declare class PaperlessCredentialsError extends PaperlessCredentialsError_base<{
|
|
21
|
-
readonly message: string;
|
|
22
|
-
}> {
|
|
23
|
-
}
|
|
24
|
-
export interface PaperlessCredentialValue {
|
|
25
|
-
/** Trailing slash stripped, e.g. `https://paperless.example.com`. */
|
|
26
|
-
readonly url: string;
|
|
27
|
-
readonly token: string;
|
|
28
|
-
}
|
|
29
|
-
declare const PaperlessCredentials_base: Context.ServiceClass<PaperlessCredentials, "PaperlessCredentials", Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError, never>>;
|
|
30
|
-
/**
|
|
31
|
-
* The lazy service. ⚠️ `Layer.succeed` registers the EFFECT, not its result — `readEnvironment`
|
|
32
|
-
* runs once per `yield* yield*`, so a call after the variables are exported sees them even if
|
|
33
|
-
* the layer itself was built before that (true in `fake-paperless.test.ts`, which sets the env
|
|
34
|
-
* vars right before each call).
|
|
35
|
-
*/
|
|
36
|
-
export declare class PaperlessCredentials extends PaperlessCredentials_base {
|
|
37
|
-
}
|
|
38
|
-
export declare const environmentLayer: Layer.Layer<PaperlessCredentials>;
|
|
39
|
-
export {};
|
|
40
|
-
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/paperless/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;;;AAEtC,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;;AA4BD;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,yBAGf;CAAG;AAE9B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAG9D,CAAC"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
declare const PaperlessNotFound_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
2
|
-
readonly _tag: "PaperlessNotFound";
|
|
3
|
-
} & Readonly<A>;
|
|
4
|
-
/** No object at that path. The only tag `matching.ts` folds to "absent". */
|
|
5
|
-
export declare class PaperlessNotFound extends PaperlessNotFound_base<{
|
|
6
|
-
readonly method: string;
|
|
7
|
-
readonly path: string;
|
|
8
|
-
}> {
|
|
9
|
-
}
|
|
10
|
-
declare const PaperlessUnauthorized_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
11
|
-
readonly _tag: "PaperlessUnauthorized";
|
|
12
|
-
} & Readonly<A>;
|
|
13
|
-
/** 401 (no/bad token) or 403 (token valid, forbidden) — NEVER read as "the object is absent". */
|
|
14
|
-
export declare class PaperlessUnauthorized extends PaperlessUnauthorized_base<{
|
|
15
|
-
readonly status: 401 | 403;
|
|
16
|
-
readonly method: string;
|
|
17
|
-
readonly path: string;
|
|
18
|
-
}> {
|
|
19
|
-
}
|
|
20
|
-
declare const PaperlessValidation_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
21
|
-
readonly _tag: "PaperlessValidation";
|
|
22
|
-
} & Readonly<A>;
|
|
23
|
-
/** 400 — the vendor rejected the body. Carries it, so the message quotes Paperless's own words. */
|
|
24
|
-
export declare class PaperlessValidation extends PaperlessValidation_base<{
|
|
25
|
-
readonly method: string;
|
|
26
|
-
readonly path: string;
|
|
27
|
-
readonly body: string;
|
|
28
|
-
}> {
|
|
29
|
-
}
|
|
30
|
-
declare const PaperlessUnavailable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
31
|
-
readonly _tag: "PaperlessUnavailable";
|
|
32
|
-
} & Readonly<A>;
|
|
33
|
-
/** 5xx, or a status this family's endpoints do not otherwise document. */
|
|
34
|
-
export declare class PaperlessUnavailable extends PaperlessUnavailable_base<{
|
|
35
|
-
readonly status: number;
|
|
36
|
-
readonly method: string;
|
|
37
|
-
readonly path: string;
|
|
38
|
-
readonly detail: string;
|
|
39
|
-
}> {
|
|
40
|
-
}
|
|
41
|
-
export type PaperlessError = PaperlessNotFound | PaperlessUnauthorized | PaperlessValidation | PaperlessUnavailable;
|
|
42
|
-
/**
|
|
43
|
-
* The one place a status code is read. ⚠️ Anything outside {400,401,403,404,5xx} — 405, 409, 429
|
|
44
|
-
* — is not documented for these four endpoints, so it falls to `PaperlessValidation` rather than
|
|
45
|
-
* inventing a fifth tag for a case that was never measured.
|
|
46
|
-
*/
|
|
47
|
-
export declare const statusToError: (status: number, method: string, path: string, body: string) => PaperlessError;
|
|
48
|
-
export {};
|
|
49
|
-
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/paperless/errors.ts"],"names":[],"mappings":";;;AAYA,4EAA4E;AAC5E,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;CAAG;;;;AAEL,iGAAiG;AACjG,qBAAa,qBAAsB,SAAQ,2BAA0C;IACnF,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;CAAG;;;;AAEL,mGAAmG;AACnG,qBAAa,mBAAoB,SAAQ,yBAAwC;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;CAAG;;;;AAEL,0EAA0E;AAC1E,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;CAAG;AAEL,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,CAAC;AAEzB;;;;GAIG;AACH,eAAO,MAAM,aAAa,WAChB,MAAM,UACN,MAAM,QACR,MAAM,QACN,MAAM,KACX,cAKF,CAAC"}
|
package/src/litellm/client.ts
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The four LiteLLM proxy calls a `LiteLLM.PassThroughEndpoint` needs, and nothing else reaches
|
|
3
|
-
* LiteLLM's `/config/pass_through_endpoint` routes.
|
|
4
|
-
*
|
|
5
|
-
* ⛔ WRAPPED IN A PER-BASE-URL SEMAPHORE. LiteLLM stores every pass-through endpoint in ONE
|
|
6
|
-
* `general_settings.pass_through_endpoints` list (pass_through_endpoints.py at tag v1.100.0),
|
|
7
|
-
* so create/update/delete are each a read-modify-write of the WHOLE list through
|
|
8
|
-
* `update_config_general_settings`. Two of this package's own calls racing each other — two
|
|
9
|
-
* endpoints created in the same deploy — would each read the list, add their own row and write
|
|
10
|
-
* it back, and the second write would silently drop the first row. One permit per proxy
|
|
11
|
-
* serialises this package's own writes; it cannot serialise against the LiteLLM UI or a second
|
|
12
|
-
* deploy process, which is why `reconcile` reads back after writing rather than trusting the
|
|
13
|
-
* call that just returned (docs/litellm.md).
|
|
14
|
-
* ★ `Authorization: Bearer <key>` — LiteLLM's own client uses it (litellm/proxy/client/cli, and
|
|
15
|
-
* every `/config/*` route decorator at the tag takes `user_api_key_dict = Depends(user_api_key_auth)`,
|
|
16
|
-
* which accepts a bearer token). Every mutation additionally requires the caller be
|
|
17
|
-
* PROXY_ADMIN (`update_config_general_settings`, proxy_server.py:16411 at the tag) — an
|
|
18
|
-
* ordinary virtual key gets `LitellmUnauthorizedError`, not a clearer 403 body naming the role.
|
|
19
|
-
* ⛔ NO MESSAGE SNIFFING (S21). Every status this file maps becomes a typed tag; nothing here or
|
|
20
|
-
* above it matches an error body's text. Deleting an id LiteLLM does not have answers 400
|
|
21
|
-
* (measured against the tag's `pass_through_endpoint_delete` — the id is looked up in the list
|
|
22
|
-
* and a miss raises `HTTPException(400)`, not 404) — but so does a genuinely-live row when the
|
|
23
|
-
* caller isn't PROXY_ADMIN, or the DB is disconnected (`update_config_general_settings`,
|
|
24
|
-
* proxy_server.py:16389-16416 at the tag): the SAME status, for a delete that did NOT happen.
|
|
25
|
-
* `deletePassThroughEndpoint` tells them apart by re-listing (a GET, which needs no PROXY_ADMIN —
|
|
26
|
-
* proxy_server.py:3245-3251) rather than by the 400's body text — see its own comment.
|
|
27
|
-
*/
|
|
28
|
-
import * as Data from 'effect/Data';
|
|
29
|
-
import * as Effect from 'effect/Effect';
|
|
30
|
-
import * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
31
|
-
import * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';
|
|
32
|
-
import * as Semaphore from 'effect/Semaphore';
|
|
33
|
-
import { LitellmCredentials, type LitellmCredentialsError } from './credentials.ts';
|
|
34
|
-
import type {
|
|
35
|
-
PassThroughEndpointResponse,
|
|
36
|
-
PassThroughGenericEndpoint,
|
|
37
|
-
} from './generated/pass-through.ts';
|
|
38
|
-
|
|
39
|
-
export type LitellmRequirements = HttpClient.HttpClient | LitellmCredentials;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The `litellm:<METHOD> <path>` operations this package writes to and reads from.
|
|
43
|
-
*
|
|
44
|
-
* ★ `codegen/litellm.ts` scans this FILE's text for these exact literals to pick which of
|
|
45
|
-
* LiteLLM 1.100.0's 696 paths to generate types from (codegen/litellm.ts header). Keep each one
|
|
46
|
-
* a literal string on one line; the generator matches text, it does not evaluate the object.
|
|
47
|
-
*/
|
|
48
|
-
export const LITELLM_OPERATIONS = {
|
|
49
|
-
create: 'litellm:POST /config/pass_through_endpoint',
|
|
50
|
-
delete: 'litellm:DELETE /config/pass_through_endpoint',
|
|
51
|
-
list: 'litellm:GET /config/pass_through_endpoint',
|
|
52
|
-
update: 'litellm:POST /config/pass_through_endpoint/{endpoint_id}',
|
|
53
|
-
} as const;
|
|
54
|
-
|
|
55
|
-
export class LitellmUnauthorizedError extends Data.TaggedError('LitellmUnauthorizedError')<{
|
|
56
|
-
readonly status: number;
|
|
57
|
-
readonly method: string;
|
|
58
|
-
readonly path: string;
|
|
59
|
-
}> {}
|
|
60
|
-
export class LitellmBadRequestError extends Data.TaggedError('LitellmBadRequestError')<{
|
|
61
|
-
readonly method: string;
|
|
62
|
-
readonly path: string;
|
|
63
|
-
readonly detail: string;
|
|
64
|
-
}> {}
|
|
65
|
-
export class LitellmHttpError extends Data.TaggedError('LitellmHttpError')<{
|
|
66
|
-
readonly status: number;
|
|
67
|
-
readonly method: string;
|
|
68
|
-
readonly path: string;
|
|
69
|
-
readonly detail: string;
|
|
70
|
-
}> {}
|
|
71
|
-
export class LitellmTransportError extends Data.TaggedError('LitellmTransportError')<{
|
|
72
|
-
readonly method: string;
|
|
73
|
-
readonly path: string;
|
|
74
|
-
readonly cause: string;
|
|
75
|
-
}> {}
|
|
76
|
-
|
|
77
|
-
export type LitellmError =
|
|
78
|
-
| LitellmUnauthorizedError
|
|
79
|
-
| LitellmBadRequestError
|
|
80
|
-
| LitellmHttpError
|
|
81
|
-
| LitellmTransportError
|
|
82
|
-
| LitellmCredentialsError;
|
|
83
|
-
|
|
84
|
-
const semaphores = new Map<string, Semaphore.Semaphore>();
|
|
85
|
-
|
|
86
|
-
/** One semaphore per base URL, created on first use. Never `Hash.structure` (S18) — a plain Map key. */
|
|
87
|
-
const semaphoreFor = (baseUrl: string): Effect.Effect<Semaphore.Semaphore> =>
|
|
88
|
-
Effect.sync(() => {
|
|
89
|
-
const existing = semaphores.get(baseUrl);
|
|
90
|
-
if (existing !== undefined) return existing;
|
|
91
|
-
const created = Semaphore.makeUnsafe(1);
|
|
92
|
-
semaphores.set(baseUrl, created);
|
|
93
|
-
return created;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const call = <T>(method: 'GET' | 'POST' | 'DELETE', path: string, body?: unknown) =>
|
|
97
|
-
Effect.gen(function* () {
|
|
98
|
-
const creds = yield* yield* LitellmCredentials;
|
|
99
|
-
const client = yield* HttpClient.HttpClient;
|
|
100
|
-
const url = `${creds.baseUrl}${path}`;
|
|
101
|
-
const request = HttpClientRequest.make(method)(url).pipe(
|
|
102
|
-
HttpClientRequest.setHeaders({
|
|
103
|
-
Accept: 'application/json',
|
|
104
|
-
Authorization: `Bearer ${creds.apiKey}`,
|
|
105
|
-
}),
|
|
106
|
-
body === undefined
|
|
107
|
-
? (self) => self
|
|
108
|
-
: HttpClientRequest.bodyText(JSON.stringify(body), 'application/json'),
|
|
109
|
-
);
|
|
110
|
-
const response = yield* client
|
|
111
|
-
.execute(request)
|
|
112
|
-
.pipe(
|
|
113
|
-
Effect.mapError(
|
|
114
|
-
(cause) => new LitellmTransportError({ cause: String(cause), method, path }),
|
|
115
|
-
),
|
|
116
|
-
);
|
|
117
|
-
const text = yield* response.text.pipe(
|
|
118
|
-
Effect.mapError((cause) => new LitellmTransportError({ cause: String(cause), method, path })),
|
|
119
|
-
);
|
|
120
|
-
if (response.status === 401 || response.status === 403) {
|
|
121
|
-
return yield* Effect.fail(
|
|
122
|
-
new LitellmUnauthorizedError({ method, path, status: response.status }),
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
if (response.status === 400) {
|
|
126
|
-
return yield* Effect.fail(
|
|
127
|
-
new LitellmBadRequestError({ detail: text.slice(0, 300), method, path }),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
if (response.status < 200 || response.status >= 300) {
|
|
131
|
-
return yield* Effect.fail(
|
|
132
|
-
new LitellmHttpError({ detail: text.slice(0, 300), method, path, status: response.status }),
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
return (text === '' ? undefined : JSON.parse(text)) as T;
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
/** GET /config/pass_through_endpoint — every row, config-file and DB alike. Not under the semaphore: reads don't race the field. */
|
|
139
|
-
export const listPassThroughEndpoints = (): Effect.Effect<
|
|
140
|
-
readonly PassThroughGenericEndpoint[],
|
|
141
|
-
LitellmError,
|
|
142
|
-
LitellmRequirements
|
|
143
|
-
> =>
|
|
144
|
-
call<PassThroughEndpointResponse>('GET', '/config/pass_through_endpoint').pipe(
|
|
145
|
-
Effect.map((body) => body.endpoints),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const mutate = <T>(io: Effect.Effect<T, LitellmError, LitellmRequirements>) =>
|
|
149
|
-
Effect.gen(function* () {
|
|
150
|
-
const creds = yield* yield* LitellmCredentials;
|
|
151
|
-
const semaphore = yield* semaphoreFor(creds.baseUrl);
|
|
152
|
-
return yield* semaphore.withPermits(1)(io);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
/** POST /config/pass_through_endpoint. `body` carries `id` — see pass-through-endpoint.ts. */
|
|
156
|
-
export const createPassThroughEndpoint = (
|
|
157
|
-
body: PassThroughGenericEndpoint,
|
|
158
|
-
): Effect.Effect<void, LitellmError, LitellmRequirements> =>
|
|
159
|
-
mutate(call('POST', '/config/pass_through_endpoint', body));
|
|
160
|
-
|
|
161
|
-
/** POST /config/pass_through_endpoint/{endpoint_id}. Merges with `exclude_none` — see pass-through-form.ts. */
|
|
162
|
-
export const updatePassThroughEndpoint = (
|
|
163
|
-
endpointId: string,
|
|
164
|
-
body: Partial<PassThroughGenericEndpoint>,
|
|
165
|
-
): Effect.Effect<void, LitellmError, LitellmRequirements> =>
|
|
166
|
-
mutate(call('POST', `/config/pass_through_endpoint/${encodeURIComponent(endpointId)}`, body));
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* DELETE /config/pass_through_endpoint?endpoint_id=… — idempotent BY THIS FUNCTION, not by the
|
|
170
|
-
* vendor. ⚠️ Measured at v1.100.0: deleting an id the list does not have raises `HTTPException(400)`
|
|
171
|
-
* from `pass_through_endpoint_delete` — but the SAME route also 400s `not_allowed_access` for a
|
|
172
|
-
* non-PROXY_ADMIN caller or a disconnected DB (`update_config_general_settings`,
|
|
173
|
-
* proxy_server.py:16389-16416), on a row that never got deleted. A 400 alone cannot tell the two
|
|
174
|
-
* apart, so THIS FUNCTION DOESN'T GUESS FROM THE STATUS ALONE: on 400 it re-lists (a GET — no
|
|
175
|
-
* PROXY_ADMIN required, proxy_server.py:3245-3251) and only treats the delete as already-done if
|
|
176
|
-
* `endpointId` is genuinely absent; otherwise the delete failed and this re-fails with the ORIGINAL
|
|
177
|
-
* error, not a message match (S21 — a status-and-a-real-read decision, never body text).
|
|
178
|
-
*/
|
|
179
|
-
export const deletePassThroughEndpoint = (
|
|
180
|
-
endpointId: string,
|
|
181
|
-
): Effect.Effect<void, LitellmError, LitellmRequirements> =>
|
|
182
|
-
mutate(
|
|
183
|
-
call(
|
|
184
|
-
'DELETE',
|
|
185
|
-
`/config/pass_through_endpoint?endpoint_id=${encodeURIComponent(endpointId)}`,
|
|
186
|
-
).pipe(
|
|
187
|
-
Effect.catchTag('LitellmBadRequestError', (original) =>
|
|
188
|
-
Effect.gen(function* () {
|
|
189
|
-
const rows = yield* listPassThroughEndpoints();
|
|
190
|
-
if (rows.some((row) => row.id === endpointId)) return yield* Effect.fail(original);
|
|
191
|
-
}),
|
|
192
|
-
),
|
|
193
|
-
Effect.asVoid,
|
|
194
|
-
),
|
|
195
|
-
);
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The lazy Auth service every LiteLLM call reads from, and nothing else reads the environment.
|
|
3
|
-
*
|
|
4
|
-
* ★ LITELLM'S OWN VARIABLE NAMES, NOT A HOUSE CHOICE. `LITELLM_PROXY_URL` and
|
|
5
|
-
* `LITELLM_PROXY_API_KEY` are what LiteLLM's own CLI client reads
|
|
6
|
-
* (litellm/proxy/client/cli/main.py:70,78 at tag v1.100.0) — using the vendor's own names means
|
|
7
|
-
* a consumer who already has a `.env` for the `litellm` CLI needs nothing new for this package.
|
|
8
|
-
* ⛔ NO DEFAULT URL. A published package that fell back to a loopback address would let a
|
|
9
|
-
* consumer who forgot the variable watch every call fail against a proxy that is not theirs,
|
|
10
|
-
* instead of being told which variable is missing (S24/S25 — the house rule, not a vendor one).
|
|
11
|
-
* ⛔ THE KEY IS NEVER A PROP. Alchemy persists attributes unencrypted (S25); the master key that
|
|
12
|
-
* `update_config_general_settings` requires (PROXY_ADMIN only, proxy_server.py:16411 at the
|
|
13
|
-
* tag) is read fresh from the environment on every call, never stored in state.
|
|
14
|
-
* ★ LAZY: the service's VALUE is an `Effect` that resolves the credential, not the credential
|
|
15
|
-
* itself — so a layer built once still reads the environment inside each operation
|
|
16
|
-
* (`yield* yield* LitellmCredentials`), the idiom `alchemy/Auth`'s custom-auth-provider guide
|
|
17
|
-
* describes for a credential a layer must not capture at build time.
|
|
18
|
-
*/
|
|
19
|
-
import * as Context from 'effect/Context';
|
|
20
|
-
import * as Data from 'effect/Data';
|
|
21
|
-
import * as Effect from 'effect/Effect';
|
|
22
|
-
import * as Layer from 'effect/Layer';
|
|
23
|
-
|
|
24
|
-
export const LITELLM_PROXY_URL_ENV = 'LITELLM_PROXY_URL';
|
|
25
|
-
export const LITELLM_PROXY_API_KEY_ENV = 'LITELLM_PROXY_API_KEY';
|
|
26
|
-
|
|
27
|
-
export interface LitellmCreds {
|
|
28
|
-
/** Trailing slash stripped. No default — see the header. */
|
|
29
|
-
readonly baseUrl: string;
|
|
30
|
-
/** LiteLLM's master (or a PROXY_ADMIN) key. Never logged, never an attribute. */
|
|
31
|
-
readonly apiKey: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export class LitellmCredentialsError extends Data.TaggedError('LitellmCredentialsError')<{
|
|
35
|
-
readonly message: string;
|
|
36
|
-
}> {}
|
|
37
|
-
|
|
38
|
-
const missing = (name: string): LitellmCredentialsError =>
|
|
39
|
-
new LitellmCredentialsError({
|
|
40
|
-
message:
|
|
41
|
-
`${name} is not set. Export it — the same variable LiteLLM's own \`litellm\` CLI ` +
|
|
42
|
-
'reads — before running a stack that declares a LiteLLM.PassThroughEndpoint.',
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
/** Read at call time. Exported so a test can call it directly against `process.env`. */
|
|
46
|
-
export const resolveCreds: Effect.Effect<LitellmCreds, LitellmCredentialsError> = Effect.gen(
|
|
47
|
-
function* () {
|
|
48
|
-
const rawUrl = process.env[LITELLM_PROXY_URL_ENV];
|
|
49
|
-
if (rawUrl === undefined || rawUrl.trim() === '')
|
|
50
|
-
return yield* Effect.fail(missing(LITELLM_PROXY_URL_ENV));
|
|
51
|
-
const rawKey = process.env[LITELLM_PROXY_API_KEY_ENV];
|
|
52
|
-
if (rawKey === undefined || rawKey.trim() === '') {
|
|
53
|
-
return yield* Effect.fail(missing(LITELLM_PROXY_API_KEY_ENV));
|
|
54
|
-
}
|
|
55
|
-
return { baseUrl: rawUrl.trim().replace(/\/$/, ''), apiKey: rawKey.trim() };
|
|
56
|
-
},
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
/** The Effect service a LiteLLM operation reads: `yield* yield* LitellmCredentials`. */
|
|
60
|
-
export class LitellmCredentials extends Context.Service<
|
|
61
|
-
LitellmCredentials,
|
|
62
|
-
Effect.Effect<LitellmCreds, LitellmCredentialsError>
|
|
63
|
-
>()('homeflare/litellm/Credentials') {}
|
|
64
|
-
|
|
65
|
-
/** The default layer: resolves from the environment, fresh, on every `yield*`. */
|
|
66
|
-
export const litellmCredentialsLayer: Layer.Layer<LitellmCredentials> = Layer.succeed(
|
|
67
|
-
LitellmCredentials,
|
|
68
|
-
resolveCreds,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
/** For a test: a fixed credential, never touching the environment. */
|
|
72
|
-
export const litellmCredentialsLayerFor = (creds: LitellmCreds): Layer.Layer<LitellmCredentials> =>
|
|
73
|
-
Layer.succeed(LitellmCredentials, Effect.succeed(creds));
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generated from LiteLLM 1.100.0's OpenAPI document -- DO NOT EDIT BY HAND.
|
|
3
|
-
*
|
|
4
|
-
* Run: bun codegen/litellm.ts (`--check` compares without writing)
|
|
5
|
-
* Manifest entry: `litellm-openapi` -- LiteLLM Proxy (app.openapi()) 1.100.0
|
|
6
|
-
* sha256 1b3e4d23da6aea92..., read from codegen/manifest.json's own note on how this document
|
|
7
|
-
* was produced (the vendor's own generator, cross-checked byte-identically — not a live read).
|
|
8
|
-
*
|
|
9
|
-
* Covers the 4 pass-through operations this package writes to:
|
|
10
|
-
* litellm:DELETE /config/pass_through_endpoint
|
|
11
|
-
* litellm:GET /config/pass_through_endpoint
|
|
12
|
-
* litellm:POST /config/pass_through_endpoint
|
|
13
|
-
* litellm:POST /config/pass_through_endpoint/{endpoint_id}
|
|
14
|
-
*
|
|
15
|
-
* Does NOT cover the other 692 paths LiteLLM 1.100.0 publishes — this generator is
|
|
16
|
-
* consumer-driven (codegen/litellm.ts), not exhaustive. See codegen/README.md.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
export interface PassThroughEndpointResponse {
|
|
20
|
-
readonly endpoints: readonly PassThroughGenericEndpoint[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface PassThroughGenericEndpoint {
|
|
24
|
-
/**
|
|
25
|
-
* Whether authentication is required for the pass-through endpoint. Defaults to True so a pass-through silently created without an explicit value still requires a valid LiteLLM API key — set to False only if the endpoint is meant to be a public forwarder (e.g. an unauthenticated webhook target).
|
|
26
|
-
* @default true
|
|
27
|
-
*/
|
|
28
|
-
readonly auth?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* The USD cost per request to the target endpoint. This is used to calculate the cost of the request to the target endpoint.
|
|
31
|
-
* @default 0
|
|
32
|
-
*/
|
|
33
|
-
readonly cost_per_request?: number;
|
|
34
|
-
/**
|
|
35
|
-
* Key-value pairs of default query parameters to be sent with every request to this endpoint. These can be overridden by client-provided query parameters. For example: {'key': 'default_value', 'api_version': '2023-01'}
|
|
36
|
-
* @default {}
|
|
37
|
-
*/
|
|
38
|
-
readonly default_query_params?: Record<string, unknown>;
|
|
39
|
-
/** Guardrails configuration for this passthrough endpoint. Dict keys are guardrail names, values are optional settings for field targeting. When set, all org/team/key level guardrails will also execute. Defaults to None (no guardrails execute). */
|
|
40
|
-
readonly guardrails?: Record<string, PassThroughGuardrailSettings | null> | null;
|
|
41
|
-
/**
|
|
42
|
-
* Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint
|
|
43
|
-
* @default {}
|
|
44
|
-
*/
|
|
45
|
-
readonly headers?: Record<string, unknown>;
|
|
46
|
-
/** Optional unique identifier for the pass-through endpoint. If not provided, endpoints will be identified by path for backwards compatibility. */
|
|
47
|
-
readonly id?: string | null;
|
|
48
|
-
/**
|
|
49
|
-
* If True, requests to subpaths of the path will be forwarded to the target endpoint. For example, if the path is /bria and include_subpath is True, requests to /bria/v1/text-to-image/base/2.3 will be forwarded to the target endpoint.
|
|
50
|
-
* @default false
|
|
51
|
-
*/
|
|
52
|
-
readonly include_subpath?: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* True if this endpoint is defined in the config file, False if from DB. Config-defined endpoints cannot be edited via the UI.
|
|
55
|
-
* @default false
|
|
56
|
-
*/
|
|
57
|
-
readonly is_from_config?: boolean;
|
|
58
|
-
/** List of HTTP methods this endpoint handles (e.g., ['GET', 'POST']). If None or empty, all methods (GET, POST, PUT, DELETE, PATCH) are supported for backward compatibility. This allows the same path to have different targets for different HTTP methods. */
|
|
59
|
-
readonly methods?: readonly string[] | null;
|
|
60
|
-
/** The route to be added to the LiteLLM Proxy Server. */
|
|
61
|
-
readonly path: string;
|
|
62
|
-
/** The URL to which requests for this path should be forwarded. */
|
|
63
|
-
readonly target: string;
|
|
64
|
-
/** Upstream request timeout in seconds for this pass-through endpoint. If unset, uses general_settings.pass_through_request_timeout (default 600). */
|
|
65
|
-
readonly timeout?: number | null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface PassThroughGuardrailSettings {
|
|
69
|
-
/** JSONPath expressions for input field targeting (pre_call). Examples: 'query', 'documents[*].text', 'messages[*].content'. If not specified, guardrail runs on entire request payload. */
|
|
70
|
-
readonly request_fields?: readonly string[] | null;
|
|
71
|
-
/** JSONPath expressions for output field targeting (post_call). Examples: 'results[*].text', 'output'. If not specified, guardrail runs on entire response payload. */
|
|
72
|
-
readonly response_fields?: readonly string[] | null;
|
|
73
|
-
}
|
package/src/paperless/client.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One authenticated call to the Paperless-ngx REST API at `/api`.
|
|
3
|
-
*
|
|
4
|
-
* ⚠️ `Authorization: Token <value>`, NOT `Bearer`. Paperless-ngx's `securitySchemes.tokenAuth`
|
|
5
|
-
* (the v3.1.1 served schema) states the "Token" prefix explicitly — the same DRF
|
|
6
|
-
* `TokenAuthentication` NetBox uses (netbox/client.ts), a different vendor, same convention.
|
|
7
|
-
*
|
|
8
|
-
* ⛔ `Accept: application/json; version=10` ON EVERY CALL. Paperless-ngx versions its API with
|
|
9
|
-
* DRF's `AcceptHeaderVersioning` (`REST_FRAMEWORK.DEFAULT_VERSION = '10'`, measured against the
|
|
10
|
-
* v3.1.1 source). Omitting the header still answers — DRF falls back to `DEFAULT_VERSION` — but
|
|
11
|
-
* pinning it here means an estate upgrade that moves the default cannot silently change what
|
|
12
|
-
* this package receives without a version bump of its own noticing.
|
|
13
|
-
*
|
|
14
|
-
* ⚠️ A LIST IS WRAPPED, A DETAIL IS NOT: `{count, next, previous, results: […]}` vs. the bare
|
|
15
|
-
* object — the same DRF pagination shape NetBox uses.
|
|
16
|
-
*/
|
|
17
|
-
import * as Effect from 'effect/Effect';
|
|
18
|
-
import * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
19
|
-
import * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';
|
|
20
|
-
import { PaperlessCredentials, type PaperlessCredentialsError } from './credentials.ts';
|
|
21
|
-
import { type PaperlessError, PaperlessUnavailable, statusToError } from './errors.ts';
|
|
22
|
-
|
|
23
|
-
const PATH_PREFIX = '/api';
|
|
24
|
-
const API_VERSION = '10';
|
|
25
|
-
|
|
26
|
-
export interface PaperlessList<T> {
|
|
27
|
-
readonly count: number;
|
|
28
|
-
readonly next: string | null;
|
|
29
|
-
readonly previous: string | null;
|
|
30
|
-
readonly results: readonly T[];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type PaperlessRow = Record<string, unknown>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* ⚠️ `PaperlessCredentials` IS PART OF THIS, NOT JUST `HttpClient`. `paperless()` reads the lazy
|
|
37
|
-
* credentials service (`yield* yield* PaperlessCredentials`), so a stack that wires the
|
|
38
|
-
* providers without also providing a credentials layer should fail to TYPECHECK, not fail at
|
|
39
|
-
* the first call. `providers.ts` supplies `environmentLayer` by default for exactly this.
|
|
40
|
-
*/
|
|
41
|
-
export type PaperlessRequirements = HttpClient.HttpClient | PaperlessCredentials;
|
|
42
|
-
|
|
43
|
-
/** One JSON call. Returns the parsed body, or `undefined` for an empty response (DELETE). */
|
|
44
|
-
export const paperless = <T>(
|
|
45
|
-
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
46
|
-
path: string,
|
|
47
|
-
body?: Record<string, unknown>,
|
|
48
|
-
): Effect.Effect<T, PaperlessError | PaperlessCredentialsError, PaperlessRequirements> =>
|
|
49
|
-
Effect.gen(function* () {
|
|
50
|
-
const credentials = yield* yield* PaperlessCredentials;
|
|
51
|
-
const url = `${credentials.url}${PATH_PREFIX}/${path.replace(/^\//, '')}`;
|
|
52
|
-
const client = yield* HttpClient.HttpClient;
|
|
53
|
-
const request = HttpClientRequest.make(method)(url).pipe(
|
|
54
|
-
HttpClientRequest.setHeaders({
|
|
55
|
-
Accept: `application/json; version=${API_VERSION}`,
|
|
56
|
-
Authorization: `Token ${credentials.token}`,
|
|
57
|
-
}),
|
|
58
|
-
// ⛔ Content-Type on `bodyText`, not the header map — `bodyText(s)` with no second argument
|
|
59
|
-
// sets `text/plain` and overwrites a `Content-Type` set earlier via `setHeaders`, because
|
|
60
|
-
// it is piped afterwards (measured against `@homeflare/alchemy/forgejo`, netbox/client.ts).
|
|
61
|
-
body === undefined
|
|
62
|
-
? (self) => self
|
|
63
|
-
: HttpClientRequest.bodyText(JSON.stringify(body), 'application/json'),
|
|
64
|
-
);
|
|
65
|
-
const response = yield* client
|
|
66
|
-
.execute(request)
|
|
67
|
-
.pipe(
|
|
68
|
-
Effect.mapError(
|
|
69
|
-
(cause) => new PaperlessUnavailable({ detail: String(cause), method, path, status: 0 }),
|
|
70
|
-
),
|
|
71
|
-
);
|
|
72
|
-
if (response.status < 200 || response.status >= 300) {
|
|
73
|
-
const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ''));
|
|
74
|
-
return yield* Effect.fail(statusToError(response.status, method, path, detail.slice(0, 500)));
|
|
75
|
-
}
|
|
76
|
-
const text = yield* response.text.pipe(
|
|
77
|
-
Effect.mapError(
|
|
78
|
-
() =>
|
|
79
|
-
new PaperlessUnavailable({
|
|
80
|
-
detail: 'body read failed',
|
|
81
|
-
method,
|
|
82
|
-
path,
|
|
83
|
-
status: response.status,
|
|
84
|
-
}),
|
|
85
|
-
),
|
|
86
|
-
);
|
|
87
|
-
return (text === '' ? undefined : JSON.parse(text)) as T;
|
|
88
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where the Paperless URL and token come from — S24's shape (alchemy-provider-standard): a lazy
|
|
3
|
-
* `Context.Service` whose VALUE is itself an `Effect`, resolved inside each operation with
|
|
4
|
-
* `yield* yield* PaperlessCredentials`. Resolving twice, not once at layer build, is what keeps a
|
|
5
|
-
* token out of any state the engine might persist and lets a consumer swap the source (an env var
|
|
6
|
-
* today, an OpenBao mint tomorrow) without touching a resource file.
|
|
7
|
-
*
|
|
8
|
-
* ⛔ NO DEFAULT HOST, AND NO DEFAULT TOKEN. A published package that fell back to a loopback
|
|
9
|
-
* address would let a consumer who forgot the variable watch every call fail against a machine
|
|
10
|
-
* that is not theirs, instead of being told which variable is missing.
|
|
11
|
-
* ⛔ THE TOKEN IS NEVER A PROP, NEVER AN ATTRIBUTE. Alchemy persists attributes unencrypted
|
|
12
|
-
* (S25); nothing this family returns from `read`/`reconcile` may carry it.
|
|
13
|
-
*/
|
|
14
|
-
import * as Context from 'effect/Context';
|
|
15
|
-
import * as Data from 'effect/Data';
|
|
16
|
-
import * as Effect from 'effect/Effect';
|
|
17
|
-
import * as Layer from 'effect/Layer';
|
|
18
|
-
|
|
19
|
-
export class PaperlessCredentialsError extends Data.TaggedError('PaperlessCredentialsError')<{
|
|
20
|
-
readonly message: string;
|
|
21
|
-
}> {}
|
|
22
|
-
|
|
23
|
-
export interface PaperlessCredentialValue {
|
|
24
|
-
/** Trailing slash stripped, e.g. `https://paperless.example.com`. */
|
|
25
|
-
readonly url: string;
|
|
26
|
-
readonly token: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* ★ `readEnvironment` NAMES EVERY VARIABLE IT CONSUMES (S24) — `PAPERLESS_URL`, `PAPERLESS_TOKEN`
|
|
31
|
-
* — so a consumer's `alchemy provider check-env` (or an equivalent preflight) can list them.
|
|
32
|
-
*/
|
|
33
|
-
const readEnvironment: Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError> =
|
|
34
|
-
Effect.suspend(() => {
|
|
35
|
-
const url = process.env['PAPERLESS_URL'];
|
|
36
|
-
const token = process.env['PAPERLESS_TOKEN'];
|
|
37
|
-
if (url === undefined || url.trim() === '') {
|
|
38
|
-
return Effect.fail(
|
|
39
|
-
new PaperlessCredentialsError({
|
|
40
|
-
message: 'PAPERLESS_URL is not set. Point it at your Paperless-ngx instance.',
|
|
41
|
-
}),
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
if (token === undefined || token.trim() === '') {
|
|
45
|
-
return Effect.fail(
|
|
46
|
-
new PaperlessCredentialsError({
|
|
47
|
-
message:
|
|
48
|
-
'PAPERLESS_TOKEN is not set. Mint a scoped token and export it — never store it in Alchemy props.',
|
|
49
|
-
}),
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
return Effect.succeed({ token: token.trim(), url: url.trim().replace(/\/$/, '') });
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* The lazy service. ⚠️ `Layer.succeed` registers the EFFECT, not its result — `readEnvironment`
|
|
57
|
-
* runs once per `yield* yield*`, so a call after the variables are exported sees them even if
|
|
58
|
-
* the layer itself was built before that (true in `fake-paperless.test.ts`, which sets the env
|
|
59
|
-
* vars right before each call).
|
|
60
|
-
*/
|
|
61
|
-
export class PaperlessCredentials extends Context.Service<
|
|
62
|
-
PaperlessCredentials,
|
|
63
|
-
Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError>
|
|
64
|
-
>()('PaperlessCredentials') {}
|
|
65
|
-
|
|
66
|
-
export const environmentLayer: Layer.Layer<PaperlessCredentials> = Layer.succeed(
|
|
67
|
-
PaperlessCredentials,
|
|
68
|
-
readEnvironment,
|
|
69
|
-
);
|