@usine/gateway 0.0.0-stage → 0.2.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/LICENSE +661 -0
- package/README.md +200 -2
- package/dist/admin/api.d.ts +146 -0
- package/dist/admin/api.d.ts.map +1 -0
- package/dist/admin/api.js +105 -0
- package/dist/admin/api.js.map +1 -0
- package/dist/admin/server.d.ts +10 -0
- package/dist/admin/server.d.ts.map +1 -0
- package/dist/admin/server.js +79 -0
- package/dist/admin/server.js.map +1 -0
- package/dist/attachment/attachment.d.ts +84 -0
- package/dist/attachment/attachment.d.ts.map +1 -0
- package/dist/attachment/attachment.js +67 -0
- package/dist/attachment/attachment.js.map +1 -0
- package/dist/attachment/store.d.ts +34 -0
- package/dist/attachment/store.d.ts.map +1 -0
- package/dist/attachment/store.js +133 -0
- package/dist/attachment/store.js.map +1 -0
- package/dist/caller/caller.d.ts +60 -0
- package/dist/caller/caller.d.ts.map +1 -0
- package/dist/caller/caller.js +131 -0
- package/dist/caller/caller.js.map +1 -0
- package/dist/chatgpt/auth-file.d.ts +47 -0
- package/dist/chatgpt/auth-file.d.ts.map +1 -0
- package/dist/chatgpt/auth-file.js +132 -0
- package/dist/chatgpt/auth-file.js.map +1 -0
- package/dist/chatgpt/logins.d.ts +50 -0
- package/dist/chatgpt/logins.d.ts.map +1 -0
- package/dist/chatgpt/logins.js +303 -0
- package/dist/chatgpt/logins.js.map +1 -0
- package/dist/egress/client-hello.d.ts +120 -0
- package/dist/egress/client-hello.d.ts.map +1 -0
- package/dist/egress/client-hello.js +155 -0
- package/dist/egress/client-hello.js.map +1 -0
- package/dist/egress/egress.d.ts +66 -0
- package/dist/egress/egress.d.ts.map +1 -0
- package/dist/egress/egress.js +114 -0
- package/dist/egress/egress.js.map +1 -0
- package/dist/gateway.d.ts +27 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +27 -0
- package/dist/gateway.js.map +1 -0
- package/dist/github/github.d.ts +54 -0
- package/dist/github/github.d.ts.map +1 -0
- package/dist/github/github.js +112 -0
- package/dist/github/github.js.map +1 -0
- package/dist/github/rename.d.ts +30 -0
- package/dist/github/rename.d.ts.map +1 -0
- package/dist/github/rename.js +83 -0
- package/dist/github/rename.js.map +1 -0
- package/dist/llm/llm.d.ts +113 -0
- package/dist/llm/llm.d.ts.map +1 -0
- package/dist/llm/llm.js +144 -0
- package/dist/llm/llm.js.map +1 -0
- package/dist/node/egress-proxy.d.ts +30 -0
- package/dist/node/egress-proxy.d.ts.map +1 -0
- package/dist/node/egress-proxy.js +491 -0
- package/dist/node/egress-proxy.js.map +1 -0
- package/dist/node/environment.d.ts +113 -0
- package/dist/node/environment.d.ts.map +1 -0
- package/dist/node/environment.js +140 -0
- package/dist/node/environment.js.map +1 -0
- package/dist/node/main.d.ts +3 -0
- package/dist/node/main.d.ts.map +1 -0
- package/dist/node/main.js +13 -0
- package/dist/node/main.js.map +1 -0
- package/dist/node/node.d.ts +28 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +81 -0
- package/dist/node/node.js.map +1 -0
- package/dist/node/reload.d.ts +27 -0
- package/dist/node/reload.d.ts.map +1 -0
- package/dist/node/reload.js +170 -0
- package/dist/node/reload.js.map +1 -0
- package/dist/proxy/forward.d.ts +12 -0
- package/dist/proxy/forward.d.ts.map +1 -0
- package/dist/proxy/forward.js +44 -0
- package/dist/proxy/forward.js.map +1 -0
- package/dist/proxy/proxy.d.ts +29 -0
- package/dist/proxy/proxy.d.ts.map +1 -0
- package/dist/proxy/proxy.js +297 -0
- package/dist/proxy/proxy.js.map +1 -0
- package/dist/settings.d.ts +52 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +27 -0
- package/dist/settings.js.map +1 -0
- package/dist/upstream.d.ts +5 -0
- package/dist/upstream.d.ts.map +1 -0
- package/dist/upstream.js +6 -0
- package/dist/upstream.js.map +1 -0
- package/package.json +58 -3
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { GatewayDomain } from "@usine/core/worker";
|
|
2
|
+
import { Config, ConfigProvider, Effect, FileSystem, Layer, Option, Schema } from "effect";
|
|
3
|
+
import { parseEgressAllowlist } from "../egress/egress.js";
|
|
4
|
+
import { GITHUB_ORIGINS } from "../github/github.js";
|
|
5
|
+
import { checkAuthFileWritable, readAuthFile } from "../chatgpt/auth-file.js";
|
|
6
|
+
import { CHATGPT_TOKEN_URL } from "../chatgpt/logins.js";
|
|
7
|
+
import { AnthropicApiKey, AnthropicProvider, AnthropicSubscriptionToken, ChatGptLogin, HostFilePath, LlmProviderName, OpenAiApiKey, OpenAiProvider, } from "../llm/llm.js";
|
|
8
|
+
import { AdminToken, CallerKey, GatewaySettings } from "../settings.js";
|
|
9
|
+
import { UpstreamBase } from "../upstream.js";
|
|
10
|
+
/** The allowlist path if `USINE_GATEWAY_EGRESS_ALLOWLIST` isn't set. */
|
|
11
|
+
export const DEFAULT_EGRESS_ALLOWLIST_PATH = "/etc/usine-gateway/egress-allowlist";
|
|
12
|
+
const Secret = Schema.RedactedFromValue(Schema.NonEmptyString);
|
|
13
|
+
const upstream = (name, fallback) => Config.schema(UpstreamBase, name).pipe(Config.withDefault(UpstreamBase.make(fallback)));
|
|
14
|
+
/** The error when two credentials are set for one provider, such as an API key and a subscription token. */
|
|
15
|
+
export class ConflictingProviderCredentials extends Schema.TaggedError()("ConflictingProviderCredentials", { provider: LlmProviderName, message: Schema.String }) {
|
|
16
|
+
}
|
|
17
|
+
/** The error when a file listed in `USINE_GATEWAY_ENVIRONMENT_FILES` can't be read. */
|
|
18
|
+
export class EnvironmentFileError extends Schema.TaggedError()("EnvironmentFileError", { path: Schema.String, message: Schema.String }) {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the `:`-separated environment files listed in `USINE_GATEWAY_ENVIRONMENT_FILES` in
|
|
22
|
+
* `base`, one `KEY=value` per line, and layers them over `base`. Later files override earlier
|
|
23
|
+
* ones, as with systemd's `EnvironmentFile=`. Without that variable, returns `base` alone.
|
|
24
|
+
*/
|
|
25
|
+
export const readSettingsSource = Effect.fn("GatewayNode.readSettingsSource")(function* (base) {
|
|
26
|
+
const fs = yield* FileSystem.FileSystem;
|
|
27
|
+
const named = yield* Config.option(Config.String("USINE_GATEWAY_ENVIRONMENT_FILES")).parse(base);
|
|
28
|
+
const files = Option.match(named, {
|
|
29
|
+
onNone: () => [],
|
|
30
|
+
onSome: (value) => value.split(":").filter((path) => path !== ""),
|
|
31
|
+
});
|
|
32
|
+
const contents = yield* Effect.forEach(files, (path) => fs
|
|
33
|
+
.readFileString(path)
|
|
34
|
+
.pipe(Effect.mapError((error) => new EnvironmentFileError({ path, message: error.message }))));
|
|
35
|
+
const provider = contents.reduce((below, text) => ConfigProvider.fromDotEnvContents(text).pipe(ConfigProvider.orElse(below)), base);
|
|
36
|
+
return { files, provider };
|
|
37
|
+
});
|
|
38
|
+
/** Returns the credential set for `provider`, or fails if both of its variables are set. */
|
|
39
|
+
const onlyOne = (provider, [firstName, first], [secondName, second]) => Option.isSome(first) && Option.isSome(second)
|
|
40
|
+
? Effect.fail(new ConflictingProviderCredentials({
|
|
41
|
+
provider,
|
|
42
|
+
message: `${firstName} and ${secondName} are both set, and the gateway sends only one ${provider} credential. Unset one of them.`,
|
|
43
|
+
}))
|
|
44
|
+
: Effect.succeed(Option.isSome(first) ? first : second);
|
|
45
|
+
/**
|
|
46
|
+
* The LLM providers and credentials from the settings. A ChatGPT login's auth file is read and
|
|
47
|
+
* parsed, and its directory checked for writing, so an unusable file fails here, before the
|
|
48
|
+
* Gateway listens or a reload applies it.
|
|
49
|
+
*/
|
|
50
|
+
export const llmProviders = Effect.gen(function* () {
|
|
51
|
+
const anthropicKey = yield* Config.option(Config.schema(Secret, "ANTHROPIC_API_KEY"));
|
|
52
|
+
const subscription = yield* Config.option(Config.schema(Secret, "USINE_GATEWAY_ANTHROPIC_SUBSCRIPTION_TOKEN"));
|
|
53
|
+
const openAiKey = yield* Config.option(Config.schema(Secret, "OPENAI_API_KEY"));
|
|
54
|
+
const chatGptAuthFile = yield* Config.option(Config.schema(HostFilePath, "USINE_GATEWAY_CHATGPT_AUTH_FILE"));
|
|
55
|
+
const anthropicUpstream = yield* upstream("USINE_GATEWAY_ANTHROPIC_UPSTREAM", "https://api.anthropic.com");
|
|
56
|
+
const openAiUpstream = yield* upstream("USINE_GATEWAY_OPENAI_UPSTREAM", "https://api.openai.com");
|
|
57
|
+
const chatGptUpstream = yield* upstream("USINE_GATEWAY_CHATGPT_UPSTREAM", "https://chatgpt.com/backend-api/codex");
|
|
58
|
+
const anthropicCredential = yield* onlyOne("anthropic", ["ANTHROPIC_API_KEY", Option.map(anthropicKey, (key) => new AnthropicApiKey({ key }))], [
|
|
59
|
+
"USINE_GATEWAY_ANTHROPIC_SUBSCRIPTION_TOKEN",
|
|
60
|
+
Option.map(subscription, (token) => new AnthropicSubscriptionToken({ token })),
|
|
61
|
+
]);
|
|
62
|
+
const openAiCredential = yield* onlyOne("openai", ["OPENAI_API_KEY", Option.map(openAiKey, (key) => new OpenAiApiKey({ key }))], [
|
|
63
|
+
"USINE_GATEWAY_CHATGPT_AUTH_FILE",
|
|
64
|
+
Option.map(chatGptAuthFile, (authFile) => new ChatGptLogin({ authFile, tokenUrl: UpstreamBase.make(CHATGPT_TOKEN_URL) })),
|
|
65
|
+
]);
|
|
66
|
+
if (Option.isSome(openAiCredential) && openAiCredential.value._tag === "ChatGptLogin") {
|
|
67
|
+
yield* readAuthFile(openAiCredential.value.authFile);
|
|
68
|
+
yield* checkAuthFileWritable(openAiCredential.value.authFile);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
anthropic: Option.map(anthropicCredential, (credential) => new AnthropicProvider({ upstream: anthropicUpstream, credential })),
|
|
72
|
+
openai: Option.map(openAiCredential, (credential) => new OpenAiProvider({
|
|
73
|
+
upstream: credential._tag === "ChatGptLogin" ? chatGptUpstream : openAiUpstream,
|
|
74
|
+
credential,
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
/** Returns each provider's credential kind for a log line, or `none`. */
|
|
79
|
+
const providerCredentials = ({ anthropic, openai }) => ({
|
|
80
|
+
anthropic: Option.match(anthropic, {
|
|
81
|
+
onNone: () => "none",
|
|
82
|
+
onSome: (held) => held.credential._tag,
|
|
83
|
+
}),
|
|
84
|
+
openai: Option.match(openai, { onNone: () => "none", onSome: (held) => held.credential._tag }),
|
|
85
|
+
});
|
|
86
|
+
export const egressAllowlistPath = Config.String("USINE_GATEWAY_EGRESS_ALLOWLIST").pipe(Config.withDefault(DEFAULT_EGRESS_ALLOWLIST_PATH));
|
|
87
|
+
/** Reads and parses the allowlist file at `path`. */
|
|
88
|
+
export const readEgressAllowlist = Effect.fn("GatewayNode.readEgressAllowlist")(function* (path) {
|
|
89
|
+
const fs = yield* FileSystem.FileSystem;
|
|
90
|
+
const text = yield* fs.readFileString(path);
|
|
91
|
+
return yield* Effect.fromResult(parseEgressAllowlist(text));
|
|
92
|
+
});
|
|
93
|
+
const listener = (prefix, host, port) => Config.all({
|
|
94
|
+
host: Config.String(`${prefix}_HOST`).pipe(Config.withDefault(host)),
|
|
95
|
+
port: Config.Port(`${prefix}_PORT`).pipe(Config.withDefault(port)),
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* Listeners from the environment. The proxies default to loopback, so an unconfigured Gateway
|
|
99
|
+
* is unreachable; the VM's unit sets its private address.
|
|
100
|
+
*/
|
|
101
|
+
export const listenersFromEnvironment = Config.all({
|
|
102
|
+
proxy: listener("USINE_GATEWAY_PROXY", "127.0.0.1", 80),
|
|
103
|
+
egress: listener("USINE_GATEWAY_EGRESS", "127.0.0.1", 3128),
|
|
104
|
+
admin: listener("USINE_GATEWAY_ADMIN", "127.0.0.1", 7070),
|
|
105
|
+
});
|
|
106
|
+
export const restartSettings = Config.all({
|
|
107
|
+
domain: Config.schema(GatewayDomain, "USINE_GATEWAY_DOMAIN"),
|
|
108
|
+
adminToken: Config.schema(AdminToken, "USINE_GATEWAY_ADMIN_TOKEN"),
|
|
109
|
+
callerKey: Config.schema(CallerKey, "USINE_GATEWAY_CALLER_KEY"),
|
|
110
|
+
listeners: listenersFromEnvironment,
|
|
111
|
+
});
|
|
112
|
+
const startupSettings = Effect.gen(function* () {
|
|
113
|
+
const { domain, adminToken, callerKey, listeners } = yield* restartSettings;
|
|
114
|
+
const path = yield* egressAllowlistPath;
|
|
115
|
+
const egress = yield* readEgressAllowlist(path);
|
|
116
|
+
yield* Effect.logInfo("Loaded the egress allowlist", { path, rules: egress.rules.length });
|
|
117
|
+
const providers = yield* llmProviders;
|
|
118
|
+
yield* Effect.logInfo("Configured LLM providers", providerCredentials(providers));
|
|
119
|
+
const settings = { domain, github: GITHUB_ORIGINS, providers, egress, adminToken, callerKey };
|
|
120
|
+
return { settings, listeners };
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* Reads the Gateway's settings from `source`. Fails on a missing secret, an invalid value, an
|
|
124
|
+
* unreadable allowlist, or an unusable ChatGPT auth file, so the Gateway stops before it listens.
|
|
125
|
+
*/
|
|
126
|
+
export const readStartupSettings = Effect.fn("GatewayNode.readStartupSettings")(function* (source) {
|
|
127
|
+
yield* Effect.logInfo("Reading the gateway settings", { environmentFiles: source.files });
|
|
128
|
+
return yield* startupSettings.pipe(Effect.provideService(ConfigProvider.ConfigProvider, source.provider));
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* The Gateway's settings, read once at startup from the files in
|
|
132
|
+
* `USINE_GATEWAY_ENVIRONMENT_FILES` over the process environment, plus the egress allowlist
|
|
133
|
+
* file. Fails on a missing secret, an invalid value, or an unreadable or unusable file.
|
|
134
|
+
*/
|
|
135
|
+
export const settingsFromEnvironment = Layer.effect(GatewaySettings, Effect.gen(function* () {
|
|
136
|
+
const source = yield* readSettingsSource(yield* ConfigProvider.ConfigProvider);
|
|
137
|
+
const { settings } = yield* readStartupSettings(source);
|
|
138
|
+
return settings;
|
|
139
|
+
}));
|
|
140
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/node/environment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/node/main.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
3
|
+
import { Cause, Effect, Exit, Layer, Logger, Runtime } from "effect";
|
|
4
|
+
import { gatewayFromEnvironment } from "./node.js";
|
|
5
|
+
// One JSON object per line, so `journalctl -o cat | jq` can filter journald output.
|
|
6
|
+
const logger = Logger.layer([Logger.consoleJson]);
|
|
7
|
+
NodeRuntime.runMain(Layer.launch(gatewayFromEnvironment).pipe(Effect.provide(Layer.mergeAll(NodeServices.layer, logger))), {
|
|
8
|
+
// The Gateway runs until a signal interrupts it, so systemd records the stop as a success.
|
|
9
|
+
teardown: (exit, onExit) => Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
|
|
10
|
+
? onExit(0)
|
|
11
|
+
: Runtime.defaultTeardown(exit, onExit),
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/node/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAEnD,oFAAoF;AACpF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAElD,WAAW,CAAC,OAAO,CACjB,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CACvC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAC3D,EACD;IACE,2FAA2F;IAC3F,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CACzB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;QACzD,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACX,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC;CAC5C,CACF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Context, Layer } from "effect";
|
|
2
|
+
import { GatewaySettings } from "../settings.ts";
|
|
3
|
+
import { type GatewayListeners, type Listener } from "./environment.ts";
|
|
4
|
+
export { ConflictingProviderCredentials, DEFAULT_EGRESS_ALLOWLIST_PATH, EnvironmentFileError, type GatewayListeners, type Listener, listenersFromEnvironment, settingsFromEnvironment, } from "./environment.ts";
|
|
5
|
+
export { EgressProxyListenError } from "./egress-proxy.ts";
|
|
6
|
+
export { GatewayReload } from "./reload.ts";
|
|
7
|
+
declare const GatewayAddresses_base: Context.ServiceClass<GatewayAddresses, "@usine/gateway/node/GatewayAddresses", {
|
|
8
|
+
readonly proxy: Listener;
|
|
9
|
+
readonly egress: Listener;
|
|
10
|
+
readonly admin: Listener;
|
|
11
|
+
}>;
|
|
12
|
+
/** The bound addresses of the Gateway's three servers, with any port 0 replaced by the assigned port. */
|
|
13
|
+
export declare class GatewayAddresses extends GatewayAddresses_base {
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The Gateway on Node: the reverse proxy, forward proxy, and admin API on their listeners,
|
|
17
|
+
* sharing one attachment store, caller token signer, settings, and ChatGPT logins. Upstream
|
|
18
|
+
* requests use undici, which doesn't change `content-encoding`, so compressed bodies pass
|
|
19
|
+
* through unchanged. ChatGPT logins read and write their auth files with Node's file system.
|
|
20
|
+
*/
|
|
21
|
+
export declare const gatewayLayer: (listeners: GatewayListeners) => Layer.Layer<import("../gateway.ts").Attachments | import("../gateway.ts").CallerTokens | import("../gateway.ts").ChatGptLogins | GatewayAddresses | import("../settings.ts").SettingsInForce, import("./egress-proxy.ts").EgressProxyListenError | import("effect/http/HttpServerError").ServeError, GatewaySettings>;
|
|
22
|
+
/**
|
|
23
|
+
* The Gateway configured from its environment files, the process environment, and its
|
|
24
|
+
* allowlist file, as run by the `usine-gateway` command. On SIGHUP, reloads the egress
|
|
25
|
+
* allowlist and LLM providers with `GatewayReload`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const gatewayFromEnvironment: Layer.Layer<import("../gateway.ts").Attachments | import("../gateway.ts").CallerTokens | import("../gateway.ts").ChatGptLogins | GatewayAddresses | import("./reload.ts").GatewayReload | import("../settings.ts").SettingsInForce, import("../gateway.ts").ChatGptAuthFileError | import("effect/Config").ConfigError | import("./environment.ts").ConflictingProviderCredentials | import("../gateway.ts").EgressAllowlistError | import("./egress-proxy.ts").EgressProxyListenError | import("./environment.ts").EnvironmentFileError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError, import("effect/FileSystem").FileSystem>;
|
|
28
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/node/node.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,OAAO,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAC;AAaxE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EAGd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC7B,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;;oBAKxB,QAAQ;qBAAmB,QAAQ;oBAAkB,QAAQ;;AAHjF,yGAAyG;AACzG,qBAAa,gBAAiB,SAAQ,qBAGK;CAAG;AA0D9C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,cAAe,gBAAgB,0TAgBrD,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,qpBAUlC,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { NodeFileSystem, NodeHttpClient, NodeHttpServer } from "@effect/platform-node";
|
|
3
|
+
import { ConfigProvider, Context, Effect, Layer, Stream } from "effect";
|
|
4
|
+
import { HttpMiddleware, HttpBody, HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse, } from "effect/http";
|
|
5
|
+
import { NetAddress } from "effect/net";
|
|
6
|
+
import { adminRoutes } from "../admin/server.js";
|
|
7
|
+
import { gatewayServices } from "../gateway.js";
|
|
8
|
+
import { GatewayProxy } from "../proxy/proxy.js";
|
|
9
|
+
import { GatewaySettings } from "../settings.js";
|
|
10
|
+
import { serveEgressProxy } from "./egress-proxy.js";
|
|
11
|
+
import { readSettingsSource, readStartupSettings, } from "./environment.js";
|
|
12
|
+
import { gatewayReloadLayer, reloadOnHangup } from "./reload.js";
|
|
13
|
+
export { ConflictingProviderCredentials, DEFAULT_EGRESS_ALLOWLIST_PATH, EnvironmentFileError, listenersFromEnvironment, settingsFromEnvironment, } from "./environment.js";
|
|
14
|
+
export { EgressProxyListenError } from "./egress-proxy.js";
|
|
15
|
+
export { GatewayReload } from "./reload.js";
|
|
16
|
+
/** The bound addresses of the Gateway's three servers, with any port 0 replaced by the assigned port. */
|
|
17
|
+
export class GatewayAddresses extends Context.Service()("@usine/gateway/node/GatewayAddresses") {
|
|
18
|
+
}
|
|
19
|
+
const boundTo = (address, requested) => address._tag === "UnixPathAddress"
|
|
20
|
+
? requested
|
|
21
|
+
: { host: NetAddress.formatHost(address), port: address.port };
|
|
22
|
+
/** Returns an address this process can connect to for `listener`, which may be bound to all interfaces. */
|
|
23
|
+
const reachable = (listener) => listener.host === "0.0.0.0" || listener.host === "::"
|
|
24
|
+
? { host: "127.0.0.1", port: listener.port }
|
|
25
|
+
: listener;
|
|
26
|
+
const serveProxy = Effect.fn("GatewayNode.serveProxy")(function* (listener) {
|
|
27
|
+
const proxy = yield* GatewayProxy;
|
|
28
|
+
// A git push or a long model response can exceed Node's default request timeout.
|
|
29
|
+
const server = yield* NodeHttpServer.make(() => createServer({ requestTimeout: 0 }), listener);
|
|
30
|
+
yield* server.serve(Effect.gen(function* () {
|
|
31
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
32
|
+
const response = yield* proxy.handle;
|
|
33
|
+
if (response.body._tag !== "Stream")
|
|
34
|
+
return response;
|
|
35
|
+
// The server destroys a response whose body fails after its headers, so the Worker sees a
|
|
36
|
+
// partial download or model response as incomplete.
|
|
37
|
+
const body = response.body.stream.pipe(Stream.onError(() => Effect.logError("The reverse proxy response body failed", {
|
|
38
|
+
host: request.headers.host,
|
|
39
|
+
method: request.method,
|
|
40
|
+
path: request.url.split("?")[0],
|
|
41
|
+
error: "UpstreamBodyFailure",
|
|
42
|
+
})));
|
|
43
|
+
return HttpServerResponse.setBody(response, HttpBody.stream(body, response.body.contentType, response.body.contentLength));
|
|
44
|
+
}));
|
|
45
|
+
const bound = boundTo(server.address, listener);
|
|
46
|
+
yield* Effect.logInfo("The gateway proxy is listening", bound);
|
|
47
|
+
return bound;
|
|
48
|
+
});
|
|
49
|
+
const serveAdmin = Effect.fn("GatewayNode.serveAdmin")(function* (listener) {
|
|
50
|
+
const server = yield* NodeHttpServer.make(() => createServer(), listener);
|
|
51
|
+
yield* Layer.build(HttpRouter.serve(adminRoutes, { disableListenLog: true }).pipe(Layer.provide(Layer.succeed(HttpServer.HttpServer)(server)), Layer.provide(NodeHttpServer.layerHttpServices)));
|
|
52
|
+
const bound = boundTo(server.address, listener);
|
|
53
|
+
yield* Effect.logInfo("The gateway admin API is listening", bound);
|
|
54
|
+
return bound;
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* The Gateway on Node: the reverse proxy, forward proxy, and admin API on their listeners,
|
|
58
|
+
* sharing one attachment store, caller token signer, settings, and ChatGPT logins. Upstream
|
|
59
|
+
* requests use undici, which doesn't change `content-encoding`, so compressed bodies pass
|
|
60
|
+
* through unchanged. ChatGPT logins read and write their auth files with Node's file system.
|
|
61
|
+
*/
|
|
62
|
+
export const gatewayLayer = (listeners) => Layer.effect(GatewayAddresses, Effect.gen(function* () {
|
|
63
|
+
const proxy = yield* serveProxy(listeners.proxy);
|
|
64
|
+
const egress = yield* serveEgressProxy(listeners.egress, reachable(proxy));
|
|
65
|
+
const admin = yield* serveAdmin(listeners.admin);
|
|
66
|
+
return { proxy, egress, admin };
|
|
67
|
+
})).pipe(Layer.provide(GatewayProxy.layer), Layer.provideMerge(gatewayServices), Layer.provide(Layer.mergeAll(NodeHttpClient.layerUndici, NodeFileSystem.layer)),
|
|
68
|
+
// No automatic HTTP span records the incoming headers. Named Gateway spans record safe fields
|
|
69
|
+
// without incoming credentials.
|
|
70
|
+
Layer.provide(Layer.succeed(HttpMiddleware.TracerDisabledWhen)(() => true)));
|
|
71
|
+
/**
|
|
72
|
+
* The Gateway configured from its environment files, the process environment, and its
|
|
73
|
+
* allowlist file, as run by the `usine-gateway` command. On SIGHUP, reloads the egress
|
|
74
|
+
* allowlist and LLM providers with `GatewayReload`.
|
|
75
|
+
*/
|
|
76
|
+
export const gatewayFromEnvironment = Layer.unwrap(Effect.gen(function* () {
|
|
77
|
+
const base = yield* ConfigProvider.ConfigProvider;
|
|
78
|
+
const started = yield* readStartupSettings(yield* readSettingsSource(base));
|
|
79
|
+
return reloadOnHangup.pipe(Layer.provideMerge(gatewayReloadLayer(started, base)), Layer.provideMerge(gatewayLayer(started.listeners)), Layer.provide(Layer.succeed(GatewaySettings)(started.settings)));
|
|
80
|
+
}));
|
|
81
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/node/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ConfigProvider, Context, Effect, FileSystem, Layer } from "effect";
|
|
2
|
+
import { ChatGptLogins } from "../chatgpt/logins.ts";
|
|
3
|
+
import { SettingsInForce } from "../settings.ts";
|
|
4
|
+
import { type StartupSettings } from "./environment.ts";
|
|
5
|
+
declare const GatewayReload_base: Context.ServiceClass<GatewayReload, "@usine/gateway/node/GatewayReload", {
|
|
6
|
+
/**
|
|
7
|
+
* Reloads once, after any reload in progress. Never fails: an unreadable or invalid file keeps
|
|
8
|
+
* the current settings, restart-only settings are unchanged, and both cases are logged.
|
|
9
|
+
*/
|
|
10
|
+
readonly reload: Effect.Effect<void>;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* The service that reloads the egress allowlist and LLM providers from the startup environment
|
|
14
|
+
* and allowlist files. Attachments, the admin API, listeners, and open tunnels are unchanged.
|
|
15
|
+
* `usine-gateway` runs it on SIGHUP.
|
|
16
|
+
*/
|
|
17
|
+
export declare class GatewayReload extends GatewayReload_base {
|
|
18
|
+
}
|
|
19
|
+
/** Returns the reload for a Gateway started with `started`, reading settings from the environment files over `base`. */
|
|
20
|
+
export declare const gatewayReloadLayer: (started: StartupSettings, base: ConfigProvider.ConfigProvider) => Layer.Layer<GatewayReload, never, ChatGptLogins | FileSystem.FileSystem | SettingsInForce>;
|
|
21
|
+
/**
|
|
22
|
+
* Runs the reload on each SIGHUP while the layer is in scope, one at a time; signals during a
|
|
23
|
+
* reload trigger one more. The listener also prevents Node's default exit on SIGHUP.
|
|
24
|
+
*/
|
|
25
|
+
export declare const reloadOnHangup: Layer.Layer<never, never, GatewayReload>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=reload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload.d.ts","sourceRoot":"","sources":["../../src/node/reload.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EACd,OAAO,EACP,MAAM,EACN,UAAU,EACV,KAAK,EAKN,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAQL,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;;IAUtB;;;OAGG;qBACc,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;;AAZxC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,kBASK;CAAG;AA0I3C,wHAAwH;AACxH,eAAO,MAAM,kBAAkB,YAAa,eAAe,QAAQ,cAAc,CAAC,cAAc,+FAmE7F,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,cAAc,0CAoB1B,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Cause, ConfigProvider, Context, Effect, FileSystem, Layer, Option, Queue, Redacted, Semaphore, } from "effect";
|
|
2
|
+
import { ChatGptLogins } from "../chatgpt/logins.js";
|
|
3
|
+
import { SettingsInForce } from "../settings.js";
|
|
4
|
+
import { egressAllowlistPath, llmProviders, readEgressAllowlist, readSettingsSource, restartSettings, } from "./environment.js";
|
|
5
|
+
/**
|
|
6
|
+
* The service that reloads the egress allowlist and LLM providers from the startup environment
|
|
7
|
+
* and allowlist files. Attachments, the admin API, listeners, and open tunnels are unchanged.
|
|
8
|
+
* `usine-gateway` runs it on SIGHUP.
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayReload extends Context.Service()("@usine/gateway/node/GatewayReload") {
|
|
11
|
+
}
|
|
12
|
+
/** Returns each restart-only setting as text, keyed by its variable. */
|
|
13
|
+
const restartValues = ({ domain, adminToken, callerKey, listeners }) => ({
|
|
14
|
+
USINE_GATEWAY_DOMAIN: domain,
|
|
15
|
+
USINE_GATEWAY_ADMIN_TOKEN: Redacted.value(adminToken),
|
|
16
|
+
USINE_GATEWAY_CALLER_KEY: Redacted.value(callerKey),
|
|
17
|
+
USINE_GATEWAY_PROXY_HOST: listeners.proxy.host,
|
|
18
|
+
USINE_GATEWAY_PROXY_PORT: String(listeners.proxy.port),
|
|
19
|
+
USINE_GATEWAY_EGRESS_HOST: listeners.egress.host,
|
|
20
|
+
USINE_GATEWAY_EGRESS_PORT: String(listeners.egress.port),
|
|
21
|
+
USINE_GATEWAY_ADMIN_HOST: listeners.admin.host,
|
|
22
|
+
USINE_GATEWAY_ADMIN_PORT: String(listeners.admin.port),
|
|
23
|
+
});
|
|
24
|
+
/** Returns each credential variable with its value, or `undefined` if unset. */
|
|
25
|
+
const credentialValues = ({ anthropic, openai }) => {
|
|
26
|
+
const anthropicCredential = Option.getOrUndefined(Option.map(anthropic, (provider) => provider.credential));
|
|
27
|
+
const openAiCredential = Option.getOrUndefined(Option.map(openai, (provider) => provider.credential));
|
|
28
|
+
return {
|
|
29
|
+
ANTHROPIC_API_KEY: anthropicCredential?._tag === "AnthropicApiKey"
|
|
30
|
+
? Redacted.value(anthropicCredential.key)
|
|
31
|
+
: undefined,
|
|
32
|
+
USINE_GATEWAY_ANTHROPIC_SUBSCRIPTION_TOKEN: anthropicCredential?._tag === "AnthropicSubscriptionToken"
|
|
33
|
+
? Redacted.value(anthropicCredential.token)
|
|
34
|
+
: undefined,
|
|
35
|
+
OPENAI_API_KEY: openAiCredential?._tag === "OpenAiApiKey" ? Redacted.value(openAiCredential.key) : undefined,
|
|
36
|
+
USINE_GATEWAY_CHATGPT_AUTH_FILE: openAiCredential?._tag === "ChatGptLogin" ? openAiCredential.authFile : undefined,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/** The variable that sets each credential's upstream. */
|
|
40
|
+
const UPSTREAM_VARIABLES = {
|
|
41
|
+
AnthropicApiKey: "USINE_GATEWAY_ANTHROPIC_UPSTREAM",
|
|
42
|
+
AnthropicSubscriptionToken: "USINE_GATEWAY_ANTHROPIC_UPSTREAM",
|
|
43
|
+
OpenAiApiKey: "USINE_GATEWAY_OPENAI_UPSTREAM",
|
|
44
|
+
ChatGptLogin: "USINE_GATEWAY_CHATGPT_UPSTREAM",
|
|
45
|
+
};
|
|
46
|
+
/** Returns the upstream variables that changed, for providers with the same kind of credential before and after. */
|
|
47
|
+
const changedUpstreams = (before, after) => {
|
|
48
|
+
const changed = (was, is) => Option.toArray(Option.flatMap(Option.zipWith(was, is, (left, right) => [left, right]), ([left, right]) => left.credential._tag === right.credential._tag && left.upstream !== right.upstream
|
|
49
|
+
? Option.some(UPSTREAM_VARIABLES[right.credential._tag])
|
|
50
|
+
: Option.none()));
|
|
51
|
+
return [...changed(before.anthropic, after.anthropic), ...changed(before.openai, after.openai)];
|
|
52
|
+
};
|
|
53
|
+
/** Returns the names of the variables whose values differ, never the values. */
|
|
54
|
+
const changedVariables = (before, after) => Object.keys(before).filter((name) => before[name] !== after[name]);
|
|
55
|
+
/** Logs a warning for restart-only settings that changed or no longer parse. */
|
|
56
|
+
const checkRestartSettings = (started) => restartSettings.pipe(Effect.flatMap((now) => {
|
|
57
|
+
const changed = changedVariables(restartValues(started), restartValues(now));
|
|
58
|
+
return changed.length === 0
|
|
59
|
+
? Effect.void
|
|
60
|
+
: Effect.logWarning("Settings changed that take effect only after a restart", {
|
|
61
|
+
settings: changed,
|
|
62
|
+
});
|
|
63
|
+
}), Effect.catchTag("ConfigError", (error) => Effect.logWarning("A restart would fail: a setting only a restart reads is invalid", {
|
|
64
|
+
error: error.message,
|
|
65
|
+
})));
|
|
66
|
+
/** Returns the allowlist path and its new contents, or `None` if it can't be read or parsed. */
|
|
67
|
+
const nextAllowlist = (previous) => Effect.gen(function* () {
|
|
68
|
+
// The path has a default, so reading it fails only if the settings source fails, which an in-memory source doesn't.
|
|
69
|
+
const path = yield* Effect.orDie(egressAllowlistPath);
|
|
70
|
+
const kept = (fields) => Effect.logError("Kept the previous egress allowlist", {
|
|
71
|
+
path,
|
|
72
|
+
...fields,
|
|
73
|
+
rules: previous.rules.length,
|
|
74
|
+
}).pipe(Effect.as({ path, egress: Option.none() }));
|
|
75
|
+
return yield* readEgressAllowlist(path).pipe(Effect.map((egress) => ({ path, egress: Option.some(egress) })), Effect.catchTags({
|
|
76
|
+
EgressAllowlistError: ({ line, message }) => kept({ line, error: message }),
|
|
77
|
+
PlatformError: (error) => kept({ error: error.message }),
|
|
78
|
+
}));
|
|
79
|
+
});
|
|
80
|
+
const keptProviders = (error) => Effect.logError("Kept the previous LLM providers", { error }).pipe(Effect.as(Option.none()));
|
|
81
|
+
/** The new LLM providers, or `None` if they're invalid. */
|
|
82
|
+
const nextProviders = llmProviders.pipe(Effect.map(Option.some), Effect.catchTags({
|
|
83
|
+
ConfigError: ({ message }) => keptProviders(message),
|
|
84
|
+
ConflictingProviderCredentials: ({ message }) => keptProviders(message),
|
|
85
|
+
ChatGptAuthFileError: ({ message }) => keptProviders(message),
|
|
86
|
+
}));
|
|
87
|
+
/**
|
|
88
|
+
* Reads a ChatGPT login's auth file again after a reload, so a login the operator copied
|
|
89
|
+
* replaces the one in memory. An unreadable file keeps the login in memory.
|
|
90
|
+
*/
|
|
91
|
+
const reloadChatGptLogin = (logins, providers) => {
|
|
92
|
+
const login = Option.getOrUndefined(Option.map(providers.openai, ({ credential }) => credential));
|
|
93
|
+
if (login?._tag !== "ChatGptLogin")
|
|
94
|
+
return Effect.void;
|
|
95
|
+
return logins.reload(login).pipe(Effect.catchTag("ChatGptAuthFileError", ({ message }) => Effect.logError("Kept the ChatGPT login in memory: its auth file could not be read", {
|
|
96
|
+
authFile: login.authFile,
|
|
97
|
+
error: message,
|
|
98
|
+
})));
|
|
99
|
+
};
|
|
100
|
+
/** Returns the reload for a Gateway started with `started`, reading settings from the environment files over `base`. */
|
|
101
|
+
export const gatewayReloadLayer = (started, base) => Layer.effect(GatewayReload, Effect.gen(function* () {
|
|
102
|
+
const inForce = yield* SettingsInForce;
|
|
103
|
+
const logins = yield* ChatGptLogins;
|
|
104
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
105
|
+
const oneAtATime = yield* Semaphore.make(1);
|
|
106
|
+
const { domain, adminToken, callerKey } = started.settings;
|
|
107
|
+
const startedRestartSettings = {
|
|
108
|
+
domain,
|
|
109
|
+
adminToken,
|
|
110
|
+
callerKey,
|
|
111
|
+
listeners: started.listeners,
|
|
112
|
+
};
|
|
113
|
+
const reloadFrom = (source) => Effect.gen(function* () {
|
|
114
|
+
yield* checkRestartSettings(startedRestartSettings);
|
|
115
|
+
const previous = yield* inForce.current;
|
|
116
|
+
const allowlist = yield* nextAllowlist(previous.egress);
|
|
117
|
+
const providers = yield* nextProviders;
|
|
118
|
+
const next = {
|
|
119
|
+
egress: Option.getOrElse(allowlist.egress, () => previous.egress),
|
|
120
|
+
providers: Option.getOrElse(providers, () => previous.providers),
|
|
121
|
+
};
|
|
122
|
+
yield* inForce.replace(next);
|
|
123
|
+
if (Option.isSome(providers))
|
|
124
|
+
yield* reloadChatGptLogin(logins, next.providers);
|
|
125
|
+
yield* Effect.logInfo("Reloaded the gateway settings", {
|
|
126
|
+
environmentFiles: source.files,
|
|
127
|
+
allowlist: Option.isSome(allowlist.egress) ? "reloaded" : "kept",
|
|
128
|
+
path: allowlist.path,
|
|
129
|
+
rules: next.egress.rules.length,
|
|
130
|
+
providers: Option.isSome(providers) ? "reloaded" : "kept",
|
|
131
|
+
changed: [
|
|
132
|
+
...changedVariables(credentialValues(previous.providers), credentialValues(next.providers)),
|
|
133
|
+
...changedUpstreams(previous.providers, next.providers),
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
}).pipe(Effect.provideService(ConfigProvider.ConfigProvider, source.provider));
|
|
137
|
+
const reload = Effect.fn("GatewayNode.reload")(function* () {
|
|
138
|
+
yield* readSettingsSource(base).pipe(Effect.flatMap(reloadFrom), Effect.catchTags({
|
|
139
|
+
EnvironmentFileError: ({ path, message }) => Effect.logError("Kept the gateway settings: an environment file could not be read", {
|
|
140
|
+
path,
|
|
141
|
+
error: message,
|
|
142
|
+
}),
|
|
143
|
+
ConfigError: ({ message }) => Effect.logError("Kept the gateway settings: the environment could not be read", {
|
|
144
|
+
error: message,
|
|
145
|
+
}),
|
|
146
|
+
}));
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
reload: oneAtATime
|
|
150
|
+
.withPermits(1)(reload())
|
|
151
|
+
.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem)),
|
|
152
|
+
};
|
|
153
|
+
}));
|
|
154
|
+
/**
|
|
155
|
+
* Runs the reload on each SIGHUP while the layer is in scope, one at a time; signals during a
|
|
156
|
+
* reload trigger one more. The listener also prevents Node's default exit on SIGHUP.
|
|
157
|
+
*/
|
|
158
|
+
export const reloadOnHangup = Layer.effectDiscard(Effect.gen(function* () {
|
|
159
|
+
const { reload } = yield* GatewayReload;
|
|
160
|
+
const hangups = yield* Queue.sliding(1);
|
|
161
|
+
const onHangup = () => {
|
|
162
|
+
Queue.offerUnsafe(hangups, undefined);
|
|
163
|
+
};
|
|
164
|
+
yield* Effect.acquireRelease(Effect.sync(() => process.on("SIGHUP", onHangup)), () => Effect.sync(() => process.off("SIGHUP", onHangup)));
|
|
165
|
+
const reloadOnce = Effect.logInfo("Reloading the gateway settings on SIGHUP").pipe(Effect.andThen(reload),
|
|
166
|
+
// Logs a defect in one reload; the next SIGHUP reloads again.
|
|
167
|
+
Effect.catchCause((cause) => Effect.logError("A reload failed", { cause: Cause.pretty(cause) })));
|
|
168
|
+
yield* Queue.take(hangups).pipe(Effect.andThen(reloadOnce), Effect.forever, Effect.forkScoped);
|
|
169
|
+
}));
|
|
170
|
+
//# sourceMappingURL=reload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload.js","sourceRoot":"","sources":["../../src/node/reload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Headers, HttpClient, type HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
|
+
/** Returns `headers` without hop-by-hop headers, including those listed in `Connection`. */
|
|
4
|
+
export declare const endToEndHeaders: (headers: Headers.Headers) => Headers.Headers;
|
|
5
|
+
/**
|
|
6
|
+
* Sends `request` to `upstream` with `headers` and streams the response back unchanged:
|
|
7
|
+
* status, end-to-end headers, and body bytes, including any `content-encoding`. Bodies stream
|
|
8
|
+
* in both directions without buffering. The client span is disabled, so no header value is
|
|
9
|
+
* recorded in a trace.
|
|
10
|
+
*/
|
|
11
|
+
export declare const forwardRequest: (client: HttpClient.HttpClient, request: HttpServerRequest.HttpServerRequest, upstream: string, headers: Headers.Headers) => Effect.Effect<HttpServerResponse.HttpServerResponse, import("effect/http/HttpClientError").HttpClientError, never>;
|
|
12
|
+
//# sourceMappingURL=forward.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forward.d.ts","sourceRoot":"","sources":["../../src/proxy/forward.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EACL,OAAO,EACP,UAAU,EAEV,KAAK,iBAAiB,EACtB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAiBrB,4FAA4F;AAC5F,eAAO,MAAM,eAAe,YAAa,OAAO,CAAC,OAAO,KAAG,OAAO,CAAC,OASlE,CAAC;AASF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,WACjB,UAAU,CAAC,UAAU,WACpB,iBAAiB,CAAC,iBAAiB,YAClC,MAAM,WACP,OAAO,CAAC,OAAO,uHAezB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { Headers, HttpClient, HttpClientRequest, HttpServerResponse, } from "effect/http";
|
|
3
|
+
/** Connection-level headers that are never forwarded (RFC 9110, section 7.6.1). */
|
|
4
|
+
const HOP_BY_HOP = [
|
|
5
|
+
"connection",
|
|
6
|
+
"keep-alive",
|
|
7
|
+
"proxy-authenticate",
|
|
8
|
+
"proxy-authorization",
|
|
9
|
+
"proxy-connection",
|
|
10
|
+
"te",
|
|
11
|
+
"trailer",
|
|
12
|
+
"transfer-encoding",
|
|
13
|
+
"upgrade",
|
|
14
|
+
"expect",
|
|
15
|
+
"host",
|
|
16
|
+
];
|
|
17
|
+
/** Returns `headers` without hop-by-hop headers, including those listed in `Connection`. */
|
|
18
|
+
export const endToEndHeaders = (headers) => {
|
|
19
|
+
const listed = Option.match(Headers.get(headers, "connection"), {
|
|
20
|
+
onNone: () => [],
|
|
21
|
+
onSome: (value) => value.split(",").map((name) => name.trim().toLowerCase()),
|
|
22
|
+
});
|
|
23
|
+
return [...HOP_BY_HOP, ...listed].reduce((remaining, name) => Headers.remove(remaining, name), headers);
|
|
24
|
+
};
|
|
25
|
+
const hasBody = (request) => Option.isSome(Headers.get(request.headers, "transfer-encoding")) ||
|
|
26
|
+
Option.match(Headers.get(request.headers, "content-length"), {
|
|
27
|
+
onNone: () => false,
|
|
28
|
+
onSome: (length) => length.trim() !== "0",
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Sends `request` to `upstream` with `headers` and streams the response back unchanged:
|
|
32
|
+
* status, end-to-end headers, and body bytes, including any `content-encoding`. Bodies stream
|
|
33
|
+
* in both directions without buffering. The client span is disabled, so no header value is
|
|
34
|
+
* recorded in a trace.
|
|
35
|
+
*/
|
|
36
|
+
export const forwardRequest = (client, request, upstream, headers) => {
|
|
37
|
+
const base = HttpClientRequest.make(request.method)(upstream);
|
|
38
|
+
const outgoing = (hasBody(request) ? HttpClientRequest.bodyStream(base, request.stream) : base).pipe(HttpClientRequest.setHeaders(headers));
|
|
39
|
+
return client.execute(outgoing).pipe(Effect.provideService(HttpClient.TracerDisabledWhen, () => true), Effect.map((response) => HttpServerResponse.stream(response.stream, {
|
|
40
|
+
status: response.status,
|
|
41
|
+
headers: endToEndHeaders(response.headers),
|
|
42
|
+
})));
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=forward.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forward.js","sourceRoot":"","sources":["../../src/proxy/forward.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EACL,OAAO,EACP,UAAU,EACV,iBAAiB,EAEjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,mFAAmF;AACnF,MAAM,UAAU,GAAG;IACjB,YAAY;IACZ,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,kBAAkB;IAClB,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;IACT,QAAQ;IACR,MAAM;CACE,CAAC;AAEX,4FAA4F;AAC5F,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAwB,EAAmB,EAAE;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE;QAC9D,MAAM,EAAE,GAA0B,EAAE,CAAC,EAAE;QACvC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;KAC7E,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,MAAM,CAAC,CAAC,MAAM,CACtC,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EACpD,OAAO,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,OAA4C,EAAW,EAAE,CACxE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE;QAC3D,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;QACnB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG;KAC1C,CAAC,CAAC;AAEL;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAA6B,EAC7B,OAA4C,EAC5C,QAAgB,EAChB,OAAwB,EACxB,EAAE;IACF,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,CACf,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAC7E,CAAC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAClC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAChE,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACtB,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE;QACzC,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC;KAC3C,CAAC,CACH,CACF,CAAC;AACJ,CAAC,CAAC"}
|