@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/dist/plugin.adapter.js
CHANGED
|
@@ -1,27 +1,65 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { LanguageModel } from "effect/ai";
|
|
3
|
-
import { Effect, Layer, Option, Schema } from "effect";
|
|
4
|
-
import { AuthStore, definePlugin, EngineSettings, ModelCatalog, SessionEnvironment, SettingsError, SettingsStore, UtilityLlm } from "@xandreed/core";
|
|
3
|
+
import { Config as EffectConfig, Context, Effect, Layer, Match, Option, Redacted, Schema } from "effect";
|
|
4
|
+
import { AuthStore, definePlugin, EngineSettings, HarnessError, ModelCatalog, SessionEnvironment, SettingsError, SettingsStore, UtilityLlm } from "@xandreed/core";
|
|
5
|
+
import { CredentialInputs } from "./auth/credential-input.entity.js";
|
|
6
|
+
import { credentialsOf } from "./auth/credential-input.entity.functions.js";
|
|
5
7
|
import { LocalAuthStoreLive } from "./auth/localAuth.js";
|
|
8
|
+
import { MemoryAuthStoreLive } from "./auth/memory-auth.adapter.js";
|
|
6
9
|
import { LanguageModelLive } from "./llm/router.js";
|
|
7
10
|
import { UtilityLlmLive } from "./llm/utilityLlm.js";
|
|
8
11
|
import { ConfiguredModelCatalogLive } from "./llm/modelCatalog.js";
|
|
9
12
|
import { LocalSettingsStoreLive } from "./settings/localSettings.js";
|
|
13
|
+
import { MemorySettingsStoreLive } from "./settings/memory-settings.adapter.js";
|
|
14
|
+
import { HostCredentials } from "./ports/host-credentials.port.js";
|
|
10
15
|
import { CurrentModelEndpoints, ModelEndpoints } from "./ports/model-endpoints.port.js";
|
|
11
|
-
|
|
16
|
+
import { ModelsTransport } from "./ports/models-transport.port.js";
|
|
17
|
+
import { ProxyUrlOption } from "./transport/http-proxy.entity.js";
|
|
18
|
+
import { modelsTransportFor } from "./transport/models-transport.adapter.js";
|
|
19
|
+
const Config = Schema.Struct({
|
|
20
|
+
model: Schema.String,
|
|
21
|
+
fastModel: Schema.String,
|
|
22
|
+
fallbackModel: Schema.String,
|
|
23
|
+
inheritPrevious: Schema.Boolean,
|
|
24
|
+
endpoints: ModelEndpoints,
|
|
25
|
+
storage: Schema.Literals(["local", "memory"]),
|
|
26
|
+
credentials: CredentialInputs,
|
|
27
|
+
credentialEnv: Schema.Record(Schema.String, Schema.String),
|
|
28
|
+
proxy: ProxyUrlOption,
|
|
29
|
+
noProxy: Schema.String
|
|
30
|
+
}).annotate({ messageUnexpectedKey: "not an option of the models plugin" }).check(Schema.makeFilter((config) => config.storage === "memory" || Object.keys(config.credentials).length === 0 && Object.keys(config.credentialEnv).length === 0 ? void 0 : 'credentials and credentialEnv need storage: "memory"')), optional = (value) => value.length === 0 ? Option.none() : Option.some(value), overlaid = (config) => (previous) => new EngineSettings({
|
|
31
|
+
...previous,
|
|
32
|
+
model: Option.orElse(optional(config.model), () => previous.model),
|
|
33
|
+
fastModel: Option.orElse(optional(config.fastModel), () => previous.fastModel),
|
|
34
|
+
fallbackModel: Option.orElse(optional(config.fallbackModel), () => previous.fallbackModel)
|
|
35
|
+
}), memoryStores = (config) => Layer.unwrap(Effect.gen(function* () {
|
|
36
|
+
const fromEnv = yield* Effect.forEach(Object.entries(config.credentialEnv), ([provider, name]) => EffectConfig.option(EffectConfig.Redacted(name)).pipe(Effect.map(Option.map((key) => [provider, { type: "api_key", key: Redacted.value(key) }])))), seed = new Map([...fromEnv.flatMap(Option.toArray), ...credentialsOf(config.credentials)]);
|
|
37
|
+
return Layer.mergeAll(MemoryAuthStoreLive(seed), MemorySettingsStoreLive(overlaid(config)(new EngineSettings({}))));
|
|
38
|
+
})), readOnly = (what) => () => Effect.fail(new SettingsError({ message: `Edit the models plugin configuration to change ${what}` })), localStores = (config, workspace) => {
|
|
39
|
+
const home = homedir(), files = Effect.service(SettingsStore).pipe(Effect.provide(LocalSettingsStoreLive(workspace, home))), settings = Layer.effect(SettingsStore, Effect.map(files, (stored) => SettingsStore.of({
|
|
40
|
+
load: config.inheritPrevious ? Effect.map(stored.load, overlaid(config)) : Effect.succeed(overlaid(config)(new EngineSettings({}))),
|
|
41
|
+
setRole: readOnly("a model"),
|
|
42
|
+
set: readOnly("settings")
|
|
43
|
+
})));
|
|
44
|
+
return Layer.mergeAll(settings, LocalAuthStoreLive(workspace, home, ".efferent/runtime", config.inheritPrevious ? ".efferent" : void 0));
|
|
45
|
+
}, optionError = (message) => new HarnessError({ code: "config.options", plugin: "@xandreed/plugin-models", message }), transportLayer = (config) => Effect.flatMap(Effect.serviceOption(ModelsTransport), Option.match({
|
|
46
|
+
onSome: (host) => Redacted.isRedacted(config.proxy) || config.proxy.length > 0 || config.noProxy.length > 0 ? Effect.fail(optionError("the host provides ModelsTransport, so the proxy and noProxy options would not apply: give the proxy to the host's transport")) : Effect.succeed(Layer.succeed(ModelsTransport, host)),
|
|
47
|
+
onNone: () => Effect.succeed(Layer.effectContext(modelsTransportFor({ proxy: config.proxy, noProxy: config.noProxy }).pipe(Effect.map(Option.match({
|
|
48
|
+
onNone: () => Context.empty(),
|
|
49
|
+
onSome: (transport) => Context.make(ModelsTransport, transport)
|
|
50
|
+
})), Effect.mapError((error) => optionError(error.message)))))
|
|
51
|
+
}));
|
|
12
52
|
export const modelsPlugin = definePlugin({
|
|
13
53
|
id: "@xandreed/plugin-models",
|
|
14
|
-
version: "0.
|
|
54
|
+
version: "0.14.0",
|
|
55
|
+
scope: "runtime",
|
|
15
56
|
config: Config,
|
|
16
|
-
defaults: { model: "", fastModel: "", fallbackModel: "", inheritPrevious: !0, endpoints: {} },
|
|
57
|
+
defaults: { model: "", fastModel: "", fallbackModel: "", inheritPrevious: !0, endpoints: {}, storage: "local", credentials: {}, credentialEnv: {}, proxy: "", noProxy: "" },
|
|
17
58
|
requires: [SessionEnvironment],
|
|
59
|
+
optional: [HostCredentials, ModelsTransport],
|
|
18
60
|
provides: [LanguageModel.LanguageModel, UtilityLlm, AuthStore, SettingsStore, ModelCatalog],
|
|
19
61
|
layer: (config) => Layer.unwrap(Effect.gen(function* () {
|
|
20
|
-
const { workspace } = yield* SessionEnvironment,
|
|
21
|
-
load: Effect.succeed(new EngineSettings({ ...previous, model: Option.orElse(optional(config.model), () => previous.model), fastModel: Option.orElse(optional(config.fastModel), () => previous.fastModel), fallbackModel: Option.orElse(optional(config.fallbackModel), () => previous.fallbackModel) })),
|
|
22
|
-
setRole: () => Effect.fail(new SettingsError({ message: "Edit the models plugin configuration to change a model" })),
|
|
23
|
-
set: () => Effect.fail(new SettingsError({ message: "Edit the models plugin configuration to change settings" }))
|
|
24
|
-
}), base = Layer.mergeAll(settings, LocalAuthStoreLive(workspace, homedir(), ".efferent/runtime", config.inheritPrevious ? ".efferent" : void 0), Layer.succeed(CurrentModelEndpoints, config.endpoints));
|
|
62
|
+
const { workspace } = yield* SessionEnvironment, host = yield* Effect.serviceOption(HostCredentials), stores = yield* Match.value(config.storage).pipe(Match.when("memory", () => Effect.succeed(memoryStores(config))), Match.when("local", () => Option.isSome(host) ? Effect.fail(optionError('the host provides HostCredentials, which needs storage: "memory"')) : Effect.succeed(localStores(config, workspace))), Match.exhaustive), transport = yield* transportLayer(config), base = Layer.mergeAll(stores, Layer.succeed(CurrentModelEndpoints, config.endpoints)).pipe(Layer.provideMerge(transport));
|
|
25
63
|
return Layer.mergeAll(LanguageModelLive, UtilityLlmLive, ConfiguredModelCatalogLive).pipe(Layer.provideMerge(base));
|
|
26
64
|
}))
|
|
27
65
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
import type { Effect, Option } from "effect";
|
|
3
|
+
import type { AuthError, ProviderId } from "@xandreed/core";
|
|
4
|
+
import type { CredentialInput } from "../auth/credential-input.entity.js";
|
|
5
|
+
declare const HostCredentials_base: Context.ServiceClass<HostCredentials, "@xandreed/plugin-models/HostCredentials", {
|
|
6
|
+
readonly load: Effect.Effect<ReadonlyMap<string, CredentialInput>, AuthError>;
|
|
7
|
+
readonly save: (provider: ProviderId, credential: Option.Option<CredentialInput>) => Effect.Effect<void, AuthError>;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Credentials a host keeps itself (a secrets manager, a database, its own
|
|
11
|
+
* session store), for the models plugin with `storage: "memory"`. `load` is
|
|
12
|
+
* read once, when the plugin starts; `save` is called after every change the
|
|
13
|
+
* AuthStore makes — a login (`set`), a logout (`None`), an OAuth token
|
|
14
|
+
* refreshed in memory — so the host can keep the new value. Secrets cross
|
|
15
|
+
* this port `Redacted`. A failed `save` fails a login or logout (memory is
|
|
16
|
+
* left as it was) but not a refresh: the refreshed token is already the
|
|
17
|
+
* only valid one, so it stays in memory and the failure is logged.
|
|
18
|
+
*/
|
|
19
|
+
export declare class HostCredentials extends HostCredentials_base {
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -5,28 +5,41 @@ import { Context, Schema } from "effect";
|
|
|
5
5
|
* optional — a local server or a private gateway may need none. It comes
|
|
6
6
|
* from `apiKey`, then the environment variable `apiKeyEnv` names, then the
|
|
7
7
|
* AuthStore's key for `<name>`; with none, no Authorization header is sent.
|
|
8
|
+
* An endpoint with its own key (`apiKey`, or a set `apiKeyEnv`) never reads
|
|
9
|
+
* the AuthStore; an empty one (`apiKey: ""`, or a variable set to "") sends
|
|
10
|
+
* no key. A host's code passes `apiKey` as a `Redacted` string so it
|
|
11
|
+
* prints as `<redacted>` in errors and graph fingerprints; JSON
|
|
12
|
+
* configuration names an environment variable in `apiKeyEnv` instead.
|
|
8
13
|
* `standardOnly` (default true) sends only standard chat-completions fields.
|
|
14
|
+
* `images` (default false) says its models read images: a user's images are
|
|
15
|
+
* then sent as `image_url` parts, and a tool result's in one user message
|
|
16
|
+
* after the tool results; without it they are label lines.
|
|
17
|
+
* The endpoint's usage is read in full: cache reads and writes from
|
|
18
|
+
* `prompt_tokens_details`, reasoning from `completion_tokens_details`.
|
|
9
19
|
*/
|
|
10
20
|
export declare const ModelEndpoint: Schema.Struct<{
|
|
11
21
|
readonly url: Schema.String;
|
|
12
|
-
readonly apiKey: Schema.optionalKey<Schema.String
|
|
22
|
+
readonly apiKey: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.Redacted<Schema.String>]>>;
|
|
13
23
|
readonly apiKeyEnv: Schema.optionalKey<Schema.String>;
|
|
14
24
|
readonly standardOnly: Schema.optionalKey<Schema.Boolean>;
|
|
25
|
+
readonly images: Schema.optionalKey<Schema.Boolean>;
|
|
15
26
|
}>;
|
|
16
27
|
export type ModelEndpoint = typeof ModelEndpoint.Type;
|
|
17
28
|
/** Endpoints by provider name; a name that matches a built-in provider replaces it. */
|
|
18
29
|
export declare const ModelEndpoints: Schema.$Record<Schema.String, Schema.Struct<{
|
|
19
30
|
readonly url: Schema.String;
|
|
20
|
-
readonly apiKey: Schema.optionalKey<Schema.String
|
|
31
|
+
readonly apiKey: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.Redacted<Schema.String>]>>;
|
|
21
32
|
readonly apiKeyEnv: Schema.optionalKey<Schema.String>;
|
|
22
33
|
readonly standardOnly: Schema.optionalKey<Schema.Boolean>;
|
|
34
|
+
readonly images: Schema.optionalKey<Schema.Boolean>;
|
|
23
35
|
}>>;
|
|
24
36
|
export type ModelEndpoints = typeof ModelEndpoints.Type;
|
|
25
37
|
/** The endpoints the models plugin was configured with; none by default. */
|
|
26
38
|
export declare const CurrentModelEndpoints: Context.Reference<{
|
|
27
39
|
readonly [x: string]: {
|
|
28
40
|
readonly url: string;
|
|
29
|
-
readonly
|
|
41
|
+
readonly images?: boolean;
|
|
42
|
+
readonly apiKey?: string | import("effect/Redacted").Redacted<string>;
|
|
30
43
|
readonly apiKeyEnv?: string;
|
|
31
44
|
readonly standardOnly?: boolean;
|
|
32
45
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { Context, Schema } from "effect";
|
|
2
2
|
export const ModelEndpoint = Schema.Struct({
|
|
3
3
|
url: Schema.String,
|
|
4
|
-
apiKey: Schema.optionalKey(Schema.
|
|
4
|
+
apiKey: Schema.optionalKey(Schema.Union([
|
|
5
|
+
Schema.String,
|
|
6
|
+
Schema.Redacted(Schema.String).annotate({ message: "apiKey is a string or a Redacted string" })
|
|
7
|
+
]).annotate({ message: "apiKey is a string or a Redacted string" })),
|
|
5
8
|
apiKeyEnv: Schema.optionalKey(Schema.String),
|
|
6
|
-
standardOnly: Schema.optionalKey(Schema.Boolean)
|
|
7
|
-
|
|
9
|
+
standardOnly: Schema.optionalKey(Schema.Boolean),
|
|
10
|
+
images: Schema.optionalKey(Schema.Boolean)
|
|
11
|
+
}).annotate({ messageUnexpectedKey: "not an option of a model endpoint" }), ModelEndpoints = Schema.Record(Schema.String, ModelEndpoint), CurrentModelEndpoints = Context.Reference("@xandreed/plugin-models/CurrentModelEndpoints", {
|
|
8
12
|
defaultValue: () => ({})
|
|
9
13
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
import type { Effect, Option, Redacted } from "effect";
|
|
3
|
+
import type { Agent } from "node:http";
|
|
4
|
+
import type { ProxyUnsupported } from "../transport/http-proxy.entity.js";
|
|
5
|
+
/**
|
|
6
|
+
* A fetch for the models plugin's HTTP calls: a URL (string or `URL`) and a
|
|
7
|
+
* `RequestInit`, as the provider clients, the OpenAI-compatible client and
|
|
8
|
+
* the OAuth token requests call it. Any `fetch` is one.
|
|
9
|
+
*/
|
|
10
|
+
export type ModelsFetch = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
11
|
+
/**
|
|
12
|
+
* How a WebSocket reaches its target. `Agent` is for Node only: the agent
|
|
13
|
+
* the `ws` client connects with (one that tunnels through the proxy with
|
|
14
|
+
* CONNECT, or a plain one for a direct connection). `Proxy` is for Bun only:
|
|
15
|
+
* the proxy URL Bun's own `WebSocket` takes as its `proxy` option. A route
|
|
16
|
+
* the runtime cannot use (`Proxy` outside Bun, `Agent` on Bun, whose `ws`
|
|
17
|
+
* stand-in takes neither) fails the call rather than connecting past the
|
|
18
|
+
* proxy.
|
|
19
|
+
*/
|
|
20
|
+
export type WebSocketProxy = {
|
|
21
|
+
readonly _tag: "Agent";
|
|
22
|
+
readonly agent: Agent;
|
|
23
|
+
} | {
|
|
24
|
+
readonly _tag: "Proxy";
|
|
25
|
+
readonly url: Redacted.Redacted<string>;
|
|
26
|
+
};
|
|
27
|
+
declare const ModelsTransport_base: Context.ServiceClass<ModelsTransport, "@xandreed/plugin-models/ModelsTransport", {
|
|
28
|
+
readonly fetch: ModelsFetch;
|
|
29
|
+
readonly webSocketAgent?: (target: URL) => Effect.Effect<Option.Option<WebSocketProxy>, ProxyUnsupported>;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* The network path of the models plugin: every HTTP call it makes (the
|
|
33
|
+
* provider SDKs, the OpenAI-compatible client, OAuth token requests) goes
|
|
34
|
+
* through `fetch`, and the OpenAI subscription's WebSocket asks
|
|
35
|
+
* `webSocketAgent` how to reach its target: `None` connects the runtime's
|
|
36
|
+
* default way (direct, unless the runtime applies an environment proxy
|
|
37
|
+
* itself, as Node's global agents do under `NODE_USE_ENV_PROXY`),
|
|
38
|
+
* `ProxyUnsupported` refuses (a proxy applies that this runtime cannot
|
|
39
|
+
* tunnel a WebSocket through). Without `webSocketAgent`, WebSockets connect
|
|
40
|
+
* the default way. `ModelsTransportLive` builds one from a proxy setting and
|
|
41
|
+
* the environment; a host provides its own (a fetch with client
|
|
42
|
+
* certificates, say) through `Agent.define({ services })`.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ModelsTransport extends ModelsTransport_base {
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
@@ -1,33 +1,12 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { Effect, Layer, Option } from "effect";
|
|
4
|
-
import { asJsonRecord,
|
|
4
|
+
import { asJsonRecord, parseJsonWarn, SettingsError, SettingsStore } from "@xandreed/core";
|
|
5
|
+
import { coerceSettingValue, ROLE_FIELDS, roleSelection, settingsFromRecord } from "./settings-value.entity.functions.js";
|
|
5
6
|
const configPaths = (cwd, home) => [
|
|
6
7
|
join(home, ".efferent", "config.json"),
|
|
7
8
|
join(cwd, ".efferent", "config.json")
|
|
8
|
-
], readConfig = (path) => Effect.tryPromise({ try: () => readFile(path, "utf-8"), catch: () => "missing" }).pipe(Effect.flatMap((text) => parseJsonWarn(text, path)), Effect.map(asJsonRecord), Effect.orElseSucceed(() => ({}))),
|
|
9
|
-
if (key === "fallbackModel")
|
|
10
|
-
return Option.match(parseModelSelection(raw), {
|
|
11
|
-
onNone: () => Effect.fail(new SettingsError({
|
|
12
|
-
message: `fallbackModel must be "<provider>:<modelId>" \u2014 got "${raw}"`
|
|
13
|
-
})),
|
|
14
|
-
onSome: () => Effect.succeed(raw)
|
|
15
|
-
});
|
|
16
|
-
if (key === "sandbox" || key === "viMode") {
|
|
17
|
-
if (raw === "true" || raw === "on")
|
|
18
|
-
return Effect.succeed(!0);
|
|
19
|
-
if (raw === "false" || raw === "off")
|
|
20
|
-
return Effect.succeed(!1);
|
|
21
|
-
return Effect.fail(new SettingsError({ message: `${key} must be true/false \u2014 got "${raw}"` }));
|
|
22
|
-
}
|
|
23
|
-
if (key === "reasoningEffort" || key === "codeReasoningEffort" || key === "fastReasoningEffort")
|
|
24
|
-
return Option.match(asReasoningEffort(raw), {
|
|
25
|
-
onNone: () => Effect.fail(new SettingsError({ message: `reasoningEffort is not supported: "${raw}"` })),
|
|
26
|
-
onSome: Effect.succeed
|
|
27
|
-
});
|
|
28
|
-
const parsed = Number(raw);
|
|
29
|
-
return Number.isInteger(parsed) && parsed >= 1 ? Effect.succeed(parsed) : Effect.fail(new SettingsError({ message: `${key} must be a positive integer \u2014 got "${raw}"` }));
|
|
30
|
-
}, writeKey = (cwd, key, value) => Effect.gen(function* () {
|
|
9
|
+
], readConfig = (path) => Effect.tryPromise({ try: () => readFile(path, "utf-8"), catch: () => "missing" }).pipe(Effect.flatMap((text) => parseJsonWarn(text, path)), Effect.map(asJsonRecord), Effect.orElseSucceed(() => ({}))), writeKey = (cwd, key, value) => Effect.gen(function* () {
|
|
31
10
|
const path = join(cwd, ".efferent", "config.json"), current = yield* readConfig(path), next = Option.match(value, {
|
|
32
11
|
onNone: () => Object.fromEntries(Object.entries(current).filter(([k]) => k !== key)),
|
|
33
12
|
onSome: (v) => ({ ...current, [key]: v })
|
|
@@ -35,33 +14,14 @@ const configPaths = (cwd, home) => [
|
|
|
35
14
|
yield* writeConfigAtomic(path, next);
|
|
36
15
|
});
|
|
37
16
|
export const LocalSettingsStoreLive = (cwd, home) => Layer.succeed(SettingsStore, {
|
|
38
|
-
load: Effect.
|
|
39
|
-
|
|
40
|
-
return new EngineSettings({
|
|
41
|
-
model: asString(merged.model),
|
|
42
|
-
codeModel: asString(merged.codeModel),
|
|
43
|
-
fastModel: asString(merged.fastModel),
|
|
44
|
-
fallbackModel: asString(merged.fallbackModel),
|
|
45
|
-
sandbox: asBoolean(merged.sandbox),
|
|
46
|
-
viMode: asBoolean(merged.viMode),
|
|
47
|
-
maxAttempts: asNumber(merged.maxAttempts),
|
|
48
|
-
budgetMillis: asNumber(merged.budgetMillis),
|
|
49
|
-
reasoningEffort: Option.orElse(asReasoningEffort(merged.reasoningEffort), () => asReasoningEffort(merged.openAiReasoningEffort)),
|
|
50
|
-
codeReasoningEffort: asReasoningEffort(merged.codeReasoningEffort),
|
|
51
|
-
fastReasoningEffort: asReasoningEffort(merged.fastReasoningEffort)
|
|
52
|
-
});
|
|
53
|
-
}),
|
|
54
|
-
setRole: (role, selection) => writeKey(cwd, ROLE_KEYS[role], selection),
|
|
17
|
+
load: Effect.forEach(configPaths(cwd, home), readConfig).pipe(Effect.map((configs) => settingsFromRecord(configs.reduce((acc, c) => ({ ...acc, ...c }), {})))),
|
|
18
|
+
setRole: (role, selection) => roleSelection(role, selection).pipe(Effect.flatMap((checked) => writeKey(cwd, ROLE_FIELDS[role], checked))),
|
|
55
19
|
set: (key, value) => Option.match(value, {
|
|
56
20
|
onNone: () => writeKey(cwd, key, Option.none()),
|
|
57
21
|
onSome: (raw) => coerceSettingValue(key, raw).pipe(Effect.flatMap((coerced) => writeKey(cwd, key, Option.some(coerced))))
|
|
58
22
|
})
|
|
59
23
|
});
|
|
60
|
-
const
|
|
61
|
-
general: "model",
|
|
62
|
-
code: "codeModel",
|
|
63
|
-
fast: "fastModel"
|
|
64
|
-
}, writeConfigAtomic = (path, config) => Effect.tryPromise({
|
|
24
|
+
const writeConfigAtomic = (path, config) => Effect.tryPromise({
|
|
65
25
|
try: async () => {
|
|
66
26
|
await mkdir(dirname(path), { recursive: !0 });
|
|
67
27
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { EngineSettings, SettingsStore } from "@xandreed/core";
|
|
3
|
+
/**
|
|
4
|
+
* `MemorySettingsStoreLive(initial)` — a SettingsStore that never touches
|
|
5
|
+
* disk: one writable value for the layer's lifetime, starting at `initial`.
|
|
6
|
+
* `setRole` and `set` check their value the way the file store does
|
|
7
|
+
* (a `"<provider>:<modelId>"` selection; each key's string form) and refuse
|
|
8
|
+
* one that does not parse, leaving the settings as they were.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MemorySettingsStoreLive: (initial?: EngineSettings) => Layer.Layer<SettingsStore>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect, Layer, SynchronizedRef } from "effect";
|
|
2
|
+
import { EngineSettings, SettingsStore } from "@xandreed/core";
|
|
3
|
+
import { applyRole, applySetting } from "./settings-value.entity.functions.js";
|
|
4
|
+
export const MemorySettingsStoreLive = (initial = new EngineSettings({})) => Layer.effect(SettingsStore, Effect.gen(function* () {
|
|
5
|
+
const state = yield* SynchronizedRef.make(initial);
|
|
6
|
+
return SettingsStore.of({
|
|
7
|
+
load: SynchronizedRef.get(state),
|
|
8
|
+
setRole: (role, selection) => SynchronizedRef.updateEffect(state, (settings) => applyRole(settings, role, selection)),
|
|
9
|
+
set: (key, value) => SynchronizedRef.updateEffect(state, (settings) => applySetting(settings, key, value))
|
|
10
|
+
});
|
|
11
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** A settings value as a config file holds it: a selection or other text, a switch, a count. */
|
|
3
|
+
export declare const SettingValue: Schema.Union<readonly [Schema.String, Schema.Boolean, Schema.Number]>;
|
|
4
|
+
export type SettingValue = typeof SettingValue.Type;
|
|
5
|
+
/** The settings field each model role is kept under. */
|
|
6
|
+
export declare const RoleField: Schema.Literals<readonly ["model", "codeModel", "fastModel"]>;
|
|
7
|
+
export type RoleField = typeof RoleField.Type;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { EngineSettings, SettingsError } from "@xandreed/core";
|
|
3
|
+
import type { ModelRole, SettingsKey } from "@xandreed/core";
|
|
4
|
+
import type { RoleField, SettingValue } from "./settings-value.entity.js";
|
|
5
|
+
/**
|
|
6
|
+
* The settings vocabulary every SettingsStore of this package shares: how a
|
|
7
|
+
* config record reads as `EngineSettings`, how a value's string form (what
|
|
8
|
+
* `set` receives: "true", "3", "provider:model") is checked and coerced, and
|
|
9
|
+
* how one key or role changes a settings value.
|
|
10
|
+
*/
|
|
11
|
+
/** The field each model role is kept under. */
|
|
12
|
+
export declare const ROLE_FIELDS: Readonly<Record<ModelRole, RoleField>>;
|
|
13
|
+
/** Settings from a config record: values of the wrong type read as unset; the legacy `openAiReasoningEffort` key backs `reasoningEffort`. */
|
|
14
|
+
export declare const settingsFromRecord: (record: Readonly<Record<string, unknown>>) => EngineSettings;
|
|
15
|
+
/** Settings as a config record: the set fields only. */
|
|
16
|
+
export declare const recordOfSettings: (settings: EngineSettings) => Readonly<Record<string, SettingValue>>;
|
|
17
|
+
/**
|
|
18
|
+
* One key's value from its string form, checked: a selection must be
|
|
19
|
+
* `"<provider>:<modelId>"`, a switch true/on/false/off, an effort one the
|
|
20
|
+
* models accept, a count a positive integer. A value that does not parse is
|
|
21
|
+
* refused, not written and then ignored on load.
|
|
22
|
+
*/
|
|
23
|
+
export declare const coerceSettingValue: (key: SettingsKey, raw: string) => Effect.Effect<SettingValue, SettingsError>;
|
|
24
|
+
/** A role's selection, checked: `None` clears the role; a value must be `"<provider>:<modelId>"`. */
|
|
25
|
+
export declare const roleSelection: (role: ModelRole, selection: Option.Option<string>) => Effect.Effect<Option.Option<string>, SettingsError>;
|
|
26
|
+
/** `settings` with one key set from its string form (checked, as `set` takes it), or cleared with `None`. */
|
|
27
|
+
export declare const applySetting: (settings: EngineSettings, key: SettingsKey, value: Option.Option<string>) => Effect.Effect<EngineSettings, SettingsError>;
|
|
28
|
+
/** `settings` with one role's selection set (checked, as `setRole` takes it), or cleared with `None`. */
|
|
29
|
+
export declare const applyRole: (settings: EngineSettings, role: ModelRole, selection: Option.Option<string>) => Effect.Effect<EngineSettings, SettingsError>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Effect, Match, Option } from "effect";
|
|
2
|
+
import { EngineSettings, parseModelSelection, SETTINGS_KEYS, SettingsError } from "@xandreed/core";
|
|
3
|
+
export const ROLE_FIELDS = {
|
|
4
|
+
general: "model",
|
|
5
|
+
code: "codeModel",
|
|
6
|
+
fast: "fastModel"
|
|
7
|
+
};
|
|
8
|
+
const FIELDS = ["model", "codeModel", "fastModel", ...SETTINGS_KEYS], asString = (value) => typeof value === "string" && value.length > 0 ? Option.some(value) : Option.none(), asBoolean = (value) => typeof value === "boolean" ? Option.some(value) : Option.none(), asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? Option.some(value) : Option.none(), asReasoningEffort = (value) => value === "none" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? Option.some(value) : Option.none();
|
|
9
|
+
export const settingsFromRecord = (record) => new EngineSettings({
|
|
10
|
+
model: asString(record.model),
|
|
11
|
+
codeModel: asString(record.codeModel),
|
|
12
|
+
fastModel: asString(record.fastModel),
|
|
13
|
+
fallbackModel: asString(record.fallbackModel),
|
|
14
|
+
sandbox: asBoolean(record.sandbox),
|
|
15
|
+
viMode: asBoolean(record.viMode),
|
|
16
|
+
maxAttempts: asNumber(record.maxAttempts),
|
|
17
|
+
budgetMillis: asNumber(record.budgetMillis),
|
|
18
|
+
reasoningEffort: Option.orElse(asReasoningEffort(record.reasoningEffort), () => asReasoningEffort(record.openAiReasoningEffort)),
|
|
19
|
+
codeReasoningEffort: asReasoningEffort(record.codeReasoningEffort),
|
|
20
|
+
fastReasoningEffort: asReasoningEffort(record.fastReasoningEffort)
|
|
21
|
+
}), recordOfSettings = (settings) => Object.fromEntries(FIELDS.flatMap((field) => Option.match(fieldOf(settings, field), {
|
|
22
|
+
onNone: () => [],
|
|
23
|
+
onSome: (value) => [[field, value]]
|
|
24
|
+
})));
|
|
25
|
+
const fieldOf = (settings, field) => settings[field], refuse = (message) => Effect.fail(new SettingsError({ message }));
|
|
26
|
+
export const coerceSettingValue = (key, raw) => Match.value(key).pipe(Match.when("fallbackModel", () => Option.match(parseModelSelection(raw), {
|
|
27
|
+
onNone: () => refuse(`fallbackModel must be "<provider>:<modelId>" \u2014 got "${raw}"`),
|
|
28
|
+
onSome: () => Effect.succeed(raw)
|
|
29
|
+
})), Match.whenOr("sandbox", "viMode", (flag) => raw === "true" || raw === "on" ? Effect.succeed(!0) : raw === "false" || raw === "off" ? Effect.succeed(!1) : refuse(`${flag} must be true/false \u2014 got "${raw}"`)), Match.whenOr("reasoningEffort", "codeReasoningEffort", "fastReasoningEffort", (effort) => Option.match(asReasoningEffort(raw), {
|
|
30
|
+
onNone: () => refuse(`${effort} is not supported: "${raw}"`),
|
|
31
|
+
onSome: (value) => Effect.succeed(value)
|
|
32
|
+
})), Match.whenOr("maxAttempts", "budgetMillis", (count) => Number.isInteger(Number(raw)) && Number(raw) >= 1 ? Effect.succeed(Number(raw)) : refuse(`${count} must be a positive integer \u2014 got "${raw}"`)), Match.exhaustive), roleSelection = (role, selection) => Option.match(selection, {
|
|
33
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
34
|
+
onSome: (raw) => Option.isSome(parseModelSelection(raw)) ? Effect.succeed(Option.some(raw)) : refuse(`the ${role} model must be "<provider>:<modelId>" \u2014 got "${raw}"`)
|
|
35
|
+
});
|
|
36
|
+
const withField = (settings, field, value) => {
|
|
37
|
+
const others = Object.fromEntries(Object.entries(recordOfSettings(settings)).filter(([key]) => key !== field));
|
|
38
|
+
return settingsFromRecord(Option.match(value, { onNone: () => others, onSome: (set) => ({ ...others, [field]: set }) }));
|
|
39
|
+
};
|
|
40
|
+
export const applySetting = (settings, key, value) => Option.match(value, {
|
|
41
|
+
onNone: () => Effect.succeed(withField(settings, key, Option.none())),
|
|
42
|
+
onSome: (raw) => coerceSettingValue(key, raw).pipe(Effect.map((coerced) => withField(settings, key, Option.some(coerced))))
|
|
43
|
+
}), applyRole = (settings, role, selection) => roleSelection(role, selection).pipe(Effect.map((checked) => withField(settings, ROLE_FIELDS[role], checked)));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** A proxy URL as an option takes it: a string, or a `Redacted` one when it carries credentials. */
|
|
3
|
+
export declare const ProxyUrlOption: Schema.Union<readonly [Schema.String, Schema.Redacted<Schema.String>]>;
|
|
4
|
+
export type ProxyUrlOption = typeof ProxyUrlOption.Type;
|
|
5
|
+
/**
|
|
6
|
+
* The proxy a host gives the models plugin, as its `proxy` and `noProxy`
|
|
7
|
+
* options. `proxy` is one URL for every model and OAuth call (`http://` or
|
|
8
|
+
* `https://`; a value without a scheme reads as `http://`); it may carry
|
|
9
|
+
* credentials (`http://user:pass@proxy:8080`), so a host's code can pass it
|
|
10
|
+
* `Redacted`. Empty or absent, the environment's `https_proxy` /
|
|
11
|
+
* `HTTPS_PROXY` and `http_proxy` / `HTTP_PROXY` apply. `noProxy` lists the
|
|
12
|
+
* hosts that go direct, in NO_PROXY syntax, added to the environment's
|
|
13
|
+
* `no_proxy` / `NO_PROXY`; `"*"` turns the proxy off.
|
|
14
|
+
*/
|
|
15
|
+
export declare const HttpProxyOptions: Schema.Struct<{
|
|
16
|
+
readonly proxy: Schema.optionalKey<Schema.Union<readonly [Schema.String, Schema.Redacted<Schema.String>]>>;
|
|
17
|
+
readonly noProxy: Schema.optionalKey<Schema.String>;
|
|
18
|
+
}>;
|
|
19
|
+
export type HttpProxyOptions = typeof HttpProxyOptions.Type;
|
|
20
|
+
/**
|
|
21
|
+
* The proxy variables of the environment, as read (an empty value counts as
|
|
22
|
+
* unset). Lower case is read before upper case, as undici (Node's
|
|
23
|
+
* environment proxy) and curl read them. `ALL_PROXY` is not read: SOCKS
|
|
24
|
+
* proxies are not supported.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ProxyEnvironment: Schema.Struct<{
|
|
27
|
+
readonly https_proxy: Schema.optionalKey<Schema.String>;
|
|
28
|
+
readonly HTTPS_PROXY: Schema.optionalKey<Schema.String>;
|
|
29
|
+
readonly http_proxy: Schema.optionalKey<Schema.String>;
|
|
30
|
+
readonly HTTP_PROXY: Schema.optionalKey<Schema.String>;
|
|
31
|
+
readonly no_proxy: Schema.optionalKey<Schema.String>;
|
|
32
|
+
readonly NO_PROXY: Schema.optionalKey<Schema.String>;
|
|
33
|
+
}>;
|
|
34
|
+
export type ProxyEnvironment = typeof ProxyEnvironment.Type;
|
|
35
|
+
/** The environment variables a `ProxyEnvironment` is read from. */
|
|
36
|
+
export declare const PROXY_VARIABLES: readonly ["https_proxy", "HTTPS_PROXY", "http_proxy", "HTTP_PROXY", "no_proxy", "NO_PROXY"];
|
|
37
|
+
/**
|
|
38
|
+
* One NO_PROXY entry: every host (`*`); a host name and its subdomains
|
|
39
|
+
* (`example.com`, `.example.com` and `*.example.com` alike); or an IP
|
|
40
|
+
* address, exact (`10.1.2.3`, `::1`, `[::1]`) or a range (`10.0.0.0/8`,
|
|
41
|
+
* `fd00::/8`, as `prefix` leading bits). A name or an exact address may
|
|
42
|
+
* carry a port (`example.com:8443`, `[::1]:8080`), which then must match.
|
|
43
|
+
*/
|
|
44
|
+
export declare const NoProxyRule: Schema.Union<readonly [Schema.TaggedStruct<"Everything", {}>, Schema.TaggedStruct<"Domain", {
|
|
45
|
+
readonly name: Schema.String;
|
|
46
|
+
readonly port: Schema.Option<Schema.Int>;
|
|
47
|
+
}>, Schema.TaggedStruct<"Address", {
|
|
48
|
+
readonly bytes: Schema.$Array<Schema.Int>;
|
|
49
|
+
readonly prefix: Schema.Int;
|
|
50
|
+
readonly port: Schema.Option<Schema.Int>;
|
|
51
|
+
}>]>;
|
|
52
|
+
export type NoProxyRule = typeof NoProxyRule.Type;
|
|
53
|
+
declare const ProxyConfig_base: Schema.Class<ProxyConfig, Schema.Struct<{
|
|
54
|
+
readonly https: Schema.Option<Schema.Redacted<Schema.String>>;
|
|
55
|
+
readonly http: Schema.Option<Schema.Redacted<Schema.String>>;
|
|
56
|
+
readonly noProxy: Schema.$Array<Schema.Union<readonly [Schema.TaggedStruct<"Everything", {}>, Schema.TaggedStruct<"Domain", {
|
|
57
|
+
readonly name: Schema.String;
|
|
58
|
+
readonly port: Schema.Option<Schema.Int>;
|
|
59
|
+
}>, Schema.TaggedStruct<"Address", {
|
|
60
|
+
readonly bytes: Schema.$Array<Schema.Int>;
|
|
61
|
+
readonly prefix: Schema.Int;
|
|
62
|
+
readonly port: Schema.Option<Schema.Int>;
|
|
63
|
+
}>]>>;
|
|
64
|
+
}>, {}>;
|
|
65
|
+
/**
|
|
66
|
+
* A resolved proxy setting: the proxy for `https:` (and `wss:`) targets,
|
|
67
|
+
* the one for `http:` (and `ws:`) targets, and the hosts that go direct.
|
|
68
|
+
* Proxy URLs stay `Redacted`, so a printed setting never shows credentials.
|
|
69
|
+
* Loopback hosts always go direct.
|
|
70
|
+
*/
|
|
71
|
+
export declare class ProxyConfig extends ProxyConfig_base {
|
|
72
|
+
}
|
|
73
|
+
declare const ProxyInvalid_base: Schema.Class<ProxyInvalid, Schema.TaggedStruct<"ProxyInvalid", {
|
|
74
|
+
readonly source: Schema.String;
|
|
75
|
+
readonly message: Schema.String;
|
|
76
|
+
}>, import("effect/Cause").YieldableError>;
|
|
77
|
+
/** A proxy URL that cannot be used: not a URL, or not `http://` / `https://`. `source` names where it came from. */
|
|
78
|
+
export declare class ProxyInvalid extends ProxyInvalid_base {
|
|
79
|
+
}
|
|
80
|
+
declare const ProxyUnsupported_base: Schema.Class<ProxyUnsupported, Schema.TaggedStruct<"ProxyUnsupported", {
|
|
81
|
+
readonly message: Schema.String;
|
|
82
|
+
}>, import("effect/Cause").YieldableError>;
|
|
83
|
+
/** A proxy applies, but this runtime cannot send the call through it: the call is refused rather than sent direct. */
|
|
84
|
+
export declare class ProxyUnsupported extends ProxyUnsupported_base {
|
|
85
|
+
}
|
|
86
|
+
/** The first Bun whose native `WebSocket` sends through its `proxy` option (1.3.0 to 1.3.5 connect directly). */
|
|
87
|
+
export declare const BUN_WEBSOCKET_PROXY_VERSION = "1.3.6";
|
|
88
|
+
/** The first Node whose `https.Agent` tunnels through its `proxyEnv` option (earlier ones connect directly). */
|
|
89
|
+
export declare const NODE_WEBSOCKET_PROXY_VERSION = "24.5.0";
|
|
90
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Option, Redacted, Result } from "effect";
|
|
2
|
+
import { ProxyConfig, ProxyInvalid } from "./http-proxy.entity.js";
|
|
3
|
+
import type { HttpProxyOptions, NoProxyRule, ProxyEnvironment } from "./http-proxy.entity.js";
|
|
4
|
+
/**
|
|
5
|
+
* Credentials in the URLs of `text` (`scheme://user:pass@host`) replaced by
|
|
6
|
+
* `<redacted>`, for error messages that may quote a proxy URL.
|
|
7
|
+
*/
|
|
8
|
+
export declare const redactCredentials: (text: string) => string;
|
|
9
|
+
/** A proxy URL as a message may show it: its scheme and host, its credentials (if any) as `<redacted>`. */
|
|
10
|
+
export declare const displayProxy: (url: Redacted.Redacted<string>) => string;
|
|
11
|
+
/**
|
|
12
|
+
* One proxy URL, checked: `None` when blank; a value without a scheme
|
|
13
|
+
* (`proxy.corp:8080`) reads as `http://`; anything that is not an `http://`
|
|
14
|
+
* or `https://` URL with a host fails with `ProxyInvalid` naming `source`,
|
|
15
|
+
* credentials redacted.
|
|
16
|
+
*/
|
|
17
|
+
export declare const proxyUrl: (raw: string, source: string) => Result.Result<Option.Option<Redacted.Redacted<string>>, ProxyInvalid>;
|
|
18
|
+
/** The IPv4 address `text` spells, as its 4 bytes. */
|
|
19
|
+
export declare const parseIpv4: (text: string) => Option.Option<ReadonlyArray<number>>;
|
|
20
|
+
/** The IPv6 address `text` spells (brackets and a zone id allowed), as its 16 bytes. */
|
|
21
|
+
export declare const parseIpv6: (text: string) => Option.Option<ReadonlyArray<number>>;
|
|
22
|
+
/** A host is loopback: `localhost` (and its subdomains), `127.0.0.0/8`, `::1`, or IPv4 loopback mapped into IPv6. */
|
|
23
|
+
export declare const isLoopback: (hostname: string) => boolean;
|
|
24
|
+
/** A NO_PROXY value (entries separated by commas or white space), parsed. */
|
|
25
|
+
export declare const parseNoProxy: (value: string) => ReadonlyArray<NoProxyRule>;
|
|
26
|
+
/** Whether a NO_PROXY rule sends `target` direct. */
|
|
27
|
+
export declare const bypassesProxy: (rules: ReadonlyArray<NoProxyRule>, target: URL) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The proxy settings of the models plugin's options over the environment's:
|
|
30
|
+
* an explicit `proxy` serves every target; otherwise `https_proxy` (then
|
|
31
|
+
* `HTTPS_PROXY`) serves `https:` targets, `http_proxy` (then `HTTP_PROXY`)
|
|
32
|
+
* `http:` ones, and an `https:` target falls back to the `http:` proxy, as
|
|
33
|
+
* undici's environment proxy (Node's, under `NODE_USE_ENV_PROXY`) does. The
|
|
34
|
+
* direct hosts are `no_proxy` (then `NO_PROXY`) and the `noProxy` option
|
|
35
|
+
* together; with `*` among them the environment's proxy variables are not
|
|
36
|
+
* read at all. Lower case comes first, as undici and curl read them. A proxy
|
|
37
|
+
* URL that cannot be used fails with `ProxyInvalid`.
|
|
38
|
+
*/
|
|
39
|
+
export declare const resolveProxy: (options: HttpProxyOptions, env: ProxyEnvironment) => Result.Result<ProxyConfig, ProxyInvalid>;
|
|
40
|
+
/** Whether any target can go through a proxy. */
|
|
41
|
+
export declare const usesProxy: (config: ProxyConfig) => boolean;
|
|
42
|
+
/** Whether the setting turns the proxy off for every host (`*` in `noProxy` or `NO_PROXY`). */
|
|
43
|
+
export declare const turnsProxyOff: (config: ProxyConfig) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The proxy a request to `target` goes through, or `None` for direct:
|
|
46
|
+
* loopback hosts and the `noProxy` hosts go direct; `https:` and `wss:`
|
|
47
|
+
* targets use the https proxy, `http:` and `ws:` ones the http proxy.
|
|
48
|
+
*/
|
|
49
|
+
export declare const proxyFor: (target: URL, config: ProxyConfig) => Option.Option<Redacted.Redacted<string>>;
|
|
50
|
+
/** Whether a runtime `version` (`24.16.0`, `1.3.6`) is at least `minimum`; an unreadable one is not. */
|
|
51
|
+
export declare const atLeastVersion: (version: string, minimum: string) => boolean;
|