@dbx-tools/cli-model-proxy 0.3.44 → 0.4.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/lib/bin/dbx-tools-model-proxy.d.ts +2 -0
- package/lib/bin/dbx-tools-model-proxy.js +13 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +12 -0
- package/lib/src/backend.d.ts +77 -0
- package/lib/src/backend.js +100 -0
- package/lib/src/cli.d.ts +25 -0
- package/lib/src/cli.js +164 -0
- package/lib/src/defaults.d.ts +43 -0
- package/lib/src/defaults.js +48 -0
- package/lib/src/server.d.ts +79 -0
- package/lib/src/server.js +515 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +28 -12
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible HTTP proxy in front of Databricks Model Serving.
|
|
3
|
+
*
|
|
4
|
+
* Databricks serving endpoints speak OpenAI wire formats, so this server is a
|
|
5
|
+
* thin pass-through: it resolves the request's (possibly fuzzy) `model` to a
|
|
6
|
+
* real endpoint id via the {@link DatabricksBackend}, stamps a fresh auth
|
|
7
|
+
* header, and forwards to the right Databricks URL:
|
|
8
|
+
*
|
|
9
|
+
* - Chat Completions → `/serving-endpoints/<name>/invocations`, except for
|
|
10
|
+
* Responses-only models (Codex) which are translated and sent to
|
|
11
|
+
* `/serving-endpoints/responses`.
|
|
12
|
+
* - Responses → native `/serving-endpoints/responses` (OpenAI-family) or
|
|
13
|
+
* `/serving-endpoints/open-responses` (Claude/Gemini/…), body forwarded
|
|
14
|
+
* as-is. No chat round-trip.
|
|
15
|
+
*
|
|
16
|
+
* Any OpenAI-compatible tool (iTerm, editors, the `openai` SDK, Codex CLI) can
|
|
17
|
+
* point its base URL at this server and use loose model names.
|
|
18
|
+
*
|
|
19
|
+
* Routes:
|
|
20
|
+
* - `GET /health`, `GET /` liveness
|
|
21
|
+
* - `GET /v1/models`, `GET /models` list resolvable endpoints. Emits the
|
|
22
|
+
* OpenAI `{object:"list",data:[…]}` shape by default; when the request
|
|
23
|
+
* looks like the Codex CLI (a `client_version` query param, which Codex
|
|
24
|
+
* always sends) it emits Codex's `{models:[{slug,…}]}` shape instead, so
|
|
25
|
+
* both standard OpenAI clients and Codex can enumerate the catalogue.
|
|
26
|
+
* - `POST /v1/chat/completions` proxy (also `/completions`,
|
|
27
|
+
* `/v1/completions`, `/v1/embeddings`, and the un-prefixed variants)
|
|
28
|
+
* - `POST /v1/responses` OpenAI Responses API, forwarded to
|
|
29
|
+
* Databricks' native Responses / Open Responses surface.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
import { createServer } from "node:http";
|
|
34
|
+
import { async as asyncUtil, error, json, log } from "@dbx-tools/shared-core";
|
|
35
|
+
import { classify, openaiChat, openaiResponses } from "@dbx-tools/shared-model";
|
|
36
|
+
import { Agent } from "undici";
|
|
37
|
+
import { DEFAULT_BIND_HOST, DEFAULT_PORT, DEFAULT_RETRY } from "./defaults.js";
|
|
38
|
+
const { chatToResponsesRequest, responseToChatCompletion, sanitizeOpenResponsesRequest } = openaiResponses;
|
|
39
|
+
const logger = log.logger("model-proxy/server");
|
|
40
|
+
/**
|
|
41
|
+
* Dispatcher for every upstream call, with undici's inactivity timeouts
|
|
42
|
+
* DISABLED (`0`).
|
|
43
|
+
*
|
|
44
|
+
* A proxied turn is only as fast as the model behind it, and undici's
|
|
45
|
+
* defaults (300s `headersTimeout`, 300s `bodyTimeout`) are both wrong here.
|
|
46
|
+
* `bodyTimeout` is the killer: it measures the gap BETWEEN chunks, so a model
|
|
47
|
+
* that thinks for a while mid-stream - extended reasoning, a long tool round
|
|
48
|
+
* trip, a slow Genie/SQL step - trips it and undici tears the socket down
|
|
49
|
+
* with `UND_ERR_BODY_TIMEOUT`, which the client sees as a truncated stream.
|
|
50
|
+
*
|
|
51
|
+
* The proxy has no opinion on how long a model may take, so it imposes no
|
|
52
|
+
* deadline: the stream ends when the upstream ends it, or when the client
|
|
53
|
+
* hangs up (see {@link requestAbortSignal}). Callers that DO want a deadline
|
|
54
|
+
* should send one, the same as they would to OpenAI.
|
|
55
|
+
*/
|
|
56
|
+
const upstreamDispatcher = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
57
|
+
/** POST routes forwarded to a serving endpoint (chat or Responses-only). */
|
|
58
|
+
const PROXY_PATHS = new Set([
|
|
59
|
+
"/v1/chat/completions",
|
|
60
|
+
"/chat/completions",
|
|
61
|
+
"/v1/completions",
|
|
62
|
+
"/completions",
|
|
63
|
+
"/v1/embeddings",
|
|
64
|
+
"/embeddings",
|
|
65
|
+
]);
|
|
66
|
+
/** GET routes that list the resolvable model catalogue. */
|
|
67
|
+
const MODELS_PATHS = new Set(["/v1/models", "/models"]);
|
|
68
|
+
/** POST routes that carry an OpenAI Responses API body (native passthrough). */
|
|
69
|
+
const RESPONSES_PATHS = new Set(["/v1/responses", "/responses"]);
|
|
70
|
+
/**
|
|
71
|
+
* Extra request fields to strip before forwarding, on top of
|
|
72
|
+
* `openaiChat.UNSUPPORTED_CHAT_FIELDS`. Comma-separated in `PROXY_DROP_FIELDS`.
|
|
73
|
+
*
|
|
74
|
+
* An escape hatch: when a workspace or a new client version trips a field
|
|
75
|
+
* Databricks rejects, an operator can unblock themselves immediately instead of
|
|
76
|
+
* waiting on a release of this package.
|
|
77
|
+
*/
|
|
78
|
+
function extraDropFields() {
|
|
79
|
+
return (process.env.PROXY_DROP_FIELDS ?? "")
|
|
80
|
+
.split(",")
|
|
81
|
+
.map((field) => field.trim())
|
|
82
|
+
.filter((field) => field.length > 0);
|
|
83
|
+
}
|
|
84
|
+
/** Build (but do not start) the proxy HTTP server. */
|
|
85
|
+
export function createProxyServer(backend, options = {}) {
|
|
86
|
+
const server = createServer((req, res) => {
|
|
87
|
+
void handleRequest(backend, options, req, res);
|
|
88
|
+
});
|
|
89
|
+
// Match the upstream policy on the INBOUND side: never cut a client off
|
|
90
|
+
// mid-turn. Node's defaults (300s `requestTimeout`, 60s `headersTimeout`)
|
|
91
|
+
// are sized for ordinary web traffic, not for holding a streamed model
|
|
92
|
+
// response open, and a client that hits one gets a dead socket rather than
|
|
93
|
+
// an error it can act on. `0` disables each; the turn now ends when the
|
|
94
|
+
// model is done or the client disconnects.
|
|
95
|
+
server.requestTimeout = 0;
|
|
96
|
+
server.headersTimeout = 0;
|
|
97
|
+
server.timeout = 0;
|
|
98
|
+
return server;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Signal that aborts when the client hangs up.
|
|
102
|
+
*
|
|
103
|
+
* Removing the timeouts means nothing else will ever end a forgotten request,
|
|
104
|
+
* so the client's own disconnect becomes the only backstop against an upstream
|
|
105
|
+
* call running on with no one to receive it. Passing this to `fetch` is what
|
|
106
|
+
* makes a cancelled turn (Ctrl-C, a closed tab, a killed CLI) release the
|
|
107
|
+
* Databricks-side stream instead of leaking it for the life of the process.
|
|
108
|
+
*
|
|
109
|
+
* Keyed off the RESPONSE, not the request: `IncomingMessage` emits `close` as
|
|
110
|
+
* soon as its body has been fully consumed, which on every POST here happens
|
|
111
|
+
* before the upstream call is even made - watching that would abort each turn
|
|
112
|
+
* instantly. `ServerResponse` emits `close` when the response finishes or the
|
|
113
|
+
* connection drops, so the `writableFinished` check separates "client left"
|
|
114
|
+
* from "we replied".
|
|
115
|
+
*/
|
|
116
|
+
function clientAbortSignal(res) {
|
|
117
|
+
const controller = new AbortController();
|
|
118
|
+
res.once("close", () => {
|
|
119
|
+
if (!res.writableFinished)
|
|
120
|
+
controller.abort();
|
|
121
|
+
});
|
|
122
|
+
return controller.signal;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* POST a JSON body to a serving endpoint on the no-timeout
|
|
126
|
+
* {@link upstreamDispatcher}, cancelled via `signal`.
|
|
127
|
+
*
|
|
128
|
+
* Every upstream call goes through here so the timeout and cancellation policy
|
|
129
|
+
* is stated once. `dispatcher` is an undici extension that the DOM
|
|
130
|
+
* `RequestInit` Node compiles against does not declare, hence the local
|
|
131
|
+
* intersection type rather than a cast at each call site.
|
|
132
|
+
*/
|
|
133
|
+
async function upstreamFetch(url, headers, body, signal) {
|
|
134
|
+
const init = { method: "POST", headers, body, signal };
|
|
135
|
+
// `dispatcher` is an undici extension the DOM `RequestInit` doesn't declare,
|
|
136
|
+
// and it can't simply be typed on: `@types/node` pins its own `undici-types`
|
|
137
|
+
// copy, so the `Agent` from our `undici` dependency is a structurally
|
|
138
|
+
// identical but nominally different type than the one the global `fetch`
|
|
139
|
+
// signature expects. Attaching it structurally sidesteps a version skew that
|
|
140
|
+
// no cast expresses cleanly, and `fetch` reads it at runtime all the same.
|
|
141
|
+
Reflect.set(init, "dispatcher", upstreamDispatcher);
|
|
142
|
+
return fetch(url, init);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Delay (ms) a `429` response asks us to wait, from its `Retry-After` header,
|
|
146
|
+
* or `undefined` when absent/unparseable. Handles both header forms: an integer
|
|
147
|
+
* count of seconds, and an HTTP date to wait until (clamped to `>= 0`).
|
|
148
|
+
*/
|
|
149
|
+
export function parseRetryAfterMs(header, nowMs) {
|
|
150
|
+
if (!header)
|
|
151
|
+
return undefined;
|
|
152
|
+
const trimmed = header.trim();
|
|
153
|
+
if (/^\d+$/.test(trimmed))
|
|
154
|
+
return Number(trimmed) * 1000;
|
|
155
|
+
const when = Date.parse(trimmed);
|
|
156
|
+
if (Number.isNaN(when))
|
|
157
|
+
return undefined;
|
|
158
|
+
return Math.max(0, when - nowMs);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Backoff (ms) before retry `attempt` (0-based): exponential from
|
|
162
|
+
* `baseDelayMs`, capped at `maxDelayMs`, with up to +50% jitter so concurrent
|
|
163
|
+
* retries from one agent don't resynchronize into the next burst. A server-sent
|
|
164
|
+
* `Retry-After` wins outright (still capped), since the server knows better
|
|
165
|
+
* than our schedule when it will accept traffic again.
|
|
166
|
+
*/
|
|
167
|
+
export function backoffDelayMs(attempt, retry, retryAfterMs, jitter) {
|
|
168
|
+
if (retryAfterMs !== undefined)
|
|
169
|
+
return Math.min(retryAfterMs, retry.maxDelayMs);
|
|
170
|
+
const exponential = retry.baseDelayMs * 2 ** attempt;
|
|
171
|
+
const capped = Math.min(exponential, retry.maxDelayMs);
|
|
172
|
+
return Math.round(capped * (1 + jitter * 0.5));
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* {@link upstreamFetch} with in-proxy `429` handling. When `retry.enabled`, a
|
|
176
|
+
* 429 is retried up to `retry.maxRetries` times with {@link backoffDelayMs}
|
|
177
|
+
* backoff instead of being surfaced; the throttled response body is drained
|
|
178
|
+
* each time so the connection is released. The retry happens BEFORE the caller
|
|
179
|
+
* writes any status line, so it is transparent to both streaming and
|
|
180
|
+
* non-streaming paths - the client only ever sees the final response.
|
|
181
|
+
*
|
|
182
|
+
* Bails early (returning the 429) when retries are exhausted, when the client
|
|
183
|
+
* has hung up (`signal.aborted`), or when the response carries no `Retry-After`
|
|
184
|
+
* and we would otherwise loop blind past the cap.
|
|
185
|
+
*/
|
|
186
|
+
async function upstreamFetchRetrying(backend, url, headers, body, res, retry) {
|
|
187
|
+
const signal = clientAbortSignal(res);
|
|
188
|
+
const payload = JSON.stringify(body);
|
|
189
|
+
let response = await upstreamFetch(url, headers, payload, signal);
|
|
190
|
+
if (!retry.enabled)
|
|
191
|
+
return response;
|
|
192
|
+
for (let attempt = 0; response.status === 429 && attempt < retry.maxRetries; attempt++) {
|
|
193
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"), Date.now());
|
|
194
|
+
const delayMs = backoffDelayMs(attempt, retry, retryAfterMs, jitterFraction());
|
|
195
|
+
// Release the throttled response's socket before waiting; its body is an
|
|
196
|
+
// error payload we are choosing not to relay.
|
|
197
|
+
await response.body?.cancel().catch(() => { });
|
|
198
|
+
logger.warn("upstream 429; backing off", {
|
|
199
|
+
attempt: attempt + 1,
|
|
200
|
+
maxRetries: retry.maxRetries,
|
|
201
|
+
delayMs,
|
|
202
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
203
|
+
});
|
|
204
|
+
try {
|
|
205
|
+
await asyncUtil.sleep(delayMs, signal);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Client hung up mid-wait: return the last 429 rather than issue a
|
|
209
|
+
// request no one is listening for.
|
|
210
|
+
return response;
|
|
211
|
+
}
|
|
212
|
+
// Fresh auth header per attempt so a long backoff can't outlive the token.
|
|
213
|
+
const retryHeaders = { ...(await backend.authHeaders()), ...headers };
|
|
214
|
+
response = await upstreamFetch(url, retryHeaders, payload, signal);
|
|
215
|
+
}
|
|
216
|
+
return response;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Jitter fraction in `[0, 1)`. Split out so tests can stub determinism;
|
|
220
|
+
* `Math.random` is fine for spreading retry timing.
|
|
221
|
+
*/
|
|
222
|
+
function jitterFraction() {
|
|
223
|
+
return Math.random();
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Build and start the proxy, resolving once it is accepting connections.
|
|
227
|
+
* Returns the server and its base URL (with the actually-bound port, so a
|
|
228
|
+
* `port: 0` request surfaces the OS-assigned port).
|
|
229
|
+
*/
|
|
230
|
+
export async function startProxyServer(backend, options = {}) {
|
|
231
|
+
const host = options.host ?? DEFAULT_BIND_HOST;
|
|
232
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
233
|
+
const server = createProxyServer(backend, options);
|
|
234
|
+
await new Promise((resolve, reject) => {
|
|
235
|
+
const onError = (err) => reject(err);
|
|
236
|
+
server.once("error", onError);
|
|
237
|
+
server.listen(port, host, () => {
|
|
238
|
+
server.off("error", onError);
|
|
239
|
+
resolve();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
const address = server.address();
|
|
243
|
+
const boundPort = typeof address === "object" && address ? address.port : port;
|
|
244
|
+
return { server, url: `http://${host}:${boundPort}` };
|
|
245
|
+
}
|
|
246
|
+
async function handleRequest(backend, options, req, res) {
|
|
247
|
+
const rawUrl = req.url ?? "/";
|
|
248
|
+
const [path, query = ""] = rawUrl.split("?");
|
|
249
|
+
const routePath = path ?? "/";
|
|
250
|
+
try {
|
|
251
|
+
if (req.method === "GET" && (routePath === "/health" || routePath === "/")) {
|
|
252
|
+
sendJson(res, 200, { status: "ok" });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (options.apiKey && !isAuthorized(req, options.apiKey)) {
|
|
256
|
+
sendJson(res, 401, errorBody("invalid api key", "invalid_request_error"));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (req.method === "GET" && MODELS_PATHS.has(routePath)) {
|
|
260
|
+
// Codex always sends `?client_version=…`; use that to pick its list shape.
|
|
261
|
+
const wantsCodexShape = new URLSearchParams(query).has("client_version");
|
|
262
|
+
await handleModels(backend, res, wantsCodexShape);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const retry = options.retry ?? DEFAULT_RETRY;
|
|
266
|
+
if (req.method === "POST" && RESPONSES_PATHS.has(routePath)) {
|
|
267
|
+
await handleResponses(backend, req, res, retry);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (req.method === "POST" && PROXY_PATHS.has(routePath)) {
|
|
271
|
+
await handleProxy(backend, req, res, retry);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
sendJson(res, 404, errorBody(`unsupported route ${req.method ?? "?"} ${routePath}`, "invalid_request_error"));
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
const message = error.errorMessage(err);
|
|
278
|
+
logger.error("request failed", { path: routePath, error: message });
|
|
279
|
+
if (!res.headersSent)
|
|
280
|
+
sendJson(res, 500, errorBody(message, "proxy_error"));
|
|
281
|
+
else
|
|
282
|
+
res.end();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* `GET /v1/models`: surface the serving catalogue. Two shapes:
|
|
287
|
+
*
|
|
288
|
+
* - OpenAI (default): `{object:"list", data:[{id,object,created,owned_by}]}`,
|
|
289
|
+
* what the `openai` SDK and most tools expect.
|
|
290
|
+
* - Codex (`codexShape`): `{models:[{slug, display_name, …}]}`, the ChatGPT
|
|
291
|
+
* backend shape the Codex CLI decodes. A response missing the top-level
|
|
292
|
+
* `models` field makes Codex log "failed to decode models response", so it
|
|
293
|
+
* gets its own envelope. Only chat endpoints are advertised to Codex.
|
|
294
|
+
*/
|
|
295
|
+
async function handleModels(backend, res, codexShape) {
|
|
296
|
+
const endpoints = await backend.models(true);
|
|
297
|
+
if (codexShape) {
|
|
298
|
+
const models = endpoints
|
|
299
|
+
.filter((endpoint) => classify.endpointCapabilities(endpoint).chat)
|
|
300
|
+
.map((endpoint) => ({
|
|
301
|
+
slug: endpoint.name,
|
|
302
|
+
display_name: endpoint.displayName ?? endpoint.name,
|
|
303
|
+
...(endpoint.description ? { description: endpoint.description } : {}),
|
|
304
|
+
// Fields Codex's strict decoder requires on every model entry.
|
|
305
|
+
default_reasoning_level: "medium",
|
|
306
|
+
supported_reasoning_levels: [],
|
|
307
|
+
shell_type: "shell_command",
|
|
308
|
+
visibility: "list",
|
|
309
|
+
supported_in_api: true,
|
|
310
|
+
}));
|
|
311
|
+
sendJson(res, 200, { models });
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const data = endpoints.map((endpoint) => ({
|
|
315
|
+
id: endpoint.name,
|
|
316
|
+
object: "model",
|
|
317
|
+
created: 0,
|
|
318
|
+
owned_by: "databricks",
|
|
319
|
+
}));
|
|
320
|
+
sendJson(res, 200, { object: "list", data });
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Resolve the request's model and forward a Chat Completions body. Responses-
|
|
324
|
+
* only models (Codex) are translated to a Responses request and posted to
|
|
325
|
+
* `/serving-endpoints/responses`; everything else goes to `/invocations`.
|
|
326
|
+
*/
|
|
327
|
+
async function handleProxy(backend, req, res, retry) {
|
|
328
|
+
const body = await readJsonBody(req);
|
|
329
|
+
const requested = typeof body.model === "string" ? body.model : undefined;
|
|
330
|
+
if (!requested) {
|
|
331
|
+
sendJson(res, 400, errorBody("missing 'model' in request body", "invalid_request_error"));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const resolved = await backend.resolve(requested);
|
|
335
|
+
body.model = resolved.modelId;
|
|
336
|
+
if (backend.isResponsesOnly(resolved.modelId)) {
|
|
337
|
+
await proxyChatViaResponses(backend, body, requested, resolved.modelId, resolved.matched, res, retry);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
// This route forwards the client's body as-is, so anything Databricks refuses
|
|
341
|
+
// to parse fails the turn. Drop the known offenders rather than relaying a
|
|
342
|
+
// 400 the client can do nothing about.
|
|
343
|
+
const dropped = openaiChat.stripUnsupportedChatFields(body, extraDropFields());
|
|
344
|
+
const wantsStream = body.stream === true;
|
|
345
|
+
const headers = await backend.authHeaders();
|
|
346
|
+
headers["content-type"] = "application/json";
|
|
347
|
+
headers.accept = wantsStream ? "text/event-stream" : "application/json";
|
|
348
|
+
logger.info("proxy", {
|
|
349
|
+
requested,
|
|
350
|
+
resolved: resolved.modelId,
|
|
351
|
+
matched: resolved.matched,
|
|
352
|
+
upstream: "invocations",
|
|
353
|
+
stream: wantsStream,
|
|
354
|
+
...(dropped.length > 0 ? { dropped } : {}),
|
|
355
|
+
});
|
|
356
|
+
const upstream = await upstreamFetchRetrying(backend, backend.invocationsUrl(resolved.modelId), headers, body, res, retry);
|
|
357
|
+
res.writeHead(upstream.status, {
|
|
358
|
+
"content-type": upstream.headers.get("content-type") ?? "application/json",
|
|
359
|
+
"x-resolved-model": resolved.modelId,
|
|
360
|
+
});
|
|
361
|
+
await streamBody(upstream, res);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Chat Completions client → Responses-only Databricks model: translate the
|
|
365
|
+
* request, POST to `/serving-endpoints/responses`, translate the reply back.
|
|
366
|
+
* Streaming is not translated yet - those clients should use `/v1/responses`.
|
|
367
|
+
*/
|
|
368
|
+
async function proxyChatViaResponses(backend, body, requested, modelId, matched, res, retry) {
|
|
369
|
+
const { responses, stream } = chatToResponsesRequest(body);
|
|
370
|
+
responses.model = modelId;
|
|
371
|
+
if (stream) {
|
|
372
|
+
sendJson(res, 400, errorBody(`model ${modelId} only supports the Responses API; use POST /v1/responses for streaming`, "invalid_request_error"));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const headers = await backend.authHeaders();
|
|
376
|
+
headers["content-type"] = "application/json";
|
|
377
|
+
headers.accept = "application/json";
|
|
378
|
+
const upstreamUrl = backend.responsesUrl(modelId);
|
|
379
|
+
const forward = upstreamUrl.includes("/open-responses")
|
|
380
|
+
? sanitizeOpenResponsesRequest(responses)
|
|
381
|
+
: responses;
|
|
382
|
+
logger.info("proxy", {
|
|
383
|
+
requested,
|
|
384
|
+
resolved: modelId,
|
|
385
|
+
matched,
|
|
386
|
+
upstream: upstreamUrl,
|
|
387
|
+
stream: false,
|
|
388
|
+
});
|
|
389
|
+
const upstream = await upstreamFetchRetrying(backend, upstreamUrl, headers, forward, res, retry);
|
|
390
|
+
if (!upstream.ok) {
|
|
391
|
+
const text = await upstream.text();
|
|
392
|
+
res.writeHead(upstream.status, { "content-type": "application/json" });
|
|
393
|
+
res.end(text || JSON.stringify(errorBody("upstream error", "proxy_error")));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const responseJson = (await upstream.json());
|
|
397
|
+
sendJson(res, 200, responseToChatCompletion(responseJson, modelId));
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* `POST /v1/responses`: forward the Responses body to Databricks' native
|
|
401
|
+
* Responses / Open Responses surface (model stays in the body; URL is
|
|
402
|
+
* workspace-level). Streaming and non-streaming both pass through as-is.
|
|
403
|
+
*
|
|
404
|
+
* Open Responses (Claude/Gemini/…) only accepts `function` tools and
|
|
405
|
+
* `input_*` content part types, and rejects replayed Claude thinking
|
|
406
|
+
* blocks — so Codex's built-in tools, prior-turn `output_text`, and
|
|
407
|
+
* `redacted_thinking` / `thinking` / `reasoning` parts are rewritten or
|
|
408
|
+
* stripped there. The OpenAI `/responses` path keeps them - GPT supports
|
|
409
|
+
* those shapes.
|
|
410
|
+
*/
|
|
411
|
+
async function handleResponses(backend, req, res, retry) {
|
|
412
|
+
const body = await readJsonBody(req);
|
|
413
|
+
const requested = typeof body.model === "string" ? body.model : undefined;
|
|
414
|
+
if (!requested) {
|
|
415
|
+
sendJson(res, 400, errorBody("missing 'model' in request body", "invalid_request_error"));
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const resolved = await backend.resolve(requested);
|
|
419
|
+
body.model = resolved.modelId;
|
|
420
|
+
const upstreamUrl = backend.responsesUrl(resolved.modelId);
|
|
421
|
+
// Cross-provider Open Responses rejects non-function tools and `output_*`
|
|
422
|
+
// content parts in `input`; OpenAI `/responses` keeps Codex's native shapes.
|
|
423
|
+
const forward = upstreamUrl.includes("/open-responses")
|
|
424
|
+
? sanitizeOpenResponsesRequest(body)
|
|
425
|
+
: body;
|
|
426
|
+
const wantsStream = forward.stream === true;
|
|
427
|
+
const headers = await backend.authHeaders();
|
|
428
|
+
headers["content-type"] = "application/json";
|
|
429
|
+
headers.accept = wantsStream ? "text/event-stream" : "application/json";
|
|
430
|
+
const stripped = Array.isArray(body.tools) &&
|
|
431
|
+
(!Array.isArray(forward.tools) || forward.tools.length !== body.tools.length);
|
|
432
|
+
logger.info("responses", {
|
|
433
|
+
requested,
|
|
434
|
+
resolved: resolved.modelId,
|
|
435
|
+
matched: resolved.matched,
|
|
436
|
+
upstream: upstreamUrl,
|
|
437
|
+
stream: wantsStream,
|
|
438
|
+
...(stripped ? { strippedNonFunctionTools: true } : {}),
|
|
439
|
+
});
|
|
440
|
+
const upstream = await upstreamFetchRetrying(backend, upstreamUrl, headers, forward, res, retry);
|
|
441
|
+
res.writeHead(upstream.status, {
|
|
442
|
+
"content-type": upstream.headers.get("content-type") ?? "application/json",
|
|
443
|
+
"x-resolved-model": resolved.modelId,
|
|
444
|
+
});
|
|
445
|
+
await streamBody(upstream, res);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Pump an upstream `fetch` Response body to the Node response, chunk by chunk.
|
|
449
|
+
*
|
|
450
|
+
* A mid-stream upstream failure is logged rather than thrown: the status line
|
|
451
|
+
* and some number of chunks have already gone out, so there is no way left to
|
|
452
|
+
* turn it into an HTTP error and the only honest move is to end the response
|
|
453
|
+
* and say why in the log. Without this the read loop rejected into the caller's
|
|
454
|
+
* catch, which found `headersSent` and ended the response silently - making an
|
|
455
|
+
* upstream teardown indistinguishable from a clean finish.
|
|
456
|
+
*/
|
|
457
|
+
async function streamBody(upstream, res) {
|
|
458
|
+
const body = upstream.body;
|
|
459
|
+
if (!body) {
|
|
460
|
+
res.end();
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const reader = body.getReader();
|
|
464
|
+
try {
|
|
465
|
+
for (;;) {
|
|
466
|
+
const { done, value } = await reader.read();
|
|
467
|
+
if (done)
|
|
468
|
+
break;
|
|
469
|
+
if (value && !res.writableEnded)
|
|
470
|
+
res.write(Buffer.from(value));
|
|
471
|
+
if (res.writableEnded)
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch (err) {
|
|
476
|
+
// An aborted request is the expected shape of a client hanging up, not a
|
|
477
|
+
// fault worth reporting at error level.
|
|
478
|
+
const aborted = res.writableEnded || !res.writable;
|
|
479
|
+
logger[aborted ? "info" : "error"]("stream ended early", {
|
|
480
|
+
error: error.errorMessage(err),
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
await reader.cancel().catch(() => { });
|
|
485
|
+
res.end();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/** True when the request carries the expected bearer token. */
|
|
489
|
+
function isAuthorized(req, apiKey) {
|
|
490
|
+
const header = req.headers.authorization;
|
|
491
|
+
if (!header)
|
|
492
|
+
return false;
|
|
493
|
+
return header.replace(/^Bearer\s+/i, "").trim() === apiKey;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Drain a request body and parse it as a JSON object. An empty, malformed, or
|
|
497
|
+
* non-object body yields `{}`, which the callers reject as a missing `model`.
|
|
498
|
+
*/
|
|
499
|
+
async function readJsonBody(req) {
|
|
500
|
+
const chunks = [];
|
|
501
|
+
for await (const chunk of req) {
|
|
502
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
503
|
+
}
|
|
504
|
+
return json.parseRecord(Buffer.concat(chunks).toString("utf8")) ?? {};
|
|
505
|
+
}
|
|
506
|
+
/** Serialize and send a JSON response. */
|
|
507
|
+
function sendJson(res, status, body) {
|
|
508
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
509
|
+
res.end(JSON.stringify(body));
|
|
510
|
+
}
|
|
511
|
+
/** OpenAI-shaped error envelope. */
|
|
512
|
+
function errorBody(message, type) {
|
|
513
|
+
return { error: { message, type } };
|
|
514
|
+
}
|
|
515
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQStCRztBQUVILE9BQU8sRUFBRSxZQUFZLEVBQTBELE1BQU0sV0FBVyxDQUFDO0FBQ2pHLE9BQU8sRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDaEYsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUcvQixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBb0IsTUFBTSxZQUFZLENBQUM7QUFFOUYsTUFBTSxFQUFFLHNCQUFzQixFQUFFLHdCQUF3QixFQUFFLDRCQUE0QixFQUFFLEdBQ3RGLGVBQWUsQ0FBQztBQUVsQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFFaEQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLEtBQUssQ0FBQyxFQUFFLGNBQWMsRUFBRSxDQUFDLEVBQUUsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7QUFFNUUsNEVBQTRFO0FBQzVFLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxDQUFDO0lBQzFCLHNCQUFzQjtJQUN0QixtQkFBbUI7SUFDbkIsaUJBQWlCO0lBQ2pCLGNBQWM7SUFDZCxnQkFBZ0I7SUFDaEIsYUFBYTtDQUNkLENBQUMsQ0FBQztBQUVILDJEQUEyRDtBQUMzRCxNQUFNLFlBQVksR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLFlBQVksRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFD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RUFBRSxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRSxDQUFDLENBQUM7SUFDOUQsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDaEMsQ0FBQztBQUVELG9DQUFvQztBQUNwQyxTQUFTLFNBQVMsQ0FBQyxPQUFlLEVBQUUsSUFBWTtJQUM5QyxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUM7QUFDdEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3BlbkFJLWNvbXBhdGlibGUgSFRUUCBwcm94eSBpbiBmcm9udCBvZiBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcuXG4gKlxuICogRGF0YWJyaWNrcyBzZXJ2aW5nIGVuZHBvaW50cyBzcGVhayBPcGVuQUkgd2lyZSBmb3JtYXRzLCBzbyB0aGlzIHNlcnZlciBpcyBhXG4gKiB0aGluIHBhc3MtdGhyb3VnaDogaXQgcmVzb2x2ZXMgdGhlIHJlcXVlc3QncyAocG9zc2libHkgZnV6enkpIGBtb2RlbGAgdG8gYVxuICogcmVhbCBlbmRwb2ludCBpZCB2aWEgdGhlIHtAbGluayBEYXRhYnJpY2tzQmFja2VuZH0sIHN0YW1wcyBhIGZyZXNoIGF1dGhcbiAqIGhlYWRlciwgYW5kIGZvcndhcmRzIHRvIHRoZSByaWdodCBEYXRhYnJpY2tzIFVSTDpcbiAqXG4gKiAgIC0gQ2hhdCBDb21wbGV0aW9ucyDihpIgYC9zZXJ2aW5nLWVuZHBvaW50cy88bmFtZT4vaW52b2NhdGlvbnNgLCBleGNlcHQgZm9yXG4gKiAgICAgUmVzcG9uc2VzLW9ubHkgbW9kZWxzIChDb2RleCkgd2hpY2ggYXJlIHRyYW5zbGF0ZWQgYW5kIHNlbnQgdG9cbiAqICAgICBgL3NlcnZpbmctZW5kcG9pbnRzL3Jlc3BvbnNlc2AuXG4gKiAgIC0gUmVzcG9uc2VzIOKGkiBuYXRpdmUgYC9zZXJ2aW5nLWVuZHBvaW50cy9yZXNwb25zZXNgIChPcGVuQUktZmFtaWx5KSBvclxuICogICAgIGAvc2VydmluZy1lbmRwb2ludHMvb3Blbi1yZXNwb25zZXNgIChDbGF1ZGUvR2VtaW5pL+KApiksIGJvZHkgZm9yd2FyZGVkXG4gKiAgICAgYXMtaXMuIE5vIGNoYXQgcm91bmQtdHJpcC5cbiAqXG4gKiBBbnkgT3BlbkFJLWNvbXBhdGlibGUgdG9vbCAoaVRlcm0sIGVkaXRvcnMsIHRoZSBgb3BlbmFpYCBTREssIENvZGV4IENMSSkgY2FuXG4gKiBwb2ludCBpdHMgYmFzZSBVUkwgYXQgdGhpcyBzZXJ2ZXIgYW5kIHVzZSBsb29zZSBtb2RlbCBuYW1lcy5cbiAqXG4gKiBSb3V0ZXM6XG4gKiAgIC0gYEdFVCAgL2hlYWx0aGAsIGBHRVQgL2AgICAgICAgICAgICBsaXZlbmVzc1xuICogICAtIGBHRVQgIC92MS9tb2RlbHNgLCBgR0VUIC9tb2RlbHNgICAgbGlzdCByZXNvbHZhYmxlIGVuZHBvaW50cy4gRW1pdHMgdGhlXG4gKiAgICAgT3BlbkFJIGB7b2JqZWN0OlwibGlzdFwiLGRhdGE6W+KApl19YCBzaGFwZSBieSBkZWZhdWx0OyB3aGVuIHRoZSByZXF1ZXN0XG4gKiAgICAgbG9va3MgbGlrZSB0aGUgQ29kZXggQ0xJIChhIGBjbGllbnRfdmVyc2lvbmAgcXVlcnkgcGFyYW0sIHdoaWNoIENvZGV4XG4gKiAgICAgYWx3YXlzIHNlbmRzKSBpdCBlbWl0cyBDb2RleCdzIGB7bW9kZWxzOlt7c2x1ZyzigKZ9XX1gIHNoYXBlIGluc3RlYWQsIHNvXG4gKiAgICAgYm90aCBzdGFuZGFyZCBPcGVuQUkgY2xpZW50cyBhbmQgQ29kZXggY2FuIGVudW1lcmF0ZSB0aGUgY2F0YWxvZ3VlLlxuICogICAtIGBQT1NUIC92MS9jaGF0L2NvbXBsZXRpb25zYCAgICAgICAgcHJveHkgKGFsc28gYC9jb21wbGV0aW9uc2AsXG4gKiAgICAgYC92MS9jb21wbGV0aW9uc2AsIGAvdjEvZW1iZWRkaW5nc2AsIGFuZCB0aGUgdW4tcHJlZml4ZWQgdmFyaWFudHMpXG4gKiAgIC0gYFBPU1QgL3YxL3Jlc3BvbnNlc2AgICAgICAgICAgICAgICBPcGVuQUkgUmVzcG9uc2VzIEFQSSwgZm9yd2FyZGVkIHRvXG4gKiAgICAgRGF0YWJyaWNrcycgbmF0aXZlIFJlc3BvbnNlcyAvIE9wZW4gUmVzcG9uc2VzIHN1cmZhY2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGNyZWF0ZVNlcnZlciwgdHlwZSBJbmNvbWluZ01lc3NhZ2UsIHR5cGUgU2VydmVyLCB0eXBlIFNlcnZlclJlc3BvbnNlIH0gZnJvbSBcIm5vZGU6aHR0cFwiO1xuaW1wb3J0IHsgYXN5bmMgYXMgYXN5bmNVdGlsLCBlcnJvciwganNvbiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IGNsYXNzaWZ5LCBvcGVuYWlDaGF0LCBvcGVuYWlSZXNwb25zZXMgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtbW9kZWxcIjtcbmltcG9ydCB7IEFnZW50IH0gZnJvbSBcInVuZGljaVwiO1xuXG5pbXBvcnQgdHlwZSB7IERhdGFicmlja3NCYWNrZW5kIH0gZnJvbSBcIi4vYmFja2VuZFwiO1xuaW1wb3J0IHsgREVGQVVMVF9CSU5EX0hPU1QsIERFRkFVTFRfUE9SVCwgREVGQVVMVF9SRVRSWSwgdHlwZSBSZXRyeUNvbmZpZyB9IGZyb20gXCIuL2RlZmF1bHRzXCI7XG5cbmNvbnN0IHsgY2hhdFRvUmVzcG9uc2VzUmVxdWVzdCwgcmVzcG9uc2VUb0NoYXRDb21wbGV0aW9uLCBzYW5pdGl6ZU9wZW5SZXNwb25zZXNSZXF1ZXN0IH0gPVxuICBvcGVuYWlSZXNwb25zZXM7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJtb2RlbC1wcm94eS9zZXJ2ZXJcIik7XG5cbi8qKlxuICogRGlzcGF0Y2hlciBmb3IgZXZlcnkgdXBzdHJlYW0gY2FsbCwgd2l0aCB1bmRpY2kncyBpbmFjdGl2aXR5IHRpbWVvdXRzXG4gKiBESVNBQkxFRCAoYDBgKS5cbiAqXG4gKiBBIHByb3hpZWQgdHVybiBpcyBvbmx5IGFzIGZhc3QgYXMgdGhlIG1vZGVsIGJlaGluZCBpdCwgYW5kIHVuZGljaSdzXG4gKiBkZWZhdWx0cyAoMzAwcyBgaGVhZGVyc1RpbWVvdXRgLCAzMDBzIGBib2R5VGltZW91dGApIGFyZSBib3RoIHdyb25nIGhlcmUuXG4gKiBgYm9keVRpbWVvdXRgIGlzIHRoZSBraWxsZXI6IGl0IG1lYXN1cmVzIHRoZSBnYXAgQkVUV0VFTiBjaHVua3MsIHNvIGEgbW9kZWxcbiAqIHRoYXQgdGhpbmtzIGZvciBhIHdoaWxlIG1pZC1zdHJlYW0gLSBleHRlbmRlZCByZWFzb25pbmcsIGEgbG9uZyB0b29sIHJvdW5kXG4gKiB0cmlwLCBhIHNsb3cgR2VuaWUvU1FMIHN0ZXAgLSB0cmlwcyBpdCBhbmQgdW5kaWNpIHRlYXJzIHRoZSBzb2NrZXQgZG93blxuICogd2l0aCBgVU5EX0VSUl9CT0RZX1RJTUVPVVRgLCB3aGljaCB0aGUgY2xpZW50IHNlZXMgYXMgYSB0cnVuY2F0ZWQgc3RyZWFtLlxuICpcbiAqIFRoZSBwcm94eSBoYXMgbm8gb3BpbmlvbiBvbiBob3cgbG9uZyBhIG1vZGVsIG1heSB0YWtlLCBzbyBpdCBpbXBvc2VzIG5vXG4gKiBkZWFkbGluZTogdGhlIHN0cmVhbSBlbmRzIHdoZW4gdGhlIHVwc3RyZWFtIGVuZHMgaXQsIG9yIHdoZW4gdGhlIGNsaWVudFxuICogaGFuZ3MgdXAgKHNlZSB7QGxpbmsgcmVxdWVzdEFib3J0U2lnbmFsfSkuIENhbGxlcnMgdGhhdCBETyB3YW50IGEgZGVhZGxpbmVcbiAqIHNob3VsZCBzZW5kIG9uZSwgdGhlIHNhbWUgYXMgdGhleSB3b3VsZCB0byBPcGVuQUkuXG4gKi9cbmNvbnN0IHVwc3RyZWFtRGlzcGF0Y2hlciA9IG5ldyBBZ2VudCh7IGhlYWRlcnNUaW1lb3V0OiAwLCBib2R5VGltZW91dDogMCB9KTtcblxuLyoqIFBPU1Qgcm91dGVzIGZvcndhcmRlZCB0byBhIHNlcnZpbmcgZW5kcG9pbnQgKGNoYXQgb3IgUmVzcG9uc2VzLW9ubHkpLiAqL1xuY29uc3QgUFJPWFlfUEFUSFMgPSBuZXcgU2V0KFtcbiAgXCIvdjEvY2hhdC9jb21wbGV0aW9uc1wiLFxuICBcIi9jaGF0L2NvbXBsZXRpb25zXCIsXG4gIFwiL3YxL2NvbXBsZXRpb25zXCIsXG4gIFwiL2NvbXBsZXRpb25zXCIsXG4gIFwiL3YxL2VtYmVkZGluZ3NcIixcbiAgXCIvZW1iZWRkaW5nc1wiLFxuXSk7XG5cbi8qKiBHRVQgcm91dGVzIHRoYXQgbGlzdCB0aGUgcmVzb2x2YWJsZSBtb2RlbCBjYXRhbG9ndWUuICovXG5jb25zdCBNT0RFTFNfUEFUSFMgPSBuZXcgU2V0KFtcIi92MS9tb2RlbHNcIiwgXCIvbW9kZWxzXCJdKTtcblxuLyoqIFBPU1Qgcm91dGVzIHRoYXQgY2FycnkgYW4gT3BlbkFJIFJlc3BvbnNlcyBBUEkgYm9keSAobmF0aXZlIHBhc3N0aHJvdWdoKS4gKi9cbmNvbnN0IFJFU1BPTlNFU19QQVRIUyA9IG5ldyBTZXQoW1wiL3YxL3Jlc3BvbnNlc1wiLCBcIi9yZXNwb25zZXNcIl0pO1xuXG4vKipcbiAqIEV4dHJhIHJlcXVlc3QgZmllbGRzIHRvIHN0cmlwIGJlZm9yZSBmb3J3YXJkaW5nLCBvbiB0b3Agb2ZcbiAqIGBvcGVuYWlDaGF0LlVOU1VQUE9SVEVEX0NIQVRfRklFTERTYC4gQ29tbWEtc2VwYXJhdGVkIGluIGBQUk9YWV9EUk9QX0ZJRUxEU2AuXG4gKlxuICogQW4gZXNjYXBlIGhhdGNoOiB3aGVuIGEgd29ya3NwYWNlIG9yIGEgbmV3IGNsaWVudCB2ZXJzaW9uIHRyaXBzIGEgZmllbGRcbiAqIERhdGFicmlja3MgcmVqZWN0cywgYW4gb3BlcmF0b3IgY2FuIHVuYmxvY2sgdGhlbXNlbHZlcyBpbW1lZGlhdGVseSBpbnN0ZWFkIG9mXG4gKiB3YWl0aW5nIG9uIGEgcmVsZWFzZSBvZiB0aGlzIHBhY2thZ2UuXG4gKi9cbmZ1bmN0aW9uIGV4dHJhRHJvcEZpZWxkcygpOiBzdHJpbmdbXSB7XG4gIHJldHVybiAocHJvY2Vzcy5lbnYuUFJPWFlfRFJPUF9GSUVMRFMgPz8gXCJcIilcbiAgICAuc3BsaXQoXCIsXCIpXG4gICAgLm1hcCgoZmllbGQpID0+IGZpZWxkLnRyaW0oKSlcbiAgICAuZmlsdGVyKChmaWVsZCkgPT4gZmllbGQubGVuZ3RoID4gMCk7XG59XG5cbi8qKiBPcHRpb25zIHNoYXJlZCBieSB7QGxpbmsgY3JlYXRlUHJveHlTZXJ2ZXJ9IGFuZCB7QGxpbmsgc3RhcnRQcm94eVNlcnZlcn0uICovXG5leHBvcnQgaW50ZXJmYWNlIFByb3h5U2VydmVyT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBXaGVuIHNldCwgbG9jYWwgY2xpZW50cyBtdXN0IHByZXNlbnQgdGhpcyB2YWx1ZSBhcyBhIGJlYXJlciB0b2tlblxuICAgKiAoYEF1dGhvcml6YXRpb246IEJlYXJlciA8a2V5PmApLiBVbnNldCBsZWF2ZXMgdGhlIHByb3h5IG9wZW4sIHdoaWNoIGlzIGZpbmVcbiAgICogZm9yIGEgbG9vcGJhY2sgYmluZCBidXQgc2hvdWxkIGJlIHBhaXJlZCB3aXRoIGEga2V5IG9uIGEgd2lkZXIgb25lLlxuICAgKi9cbiAgYXBpS2V5Pzogc3RyaW5nO1xuICAvKipcbiAgICogUG9saWN5IGZvciBhYnNvcmJpbmcgdXBzdHJlYW0gNDI5cy4gT21pdHRlZCBkZWZhdWx0cyB0byB7QGxpbmtcbiAgICogREVGQVVMVF9SRVRSWX0gKHJldHJ5IG9uKS4gU2VlIHtAbGluayBSZXRyeUNvbmZpZ30uXG4gICAqL1xuICByZXRyeT86IFJldHJ5Q29uZmlnO1xufVxuXG4vKiogT3B0aW9ucyBmb3Ige0BsaW5rIHN0YXJ0UHJveHlTZXJ2ZXJ9LCBhZGRpbmcgdGhlIGxpc3RlbiBhZGRyZXNzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydFByb3h5T3B0aW9ucyBleHRlbmRzIFByb3h5U2VydmVyT3B0aW9ucyB7XG4gIGhvc3Q/OiBzdHJpbmc7XG4gIHBvcnQ/OiBudW1iZXI7XG59XG5cbi8qKiBCdWlsZCAoYnV0IGRvIG5vdCBzdGFydCkgdGhlIHByb3h5IEhUVFAgc2VydmVyLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVByb3h5U2VydmVyKFxuICBiYWNrZW5kOiBEYXRhYnJpY2tzQmFja2VuZCxcbiAgb3B0aW9uczogUHJveHlTZXJ2ZXJPcHRpb25zID0ge30sXG4pOiBTZXJ2ZXIge1xuICBjb25zdCBzZXJ2ZXIgPSBjcmVhdGVTZXJ2ZXIoKHJlcSwgcmVzKSA9PiB7XG4gICAgdm9pZCBoYW5kbGVSZXF1ZXN0KGJhY2tlbmQsIG9wdGlvbnMsIHJlcSwgcmVzKTtcbiAgfSk7XG4gIC8vIE1hdGNoIHRoZSB1cHN0cmVhbSBwb2xpY3kgb24gdGhlIElOQk9VTkQgc2lkZTogbmV2ZXIgY3V0IGEgY2xpZW50IG9mZlxuICAvLyBtaWQtdHVybi4gTm9kZSdzIGRlZmF1bHRzICgzMDBzIGByZXF1ZXN0VGltZW91dGAsIDYwcyBgaGVhZGVyc1RpbWVvdXRgKVxuICAvLyBhcmUgc2l6ZWQgZm9yIG9yZGluYXJ5IHdlYiB0cmFmZmljLCBub3QgZm9yIGhvbGRpbmcgYSBzdHJlYW1lZCBtb2RlbFxuICAvLyByZXNwb25zZSBvcGVuLCBhbmQgYSBjbGllbnQgdGhhdCBoaXRzIG9uZSBnZXRzIGEgZGVhZCBzb2NrZXQgcmF0aGVyIHRoYW5cbiAgLy8gYW4gZXJyb3IgaXQgY2FuIGFjdCBvbi4gYDBgIGRpc2FibGVzIGVhY2g7IHRoZSB0dXJuIG5vdyBlbmRzIHdoZW4gdGhlXG4gIC8vIG1vZGVsIGlzIGRvbmUgb3IgdGhlIGNsaWVudCBkaXNjb25uZWN0cy5cbiAgc2VydmVyLnJlcXVlc3RUaW1lb3V0ID0gMDtcbiAgc2VydmVyLmhlYWRlcnNUaW1lb3V0ID0gMDtcbiAgc2VydmVyLnRpbWVvdXQgPSAwO1xuICByZXR1cm4gc2VydmVyO1xufVxuXG4vKipcbiAqIFNpZ25hbCB0aGF0IGFib3J0cyB3aGVuIHRoZSBjbGllbnQgaGFuZ3MgdXAuXG4gKlxuICogUmVtb3ZpbmcgdGhlIHRpbWVvdXRzIG1lYW5zIG5vdGhpbmcgZWxzZSB3aWxsIGV2ZXIgZW5kIGEgZm9yZ290dGVuIHJlcXVlc3QsXG4gKiBzbyB0aGUgY2xpZW50J3Mgb3duIGRpc2Nvbm5lY3QgYmVjb21lcyB0aGUgb25seSBiYWNrc3RvcCBhZ2FpbnN0IGFuIHVwc3RyZWFtXG4gKiBjYWxsIHJ1bm5pbmcgb24gd2l0aCBubyBvbmUgdG8gcmVjZWl2ZSBpdC4gUGFzc2luZyB0aGlzIHRvIGBmZXRjaGAgaXMgd2hhdFxuICogbWFrZXMgYSBjYW5jZWxsZWQgdHVybiAoQ3RybC1DLCBhIGNsb3NlZCB0YWIsIGEga2lsbGVkIENMSSkgcmVsZWFzZSB0aGVcbiAqIERhdGFicmlja3Mtc2lkZSBzdHJlYW0gaW5zdGVhZCBvZiBsZWFraW5nIGl0IGZvciB0aGUgbGlmZSBvZiB0aGUgcHJvY2Vzcy5cbiAqXG4gKiBLZXllZCBvZmYgdGhlIFJFU1BPTlNFLCBub3QgdGhlIHJlcXVlc3Q6IGBJbmNvbWluZ01lc3NhZ2VgIGVtaXRzIGBjbG9zZWAgYXNcbiAqIHNvb24gYXMgaXRzIGJvZHkgaGFzIGJlZW4gZnVsbHkgY29uc3VtZWQsIHdoaWNoIG9uIGV2ZXJ5IFBPU1QgaGVyZSBoYXBwZW5zXG4gKiBiZWZvcmUgdGhlIHVwc3RyZWFtIGNhbGwgaXMgZXZlbiBtYWRlIC0gd2F0Y2hpbmcgdGhhdCB3b3VsZCBhYm9ydCBlYWNoIHR1cm5cbiAqIGluc3RhbnRseS4gYFNlcnZlclJlc3BvbnNlYCBlbWl0cyBgY2xvc2VgIHdoZW4gdGhlIHJlc3BvbnNlIGZpbmlzaGVzIG9yIHRoZVxuICogY29ubmVjdGlvbiBkcm9wcywgc28gdGhlIGB3cml0YWJsZUZpbmlzaGVkYCBjaGVjayBzZXBhcmF0ZXMgXCJjbGllbnQgbGVmdFwiXG4gKiBmcm9tIFwid2UgcmVwbGllZFwiLlxuICovXG5mdW5jdGlvbiBjbGllbnRBYm9ydFNpZ25hbChyZXM6IFNlcnZlclJlc3BvbnNlKTogQWJvcnRTaWduYWwge1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICByZXMub25jZShcImNsb3NlXCIsICgpID0+IHtcbiAgICBpZiAoIXJlcy53cml0YWJsZUZpbmlzaGVkKSBjb250cm9sbGVyLmFib3J0KCk7XG4gIH0pO1xuICByZXR1cm4gY29udHJvbGxlci5zaWduYWw7XG59XG5cbi8qKlxuICogUE9TVCBhIEpTT04gYm9keSB0byBhIHNlcnZpbmcgZW5kcG9pbnQgb24gdGhlIG5vLXRpbWVvdXRcbiAqIHtAbGluayB1cHN0cmVhbURpc3BhdGNoZXJ9LCBjYW5jZWxsZWQgdmlhIGBzaWduYWxgLlxuICpcbiAqIEV2ZXJ5IHVwc3RyZWFtIGNhbGwgZ29lcyB0aHJvdWdoIGhlcmUgc28gdGhlIHRpbWVvdXQgYW5kIGNhbmNlbGxhdGlvbiBwb2xpY3lcbiAqIGlzIHN0YXRlZCBvbmNlLiBgZGlzcGF0Y2hlcmAgaXMgYW4gdW5kaWNpIGV4dGVuc2lvbiB0aGF0IHRoZSBET01cbiAqIGBSZXF1ZXN0SW5pdGAgTm9kZSBjb21waWxlcyBhZ2FpbnN0IGRvZXMgbm90IGRlY2xhcmUsIGhlbmNlIHRoZSBsb2NhbFxuICogaW50ZXJzZWN0aW9uIHR5cGUgcmF0aGVyIHRoYW4gYSBjYXN0IGF0IGVhY2ggY2FsbCBzaXRlLlxuICovXG5hc3luYyBmdW5jdGlvbiB1cHN0cmVhbUZldGNoKFxuICB1cmw6IHN0cmluZyxcbiAgaGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPixcbiAgYm9keTogc3RyaW5nLFxuICBzaWduYWw6IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTxSZXNwb25zZT4ge1xuICBjb25zdCBpbml0OiBSZXF1ZXN0SW5pdCA9IHsgbWV0aG9kOiBcIlBPU1RcIiwgaGVhZGVycywgYm9keSwgc2lnbmFsIH07XG4gIC8vIGBkaXNwYXRjaGVyYCBpcyBhbiB1bmRpY2kgZXh0ZW5zaW9uIHRoZSBET00gYFJlcXVlc3RJbml0YCBkb2Vzbid0IGRlY2xhcmUsXG4gIC8vIGFuZCBpdCBjYW4ndCBzaW1wbHkgYmUgdHlwZWQgb246IGBAdHlwZXMvbm9kZWAgcGlucyBpdHMgb3duIGB1bmRpY2ktdHlwZXNgXG4gIC8vIGNvcHksIHNvIHRoZSBgQWdlbnRgIGZyb20gb3VyIGB1bmRpY2lgIGRlcGVuZGVuY3kgaXMgYSBzdHJ1Y3R1cmFsbHlcbiAgLy8gaWRlbnRpY2FsIGJ1dCBub21pbmFsbHkgZGlmZmVyZW50IHR5cGUgdGhhbiB0aGUgb25lIHRoZSBnbG9iYWwgYGZldGNoYFxuICAvLyBzaWduYXR1cmUgZXhwZWN0cy4gQXR0YWNoaW5nIGl0IHN0cnVjdHVyYWxseSBzaWRlc3RlcHMgYSB2ZXJzaW9uIHNrZXcgdGhhdFxuICAvLyBubyBjYXN0IGV4cHJlc3NlcyBjbGVhbmx5LCBhbmQgYGZldGNoYCByZWFkcyBpdCBhdCBydW50aW1lIGFsbCB0aGUgc2FtZS5cbiAgUmVmbGVjdC5zZXQoaW5pdCwgXCJkaXNwYXRjaGVyXCIsIHVwc3RyZWFtRGlzcGF0Y2hlcik7XG4gIHJldHVybiBmZXRjaCh1cmwsIGluaXQpO1xufVxuXG4vKipcbiAqIERlbGF5IChtcykgYSBgNDI5YCByZXNwb25zZSBhc2tzIHVzIHRvIHdhaXQsIGZyb20gaXRzIGBSZXRyeS1BZnRlcmAgaGVhZGVyLFxuICogb3IgYHVuZGVmaW5lZGAgd2hlbiBhYnNlbnQvdW5wYXJzZWFibGUuIEhhbmRsZXMgYm90aCBoZWFkZXIgZm9ybXM6IGFuIGludGVnZXJcbiAqIGNvdW50IG9mIHNlY29uZHMsIGFuZCBhbiBIVFRQIGRhdGUgdG8gd2FpdCB1bnRpbCAoY2xhbXBlZCB0byBgPj0gMGApLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSZXRyeUFmdGVyTXMoaGVhZGVyOiBzdHJpbmcgfCBudWxsLCBub3dNczogbnVtYmVyKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFoZWFkZXIpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHRyaW1tZWQgPSBoZWFkZXIudHJpbSgpO1xuICBpZiAoL15cXGQrJC8udGVzdCh0cmltbWVkKSkgcmV0dXJuIE51bWJlcih0cmltbWVkKSAqIDEwMDA7XG4gIGNvbnN0IHdoZW4gPSBEYXRlLnBhcnNlKHRyaW1tZWQpO1xuICBpZiAoTnVtYmVyLmlzTmFOKHdoZW4pKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4gTWF0aC5tYXgoMCwgd2hlbiAtIG5vd01zKTtcbn1cblxuLyoqXG4gKiBCYWNrb2ZmIChtcykgYmVmb3JlIHJldHJ5IGBhdHRlbXB0YCAoMC1iYXNlZCk6IGV4cG9uZW50aWFsIGZyb21cbiAqIGBiYXNlRGVsYXlNc2AsIGNhcHBlZCBhdCBgbWF4RGVsYXlNc2AsIHdpdGggdXAgdG8gKzUwJSBqaXR0ZXIgc28gY29uY3VycmVudFxuICogcmV0cmllcyBmcm9tIG9uZSBhZ2VudCBkb24ndCByZXN5bmNocm9uaXplIGludG8gdGhlIG5leHQgYnVyc3QuIEEgc2VydmVyLXNlbnRcbiAqIGBSZXRyeS1BZnRlcmAgd2lucyBvdXRyaWdodCAoc3RpbGwgY2FwcGVkKSwgc2luY2UgdGhlIHNlcnZlciBrbm93cyBiZXR0ZXJcbiAqIHRoYW4gb3VyIHNjaGVkdWxlIHdoZW4gaXQgd2lsbCBhY2NlcHQgdHJhZmZpYyBhZ2Fpbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJhY2tvZmZEZWxheU1zKFxuICBhdHRlbXB0OiBudW1iZXIsXG4gIHJldHJ5OiBSZXRyeUNvbmZpZyxcbiAgcmV0cnlBZnRlck1zOiBudW1iZXIgfCB1bmRlZmluZWQsXG4gIGppdHRlcjogbnVtYmVyLFxuKTogbnVtYmVyIHtcbiAgaWYgKHJldHJ5QWZ0ZXJNcyAhPT0gdW5kZWZpbmVkKSByZXR1cm4gTWF0aC5taW4ocmV0cnlBZnRlck1zLCByZXRyeS5tYXhEZWxheU1zKTtcbiAgY29uc3QgZXhwb25lbnRpYWwgPSByZXRyeS5iYXNlRGVsYXlNcyAqIDIgKiogYXR0ZW1wdDtcbiAgY29uc3QgY2FwcGVkID0gTWF0aC5taW4oZXhwb25lbnRpYWwsIHJldHJ5Lm1heERlbGF5TXMpO1xuICByZXR1cm4gTWF0aC5yb3VuZChjYXBwZWQgKiAoMSArIGppdHRlciAqIDAuNSkpO1xufVxuXG4vKipcbiAqIHtAbGluayB1cHN0cmVhbUZldGNofSB3aXRoIGluLXByb3h5IGA0MjlgIGhhbmRsaW5nLiBXaGVuIGByZXRyeS5lbmFibGVkYCwgYVxuICogNDI5IGlzIHJldHJpZWQgdXAgdG8gYHJldHJ5Lm1heFJldHJpZXNgIHRpbWVzIHdpdGgge0BsaW5rIGJhY2tvZmZEZWxheU1zfVxuICogYmFja29mZiBpbnN0ZWFkIG9mIGJlaW5nIHN1cmZhY2VkOyB0aGUgdGhyb3R0bGVkIHJlc3BvbnNlIGJvZHkgaXMgZHJhaW5lZFxuICogZWFjaCB0aW1lIHNvIHRoZSBjb25uZWN0aW9uIGlzIHJlbGVhc2VkLiBUaGUgcmV0cnkgaGFwcGVucyBCRUZPUkUgdGhlIGNhbGxlclxuICogd3JpdGVzIGFueSBzdGF0dXMgbGluZSwgc28gaXQgaXMgdHJhbnNwYXJlbnQgdG8gYm90aCBzdHJlYW1pbmcgYW5kXG4gKiBub24tc3RyZWFtaW5nIHBhdGhzIC0gdGhlIGNsaWVudCBvbmx5IGV2ZXIgc2VlcyB0aGUgZmluYWwgcmVzcG9uc2UuXG4gKlxuICogQmFpbHMgZWFybHkgKHJldHVybmluZyB0aGUgNDI5KSB3aGVuIHJldHJpZXMgYXJlIGV4aGF1c3RlZCwgd2hlbiB0aGUgY2xpZW50XG4gKiBoYXMgaHVuZyB1cCAoYHNpZ25hbC5hYm9ydGVkYCksIG9yIHdoZW4gdGhlIHJlc3BvbnNlIGNhcnJpZXMgbm8gYFJldHJ5LUFmdGVyYFxuICogYW5kIHdlIHdvdWxkIG90aGVyd2lzZSBsb29wIGJsaW5kIHBhc3QgdGhlIGNhcC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdXBzdHJlYW1GZXRjaFJldHJ5aW5nKFxuICBiYWNrZW5kOiBEYXRhYnJpY2tzQmFja2VuZCxcbiAgdXJsOiBzdHJpbmcsXG4gIGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sXG4gIGJvZHk6IHVua25vd24sXG4gIHJlczogU2VydmVyUmVzcG9uc2UsXG4gIHJldHJ5OiBSZXRyeUNvbmZpZyxcbik6IFByb21pc2U8UmVzcG9uc2U+IHtcbiAgY29uc3Qgc2lnbmFsID0gY2xpZW50QWJvcnRTaWduYWwocmVzKTtcbiAgY29uc3QgcGF5bG9hZCA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xuICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB1cHN0cmVhbUZldGNoKHVybCwgaGVhZGVycywgcGF5bG9hZCwgc2lnbmFsKTtcbiAgaWYgKCFyZXRyeS5lbmFibGVkKSByZXR1cm4gcmVzcG9uc2U7XG5cbiAgZm9yIChsZXQgYXR0ZW1wdCA9IDA7IHJlc3BvbnNlLnN0YXR1cyA9PT0gNDI5ICYmIGF0dGVtcHQgPCByZXRyeS5tYXhSZXRyaWVzOyBhdHRlbXB0KyspIHtcbiAgICBjb25zdCByZXRyeUFmdGVyTXMgPSBwYXJzZVJldHJ5QWZ0ZXJNcyhyZXNwb25zZS5oZWFkZXJzLmdldChcInJldHJ5LWFmdGVyXCIpLCBEYXRlLm5vdygpKTtcbiAgICBjb25zdCBkZWxheU1zID0gYmFja29mZkRlbGF5TXMoYXR0ZW1wdCwgcmV0cnksIHJldHJ5QWZ0ZXJNcywgaml0dGVyRnJhY3Rpb24oKSk7XG4gICAgLy8gUmVsZWFzZSB0aGUgdGhyb3R0bGVkIHJlc3BvbnNlJ3Mgc29ja2V0IGJlZm9yZSB3YWl0aW5nOyBpdHMgYm9keSBpcyBhblxuICAgIC8vIGVycm9yIHBheWxvYWQgd2UgYXJlIGNob29zaW5nIG5vdCB0byByZWxheS5cbiAgICBhd2FpdCByZXNwb25zZS5ib2R5Py5jYW5jZWwoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgbG9nZ2VyLndhcm4oXCJ1cHN0cmVhbSA0Mjk7IGJhY2tpbmcgb2ZmXCIsIHtcbiAgICAgIGF0dGVtcHQ6IGF0dGVtcHQgKyAxLFxuICAgICAgbWF4UmV0cmllczogcmV0cnkubWF4UmV0cmllcyxcbiAgICAgIGRlbGF5TXMsXG4gICAgICAuLi4ocmV0cnlBZnRlck1zICE9PSB1bmRlZmluZWQgPyB7IHJldHJ5QWZ0ZXJNcyB9IDoge30pLFxuICAgIH0pO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBhc3luY1V0aWwuc2xlZXAoZGVsYXlNcywgc2lnbmFsKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIENsaWVudCBodW5nIHVwIG1pZC13YWl0OiByZXR1cm4gdGhlIGxhc3QgNDI5IHJhdGhlciB0aGFuIGlzc3VlIGFcbiAgICAgIC8vIHJlcXVlc3Qgbm8gb25lIGlzIGxpc3RlbmluZyBmb3IuXG4gICAgICByZXR1cm4gcmVzcG9uc2U7XG4gICAgfVxuICAgIC8vIEZyZXNoIGF1dGggaGVhZGVyIHBlciBhdHRlbXB0IHNvIGEgbG9uZyBiYWNrb2ZmIGNhbid0IG91dGxpdmUgdGhlIHRva2VuLlxuICAgIGNvbnN0IHJldHJ5SGVhZGVycyA9IHsgLi4uKGF3YWl0IGJhY2tlbmQuYXV0aEhlYWRlcnMoKSksIC4uLmhlYWRlcnMgfTtcbiAgICByZXNwb25zZSA9IGF3YWl0IHVwc3RyZWFtRmV0Y2godXJsLCByZXRyeUhlYWRlcnMsIHBheWxvYWQsIHNpZ25hbCk7XG4gIH1cbiAgcmV0dXJuIHJlc3BvbnNlO1xufVxuXG4vKipcbiAqIEppdHRlciBmcmFjdGlvbiBpbiBgWzAsIDEpYC4gU3BsaXQgb3V0IHNvIHRlc3RzIGNhbiBzdHViIGRldGVybWluaXNtO1xuICogYE1hdGgucmFuZG9tYCBpcyBmaW5lIGZvciBzcHJlYWRpbmcgcmV0cnkgdGltaW5nLlxuICovXG5mdW5jdGlvbiBqaXR0ZXJGcmFjdGlvbigpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhbmQgc3RhcnQgdGhlIHByb3h5LCByZXNvbHZpbmcgb25jZSBpdCBpcyBhY2NlcHRpbmcgY29ubmVjdGlvbnMuXG4gKiBSZXR1cm5zIHRoZSBzZXJ2ZXIgYW5kIGl0cyBiYXNlIFVSTCAod2l0aCB0aGUgYWN0dWFsbHktYm91bmQgcG9ydCwgc28gYVxuICogYHBvcnQ6IDBgIHJlcXVlc3Qgc3VyZmFjZXMgdGhlIE9TLWFzc2lnbmVkIHBvcnQpLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRQcm94eVNlcnZlcihcbiAgYmFja2VuZDogRGF0YWJyaWNrc0JhY2tlbmQsXG4gIG9wdGlvbnM6IFN0YXJ0UHJveHlPcHRpb25zID0ge30sXG4pOiBQcm9taXNlPHsgc2VydmVyOiBTZXJ2ZXI7IHVybDogc3RyaW5nIH0+IHtcbiAgY29uc3QgaG9zdCA9IG9wdGlvbnMuaG9zdCA/PyBERUZBVUxUX0JJTkRfSE9TVDtcbiAgY29uc3QgcG9ydCA9IG9wdGlvbnMucG9ydCA/PyBERUZBVUxUX1BPUlQ7XG4gIGNvbnN0IHNlcnZlciA9IGNyZWF0ZVByb3h5U2VydmVyKGJhY2tlbmQsIG9wdGlvbnMpO1xuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3Qgb25FcnJvciA9IChlcnI6IEVycm9yKSA9PiByZWplY3QoZXJyKTtcbiAgICBzZXJ2ZXIub25jZShcImVycm9yXCIsIG9uRXJyb3IpO1xuICAgIHNlcnZlci5saXN0ZW4ocG9ydCwgaG9zdCwgKCkgPT4ge1xuICAgICAgc2VydmVyLm9mZihcImVycm9yXCIsIG9uRXJyb3IpO1xuICAgICAgcmVzb2x2ZSgpO1xuICAgIH0pO1xuICB9KTtcbiAgY29uc3QgYWRkcmVzcyA9IHNlcnZlci5hZGRyZXNzKCk7XG4gIGNvbnN0IGJvdW5kUG9ydCA9IHR5cGVvZiBhZGRyZXNzID09PSBcIm9iamVjdFwiICYmIGFkZHJlc3MgPyBhZGRyZXNzLnBvcnQgOiBwb3J0O1xuICByZXR1cm4geyBzZXJ2ZXIsIHVybDogYGh0dHA6Ly8ke2hvc3R9OiR7Ym91bmRQb3J0fWAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVxdWVzdChcbiAgYmFja2VuZDogRGF0YWJyaWNrc0JhY2tlbmQsXG4gIG9wdGlvbnM6IFByb3h5U2VydmVyT3B0aW9ucyxcbiAgcmVxOiBJbmNvbWluZ01lc3NhZ2UsXG4gIHJlczogU2VydmVyUmVzcG9uc2UsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgcmF3VXJsID0gcmVxLnVybCA/PyBcIi9cIjtcbiAgY29uc3QgW3BhdGgsIHF1ZXJ5ID0gXCJcIl0gPSByYXdVcmwuc3BsaXQoXCI/XCIpO1xuICBjb25zdCByb3V0ZVBhdGggPSBwYXRoID8/IFwiL1wiO1xuICB0cnkge1xuICAgIGlmIChyZXEubWV0aG9kID09PSBcIkdFVFwiICYmIChyb3V0ZVBhdGggPT09IFwiL2hlYWx0aFwiIHx8IHJvdXRlUGF0aCA9PT0gXCIvXCIpKSB7XG4gICAgICBzZW5kSnNvbihyZXMsIDIwMCwgeyBzdGF0dXM6IFwib2tcIiB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKG9wdGlvbnMuYXBpS2V5ICYmICFpc0F1dGhvcml6ZWQocmVxLCBvcHRpb25zLmFwaUtleSkpIHtcbiAgICAgIHNlbmRKc29uKHJlcywgNDAxLCBlcnJvckJvZHkoXCJpbnZhbGlkIGFwaSBrZXlcIiwgXCJpbnZhbGlkX3JlcXVlc3RfZXJyb3JcIikpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmVxLm1ldGhvZCA9PT0gXCJHRVRcIiAmJiBNT0RFTFNfUEFUSFMuaGFzKHJvdXRlUGF0aCkpIHtcbiAgICAgIC8vIENvZGV4IGFsd2F5cyBzZW5kcyBgP2NsaWVudF92ZXJzaW9uPeKApmA7IHVzZSB0aGF0IHRvIHBpY2sgaXRzIGxpc3Qgc2hhcGUuXG4gICAgICBjb25zdCB3YW50c0NvZGV4U2hhcGUgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHF1ZXJ5KS5oYXMoXCJjbGllbnRfdmVyc2lvblwiKTtcbiAgICAgIGF3YWl0IGhhbmRsZU1vZGVscyhiYWNrZW5kLCByZXMsIHdhbnRzQ29kZXhTaGFwZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHJldHJ5ID0gb3B0aW9ucy5yZXRyeSA/PyBERUZBVUxUX1JFVFJZO1xuICAgIGlmIChyZXEubWV0aG9kID09PSBcIlBPU1RcIiAmJiBSRVNQT05TRVNfUEFUSFMuaGFzKHJvdXRlUGF0aCkpIHtcbiAgICAgIGF3YWl0IGhhbmRsZVJlc3BvbnNlcyhiYWNrZW5kLCByZXEsIHJlcywgcmV0cnkpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmVxLm1ldGhvZCA9PT0gXCJQT1NUXCIgJiYgUFJPWFlfUEFUSFMuaGFzKHJvdXRlUGF0aCkpIHtcbiAgICAgIGF3YWl0IGhhbmRsZVByb3h5KGJhY2tlbmQsIHJlcSwgcmVzLCByZXRyeSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHNlbmRKc29uKFxuICAgICAgcmVzLFxuICAgICAgNDA0LFxuICAgICAgZXJyb3JCb2R5KGB1bnN1cHBvcnRlZCByb3V0ZSAke3JlcS5tZXRob2QgPz8gXCI/XCJ9ICR7cm91dGVQYXRofWAsIFwiaW52YWxpZF9yZXF1ZXN0X2Vycm9yXCIpLFxuICAgICk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBlcnJvci5lcnJvck1lc3NhZ2UoZXJyKTtcbiAgICBsb2dnZXIuZXJyb3IoXCJyZXF1ZXN0IGZhaWxlZFwiLCB7IHBhdGg6IHJvdXRlUGF0aCwgZXJyb3I6IG1lc3NhZ2UgfSk7XG4gICAgaWYgKCFyZXMuaGVhZGVyc1NlbnQpIHNlbmRKc29uKHJlcywgNTAwLCBlcnJvckJvZHkobWVzc2FnZSwgXCJwcm94eV9lcnJvclwiKSk7XG4gICAgZWxzZSByZXMuZW5kKCk7XG4gIH1cbn1cblxuLyoqXG4gKiBgR0VUIC92MS9tb2RlbHNgOiBzdXJmYWNlIHRoZSBzZXJ2aW5nIGNhdGFsb2d1ZS4gVHdvIHNoYXBlczpcbiAqXG4gKiAgIC0gT3BlbkFJIChkZWZhdWx0KTogYHtvYmplY3Q6XCJsaXN0XCIsIGRhdGE6W3tpZCxvYmplY3QsY3JlYXRlZCxvd25lZF9ieX1dfWAsXG4gKiAgICAgd2hhdCB0aGUgYG9wZW5haWAgU0RLIGFuZCBtb3N0IHRvb2xzIGV4cGVjdC5cbiAqICAgLSBDb2RleCAoYGNvZGV4U2hhcGVgKTogYHttb2RlbHM6W3tzbHVnLCBkaXNwbGF5X25hbWUsIOKApn1dfWAsIHRoZSBDaGF0R1BUXG4gKiAgICAgYmFja2VuZCBzaGFwZSB0aGUgQ29kZXggQ0xJIGRlY29kZXMuIEEgcmVzcG9uc2UgbWlzc2luZyB0aGUgdG9wLWxldmVsXG4gKiAgICAgYG1vZGVsc2AgZmllbGQgbWFrZXMgQ29kZXggbG9nIFwiZmFpbGVkIHRvIGRlY29kZSBtb2RlbHMgcmVzcG9uc2VcIiwgc28gaXRcbiAqICAgICBnZXRzIGl0cyBvd24gZW52ZWxvcGUuIE9ubHkgY2hhdCBlbmRwb2ludHMgYXJlIGFkdmVydGlzZWQgdG8gQ29kZXguXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGhhbmRsZU1vZGVscyhcbiAgYmFja2VuZDogRGF0YWJyaWNrc0JhY2tlbmQsXG4gIHJlczogU2VydmVyUmVzcG9uc2UsXG4gIGNvZGV4U2hhcGU6IGJvb2xlYW4sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgZW5kcG9pbnRzID0gYXdhaXQgYmFja2VuZC5tb2RlbHModHJ1ZSk7XG4gIGlmIChjb2RleFNoYXBlKSB7XG4gICAgY29uc3QgbW9kZWxzID0gZW5kcG9pbnRzXG4gICAgICAuZmlsdGVyKChlbmRwb2ludCkgPT4gY2xhc3NpZnkuZW5kcG9pbnRDYXBhYmlsaXRpZXMoZW5kcG9pbnQpLmNoYXQpXG4gICAgICAubWFwKChlbmRwb2ludCkgPT4gKHtcbiAgICAgICAgc2x1ZzogZW5kcG9pbnQubmFtZSxcbiAgICAgICAgZGlzcGxheV9uYW1lOiBlbmRwb2ludC5kaXNwbGF5TmFtZSA/PyBlbmRwb2ludC5uYW1lLFxuICAgICAgICAuLi4oZW5kcG9pbnQuZGVzY3JpcHRpb24gPyB7IGRlc2NyaXB0aW9uOiBlbmRwb2ludC5kZXNjcmlwdGlvbiB9IDoge30pLFxuICAgICAgICAvLyBGaWVsZHMgQ29kZXgncyBzdHJpY3QgZGVjb2RlciByZXF1aXJlcyBvbiBldmVyeSBtb2RlbCBlbnRyeS5cbiAgICAgICAgZGVmYXVsdF9yZWFzb25pbmdfbGV2ZWw6IFwibWVkaXVtXCIsXG4gICAgICAgIHN1cHBvcnRlZF9yZWFzb25pbmdfbGV2ZWxzOiBbXSBhcyBzdHJpbmdbXSxcbiAgICAgICAgc2hlbGxfdHlwZTogXCJzaGVsbF9jb21tYW5kXCIsXG4gICAgICAgIHZpc2liaWxpdHk6IFwibGlzdFwiLFxuICAgICAgICBzdXBwb3J0ZWRfaW5fYXBpOiB0cnVlLFxuICAgICAgfSkpO1xuICAgIHNlbmRKc29uKHJlcywgMjAwLCB7IG1vZGVscyB9KTtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgZGF0YSA9IGVuZHBvaW50cy5tYXAoKGVuZHBvaW50KSA9PiAoe1xuICAgIGlkOiBlbmRwb2ludC5uYW1lLFxuICAgIG9iamVjdDogXCJtb2RlbFwiLFxuICAgIGNyZWF0ZWQ6IDAsXG4gICAgb3duZWRfYnk6IFwiZGF0YWJyaWNrc1wiLFxuICB9KSk7XG4gIHNlbmRKc29uKHJlcywgMjAwLCB7IG9iamVjdDogXCJsaXN0XCIsIGRhdGEgfSk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgcmVxdWVzdCdzIG1vZGVsIGFuZCBmb3J3YXJkIGEgQ2hhdCBDb21wbGV0aW9ucyBib2R5LiBSZXNwb25zZXMtXG4gKiBvbmx5IG1vZGVscyAoQ29kZXgpIGFyZSB0cmFuc2xhdGVkIHRvIGEgUmVzcG9uc2VzIHJlcXVlc3QgYW5kIHBvc3RlZCB0b1xuICogYC9zZXJ2aW5nLWVuZHBvaW50cy9yZXNwb25zZXNgOyBldmVyeXRoaW5nIGVsc2UgZ29lcyB0byBgL2ludm9jYXRpb25zYC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gaGFuZGxlUHJveHkoXG4gIGJhY2tlbmQ6IERhdGFicmlja3NCYWNrZW5kLFxuICByZXE6IEluY29taW5nTWVzc2FnZSxcbiAgcmVzOiBTZXJ2ZXJSZXNwb25zZSxcbiAgcmV0cnk6IFJldHJ5Q29uZmlnLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGJvZHkgPSBhd2FpdCByZWFkSnNvbkJvZHkocmVxKTtcbiAgY29uc3QgcmVxdWVzdGVkID0gdHlwZW9mIGJvZHkubW9kZWwgPT09IFwic3RyaW5nXCIgPyBib2R5Lm1vZGVsIDogdW5kZWZpbmVkO1xuICBpZiAoIXJlcXVlc3RlZCkge1xuICAgIHNlbmRKc29uKHJlcywgNDAwLCBlcnJvckJvZHkoXCJtaXNzaW5nICdtb2RlbCcgaW4gcmVxdWVzdCBib2R5XCIsIFwiaW52YWxpZF9yZXF1ZXN0X2Vycm9yXCIpKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCByZXNvbHZlZCA9IGF3YWl0IGJhY2tlbmQucmVzb2x2ZShyZXF1ZXN0ZWQpO1xuICBib2R5Lm1vZGVsID0gcmVzb2x2ZWQubW9kZWxJZDtcblxuICBpZiAoYmFja2VuZC5pc1Jlc3BvbnNlc09ubHkocmVzb2x2ZWQubW9kZWxJZCkpIHtcbiAgICBhd2FpdCBwcm94eUNoYXRWaWFSZXNwb25zZXMoXG4gICAgICBiYWNrZW5kLFxuICAgICAgYm9keSxcbiAgICAgIHJlcXVlc3RlZCxcbiAgICAgIHJlc29sdmVkLm1vZGVsSWQsXG4gICAgICByZXNvbHZlZC5tYXRjaGVkLFxuICAgICAgcmVzLFxuICAgICAgcmV0cnksXG4gICAgKTtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBUaGlzIHJvdXRlIGZvcndhcmRzIHRoZSBjbGllbnQncyBib2R5IGFzLWlzLCBzbyBhbnl0aGluZyBEYXRhYnJpY2tzIHJlZnVzZXNcbiAgLy8gdG8gcGFyc2UgZmFpbHMgdGhlIHR1cm4uIERyb3AgdGhlIGtub3duIG9mZmVuZGVycyByYXRoZXIgdGhhbiByZWxheWluZyBhXG4gIC8vIDQwMCB0aGUgY2xpZW50IGNhbiBkbyBub3RoaW5nIGFib3V0LlxuICBjb25zdCBkcm9wcGVkID0gb3BlbmFpQ2hhdC5zdHJpcFVuc3VwcG9ydGVkQ2hhdEZpZWxkcyhib2R5LCBleHRyYURyb3BGaWVsZHMoKSk7XG5cbiAgY29uc3Qgd2FudHNTdHJlYW0gPSBib2R5LnN0cmVhbSA9PT0gdHJ1ZTtcbiAgY29uc3QgaGVhZGVycyA9IGF3YWl0IGJhY2tlbmQuYXV0aEhlYWRlcnMoKTtcbiAgaGVhZGVyc1tcImNvbnRlbnQtdHlwZVwiXSA9IFwiYXBwbGljYXRpb24vanNvblwiO1xuICBoZWFkZXJzLmFjY2VwdCA9IHdhbnRzU3RyZWFtID8gXCJ0ZXh0L2V2ZW50LXN0cmVhbVwiIDogXCJhcHBsaWNhdGlvbi9qc29uXCI7XG5cbiAgbG9nZ2VyLmluZm8oXCJwcm94eVwiLCB7XG4gICAgcmVxdWVzdGVkLFxuICAgIHJlc29sdmVkOiByZXNvbHZlZC5tb2RlbElkLFxuICAgIG1hdGNoZWQ6IHJlc29sdmVkLm1hdGNoZWQsXG4gICAgdXBzdHJlYW06IFwiaW52b2NhdGlvbnNcIixcbiAgICBzdHJlYW06IHdhbnRzU3RyZWFtLFxuICAgIC4uLihkcm9wcGVkLmxlbmd0aCA+IDAgPyB7IGRyb3BwZWQgfSA6IHt9KSxcbiAgfSk7XG5cbiAgY29uc3QgdXBzdHJlYW0gPSBhd2FpdCB1cHN0cmVhbUZldGNoUmV0cnlpbmcoXG4gICAgYmFja2VuZCxcbiAgICBiYWNrZW5kLmludm9jYXRpb25zVXJsKHJlc29sdmVkLm1vZGVsSWQpLFxuICAgIGhlYWRlcnMsXG4gICAgYm9keSxcbiAgICByZXMsXG4gICAgcmV0cnksXG4gICk7XG5cbiAgcmVzLndyaXRlSGVhZCh1cHN0cmVhbS5zdGF0dXMsIHtcbiAgICBcImNvbnRlbnQtdHlwZVwiOiB1cHN0cmVhbS5oZWFkZXJzLmdldChcImNvbnRlbnQtdHlwZVwiKSA/PyBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICBcIngtcmVzb2x2ZWQtbW9kZWxcIjogcmVzb2x2ZWQubW9kZWxJZCxcbiAgfSk7XG4gIGF3YWl0IHN0cmVhbUJvZHkodXBzdHJlYW0sIHJlcyk7XG59XG5cbi8qKlxuICogQ2hhdCBDb21wbGV0aW9ucyBjbGllbnQg4oaSIFJlc3BvbnNlcy1vbmx5IERhdGFicmlja3MgbW9kZWw6IHRyYW5zbGF0ZSB0aGVcbiAqIHJlcXVlc3QsIFBPU1QgdG8gYC9zZXJ2aW5nLWVuZHBvaW50cy9yZXNwb25zZXNgLCB0cmFuc2xhdGUgdGhlIHJlcGx5IGJhY2suXG4gKiBTdHJlYW1pbmcgaXMgbm90IHRyYW5zbGF0ZWQgeWV0IC0gdGhvc2UgY2xpZW50cyBzaG91bGQgdXNlIGAvdjEvcmVzcG9uc2VzYC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHJveHlDaGF0VmlhUmVzcG9uc2VzKFxuICBiYWNrZW5kOiBEYXRhYnJpY2tzQmFja2VuZCxcbiAgYm9keTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIHJlcXVlc3RlZDogc3RyaW5nLFxuICBtb2RlbElkOiBzdHJpbmcsXG4gIG1hdGNoZWQ6IGJvb2xlYW4sXG4gIHJlczogU2VydmVyUmVzcG9uc2UsXG4gIHJldHJ5OiBSZXRyeUNvbmZpZyxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB7IHJlc3BvbnNlcywgc3RyZWFtIH0gPSBjaGF0VG9SZXNwb25zZXNSZXF1ZXN0KGJvZHkpO1xuICByZXNwb25zZXMubW9kZWwgPSBtb2RlbElkO1xuXG4gIGlmIChzdHJlYW0pIHtcbiAgICBzZW5kSnNvbihcbiAgICAgIHJlcyxcbiAgICAgIDQwMCxcbiAgICAgIGVycm9yQm9keShcbiAgICAgICAgYG1vZGVsICR7bW9kZWxJZH0gb25seSBzdXBwb3J0cyB0aGUgUmVzcG9uc2VzIEFQSTsgdXNlIFBPU1QgL3YxL3Jlc3BvbnNlcyBmb3Igc3RyZWFtaW5nYCxcbiAgICAgICAgXCJpbnZhbGlkX3JlcXVlc3RfZXJyb3JcIixcbiAgICAgICksXG4gICAgKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBoZWFkZXJzID0gYXdhaXQgYmFja2VuZC5hdXRoSGVhZGVycygpO1xuICBoZWFkZXJzW1wiY29udGVudC10eXBlXCJdID0gXCJhcHBsaWNhdGlvbi9qc29uXCI7XG4gIGhlYWRlcnMuYWNjZXB0ID0gXCJhcHBsaWNhdGlvbi9qc29uXCI7XG5cbiAgY29uc3QgdXBzdHJlYW1VcmwgPSBiYWNrZW5kLnJlc3BvbnNlc1VybChtb2RlbElkKTtcbiAgY29uc3QgZm9yd2FyZCA9IHVwc3RyZWFtVXJsLmluY2x1ZGVzKFwiL29wZW4tcmVzcG9uc2VzXCIpXG4gICAgPyBzYW5pdGl6ZU9wZW5SZXNwb25zZXNSZXF1ZXN0KHJlc3BvbnNlcylcbiAgICA6IHJlc3BvbnNlcztcblxuICBsb2dnZXIuaW5mbyhcInByb3h5XCIsIHtcbiAgICByZXF1ZXN0ZWQsXG4gICAgcmVzb2x2ZWQ6IG1vZGVsSWQsXG4gICAgbWF0Y2hlZCxcbiAgICB1cHN0cmVhbTogdXBzdHJlYW1VcmwsXG4gICAgc3RyZWFtOiBmYWxzZSxcbiAgfSk7XG5cbiAgY29uc3QgdXBzdHJlYW0gPSBhd2FpdCB1cHN0cmVhbUZldGNoUmV0cnlpbmcoYmFja2VuZCwgdXBzdHJlYW1VcmwsIGhlYWRlcnMsIGZvcndhcmQsIHJlcywgcmV0cnkpO1xuXG4gIGlmICghdXBzdHJlYW0ub2spIHtcbiAgICBjb25zdCB0ZXh0ID0gYXdhaXQgdXBzdHJlYW0udGV4dCgpO1xuICAgIHJlcy53cml0ZUhlYWQodXBzdHJlYW0uc3RhdHVzLCB7IFwiY29udGVudC10eXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0pO1xuICAgIHJlcy5lbmQodGV4dCB8fCBKU09OLnN0cmluZ2lmeShlcnJvckJvZHkoXCJ1cHN0cmVhbSBlcnJvclwiLCBcInByb3h5X2Vycm9yXCIpKSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgcmVzcG9uc2VKc29uID0gKGF3YWl0IHVwc3RyZWFtLmpzb24oKSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIHNlbmRKc29uKHJlcywgMjAwLCByZXNwb25zZVRvQ2hhdENvbXBsZXRpb24ocmVzcG9uc2VKc29uLCBtb2RlbElkKSk7XG59XG5cbi8qKlxuICogYFBPU1QgL3YxL3Jlc3BvbnNlc2A6IGZvcndhcmQgdGhlIFJlc3BvbnNlcyBib2R5IHRvIERhdGFicmlja3MnIG5hdGl2ZVxuICogUmVzcG9uc2VzIC8gT3BlbiBSZXNwb25zZXMgc3VyZmFjZSAobW9kZWwgc3RheXMgaW4gdGhlIGJvZHk7IFVSTCBpc1xuICogd29ya3NwYWNlLWxldmVsKS4gU3RyZWFtaW5nIGFuZCBub24tc3RyZWFtaW5nIGJvdGggcGFzcyB0aHJvdWdoIGFzLWlzLlxuICpcbiAqIE9wZW4gUmVzcG9uc2VzIChDbGF1ZGUvR2VtaW5pL+KApikgb25seSBhY2NlcHRzIGBmdW5jdGlvbmAgdG9vbHMgYW5kXG4gKiBgaW5wdXRfKmAgY29udGVudCBwYXJ0IHR5cGVzLCBhbmQgcmVqZWN0cyByZXBsYXllZCBDbGF1ZGUgdGhpbmtpbmdcbiAqIGJsb2NrcyDigJQgc28gQ29kZXgncyBidWlsdC1pbiB0b29scywgcHJpb3ItdHVybiBgb3V0cHV0X3RleHRgLCBhbmRcbiAqIGByZWRhY3RlZF90aGlua2luZ2AgLyBgdGhpbmtpbmdgIC8gYHJlYXNvbmluZ2AgcGFydHMgYXJlIHJld3JpdHRlbiBvclxuICogc3RyaXBwZWQgdGhlcmUuIFRoZSBPcGVuQUkgYC9yZXNwb25zZXNgIHBhdGgga2VlcHMgdGhlbSAtIEdQVCBzdXBwb3J0c1xuICogdGhvc2Ugc2hhcGVzLlxuICovXG5hc3luYyBmdW5jdGlvbiBoYW5kbGVSZXNwb25zZXMoXG4gIGJhY2tlbmQ6IERhdGFicmlja3NCYWNrZW5kLFxuICByZXE6IEluY29taW5nTWVzc2FnZSxcbiAgcmVzOiBTZXJ2ZXJSZXNwb25zZSxcbiAgcmV0cnk6IFJldHJ5Q29uZmlnLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGJvZHkgPSBhd2FpdCByZWFkSnNvbkJvZHkocmVxKTtcbiAgY29uc3QgcmVxdWVzdGVkID0gdHlwZW9mIGJvZHkubW9kZWwgPT09IFwic3RyaW5nXCIgPyBib2R5Lm1vZGVsIDogdW5kZWZpbmVkO1xuICBpZiAoIXJlcXVlc3RlZCkge1xuICAgIHNlbmRKc29uKHJlcywgNDAwLCBlcnJvckJvZHkoXCJtaXNzaW5nICdtb2RlbCcgaW4gcmVxdWVzdCBib2R5XCIsIFwiaW52YWxpZF9yZXF1ZXN0X2Vycm9yXCIpKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCByZXNvbHZlZCA9IGF3YWl0IGJhY2tlbmQucmVzb2x2ZShyZXF1ZXN0ZWQpO1xuICBib2R5Lm1vZGVsID0gcmVzb2x2ZWQubW9kZWxJZDtcblxuICBjb25zdCB1cHN0cmVhbVVybCA9IGJhY2tlbmQucmVzcG9uc2VzVXJsKHJlc29sdmVkLm1vZGVsSWQpO1xuICAvLyBDcm9zcy1wcm92aWRlciBPcGVuIFJlc3BvbnNlcyByZWplY3RzIG5vbi1mdW5jdGlvbiB0b29scyBhbmQgYG91dHB1dF8qYFxuICAvLyBjb250ZW50IHBhcnRzIGluIGBpbnB1dGA7IE9wZW5BSSBgL3Jlc3BvbnNlc2Aga2VlcHMgQ29kZXgncyBuYXRpdmUgc2hhcGVzLlxuICBjb25zdCBmb3J3YXJkID0gdXBzdHJlYW1VcmwuaW5jbHVkZXMoXCIvb3Blbi1yZXNwb25zZXNcIilcbiAgICA/IHNhbml0aXplT3BlblJlc3BvbnNlc1JlcXVlc3QoYm9keSlcbiAgICA6IGJvZHk7XG5cbiAgY29uc3Qgd2FudHNTdHJlYW0gPSBmb3J3YXJkLnN0cmVhbSA9PT0gdHJ1ZTtcbiAgY29uc3QgaGVhZGVycyA9IGF3YWl0IGJhY2tlbmQuYXV0aEhlYWRlcnMoKTtcbiAgaGVhZGVyc1tcImNvbnRlbnQtdHlwZVwiXSA9IFwiYXBwbGljYXRpb24vanNvblwiO1xuICBoZWFkZXJzLmFjY2VwdCA9IHdhbnRzU3RyZWFtID8gXCJ0ZXh0L2V2ZW50LXN0cmVhbVwiIDogXCJhcHBsaWNhdGlvbi9qc29uXCI7XG5cbiAgY29uc3Qgc3RyaXBwZWQgPVxuICAgIEFycmF5LmlzQXJyYXkoYm9keS50b29scykgJiZcbiAgICAoIUFycmF5LmlzQXJyYXkoZm9yd2FyZC50b29scykgfHwgZm9yd2FyZC50b29scy5sZW5ndGggIT09IGJvZHkudG9vbHMubGVuZ3RoKTtcblxuICBsb2dnZXIuaW5mbyhcInJlc3BvbnNlc1wiLCB7XG4gICAgcmVxdWVzdGVkLFxuICAgIHJlc29sdmVkOiByZXNvbHZlZC5tb2RlbElkLFxuICAgIG1hdGNoZWQ6IHJlc29sdmVkLm1hdGNoZWQsXG4gICAgdXBzdHJlYW06IHVwc3RyZWFtVXJsLFxuICAgIHN0cmVhbTogd2FudHNTdHJlYW0sXG4gICAgLi4uKHN0cmlwcGVkID8geyBzdHJpcHBlZE5vbkZ1bmN0aW9uVG9vbHM6IHRydWUgfSA6IHt9KSxcbiAgfSk7XG5cbiAgY29uc3QgdXBzdHJlYW0gPSBhd2FpdCB1cHN0cmVhbUZldGNoUmV0cnlpbmcoYmFja2VuZCwgdXBzdHJlYW1VcmwsIGhlYWRlcnMsIGZvcndhcmQsIHJlcywgcmV0cnkpO1xuXG4gIHJlcy53cml0ZUhlYWQodXBzdHJlYW0uc3RhdHVzLCB7XG4gICAgXCJjb250ZW50LXR5cGVcIjogdXBzdHJlYW0uaGVhZGVycy5nZXQoXCJjb250ZW50LXR5cGVcIikgPz8gXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgXCJ4LXJlc29sdmVkLW1vZGVsXCI6IHJlc29sdmVkLm1vZGVsSWQsXG4gIH0pO1xuICBhd2FpdCBzdHJlYW1Cb2R5KHVwc3RyZWFtLCByZXMpO1xufVxuXG4vKipcbiAqIFB1bXAgYW4gdXBzdHJlYW0gYGZldGNoYCBSZXNwb25zZSBib2R5IHRvIHRoZSBOb2RlIHJlc3BvbnNlLCBjaHVuayBieSBjaHVuay5cbiAqXG4gKiBBIG1pZC1zdHJlYW0gdXBzdHJlYW0gZmFpbHVyZSBpcyBsb2dnZWQgcmF0aGVyIHRoYW4gdGhyb3duOiB0aGUgc3RhdHVzIGxpbmVcbiAqIGFuZCBzb21lIG51bWJlciBvZiBjaHVua3MgaGF2ZSBhbHJlYWR5IGdvbmUgb3V0LCBzbyB0aGVyZSBpcyBubyB3YXkgbGVmdCB0b1xuICogdHVybiBpdCBpbnRvIGFuIEhUVFAgZXJyb3IgYW5kIHRoZSBvbmx5IGhvbmVzdCBtb3ZlIGlzIHRvIGVuZCB0aGUgcmVzcG9uc2VcbiAqIGFuZCBzYXkgd2h5IGluIHRoZSBsb2cuIFdpdGhvdXQgdGhpcyB0aGUgcmVhZCBsb29wIHJlamVjdGVkIGludG8gdGhlIGNhbGxlcidzXG4gKiBjYXRjaCwgd2hpY2ggZm91bmQgYGhlYWRlcnNTZW50YCBhbmQgZW5kZWQgdGhlIHJlc3BvbnNlIHNpbGVudGx5IC0gbWFraW5nIGFuXG4gKiB1cHN0cmVhbSB0ZWFyZG93biBpbmRpc3Rpbmd1aXNoYWJsZSBmcm9tIGEgY2xlYW4gZmluaXNoLlxuICovXG5hc3luYyBmdW5jdGlvbiBzdHJlYW1Cb2R5KHVwc3RyZWFtOiBSZXNwb25zZSwgcmVzOiBTZXJ2ZXJSZXNwb25zZSk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBib2R5ID0gdXBzdHJlYW0uYm9keTtcbiAgaWYgKCFib2R5KSB7XG4gICAgcmVzLmVuZCgpO1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCByZWFkZXIgPSBib2R5LmdldFJlYWRlcigpO1xuICB0cnkge1xuICAgIGZvciAoOzspIHtcbiAgICAgIGNvbnN0IHsgZG9uZSwgdmFsdWUgfSA9IGF3YWl0IHJlYWRlci5yZWFkKCk7XG4gICAgICBpZiAoZG9uZSkgYnJlYWs7XG4gICAgICBpZiAodmFsdWUgJiYgIXJlcy53cml0YWJsZUVuZGVkKSByZXMud3JpdGUoQnVmZmVyLmZyb20odmFsdWUpKTtcbiAgICAgIGlmIChyZXMud3JpdGFibGVFbmRlZCkgYnJlYWs7XG4gICAgfVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICAvLyBBbiBhYm9ydGVkIHJlcXVlc3QgaXMgdGhlIGV4cGVjdGVkIHNoYXBlIG9mIGEgY2xpZW50IGhhbmdpbmcgdXAsIG5vdCBhXG4gICAgLy8gZmF1bHQgd29ydGggcmVwb3J0aW5nIGF0IGVycm9yIGxldmVsLlxuICAgIGNvbnN0IGFib3J0ZWQgPSByZXMud3JpdGFibGVFbmRlZCB8fCAhcmVzLndyaXRhYmxlO1xuICAgIGxvZ2dlclthYm9ydGVkID8gXCJpbmZvXCIgOiBcImVycm9yXCJdKFwic3RyZWFtIGVuZGVkIGVhcmx5XCIsIHtcbiAgICAgIGVycm9yOiBlcnJvci5lcnJvck1lc3NhZ2UoZXJyKSxcbiAgICB9KTtcbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCByZWFkZXIuY2FuY2VsKCkuY2F0Y2goKCkgPT4ge30pO1xuICAgIHJlcy5lbmQoKTtcbiAgfVxufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSByZXF1ZXN0IGNhcnJpZXMgdGhlIGV4cGVjdGVkIGJlYXJlciB0b2tlbi4gKi9cbmZ1bmN0aW9uIGlzQXV0aG9yaXplZChyZXE6IEluY29taW5nTWVzc2FnZSwgYXBpS2V5OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgaGVhZGVyID0gcmVxLmhlYWRlcnMuYXV0aG9yaXphdGlvbjtcbiAgaWYgKCFoZWFkZXIpIHJldHVybiBmYWxzZTtcbiAgcmV0dXJuIGhlYWRlci5yZXBsYWNlKC9eQmVhcmVyXFxzKy9pLCBcIlwiKS50cmltKCkgPT09IGFwaUtleTtcbn1cblxuLyoqXG4gKiBEcmFpbiBhIHJlcXVlc3QgYm9keSBhbmQgcGFyc2UgaXQgYXMgYSBKU09OIG9iamVjdC4gQW4gZW1wdHksIG1hbGZvcm1lZCwgb3JcbiAqIG5vbi1vYmplY3QgYm9keSB5aWVsZHMgYHt9YCwgd2hpY2ggdGhlIGNhbGxlcnMgcmVqZWN0IGFzIGEgbWlzc2luZyBgbW9kZWxgLlxuICovXG5hc3luYyBmdW5jdGlvbiByZWFkSnNvbkJvZHkocmVxOiBJbmNvbWluZ01lc3NhZ2UpOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIHVua25vd24+PiB7XG4gIGNvbnN0IGNodW5rczogQnVmZmVyW10gPSBbXTtcbiAgZm9yIGF3YWl0IChjb25zdCBjaHVuayBvZiByZXEpIHtcbiAgICBjaHVua3MucHVzaChCdWZmZXIuaXNCdWZmZXIoY2h1bmspID8gY2h1bmsgOiBCdWZmZXIuZnJvbShjaHVuayBhcyBVaW50OEFycmF5KSk7XG4gIH1cbiAgcmV0dXJuIGpzb24ucGFyc2VSZWNvcmQoQnVmZmVyLmNvbmNhdChjaHVua3MpLnRvU3RyaW5nKFwidXRmOFwiKSkgPz8ge307XG59XG5cbi8qKiBTZXJpYWxpemUgYW5kIHNlbmQgYSBKU09OIHJlc3BvbnNlLiAqL1xuZnVuY3Rpb24gc2VuZEpzb24ocmVzOiBTZXJ2ZXJSZXNwb25zZSwgc3RhdHVzOiBudW1iZXIsIGJvZHk6IHVua25vd24pOiB2b2lkIHtcbiAgcmVzLndyaXRlSGVhZChzdGF0dXMsIHsgXCJjb250ZW50LXR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIgfSk7XG4gIHJlcy5lbmQoSlNPTi5zdHJpbmdpZnkoYm9keSkpO1xufVxuXG4vKiogT3BlbkFJLXNoYXBlZCBlcnJvciBlbnZlbG9wZS4gKi9cbmZ1bmN0aW9uIGVycm9yQm9keShtZXNzYWdlOiBzdHJpbmcsIHR5cGU6IHN0cmluZyk6IHsgZXJyb3I6IHsgbWVzc2FnZTogc3RyaW5nOyB0eXBlOiBzdHJpbmcgfSB9IHtcbiAgcmV0dXJuIHsgZXJyb3I6IHsgbWVzc2FnZSwgdHlwZSB9IH07XG59XG4iXX0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/backend.ts","../src/cli.ts","../src/defaults.ts","../src/server.ts","../index.ts","../bin/dbx-tools-model-proxy.ts"],"version":"5.9.3"}
|