@xandreed/plugin-models 0.14.0 → 0.15.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 +30 -10
- package/dist/auth/anthropicOAuth.js +15 -18
- package/dist/auth/auth-store.conformance.d.ts +63 -0
- package/dist/auth/auth-store.conformance.js +152 -0
- package/dist/auth/credential-input.entity.d.ts +23 -5
- package/dist/auth/credential-input.entity.functions.d.ts +15 -5
- package/dist/auth/credential-input.entity.functions.js +12 -9
- package/dist/auth/credential-input.entity.js +5 -3
- package/dist/auth/credential-refresh.entity.d.ts +11 -3
- package/dist/auth/credential-refresh.entity.functions.d.ts +48 -6
- package/dist/auth/credential-refresh.entity.functions.js +12 -3
- package/dist/auth/localAuth.d.ts +10 -1
- package/dist/auth/localAuth.js +53 -79
- package/dist/auth/memory-auth.adapter.d.ts +12 -4
- package/dist/auth/memory-auth.adapter.js +13 -25
- package/dist/auth/openAiCodexOAuth.d.ts +26 -5
- package/dist/auth/openAiCodexOAuth.js +17 -28
- package/dist/auth/token-response.entity.d.ts +21 -0
- package/dist/auth/token-response.entity.functions.d.ts +20 -0
- package/dist/auth/token-response.entity.functions.js +10 -0
- package/dist/auth/token-response.entity.js +10 -0
- package/dist/compat/index.d.ts +2 -0
- package/dist/compat/index.js +1 -0
- package/dist/conformance/index.d.ts +6 -0
- package/dist/conformance/index.js +3 -0
- package/dist/index.d.ts +19 -19
- package/dist/index.js +17 -69
- package/dist/llm/chat-wire.entity.d.ts +123 -0
- package/dist/llm/chat-wire.entity.functions.d.ts +28 -0
- package/dist/llm/chat-wire.entity.functions.js +10 -0
- package/dist/llm/chat-wire.entity.js +37 -0
- package/dist/llm/compat-telemetry.adapter.d.ts +6 -0
- package/dist/llm/compat-telemetry.adapter.js +21 -0
- package/dist/llm/compat.d.ts +50 -30
- package/dist/llm/compat.js +149 -133
- package/dist/llm/model-completion.adapter.d.ts +49 -0
- package/dist/llm/model-completion.adapter.js +77 -0
- package/dist/llm/model-completion.entity.d.ts +4 -0
- package/dist/llm/model-completion.entity.functions.d.ts +3 -0
- package/dist/llm/model-completion.entity.functions.js +7 -0
- package/dist/llm/model-completion.entity.js +7 -0
- package/dist/llm/model-retry-policy.entity.d.ts +37 -0
- package/dist/llm/model-retry-policy.entity.functions.d.ts +16 -0
- package/dist/llm/model-retry-policy.entity.functions.js +33 -0
- package/dist/llm/model-retry-policy.entity.js +23 -0
- package/dist/llm/model-settings.entity.d.ts +85 -0
- package/dist/llm/model-settings.entity.functions.d.ts +11 -0
- package/dist/llm/model-settings.entity.functions.js +49 -0
- package/dist/llm/model-settings.entity.js +28 -0
- package/dist/llm/model-usage.entity.d.ts +61 -0
- package/dist/llm/model-usage.entity.functions.d.ts +11 -0
- package/dist/llm/model-usage.entity.functions.js +24 -0
- package/dist/llm/model-usage.entity.js +19 -0
- package/dist/llm/modelCatalog.d.ts +29 -8
- package/dist/llm/modelCatalog.js +28 -66
- package/dist/llm/openAiCodex.d.ts +15 -7
- package/dist/llm/openAiCodex.js +57 -45
- package/dist/llm/openAiCodexWebSocket.d.ts +48 -15
- package/dist/llm/openAiCodexWebSocket.js +101 -147
- package/dist/llm/prompt-cache-frame.entity.d.ts +18 -0
- package/dist/llm/prompt-cache-frame.entity.js +10 -0
- package/dist/llm/prompt-cache.entity.d.ts +57 -0
- package/dist/llm/prompt-cache.entity.functions.d.ts +8 -0
- package/dist/llm/prompt-cache.entity.functions.js +73 -0
- package/dist/llm/prompt-cache.entity.js +27 -0
- package/dist/llm/provider-client.adapter.d.ts +9 -0
- package/dist/llm/provider-client.adapter.js +60 -0
- package/dist/llm/provider-failure.entity.d.ts +42 -0
- package/dist/llm/provider-failure.entity.functions.d.ts +8 -0
- package/dist/llm/provider-failure.entity.functions.js +67 -0
- package/dist/llm/provider-failure.entity.js +20 -0
- package/dist/llm/provider-profile.entity.d.ts +79 -0
- package/dist/llm/provider-profile.entity.functions.d.ts +23 -0
- package/dist/llm/provider-profile.entity.functions.js +83 -0
- package/dist/llm/provider-profile.entity.js +10 -0
- package/dist/llm/providers.d.ts +116 -34
- package/dist/llm/providers.js +298 -129
- package/dist/llm/retry.d.ts +11 -47
- package/dist/llm/retry.js +33 -52
- package/dist/llm/router.d.ts +54 -36
- package/dist/llm/router.js +91 -78
- package/dist/llm/sse.d.ts +44 -37
- package/dist/llm/sse.js +43 -35
- package/dist/llm/thinking-binding.entity.d.ts +49 -0
- package/dist/llm/thinking-binding.entity.functions.d.ts +21 -0
- package/dist/llm/thinking-binding.entity.functions.js +16 -0
- package/dist/llm/thinking-binding.entity.js +11 -0
- package/dist/llm/utilityLlm.d.ts +14 -5
- package/dist/llm/utilityLlm.js +18 -13
- package/dist/plugin.adapter.d.ts +94 -6
- package/dist/plugin.adapter.js +18 -13
- package/dist/ports/auth-store.port.d.ts +73 -0
- package/dist/ports/auth-store.port.js +24 -0
- package/dist/ports/host-credentials.port.d.ts +2 -1
- package/dist/ports/model-call-completion.port.d.ts +9 -0
- package/dist/ports/model-call-completion.port.js +2 -0
- package/dist/ports/model-endpoints.port.d.ts +60 -26
- package/dist/ports/model-endpoints.port.js +17 -3
- package/dist/ports/model-retry-policy.port.d.ts +17 -0
- package/dist/ports/model-retry-policy.port.js +3 -0
- package/dist/ports/model-settings.port.d.ts +32 -0
- package/dist/ports/model-settings.port.js +4 -0
- package/dist/ports/models-transport.port.d.ts +9 -2
- package/dist/ports/models-transport.port.js +4 -1
- package/dist/ports/prompt-cache-frame.port.d.ts +21 -0
- package/dist/ports/prompt-cache-frame.port.js +4 -0
- package/dist/ports/settings-store.port.d.ts +70 -0
- package/dist/ports/settings-store.port.js +33 -0
- package/dist/ports/thinking-binding.port.d.ts +7 -0
- package/dist/ports/thinking-binding.port.js +4 -0
- package/dist/settings/localSettings.d.ts +2 -1
- package/dist/settings/localSettings.js +17 -20
- package/dist/settings/memory-settings.adapter.d.ts +3 -1
- package/dist/settings/memory-settings.adapter.js +1 -1
- package/dist/settings/roleView.d.ts +3 -1
- package/dist/settings/roleView.js +1 -1
- package/dist/settings/settings-store.conformance.d.ts +47 -0
- package/dist/settings/settings-store.conformance.js +100 -0
- package/dist/settings/settings-value.entity.d.ts +10 -2
- package/dist/settings/settings-value.entity.functions.d.ts +31 -9
- package/dist/settings/settings-value.entity.functions.js +5 -6
- package/dist/storage/json-file.d.ts +18 -0
- package/dist/storage/json-file.js +26 -0
- package/dist/transport/http-proxy.entity.d.ts +17 -3
- package/dist/transport/model-request.conformance.d.ts +13 -0
- package/dist/transport/model-request.conformance.js +36 -0
- package/dist/transport/models-transport.adapter.d.ts +18 -17
- package/dist/transport/models-transport.adapter.js +28 -31
- package/dist/transport/resilient-http-client.adapter.d.ts +73 -0
- package/dist/transport/resilient-http-client.adapter.js +177 -0
- package/package.json +11 -5
- package/dist/auth/anthropicOAuth.test.d.ts +0 -1
- package/dist/auth/credential-refresh.entity.functions.test.d.ts +0 -1
- package/dist/auth/localAuth.test.d.ts +0 -1
- package/dist/auth/memory-auth.adapter.test.d.ts +0 -1
- package/dist/llm/compat.test.d.ts +0 -1
- package/dist/llm/modelCatalog.test.d.ts +0 -1
- package/dist/llm/openAiCodexWebSocket.test.d.ts +0 -1
- package/dist/llm/providers.test.d.ts +0 -1
- package/dist/llm/retry.test.d.ts +0 -1
- package/dist/llm/router.test.d.ts +0 -1
- package/dist/llm/sse.test.d.ts +0 -1
- package/dist/plugin.adapter.test.d.ts +0 -1
- package/dist/settings/localSettings.test.d.ts +0 -1
- package/dist/settings/memory-settings.adapter.test.d.ts +0 -1
- package/dist/settings/roleView.test.d.ts +0 -1
- package/dist/settings/settings-value.entity.functions.test.d.ts +0 -1
- package/dist/transport/http-proxy.entity.functions.test.d.ts +0 -1
- package/dist/transport/models-transport.adapter.test.d.ts +0 -1
- package/dist/transport/recording-proxy.testing.d.ts +0 -33
package/dist/auth/localAuth.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { Layer } from "effect";
|
|
2
|
-
import { AuthStore } from "
|
|
2
|
+
import { AuthStore } from "../ports/auth-store.port.js";
|
|
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
6
|
* so a credential added mid-session applies on the next request. A token
|
|
7
7
|
* refresh goes through the `ModelsTransport` the layer is built over, if any.
|
|
8
|
+
*
|
|
9
|
+
* A file that cannot be read or is not a JSON object fails every call with
|
|
10
|
+
* an `AuthError` of code `auth.corrupt`; only a missing file reads as empty.
|
|
11
|
+
* Each write re-reads its file under that file's lock, which every store in
|
|
12
|
+
* the process shares, and replaces it through a temp file of its own. One
|
|
13
|
+
* refresh of a provider's token runs at a time, and stores its token only if
|
|
14
|
+
* the provider still holds the one it refreshed, so a login or logout made
|
|
15
|
+
* during the refresh wins. The locks hold within one process: two processes
|
|
16
|
+
* sharing these files can still lose an update.
|
|
8
17
|
*/
|
|
9
18
|
export declare const LocalAuthStoreLive: (cwd: string, home: string, directory?: string, fallbackDirectory?: string) => Layer.Layer<AuthStore, never, never>;
|
package/dist/auth/localAuth.js
CHANGED
|
@@ -1,88 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { Effect, Layer, Option, Schema } from "effect";
|
|
3
|
+
import { AuthError, AuthStore, Credential } from "../ports/auth-store.port.js";
|
|
4
|
+
import { encodeCredential } from "./credential-input.entity.functions.js";
|
|
5
|
+
import { isSameToken, resolveSlotKey } from "./credential-refresh.entity.functions.js";
|
|
6
6
|
import { ModelsTransport } from "../ports/models-transport.port.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return Option.none();
|
|
30
|
-
}, readAuthFile = (path) => Effect.tryPromise({ try: () => readFile(path, "utf-8"), catch: () => "missing" }).pipe(Effect.flatMap((text) => parseJsonWarn(text, path)), Effect.map((parsed) => {
|
|
31
|
-
const record = asJsonRecord(parsed), entries = new Map(Object.entries(record).flatMap(([provider, raw]) => Option.match(decodeCredential(raw), {
|
|
32
|
-
onNone: () => [],
|
|
33
|
-
onSome: (cred) => [[provider, cred]]
|
|
34
|
-
})));
|
|
35
|
-
return { path, entries };
|
|
36
|
-
}), Effect.orElseSucceed(() => ({ path, entries: new Map }))), writeAuthFile = (path, entries) => Effect.tryPromise({
|
|
37
|
-
try: async () => {
|
|
38
|
-
await mkdir(dirname(path), { recursive: !0 });
|
|
39
|
-
const tmp = `${path}.tmp-${process.pid}`;
|
|
40
|
-
await writeFile(tmp, JSON.stringify(Object.fromEntries(entries), null, 2), "utf-8");
|
|
41
|
-
await chmod(tmp, 384);
|
|
42
|
-
await rename(tmp, path);
|
|
43
|
-
},
|
|
44
|
-
catch: (e) => new AuthError({ provider: "*", message: `auth.json write failed: ${String(e)}` })
|
|
45
|
-
});
|
|
7
|
+
import { namedLock, readJsonObject, withFileLock, writeJsonObject } from "../storage/json-file.js";
|
|
8
|
+
const decodeStored = Schema.decodeUnknownOption(Credential), decodeLegacyKey = Schema.decodeUnknownOption(Schema.String), decodeCredential = (raw) => Option.orElse(decodeStored(raw), () => Option.flatMap(decodeLegacyKey(raw), (key) => decodeStored({ type: "api_key", key }))), corrupt = (path) => (reason) => new AuthError({
|
|
9
|
+
provider: "*",
|
|
10
|
+
code: "auth.corrupt",
|
|
11
|
+
message: `${path} ${reason}: fix or remove it; nothing was read from it or written to it`
|
|
12
|
+
}), readAuthFile = (path) => readJsonObject(path, corrupt(path)).pipe(Effect.map((record) => {
|
|
13
|
+
const decoded = Object.entries(Option.getOrElse(record, () => ({}))).map(([provider, raw]) => ({ provider, raw, credential: decodeCredential(raw) }));
|
|
14
|
+
return {
|
|
15
|
+
path,
|
|
16
|
+
entries: new Map(decoded.flatMap(({ provider, credential }) => Option.match(credential, {
|
|
17
|
+
onNone: () => [],
|
|
18
|
+
onSome: (found) => [[provider, found]]
|
|
19
|
+
}))),
|
|
20
|
+
unread: new Map(decoded.filter(({ credential }) => Option.isNone(credential)).map(({ provider, raw }) => [provider, raw]))
|
|
21
|
+
};
|
|
22
|
+
})), writeAuthFile = (file, provider, credential) => {
|
|
23
|
+
const others = (entries) => [...entries].filter(([key]) => key !== provider), credentials = [...others(file.entries), ...Option.toArray(Option.map(credential, (value) => [provider, value]))];
|
|
24
|
+
return Effect.forEach(credentials, ([key, value]) => Effect.map(encodeCredential(key, value), (encoded) => [key, encoded]), { concurrency: 1 }).pipe(Effect.flatMap((encoded) => {
|
|
25
|
+
const record = Object.fromEntries([...others(file.unread), ...encoded]);
|
|
26
|
+
return writeJsonObject(file.path, record, Option.some(384), (reason) => new AuthError({ provider: "*", message: `auth.json write failed: ${reason}` }));
|
|
27
|
+
}));
|
|
28
|
+
};
|
|
46
29
|
export const authPaths = (cwd, home, directory = ".efferent") => [
|
|
47
30
|
join(home, directory, "auth.json"),
|
|
48
31
|
join(cwd, directory, "auth.json")
|
|
49
|
-
]
|
|
50
|
-
|
|
32
|
+
];
|
|
33
|
+
const holderOf = (files, provider) => Option.firstSomeOf([...files].reverse().map((file) => Option.map(Option.fromNullishOr(file.entries.get(provider)), (credential) => ({ file, credential }))));
|
|
34
|
+
export const LocalAuthStoreLive = (cwd, home, directory = ".efferent", fallbackDirectory) => {
|
|
35
|
+
const writable = authPaths(cwd, home, directory), loginPath = writable[0] ?? join(home, directory, "auth.json"), paths = [...fallbackDirectory === void 0 ? [] : authPaths(cwd, home, fallbackDirectory), ...writable], readAll = Effect.forEach(paths, readAuthFile, { concurrency: 1 }), targetOf = (path) => writable.includes(path) ? path : loginPath;
|
|
51
36
|
return Layer.effect(AuthStore, Effect.map(Effect.serviceOption(ModelsTransport), (transport) => {
|
|
52
|
-
const tokens = Option.match(transport, { onNone: () => ({}), onSome: ({ fetch }) => ({ fetch }) })
|
|
37
|
+
const tokens = Option.match(transport, { onNone: () => ({}), onSome: ({ fetch }) => ({ fetch }) }), slotOf = (provider) => ({
|
|
38
|
+
current: Effect.map(readAll, (files) => Option.map(holderOf(files, provider), ({ credential }) => credential)),
|
|
39
|
+
refreshLock: namedLock(loginPath, `refresh:${provider}`),
|
|
40
|
+
commit: (stale, fresh) => Effect.map(readAll, (files) => holderOf(files, provider)).pipe(Effect.flatMap(Option.match({
|
|
41
|
+
onNone: () => Effect.succeed(!1),
|
|
42
|
+
onSome: ({ file }) => {
|
|
43
|
+
const target = targetOf(file.path);
|
|
44
|
+
return withFileLock(target)(Effect.gen(function* () {
|
|
45
|
+
const now = holderOf(yield* readAll, provider);
|
|
46
|
+
if (!Option.exists(now, (held) => targetOf(held.file.path) === target && isSameToken(held.credential, stale)))
|
|
47
|
+
return !1;
|
|
48
|
+
yield* writeAuthFile(yield* readAuthFile(target), provider, Option.some(fresh));
|
|
49
|
+
return !0;
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
})))
|
|
53
|
+
});
|
|
53
54
|
return {
|
|
54
|
-
all: Effect.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)
|
|
55
|
+
all: Effect.map(readAll, (files) => new Map(files.flatMap((f) => [...f.entries]))),
|
|
56
|
+
get: (provider) => slotOf(provider).current,
|
|
57
|
+
resolveKey: (provider) => resolveSlotKey(provider, slotOf(provider), tokens),
|
|
58
|
+
set: (provider, credential) => withFileLock(loginPath)(readAuthFile(loginPath).pipe(Effect.flatMap((file) => writeAuthFile(file, provider, Option.some(credential))))),
|
|
59
|
+
remove: (provider) => readAll.pipe(Effect.andThen(Effect.forEach(paths, (path) => withFileLock(path)(readAuthFile(path).pipe(Effect.flatMap((file) => file.entries.has(provider) || file.unread.has(provider) ? writeAuthFile(file, provider, Option.none()) : Effect.void))), { concurrency: 1, discard: !0 })))
|
|
86
60
|
};
|
|
87
61
|
}));
|
|
88
62
|
};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Layer } from "effect";
|
|
2
|
-
import { AuthStore } from "
|
|
3
|
-
import type { AuthError, Credential } from "
|
|
2
|
+
import { AuthStore } from "../ports/auth-store.port.js";
|
|
3
|
+
import type { AuthError, Credential } from "../ports/auth-store.port.js";
|
|
4
4
|
import type { ModelsFetch } from "../ports/models-transport.port.js";
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* How the in-memory store reaches the OAuth token endpoints.
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
6
10
|
export interface MemoryAuthOptions {
|
|
7
11
|
/** The fetch token refreshes go through; default: the `ModelsTransport`'s when the layer is built over one, else the global fetch. */
|
|
8
12
|
readonly fetch?: ModelsFetch;
|
|
@@ -14,7 +18,9 @@ export interface MemoryAuthOptions {
|
|
|
14
18
|
* `seed`. `resolveKey` refreshes a near-expiry anthropic or openai-codex
|
|
15
19
|
* OAuth token in memory, one refresh per provider at a time (through the
|
|
16
20
|
* `ModelsTransport` it was built over, so through the plugin's proxy), and
|
|
17
|
-
* hands the new token to `HostCredentials.save`.
|
|
21
|
+
* hands the new token to `HostCredentials.save`. Secrets stay `Redacted`
|
|
22
|
+
* throughout; a `set` whose secrets are not (a plain string from untyped
|
|
23
|
+
* code) fails with an `AuthError`, as it does on the file store.
|
|
18
24
|
*
|
|
19
25
|
* A login or logout (`set`, `remove`) does not wait for a refresh's token
|
|
20
26
|
* request; it waits only while a refreshed token is committed, which
|
|
@@ -23,5 +29,7 @@ export interface MemoryAuthOptions {
|
|
|
23
29
|
* credential it refreshed (compare-and-set under the provider's lock): a
|
|
24
30
|
* logout during a stalled token request stays a logout, and the host never
|
|
25
31
|
* receives the refreshed token.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
26
34
|
*/
|
|
27
35
|
export declare const MemoryAuthStoreLive: (seed?: ReadonlyMap<string, Credential>, options?: MemoryAuthOptions) => Layer.Layer<AuthStore, AuthError>;
|
|
@@ -1,47 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AuthStore } from "
|
|
1
|
+
import { Effect, Layer, Option, Ref, Semaphore, SynchronizedRef } from "effect";
|
|
2
|
+
import { AuthStore } from "../ports/auth-store.port.js";
|
|
3
3
|
import { HostCredentials } from "../ports/host-credentials.port.js";
|
|
4
4
|
import { ModelsTransport } from "../ports/models-transport.port.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { encodeCredential, toCredentialInput } from "./credential-input.entity.functions.js";
|
|
6
|
+
import { isSameToken, resolveSlotKey } from "./credential-refresh.entity.functions.js";
|
|
7
7
|
export const MemoryAuthStoreLive = (seed = new Map, options = {}) => Layer.effect(AuthStore, Effect.gen(function* () {
|
|
8
8
|
const host = yield* Effect.serviceOption(HostCredentials), loaded = yield* Option.match(host, {
|
|
9
9
|
onNone: () => Effect.succeed(new Map),
|
|
10
|
-
onSome: (credentials) => credentials.load
|
|
10
|
+
onSome: (credentials) => credentials.load
|
|
11
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
12
|
onNone: () => ({}),
|
|
13
13
|
onSome: (fetch) => ({ fetch })
|
|
14
14
|
}), locksOf = (provider) => SynchronizedRef.modifyEffect(locks, (all) => Option.match(Option.fromNullishOr(all.get(provider)), {
|
|
15
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]])]))
|
|
16
|
+
onNone: () => Effect.all({ commit: Semaphore.make(1), refresh: Semaphore.make(1) }, { concurrency: 1 }).pipe(Effect.map((made) => [made, new Map([...all, [provider, made]])]))
|
|
17
17
|
})), current = (provider) => Ref.get(state).pipe(Effect.map((all) => Option.fromNullishOr(all.get(provider)))), save = (provider, credential) => Option.match(host, {
|
|
18
18
|
onNone: () => Effect.void,
|
|
19
19
|
onSome: (credentials) => credentials.save(provider, Option.map(credential, toCredentialInput))
|
|
20
20
|
}), write = (provider, credential) => Ref.update(state, (all) => Option.match(credential, {
|
|
21
21
|
onNone: () => new Map([...all].filter(([key]) => key !== provider)),
|
|
22
22
|
onSome: (value) => new Map([...all, [provider, value]])
|
|
23
|
-
})),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
})));
|
|
23
|
+
})), slotOf = (provider) => ({
|
|
24
|
+
current: current(provider),
|
|
25
|
+
refreshLock: Effect.map(locksOf(provider), (lock) => lock.refresh),
|
|
26
|
+
commit: (stale, fresh) => locksOf(provider).pipe(Effect.flatMap((lock) => lock.commit.withPermit(current(provider).pipe(Effect.flatMap((now) => !Option.exists(now, (held) => isSameToken(held, stale)) ? Effect.succeed(!1) : write(provider, Option.some(fresh)).pipe(Effect.andThen(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}`)))), Effect.as(!0)))))))
|
|
27
|
+
});
|
|
40
28
|
return AuthStore.of({
|
|
41
29
|
all: Ref.get(state),
|
|
42
30
|
get: current,
|
|
43
|
-
resolveKey,
|
|
44
|
-
set: (provider, credential) =>
|
|
31
|
+
resolveKey: (provider) => resolveSlotKey(provider, slotOf(provider), tokens),
|
|
32
|
+
set: (provider, credential) => encodeCredential(provider, credential).pipe(Effect.andThen(locksOf(provider)), Effect.flatMap((lock) => lock.commit.withPermit(save(provider, Option.some(credential)).pipe(Effect.andThen(write(provider, Option.some(credential))))))),
|
|
45
33
|
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
34
|
});
|
|
47
35
|
}));
|
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
import { Effect, Option } from "effect";
|
|
2
|
-
import { AuthError } from "
|
|
1
|
+
import { Effect, Option, Redacted } from "effect";
|
|
2
|
+
import { AuthError } from "../ports/auth-store.port.js";
|
|
3
3
|
import type { TokenRequestOptions } from "./anthropicOAuth.js";
|
|
4
4
|
/** ChatGPT Plus/Pro OAuth protocol used by the OpenAI Codex subscription. */
|
|
5
5
|
export declare const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
6
|
+
/** The originator registered with `OPENAI_CODEX_CLIENT_ID`. The authorize
|
|
7
|
+
* request, the token and the subscription transports must all send it: the
|
|
8
|
+
* backend keys model entitlement to the client/originator pair. */
|
|
9
|
+
export declare const OPENAI_CODEX_ORIGINATOR = "pi";
|
|
6
10
|
export declare const OPENAI_CODEX_CALLBACK_PORT = 1455;
|
|
7
11
|
export declare const OPENAI_CODEX_CALLBACK_PATH = "/auth/callback";
|
|
8
12
|
export declare const OPENAI_CODEX_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
13
|
+
/**
|
|
14
|
+
* The tokens a sign-in or a refresh grants, `Redacted` from the token response on, and the account id read from the access token.
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
9
18
|
export interface OpenAiCodexTokens {
|
|
10
|
-
readonly access: string
|
|
11
|
-
readonly refresh: string
|
|
19
|
+
readonly access: Redacted.Redacted<string>;
|
|
20
|
+
readonly refresh: Redacted.Redacted<string>;
|
|
12
21
|
readonly expires: number;
|
|
13
22
|
readonly accountId?: string;
|
|
14
23
|
}
|
|
24
|
+
/** @public */
|
|
15
25
|
export interface OpenAiCodexOAuthBegin {
|
|
16
26
|
readonly verifier: string;
|
|
17
27
|
readonly state: string;
|
|
@@ -21,6 +31,17 @@ export interface OpenAiCodexOAuthBegin {
|
|
|
21
31
|
}
|
|
22
32
|
/** Extract the subscription account lane without exposing the token. */
|
|
23
33
|
export declare const openAiCodexAccountId: (accessToken: string) => Option.Option<string>;
|
|
34
|
+
/** @public */
|
|
24
35
|
export declare const beginOpenAiCodexOAuth: Effect.Effect<OpenAiCodexOAuthBegin>;
|
|
36
|
+
/**
|
|
37
|
+
* Exchange the authorization code for tokens, `Redacted` (ready for `AuthStore.set`).
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
25
41
|
export declare const exchangeOpenAiCodexCode: (code: string, verifier: string, options?: TokenRequestOptions) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
26
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Refresh an expired (or near-expiry) access token; the refresh token is unwrapped only into the request form.
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export declare const refreshOpenAiCodexToken: (refreshToken: Redacted.Redacted<string>, options?: TokenRequestOptions) => Effect.Effect<OpenAiCodexTokens, AuthError>;
|
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
import { Effect, Result, Option } from "effect";
|
|
2
|
-
import {
|
|
1
|
+
import { Effect, Result, Option, Redacted, Schema } from "effect";
|
|
2
|
+
import { Base64Url } from "effect/encoding";
|
|
3
|
+
import { AuthError } from "../ports/auth-store.port.js";
|
|
3
4
|
import { generatePkce, tokenTime } from "./anthropicOAuth.js";
|
|
4
5
|
import { redactCredentials } from "../transport/http-proxy.entity.functions.js";
|
|
5
|
-
|
|
6
|
+
import { decodeTokenResponse, tokenRefusalText } from "./token-response.entity.functions.js";
|
|
7
|
+
export const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann", OPENAI_CODEX_ORIGINATOR = "pi";
|
|
6
8
|
const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize", TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
7
9
|
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}`;
|
|
8
|
-
const SCOPE = "openid profile email offline_access", ACCOUNT_CLAIM = "https://api.openai.com/auth", oauthError = (stage, detail) => new AuthError({ provider: "openai-codex", message: `OAuth ${stage} failed: ${detail}` }),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const segment = accessToken.split(".")[1];
|
|
14
|
-
return Option.flatMap(Option.fromNullishOr(segment), (encoded) => Option.flatMap(Result.getSuccess(Result.try(() => base64UrlJson(encoded))), (payload) => {
|
|
15
|
-
const claim = payload[ACCOUNT_CLAIM];
|
|
16
|
-
if (typeof claim !== "object" || claim === null)
|
|
17
|
-
return Option.none();
|
|
18
|
-
const accountId = claim.chatgpt_account_id;
|
|
19
|
-
return Option.filter(Option.fromNullishOr(typeof accountId === "string" ? accountId : void 0), (value) => value.length > 0);
|
|
20
|
-
}));
|
|
21
|
-
};
|
|
22
|
-
const postToken = (body, stage, options) => Effect.tryPromise({
|
|
10
|
+
const SCOPE = "openid profile email offline_access", ACCOUNT_CLAIM = "https://api.openai.com/auth", oauthError = (stage, detail) => new AuthError({ provider: "openai-codex", message: `OAuth ${stage} failed: ${detail}` }), AccessTokenClaims = Schema.Struct({
|
|
11
|
+
[ACCOUNT_CLAIM]: Schema.optionalKey(Schema.Struct({ chatgpt_account_id: Schema.optionalKey(Schema.NonEmptyString) }))
|
|
12
|
+
}), decodeClaims = Schema.decodeUnknownOption(Schema.fromJsonString(AccessTokenClaims)), claimsOf = (segment) => Option.flatMap(Result.getSuccess(Base64Url.decodeString(segment)), decodeClaims);
|
|
13
|
+
export const openAiCodexAccountId = (accessToken) => Option.flatMap(Option.flatMap(Option.fromNullishOr(accessToken.split(".")[1]), claimsOf), (claims) => Option.fromNullishOr(claims[ACCOUNT_CLAIM]?.chatgpt_account_id));
|
|
14
|
+
const postToken = (body, stage, options, previous) => Effect.tryPromise({
|
|
23
15
|
try: async () => {
|
|
24
16
|
const response = await (options.fetch ?? fetch)(TOKEN_URL, {
|
|
25
17
|
method: "POST",
|
|
@@ -30,14 +22,11 @@ const postToken = (body, stage, options) => Effect.tryPromise({
|
|
|
30
22
|
return { ok: response.ok, status: response.status, text: await response.text() };
|
|
31
23
|
},
|
|
32
24
|
catch: (error) => oauthError(stage, redactCredentials(String(error)))
|
|
33
|
-
}).pipe(Effect.filterOrFail((response) => response.ok, (response) => oauthError(stage,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
access:
|
|
38
|
-
refresh: token.refresh_token,
|
|
39
|
-
expires: now + token.expires_in * 1000,
|
|
40
|
-
...Option.match(openAiCodexAccountId(token.access_token), { onNone: () => ({}), onSome: (accountId) => ({ accountId }) })
|
|
25
|
+
}).pipe(Effect.filterOrFail((response) => response.ok, (response) => oauthError(stage, tokenRefusalText(response.status, response.text, body))), Effect.flatMap((response) => decodeTokenResponse(response.text, previous).pipe(Effect.mapError((reason) => oauthError(stage, reason)))), Effect.flatMap((issued) => Effect.map(tokenTime(options), (now) => ({
|
|
26
|
+
access: issued.access,
|
|
27
|
+
refresh: issued.refresh,
|
|
28
|
+
expires: now + issued.lifetimeMs,
|
|
29
|
+
...Option.match(openAiCodexAccountId(Redacted.value(issued.access)), { onNone: () => ({}), onSome: (accountId) => ({ accountId }) })
|
|
41
30
|
}))));
|
|
42
31
|
export const beginOpenAiCodexOAuth = Effect.gen(function* () {
|
|
43
32
|
const pkce = yield* generatePkce, stateBytes = new Uint8Array(16);
|
|
@@ -53,7 +42,7 @@ export const beginOpenAiCodexOAuth = Effect.gen(function* () {
|
|
|
53
42
|
state,
|
|
54
43
|
id_token_add_organizations: "true",
|
|
55
44
|
codex_cli_simplified_flow: "true",
|
|
56
|
-
originator:
|
|
45
|
+
originator: OPENAI_CODEX_ORIGINATOR
|
|
57
46
|
}).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
58
47
|
return { verifier: pkce.verifier, state, authorizeUrl: url.toString(), callbackPort: OPENAI_CODEX_CALLBACK_PORT, callbackPath: OPENAI_CODEX_CALLBACK_PATH };
|
|
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);
|
|
48
|
+
}), 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, Option.none()), refreshOpenAiCodexToken = (refreshToken, options = {}) => postToken({ grant_type: "refresh_token", refresh_token: Redacted.value(refreshToken), client_id: OPENAI_CODEX_CLIENT_ID }, "token refresh", options, Option.some(refreshToken));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* A token endpoint's answer, as both subscription providers send it: the
|
|
4
|
+
* access token, its lifetime in seconds, and a refresh token. A refresh may
|
|
5
|
+
* leave the refresh token out, when the provider does not rotate it. The
|
|
6
|
+
* tokens decode `Redacted`, so from the response on they print as
|
|
7
|
+
* `<redacted>` in errors, logs and fingerprints.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TokenResponse: Schema.Struct<{
|
|
10
|
+
readonly access_token: Schema.RedactedFromValue<Schema.NonEmptyString>;
|
|
11
|
+
readonly expires_in: Schema.Finite;
|
|
12
|
+
readonly refresh_token: Schema.OptionFromOptional<Schema.RedactedFromValue<Schema.String>>;
|
|
13
|
+
}>;
|
|
14
|
+
export type TokenResponse = typeof TokenResponse.Type;
|
|
15
|
+
/** The tokens a response grants, `Redacted`: the access token, the refresh token to keep, and the access token's lifetime in ms. */
|
|
16
|
+
export declare const IssuedTokens: Schema.Struct<{
|
|
17
|
+
readonly access: Schema.Redacted<Schema.String>;
|
|
18
|
+
readonly refresh: Schema.Redacted<Schema.String>;
|
|
19
|
+
readonly lifetimeMs: Schema.Number;
|
|
20
|
+
}>;
|
|
21
|
+
export type IssuedTokens = typeof IssuedTokens.Type;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect, Option, Redacted } from "effect";
|
|
2
|
+
import type { IssuedTokens } from "./token-response.entity.js";
|
|
3
|
+
/** How much of a refused token request's response body an error quotes. */
|
|
4
|
+
export declare const TOKEN_ERROR_BODY_CHARS = 200;
|
|
5
|
+
/**
|
|
6
|
+
* What an error says of a token endpoint's refusal: its status and the
|
|
7
|
+
* start of its body, every secret the request sent (`SECRET_FIELDS` of
|
|
8
|
+
* `sent`) replaced by `<redacted>` first, so an endpoint that echoes the
|
|
9
|
+
* request never puts a token into an error, a log or the model's view.
|
|
10
|
+
*/
|
|
11
|
+
export declare const tokenRefusalText: (status: number, body: string, sent: Readonly<Record<string, string>>) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Decode a token endpoint's answer, its tokens `Redacted`. `previous` is
|
|
14
|
+
* the refresh token a refresh sent: a response without one keeps it. A code
|
|
15
|
+
* exchange sends none, so its response must carry a refresh token. A
|
|
16
|
+
* response that is not JSON, has no access token or no lifetime above zero
|
|
17
|
+
* seconds fails with a reason that never quotes the response, which holds
|
|
18
|
+
* secrets.
|
|
19
|
+
*/
|
|
20
|
+
export declare const decodeTokenResponse: (text: string, previous: Option.Option<Redacted.Redacted<string>>) => Effect.Effect<IssuedTokens, string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
|
+
import { TokenResponse } from "./token-response.entity.js";
|
|
3
|
+
const decodeResponse = Schema.decodeUnknownEffect(Schema.fromJsonString(TokenResponse)), isNonEmpty = (token) => Redacted.value(token).length > 0, SECRET_FIELDS = ["refresh_token", "code", "code_verifier"];
|
|
4
|
+
export const TOKEN_ERROR_BODY_CHARS = 200, tokenRefusalText = (status, body, sent) => {
|
|
5
|
+
const redacted = SECRET_FIELDS.flatMap((field) => Option.toArray(Option.filter(Option.fromNullishOr(sent[field]), (value) => value.length > 0))).reduce((text, secret) => text.replaceAll(secret, "<redacted>"), body);
|
|
6
|
+
return `status ${status}: ${redacted.slice(0, TOKEN_ERROR_BODY_CHARS)}`;
|
|
7
|
+
}, decodeTokenResponse = (text, previous) => decodeResponse(text).pipe(Effect.mapError(() => "the token response did not hold an access token and its lifetime (expires_in, in seconds, above 0)"), Effect.flatMap((response) => Option.match(Option.orElse(Option.filter(response.refresh_token, isNonEmpty), () => previous), {
|
|
8
|
+
onNone: () => Effect.fail("the token response did not hold a refresh token"),
|
|
9
|
+
onSome: (refresh) => Effect.succeed({ access: response.access_token, refresh, lifetimeMs: response.expires_in * 1000 })
|
|
10
|
+
})));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export const TokenResponse = Schema.Struct({
|
|
3
|
+
access_token: Schema.RedactedFromValue(Schema.NonEmptyString),
|
|
4
|
+
expires_in: Schema.Finite.check(Schema.isGreaterThan(0)),
|
|
5
|
+
refresh_token: Schema.OptionFromOptional(Schema.RedactedFromValue(Schema.String))
|
|
6
|
+
}), IssuedTokens = Schema.Struct({
|
|
7
|
+
access: Schema.Redacted(Schema.String),
|
|
8
|
+
refresh: Schema.Redacted(Schema.String),
|
|
9
|
+
lifetimeMs: Schema.Number
|
|
10
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { makeCompatLanguageModel, promptWithImageLabels, toChatMessages } from "../llm/compat.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { authStoreConformance } from "../auth/auth-store.conformance.js";
|
|
2
|
+
export type { AuthStoreConformanceOptions, AuthStoreRefreshProbe } from "../auth/auth-store.conformance.js";
|
|
3
|
+
export { settingsStoreConformance } from "../settings/settings-store.conformance.js";
|
|
4
|
+
export type { SettingsStoreConformanceOptions } from "../settings/settings-store.conformance.js";
|
|
5
|
+
export { modelRequestConformance } from "../transport/model-request.conformance.js";
|
|
6
|
+
export type { ModelRequestSubject } from "../transport/model-request.conformance.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { AuthRedirect, OAuthBegin,
|
|
3
|
-
export { beginOpenAiCodexOAuth, exchangeOpenAiCodexCode,
|
|
1
|
+
export { beginAnthropicOAuth, exchangeAnthropicCode, parseAuthorizationInput, refreshAnthropicToken } from "./auth/anthropicOAuth.js";
|
|
2
|
+
export type { AuthRedirect, OAuthBegin, RefreshedTokens, TokenRequestOptions } from "./auth/anthropicOAuth.js";
|
|
3
|
+
export { beginOpenAiCodexOAuth, exchangeOpenAiCodexCode, refreshOpenAiCodexToken } from "./auth/openAiCodexOAuth.js";
|
|
4
4
|
export type { OpenAiCodexOAuthBegin, OpenAiCodexTokens } from "./auth/openAiCodexOAuth.js";
|
|
5
|
-
export {
|
|
5
|
+
export { LocalAuthStoreLive } from "./auth/localAuth.js";
|
|
6
6
|
export { MemoryAuthStoreLive } from "./auth/memory-auth.adapter.js";
|
|
7
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";
|
|
8
|
+
export { AccountId, CredentialInput, CredentialInputs, InstallationId } from "./auth/credential-input.entity.js";
|
|
10
9
|
export { RefreshableProvider } from "./auth/credential-refresh.entity.js";
|
|
11
10
|
export type { OAuthCredential } from "./auth/credential-refresh.entity.js";
|
|
12
11
|
export { isUsableAt, REFRESH_SKEW_MS, refreshCredential } from "./auth/credential-refresh.entity.functions.js";
|
|
13
12
|
export { HostCredentials } from "./ports/host-credentials.port.js";
|
|
13
|
+
export { AuthError, AuthStore, Credential } from "./ports/auth-store.port.js";
|
|
14
|
+
export { EngineSettings, SETTINGS_KEYS, SettingsError, SettingsStore } from "./ports/settings-store.port.js";
|
|
15
|
+
export type { ModelRole, SettingsKey } from "./ports/settings-store.port.js";
|
|
14
16
|
export { LocalSettingsStoreLive } from "./settings/localSettings.js";
|
|
15
17
|
export { MemorySettingsStoreLive } from "./settings/memory-settings.adapter.js";
|
|
16
18
|
export { RoleField, SettingValue } from "./settings/settings-value.entity.js";
|
|
17
19
|
export { applyRole, applySetting, coerceSettingValue, ROLE_FIELDS, roleSelection, settingsFromRecord } from "./settings/settings-value.entity.functions.js";
|
|
18
20
|
export { roleModelView } from "./settings/roleView.js";
|
|
19
|
-
export {
|
|
20
|
-
export
|
|
21
|
-
export
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export { classifyLlmError, LLM_REQUEST_TIMEOUT_MS, MAX_HONORED_RETRY_AFTER_MS, rejectEmptyResponse, retryableLlm, } from "./llm/retry.js";
|
|
26
|
-
export type { ErrorClass } from "./llm/retry.js";
|
|
27
|
-
export { generateWith, LanguageModelLive, LanguageModelSelectionLive } from "./llm/router.js";
|
|
28
|
-
export { configuredModelCatalog, ConfiguredModelCatalogLive, imageInputFor, reasoningEffortsFor, } from "./llm/modelCatalog.js";
|
|
21
|
+
export { makeOpenAiCodexWebSocketHttpClient } from "./llm/openAiCodexWebSocket.js";
|
|
22
|
+
export { LLM_CALL_BUDGET_MS, LLM_REQUEST_TIMEOUT_MS } from "./llm/retry.js";
|
|
23
|
+
export { ModelRetryPolicy, modelRetryDefaults } from "./llm/model-retry-policy.entity.js";
|
|
24
|
+
export { CurrentModelRetryPolicy } from "./ports/model-retry-policy.port.js";
|
|
25
|
+
export { LanguageModelLive, LanguageModelSelectionLive } from "./llm/router.js";
|
|
26
|
+
export { ConfiguredModelCatalogLive, imageInputFor, reasoningEffortsFor } from "./llm/modelCatalog.js";
|
|
29
27
|
export type { ReasoningEffort } from "./llm/modelCatalog.js";
|
|
30
28
|
export { UtilityLlmLive } from "./llm/utilityLlm.js";
|
|
31
29
|
export { modelsPlugin, default } from "./plugin.adapter.js";
|
|
32
30
|
export { CurrentModelEndpoints, ModelEndpoint, ModelEndpoints } from "./ports/model-endpoints.port.js";
|
|
33
31
|
export { ModelsTransport } from "./ports/models-transport.port.js";
|
|
34
32
|
export type { ModelsFetch, WebSocketProxy } from "./ports/models-transport.port.js";
|
|
35
|
-
export { ModelsTransportLive
|
|
36
|
-
export {
|
|
37
|
-
export {
|
|
33
|
+
export { ModelsTransportLive } from "./transport/models-transport.adapter.js";
|
|
34
|
+
export { HttpProxyOptions, ProxyInvalid, ProxyUnsupported, ProxyUrlOption } from "./transport/http-proxy.entity.js";
|
|
35
|
+
export { ModelThinking, ModelSettings, ModelSettingsMap, ModelSettingsError } from "./llm/model-settings.entity.js";
|
|
36
|
+
export { PromptCacheSettings } from "./llm/prompt-cache.entity.js";
|
|
37
|
+
export { CurrentModelSettings } from "./ports/model-settings.port.js";
|