@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,112 @@
|
|
|
1
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { Option, Redacted, Result, Schema } from "effect";
|
|
3
|
+
import { Base64 } from "effect/encoding";
|
|
4
|
+
import { Headers } from "effect/http";
|
|
5
|
+
import { UpstreamBase } from "../upstream.js";
|
|
6
|
+
/** The github.com origins. */
|
|
7
|
+
export const GITHUB_ORIGINS = {
|
|
8
|
+
web: UpstreamBase.make("https://github.com"),
|
|
9
|
+
api: UpstreamBase.make("https://api.github.com"),
|
|
10
|
+
uploads: UpstreamBase.make("https://uploads.github.com"),
|
|
11
|
+
};
|
|
12
|
+
/** GitHub's host names and what each serves. */
|
|
13
|
+
export const GITHUB_HOSTS = new Map([
|
|
14
|
+
["github.com", "web"],
|
|
15
|
+
["api.github.com", "api"],
|
|
16
|
+
["uploads.github.com", "uploads"],
|
|
17
|
+
]);
|
|
18
|
+
const GIT_PATH = /^\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/(?:info\/refs|git-upload-pack|git-receive-pack|info\/lfs(?:\/[^?#]*)?)$/u;
|
|
19
|
+
/** `/OWNER/REPO/releases/download/TAG/FILE`, whose tag can contain `/`, and `/OWNER/REPO/releases/latest/download/FILE`. */
|
|
20
|
+
const RELEASE_DOWNLOAD_PATH = /^\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/releases\/(?:download\/.+|latest\/download)\/[^/]+$/u;
|
|
21
|
+
const WEB_PATHS = [
|
|
22
|
+
["git", GIT_PATH],
|
|
23
|
+
["release", RELEASE_DOWNLOAD_PATH],
|
|
24
|
+
];
|
|
25
|
+
const REPOSITORY_PATH = /^\/repos\/([^/]+)\/([^/]+)(?:\/|$)/u;
|
|
26
|
+
const decodeRepository = Schema.decodeUnknownOption(RepositoryName);
|
|
27
|
+
/** Returns the github.com route for a repository's git smart HTTP or release download path. */
|
|
28
|
+
const webRoute = (origins, pathname, search) => {
|
|
29
|
+
for (const [protocol, pattern] of WEB_PATHS) {
|
|
30
|
+
const match = pattern.exec(pathname);
|
|
31
|
+
if (match !== null)
|
|
32
|
+
return Option.map(decodeRepository(`${match[1]}/${match[2]}`), (repository) => ({
|
|
33
|
+
protocol,
|
|
34
|
+
upstream: `${origins.web}${pathname}${search}`,
|
|
35
|
+
repository: Option.some(repository),
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
return Option.none();
|
|
39
|
+
};
|
|
40
|
+
/** Returns the route for an API path, or `None` for a `/repos/OWNER/REPO` path whose segments aren't a valid repository name. */
|
|
41
|
+
const apiRoute = (base, rest, search) => {
|
|
42
|
+
const path = rest === "" ? "/" : rest;
|
|
43
|
+
const match = REPOSITORY_PATH.exec(path);
|
|
44
|
+
const repository = match === null
|
|
45
|
+
? Option.some(Option.none())
|
|
46
|
+
: Option.map(decodeRepository(`${match[1]}/${match[2]}`), Option.some);
|
|
47
|
+
return Option.map(repository, (named) => ({
|
|
48
|
+
protocol: "api",
|
|
49
|
+
upstream: `${base}${path}${search}`,
|
|
50
|
+
repository: named,
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
53
|
+
/** Returns `value` as a URL if it is an absolute URL. */
|
|
54
|
+
export const absoluteUrl = (value) => Result.getSuccess(Result.try(() => new URL(value)));
|
|
55
|
+
/** Returns `pathname` after `prefix`, if it is `prefix` or a path under it. */
|
|
56
|
+
export const afterPrefix = (pathname, prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`)
|
|
57
|
+
? Option.some(pathname.slice(prefix.length))
|
|
58
|
+
: Option.none();
|
|
59
|
+
/**
|
|
60
|
+
* Returns the upstream URL and repository for a GitHub request. Returns `None` for paths
|
|
61
|
+
* outside git smart HTTP, Git LFS, release downloads, and the API, such as web pages.
|
|
62
|
+
*/
|
|
63
|
+
export const githubRoute = (origins, host, pathname, search) => {
|
|
64
|
+
switch (host) {
|
|
65
|
+
case "web":
|
|
66
|
+
return webRoute(origins, pathname, search);
|
|
67
|
+
case "api":
|
|
68
|
+
return apiRoute(origins.api, pathname, search);
|
|
69
|
+
case "uploads":
|
|
70
|
+
return apiRoute(origins.uploads, pathname, search);
|
|
71
|
+
case "gateway": {
|
|
72
|
+
if (pathname === "/api/graphql")
|
|
73
|
+
return apiRoute(origins.api, "/graphql", search);
|
|
74
|
+
const rest = afterPrefix(pathname, "/api/v3");
|
|
75
|
+
if (Option.isSome(rest))
|
|
76
|
+
return apiRoute(origins.api, rest.value, search);
|
|
77
|
+
const upload = afterPrefix(pathname, "/api/uploads");
|
|
78
|
+
if (Option.isSome(upload))
|
|
79
|
+
return apiRoute(origins.uploads, upload.value, search);
|
|
80
|
+
return webRoute(origins, pathname, search);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Returns `location` on `git.<domain>` if it points at github.com or api.github.com, with its
|
|
86
|
+
* path and query, so a Worker follows GitHub's redirects, such as those for a renamed
|
|
87
|
+
* repository, through the gateway. API paths move under `/api/v3`. Returns `None` for other
|
|
88
|
+
* hosts, such as the release asset hosts.
|
|
89
|
+
*/
|
|
90
|
+
export const gatewayLocation = (origins, gitHost, location) => {
|
|
91
|
+
const target = `${location.origin}${location.pathname}`;
|
|
92
|
+
const onGateway = (prefix) => (rest) => new URL(`http://${gitHost}${prefix}${rest}${location.search}${location.hash}`);
|
|
93
|
+
return Option.map(afterPrefix(target, origins.web), onGateway("")).pipe(Option.orElse(() => Option.map(afterPrefix(target, origins.api), onGateway("/api/v3"))));
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Adds the attachment's credential to a GitHub request whose caller credential was removed: as
|
|
97
|
+
* the `x-access-token` password for github.com, which serves git and release downloads, or as a
|
|
98
|
+
* bearer token for the API. An anonymous attachment adds nothing.
|
|
99
|
+
*/
|
|
100
|
+
export const withGitHubCredential = (protocol, credential, headers) => {
|
|
101
|
+
switch (credential._tag) {
|
|
102
|
+
case "AnonymousCredential":
|
|
103
|
+
return headers;
|
|
104
|
+
case "InstallationCredential": {
|
|
105
|
+
const token = Redacted.value(credential.token);
|
|
106
|
+
return Headers.set(headers, "authorization", protocol === "api"
|
|
107
|
+
? `Bearer ${token}`
|
|
108
|
+
: `Basic ${Base64.encode(`x-access-token:${token}`)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/github/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/http";
|
|
4
|
+
import type { Attachment } from "../attachment/attachment.ts";
|
|
5
|
+
import { type GitHubOrigins } from "./github.ts";
|
|
6
|
+
/** The statuses GitHub uses to redirect a renamed or transferred repository's former name. */
|
|
7
|
+
export declare const PERMANENT_REDIRECTS: ReadonlySet<number>;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the repository name in `location`, GitHub's redirect of the upstream URL `requested`,
|
|
10
|
+
* if both are git's `/OWNER/REPO/info/refs` or both are the API's `/repos/OWNER/REPO`, so only
|
|
11
|
+
* the owner and repository differ. Returns `None` for other redirects, such as a release
|
|
12
|
+
* download's, which can point at another repository without a rename.
|
|
13
|
+
*/
|
|
14
|
+
export declare const redirectedName: (origins: GitHubOrigins, requested: URL, location: URL) => Option.Option<RepositoryName>;
|
|
15
|
+
declare const RenameUnconfirmed_base: Schema.Class<RenameUnconfirmed, Schema.TaggedStruct<"RenameUnconfirmed", {
|
|
16
|
+
readonly reason: Schema.Literals<readonly ["former-name-not-redirected", "repository-unreadable", "other-current-name", "github-unreachable"]>;
|
|
17
|
+
readonly detail: Schema.String;
|
|
18
|
+
}>, import("effect/Cause").YieldableError>;
|
|
19
|
+
/** Why GitHub's API didn't confirm that the attached repository has a redirect's name. */
|
|
20
|
+
declare class RenameUnconfirmed extends RenameUnconfirmed_base {
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Confirms with GitHub's API, using the attachment's credential, that `newName` is the attached
|
|
24
|
+
* repository's current name: `/repos/OWNER/REPO` for the attached name must answer a permanent
|
|
25
|
+
* redirect to `/repositories/ID`, and the `full_name` of repository ID must be `newName`. Fails
|
|
26
|
+
* with `RenameUnconfirmed` otherwise, or if GitHub can't be reached.
|
|
27
|
+
*/
|
|
28
|
+
export declare const confirmRename: (client: HttpClient.HttpClient, origins: GitHubOrigins, attachment: Attachment, newName: string & import("effect/Brand").Brand<"RepositoryName">) => Effect.Effect<undefined, RenameUnconfirmed, never>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../src/github/rename.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAW,UAAU,EAAqB,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAA4B,KAAK,aAAa,EAAwB,MAAM,aAAa,CAAC;AAEjG,8FAA8F;AAC9F,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAuB,CAAC;AAmC5E;;;;;GAKG;AACH,eAAO,MAAM,cAAc,YAChB,aAAa,aACX,GAAG,YACJ,GAAG,KACZ,MAAM,CAAC,MAAM,CAAC,cAAc,CAM5B,CAAC;;;;;AAEJ,0FAA0F;AAC1F,cAAM,iBAAkB,SAAQ,sBAQ9B;CAAG;AAcL;;;;;GAKG;AACH,eAAO,MAAM,aAAa,yMAqDxB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { canonicalRepositoryName, RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import { Headers, HttpClient, HttpClientRequest } from "effect/http";
|
|
4
|
+
import { absoluteUrl, afterPrefix, withGitHubCredential } from "./github.js";
|
|
5
|
+
/** The statuses GitHub uses to redirect a renamed or transferred repository's former name. */
|
|
6
|
+
export const PERMANENT_REDIRECTS = new Set([301, 308]);
|
|
7
|
+
const GIT_REFS_PATH = /^\/([^/]+)\/([^/]+?)(?:\.git)?\/info\/refs$/u;
|
|
8
|
+
const API_REPOSITORY_PATH = /^\/repos\/([^/]+)\/([^/]+)$/u;
|
|
9
|
+
const decodeRepository = Schema.decodeUnknownOption(RepositoryName);
|
|
10
|
+
const repositoryItself = (origins, url) => {
|
|
11
|
+
const target = `${url.origin}${url.pathname}`;
|
|
12
|
+
const named = (resource, pattern) => (path) => {
|
|
13
|
+
const match = pattern.exec(path);
|
|
14
|
+
return match === null
|
|
15
|
+
? Option.none()
|
|
16
|
+
: Option.map(decodeRepository(`${match[1]}/${match[2]}`), (repository) => ({
|
|
17
|
+
resource,
|
|
18
|
+
repository,
|
|
19
|
+
}));
|
|
20
|
+
};
|
|
21
|
+
return Option.flatMap(afterPrefix(target, origins.web), named("git-refs", GIT_REFS_PATH)).pipe(Option.orElse(() => Option.flatMap(afterPrefix(target, origins.api), named("api-repository", API_REPOSITORY_PATH))));
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns the repository name in `location`, GitHub's redirect of the upstream URL `requested`,
|
|
25
|
+
* if both are git's `/OWNER/REPO/info/refs` or both are the API's `/repos/OWNER/REPO`, so only
|
|
26
|
+
* the owner and repository differ. Returns `None` for other redirects, such as a release
|
|
27
|
+
* download's, which can point at another repository without a rename.
|
|
28
|
+
*/
|
|
29
|
+
export const redirectedName = (origins, requested, location) => Option.flatMap(repositoryItself(origins, requested), ({ resource }) => repositoryItself(origins, location).pipe(Option.filter((redirected) => redirected.resource === resource), Option.map(({ repository }) => repository)));
|
|
30
|
+
/** Why GitHub's API didn't confirm that the attached repository has a redirect's name. */
|
|
31
|
+
class RenameUnconfirmed extends Schema.TaggedError()("RenameUnconfirmed", {
|
|
32
|
+
reason: Schema.Literals([
|
|
33
|
+
"former-name-not-redirected",
|
|
34
|
+
"repository-unreadable",
|
|
35
|
+
"other-current-name",
|
|
36
|
+
"github-unreachable",
|
|
37
|
+
]),
|
|
38
|
+
detail: Schema.String,
|
|
39
|
+
}) {
|
|
40
|
+
}
|
|
41
|
+
/** Where GitHub's API redirects a renamed or transferred repository's former name. */
|
|
42
|
+
const REPOSITORY_BY_ID_PATH = /^\/repositories\/[1-9][0-9]*$/u;
|
|
43
|
+
const decodeCurrentName = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Struct({ full_name: RepositoryName })));
|
|
44
|
+
const API_HEADERS = Headers.fromInput({
|
|
45
|
+
accept: "application/vnd.github+json",
|
|
46
|
+
"user-agent": "usine-gateway",
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Confirms with GitHub's API, using the attachment's credential, that `newName` is the attached
|
|
50
|
+
* repository's current name: `/repos/OWNER/REPO` for the attached name must answer a permanent
|
|
51
|
+
* redirect to `/repositories/ID`, and the `full_name` of repository ID must be `newName`. Fails
|
|
52
|
+
* with `RenameUnconfirmed` otherwise, or if GitHub can't be reached.
|
|
53
|
+
*/
|
|
54
|
+
export const confirmRename = Effect.fn("GitHubRename.confirm")(function* (client, origins, attachment, newName) {
|
|
55
|
+
yield* Effect.annotateCurrentSpan({ attachment: attachment.id, newName });
|
|
56
|
+
const get = (url) => client
|
|
57
|
+
.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeaders(withGitHubCredential("api", attachment.credential, API_HEADERS))))
|
|
58
|
+
.pipe(Effect.flatMap((response) => Effect.map(response.text, (body) => ({
|
|
59
|
+
status: response.status,
|
|
60
|
+
location: Headers.get(response.headers, "location"),
|
|
61
|
+
body,
|
|
62
|
+
}))), Effect.provideService(HttpClient.TracerDisabledWhen, () => true), Effect.mapError((error) => new RenameUnconfirmed({ reason: "github-unreachable", detail: error.reason._tag })));
|
|
63
|
+
const former = yield* get(`${origins.api}/repos/${attachment.repository}`);
|
|
64
|
+
const byId = Option.flatMap(former.location, (location) => Option.flatMap(absoluteUrl(location), (url) => afterPrefix(`${url.origin}${url.pathname}`, origins.api))).pipe(Option.filter((path) => REPOSITORY_BY_ID_PATH.test(path)));
|
|
65
|
+
if (!PERMANENT_REDIRECTS.has(former.status) || Option.isNone(byId))
|
|
66
|
+
return yield* new RenameUnconfirmed({
|
|
67
|
+
reason: "former-name-not-redirected",
|
|
68
|
+
detail: `/repos/${attachment.repository} answered ${String(former.status)}`,
|
|
69
|
+
});
|
|
70
|
+
const current = yield* get(`${origins.api}${byId.value}`);
|
|
71
|
+
const name = current.status === 200 ? decodeCurrentName(current.body) : Option.none();
|
|
72
|
+
if (Option.isNone(name))
|
|
73
|
+
return yield* new RenameUnconfirmed({
|
|
74
|
+
reason: "repository-unreadable",
|
|
75
|
+
detail: `${byId.value} answered ${String(current.status)}`,
|
|
76
|
+
});
|
|
77
|
+
if (canonicalRepositoryName(name.value.full_name) !== canonicalRepositoryName(newName))
|
|
78
|
+
return yield* new RenameUnconfirmed({
|
|
79
|
+
reason: "other-current-name",
|
|
80
|
+
detail: `${byId.value} is ${name.value.full_name}`,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.js","sourceRoot":"","sources":["../../src/github/rename.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { type GatewayCredentialKind, GatewayLlmProvider } from "@usine/core/worker";
|
|
2
|
+
import { Option, Redacted, Schema } from "effect";
|
|
3
|
+
import { Headers } from "effect/http";
|
|
4
|
+
declare const AnthropicApiKey_base: Schema.Class<AnthropicApiKey, Schema.TaggedStruct<"AnthropicApiKey", {
|
|
5
|
+
readonly key: Schema.Redacted<Schema.NonEmptyString>;
|
|
6
|
+
}>, {}>;
|
|
7
|
+
/** An Anthropic API key, sent upstream as `x-api-key`. */
|
|
8
|
+
export declare class AnthropicApiKey extends AnthropicApiKey_base {
|
|
9
|
+
}
|
|
10
|
+
declare const AnthropicSubscriptionToken_base: Schema.Class<AnthropicSubscriptionToken, Schema.TaggedStruct<"AnthropicSubscriptionToken", {
|
|
11
|
+
readonly token: Schema.Redacted<Schema.NonEmptyString>;
|
|
12
|
+
}>, {}>;
|
|
13
|
+
/**
|
|
14
|
+
* A Claude subscription token from `claude setup-token`, sent upstream as a bearer token with
|
|
15
|
+
* the OAuth beta flag. The token is valid for one year and has no refresh token. Anthropic
|
|
16
|
+
* doesn't document swapping a placeholder for one through a proxy; `test/placeholder-swap`
|
|
17
|
+
* checks that it works.
|
|
18
|
+
*/
|
|
19
|
+
export declare class AnthropicSubscriptionToken extends AnthropicSubscriptionToken_base {
|
|
20
|
+
}
|
|
21
|
+
/** The credential added to Anthropic requests. */
|
|
22
|
+
export declare const AnthropicCredential: Schema.Union<readonly [typeof AnthropicApiKey, typeof AnthropicSubscriptionToken]>;
|
|
23
|
+
export type AnthropicCredential = typeof AnthropicCredential.Type;
|
|
24
|
+
declare const AnthropicProvider_base: Schema.Class<AnthropicProvider, Schema.TaggedStruct<"AnthropicProvider", {
|
|
25
|
+
readonly upstream: Schema.brand<Schema.String, "UpstreamBase">;
|
|
26
|
+
readonly credential: Schema.Union<readonly [typeof AnthropicApiKey, typeof AnthropicSubscriptionToken]>;
|
|
27
|
+
}>, {}>;
|
|
28
|
+
/** Claude Code's upstream. On the Worker, `ANTHROPIC_BASE_URL` is `http://llm.<domain>/anthropic`. */
|
|
29
|
+
export declare class AnthropicProvider extends AnthropicProvider_base {
|
|
30
|
+
}
|
|
31
|
+
declare const OpenAiApiKey_base: Schema.Class<OpenAiApiKey, Schema.TaggedStruct<"OpenAiApiKey", {
|
|
32
|
+
readonly key: Schema.Redacted<Schema.NonEmptyString>;
|
|
33
|
+
}>, {}>;
|
|
34
|
+
/** An OpenAI-compatible API key, sent upstream as a bearer token. */
|
|
35
|
+
export declare class OpenAiApiKey extends OpenAiApiKey_base {
|
|
36
|
+
}
|
|
37
|
+
/** An absolute path to a file on the Gateway host. */
|
|
38
|
+
export declare const HostFilePath: Schema.brand<Schema.String, "HostFilePath">;
|
|
39
|
+
export type HostFilePath = typeof HostFilePath.Type;
|
|
40
|
+
declare const ChatGptLogin_base: Schema.Class<ChatGptLogin, Schema.TaggedStruct<"ChatGptLogin", {
|
|
41
|
+
readonly authFile: Schema.brand<Schema.String, "HostFilePath">;
|
|
42
|
+
readonly tokenUrl: Schema.brand<Schema.String, "UpstreamBase">;
|
|
43
|
+
}>, {}>;
|
|
44
|
+
/**
|
|
45
|
+
* A ChatGPT login from `codex login`, read from the `auth.json` that Codex writes. The Gateway
|
|
46
|
+
* keeps its tokens in memory, refreshes them at `tokenUrl` before they expire, and writes the
|
|
47
|
+
* rotated tokens back to `authFile`.
|
|
48
|
+
*/
|
|
49
|
+
export declare class ChatGptLogin extends ChatGptLogin_base {
|
|
50
|
+
}
|
|
51
|
+
/** The credential added to OpenAI requests. */
|
|
52
|
+
export declare const OpenAiCredential: Schema.Union<readonly [typeof OpenAiApiKey, typeof ChatGptLogin]>;
|
|
53
|
+
export type OpenAiCredential = typeof OpenAiCredential.Type;
|
|
54
|
+
declare const OpenAiProvider_base: Schema.Class<OpenAiProvider, Schema.TaggedStruct<"OpenAiProvider", {
|
|
55
|
+
readonly upstream: Schema.brand<Schema.String, "UpstreamBase">;
|
|
56
|
+
readonly credential: Schema.Union<readonly [typeof OpenAiApiKey, typeof ChatGptLogin]>;
|
|
57
|
+
}>, {}>;
|
|
58
|
+
/**
|
|
59
|
+
* Codex's upstream: an OpenAI-compatible API with an API key, or the ChatGPT backend with a
|
|
60
|
+
* ChatGPT login. On the Worker, the `model_provider` `base_url` is `http://llm.<domain>/openai/v1`
|
|
61
|
+
* in both cases.
|
|
62
|
+
*/
|
|
63
|
+
export declare class OpenAiProvider extends OpenAiProvider_base {
|
|
64
|
+
}
|
|
65
|
+
/** The LLM upstreams the Gateway forwards to. Requests for a provider without a credential are refused. */
|
|
66
|
+
export interface LlmProviders {
|
|
67
|
+
readonly anthropic: Option.Option<AnthropicProvider>;
|
|
68
|
+
readonly openai: Option.Option<OpenAiProvider>;
|
|
69
|
+
}
|
|
70
|
+
/** A provider with its credential. */
|
|
71
|
+
export type LlmProvider = AnthropicProvider | OpenAiProvider;
|
|
72
|
+
/** The `llm.<domain>` path prefixes that select a provider. */
|
|
73
|
+
export declare const LlmProviderName: Schema.Literals<readonly ["anthropic", "openai"]>;
|
|
74
|
+
export type LlmProviderName = GatewayLlmProvider;
|
|
75
|
+
/** An LLM request's provider and the path after its prefix. */
|
|
76
|
+
export interface LlmTarget {
|
|
77
|
+
readonly provider: LlmProviderName;
|
|
78
|
+
readonly rest: string;
|
|
79
|
+
}
|
|
80
|
+
/** Returns the provider from the first path segment; for example, `/anthropic/v1/messages` is Anthropic's `/v1/messages`. */
|
|
81
|
+
export declare const llmTarget: (pathname: string) => Option.Option<LlmTarget>;
|
|
82
|
+
/** Returns the credential kind sent to `provider`: a subscription for a Claude token or a ChatGPT login. */
|
|
83
|
+
export declare const credentialKind: (provider: LlmProvider) => GatewayCredentialKind;
|
|
84
|
+
/** Returns the configured provider for `name` as a single forwardable value. */
|
|
85
|
+
export declare const configuredProvider: (providers: LlmProviders, name: LlmProviderName) => Option.Option<LlmProvider>;
|
|
86
|
+
/**
|
|
87
|
+
* Returns the upstream path for `rest`, the path after the provider's prefix, if it's one of the
|
|
88
|
+
* provider's `LLM_ROUTES`. Codex calls `<base_url>/responses` with `base_url` ending in `/v1`;
|
|
89
|
+
* the ChatGPT backend serves the same routes without `/v1`, so with a ChatGPT login
|
|
90
|
+
* `/v1/responses` is the backend's `/responses`.
|
|
91
|
+
*/
|
|
92
|
+
export declare const llmRoute: (provider: LlmProvider, rest: string) => Option.Option<string>;
|
|
93
|
+
/** A credential sent as it's configured, without a refresh. */
|
|
94
|
+
export type StaticCredential = AnthropicApiKey | AnthropicSubscriptionToken | OpenAiApiKey;
|
|
95
|
+
/**
|
|
96
|
+
* Adds `credential` to headers without the caller's credential. For a subscription token, also
|
|
97
|
+
* merges the OAuth beta flag into the harness's flags.
|
|
98
|
+
*/
|
|
99
|
+
export declare const withStaticCredential: (credential: StaticCredential, headers: Headers.Headers) => Headers.Headers;
|
|
100
|
+
/** A ChatGPT login's current access token and account, as Codex sends them to the ChatGPT backend. */
|
|
101
|
+
export interface ChatGptAccess {
|
|
102
|
+
readonly accessToken: Redacted.Redacted<string>;
|
|
103
|
+
/** The ChatGPT account or workspace ID, sent as `ChatGPT-Account-ID`; `None` if the login has none. */
|
|
104
|
+
readonly accountId: Option.Option<string>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Adds a ChatGPT login's access token as a bearer token and its account as `ChatGPT-Account-ID`,
|
|
108
|
+
* replacing any account header the caller sent. Other headers, such as Codex's `originator` and
|
|
109
|
+
* `session-id`, pass unchanged.
|
|
110
|
+
*/
|
|
111
|
+
export declare const withChatGptAccess: (access: ChatGptAccess, headers: Headers.Headers) => Headers.Headers;
|
|
112
|
+
export {};
|
|
113
|
+
//# sourceMappingURL=llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/llm/llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;;;AAKtC,0DAA0D;AAC1D,qBAAa,eAAgB,SAAQ,oBAEnC;CAAG;;;;AAEL;;;;;GAKG;AACH,qBAAa,0BAA2B,SAAQ,+BAG/C;CAAG;AAEJ,kDAAkD;AAClD,eAAO,MAAM,mBAAmB,oFAA8D,CAAC;AAC/F,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAC;;;;;AAElE,sGAAsG;AACtG,qBAAa,iBAAkB,SAAQ,sBAGtC;CAAG;;;;AAEJ,qEAAqE;AACrE,qBAAa,YAAa,SAAQ,iBAEhC;CAAG;AAEL,sDAAsD;AACtD,eAAO,MAAM,YAAY,6CAEW,CAAC;AACrC,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAC;;;;;AAEpD;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,iBAGhC;CAAG;AAEL,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,mEAA6C,CAAC;AAC3E,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,IAAI,CAAC;;;;;AAE5D;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,mBAGlC;CAAG;AAEL,2GAA2G;AAC3G,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;CAChD;AAED,sCAAsC;AACtC,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAE7D,+DAA+D;AAC/D,eAAO,MAAM,eAAe,mDAAqB,CAAC;AAClD,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAIjD,+DAA+D;AAC/D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,6HAA6H;AAC7H,eAAO,MAAM,SAAS,aAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAKnE,CAAC;AAEF,4GAA4G;AAC5G,eAAO,MAAM,cAAc,aAAc,WAAW,KAAG,qBAStD,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,cAClB,YAAY,QACjB,eAAe,KACpB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAoE,CAAC;AAkBjG;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,aAAc,WAAW,QAAQ,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAIlF,CAAC;AAgBF,+DAA+D;AAC/D,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,0BAA0B,GAAG,YAAY,CAAC;AAE3F;;;GAGG;AACH,eAAO,MAAM,oBAAoB,eACnB,gBAAgB,WACnB,OAAO,CAAC,OAAO,KACvB,OAAO,CAAC,OAYV,CAAC;AAEF,sGAAsG;AACtG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChD,uGAAuG;IACvG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WACpB,aAAa,WACZ,OAAO,CAAC,OAAO,KACvB,OAAO,CAAC,OAUV,CAAC"}
|
package/dist/llm/llm.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { GatewayLlmProvider } from "@usine/core/worker";
|
|
2
|
+
import { Option, Redacted, Schema } from "effect";
|
|
3
|
+
import { Headers } from "effect/http";
|
|
4
|
+
import { UpstreamBase } from "../upstream.js";
|
|
5
|
+
const Secret = Schema.Redacted(Schema.NonEmptyString);
|
|
6
|
+
/** An Anthropic API key, sent upstream as `x-api-key`. */
|
|
7
|
+
export class AnthropicApiKey extends Schema.TaggedClass()("AnthropicApiKey", {
|
|
8
|
+
key: Secret,
|
|
9
|
+
}) {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A Claude subscription token from `claude setup-token`, sent upstream as a bearer token with
|
|
13
|
+
* the OAuth beta flag. The token is valid for one year and has no refresh token. Anthropic
|
|
14
|
+
* doesn't document swapping a placeholder for one through a proxy; `test/placeholder-swap`
|
|
15
|
+
* checks that it works.
|
|
16
|
+
*/
|
|
17
|
+
export class AnthropicSubscriptionToken extends Schema.TaggedClass()("AnthropicSubscriptionToken", { token: Secret }) {
|
|
18
|
+
}
|
|
19
|
+
/** The credential added to Anthropic requests. */
|
|
20
|
+
export const AnthropicCredential = Schema.Union([AnthropicApiKey, AnthropicSubscriptionToken]);
|
|
21
|
+
/** Claude Code's upstream. On the Worker, `ANTHROPIC_BASE_URL` is `http://llm.<domain>/anthropic`. */
|
|
22
|
+
export class AnthropicProvider extends Schema.TaggedClass()("AnthropicProvider", { upstream: UpstreamBase, credential: AnthropicCredential }) {
|
|
23
|
+
}
|
|
24
|
+
/** An OpenAI-compatible API key, sent upstream as a bearer token. */
|
|
25
|
+
export class OpenAiApiKey extends Schema.TaggedClass()("OpenAiApiKey", {
|
|
26
|
+
key: Secret,
|
|
27
|
+
}) {
|
|
28
|
+
}
|
|
29
|
+
/** An absolute path to a file on the Gateway host. */
|
|
30
|
+
export const HostFilePath = Schema.String.check(Schema.isPattern(/^\/\S*[^/\s]$/u, { message: "Expected an absolute file path" })).pipe(Schema.brand("HostFilePath"));
|
|
31
|
+
/**
|
|
32
|
+
* A ChatGPT login from `codex login`, read from the `auth.json` that Codex writes. The Gateway
|
|
33
|
+
* keeps its tokens in memory, refreshes them at `tokenUrl` before they expire, and writes the
|
|
34
|
+
* rotated tokens back to `authFile`.
|
|
35
|
+
*/
|
|
36
|
+
export class ChatGptLogin extends Schema.TaggedClass()("ChatGptLogin", {
|
|
37
|
+
authFile: HostFilePath,
|
|
38
|
+
tokenUrl: UpstreamBase,
|
|
39
|
+
}) {
|
|
40
|
+
}
|
|
41
|
+
/** The credential added to OpenAI requests. */
|
|
42
|
+
export const OpenAiCredential = Schema.Union([OpenAiApiKey, ChatGptLogin]);
|
|
43
|
+
/**
|
|
44
|
+
* Codex's upstream: an OpenAI-compatible API with an API key, or the ChatGPT backend with a
|
|
45
|
+
* ChatGPT login. On the Worker, the `model_provider` `base_url` is `http://llm.<domain>/openai/v1`
|
|
46
|
+
* in both cases.
|
|
47
|
+
*/
|
|
48
|
+
export class OpenAiProvider extends Schema.TaggedClass()("OpenAiProvider", {
|
|
49
|
+
upstream: UpstreamBase,
|
|
50
|
+
credential: OpenAiCredential,
|
|
51
|
+
}) {
|
|
52
|
+
}
|
|
53
|
+
/** The `llm.<domain>` path prefixes that select a provider. */
|
|
54
|
+
export const LlmProviderName = GatewayLlmProvider;
|
|
55
|
+
const isProviderName = Schema.is(LlmProviderName);
|
|
56
|
+
/** Returns the provider from the first path segment; for example, `/anthropic/v1/messages` is Anthropic's `/v1/messages`. */
|
|
57
|
+
export const llmTarget = (pathname) => {
|
|
58
|
+
const match = /^\/([a-z]+)(\/.*)?$/u.exec(pathname);
|
|
59
|
+
const provider = match?.[1];
|
|
60
|
+
if (match === null || provider === undefined || !isProviderName(provider))
|
|
61
|
+
return Option.none();
|
|
62
|
+
return Option.some({ provider, rest: match[2] ?? "" });
|
|
63
|
+
};
|
|
64
|
+
/** Returns the credential kind sent to `provider`: a subscription for a Claude token or a ChatGPT login. */
|
|
65
|
+
export const credentialKind = (provider) => {
|
|
66
|
+
switch (provider.credential._tag) {
|
|
67
|
+
case "AnthropicApiKey":
|
|
68
|
+
case "OpenAiApiKey":
|
|
69
|
+
return "api-key";
|
|
70
|
+
case "AnthropicSubscriptionToken":
|
|
71
|
+
case "ChatGptLogin":
|
|
72
|
+
return "subscription";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/** Returns the configured provider for `name` as a single forwardable value. */
|
|
76
|
+
export const configuredProvider = (providers, name) => (name === "anthropic" ? providers.anthropic : providers.openai);
|
|
77
|
+
/**
|
|
78
|
+
* The routes each provider serves after its prefix: those Claude Code and Codex call. Each
|
|
79
|
+
* pattern matches whole plain segments, so a percent-encoded separator or a dot segment never
|
|
80
|
+
* matches, and other parts of the provider's API, such as files or organization administration,
|
|
81
|
+
* can't be reached with the Gateway's credential.
|
|
82
|
+
*/
|
|
83
|
+
const LLM_ROUTES = {
|
|
84
|
+
anthropic: [
|
|
85
|
+
/^\/v1\/messages$/u,
|
|
86
|
+
/^\/v1\/messages\/count_tokens$/u,
|
|
87
|
+
/^\/v1\/models$/u,
|
|
88
|
+
/^\/v1\/models\/[A-Za-z0-9][A-Za-z0-9._-]*$/u,
|
|
89
|
+
],
|
|
90
|
+
openai: [/^\/v1\/responses$/u, /^\/v1\/responses\/compact$/u, /^\/v1\/models$/u],
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Returns the upstream path for `rest`, the path after the provider's prefix, if it's one of the
|
|
94
|
+
* provider's `LLM_ROUTES`. Codex calls `<base_url>/responses` with `base_url` ending in `/v1`;
|
|
95
|
+
* the ChatGPT backend serves the same routes without `/v1`, so with a ChatGPT login
|
|
96
|
+
* `/v1/responses` is the backend's `/responses`.
|
|
97
|
+
*/
|
|
98
|
+
export const llmRoute = (provider, rest) => {
|
|
99
|
+
const routes = provider._tag === "AnthropicProvider" ? LLM_ROUTES.anthropic : LLM_ROUTES.openai;
|
|
100
|
+
if (!routes.some((route) => route.test(rest)))
|
|
101
|
+
return Option.none();
|
|
102
|
+
return Option.some(provider.credential._tag === "ChatGptLogin" ? rest.slice("/v1".length) : rest);
|
|
103
|
+
};
|
|
104
|
+
const OAUTH_BETA = "oauth-2025-04-20";
|
|
105
|
+
const withBeta = (incoming, flag) => {
|
|
106
|
+
const flags = Option.match(incoming, {
|
|
107
|
+
onNone: () => [],
|
|
108
|
+
onSome: (value) => value
|
|
109
|
+
.split(",")
|
|
110
|
+
.map((part) => part.trim())
|
|
111
|
+
.filter((part) => part !== ""),
|
|
112
|
+
});
|
|
113
|
+
return (flags.includes(flag) ? flags : [...flags, flag]).join(",");
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Adds `credential` to headers without the caller's credential. For a subscription token, also
|
|
117
|
+
* merges the OAuth beta flag into the harness's flags.
|
|
118
|
+
*/
|
|
119
|
+
export const withStaticCredential = (credential, headers) => {
|
|
120
|
+
switch (credential._tag) {
|
|
121
|
+
case "OpenAiApiKey":
|
|
122
|
+
return Headers.set(headers, "authorization", `Bearer ${Redacted.value(credential.key)}`);
|
|
123
|
+
case "AnthropicApiKey":
|
|
124
|
+
return Headers.set(headers, "x-api-key", Redacted.value(credential.key));
|
|
125
|
+
case "AnthropicSubscriptionToken":
|
|
126
|
+
return Headers.setAll(headers, {
|
|
127
|
+
authorization: `Bearer ${Redacted.value(credential.token)}`,
|
|
128
|
+
"anthropic-beta": withBeta(Headers.get(headers, "anthropic-beta"), OAUTH_BETA),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Adds a ChatGPT login's access token as a bearer token and its account as `ChatGPT-Account-ID`,
|
|
134
|
+
* replacing any account header the caller sent. Other headers, such as Codex's `originator` and
|
|
135
|
+
* `session-id`, pass unchanged.
|
|
136
|
+
*/
|
|
137
|
+
export const withChatGptAccess = (access, headers) => {
|
|
138
|
+
const bearer = Headers.set(Headers.remove(headers, "chatgpt-account-id"), "authorization", `Bearer ${Redacted.value(access.accessToken)}`);
|
|
139
|
+
return Option.match(access.accountId, {
|
|
140
|
+
onNone: () => bearer,
|
|
141
|
+
onSome: (accountId) => Headers.set(bearer, "chatgpt-account-id", accountId),
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/llm/llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { CallerTokens } from "../caller/caller.ts";
|
|
3
|
+
import { GatewaySettings, SettingsInForce } from "../settings.ts";
|
|
4
|
+
import type { Listener } from "./environment.ts";
|
|
5
|
+
declare const EgressProxyListenError_base: Schema.Class<EgressProxyListenError, Schema.TaggedStruct<"EgressProxyListenError", {
|
|
6
|
+
readonly host: Schema.String;
|
|
7
|
+
readonly port: Schema.Int;
|
|
8
|
+
readonly message: Schema.String;
|
|
9
|
+
}>, import("effect/Cause").YieldableError>;
|
|
10
|
+
/** The error when the forward proxy can't listen on its address. */
|
|
11
|
+
export declare class EgressProxyListenError extends EgressProxyListenError_base {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Serves the forward proxy that Workers use for package registries and other allowlisted
|
|
15
|
+
* hosts: `CONNECT host:443` for HTTPS, and absolute-form requests (`GET http://host/...`) for
|
|
16
|
+
* plain HTTP mirrors such as apt.
|
|
17
|
+
*
|
|
18
|
+
* - A tunnel opens only after the ClientHello's server name matches the CONNECT host.
|
|
19
|
+
* - The proxy resolves names itself and refuses one that resolves to a private address.
|
|
20
|
+
* - Requests for `git.<domain>` and `llm.<domain>` on port 80 go to the reverse proxy at
|
|
21
|
+
* `gateway`, so a client that proxies everything (Claude Code with `HTTPS_PROXY`) still
|
|
22
|
+
* connects to them.
|
|
23
|
+
* - A caller token in `Proxy-Authorization` is optional and only identifies the Worker in logs.
|
|
24
|
+
* - Each refusal is logged with the host, reason, and client address.
|
|
25
|
+
* - Each request is checked against the allowlist current at arrival, so an open tunnel stays
|
|
26
|
+
* open after a reload removes its host.
|
|
27
|
+
*/
|
|
28
|
+
export declare const serveEgressProxy: (listener: Listener, gateway: Listener) => Effect.Effect<Listener, EgressProxyListenError, CallerTokens | GatewaySettings | import("effect/Scope").Scope | SettingsInForce>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=egress-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress-proxy.d.ts","sourceRoot":"","sources":["../../src/node/egress-proxy.ts"],"names":[],"mappings":"AAUA,OAAO,EAAQ,MAAM,EAAmC,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/E,OAAO,EAA0B,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAK3E,OAAO,EAAE,eAAe,EAAgB,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;;;;;;AAqCjD,oEAAoE;AACpE,qBAAa,sBAAuB,SAAQ,2BAG3C;CAAG;AA4KJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,6KA0a3B,CAAC"}
|