@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,120 @@
|
|
|
1
|
+
import { Data, Option } from "effect";
|
|
2
|
+
/** The maximum bytes the forward proxy buffers while waiting for a complete ClientHello. */
|
|
3
|
+
export declare const CLIENT_HELLO_LIMIT: number;
|
|
4
|
+
/**
|
|
5
|
+
* The result of reading a tunnel's first bytes: incomplete with the byte count needed, not a
|
|
6
|
+
* TLS ClientHello, or a ClientHello with its server name and whether it uses ECH.
|
|
7
|
+
*/
|
|
8
|
+
export type ClientHelloRead = Data.TaggedEnum<{
|
|
9
|
+
Incomplete: {
|
|
10
|
+
readonly needed: number;
|
|
11
|
+
};
|
|
12
|
+
NotTls: {
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
};
|
|
15
|
+
ClientHello: {
|
|
16
|
+
readonly serverName: Option.Option<string>;
|
|
17
|
+
readonly encryptedClientHello: boolean;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export declare const ClientHelloRead: {
|
|
21
|
+
readonly $is: <Tag extends "ClientHello" | "Incomplete" | "NotTls">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
22
|
+
readonly _tag: "ClientHello";
|
|
23
|
+
readonly serverName: Option.Option<string>;
|
|
24
|
+
readonly encryptedClientHello: boolean;
|
|
25
|
+
}, {
|
|
26
|
+
readonly _tag: Tag;
|
|
27
|
+
}> | Extract<{
|
|
28
|
+
readonly _tag: "Incomplete";
|
|
29
|
+
readonly needed: number;
|
|
30
|
+
}, {
|
|
31
|
+
readonly _tag: Tag;
|
|
32
|
+
}> | Extract<{
|
|
33
|
+
readonly _tag: "NotTls";
|
|
34
|
+
readonly detail: string;
|
|
35
|
+
}, {
|
|
36
|
+
readonly _tag: Tag;
|
|
37
|
+
}>;
|
|
38
|
+
readonly $match: {
|
|
39
|
+
<Cases extends { readonly [Tag in "ClientHello" | "Incomplete" | "NotTls"]: (args: Extract<{
|
|
40
|
+
readonly _tag: "ClientHello";
|
|
41
|
+
readonly serverName: Option.Option<string>;
|
|
42
|
+
readonly encryptedClientHello: boolean;
|
|
43
|
+
} | {
|
|
44
|
+
readonly _tag: "Incomplete";
|
|
45
|
+
readonly needed: number;
|
|
46
|
+
} | {
|
|
47
|
+
readonly _tag: "NotTls";
|
|
48
|
+
readonly detail: string;
|
|
49
|
+
}, {
|
|
50
|
+
readonly _tag: Tag;
|
|
51
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
52
|
+
readonly _tag: "ClientHello";
|
|
53
|
+
readonly serverName: Option.Option<string>;
|
|
54
|
+
readonly encryptedClientHello: boolean;
|
|
55
|
+
} | {
|
|
56
|
+
readonly _tag: "Incomplete";
|
|
57
|
+
readonly needed: number;
|
|
58
|
+
} | {
|
|
59
|
+
readonly _tag: "NotTls";
|
|
60
|
+
readonly detail: string;
|
|
61
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["ClientHello" | "Incomplete" | "NotTls"]>>;
|
|
62
|
+
<Cases extends { readonly [Tag in "ClientHello" | "Incomplete" | "NotTls"]: (args: Extract<{
|
|
63
|
+
readonly _tag: "ClientHello";
|
|
64
|
+
readonly serverName: Option.Option<string>;
|
|
65
|
+
readonly encryptedClientHello: boolean;
|
|
66
|
+
} | {
|
|
67
|
+
readonly _tag: "Incomplete";
|
|
68
|
+
readonly needed: number;
|
|
69
|
+
} | {
|
|
70
|
+
readonly _tag: "NotTls";
|
|
71
|
+
readonly detail: string;
|
|
72
|
+
}, {
|
|
73
|
+
readonly _tag: Tag;
|
|
74
|
+
}>) => any; }>(value: {
|
|
75
|
+
readonly _tag: "ClientHello";
|
|
76
|
+
readonly serverName: Option.Option<string>;
|
|
77
|
+
readonly encryptedClientHello: boolean;
|
|
78
|
+
} | {
|
|
79
|
+
readonly _tag: "Incomplete";
|
|
80
|
+
readonly needed: number;
|
|
81
|
+
} | {
|
|
82
|
+
readonly _tag: "NotTls";
|
|
83
|
+
readonly detail: string;
|
|
84
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["ClientHello" | "Incomplete" | "NotTls"]>>;
|
|
85
|
+
};
|
|
86
|
+
readonly ClientHello: Data.TaggedEnum.ConstructorFrom<{
|
|
87
|
+
readonly _tag: "ClientHello";
|
|
88
|
+
readonly serverName: Option.Option<string>;
|
|
89
|
+
readonly encryptedClientHello: boolean;
|
|
90
|
+
}, "_tag">;
|
|
91
|
+
readonly Incomplete: Data.TaggedEnum.ConstructorFrom<{
|
|
92
|
+
readonly _tag: "Incomplete";
|
|
93
|
+
readonly needed: number;
|
|
94
|
+
}, "_tag">;
|
|
95
|
+
readonly NotTls: Data.TaggedEnum.ConstructorFrom<{
|
|
96
|
+
readonly _tag: "NotTls";
|
|
97
|
+
readonly detail: string;
|
|
98
|
+
}, "_tag">;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Reads the TLS ClientHello at the start of `bytes`, joining handshake records if the client
|
|
102
|
+
* split it. For `Incomplete`, `needed` is the smallest byte count that can change the result.
|
|
103
|
+
*/
|
|
104
|
+
export declare const readClientHello: (bytes: Uint8Array) => ClientHelloRead;
|
|
105
|
+
/**
|
|
106
|
+
* A buffer for a tunnel's first bytes that parses only when the byte count the last parse
|
|
107
|
+
* needed is reached. A ClientHello longer than 16 KiB is read as `NotTls`.
|
|
108
|
+
*/
|
|
109
|
+
export declare class ClientHelloReader {
|
|
110
|
+
#private;
|
|
111
|
+
/** Adds bytes from the client and returns the current read result. */
|
|
112
|
+
push(chunk: Uint8Array): ClientHelloRead;
|
|
113
|
+
/** All bytes collected so far, in order; the proxy writes them upstream once the tunnel opens. */
|
|
114
|
+
get bytes(): Uint8Array;
|
|
115
|
+
/** The number of parses so far. */
|
|
116
|
+
get parses(): number;
|
|
117
|
+
}
|
|
118
|
+
/** Returns `host` lowercased and without a trailing dot, for comparing TLS and CONNECT host names. */
|
|
119
|
+
export declare const comparableHost: (host: string) => string;
|
|
120
|
+
//# sourceMappingURL=client-hello.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-hello.d.ts","sourceRoot":"","sources":["../../src/egress/client-hello.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEtC,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAS5C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC;IAC5C,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,WAAW,EAAE;QACX,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;KACxC,CAAC;CACH,CAAC,CAAC;AACH,eAAO,MAAM,eAAe;;;6BAJH,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;uCACX,OAAO;;;;;yBAJT,MAAM;;;;;yBACV,MAAM;;;;;;;iCAEV,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;2CACX,OAAO;;;6BAJT,MAAM;;;6BACV,MAAM;;;;;;;;;;;;;;;;iCAEV,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;2CACX,OAAO;;;6BAJT,MAAM;;;6BACV,MAAM;;;;;;;;;;;;;;;;;6BAEV,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;uCACX,OAAO;;;;yBAJT,MAAM;;;;yBACV,MAAM;;CAM8B,CAAC;AAsDlE;;;GAGG;AACH,eAAO,MAAM,eAAe,UAAW,UAAU,KAAG,eA+BnD,CAAC;AAEF;;;GAGG;AACH,qBAAa,iBAAiB;;IAM5B,sEAAsE;IACtE,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAUvC;IAED,kGAAkG;IAClG,IAAI,KAAK,IAAI,UAAU,CAWtB;IAED,mCAAmC;IACnC,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF;AAED,sGAAsG;AACtG,eAAO,MAAM,cAAc,SAAU,MAAM,KAAG,MAAgD,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Data, Option } from "effect";
|
|
2
|
+
/** The maximum bytes the forward proxy buffers while waiting for a complete ClientHello. */
|
|
3
|
+
export const CLIENT_HELLO_LIMIT = 16 * 1024;
|
|
4
|
+
const HANDSHAKE_RECORD = 0x16;
|
|
5
|
+
const CLIENT_HELLO = 0x01;
|
|
6
|
+
const SERVER_NAME = 0x0000;
|
|
7
|
+
const HOST_NAME = 0x00;
|
|
8
|
+
const ENCRYPTED_CLIENT_HELLO = 0xfe0d;
|
|
9
|
+
const MAX_RECORD_LENGTH = 1 << 14;
|
|
10
|
+
export const ClientHelloRead = Data.taggedEnum();
|
|
11
|
+
const notTls = (detail) => ClientHelloRead.NotTls({ detail });
|
|
12
|
+
const u16 = (bytes, at) => ((bytes[at] ?? 0) << 8) | (bytes[at + 1] ?? 0);
|
|
13
|
+
/** Returns the `host_name` entry of a `server_name` extension (RFC 6066, section 3). */
|
|
14
|
+
const hostName = (data) => {
|
|
15
|
+
if (data.length < 2 || u16(data, 0) !== data.length - 2)
|
|
16
|
+
return Option.none();
|
|
17
|
+
let at = 2;
|
|
18
|
+
while (at + 3 <= data.length) {
|
|
19
|
+
const type = data[at];
|
|
20
|
+
const length = u16(data, at + 1);
|
|
21
|
+
const start = at + 3;
|
|
22
|
+
if (start + length > data.length)
|
|
23
|
+
return Option.none();
|
|
24
|
+
if (type === HOST_NAME && length > 0)
|
|
25
|
+
return Option.some(new TextDecoder().decode(data.subarray(start, start + length)));
|
|
26
|
+
at = start + length;
|
|
27
|
+
}
|
|
28
|
+
return Option.none();
|
|
29
|
+
};
|
|
30
|
+
/** Parses a ClientHello body (RFC 8446, section 4.1.2). */
|
|
31
|
+
const clientHelloBody = (body) => {
|
|
32
|
+
let at = 2 + 32;
|
|
33
|
+
if (at + 1 > body.length)
|
|
34
|
+
return notTls("ClientHello is truncated");
|
|
35
|
+
at += 1 + (body[at] ?? 0);
|
|
36
|
+
if (at + 2 > body.length)
|
|
37
|
+
return notTls("ClientHello is truncated");
|
|
38
|
+
at += 2 + u16(body, at);
|
|
39
|
+
if (at + 1 > body.length)
|
|
40
|
+
return notTls("ClientHello is truncated");
|
|
41
|
+
at += 1 + (body[at] ?? 0);
|
|
42
|
+
if (at > body.length)
|
|
43
|
+
return notTls("ClientHello is truncated");
|
|
44
|
+
if (at === body.length)
|
|
45
|
+
return ClientHelloRead.ClientHello({ serverName: Option.none(), encryptedClientHello: false });
|
|
46
|
+
if (at + 2 > body.length)
|
|
47
|
+
return notTls("ClientHello extensions are truncated");
|
|
48
|
+
const end = at + 2 + u16(body, at);
|
|
49
|
+
if (end > body.length)
|
|
50
|
+
return notTls("ClientHello extensions are truncated");
|
|
51
|
+
at += 2;
|
|
52
|
+
let serverName = Option.none();
|
|
53
|
+
let encryptedClientHello = false;
|
|
54
|
+
while (at + 4 <= end) {
|
|
55
|
+
const type = u16(body, at);
|
|
56
|
+
const length = u16(body, at + 2);
|
|
57
|
+
const start = at + 4;
|
|
58
|
+
if (start + length > end)
|
|
59
|
+
return notTls("a ClientHello extension is truncated");
|
|
60
|
+
if (type === SERVER_NAME && Option.isNone(serverName))
|
|
61
|
+
serverName = hostName(body.subarray(start, start + length));
|
|
62
|
+
if (type === ENCRYPTED_CLIENT_HELLO)
|
|
63
|
+
encryptedClientHello = true;
|
|
64
|
+
at = start + length;
|
|
65
|
+
}
|
|
66
|
+
return ClientHelloRead.ClientHello({ serverName, encryptedClientHello });
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Reads the TLS ClientHello at the start of `bytes`, joining handshake records if the client
|
|
70
|
+
* split it. For `Incomplete`, `needed` is the smallest byte count that can change the result.
|
|
71
|
+
*/
|
|
72
|
+
export const readClientHello = (bytes) => {
|
|
73
|
+
const chunks = [];
|
|
74
|
+
let handshakeLength = 0;
|
|
75
|
+
let at = 0;
|
|
76
|
+
for (;;) {
|
|
77
|
+
if (bytes.length > at && bytes[at] !== HANDSHAKE_RECORD)
|
|
78
|
+
return notTls("the first bytes are not a TLS handshake record");
|
|
79
|
+
if (bytes.length < at + 5)
|
|
80
|
+
return ClientHelloRead.Incomplete({ needed: at + 5 });
|
|
81
|
+
if (bytes[at + 1] !== 0x03)
|
|
82
|
+
return notTls("the record version is not TLS");
|
|
83
|
+
const length = u16(bytes, at + 3);
|
|
84
|
+
if (length === 0 || length > MAX_RECORD_LENGTH)
|
|
85
|
+
return notTls("the record length is invalid");
|
|
86
|
+
if (bytes.length < at + 5 + length)
|
|
87
|
+
return ClientHelloRead.Incomplete({ needed: at + 5 + length });
|
|
88
|
+
chunks.push(bytes.subarray(at + 5, at + 5 + length));
|
|
89
|
+
handshakeLength += length;
|
|
90
|
+
at += 5 + length;
|
|
91
|
+
if (handshakeLength < 4)
|
|
92
|
+
continue;
|
|
93
|
+
const handshake = new Uint8Array(handshakeLength);
|
|
94
|
+
let filled = 0;
|
|
95
|
+
for (const chunk of chunks) {
|
|
96
|
+
handshake.set(chunk, filled);
|
|
97
|
+
filled += chunk.length;
|
|
98
|
+
}
|
|
99
|
+
if (handshake[0] !== CLIENT_HELLO)
|
|
100
|
+
return notTls("the handshake is not a ClientHello");
|
|
101
|
+
const messageLength = ((handshake[1] ?? 0) << 16) | u16(handshake, 2);
|
|
102
|
+
if (messageLength > CLIENT_HELLO_LIMIT)
|
|
103
|
+
return notTls("the ClientHello is too large");
|
|
104
|
+
if (handshakeLength >= 4 + messageLength)
|
|
105
|
+
return clientHelloBody(handshake.subarray(4, 4 + messageLength));
|
|
106
|
+
const rest = at + 5 + (4 + messageLength - handshakeLength);
|
|
107
|
+
if (bytes.length < rest)
|
|
108
|
+
return ClientHelloRead.Incomplete({ needed: rest });
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* A buffer for a tunnel's first bytes that parses only when the byte count the last parse
|
|
113
|
+
* needed is reached. A ClientHello longer than 16 KiB is read as `NotTls`.
|
|
114
|
+
*/
|
|
115
|
+
export class ClientHelloReader {
|
|
116
|
+
#chunks = [];
|
|
117
|
+
#length = 0;
|
|
118
|
+
#needed = 1;
|
|
119
|
+
#parses = 0;
|
|
120
|
+
/** Adds bytes from the client and returns the current read result. */
|
|
121
|
+
push(chunk) {
|
|
122
|
+
this.#chunks.push(chunk);
|
|
123
|
+
this.#length += chunk.length;
|
|
124
|
+
if (this.#length < this.#needed)
|
|
125
|
+
return ClientHelloRead.Incomplete({ needed: this.#needed });
|
|
126
|
+
this.#parses += 1;
|
|
127
|
+
const read = readClientHello(this.bytes);
|
|
128
|
+
if (read._tag !== "Incomplete")
|
|
129
|
+
return read;
|
|
130
|
+
if (read.needed > CLIENT_HELLO_LIMIT)
|
|
131
|
+
return notTls("the ClientHello is larger than 16 KiB");
|
|
132
|
+
this.#needed = read.needed;
|
|
133
|
+
return read;
|
|
134
|
+
}
|
|
135
|
+
/** All bytes collected so far, in order; the proxy writes them upstream once the tunnel opens. */
|
|
136
|
+
get bytes() {
|
|
137
|
+
if (this.#chunks.length !== 1) {
|
|
138
|
+
const joined = new Uint8Array(this.#length);
|
|
139
|
+
let at = 0;
|
|
140
|
+
for (const chunk of this.#chunks) {
|
|
141
|
+
joined.set(chunk, at);
|
|
142
|
+
at += chunk.length;
|
|
143
|
+
}
|
|
144
|
+
this.#chunks = [joined];
|
|
145
|
+
}
|
|
146
|
+
return this.#chunks[0] ?? new Uint8Array(0);
|
|
147
|
+
}
|
|
148
|
+
/** The number of parses so far. */
|
|
149
|
+
get parses() {
|
|
150
|
+
return this.#parses;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Returns `host` lowercased and without a trailing dot, for comparing TLS and CONNECT host names. */
|
|
154
|
+
export const comparableHost = (host) => host.toLowerCase().replace(/\.$/u, "");
|
|
155
|
+
//# sourceMappingURL=client-hello.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-hello.js","sourceRoot":"","sources":["../../src/egress/client-hello.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Result, Schema } from "effect";
|
|
2
|
+
/** A lowercase DNS host name without a trailing dot. */
|
|
3
|
+
export declare const HostName: Schema.brand<Schema.String, "HostName">;
|
|
4
|
+
export type HostName = typeof HostName.Type;
|
|
5
|
+
export declare const Port: Schema.brand<Schema.Int, "Port">;
|
|
6
|
+
export type Port = typeof Port.Type;
|
|
7
|
+
declare const EgressRule_base: Schema.Class<EgressRule, Schema.Struct<{
|
|
8
|
+
readonly host: Schema.brand<Schema.String, "HostName">;
|
|
9
|
+
readonly match: Schema.Literals<readonly ["exact", "subdomains"]>;
|
|
10
|
+
readonly ports: Schema.NonEmptyArray<Schema.brand<Schema.Int, "Port">>;
|
|
11
|
+
}>, {}>;
|
|
12
|
+
/**
|
|
13
|
+
* An allowlist line. `exact` permits only that host; `subdomains` permits every name below the
|
|
14
|
+
* domain (`*.pythonhosted.org`) but not the domain itself.
|
|
15
|
+
*/
|
|
16
|
+
export declare class EgressRule extends EgressRule_base {
|
|
17
|
+
/** Returns whether this rule permits `host` on any port. */
|
|
18
|
+
covers(host: HostName): boolean;
|
|
19
|
+
}
|
|
20
|
+
declare const EgressAllowlistError_base: Schema.Class<EgressAllowlistError, Schema.TaggedStruct<"EgressAllowlistError", {
|
|
21
|
+
readonly line: Schema.Int;
|
|
22
|
+
readonly message: Schema.String;
|
|
23
|
+
}>, import("effect/Cause").YieldableError>;
|
|
24
|
+
/** The error for an unreadable allowlist file, with the line and the problem. */
|
|
25
|
+
export declare class EgressAllowlistError extends EgressAllowlistError_base {
|
|
26
|
+
}
|
|
27
|
+
declare const EgressPermitted_base: Schema.Class<EgressPermitted, Schema.TaggedStruct<"EgressPermitted", {
|
|
28
|
+
readonly host: Schema.brand<Schema.String, "HostName">;
|
|
29
|
+
readonly port: Schema.brand<Schema.Int, "Port">;
|
|
30
|
+
readonly rule: typeof EgressRule;
|
|
31
|
+
}>, {}>;
|
|
32
|
+
/** A permitted destination and the rule that permits it. */
|
|
33
|
+
export declare class EgressPermitted extends EgressPermitted_base {
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The reason a destination is refused: unlisted host or port, an IP address, or a name that
|
|
37
|
+
* resolves to a private, loopback, or link-local address. For a tunnel, also: the first bytes
|
|
38
|
+
* aren't a TLS ClientHello, or the ClientHello has no server name, hides it with ECH, or has a
|
|
39
|
+
* different server name than the CONNECT host.
|
|
40
|
+
*/
|
|
41
|
+
export declare const EgressRefusalReason: Schema.Literals<readonly ["host-not-allowed", "port-not-allowed", "address-literal", "private-address", "not-tls", "server-name-missing", "server-name-mismatch"]>;
|
|
42
|
+
export type EgressRefusalReason = typeof EgressRefusalReason.Type;
|
|
43
|
+
declare const EgressRefused_base: Schema.Class<EgressRefused, Schema.TaggedStruct<"EgressRefused", {
|
|
44
|
+
readonly host: Schema.String;
|
|
45
|
+
readonly port: Schema.Int;
|
|
46
|
+
readonly reason: Schema.Literals<readonly ["host-not-allowed", "port-not-allowed", "address-literal", "private-address", "not-tls", "server-name-missing", "server-name-mismatch"]>;
|
|
47
|
+
}>, {}>;
|
|
48
|
+
/** A refused destination. `host` is the raw host the client requested. */
|
|
49
|
+
export declare class EgressRefused extends EgressRefused_base {
|
|
50
|
+
}
|
|
51
|
+
export type EgressDecision = EgressPermitted | EgressRefused;
|
|
52
|
+
declare const EgressAllowlist_base: Schema.Class<EgressAllowlist, Schema.Struct<{
|
|
53
|
+
readonly rules: Schema.$Array<typeof EgressRule>;
|
|
54
|
+
}>, {}>;
|
|
55
|
+
/** The hosts Workers can connect to through the forward proxy. Unlisted hosts and all IP addresses are refused. */
|
|
56
|
+
export declare class EgressAllowlist extends EgressAllowlist_base {
|
|
57
|
+
/** Returns the decision for a connection to `host` on `port`. */
|
|
58
|
+
decide(host: string, port: number): EgressDecision;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parses an allowlist file with one rule per line: `host` or `*.domain`, optionally with
|
|
62
|
+
* `:port` (443 and 80 by default). `#` starts a comment and blank lines are skipped.
|
|
63
|
+
*/
|
|
64
|
+
export declare const parseEgressAllowlist: (text: string) => Result.Result<EgressAllowlist, EgressAllowlistError>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=egress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.d.ts","sourceRoot":"","sources":["../../src/egress/egress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhD,wDAAwD;AACxD,eAAO,MAAM,QAAQ,yCAOW,CAAC;AACjC,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C,eAAO,MAAM,IAAI,kCAEhB,CAAC;AACF,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC;;;;;;AAKpC;;;GAGG;AACH,qBAAa,UAAW,SAAQ,eAI9B;IACA,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAE9B;CACF;;;;;AAED,iFAAiF;AACjF,qBAAa,oBAAqB,SAAQ,yBAGzC;CAAG;;;;;;AAEJ,4DAA4D;AAC5D,qBAAa,eAAgB,SAAQ,oBAInC;CAAG;AAEL;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,oKAQ9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAC;;;;;;AAElE,0EAA0E;AAC1E,qBAAa,aAAc,SAAQ,kBAIjC;CAAG;AAEL,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,aAAa,CAAC;;;;AAO7D,mHAAmH;AACnH,qBAAa,eAAgB,SAAQ,oBAEC;IACpC,iEAAiE;IACjE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAYjD;CACF;AA6BD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,SACzB,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,oBAAoB,CAWrD,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Option, Result, Schema } from "effect";
|
|
2
|
+
/** A lowercase DNS host name without a trailing dot. */
|
|
3
|
+
export const HostName = Schema.String.check(Schema.isPattern(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/u, {
|
|
4
|
+
message: "Expected a lowercase DNS host name",
|
|
5
|
+
})).pipe(Schema.brand("HostName"));
|
|
6
|
+
export const Port = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65_535 })).pipe(Schema.brand("Port"));
|
|
7
|
+
/** The ports for an allowlist line without a port: HTTPS, and HTTP for apt mirrors. */
|
|
8
|
+
const DEFAULT_PORTS = [Port.make(443), Port.make(80)];
|
|
9
|
+
/**
|
|
10
|
+
* An allowlist line. `exact` permits only that host; `subdomains` permits every name below the
|
|
11
|
+
* domain (`*.pythonhosted.org`) but not the domain itself.
|
|
12
|
+
*/
|
|
13
|
+
export class EgressRule extends Schema.Class("@usine/gateway/EgressRule")({
|
|
14
|
+
host: HostName,
|
|
15
|
+
match: Schema.Literals(["exact", "subdomains"]),
|
|
16
|
+
ports: Schema.NonEmptyArray(Port),
|
|
17
|
+
}) {
|
|
18
|
+
/** Returns whether this rule permits `host` on any port. */
|
|
19
|
+
covers(host) {
|
|
20
|
+
return this.match === "exact" ? host === this.host : host.endsWith(`.${this.host}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** The error for an unreadable allowlist file, with the line and the problem. */
|
|
24
|
+
export class EgressAllowlistError extends Schema.TaggedError()("EgressAllowlistError", { line: Schema.Int, message: Schema.String }) {
|
|
25
|
+
}
|
|
26
|
+
/** A permitted destination and the rule that permits it. */
|
|
27
|
+
export class EgressPermitted extends Schema.TaggedClass()("EgressPermitted", {
|
|
28
|
+
host: HostName,
|
|
29
|
+
port: Port,
|
|
30
|
+
rule: EgressRule,
|
|
31
|
+
}) {
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The reason a destination is refused: unlisted host or port, an IP address, or a name that
|
|
35
|
+
* resolves to a private, loopback, or link-local address. For a tunnel, also: the first bytes
|
|
36
|
+
* aren't a TLS ClientHello, or the ClientHello has no server name, hides it with ECH, or has a
|
|
37
|
+
* different server name than the CONNECT host.
|
|
38
|
+
*/
|
|
39
|
+
export const EgressRefusalReason = Schema.Literals([
|
|
40
|
+
"host-not-allowed",
|
|
41
|
+
"port-not-allowed",
|
|
42
|
+
"address-literal",
|
|
43
|
+
"private-address",
|
|
44
|
+
"not-tls",
|
|
45
|
+
"server-name-missing",
|
|
46
|
+
"server-name-mismatch",
|
|
47
|
+
]);
|
|
48
|
+
/** A refused destination. `host` is the raw host the client requested. */
|
|
49
|
+
export class EgressRefused extends Schema.TaggedClass()("EgressRefused", {
|
|
50
|
+
host: Schema.String,
|
|
51
|
+
port: Schema.Int,
|
|
52
|
+
reason: EgressRefusalReason,
|
|
53
|
+
}) {
|
|
54
|
+
}
|
|
55
|
+
const IPV4_LITERAL = /^\d{1,3}(?:\.\d{1,3}){3}$/u;
|
|
56
|
+
const isHostName = Schema.is(HostName);
|
|
57
|
+
const isPort = Schema.is(Port);
|
|
58
|
+
/** The hosts Workers can connect to through the forward proxy. Unlisted hosts and all IP addresses are refused. */
|
|
59
|
+
export class EgressAllowlist extends Schema.Class("@usine/gateway/EgressAllowlist")({ rules: Schema.Array(EgressRule) }) {
|
|
60
|
+
/** Returns the decision for a connection to `host` on `port`. */
|
|
61
|
+
decide(host, port) {
|
|
62
|
+
const normalized = host.toLowerCase().replace(/\.$/u, "");
|
|
63
|
+
if (IPV4_LITERAL.test(normalized) || normalized.includes(":"))
|
|
64
|
+
return new EgressRefused({ host, port, reason: "address-literal" });
|
|
65
|
+
if (!isHostName(normalized) || !isPort(port))
|
|
66
|
+
return new EgressRefused({ host, port, reason: "host-not-allowed" });
|
|
67
|
+
const covering = this.rules.filter((rule) => rule.covers(normalized));
|
|
68
|
+
if (covering.length === 0)
|
|
69
|
+
return new EgressRefused({ host, port, reason: "host-not-allowed" });
|
|
70
|
+
const rule = covering.find((candidate) => candidate.ports.includes(port));
|
|
71
|
+
return rule === undefined
|
|
72
|
+
? new EgressRefused({ host, port, reason: "port-not-allowed" })
|
|
73
|
+
: new EgressPermitted({ host: normalized, port, rule });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const parseLine = (text, line) => {
|
|
77
|
+
const match = /^(\*\.)?([^:\s]+)(?::(\d+))?$/u.exec(text);
|
|
78
|
+
const host = match?.[2]?.toLowerCase();
|
|
79
|
+
if (match === null || host === undefined || !isHostName(host) || IPV4_LITERAL.test(host))
|
|
80
|
+
return Result.fail(new EgressAllowlistError({
|
|
81
|
+
line,
|
|
82
|
+
message: `Expected a host name, or *. and a domain, optionally followed by :port, got "${text}"`,
|
|
83
|
+
}));
|
|
84
|
+
const port = Option.fromNullishOr(match[3]).pipe(Option.map(Number));
|
|
85
|
+
if (Option.isSome(port) && !isPort(port.value))
|
|
86
|
+
return Result.fail(new EgressAllowlistError({ line, message: `Port ${port.value} is not between 1 and 65535` }));
|
|
87
|
+
return Result.succeed(new EgressRule({
|
|
88
|
+
host,
|
|
89
|
+
match: match[1] === undefined ? "exact" : "subdomains",
|
|
90
|
+
ports: Option.match(port, {
|
|
91
|
+
onNone: () => DEFAULT_PORTS,
|
|
92
|
+
onSome: (value) => [Port.make(value)],
|
|
93
|
+
}),
|
|
94
|
+
}));
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Parses an allowlist file with one rule per line: `host` or `*.domain`, optionally with
|
|
98
|
+
* `:port` (443 and 80 by default). `#` starts a comment and blank lines are skipped.
|
|
99
|
+
*/
|
|
100
|
+
export const parseEgressAllowlist = (text) => {
|
|
101
|
+
const rules = [];
|
|
102
|
+
const lines = text.split(/\r?\n/u);
|
|
103
|
+
for (const [index, raw] of lines.entries()) {
|
|
104
|
+
const content = raw.replace(/#.*$/u, "").trim();
|
|
105
|
+
if (content === "")
|
|
106
|
+
continue;
|
|
107
|
+
const parsed = parseLine(content, index + 1);
|
|
108
|
+
if (Result.isFailure(parsed))
|
|
109
|
+
return Result.fail(parsed.failure);
|
|
110
|
+
rules.push(parsed.success);
|
|
111
|
+
}
|
|
112
|
+
return Result.succeed(new EgressAllowlist({ rules }));
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=egress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.js","sourceRoot":"","sources":["../../src/egress/egress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { Attachments } from "./attachment/store.ts";
|
|
3
|
+
import { CallerTokens } from "./caller/caller.ts";
|
|
4
|
+
import { ChatGptLogins } from "./chatgpt/logins.ts";
|
|
5
|
+
import { SettingsInForce } from "./settings.ts";
|
|
6
|
+
export { adminRoutes } from "./admin/server.ts";
|
|
7
|
+
export { AnonymousCredential, Attachment, AttachmentAlreadyExpired, AttachmentConflict, AttachmentCredential, AttachmentId, type AttachmentLookup, ExpiredAttachment, InstallationCredential, LiveAttachment, MissingAttachment, } from "./attachment/attachment.ts";
|
|
8
|
+
export { Attachments } from "./attachment/store.ts";
|
|
9
|
+
export { CallerClaims, CallerToken, CallerTokenRejected, CallerTokens, GATEWAY_TOKEN_HEADER, tokenFromAuthorization, } from "./caller/caller.ts";
|
|
10
|
+
export { type EgressDecision, EgressAllowlist, EgressAllowlistError, EgressPermitted, EgressRefusalReason, EgressRefused, EgressRule, HostName, parseEgressAllowlist, Port, } from "./egress/egress.ts";
|
|
11
|
+
export { GITHUB_ORIGINS, type GitHubOrigins } from "./github/github.ts";
|
|
12
|
+
export { ChatGptAuthFileError, ChatGptTokens } from "./chatgpt/auth-file.ts";
|
|
13
|
+
export { CHATGPT_TOKEN_URL, ChatGptLoginUnavailable, ChatGptLogins } from "./chatgpt/logins.ts";
|
|
14
|
+
export { AnthropicApiKey, AnthropicCredential, AnthropicProvider, AnthropicSubscriptionToken, type ChatGptAccess, ChatGptLogin, HostFilePath, type LlmProvider, type LlmProviders, OpenAiApiKey, OpenAiCredential, OpenAiProvider, } from "./llm/llm.ts";
|
|
15
|
+
export { endToEndHeaders } from "./proxy/forward.ts";
|
|
16
|
+
export { GatewayProxy, REFUSAL_HEADER, RefusalReason } from "./proxy/proxy.ts";
|
|
17
|
+
export { GatewayDomain } from "@usine/core/worker";
|
|
18
|
+
export { AdminToken, CallerKey, GatewaySettings, gatewayHosts, type ReloadableSettings, SettingsInForce, } from "./settings.ts";
|
|
19
|
+
export { UpstreamBase } from "./upstream.ts";
|
|
20
|
+
/**
|
|
21
|
+
* The Gateway's shared state for one process: the attachment store, the caller token signer,
|
|
22
|
+
* the current settings, and the ChatGPT logins. Provide this layer once to the reverse proxy,
|
|
23
|
+
* forward proxy, admin API, and reload. The logins need a `FileSystem` for their auth files and
|
|
24
|
+
* an `HttpClient` for refreshes.
|
|
25
|
+
*/
|
|
26
|
+
export declare const gatewayServices: Layer.Layer<Attachments | CallerTokens | ChatGptLogins | SettingsInForce, never, import("effect/FileSystem").FileSystem | import("./settings.ts").GatewaySettings | import("effect/http/HttpClient").HttpClient>;
|
|
27
|
+
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,cAAc,EACnB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,IAAI,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,YAAY,EACZ,gBAAgB,EAChB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,UAAU,EACV,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,kBAAkB,EACvB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kNAK3B,CAAC"}
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { Attachments } from "./attachment/store.js";
|
|
3
|
+
import { CallerTokens } from "./caller/caller.js";
|
|
4
|
+
import { ChatGptLogins } from "./chatgpt/logins.js";
|
|
5
|
+
import { SettingsInForce } from "./settings.js";
|
|
6
|
+
export { adminRoutes } from "./admin/server.js";
|
|
7
|
+
export { AnonymousCredential, Attachment, AttachmentAlreadyExpired, AttachmentConflict, AttachmentCredential, AttachmentId, ExpiredAttachment, InstallationCredential, LiveAttachment, MissingAttachment, } from "./attachment/attachment.js";
|
|
8
|
+
export { Attachments } from "./attachment/store.js";
|
|
9
|
+
export { CallerClaims, CallerToken, CallerTokenRejected, CallerTokens, GATEWAY_TOKEN_HEADER, tokenFromAuthorization, } from "./caller/caller.js";
|
|
10
|
+
export { EgressAllowlist, EgressAllowlistError, EgressPermitted, EgressRefusalReason, EgressRefused, EgressRule, HostName, parseEgressAllowlist, Port, } from "./egress/egress.js";
|
|
11
|
+
export { GITHUB_ORIGINS } from "./github/github.js";
|
|
12
|
+
export { ChatGptAuthFileError, ChatGptTokens } from "./chatgpt/auth-file.js";
|
|
13
|
+
export { CHATGPT_TOKEN_URL, ChatGptLoginUnavailable, ChatGptLogins } from "./chatgpt/logins.js";
|
|
14
|
+
export { AnthropicApiKey, AnthropicCredential, AnthropicProvider, AnthropicSubscriptionToken, ChatGptLogin, HostFilePath, OpenAiApiKey, OpenAiCredential, OpenAiProvider, } from "./llm/llm.js";
|
|
15
|
+
export { endToEndHeaders } from "./proxy/forward.js";
|
|
16
|
+
export { GatewayProxy, REFUSAL_HEADER, RefusalReason } from "./proxy/proxy.js";
|
|
17
|
+
export { GatewayDomain } from "@usine/core/worker";
|
|
18
|
+
export { AdminToken, CallerKey, GatewaySettings, gatewayHosts, SettingsInForce, } from "./settings.js";
|
|
19
|
+
export { UpstreamBase } from "./upstream.js";
|
|
20
|
+
/**
|
|
21
|
+
* The Gateway's shared state for one process: the attachment store, the caller token signer,
|
|
22
|
+
* the current settings, and the ChatGPT logins. Provide this layer once to the reverse proxy,
|
|
23
|
+
* forward proxy, admin API, and reload. The logins need a `FileSystem` for their auth files and
|
|
24
|
+
* an `HttpClient` for refreshes.
|
|
25
|
+
*/
|
|
26
|
+
export const gatewayServices = Layer.mergeAll(Attachments.layer, CallerTokens.layer, SettingsInForce.layer, ChatGptLogins.layer);
|
|
27
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EAEZ,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,IAAI,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAsB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,0BAA0B,EAE1B,YAAY,EACZ,YAAY,EAGZ,YAAY,EACZ,gBAAgB,EAChB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,UAAU,EACV,SAAS,EACT,eAAe,EACf,YAAY,EAEZ,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,QAAQ,CAC3C,WAAW,CAAC,KAAK,EACjB,YAAY,CAAC,KAAK,EAClB,eAAe,CAAC,KAAK,EACrB,aAAa,CAAC,KAAK,CACpB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { Option } from "effect";
|
|
3
|
+
import { Headers } from "effect/http";
|
|
4
|
+
import type { AttachmentCredential } from "../attachment/attachment.ts";
|
|
5
|
+
import { UpstreamBase } from "../upstream.ts";
|
|
6
|
+
/** The upstream origins for GitHub requests: `web` for git smart HTTP, `api` for REST and GraphQL, `uploads` for release asset uploads. */
|
|
7
|
+
export interface GitHubOrigins {
|
|
8
|
+
readonly web: UpstreamBase;
|
|
9
|
+
readonly api: UpstreamBase;
|
|
10
|
+
readonly uploads: UpstreamBase;
|
|
11
|
+
}
|
|
12
|
+
/** The github.com origins. */
|
|
13
|
+
export declare const GITHUB_ORIGINS: GitHubOrigins;
|
|
14
|
+
/**
|
|
15
|
+
* The host a GitHub request was sent to. `gateway` is `git.<domain>`, which serves git at
|
|
16
|
+
* `/OWNER/REPO.git` and the API under `/api/` like GitHub Enterprise Server, so `gh` works with
|
|
17
|
+
* `GH_HOST` set to it. The others are GitHub's own hosts, used when `gh` sends requests through
|
|
18
|
+
* its `http_unix_socket`, for example to follow a URL from an API response.
|
|
19
|
+
*/
|
|
20
|
+
export type GitHubHost = "gateway" | "web" | "api" | "uploads";
|
|
21
|
+
/** GitHub's host names and what each serves. */
|
|
22
|
+
export declare const GITHUB_HOSTS: ReadonlyMap<string, GitHubHost>;
|
|
23
|
+
/**
|
|
24
|
+
* A forwarded GitHub request: git smart HTTP or a release download for a repository, or an API
|
|
25
|
+
* call. `repository` is the repository in the path; GraphQL and calls such as `/user` have none.
|
|
26
|
+
*/
|
|
27
|
+
export interface GitHubRoute {
|
|
28
|
+
readonly protocol: "git" | "release" | "api";
|
|
29
|
+
readonly upstream: string;
|
|
30
|
+
readonly repository: Option.Option<RepositoryName>;
|
|
31
|
+
}
|
|
32
|
+
/** Returns `value` as a URL if it is an absolute URL. */
|
|
33
|
+
export declare const absoluteUrl: (value: string) => Option.Option<URL>;
|
|
34
|
+
/** Returns `pathname` after `prefix`, if it is `prefix` or a path under it. */
|
|
35
|
+
export declare const afterPrefix: (pathname: string, prefix: string) => Option.Option<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the upstream URL and repository for a GitHub request. Returns `None` for paths
|
|
38
|
+
* outside git smart HTTP, Git LFS, release downloads, and the API, such as web pages.
|
|
39
|
+
*/
|
|
40
|
+
export declare const githubRoute: (origins: GitHubOrigins, host: GitHubHost, pathname: string, search: string) => Option.Option<GitHubRoute>;
|
|
41
|
+
/**
|
|
42
|
+
* Returns `location` on `git.<domain>` if it points at github.com or api.github.com, with its
|
|
43
|
+
* path and query, so a Worker follows GitHub's redirects, such as those for a renamed
|
|
44
|
+
* repository, through the gateway. API paths move under `/api/v3`. Returns `None` for other
|
|
45
|
+
* hosts, such as the release asset hosts.
|
|
46
|
+
*/
|
|
47
|
+
export declare const gatewayLocation: (origins: GitHubOrigins, gitHost: string, location: URL) => Option.Option<URL>;
|
|
48
|
+
/**
|
|
49
|
+
* Adds the attachment's credential to a GitHub request whose caller credential was removed: as
|
|
50
|
+
* the `x-access-token` password for github.com, which serves git and release downloads, or as a
|
|
51
|
+
* bearer token for the API. An anonymous attachment adds nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare const withGitHubCredential: (protocol: GitHubRoute["protocol"], credential: AttachmentCredential, headers: Headers.Headers) => Headers.Headers;
|
|
54
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/github/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAA4B,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,2IAA2I;AAC3I,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAED,8BAA8B;AAC9B,eAAO,MAAM,cAAc,EAAE,aAI5B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/D,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAIvD,CAAC;AAEH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;CACpD;AAmDD,yDAAyD;AACzD,eAAO,MAAM,WAAW,UAAW,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CACR,CAAC;AAEtD,+EAA+E;AAC/E,eAAO,MAAM,WAAW,aAAc,MAAM,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAGhE,CAAC;AAEpB;;;GAGG;AACH,eAAO,MAAM,WAAW,YACb,aAAa,QAChB,UAAU,YACN,MAAM,UACR,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,WAAW,CAiB3B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,YACjB,aAAa,WACb,MAAM,YACL,GAAG,KACZ,MAAM,CAAC,MAAM,CAAC,GAAG,CAOnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,aACrB,WAAW,CAAC,UAAU,CAAC,cACrB,oBAAoB,WACvB,OAAO,CAAC,OAAO,KACvB,OAAO,CAAC,OAeV,CAAC"}
|