@dbx-tools/appkit-mastra 0.9.128 → 0.9.131
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 +90 -31
- package/index.ts +10 -8
- package/lib/index.d.ts +10 -8
- package/lib/index.js +8 -7
- package/lib/src/agents.d.ts +4 -0
- package/lib/src/agents.js +12 -3
- package/lib/src/config.d.ts +7 -2
- package/lib/src/config.js +6 -3
- package/lib/src/defaults.d.ts +9 -0
- package/lib/src/defaults.js +29 -1
- package/lib/src/genie-chat.d.ts +71 -0
- package/lib/src/genie-chat.js +272 -0
- package/lib/src/genie.d.ts +2 -0
- package/lib/src/genie.js +11 -8
- package/lib/src/memory.d.ts +5 -5
- package/lib/src/memory.js +10 -3
- package/lib/src/observability.d.ts +3 -0
- package/lib/src/observability.js +2 -2
- package/lib/src/plugin.d.ts +36 -7
- package/lib/src/plugin.js +220 -33
- package/lib/src/request-context.d.ts +2 -0
- package/lib/src/request-context.js +3 -0
- package/lib/src/server.d.ts +4 -0
- package/lib/src/server.js +40 -2
- package/lib/src/statement.d.ts +16 -1
- package/lib/src/statement.js +50 -18
- package/lib/src/telemetry.d.ts +22 -1
- package/lib/src/telemetry.js +174 -18
- package/lib/src/workspaces.d.ts +2 -2
- package/lib/src/workspaces.js +3 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -25
- package/src/agents.ts +15 -1
- package/src/config.ts +10 -4
- package/src/defaults.ts +30 -0
- package/src/genie-chat.ts +341 -0
- package/src/genie.ts +11 -7
- package/src/memory.ts +9 -4
- package/src/observability.ts +6 -2
- package/src/plugin.ts +262 -31
- package/src/request-context.ts +2 -0
- package/src/server.ts +35 -0
- package/src/statement.ts +71 -19
- package/src/telemetry.ts +206 -16
- package/src/workspaces.ts +4 -3
- package/exports.ts +0 -7
- package/lib/exports.d.ts +0 -1
- package/lib/exports.js +0 -2
- package/lib/src/openapi-tool.d.ts +0 -29
- package/lib/src/openapi-tool.js +0 -175
- package/src/openapi-tool.ts +0 -245
package/src/telemetry.ts
CHANGED
|
@@ -20,16 +20,30 @@
|
|
|
20
20
|
|
|
21
21
|
import { StringDecoder } from "node:string_decoder";
|
|
22
22
|
|
|
23
|
+
import { telemetry as appkitTelemetry } from "@dbx-tools/appkit";
|
|
23
24
|
import { getRequestTags } from "@dbx-tools/appkit/request-tags";
|
|
24
25
|
import { environmentUtils, json, log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
25
26
|
import { feedback, thread } from "@dbx-tools/shared-mastra";
|
|
26
|
-
import {
|
|
27
|
+
import type { TracingOptions } from "@mastra/core/observability";
|
|
28
|
+
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
|
|
29
|
+
import {
|
|
30
|
+
context,
|
|
31
|
+
createContextKey,
|
|
32
|
+
SpanKind,
|
|
33
|
+
SpanStatusCode,
|
|
34
|
+
trace,
|
|
35
|
+
type Context,
|
|
36
|
+
type Span,
|
|
37
|
+
} from "@opentelemetry/api";
|
|
27
38
|
import { getRPCMetadata, RPCType } from "@opentelemetry/core";
|
|
39
|
+
import type { ReadableSpan, Span as SdkSpan, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
28
40
|
import type express from "express";
|
|
29
41
|
|
|
42
|
+
import { MASTRA_AUTH_MODE_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_NAME_KEY } from "./config.ts";
|
|
30
43
|
import { USER_EMAIL_HEADER, USER_ID_HEADER } from "./identity.ts";
|
|
31
44
|
import {
|
|
32
45
|
directMlflowTracingActive,
|
|
46
|
+
initializeAppMlflowTraceInfo,
|
|
33
47
|
persistAppMlflowTraceInfo,
|
|
34
48
|
updateActiveMlflowTrace,
|
|
35
49
|
updateActiveMlflowTraceTags,
|
|
@@ -78,7 +92,7 @@ const CHAT_TURN_TRACER = "@dbx-tools/appkit-mastra/telemetry";
|
|
|
78
92
|
* are intentionally excluded: their bodies are tool decisions, not the
|
|
79
93
|
* user prompt, and they rarely produce a fresh answer worth surfacing.
|
|
80
94
|
*/
|
|
81
|
-
const AGENT_TURN_ROUTE =
|
|
95
|
+
const AGENT_TURN_ROUTE = /(?:^|\/)(?:agents\/[^/]+\/(?:stream|generate)|chat\/[^/]+)\/?$/i;
|
|
82
96
|
|
|
83
97
|
/** Attribute keys the MLflow UC `*_trace_unified` view reads from the root span. */
|
|
84
98
|
export const MLFLOW_SPAN_INPUTS_ATTR = "mlflow.spanInputs";
|
|
@@ -129,6 +143,8 @@ interface TraceTarget {
|
|
|
129
143
|
|
|
130
144
|
interface ChatTraceState {
|
|
131
145
|
readonly tags: Map<string, string>;
|
|
146
|
+
readonly traceId: string;
|
|
147
|
+
readonly parentSpanId?: string;
|
|
132
148
|
firstModel?: string;
|
|
133
149
|
user?: string;
|
|
134
150
|
}
|
|
@@ -185,6 +201,26 @@ function activeChatTraceState(): ChatTraceState | undefined {
|
|
|
185
201
|
return context.active().getValue(CHAT_TRACE_STATE_KEY) as ChatTraceState | undefined;
|
|
186
202
|
}
|
|
187
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Bind a Mastra agent run to the trace selected by chat telemetry.
|
|
206
|
+
*
|
|
207
|
+
* Databricks Apps uses a dedicated agent trace because evented agents finish
|
|
208
|
+
* before the HTTP server span. Promoting MLflow trace info after the agent root
|
|
209
|
+
* closes avoids the sidecar replacing clean previews and tags with empty HTTP
|
|
210
|
+
* trace metadata. Other runtimes retain the existing HTTP/local parentage.
|
|
211
|
+
*/
|
|
212
|
+
export function activeChatTracingOptions(
|
|
213
|
+
options: TracingOptions | undefined,
|
|
214
|
+
): TracingOptions | undefined {
|
|
215
|
+
const state = activeChatTraceState();
|
|
216
|
+
if (!state) return options;
|
|
217
|
+
return {
|
|
218
|
+
...options,
|
|
219
|
+
traceId: state.traceId,
|
|
220
|
+
...(state.parentSpanId ? { parentSpanId: state.parentSpanId } : { parentSpanId: undefined }),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
188
224
|
function setChatTraceTag(state: ChatTraceState, name: string, value: ChatTraceTagValue): void {
|
|
189
225
|
const tag = stringUtils.trimToNull(name);
|
|
190
226
|
if (!tag) return;
|
|
@@ -235,9 +271,8 @@ export function recordActiveTraceAuth(auth: "obo" | "service-principal"): void {
|
|
|
235
271
|
}
|
|
236
272
|
|
|
237
273
|
/** Publish the turn's MLflow trace id before the handler writes the body. */
|
|
238
|
-
function stampMlflowTraceId(res: express.Response,
|
|
274
|
+
function stampMlflowTraceId(res: express.Response, traceId: string): void {
|
|
239
275
|
if (res.headersSent) return;
|
|
240
|
-
const traceId = span.spanContext().traceId;
|
|
241
276
|
if (!traceId || traceId === INVALID_TRACE_ID) return;
|
|
242
277
|
res.setHeader(feedback.MLFLOW_TRACE_ID_HEADER, `tr-${traceId}`);
|
|
243
278
|
}
|
|
@@ -329,6 +364,155 @@ export function assistantTextFromJson(body: unknown): string {
|
|
|
329
364
|
return body.text.slice(0, TRACE_IO_LIMIT);
|
|
330
365
|
}
|
|
331
366
|
|
|
367
|
+
const MASTRA_AGENT_INPUT_ATTR = "mastra.agent_run.input";
|
|
368
|
+
const MASTRA_AGENT_OUTPUT_ATTR = "mastra.agent_run.output";
|
|
369
|
+
const GEN_AI_REQUEST_MODEL_ATTR = "gen_ai.request.model";
|
|
370
|
+
const GEN_AI_AGENT_ID_ATTR = "gen_ai.agent.id";
|
|
371
|
+
const GEN_AI_AGENT_NAME_ATTR = "gen_ai.agent.name";
|
|
372
|
+
const MASTRA_MODEL_METADATA_ATTR = "mastra.metadata.modelMetadata";
|
|
373
|
+
const MASTRA_METADATA_PREFIX = "mastra.metadata.";
|
|
374
|
+
|
|
375
|
+
interface AppMlflowTraceState {
|
|
376
|
+
readonly tags: Map<string, string>;
|
|
377
|
+
genie: boolean;
|
|
378
|
+
model?: string;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function attributeString(span: ReadableSpan, name: string): string | undefined {
|
|
382
|
+
const value = span.attributes[name];
|
|
383
|
+
return typeof value === "string" ? (stringUtils.trimToNull(value) ?? undefined) : undefined;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function metadataAttribute(key: string): string {
|
|
387
|
+
return `${MASTRA_METADATA_PREFIX}${key}`;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function modelFromSpan(span: ReadableSpan): string | undefined {
|
|
391
|
+
const direct = attributeString(span, GEN_AI_REQUEST_MODEL_ATTR);
|
|
392
|
+
if (direct) return direct;
|
|
393
|
+
const metadata = json.parse(attributeString(span, MASTRA_MODEL_METADATA_ATTR));
|
|
394
|
+
return object.isRecord(metadata) && typeof metadata.modelId === "string"
|
|
395
|
+
? (stringUtils.trimToNull(metadata.modelId) ?? undefined)
|
|
396
|
+
: undefined;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function spanUsesGenie(span: ReadableSpan): boolean {
|
|
400
|
+
if (span.attributes[MLFLOW_GENIE_TAG_ATTR] === "true") return true;
|
|
401
|
+
if (/\bask_genie(?:_|\b)/i.test(span.name)) return true;
|
|
402
|
+
return ["gen_ai.tool.name", "mastra.tool_call.name"].some((name) =>
|
|
403
|
+
/^ask_genie(?:_|$)/i.test(attributeString(span, name) ?? ""),
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function collectTraceTags(span: ReadableSpan, state: AppMlflowTraceState): void {
|
|
408
|
+
for (const [name, value] of Object.entries(span.attributes)) {
|
|
409
|
+
if (!name.startsWith(MLFLOW_TRACE_TAG_PREFIX)) continue;
|
|
410
|
+
const tag = name.slice(MLFLOW_TRACE_TAG_PREFIX.length);
|
|
411
|
+
if (
|
|
412
|
+
tag &&
|
|
413
|
+
(typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
414
|
+
) {
|
|
415
|
+
state.tags.set(tag, String(value));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function hrTimeMilliseconds(value: readonly [number, number]): number {
|
|
421
|
+
return value[0] * 1_000 + value[1] / 1_000_000;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function previewInput(span: ReadableSpan): string | undefined {
|
|
425
|
+
const raw = attributeString(span, MASTRA_AGENT_INPUT_ATTR);
|
|
426
|
+
if (!raw) return undefined;
|
|
427
|
+
return (textOnlyChatInput(json.parse(raw)) ?? raw).slice(0, TRACE_IO_LIMIT);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function previewOutput(span: ReadableSpan): string | undefined {
|
|
431
|
+
const raw = attributeString(span, MASTRA_AGENT_OUTPUT_ATTR);
|
|
432
|
+
if (!raw) return undefined;
|
|
433
|
+
const parsed = json.parse(raw);
|
|
434
|
+
const text = assistantTextFromJson(parsed) || (typeof parsed === "string" ? parsed : raw);
|
|
435
|
+
return text.slice(0, TRACE_IO_LIMIT);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Promote the actual Mastra `invoke_agent` trace exported by AppKit's provider. */
|
|
439
|
+
export class AppMlflowTraceInfoSpanProcessor implements SpanProcessor {
|
|
440
|
+
private readonly traces = new Map<string, AppMlflowTraceState>();
|
|
441
|
+
|
|
442
|
+
onStart(span: SdkSpan, _parentContext: Context): void {
|
|
443
|
+
const traceId = span.spanContext().traceId;
|
|
444
|
+
if (!this.traces.has(traceId)) {
|
|
445
|
+
this.traces.set(traceId, { tags: new Map(), genie: false });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
onEnd(span: ReadableSpan): void {
|
|
450
|
+
const traceId = span.spanContext().traceId;
|
|
451
|
+
const state: AppMlflowTraceState = this.traces.get(traceId) ?? {
|
|
452
|
+
tags: new Map(),
|
|
453
|
+
genie: false,
|
|
454
|
+
};
|
|
455
|
+
this.traces.set(traceId, state);
|
|
456
|
+
collectTraceTags(span, state);
|
|
457
|
+
state.model ??= modelFromSpan(span);
|
|
458
|
+
state.genie ||= spanUsesGenie(span);
|
|
459
|
+
|
|
460
|
+
if (attributeString(span, GEN_AI_OPERATION_NAME_ATTR) !== "invoke_agent") {
|
|
461
|
+
if (!span.parentSpanContext?.spanId) this.traces.delete(traceId);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const agent =
|
|
466
|
+
state.model ??
|
|
467
|
+
attributeString(span, GEN_AI_AGENT_ID_ATTR) ??
|
|
468
|
+
attributeString(span, GEN_AI_AGENT_NAME_ATTR);
|
|
469
|
+
if (agent) state.tags.set(MLFLOW_AGENT_TAG, agent);
|
|
470
|
+
if (state.genie) state.tags.set(MLFLOW_GENIE_TAG, "true");
|
|
471
|
+
const auth = attributeString(span, metadataAttribute(MASTRA_AUTH_MODE_KEY));
|
|
472
|
+
if (auth === "obo") state.tags.set(MLFLOW_OBO_AUTH_TAG, "true");
|
|
473
|
+
else if (auth === "service-principal") state.tags.set(MLFLOW_SP_AUTH_TAG, "true");
|
|
474
|
+
|
|
475
|
+
const user =
|
|
476
|
+
attributeString(span, metadataAttribute(MASTRA_USER_EMAIL_KEY)) ??
|
|
477
|
+
attributeString(span, metadataAttribute(MASTRA_USER_NAME_KEY)) ??
|
|
478
|
+
attributeString(span, metadataAttribute(MASTRA_RESOURCE_ID_KEY));
|
|
479
|
+
const sessionId =
|
|
480
|
+
attributeString(span, metadataAttribute(MASTRA_THREAD_ID_KEY)) ??
|
|
481
|
+
attributeString(span, "gen_ai.conversation.id");
|
|
482
|
+
const requestPreview = previewInput(span);
|
|
483
|
+
const responsePreview = previewOutput(span);
|
|
484
|
+
persistAppMlflowTraceInfo({
|
|
485
|
+
traceId,
|
|
486
|
+
requestTime: hrTimeMilliseconds(span.startTime),
|
|
487
|
+
executionDuration: hrTimeMilliseconds(span.duration),
|
|
488
|
+
error: span.status.code === SpanStatusCode.ERROR,
|
|
489
|
+
tags: Object.fromEntries(state.tags),
|
|
490
|
+
...(user ? { user } : {}),
|
|
491
|
+
...(sessionId ? { sessionId } : {}),
|
|
492
|
+
...(requestPreview ? { requestPreview } : {}),
|
|
493
|
+
...(responsePreview ? { responsePreview } : {}),
|
|
494
|
+
});
|
|
495
|
+
this.traces.delete(traceId);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async forceFlush(): Promise<void> {}
|
|
499
|
+
|
|
500
|
+
async shutdown(): Promise<void> {
|
|
501
|
+
this.traces.clear();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
let appMlflowTraceInfoProcessorRegistered = false;
|
|
506
|
+
|
|
507
|
+
/** Register trace enrichment before AppKit constructs its native provider. */
|
|
508
|
+
export async function registerAppMlflowTraceInfoProcessor(): Promise<boolean> {
|
|
509
|
+
if (appMlflowTraceInfoProcessorRegistered) return true;
|
|
510
|
+
if (!(await initializeAppMlflowTraceInfo())) return false;
|
|
511
|
+
await appkitTelemetry.registerNativeSpanProcessor(new AppMlflowTraceInfoSpanProcessor());
|
|
512
|
+
appMlflowTraceInfoProcessorRegistered = true;
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
|
|
332
516
|
/** Incrementally assemble UTF-8 SSE text while retaining a bounded JSON fallback. */
|
|
333
517
|
class AssistantResponseCollector {
|
|
334
518
|
private readonly decoder = new StringDecoder("utf8");
|
|
@@ -485,7 +669,11 @@ export function chatTurnTelemetryMiddleware(
|
|
|
485
669
|
next();
|
|
486
670
|
return;
|
|
487
671
|
}
|
|
488
|
-
const traceState: ChatTraceState = {
|
|
672
|
+
const traceState: ChatTraceState = {
|
|
673
|
+
tags: new Map(),
|
|
674
|
+
traceId: targetTraceId,
|
|
675
|
+
parentSpanId: target.span.spanContext().spanId,
|
|
676
|
+
};
|
|
489
677
|
const traceRequestTime = Date.now();
|
|
490
678
|
if (!environmentUtils.isDatabricksAppEnv()) {
|
|
491
679
|
setChatTraceTag(traceState, MLFLOW_LOCAL_TAG, true);
|
|
@@ -499,7 +687,7 @@ export function chatTurnTelemetryMiddleware(
|
|
|
499
687
|
if (value !== undefined) setChatTraceTag(traceState, name, value);
|
|
500
688
|
}
|
|
501
689
|
}
|
|
502
|
-
stampMlflowTraceId(res,
|
|
690
|
+
stampMlflowTraceId(res, traceState.traceId);
|
|
503
691
|
traceState.user = stampMlflowActor(target.span, req);
|
|
504
692
|
target.span.setAttribute(MLFLOW_SPAN_TYPE_ATTR, MLFLOW_SPAN_TYPE_AGENT);
|
|
505
693
|
target.span.setAttribute(GEN_AI_OPERATION_NAME_ATTR, "invoke_agent");
|
|
@@ -569,16 +757,18 @@ export function chatTurnTelemetryMiddleware(
|
|
|
569
757
|
...(traceState.user ? { user: traceState.user } : {}),
|
|
570
758
|
});
|
|
571
759
|
});
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
760
|
+
if (!environmentUtils.isDatabricksAppEnv()) {
|
|
761
|
+
persistAppMlflowTraceInfo({
|
|
762
|
+
traceId: traceState.traceId,
|
|
763
|
+
requestTime: traceRequestTime,
|
|
764
|
+
executionDuration: Date.now() - traceRequestTime,
|
|
765
|
+
error: res.statusCode >= 500,
|
|
766
|
+
tags,
|
|
767
|
+
...(traceState.user ? { user: traceState.user } : {}),
|
|
768
|
+
...(requestPreview ? { requestPreview } : {}),
|
|
769
|
+
...(responsePreview ? { responsePreview } : {}),
|
|
770
|
+
});
|
|
771
|
+
}
|
|
582
772
|
};
|
|
583
773
|
const endOwnedSpan = (): void => {
|
|
584
774
|
if (!target.owned || ownedSpanEnded) return;
|
package/src/workspaces.ts
CHANGED
|
@@ -209,8 +209,8 @@ export interface DatabricksWorkspaceOptions extends Omit<
|
|
|
209
209
|
/** Add user-scoped `/tmp` only when no configured Databricks path mounts writable. */
|
|
210
210
|
fallbackToTmp?: boolean;
|
|
211
211
|
/**
|
|
212
|
-
* Extra local skill trees mounted read-only for file access
|
|
213
|
-
* converts them to native agent-level skills
|
|
212
|
+
* Extra local skill trees mounted read-only for file access and workspace
|
|
213
|
+
* skill search. The plugin also converts them to native agent-level skills.
|
|
214
214
|
*/
|
|
215
215
|
extraSkillMounts?: LocalSkillMount[];
|
|
216
216
|
/** Skill names provided directly to agents and excluded from workspace search. */
|
|
@@ -292,7 +292,7 @@ export function databricksWorkspaceConfig(
|
|
|
292
292
|
retainFilesystemSource ? mountFilesCache : undefined,
|
|
293
293
|
filesystemSourceKey,
|
|
294
294
|
);
|
|
295
|
-
const hasSkillPaths = paths.some(pathMayProvideSkills);
|
|
295
|
+
const hasSkillPaths = extraSkillMounts.length > 0 || paths.some(pathMayProvideSkills);
|
|
296
296
|
const excludedSkillNames = new Set(options.excludeSkillNames ?? []);
|
|
297
297
|
const skills: SkillsResolver =
|
|
298
298
|
options.skills ??
|
|
@@ -820,6 +820,7 @@ function localSkillMountResolvers(mounts: readonly LocalSkillMount[]): Workspace
|
|
|
820
820
|
const source = await filesystem;
|
|
821
821
|
return {
|
|
822
822
|
mounts: { [mount]: filesystems(source, { readOnly: true }) },
|
|
823
|
+
skillPaths: [mount],
|
|
823
824
|
};
|
|
824
825
|
};
|
|
825
826
|
});
|
package/exports.ts
DELETED
package/lib/exports.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { recordActiveTraceAuth, recordActiveTraceModel, recordActiveTraceTag, recordActiveTraceTags, recordActiveTraceUser, } from "./src/telemetry.ts";
|
package/lib/exports.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export { recordActiveTraceAuth, recordActiveTraceModel, recordActiveTraceTag, recordActiveTraceTags, recordActiveTraceUser, } from "./src/telemetry.js";
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwb3J0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL2V4cG9ydHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLHFCQUFxQixFQUNyQixzQkFBc0IsRUFDdEIsb0JBQW9CLEVBQ3BCLHFCQUFxQixFQUNyQixxQkFBcUIsR0FDdEIsTUFBTSxvQkFBb0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7XG4gIHJlY29yZEFjdGl2ZVRyYWNlQXV0aCxcbiAgcmVjb3JkQWN0aXZlVHJhY2VNb2RlbCxcbiAgcmVjb3JkQWN0aXZlVHJhY2VUYWcsXG4gIHJlY29yZEFjdGl2ZVRyYWNlVGFncyxcbiAgcmVjb3JkQWN0aXZlVHJhY2VVc2VyLFxufSBmcm9tIFwiLi9zcmMvdGVsZW1ldHJ5LnRzXCI7XG4iXX0=
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema7 } from "json-schema";
|
|
2
|
-
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD", "TRACE"];
|
|
3
|
-
/** HTTP method supported by an {@link OpenApiTool}. */
|
|
4
|
-
export type OpenApiHttpMethod = (typeof HTTP_METHODS)[number];
|
|
5
|
-
/** One executable HTTP operation derived from an OpenAPI document. */
|
|
6
|
-
export interface OpenApiTool {
|
|
7
|
-
/** Stable operation identifier. */
|
|
8
|
-
id: string;
|
|
9
|
-
/** Model-facing operation description. */
|
|
10
|
-
description: string;
|
|
11
|
-
/** Resolved operation URL, or the OpenAPI path for a local document without a server URL. */
|
|
12
|
-
url: string;
|
|
13
|
-
/** HTTP method used by the operation. */
|
|
14
|
-
method: OpenApiHttpMethod;
|
|
15
|
-
/** JSON Schema describing operation input. */
|
|
16
|
-
inputSchema: JSONSchema7;
|
|
17
|
-
/** JSON Schema describing a successful operation result. */
|
|
18
|
-
outputSchema: JSONSchema7;
|
|
19
|
-
}
|
|
20
|
-
/** Request options used while loading an OpenAPI document. */
|
|
21
|
-
export interface OpenApiToolsOptions {
|
|
22
|
-
/** Cancels an HTTP request or file read. */
|
|
23
|
-
signal?: AbortSignal;
|
|
24
|
-
/** Headers sent when `source` is an HTTP(S) URL. */
|
|
25
|
-
headers?: RequestInit["headers"];
|
|
26
|
-
}
|
|
27
|
-
/** Load an HTTP(S) URL or local file and return its OpenAPI operations as tool schemas. */
|
|
28
|
-
export declare function openApiTools(source: string, options?: OpenApiToolsOptions): Promise<OpenApiTool[]>;
|
|
29
|
-
export {};
|
package/lib/src/openapi-tool.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAPI operation loading and JSON Schema extraction.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
import { readFile } from "node:fs/promises";
|
|
7
|
-
import { object } from "@dbx-tools/shared-core";
|
|
8
|
-
import { urlBuilder } from "@dbx-tools/shared-core/net";
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD", "TRACE"];
|
|
11
|
-
const JsonObjectSchema = z
|
|
12
|
-
.record(z.string(), z.unknown())
|
|
13
|
-
.describe("A JSON object embedded in an OpenAPI document.");
|
|
14
|
-
const OpenApiMediaTypeSchema = z
|
|
15
|
-
.object({
|
|
16
|
-
schema: JsonObjectSchema.describe("The JSON Schema for this media type."),
|
|
17
|
-
})
|
|
18
|
-
.describe("One OpenAPI media type.");
|
|
19
|
-
const OpenApiRequestBodySchema = z
|
|
20
|
-
.object({
|
|
21
|
-
content: z
|
|
22
|
-
.record(z.string(), OpenApiMediaTypeSchema)
|
|
23
|
-
.describe("Request media types keyed by content type."),
|
|
24
|
-
})
|
|
25
|
-
.describe("An OpenAPI request body.");
|
|
26
|
-
const OpenApiResponseSchema = z
|
|
27
|
-
.object({
|
|
28
|
-
content: z
|
|
29
|
-
.record(z.string(), OpenApiMediaTypeSchema)
|
|
30
|
-
.optional()
|
|
31
|
-
.describe("Response media types keyed by content type."),
|
|
32
|
-
})
|
|
33
|
-
.describe("One OpenAPI response.");
|
|
34
|
-
const OpenApiOperationSchema = z
|
|
35
|
-
.object({
|
|
36
|
-
operationId: z.string().describe("The stable operation identifier."),
|
|
37
|
-
summary: z.string().optional().describe("The short operation summary."),
|
|
38
|
-
description: z.string().optional().describe("The full operation description."),
|
|
39
|
-
requestBody: OpenApiRequestBodySchema.optional().describe("The optional operation request body."),
|
|
40
|
-
responses: z
|
|
41
|
-
.record(z.string(), OpenApiResponseSchema)
|
|
42
|
-
.describe("Operation responses keyed by HTTP status."),
|
|
43
|
-
})
|
|
44
|
-
.describe("The OpenAPI fields required to define a tool.");
|
|
45
|
-
const OpenApiPathSchema = z
|
|
46
|
-
.object({
|
|
47
|
-
get: OpenApiOperationSchema.optional(),
|
|
48
|
-
post: OpenApiOperationSchema.optional(),
|
|
49
|
-
put: OpenApiOperationSchema.optional(),
|
|
50
|
-
patch: OpenApiOperationSchema.optional(),
|
|
51
|
-
delete: OpenApiOperationSchema.optional(),
|
|
52
|
-
options: OpenApiOperationSchema.optional(),
|
|
53
|
-
head: OpenApiOperationSchema.optional(),
|
|
54
|
-
trace: OpenApiOperationSchema.optional(),
|
|
55
|
-
})
|
|
56
|
-
.describe("Operations available at one OpenAPI path.");
|
|
57
|
-
const OpenApiDocumentSchema = z
|
|
58
|
-
.object({
|
|
59
|
-
paths: z
|
|
60
|
-
.record(z.string(), OpenApiPathSchema)
|
|
61
|
-
.refine((paths) => Object.keys(paths).length > 0, "OpenAPI paths must not be empty")
|
|
62
|
-
.describe("OpenAPI path items keyed by URL path."),
|
|
63
|
-
components: JsonObjectSchema.optional().describe("Reusable OpenAPI components."),
|
|
64
|
-
servers: z
|
|
65
|
-
.array(z.object({
|
|
66
|
-
url: z.string().describe("Server base URL."),
|
|
67
|
-
}))
|
|
68
|
-
.optional()
|
|
69
|
-
.describe("OpenAPI server definitions."),
|
|
70
|
-
})
|
|
71
|
-
.describe("An OpenAPI document used to define HTTP tools.");
|
|
72
|
-
/** Load an HTTP(S) URL or local file and return its OpenAPI operations as tool schemas. */
|
|
73
|
-
export async function openApiTools(source, options = {}) {
|
|
74
|
-
const sourceUrl = httpUrl(source);
|
|
75
|
-
const document = OpenApiDocumentSchema.parse(sourceUrl
|
|
76
|
-
? await fetchDocument(sourceUrl, options)
|
|
77
|
-
: await readDocument(source, options.signal));
|
|
78
|
-
return Object.entries(document.paths).flatMap(([path, item]) => HTTP_METHODS.flatMap((method) => {
|
|
79
|
-
const operation = item[method.toLowerCase()];
|
|
80
|
-
return operation ? [operationTool(document, sourceUrl, path, method, operation)] : [];
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
function httpUrl(source) {
|
|
84
|
-
const value = source.trim();
|
|
85
|
-
if (!value.startsWith("http://") && !value.startsWith("https://"))
|
|
86
|
-
return undefined;
|
|
87
|
-
const url = urlBuilder(value);
|
|
88
|
-
return url?.scheme === "http" || url?.scheme === "https" ? url : undefined;
|
|
89
|
-
}
|
|
90
|
-
async function fetchDocument(url, options) {
|
|
91
|
-
const response = await fetch(url, {
|
|
92
|
-
signal: options.signal,
|
|
93
|
-
headers: options.headers,
|
|
94
|
-
});
|
|
95
|
-
if (!response.ok) {
|
|
96
|
-
throw new Error(`OpenAPI request failed with HTTP ${response.status}: ${url}`);
|
|
97
|
-
}
|
|
98
|
-
return response.json();
|
|
99
|
-
}
|
|
100
|
-
async function readDocument(path, signal) {
|
|
101
|
-
return JSON.parse(await readFile(path, { encoding: "utf8", signal }));
|
|
102
|
-
}
|
|
103
|
-
function operationTool(document, sourceUrl, path, method, operation) {
|
|
104
|
-
const requestSchema = jsonSchema(operation.requestBody?.content) ?? { type: "object" };
|
|
105
|
-
const responseSchema = successfulResponseSchema(operation);
|
|
106
|
-
return {
|
|
107
|
-
id: operation.operationId,
|
|
108
|
-
description: operation.description?.trim() || operation.summary?.trim() || operation.operationId,
|
|
109
|
-
url: operationUrl(document, sourceUrl, path),
|
|
110
|
-
method,
|
|
111
|
-
inputSchema: resolveReferences(document, requestSchema),
|
|
112
|
-
outputSchema: resolveReferences(document, responseSchema),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
function successfulResponseSchema(operation) {
|
|
116
|
-
for (const [status, response] of Object.entries(operation.responses)) {
|
|
117
|
-
const schema = jsonSchema(response.content);
|
|
118
|
-
if (/^2(?:\d{2}|XX)$/i.test(status) && schema)
|
|
119
|
-
return schema;
|
|
120
|
-
}
|
|
121
|
-
return {};
|
|
122
|
-
}
|
|
123
|
-
function jsonSchema(content) {
|
|
124
|
-
const mediaType = Object.entries(content ?? {}).find(([type]) => /^application\/(?:[^+;]+\+)?json(?:;|$)/i.test(type));
|
|
125
|
-
return mediaType?.[1].schema;
|
|
126
|
-
}
|
|
127
|
-
function operationUrl(document, sourceUrl, path) {
|
|
128
|
-
const server = document.servers?.[0]?.url;
|
|
129
|
-
if (server) {
|
|
130
|
-
const base = sourceUrl ? urlBuilder(new URL(server, sourceUrl)) : httpUrl(server);
|
|
131
|
-
return base
|
|
132
|
-
? base.withPathAppend(path).toString()
|
|
133
|
-
: `${server.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
134
|
-
}
|
|
135
|
-
return sourceUrl ? (urlBuilder(sourceUrl)?.withPathReplace(path).toString() ?? path) : path;
|
|
136
|
-
}
|
|
137
|
-
function resolveReferences(document, value, active = new Set()) {
|
|
138
|
-
if (Array.isArray(value))
|
|
139
|
-
return value.map((item) => resolveReferences(document, item, active));
|
|
140
|
-
if (!object.isRecord(value))
|
|
141
|
-
return value;
|
|
142
|
-
const reference = typeof value.$ref === "string" ? value.$ref : undefined;
|
|
143
|
-
if (reference) {
|
|
144
|
-
if (active.has(reference))
|
|
145
|
-
throw new Error(`Cyclic OpenAPI reference: ${reference}`);
|
|
146
|
-
const target = referenceTarget(document, reference);
|
|
147
|
-
const next = new Set(active).add(reference);
|
|
148
|
-
const { $ref: _ignored, ...siblings } = value;
|
|
149
|
-
return {
|
|
150
|
-
...resolveReferences(document, target, next),
|
|
151
|
-
...resolveReferences(document, siblings, next),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, resolveReferences(document, child, active)]));
|
|
155
|
-
}
|
|
156
|
-
function referenceTarget(document, reference) {
|
|
157
|
-
if (!reference.startsWith("#/"))
|
|
158
|
-
throw new Error(`Unsupported OpenAPI reference: ${reference}`);
|
|
159
|
-
let current = document;
|
|
160
|
-
for (const rawSegment of reference.slice(2).split("/")) {
|
|
161
|
-
const segment = rawSegment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
162
|
-
if (!object.isRecord(current) || !(segment in current)) {
|
|
163
|
-
throw new Error(`Unresolved OpenAPI reference: ${reference}`);
|
|
164
|
-
}
|
|
165
|
-
current = current[segment];
|
|
166
|
-
}
|
|
167
|
-
return current;
|
|
168
|
-
}
|
|
169
|
-
if (import.meta.main) {
|
|
170
|
-
const source = process.argv[2];
|
|
171
|
-
if (!source)
|
|
172
|
-
throw new Error("Usage: bun openapi-tool.ts <openapi-url-or-file>");
|
|
173
|
-
console.log(JSON.stringify(await openApiTools(source), null, 2));
|
|
174
|
-
}
|
|
175
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS10b29sLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29wZW5hcGktdG9vbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBQ0gsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFtQixNQUFNLDRCQUE0QixDQUFDO0FBRXpFLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEIsTUFBTSxZQUFZLEdBQUcsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFVLENBQUM7QUFFcEcsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDO0tBQ3ZCLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0tBQy9CLFFBQVEsQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDO0FBRTlELE1BQU0sc0JBQXNCLEdBQUcsQ0FBQztLQUM3QixNQUFNLENBQUM7SUFDTixNQUFNLEVBQUUsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLHNDQUFzQyxDQUFDO0NBQzFFLENBQUM7S0FDRCxRQUFRLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUV2QyxNQUFNLHdCQUF3QixHQUFHLENBQUM7S0FDL0IsTUFBTSxDQUFDO0lBQ04sT0FBTyxFQUFFLENBQUM7U0FDUCxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLHNCQUFzQixDQUFDO1NBQzFDLFFBQVEsQ0FBQyw0Q0FBNEMsQ0FBQztDQUMxRCxDQUFDO0tBQ0QsUUFBUSxDQUFDLDBCQUEwQixDQUFDLENBQUM7QUFFeEMsTUFBTSxxQkFBcUIsR0FBRyxDQUFDO0tBQzVCLE1BQU0s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QUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ2hFLENBQUM7UUFDRCxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzdCLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3JCLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0IsSUFBSSxDQUFDLE1BQU07UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGtEQUFrRCxDQUFDLENBQUM7SUFDakYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sWUFBWSxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ25FLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE9wZW5BUEkgb3BlcmF0aW9uIGxvYWRpbmcgYW5kIEpTT04gU2NoZW1hIGV4dHJhY3Rpb24uXG4gKlxuICogQG1vZHVsZVxuICovXG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgdXJsQnVpbGRlciwgdHlwZSBVcmxCdWlsZGVyIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmUvbmV0XCI7XG5pbXBvcnQgdHlwZSB7IEpTT05TY2hlbWE3IH0gZnJvbSBcImpzb24tc2NoZW1hXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuXG5jb25zdCBIVFRQX01FVEhPRFMgPSBbXCJHRVRcIiwgXCJQT1NUXCIsIFwiUFVUXCIsIFwiUEFUQ0hcIiwgXCJERUxFVEVcIiwgXCJPUFRJT05TXCIsIFwiSEVBRFwiLCBcIlRSQUNFXCJdIGFzIGNvbnN0O1xuXG5jb25zdCBKc29uT2JqZWN0U2NoZW1hID0gelxuICAucmVjb3JkKHouc3RyaW5nKCksIHoudW5rbm93bigpKVxuICAuZGVzY3JpYmUoXCJBIEpTT04gb2JqZWN0IGVtYmVkZGVkIGluIGFuIE9wZW5BUEkgZG9jdW1lbnQuXCIpO1xuXG5jb25zdCBPcGVuQXBpTWVkaWFUeXBlU2NoZW1hID0gelxuICAub2JqZWN0KHtcbiAgICBzY2hlbWE6IEpzb25PYmplY3RTY2hlbWEuZGVzY3JpYmUoXCJUaGUgSlNPTiBTY2hlbWEgZm9yIHRoaXMgbWVkaWEgdHlwZS5cIiksXG4gIH0pXG4gIC5kZXNjcmliZShcIk9uZSBPcGVuQVBJIG1lZGlhIHR5cGUuXCIpO1xuXG5jb25zdCBPcGVuQXBpUmVxdWVzdEJvZHlTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIGNvbnRlbnQ6IHpcbiAgICAgIC5yZWNvcmQoei5zdHJpbmcoKSwgT3BlbkFwaU1lZGlhVHlwZVNjaGVtYSlcbiAgICAgIC5kZXNjcmliZShcIlJlcXVlc3QgbWVkaWEgdHlwZXMga2V5ZWQgYnkgY29udGVudCB0eXBlLlwiKSxcbiAgfSlcbiAgLmRlc2NyaWJlKFwiQW4gT3BlbkFQSSByZXF1ZXN0IGJvZHkuXCIpO1xuXG5jb25zdCBPcGVuQXBpUmVzcG9uc2VTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIGNvbnRlbnQ6IHpcbiAgICAgIC5yZWNvcmQoei5zdHJpbmcoKSwgT3BlbkFwaU1lZGlhVHlwZVNjaGVtYSlcbiAgICAgIC5vcHRpb25hbCgpXG4gICAgICAuZGVzY3JpYmUoXCJSZXNwb25zZSBtZWRpYSB0eXBlcyBrZXllZCBieSBjb250ZW50IHR5cGUuXCIpLFxuICB9KVxuICAuZGVzY3JpYmUoXCJPbmUgT3BlbkFQSSByZXNwb25zZS5cIik7XG5cbmNvbnN0IE9wZW5BcGlPcGVyYXRpb25TY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIG9wZXJhdGlvbklkOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVGhlIHN0YWJsZSBvcGVyYXRpb24gaWRlbnRpZmllci5cIiksXG4gICAgc3VtbWFyeTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVGhlIHNob3J0IG9wZXJhdGlvbiBzdW1tYXJ5LlwiKSxcbiAgICBkZXNjcmlwdGlvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVGhlIGZ1bGwgb3BlcmF0aW9uIGRlc2NyaXB0aW9uLlwiKSxcbiAgICByZXF1ZXN0Qm9keTogT3BlbkFwaVJlcXVlc3RCb2R5U2NoZW1hLm9wdGlvbmFsKCkuZGVzY3JpYmUoXG4gICAgICBcIlRoZSBvcHRpb25hbCBvcGVyYXRpb24gcmVxdWVzdCBib2R5LlwiLFxuICAgICksXG4gICAgcmVzcG9uc2VzOiB6XG4gICAgICAucmVjb3JkKHouc3RyaW5nKCksIE9wZW5BcGlSZXNwb25zZVNjaGVtYSlcbiAgICAgIC5kZXNjcmliZShcIk9wZXJhdGlvbiByZXNwb25zZXMga2V5ZWQgYnkgSFRUUCBzdGF0dXMuXCIpLFxuICB9KVxuICAuZGVzY3JpYmUoXCJUaGUgT3BlbkFQSSBmaWVsZHMgcmVxdWlyZWQgdG8gZGVmaW5lIGEgdG9vbC5cIik7XG5cbmNvbnN0IE9wZW5BcGlQYXRoU2NoZW1hID0gelxuICAub2JqZWN0KHtcbiAgICBnZXQ6IE9wZW5BcGlPcGVyYXRpb25TY2hlbWEub3B0aW9uYWwoKSxcbiAgICBwb3N0OiBPcGVuQXBpT3BlcmF0aW9uU2NoZW1hLm9wdGlvbmFsKCksXG4gICAgcHV0OiBPcGVuQXBpT3BlcmF0aW9uU2NoZW1hLm9wdGlvbmFsKCksXG4gICAgcGF0Y2g6IE9wZW5BcGlPcGVyYXRpb25TY2hlbWEub3B0aW9uYWwoKSxcbiAgICBkZWxldGU6IE9wZW5BcGlPcGVyYXRpb25TY2hlbWEub3B0aW9uYWwoKSxcbiAgICBvcHRpb25zOiBPcGVuQXBpT3BlcmF0aW9uU2NoZW1hLm9wdGlvbmFsKCksXG4gICAgaGVhZDogT3BlbkFwaU9wZXJhdGlvblNjaGVtYS5vcHRpb25hbCgpLFxuICAgIHRyYWNlOiBPcGVuQXBpT3BlcmF0aW9uU2NoZW1hLm9wdGlvbmFsKCksXG4gIH0pXG4gIC5kZXNjcmliZShcIk9wZXJhdGlvbnMgYXZhaWxhYmxlIGF0IG9uZSBPcGVuQVBJIHBhdGguXCIpO1xuXG5jb25zdCBPcGVuQXBpRG9jdW1lbnRTY2hlbWEgPSB6XG4gIC5vYmplY3Qoe1xuICAgIHBhdGhzOiB6XG4gICAgICAucmVjb3JkKHouc3RyaW5nKCksIE9wZW5BcGlQYXRoU2NoZW1hKVxuICAgICAgLnJlZmluZSgocGF0aHMpID0+IE9iamVjdC5rZXlzKHBhdGhzKS5sZW5ndGggPiAwLCBcIk9wZW5BUEkgcGF0aHMgbXVzdCBub3QgYmUgZW1wdHlcIilcbiAgICAgIC5kZXNjcmliZShcIk9wZW5BUEkgcGF0aCBpdGVtcyBrZXllZCBieSBVUkwgcGF0aC5cIiksXG4gICAgY29tcG9uZW50czogSnNvbk9iamVjdFNjaGVtYS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiUmV1c2FibGUgT3BlbkFQSSBjb21wb25lbnRzLlwiKSxcbiAgICBzZXJ2ZXJzOiB6XG4gICAgICAuYXJyYXkoXG4gICAgICAgIHoub2JqZWN0KHtcbiAgICAgICAgICB1cmw6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJTZXJ2ZXIgYmFzZSBVUkwuXCIpLFxuICAgICAgICB9KSxcbiAgICAgIClcbiAgICAgIC5vcHRpb25hbCgpXG4gICAgICAuZGVzY3JpYmUoXCJPcGVuQVBJIHNlcnZlciBkZWZpbml0aW9ucy5cIiksXG4gIH0pXG4gIC5kZXNjcmliZShcIkFuIE9wZW5BUEkgZG9jdW1lbnQgdXNlZCB0byBkZWZpbmUgSFRUUCB0b29scy5cIik7XG5cbnR5cGUgT3BlbkFwaURvY3VtZW50ID0gei5pbmZlcjx0eXBlb2YgT3BlbkFwaURvY3VtZW50U2NoZW1hPjtcbnR5cGUgT3BlbkFwaU9wZXJhdGlvbiA9IHouaW5mZXI8dHlwZW9mIE9wZW5BcGlPcGVyYXRpb25TY2hlbWE+O1xuXG4vKiogSFRUUCBtZXRob2Qgc3VwcG9ydGVkIGJ5IGFuIHtAbGluayBPcGVuQXBpVG9vbH0uICovXG5leHBvcnQgdHlwZSBPcGVuQXBpSHR0cE1ldGhvZCA9ICh0eXBlb2YgSFRUUF9NRVRIT0RTKVtudW1iZXJdO1xuXG4vKiogT25lIGV4ZWN1dGFibGUgSFRUUCBvcGVyYXRpb24gZGVyaXZlZCBmcm9tIGFuIE9wZW5BUEkgZG9jdW1lbnQuICovXG5leHBvcnQgaW50ZXJmYWNlIE9wZW5BcGlUb29sIHtcbiAgLyoqIFN0YWJsZSBvcGVyYXRpb24gaWRlbnRpZmllci4gKi9cbiAgaWQ6IHN0cmluZztcbiAgLyoqIE1vZGVsLWZhY2luZyBvcGVyYXRpb24gZGVzY3JpcHRpb24uICovXG4gIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIC8qKiBSZXNvbHZlZCBvcGVyYXRpb24gVVJMLCBvciB0aGUgT3BlbkFQSSBwYXRoIGZvciBhIGxvY2FsIGRvY3VtZW50IHdpdGhvdXQgYSBzZXJ2ZXIgVVJMLiAqL1xuICB1cmw6IHN0cmluZztcbiAgLyoqIEhUVFAgbWV0aG9kIHVzZWQgYnkgdGhlIG9wZXJhdGlvbi4gKi9cbiAgbWV0aG9kOiBPcGVuQXBpSHR0cE1ldGhvZDtcbiAgLyoqIEpTT04gU2NoZW1hIGRlc2NyaWJpbmcgb3BlcmF0aW9uIGlucHV0LiAqL1xuICBpbnB1dFNjaGVtYTogSlNPTlNjaGVtYTc7XG4gIC8qKiBKU09OIFNjaGVtYSBkZXNjcmliaW5nIGEgc3VjY2Vzc2Z1bCBvcGVyYXRpb24gcmVzdWx0LiAqL1xuICBvdXRwdXRTY2hlbWE6IEpTT05TY2hlbWE3O1xufVxuXG4vKiogUmVxdWVzdCBvcHRpb25zIHVzZWQgd2hpbGUgbG9hZGluZyBhbiBPcGVuQVBJIGRvY3VtZW50LiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQXBpVG9vbHNPcHRpb25zIHtcbiAgLyoqIENhbmNlbHMgYW4gSFRUUCByZXF1ZXN0IG9yIGZpbGUgcmVhZC4gKi9cbiAgc2lnbmFsPzogQWJvcnRTaWduYWw7XG4gIC8qKiBIZWFkZXJzIHNlbnQgd2hlbiBgc291cmNlYCBpcyBhbiBIVFRQKFMpIFVSTC4gKi9cbiAgaGVhZGVycz86IFJlcXVlc3RJbml0W1wiaGVhZGVyc1wiXTtcbn1cblxuLyoqIExvYWQgYW4gSFRUUChTKSBVUkwgb3IgbG9jYWwgZmlsZSBhbmQgcmV0dXJuIGl0cyBPcGVuQVBJIG9wZXJhdGlvbnMgYXMgdG9vbCBzY2hlbWFzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG9wZW5BcGlUb29scyhcbiAgc291cmNlOiBzdHJpbmcsXG4gIG9wdGlvbnM6IE9wZW5BcGlUb29sc09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8T3BlbkFwaVRvb2xbXT4ge1xuICBjb25zdCBzb3VyY2VVcmwgPSBodHRwVXJsKHNvdXJjZSk7XG4gIGNvbnN0IGRvY3VtZW50ID0gT3BlbkFwaURvY3VtZW50U2NoZW1hLnBhcnNlKFxuICAgIHNvdXJjZVVybFxuICAgICAgPyBhd2FpdCBmZXRjaERvY3VtZW50KHNvdXJjZVVybCwgb3B0aW9ucylcbiAgICAgIDogYXdhaXQgcmVhZERvY3VtZW50KHNvdXJjZSwgb3B0aW9ucy5zaWduYWwpLFxuICApO1xuICByZXR1cm4gT2JqZWN0LmVudHJpZXMoZG9jdW1lbnQucGF0aHMpLmZsYXRNYXAoKFtwYXRoLCBpdGVtXSkgPT5cbiAgICBIVFRQX01FVEhPRFMuZmxhdE1hcCgobWV0aG9kKSA9PiB7XG4gICAgICBjb25zdCBvcGVyYXRpb24gPSBpdGVtW21ldGhvZC50b0xvd2VyQ2FzZSgpIGFzIExvd2VyY2FzZTxPcGVuQXBpSHR0cE1ldGhvZD5dO1xuICAgICAgcmV0dXJuIG9wZXJhdGlvbiA/IFtvcGVyYXRpb25Ub29sKGRvY3VtZW50LCBzb3VyY2VVcmwsIHBhdGgsIG1ldGhvZCwgb3BlcmF0aW9uKV0gOiBbXTtcbiAgICB9KSxcbiAgKTtcbn1cblxuZnVuY3Rpb24gaHR0cFVybChzb3VyY2U6IHN0cmluZyk6IFVybEJ1aWxkZXIgfCB1bmRlZmluZWQge1xuICBjb25zdCB2YWx1ZSA9IHNvdXJjZS50cmltKCk7XG4gIGlmICghdmFsdWUuc3RhcnRzV2l0aChcImh0dHA6Ly9cIikgJiYgIXZhbHVlLnN0YXJ0c1dpdGgoXCJodHRwczovL1wiKSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgdXJsID0gdXJsQnVpbGRlcih2YWx1ZSk7XG4gIHJldHVybiB1cmw/LnNjaGVtZSA9PT0gXCJodHRwXCIgfHwgdXJsPy5zY2hlbWUgPT09IFwiaHR0cHNcIiA/IHVybCA6IHVuZGVmaW5lZDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZmV0Y2hEb2N1bWVudCh1cmw6IFVSTCwgb3B0aW9uczogT3BlbkFwaVRvb2xzT3B0aW9ucyk6IFByb21pc2U8dW5rbm93bj4ge1xuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHVybCwge1xuICAgIHNpZ25hbDogb3B0aW9ucy5zaWduYWwsXG4gICAgaGVhZGVyczogb3B0aW9ucy5oZWFkZXJzLFxuICB9KTtcbiAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgIHRocm93IG5ldyBFcnJvcihgT3BlbkFQSSByZXF1ZXN0IGZhaWxlZCB3aXRoIEhUVFAgJHtyZXNwb25zZS5zdGF0dXN9OiAke3VybH1gKTtcbiAgfVxuICByZXR1cm4gcmVzcG9uc2UuanNvbigpO1xufVxuXG5hc3luYyBmdW5jdGlvbiByZWFkRG9jdW1lbnQocGF0aDogc3RyaW5nLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8dW5rbm93bj4ge1xuICByZXR1cm4gSlNPTi5wYXJzZShhd2FpdCByZWFkRmlsZShwYXRoLCB7IGVuY29kaW5nOiBcInV0ZjhcIiwgc2lnbmFsIH0pKTtcbn1cblxuZnVuY3Rpb24gb3BlcmF0aW9uVG9vbChcbiAgZG9jdW1lbnQ6IE9wZW5BcGlEb2N1bWVudCxcbiAgc291cmNlVXJsOiBVUkwgfCB1bmRlZmluZWQsXG4gIHBhdGg6IHN0cmluZyxcbiAgbWV0aG9kOiBPcGVuQXBpSHR0cE1ldGhvZCxcbiAgb3BlcmF0aW9uOiBPcGVuQXBpT3BlcmF0aW9uLFxuKTogT3BlbkFwaVRvb2wge1xuICBjb25zdCByZXF1ZXN0U2NoZW1hID0ganNvblNjaGVtYShvcGVyYXRpb24ucmVxdWVzdEJvZHk/LmNvbnRlbnQpID8/IHsgdHlwZTogXCJvYmplY3RcIiB9O1xuICBjb25zdCByZXNwb25zZVNjaGVtYSA9IHN1Y2Nlc3NmdWxSZXNwb25zZVNjaGVtYShvcGVyYXRpb24pO1xuICByZXR1cm4ge1xuICAgIGlkOiBvcGVyYXRpb24ub3BlcmF0aW9uSWQsXG4gICAgZGVzY3JpcHRpb246XG4gICAgICBvcGVyYXRpb24uZGVzY3JpcHRpb24/LnRyaW0oKSB8fCBvcGVyYXRpb24uc3VtbWFyeT8udHJpbSgpIHx8IG9wZXJhdGlvbi5vcGVyYXRpb25JZCxcbiAgICB1cmw6IG9wZXJhdGlvblVybChkb2N1bWVudCwgc291cmNlVXJsLCBwYXRoKSxcbiAgICBtZXRob2QsXG4gICAgaW5wdXRTY2hlbWE6IHJlc29sdmVSZWZlcmVuY2VzKGRvY3VtZW50LCByZXF1ZXN0U2NoZW1hKSBhcyBKU09OU2NoZW1hNyxcbiAgICBvdXRwdXRTY2hlbWE6IHJlc29sdmVSZWZlcmVuY2VzKGRvY3VtZW50LCByZXNwb25zZVNjaGVtYSkgYXMgSlNPTlNjaGVtYTcsXG4gIH07XG59XG5cbmZ1bmN0aW9uIHN1Y2Nlc3NmdWxSZXNwb25zZVNjaGVtYShvcGVyYXRpb246IE9wZW5BcGlPcGVyYXRpb24pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGZvciAoY29uc3QgW3N0YXR1cywgcmVzcG9uc2VdIG9mIE9iamVjdC5lbnRyaWVzKG9wZXJhdGlvbi5yZXNwb25zZXMpKSB7XG4gICAgY29uc3Qgc2NoZW1hID0ganNvblNjaGVtYShyZXNwb25zZS5jb250ZW50KTtcbiAgICBpZiAoL14yKD86XFxkezJ9fFhYKSQvaS50ZXN0KHN0YXR1cykgJiYgc2NoZW1hKSByZXR1cm4gc2NoZW1hO1xuICB9XG4gIHJldHVybiB7fTtcbn1cblxuZnVuY3Rpb24ganNvblNjaGVtYShcbiAgY29udGVudDogUmVjb3JkPHN0cmluZywgei5pbmZlcjx0eXBlb2YgT3BlbkFwaU1lZGlhVHlwZVNjaGVtYT4+IHwgdW5kZWZpbmVkLFxuKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWQge1xuICBjb25zdCBtZWRpYVR5cGUgPSBPYmplY3QuZW50cmllcyhjb250ZW50ID8/IHt9KS5maW5kKChbdHlwZV0pID0+XG4gICAgL15hcHBsaWNhdGlvblxcLyg/OlteKztdK1xcKyk/anNvbig/Ojt8JCkvaS50ZXN0KHR5cGUpLFxuICApO1xuICByZXR1cm4gbWVkaWFUeXBlPy5bMV0uc2NoZW1hO1xufVxuXG5mdW5jdGlvbiBvcGVyYXRpb25VcmwoZG9jdW1lbnQ6IE9wZW5BcGlEb2N1bWVudCwgc291cmNlVXJsOiBVUkwgfCB1bmRlZmluZWQsIHBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHNlcnZlciA9IGRvY3VtZW50LnNlcnZlcnM/LlswXT8udXJsO1xuICBpZiAoc2VydmVyKSB7XG4gICAgY29uc3QgYmFzZSA9IHNvdXJjZVVybCA/IHVybEJ1aWxkZXIobmV3IFVSTChzZXJ2ZXIsIHNvdXJjZVVybCkpIDogaHR0cFVybChzZXJ2ZXIpO1xuICAgIHJldHVybiBiYXNlXG4gICAgICA/IGJhc2Uud2l0aFBhdGhBcHBlbmQocGF0aCkudG9TdHJpbmcoKVxuICAgICAgOiBgJHtzZXJ2ZXIucmVwbGFjZSgvXFwvJC8sIFwiXCIpfS8ke3BhdGgucmVwbGFjZSgvXlxcLy8sIFwiXCIpfWA7XG4gIH1cbiAgcmV0dXJuIHNvdXJjZVVybCA/ICh1cmxCdWlsZGVyKHNvdXJjZVVybCk/LndpdGhQYXRoUmVwbGFjZShwYXRoKS50b1N0cmluZygpID8/IHBhdGgpIDogcGF0aDtcbn1cblxuZnVuY3Rpb24gcmVzb2x2ZVJlZmVyZW5jZXMoXG4gIGRvY3VtZW50OiBPcGVuQXBpRG9jdW1lbnQsXG4gIHZhbHVlOiB1bmtub3duLFxuICBhY3RpdmU6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KCksXG4pOiB1bmtub3duIHtcbiAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSByZXR1cm4gdmFsdWUubWFwKChpdGVtKSA9PiByZXNvbHZlUmVmZXJlbmNlcyhkb2N1bWVudCwgaXRlbSwgYWN0aXZlKSk7XG4gIGlmICghb2JqZWN0LmlzUmVjb3JkKHZhbHVlKSkgcmV0dXJuIHZhbHVlO1xuICBjb25zdCByZWZlcmVuY2UgPSB0eXBlb2YgdmFsdWUuJHJlZiA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLiRyZWYgOiB1bmRlZmluZWQ7XG4gIGlmIChyZWZlcmVuY2UpIHtcbiAgICBpZiAoYWN0aXZlLmhhcyhyZWZlcmVuY2UpKSB0aHJvdyBuZXcgRXJyb3IoYEN5Y2xpYyBPcGVuQVBJIHJlZmVyZW5jZTogJHtyZWZlcmVuY2V9YCk7XG4gICAgY29uc3QgdGFyZ2V0ID0gcmVmZXJlbmNlVGFyZ2V0KGRvY3VtZW50LCByZWZlcmVuY2UpO1xuICAgIGNvbnN0IG5leHQgPSBuZXcgU2V0KGFjdGl2ZSkuYWRkKHJlZmVyZW5jZSk7XG4gICAgY29uc3QgeyAkcmVmOiBfaWdub3JlZCwgLi4uc2libGluZ3MgfSA9IHZhbHVlO1xuICAgIHJldHVybiB7XG4gICAgICAuLi4ocmVzb2x2ZVJlZmVyZW5jZXMoZG9jdW1lbnQsIHRhcmdldCwgbmV4dCkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLFxuICAgICAgLi4uKHJlc29sdmVSZWZlcmVuY2VzKGRvY3VtZW50LCBzaWJsaW5ncywgbmV4dCkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLFxuICAgIH07XG4gIH1cbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBPYmplY3QuZW50cmllcyh2YWx1ZSkubWFwKChba2V5LCBjaGlsZF0pID0+IFtrZXksIHJlc29sdmVSZWZlcmVuY2VzKGRvY3VtZW50LCBjaGlsZCwgYWN0aXZlKV0pLFxuICApO1xufVxuXG5mdW5jdGlvbiByZWZlcmVuY2VUYXJnZXQoZG9jdW1lbnQ6IE9wZW5BcGlEb2N1bWVudCwgcmVmZXJlbmNlOiBzdHJpbmcpOiB1bmtub3duIHtcbiAgaWYgKCFyZWZlcmVuY2Uuc3RhcnRzV2l0aChcIiMvXCIpKSB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIE9wZW5BUEkgcmVmZXJlbmNlOiAke3JlZmVyZW5jZX1gKTtcbiAgbGV0IGN1cnJlbnQ6IHVua25vd24gPSBkb2N1bWVudDtcbiAgZm9yIChjb25zdCByYXdTZWdtZW50IG9mIHJlZmVyZW5jZS5zbGljZSgyKS5zcGxpdChcIi9cIikpIHtcbiAgICBjb25zdCBzZWdtZW50ID0gcmF3U2VnbWVudC5yZXBsYWNlQWxsKFwifjFcIiwgXCIvXCIpLnJlcGxhY2VBbGwoXCJ+MFwiLCBcIn5cIik7XG4gICAgaWYgKCFvYmplY3QuaXNSZWNvcmQoY3VycmVudCkgfHwgIShzZWdtZW50IGluIGN1cnJlbnQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVucmVzb2x2ZWQgT3BlbkFQSSByZWZlcmVuY2U6ICR7cmVmZXJlbmNlfWApO1xuICAgIH1cbiAgICBjdXJyZW50ID0gY3VycmVudFtzZWdtZW50XTtcbiAgfVxuICByZXR1cm4gY3VycmVudDtcbn1cblxuaWYgKGltcG9ydC5tZXRhLm1haW4pIHtcbiAgY29uc3Qgc291cmNlID0gcHJvY2Vzcy5hcmd2WzJdO1xuICBpZiAoIXNvdXJjZSkgdGhyb3cgbmV3IEVycm9yKFwiVXNhZ2U6IGJ1biBvcGVuYXBpLXRvb2wudHMgPG9wZW5hcGktdXJsLW9yLWZpbGU+XCIpO1xuICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShhd2FpdCBvcGVuQXBpVG9vbHMoc291cmNlKSwgbnVsbCwgMikpO1xufVxuIl19
|