@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.
Files changed (91) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +200 -2
  3. package/dist/admin/api.d.ts +146 -0
  4. package/dist/admin/api.d.ts.map +1 -0
  5. package/dist/admin/api.js +105 -0
  6. package/dist/admin/api.js.map +1 -0
  7. package/dist/admin/server.d.ts +10 -0
  8. package/dist/admin/server.d.ts.map +1 -0
  9. package/dist/admin/server.js +79 -0
  10. package/dist/admin/server.js.map +1 -0
  11. package/dist/attachment/attachment.d.ts +84 -0
  12. package/dist/attachment/attachment.d.ts.map +1 -0
  13. package/dist/attachment/attachment.js +67 -0
  14. package/dist/attachment/attachment.js.map +1 -0
  15. package/dist/attachment/store.d.ts +34 -0
  16. package/dist/attachment/store.d.ts.map +1 -0
  17. package/dist/attachment/store.js +133 -0
  18. package/dist/attachment/store.js.map +1 -0
  19. package/dist/caller/caller.d.ts +60 -0
  20. package/dist/caller/caller.d.ts.map +1 -0
  21. package/dist/caller/caller.js +131 -0
  22. package/dist/caller/caller.js.map +1 -0
  23. package/dist/chatgpt/auth-file.d.ts +47 -0
  24. package/dist/chatgpt/auth-file.d.ts.map +1 -0
  25. package/dist/chatgpt/auth-file.js +132 -0
  26. package/dist/chatgpt/auth-file.js.map +1 -0
  27. package/dist/chatgpt/logins.d.ts +50 -0
  28. package/dist/chatgpt/logins.d.ts.map +1 -0
  29. package/dist/chatgpt/logins.js +303 -0
  30. package/dist/chatgpt/logins.js.map +1 -0
  31. package/dist/egress/client-hello.d.ts +120 -0
  32. package/dist/egress/client-hello.d.ts.map +1 -0
  33. package/dist/egress/client-hello.js +155 -0
  34. package/dist/egress/client-hello.js.map +1 -0
  35. package/dist/egress/egress.d.ts +66 -0
  36. package/dist/egress/egress.d.ts.map +1 -0
  37. package/dist/egress/egress.js +114 -0
  38. package/dist/egress/egress.js.map +1 -0
  39. package/dist/gateway.d.ts +27 -0
  40. package/dist/gateway.d.ts.map +1 -0
  41. package/dist/gateway.js +27 -0
  42. package/dist/gateway.js.map +1 -0
  43. package/dist/github/github.d.ts +54 -0
  44. package/dist/github/github.d.ts.map +1 -0
  45. package/dist/github/github.js +112 -0
  46. package/dist/github/github.js.map +1 -0
  47. package/dist/github/rename.d.ts +30 -0
  48. package/dist/github/rename.d.ts.map +1 -0
  49. package/dist/github/rename.js +83 -0
  50. package/dist/github/rename.js.map +1 -0
  51. package/dist/llm/llm.d.ts +113 -0
  52. package/dist/llm/llm.d.ts.map +1 -0
  53. package/dist/llm/llm.js +144 -0
  54. package/dist/llm/llm.js.map +1 -0
  55. package/dist/node/egress-proxy.d.ts +30 -0
  56. package/dist/node/egress-proxy.d.ts.map +1 -0
  57. package/dist/node/egress-proxy.js +491 -0
  58. package/dist/node/egress-proxy.js.map +1 -0
  59. package/dist/node/environment.d.ts +113 -0
  60. package/dist/node/environment.d.ts.map +1 -0
  61. package/dist/node/environment.js +140 -0
  62. package/dist/node/environment.js.map +1 -0
  63. package/dist/node/main.d.ts +3 -0
  64. package/dist/node/main.d.ts.map +1 -0
  65. package/dist/node/main.js +13 -0
  66. package/dist/node/main.js.map +1 -0
  67. package/dist/node/node.d.ts +28 -0
  68. package/dist/node/node.d.ts.map +1 -0
  69. package/dist/node/node.js +81 -0
  70. package/dist/node/node.js.map +1 -0
  71. package/dist/node/reload.d.ts +27 -0
  72. package/dist/node/reload.d.ts.map +1 -0
  73. package/dist/node/reload.js +170 -0
  74. package/dist/node/reload.js.map +1 -0
  75. package/dist/proxy/forward.d.ts +12 -0
  76. package/dist/proxy/forward.d.ts.map +1 -0
  77. package/dist/proxy/forward.js +44 -0
  78. package/dist/proxy/forward.js.map +1 -0
  79. package/dist/proxy/proxy.d.ts +29 -0
  80. package/dist/proxy/proxy.d.ts.map +1 -0
  81. package/dist/proxy/proxy.js +297 -0
  82. package/dist/proxy/proxy.js.map +1 -0
  83. package/dist/settings.d.ts +52 -0
  84. package/dist/settings.d.ts.map +1 -0
  85. package/dist/settings.js +27 -0
  86. package/dist/settings.js.map +1 -0
  87. package/dist/upstream.d.ts +5 -0
  88. package/dist/upstream.d.ts.map +1 -0
  89. package/dist/upstream.js +6 -0
  90. package/dist/upstream.js.map +1 -0
  91. 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":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAS9C,8BAA8B;AAC9B,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,oBAAoB,CAAC;IAC5C,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,wBAAwB,CAAC;IAChD,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,4BAA4B,CAAC;CACzD,CAAC;AAUF,gDAAgD;AAChD,MAAM,CAAC,MAAM,YAAY,GAAoC,IAAI,GAAG,CAAC;IACnE,CAAC,YAAY,EAAE,KAAK,CAAC;IACrB,CAAC,gBAAgB,EAAE,KAAK,CAAC;IACzB,CAAC,oBAAoB,EAAE,SAAS,CAAC;CAClC,CAAC,CAAC;AAYH,MAAM,QAAQ,GACZ,8HAA8H,CAAC;AAEjI,4HAA4H;AAC5H,MAAM,qBAAqB,GACzB,+FAA+F,CAAC;AAElG,MAAM,SAAS,GAAG;IAChB,CAAC,KAAK,EAAE,QAAQ,CAAC;IACjB,CAAC,SAAS,EAAE,qBAAqB,CAAC;CAC1B,CAAC;AAEX,MAAM,eAAe,GAAG,qCAAqC,CAAC;AAE9D,MAAM,gBAAgB,GAAG,MAAM,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC;AAEpE,+FAA+F;AAC/F,MAAM,QAAQ,GAAG,CACf,OAAsB,EACtB,QAAgB,EAChB,MAAc,EACc,EAAE;IAC9B,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,SAAS,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,IAAI;YAChB,OAAO,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC9E,QAAQ;gBACR,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,QAAQ,GAAG,MAAM,EAAE;gBAC9C,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;aACpC,CAAC,CAAC,CAAC;IACR,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC,CAAC;AAEF,iIAAiI;AACjI,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAE,IAAY,EAAE,MAAc,EAA8B,EAAE;IAChG,MAAM,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,UAAU,GACd,KAAK,KAAK,IAAI;QACZ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE;QACnC,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC,CAAC;AACN,CAAC,CAAC;AAEF,yDAAyD;AACzD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAsB,EAAE,CAC/D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEtD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,MAAc,EAAyB,EAAE,CACrF,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;IACtD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAEpB;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,OAAsB,EACtB,IAAgB,EAChB,QAAgB,EAChB,MAAc,EACc,EAAE;IAC9B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,KAAK;YACR,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,KAAK,KAAK;YACR,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjD,KAAK,SAAS;YACZ,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrD,KAAK,SAAS,EAAE,CAAC;YACf,IAAI,QAAQ,KAAK,cAAc;gBAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;YAClF,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;YACrD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;gBAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAClF,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAsB,EACtB,OAAe,EACf,QAAa,EACO,EAAE;IACtB,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACxD,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC,IAAY,EAAE,EAAE,CACrD,IAAI,GAAG,CAAC,UAAU,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjF,OAAO,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACrE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CACxF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,QAAiC,EACjC,UAAgC,EAChC,OAAwB,EACP,EAAE;IACnB,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,qBAAqB;YACxB,OAAO,OAAO,CAAC;QACjB,KAAK,wBAAwB,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC/C,OAAO,OAAO,CAAC,GAAG,CAChB,OAAO,EACP,eAAe,EACf,QAAQ,KAAK,KAAK;gBAChB,CAAC,CAAC,UAAU,KAAK,EAAE;gBACnB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,kBAAkB,KAAK,EAAE,CAAC,EAAE,CACxD,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
