@dbx-tools/cli 0.9.58 → 0.9.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -58
- package/bin/dbx-graphiti.ts +5 -0
- package/bin/dbx-lakebase-proxy.ts +4 -0
- package/bin/dbx-model-gateway.ts +5 -0
- package/index.ts +35 -1
- package/lib/bin/dbx-graphiti.d.ts +2 -0
- package/lib/bin/dbx-graphiti.js +4 -0
- package/lib/bin/dbx-lakebase-proxy.d.ts +2 -0
- package/lib/bin/dbx-lakebase-proxy.js +4 -0
- package/lib/bin/dbx-model-gateway.d.ts +2 -0
- package/lib/bin/dbx-model-gateway.js +4 -0
- package/lib/index.d.ts +35 -1
- package/lib/index.js +26 -2
- package/lib/src/appkit/cli.d.ts +24 -0
- package/lib/src/appkit/cli.js +54 -0
- package/lib/src/appkit/env-export.d.ts +40 -0
- package/lib/src/appkit/env-export.js +99 -0
- package/lib/src/auth/cli.d.ts +20 -0
- package/lib/src/auth/cli.js +118 -0
- package/lib/src/auth/options.d.ts +41 -0
- package/lib/src/auth/options.js +64 -0
- package/lib/src/cli.d.ts +2 -0
- package/lib/src/cli.js +13 -7
- package/lib/src/graphiti/cli.d.ts +18 -0
- package/lib/src/graphiti/cli.js +43 -0
- package/lib/src/graphiti/options.d.ts +10 -0
- package/lib/src/graphiti/options.js +10 -0
- package/lib/src/lakebase-proxy/cancellation.d.ts +22 -0
- package/lib/src/lakebase-proxy/cancellation.js +100 -0
- package/lib/src/lakebase-proxy/cli.d.ts +13 -0
- package/lib/src/lakebase-proxy/cli.js +54 -0
- package/lib/src/lakebase-proxy/options.d.ts +23 -0
- package/lib/src/lakebase-proxy/options.js +41 -0
- package/lib/src/lakebase-proxy/protocol.d.ts +37 -0
- package/lib/src/lakebase-proxy/protocol.js +124 -0
- package/lib/src/lakebase-proxy/proxy.d.ts +22 -0
- package/lib/src/lakebase-proxy/proxy.js +200 -0
- package/lib/src/model-gateway/cli.d.ts +19 -0
- package/lib/src/model-gateway/cli.js +63 -0
- package/lib/src/model-gateway/server.d.ts +13 -0
- package/lib/src/model-gateway/server.js +53 -0
- package/lib/src/tunnel/app.d.ts +20 -0
- package/lib/src/tunnel/app.js +52 -0
- package/lib/src/tunnel/cli.d.ts +27 -0
- package/lib/src/tunnel/cli.js +190 -0
- package/lib/src/tunnel/options.d.ts +72 -0
- package/lib/src/tunnel/options.js +138 -0
- package/lib/src/tunnel/proxy.d.ts +51 -0
- package/lib/src/tunnel/proxy.js +143 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +124 -9
- package/src/appkit/cli.ts +72 -0
- package/src/appkit/env-export.ts +124 -0
- package/src/auth/cli.ts +165 -0
- package/src/auth/options.ts +79 -0
- package/src/cli.ts +22 -6
- package/src/graphiti/cli.ts +75 -0
- package/src/graphiti/options.ts +20 -0
- package/src/lakebase-proxy/cancellation.ts +116 -0
- package/src/lakebase-proxy/cli.ts +80 -0
- package/src/lakebase-proxy/options.ts +56 -0
- package/src/lakebase-proxy/protocol.ts +170 -0
- package/src/lakebase-proxy/proxy.ts +256 -0
- package/src/model-gateway/cli.ts +89 -0
- package/src/model-gateway/server.ts +62 -0
- package/src/tunnel/app.ts +58 -0
- package/src/tunnel/cli.ts +218 -0
- package/src/tunnel/options.ts +169 -0
- package/src/tunnel/proxy.ts +194 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Minimal PostgreSQL startup protocol helpers for the loopback proxy. */
|
|
2
|
+
import type { Socket } from "node:net";
|
|
3
|
+
/** PostgreSQL SSLRequest protocol code. */
|
|
4
|
+
export declare const SSL_REQUEST = 80877103;
|
|
5
|
+
/** PostgreSQL CancelRequest protocol code. */
|
|
6
|
+
export declare const CANCEL_REQUEST = 80877102;
|
|
7
|
+
/** PostgreSQL protocol version 3 code. */
|
|
8
|
+
export declare const PROTOCOL_3 = 196608;
|
|
9
|
+
/** Parsed PostgreSQL startup packet. */
|
|
10
|
+
export interface StartupMessage {
|
|
11
|
+
kind: "startup";
|
|
12
|
+
protocol: number;
|
|
13
|
+
parameters: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/** Parsed PostgreSQL cancellation packet. */
|
|
16
|
+
export interface CancelMessage {
|
|
17
|
+
kind: "cancel";
|
|
18
|
+
processId: number;
|
|
19
|
+
secretKey: number;
|
|
20
|
+
}
|
|
21
|
+
/** First supported packet received from a PostgreSQL client. */
|
|
22
|
+
export type InitialMessage = StartupMessage | CancelMessage;
|
|
23
|
+
/** Error caused by an invalid or unsupported PostgreSQL startup packet. */
|
|
24
|
+
export declare class PostgresProtocolError extends Error {
|
|
25
|
+
}
|
|
26
|
+
/** Read and classify the first PostgreSQL frontend message from a socket. */
|
|
27
|
+
export declare function readInitialMessage(socket: Socket, timeoutMs: number): Promise<InitialMessage>;
|
|
28
|
+
/** Build a PostgreSQL SSL negotiation request packet. */
|
|
29
|
+
export declare function sslRequest(): Buffer;
|
|
30
|
+
/** Build a PostgreSQL cancellation request packet. */
|
|
31
|
+
export declare function cancelRequest(processId: number, secretKey: number): Buffer;
|
|
32
|
+
/** Build the backend messages that complete PostgreSQL startup. */
|
|
33
|
+
export declare function startupComplete(parameters: ReadonlyMap<string, string>, processId: number, secretKey: number, transactionStatus?: string): Buffer;
|
|
34
|
+
/** Replace client startup identity with the resolved upstream identity. */
|
|
35
|
+
export declare function upstreamStartupParameters(parameters: Record<string, string>, user: string, database: string): Record<string, string>;
|
|
36
|
+
/** Build a fatal PostgreSQL error response. */
|
|
37
|
+
export declare function fatalError(message: string, sqlstate?: string): Buffer;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** Minimal PostgreSQL startup protocol helpers for the loopback proxy. */
|
|
2
|
+
/** PostgreSQL SSLRequest protocol code. */
|
|
3
|
+
export const SSL_REQUEST = 80_877_103;
|
|
4
|
+
/** PostgreSQL CancelRequest protocol code. */
|
|
5
|
+
export const CANCEL_REQUEST = 80_877_102;
|
|
6
|
+
/** PostgreSQL protocol version 3 code. */
|
|
7
|
+
export const PROTOCOL_3 = 196_608;
|
|
8
|
+
/** Error caused by an invalid or unsupported PostgreSQL startup packet. */
|
|
9
|
+
export class PostgresProtocolError extends Error {
|
|
10
|
+
}
|
|
11
|
+
/** Read and classify the first PostgreSQL frontend message from a socket. */
|
|
12
|
+
export async function readInitialMessage(socket, timeoutMs) {
|
|
13
|
+
const deadline = Date.now() + timeoutMs;
|
|
14
|
+
for (;;) {
|
|
15
|
+
const packet = await readPacket(socket, deadline);
|
|
16
|
+
const code = packet.readInt32BE(4);
|
|
17
|
+
if (code === SSL_REQUEST) {
|
|
18
|
+
socket.write("N");
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (code === CANCEL_REQUEST) {
|
|
22
|
+
if (packet.length !== 16)
|
|
23
|
+
throw new PostgresProtocolError("Invalid CancelRequest packet");
|
|
24
|
+
return {
|
|
25
|
+
kind: "cancel",
|
|
26
|
+
processId: packet.readInt32BE(8),
|
|
27
|
+
secretKey: packet.readInt32BE(12),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (code !== PROTOCOL_3) {
|
|
31
|
+
throw new PostgresProtocolError(`Unsupported PostgreSQL protocol version ${code}`);
|
|
32
|
+
}
|
|
33
|
+
return { kind: "startup", protocol: code, parameters: parseParameters(packet.subarray(8)) };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Build a PostgreSQL SSL negotiation request packet. */
|
|
37
|
+
export function sslRequest() {
|
|
38
|
+
return requestPacket(SSL_REQUEST);
|
|
39
|
+
}
|
|
40
|
+
/** Build a PostgreSQL cancellation request packet. */
|
|
41
|
+
export function cancelRequest(processId, secretKey) {
|
|
42
|
+
return requestPacket(CANCEL_REQUEST, processId, secretKey);
|
|
43
|
+
}
|
|
44
|
+
/** Build the backend messages that complete PostgreSQL startup. */
|
|
45
|
+
export function startupComplete(parameters, processId, secretKey, transactionStatus = "I") {
|
|
46
|
+
return Buffer.concat([
|
|
47
|
+
backendMessage("R", int32(0)),
|
|
48
|
+
...[...parameters].map(([name, value]) => backendMessage("S", Buffer.from(`${name}\0${value}\0`))),
|
|
49
|
+
backendMessage("K", Buffer.concat([int32(processId), int32(secretKey)])),
|
|
50
|
+
backendMessage("Z", Buffer.from(transactionStatus.slice(0, 1) || "I")),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
/** Replace client startup identity with the resolved upstream identity. */
|
|
54
|
+
export function upstreamStartupParameters(parameters, user, database) {
|
|
55
|
+
return { ...parameters, user, database };
|
|
56
|
+
}
|
|
57
|
+
/** Build a fatal PostgreSQL error response. */
|
|
58
|
+
export function fatalError(message, sqlstate = "08001") {
|
|
59
|
+
return backendMessage("E", Buffer.concat([Buffer.from(`SFATAL\0C${sqlstate}\0M${message}\0`), Buffer.from([0])]));
|
|
60
|
+
}
|
|
61
|
+
function requestPacket(code, processId, secretKey) {
|
|
62
|
+
const values = [code];
|
|
63
|
+
if (processId !== undefined && secretKey !== undefined)
|
|
64
|
+
values.push(processId, secretKey);
|
|
65
|
+
return Buffer.concat([int32(4 + values.length * 4), ...values.map(int32)]);
|
|
66
|
+
}
|
|
67
|
+
function backendMessage(code, body) {
|
|
68
|
+
return Buffer.concat([Buffer.from(code), int32(body.length + 4), body]);
|
|
69
|
+
}
|
|
70
|
+
function int32(value) {
|
|
71
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
72
|
+
buffer.writeInt32BE(value);
|
|
73
|
+
return buffer;
|
|
74
|
+
}
|
|
75
|
+
async function readPacket(socket, deadline) {
|
|
76
|
+
let buffer = Buffer.alloc(0);
|
|
77
|
+
while (buffer.length < 4)
|
|
78
|
+
buffer = Buffer.concat([buffer, await readChunk(socket, deadline)]);
|
|
79
|
+
const length = buffer.readInt32BE(0);
|
|
80
|
+
if (length < 8 || length > 1024 * 1024) {
|
|
81
|
+
throw new PostgresProtocolError("Invalid PostgreSQL startup packet");
|
|
82
|
+
}
|
|
83
|
+
while (buffer.length < length) {
|
|
84
|
+
buffer = Buffer.concat([buffer, await readChunk(socket, deadline)]);
|
|
85
|
+
}
|
|
86
|
+
const remainder = buffer.subarray(length);
|
|
87
|
+
if (remainder.length)
|
|
88
|
+
socket.unshift(remainder);
|
|
89
|
+
return buffer.subarray(0, length);
|
|
90
|
+
}
|
|
91
|
+
function readChunk(socket, deadline) {
|
|
92
|
+
const remaining = deadline - Date.now();
|
|
93
|
+
if (remaining <= 0) {
|
|
94
|
+
return Promise.reject(new PostgresProtocolError("PostgreSQL startup timed out"));
|
|
95
|
+
}
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const timer = setTimeout(() => finish(new PostgresProtocolError("PostgreSQL startup timed out")), remaining);
|
|
98
|
+
const onData = (chunk) => finish(undefined, chunk);
|
|
99
|
+
const onError = (error) => finish(error);
|
|
100
|
+
const onEnd = () => finish(new PostgresProtocolError("PostgreSQL client closed during startup"));
|
|
101
|
+
const finish = (error, chunk) => {
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
socket.off("data", onData);
|
|
104
|
+
socket.off("error", onError);
|
|
105
|
+
socket.off("end", onEnd);
|
|
106
|
+
if (error)
|
|
107
|
+
reject(error);
|
|
108
|
+
else
|
|
109
|
+
resolve(chunk);
|
|
110
|
+
};
|
|
111
|
+
socket.once("data", onData);
|
|
112
|
+
socket.once("error", onError);
|
|
113
|
+
socket.once("end", onEnd);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function parseParameters(body) {
|
|
117
|
+
const values = body.toString("utf8").split("\0");
|
|
118
|
+
const parameters = {};
|
|
119
|
+
for (let index = 0; index + 1 < values.length && values[index]; index += 2) {
|
|
120
|
+
parameters[values[index]] = values[index + 1] ?? "";
|
|
121
|
+
}
|
|
122
|
+
return parameters;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdG9jb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFrZWJhc2UtcHJveHkvcHJvdG9jb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMEVBQTBFO0FBSTFFLDJDQUEyQztBQUMzQyxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUFDO0FBQ3RDLDhDQUE4QztBQUM5QyxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsVUFBVSxDQUFDO0FBQ3pDLDBDQUEwQztBQUMxQyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDO0FBbUJsQywyRUFBMkU7QUFDM0UsTUFBTSxPQUFPLHFCQUFzQixTQUFRLEtBQUs7Q0FBRztBQUVuRCw2RUFBNkU7QUFDN0UsTUFBTSxDQUFDLEtBQUssVUFBVSxrQkFBa0IsQ0FDdEMsTUFBYyxFQUNkLFNBQWlCO0lBRWpCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLENBQUM7SUFDeEMsU0FBUyxDQUFDO1FBQ1IsTUFBTSxNQUFNLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2xELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkMsSUFBSSxJQUFJLEtBQUssV0FBVyxFQUFFLENBQUM7WUFDekIsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNsQixTQUFTO1FBQ1gsQ0FBQztRQUNELElBQUksSUFBSSxLQUFLLGNBQWMsRUFBRSxDQUFDO1lBQzVCLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxFQUFFO2dCQUFFLE1BQU0sSUFBSSxxQkFBcUIsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1lBQzFGLE9BQU87Z0JBQ0wsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsU0FBUyxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDO2dCQUNoQyxTQUFTLEVBQUUsTUFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7YUFDbEMsQ0FBQztRQUNKLENBQUM7UUFDRCxJQUFJLElBQUksS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUN4QixNQUFNLElBQUkscUJBQXFCLENBQUMsMkNBQTJDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDckYsQ0FBQztRQUNELE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLGVBQWUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUM5RixDQUFDO0FBQ0gsQ0FBQztBQUVELHlEQUF5RDtBQUN6RCxNQUFNLFVBQVUsVUFBVTtJQUN4QixPQUFPLGFBQWEsQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUNwQyxDQUFDO0FBRUQsc0RBQXNEO0FBQ3RELE1BQU0sVUFBVSxhQUFhLENBQUMsU0FBaUIsRUFBRSxTQUFpQjtJQUNoRSxPQUFPLGFBQWEsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQzdELENBQUM7QUFFRCxtRUFBbUU7QUFDbkUsTUFBTSxVQUFVLGVBQWUsQ0FDN0IsVUFBdUMsRUFDdkMsU0FBaUIsRUFDakIsU0FBaUIsRUFDakIsaUJBQWlCLEdBQUcsR0FBRztJQUV2QixPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDbkIsY0FBYyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDN0IsR0FBRyxDQUFDLEdBQUcsVUFBVSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUN2QyxjQUFjLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLEtBQUssS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUN4RDtRQUNELGNBQWMsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLGNBQWMsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO0tBQ3ZFLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCwyRUFBMkU7QUFDM0UsTUFBTSxVQUFVLHlCQUF5QixDQUN2QyxVQUFrQyxFQUNsQyxJQUFZLEVBQ1osUUFBZ0I7SUFFaEIsT0FBTyxFQUFFLEdBQUcsVUFBVSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUMzQyxDQUFDO0FBRUQsK0NBQStDO0FBQy9DLE1BQU0sVUFBVSxVQUFVLENBQUMsT0FBZSxFQUFFLFFBQVEsR0FBRyxPQUFPO0lBQzVELE9BQU8sY0FBYyxDQUNuQixHQUFHLEVBQ0gsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxRQUFRLE1BQU0sT0FBTyxJQUFJLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQ3RGLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsSUFBWSxFQUFFLFNBQWtCLEVBQUUsU0FBa0I7SUFDekUsTUFBTSxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0QixJQUFJLFNBQVMsS0FBSyxTQUFTLElBQUksU0FBUyxLQUFLLFNBQVM7UUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUMxRixPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUM3RSxDQUFDO0FBRUQsU0FBUyxjQUFjLENBQUMsSUFBWSxFQUFFLElBQVk7SUFDaEQsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0FBQzFFLENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxLQUFhO0lBQzFCLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDckMsTUFBTSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQixPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQsS0FBSyxVQUFVLFVBQVUsQ0FBQyxNQUFjLEVBQUUsUUFBZ0I7SUFDeEQsSUFBSSxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3QixPQUFPLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUFFLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxFQUFFLE1BQU0sU0FBUyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDOUYsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNyQyxJQUFJLE1BQU0sR0FBRyxDQUFDLElBQUksTUFBTSxHQUFHLElBQUksR0FBRyxJQUFJLEVBQUUsQ0FBQztRQUN2QyxNQUFNLElBQUkscUJBQXFCLENBQUMsbUNBQW1DLENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUMsTUFBTSxHQUFHLE1BQU0sRUFBRSxDQUFDO1FBQzlCLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxFQUFFLE1BQU0sU0FBUyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDMUMsSUFBSSxTQUFTLENBQUMsTUFBTTtRQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDaEQsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUNwQyxDQUFDO0FBRUQsU0FBUyxTQUFTLENBQUMsTUFBYyxFQUFFLFFBQWdCO0lBQ2pELE1BQU0sU0FBUyxHQUFHLFFBQVEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDeEMsSUFBSSxTQUFTLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDbkIsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUkscUJBQXFCLENBQUMsOEJBQThCLENBQUMsQ0FBQyxDQUFDO0lBQ25GLENBQUM7SUFDRCxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1FBQ3JDLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FDdEIsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUkscUJBQXFCLENBQUMsOEJBQThCLENBQUMsQ0FBQyxFQUN2RSxTQUFTLENBQ1YsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzNELE1BQU0sT0FBTyxHQUFHLENBQUMsS0FBWSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDaEQsTUFBTSxLQUFLLEdBQUcsR0FBRyxFQUFFLENBQ2pCLE1BQU0sQ0FBQyxJQUFJLHFCQUFxQixDQUFDLHlDQUF5QyxDQUFDLENBQUMsQ0FBQztRQUMvRSxNQUFNLE1BQU0sR0FBRyxDQUFDLEtBQWEsRUFBRSxLQUFjLEVBQUUsRUFBRTtZQUMvQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDM0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDN0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDekIsSUFBSSxLQUFLO2dCQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQzs7Z0JBQ3BCLE9BQU8sQ0FBQyxLQUFNLENBQUMsQ0FBQztRQUN2QixDQUFDLENBQUM7UUFDRixNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUM1QixNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM5QixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztJQUM1QixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLGVBQWUsQ0FBQyxJQUFZO0lBQ25DLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2pELE1BQU0sVUFBVSxHQUEyQixFQUFFLENBQUM7SUFDOUMsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxLQUFLLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDM0UsVUFBVSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3ZELENBQUM7SUFDRCxPQUFPLFVBQVUsQ0FBQztBQUNwQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqIE1pbmltYWwgUG9zdGdyZVNRTCBzdGFydHVwIHByb3RvY29sIGhlbHBlcnMgZm9yIHRoZSBsb29wYmFjayBwcm94eS4gKi9cblxuaW1wb3J0IHR5cGUgeyBTb2NrZXQgfSBmcm9tIFwibm9kZTpuZXRcIjtcblxuLyoqIFBvc3RncmVTUUwgU1NMUmVxdWVzdCBwcm90b2NvbCBjb2RlLiAqL1xuZXhwb3J0IGNvbnN0IFNTTF9SRVFVRVNUID0gODBfODc3XzEwMztcbi8qKiBQb3N0Z3JlU1FMIENhbmNlbFJlcXVlc3QgcHJvdG9jb2wgY29kZS4gKi9cbmV4cG9ydCBjb25zdCBDQU5DRUxfUkVRVUVTVCA9IDgwXzg3N18xMDI7XG4vKiogUG9zdGdyZVNRTCBwcm90b2NvbCB2ZXJzaW9uIDMgY29kZS4gKi9cbmV4cG9ydCBjb25zdCBQUk9UT0NPTF8zID0gMTk2XzYwODtcblxuLyoqIFBhcnNlZCBQb3N0Z3JlU1FMIHN0YXJ0dXAgcGFja2V0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydHVwTWVzc2FnZSB7XG4gIGtpbmQ6IFwic3RhcnR1cFwiO1xuICBwcm90b2NvbDogbnVtYmVyO1xuICBwYXJhbWV0ZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xufVxuXG4vKiogUGFyc2VkIFBvc3RncmVTUUwgY2FuY2VsbGF0aW9uIHBhY2tldC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2FuY2VsTWVzc2FnZSB7XG4gIGtpbmQ6IFwiY2FuY2VsXCI7XG4gIHByb2Nlc3NJZDogbnVtYmVyO1xuICBzZWNyZXRLZXk6IG51bWJlcjtcbn1cblxuLyoqIEZpcnN0IHN1cHBvcnRlZCBwYWNrZXQgcmVjZWl2ZWQgZnJvbSBhIFBvc3RncmVTUUwgY2xpZW50LiAqL1xuZXhwb3J0IHR5cGUgSW5pdGlhbE1lc3NhZ2UgPSBTdGFydHVwTWVzc2FnZSB8IENhbmNlbE1lc3NhZ2U7XG5cbi8qKiBFcnJvciBjYXVzZWQgYnkgYW4gaW52YWxpZCBvciB1bnN1cHBvcnRlZCBQb3N0Z3JlU1FMIHN0YXJ0dXAgcGFja2V0LiAqL1xuZXhwb3J0IGNsYXNzIFBvc3RncmVzUHJvdG9jb2xFcnJvciBleHRlbmRzIEVycm9yIHt9XG5cbi8qKiBSZWFkIGFuZCBjbGFzc2lmeSB0aGUgZmlyc3QgUG9zdGdyZVNRTCBmcm9udGVuZCBtZXNzYWdlIGZyb20gYSBzb2NrZXQuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZEluaXRpYWxNZXNzYWdlKFxuICBzb2NrZXQ6IFNvY2tldCxcbiAgdGltZW91dE1zOiBudW1iZXIsXG4pOiBQcm9taXNlPEluaXRpYWxNZXNzYWdlPiB7XG4gIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHRpbWVvdXRNcztcbiAgZm9yICg7Oykge1xuICAgIGNvbnN0IHBhY2tldCA9IGF3YWl0IHJlYWRQYWNrZXQoc29ja2V0LCBkZWFkbGluZSk7XG4gICAgY29uc3QgY29kZSA9IHBhY2tldC5yZWFkSW50MzJCRSg0KTtcbiAgICBpZiAoY29kZSA9PT0gU1NMX1JFUVVFU1QpIHtcbiAgICAgIHNvY2tldC53cml0ZShcIk5cIik7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgaWYgKGNvZGUgPT09IENBTkNFTF9SRVFVRVNUKSB7XG4gICAgICBpZiAocGFja2V0Lmxlbmd0aCAhPT0gMTYpIHRocm93IG5ldyBQb3N0Z3Jlc1Byb3RvY29sRXJyb3IoXCJJbnZhbGlkIENhbmNlbFJlcXVlc3QgcGFja2V0XCIpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2luZDogXCJjYW5jZWxcIixcbiAgICAgICAgcHJvY2Vzc0lkOiBwYWNrZXQucmVhZEludDMyQkUoOCksXG4gICAgICAgIHNlY3JldEtleTogcGFja2V0LnJlYWRJbnQzMkJFKDEyKSxcbiAgICAgIH07XG4gICAgfVxuICAgIGlmIChjb2RlICE9PSBQUk9UT0NPTF8zKSB7XG4gICAgICB0aHJvdyBuZXcgUG9zdGdyZXNQcm90b2NvbEVycm9yKGBVbnN1cHBvcnRlZCBQb3N0Z3JlU1FMIHByb3RvY29sIHZlcnNpb24gJHtjb2RlfWApO1xuICAgIH1cbiAgICByZXR1cm4geyBraW5kOiBcInN0YXJ0dXBcIiwgcHJvdG9jb2w6IGNvZGUsIHBhcmFtZXRlcnM6IHBhcnNlUGFyYW1ldGVycyhwYWNrZXQuc3ViYXJyYXkoOCkpIH07XG4gIH1cbn1cblxuLyoqIEJ1aWxkIGEgUG9zdGdyZVNRTCBTU0wgbmVnb3RpYXRpb24gcmVxdWVzdCBwYWNrZXQuICovXG5leHBvcnQgZnVuY3Rpb24gc3NsUmVxdWVzdCgpOiBCdWZmZXIge1xuICByZXR1cm4gcmVxdWVzdFBhY2tldChTU0xfUkVRVUVTVCk7XG59XG5cbi8qKiBCdWlsZCBhIFBvc3RncmVTUUwgY2FuY2VsbGF0aW9uIHJlcXVlc3QgcGFja2V0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbmNlbFJlcXVlc3QocHJvY2Vzc0lkOiBudW1iZXIsIHNlY3JldEtleTogbnVtYmVyKTogQnVmZmVyIHtcbiAgcmV0dXJuIHJlcXVlc3RQYWNrZXQoQ0FOQ0VMX1JFUVVFU1QsIHByb2Nlc3NJZCwgc2VjcmV0S2V5KTtcbn1cblxuLyoqIEJ1aWxkIHRoZSBiYWNrZW5kIG1lc3NhZ2VzIHRoYXQgY29tcGxldGUgUG9zdGdyZVNRTCBzdGFydHVwLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0dXBDb21wbGV0ZShcbiAgcGFyYW1ldGVyczogUmVhZG9ubHlNYXA8c3RyaW5nLCBzdHJpbmc+LFxuICBwcm9jZXNzSWQ6IG51bWJlcixcbiAgc2VjcmV0S2V5OiBudW1iZXIsXG4gIHRyYW5zYWN0aW9uU3RhdHVzID0gXCJJXCIsXG4pOiBCdWZmZXIge1xuICByZXR1cm4gQnVmZmVyLmNvbmNhdChbXG4gICAgYmFja2VuZE1lc3NhZ2UoXCJSXCIsIGludDMyKDApKSxcbiAgICAuLi5bLi4ucGFyYW1ldGVyc10ubWFwKChbbmFtZSwgdmFsdWVdKSA9PlxuICAgICAgYmFja2VuZE1lc3NhZ2UoXCJTXCIsIEJ1ZmZlci5mcm9tKGAke25hbWV9XFwwJHt2YWx1ZX1cXDBgKSksXG4gICAgKSxcbiAgICBiYWNrZW5kTWVzc2FnZShcIktcIiwgQnVmZmVyLmNvbmNhdChbaW50MzIocHJvY2Vzc0lkKSwgaW50MzIoc2VjcmV0S2V5KV0pKSxcbiAgICBiYWNrZW5kTWVzc2FnZShcIlpcIiwgQnVmZmVyLmZyb20odHJhbnNhY3Rpb25TdGF0dXMuc2xpY2UoMCwgMSkgfHwgXCJJXCIpKSxcbiAgXSk7XG59XG5cbi8qKiBSZXBsYWNlIGNsaWVudCBzdGFydHVwIGlkZW50aXR5IHdpdGggdGhlIHJlc29sdmVkIHVwc3RyZWFtIGlkZW50aXR5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVwc3RyZWFtU3RhcnR1cFBhcmFtZXRlcnMoXG4gIHBhcmFtZXRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4gIHVzZXI6IHN0cmluZyxcbiAgZGF0YWJhc2U6IHN0cmluZyxcbik6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xuICByZXR1cm4geyAuLi5wYXJhbWV0ZXJzLCB1c2VyLCBkYXRhYmFzZSB9O1xufVxuXG4vKiogQnVpbGQgYSBmYXRhbCBQb3N0Z3JlU1FMIGVycm9yIHJlc3BvbnNlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZhdGFsRXJyb3IobWVzc2FnZTogc3RyaW5nLCBzcWxzdGF0ZSA9IFwiMDgwMDFcIik6IEJ1ZmZlciB7XG4gIHJldHVybiBiYWNrZW5kTWVzc2FnZShcbiAgICBcIkVcIixcbiAgICBCdWZmZXIuY29uY2F0KFtCdWZmZXIuZnJvbShgU0ZBVEFMXFwwQyR7c3Fsc3RhdGV9XFwwTSR7bWVzc2FnZX1cXDBgKSwgQnVmZmVyLmZyb20oWzBdKV0pLFxuICApO1xufVxuXG5mdW5jdGlvbiByZXF1ZXN0UGFja2V0KGNvZGU6IG51bWJlciwgcHJvY2Vzc0lkPzogbnVtYmVyLCBzZWNyZXRLZXk/OiBudW1iZXIpOiBCdWZmZXIge1xuICBjb25zdCB2YWx1ZXMgPSBbY29kZV07XG4gIGlmIChwcm9jZXNzSWQgIT09IHVuZGVmaW5lZCAmJiBzZWNyZXRLZXkgIT09IHVuZGVmaW5lZCkgdmFsdWVzLnB1c2gocHJvY2Vzc0lkLCBzZWNyZXRLZXkpO1xuICByZXR1cm4gQnVmZmVyLmNvbmNhdChbaW50MzIoNCArIHZhbHVlcy5sZW5ndGggKiA0KSwgLi4udmFsdWVzLm1hcChpbnQzMildKTtcbn1cblxuZnVuY3Rpb24gYmFja2VuZE1lc3NhZ2UoY29kZTogc3RyaW5nLCBib2R5OiBCdWZmZXIpOiBCdWZmZXIge1xuICByZXR1cm4gQnVmZmVyLmNvbmNhdChbQnVmZmVyLmZyb20oY29kZSksIGludDMyKGJvZHkubGVuZ3RoICsgNCksIGJvZHldKTtcbn1cblxuZnVuY3Rpb24gaW50MzIodmFsdWU6IG51bWJlcik6IEJ1ZmZlciB7XG4gIGNvbnN0IGJ1ZmZlciA9IEJ1ZmZlci5hbGxvY1Vuc2FmZSg0KTtcbiAgYnVmZmVyLndyaXRlSW50MzJCRSh2YWx1ZSk7XG4gIHJldHVybiBidWZmZXI7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRQYWNrZXQoc29ja2V0OiBTb2NrZXQsIGRlYWRsaW5lOiBudW1iZXIpOiBQcm9taXNlPEJ1ZmZlcj4ge1xuICBsZXQgYnVmZmVyID0gQnVmZmVyLmFsbG9jKDApO1xuICB3aGlsZSAoYnVmZmVyLmxlbmd0aCA8IDQpIGJ1ZmZlciA9IEJ1ZmZlci5jb25jYXQoW2J1ZmZlciwgYXdhaXQgcmVhZENodW5rKHNvY2tldCwgZGVhZGxpbmUpXSk7XG4gIGNvbnN0IGxlbmd0aCA9IGJ1ZmZlci5yZWFkSW50MzJCRSgwKTtcbiAgaWYgKGxlbmd0aCA8IDggfHwgbGVuZ3RoID4gMTAyNCAqIDEwMjQpIHtcbiAgICB0aHJvdyBuZXcgUG9zdGdyZXNQcm90b2NvbEVycm9yKFwiSW52YWxpZCBQb3N0Z3JlU1FMIHN0YXJ0dXAgcGFja2V0XCIpO1xuICB9XG4gIHdoaWxlIChidWZmZXIubGVuZ3RoIDwgbGVuZ3RoKSB7XG4gICAgYnVmZmVyID0gQnVmZmVyLmNvbmNhdChbYnVmZmVyLCBhd2FpdCByZWFkQ2h1bmsoc29ja2V0LCBkZWFkbGluZSldKTtcbiAgfVxuICBjb25zdCByZW1haW5kZXIgPSBidWZmZXIuc3ViYXJyYXkobGVuZ3RoKTtcbiAgaWYgKHJlbWFpbmRlci5sZW5ndGgpIHNvY2tldC51bnNoaWZ0KHJlbWFpbmRlcik7XG4gIHJldHVybiBidWZmZXIuc3ViYXJyYXkoMCwgbGVuZ3RoKTtcbn1cblxuZnVuY3Rpb24gcmVhZENodW5rKHNvY2tldDogU29ja2V0LCBkZWFkbGluZTogbnVtYmVyKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgY29uc3QgcmVtYWluaW5nID0gZGVhZGxpbmUgLSBEYXRlLm5vdygpO1xuICBpZiAocmVtYWluaW5nIDw9IDApIHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IFBvc3RncmVzUHJvdG9jb2xFcnJvcihcIlBvc3RncmVTUUwgc3RhcnR1cCB0aW1lZCBvdXRcIikpO1xuICB9XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgKCkgPT4gZmluaXNoKG5ldyBQb3N0Z3Jlc1Byb3RvY29sRXJyb3IoXCJQb3N0Z3JlU1FMIHN0YXJ0dXAgdGltZWQgb3V0XCIpKSxcbiAgICAgIHJlbWFpbmluZyxcbiAgICApO1xuICAgIGNvbnN0IG9uRGF0YSA9IChjaHVuazogQnVmZmVyKSA9PiBmaW5pc2godW5kZWZpbmVkLCBjaHVuayk7XG4gICAgY29uc3Qgb25FcnJvciA9IChlcnJvcjogRXJyb3IpID0+IGZpbmlzaChlcnJvcik7XG4gICAgY29uc3Qgb25FbmQgPSAoKSA9PlxuICAgICAgZmluaXNoKG5ldyBQb3N0Z3Jlc1Byb3RvY29sRXJyb3IoXCJQb3N0Z3JlU1FMIGNsaWVudCBjbG9zZWQgZHVyaW5nIHN0YXJ0dXBcIikpO1xuICAgIGNvbnN0IGZpbmlzaCA9IChlcnJvcj86IEVycm9yLCBjaHVuaz86IEJ1ZmZlcikgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHNvY2tldC5vZmYoXCJkYXRhXCIsIG9uRGF0YSk7XG4gICAgICBzb2NrZXQub2ZmKFwiZXJyb3JcIiwgb25FcnJvcik7XG4gICAgICBzb2NrZXQub2ZmKFwiZW5kXCIsIG9uRW5kKTtcbiAgICAgIGlmIChlcnJvcikgcmVqZWN0KGVycm9yKTtcbiAgICAgIGVsc2UgcmVzb2x2ZShjaHVuayEpO1xuICAgIH07XG4gICAgc29ja2V0Lm9uY2UoXCJkYXRhXCIsIG9uRGF0YSk7XG4gICAgc29ja2V0Lm9uY2UoXCJlcnJvclwiLCBvbkVycm9yKTtcbiAgICBzb2NrZXQub25jZShcImVuZFwiLCBvbkVuZCk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZVBhcmFtZXRlcnMoYm9keTogQnVmZmVyKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gIGNvbnN0IHZhbHVlcyA9IGJvZHkudG9TdHJpbmcoXCJ1dGY4XCIpLnNwbGl0KFwiXFwwXCIpO1xuICBjb25zdCBwYXJhbWV0ZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggKyAxIDwgdmFsdWVzLmxlbmd0aCAmJiB2YWx1ZXNbaW5kZXhdOyBpbmRleCArPSAyKSB7XG4gICAgcGFyYW1ldGVyc1t2YWx1ZXNbaW5kZXhdIV0gPSB2YWx1ZXNbaW5kZXggKyAxXSA/PyBcIlwiO1xuICB9XG4gIHJldHVybiBwYXJhbWV0ZXJzO1xufVxuIl19
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Pure Node PostgreSQL wire proxy for Databricks Lakebase. */
|
|
2
|
+
import { type LakebaseProxyOptions } from "./options.ts";
|
|
3
|
+
/** Loopback PostgreSQL wire proxy backed by Databricks Lakebase credentials. */
|
|
4
|
+
export declare class LakebaseProxy {
|
|
5
|
+
private readonly client;
|
|
6
|
+
private readonly options;
|
|
7
|
+
private readonly cancellations;
|
|
8
|
+
private server?;
|
|
9
|
+
private statsTimer?;
|
|
10
|
+
private opened;
|
|
11
|
+
private closed;
|
|
12
|
+
private failed;
|
|
13
|
+
private active;
|
|
14
|
+
constructor(options?: LakebaseProxyOptions);
|
|
15
|
+
listen(): Promise<{
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
}>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
private handle;
|
|
21
|
+
private reportStats;
|
|
22
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** Pure Node PostgreSQL wire proxy for Databricks Lakebase. */
|
|
2
|
+
import { createServer } from "node:net";
|
|
3
|
+
import { LakebaseClient, requireAddress } from "@dbx-tools/lakebase";
|
|
4
|
+
import { postgresConnectionOptions } from "@dbx-tools/postgres";
|
|
5
|
+
import { log, stringUtils } from "@dbx-tools/shared-core";
|
|
6
|
+
import { Client } from "pg";
|
|
7
|
+
import { CancellationRegistry } from "./cancellation.js";
|
|
8
|
+
import { resolveLakebaseProxyOptions, } from "./options.js";
|
|
9
|
+
import { fatalError, PostgresProtocolError, readInitialMessage, startupComplete, upstreamStartupParameters, } from "./protocol.js";
|
|
10
|
+
const logger = log.logger("lakebase-proxy");
|
|
11
|
+
/** Loopback PostgreSQL wire proxy backed by Databricks Lakebase credentials. */
|
|
12
|
+
export class LakebaseProxy {
|
|
13
|
+
client;
|
|
14
|
+
options;
|
|
15
|
+
cancellations = new CancellationRegistry();
|
|
16
|
+
server;
|
|
17
|
+
statsTimer;
|
|
18
|
+
opened = 0;
|
|
19
|
+
closed = 0;
|
|
20
|
+
failed = 0;
|
|
21
|
+
active = 0;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.options = resolveLakebaseProxyOptions(options);
|
|
24
|
+
this.client = new LakebaseClient({ profile: this.options.profile });
|
|
25
|
+
}
|
|
26
|
+
async listen() {
|
|
27
|
+
if (this.server)
|
|
28
|
+
throw new Error("Lakebase proxy is already listening");
|
|
29
|
+
const host = this.options.listen.host;
|
|
30
|
+
const server = createServer((socket) => void this.handle(socket));
|
|
31
|
+
this.server = server;
|
|
32
|
+
this.statsTimer = setInterval(() => this.reportStats(), 60_000);
|
|
33
|
+
this.statsTimer.unref();
|
|
34
|
+
await new Promise((resolve, reject) => {
|
|
35
|
+
server.once("error", reject);
|
|
36
|
+
server.listen(this.options.listen.port, host, () => {
|
|
37
|
+
server.off("error", reject);
|
|
38
|
+
resolve();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
const address = server.address();
|
|
42
|
+
if (!address || typeof address === "string") {
|
|
43
|
+
throw new Error("Lakebase listener has no address");
|
|
44
|
+
}
|
|
45
|
+
logger.info("Lakebase proxy listening", { host: address.address, port: address.port });
|
|
46
|
+
return { host: address.address, port: address.port };
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
const server = this.server;
|
|
50
|
+
this.server = undefined;
|
|
51
|
+
if (this.statsTimer)
|
|
52
|
+
clearInterval(this.statsTimer);
|
|
53
|
+
this.statsTimer = undefined;
|
|
54
|
+
if (!server)
|
|
55
|
+
return;
|
|
56
|
+
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
|
57
|
+
}
|
|
58
|
+
async handle(local) {
|
|
59
|
+
const started = Date.now();
|
|
60
|
+
let upstream;
|
|
61
|
+
let cancellation;
|
|
62
|
+
this.opened += 1;
|
|
63
|
+
this.active += 1;
|
|
64
|
+
try {
|
|
65
|
+
local.setNoDelay(true);
|
|
66
|
+
const initial = await readInitialMessage(local, this.options.startupTimeoutSeconds * 1000);
|
|
67
|
+
if (initial.kind === "cancel") {
|
|
68
|
+
await this.cancellations.forward(initial);
|
|
69
|
+
local.end();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const startup = initial;
|
|
73
|
+
const targetText = startup.parameters.database;
|
|
74
|
+
if (!targetText)
|
|
75
|
+
throw new ProxyFailure("startup database is required", "3D000");
|
|
76
|
+
let target;
|
|
77
|
+
try {
|
|
78
|
+
target = requireAddress(targetText);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw new ProxyFailure(message(error), "3D000");
|
|
82
|
+
}
|
|
83
|
+
const startupUser = stringUtils.trimToUndefined(startup.parameters.user);
|
|
84
|
+
let resolved;
|
|
85
|
+
try {
|
|
86
|
+
resolved = await this.client.resolve(target, startupUser);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new ProxyFailure(message(error), "3D000");
|
|
90
|
+
}
|
|
91
|
+
let password;
|
|
92
|
+
try {
|
|
93
|
+
password = await this.client.generateDatabaseCredential(resolved.endpoint, startupUser);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new ProxyFailure(message(error), "28000");
|
|
97
|
+
}
|
|
98
|
+
let connected;
|
|
99
|
+
try {
|
|
100
|
+
connected = await connectUpstream(resolved, password, postgresConnectionOptions(startup.parameters, this.options.postgresRole));
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw new ProxyFailure(message(error), "08001");
|
|
104
|
+
}
|
|
105
|
+
upstream = connected.socket;
|
|
106
|
+
cancellation = this.cancellations.register({
|
|
107
|
+
host: resolved.host,
|
|
108
|
+
port: resolved.port,
|
|
109
|
+
processId: connected.processId,
|
|
110
|
+
secretKey: connected.secretKey,
|
|
111
|
+
});
|
|
112
|
+
local.write(startupComplete(connected.parameters, cancellation.processId, cancellation.secretKey, connected.transactionStatus));
|
|
113
|
+
local.pipe(upstream).pipe(local);
|
|
114
|
+
await Promise.race([closed(local), closed(upstream)]);
|
|
115
|
+
logger.debug("Lakebase connection closed", {
|
|
116
|
+
durationMs: Date.now() - started,
|
|
117
|
+
project: resolved.project,
|
|
118
|
+
branch: resolved.branch,
|
|
119
|
+
endpoint: resolved.endpoint,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.failed += 1;
|
|
124
|
+
const failure = error instanceof ProxyFailure
|
|
125
|
+
? error
|
|
126
|
+
: error instanceof PostgresProtocolError
|
|
127
|
+
? new ProxyFailure(error.message, "08P01")
|
|
128
|
+
: new ProxyFailure(message(error));
|
|
129
|
+
logger.warn("Lakebase connection failed", {
|
|
130
|
+
durationMs: Date.now() - started,
|
|
131
|
+
error: failure.message,
|
|
132
|
+
});
|
|
133
|
+
if (!local.destroyed)
|
|
134
|
+
local.end(fatalError(failure.message, failure.sqlstate));
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
if (cancellation)
|
|
138
|
+
this.cancellations.remove(cancellation);
|
|
139
|
+
upstream?.destroy();
|
|
140
|
+
local.destroy();
|
|
141
|
+
this.closed += 1;
|
|
142
|
+
this.active -= 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
reportStats() {
|
|
146
|
+
logger.info("Lakebase proxy connections", {
|
|
147
|
+
opened: this.opened,
|
|
148
|
+
closed: this.closed,
|
|
149
|
+
failed: this.failed,
|
|
150
|
+
active: this.active,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function connectUpstream(resolved, password, startup) {
|
|
155
|
+
const client = new Client({
|
|
156
|
+
host: resolved.host,
|
|
157
|
+
port: resolved.port,
|
|
158
|
+
database: resolved.database,
|
|
159
|
+
user: resolved.user,
|
|
160
|
+
password,
|
|
161
|
+
ssl: { rejectUnauthorized: true, servername: resolved.host },
|
|
162
|
+
application_name: startup.application_name,
|
|
163
|
+
connectionTimeoutMillis: 30_000,
|
|
164
|
+
});
|
|
165
|
+
const parameters = new Map();
|
|
166
|
+
client.getStartupConf = () => upstreamStartupParameters(startup, resolved.user, resolved.database);
|
|
167
|
+
client.on("error", (error) => {
|
|
168
|
+
logger.debug("Lakebase upstream client closed", { error: error.message });
|
|
169
|
+
});
|
|
170
|
+
client.connection.on("parameterStatus", (message) => {
|
|
171
|
+
parameters.set(message.parameterName, message.parameterValue);
|
|
172
|
+
});
|
|
173
|
+
await client.connect();
|
|
174
|
+
const socket = client.connection.stream;
|
|
175
|
+
socket.removeAllListeners("data");
|
|
176
|
+
return {
|
|
177
|
+
socket,
|
|
178
|
+
parameters,
|
|
179
|
+
processId: client.processID,
|
|
180
|
+
secretKey: client.secretKey,
|
|
181
|
+
transactionStatus: client._txStatus ?? "I",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function closed(socket) {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
socket.once("close", () => resolve());
|
|
187
|
+
socket.once("error", reject);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function message(error) {
|
|
191
|
+
return error instanceof Error ? error.message : String(error);
|
|
192
|
+
}
|
|
193
|
+
class ProxyFailure extends Error {
|
|
194
|
+
sqlstate;
|
|
195
|
+
constructor(message, sqlstate = "08001") {
|
|
196
|
+
super(message);
|
|
197
|
+
this.sqlstate = sqlstate;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGFrZWJhc2UtcHJveHkvcHJveHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0RBQStEO0FBRS9ELE9BQU8sRUFBRSxZQUFZLEVBQTRCLE1BQU0sVUFBVSxDQUFDO0FBRWxFLE9BQU8sRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckUsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDaEUsT0FBTyxFQUFFLEdBQUcsRUFBRSxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBRTVCLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3pELE9BQU8sRUFDTCwyQkFBMkIsR0FHNUIsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxFQUNMLFVBQVUsRUFDVixxQkFBcUIsRUFDckIsa0JBQWtCLEVBQ2xCLGVBQWUsRUFDZix5QkFBeUIsR0FFMUIsTUFBTSxlQUFlLENBQUM7QUFFdkIsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBb0I1QyxnRkFBZ0Y7QUFDaEYsTUFBTSxPQUFPLGFBQWE7SUFDUCxNQUFNLENBQWlCO0lBQ3ZCLE9BQU8sQ0FBK0I7SUFDdEMsYUFBYSxHQUFHLElBQUksb0JBQW9CLEVBQUUsQ0FBQztJQUNwRCxNQUFNLENBQVU7SUFDaEIsVUFBVSxDQUFrQztJQUM1QyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ1gsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNYLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBRW5CLFlBQVksVUFBZ0MsRUFBRTtRQUM1QyxJQUFJLENBQUMsT0FBTyxHQUFHLDJCQUEyQixD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QUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDL0IsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsS0FBYztJQUM3QixPQUFPLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsTUFBTSxZQUFhLFNBQVEsS0FBSztJQUduQjtJQUZYLFlBQ0UsT0FBZSxFQUNOLFdBQVcsT0FBTztRQUUzQixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFGTixhQUFRLEdBQVIsUUFBUSxDQUFVO0lBRzdCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKiBQdXJlIE5vZGUgUG9zdGdyZVNRTCB3aXJlIHByb3h5IGZvciBEYXRhYnJpY2tzIExha2ViYXNlLiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVTZXJ2ZXIsIHR5cGUgU2VydmVyLCB0eXBlIFNvY2tldCB9IGZyb20gXCJub2RlOm5ldFwiO1xuaW1wb3J0IHR5cGUgeyBEdXBsZXggfSBmcm9tIFwibm9kZTpzdHJlYW1cIjtcbmltcG9ydCB7IExha2ViYXNlQ2xpZW50LCByZXF1aXJlQWRkcmVzcyB9IGZyb20gXCJAZGJ4LXRvb2xzL2xha2ViYXNlXCI7XG5pbXBvcnQgeyBwb3N0Z3Jlc0Nvbm5lY3Rpb25PcHRpb25zIH0gZnJvbSBcIkBkYngtdG9vbHMvcG9zdGdyZXNcIjtcbmltcG9ydCB7IGxvZywgc3RyaW5nVXRpbHMgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgQ2xpZW50IH0gZnJvbSBcInBnXCI7XG5cbmltcG9ydCB7IENhbmNlbGxhdGlvblJlZ2lzdHJ5IH0gZnJvbSBcIi4vY2FuY2VsbGF0aW9uLnRzXCI7XG5pbXBvcnQge1xuICByZXNvbHZlTGFrZWJhc2VQcm94eU9wdGlvbnMsXG4gIHR5cGUgTGFrZWJhc2VQcm94eU9wdGlvbnMsXG4gIHR5cGUgUmVzb2x2ZWRMYWtlYmFzZVByb3h5T3B0aW9ucyxcbn0gZnJvbSBcIi4vb3B0aW9ucy50c1wiO1xuaW1wb3J0IHtcbiAgZmF0YWxFcnJvcixcbiAgUG9zdGdyZXNQcm90b2NvbEVycm9yLFxuICByZWFkSW5pdGlhbE1lc3NhZ2UsXG4gIHN0YXJ0dXBDb21wbGV0ZSxcbiAgdXBzdHJlYW1TdGFydHVwUGFyYW1ldGVycyxcbiAgdHlwZSBDYW5jZWxNZXNzYWdlLFxufSBmcm9tIFwiLi9wcm90b2NvbC50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwibGFrZWJhc2UtcHJveHlcIik7XG5cbmludGVyZmFjZSBQZ0Nvbm5lY3Rpb25JbnRlcm5hbHMge1xuICBzdHJlYW06IER1cGxleDtcbiAgb24oZXZlbnQ6IFwicGFyYW1ldGVyU3RhdHVzXCIsIGxpc3RlbmVyOiAobWVzc2FnZTogUGFyYW1ldGVyU3RhdHVzKSA9PiB2b2lkKTogdm9pZDtcbn1cblxudHlwZSBQZ0NsaWVudEludGVybmFscyA9IENsaWVudCAmIHtcbiAgY29ubmVjdGlvbjogQ2xpZW50W1wiY29ubmVjdGlvblwiXSAmIFBnQ29ubmVjdGlvbkludGVybmFscztcbiAgcHJvY2Vzc0lEOiBudW1iZXI7XG4gIHNlY3JldEtleTogbnVtYmVyO1xuICBfdHhTdGF0dXM/OiBzdHJpbmc7XG4gIGdldFN0YXJ0dXBDb25mKCk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59O1xuXG5pbnRlcmZhY2UgUGFyYW1ldGVyU3RhdHVzIHtcbiAgcGFyYW1ldGVyTmFtZTogc3RyaW5nO1xuICBwYXJhbWV0ZXJWYWx1ZTogc3RyaW5nO1xufVxuXG4vKiogTG9vcGJhY2sgUG9zdGdyZVNRTCB3aXJlIHByb3h5IGJhY2tlZCBieSBEYXRhYnJpY2tzIExha2ViYXNlIGNyZWRlbnRpYWxzLiAqL1xuZXhwb3J0IGNsYXNzIExha2ViYXNlUHJveHkge1xuICBwcml2YXRlIHJlYWRvbmx5IGNsaWVudDogTGFrZWJhc2VDbGllbnQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczogUmVzb2x2ZWRMYWtlYmFzZVByb3h5T3B0aW9ucztcbiAgcHJpdmF0ZSByZWFkb25seSBjYW5jZWxsYXRpb25zID0gbmV3IENhbmNlbGxhdGlvblJlZ2lzdHJ5KCk7XG4gIHByaXZhdGUgc2VydmVyPzogU2VydmVyO1xuICBwcml2YXRlIHN0YXRzVGltZXI/OiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRJbnRlcnZhbD47XG4gIHByaXZhdGUgb3BlbmVkID0gMDtcbiAgcHJpdmF0ZSBjbG9zZWQgPSAwO1xuICBwcml2YXRlIGZhaWxlZCA9IDA7XG4gIHByaXZhdGUgYWN0aXZlID0gMDtcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBMYWtlYmFzZVByb3h5T3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5vcHRpb25zID0gcmVzb2x2ZUxha2ViYXNlUHJveHlPcHRpb25zKG9wdGlvbnMpO1xuICAgIHRoaXMuY2xpZW50ID0gbmV3IExha2ViYXNlQ2xpZW50KHsgcHJvZmlsZTogdGhpcy5vcHRpb25zLnByb2ZpbGUgfSk7XG4gIH1cblxuICBhc3luYyBsaXN0ZW4oKTogUHJvbWlzZTx7IGhvc3Q6IHN0cmluZzsgcG9ydDogbnVtYmVyIH0+IHtcbiAgICBpZiAodGhpcy5zZXJ2ZXIpIHRocm93IG5ldyBFcnJvcihcIkxha2ViYXNlIHByb3h5IGlzIGFscmVhZHkgbGlzdGVuaW5nXCIpO1xuICAgIGNvbnN0IGhvc3QgPSB0aGlzLm9wdGlvbnMubGlzdGVuLmhvc3Q7XG4gICAgY29uc3Qgc2VydmVyID0gY3JlYXRlU2VydmVyKChzb2NrZXQpID0+IHZvaWQgdGhpcy5oYW5kbGUoc29ja2V0KSk7XG4gICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXI7XG4gICAgdGhpcy5zdGF0c1RpbWVyID0gc2V0SW50ZXJ2YWwoKCkgPT4gdGhpcy5yZXBvcnRTdGF0cygpLCA2MF8wMDApO1xuICAgIHRoaXMuc3RhdHNUaW1lci51bnJlZigpO1xuICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHNlcnZlci5vbmNlKFwiZXJyb3JcIiwgcmVqZWN0KTtcbiAgICAgIHNlcnZlci5saXN0ZW4odGhpcy5vcHRpb25zLmxpc3Rlbi5wb3J0LCBob3N0LCAoKSA9PiB7XG4gICAgICAgIHNlcnZlci5vZmYoXCJlcnJvclwiLCByZWplY3QpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgICBjb25zdCBhZGRyZXNzID0gc2VydmVyLmFkZHJlc3MoKTtcbiAgICBpZiAoIWFkZHJlc3MgfHwgdHlwZW9mIGFkZHJlc3MgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkxha2ViYXNlIGxpc3RlbmVyIGhhcyBubyBhZGRyZXNzXCIpO1xuICAgIH1cbiAgICBsb2dnZXIuaW5mbyhcIkxha2ViYXNlIHByb3h5IGxpc3RlbmluZ1wiLCB7IGhvc3Q6IGFkZHJlc3MuYWRkcmVzcywgcG9ydDogYWRkcmVzcy5wb3J0IH0pO1xuICAgIHJldHVybiB7IGhvc3Q6IGFkZHJlc3MuYWRkcmVzcywgcG9ydDogYWRkcmVzcy5wb3J0IH07XG4gIH1cblxuICBhc3luYyBjbG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzZXJ2ZXIgPSB0aGlzLnNlcnZlcjtcbiAgICB0aGlzLnNlcnZlciA9IHVuZGVmaW5lZDtcbiAgICBpZiAodGhpcy5zdGF0c1RpbWVyKSBjbGVhckludGVydmFsKHRoaXMuc3RhdHNUaW1lcik7XG4gICAgdGhpcy5zdGF0c1RpbWVyID0gdW5kZWZpbmVkO1xuICAgIGlmICghc2VydmVyKSByZXR1cm47XG4gICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT5cbiAgICAgIHNlcnZlci5jbG9zZSgoZXJyb3IpID0+IChlcnJvciA/IHJlamVjdChlcnJvcikgOiByZXNvbHZlKCkpKSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBoYW5kbGUobG9jYWw6IFNvY2tldCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHN0YXJ0ZWQgPSBEYXRlLm5vdygpO1xuICAgIGxldCB1cHN0cmVhbTogRHVwbGV4IHwgdW5kZWZpbmVkO1xuICAgIGxldCBjYW5jZWxsYXRpb246IENhbmNlbE1lc3NhZ2UgfCB1bmRlZmluZWQ7XG4gICAgdGhpcy5vcGVuZWQgKz0gMTtcbiAgICB0aGlzLmFjdGl2ZSArPSAxO1xuICAgIHRyeSB7XG4gICAgICBsb2NhbC5zZXROb0RlbGF5KHRydWUpO1xuICAgICAgY29uc3QgaW5pdGlhbCA9IGF3YWl0IHJlYWRJbml0aWFsTWVzc2FnZShsb2NhbCwgdGhpcy5vcHRpb25zLnN0YXJ0dXBUaW1lb3V0U2Vjb25kcyAqIDEwMDApO1xuICAgICAgaWYgKGluaXRpYWwua2luZCA9PT0gXCJjYW5jZWxcIikge1xuICAgICAgICBhd2FpdCB0aGlzLmNhbmNlbGxhdGlvbnMuZm9yd2FyZChpbml0aWFsKTtcbiAgICAgICAgbG9jYWwuZW5kKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0YXJ0dXAgPSBpbml0aWFsO1xuICAgICAgY29uc3QgdGFyZ2V0VGV4dCA9IHN0YXJ0dXAucGFyYW1ldGVycy5kYXRhYmFzZTtcbiAgICAgIGlmICghdGFyZ2V0VGV4dCkgdGhyb3cgbmV3IFByb3h5RmFpbHVyZShcInN0YXJ0dXAgZGF0YWJhc2UgaXMgcmVxdWlyZWRcIiwgXCIzRDAwMFwiKTtcbiAgICAgIGxldCB0YXJnZXQ7XG4gICAgICB0cnkge1xuICAgICAgICB0YXJnZXQgPSByZXF1aXJlQWRkcmVzcyh0YXJnZXRUZXh0KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHRocm93IG5ldyBQcm94eUZhaWx1cmUobWVzc2FnZShlcnJvciksIFwiM0QwMDBcIik7XG4gICAgICB9XG4gICAgICBjb25zdCBzdGFydHVwVXNlciA9IHN0cmluZ1V0aWxzLnRyaW1Ub1VuZGVmaW5lZChzdGFydHVwLnBhcmFtZXRlcnMudXNlcik7XG4gICAgICBsZXQgcmVzb2x2ZWQ7XG4gICAgICB0cnkge1xuICAgICAgICByZXNvbHZlZCA9IGF3YWl0IHRoaXMuY2xpZW50LnJlc29sdmUodGFyZ2V0LCBzdGFydHVwVXNlcik7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgUHJveHlGYWlsdXJlKG1lc3NhZ2UoZXJyb3IpLCBcIjNEMDAwXCIpO1xuICAgICAgfVxuICAgICAgbGV0IHBhc3N3b3JkO1xuICAgICAgdHJ5IHtcbiAgICAgICAgcGFzc3dvcmQgPSBhd2FpdCB0aGlzLmNsaWVudC5nZW5lcmF0ZURhdGFiYXNlQ3JlZGVudGlhbChyZXNvbHZlZC5lbmRwb2ludCwgc3RhcnR1cFVzZXIpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IFByb3h5RmFpbHVyZShtZXNzYWdlKGVycm9yKSwgXCIyODAwMFwiKTtcbiAgICAgIH1cbiAgICAgIGxldCBjb25uZWN0ZWQ7XG4gICAgICB0cnkge1xuICAgICAgICBjb25uZWN0ZWQgPSBhd2FpdCBjb25uZWN0VXBzdHJlYW0oXG4gICAgICAgICAgcmVzb2x2ZWQsXG4gICAgICAgICAgcGFzc3dvcmQsXG4gICAgICAgICAgcG9zdGdyZXNDb25uZWN0aW9uT3B0aW9ucyhzdGFydHVwLnBhcmFtZXRlcnMsIHRoaXMub3B0aW9ucy5wb3N0Z3Jlc1JvbGUpLFxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IFByb3h5RmFpbHVyZShtZXNzYWdlKGVycm9yKSwgXCIwODAwMVwiKTtcbiAgICAgIH1cbiAgICAgIHVwc3RyZWFtID0gY29ubmVjdGVkLnNvY2tldDtcbiAgICAgIGNhbmNlbGxhdGlvbiA9IHRoaXMuY2FuY2VsbGF0aW9ucy5yZWdpc3Rlcih7XG4gICAgICAgIGhvc3Q6IHJlc29sdmVkLmhvc3QsXG4gICAgICAgIHBvcnQ6IHJlc29sdmVkLnBvcnQsXG4gICAgICAgIHByb2Nlc3NJZDogY29ubmVjdGVkLnByb2Nlc3NJZCxcbiAgICAgICAgc2VjcmV0S2V5OiBjb25uZWN0ZWQuc2VjcmV0S2V5LFxuICAgICAgfSk7XG4gICAgICBsb2NhbC53cml0ZShcbiAgICAgICAgc3RhcnR1cENvbXBsZXRlKFxuICAgICAgICAgIGNvbm5lY3RlZC5wYXJhbWV0ZXJzLFxuICAgICAgICAgIGNhbmNlbGxhdGlvbi5wcm9jZXNzSWQsXG4gICAgICAgICAgY2FuY2VsbGF0aW9uLnNlY3JldEtleSxcbiAgICAgICAgICBjb25uZWN0ZWQudHJhbnNhY3Rpb25TdGF0dXMsXG4gICAgICAgICksXG4gICAgICApO1xuICAgICAgbG9jYWwucGlwZSh1cHN0cmVhbSkucGlwZShsb2NhbCk7XG4gICAgICBhd2FpdCBQcm9taXNlLnJhY2UoW2Nsb3NlZChsb2NhbCksIGNsb3NlZCh1cHN0cmVhbSldKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcIkxha2ViYXNlIGNvbm5lY3Rpb24gY2xvc2VkXCIsIHtcbiAgICAgICAgZHVyYXRpb25NczogRGF0ZS5ub3coKSAtIHN0YXJ0ZWQsXG4gICAgICAgIHByb2plY3Q6IHJlc29sdmVkLnByb2plY3QsXG4gICAgICAgIGJyYW5jaDogcmVzb2x2ZWQuYnJhbmNoLFxuICAgICAgICBlbmRwb2ludDogcmVzb2x2ZWQuZW5kcG9pbnQsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5mYWlsZWQgKz0gMTtcbiAgICAgIGNvbnN0IGZhaWx1cmUgPVxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIFByb3h5RmFpbHVyZVxuICAgICAgICAgID8gZXJyb3JcbiAgICAgICAgICA6IGVycm9yIGluc3RhbmNlb2YgUG9zdGdyZXNQcm90b2NvbEVycm9yXG4gICAgICAgICAgICA/IG5ldyBQcm94eUZhaWx1cmUoZXJyb3IubWVzc2FnZSwgXCIwOFAwMVwiKVxuICAgICAgICAgICAgOiBuZXcgUHJveHlGYWlsdXJlKG1lc3NhZ2UoZXJyb3IpKTtcbiAgICAgIGxvZ2dlci53YXJuKFwiTGFrZWJhc2UgY29ubmVjdGlvbiBmYWlsZWRcIiwge1xuICAgICAgICBkdXJhdGlvbk1zOiBEYXRlLm5vdygpIC0gc3RhcnRlZCxcbiAgICAgICAgZXJyb3I6IGZhaWx1cmUubWVzc2FnZSxcbiAgICAgIH0pO1xuICAgICAgaWYgKCFsb2NhbC5kZXN0cm95ZWQpIGxvY2FsLmVuZChmYXRhbEVycm9yKGZhaWx1cmUubWVzc2FnZSwgZmFpbHVyZS5zcWxzdGF0ZSkpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAoY2FuY2VsbGF0aW9uKSB0aGlzLmNhbmNlbGxhdGlvbnMucmVtb3ZlKGNhbmNlbGxhdGlvbik7XG4gICAgICB1cHN0cmVhbT8uZGVzdHJveSgpO1xuICAgICAgbG9jYWwuZGVzdHJveSgpO1xuICAgICAgdGhpcy5jbG9zZWQgKz0gMTtcbiAgICAgIHRoaXMuYWN0aXZlIC09IDE7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSByZXBvcnRTdGF0cygpOiB2b2lkIHtcbiAgICBsb2dnZXIuaW5mbyhcIkxha2ViYXNlIHByb3h5IGNvbm5lY3Rpb25zXCIsIHtcbiAgICAgIG9wZW5lZDogdGhpcy5vcGVuZWQsXG4gICAgICBjbG9zZWQ6IHRoaXMuY2xvc2VkLFxuICAgICAgZmFpbGVkOiB0aGlzLmZhaWxlZCxcbiAgICAgIGFjdGl2ZTogdGhpcy5hY3RpdmUsXG4gICAgfSk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gY29ubmVjdFVwc3RyZWFtKFxuICByZXNvbHZlZDogQXdhaXRlZDxSZXR1cm5UeXBlPExha2ViYXNlQ2xpZW50W1wicmVzb2x2ZVwiXT4+LFxuICBwYXNzd29yZDogc3RyaW5nLFxuICBzdGFydHVwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuKTogUHJvbWlzZTx7XG4gIHNvY2tldDogRHVwbGV4O1xuICBwYXJhbWV0ZXJzOiBNYXA8c3RyaW5nLCBzdHJpbmc+O1xuICBwcm9jZXNzSWQ6IG51bWJlcjtcbiAgc2VjcmV0S2V5OiBudW1iZXI7XG4gIHRyYW5zYWN0aW9uU3RhdHVzOiBzdHJpbmc7XG59PiB7XG4gIGNvbnN0IGNsaWVudCA9IG5ldyBDbGllbnQoe1xuICAgIGhvc3Q6IHJlc29sdmVkLmhvc3QsXG4gICAgcG9ydDogcmVzb2x2ZWQucG9ydCxcbiAgICBkYXRhYmFzZTogcmVzb2x2ZWQuZGF0YWJhc2UsXG4gICAgdXNlcjogcmVzb2x2ZWQudXNlcixcbiAgICBwYXNzd29yZCxcbiAgICBzc2w6IHsgcmVqZWN0VW5hdXRob3JpemVkOiB0cnVlLCBzZXJ2ZXJuYW1lOiByZXNvbHZlZC5ob3N0IH0sXG4gICAgYXBwbGljYXRpb25fbmFtZTogc3RhcnR1cC5hcHBsaWNhdGlvbl9uYW1lLFxuICAgIGNvbm5lY3Rpb25UaW1lb3V0TWlsbGlzOiAzMF8wMDAsXG4gIH0pIGFzIHVua25vd24gYXMgUGdDbGllbnRJbnRlcm5hbHM7XG4gIGNvbnN0IHBhcmFtZXRlcnMgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICBjbGllbnQuZ2V0U3RhcnR1cENvbmYgPSAoKSA9PlxuICAgIHVwc3RyZWFtU3RhcnR1cFBhcmFtZXRlcnMoc3RhcnR1cCwgcmVzb2x2ZWQudXNlciwgcmVzb2x2ZWQuZGF0YWJhc2UpO1xuICBjbGllbnQub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IHtcbiAgICBsb2dnZXIuZGVidWcoXCJMYWtlYmFzZSB1cHN0cmVhbSBjbGllbnQgY2xvc2VkXCIsIHsgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfSk7XG4gIH0pO1xuICBjbGllbnQuY29ubmVjdGlvbi5vbihcInBhcmFtZXRlclN0YXR1c1wiLCAobWVzc2FnZSkgPT4ge1xuICAgIHBhcmFtZXRlcnMuc2V0KG1lc3NhZ2UucGFyYW1ldGVyTmFtZSwgbWVzc2FnZS5wYXJhbWV0ZXJWYWx1ZSk7XG4gIH0pO1xuICBhd2FpdCBjbGllbnQuY29ubmVjdCgpO1xuICBjb25zdCBzb2NrZXQgPSBjbGllbnQuY29ubmVjdGlvbi5zdHJlYW07XG4gIHNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoXCJkYXRhXCIpO1xuICByZXR1cm4ge1xuICAgIHNvY2tldCxcbiAgICBwYXJhbWV0ZXJzLFxuICAgIHByb2Nlc3NJZDogY2xpZW50LnByb2Nlc3NJRCxcbiAgICBzZWNyZXRLZXk6IGNsaWVudC5zZWNyZXRLZXksXG4gICAgdHJhbnNhY3Rpb25TdGF0dXM6IGNsaWVudC5fdHhTdGF0dXMgPz8gXCJJXCIsXG4gIH07XG59XG5cbmZ1bmN0aW9uIGNsb3NlZChzb2NrZXQ6IER1cGxleCk6IFByb21pc2U8dm9pZD4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIHNvY2tldC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICBzb2NrZXQub25jZShcImVycm9yXCIsIHJlamVjdCk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBtZXNzYWdlKGVycm9yOiB1bmtub3duKTogc3RyaW5nIHtcbiAgcmV0dXJuIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbn1cblxuY2xhc3MgUHJveHlGYWlsdXJlIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihcbiAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICAgcmVhZG9ubHkgc3Fsc3RhdGUgPSBcIjA4MDAxXCIsXG4gICkge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foreground `dbx model-gateway` command.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type CliServiceCliDependencies } from "@dbx-tools/cli-service/cli";
|
|
7
|
+
import { type CliServiceDefinition } from "@dbx-tools/cli-service/definition";
|
|
8
|
+
import { type ModelGatewayOptions } from "@dbx-tools/shared-model-gateway/options";
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
/** Injectable foreground gateway boundary for CLI tests. */
|
|
11
|
+
export interface ModelGatewayCliDependencies {
|
|
12
|
+
start(options: ModelGatewayOptions): Promise<void>;
|
|
13
|
+
/** Optional service CLI boundary for tests and embedding. */
|
|
14
|
+
readonly service?: CliServiceCliDependencies;
|
|
15
|
+
}
|
|
16
|
+
/** Build the tray-only model-gateway service definition. */
|
|
17
|
+
export declare function modelGatewayServiceDefinition(options?: ModelGatewayOptions): CliServiceDefinition;
|
|
18
|
+
/** Build foreground and service model-gateway commands without starting a server. */
|
|
19
|
+
export declare function buildProgram(name?: string, dependencies?: ModelGatewayCliDependencies): Command;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foreground `dbx model-gateway` command.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { addArgs, parseArgs, serializeArgs } from "@dbx-tools/cli-args";
|
|
7
|
+
import { buildServiceCommand } from "@dbx-tools/cli-service/cli";
|
|
8
|
+
import { defineService } from "@dbx-tools/cli-service/definition";
|
|
9
|
+
import { ModelGatewayCliOptionsSchema, resolveModelGatewayCliOptions, } from "@dbx-tools/shared-model-gateway/options";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { PACKAGE_VERSION } from "../../index.js";
|
|
12
|
+
import { startModelGateway } from "./server.js";
|
|
13
|
+
const DEFAULT_DEPENDENCIES = {
|
|
14
|
+
start: startModelGateway,
|
|
15
|
+
};
|
|
16
|
+
/** Build the tray-only model-gateway service definition. */
|
|
17
|
+
export function modelGatewayServiceDefinition(options = {}) {
|
|
18
|
+
const { runtimeInfo: _, ...resolved } = resolveModelGatewayCliOptions(options);
|
|
19
|
+
const host = urlHost(resolved.listen.host);
|
|
20
|
+
return defineService(import.meta.url, {
|
|
21
|
+
id: "dbx-tools.cli-model-gateway",
|
|
22
|
+
name: "dbx model gateway",
|
|
23
|
+
command: {
|
|
24
|
+
binName: "dbx-model-gateway",
|
|
25
|
+
arguments: serializeArgs(resolved),
|
|
26
|
+
},
|
|
27
|
+
menu: [
|
|
28
|
+
{
|
|
29
|
+
type: "url",
|
|
30
|
+
label: "Models",
|
|
31
|
+
url: `http://${host}:${resolved.listen.port}/v1/models`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/** Build foreground and service model-gateway commands without starting a server. */
|
|
37
|
+
export function buildProgram(name = "dbx model-gateway", dependencies = DEFAULT_DEPENDENCIES) {
|
|
38
|
+
const version = PACKAGE_VERSION;
|
|
39
|
+
const program = addArgs(new Command()
|
|
40
|
+
.name(name)
|
|
41
|
+
.description("Run or manage the AppKit Databricks model gateway")
|
|
42
|
+
.version(version, "-v, --version"), ModelGatewayCliOptionsSchema).action(async () => {
|
|
43
|
+
const options = modelGatewayOptions(program);
|
|
44
|
+
if (options.runtimeInfo) {
|
|
45
|
+
process.stdout.write(`${JSON.stringify({ implementation: "typescript-appkit", version })}\n`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const { runtimeInfo: _, ...serverOptions } = options;
|
|
49
|
+
await dependencies.start(serverOptions);
|
|
50
|
+
});
|
|
51
|
+
const serviceCommand = buildServiceCommand(() => {
|
|
52
|
+
return modelGatewayServiceDefinition(modelGatewayOptions(program));
|
|
53
|
+
}, dependencies.service);
|
|
54
|
+
program.addCommand(serviceCommand);
|
|
55
|
+
return program;
|
|
56
|
+
}
|
|
57
|
+
function modelGatewayOptions(command) {
|
|
58
|
+
return parseArgs(command, ModelGatewayCliOptionsSchema);
|
|
59
|
+
}
|
|
60
|
+
function urlHost(host) {
|
|
61
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGVsLWdhdGV3YXkvY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RSxPQUFPLEVBQUUsbUJBQW1CLEVBQWtDLE1BQU0sNEJBQTRCLENBQUM7QUFDakcsT0FBTyxFQUFFLGFBQWEsRUFBNkIsTUFBTSxtQ0FBbUMsQ0FBQztBQUM3RixPQUFPLEVBQ0wsNEJBQTRCLEVBQzVCLDZCQUE2QixHQUc5QixNQUFNLHlDQUF5QyxDQUFDO0FBQ2pELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFcEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQVNoRCxNQUFNLG9CQUFvQixHQUFnQztJQUN4RCxLQUFLLEVBQUUsaUJBQWlCO0NBQ3pCLENBQUM7QUFFRiw0REFBNEQ7QUFDNUQsTUFBTSxVQUFVLDZCQUE2QixDQUMzQyxVQUErQixFQUFFO0lBRWpDLE1BQU0sRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFLEdBQUcsUUFBUSxFQUFFLEdBQUcsNkJBQTZCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDL0UsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0MsT0FBTyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDcEMsRUFBRSxFQUFFLDZCQUE2QjtRQUNqQyxJQUFJLEVBQUUsbUJBQW1CO1FBQ3pCLE9BQU8sRUFBRTtZQUNQLE9BQU8sRUFBRSxtQkFBbUI7WUFDNUIsU0FBUyxFQUFFLGFBQWEsQ0FBQyxRQUFRLENBQUM7U0FDbkM7UUFDRCxJQUFJLEVBQUU7WUFDSjtnQkFDRSxJQUFJLEVBQUUsS0FBSztnQkFDWCxLQUFLLEVBQUUsUUFBUTtnQkFDZixHQUFHLEVBQUUsVUFBVSxJQUFJLElBQUksUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLFlBQVk7YUFDeEQ7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxxRkFBcUY7QUFDckYsTUFBTSxVQUFVLFlBQVksQ0FDMUIsSUFBSSxHQUFHLG1CQUFtQixFQUMxQixlQUE0QyxvQkFBb0I7SUFFaEUsTUFBTSxPQUFPLEdBQUcsZUFBZSxDQUFDO0lBQ2hDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FDckIsSUFBSSxPQUFPLEVBQUU7U0FDVixJQUFJLENBQUMsSUFBSSxDQUFDO1NBQ1YsV0FBVyxDQUFDLG1EQUFtRCxDQUFDO1NBQ2hFLE9BQU8sQ0FBQyxPQUFPLEVBQUUsZUFBZSxDQUFDLEVBQ3BDLDRCQUE0QixDQUM3QixDQUFDLE1BQU0sQ0FBQyxLQUFLLElBQUksRUFBRTtRQUNsQixNQUFNLE9BQU8sR0FBRyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM3QyxJQUFJLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUN4QixPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDOUYsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUMsRUFBRSxHQUFHLGFBQWEsRUFBRSxHQUFHLE9BQU8sQ0FBQztRQUNyRCxNQUFNLFlBQVksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDMUMsQ0FBQyxDQUFDLENBQUM7SUFDSCxNQUFNLGNBQWMsR0FBRyxtQkFBbUIsQ0FBQyxHQUFHLEVBQUU7UUFDOUMsT0FBTyw2QkFBNkIsQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ3JFLENBQUMsRUFBRSxZQUFZLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDekIsT0FBTyxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUNuQyxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsU0FBUyxtQkFBbUIsQ0FBQyxPQUFnQjtJQUMzQyxPQUFPLFNBQVMsQ0FBQyxPQUFPLEVBQUUsNEJBQTRCLENBQUMsQ0FBQztBQUMxRCxDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsSUFBWTtJQUMzQixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztBQUNqRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGb3JlZ3JvdW5kIGBkYnggbW9kZWwtZ2F0ZXdheWAgY29tbWFuZC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgYWRkQXJncywgcGFyc2VBcmdzLCBzZXJpYWxpemVBcmdzIH0gZnJvbSBcIkBkYngtdG9vbHMvY2xpLWFyZ3NcIjtcbmltcG9ydCB7IGJ1aWxkU2VydmljZUNvbW1hbmQsIHR5cGUgQ2xpU2VydmljZUNsaURlcGVuZGVuY2llcyB9IGZyb20gXCJAZGJ4LXRvb2xzL2NsaS1zZXJ2aWNlL2NsaVwiO1xuaW1wb3J0IHsgZGVmaW5lU2VydmljZSwgdHlwZSBDbGlTZXJ2aWNlRGVmaW5pdGlvbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2NsaS1zZXJ2aWNlL2RlZmluaXRpb25cIjtcbmltcG9ydCB7XG4gIE1vZGVsR2F0ZXdheUNsaU9wdGlvbnNTY2hlbWEsXG4gIHJlc29sdmVNb2RlbEdhdGV3YXlDbGlPcHRpb25zLFxuICB0eXBlIE1vZGVsR2F0ZXdheUNsaU9wdGlvbnMsXG4gIHR5cGUgTW9kZWxHYXRld2F5T3B0aW9ucyxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsLWdhdGV3YXkvb3B0aW9uc1wiO1xuaW1wb3J0IHsgQ29tbWFuZCB9IGZyb20gXCJjb21tYW5kZXJcIjtcblxuaW1wb3J0IHsgUEFDS0FHRV9WRVJTSU9OIH0gZnJvbSBcIi4uLy4uL2luZGV4LnRzXCI7XG5pbXBvcnQgeyBzdGFydE1vZGVsR2F0ZXdheSB9IGZyb20gXCIuL3NlcnZlci50c1wiO1xuXG4vKiogSW5qZWN0YWJsZSBmb3JlZ3JvdW5kIGdhdGV3YXkgYm91bmRhcnkgZm9yIENMSSB0ZXN0cy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTW9kZWxHYXRld2F5Q2xpRGVwZW5kZW5jaWVzIHtcbiAgc3RhcnQob3B0aW9uczogTW9kZWxHYXRld2F5T3B0aW9ucyk6IFByb21pc2U8dm9pZD47XG4gIC8qKiBPcHRpb25hbCBzZXJ2aWNlIENMSSBib3VuZGFyeSBmb3IgdGVzdHMgYW5kIGVtYmVkZGluZy4gKi9cbiAgcmVhZG9ubHkgc2VydmljZT86IENsaVNlcnZpY2VDbGlEZXBlbmRlbmNpZXM7XG59XG5cbmNvbnN0IERFRkFVTFRfREVQRU5ERU5DSUVTOiBNb2RlbEdhdGV3YXlDbGlEZXBlbmRlbmNpZXMgPSB7XG4gIHN0YXJ0OiBzdGFydE1vZGVsR2F0ZXdheSxcbn07XG5cbi8qKiBCdWlsZCB0aGUgdHJheS1vbmx5IG1vZGVsLWdhdGV3YXkgc2VydmljZSBkZWZpbml0aW9uLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1vZGVsR2F0ZXdheVNlcnZpY2VEZWZpbml0aW9uKFxuICBvcHRpb25zOiBNb2RlbEdhdGV3YXlPcHRpb25zID0ge30sXG4pOiBDbGlTZXJ2aWNlRGVmaW5pdGlvbiB7XG4gIGNvbnN0IHsgcnVudGltZUluZm86IF8sIC4uLnJlc29sdmVkIH0gPSByZXNvbHZlTW9kZWxHYXRld2F5Q2xpT3B0aW9ucyhvcHRpb25zKTtcbiAgY29uc3QgaG9zdCA9IHVybEhvc3QocmVzb2x2ZWQubGlzdGVuLmhvc3QpO1xuICByZXR1cm4gZGVmaW5lU2VydmljZShpbXBvcnQubWV0YS51cmwsIHtcbiAgICBpZDogXCJkYngtdG9vbHMuY2xpLW1vZGVsLWdhdGV3YXlcIixcbiAgICBuYW1lOiBcImRieCBtb2RlbCBnYXRld2F5XCIsXG4gICAgY29tbWFuZDoge1xuICAgICAgYmluTmFtZTogXCJkYngtbW9kZWwtZ2F0ZXdheVwiLFxuICAgICAgYXJndW1lbnRzOiBzZXJpYWxpemVBcmdzKHJlc29sdmVkKSxcbiAgICB9LFxuICAgIG1lbnU6IFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogXCJ1cmxcIixcbiAgICAgICAgbGFiZWw6IFwiTW9kZWxzXCIsXG4gICAgICAgIHVybDogYGh0dHA6Ly8ke2hvc3R9OiR7cmVzb2x2ZWQubGlzdGVuLnBvcnR9L3YxL21vZGVsc2AsXG4gICAgICB9LFxuICAgIF0sXG4gIH0pO1xufVxuXG4vKiogQnVpbGQgZm9yZWdyb3VuZCBhbmQgc2VydmljZSBtb2RlbC1nYXRld2F5IGNvbW1hbmRzIHdpdGhvdXQgc3RhcnRpbmcgYSBzZXJ2ZXIuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRQcm9ncmFtKFxuICBuYW1lID0gXCJkYnggbW9kZWwtZ2F0ZXdheVwiLFxuICBkZXBlbmRlbmNpZXM6IE1vZGVsR2F0ZXdheUNsaURlcGVuZGVuY2llcyA9IERFRkFVTFRfREVQRU5ERU5DSUVTLFxuKTogQ29tbWFuZCB7XG4gIGNvbnN0IHZlcnNpb24gPSBQQUNLQUdFX1ZFUlNJT047XG4gIGNvbnN0IHByb2dyYW0gPSBhZGRBcmdzKFxuICAgIG5ldyBDb21tYW5kKClcbiAgICAgIC5uYW1lKG5hbWUpXG4gICAgICAuZGVzY3JpcHRpb24oXCJSdW4gb3IgbWFuYWdlIHRoZSBBcHBLaXQgRGF0YWJyaWNrcyBtb2RlbCBnYXRld2F5XCIpXG4gICAgICAudmVyc2lvbih2ZXJzaW9uLCBcIi12LCAtLXZlcnNpb25cIiksXG4gICAgTW9kZWxHYXRld2F5Q2xpT3B0aW9uc1NjaGVtYSxcbiAgKS5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBtb2RlbEdhdGV3YXlPcHRpb25zKHByb2dyYW0pO1xuICAgIGlmIChvcHRpb25zLnJ1bnRpbWVJbmZvKSB7XG4gICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShgJHtKU09OLnN0cmluZ2lmeSh7IGltcGxlbWVudGF0aW9uOiBcInR5cGVzY3JpcHQtYXBwa2l0XCIsIHZlcnNpb24gfSl9XFxuYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHsgcnVudGltZUluZm86IF8sIC4uLnNlcnZlck9wdGlvbnMgfSA9IG9wdGlvbnM7XG4gICAgYXdhaXQgZGVwZW5kZW5jaWVzLnN0YXJ0KHNlcnZlck9wdGlvbnMpO1xuICB9KTtcbiAgY29uc3Qgc2VydmljZUNvbW1hbmQgPSBidWlsZFNlcnZpY2VDb21tYW5kKCgpID0+IHtcbiAgICByZXR1cm4gbW9kZWxHYXRld2F5U2VydmljZURlZmluaXRpb24obW9kZWxHYXRld2F5T3B0aW9ucyhwcm9ncmFtKSk7XG4gIH0sIGRlcGVuZGVuY2llcy5zZXJ2aWNlKTtcbiAgcHJvZ3JhbS5hZGRDb21tYW5kKHNlcnZpY2VDb21tYW5kKTtcbiAgcmV0dXJuIHByb2dyYW07XG59XG5cbmZ1bmN0aW9uIG1vZGVsR2F0ZXdheU9wdGlvbnMoY29tbWFuZDogQ29tbWFuZCk6IE1vZGVsR2F0ZXdheUNsaU9wdGlvbnMge1xuICByZXR1cm4gcGFyc2VBcmdzKGNvbW1hbmQsIE1vZGVsR2F0ZXdheUNsaU9wdGlvbnNTY2hlbWEpO1xufVxuXG5mdW5jdGlvbiB1cmxIb3N0KGhvc3Q6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBob3N0LmluY2x1ZGVzKFwiOlwiKSA/IGBbJHtob3N0fV1gIDogaG9zdDtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foreground model-gateway server owned by the CLI package.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { server } from "@databricks/appkit";
|
|
7
|
+
import { type ModelGatewayOptions } from "@dbx-tools/shared-model-gateway/options";
|
|
8
|
+
type AppKitServerOptions = NonNullable<Parameters<typeof server>[0]>;
|
|
9
|
+
/** Resolve AppKit server settings for the loopback model gateway. */
|
|
10
|
+
export declare function modelGatewayServerOptions(options?: ModelGatewayOptions): AppKitServerOptions;
|
|
11
|
+
/** Start the foreground model gateway and keep its AppKit server active. */
|
|
12
|
+
export declare function startModelGateway(options?: ModelGatewayOptions): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foreground model-gateway server owned by the CLI package.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { createApp, server } from "@databricks/appkit";
|
|
7
|
+
import { modelGateway } from "@dbx-tools/appkit-model-gateway/plugin";
|
|
8
|
+
import { sendHealth } from "@dbx-tools/appkit-model-gateway/routes";
|
|
9
|
+
import { workspaceClient } from "@dbx-tools/databricks";
|
|
10
|
+
import { resolveModelGatewayOptions, } from "@dbx-tools/shared-model-gateway/options";
|
|
11
|
+
/** Resolve AppKit server settings for the loopback model gateway. */
|
|
12
|
+
export function modelGatewayServerOptions(options = {}) {
|
|
13
|
+
return appKitServerOptions(resolveModelGatewayOptions(options));
|
|
14
|
+
}
|
|
15
|
+
function appKitServerOptions(options) {
|
|
16
|
+
return {
|
|
17
|
+
bodyLimit: options.bodyLimit,
|
|
18
|
+
host: options.listen.host,
|
|
19
|
+
port: options.listen.port,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Start the foreground model gateway and keep its AppKit server active. */
|
|
23
|
+
export async function startModelGateway(options = {}) {
|
|
24
|
+
const resolved = resolveModelGatewayOptions(options);
|
|
25
|
+
const client = await workspaceClient.createWorkspaceClient({
|
|
26
|
+
...(resolved.profile ? { profile: resolved.profile } : {}),
|
|
27
|
+
});
|
|
28
|
+
await createApp({
|
|
29
|
+
client,
|
|
30
|
+
plugins: [modelGateway(), server(appKitServerOptions(resolved))],
|
|
31
|
+
onPluginsReady(appkit) {
|
|
32
|
+
appkit.server.extend((application) => {
|
|
33
|
+
application.get("/api/healthz", (_request, response) => {
|
|
34
|
+
sendHealth(response);
|
|
35
|
+
});
|
|
36
|
+
application.get("/v1/models", (request, response) => {
|
|
37
|
+
void appkit.modelGateway.models(request, response);
|
|
38
|
+
});
|
|
39
|
+
for (const [path, protocol] of [
|
|
40
|
+
["/v1/chat/completions", "openai-chat"],
|
|
41
|
+
["/v1/responses", "openai-responses"],
|
|
42
|
+
["/v1/messages", "anthropic-messages"],
|
|
43
|
+
["/v1/embeddings", "openai-embeddings"],
|
|
44
|
+
]) {
|
|
45
|
+
application.post(path, (request, response) => {
|
|
46
|
+
void appkit.modelGateway.inference(protocol, request, response);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21vZGVsLWdhdGV3YXkvc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDcEUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3hELE9BQU8sRUFDTCwwQkFBMEIsR0FHM0IsTUFBTSx5Q0FBeUMsQ0FBQztBQUlqRCxxRUFBcUU7QUFDckUsTUFBTSxVQUFVLHlCQUF5QixDQUFDLFVBQStCLEVBQUU7SUFDekUsT0FBTyxtQkFBbUIsQ0FBQywwQkFBMEIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBQ2xFLENBQUM7QUFFRCxTQUFTLG1CQUFtQixDQUFDLE9BQW9DO0lBQy9ELE9BQU87UUFDTCxTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7UUFDNUIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSTtRQUN6QixJQUFJLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJO0tBQzFCLENBQUM7QUFDSixDQUFDO0FBRUQsNEVBQTRFO0FBQzVFLE1BQU0sQ0FBQyxLQUFLLFVBQVUsaUJBQWlCLENBQUMsVUFBK0IsRUFBRTtJQUN2RSxNQUFNLFFBQVEsR0FBRywwQkFBMEIsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNyRCxNQUFNLE1BQU0sR0FBRyxNQUFNLGVBQWUsQ0FBQyxxQkFBcUIsQ0FBQztRQUN6RCxHQUFHLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7S0FDM0QsQ0FBQyxDQUFDO0lBQ0gsTUFBTSxTQUFTLENBQUM7UUFDZCxNQUFNO1FBQ04sT0FBTyxFQUFFLENBQUMsWUFBWSxFQUFFLEVBQUUsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDaEUsY0FBYyxDQUFDLE1BQU07WUFDbkIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtnQkFDbkMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxRQUFRLEVBQUUsUUFBUSxFQUFFLEVBQUU7b0JBQ3JELFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztnQkFDdkIsQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsV0FBVyxDQUFDLEdBQUcsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUU7b0JBQ2xELEtBQUssTUFBTSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUNyRCxDQUFDLENBQUMsQ0FBQztnQkFDSCxLQUFLLE1BQU0sQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUk7b0JBQzdCLENBQUMsc0JBQXNCLEVBQUUsYUFBYSxDQUFDO29CQUN2QyxDQUFDLGVBQWUsRUFBRSxrQkFBa0IsQ0FBQztvQkFDckMsQ0FBQyxjQUFjLEVBQUUsb0JBQW9CLENBQUM7b0JBQ3RDLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLENBQUM7aUJBQy9CLEVBQUUsQ0FBQztvQkFDWCxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsRUFBRTt3QkFDM0MsS0FBSyxNQUFNLENBQUMsWUFBWSxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO29CQUNsRSxDQUFDLENBQUMsQ0FBQztnQkFDTCxDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRm9yZWdyb3VuZCBtb2RlbC1nYXRld2F5IHNlcnZlciBvd25lZCBieSB0aGUgQ0xJIHBhY2thZ2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGNyZWF0ZUFwcCwgc2VydmVyIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgbW9kZWxHYXRld2F5IH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0LW1vZGVsLWdhdGV3YXkvcGx1Z2luXCI7XG5pbXBvcnQgeyBzZW5kSGVhbHRoIH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0LW1vZGVsLWdhdGV3YXkvcm91dGVzXCI7XG5pbXBvcnQgeyB3b3Jrc3BhY2VDbGllbnQgfSBmcm9tIFwiQGRieC10b29scy9kYXRhYnJpY2tzXCI7XG5pbXBvcnQge1xuICByZXNvbHZlTW9kZWxHYXRld2F5T3B0aW9ucyxcbiAgdHlwZSBNb2RlbEdhdGV3YXlPcHRpb25zLFxuICB0eXBlIFJlc29sdmVkTW9kZWxHYXRld2F5T3B0aW9ucyxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsLWdhdGV3YXkvb3B0aW9uc1wiO1xuXG50eXBlIEFwcEtpdFNlcnZlck9wdGlvbnMgPSBOb25OdWxsYWJsZTxQYXJhbWV0ZXJzPHR5cGVvZiBzZXJ2ZXI+WzBdPjtcblxuLyoqIFJlc29sdmUgQXBwS2l0IHNlcnZlciBzZXR0aW5ncyBmb3IgdGhlIGxvb3BiYWNrIG1vZGVsIGdhdGV3YXkuICovXG5leHBvcnQgZnVuY3Rpb24gbW9kZWxHYXRld2F5U2VydmVyT3B0aW9ucyhvcHRpb25zOiBNb2RlbEdhdGV3YXlPcHRpb25zID0ge30pOiBBcHBLaXRTZXJ2ZXJPcHRpb25zIHtcbiAgcmV0dXJuIGFwcEtpdFNlcnZlck9wdGlvbnMocmVzb2x2ZU1vZGVsR2F0ZXdheU9wdGlvbnMob3B0aW9ucykpO1xufVxuXG5mdW5jdGlvbiBhcHBLaXRTZXJ2ZXJPcHRpb25zKG9wdGlvbnM6IFJlc29sdmVkTW9kZWxHYXRld2F5T3B0aW9ucyk6IEFwcEtpdFNlcnZlck9wdGlvbnMge1xuICByZXR1cm4ge1xuICAgIGJvZHlMaW1pdDogb3B0aW9ucy5ib2R5TGltaXQsXG4gICAgaG9zdDogb3B0aW9ucy5saXN0ZW4uaG9zdCxcbiAgICBwb3J0OiBvcHRpb25zLmxpc3Rlbi5wb3J0LFxuICB9O1xufVxuXG4vKiogU3RhcnQgdGhlIGZvcmVncm91bmQgbW9kZWwgZ2F0ZXdheSBhbmQga2VlcCBpdHMgQXBwS2l0IHNlcnZlciBhY3RpdmUuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRNb2RlbEdhdGV3YXkob3B0aW9uczogTW9kZWxHYXRld2F5T3B0aW9ucyA9IHt9KTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZU1vZGVsR2F0ZXdheU9wdGlvbnMob3B0aW9ucyk7XG4gIGNvbnN0IGNsaWVudCA9IGF3YWl0IHdvcmtzcGFjZUNsaWVudC5jcmVhdGVXb3Jrc3BhY2VDbGllbnQoe1xuICAgIC4uLihyZXNvbHZlZC5wcm9maWxlID8geyBwcm9maWxlOiByZXNvbHZlZC5wcm9maWxlIH0gOiB7fSksXG4gIH0pO1xuICBhd2FpdCBjcmVhdGVBcHAoe1xuICAgIGNsaWVudCxcbiAgICBwbHVnaW5zOiBbbW9kZWxHYXRld2F5KCksIHNlcnZlcihhcHBLaXRTZXJ2ZXJPcHRpb25zKHJlc29sdmVkKSldLFxuICAgIG9uUGx1Z2luc1JlYWR5KGFwcGtpdCkge1xuICAgICAgYXBwa2l0LnNlcnZlci5leHRlbmQoKGFwcGxpY2F0aW9uKSA9PiB7XG4gICAgICAgIGFwcGxpY2F0aW9uLmdldChcIi9hcGkvaGVhbHRoelwiLCAoX3JlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICAgICAgc2VuZEhlYWx0aChyZXNwb25zZSk7XG4gICAgICAgIH0pO1xuICAgICAgICBhcHBsaWNhdGlvbi5nZXQoXCIvdjEvbW9kZWxzXCIsIChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xuICAgICAgICAgIHZvaWQgYXBwa2l0Lm1vZGVsR2F0ZXdheS5tb2RlbHMocmVxdWVzdCwgcmVzcG9uc2UpO1xuICAgICAgICB9KTtcbiAgICAgICAgZm9yIChjb25zdCBbcGF0aCwgcHJvdG9jb2xdIG9mIFtcbiAgICAgICAgICBbXCIvdjEvY2hhdC9jb21wbGV0aW9uc1wiLCBcIm9wZW5haS1jaGF0XCJdLFxuICAgICAgICAgIFtcIi92MS9yZXNwb25zZXNcIiwgXCJvcGVuYWktcmVzcG9uc2VzXCJdLFxuICAgICAgICAgIFtcIi92MS9tZXNzYWdlc1wiLCBcImFudGhyb3BpYy1tZXNzYWdlc1wiXSxcbiAgICAgICAgICBbXCIvdjEvZW1iZWRkaW5nc1wiLCBcIm9wZW5haS1lbWJlZGRpbmdzXCJdLFxuICAgICAgICBdIGFzIGNvbnN0KSB7XG4gICAgICAgICAgYXBwbGljYXRpb24ucG9zdChwYXRoLCAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcbiAgICAgICAgICAgIHZvaWQgYXBwa2l0Lm1vZGVsR2F0ZXdheS5pbmZlcmVuY2UocHJvdG9jb2wsIHJlcXVlc3QsIHJlc3BvbnNlKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSxcbiAgfSk7XG59XG4iXX0=
|