@xandreed/plugin-models 0.12.0 → 0.14.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 +1 -1
- package/dist/auth/anthropicOAuth.d.ts +16 -2
- package/dist/auth/anthropicOAuth.js +14 -11
- package/dist/auth/credential-input.entity.d.ts +34 -0
- package/dist/auth/credential-input.entity.functions.d.ts +8 -0
- package/dist/auth/credential-input.entity.functions.js +9 -0
- package/dist/auth/credential-input.entity.js +20 -0
- package/dist/auth/credential-refresh.entity.d.ts +9 -0
- package/dist/auth/credential-refresh.entity.functions.d.ts +15 -0
- package/dist/auth/credential-refresh.entity.functions.js +12 -0
- package/dist/auth/credential-refresh.entity.functions.test.d.ts +1 -0
- package/dist/auth/credential-refresh.entity.js +2 -0
- package/dist/auth/localAuth.d.ts +2 -1
- package/dist/auth/localAuth.js +41 -43
- package/dist/auth/memory-auth.adapter.d.ts +27 -0
- package/dist/auth/memory-auth.adapter.js +47 -0
- package/dist/auth/memory-auth.adapter.test.d.ts +1 -0
- package/dist/auth/openAiCodexOAuth.d.ts +3 -2
- package/dist/auth/openAiCodexOAuth.js +9 -8
- package/dist/index.d.ts +23 -6
- package/dist/index.js +28 -1
- package/dist/llm/compat.d.ts +39 -8
- package/dist/llm/compat.js +80 -29
- package/dist/llm/modelCatalog.d.ts +20 -1
- package/dist/llm/modelCatalog.js +19 -7
- package/dist/llm/openAiCodex.d.ts +4 -1
- package/dist/llm/openAiCodex.js +2 -2
- package/dist/llm/openAiCodexWebSocket.d.ts +23 -3
- package/dist/llm/openAiCodexWebSocket.js +125 -71
- package/dist/llm/openAiCodexWebSocket.test.d.ts +1 -0
- package/dist/llm/providers.d.ts +18 -4
- package/dist/llm/providers.js +49 -24
- package/dist/llm/router.d.ts +11 -1
- package/dist/llm/router.js +55 -31
- package/dist/llm/sse.d.ts +25 -3
- package/dist/llm/sse.js +8 -8
- package/dist/llm/utilityLlm.d.ts +1 -1
- package/dist/llm/utilityLlm.js +4 -4
- package/dist/plugin.adapter.d.ts +64 -4
- package/dist/plugin.adapter.js +48 -10
- package/dist/ports/host-credentials.port.d.ts +21 -0
- package/dist/ports/host-credentials.port.js +4 -0
- package/dist/ports/model-endpoints.port.d.ts +16 -3
- package/dist/ports/model-endpoints.port.js +7 -3
- package/dist/ports/models-transport.port.d.ts +46 -0
- package/dist/ports/models-transport.port.js +4 -0
- package/dist/settings/localSettings.js +6 -46
- package/dist/settings/memory-settings.adapter.d.ts +10 -0
- package/dist/settings/memory-settings.adapter.js +11 -0
- package/dist/settings/memory-settings.adapter.test.d.ts +1 -0
- package/dist/settings/settings-value.entity.d.ts +7 -0
- package/dist/settings/settings-value.entity.functions.d.ts +29 -0
- package/dist/settings/settings-value.entity.functions.js +43 -0
- package/dist/settings/settings-value.entity.functions.test.d.ts +1 -0
- package/dist/settings/settings-value.entity.js +2 -0
- package/dist/transport/http-proxy.entity.d.ts +90 -0
- package/dist/transport/http-proxy.entity.functions.d.ts +51 -0
- package/dist/transport/http-proxy.entity.functions.js +112 -0
- package/dist/transport/http-proxy.entity.functions.test.d.ts +1 -0
- package/dist/transport/http-proxy.entity.js +42 -0
- package/dist/transport/models-transport.adapter.d.ts +48 -0
- package/dist/transport/models-transport.adapter.js +102 -0
- package/dist/transport/models-transport.adapter.test.d.ts +1 -0
- package/dist/transport/recording-proxy.testing.d.ts +33 -0
- package/dist/transport/undici-fetch.adapter.d.ts +13 -0
- package/dist/transport/undici-fetch.adapter.js +1 -0
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Option } from "effect";
|
|
2
2
|
import { AuthError } from "@xandreed/core";
|
|
3
|
+
import type { ModelsFetch } from "../ports/models-transport.port.js";
|
|
3
4
|
/**
|
|
4
5
|
* Anthropic OAuth (Claude Pro/Max subscription) — the full protocol:
|
|
5
6
|
* authorization-code + PKCE (begin/exchange) and refresh. Constants match
|
|
@@ -21,8 +22,21 @@ export interface RefreshedTokens {
|
|
|
21
22
|
/** Absolute expiry, epoch ms (with a 5-minute early-refresh skew baked in). */
|
|
22
23
|
readonly expires: number;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* How one token request is made, for both subscription providers. `fetch`
|
|
27
|
+
* replaces the global fetch (a test stub, or a `ModelsTransport`'s fetch, so
|
|
28
|
+
* the request goes through the models plugin's proxy); `now` is
|
|
29
|
+
* the time the new token's expiry is counted from, in epoch ms (default: the
|
|
30
|
+
* Effect `Clock`).
|
|
31
|
+
*/
|
|
32
|
+
export interface TokenRequestOptions {
|
|
33
|
+
readonly fetch?: ModelsFetch;
|
|
34
|
+
readonly now?: number;
|
|
35
|
+
}
|
|
36
|
+
/** The time a token's expiry is counted from: the caller's, or the Effect Clock's. */
|
|
37
|
+
export declare const tokenTime: (options: TokenRequestOptions) => Effect.Effect<number>;
|
|
24
38
|
/** Refresh an expired (or near-expiry) access token. */
|
|
25
|
-
export declare const refreshAnthropicToken: (refreshToken: string) => Effect.Effect<RefreshedTokens, AuthError>;
|
|
39
|
+
export declare const refreshAnthropicToken: (refreshToken: string, options?: TokenRequestOptions) => Effect.Effect<RefreshedTokens, AuthError>;
|
|
26
40
|
export interface Pkce {
|
|
27
41
|
readonly verifier: string;
|
|
28
42
|
readonly challenge: string;
|
|
@@ -40,7 +54,7 @@ export interface OAuthBegin {
|
|
|
40
54
|
/** Start an authorization: PKCE material + the browser URL. */
|
|
41
55
|
export declare const beginAnthropicOAuth: Effect.Effect<OAuthBegin>;
|
|
42
56
|
/** Exchange the authorization code for tokens. */
|
|
43
|
-
export declare const exchangeAnthropicCode: (code: string, verifier: string) => Effect.Effect<RefreshedTokens, AuthError>;
|
|
57
|
+
export declare const exchangeAnthropicCode: (code: string, verifier: string, options?: TokenRequestOptions) => Effect.Effect<RefreshedTokens, AuthError>;
|
|
44
58
|
export interface AuthRedirect {
|
|
45
59
|
readonly code: Option.Option<string>;
|
|
46
60
|
readonly state: Option.Option<string>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Effect, Option } from "effect";
|
|
1
|
+
import { Clock, Effect, Option } from "effect";
|
|
2
2
|
import { AuthError } from "@xandreed/core";
|
|
3
|
+
import { redactCredentials } from "../transport/http-proxy.entity.functions.js";
|
|
3
4
|
export const ANTHROPIC_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
4
5
|
const AUTHORIZE_URL = "https://claude.ai/oauth/authorize", TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
5
6
|
export const ANTHROPIC_CALLBACK_PORT = 53692, ANTHROPIC_CALLBACK_PATH = "/callback", ANTHROPIC_REDIRECT_URI = `http://localhost:${ANTHROPIC_CALLBACK_PORT}${ANTHROPIC_CALLBACK_PATH}`;
|
|
@@ -8,11 +9,13 @@ export const ANTHROPIC_OAUTH_BETA = "claude-code-20250219,oauth-2025-04-20", CLA
|
|
|
8
9
|
const oauthError = (stage) => (detail) => new AuthError({
|
|
9
10
|
provider: "anthropic",
|
|
10
11
|
message: `OAuth ${stage} failed: ${detail}`
|
|
11
|
-
})
|
|
12
|
+
});
|
|
13
|
+
export const tokenTime = (options) => options.now === void 0 ? Clock.currentTimeMillis : Effect.succeed(options.now);
|
|
14
|
+
const postToken = (body, stage, options) => {
|
|
12
15
|
const fail = oauthError(stage);
|
|
13
16
|
return Effect.tryPromise({
|
|
14
17
|
try: async () => {
|
|
15
|
-
const res = await fetch(TOKEN_URL, {
|
|
18
|
+
const res = await (options.fetch ?? fetch)(TOKEN_URL, {
|
|
16
19
|
method: "POST",
|
|
17
20
|
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
18
21
|
body: JSON.stringify(body),
|
|
@@ -20,21 +23,21 @@ const oauthError = (stage) => (detail) => new AuthError({
|
|
|
20
23
|
});
|
|
21
24
|
return { ok: res.ok, status: res.status, text: await res.text() };
|
|
22
25
|
},
|
|
23
|
-
catch: (e) => fail(String(e))
|
|
26
|
+
catch: (e) => fail(redactCredentials(String(e)))
|
|
24
27
|
}).pipe(Effect.filterOrFail((res) => res.ok, (res) => fail(`status ${res.status}: ${res.text.slice(0, 200)}`)), Effect.flatMap((res) => Effect.try({
|
|
25
28
|
try: () => JSON.parse(res.text),
|
|
26
29
|
catch: () => fail("the token response was not JSON")
|
|
27
|
-
})), Effect.
|
|
30
|
+
})), Effect.flatMap((data) => Effect.map(tokenTime(options), (now) => ({
|
|
28
31
|
access: data.access_token,
|
|
29
32
|
refresh: data.refresh_token,
|
|
30
|
-
expires:
|
|
31
|
-
})));
|
|
33
|
+
expires: now + data.expires_in * 1000 - 300000
|
|
34
|
+
}))));
|
|
32
35
|
};
|
|
33
|
-
export const refreshAnthropicToken = (refreshToken) => postToken({
|
|
36
|
+
export const refreshAnthropicToken = (refreshToken, options = {}) => postToken({
|
|
34
37
|
grant_type: "refresh_token",
|
|
35
38
|
client_id: ANTHROPIC_CLIENT_ID,
|
|
36
39
|
refresh_token: refreshToken
|
|
37
|
-
}, "token refresh");
|
|
40
|
+
}, "token refresh", options);
|
|
38
41
|
const base64url = (bytes) => btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join("")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
39
42
|
export const generatePkce = Effect.promise(async () => {
|
|
40
43
|
const verifierBytes = new Uint8Array(32);
|
|
@@ -55,14 +58,14 @@ export const generatePkce = Effect.promise(async () => {
|
|
|
55
58
|
}).toString()}`,
|
|
56
59
|
callbackPort: ANTHROPIC_CALLBACK_PORT,
|
|
57
60
|
callbackPath: ANTHROPIC_CALLBACK_PATH
|
|
58
|
-
})), exchangeAnthropicCode = (code, verifier) => postToken({
|
|
61
|
+
})), exchangeAnthropicCode = (code, verifier, options = {}) => postToken({
|
|
59
62
|
grant_type: "authorization_code",
|
|
60
63
|
client_id: ANTHROPIC_CLIENT_ID,
|
|
61
64
|
code,
|
|
62
65
|
redirect_uri: ANTHROPIC_REDIRECT_URI,
|
|
63
66
|
code_verifier: verifier,
|
|
64
67
|
state: verifier
|
|
65
|
-
}, "code exchange");
|
|
68
|
+
}, "code exchange", options);
|
|
66
69
|
const fromNullableTrimmed = (value) => Option.filter(Option.fromNullishOr(value), (v) => v.trim().length > 0);
|
|
67
70
|
export const parseAuthorizationInput = (input) => {
|
|
68
71
|
const value = input.trim();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const CredentialInput: Schema.Union<readonly [Schema.Struct<{
|
|
3
|
+
readonly type: Schema.Literal<"api_key">;
|
|
4
|
+
readonly key: Schema.Redacted<Schema.String>;
|
|
5
|
+
}>, Schema.Struct<{
|
|
6
|
+
readonly type: Schema.Literal<"oauth">;
|
|
7
|
+
readonly access: Schema.Redacted<Schema.String>;
|
|
8
|
+
readonly refresh: Schema.Redacted<Schema.String>;
|
|
9
|
+
/** Absolute expiry, epoch ms. */
|
|
10
|
+
readonly expires: Schema.Number;
|
|
11
|
+
readonly accountId: Schema.optionalKey<Schema.String>;
|
|
12
|
+
readonly installationId: Schema.optionalKey<Schema.String>;
|
|
13
|
+
}>, Schema.Struct<{
|
|
14
|
+
readonly type: Schema.Literal<"local">;
|
|
15
|
+
readonly baseUrl: Schema.optionalKey<Schema.String>;
|
|
16
|
+
}>]>;
|
|
17
|
+
export type CredentialInput = typeof CredentialInput.Type;
|
|
18
|
+
/** Credentials by provider id. */
|
|
19
|
+
export declare const CredentialInputs: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Struct<{
|
|
20
|
+
readonly type: Schema.Literal<"api_key">;
|
|
21
|
+
readonly key: Schema.Redacted<Schema.String>;
|
|
22
|
+
}>, Schema.Struct<{
|
|
23
|
+
readonly type: Schema.Literal<"oauth">;
|
|
24
|
+
readonly access: Schema.Redacted<Schema.String>;
|
|
25
|
+
readonly refresh: Schema.Redacted<Schema.String>;
|
|
26
|
+
/** Absolute expiry, epoch ms. */
|
|
27
|
+
readonly expires: Schema.Number;
|
|
28
|
+
readonly accountId: Schema.optionalKey<Schema.String>;
|
|
29
|
+
readonly installationId: Schema.optionalKey<Schema.String>;
|
|
30
|
+
}>, Schema.Struct<{
|
|
31
|
+
readonly type: Schema.Literal<"local">;
|
|
32
|
+
readonly baseUrl: Schema.optionalKey<Schema.String>;
|
|
33
|
+
}>]>>;
|
|
34
|
+
export type CredentialInputs = typeof CredentialInputs.Type;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Credential } from "@xandreed/core";
|
|
2
|
+
import type { CredentialInput, CredentialInputs } from "./credential-input.entity.js";
|
|
3
|
+
/** The stored form of a credential a host handed over: its secrets unwrapped. */
|
|
4
|
+
export declare const toCredential: (input: CredentialInput) => Credential;
|
|
5
|
+
/** A stored credential as a host receives it: its secrets `Redacted`. */
|
|
6
|
+
export declare const toCredentialInput: (credential: Credential) => CredentialInput;
|
|
7
|
+
/** Handed-over credentials, by provider id, in their stored form. */
|
|
8
|
+
export declare const credentialsOf: (inputs: CredentialInputs) => ReadonlyMap<string, Credential>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Match, Redacted } from "effect";
|
|
2
|
+
export const toCredential = (input) => Match.value(input).pipe(Match.when({ type: "api_key" }, ({ key }) => ({ type: "api_key", key: Redacted.value(key) })), Match.when({ type: "oauth" }, ({ access, refresh, ...rest }) => ({ ...rest, access: Redacted.value(access), refresh: Redacted.value(refresh) })), Match.when({ type: "local" }, (local) => local), Match.exhaustive), toCredentialInput = (credential) => Match.value(credential).pipe(Match.when({ type: "api_key" }, ({ key }) => ({ type: "api_key", key: Redacted.make(key) })), Match.when({ type: "oauth" }, ({ type, access, refresh, expires, accountId, installationId }) => ({
|
|
3
|
+
type,
|
|
4
|
+
expires,
|
|
5
|
+
access: Redacted.make(access),
|
|
6
|
+
refresh: Redacted.make(refresh),
|
|
7
|
+
...accountId === void 0 ? {} : { accountId },
|
|
8
|
+
...installationId === void 0 ? {} : { installationId }
|
|
9
|
+
})), Match.when({ type: "local" }, ({ type, baseUrl }) => ({ type, ...baseUrl === void 0 ? {} : { baseUrl } })), Match.exhaustive), credentialsOf = (inputs) => new Map(Object.entries(inputs).map(([provider, input]) => [provider, toCredential(input)]));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
const secret = Schema.Redacted(Schema.String).annotate({
|
|
3
|
+
message: "a secret must be a Redacted value (Redacted.make(...)); JSON configuration names an environment variable in credentialEnv instead"
|
|
4
|
+
}), unexpectedKey = { messageUnexpectedKey: "not a field of a credential" };
|
|
5
|
+
export const CredentialInput = Schema.Union([
|
|
6
|
+
Schema.Struct({ type: Schema.Literal("api_key"), key: secret }).annotate(unexpectedKey),
|
|
7
|
+
Schema.Struct({
|
|
8
|
+
type: Schema.Literal("oauth"),
|
|
9
|
+
access: secret,
|
|
10
|
+
refresh: secret,
|
|
11
|
+
expires: Schema.Number.annotate({ message: "expires is the access token's expiry, in epoch ms" }).check(Schema.isFinite()),
|
|
12
|
+
accountId: Schema.optionalKey(Schema.String),
|
|
13
|
+
installationId: Schema.optionalKey(Schema.String)
|
|
14
|
+
}).annotate(unexpectedKey),
|
|
15
|
+
Schema.Struct({ type: Schema.Literal("local"), baseUrl: Schema.optionalKey(Schema.String) }).annotate(unexpectedKey)
|
|
16
|
+
]).annotate({
|
|
17
|
+
message: 'a credential is { type: "api_key", key }, { type: "oauth", access, refresh, expires, accountId?, installationId? } or { type: "local", baseUrl? }, with Redacted secrets'
|
|
18
|
+
}), CredentialInputs = Schema.Record(Schema.String, CredentialInput).annotate({
|
|
19
|
+
message: "credentials maps provider ids to credentials"
|
|
20
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import type { Credential } from "@xandreed/core";
|
|
3
|
+
/** The providers whose OAuth tokens a store can refresh: each has its token endpoint in an auth module. */
|
|
4
|
+
export declare const RefreshableProvider: Schema.Literals<readonly ["anthropic", "openai-codex"]>;
|
|
5
|
+
export type RefreshableProvider = typeof RefreshableProvider.Type;
|
|
6
|
+
/** An OAuth subscription credential: an access token, its refresh token and its expiry (epoch ms). */
|
|
7
|
+
export type OAuthCredential = Extract<Credential, {
|
|
8
|
+
readonly type: "oauth";
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { AuthError } from "@xandreed/core";
|
|
3
|
+
import type { TokenRequestOptions } from "./anthropicOAuth.js";
|
|
4
|
+
import type { OAuthCredential } from "./credential-refresh.entity.js";
|
|
5
|
+
/** Refresh this many ms before the recorded expiry. */
|
|
6
|
+
export declare const REFRESH_SKEW_MS = 60000;
|
|
7
|
+
/** Whether an OAuth credential can still be used at `now`: more than the skew before its expiry. */
|
|
8
|
+
export declare const isUsableAt: (credential: OAuthCredential, now: number) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Refresh one provider's OAuth credential through that provider's token
|
|
11
|
+
* endpoint: the same credential with the new access token, refresh token
|
|
12
|
+
* and expiry (and account id, where the provider sends one). A provider
|
|
13
|
+
* with no refresh flow fails with an `AuthError` naming it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const refreshCredential: (provider: string, credential: OAuthCredential, options?: TokenRequestOptions) => Effect.Effect<OAuthCredential, AuthError>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect, Match, Option, Schema } from "effect";
|
|
2
|
+
import { AuthError } from "@xandreed/core";
|
|
3
|
+
import { refreshAnthropicToken } from "./anthropicOAuth.js";
|
|
4
|
+
import { RefreshableProvider } from "./credential-refresh.entity.js";
|
|
5
|
+
import { refreshOpenAiCodexToken } from "./openAiCodexOAuth.js";
|
|
6
|
+
export const REFRESH_SKEW_MS = 60000, isUsableAt = (credential, now) => credential.expires > now + REFRESH_SKEW_MS, refreshCredential = (provider, credential, options = {}) => Option.match(Schema.decodeUnknownOption(RefreshableProvider)(provider), {
|
|
7
|
+
onNone: () => Effect.fail(new AuthError({
|
|
8
|
+
provider,
|
|
9
|
+
message: `the ${provider} OAuth token expired and only anthropic and openai-codex tokens can be refreshed \u2014 sign in again or use an API key`
|
|
10
|
+
})),
|
|
11
|
+
onSome: (refreshable) => Match.value(refreshable).pipe(Match.when("anthropic", () => refreshAnthropicToken(credential.refresh, options)), Match.when("openai-codex", () => refreshOpenAiCodexToken(credential.refresh, options)), Match.exhaustive).pipe(Effect.map((fresh) => ({ ...credential, ...fresh })))
|
|
12
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/auth/localAuth.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { AuthStore } from "@xandreed/core";
|
|
|
3
3
|
export declare const authPaths: (cwd: string, home: string, directory?: string) => ReadonlyArray<string>;
|
|
4
4
|
/**
|
|
5
5
|
* `LocalAuthStoreLive(cwd, home)` — read-through: every call re-reads disk,
|
|
6
|
-
* so a credential added mid-session applies on the next request.
|
|
6
|
+
* so a credential added mid-session applies on the next request. A token
|
|
7
|
+
* refresh goes through the `ModelsTransport` the layer is built over, if any.
|
|
7
8
|
*/
|
|
8
9
|
export declare const LocalAuthStoreLive: (cwd: string, home: string, directory?: string, fallbackDirectory?: string) => Layer.Layer<AuthStore, never, never>;
|
package/dist/auth/localAuth.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { Effect, Layer, Option, Redacted } from "effect";
|
|
3
|
+
import { Clock, Effect, Layer, Option, Redacted } from "effect";
|
|
4
4
|
import { asJsonRecord, AuthError, AuthStore, parseJsonWarn } from "@xandreed/core";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { isUsableAt, refreshCredential } from "./credential-refresh.entity.functions.js";
|
|
6
|
+
import { ModelsTransport } from "../ports/models-transport.port.js";
|
|
7
7
|
const decodeCredential = (value) => {
|
|
8
8
|
if (typeof value === "string")
|
|
9
9
|
return Option.some({ type: "api_key", key: value });
|
|
@@ -42,49 +42,47 @@ const decodeCredential = (value) => {
|
|
|
42
42
|
await rename(tmp, path);
|
|
43
43
|
},
|
|
44
44
|
catch: (e) => new AuthError({ provider: "*", message: `auth.json write failed: ${String(e)}` })
|
|
45
|
-
})
|
|
45
|
+
});
|
|
46
46
|
export const authPaths = (cwd, home, directory = ".efferent") => [
|
|
47
47
|
join(home, directory, "auth.json"),
|
|
48
48
|
join(cwd, directory, "auth.json")
|
|
49
49
|
], LocalAuthStoreLive = (cwd, home, directory = ".efferent", fallbackDirectory) => {
|
|
50
50
|
const paths = [...fallbackDirectory === void 0 ? [] : authPaths(cwd, home, fallbackDirectory), ...authPaths(cwd, home, directory)];
|
|
51
|
-
return Layer.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
})).pipe(Effect.asVoid)
|
|
89
|
-
});
|
|
51
|
+
return Layer.effect(AuthStore, Effect.map(Effect.serviceOption(ModelsTransport), (transport) => {
|
|
52
|
+
const tokens = Option.match(transport, { onNone: () => ({}), onSome: ({ fetch }) => ({ fetch }) });
|
|
53
|
+
return {
|
|
54
|
+
all: Effect.gen(function* () {
|
|
55
|
+
const files = yield* Effect.forEach(paths, readAuthFile);
|
|
56
|
+
return new Map(files.flatMap((f) => [...f.entries]));
|
|
57
|
+
}),
|
|
58
|
+
get: (provider) => Effect.gen(function* () {
|
|
59
|
+
const files = yield* Effect.forEach(paths, readAuthFile), merged = new Map(files.flatMap((f) => [...f.entries]));
|
|
60
|
+
return Option.fromNullishOr(merged.get(provider));
|
|
61
|
+
}),
|
|
62
|
+
resolveKey: (provider) => Effect.gen(function* () {
|
|
63
|
+
const holder = [...yield* Effect.forEach(paths, readAuthFile)].reverse().find((f) => f.entries.has(provider)), cred = holder?.entries.get(provider);
|
|
64
|
+
if (cred === void 0)
|
|
65
|
+
return Option.none();
|
|
66
|
+
if (cred.type === "api_key")
|
|
67
|
+
return Option.some(Redacted.make(cred.key));
|
|
68
|
+
if (cred.type === "local")
|
|
69
|
+
return Option.none();
|
|
70
|
+
if (isUsableAt(cred, yield* Clock.currentTimeMillis))
|
|
71
|
+
return Option.some(Redacted.make(cred.access));
|
|
72
|
+
const updated = yield* refreshCredential(provider, cred, tokens), target = holder !== void 0 && authPaths(cwd, home, directory).includes(holder.path) ? holder : yield* readAuthFile(authPaths(cwd, home, directory)[0] ?? "");
|
|
73
|
+
yield* writeAuthFile(target.path, new Map([...target.entries, [provider, updated]]));
|
|
74
|
+
return Option.some(Redacted.make(updated.access));
|
|
75
|
+
}),
|
|
76
|
+
set: (provider, credential) => Effect.gen(function* () {
|
|
77
|
+
const globalPath = authPaths(cwd, home, directory)[0] ?? "", file = yield* readAuthFile(globalPath);
|
|
78
|
+
yield* writeAuthFile(globalPath, new Map([...file.entries, [provider, credential]]));
|
|
79
|
+
}),
|
|
80
|
+
remove: (provider) => Effect.forEach(paths, (path) => Effect.gen(function* () {
|
|
81
|
+
const file = yield* readAuthFile(path);
|
|
82
|
+
if (!file.entries.has(provider))
|
|
83
|
+
return;
|
|
84
|
+
yield* writeAuthFile(path, new Map([...file.entries].filter(([p]) => p !== provider)));
|
|
85
|
+
})).pipe(Effect.asVoid)
|
|
86
|
+
};
|
|
87
|
+
}));
|
|
90
88
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { AuthStore } from "@xandreed/core";
|
|
3
|
+
import type { AuthError, Credential } from "@xandreed/core";
|
|
4
|
+
import type { ModelsFetch } from "../ports/models-transport.port.js";
|
|
5
|
+
/** How the in-memory store reaches the OAuth token endpoints. */
|
|
6
|
+
export interface MemoryAuthOptions {
|
|
7
|
+
/** The fetch token refreshes go through; default: the `ModelsTransport`'s when the layer is built over one, else the global fetch. */
|
|
8
|
+
readonly fetch?: ModelsFetch;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `MemoryAuthStoreLive(seed?)` — an AuthStore that never touches disk.
|
|
12
|
+
* Credentials live in memory for the layer's lifetime: the host's
|
|
13
|
+
* `HostCredentials.load` (when the host provides that service), overlaid by
|
|
14
|
+
* `seed`. `resolveKey` refreshes a near-expiry anthropic or openai-codex
|
|
15
|
+
* OAuth token in memory, one refresh per provider at a time (through the
|
|
16
|
+
* `ModelsTransport` it was built over, so through the plugin's proxy), and
|
|
17
|
+
* hands the new token to `HostCredentials.save`.
|
|
18
|
+
*
|
|
19
|
+
* A login or logout (`set`, `remove`) does not wait for a refresh's token
|
|
20
|
+
* request; it waits only while a refreshed token is committed, which
|
|
21
|
+
* includes the host's `save` of it, so a slow host store delays it that
|
|
22
|
+
* long. A refresh writes its token only if the provider still holds the
|
|
23
|
+
* credential it refreshed (compare-and-set under the provider's lock): a
|
|
24
|
+
* logout during a stalled token request stays a logout, and the host never
|
|
25
|
+
* receives the refreshed token.
|
|
26
|
+
*/
|
|
27
|
+
export declare const MemoryAuthStoreLive: (seed?: ReadonlyMap<string, Credential>, options?: MemoryAuthOptions) => Layer.Layer<AuthStore, AuthError>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Clock, Effect, Layer, Match, Option, Redacted, Ref, Semaphore, SynchronizedRef } from "effect";
|
|
2
|
+
import { AuthStore } from "@xandreed/core";
|
|
3
|
+
import { HostCredentials } from "../ports/host-credentials.port.js";
|
|
4
|
+
import { ModelsTransport } from "../ports/models-transport.port.js";
|
|
5
|
+
import { toCredential, toCredentialInput } from "./credential-input.entity.functions.js";
|
|
6
|
+
import { isUsableAt, refreshCredential } from "./credential-refresh.entity.functions.js";
|
|
7
|
+
export const MemoryAuthStoreLive = (seed = new Map, options = {}) => Layer.effect(AuthStore, Effect.gen(function* () {
|
|
8
|
+
const host = yield* Effect.serviceOption(HostCredentials), loaded = yield* Option.match(host, {
|
|
9
|
+
onNone: () => Effect.succeed(new Map),
|
|
10
|
+
onSome: (credentials) => credentials.load.pipe(Effect.map((all) => new Map([...all].map(([provider, input]) => [provider, toCredential(input)]))))
|
|
11
|
+
}), state = yield* Ref.make(new Map([...loaded, ...seed])), locks = yield* SynchronizedRef.make(new Map), transport = yield* Effect.serviceOption(ModelsTransport), tokens = Option.match(Option.orElse(Option.fromNullishOr(options.fetch), () => Option.map(transport, (found) => found.fetch)), {
|
|
12
|
+
onNone: () => ({}),
|
|
13
|
+
onSome: (fetch) => ({ fetch })
|
|
14
|
+
}), locksOf = (provider) => SynchronizedRef.modifyEffect(locks, (all) => Option.match(Option.fromNullishOr(all.get(provider)), {
|
|
15
|
+
onSome: (found) => Effect.succeed([found, all]),
|
|
16
|
+
onNone: () => Effect.all({ commit: Semaphore.make(1), refresh: Semaphore.make(1) }).pipe(Effect.map((made) => [made, new Map([...all, [provider, made]])]))
|
|
17
|
+
})), current = (provider) => Ref.get(state).pipe(Effect.map((all) => Option.fromNullishOr(all.get(provider)))), save = (provider, credential) => Option.match(host, {
|
|
18
|
+
onNone: () => Effect.void,
|
|
19
|
+
onSome: (credentials) => credentials.save(provider, Option.map(credential, toCredentialInput))
|
|
20
|
+
}), write = (provider, credential) => Ref.update(state, (all) => Option.match(credential, {
|
|
21
|
+
onNone: () => new Map([...all].filter(([key]) => key !== provider)),
|
|
22
|
+
onSome: (value) => new Map([...all, [provider, value]])
|
|
23
|
+
})), refreshing = (provider, stale) => locksOf(provider).pipe(Effect.flatMap((lock) => lock.refresh.withPermit(Effect.gen(function* () {
|
|
24
|
+
const before = yield* current(provider);
|
|
25
|
+
if (!Option.exists(before, (held) => held === stale))
|
|
26
|
+
return { _tag: "Changed" };
|
|
27
|
+
const fresh = yield* refreshCredential(provider, stale, tokens);
|
|
28
|
+
return yield* lock.commit.withPermit(Effect.gen(function* () {
|
|
29
|
+
const now = yield* current(provider);
|
|
30
|
+
if (!Option.exists(now, (held) => held === stale))
|
|
31
|
+
return { _tag: "Changed" };
|
|
32
|
+
yield* write(provider, Option.some(fresh));
|
|
33
|
+
yield* save(provider, Option.some(fresh)).pipe(Effect.catch((error) => Effect.logWarning(`the refreshed ${provider} token is kept in memory, but the host did not save it: ${error.message}`)));
|
|
34
|
+
return { _tag: "Key", key: Redacted.make(fresh.access) };
|
|
35
|
+
}));
|
|
36
|
+
}))), Effect.flatMap((refreshed) => Match.value(refreshed).pipe(Match.tag("Key", ({ key }) => Effect.succeed(Option.some(key))), Match.tag("Changed", () => resolveKey(provider)), Match.exhaustive))), resolveKey = (provider) => current(provider).pipe(Effect.flatMap(Option.match({
|
|
37
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
38
|
+
onSome: (credential) => Match.value(credential).pipe(Match.when({ type: "api_key" }, ({ key }) => Effect.succeed(Option.some(Redacted.make(key)))), Match.when({ type: "local" }, () => Effect.succeed(Option.none())), Match.when({ type: "oauth" }, (oauth) => Clock.currentTimeMillis.pipe(Effect.flatMap((now) => isUsableAt(oauth, now) ? Effect.succeed(Option.some(Redacted.make(oauth.access))) : refreshing(provider, oauth)))), Match.exhaustive)
|
|
39
|
+
})));
|
|
40
|
+
return AuthStore.of({
|
|
41
|
+
all: Ref.get(state),
|
|
42
|
+
get: current,
|
|
43
|
+
resolveKey,
|
|
44
|
+
set: (provider, credential) => locksOf(provider).pipe(Effect.flatMap((lock) => lock.commit.withPermit(save(provider, Option.some(credential)).pipe(Effect.andThen(write(provider, Option.some(credential))))))),
|
|
45
|
+
remove: (provider) => locksOf(provider).pipe(Effect.flatMap((lock) => lock.commit.withPermit(current(provider).pipe(Effect.flatMap((held) => Option.isNone(held) ? Effect.void : save(provider, Option.none()).pipe(Effect.andThen(write(provider, Option.none()))))))))
|
|
46
|
+
});
|
|
47
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Option } from "effect";
|
|
2
2
|
import { AuthError } from "@xandreed/core";
|
|
3
|
+
import type { TokenRequestOptions } from "./anthropicOAuth.js";
|
|
3
4
|
/** ChatGPT Plus/Pro OAuth protocol used by the OpenAI Codex subscription. */
|
|
4
5
|
export declare const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
5
6
|
export declare const OPENAI_CODEX_CALLBACK_PORT = 1455;
|
|
@@ -21,5 +22,5 @@ export interface OpenAiCodexOAuthBegin {
|
|
|
21
22
|
/** Extract the subscription account lane without exposing the token. */
|
|
22
23
|
export declare const openAiCodexAccountId: (accessToken: string) => Option.Option<string>;
|
|
23
24
|
export declare const beginOpenAiCodexOAuth: Effect.Effect<OpenAiCodexOAuthBegin>;
|
|
24
|
-
export declare const exchangeOpenAiCodexCode: (code: string, verifier: string) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
25
|
-
export declare const refreshOpenAiCodexToken: (refreshToken: string) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
25
|
+
export declare const exchangeOpenAiCodexCode: (code: string, verifier: string, options?: TokenRequestOptions) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
26
|
+
export declare const refreshOpenAiCodexToken: (refreshToken: string, options?: TokenRequestOptions) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect, Result, Option } from "effect";
|
|
2
2
|
import { AuthError } from "@xandreed/core";
|
|
3
|
-
import { generatePkce } from "./anthropicOAuth.js";
|
|
3
|
+
import { generatePkce, tokenTime } from "./anthropicOAuth.js";
|
|
4
|
+
import { redactCredentials } from "../transport/http-proxy.entity.functions.js";
|
|
4
5
|
export const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
5
6
|
const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize", TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
6
7
|
export const OPENAI_CODEX_CALLBACK_PORT = 1455, OPENAI_CODEX_CALLBACK_PATH = "/auth/callback", OPENAI_CODEX_REDIRECT_URI = `http://localhost:${OPENAI_CODEX_CALLBACK_PORT}${OPENAI_CODEX_CALLBACK_PATH}`;
|
|
@@ -18,9 +19,9 @@ export const openAiCodexAccountId = (accessToken) => {
|
|
|
18
19
|
return Option.filter(Option.fromNullishOr(typeof accountId === "string" ? accountId : void 0), (value) => value.length > 0);
|
|
19
20
|
}));
|
|
20
21
|
};
|
|
21
|
-
const postToken = (body, stage) => Effect.tryPromise({
|
|
22
|
+
const postToken = (body, stage, options) => Effect.tryPromise({
|
|
22
23
|
try: async () => {
|
|
23
|
-
const response = await fetch(TOKEN_URL, {
|
|
24
|
+
const response = await (options.fetch ?? fetch)(TOKEN_URL, {
|
|
24
25
|
method: "POST",
|
|
25
26
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
26
27
|
body: new URLSearchParams(body),
|
|
@@ -28,16 +29,16 @@ const postToken = (body, stage) => Effect.tryPromise({
|
|
|
28
29
|
});
|
|
29
30
|
return { ok: response.ok, status: response.status, text: await response.text() };
|
|
30
31
|
},
|
|
31
|
-
catch: (error) => oauthError(stage, String(error))
|
|
32
|
+
catch: (error) => oauthError(stage, redactCredentials(String(error)))
|
|
32
33
|
}).pipe(Effect.filterOrFail((response) => response.ok, (response) => oauthError(stage, `status ${response.status}: ${response.text.slice(0, 200)}`)), Effect.flatMap((response) => Effect.try({
|
|
33
34
|
try: () => JSON.parse(response.text),
|
|
34
35
|
catch: () => oauthError(stage, "the token response was not JSON")
|
|
35
|
-
})), Effect.filterOrFail((token) => typeof token.access_token === "string" && typeof token.refresh_token === "string" && typeof token.expires_in === "number", () => oauthError(stage, "the token response omitted required fields")), Effect.
|
|
36
|
+
})), Effect.filterOrFail((token) => typeof token.access_token === "string" && typeof token.refresh_token === "string" && typeof token.expires_in === "number", () => oauthError(stage, "the token response omitted required fields")), Effect.flatMap((token) => Effect.map(tokenTime(options), (now) => ({
|
|
36
37
|
access: token.access_token,
|
|
37
38
|
refresh: token.refresh_token,
|
|
38
|
-
expires:
|
|
39
|
+
expires: now + token.expires_in * 1000,
|
|
39
40
|
...Option.match(openAiCodexAccountId(token.access_token), { onNone: () => ({}), onSome: (accountId) => ({ accountId }) })
|
|
40
|
-
})));
|
|
41
|
+
}))));
|
|
41
42
|
export const beginOpenAiCodexOAuth = Effect.gen(function* () {
|
|
42
43
|
const pkce = yield* generatePkce, stateBytes = new Uint8Array(16);
|
|
43
44
|
crypto.getRandomValues(stateBytes);
|
|
@@ -55,4 +56,4 @@ export const beginOpenAiCodexOAuth = Effect.gen(function* () {
|
|
|
55
56
|
originator: "pi"
|
|
56
57
|
}).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
57
58
|
return { verifier: pkce.verifier, state, authorizeUrl: url.toString(), callbackPort: OPENAI_CODEX_CALLBACK_PORT, callbackPath: OPENAI_CODEX_CALLBACK_PATH };
|
|
58
|
-
}), exchangeOpenAiCodexCode = (code, verifier) => postToken({ grant_type: "authorization_code", client_id: OPENAI_CODEX_CLIENT_ID, code, code_verifier: verifier, redirect_uri: OPENAI_CODEX_REDIRECT_URI }, "code exchange"), refreshOpenAiCodexToken = (refreshToken) => postToken({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: OPENAI_CODEX_CLIENT_ID }, "token refresh");
|
|
59
|
+
}), exchangeOpenAiCodexCode = (code, verifier, options = {}) => postToken({ grant_type: "authorization_code", client_id: OPENAI_CODEX_CLIENT_ID, code, code_verifier: verifier, redirect_uri: OPENAI_CODEX_REDIRECT_URI }, "code exchange", options), refreshOpenAiCodexToken = (refreshToken, options = {}) => postToken({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: OPENAI_CODEX_CLIENT_ID }, "token refresh", options);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
export { ANTHROPIC_OAUTH_BETA, beginAnthropicOAuth, CLAUDE_CODE_SYSTEM, exchangeAnthropicCode, generatePkce, parseAuthorizationInput, refreshAnthropicToken, } from "./auth/anthropicOAuth.js";
|
|
2
|
-
export type { AuthRedirect, OAuthBegin, Pkce, RefreshedTokens } from "./auth/anthropicOAuth.js";
|
|
2
|
+
export type { AuthRedirect, OAuthBegin, Pkce, RefreshedTokens, TokenRequestOptions } from "./auth/anthropicOAuth.js";
|
|
3
3
|
export { beginOpenAiCodexOAuth, exchangeOpenAiCodexCode, OPENAI_CODEX_CALLBACK_PATH, OPENAI_CODEX_CALLBACK_PORT, OPENAI_CODEX_CLIENT_ID, OPENAI_CODEX_REDIRECT_URI, openAiCodexAccountId, refreshOpenAiCodexToken, } from "./auth/openAiCodexOAuth.js";
|
|
4
4
|
export type { OpenAiCodexOAuthBegin, OpenAiCodexTokens } from "./auth/openAiCodexOAuth.js";
|
|
5
5
|
export { authPaths, LocalAuthStoreLive } from "./auth/localAuth.js";
|
|
6
|
+
export { MemoryAuthStoreLive } from "./auth/memory-auth.adapter.js";
|
|
7
|
+
export type { MemoryAuthOptions } from "./auth/memory-auth.adapter.js";
|
|
8
|
+
export { CredentialInput, CredentialInputs } from "./auth/credential-input.entity.js";
|
|
9
|
+
export { credentialsOf, toCredential, toCredentialInput } from "./auth/credential-input.entity.functions.js";
|
|
10
|
+
export { RefreshableProvider } from "./auth/credential-refresh.entity.js";
|
|
11
|
+
export type { OAuthCredential } from "./auth/credential-refresh.entity.js";
|
|
12
|
+
export { isUsableAt, REFRESH_SKEW_MS, refreshCredential } from "./auth/credential-refresh.entity.functions.js";
|
|
13
|
+
export { HostCredentials } from "./ports/host-credentials.port.js";
|
|
6
14
|
export { LocalSettingsStoreLive } from "./settings/localSettings.js";
|
|
15
|
+
export { MemorySettingsStoreLive } from "./settings/memory-settings.adapter.js";
|
|
16
|
+
export { RoleField, SettingValue } from "./settings/settings-value.entity.js";
|
|
17
|
+
export { applyRole, applySetting, coerceSettingValue, ROLE_FIELDS, roleSelection, settingsFromRecord } from "./settings/settings-value.entity.functions.js";
|
|
7
18
|
export { roleModelView } from "./settings/roleView.js";
|
|
8
|
-
export { fromChatCompletion, makeCompatLanguageModel, toChatMessages, toChatTools, } from "./llm/compat.js";
|
|
9
|
-
export type { CompatConfig } from "./llm/compat.js";
|
|
19
|
+
export { fromChatCompletion, makeCompatLanguageModel, promptWithImageLabels, toChatMessages, toChatTools, } from "./llm/compat.js";
|
|
20
|
+
export type { ChatMessageOptions, CompatConfig } from "./llm/compat.js";
|
|
21
|
+
export type { UsageReading } from "./llm/sse.js";
|
|
10
22
|
export { makeOpenAiCodexLanguageModel, OPENAI_CODEX_API_URL, toOpenAiCodexRequestBody } from "./llm/openAiCodex.js";
|
|
11
|
-
export { normalizeOpenAiCodexWebSocketEvent, openAiCodexUuidV7, OpenAiCodexWebSocketHttpClient } from "./llm/openAiCodexWebSocket.js";
|
|
12
|
-
export { buildProvider, chatCompletionsUrl, OPENCODE_CHAT_URL, OPENCODE_RESPONSES_API_URL, prependClaudeCode, usesOpenCodeResponses, withAnthropicCacheBreakpoints, } from "./llm/providers.js";
|
|
23
|
+
export { makeOpenAiCodexWebSocketHttpClient, normalizeOpenAiCodexWebSocketEvent, openAiCodexRefusal, openAiCodexUuidV7, OpenAiCodexWebSocketHttpClient } from "./llm/openAiCodexWebSocket.js";
|
|
24
|
+
export { buildProvider, chatCompletionsUrl, endpointOwnKey, OPENCODE_CHAT_URL, OPENCODE_RESPONSES_API_URL, prependClaudeCode, usesOpenCodeResponses, withAnthropicCacheBreakpoints, } from "./llm/providers.js";
|
|
13
25
|
export { classifyLlmError, LLM_REQUEST_TIMEOUT_MS, MAX_HONORED_RETRY_AFTER_MS, rejectEmptyResponse, retryableLlm, } from "./llm/retry.js";
|
|
14
26
|
export type { ErrorClass } from "./llm/retry.js";
|
|
15
27
|
export { generateWith, LanguageModelLive, LanguageModelSelectionLive } from "./llm/router.js";
|
|
16
|
-
export { configuredModelCatalog, ConfiguredModelCatalogLive, reasoningEffortsFor, } from "./llm/modelCatalog.js";
|
|
28
|
+
export { configuredModelCatalog, ConfiguredModelCatalogLive, imageInputFor, reasoningEffortsFor, } from "./llm/modelCatalog.js";
|
|
17
29
|
export type { ReasoningEffort } from "./llm/modelCatalog.js";
|
|
18
30
|
export { UtilityLlmLive } from "./llm/utilityLlm.js";
|
|
19
31
|
export { modelsPlugin, default } from "./plugin.adapter.js";
|
|
20
32
|
export { CurrentModelEndpoints, ModelEndpoint, ModelEndpoints } from "./ports/model-endpoints.port.js";
|
|
33
|
+
export { ModelsTransport } from "./ports/models-transport.port.js";
|
|
34
|
+
export type { ModelsFetch, WebSocketProxy } from "./ports/models-transport.port.js";
|
|
35
|
+
export { ModelsTransportLive, transportFetch } from "./transport/models-transport.adapter.js";
|
|
36
|
+
export { BUN_WEBSOCKET_PROXY_VERSION, HttpProxyOptions, NODE_WEBSOCKET_PROXY_VERSION, NoProxyRule, PROXY_VARIABLES, ProxyConfig, ProxyEnvironment, ProxyInvalid, ProxyUnsupported, ProxyUrlOption, } from "./transport/http-proxy.entity.js";
|
|
37
|
+
export { bypassesProxy, isLoopback, parseNoProxy, proxyFor, redactCredentials, resolveProxy } from "./transport/http-proxy.entity.functions.js";
|
package/dist/index.js
CHANGED
|
@@ -18,19 +18,30 @@ export {
|
|
|
18
18
|
refreshOpenAiCodexToken
|
|
19
19
|
} from "./auth/openAiCodexOAuth.js";
|
|
20
20
|
export { authPaths, LocalAuthStoreLive } from "./auth/localAuth.js";
|
|
21
|
+
export { MemoryAuthStoreLive } from "./auth/memory-auth.adapter.js";
|
|
22
|
+
export { CredentialInput, CredentialInputs } from "./auth/credential-input.entity.js";
|
|
23
|
+
export { credentialsOf, toCredential, toCredentialInput } from "./auth/credential-input.entity.functions.js";
|
|
24
|
+
export { RefreshableProvider } from "./auth/credential-refresh.entity.js";
|
|
25
|
+
export { isUsableAt, REFRESH_SKEW_MS, refreshCredential } from "./auth/credential-refresh.entity.functions.js";
|
|
26
|
+
export { HostCredentials } from "./ports/host-credentials.port.js";
|
|
21
27
|
export { LocalSettingsStoreLive } from "./settings/localSettings.js";
|
|
28
|
+
export { MemorySettingsStoreLive } from "./settings/memory-settings.adapter.js";
|
|
29
|
+
export { RoleField, SettingValue } from "./settings/settings-value.entity.js";
|
|
30
|
+
export { applyRole, applySetting, coerceSettingValue, ROLE_FIELDS, roleSelection, settingsFromRecord } from "./settings/settings-value.entity.functions.js";
|
|
22
31
|
export { roleModelView } from "./settings/roleView.js";
|
|
23
32
|
export {
|
|
24
33
|
fromChatCompletion,
|
|
25
34
|
makeCompatLanguageModel,
|
|
35
|
+
promptWithImageLabels,
|
|
26
36
|
toChatMessages,
|
|
27
37
|
toChatTools
|
|
28
38
|
} from "./llm/compat.js";
|
|
29
39
|
export { makeOpenAiCodexLanguageModel, OPENAI_CODEX_API_URL, toOpenAiCodexRequestBody } from "./llm/openAiCodex.js";
|
|
30
|
-
export { normalizeOpenAiCodexWebSocketEvent, openAiCodexUuidV7, OpenAiCodexWebSocketHttpClient } from "./llm/openAiCodexWebSocket.js";
|
|
40
|
+
export { makeOpenAiCodexWebSocketHttpClient, normalizeOpenAiCodexWebSocketEvent, openAiCodexRefusal, openAiCodexUuidV7, OpenAiCodexWebSocketHttpClient } from "./llm/openAiCodexWebSocket.js";
|
|
31
41
|
export {
|
|
32
42
|
buildProvider,
|
|
33
43
|
chatCompletionsUrl,
|
|
44
|
+
endpointOwnKey,
|
|
34
45
|
OPENCODE_CHAT_URL,
|
|
35
46
|
OPENCODE_RESPONSES_API_URL,
|
|
36
47
|
prependClaudeCode,
|
|
@@ -48,8 +59,24 @@ export { generateWith, LanguageModelLive, LanguageModelSelectionLive } from "./l
|
|
|
48
59
|
export {
|
|
49
60
|
configuredModelCatalog,
|
|
50
61
|
ConfiguredModelCatalogLive,
|
|
62
|
+
imageInputFor,
|
|
51
63
|
reasoningEffortsFor
|
|
52
64
|
} from "./llm/modelCatalog.js";
|
|
53
65
|
export { UtilityLlmLive } from "./llm/utilityLlm.js";
|
|
54
66
|
export { modelsPlugin, default } from "./plugin.adapter.js";
|
|
55
67
|
export { CurrentModelEndpoints, ModelEndpoint, ModelEndpoints } from "./ports/model-endpoints.port.js";
|
|
68
|
+
export { ModelsTransport } from "./ports/models-transport.port.js";
|
|
69
|
+
export { ModelsTransportLive, transportFetch } from "./transport/models-transport.adapter.js";
|
|
70
|
+
export {
|
|
71
|
+
BUN_WEBSOCKET_PROXY_VERSION,
|
|
72
|
+
HttpProxyOptions,
|
|
73
|
+
NODE_WEBSOCKET_PROXY_VERSION,
|
|
74
|
+
NoProxyRule,
|
|
75
|
+
PROXY_VARIABLES,
|
|
76
|
+
ProxyConfig,
|
|
77
|
+
ProxyEnvironment,
|
|
78
|
+
ProxyInvalid,
|
|
79
|
+
ProxyUnsupported,
|
|
80
|
+
ProxyUrlOption
|
|
81
|
+
} from "./transport/http-proxy.entity.js";
|
|
82
|
+
export { bypassesProxy, isLoopback, parseNoProxy, proxyFor, redactCredentials, resolveProxy } from "./transport/http-proxy.entity.functions.js";
|