@@ -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"}
@@ -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":"AAAA,OAAO,EAA8B,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;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAEtD,0DAA0D;AAC1D,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,WAAW,EAAmB,CAAC,iBAAiB,EAAE;IAC5F,GAAG,EAAE,MAAM;CACZ,CAAC;CAAG;AAEL;;;;;GAKG;AACH,MAAM,OAAO,0BAA2B,SAAQ,MAAM,CAAC,WAAW,EAA8B,CAC9F,4BAA4B,EAC5B,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB;CAAG;AAEJ,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAC,CAAC;AAG/F,sGAAsG;AACtG,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,WAAW,EAAqB,CAC5E,mBAAmB,EACnB,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAC5D;CAAG;AAEJ,qEAAqE;AACrE,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,EAAgB,CAAC,cAAc,EAAE;IACnF,GAAG,EAAE,MAAM;CACZ,CAAC;CAAG;AAEL,sDAAsD;AACtD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAC7C,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAClF,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;AAGrC;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,EAAgB,CAAC,cAAc,EAAE;IACnF,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,YAAY;CACvB,CAAC;CAAG;AAEL,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAG3E;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,EAAkB,CAAC,gBAAgB,EAAE;IACzF,QAAQ,EAAE,YAAY;IACtB,UAAU,EAAE,gBAAgB;CAC7B,CAAC;CAAG;AAWL,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAGlD,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC;AAQlD,6HAA6H;AAC7H,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,QAAgB,EAA4B,EAAE;IACtE,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IAChG,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,4GAA4G;AAC5G,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAqB,EAAyB,EAAE;IAC7E,QAAQ,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACjC,KAAK,iBAAiB,CAAC;QACvB,KAAK,cAAc;YACjB,OAAO,SAAS,CAAC;QACnB,KAAK,4BAA4B,CAAC;QAClC,KAAK,cAAc;YACjB,OAAO,cAAc,CAAC;IAC1B,CAAC;AACH,CAAC,CAAC;AAEF,gFAAgF;AAChF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,SAAuB,EACvB,IAAqB,EACO,EAAE,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAEjG;;;;;GAKG;AACH,MAAM,UAAU,GAAG;IACjB,SAAS,EAAE;QACT,mBAAmB;QACnB,iCAAiC;QACjC,iBAAiB;QACjB,6CAA6C;KAC9C;IACD,MAAM,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,EAAE,iBAAiB,CAAC;CACf,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,QAAqB,EAAE,IAAY,EAAyB,EAAE;IACrF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;IAChG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACpE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACpG,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAEtC,MAAM,QAAQ,GAAG,CAAC,QAA+B,EAAE,IAAY,EAAU,EAAE;IACzE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE;QACnC,MAAM,EAAE,GAA0B,EAAE,CAAC,EAAE;QACvC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAChB,KAAK;aACF,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;KACnC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrE,CAAC,CAAC;AAKF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,UAA4B,EAC5B,OAAwB,EACP,EAAE;IACnB,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,cAAc;YACjB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3F,KAAK,iBAAiB;YACpB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,KAAK,4BAA4B;YAC/B,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE;gBAC7B,aAAa,EAAE,UAAU,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBAC3D,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,UAAU,CAAC;aAC/E,CAAC,CAAC;IACP,CAAC;AACH,CAAC,CAAC;AASF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAAqB,EACrB,OAAwB,EACP,EAAE;IACnB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CACxB,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAC7C,eAAe,EACf,UAAU,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAC/C,CAAC;IACF,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE;QACpC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM;QACpB,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,oBAAoB,EAAE,SAAS,CAAC;KAC5E,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -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"}