From 9d4acadfbeeeed6fb5662e04bd6839dd48e0ac93 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Fri, 4 Sep 2026 13:11:19 -0700 Subject: [PATCH 01/10] fix(mcp): fail unsuccessful tool discovery Signed-off-by: Apurv Kumaria --- .github/workflows/managed-images.yaml | 16 ++- ci/e2e-assertion-budget.json | 14 +- docs/manage-sandboxes/manage-mcp-servers.mdx | 10 +- .../actions/sandbox/mcp-bridge-contracts.ts | 32 ++++- src/lib/actions/sandbox/mcp-bridge-render.ts | 7 +- .../mcp-bridge-status-resolution.test.ts | 98 +++++++++++-- src/lib/actions/sandbox/mcp-bridge-status.ts | 23 ++- .../sandbox/mcp-bridge-tool-discovery.test.ts | 97 +++++++++++-- .../sandbox/mcp-bridge-tool-discovery.ts | 135 +++++++++++++++--- src/lib/actions/sandbox/mcp-bridge.ts | 3 +- .../mcp-tool-discovery-runtime.test.ts | 93 ++++++++++++ test/e2e/live/mcp-bridge-tool-discovery.ts | 123 ++++++++++++++-- test/e2e/live/mcp-bridge.test.ts | 17 ++- .../support/mcp-bridge-tool-discovery.test.ts | 101 +++++++++++++ ...managed-image-publication-workflow.test.ts | 10 +- .../mcp-tool-discovery-image-contract.test.ts | 6 +- .../managed-bootstrap-image-contract.ts | 10 +- .../mcp-tool-discovery.ts | 4 + .../mcp-tool-discovery.bundle | 4 +- .../tool-discovery-core.ts | 101 ++++++++++--- 20 files changed, 788 insertions(+), 116 deletions(-) diff --git a/.github/workflows/managed-images.yaml b/.github/workflows/managed-images.yaml index c681a1cd388..c72cb1234da 100644 --- a/.github/workflows/managed-images.yaml +++ b/.github/workflows/managed-images.yaml @@ -352,9 +352,11 @@ jobs: fi if ! node -e ' const expected = { - protocol: 1, + protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }; let result; try { @@ -370,7 +372,9 @@ jobs: !record || record.protocol !== expected.protocol || record.ok !== expected.ok || - record.detail !== expected.detail + record.detail !== expected.detail || + record.failedStage !== expected.failedStage || + record.failureClass !== expected.failureClass ) { process.exit(1); } @@ -740,9 +744,11 @@ jobs: fi if ! node -e ' const expected = { - protocol: 1, + protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }; let result; try { @@ -758,7 +764,9 @@ jobs: !record || record.protocol !== expected.protocol || record.ok !== expected.ok || - record.detail !== expected.detail + record.detail !== expected.detail || + record.failedStage !== expected.failedStage || + record.failureClass !== expected.failureClass ) { process.exit(1); } diff --git a/ci/e2e-assertion-budget.json b/ci/e2e-assertion-budget.json index f6ca798a930..aa07046ea2e 100644 --- a/ci/e2e-assertion-budget.json +++ b/ci/e2e-assertion-budget.json @@ -15,14 +15,14 @@ "testFileCount": 86, "liveFileCount": 222, "direct": { - "expectCalls": 1888, - "matcherAssertions": 1857, + "expectCalls": 1887, + "matcherAssertions": 1856, "nodeAssertions": 100, "namedAssertionHelpers": 628, "failCalls": 8, "throwGuards": 87, "objectFieldAssertions": 245, - "assertionPoints": 2925, + "assertionPoints": 2924, "generatedProbeBlocks": 136, "generatedProbeConditions": 354 }, @@ -32,9 +32,9 @@ "nodeAssertions": 119, "namedAssertionHelpers": 931, "failCalls": 38, - "throwGuards": 646, - "objectFieldAssertions": 348, - "assertionPoints": 4426, + "throwGuards": 648, + "objectFieldAssertions": 344, + "assertionPoints": 4424, "generatedProbeBlocks": 290, "generatedProbeConditions": 980 }, @@ -88,7 +88,7 @@ "test/e2e/live/managed-image-activation-e2e.test.ts": [0,0,16,35,0], "test/e2e/live/managed-image-multiarch-startup.test.ts": [2,2,2,8,0], "test/e2e/live/managed-image-protected-runtime.test.ts": [0,0,42,79,15], - "test/e2e/live/mcp-bridge.test.ts": [50,170,203,511,46], + "test/e2e/live/mcp-bridge.test.ts": [49,169,203,509,46], "test/e2e/live/messaging-compatible-endpoint.test.ts": [13,19,13,28,2], "test/e2e/live/messaging-providers.test.ts": [0,11,6,51,12], "test/e2e/live/model-router-provider-routed-inference.test.ts": [4,4,4,5,0], diff --git a/docs/manage-sandboxes/manage-mcp-servers.mdx b/docs/manage-sandboxes/manage-mcp-servers.mdx index 9ea4ab8c406..2c6bbe68564 100644 --- a/docs/manage-sandboxes/manage-mcp-servers.mdx +++ b/docs/manage-sandboxes/manage-mcp-servers.mdx @@ -62,7 +62,8 @@ JSON output adds a per-server `toolDiscovery` object without changing the existi "ok": true, "count": 2, "tools": ["createJiraIssue", "getJiraIssue"], - "truncated": false + "truncated": false, + "commandStatus": 0 } } ``` @@ -71,7 +72,12 @@ Discovery is opt-in and sends authenticated network traffic to the configured en An older sandbox image that does not contain the shared discovery client reports that a rebuild is required. Run `$$nemoclaw my-sandbox rebuild` and retry the command. -A discovery failure sets `toolDiscovery.ok` to `false` and leaves the ordinary provider, policy, environment, and adapter status available. Bounded partial results set `truncated` to `true` and include a redacted `detail` value. +A discovery failure sets `toolDiscovery.ok` to `false` and makes the command exit nonzero. +The command still returns the ordinary provider, policy, environment, and adapter status. +The `commandStatus` field records the sandbox discovery runtime's exit status, or `null` when the runtime did not start. +A failed result includes `failedStage`, `failureClass`, and a redacted `detail` value. +Bounded partial results set `truncated` to `true`. +Correct the reported precondition, runtime, connection, authentication, protocol, or tool-operation failure, and then rerun the same command. ## Verify Credential Resolution diff --git a/src/lib/actions/sandbox/mcp-bridge-contracts.ts b/src/lib/actions/sandbox/mcp-bridge-contracts.ts index e17775bf0e3..dc267b0d015 100644 --- a/src/lib/actions/sandbox/mcp-bridge-contracts.ts +++ b/src/lib/actions/sandbox/mcp-bridge-contracts.ts @@ -30,6 +30,30 @@ export interface ParsedMcpAddArgs { export interface McpBridgeAddOptions extends ParsedMcpAddArgs {} +export type McpBridgeToolDiscoveryFailedStage = + | "preflight" + | "runtime" + | "initialization" + | "tool-discovery"; +export type McpBridgeToolDiscoveryFailureClass = + | "precondition" + | "runtime" + | "connection" + | "authentication" + | "protocol" + | "tool-operation"; + +export interface McpBridgeToolDiscoveryResult { + ok: boolean; + count: number; + tools: string[]; + truncated: boolean; + commandStatus: number | null; + detail?: string; + failedStage?: McpBridgeToolDiscoveryFailedStage; + failureClass?: McpBridgeToolDiscoveryFailureClass; +} + export interface McpBridgeStatus { server: string; agent: string; @@ -82,13 +106,7 @@ export interface McpBridgeStatus { detail?: string; }; /** Names advertised by the MCP endpoint when live discovery is requested. */ - toolDiscovery?: { - ok: boolean; - count: number; - tools: string[]; - truncated: boolean; - detail?: string; - }; + toolDiscovery?: McpBridgeToolDiscoveryResult; addState?: "prepared" | "preflighted"; addedAt?: string; updatedAt?: string; diff --git a/src/lib/actions/sandbox/mcp-bridge-render.ts b/src/lib/actions/sandbox/mcp-bridge-render.ts index ee672e21457..07362ae8eb3 100644 --- a/src/lib/actions/sandbox/mcp-bridge-render.ts +++ b/src/lib/actions/sandbox/mcp-bridge-render.ts @@ -96,9 +96,14 @@ export function renderMcpBridgeStatus( } const discovery = status.toolDiscovery; if (discovery) { + const failureContext = discovery.ok + ? "" + : ` [${discovery.failureClass ?? "runtime"}, ${discovery.failedStage ?? "runtime"}, runtime exit ${discovery.commandStatus ?? "unavailable"}]`; console.log( ` tool discovery: ${ - discovery.ok ? "successful" : `FAILED${discovery.detail ? ` (${discovery.detail})` : ""}` + discovery.ok + ? "successful" + : `FAILED${failureContext}${discovery.detail ? ` (${discovery.detail})` : ""}` }`, ); console.log( diff --git a/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts b/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts index f7b434a2b42..daee5c5ad37 100644 --- a/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts +++ b/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts @@ -89,6 +89,14 @@ policies.getPresetContentGatewayState = () => activePolicyState; const executedSandboxCommands = []; let providerCredentialObservation = "v11"; let credentialObservationCount = 0; +let toolDiscoveryStatus = 0; +let toolDiscoveryResult = { + protocol: 2, + ok: true, + count: 2, + tools: ["alpha", "zeta"], + truncated: false, +}; processRecovery.executeSandboxExecCommand = () => { credentialObservationCount += 1; return { @@ -119,14 +127,8 @@ processRecovery.executeSandboxCommand = (sandboxName, command) => { const resultMarker = command.match(/__NEMOCLAW_SANDBOX_EXEC_STARTED___[0-9a-f]{32}/)?.[0]; if (!resultMarker) throw new Error("tool discovery result marker missing"); return { - status: 0, - stdout: resultMarker + "\n" + JSON.stringify({ - protocol: 1, - ok: true, - count: 2, - tools: ["alpha", "zeta"], - truncated: false, - }), + status: toolDiscoveryStatus, + stdout: resultMarker + "\n" + JSON.stringify(toolDiscoveryResult), stderr: "", }; } @@ -793,7 +795,13 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 const payload = JSON.parse(stdout) as { status: { provider: { credentialResolution?: unknown }; - toolDiscovery: { ok: boolean; count: number; tools: string[]; truncated: boolean }; + toolDiscovery: { + ok: boolean; + count: number; + tools: string[]; + truncated: boolean; + commandStatus: number | null; + }; }; probed: boolean; discovered: boolean; @@ -806,9 +814,69 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 2, tools: ["alpha", "zeta"], truncated: false, + commandStatus: 0, }); }); + it("exits nonzero when a zero-exit runtime reports denied authentication (#10944)", () => { + const home = createTempHome("nemoclaw-mcp-tools-auth-failure-"); + const { stdout } = runHarness( + home, + String.raw` + toolDiscoveryResult = { + protocol: 2, + ok: false, + count: 0, + tools: [], + truncated: false, + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + failedStage: "initialization", + failureClass: "authentication", + }; + await bridge.dispatchMcpBridgeCommand("alpha", ["status", "github", "--tools", "--json"]); + const observedExitCode = process.exitCode ?? 0; + process.exitCode = 0; + writeHarnessResult(JSON.stringify({ + observedExitCode, + status: JSON.parse(logLines.join("\n")), + })); +`, + ); + const payload = JSON.parse(stdout) as { + observedExitCode: number; + status: { toolDiscovery: Record }; + }; + expect(payload.observedExitCode).toBe(1); + expect(payload.status.toolDiscovery).toEqual({ + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus: 0, + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + failedStage: "initialization", + failureClass: "authentication", + }); + }); + + it("does not accept a successful payload from a nonzero runtime (#10944)", () => { + const home = createTempHome("nemoclaw-mcp-tools-runtime-failure-"); + const { stdout } = runHarness( + home, + String.raw` + toolDiscoveryStatus = 7; + await bridge.dispatchMcpBridgeCommand("alpha", ["status", "github", "--tools"]); + const observedExitCode = process.exitCode ?? 0; + process.exitCode = 0; + writeHarnessResult(JSON.stringify({ observedExitCode, rendered: logLines })); +`, + ); + const payload = JSON.parse(stdout) as { observedExitCode: number; rendered: string[] }; + expect(payload.observedExitCode).toBe(1); + expect(payload.rendered.join("\n")).toContain("runtime exit 7"); + expect(payload.rendered.join("\n")).toContain("FAILED"); + }); + it("skips authenticated discovery until provider readiness is verified (#6901)", () => { const home = createTempHome("nemoclaw-mcp-tools-provider-readiness-"); const { stdout } = runHarness( @@ -870,7 +938,10 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 0, tools: [], truncated: false, + commandStatus: null, detail: "tool discovery skipped: the credential provider is not attached to the sandbox", + failedStage: "preflight", + failureClass: "precondition", }, discoveryCommands: 0, }, @@ -881,7 +952,10 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 0, tools: [], truncated: false, + commandStatus: null, detail: "tool discovery skipped: provider attachment could not be inspected", + failedStage: "preflight", + failureClass: "precondition", }, discoveryCommands: 0, }, @@ -892,7 +966,10 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 0, tools: [], truncated: false, + commandStatus: null, detail: "tool discovery skipped: provider attachment could not be inspected", + failedStage: "preflight", + failureClass: "precondition", }, discoveryCommands: 0, }, @@ -903,8 +980,11 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 0, tools: [], truncated: false, + commandStatus: null, detail: "tool discovery skipped: the OpenShell provider is absent or does not match the recorded credential binding", + failedStage: "preflight", + failureClass: "precondition", }, discoveryCommands: 0, }, diff --git a/src/lib/actions/sandbox/mcp-bridge-status.ts b/src/lib/actions/sandbox/mcp-bridge-status.ts index 802c005da5d..db59a497cb8 100644 --- a/src/lib/actions/sandbox/mcp-bridge-status.ts +++ b/src/lib/actions/sandbox/mcp-bridge-status.ts @@ -42,7 +42,10 @@ import { getSandboxAgent, getSandboxOrThrow, } from "./mcp-bridge-state"; -import { discoverMcpTools } from "./mcp-bridge-tool-discovery"; +import { + discoverMcpTools, + mcpToolDiscoveryPreconditionFailure, +} from "./mcp-bridge-tool-discovery"; import { inspectMcpRecordedTargetPins, type McpBridgeRecordedPinStatus, @@ -235,11 +238,9 @@ export async function statusMcpBridge( ...(options.discoverTools ? { toolDiscovery: { - ok: false, - count: 0, - tools: [], - truncated: false, - detail: "tool discovery skipped: MCP server is not registered", + ...mcpToolDiscoveryPreconditionFailure( + "tool discovery skipped: MCP server is not registered", + ), }, } : {}), @@ -416,13 +417,9 @@ export async function statusMcpBridge( const toolDiscovery = options.discoverTools && entry ? unsafeCredentialMayBeAttached - ? { - ok: false, - count: 0, - tools: [], - truncated: false, - detail: `tool discovery skipped: ${UNSUPPORTED_ATTACHED_CREDENTIAL_DETAIL}`, - } + ? mcpToolDiscoveryPreconditionFailure( + `tool discovery skipped: ${UNSUPPORTED_ATTACHED_CREDENTIAL_DETAIL}`, + ) : discoverMcpTools( sandboxName, entry, diff --git a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts index 524dbb82625..c028050caeb 100644 --- a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts +++ b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts @@ -12,6 +12,7 @@ import type { McpBridgeEntry } from "../../state/registry"; import { buildMcpToolDiscoveryCommand, classifyMcpToolDiscoveryResult, + MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, MCP_TOOL_DISCOVERY_RUNTIME_PATH, toolDiscoveryReadinessSkipDetail, } from "./mcp-bridge-tool-discovery"; @@ -107,7 +108,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { expect( classifyMcpToolDiscoveryResult( framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 2, tools: ["alpha", "zeta"], @@ -121,6 +122,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { count: 2, tools: ["alpha", "zeta"], truncated: false, + commandStatus: 0, }); }); @@ -133,7 +135,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { expect( classifyMcpToolDiscoveryResult( framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: tools.length, tools, @@ -147,6 +149,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { count: tools.length, tools, truncated: false, + commandStatus: 0, }); }); @@ -166,55 +169,124 @@ describe("MCP tool discovery host boundary (#6901)", () => { count: 0, tools: [], truncated: false, + commandStatus: 0, detail: "tool discovery returned an oversized result", + failedStage: "runtime", + failureClass: "runtime", + }); + }); + + it("preserves a structured authentication failure when the runtime exits zero (#10944)", () => { + expect( + classifyMcpToolDiscoveryResult( + framedResult({ + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, + ok: false, + count: 0, + tools: [], + truncated: false, + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + failedStage: "initialization", + failureClass: "authentication", + }), + entry, + marker, + ), + ).toEqual({ + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus: 0, + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + failedStage: "initialization", + failureClass: "authentication", + }); + }); + + it("preserves a structured missing-runtime failure when the wrapper exits zero (#10944)", () => { + expect( + classifyMcpToolDiscoveryResult( + framedResult({ + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, + ok: false, + count: 0, + tools: [], + truncated: false, + detail: "sandbox image does not include the MCP tool discovery runtime; rebuild the sandbox", + failedStage: "runtime", + failureClass: "runtime", + }), + entry, + marker, + ), + ).toEqual({ + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus: 0, + detail: "sandbox image does not include the MCP tool discovery runtime; rebuild the sandbox", + failedStage: "runtime", + failureClass: "runtime", }); }); it.each([ - framedResult({ protocol: 1, ok: true, count: 1, tools: ["bad\nname"], truncated: false }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, + ok: true, + count: 1, + tools: ["bad\nname"], + truncated: false, + }), + framedResult({ + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 1, tools: ["bad\ud800name"], truncated: false, }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 1, tools: ["safe\u202eevil"], truncated: false, }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 1, tools: ["safe\u2066evil"], truncated: false, }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 1, tools: ["safe\u2028evil"], truncated: false, }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 2, tools: ["same", "same"], truncated: false, }), framedResult({ - protocol: 1, + protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL, ok: true, count: 2, tools: ["zeta", "alpha"], truncated: false, }), - { status: 0, stdout: JSON.stringify({ protocol: 1 }), stderr: "" }, + { + status: 0, + stdout: JSON.stringify({ protocol: MCP_TOOL_DISCOVERY_RESULT_PROTOCOL }), + stderr: "", + }, ])("fails closed on malformed, duplicate, unsorted, or unframed results [case %#]", (result) => { expect(classifyMcpToolDiscoveryResult(result, entry, marker)).toMatchObject({ ok: false, @@ -237,6 +309,11 @@ describe("MCP tool discovery host boundary (#6901)", () => { expect(result.detail).toContain("rebuild the sandbox"); expect(result.detail).toContain("exit 1"); expect(result.detail).toContain("proxy startup refused"); + expect(result).toMatchObject({ + commandStatus: 1, + failedStage: "runtime", + failureClass: "runtime", + }); expect(JSON.stringify(result)).not.toContain("should-not-leak"); }); diff --git a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.ts b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.ts index 6dd78ba149e..fe4d4e7d9c2 100644 --- a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.ts +++ b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.ts @@ -4,7 +4,11 @@ import type { AgentMcpAdapter } from "../../agent/defs"; import { shellQuote } from "../../core/shell-quote"; import type { McpBridgeEntry } from "../../state/registry"; -import type { McpBridgeStatus } from "./mcp-bridge-contracts"; +import type { + McpBridgeStatus, + McpBridgeToolDiscoveryFailedStage, + McpBridgeToolDiscoveryFailureClass, +} from "./mcp-bridge-contracts"; import { redactBridgeSecretsForDisplay } from "./mcp-bridge-output"; import type { McpProviderInspectionRuntimeSelection } from "./mcp-bridge-provider-inspection"; import type { CredentialResolutionProbeReadiness } from "./mcp-bridge-resolution-readiness"; @@ -23,7 +27,7 @@ import { buildTrustedProxyEnvSourceShell } from "./trusted-proxy-env"; export const MCP_TOOL_DISCOVERY_RUNTIME_PATH = "/usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs"; -export const MCP_TOOL_DISCOVERY_RESULT_PROTOCOL = 1; +export const MCP_TOOL_DISCOVERY_RESULT_PROTOCOL = 2; export const MCP_TOOL_DISCOVERY_MAX_TOOLS = 500; export const MCP_TOOL_DISCOVERY_MAX_NAME_BYTES = 256; const MCP_TOOL_DISCOVERY_MAX_DETAIL_BYTES = 512; @@ -40,8 +44,28 @@ export interface McpToolDiscoveryCommand { resultMarker: string; } -function failure(detail: string): NonNullable { - return { ok: false, count: 0, tools: [], truncated: false, detail }; +function failure( + detail: string, + failedStage: McpBridgeToolDiscoveryFailedStage, + failureClass: McpBridgeToolDiscoveryFailureClass, + commandStatus: number | null, +): NonNullable { + return { + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus, + detail, + failedStage, + failureClass, + }; +} + +export function mcpToolDiscoveryPreconditionFailure( + detail: string, +): NonNullable { + return failure(detail, "preflight", "precondition", null); } export function toolDiscoveryReadinessSkipDetail( @@ -91,6 +115,8 @@ export function buildMcpToolDiscoveryCommand( tools: [], truncated: false, detail: "sandbox image does not include the MCP tool discovery runtime; rebuild the sandbox", + failedStage: "runtime", + failureClass: "runtime", }); const runtimeCommand = wrapMcpRuntimeCommand(adapter, [ "/usr/local/bin/node", @@ -135,7 +161,7 @@ export function classifyMcpToolDiscoveryResult( entry: Pick, resultMarker: string, ): NonNullable { - if (result === null) return failure("sandbox unreachable"); + if (result === null) return failure("sandbox unreachable", "runtime", "runtime", null); if (result.status !== 0) { const safeFailure = `${result.stderr}\n${result.stdout}` .split(/\r?\n/u) @@ -145,29 +171,41 @@ export function classifyMcpToolDiscoveryResult( .find((line) => safeString(line, MCP_TOOL_DISCOVERY_MAX_DETAIL_BYTES)); return failure( `MCP tool discovery runtime failed to start (exit ${String(result.status)})${safeFailure ? `: ${safeFailure}` : ""}; rebuild the sandbox if the image predates this diagnostic`, + "runtime", + "runtime", + result.status, ); } const output = extractSandboxExecCommandStdoutFromStreams( { stdout: result.stdout, stderr: result.stderr }, resultMarker, ); - if (output === null) return failure("tool discovery output missing trusted result frame"); + if (output === null) { + return failure("tool discovery output missing trusted result frame", "runtime", "runtime", 0); + } if (utf8Bytes(output) > MCP_TOOL_DISCOVERY_MAX_OUTPUT_BYTES) { - return failure("tool discovery returned an oversized result"); + return failure("tool discovery returned an oversized result", "runtime", "runtime", 0); } let parsed: unknown; try { parsed = JSON.parse(output); } catch { - return failure("tool discovery returned an invalid result"); + return failure("tool discovery returned an invalid result", "runtime", "runtime", 0); } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { - return failure("tool discovery returned an invalid result"); + return failure("tool discovery returned an invalid result", "runtime", "runtime", 0); } const value = parsed as Record; + if (value.protocol !== MCP_TOOL_DISCOVERY_RESULT_PROTOCOL) { + return failure( + "tool discovery runtime result is incompatible; rebuild the sandbox", + "runtime", + "runtime", + 0, + ); + } if ( - value.protocol !== MCP_TOOL_DISCOVERY_RESULT_PROTOCOL || typeof value.ok !== "boolean" || typeof value.count !== "number" || !Number.isSafeInteger(value.count) || @@ -177,7 +215,7 @@ export function classifyMcpToolDiscoveryResult( value.tools.length > MCP_TOOL_DISCOVERY_MAX_TOOLS || typeof value.truncated !== "boolean" ) { - return failure("tool discovery returned an invalid result"); + return failure("tool discovery returned an invalid result", "runtime", "runtime", 0); } const tools: string[] = []; @@ -188,37 +226,80 @@ export function classifyMcpToolDiscoveryResult( tool.length === 0 || seen.has(tool) ) { - return failure("tool discovery returned an invalid tool name"); + return failure("tool discovery returned an invalid tool name", "runtime", "runtime", 0); } seen.add(tool); tools.push(tool); } const sortedTools = [...tools].sort(compareNames); if (tools.some((tool, index) => tool !== sortedTools[index])) { - return failure("tool discovery returned a non-deterministic tool inventory"); + return failure( + "tool discovery returned a non-deterministic tool inventory", + "runtime", + "runtime", + 0, + ); } const detail = value.detail; if (detail !== undefined && !safeString(detail, MCP_TOOL_DISCOVERY_MAX_DETAIL_BYTES)) { - return failure("tool discovery returned an invalid detail"); + return failure("tool discovery returned an invalid detail", "runtime", "runtime", 0); } - if ((value.ok && value.truncated) || (!value.ok && !detail)) { - return failure("tool discovery returned an inconsistent result"); + const failedStage = value.failedStage; + const failureClass = value.failureClass; + const validFailedStage = + failedStage === "preflight" || + failedStage === "runtime" || + failedStage === "initialization" || + failedStage === "tool-discovery"; + const validFailureClass = + failureClass === "precondition" || + failureClass === "runtime" || + failureClass === "connection" || + failureClass === "authentication" || + failureClass === "protocol" || + failureClass === "tool-operation"; + if ( + (value.ok && + (value.truncated || + detail !== undefined || + failedStage !== undefined || + failureClass !== undefined)) || + (!value.ok && (!detail || !validFailedStage || !validFailureClass)) + ) { + return failure("tool discovery returned an inconsistent result", "runtime", "runtime", 0); } if (!value.ok && !value.truncated && tools.length > 0) { - return failure("tool discovery returned an inconsistent partial result"); + return failure( + "tool discovery returned an inconsistent partial result", + "runtime", + "runtime", + 0, + ); } const redactedDetail = detail ? redactBridgeSecretsForDisplay(detail, entry) : undefined; if (redactedDetail && utf8Bytes(redactedDetail) > MCP_TOOL_DISCOVERY_MAX_DETAIL_BYTES) { - return failure("tool discovery returned an oversized detail after redaction"); + return failure( + "tool discovery returned an oversized detail after redaction", + "runtime", + "runtime", + 0, + ); } return { ok: value.ok, count: tools.length, tools, truncated: value.truncated, + commandStatus: 0, ...(redactedDetail ? { detail: redactedDetail } : {}), + ...(!value.ok + ? { + failedStage: failedStage as McpBridgeToolDiscoveryFailedStage, + failureClass: failureClass as McpBridgeToolDiscoveryFailureClass, + } + : {}), }; } @@ -229,13 +310,23 @@ export function discoverMcpTools( readiness: McpToolDiscoveryReadiness, runtimeSelection: McpProviderInspectionRuntimeSelection, ): NonNullable { - if (!adapter) return failure("tool discovery skipped: MCP adapter is not declared"); - if (entry.addState) return failure("tool discovery skipped: add transaction is incomplete"); + if (!adapter) { + return mcpToolDiscoveryPreconditionFailure( + "tool discovery skipped: MCP adapter is not declared", + ); + } + if (entry.addState) { + return mcpToolDiscoveryPreconditionFailure( + "tool discovery skipped: add transaction is incomplete", + ); + } const readinessSkipDetail = toolDiscoveryReadinessSkipDetail(readiness); - if (readinessSkipDetail) return failure(readinessSkipDetail); + if (readinessSkipDetail) return mcpToolDiscoveryPreconditionFailure(readinessSkipDetail); const discoveryCommand = buildMcpToolDiscoveryCommand(entry, adapter); if (!discoveryCommand) { - return failure("tool discovery skipped: no valid managed endpoint is available"); + return mcpToolDiscoveryPreconditionFailure( + "tool discovery skipped: no valid managed endpoint is available", + ); } return classifyMcpToolDiscoveryResult( executeSandboxCommand(sandboxName, discoveryCommand.command, { runtimeSelection }), diff --git a/src/lib/actions/sandbox/mcp-bridge.ts b/src/lib/actions/sandbox/mcp-bridge.ts index ebea3223f6e..dc5f3a719e7 100644 --- a/src/lib/actions/sandbox/mcp-bridge.ts +++ b/src/lib/actions/sandbox/mcp-bridge.ts @@ -289,7 +289,7 @@ FLAGS --json Emit MCP server status as JSON --probe Request the wire-level credential-resolution probe for every server --no-probe Skip the probe (it defaults on only when a single server is named) - --tools Discover names advertised by one named MCP server`); + --tools Discover names advertised by one named MCP server; exit nonzero on failure`); return; case "restart": console.log(`USAGE @@ -375,6 +375,7 @@ export async function dispatchMcpBridgeCommand( )}\n`, ); } else renderMcpBridgeStatus(sandboxName, statuses, agent); + if (tools && statuses[0]?.toolDiscovery?.ok !== true) process.exitCode = 1; return; } case "restart": { diff --git a/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts b/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts index fbb9ff42fcc..102511c1f06 100644 --- a/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts +++ b/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts @@ -9,6 +9,7 @@ import { enumerateMcpToolNames, MCP_TOOL_DISCOVERY_LIMITS, MCP_TOOL_DISCOVERY_PROTOCOL, + mcpToolDiscoveryFailure, normalizeMcpToolPage, parseMcpToolDiscoveryArguments, runMcpToolDiscoverySession, @@ -174,6 +175,8 @@ describe("shared MCP tool discovery runtime", () => { tools: [], truncated: true, detail: `tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`, + failedStage: "tool-discovery", + failureClass: "tool-operation", }); }); @@ -239,12 +242,43 @@ describe("shared MCP tool discovery runtime", () => { tools: [], truncated: false, detail: "MCP tool discovery request failed", + failedStage: "tool-discovery", + failureClass: "tool-operation", }); expect(JSON.stringify(result)).not.toContain("Bearer"); expect(JSON.stringify(result)).not.toContain("terminate failure"); expect(JSON.stringify(result)).not.toContain("close failure"); }); + it("closes the client after authentication fails during initialization (#10944)", async () => { + const terminateSession = vi.fn(); + const close = vi.fn(async () => undefined); + const publishResult = vi.fn(); + + await runMcpToolDiscoverySession({ + connect: vi.fn(async () => { + throw new ToolDiscoveryRuntimeError("http-error", 401); + }), + loadPage: vi.fn(), + hasSession: () => false, + terminateSession, + close, + publishResult, + }); + + expect(terminateSession).not.toHaveBeenCalled(); + expect(close).toHaveBeenCalledOnce(); + expect(publishResult).toHaveBeenCalledWith({ + ok: false, + count: 0, + tools: [], + truncated: false, + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + failedStage: "initialization", + failureClass: "authentication", + }); + }); + it("rejects redirects, HTTP failures, and declared oversized responses before reading bodies", async () => { const deadline = AbortSignal.timeout(1_000); const redirectFetch = createBoundedMcpFetch( @@ -264,6 +298,14 @@ describe("shared MCP tool discovery runtime", () => { httpStatus: 401, }); + const connectionFailureFetch = createBoundedMcpFetch( + async () => Promise.reject(new TypeError("untrusted DNS, TLS, or refusal detail")), + deadline, + ); + await expect(connectionFailureFetch("https://example.test/mcp")).rejects.toMatchObject({ + code: "connection", + }); + const oversizedFetch = createBoundedMcpFetch( async () => new Response("small", { @@ -303,6 +345,23 @@ describe("shared MCP tool discovery runtime", () => { expect(sourceCancel).toHaveBeenCalledOnce(); }); + it("classifies a response-body transport interruption without exposing its error (#10944)", async () => { + const body = new ReadableStream({ + start(controller) { + controller.error(new TypeError("untrusted connection interruption")); + }, + }); + const boundedFetch = createBoundedMcpFetch( + async () => new Response(body), + AbortSignal.timeout(1_000), + ); + + const response = await boundedFetch("https://example.test/mcp"); + const error = await response.arrayBuffer().catch((caught: unknown) => caught); + expect(error).toMatchObject({ code: "connection" }); + expect(JSON.stringify(error)).not.toContain("untrusted connection interruption"); + }); + it.each(["deadline", "request"] as const)( "bounds both total-deadline and per-request aborts with a credential-safe timeout [case %#]", async (abortSource) => { @@ -329,6 +388,9 @@ describe("shared MCP tool discovery runtime", () => { ); it("maps failures to bounded details without echoing untrusted messages", () => { + expect(safeToolDiscoveryErrorDetail(new ToolDiscoveryRuntimeError("connection"))).toBe( + "MCP endpoint connection failed", + ); expect(safeToolDiscoveryErrorDetail(new ToolDiscoveryRuntimeError("redirect"))).toBe( "MCP endpoint redirect was rejected", ); @@ -345,6 +407,37 @@ describe("shared MCP tool discovery runtime", () => { ); }); + it.each([ + ["connection", new ToolDiscoveryRuntimeError("connection"), "initialization", "connection"], + [ + "authentication", + new ToolDiscoveryRuntimeError("http-error", 401), + "initialization", + "authentication", + ], + [ + "forbidden authentication", + new ToolDiscoveryRuntimeError("http-error", 403), + "initialization", + "authentication", + ], + ["protocol", new ToolDiscoveryRuntimeError("invalid-response"), "tool-discovery", "protocol"], + ["initialization protocol", new Error("untrusted parse failure"), "initialization", "protocol"], + [ + "tool operation", + new Error("untrusted operation failure"), + "tool-discovery", + "tool-operation", + ], + ] as const)( + "classifies %s failures without returning untrusted error text (#10944)", + (_label, error, failedStage, failureClass) => { + const result = mcpToolDiscoveryFailure(error, failedStage); + expect(result).toMatchObject({ ok: false, failedStage, failureClass }); + expect(JSON.stringify(result)).not.toContain("untrusted operation failure"); + }, + ); + it("keeps the host parser and image runtime on the same result limits", () => { expect(MCP_TOOL_DISCOVERY_RESULT_PROTOCOL).toBe(MCP_TOOL_DISCOVERY_PROTOCOL); expect(MCP_TOOL_DISCOVERY_MAX_TOOLS).toBe(MCP_TOOL_DISCOVERY_LIMITS.maxTools); diff --git a/test/e2e/live/mcp-bridge-tool-discovery.ts b/test/e2e/live/mcp-bridge-tool-discovery.ts index b3bf15fc6e2..b9b895f91ee 100644 --- a/test/e2e/live/mcp-bridge-tool-discovery.ts +++ b/test/e2e/live/mcp-bridge-tool-discovery.ts @@ -5,8 +5,9 @@ import { expect } from "vitest"; import type { ArtifactSink } from "../fixtures/artifacts.ts"; import { buildAvailabilityProbeEnv } from "../fixtures/availability-env.ts"; -import { assertExitZero } from "../fixtures/clients/command.ts"; +import { assertExitZero, resultText } from "../fixtures/clients/command.ts"; import type { HostCliClient } from "../fixtures/clients/host.ts"; +import { type SandboxClient, trustedSandboxShellScript } from "../fixtures/clients/sandbox.ts"; import type { TestProgress } from "../fixtures/progress.ts"; import type { FakeMcpHttpsServer, FakeMcpRequest } from "./mcp-bridge-servers.ts"; @@ -67,7 +68,10 @@ type McpToolDiscoveryStatusJson = { count: number; tools: string[]; truncated: boolean; + commandStatus: number | null; detail?: string; + failedStage?: string; + failureClass?: string; }; }; @@ -103,7 +107,14 @@ function buildMcpToolDiscoveryDiagnostics( count: status.toolDiscovery.count, tools: [...status.toolDiscovery.tools], truncated: status.toolDiscovery.truncated, + commandStatus: status.toolDiscovery.commandStatus, ...(status.toolDiscovery.detail !== undefined ? { detail: status.toolDiscovery.detail } : {}), + ...(status.toolDiscovery.failedStage !== undefined + ? { failedStage: status.toolDiscovery.failedStage } + : {}), + ...(status.toolDiscovery.failureClass !== undefined + ? { failureClass: status.toolDiscovery.failureClass } + : {}), }, requests: requests.map((request) => ({ httpMethod: request.method, @@ -424,6 +435,69 @@ export async function runHermesInitialMcpReadiness(operations: { await operations.runModelTurn(); } +export async function captureHermesMcpVerificationVersions( + host: HostCliClient, + sandbox: SandboxClient, + sandboxName: string, + artifacts: Pick, +): Promise { + const containerRuntime = process.env.NEMOCLAW_GATEWAY_RUNTIME; + if (containerRuntime !== "docker" && containerRuntime !== "podman") { + throw new Error("MCP verification container runtime is unavailable"); + } + const [nemoclawVersion, openshellVersion, hermesVersion, hostPlatform, sandboxPlatform] = + await Promise.all([ + host.nemoclaw(["--version"], { + artifactName: "hermes-mcp-nemoclaw-version", + env: buildAvailabilityProbeEnv(), + }), + host.command(host.openshellCommandPath, ["--version"], { + artifactName: "hermes-mcp-openshell-version", + env: buildAvailabilityProbeEnv(), + }), + sandbox.execShell(sandboxName, trustedSandboxShellScript("hermes --version"), { + artifactName: "hermes-mcp-hermes-version", + env: buildAvailabilityProbeEnv(), + }), + host.command( + "bash", + [ + "-lc", + "set -eu; . /etc/os-release; printf 'operating-system=%s\\n' \"$PRETTY_NAME\"; uname -a; \"$1\" version", + "mcp-verification-platform", + containerRuntime, + ], + { + artifactName: "hermes-mcp-host-platform", + env: buildAvailabilityProbeEnv(), + }, + ), + sandbox.execShell( + sandboxName, + trustedSandboxShellScript("cat /etc/os-release && uname -a"), + { + artifactName: "hermes-mcp-sandbox-platform", + env: buildAvailabilityProbeEnv(), + }, + ), + ]); + if ( + [nemoclawVersion, openshellVersion, hermesVersion, hostPlatform, sandboxPlatform].some( + (result) => result.exitCode !== 0, + ) + ) { + throw new Error("MCP verification version capture failed"); + } + await artifacts.writeJson("hermes-mcp-verification-versions.json", { + sourceRevision: process.env.NEMOCLAW_E2E_EXPECTED_SHA, + nemoclaw: resultText(nemoclawVersion).trim(), + openshell: resultText(openshellVersion).trim(), + hermes: resultText(hermesVersion).trim(), + hostPlatform: resultText(hostPlatform).trim(), + sandboxPlatform: resultText(sandboxPlatform).trim(), + }); +} + export async function assertAuthenticatedMcpToolDiscovery( host: HostCliClient, fakeMcp: FakeMcpHttpsServer, @@ -432,6 +506,7 @@ export async function assertAuthenticatedMcpToolDiscovery( sandboxName: string; artifactPrefix: string; credentialKey?: string; + deniedSecret?: string; hostSecret: string; progress: Pick; serverName?: string; @@ -477,19 +552,12 @@ export async function assertAuthenticatedMcpToolDiscovery( `${options.artifactPrefix}-mcp-tool-discovery-diagnostics.json`, buildMcpToolDiscoveryDiagnostics(statusJson, discoveryRequests, options.hostSecret), ); - expect(statusJson.provider.credentialResolution).toBeUndefined(); - expect(statusJson.toolDiscovery).toMatchObject({ - ok: true, - count: 2, - tools: ["fake_echo", "fake_status"], - truncated: false, - }); + expect(statusJson.toolDiscovery.ok).toBe(true); expect(status.stdout).not.toContain(options.hostSecret); const discoveryProtocolRequests = discoveryRequests.filter( (request) => (request.method === "POST" || request.method === "DELETE") && request.path === "/mcp", ); - expect(discoveryProtocolRequests.length).toBeGreaterThan(0); expect( discoveryProtocolRequests.every((request) => request.auth === `Bearer ${options.hostSecret}`), ).toBe(true); @@ -570,4 +638,41 @@ export async function assertAuthenticatedMcpToolDiscovery( // DELETE, so SDK session termination is intentionally best effort at this // boundary. Unit coverage pins that cleanup attempt; protected E2E proves the // negotiated metadata on every post-initialize JSON-RPC request. + if (!options.deniedSecret) return; + + const deniedRequestOffset = fakeMcp.requests.length; + fakeMcp.setSecret(options.deniedSecret); + try { + const result = await host.nemoclaw( + [options.sandboxName, "mcp", "status", serverName, "--tools", "--json"], + { + artifactName: `${options.artifactPrefix}-mcp-status-tools-denied-auth-json`, + env: { + ...buildAvailabilityProbeEnv(), + [credentialKey]: options.hostSecret, + }, + redactionValues: [options.hostSecret], + timeoutMs: 60_000, + }, + ); + expect(result.exitCode).not.toBe(0); + const deniedStatusJson = JSON.parse(result.stdout) as McpToolDiscoveryStatusJson; + const deniedRequests = fakeMcp.requests.slice(deniedRequestOffset); + await options.artifacts.writeJson( + `${options.artifactPrefix}-mcp-tool-discovery-denied-auth.json`, + buildMcpToolDiscoveryDiagnostics(deniedStatusJson, deniedRequests, options.hostSecret), + ); + expect(deniedStatusJson.toolDiscovery.failureClass).toBe("authentication"); + expect( + deniedRequests.some( + (request) => + request.method === "POST" && + request.path === "/mcp" && + request.rpcMethod === "initialize" && + request.responseStatus === 401, + ), + ).toBe(true); + } finally { + fakeMcp.setSecret(options.hostSecret); + } } diff --git a/test/e2e/live/mcp-bridge.test.ts b/test/e2e/live/mcp-bridge.test.ts index ad218ed19cf..379cac0ac63 100644 --- a/test/e2e/live/mcp-bridge.test.ts +++ b/test/e2e/live/mcp-bridge.test.ts @@ -82,6 +82,7 @@ import { assertAuthenticatedMcpDiscoveryWithOneRestart, assertAuthenticatedMcpRediscovery, assertAuthenticatedMcpToolDiscovery, + captureHermesMcpVerificationVersions, runHermesInitialMcpReadiness, } from "./mcp-bridge-tool-discovery.ts"; import { assertTrustedPrivateMcpRebindingDenied } from "./mcp-bridge-trusted-private.ts"; @@ -161,6 +162,7 @@ async function onboardAgent( expectExitZero(result, `onboard ${options.agent} sandbox for MCP bridge`); expectManagedImageQualificationReceipt(options.sandboxName, options.agent); } + async function assertSecretAbsentFromSandbox( sandbox: SandboxClient, sandboxName: string, @@ -863,7 +865,6 @@ test("mcp-bridge", { expectedSecret: HOST_SECRET, label: "mcporter authenticated MCP tool discovery", }); - expect(fakeMcp.requests.some((request) => request.auth === `Bearer ${HOST_SECRET}`)).toBe(true); expect(fakeMcp.requests.every((request) => !request.auth.includes("openshell:resolve:env"))).toBe( true, ); @@ -1159,14 +1160,22 @@ mcpBridgeShardTest("hermes")( ); }, }), - inspectToolStatus: () => - assertAuthenticatedMcpToolDiscovery(host, fakeMcp, { + inspectToolStatus: async () => { + await captureHermesMcpVerificationVersions( + host, + sandbox, + HERMES_SANDBOX_NAME, + artifacts, + ); + await assertAuthenticatedMcpToolDiscovery(host, fakeMcp, { artifacts, sandboxName: HERMES_SANDBOX_NAME, artifactPrefix: "hermes", + deniedSecret: ROTATED_HOST_SECRET, hostSecret: HOST_SECRET, progress, - }), + }); + }, prepareModelTurn: async () => { await assertBridgeInfrastructure(host, sandbox, { sandboxName: HERMES_SANDBOX_NAME, diff --git a/test/e2e/support/mcp-bridge-tool-discovery.test.ts b/test/e2e/support/mcp-bridge-tool-discovery.test.ts index 93585cbe331..213f632762d 100644 --- a/test/e2e/support/mcp-bridge-tool-discovery.test.ts +++ b/test/e2e/support/mcp-bridge-tool-discovery.test.ts @@ -284,6 +284,101 @@ describe("authenticated MCP rediscovery evidence", () => { }); describe("authenticated MCP tool discovery transport retry", () => { + it("records denied authentication after success and restores the fixture credential (#10944)", async () => { + const requests: FakeMcpRequest[] = []; + const setSecret = vi.fn(); + const fakeMcp = { requests, setSecret } as unknown as FakeMcpHttpsServer; + const provider = { + registryPresent: true, + gatewayPresent: true, + attached: true, + credentialReady: true, + }; + const policy = { registryPresent: true, gatewayPresent: true }; + const adapter = { registered: true }; + const host = { + nemoclaw: vi + .fn() + .mockImplementationOnce(async () => { + requests.push( + successfulInitialize(), + request("notifications/initialized"), + request("tools/list"), + request("tools/list"), + ); + return { + exitCode: 0, + stdout: JSON.stringify({ + provider, + policy, + adapter, + toolDiscovery: { + ok: true, + count: 2, + tools: ["fake_echo", "fake_status"], + truncated: false, + commandStatus: 0, + }, + }), + stderr: "", + }; + }) + .mockImplementationOnce(async () => { + requests.push( + request("initialize", { + sessionId: "", + protocolVersion: "", + responseStatus: 401, + responseHasResult: false, + }), + ); + return { + exitCode: 1, + stdout: JSON.stringify({ + provider, + policy, + adapter, + toolDiscovery: { + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus: 0, + failedStage: "initialization", + failureClass: "authentication", + detail: "MCP endpoint rejected tool discovery (HTTP 401)", + }, + }), + stderr: "", + }; + }), + } as unknown as Parameters[0]; + const artifacts = discoveryArtifacts(); + + await assertAuthenticatedMcpToolDiscovery(host, fakeMcp, { + artifacts, + sandboxName: "sandbox", + artifactPrefix: "hermes", + deniedSecret: "denied-secret", + hostSecret: EXPECTED_SECRET, + progress: { event: vi.fn() }, + }); + + expect(setSecret.mock.calls).toEqual([["denied-secret"], [EXPECTED_SECRET]]); + expect(artifacts.writeJson).toHaveBeenLastCalledWith( + "hermes-mcp-tool-discovery-denied-auth.json", + expect.objectContaining({ + toolDiscovery: expect.objectContaining({ + commandStatus: 0, + failedStage: "initialization", + failureClass: "authentication", + }), + }), + ); + expect(JSON.stringify(artifacts.writeJson.mock.calls)).not.toContain("denied-secret"); + expect(JSON.stringify(artifacts.writeJson.mock.calls)).not.toContain(EXPECTED_SECRET); + }); + it("writes redacted boundary diagnostics before a discovery failure (#8746)", async () => { const statusJson = { provider: { @@ -307,6 +402,9 @@ describe("authenticated MCP tool discovery transport retry", () => { count: 0, tools: [], truncated: false, + commandStatus: 0, + failedStage: "initialization" as const, + failureClass: "connection" as const, detail: "MCP tool discovery request failed", credential: STATUS_SECRET, }, @@ -353,6 +451,9 @@ describe("authenticated MCP tool discovery transport retry", () => { count: 0, tools: [], truncated: false, + commandStatus: 0, + failedStage: "initialization", + failureClass: "connection", detail: "MCP tool discovery request failed", }, requests: [ diff --git a/test/inference/managed/managed-image-publication-workflow.test.ts b/test/inference/managed/managed-image-publication-workflow.test.ts index c20211d8e1a..8c1c71a72bd 100644 --- a/test/inference/managed/managed-image-publication-workflow.test.ts +++ b/test/inference/managed/managed-image-publication-workflow.test.ts @@ -605,12 +605,14 @@ describe("complete managed-image publication workflow", () => { const bundleResult = spawnSync(process.execPath, [executableBundle], { encoding: "utf8" }); expect(bundleResult.status, bundleResult.stderr).toBe(0); expect(JSON.parse(bundleResult.stdout)).toMatchObject({ - protocol: 1, + protocol: 2, ok: false, count: 0, tools: [], truncated: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }); const acceptedContract = spawnSync(process.execPath, ["-e", contractValidator], { encoding: "utf8", @@ -618,9 +620,9 @@ describe("complete managed-image publication workflow", () => { }); expect(acceptedContract.status, acceptedContract.stderr).toBe(0); for (const rejectedOutput of [ - '{"protocol":1,"ok":false,"detail":"wrong"}\n', - '{"protocol":1,"ok":false,"detail":"tool discovery received invalid runtime arguments","extra":NaN}\n', - '\uFEFF{"protocol":1,"ok":false,"detail":"tool discovery received invalid runtime arguments"}\n', + '{"protocol":2,"ok":false,"detail":"wrong"}\n', + '{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition","extra":NaN}\n', + '\uFEFF{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}\n', ]) { const rejectedContract = spawnSync(process.execPath, ["-e", contractValidator], { encoding: "utf8", diff --git a/test/mcp/mcp-tool-discovery-image-contract.test.ts b/test/mcp/mcp-tool-discovery-image-contract.test.ts index 53758a7b171..7088658dbfd 100644 --- a/test/mcp/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp/mcp-tool-discovery-image-contract.test.ts @@ -221,7 +221,7 @@ describe("MCP tool discovery image contract", () => { relativePath: "mcp-tool-discovery/THIRD_PARTY_LICENSES.txt", }, { - expectedHash: "47b9c1f7f1f5b6c9d5bf304953701b2cff107a81ced8a9646ea66ec12bc6b7f1", + expectedHash: "bb14dc1b9add61a97cf6d1cfb134b8e86241fdf59189a3c3476022131ea60037", relativePath: "mcp-tool-discovery/mcp-tool-discovery.bundle", }, ])("pins the reviewed image runtime artifacts exactly", ({ expectedHash, relativePath }) => { @@ -253,12 +253,14 @@ describe("MCP tool discovery image contract", () => { const discoveryResult = spawnSync(process.execPath, [executablePath], { encoding: "utf8" }); expect(discoveryResult).toMatchObject({ status: 0, stderr: "" }); expect(JSON.parse(discoveryResult.stdout)).toEqual({ - protocol: 1, + protocol: 2, ok: false, count: 0, tools: [], truncated: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }); } finally { fs.rmSync(executableFixture, { force: true, recursive: true }); diff --git a/test/support/managed-bootstrap-image-contract.ts b/test/support/managed-bootstrap-image-contract.ts index 130b04bd9eb..a398db7159a 100644 --- a/test/support/managed-bootstrap-image-contract.ts +++ b/test/support/managed-bootstrap-image-contract.ts @@ -34,7 +34,7 @@ const MANAGED_BOOTSTRAP_BUILDER_IMAGE = const DISCOVERY_RUNTIME_ROOT = "/usr/local/lib/nemoclaw/mcp-tool-discovery-runtime"; const DISCOVERY_RUNTIME_PATH = `${DISCOVERY_RUNTIME_ROOT}/mcp-tool-discovery.mjs`; const DISCOVERY_EXPECTED_CONTRACT = - '{"protocol":1,"ok":false,"detail":"tool discovery received invalid runtime arguments"}'; + '{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}'; const REVIEWED_DISCOVERY_RUNTIME_ROOT = path.join( import.meta.dirname, "..", @@ -260,9 +260,11 @@ function expectManagedRuntimeDiagnostic(dockerfile: string): void { expect(permissionReplay.status, permissionReplay.stderr).toBe(0); expect(permissionReplay.stderr).toBe(""); expect(JSON.parse(permissionReplay.stdout)).toMatchObject({ - protocol: 1, + protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }); const expectedUid = process.getuid?.() ?? 0; const expectedGid = process.getgid?.() ?? 0; @@ -403,12 +405,14 @@ function expectManagedRuntimeDiagnostic(dockerfile: string): void { const success = runDiscoveryChecks({ discoveryOutput: JSON.stringify({ - protocol: 1, + protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", count: 0, tools: [], truncated: false, + failedStage: "preflight", + failureClass: "precondition", }), }); expect(success.status, success.stderr).toBe(0); diff --git a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts index 7139cd6593b..078d0ab7276 100644 --- a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts +++ b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts @@ -30,6 +30,8 @@ async function main(): Promise { tools: [], truncated: false, detail: "tool discovery received invalid runtime arguments", + failedStage: "preflight", + failureClass: "precondition", }); return; } @@ -50,6 +52,8 @@ async function main(): Promise { tools: [], truncated: false, detail: "managed MCP credential placeholder is unavailable", + failedStage: "preflight", + failureClass: "precondition", }); return; } diff --git a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle index e967b0586f8..b207a40a89f 100644 --- a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle +++ b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle @@ -74,8 +74,8 @@ ${value}`,dataLines++}else isEventPrefix(chunk,searchIndex,firstCharCode)?eventT `,searchIndex)}return chunk.slice(searchIndex)}for(;searchIndex20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=1;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){try{await session.connect();session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult({ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2)})}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){if(deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"){throw new ToolDiscoveryRuntimeError("timeout")}throw error2}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(error2)}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected tool discovery (HTTP ${error2.httpStatus})`:"MCP endpoint rejected tool discovery";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}if(error2 instanceof Error){if(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message)){return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP tool discovery request failed"}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} -`)}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>client.connect(transport,requestOptions),loadPage:async cursor=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)},hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); +${value}`,dataLines++;break;case"id":id=value.includes("\0")?void 0:value;break;case"retry":/^\d+$/.test(value)?onRetry(parseInt(value,10)):onError(new ParseError(`Invalid \`retry\` value: "${value}"`,{type:"invalid-retry",value,line}));break;default:onError(new ParseError(`Unknown field "${field.length>20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected tool discovery (HTTP ${error2.httpStatus})`:"MCP endpoint rejected tool discovery";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}if(error2 instanceof Error){if(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message)){return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP tool discovery request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else if(error2 instanceof Error&&(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message))){failureClass="connection"}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} +`)}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>client.connect(transport,requestOptions),loadPage:async cursor=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)},hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); /*! Bundled license information: content-type/index.js: diff --git a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts index 05abc36ee8e..cdcbd579b9c 100644 --- a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts +++ b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -export const MCP_TOOL_DISCOVERY_PROTOCOL = 1; +export const MCP_TOOL_DISCOVERY_PROTOCOL = 2; export const MCP_TOOL_DISCOVERY_LIMITS = { maxTotalTimeMs: 10_000, @@ -13,14 +13,39 @@ export const MCP_TOOL_DISCOVERY_LIMITS = { maxToolNameBytes: 256, } as const; -export interface McpToolDiscoveryResult { - ok: boolean; +export type McpToolDiscoveryFailedStage = + | "preflight" + | "runtime" + | "initialization" + | "tool-discovery"; +export type McpToolDiscoveryFailureClass = + | "precondition" + | "runtime" + | "connection" + | "authentication" + | "protocol" + | "tool-operation"; + +interface McpToolDiscoveryResultBase { count: number; tools: string[]; +} + +export interface McpToolDiscoverySuccessResult extends McpToolDiscoveryResultBase { + ok: true; + truncated: false; +} + +export interface McpToolDiscoveryFailureResult extends McpToolDiscoveryResultBase { + ok: false; truncated: boolean; - detail?: string; + detail: string; + failedStage: McpToolDiscoveryFailedStage; + failureClass: McpToolDiscoveryFailureClass; } +export type McpToolDiscoveryResult = McpToolDiscoverySuccessResult | McpToolDiscoveryFailureResult; + export interface McpToolPage { tools: Array<{ name: string }>; nextCursor?: string; @@ -83,6 +108,7 @@ export function normalizeMcpToolPage(page: McpToolPage): McpToolPage { } type ToolDiscoveryErrorCode = + | "connection" | "http-error" | "invalid-response" | "redirect" @@ -137,6 +163,8 @@ function truncatedResult(tools: string[], detail: string): McpToolDiscoveryResul tools: sorted, truncated: true, detail, + failedStage: "tool-discovery", + failureClass: "tool-operation", }; } @@ -203,17 +231,14 @@ export async function enumerateMcpToolNames( } export async function runMcpToolDiscoverySession(session: McpToolDiscoverySession): Promise { + let failedStage: Extract = + "initialization"; try { await session.connect(); + failedStage = "tool-discovery"; session.publishResult(await enumerateMcpToolNames(session.loadPage)); } catch (error) { - session.publishResult({ - ok: false, - count: 0, - tools: [], - truncated: false, - detail: safeToolDiscoveryErrorDetail(error), - }); + session.publishResult(mcpToolDiscoveryFailure(error, failedStage)); } finally { // Source boundary: after connect, the remote MCP server owns session // lifetime. SDK cleanup can reject once the transport has failed, so the @@ -244,6 +269,15 @@ function combinedSignal(left: AbortSignal | null | undefined, right: AbortSignal return left ? AbortSignal.any([left, right]) : right; } +function boundedFetchError( + error: unknown, + deadlineSignal: AbortSignal, +): ToolDiscoveryRuntimeError { + return deadlineSignal.aborted || (error instanceof Error && error.name === "AbortError") + ? new ToolDiscoveryRuntimeError("timeout") + : new ToolDiscoveryRuntimeError("connection"); +} + export function createBoundedMcpFetch( fetchImpl: ToolDiscoveryFetch, deadlineSignal: AbortSignal, @@ -259,10 +293,7 @@ export function createBoundedMcpFetch( signal: combinedSignal(init.signal, deadlineSignal), }); } catch (error) { - if (deadlineSignal.aborted || (error instanceof Error && error.name === "AbortError")) { - throw new ToolDiscoveryRuntimeError("timeout"); - } - throw error; + throw boundedFetchError(error, deadlineSignal); } if (response.status >= 300 && response.status < 400) { @@ -304,7 +335,7 @@ export function createBoundedMcpFetch( } controller.enqueue(value); } catch (error) { - controller.error(error); + controller.error(boundedFetchError(error, deadlineSignal)); } }, cancel(reason) { @@ -322,6 +353,8 @@ export function createBoundedMcpFetch( export function safeToolDiscoveryErrorDetail(error: unknown): string { if (error instanceof ToolDiscoveryRuntimeError) { switch (error.code) { + case "connection": + return "MCP endpoint connection failed"; case "http-error": return typeof error.httpStatus === "number" ? `MCP endpoint rejected tool discovery (HTTP ${error.httpStatus})` @@ -348,3 +381,39 @@ export function safeToolDiscoveryErrorDetail(error: unknown): string { return "MCP tool discovery request failed"; } + +export function mcpToolDiscoveryFailure( + error: unknown, + failedStage: Extract, +): McpToolDiscoveryFailureResult { + let failureClass: McpToolDiscoveryFailureClass; + if (error instanceof ToolDiscoveryRuntimeError) { + if (error.code === "connection" || error.code === "timeout") { + failureClass = "connection"; + } else if ( + error.code === "http-error" && + (error.httpStatus === 401 || error.httpStatus === 403) + ) { + failureClass = "authentication"; + } else { + failureClass = "protocol"; + } + } else if ( + error instanceof Error && + (error.name === "AbortError" || + /(?:request|maximum total) timeout|timed out/iu.test(error.message)) + ) { + failureClass = "connection"; + } else { + failureClass = failedStage === "initialization" ? "protocol" : "tool-operation"; + } + return { + ok: false, + count: 0, + tools: [], + truncated: false, + detail: safeToolDiscoveryErrorDetail(error), + failedStage, + failureClass, + }; +} From 4113fa845e8430d619ab3896c1181381a0c9ea24 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Fri, 4 Sep 2026 15:21:59 -0700 Subject: [PATCH 02/10] fix(mcp): align discovery verification evidence Signed-off-by: Apurv Kumaria --- Dockerfile | 2 +- agents/hermes/Dockerfile | 2 +- agents/langchain-deepagents-code/Dockerfile | 2 +- docs/manage-sandboxes/manage-mcp-servers.mdx | 2 +- docs/reference/commands.mdx | 4 +-- test/e2e/RETRY_INVENTORY.md | 1 + test/e2e/live/mcp-bridge-tool-discovery.ts | 18 +++++----- test/e2e/mock-parity.json | 1 + .../support/mcp-bridge-tool-discovery.test.ts | 34 +++++++++++++++---- test/helpers/vitest-watch-triggers.ts | 7 +++- .../mcp-tool-discovery-image-contract.test.ts | 7 ++++ .../managed-bootstrap-image-contract.ts | 9 +++-- 12 files changed, 61 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index cf22361afed..95ad719a33c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1901,7 +1901,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 1, ok: false, detail: "tool discovery received invalid runtime arguments" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record && record.protocol === expected.protocol && record.ok === expected.ok && record.detail === expected.detail) process.exit(0); const actual = record ? { protocol: sanitize(record.protocol), ok: sanitize(record.ok), detail: sanitize(record.detail) } : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/agents/hermes/Dockerfile b/agents/hermes/Dockerfile index b5e6664f534..ca9db58142d 100644 --- a/agents/hermes/Dockerfile +++ b/agents/hermes/Dockerfile @@ -534,7 +534,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 1, ok: false, detail: "tool discovery received invalid runtime arguments" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record && record.protocol === expected.protocol && record.ok === expected.ok && record.detail === expected.detail) process.exit(0); const actual = record ? { protocol: sanitize(record.protocol), ok: sanitize(record.ok), detail: sanitize(record.detail) } : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/agents/langchain-deepagents-code/Dockerfile b/agents/langchain-deepagents-code/Dockerfile index 5d14b60c9da..735bb5eeb39 100644 --- a/agents/langchain-deepagents-code/Dockerfile +++ b/agents/langchain-deepagents-code/Dockerfile @@ -134,7 +134,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 1, ok: false, detail: "tool discovery received invalid runtime arguments" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record && record.protocol === expected.protocol && record.ok === expected.ok && record.detail === expected.detail) process.exit(0); const actual = record ? { protocol: sanitize(record.protocol), ok: sanitize(record.ok), detail: sanitize(record.detail) } : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/docs/manage-sandboxes/manage-mcp-servers.mdx b/docs/manage-sandboxes/manage-mcp-servers.mdx index 2c6bbe68564..a0ecf7c6bd8 100644 --- a/docs/manage-sandboxes/manage-mcp-servers.mdx +++ b/docs/manage-sandboxes/manage-mcp-servers.mdx @@ -74,7 +74,7 @@ An older sandbox image that does not contain the shared discovery client reports A discovery failure sets `toolDiscovery.ok` to `false` and makes the command exit nonzero. The command still returns the ordinary provider, policy, environment, and adapter status. -The `commandStatus` field records the sandbox discovery runtime's exit status, or `null` when the runtime did not start. +The `commandStatus` field records the sandbox discovery runtime's exit status. It is `null` when no runtime exit status exists, such as a failed precondition or an unreachable sandbox. A failed result includes `failedStage`, `failureClass`, and a redacted `detail` value. Bounded partial results set `truncated` to `true`. Correct the reported precondition, runtime, connection, authentication, protocol, or tool-operation failure, and then rerun the same command. diff --git a/docs/reference/commands.mdx b/docs/reference/commands.mdx index 6d2fbc2b060..ac7d56d1b12 100644 --- a/docs/reference/commands.mdx +++ b/docs/reference/commands.mdx @@ -2300,9 +2300,9 @@ Pass `--tools` with one server name to request a live tool inventory. The shared Use `--tools` only with a configured endpoint you trust to advertise names while authenticated. The endpoint controls its returned names and can derive them from the request or credential it receives; NemoClaw validates and bounds the text but cannot prove that the endpoint did not encode credential-derived data in an otherwise valid name. -The `toolDiscovery` JSON field contains `ok`, `count`, `tools`, and `truncated`, plus a redacted `detail` on failure or a bounded partial result. These names are the server's point-in-time advertised tools, not an attestation of the exact tools visible to the model after agent filters, progressive disclosure, or session state. An older sandbox image without the shared client reports that the sandbox must be rebuilt. +The `toolDiscovery` JSON field contains `ok`, `count`, `tools`, `truncated`, and `commandStatus`. A failed result also contains `failedStage`, `failureClass`, and a redacted `detail`. `commandStatus` is `null` when no runtime exit status exists, such as a failed precondition or an unreachable sandbox. These names are the server's point-in-time advertised tools, not an attestation of the exact tools visible to the model after agent filters, progressive disclosure, or session state. An older sandbox image without the shared client reports that the sandbox must be rebuilt. -Tool discovery is opt-in and sends authenticated network traffic to the configured endpoint. Passing `--tools` suppresses the named-server credential-resolution probe that otherwise runs by default. Pass `--probe --tools` to request both checks explicitly. An unsuccessful discovery does not remove the ordinary provider, policy, environment, or adapter status from the result. +Tool discovery is opt-in and sends authenticated network traffic to the configured endpoint. Passing `--tools` suppresses the named-server credential-resolution probe that otherwise runs by default. Pass `--probe --tools` to request both checks explicitly. An unsuccessful discovery exits nonzero but does not remove the ordinary provider, policy, environment, or adapter status from the result. ```bash $$nemoclaw my-assistant mcp status [server] [--json] [--probe|--no-probe] [--tools] diff --git a/test/e2e/RETRY_INVENTORY.md b/test/e2e/RETRY_INVENTORY.md index 7522c2b801b..16a269c5c9b 100644 --- a/test/e2e/RETRY_INVENTORY.md +++ b/test/e2e/RETRY_INVENTORY.md @@ -6,3 +6,4 @@ | Operation | Owner | Limit | Retry condition | Safety basis | Evidence | | --- | --- | --- | --- | --- | --- | | `external-gateway-health.tcp-readiness` | `openshell-gateway` | 10 attempts, one second apart | The newly started gateway listener rejects a TCP connection with `ECONNREFUSED` | The probe is read-only. Other errors stop without retry. The Blueprint Runner health operation runs once after the listener opens. | `external-gateway-readiness-retry.json` | +| `mcp-bridge.tool-discovery` | `test/e2e/live/mcp-bridge-tool-discovery.ts` | 2 attempts, one second apart | Discovery reports `failureClass: connection` before any request reaches the fixture | The operation lists tools without calling them. Any fixture request or other failure class stops the retry. | First-attempt and `retry-2` command artifacts plus `*-mcp-tool-discovery-diagnostics.json` | diff --git a/test/e2e/live/mcp-bridge-tool-discovery.ts b/test/e2e/live/mcp-bridge-tool-discovery.ts index b9b895f91ee..04bff12351d 100644 --- a/test/e2e/live/mcp-bridge-tool-discovery.ts +++ b/test/e2e/live/mcp-bridge-tool-discovery.ts @@ -17,19 +17,18 @@ export interface AuthenticatedMcpDiscoveryTarget { label: string; } -const MCP_TOOL_DISCOVERY_TRANSPORT_FAILURE = "MCP tool discovery request failed"; const MCP_TOOL_DISCOVERY_ATTEMPTS = 2; const MCP_TOOL_DISCOVERY_RETRY_DELAY_MS = 1_000; export function shouldRetryMcpToolDiscoveryTransportFailure( - toolDiscovery: { ok: boolean; detail?: string }, + toolDiscovery: { ok: boolean; failureClass?: string }, requestsSinceAttempt: readonly FakeMcpRequest[], attempt: number, ): boolean { return ( attempt < MCP_TOOL_DISCOVERY_ATTEMPTS && !toolDiscovery.ok && - toolDiscovery.detail === MCP_TOOL_DISCOVERY_TRANSPORT_FAILURE && + toolDiscovery.failureClass === "connection" && requestsSinceAttempt.length === 0 ); } @@ -530,23 +529,22 @@ export async function assertAuthenticatedMcpToolDiscovery( timeoutMs: 60_000, }, ); - assertExitZero(status, `${options.artifactPrefix} mcp status --tools --json`); statusJson = JSON.parse(status.stdout) as McpToolDiscoveryStatusJson; - if ( - !shouldRetryMcpToolDiscoveryTransportFailure( + const shouldRetry = + status.exitCode !== 0 && + shouldRetryMcpToolDiscoveryTransportFailure( statusJson.toolDiscovery, fakeMcp.requests.slice(requestOffset), attempt, - ) - ) { - break; - } + ); + if (!shouldRetry) break; options.progress.event( "MCP tool discovery transport failed before reaching the fixture; retrying once", ); await new Promise((resolve) => setTimeout(resolve, MCP_TOOL_DISCOVERY_RETRY_DELAY_MS)); } if (!status || !statusJson) throw new Error("MCP tool discovery did not run"); + assertExitZero(status, `${options.artifactPrefix} mcp status --tools --json`); const discoveryRequests = fakeMcp.requests.slice(requestOffset); await options.artifacts.writeJson( `${options.artifactPrefix}-mcp-tool-discovery-diagnostics.json`, diff --git a/test/e2e/mock-parity.json b/test/e2e/mock-parity.json index 7cc47e2d960..af8b2595f9f 100644 --- a/test/e2e/mock-parity.json +++ b/test/e2e/mock-parity.json @@ -595,6 +595,7 @@ "test/e2e/live/mcp-bridge-hermes-lifecycle.ts", "test/e2e/live/mcp-bridge-phases.ts", "test/e2e/live/mcp-bridge-sandbox.ts", + "test/e2e/live/mcp-bridge-tool-discovery.ts", "test/e2e/live/mcp-bridge-trusted-private.ts", "test/e2e/live/openshell-allowed-ips-rebinding.ts", "test/e2e/live/openshell-exact-main-runtime-contracts.ts" diff --git a/test/e2e/support/mcp-bridge-tool-discovery.test.ts b/test/e2e/support/mcp-bridge-tool-discovery.test.ts index 213f632762d..883a4ce8ba7 100644 --- a/test/e2e/support/mcp-bridge-tool-discovery.test.ts +++ b/test/e2e/support/mcp-bridge-tool-discovery.test.ts @@ -284,7 +284,7 @@ describe("authenticated MCP rediscovery evidence", () => { }); describe("authenticated MCP tool discovery transport retry", () => { - it("records denied authentication after success and restores the fixture credential (#10944)", async () => { + it("retries one connection failure, records denied authentication, and restores the credential (#10944)", async () => { const requests: FakeMcpRequest[] = []; const setSecret = vi.fn(); const fakeMcp = { requests, setSecret } as unknown as FakeMcpHttpsServer; @@ -299,6 +299,25 @@ describe("authenticated MCP tool discovery transport retry", () => { const host = { nemoclaw: vi .fn() + .mockImplementationOnce(async () => ({ + exitCode: 1, + stdout: JSON.stringify({ + provider, + policy, + adapter, + toolDiscovery: { + ok: false, + count: 0, + tools: [], + truncated: false, + commandStatus: 0, + failedStage: "initialization", + failureClass: "connection", + detail: "MCP tool discovery request failed", + }, + }), + stderr: "", + })) .mockImplementationOnce(async () => { requests.push( successfulInitialize(), @@ -354,6 +373,7 @@ describe("authenticated MCP tool discovery transport retry", () => { }), } as unknown as Parameters[0]; const artifacts = discoveryArtifacts(); + const progress = { event: vi.fn() }; await assertAuthenticatedMcpToolDiscovery(host, fakeMcp, { artifacts, @@ -361,9 +381,11 @@ describe("authenticated MCP tool discovery transport retry", () => { artifactPrefix: "hermes", deniedSecret: "denied-secret", hostSecret: EXPECTED_SECRET, - progress: { event: vi.fn() }, + progress, }); + expect(host.nemoclaw).toHaveBeenCalledTimes(3); + expect(progress.event).toHaveBeenCalledOnce(); expect(setSecret.mock.calls).toEqual([["denied-secret"], [EXPECTED_SECRET]]); expect(artifacts.writeJson).toHaveBeenLastCalledWith( "hermes-mcp-tool-discovery-denied-auth.json", @@ -484,10 +506,10 @@ describe("authenticated MCP tool discovery transport retry", () => { expect(diagnostics).not.toContain(PROTOCOL_VERSION); }); - it("retries one generic transport failure before any request reaches the fixture", () => { + it("retries one connection failure before any request reaches the fixture", () => { expect( shouldRetryMcpToolDiscoveryTransportFailure( - { ok: false, detail: "MCP tool discovery request failed" }, + { ok: false, failureClass: "connection" }, [], 1, ), @@ -500,7 +522,7 @@ describe("authenticated MCP tool discovery transport retry", () => { ])("does not retry when %s", (_case, requests, attempt) => { expect( shouldRetryMcpToolDiscoveryTransportFailure( - { ok: false, detail: "MCP tool discovery request failed" }, + { ok: false, failureClass: "connection" }, requests as FakeMcpRequest[], attempt as number, ), @@ -510,7 +532,7 @@ describe("authenticated MCP tool discovery transport retry", () => { it("does not retry a classified product or endpoint failure", () => { expect( shouldRetryMcpToolDiscoveryTransportFailure( - { ok: false, detail: "MCP endpoint returned an invalid tool-list response" }, + { ok: false, failureClass: "protocol" }, [], 1, ), diff --git a/test/helpers/vitest-watch-triggers.ts b/test/helpers/vitest-watch-triggers.ts index ea73d442abc..05da1010701 100644 --- a/test/helpers/vitest-watch-triggers.ts +++ b/test/helpers/vitest-watch-triggers.ts @@ -140,13 +140,18 @@ export const vitestWatchTriggerPatterns: VitestWatchTriggerPattern[] = [ "src/lib/onboard/experimental/hermes-portable-build-context.test.ts", "src/lib/onboard/managed-startup-profile.test.ts", "test/agents/hermes/hermes-mcp-runtime-capability.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", ]; } return match[1] - ? ["src/lib/onboard/managed-startup-profile.test.ts"] + ? [ + "src/lib/onboard/managed-startup-profile.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", + ] : [ "src/lib/onboard/managed-startup-profile.test.ts", "src/lib/sandbox/optimized-build-context-copy-sources.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", ]; }, }, diff --git a/test/mcp/mcp-tool-discovery-image-contract.test.ts b/test/mcp/mcp-tool-discovery-image-contract.test.ts index 7088658dbfd..cfea7c9dc66 100644 --- a/test/mcp/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp/mcp-tool-discovery-image-contract.test.ts @@ -9,6 +9,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import { stripVTControlCharacters } from "node:util"; import { describe, expect, it } from "vitest"; +import { expectManagedToolDiscoveryRuntimeImageContract } from "../support/managed-bootstrap-image-contract"; const repoRoot = path.join(import.meta.dirname, "../.."); const runtimeRoot = "/usr/local/lib/nemoclaw/mcp-tool-discovery-runtime"; @@ -108,6 +109,12 @@ function createCacheSeedFixture(): { } describe("MCP tool discovery image contract", () => { + it.each(dockerfiles)("executes the discovery runtime contract in %s", (dockerfilePath) => { + expectManagedToolDiscoveryRuntimeImageContract( + fs.readFileSync(path.join(repoRoot, dockerfilePath), "utf8"), + ); + }); + it.skipIf(process.platform === "win32")( "installs the complete pinned cache seed offline before registry access", async () => { diff --git a/test/support/managed-bootstrap-image-contract.ts b/test/support/managed-bootstrap-image-contract.ts index a398db7159a..38e5bb95009 100644 --- a/test/support/managed-bootstrap-image-contract.ts +++ b/test/support/managed-bootstrap-image-contract.ts @@ -51,7 +51,7 @@ const REVIEWED_DISCOVERY_RUNTIME_FILES = [ ] as const; const MANAGED_STARTUP_RUNTIME_PATH = "/usr/local/lib/nemoclaw/managed-startup-image-runtime.cjs"; -function expectManagedRuntimeDiagnostic(dockerfile: string): void { +export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: string): void { const instructions = dockerfileInstructions(dockerfile).filter( (instruction) => instruction.keyword === "RUN" && @@ -335,7 +335,7 @@ function expectManagedRuntimeDiagnostic(dockerfile: string): void { expect(contractFailure.status).toBe(1); expect(contractFailure.stdout).toBe(""); expect(contractFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong??continued?[31m"} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong??continued?[31m","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(contractFailure.stderr).not.toContain(credential); expect(contractFailure.stderr).not.toContain("\u001b"); @@ -356,7 +356,7 @@ function expectManagedRuntimeDiagnostic(dockerfile: string): void { expect(contractFailure.status).toBe(1); expect(contractFailure.stdout).toBe(""); expect(contractFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"${sanitized}"} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"${sanitized}","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(contractFailure.stderr).not.toContain(credential); } @@ -381,7 +381,7 @@ function expectManagedRuntimeDiagnostic(dockerfile: string): void { expect(privateKeyFailure.status).toBe(1); expect(privateKeyFailure.stdout).toBe(""); expect(privateKeyFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong?"} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong?","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(privateKeyFailure.stderr).not.toContain("private-material"); @@ -496,5 +496,4 @@ export function expectManagedBootstrapNativeImageContract(dockerfile: string): v ).toHaveLength(1); expect(dockerfile).toContain("test ! -L /usr/local/bin/nemoclaw-managed-bootstrap"); expect(dockerfile).toContain("test ! -L /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh"); - expectManagedRuntimeDiagnostic(dockerfile); } From 896eb1a9b620c0584a56720ccdae0370a98510ad Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Fri, 4 Sep 2026 17:54:53 -0700 Subject: [PATCH 03/10] fix(onboard): refresh managed Dockerfile contract Signed-off-by: Apurv Kumaria --- src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts b/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts index abd2b99374a..3552beba9f3 100644 --- a/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts +++ b/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts @@ -75,7 +75,7 @@ const CANONICAL_POST_GENERATOR_INSTRUCTION_SHA256 = new Set([ "7e5f7e1dfb90e5e4b863afdfb9ba58e57e3693bdc6f47ac8c13e80bdc9eff56b", "8f5966da093ef75cefd35c2b7f1361fbf5b32e63a4a8a34cb3ac7f76a1330e5e", "c6b042ac2cc3d5570ae43f1e387a951cbe89fbb22e8cd9df486f99719bb32939", - "ba29b499af923b4331cf7abf14648f187dc0e0b8f3dc2c33dac61f079981c187", + "b4b95b82af76bfab4c8a8182080f7fb1d9ab3f6eb6e3750c92aa3302a7686631", "9c39e3aefbfefb7a48c6ba6879701954ad300f3593d18713063a12056f71af93", // COPY --from=openclaw-runtime-payload / / // The reviewed scratch payload has no /sandbox/.openclaw content, so this From a5a552e274322201ff4336db64ae2228707e790d Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Fri, 4 Sep 2026 18:58:49 -0700 Subject: [PATCH 04/10] test(repository): align managed image watch triggers Signed-off-by: Apurv Kumaria --- test/repository/vitest-watch-triggers.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/repository/vitest-watch-triggers.test.ts b/test/repository/vitest-watch-triggers.test.ts index 5813c3fa6fe..59a1673950a 100644 --- a/test/repository/vitest-watch-triggers.test.ts +++ b/test/repository/vitest-watch-triggers.test.ts @@ -210,6 +210,7 @@ describe("Vitest opaque-input watch triggers", () => { expect(triggeredBy("Dockerfile")).toEqual([ "src/lib/onboard/managed-startup-profile.test.ts", "src/lib/sandbox/optimized-build-context-copy-sources.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", ]); expect(triggeredBy("agents/hermes/Dockerfile.base")).toEqual([ "test/agents/hermes/hermes-share-mount-deps.test.ts", @@ -226,6 +227,7 @@ describe("Vitest opaque-input watch triggers", () => { "src/lib/onboard/experimental/hermes-portable-build-context.test.ts", "src/lib/onboard/managed-startup-profile.test.ts", "test/agents/hermes/hermes-mcp-runtime-capability.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", ]); expect(triggeredBy("scripts/checks/download-hermes-source-archive.sh")).toEqual([ "test/agents/hermes/hermes-share-mount-deps.test.ts", @@ -233,6 +235,7 @@ describe("Vitest opaque-input watch triggers", () => { ]); expect(triggeredBy("agents/langchain-deepagents-code/Dockerfile")).toEqual([ "src/lib/onboard/managed-startup-profile.test.ts", + "test/mcp/mcp-tool-discovery-image-contract.test.ts", ]); expect(triggeredBy("agents/hermes/policy-additions.yaml")).toEqual([ "src/lib/onboard/initial-policy-real-policy.test.ts", From 7c20414155199ae7c1a4095fa8dca435ad065733 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 6 Sep 2026 12:52:20 -0700 Subject: [PATCH 05/10] fix(mcp): tighten discovery verification contracts Signed-off-by: Apurv Kumaria --- .github/workflows/managed-images.yaml | 16 ++++++++++ Dockerfile | 2 +- agents/hermes/Dockerfile | 2 +- agents/langchain-deepagents-code/Dockerfile | 2 +- docs/manage-sandboxes/manage-mcp-servers.mdx | 5 +++- docs/reference/commands.mdx | 8 ++++- .../mcp-bridge-status-resolution.test.ts | 4 +-- .../sandbox/mcp-bridge-tool-discovery.test.ts | 4 +-- .../mcp-tool-discovery-runtime.test.ts | 12 ++++---- .../support/mcp-bridge-tool-discovery.test.ts | 8 ++--- ...managed-image-publication-workflow.test.ts | 5 ++-- .../mcp-tool-discovery-image-contract.test.ts | 2 +- .../managed-bootstrap-image-contract.ts | 29 +++++++++++++++---- .../mcp-tool-discovery.bundle | 2 +- .../tool-discovery-core.ts | 10 +++---- 15 files changed, 78 insertions(+), 33 deletions(-) diff --git a/.github/workflows/managed-images.yaml b/.github/workflows/managed-images.yaml index 0aa45551c2a..6bcd30cf4c2 100644 --- a/.github/workflows/managed-images.yaml +++ b/.github/workflows/managed-images.yaml @@ -355,6 +355,9 @@ jobs: const expected = { protocol: 2, ok: false, + count: 0, + tools: [], + truncated: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition", @@ -371,8 +374,13 @@ jobs: : undefined; if ( !record || + Object.keys(record).length !== Object.keys(expected).length || record.protocol !== expected.protocol || record.ok !== expected.ok || + record.count !== expected.count || + !Array.isArray(record.tools) || + record.tools.length !== 0 || + record.truncated !== expected.truncated || record.detail !== expected.detail || record.failedStage !== expected.failedStage || record.failureClass !== expected.failureClass @@ -747,6 +755,9 @@ jobs: const expected = { protocol: 2, ok: false, + count: 0, + tools: [], + truncated: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition", @@ -763,8 +774,13 @@ jobs: : undefined; if ( !record || + Object.keys(record).length !== Object.keys(expected).length || record.protocol !== expected.protocol || record.ok !== expected.ok || + record.count !== expected.count || + !Array.isArray(record.tools) || + record.tools.length !== 0 || + record.truncated !== expected.truncated || record.detail !== expected.detail || record.failedStage !== expected.failedStage || record.failureClass !== expected.failureClass diff --git a/Dockerfile b/Dockerfile index 95ad719a33c..608eae1fbaa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1901,7 +1901,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected={protocol:2,ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"}; const secretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redact = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let text = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of secretPatterns) text = text.replace(pattern, ""); text = redact(text); return text.length <= 240 ? text : text.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if(record&&Object.keys(record).length===8&&Object.keys(expected).every(k=>k==="tools"?Array.isArray(record[k])&&!record[k].length:record[k]===expected[k]))process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/agents/hermes/Dockerfile b/agents/hermes/Dockerfile index b31d7108a9c..7896c93bb61 100644 --- a/agents/hermes/Dockerfile +++ b/agents/hermes/Dockerfile @@ -534,7 +534,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected={protocol:2,ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"}; const secretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redact = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let text = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of secretPatterns) text = text.replace(pattern, ""); text = redact(text); return text.length <= 240 ? text : text.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if(record&&Object.keys(record).length===8&&Object.keys(expected).every(k=>k==="tools"?Array.isArray(record[k])&&!record[k].length:record[k]===expected[k]))process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/agents/langchain-deepagents-code/Dockerfile b/agents/langchain-deepagents-code/Dockerfile index 735bb5eeb39..a6357eea038 100644 --- a/agents/langchain-deepagents-code/Dockerfile +++ b/agents/langchain-deepagents-code/Dockerfile @@ -134,7 +134,7 @@ RUN managed_runtime_assertion_failed() { \ fi; \ discovery_contract="$(node /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/mcp-tool-discovery.mjs)" \ || managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \ - node -e 'const expected = { protocol: 2, ok: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, ""); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record&&Object.entries(expected).every(([k,v])=>record[k]===v)) process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ + node -e 'const expected={protocol:2,ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"}; const secretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redact = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/giu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + "").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]+/gu, (_match, prefix) => prefix + ""); const sanitize = (value) => { if (value === undefined) return ""; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let text = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of secretPatterns) text = text.replace(pattern, ""); text = redact(text); return text.length <= 240 ? text : text.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if(record&&Object.keys(record).length===8&&Object.keys(expected).every(k=>k==="tools"?Array.isArray(record[k])&&!record[k].length:record[k]===expected[k]))process.exit(0); const actual = record ? Object.fromEntries(Object.keys(expected).map(k=>[k,sanitize(record[k])])) : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \ || exit 1; \ discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \ || managed_image_command_failed mcp-tool-discovery-tree-find-execution "$?"; \ diff --git a/docs/manage-sandboxes/manage-mcp-servers.mdx b/docs/manage-sandboxes/manage-mcp-servers.mdx index a0ecf7c6bd8..16849ac5d9d 100644 --- a/docs/manage-sandboxes/manage-mcp-servers.mdx +++ b/docs/manage-sandboxes/manage-mcp-servers.mdx @@ -74,7 +74,10 @@ An older sandbox image that does not contain the shared discovery client reports A discovery failure sets `toolDiscovery.ok` to `false` and makes the command exit nonzero. The command still returns the ordinary provider, policy, environment, and adapter status. -The `commandStatus` field records the sandbox discovery runtime's exit status. It is `null` when no runtime exit status exists, such as a failed precondition or an unreachable sandbox. +The `commandStatus` field records the sandbox discovery runtime's exit status. +A runtime-emitted precondition reports `0` because the runtime completed and returned a failure result. +A bridge-level precondition that skips runtime execution reports `null`. +An unreachable sandbox also reports `null` when no runtime exit status exists. A failed result includes `failedStage`, `failureClass`, and a redacted `detail` value. Bounded partial results set `truncated` to `true`. Correct the reported precondition, runtime, connection, authentication, protocol, or tool-operation failure, and then rerun the same command. diff --git a/docs/reference/commands.mdx b/docs/reference/commands.mdx index db6a26ab187..085c21bdc10 100644 --- a/docs/reference/commands.mdx +++ b/docs/reference/commands.mdx @@ -2300,7 +2300,13 @@ Pass `--tools` with one server name to request a live tool inventory. The shared Use `--tools` only with a configured endpoint you trust to advertise names while authenticated. The endpoint controls its returned names and can derive them from the request or credential it receives; NemoClaw validates and bounds the text but cannot prove that the endpoint did not encode credential-derived data in an otherwise valid name. -The `toolDiscovery` JSON field contains `ok`, `count`, `tools`, `truncated`, and `commandStatus`. A failed result also contains `failedStage`, `failureClass`, and a redacted `detail`. `commandStatus` is `null` when no runtime exit status exists, such as a failed precondition or an unreachable sandbox. These names are the server's point-in-time advertised tools, not an attestation of the exact tools visible to the model after agent filters, progressive disclosure, or session state. An older sandbox image without the shared client reports that the sandbox must be rebuilt. +The `toolDiscovery` JSON field contains `ok`, `count`, `tools`, `truncated`, and `commandStatus`. +A failed result also contains `failedStage`, `failureClass`, and a redacted `detail`. +A runtime-emitted precondition reports `commandStatus` as `0`. +A bridge-level precondition that skips runtime execution reports `null`. +An unreachable sandbox also reports `null` when no runtime exit status exists. +These names are the server's point-in-time advertised tools, not an attestation of the exact tools visible to the model after agent filters, progressive disclosure, or session state. +An older sandbox image without the shared client reports that the sandbox must be rebuilt. Tool discovery is opt-in and sends authenticated network traffic to the configured endpoint. Passing `--tools` suppresses the named-server credential-resolution probe that otherwise runs by default. Pass `--probe --tools` to request both checks explicitly. An unsuccessful discovery exits nonzero but does not remove the ordinary provider, policy, environment, or adapter status from the result. diff --git a/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts b/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts index daee5c5ad37..26a0e379578 100644 --- a/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts +++ b/src/lib/actions/sandbox/mcp-bridge-status-resolution.test.ts @@ -829,7 +829,7 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 count: 0, tools: [], truncated: false, - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", failedStage: "initialization", failureClass: "authentication", }; @@ -853,7 +853,7 @@ describe("MCP status wire-level credential-resolution probe", { timeout: 15_000 tools: [], truncated: false, commandStatus: 0, - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", failedStage: "initialization", failureClass: "authentication", }); diff --git a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts index c028050caeb..b23e5bb1572 100644 --- a/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts +++ b/src/lib/actions/sandbox/mcp-bridge-tool-discovery.test.ts @@ -185,7 +185,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { count: 0, tools: [], truncated: false, - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", failedStage: "initialization", failureClass: "authentication", }), @@ -198,7 +198,7 @@ describe("MCP tool discovery host boundary (#6901)", () => { tools: [], truncated: false, commandStatus: 0, - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", failedStage: "initialization", failureClass: "authentication", }); diff --git a/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts b/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts index 102511c1f06..52141b65532 100644 --- a/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts +++ b/src/lib/actions/sandbox/mcp-tool-discovery-runtime.test.ts @@ -241,7 +241,7 @@ describe("shared MCP tool discovery runtime", () => { count: 0, tools: [], truncated: false, - detail: "MCP tool discovery request failed", + detail: "MCP request failed", failedStage: "tool-discovery", failureClass: "tool-operation", }); @@ -273,7 +273,7 @@ describe("shared MCP tool discovery runtime", () => { count: 0, tools: [], truncated: false, - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", failedStage: "initialization", failureClass: "authentication", }); @@ -382,7 +382,7 @@ describe("shared MCP tool discovery runtime", () => { (abortSource === "deadline" ? deadline : request).abort(); const error = await pending.catch((caught: unknown) => caught); expect(error).toMatchObject({ code: "timeout" }); - expect(safeToolDiscoveryErrorDetail(error)).toBe("tool discovery timed out after 10s"); + expect(safeToolDiscoveryErrorDetail(error)).toBe("MCP request timed out after 10s"); expect(safeToolDiscoveryErrorDetail(error)).not.toContain("untrusted-timeout-detail"); }, ); @@ -395,15 +395,15 @@ describe("shared MCP tool discovery runtime", () => { "MCP endpoint redirect was rejected", ); expect(safeToolDiscoveryErrorDetail(new ToolDiscoveryRuntimeError("http-error", 401))).toBe( - "MCP endpoint rejected tool discovery (HTTP 401)", + "MCP endpoint rejected the request (HTTP 401)", ); expect( safeToolDiscoveryErrorDetail( Object.assign(new Error("remote body contains Bearer secret-value"), { code: 401 }), ), - ).toBe("MCP tool discovery request failed"); + ).toBe("MCP request failed"); expect(safeToolDiscoveryErrorDetail(new Error("Bearer secret-value"))).toBe( - "MCP tool discovery request failed", + "MCP request failed", ); }); diff --git a/test/e2e/support/mcp-bridge-tool-discovery.test.ts b/test/e2e/support/mcp-bridge-tool-discovery.test.ts index 883a4ce8ba7..d54fbc12a06 100644 --- a/test/e2e/support/mcp-bridge-tool-discovery.test.ts +++ b/test/e2e/support/mcp-bridge-tool-discovery.test.ts @@ -313,7 +313,7 @@ describe("authenticated MCP tool discovery transport retry", () => { commandStatus: 0, failedStage: "initialization", failureClass: "connection", - detail: "MCP tool discovery request failed", + detail: "MCP request failed", }, }), stderr: "", @@ -365,7 +365,7 @@ describe("authenticated MCP tool discovery transport retry", () => { commandStatus: 0, failedStage: "initialization", failureClass: "authentication", - detail: "MCP endpoint rejected tool discovery (HTTP 401)", + detail: "MCP endpoint rejected the request (HTTP 401)", }, }), stderr: "", @@ -427,7 +427,7 @@ describe("authenticated MCP tool discovery transport retry", () => { commandStatus: 0, failedStage: "initialization" as const, failureClass: "connection" as const, - detail: "MCP tool discovery request failed", + detail: "MCP request failed", credential: STATUS_SECRET, }, }; @@ -476,7 +476,7 @@ describe("authenticated MCP tool discovery transport retry", () => { commandStatus: 0, failedStage: "initialization", failureClass: "connection", - detail: "MCP tool discovery request failed", + detail: "MCP request failed", }, requests: [ { diff --git a/test/inference/managed/managed-image-publication-workflow.test.ts b/test/inference/managed/managed-image-publication-workflow.test.ts index 8c1c71a72bd..54f221ef498 100644 --- a/test/inference/managed/managed-image-publication-workflow.test.ts +++ b/test/inference/managed/managed-image-publication-workflow.test.ts @@ -547,6 +547,7 @@ describe("complete managed-image publication workflow", () => { expect(contractSource).toContain('result = JSON.parse(require("node:fs").readFileSync(0'); expect(contractSource).toContain("record.protocol !== expected.protocol"); expect(contractSource).toContain("record.ok !== expected.ok"); + expect(contractSource).toMatch(/Object\.keys.*record\.count.*record\.tools.*record\.truncated/su); expect(contractSource).toContain("record.detail !== expected.detail"); expect(contractSource).not.toContain( '[ "$actual_discovery_contract" != "$expected_discovery_contract" ]', @@ -621,8 +622,8 @@ describe("complete managed-image publication workflow", () => { expect(acceptedContract.status, acceptedContract.stderr).toBe(0); for (const rejectedOutput of [ '{"protocol":2,"ok":false,"detail":"wrong"}\n', - '{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition","extra":NaN}\n', - '\uFEFF{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}\n', + '{"protocol":2,"ok":false,"count":0,"tools":[],"truncated":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition","extra":"unexpected"}\n', + '\uFEFF{"protocol":2,"ok":false,"count":0,"tools":[],"truncated":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}\n', ]) { const rejectedContract = spawnSync(process.execPath, ["-e", contractValidator], { encoding: "utf8", diff --git a/test/mcp/mcp-tool-discovery-image-contract.test.ts b/test/mcp/mcp-tool-discovery-image-contract.test.ts index cfea7c9dc66..629b234d4c1 100644 --- a/test/mcp/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp/mcp-tool-discovery-image-contract.test.ts @@ -228,7 +228,7 @@ describe("MCP tool discovery image contract", () => { relativePath: "mcp-tool-discovery/THIRD_PARTY_LICENSES.txt", }, { - expectedHash: "bb14dc1b9add61a97cf6d1cfb134b8e86241fdf59189a3c3476022131ea60037", + expectedHash: "3cff01c9552108804673bc93b5e7c50aa5efd3f15b85a3c64711440a8f980edc", relativePath: "mcp-tool-discovery/mcp-tool-discovery.bundle", }, ])("pins the reviewed image runtime artifacts exactly", ({ expectedHash, relativePath }) => { diff --git a/test/support/managed-bootstrap-image-contract.ts b/test/support/managed-bootstrap-image-contract.ts index 38e5bb95009..85b7cdb8074 100644 --- a/test/support/managed-bootstrap-image-contract.ts +++ b/test/support/managed-bootstrap-image-contract.ts @@ -34,7 +34,7 @@ const MANAGED_BOOTSTRAP_BUILDER_IMAGE = const DISCOVERY_RUNTIME_ROOT = "/usr/local/lib/nemoclaw/mcp-tool-discovery-runtime"; const DISCOVERY_RUNTIME_PATH = `${DISCOVERY_RUNTIME_ROOT}/mcp-tool-discovery.mjs`; const DISCOVERY_EXPECTED_CONTRACT = - '{"protocol":2,"ok":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}'; + '{"protocol":2,"ok":false,"count":0,"tools":[],"truncated":false,"detail":"tool discovery received invalid runtime arguments","failedStage":"preflight","failureClass":"precondition"}'; const REVIEWED_DISCOVERY_RUNTIME_ROOT = path.join( import.meta.dirname, "..", @@ -259,9 +259,12 @@ export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: strin const permissionReplay = runPermissionReplay(); expect(permissionReplay.status, permissionReplay.stderr).toBe(0); expect(permissionReplay.stderr).toBe(""); - expect(JSON.parse(permissionReplay.stdout)).toMatchObject({ + expect(JSON.parse(permissionReplay.stdout)).toEqual({ protocol: 2, ok: false, + count: 0, + tools: [], + truncated: false, detail: "tool discovery received invalid runtime arguments", failedStage: "preflight", failureClass: "precondition", @@ -335,7 +338,7 @@ export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: strin expect(contractFailure.status).toBe(1); expect(contractFailure.stdout).toBe(""); expect(contractFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong??continued?[31m","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"count":"","tools":"","truncated":"","detail":"wrong??continued?[31m","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(contractFailure.stderr).not.toContain(credential); expect(contractFailure.stderr).not.toContain("\u001b"); @@ -356,7 +359,7 @@ export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: strin expect(contractFailure.status).toBe(1); expect(contractFailure.stdout).toBe(""); expect(contractFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"${sanitized}","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"count":"","tools":"","truncated":"","detail":"${sanitized}","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(contractFailure.stderr).not.toContain(credential); } @@ -381,7 +384,7 @@ export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: strin expect(privateKeyFailure.status).toBe(1); expect(privateKeyFailure.stdout).toBe(""); expect(privateKeyFailure.stderr).toBe( - `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"detail":"wrong?","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, + `ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual={"protocol":2,"ok":true,"count":"","tools":"","truncated":"","detail":"wrong?","failedStage":"","failureClass":""} expected=${DISCOVERY_EXPECTED_CONTRACT}\n`, ); expect(privateKeyFailure.stderr).not.toContain("private-material"); @@ -419,6 +422,22 @@ export function expectManagedToolDiscoveryRuntimeImageContract(dockerfile: strin expect(success.stdout).toBe("discovery-ok\n"); expect(success.stderr).toBe(""); + for (const rejectedContract of [ + { ...JSON.parse(DISCOVERY_EXPECTED_CONTRACT), count: 1 }, + { ...JSON.parse(DISCOVERY_EXPECTED_CONTRACT), tools: ["unexpected"] }, + { ...JSON.parse(DISCOVERY_EXPECTED_CONTRACT), truncated: true }, + { ...JSON.parse(DISCOVERY_EXPECTED_CONTRACT), extra: "unexpected" }, + ]) { + const contractFailure = runDiscoveryChecks({ + discoveryOutput: JSON.stringify(rejectedContract), + }); + expect(contractFailure.status).toBe(1); + expect(contractFailure.stdout).toBe(""); + expect(contractFailure.stderr).toContain( + "ERROR: managed image assertion failed: mcp-tool-discovery-json-contract", + ); + } + const missing = runDiagnostic(missingPath, "regular-file", "unused"); expect(missing.status).toBe(1); expect(missing.stdout).toBe(""); diff --git a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle index b207a40a89f..bf8789f0663 100644 --- a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle +++ b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle @@ -74,7 +74,7 @@ ${value}`,dataLines++}else isEventPrefix(chunk,searchIndex,firstCharCode)?eventT `,searchIndex)}return chunk.slice(searchIndex)}for(;searchIndex20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected tool discovery (HTTP ${error2.httpStatus})`:"MCP endpoint rejected tool discovery";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}if(error2 instanceof Error){if(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message)){return`tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP tool discovery request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else if(error2 instanceof Error&&(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message))){failureClass="connection"}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} +${value}`,dataLines++;break;case"id":id=value.includes("\0")?void 0:value;break;case"retry":/^\d+$/.test(value)?onRetry(parseInt(value,10)):onError(new ParseError(`Invalid \`retry\` value: "${value}"`,{type:"invalid-retry",value,line}));break;default:onError(new ParseError(`Unknown field "${field.length>20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected the request (HTTP ${error2.httpStatus})`:"MCP endpoint rejected the request";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}if(error2 instanceof Error){if(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message)){return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else if(error2 instanceof Error&&(error2.name==="AbortError"||/(?:request|maximum total) timeout|timed out/iu.test(error2.message))){failureClass="connection"}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} `)}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>client.connect(transport,requestOptions),loadPage:async cursor=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)},hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); /*! Bundled license information: diff --git a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts index cdcbd579b9c..29ef0d8358b 100644 --- a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts +++ b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts @@ -357,8 +357,8 @@ export function safeToolDiscoveryErrorDetail(error: unknown): string { return "MCP endpoint connection failed"; case "http-error": return typeof error.httpStatus === "number" - ? `MCP endpoint rejected tool discovery (HTTP ${error.httpStatus})` - : "MCP endpoint rejected tool discovery"; + ? `MCP endpoint rejected the request (HTTP ${error.httpStatus})` + : "MCP endpoint rejected the request"; case "invalid-response": return "MCP endpoint returned an invalid tool-list response"; case "redirect": @@ -366,7 +366,7 @@ export function safeToolDiscoveryErrorDetail(error: unknown): string { case "response-too-large": return `MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`; case "timeout": - return `tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs / 1_000}s`; + return `MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs / 1_000}s`; } } @@ -375,11 +375,11 @@ export function safeToolDiscoveryErrorDetail(error: unknown): string { error.name === "AbortError" || /(?:request|maximum total) timeout|timed out/iu.test(error.message) ) { - return `tool discovery timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs / 1_000}s`; + return `MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs / 1_000}s`; } } - return "MCP tool discovery request failed"; + return "MCP request failed"; } export function mcpToolDiscoveryFailure( From c4588dc3d6162df15515ba3c4f557873185893fe Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 6 Sep 2026 13:17:30 -0700 Subject: [PATCH 06/10] fix(onboard): refresh managed Dockerfile contract Signed-off-by: Apurv Kumaria --- src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts b/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts index 3552beba9f3..fd1959c9ae7 100644 --- a/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts +++ b/src/lib/onboard/dockerfile-remote-dashboard-bind-contract.ts @@ -75,7 +75,7 @@ const CANONICAL_POST_GENERATOR_INSTRUCTION_SHA256 = new Set([ "7e5f7e1dfb90e5e4b863afdfb9ba58e57e3693bdc6f47ac8c13e80bdc9eff56b", "8f5966da093ef75cefd35c2b7f1361fbf5b32e63a4a8a34cb3ac7f76a1330e5e", "c6b042ac2cc3d5570ae43f1e387a951cbe89fbb22e8cd9df486f99719bb32939", - "b4b95b82af76bfab4c8a8182080f7fb1d9ab3f6eb6e3750c92aa3302a7686631", + "761ea4fbb0da5cf3390ee8f2e56f3703a7da88c96ac47fea89d0fff800f728ae", "9c39e3aefbfefb7a48c6ba6879701954ad300f3593d18713063a12056f71af93", // COPY --from=openclaw-runtime-payload / / // The reviewed scratch payload has no /sandbox/.openclaw content, so this From e69b1ee85fee20a4c1046ddd73ac5b3ed7c37141 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Mon, 7 Sep 2026 14:07:03 -0700 Subject: [PATCH 07/10] test(mcp): harden discovery failure evidence Signed-off-by: Apurv Kumaria --- test/e2e/live/mcp-bridge-tool-discovery.ts | 37 +++++++++++++++--- test/e2e/live/mcp-bridge.test.ts | 1 + .../support/mcp-bridge-tool-discovery.test.ts | 38 +++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/test/e2e/live/mcp-bridge-tool-discovery.ts b/test/e2e/live/mcp-bridge-tool-discovery.ts index 6e4b6584e14..ea950775a83 100644 --- a/test/e2e/live/mcp-bridge-tool-discovery.ts +++ b/test/e2e/live/mcp-bridge-tool-discovery.ts @@ -74,6 +74,24 @@ type McpToolDiscoveryStatusJson = { }; }; +function parseMcpToolDiscoveryStatusJson(stdout: string): McpToolDiscoveryStatusJson | undefined { + try { + return JSON.parse(stdout) as McpToolDiscoveryStatusJson; + } catch { + return undefined; + } +} + +function requireMcpToolDiscoveryStatusJson( + status: McpToolDiscoveryStatusJson | undefined, + label: string, +): McpToolDiscoveryStatusJson { + if (!status?.toolDiscovery) { + throw new Error(`${label} did not return valid MCP discovery JSON`); + } + return status; +} + function buildMcpToolDiscoveryDiagnostics( status: McpToolDiscoveryStatusJson, requests: readonly FakeMcpRequest[], @@ -561,11 +579,13 @@ export async function assertAuthenticatedMcpToolDiscovery( timeoutMs: 60_000, }, ); - statusJson = JSON.parse(status.stdout) as McpToolDiscoveryStatusJson; + statusJson = parseMcpToolDiscoveryStatusJson(status.stdout); + const retryDiscovery = statusJson?.toolDiscovery; const shouldRetry = status.exitCode !== 0 && + retryDiscovery !== undefined && shouldRetryMcpToolDiscoveryTransportFailure( - statusJson.toolDiscovery, + retryDiscovery, fakeMcp.requests.slice(requestOffset), attempt, ); @@ -575,14 +595,16 @@ export async function assertAuthenticatedMcpToolDiscovery( ); await new Promise((resolve) => setTimeout(resolve, MCP_TOOL_DISCOVERY_RETRY_DELAY_MS)); } - if (!status || !statusJson) throw new Error("MCP tool discovery did not run"); - assertExitZero(status, `${options.artifactPrefix} mcp status --tools --json`); + const statusLabel = `${options.artifactPrefix} mcp status --tools --json`; + const completedStatus = status!; + assertExitZero(completedStatus, statusLabel); + statusJson = requireMcpToolDiscoveryStatusJson(statusJson, statusLabel); const discoveryRequests = fakeMcp.requests.slice(requestOffset); await options.artifacts.writeJson( `${options.artifactPrefix}-mcp-tool-discovery-diagnostics.json`, buildMcpToolDiscoveryDiagnostics(statusJson, discoveryRequests, options.hostSecret), ); - expect(status.stdout).not.toContain(options.hostSecret); + expect(completedStatus.stdout).not.toContain(options.hostSecret); const discoveryProtocolRequests = discoveryRequests.filter( (request) => (request.method === "POST" || request.method === "DELETE") && request.path === "/mcp", @@ -685,7 +707,10 @@ export async function assertAuthenticatedMcpToolDiscovery( }, ); expect(result.exitCode).not.toBe(0); - const deniedStatusJson = JSON.parse(result.stdout) as McpToolDiscoveryStatusJson; + const deniedStatusJson = requireMcpToolDiscoveryStatusJson( + parseMcpToolDiscoveryStatusJson(result.stdout), + `${options.artifactPrefix} denied-authentication mcp status --tools --json`, + ); const deniedRequests = fakeMcp.requests.slice(deniedRequestOffset); await options.artifacts.writeJson( `${options.artifactPrefix}-mcp-tool-discovery-denied-auth.json`, diff --git a/test/e2e/live/mcp-bridge.test.ts b/test/e2e/live/mcp-bridge.test.ts index 26a627fb56a..919d91111e9 100644 --- a/test/e2e/live/mcp-bridge.test.ts +++ b/test/e2e/live/mcp-bridge.test.ts @@ -842,6 +842,7 @@ test( artifacts, sandboxName: OPENCLAW_SANDBOX_NAME, artifactPrefix: "openclaw", + deniedSecret: ROTATED_HOST_SECRET, hostSecret: HOST_SECRET, progress, }); diff --git a/test/e2e/support/mcp-bridge-tool-discovery.test.ts b/test/e2e/support/mcp-bridge-tool-discovery.test.ts index d54fbc12a06..40c74348134 100644 --- a/test/e2e/support/mcp-bridge-tool-discovery.test.ts +++ b/test/e2e/support/mcp-bridge-tool-discovery.test.ts @@ -401,6 +401,44 @@ describe("authenticated MCP tool discovery transport retry", () => { expect(JSON.stringify(artifacts.writeJson.mock.calls)).not.toContain(EXPECTED_SECRET); }); + it.each([ + [ + "a failed command", + 1, + "runtime failed", + "openclaw mcp status --tools --json failed: not-json\nruntime failed", + ], + [ + "a successful command", + 0, + "", + "openclaw mcp status --tools --json did not return valid MCP discovery JSON", + ], + ])( + "does not retry malformed status output from %s (#10944)", + async (_case, exitCode, stderr, expectedError) => { + const host = { + nemoclaw: vi.fn(async () => ({ exitCode, stdout: "not-json", stderr })), + } as unknown as Parameters[0]; + const artifacts = discoveryArtifacts(); + const progress = { event: vi.fn() }; + + await expect( + assertAuthenticatedMcpToolDiscovery(host, fakeDiscoveryServer(), { + artifacts, + sandboxName: "sandbox", + artifactPrefix: "openclaw", + hostSecret: EXPECTED_SECRET, + progress, + }), + ).rejects.toThrow(expectedError); + + expect(host.nemoclaw).toHaveBeenCalledOnce(); + expect(progress.event).not.toHaveBeenCalled(); + expect(artifacts.writeJson).not.toHaveBeenCalled(); + }, + ); + it("writes redacted boundary diagnostics before a discovery failure (#8746)", async () => { const statusJson = { provider: { From 532e269bbabaddeb7766b79c123226a59b5957c2 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Mon, 7 Sep 2026 15:04:46 -0700 Subject: [PATCH 08/10] fix(e2e): validate MCP discovery status shape Signed-off-by: Apurv Kumaria --- test/e2e/live/mcp-bridge-tool-discovery.ts | 45 ++++++++++++++++++- .../support/mcp-bridge-tool-discovery.test.ts | 23 +++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/test/e2e/live/mcp-bridge-tool-discovery.ts b/test/e2e/live/mcp-bridge-tool-discovery.ts index ea950775a83..ad69569be9f 100644 --- a/test/e2e/live/mcp-bridge-tool-discovery.ts +++ b/test/e2e/live/mcp-bridge-tool-discovery.ts @@ -74,9 +74,52 @@ type McpToolDiscoveryStatusJson = { }; }; +function isJsonRecord(value: unknown): value is Record { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function isBooleanOrNull(value: unknown): value is boolean | null { + return typeof value === "boolean" || value === null; +} + +function isMcpToolDiscoveryStatusJson(value: unknown): value is McpToolDiscoveryStatusJson { + if (!isJsonRecord(value)) return false; + const { provider, policy, adapter, trustedPrivateTarget, toolDiscovery } = value; + return ( + isJsonRecord(provider) && + typeof provider.registryPresent === "boolean" && + isBooleanOrNull(provider.gatewayPresent) && + isBooleanOrNull(provider.attached) && + isBooleanOrNull(provider.credentialReady) && + isJsonRecord(policy) && + typeof policy.registryPresent === "boolean" && + isBooleanOrNull(policy.gatewayPresent) && + isJsonRecord(adapter) && + isBooleanOrNull(adapter.registered) && + (trustedPrivateTarget === undefined || + (isJsonRecord(trustedPrivateTarget) && + (trustedPrivateTarget.state === "match" || + trustedPrivateTarget.state === "drift" || + trustedPrivateTarget.state === "unresolved"))) && + isJsonRecord(toolDiscovery) && + typeof toolDiscovery.ok === "boolean" && + Number.isSafeInteger(toolDiscovery.count) && + (toolDiscovery.count as number) >= 0 && + Array.isArray(toolDiscovery.tools) && + toolDiscovery.tools.length === toolDiscovery.count && + toolDiscovery.tools.every((tool) => typeof tool === "string") && + typeof toolDiscovery.truncated === "boolean" && + (toolDiscovery.commandStatus === null || Number.isSafeInteger(toolDiscovery.commandStatus)) && + (toolDiscovery.detail === undefined || typeof toolDiscovery.detail === "string") && + (toolDiscovery.failedStage === undefined || typeof toolDiscovery.failedStage === "string") && + (toolDiscovery.failureClass === undefined || typeof toolDiscovery.failureClass === "string") + ); +} + function parseMcpToolDiscoveryStatusJson(stdout: string): McpToolDiscoveryStatusJson | undefined { try { - return JSON.parse(stdout) as McpToolDiscoveryStatusJson; + const value: unknown = JSON.parse(stdout); + return isMcpToolDiscoveryStatusJson(value) ? value : undefined; } catch { return undefined; } diff --git a/test/e2e/support/mcp-bridge-tool-discovery.test.ts b/test/e2e/support/mcp-bridge-tool-discovery.test.ts index 40c74348134..6cac92f990f 100644 --- a/test/e2e/support/mcp-bridge-tool-discovery.test.ts +++ b/test/e2e/support/mcp-bridge-tool-discovery.test.ts @@ -405,20 +405,39 @@ describe("authenticated MCP tool discovery transport retry", () => { [ "a failed command", 1, + "not-json", "runtime failed", "openclaw mcp status --tools --json failed: not-json\nruntime failed", ], [ "a successful command", 0, + "not-json", + "", + "openclaw mcp status --tools --json did not return valid MCP discovery JSON", + ], + [ + "a successful command with incomplete JSON", + 0, + JSON.stringify({ + provider: { + registryPresent: true, + gatewayPresent: true, + attached: true, + credentialReady: true, + }, + policy: { registryPresent: true, gatewayPresent: true }, + adapter: { registered: true }, + toolDiscovery: {}, + }), "", "openclaw mcp status --tools --json did not return valid MCP discovery JSON", ], ])( "does not retry malformed status output from %s (#10944)", - async (_case, exitCode, stderr, expectedError) => { + async (_case, exitCode, stdout, stderr, expectedError) => { const host = { - nemoclaw: vi.fn(async () => ({ exitCode, stdout: "not-json", stderr })), + nemoclaw: vi.fn(async () => ({ exitCode, stdout, stderr })), } as unknown as Parameters[0]; const artifacts = discoveryArtifacts(); const progress = { event: vi.fn() }; From 62710c6fff2ab353a20cea200c87f12e174f009d Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Tue, 8 Sep 2026 15:39:07 -0700 Subject: [PATCH 09/10] fix(mcp): keep discovery core package-neutral Signed-off-by: Apurv Kumaria --- .../mcp-tool-discovery-image-contract.test.ts | 2 +- .../mcp-sdk-error.ts | 12 ++++++++ .../mcp-tool-discovery.ts | 20 +++++++++---- .../mcp-tool-discovery.bundle | 4 +-- .../streamable-http-client.test.ts | 13 ++++++--- .../tool-discovery-core.ts | 28 ++++++------------- 6 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts diff --git a/test/mcp/mcp-tool-discovery-image-contract.test.ts b/test/mcp/mcp-tool-discovery-image-contract.test.ts index 148f8a1ef76..7999c87c919 100644 --- a/test/mcp/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp/mcp-tool-discovery-image-contract.test.ts @@ -227,7 +227,7 @@ describe("MCP tool discovery image contract", () => { relativePath: "mcp-tool-discovery/THIRD_PARTY_LICENSES.txt", }, { - expectedHash: "864d44c272bd00b8eaa52917a23a22e64c1c10f113b68db27a98509863918a61", + expectedHash: "f27c92e5d5e6c3d49da1a90053516c3818a6261d798c81475d0f332c2eb1a727", relativePath: "mcp-tool-discovery/mcp-tool-discovery.bundle", }, ])("pins the reviewed image runtime artifacts exactly", ({ expectedHash, relativePath }) => { diff --git a/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts b/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts new file mode 100644 index 00000000000..f7635d644f2 --- /dev/null +++ b/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; + +import { ToolDiscoveryRuntimeError } from "./tool-discovery-core.ts"; + +export function normalizeMcpSdkError(error: unknown): unknown { + return error instanceof McpError && error.code === ErrorCode.RequestTimeout + ? new ToolDiscoveryRuntimeError("timeout") + : error; +} diff --git a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts index 078d0ab7276..678bb305d12 100644 --- a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts +++ b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts @@ -14,11 +14,20 @@ import { parseMcpToolDiscoveryArguments, runMcpToolDiscoverySession, } from "./tool-discovery-core.ts"; +import { normalizeMcpSdkError } from "./mcp-sdk-error.ts"; function writeResult(result: McpToolDiscoveryResult): void { process.stdout.write(`${JSON.stringify({ protocol: MCP_TOOL_DISCOVERY_PROTOCOL, ...result })}\n`); } +async function callMcpSdk(operation: () => Promise): Promise { + try { + return await operation(); + } catch (error) { + throw normalizeMcpSdkError(error); + } +} + async function main(): Promise { let runtimeArguments: { url: URL; credentialEnv: string }; try { @@ -83,11 +92,12 @@ async function main(): Promise { }; await runMcpToolDiscoverySession({ - connect: () => client.connect(transport, requestOptions), - loadPage: async (cursor) => { - const page = await client.listTools(cursor ? { cursor } : undefined, requestOptions); - return normalizeMcpToolPage(page); - }, + connect: () => callMcpSdk(() => client.connect(transport, requestOptions)), + loadPage: (cursor) => + callMcpSdk(async () => { + const page = await client.listTools(cursor ? { cursor } : undefined, requestOptions); + return normalizeMcpToolPage(page); + }), hasSession: () => Boolean(transport.sessionId), terminateSession: () => transport.terminateSession(), close: () => client.close(), diff --git a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle index faa6c14a3d3..0f9ecb15d46 100644 --- a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle +++ b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle @@ -74,8 +74,8 @@ ${value}`,dataLines++}else isEventPrefix(chunk,searchIndex,firstCharCode)?eventT `,searchIndex)}return chunk.slice(searchIndex)}for(;searchIndex20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function normalizeMcpToolDiscoveryError(error2){return error2 instanceof McpError&&error2.code===ErrorCode.RequestTimeout?new ToolDiscoveryRuntimeError("timeout"):error2}function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){const normalizedError=normalizeMcpToolDiscoveryError(error2);if(normalizedError instanceof ToolDiscoveryRuntimeError){switch(normalizedError.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof normalizedError.httpStatus==="number"?`MCP endpoint rejected the request (HTTP ${normalizedError.httpStatus})`:"MCP endpoint rejected the request";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP request failed"}function mcpToolDiscoveryFailure(error2,failedStage){const normalizedError=normalizeMcpToolDiscoveryError(error2);let failureClass;if(normalizedError instanceof ToolDiscoveryRuntimeError){if(normalizedError.code==="connection"||normalizedError.code==="timeout"){failureClass="connection"}else if(normalizedError.code==="http-error"&&(normalizedError.httpStatus===401||normalizedError.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(normalizedError),failedStage,failureClass}}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} -`)}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>client.connect(transport,requestOptions),loadPage:async cursor=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)},hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); +${value}`,dataLines++;break;case"id":id=value.includes("\0")?void 0:value;break;case"retry":/^\d+$/.test(value)?onRetry(parseInt(value,10)):onError(new ParseError(`Invalid \`retry\` value: "${value}"`,{type:"invalid-retry",value,line}));break;default:onError(new ParseError(`Unknown field "${field.length>20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected the request (HTTP ${error2.httpStatus})`:"MCP endpoint rejected the request";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function normalizeMcpSdkError(error2){return error2 instanceof McpError&&error2.code===ErrorCode.RequestTimeout?new ToolDiscoveryRuntimeError("timeout"):error2}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} +`)}async function callMcpSdk(operation){try{return await operation()}catch(error2){throw normalizeMcpSdkError(error2)}}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>callMcpSdk(()=>client.connect(transport,requestOptions)),loadPage:cursor=>callMcpSdk(async()=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)}),hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); /*! Bundled license information: content-type/index.js: diff --git a/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts b/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts index 9e9a2a0cb38..a142ed84918 100644 --- a/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts +++ b/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts @@ -19,10 +19,13 @@ import { normalizeMcpToolPage, runMcpToolDiscoverySession, } from "./tool-discovery-core.ts"; +import { normalizeMcpSdkError } from "./mcp-sdk-error.ts"; test("classifies only the SDK request-timeout code as a remote request timeout (#10944)", () => { const timeout = mcpToolDiscoveryFailure( - new McpError(ErrorCode.RequestTimeout, "Bearer untrusted-timeout-detail"), + normalizeMcpSdkError( + new McpError(ErrorCode.RequestTimeout, "Bearer untrusted-timeout-detail"), + ), "tool-discovery", ); assert.deepEqual(timeout, { @@ -36,9 +39,11 @@ test("classifies only the SDK request-timeout code as a remote request timeout ( }); const remoteFailure = mcpToolDiscoveryFailure( - new McpError( - ErrorCode.InternalError, - "remote tool operation timed out with Bearer untrusted-timeout-detail", + normalizeMcpSdkError( + new McpError( + ErrorCode.InternalError, + "remote tool operation timed out with Bearer untrusted-timeout-detail", + ), ), "tool-discovery", ); diff --git a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts index 2e32addb506..a3495359f1a 100644 --- a/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts +++ b/tools/mcp-tool-discovery-runtime/tool-discovery-core.ts @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; - export const MCP_TOOL_DISCOVERY_PROTOCOL = 2; export const MCP_TOOL_DISCOVERY_LIMITS = { @@ -129,12 +127,6 @@ export class ToolDiscoveryRuntimeError extends Error { } } -function normalizeMcpToolDiscoveryError(error: unknown): unknown { - return error instanceof McpError && error.code === ErrorCode.RequestTimeout - ? new ToolDiscoveryRuntimeError("timeout") - : error; -} - function utf8Bytes(value: string): number { return new TextEncoder().encode(value).byteLength; } @@ -356,14 +348,13 @@ export function createBoundedMcpFetch( } export function safeToolDiscoveryErrorDetail(error: unknown): string { - const normalizedError = normalizeMcpToolDiscoveryError(error); - if (normalizedError instanceof ToolDiscoveryRuntimeError) { - switch (normalizedError.code) { + if (error instanceof ToolDiscoveryRuntimeError) { + switch (error.code) { case "connection": return "MCP endpoint connection failed"; case "http-error": - return typeof normalizedError.httpStatus === "number" - ? `MCP endpoint rejected the request (HTTP ${normalizedError.httpStatus})` + return typeof error.httpStatus === "number" + ? `MCP endpoint rejected the request (HTTP ${error.httpStatus})` : "MCP endpoint rejected the request"; case "invalid-response": return "MCP endpoint returned an invalid tool-list response"; @@ -382,14 +373,13 @@ export function mcpToolDiscoveryFailure( error: unknown, failedStage: Extract, ): McpToolDiscoveryFailureResult { - const normalizedError = normalizeMcpToolDiscoveryError(error); let failureClass: McpToolDiscoveryFailureClass; - if (normalizedError instanceof ToolDiscoveryRuntimeError) { - if (normalizedError.code === "connection" || normalizedError.code === "timeout") { + if (error instanceof ToolDiscoveryRuntimeError) { + if (error.code === "connection" || error.code === "timeout") { failureClass = "connection"; } else if ( - normalizedError.code === "http-error" && - (normalizedError.httpStatus === 401 || normalizedError.httpStatus === 403) + error.code === "http-error" && + (error.httpStatus === 401 || error.httpStatus === 403) ) { failureClass = "authentication"; } else { @@ -403,7 +393,7 @@ export function mcpToolDiscoveryFailure( count: 0, tools: [], truncated: false, - detail: safeToolDiscoveryErrorDetail(normalizedError), + detail: safeToolDiscoveryErrorDetail(error), failedStage, failureClass, }; From fa636a6f1b820503070a91f4fb51fcc7b0c486ee Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Tue, 8 Sep 2026 16:33:16 -0700 Subject: [PATCH 10/10] fix(mcp): isolate SDK timeout adapter Signed-off-by: Apurv Kumaria --- .../mcp-tool-discovery-image-contract.test.ts | 2 +- .../mcp-sdk-error.ts | 12 ----------- .../mcp-tool-discovery.ts | 20 +++++++++++++++++-- .../mcp-tool-discovery.bundle | 6 +++--- .../streamable-http-client.test.ts | 2 +- 5 files changed, 23 insertions(+), 19 deletions(-) delete mode 100644 tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts diff --git a/test/mcp/mcp-tool-discovery-image-contract.test.ts b/test/mcp/mcp-tool-discovery-image-contract.test.ts index 7999c87c919..43194fa320a 100644 --- a/test/mcp/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp/mcp-tool-discovery-image-contract.test.ts @@ -227,7 +227,7 @@ describe("MCP tool discovery image contract", () => { relativePath: "mcp-tool-discovery/THIRD_PARTY_LICENSES.txt", }, { - expectedHash: "f27c92e5d5e6c3d49da1a90053516c3818a6261d798c81475d0f332c2eb1a727", + expectedHash: "e98d0875b06923ca023da35ef53193c0e72037afec954f63d26a2c5a02e9b705", relativePath: "mcp-tool-discovery/mcp-tool-discovery.bundle", }, ])("pins the reviewed image runtime artifacts exactly", ({ expectedHash, relativePath }) => { diff --git a/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts b/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts deleted file mode 100644 index f7635d644f2..00000000000 --- a/tools/mcp-tool-discovery-runtime/mcp-sdk-error.ts +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; - -import { ToolDiscoveryRuntimeError } from "./tool-discovery-core.ts"; - -export function normalizeMcpSdkError(error: unknown): unknown { - return error instanceof McpError && error.code === ErrorCode.RequestTimeout - ? new ToolDiscoveryRuntimeError("timeout") - : error; -} diff --git a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts index 678bb305d12..d747dfbf51a 100644 --- a/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts +++ b/tools/mcp-tool-discovery-runtime/mcp-tool-discovery.ts @@ -1,8 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { realpathSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; import { buildMcpToolDiscoveryAuthorizationPlaceholder, @@ -13,13 +17,19 @@ import { normalizeMcpToolPage, parseMcpToolDiscoveryArguments, runMcpToolDiscoverySession, + ToolDiscoveryRuntimeError, } from "./tool-discovery-core.ts"; -import { normalizeMcpSdkError } from "./mcp-sdk-error.ts"; function writeResult(result: McpToolDiscoveryResult): void { process.stdout.write(`${JSON.stringify({ protocol: MCP_TOOL_DISCOVERY_PROTOCOL, ...result })}\n`); } +export function normalizeMcpSdkError(error: unknown): unknown { + return error instanceof McpError && error.code === ErrorCode.RequestTimeout + ? new ToolDiscoveryRuntimeError("timeout") + : error; +} + async function callMcpSdk(operation: () => Promise): Promise { try { return await operation(); @@ -105,4 +115,10 @@ async function main(): Promise { }); } -await main(); +const entrypointPath = process.argv[1]; +if ( + entrypointPath && + realpathSync(fileURLToPath(import.meta.url)) === realpathSync(entrypointPath) +) { + await main(); +} diff --git a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle index 0f9ecb15d46..027479248d8 100644 --- a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle +++ b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/mcp-tool-discovery/mcp-tool-discovery.bundle @@ -4,7 +4,7 @@ var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropD || ${dataType} === "boolean" || ${data} === null`).assign(coerced,(0,codegen_1._)`[${data}]`)}}}function assignParentData({gen,parentData,parentDataProperty},expr){gen.if((0,codegen_1._)`${parentData} !== undefined`,()=>gen.assign((0,codegen_1._)`${parentData}[${parentDataProperty}]`,expr))}function checkDataType(dataType,data,strictNums,correct=DataType.Correct){const EQ=correct===DataType.Correct?codegen_1.operators.EQ:codegen_1.operators.NEQ;let cond;switch(dataType){case"null":return(0,codegen_1._)`${data} ${EQ} null`;case"array":cond=(0,codegen_1._)`Array.isArray(${data})`;break;case"object":cond=(0,codegen_1._)`${data} && typeof ${data} == "object" && !Array.isArray(${data})`;break;case"integer":cond=numCond((0,codegen_1._)`!(${data} % 1) && !isNaN(${data})`);break;case"number":cond=numCond();break;default:return(0,codegen_1._)`typeof ${data} ${EQ} ${dataType}`}return correct===DataType.Correct?cond:(0,codegen_1.not)(cond);function numCond(_cond=codegen_1.nil){return(0,codegen_1.and)((0,codegen_1._)`typeof ${data} == "number"`,_cond,strictNums?(0,codegen_1._)`isFinite(${data})`:codegen_1.nil)}}exports.checkDataType=checkDataType;function checkDataTypes(dataTypes,data,strictNums,correct){if(dataTypes.length===1){return checkDataType(dataTypes[0],data,strictNums,correct)}let cond;const types=(0,util_1.toHash)(dataTypes);if(types.array&&types.object){const notObj=(0,codegen_1._)`typeof ${data} != "object"`;cond=types.null?notObj:(0,codegen_1._)`!${data} || ${notObj}`;delete types.null;delete types.array;delete types.object}else{cond=codegen_1.nil}if(types.number)delete types.integer;for(const t in types)cond=(0,codegen_1.and)(cond,checkDataType(t,data,strictNums,correct));return cond}exports.checkDataTypes=checkDataTypes;var typeError={message:({schema})=>`must be ${schema}`,params:({schema,schemaValue})=>typeof schema=="string"?(0,codegen_1._)`{type: ${schema}}`:(0,codegen_1._)`{type: ${schemaValue}}`};function reportTypeError(it){const cxt=getTypeErrorContext(it);(0,errors_1.reportError)(cxt,typeError)}exports.reportTypeError=reportTypeError;function getTypeErrorContext(it){const{gen,data,schema}=it;const schemaCode=(0,util_1.schemaRefOrVal)(it,schema,"type");return{gen,keyword:"type",data,schema:schema.type,schemaCode,schemaValue:schemaCode,parentSchema:schema,params:{},it}}}});var require_defaults=__commonJS({"node_modules/ajv/dist/compile/validate/defaults.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.assignDefaults=void 0;var codegen_1=require_codegen();var util_1=require_util();function assignDefaults(it,ty){const{properties,items}=it.schema;if(ty==="object"&&properties){for(const key in properties){assignDefault(it,key,properties[key].default)}}else if(ty==="array"&&Array.isArray(items)){items.forEach((sch,i)=>assignDefault(it,i,sch.default))}}exports.assignDefaults=assignDefaults;function assignDefault(it,prop,defaultValue){const{gen,compositeRule,data,opts}=it;if(defaultValue===void 0)return;const childData=(0,codegen_1._)`${data}${(0,codegen_1.getProperty)(prop)}`;if(compositeRule){(0,util_1.checkStrictMode)(it,`default is ignored for: ${childData}`);return}let condition=(0,codegen_1._)`${childData} === undefined`;if(opts.useDefaults==="empty"){condition=(0,codegen_1._)`${condition} || ${childData} === null || ${childData} === ""`}gen.if(condition,(0,codegen_1._)`${childData} = ${(0,codegen_1.stringify)(defaultValue)}`)}}});var require_code2=__commonJS({"node_modules/ajv/dist/vocabularies/code.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.validateUnion=exports.validateArray=exports.usePattern=exports.callValidateCode=exports.schemaProperties=exports.allSchemaProperties=exports.noPropertyInData=exports.propertyInData=exports.isOwnProperty=exports.hasPropFunc=exports.reportMissingProp=exports.checkMissingProp=exports.checkReportMissingProp=void 0;var codegen_1=require_codegen();var util_1=require_util();var names_1=require_names();var util_2=require_util();function checkReportMissingProp(cxt,prop){const{gen,data,it}=cxt;gen.if(noPropertyInData(gen,data,prop,it.opts.ownProperties),()=>{cxt.setParams({missingProperty:(0,codegen_1._)`${prop}`},true);cxt.error()})}exports.checkReportMissingProp=checkReportMissingProp;function checkMissingProp({gen,data,it:{opts}},properties,missing){return(0,codegen_1.or)(...properties.map(prop=>(0,codegen_1.and)(noPropertyInData(gen,data,prop,opts.ownProperties),(0,codegen_1._)`${missing} = ${prop}`)))}exports.checkMissingProp=checkMissingProp;function reportMissingProp(cxt,missing){cxt.setParams({missingProperty:missing},true);cxt.error()}exports.reportMissingProp=reportMissingProp;function hasPropFunc(gen){return gen.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:(0,codegen_1._)`Object.prototype.hasOwnProperty`})}exports.hasPropFunc=hasPropFunc;function isOwnProperty(gen,data,property){return(0,codegen_1._)`${hasPropFunc(gen)}.call(${data}, ${property})`}exports.isOwnProperty=isOwnProperty;function propertyInData(gen,data,property,ownProperties){const cond=(0,codegen_1._)`${data}${(0,codegen_1.getProperty)(property)} !== undefined`;return ownProperties?(0,codegen_1._)`${cond} && ${isOwnProperty(gen,data,property)}`:cond}exports.propertyInData=propertyInData;function noPropertyInData(gen,data,property,ownProperties){const cond=(0,codegen_1._)`${data}${(0,codegen_1.getProperty)(property)} === undefined`;return ownProperties?(0,codegen_1.or)(cond,(0,codegen_1.not)(isOwnProperty(gen,data,property))):cond}exports.noPropertyInData=noPropertyInData;function allSchemaProperties(schemaMap){return schemaMap?Object.keys(schemaMap).filter(p=>p!=="__proto__"):[]}exports.allSchemaProperties=allSchemaProperties;function schemaProperties(it,schemaMap){return allSchemaProperties(schemaMap).filter(p=>!(0,util_1.alwaysValidSchema)(it,schemaMap[p]))}exports.schemaProperties=schemaProperties;function callValidateCode({schemaCode,data,it:{gen,topSchemaRef,schemaPath,errorPath},it},func,context,passSchema){const dataAndSchema=passSchema?(0,codegen_1._)`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}`:data;const valCxt=[[names_1.default.instancePath,(0,codegen_1.strConcat)(names_1.default.instancePath,errorPath)],[names_1.default.parentData,it.parentData],[names_1.default.parentDataProperty,it.parentDataProperty],[names_1.default.rootData,names_1.default.rootData]];if(it.opts.dynamicRef)valCxt.push([names_1.default.dynamicAnchors,names_1.default.dynamicAnchors]);const args=(0,codegen_1._)`${dataAndSchema}, ${gen.object(...valCxt)}`;return context!==codegen_1.nil?(0,codegen_1._)`${func}.call(${context}, ${args})`:(0,codegen_1._)`${func}(${args})`}exports.callValidateCode=callValidateCode;var newRegExp=(0,codegen_1._)`new RegExp`;function usePattern({gen,it:{opts}},pattern){const u=opts.unicodeRegExp?"u":"";const{regExp}=opts.code;const rx=regExp(pattern,u);return gen.scopeValue("pattern",{key:rx.toString(),ref:rx,code:(0,codegen_1._)`${regExp.code==="new RegExp"?newRegExp:(0,util_2.useFunc)(gen,regExp)}(${pattern}, ${u})`})}exports.usePattern=usePattern;function validateArray(cxt){const{gen,data,keyword,it}=cxt;const valid=gen.name("valid");if(it.allErrors){const validArr=gen.let("valid",true);validateItems(()=>gen.assign(validArr,false));return validArr}gen.var(valid,true);validateItems(()=>gen.break());return valid;function validateItems(notValid){const len=gen.const("len",(0,codegen_1._)`${data}.length`);gen.forRange("i",0,len,i=>{cxt.subschema({keyword,dataProp:i,dataPropType:util_1.Type.Num},valid);gen.if((0,codegen_1.not)(valid),notValid)})}}exports.validateArray=validateArray;function validateUnion(cxt){const{gen,schema,keyword,it}=cxt;if(!Array.isArray(schema))throw new Error("ajv implementation error");const alwaysValid=schema.some(sch=>(0,util_1.alwaysValidSchema)(it,sch));if(alwaysValid&&!it.opts.unevaluated)return;const valid=gen.let("valid",false);const schValid=gen.name("_valid");gen.block(()=>schema.forEach((_sch,i)=>{const schCxt=cxt.subschema({keyword,schemaProp:i,compositeRule:true},schValid);gen.assign(valid,(0,codegen_1._)`${valid} || ${schValid}`);const merged=cxt.mergeValidEvaluated(schCxt,schValid);if(!merged)gen.if((0,codegen_1.not)(valid))}));cxt.result(valid,()=>cxt.reset(),()=>cxt.error(true))}exports.validateUnion=validateUnion}});var require_keyword=__commonJS({"node_modules/ajv/dist/compile/validate/keyword.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.validateKeywordUsage=exports.validSchemaType=exports.funcKeywordCode=exports.macroKeywordCode=void 0;var codegen_1=require_codegen();var names_1=require_names();var code_1=require_code2();var errors_1=require_errors();function macroKeywordCode(cxt,def){const{gen,keyword,schema,parentSchema,it}=cxt;const macroSchema=def.macro.call(it.self,schema,parentSchema,it);const schemaRef=useKeyword(gen,keyword,macroSchema);if(it.opts.validateSchema!==false)it.self.validateSchema(macroSchema,true);const valid=gen.name("valid");cxt.subschema({schema:macroSchema,schemaPath:codegen_1.nil,errSchemaPath:`${it.errSchemaPath}/${keyword}`,topSchemaRef:schemaRef,compositeRule:true},valid);cxt.pass(valid,()=>cxt.error(true))}exports.macroKeywordCode=macroKeywordCode;function funcKeywordCode(cxt,def){var _a3;const{gen,keyword,schema,parentSchema,$data,it}=cxt;checkAsyncKeyword(it,def);const validate=!$data&&def.compile?def.compile.call(it.self,schema,parentSchema,it):def.validate;const validateRef=useKeyword(gen,keyword,validate);const valid=gen.let("valid");cxt.block$data(valid,validateKeyword);cxt.ok((_a3=def.valid)!==null&&_a3!==void 0?_a3:valid);function validateKeyword(){if(def.errors===false){assignValid();if(def.modifying)modifyData(cxt);reportErrs(()=>cxt.error())}else{const ruleErrs=def.async?validateAsync():validateSync();if(def.modifying)modifyData(cxt);reportErrs(()=>addErrs(cxt,ruleErrs))}}function validateAsync(){const ruleErrs=gen.let("ruleErrs",null);gen.try(()=>assignValid((0,codegen_1._)`await `),e=>gen.assign(valid,false).if((0,codegen_1._)`${e} instanceof ${it.ValidationError}`,()=>gen.assign(ruleErrs,(0,codegen_1._)`${e}.errors`),()=>gen.throw(e)));return ruleErrs}function validateSync(){const validateErrs=(0,codegen_1._)`${validateRef}.errors`;gen.assign(validateErrs,null);assignValid(codegen_1.nil);return validateErrs}function assignValid(_await=def.async?(0,codegen_1._)`await `:codegen_1.nil){const passCxt=it.opts.passContext?names_1.default.this:names_1.default.self;const passSchema=!("compile"in def&&!$data||def.schema===false);gen.assign(valid,(0,codegen_1._)`${_await}${(0,code_1.callValidateCode)(cxt,validateRef,passCxt,passSchema)}`,def.modifying)}function reportErrs(errors){var _a4;gen.if((0,codegen_1.not)((_a4=def.valid)!==null&&_a4!==void 0?_a4:valid),errors)}}exports.funcKeywordCode=funcKeywordCode;function modifyData(cxt){const{gen,data,it}=cxt;gen.if(it.parentData,()=>gen.assign(data,(0,codegen_1._)`${it.parentData}[${it.parentDataProperty}]`))}function addErrs(cxt,errs){const{gen}=cxt;gen.if((0,codegen_1._)`Array.isArray(${errs})`,()=>{gen.assign(names_1.default.vErrors,(0,codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`).assign(names_1.default.errors,(0,codegen_1._)`${names_1.default.vErrors}.length`);(0,errors_1.extendErrors)(cxt)},()=>cxt.error())}function checkAsyncKeyword({schemaEnv},def){if(def.async&&!schemaEnv.$async)throw new Error("async keyword in sync schema")}function useKeyword(gen,keyword,result){if(result===void 0)throw new Error(`keyword "${keyword}" failed to compile`);return gen.scopeValue("keyword",typeof result=="function"?{ref:result}:{ref:result,code:(0,codegen_1.stringify)(result)})}function validSchemaType(schema,schemaType,allowUndefined=false){return!schemaType.length||schemaType.some(st=>st==="array"?Array.isArray(schema):st==="object"?schema&&typeof schema=="object"&&!Array.isArray(schema):typeof schema==st||allowUndefined&&typeof schema=="undefined")}exports.validSchemaType=validSchemaType;function validateKeywordUsage({schema,opts,self,errSchemaPath},def,keyword){if(Array.isArray(def.keyword)?!def.keyword.includes(keyword):def.keyword!==keyword){throw new Error("ajv implementation error")}const deps=def.dependencies;if(deps===null||deps===void 0?void 0:deps.some(kwd=>!Object.prototype.hasOwnProperty.call(schema,kwd))){throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`)}if(def.validateSchema){const valid=def.validateSchema(schema[keyword]);if(!valid){const msg=`keyword "${keyword}" value is invalid at path "${errSchemaPath}": `+self.errorsText(def.validateSchema.errors);if(opts.validateSchema==="log")self.logger.error(msg);else throw new Error(msg)}}}exports.validateKeywordUsage=validateKeywordUsage}});var require_subschema=__commonJS({"node_modules/ajv/dist/compile/validate/subschema.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.extendSubschemaMode=exports.extendSubschemaData=exports.getSubschema=void 0;var codegen_1=require_codegen();var util_1=require_util();function getSubschema(it,{keyword,schemaProp,schema,schemaPath,errSchemaPath,topSchemaRef}){if(keyword!==void 0&&schema!==void 0){throw new Error('both "keyword" and "schema" passed, only one allowed')}if(keyword!==void 0){const sch=it.schema[keyword];return schemaProp===void 0?{schema:sch,schemaPath:(0,codegen_1._)`${it.schemaPath}${(0,codegen_1.getProperty)(keyword)}`,errSchemaPath:`${it.errSchemaPath}/${keyword}`}:{schema:sch[schemaProp],schemaPath:(0,codegen_1._)`${it.schemaPath}${(0,codegen_1.getProperty)(keyword)}${(0,codegen_1.getProperty)(schemaProp)}`,errSchemaPath:`${it.errSchemaPath}/${keyword}/${(0,util_1.escapeFragment)(schemaProp)}`}}if(schema!==void 0){if(schemaPath===void 0||errSchemaPath===void 0||topSchemaRef===void 0){throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"')}return{schema,schemaPath,topSchemaRef,errSchemaPath}}throw new Error('either "keyword" or "schema" must be passed')}exports.getSubschema=getSubschema;function extendSubschemaData(subschema,it,{dataProp,dataPropType:dpType,data,dataTypes,propertyName}){if(data!==void 0&&dataProp!==void 0){throw new Error('both "data" and "dataProp" passed, only one allowed')}const{gen}=it;if(dataProp!==void 0){const{errorPath,dataPathArr,opts}=it;const nextData=gen.let("data",(0,codegen_1._)`${it.data}${(0,codegen_1.getProperty)(dataProp)}`,true);dataContextProps(nextData);subschema.errorPath=(0,codegen_1.str)`${errorPath}${(0,util_1.getErrorPath)(dataProp,dpType,opts.jsPropertySyntax)}`;subschema.parentDataProperty=(0,codegen_1._)`${dataProp}`;subschema.dataPathArr=[...dataPathArr,subschema.parentDataProperty]}if(data!==void 0){const nextData=data instanceof codegen_1.Name?data:gen.let("data",data,true);dataContextProps(nextData);if(propertyName!==void 0)subschema.propertyName=propertyName}if(dataTypes)subschema.dataTypes=dataTypes;function dataContextProps(_nextData){subschema.data=_nextData;subschema.dataLevel=it.dataLevel+1;subschema.dataTypes=[];it.definedProperties=new Set;subschema.parentData=it.data;subschema.dataNames=[...it.dataNames,_nextData]}}exports.extendSubschemaData=extendSubschemaData;function extendSubschemaMode(subschema,{jtdDiscriminator,jtdMetadata,compositeRule,createErrors,allErrors}){if(compositeRule!==void 0)subschema.compositeRule=compositeRule;if(createErrors!==void 0)subschema.createErrors=createErrors;if(allErrors!==void 0)subschema.allErrors=allErrors;subschema.jtdDiscriminator=jtdDiscriminator;subschema.jtdMetadata=jtdMetadata}exports.extendSubschemaMode=extendSubschemaMode}});var require_fast_deep_equal=__commonJS({"node_modules/fast-deep-equal/index.js"(exports,module){"use strict";module.exports=function equal(a,b){if(a===b)return true;if(a&&b&&typeof a=="object"&&typeof b=="object"){if(a.constructor!==b.constructor)return false;var length,i,keys;if(Array.isArray(a)){length=a.length;if(length!=b.length)return false;for(i=length;i--!==0;)if(!equal(a[i],b[i]))return false;return true}if(a.constructor===RegExp)return a.source===b.source&&a.flags===b.flags;if(a.valueOf!==Object.prototype.valueOf)return a.valueOf()===b.valueOf();if(a.toString!==Object.prototype.toString)return a.toString()===b.toString();keys=Object.keys(a);length=keys.length;if(length!==Object.keys(b).length)return false;for(i=length;i--!==0;)if(!Object.prototype.hasOwnProperty.call(b,keys[i]))return false;for(i=length;i--!==0;){var key=keys[i];if(!equal(a[key],b[key]))return false}return true}return a!==a&&b!==b}}});var require_json_schema_traverse=__commonJS({"node_modules/json-schema-traverse/index.js"(exports,module){"use strict";var traverse=module.exports=function(schema,opts,cb){if(typeof opts=="function"){cb=opts;opts={}}cb=opts.cb||cb;var pre=typeof cb=="function"?cb:cb.pre||function(){};var post=cb.post||function(){};_traverse(opts,pre,post,schema,"",schema)};traverse.keywords={additionalItems:true,items:true,contains:true,additionalProperties:true,propertyNames:true,not:true,if:true,then:true,else:true};traverse.arrayKeywords={items:true,allOf:true,anyOf:true,oneOf:true};traverse.propsKeywords={$defs:true,definitions:true,properties:true,patternProperties:true,dependencies:true};traverse.skipKeywords={default:true,enum:true,const:true,required:true,maximum:true,minimum:true,exclusiveMaximum:true,exclusiveMinimum:true,multipleOf:true,maxLength:true,minLength:true,pattern:true,format:true,maxItems:true,minItems:true,uniqueItems:true,maxProperties:true,minProperties:true};function _traverse(opts,pre,post,schema,jsonPtr,rootSchema,parentJsonPtr,parentKeyword,parentSchema,keyIndex){if(schema&&typeof schema=="object"&&!Array.isArray(schema)){pre(schema,jsonPtr,rootSchema,parentJsonPtr,parentKeyword,parentSchema,keyIndex);for(var key in schema){var sch=schema[key];if(Array.isArray(sch)){if(key in traverse.arrayKeywords){for(var i=0;icount+=countKeys(sch))}if(count===Infinity)return Infinity}return count}function getFullPath(resolver,id="",normalize){if(normalize!==false)id=normalizeId(id);const p=resolver.parse(id);return _getFullPath(resolver,p)}exports.getFullPath=getFullPath;function _getFullPath(resolver,p){const serialized=resolver.serialize(p);return serialized.split("#")[0]+"#"}exports._getFullPath=_getFullPath;var TRAILING_SLASH_HASH=/#\/?$/;function normalizeId(id){return id?id.replace(TRAILING_SLASH_HASH,""):""}exports.normalizeId=normalizeId;function resolveUrl(resolver,baseId,id){id=normalizeId(id);return resolver.resolve(baseId,id)}exports.resolveUrl=resolveUrl;var ANCHOR=/^[a-z_][-a-z0-9._]*$/i;function getSchemaRefs(schema,baseId){if(typeof schema=="boolean")return{};const{schemaId,uriResolver}=this.opts;const schId=normalizeId(schema[schemaId]||baseId);const baseIds={"":schId};const pathPrefix=getFullPath(uriResolver,schId,false);const localRefs={};const schemaRefs=new Set;traverse(schema,{allKeys:true},(sch,jsonPtr,_,parentJsonPtr)=>{if(parentJsonPtr===void 0)return;const fullPath=pathPrefix+jsonPtr;let innerBaseId=baseIds[parentJsonPtr];if(typeof sch[schemaId]=="string")innerBaseId=addRef.call(this,sch[schemaId]);addAnchor.call(this,sch.$anchor);addAnchor.call(this,sch.$dynamicAnchor);baseIds[jsonPtr]=innerBaseId;function addRef(ref){const _resolve=this.opts.uriResolver.resolve;ref=normalizeId(innerBaseId?_resolve(innerBaseId,ref):ref);if(schemaRefs.has(ref))throw ambiguos(ref);schemaRefs.add(ref);let schOrRef=this.refs[ref];if(typeof schOrRef=="string")schOrRef=this.refs[schOrRef];if(typeof schOrRef=="object"){checkAmbiguosRef(sch,schOrRef.schema,ref)}else if(ref!==normalizeId(fullPath)){if(ref[0]==="#"){checkAmbiguosRef(sch,localRefs[ref],ref);localRefs[ref]=sch}else{this.refs[ref]=fullPath}}return ref}function addAnchor(anchor){if(typeof anchor=="string"){if(!ANCHOR.test(anchor))throw new Error(`invalid anchor "${anchor}"`);addRef.call(this,`#${anchor}`)}}});return localRefs;function checkAmbiguosRef(sch1,sch2,ref){if(sch2!==void 0&&!equal(sch1,sch2))throw ambiguos(ref)}function ambiguos(ref){return new Error(`reference "${ref}" resolves to more than one schema`)}}exports.getSchemaRefs=getSchemaRefs}});var require_validate=__commonJS({"node_modules/ajv/dist/compile/validate/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getData=exports.KeywordCxt=exports.validateFunctionCode=void 0;var boolSchema_1=require_boolSchema();var dataType_1=require_dataType();var applicability_1=require_applicability();var dataType_2=require_dataType();var defaults_1=require_defaults();var keyword_1=require_keyword();var subschema_1=require_subschema();var codegen_1=require_codegen();var names_1=require_names();var resolve_1=require_resolve();var util_1=require_util();var errors_1=require_errors();function validateFunctionCode(it){if(isSchemaObj(it)){checkKeywords(it);if(schemaCxtHasRules(it)){topSchemaObjCode(it);return}}validateFunction(it,()=>(0,boolSchema_1.topBoolOrEmptySchema)(it))}exports.validateFunctionCode=validateFunctionCode;function validateFunction({gen,validateName,schema,schemaEnv,opts},body){if(opts.code.es5){gen.func(validateName,(0,codegen_1._)`${names_1.default.data}, ${names_1.default.valCxt}`,schemaEnv.$async,()=>{gen.code((0,codegen_1._)`"use strict"; ${funcSourceUrl(schema,opts)}`);destructureValCxtES5(gen,opts);gen.code(body)})}else{gen.func(validateName,(0,codegen_1._)`${names_1.default.data}, ${destructureValCxt(opts)}`,schemaEnv.$async,()=>gen.code(funcSourceUrl(schema,opts)).code(body))}}function destructureValCxt(opts){return(0,codegen_1._)`{${names_1.default.instancePath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef?(0,codegen_1._)`, ${names_1.default.dynamicAnchors}={}`:codegen_1.nil}}={}`}function destructureValCxtES5(gen,opts){gen.if(names_1.default.valCxt,()=>{gen.var(names_1.default.instancePath,(0,codegen_1._)`${names_1.default.valCxt}.${names_1.default.instancePath}`);gen.var(names_1.default.parentData,(0,codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentData}`);gen.var(names_1.default.parentDataProperty,(0,codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentDataProperty}`);gen.var(names_1.default.rootData,(0,codegen_1._)`${names_1.default.valCxt}.${names_1.default.rootData}`);if(opts.dynamicRef)gen.var(names_1.default.dynamicAnchors,(0,codegen_1._)`${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`)},()=>{gen.var(names_1.default.instancePath,(0,codegen_1._)`""`);gen.var(names_1.default.parentData,(0,codegen_1._)`undefined`);gen.var(names_1.default.parentDataProperty,(0,codegen_1._)`undefined`);gen.var(names_1.default.rootData,names_1.default.data);if(opts.dynamicRef)gen.var(names_1.default.dynamicAnchors,(0,codegen_1._)`{}`)})}function topSchemaObjCode(it){const{schema,opts,gen}=it;validateFunction(it,()=>{if(opts.$comment&&schema.$comment)commentKeyword(it);checkNoDefault(it);gen.let(names_1.default.vErrors,null);gen.let(names_1.default.errors,0);if(opts.unevaluated)resetEvaluated(it);typeAndKeywords(it);returnResults(it)});return}function resetEvaluated(it){const{gen,validateName}=it;it.evaluated=gen.const("evaluated",(0,codegen_1._)`${validateName}.evaluated`);gen.if((0,codegen_1._)`${it.evaluated}.dynamicProps`,()=>gen.assign((0,codegen_1._)`${it.evaluated}.props`,(0,codegen_1._)`undefined`));gen.if((0,codegen_1._)`${it.evaluated}.dynamicItems`,()=>gen.assign((0,codegen_1._)`${it.evaluated}.items`,(0,codegen_1._)`undefined`))}function funcSourceUrl(schema,opts){const schId=typeof schema=="object"&&schema[opts.schemaId];return schId&&(opts.code.source||opts.code.process)?(0,codegen_1._)`/*# sourceURL=${schId} */`:codegen_1.nil}function subschemaCode(it,valid){if(isSchemaObj(it)){checkKeywords(it);if(schemaCxtHasRules(it)){subSchemaObjCode(it,valid);return}}(0,boolSchema_1.boolOrEmptySchema)(it,valid)}function schemaCxtHasRules({schema,self}){if(typeof schema=="boolean")return!schema;for(const key in schema)if(self.RULES.all[key])return true;return false}function isSchemaObj(it){return typeof it.schema!="boolean"}function subSchemaObjCode(it,valid){const{schema,gen,opts}=it;if(opts.$comment&&schema.$comment)commentKeyword(it);updateContext(it);checkAsyncSchema(it);const errsCount=gen.const("_errs",names_1.default.errors);typeAndKeywords(it,errsCount);gen.var(valid,(0,codegen_1._)`${errsCount} === ${names_1.default.errors}`)}function checkKeywords(it){(0,util_1.checkUnknownRules)(it);checkRefsAndKeywords(it)}function typeAndKeywords(it,errsCount){if(it.opts.jtd)return schemaKeywords(it,[],false,errsCount);const types=(0,dataType_1.getSchemaTypes)(it.schema);const checkedTypes=(0,dataType_1.coerceAndCheckDataType)(it,types);schemaKeywords(it,types,!checkedTypes,errsCount)}function checkRefsAndKeywords(it){const{schema,errSchemaPath,opts,self}=it;if(schema.$ref&&opts.ignoreKeywordsWithRef&&(0,util_1.schemaHasRulesButRef)(schema,self.RULES)){self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`)}}function checkNoDefault(it){const{schema,opts}=it;if(schema.default!==void 0&&opts.useDefaults&&opts.strictSchema){(0,util_1.checkStrictMode)(it,"default is ignored in the schema root")}}function updateContext(it){const schId=it.schema[it.opts.schemaId];if(schId)it.baseId=(0,resolve_1.resolveUrl)(it.opts.uriResolver,it.baseId,schId)}function checkAsyncSchema(it){if(it.schema.$async&&!it.schemaEnv.$async)throw new Error("async schema in sync schema")}function commentKeyword({gen,schemaEnv,schema,errSchemaPath,opts}){const msg=schema.$comment;if(opts.$comment===true){gen.code((0,codegen_1._)`${names_1.default.self}.logger.log(${msg})`)}else if(typeof opts.$comment=="function"){const schemaPath=(0,codegen_1.str)`${errSchemaPath}/$comment`;const rootName=gen.scopeValue("root",{ref:schemaEnv.root});gen.code((0,codegen_1._)`${names_1.default.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`)}}function returnResults(it){const{gen,schemaEnv,validateName,ValidationError,opts}=it;if(schemaEnv.$async){gen.if((0,codegen_1._)`${names_1.default.errors} === 0`,()=>gen.return(names_1.default.data),()=>gen.throw((0,codegen_1._)`new ${ValidationError}(${names_1.default.vErrors})`))}else{gen.assign((0,codegen_1._)`${validateName}.errors`,names_1.default.vErrors);if(opts.unevaluated)assignEvaluated(it);gen.return((0,codegen_1._)`${names_1.default.errors} === 0`)}}function assignEvaluated({gen,evaluated,props,items}){if(props instanceof codegen_1.Name)gen.assign((0,codegen_1._)`${evaluated}.props`,props);if(items instanceof codegen_1.Name)gen.assign((0,codegen_1._)`${evaluated}.items`,items)}function schemaKeywords(it,types,typeErrors,errsCount){const{gen,schema,data,allErrors,opts,self}=it;const{RULES}=self;if(schema.$ref&&(opts.ignoreKeywordsWithRef||!(0,util_1.schemaHasRulesButRef)(schema,RULES))){gen.block(()=>keywordCode(it,"$ref",RULES.all.$ref.definition));return}if(!opts.jtd)checkStrictTypes(it,types);gen.block(()=>{for(const group of RULES.rules)groupKeywords(group);groupKeywords(RULES.post)});function groupKeywords(group){if(!(0,applicability_1.shouldUseGroup)(schema,group))return;if(group.type){gen.if((0,dataType_2.checkDataType)(group.type,data,opts.strictNumbers));iterateKeywords(it,group);if(types.length===1&&types[0]===group.type&&typeErrors){gen.else();(0,dataType_2.reportTypeError)(it)}gen.endIf()}else{iterateKeywords(it,group)}if(!allErrors)gen.if((0,codegen_1._)`${names_1.default.errors} === ${errsCount||0}`)}}function iterateKeywords(it,group){const{gen,schema,opts:{useDefaults}}=it;if(useDefaults)(0,defaults_1.assignDefaults)(it,group.type);gen.block(()=>{for(const rule of group.rules){if((0,applicability_1.shouldUseRule)(schema,rule)){keywordCode(it,rule.keyword,rule.definition,group.type)}}})}function checkStrictTypes(it,types){if(it.schemaEnv.meta||!it.opts.strictTypes)return;checkContextTypes(it,types);if(!it.opts.allowUnionTypes)checkMultipleTypes(it,types);checkKeywordTypes(it,it.dataTypes)}function checkContextTypes(it,types){if(!types.length)return;if(!it.dataTypes.length){it.dataTypes=types;return}types.forEach(t=>{if(!includesType(it.dataTypes,t)){strictTypesError(it,`type "${t}" not allowed by context "${it.dataTypes.join(",")}"`)}});narrowSchemaTypes(it,types)}function checkMultipleTypes(it,ts){if(ts.length>1&&!(ts.length===2&&ts.includes("null"))){strictTypesError(it,"use allowUnionTypes to allow union type keyword")}}function checkKeywordTypes(it,ts){const rules=it.self.RULES.all;for(const keyword in rules){const rule=rules[keyword];if(typeof rule=="object"&&(0,applicability_1.shouldUseRule)(it.schema,rule)){const{type}=rule.definition;if(type.length&&!type.some(t=>hasApplicableType(ts,t))){strictTypesError(it,`missing type "${type.join(",")}" for keyword "${keyword}"`)}}}}function hasApplicableType(schTs,kwdT){return schTs.includes(kwdT)||kwdT==="number"&&schTs.includes("integer")}function includesType(ts,t){return ts.includes(t)||t==="integer"&&ts.includes("number")}function narrowSchemaTypes(it,withTypes){const ts=[];for(const t of it.dataTypes){if(includesType(withTypes,t))ts.push(t);else if(withTypes.includes("integer")&&t==="number")ts.push("integer")}it.dataTypes=ts}function strictTypesError(it,msg){const schemaPath=it.schemaEnv.baseId+it.errSchemaPath;msg+=` at "${schemaPath}" (strictTypes)`;(0,util_1.checkStrictMode)(it,msg,it.opts.strictTypes)}var KeywordCxt=class{constructor(it,def,keyword){(0,keyword_1.validateKeywordUsage)(it,def,keyword);this.gen=it.gen;this.allErrors=it.allErrors;this.keyword=keyword;this.data=it.data;this.schema=it.schema[keyword];this.$data=def.$data&&it.opts.$data&&this.schema&&this.schema.$data;this.schemaValue=(0,util_1.schemaRefOrVal)(it,this.schema,keyword,this.$data);this.schemaType=def.schemaType;this.parentSchema=it.schema;this.params={};this.it=it;this.def=def;if(this.$data){this.schemaCode=it.gen.const("vSchema",getData(this.$data,it))}else{this.schemaCode=this.schemaValue;if(!(0,keyword_1.validSchemaType)(this.schema,def.schemaType,def.allowUndefined)){throw new Error(`${keyword} value must be ${JSON.stringify(def.schemaType)}`)}}if("code"in def?def.trackErrors:def.errors!==false){this.errsCount=it.gen.const("_errs",names_1.default.errors)}}result(condition,successAction,failAction){this.failResult((0,codegen_1.not)(condition),successAction,failAction)}failResult(condition,successAction,failAction){this.gen.if(condition);if(failAction)failAction();else this.error();if(successAction){this.gen.else();successAction();if(this.allErrors)this.gen.endIf()}else{if(this.allErrors)this.gen.endIf();else this.gen.else()}}pass(condition,failAction){this.failResult((0,codegen_1.not)(condition),void 0,failAction)}fail(condition){if(condition===void 0){this.error();if(!this.allErrors)this.gen.if(false);return}this.gen.if(condition);this.error();if(this.allErrors)this.gen.endIf();else this.gen.else()}fail$data(condition){if(!this.$data)return this.fail(condition);const{schemaCode}=this;this.fail((0,codegen_1._)`${schemaCode} !== undefined && (${(0,codegen_1.or)(this.invalid$data(),condition)})`)}error(append,errorParams,errorPaths){if(errorParams){this.setParams(errorParams);this._error(append,errorPaths);this.setParams({});return}this._error(append,errorPaths)}_error(append,errorPaths){;(append?errors_1.reportExtraError:errors_1.reportError)(this,this.def.error,errorPaths)}$dataError(){(0,errors_1.reportError)(this,this.def.$dataError||errors_1.keyword$DataError)}reset(){if(this.errsCount===void 0)throw new Error('add "trackErrors" to keyword definition');(0,errors_1.resetErrorsCount)(this.gen,this.errsCount)}ok(cond){if(!this.allErrors)this.gen.if(cond)}setParams(obj,assign){if(assign)Object.assign(this.params,obj);else this.params=obj}block$data(valid,codeBlock,$dataValid=codegen_1.nil){this.gen.block(()=>{this.check$data(valid,$dataValid);codeBlock()})}check$data(valid=codegen_1.nil,$dataValid=codegen_1.nil){if(!this.$data)return;const{gen,schemaCode,schemaType,def}=this;gen.if((0,codegen_1.or)((0,codegen_1._)`${schemaCode} === undefined`,$dataValid));if(valid!==codegen_1.nil)gen.assign(valid,true);if(schemaType.length||def.validateSchema){gen.elseIf(this.invalid$data());this.$dataError();if(valid!==codegen_1.nil)gen.assign(valid,false)}gen.else()}invalid$data(){const{gen,schemaCode,schemaType,def,it}=this;return(0,codegen_1.or)(wrong$DataType(),invalid$DataSchema());function wrong$DataType(){if(schemaType.length){if(!(schemaCode instanceof codegen_1.Name))throw new Error("ajv implementation error");const st=Array.isArray(schemaType)?schemaType:[schemaType];return(0,codegen_1._)`${(0,dataType_2.checkDataTypes)(st,schemaCode,it.opts.strictNumbers,dataType_2.DataType.Wrong)}`}return codegen_1.nil}function invalid$DataSchema(){if(def.validateSchema){const validateSchemaRef=gen.scopeValue("validate$data",{ref:def.validateSchema});return(0,codegen_1._)`!${validateSchemaRef}(${schemaCode})`}return codegen_1.nil}}subschema(appl,valid){const subschema=(0,subschema_1.getSubschema)(this.it,appl);(0,subschema_1.extendSubschemaData)(subschema,this.it,appl);(0,subschema_1.extendSubschemaMode)(subschema,appl);const nextContext={...this.it,...subschema,items:void 0,props:void 0};subschemaCode(nextContext,valid);return nextContext}mergeEvaluated(schemaCxt,toName){const{it,gen}=this;if(!it.opts.unevaluated)return;if(it.props!==true&&schemaCxt.props!==void 0){it.props=util_1.mergeEvaluated.props(gen,schemaCxt.props,it.props,toName)}if(it.items!==true&&schemaCxt.items!==void 0){it.items=util_1.mergeEvaluated.items(gen,schemaCxt.items,it.items,toName)}}mergeValidEvaluated(schemaCxt,valid){const{it,gen}=this;if(it.opts.unevaluated&&(it.props!==true||it.items!==true)){gen.if(valid,()=>this.mergeEvaluated(schemaCxt,codegen_1.Name));return true}}};exports.KeywordCxt=KeywordCxt;function keywordCode(it,keyword,def,ruleType){const cxt=new KeywordCxt(it,def,keyword);if("code"in def){def.code(cxt,ruleType)}else if(cxt.$data&&def.validate){(0,keyword_1.funcKeywordCode)(cxt,def)}else if("macro"in def){(0,keyword_1.macroKeywordCode)(cxt,def)}else if(def.compile||def.validate){(0,keyword_1.funcKeywordCode)(cxt,def)}}var JSON_POINTER=/^\/(?:[^~]|~0|~1)*$/;var RELATIVE_JSON_POINTER=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function getData($data,{dataLevel,dataNames,dataPathArr}){let jsonPointer;let data;if($data==="")return names_1.default.rootData;if($data[0]==="/"){if(!JSON_POINTER.test($data))throw new Error(`Invalid JSON-pointer: ${$data}`);jsonPointer=$data;data=names_1.default.rootData}else{const matches=RELATIVE_JSON_POINTER.exec($data);if(!matches)throw new Error(`Invalid JSON-pointer: ${$data}`);const up=+matches[1];jsonPointer=matches[2];if(jsonPointer==="#"){if(up>=dataLevel)throw new Error(errorMsg("property/index",up));return dataPathArr[dataLevel-up]}if(up>dataLevel)throw new Error(errorMsg("data",up));data=dataNames[dataLevel-up];if(!jsonPointer)return data}let expr=data;const segments=jsonPointer.split("/");for(const segment of segments){if(segment){data=(0,codegen_1._)`${data}${(0,codegen_1.getProperty)((0,util_1.unescapeJsonPointer)(segment))}`;expr=(0,codegen_1._)`${expr} && ${data}`}}return expr;function errorMsg(pointerType,up){return`Cannot access ${pointerType} ${up} levels up, current level is ${dataLevel}`}}exports.getData=getData}});var require_validation_error=__commonJS({"node_modules/ajv/dist/runtime/validation_error.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var ValidationError=class extends Error{constructor(errors){super("validation failed");this.errors=errors;this.ajv=this.validation=true}};exports.default=ValidationError}});var require_ref_error=__commonJS({"node_modules/ajv/dist/compile/ref_error.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var resolve_1=require_resolve();var MissingRefError=class extends Error{constructor(resolver,baseId,ref,msg){super(msg||`can't resolve reference ${ref} from id ${baseId}`);this.missingRef=(0,resolve_1.resolveUrl)(resolver,baseId,ref);this.missingSchema=(0,resolve_1.normalizeId)((0,resolve_1.getFullPath)(resolver,this.missingRef))}};exports.default=MissingRefError}});var require_compile=__commonJS({"node_modules/ajv/dist/compile/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.resolveSchema=exports.getCompilingSchema=exports.resolveRef=exports.compileSchema=exports.SchemaEnv=void 0;var codegen_1=require_codegen();var validation_error_1=require_validation_error();var names_1=require_names();var resolve_1=require_resolve();var util_1=require_util();var validate_1=require_validate();var SchemaEnv=class{constructor(env){var _a3;this.refs={};this.dynamicAnchors={};let schema;if(typeof env.schema=="object")schema=env.schema;this.schema=env.schema;this.schemaId=env.schemaId;this.root=env.root||this;this.baseId=(_a3=env.baseId)!==null&&_a3!==void 0?_a3:(0,resolve_1.normalizeId)(schema===null||schema===void 0?void 0:schema[env.schemaId||"$id"]);this.schemaPath=env.schemaPath;this.localRefs=env.localRefs;this.meta=env.meta;this.$async=schema===null||schema===void 0?void 0:schema.$async;this.refs={}}};exports.SchemaEnv=SchemaEnv;function compileSchema(sch){const _sch=getCompilingSchema.call(this,sch);if(_sch)return _sch;const rootId=(0,resolve_1.getFullPath)(this.opts.uriResolver,sch.root.baseId);const{es5,lines}=this.opts.code;const{ownProperties}=this.opts;const gen=new codegen_1.CodeGen(this.scope,{es5,lines,ownProperties});let _ValidationError;if(sch.$async){_ValidationError=gen.scopeValue("Error",{ref:validation_error_1.default,code:(0,codegen_1._)`require("ajv/dist/runtime/validation_error").default`})}const validateName=gen.scopeName("validate");sch.validateName=validateName;const schemaCxt={gen,allErrors:this.opts.allErrors,data:names_1.default.data,parentData:names_1.default.parentData,parentDataProperty:names_1.default.parentDataProperty,dataNames:[names_1.default.data],dataPathArr:[codegen_1.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:gen.scopeValue("schema",this.opts.code.source===true?{ref:sch.schema,code:(0,codegen_1.stringify)(sch.schema)}:{ref:sch.schema}),validateName,ValidationError:_ValidationError,schema:sch.schema,schemaEnv:sch,rootId,baseId:sch.baseId||rootId,schemaPath:codegen_1.nil,errSchemaPath:sch.schemaPath||(this.opts.jtd?"":"#"),errorPath:(0,codegen_1._)`""`,opts:this.opts,self:this};let sourceCode;try{this._compilations.add(sch);(0,validate_1.validateFunctionCode)(schemaCxt);gen.optimize(this.opts.code.optimize);const validateCode=gen.toString();sourceCode=`${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`;if(this.opts.code.process)sourceCode=this.opts.code.process(sourceCode,sch);const makeValidate=new Function(`${names_1.default.self}`,`${names_1.default.scope}`,sourceCode);const validate=makeValidate(this,this.scope.get());this.scope.value(validateName,{ref:validate});validate.errors=null;validate.schema=sch.schema;validate.schemaEnv=sch;if(sch.$async)validate.$async=true;if(this.opts.code.source===true){validate.source={validateName,validateCode,scopeValues:gen._values}}if(this.opts.unevaluated){const{props,items}=schemaCxt;validate.evaluated={props:props instanceof codegen_1.Name?void 0:props,items:items instanceof codegen_1.Name?void 0:items,dynamicProps:props instanceof codegen_1.Name,dynamicItems:items instanceof codegen_1.Name};if(validate.source)validate.source.evaluated=(0,codegen_1.stringify)(validate.evaluated)}sch.validate=validate;return sch}catch(e){delete sch.validate;delete sch.validateName;if(sourceCode)this.logger.error("Error compiling schema, function code:",sourceCode);throw e}finally{this._compilations.delete(sch)}}exports.compileSchema=compileSchema;function resolveRef(root,baseId,ref){var _a3;ref=(0,resolve_1.resolveUrl)(this.opts.uriResolver,baseId,ref);const schOrFunc=root.refs[ref];if(schOrFunc)return schOrFunc;let _sch=resolve.call(this,root,ref);if(_sch===void 0){const schema=(_a3=root.localRefs)===null||_a3===void 0?void 0:_a3[ref];const{schemaId}=this.opts;if(schema)_sch=new SchemaEnv({schema,schemaId,root,baseId})}if(_sch===void 0)return;return root.refs[ref]=inlineOrCompile.call(this,_sch)}exports.resolveRef=resolveRef;function inlineOrCompile(sch){if((0,resolve_1.inlineRef)(sch.schema,this.opts.inlineRefs))return sch.schema;return sch.validate?sch:compileSchema.call(this,sch)}function getCompilingSchema(schEnv){for(const sch of this._compilations){if(sameSchemaEnv(sch,schEnv))return sch}}exports.getCompilingSchema=getCompilingSchema;function sameSchemaEnv(s1,s2){return s1.schema===s2.schema&&s1.root===s2.root&&s1.baseId===s2.baseId}function resolve(root,ref){let sch;while(typeof(sch=this.refs[ref])=="string")ref=sch;return sch||this.schemas[ref]||resolveSchema.call(this,root,ref)}function resolveSchema(root,ref){const p=this.opts.uriResolver.parse(ref);const refPath=(0,resolve_1._getFullPath)(this.opts.uriResolver,p);let baseId=(0,resolve_1.getFullPath)(this.opts.uriResolver,root.baseId,void 0);if(Object.keys(root.schema).length>0&&refPath===baseId){return getJsonPointer.call(this,p,root)}const id=(0,resolve_1.normalizeId)(refPath);const schOrRef=this.refs[id]||this.schemas[id];if(typeof schOrRef=="string"){const sch=resolveSchema.call(this,root,schOrRef);if(typeof(sch===null||sch===void 0?void 0:sch.schema)!=="object")return;return getJsonPointer.call(this,p,sch)}if(typeof(schOrRef===null||schOrRef===void 0?void 0:schOrRef.schema)!=="object")return;if(!schOrRef.validate)compileSchema.call(this,schOrRef);if(id===(0,resolve_1.normalizeId)(ref)){const{schema}=schOrRef;const{schemaId}=this.opts;const schId=schema[schemaId];if(schId)baseId=(0,resolve_1.resolveUrl)(this.opts.uriResolver,baseId,schId);return new SchemaEnv({schema,schemaId,root,baseId})}return getJsonPointer.call(this,p,schOrRef)}exports.resolveSchema=resolveSchema;var PREVENT_SCOPE_CHANGE=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function getJsonPointer(parsedRef,{baseId,schema,root}){var _a3;if(((_a3=parsedRef.fragment)===null||_a3===void 0?void 0:_a3[0])!=="/")return;for(const part of parsedRef.fragment.slice(1).split("/")){if(typeof schema==="boolean")return;const partSchema=schema[(0,util_1.unescapeFragment)(part)];if(partSchema===void 0)return;schema=partSchema;const schId=typeof schema==="object"&&schema[this.opts.schemaId];if(!PREVENT_SCOPE_CHANGE.has(part)&&schId){baseId=(0,resolve_1.resolveUrl)(this.opts.uriResolver,baseId,schId)}}let env;if(typeof schema!="boolean"&&schema.$ref&&!(0,util_1.schemaHasRulesButRef)(schema,this.RULES)){const $ref=(0,resolve_1.resolveUrl)(this.opts.uriResolver,baseId,schema.$ref);env=resolveSchema.call(this,root,$ref)}const{schemaId}=this.opts;env=env||new SchemaEnv({schema,schemaId,root,baseId});if(env.schema!==env.root.schema)return env;return void 0}}});var require_data=__commonJS({"node_modules/ajv/dist/refs/data.json"(exports,module){module.exports={$id:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",description:"Meta-schema for $data reference (JSON AnySchema extension proposal)",type:"object",required:["$data"],properties:{$data:{type:"string",anyOf:[{format:"relative-json-pointer"},{format:"json-pointer"}]}},additionalProperties:false}}});var require_utils=__commonJS({"node_modules/fast-uri/lib/utils.js"(exports,module){"use strict";var isUUID=RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);var isIPv4=RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);var isHexPair=RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);var isUnreserved=RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);var isPathCharacter=RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);var isQueryFragmentCharacter=RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);var isUserinfoCharacter=RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);var BYTE_HEX=new Array(256);{const HEX_DIGITS="0123456789ABCDEF";for(let i=0;i<256;i++){BYTE_HEX[i]="%"+HEX_DIGITS[i>>4]+HEX_DIGITS[i&15]}}function percentEncodeNonAscii(cp){if(cp<2048){return BYTE_HEX[192|cp>>6]+BYTE_HEX[128|cp&63]}if(cp<65536){return BYTE_HEX[224|cp>>12]+BYTE_HEX[128|cp>>6&63]+BYTE_HEX[128|cp&63]}return BYTE_HEX[240|cp>>18]+BYTE_HEX[128|cp>>12&63]+BYTE_HEX[128|cp>>6&63]+BYTE_HEX[128|cp&63]}function stringArrayToHexStripped(input){let acc="";let code=0;let i=0;for(i=0;i=48&&code<=57||code>=65&&code<=70||code>=97&&code<=102)){return""}acc+=input[i];break}for(i+=1;i=48&&code<=57||code>=65&&code<=70||code>=97&&code<=102)){return""}acc+=input[i]}return acc}var isHextet=RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);var isIPvFuture=RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);var isZoneCharacter=RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);var nonSimpleDomain=RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);function isZoneIdentifier(zone){if(zone.length===0)return false;for(let i=0;ibestLength){bestLength=runLength;bestStart=runStart}}else{runStart=-1;runLength=0}}if(bestLength<2)return hextets.join(":");const head=hextets.slice(0,bestStart).join(":");const tail=hextets.slice(bestStart+bestLength).join(":");return head+"::"+tail}function normalizeIPv6Address(input){const compression=input.indexOf("::");if(compression!==-1&&input.indexOf("::",compression+1)!==-1)return void 0;const left=compression===-1?input.split(":"):input.slice(0,compression).split(":");const right=compression===-1?[]:input.slice(compression+2).split(":");if(compression!==-1){if(left.length===1&&left[0]==="")left.length=0;if(right.length===1&&right[0]==="")right.length=0}const parts=left.concat(right);let hextetCount=0;for(let i=0;i=8)return void 0;const expanded=parts.slice(0,left.length);for(let i=hextetCount;i<8;i++)expanded.push("0");for(let i=left.length;iHOST_DELIMS[ch])}function normalizePercentEncoding(input,decodeUnreserved=false){if(input.indexOf("%")===-1){return input}let output="";for(let i=0;i57343){output+=percentEncodeNonAscii(code)}else if(code<=56319&&i+1=56320&&low<=57343){output+=percentEncodeNonAscii(65536+(code-55296<<10)+(low-56320));i++}else{output+=percentEncodeNonAscii(65533)}}else{output+=percentEncodeNonAscii(65533)}}}return output}function serializePathEncoding(input,pathNoScheme=false){let output="";let firstSegment=pathNoScheme&&input[0]!=="/";for(let i=0;i57343){output+=percentEncodeNonAscii(code)}else if(code<=56319&&i+1=56320&&low<=57343){output+=percentEncodeNonAscii(65536+(code-55296<<10)+(low-56320));i++}else{output+=percentEncodeNonAscii(65533)}}else{output+=percentEncodeNonAscii(65533)}}}return output}function encodeComponent(input,isAllowed){let output="";for(let i=0;i57343){output+=percentEncodeNonAscii(code)}else if(code<=56319&&i+1=56320&&low<=57343){output+=percentEncodeNonAscii(65536+(code-55296<<10)+(low-56320));i++}else{output+=percentEncodeNonAscii(65533)}}else{output+=percentEncodeNonAscii(65533)}}}return output}function encodeUserinfo(input){return encodeComponent(input,isUserinfoCharacter)}function encodeQuery(input){return encodeComponent(input,isQueryFragmentCharacter)}function encodeFragment(input){return encodeComponent(input,isQueryFragmentCharacter)}function isEscapeSafe(cp){return cp>=48&&cp<=57||cp>=65&&cp<=90||cp>=97&&cp<=122||cp===42||cp===43||cp===45||cp===46||cp===47||cp===64||cp===95}function normalizeQueryFragmentEncoding(input){let output="";for(let i=0;i57343){output+=percentEncodeNonAscii(code)}else if(code<=56319&&i+1=56320&&low<=57343){output+=percentEncodeNonAscii(65536+(code-55296<<10)+(low-56320));i++}else{output+=percentEncodeNonAscii(65533)}}else{output+=percentEncodeNonAscii(65533)}}}return output}function escapePreservingEscapes(input){let output="";for(let i=0;i65535)){return"URI port is malformed."}return void 0}function hasMalformedPercentEncoding(component){if(component===void 0)return false;let percent=component.indexOf("%");while(percent!==-1){if(percent+2>=component.length||!/^[\da-f]{2}$/iu.test(component.slice(percent+1,percent+3))){return true}percent=component.indexOf("%",percent+3)}return false}function hasMalformedComponentPercentEncoding(matches){const host=matches[4];return hasMalformedPercentEncoding(matches[3])||host!==void 0&&!(host[0]==="["&&host[host.length-1]==="]")&&hasMalformedPercentEncoding(host)||hasMalformedPercentEncoding(matches[6])||hasMalformedPercentEncoding(matches[7])||hasMalformedPercentEncoding(matches[8])}function canonicalizeHost(parsed,options,schemeHandler,isIP){if(!options.unicodeSupport&&(!schemeHandler||!schemeHandler.unicodeSupport)&&parsed.host&&parsed.host[0]!=="["&&(options.domainHost||schemeHandler&&schemeHandler.domainHost)&&isIP===false&&nonSimpleDomain(parsed.host)){try{parsed.host=new URL("http://"+parsed.host).hostname}catch(e){parsed.error=parsed.error||"Host's domain name can not be converted to ASCII: "+e;return true}}return false}function parseWithStatus(uri,opts){const options=Object.assign({},opts);const parsed={scheme:void 0,userinfo:void 0,host:"",port:void 0,path:"",query:void 0,fragment:void 0};let malformedAuthorityOrPort=false;let malformedPercentEncoding=false;let malformedSchemeSpecific=false;let malformedHost=false;let malformedIPLiteral=false;let malformedScheme=false;let isIP=false;if(options.reference==="suffix"){if(options.scheme){uri=options.scheme+":"+uri}else{uri="//"+uri}}const authorityMatch=uri.match(AUTHORITY_PREFIX);if(authorityMatch!==null&&authorityMatch[1].indexOf("\\")!==-1){parsed.error="URI authority must not contain a literal backslash.";malformedAuthorityOrPort=true}const introducerMatch=uri.match(AUTHORITY_INTRODUCER_REGION);if(introducerMatch!==null){const region=introducerMatch[1];const normalizedRegion=region.replace(/[\t\n\r]/g,"");if(normalizedRegion.length>=2){if(normalizedRegion.slice(0,2)!=="//"){parsed.error=parsed.error||"URI authority must not contain a literal backslash.";malformedAuthorityOrPort=true}else if(region.length!==normalizedRegion.length){parsed.error=parsed.error||"URI authority introducer must not contain whitespace.";malformedAuthorityOrPort=true}}}const matches=uri.match(URI_PARSE);if(matches){parsed.scheme=matches[1];parsed.userinfo=matches[3];parsed.host=matches[4];parsed.port=parseInt(matches[5],10);parsed.path=matches[6]||"";parsed.query=matches[7];parsed.fragment=matches[8];if(parsed.scheme!==void 0){const decodedScheme=unescape(parsed.scheme);if(VALID_SCHEME.test(decodedScheme)){parsed.scheme=decodedScheme.toLowerCase()}else{parsed.error=parsed.error||MALFORMED_SCHEME_ERROR;malformedScheme=true}}malformedPercentEncoding=hasMalformedComponentPercentEncoding(matches);if(malformedPercentEncoding){parsed.error=parsed.error||"URI contains malformed percent-encoding."}if(isNaN(parsed.port)){parsed.port=matches[5]}const parseError=getParseError(parsed,matches);if(parseError!==void 0){parsed.error=parsed.error||parseError;malformedAuthorityOrPort=true}if(parsed.host){const ipv4result=isIPv4(parsed.host);if(ipv4result===false){const bracketedIPLiteral=parsed.host[0]==="["&&parsed.host[parsed.host.length-1]==="]";const ipv6result=normalizeIPv6(parsed.host);isIP=ipv6result.isIPV6||ipv6result.isIPVFuture===true;malformedIPLiteral=bracketedIPLiteral&&ipv6result.error===true;parsed.host=isIP?ipv6result.host:ipv6result.host.toLowerCase();if(malformedIPLiteral){parsed.error=parsed.error||"URI host is malformed.";malformedAuthorityOrPort=true}}else{isIP=true}}if(parsed.scheme===void 0&&parsed.userinfo===void 0&&parsed.host===void 0&&parsed.port===void 0&&parsed.query===void 0&&!parsed.path){parsed.reference="same-document"}else if(parsed.scheme===void 0){parsed.reference="relative"}else if(parsed.fragment===void 0){parsed.reference="absolute"}else{parsed.reference="uri"}if(options.reference&&options.reference!=="suffix"&&options.reference!==parsed.reference){parsed.error=parsed.error||"URI is not a "+options.reference+" reference."}const schemeHandler=getSchemeHandler(options.scheme||parsed.scheme);malformedHost=canonicalizeHost(parsed,options,schemeHandler,isIP);if(!schemeHandler||schemeHandler&&!schemeHandler.skipNormalize){if(uri.indexOf("%")!==-1){if(parsed.host!==void 0&&!malformedIPLiteral){const host=isIP?parsed.host:normalizePercentEncoding(parsed.host,true);parsed.host=reescapeHostDelimiters(host,isIP)}}if(parsed.path){parsed.path=normalizePathEncoding(parsed.path)}if(parsed.query){parsed.query=normalizeQueryFragmentEncoding(parsed.query)}if(parsed.fragment){parsed.fragment=normalizeQueryFragmentEncoding(parsed.fragment)}}if(schemeHandler&&schemeHandler.parse){schemeHandler.parse(parsed,options);if(schemeHandler===SCHEMES.urn&&parsed.nid===void 0){malformedSchemeSpecific=true}}}else{parsed.error=parsed.error||"URI can not be parsed."}return{parsed,malformedAuthorityOrPort,malformedPercentEncoding,malformedSchemeSpecific,malformedHost,malformedScheme}}function parse3(uri,opts){return parseWithStatus(uri,opts).parsed}function normalizeString(uri,opts){return normalizeStringWithStatus(uri,opts).normalized}function normalizeStringWithStatus(uri,opts){const{parsed,malformedAuthorityOrPort,malformedPercentEncoding,malformedSchemeSpecific,malformedHost,malformedScheme}=parseWithStatus(uri,opts);return{normalized:malformedAuthorityOrPort||malformedPercentEncoding||malformedSchemeSpecific||malformedHost||malformedScheme?uri:serialize(parsed,opts),malformedAuthorityOrPort,malformedPercentEncoding,malformedSchemeSpecific,malformedHost,malformedScheme}}function normalizeComparableURI(uri,opts){if(typeof uri!=="string"&&typeof uri!=="object"){return void 0}let value;try{value=typeof uri==="string"?uri:serialize(uri,opts)}catch{return void 0}const{normalized,malformedAuthorityOrPort,malformedPercentEncoding,malformedSchemeSpecific,malformedHost,malformedScheme}=normalizeStringWithStatus(value,opts);return malformedAuthorityOrPort||malformedPercentEncoding||malformedSchemeSpecific||malformedHost||malformedScheme?void 0:normalized}var fastUri={SCHEMES,normalize,resolve,resolveComponent,equal,serialize,parse:parse3};module.exports=fastUri;module.exports.default=fastUri;module.exports.fastUri=fastUri}});var require_uri=__commonJS({"node_modules/ajv/dist/runtime/uri.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var uri=require_fast_uri();uri.code='require("ajv/dist/runtime/uri").default';exports.default=uri}});var require_core=__commonJS({"node_modules/ajv/dist/core.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.CodeGen=exports.Name=exports.nil=exports.stringify=exports.str=exports._=exports.KeywordCxt=void 0;var validate_1=require_validate();Object.defineProperty(exports,"KeywordCxt",{enumerable:true,get:function(){return validate_1.KeywordCxt}});var codegen_1=require_codegen();Object.defineProperty(exports,"_",{enumerable:true,get:function(){return codegen_1._}});Object.defineProperty(exports,"str",{enumerable:true,get:function(){return codegen_1.str}});Object.defineProperty(exports,"stringify",{enumerable:true,get:function(){return codegen_1.stringify}});Object.defineProperty(exports,"nil",{enumerable:true,get:function(){return codegen_1.nil}});Object.defineProperty(exports,"Name",{enumerable:true,get:function(){return codegen_1.Name}});Object.defineProperty(exports,"CodeGen",{enumerable:true,get:function(){return codegen_1.CodeGen}});var validation_error_1=require_validation_error();var ref_error_1=require_ref_error();var rules_1=require_rules();var compile_1=require_compile();var codegen_2=require_codegen();var resolve_1=require_resolve();var dataType_1=require_dataType();var util_1=require_util();var $dataRefSchema=require_data();var uri_1=require_uri();var defaultRegExp=(str,flags)=>new RegExp(str,flags);defaultRegExp.code="new RegExp";var META_IGNORE_OPTIONS=["removeAdditional","useDefaults","coerceTypes"];var EXT_SCOPE_NAMES=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]);var removedOptions={errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."};var deprecatedOptions={ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'};var MAX_EXPRESSION=200;function requiredOptions(o){var _a3,_b,_c,_d,_e,_f,_g,_h,_j,_k,_l,_m,_o,_p,_q,_r,_s,_t,_u,_v,_w,_x,_y,_z,_0;const s=o.strict;const _optz=(_a3=o.code)===null||_a3===void 0?void 0:_a3.optimize;const optimize=_optz===true||_optz===void 0?1:_optz||0;const regExp=(_c=(_b=o.code)===null||_b===void 0?void 0:_b.regExp)!==null&&_c!==void 0?_c:defaultRegExp;const uriResolver=(_d=o.uriResolver)!==null&&_d!==void 0?_d:uri_1.default;return{strictSchema:(_f=(_e=o.strictSchema)!==null&&_e!==void 0?_e:s)!==null&&_f!==void 0?_f:true,strictNumbers:(_h=(_g=o.strictNumbers)!==null&&_g!==void 0?_g:s)!==null&&_h!==void 0?_h:true,strictTypes:(_k=(_j=o.strictTypes)!==null&&_j!==void 0?_j:s)!==null&&_k!==void 0?_k:"log",strictTuples:(_m=(_l=o.strictTuples)!==null&&_l!==void 0?_l:s)!==null&&_m!==void 0?_m:"log",strictRequired:(_p=(_o=o.strictRequired)!==null&&_o!==void 0?_o:s)!==null&&_p!==void 0?_p:false,code:o.code?{...o.code,optimize,regExp}:{optimize,regExp},loopRequired:(_q=o.loopRequired)!==null&&_q!==void 0?_q:MAX_EXPRESSION,loopEnum:(_r=o.loopEnum)!==null&&_r!==void 0?_r:MAX_EXPRESSION,meta:(_s=o.meta)!==null&&_s!==void 0?_s:true,messages:(_t=o.messages)!==null&&_t!==void 0?_t:true,inlineRefs:(_u=o.inlineRefs)!==null&&_u!==void 0?_u:true,schemaId:(_v=o.schemaId)!==null&&_v!==void 0?_v:"$id",addUsedSchema:(_w=o.addUsedSchema)!==null&&_w!==void 0?_w:true,validateSchema:(_x=o.validateSchema)!==null&&_x!==void 0?_x:true,validateFormats:(_y=o.validateFormats)!==null&&_y!==void 0?_y:true,unicodeRegExp:(_z=o.unicodeRegExp)!==null&&_z!==void 0?_z:true,int32range:(_0=o.int32range)!==null&&_0!==void 0?_0:true,uriResolver}}var Ajv2=class{constructor(opts={}){this.schemas={};this.refs={};this.formats=Object.create(null);this._compilations=new Set;this._loading={};this._cache=new Map;opts=this.opts={...opts,...requiredOptions(opts)};const{es5,lines}=this.opts.code;this.scope=new codegen_2.ValueScope({scope:{},prefixes:EXT_SCOPE_NAMES,es5,lines});this.logger=getLogger(opts.logger);const formatOpt=opts.validateFormats;opts.validateFormats=false;this.RULES=(0,rules_1.getRules)();checkOptions.call(this,removedOptions,opts,"NOT SUPPORTED");checkOptions.call(this,deprecatedOptions,opts,"DEPRECATED","warn");this._metaOpts=getMetaSchemaOptions.call(this);if(opts.formats)addInitialFormats.call(this);this._addVocabularies();this._addDefaultMetaSchema();if(opts.keywords)addInitialKeywords.call(this,opts.keywords);if(typeof opts.meta=="object")this.addMetaSchema(opts.meta);addInitialSchemas.call(this);opts.validateFormats=formatOpt}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){const{$data,meta:meta2,schemaId}=this.opts;let _dataRefSchema=$dataRefSchema;if(schemaId==="id"){_dataRefSchema={...$dataRefSchema};_dataRefSchema.id=_dataRefSchema.$id;delete _dataRefSchema.$id}if(meta2&&$data)this.addMetaSchema(_dataRefSchema,_dataRefSchema[schemaId],false)}defaultMeta(){const{meta:meta2,schemaId}=this.opts;return this.opts.defaultMeta=typeof meta2=="object"?meta2[schemaId]||meta2:void 0}validate(schemaKeyRef,data){let v;if(typeof schemaKeyRef=="string"){v=this.getSchema(schemaKeyRef);if(!v)throw new Error(`no schema with key or ref "${schemaKeyRef}"`)}else{v=this.compile(schemaKeyRef)}const valid=v(data);if(!("$async"in v))this.errors=v.errors;return valid}compile(schema,_meta){const sch=this._addSchema(schema,_meta);return sch.validate||this._compileSchemaEnv(sch)}compileAsync(schema,meta2){if(typeof this.opts.loadSchema!="function"){throw new Error("options.loadSchema should be a function")}const{loadSchema}=this.opts;return runCompileAsync.call(this,schema,meta2);async function runCompileAsync(_schema,_meta){await loadMetaSchema.call(this,_schema.$schema);const sch=this._addSchema(_schema,_meta);return sch.validate||_compileAsync.call(this,sch)}async function loadMetaSchema($ref){if($ref&&!this.getSchema($ref)){await runCompileAsync.call(this,{$ref},true)}}async function _compileAsync(sch){try{return this._compileSchemaEnv(sch)}catch(e){if(!(e instanceof ref_error_1.default))throw e;checkLoaded.call(this,e);await loadMissingSchema.call(this,e.missingSchema);return _compileAsync.call(this,sch)}}function checkLoaded({missingSchema:ref,missingRef}){if(this.refs[ref]){throw new Error(`AnySchema ${ref} is loaded but ${missingRef} cannot be resolved`)}}async function loadMissingSchema(ref){const _schema=await _loadSchema.call(this,ref);if(!this.refs[ref])await loadMetaSchema.call(this,_schema.$schema);if(!this.refs[ref])this.addSchema(_schema,ref,meta2)}async function _loadSchema(ref){const p=this._loading[ref];if(p)return p;try{return await(this._loading[ref]=loadSchema(ref))}finally{delete this._loading[ref]}}}addSchema(schema,key,_meta,_validateSchema=this.opts.validateSchema){if(Array.isArray(schema)){for(const sch of schema)this.addSchema(sch,void 0,_meta,_validateSchema);return this}let id;if(typeof schema==="object"){const{schemaId}=this.opts;id=schema[schemaId];if(id!==void 0&&typeof id!="string"){throw new Error(`schema ${schemaId} must be string`)}}key=(0,resolve_1.normalizeId)(key||id);this._checkUnique(key);this.schemas[key]=this._addSchema(schema,_meta,key,_validateSchema,true);return this}addMetaSchema(schema,key,_validateSchema=this.opts.validateSchema){this.addSchema(schema,key,true,_validateSchema);return this}validateSchema(schema,throwOrLogError){if(typeof schema=="boolean")return true;let $schema;$schema=schema.$schema;if($schema!==void 0&&typeof $schema!="string"){throw new Error("$schema must be a string")}$schema=$schema||this.opts.defaultMeta||this.defaultMeta();if(!$schema){this.logger.warn("meta-schema not available");this.errors=null;return true}const valid=this.validate($schema,schema);if(!valid&&throwOrLogError){const message="schema is invalid: "+this.errorsText();if(this.opts.validateSchema==="log")this.logger.error(message);else throw new Error(message)}return valid}getSchema(keyRef){let sch;while(typeof(sch=getSchEnv.call(this,keyRef))=="string")keyRef=sch;if(sch===void 0){const{schemaId}=this.opts;const root=new compile_1.SchemaEnv({schema:{},schemaId});sch=compile_1.resolveSchema.call(this,root,keyRef);if(!sch)return;this.refs[keyRef]=sch}return sch.validate||this._compileSchemaEnv(sch)}removeSchema(schemaKeyRef){if(schemaKeyRef instanceof RegExp){this._removeAllSchemas(this.schemas,schemaKeyRef);this._removeAllSchemas(this.refs,schemaKeyRef);return this}switch(typeof schemaKeyRef){case"undefined":this._removeAllSchemas(this.schemas);this._removeAllSchemas(this.refs);this._cache.clear();return this;case"string":{const sch=getSchEnv.call(this,schemaKeyRef);if(typeof sch=="object")this._cache.delete(sch.schema);delete this.schemas[schemaKeyRef];delete this.refs[schemaKeyRef];return this}case"object":{const cacheKey=schemaKeyRef;this._cache.delete(cacheKey);let id=schemaKeyRef[this.opts.schemaId];if(id){id=(0,resolve_1.normalizeId)(id);delete this.schemas[id];delete this.refs[id]}return this}default:throw new Error("ajv.removeSchema: invalid parameter")}}addVocabulary(definitions){for(const def of definitions)this.addKeyword(def);return this}addKeyword(kwdOrDef,def){let keyword;if(typeof kwdOrDef=="string"){keyword=kwdOrDef;if(typeof def=="object"){this.logger.warn("these parameters are deprecated, see docs for addKeyword");def.keyword=keyword}}else if(typeof kwdOrDef=="object"&&def===void 0){def=kwdOrDef;keyword=def.keyword;if(Array.isArray(keyword)&&!keyword.length){throw new Error("addKeywords: keyword must be string or non-empty array")}}else{throw new Error("invalid addKeywords parameters")}checkKeyword.call(this,keyword,def);if(!def){(0,util_1.eachItem)(keyword,kwd=>addRule.call(this,kwd));return this}keywordMetaschema.call(this,def);const definition={...def,type:(0,dataType_1.getJSONTypes)(def.type),schemaType:(0,dataType_1.getJSONTypes)(def.schemaType)};(0,util_1.eachItem)(keyword,definition.type.length===0?k=>addRule.call(this,k,definition):k=>definition.type.forEach(t=>addRule.call(this,k,definition,t)));return this}getKeyword(keyword){const rule=this.RULES.all[keyword];return typeof rule=="object"?rule.definition:!!rule}removeKeyword(keyword){const{RULES}=this;delete RULES.keywords[keyword];delete RULES.all[keyword];for(const group of RULES.rules){const i=group.rules.findIndex(rule=>rule.keyword===keyword);if(i>=0)group.rules.splice(i,1)}return this}addFormat(name,format){if(typeof format=="string")format=new RegExp(format);this.formats[name]=format;return this}errorsText(errors=this.errors,{separator=", ",dataVar="data"}={}){if(!errors||errors.length===0)return"No errors";return errors.map(e=>`${dataVar}${e.instancePath} ${e.message}`).reduce((text,msg)=>text+separator+msg)}$dataMetaSchema(metaSchema,keywordsJsonPointers){const rules=this.RULES.all;metaSchema=JSON.parse(JSON.stringify(metaSchema));for(const jsonPointer of keywordsJsonPointers){const segments=jsonPointer.split("/").slice(1);let keywords=metaSchema;for(const seg of segments)keywords=keywords[seg];for(const key in rules){const rule=rules[key];if(typeof rule!="object")continue;const{$data}=rule.definition;const schema=keywords[key];if($data&&schema)keywords[key]=schemaOrData(schema)}}return metaSchema}_removeAllSchemas(schemas,regex){for(const keyRef in schemas){const sch=schemas[keyRef];if(!regex||regex.test(keyRef)){if(typeof sch=="string"){delete schemas[keyRef]}else if(sch&&!sch.meta){this._cache.delete(sch.schema);delete schemas[keyRef]}}}}_addSchema(schema,meta2,baseId,validateSchema=this.opts.validateSchema,addSchema=this.opts.addUsedSchema){let id;const{schemaId}=this.opts;if(typeof schema=="object"){id=schema[schemaId]}else{if(this.opts.jtd)throw new Error("schema must be object");else if(typeof schema!="boolean")throw new Error("schema must be object or boolean")}let sch=this._cache.get(schema);if(sch!==void 0)return sch;baseId=(0,resolve_1.normalizeId)(id||baseId);const localRefs=resolve_1.getSchemaRefs.call(this,schema,baseId);sch=new compile_1.SchemaEnv({schema,schemaId,meta:meta2,baseId,localRefs});this._cache.set(sch.schema,sch);if(addSchema&&!baseId.startsWith("#")){if(baseId)this._checkUnique(baseId);this.refs[baseId]=sch}if(validateSchema)this.validateSchema(schema,true);return sch}_checkUnique(id){if(this.schemas[id]||this.refs[id]){throw new Error(`schema with key or id "${id}" already exists`)}}_compileSchemaEnv(sch){if(sch.meta)this._compileMetaSchema(sch);else compile_1.compileSchema.call(this,sch);if(!sch.validate)throw new Error("ajv implementation error");return sch.validate}_compileMetaSchema(sch){const currentOpts=this.opts;this.opts=this._metaOpts;try{compile_1.compileSchema.call(this,sch)}finally{this.opts=currentOpts}}};Ajv2.ValidationError=validation_error_1.default;Ajv2.MissingRefError=ref_error_1.default;exports.default=Ajv2;function checkOptions(checkOpts,options,msg,log="error"){for(const key in checkOpts){const opt=key;if(opt in options)this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`)}}function getSchEnv(keyRef){keyRef=(0,resolve_1.normalizeId)(keyRef);return this.schemas[keyRef]||this.refs[keyRef]}function addInitialSchemas(){const optsSchemas=this.opts.schemas;if(!optsSchemas)return;if(Array.isArray(optsSchemas))this.addSchema(optsSchemas);else for(const key in optsSchemas)this.addSchema(optsSchemas[key],key)}function addInitialFormats(){for(const name in this.opts.formats){const format=this.opts.formats[name];if(format)this.addFormat(name,format)}}function addInitialKeywords(defs){if(Array.isArray(defs)){this.addVocabulary(defs);return}this.logger.warn("keywords option as map is deprecated, pass array");for(const keyword in defs){const def=defs[keyword];if(!def.keyword)def.keyword=keyword;this.addKeyword(def)}}function getMetaSchemaOptions(){const metaOpts={...this.opts};for(const opt of META_IGNORE_OPTIONS)delete metaOpts[opt];return metaOpts}var noLogs={log(){},warn(){},error(){}};function getLogger(logger){if(logger===false)return noLogs;if(logger===void 0)return console;if(logger.log&&logger.warn&&logger.error)return logger;throw new Error("logger must implement log, warn and error methods")}var KEYWORD_NAME=/^[a-z_$][a-z0-9_$:-]*$/i;function checkKeyword(keyword,def){const{RULES}=this;(0,util_1.eachItem)(keyword,kwd=>{if(RULES.keywords[kwd])throw new Error(`Keyword ${kwd} is already defined`);if(!KEYWORD_NAME.test(kwd))throw new Error(`Keyword ${kwd} has invalid name`)});if(!def)return;if(def.$data&&!("code"in def||"validate"in def)){throw new Error('$data keyword must have "code" or "validate" function')}}function addRule(keyword,definition,dataType){var _a3;const post=definition===null||definition===void 0?void 0:definition.post;if(dataType&&post)throw new Error('keyword with "post" flag cannot have "type"');const{RULES}=this;let ruleGroup=post?RULES.post:RULES.rules.find(({type:t})=>t===dataType);if(!ruleGroup){ruleGroup={type:dataType,rules:[]};RULES.rules.push(ruleGroup)}RULES.keywords[keyword]=true;if(!definition)return;const rule={keyword,definition:{...definition,type:(0,dataType_1.getJSONTypes)(definition.type),schemaType:(0,dataType_1.getJSONTypes)(definition.schemaType)}};if(definition.before)addBeforeRule.call(this,ruleGroup,rule,definition.before);else ruleGroup.rules.push(rule);RULES.all[keyword]=rule;(_a3=definition.implements)===null||_a3===void 0?void 0:_a3.forEach(kwd=>this.addKeyword(kwd))}function addBeforeRule(ruleGroup,rule,before){const i=ruleGroup.rules.findIndex(_rule=>_rule.keyword===before);if(i>=0){ruleGroup.rules.splice(i,0,rule)}else{ruleGroup.rules.push(rule);this.logger.warn(`rule ${before} is not defined`)}}function keywordMetaschema(def){let{metaSchema}=def;if(metaSchema===void 0)return;if(def.$data&&this.opts.$data)metaSchema=schemaOrData(metaSchema);def.validateSchema=this.compile(metaSchema,true)}var $dataRef={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function schemaOrData(schema){return{anyOf:[schema,$dataRef]}}}});var require_id=__commonJS({"node_modules/ajv/dist/vocabularies/core/id.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var def={keyword:"id",code(){throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}};exports.default=def}});var require_ref=__commonJS({"node_modules/ajv/dist/vocabularies/core/ref.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.callRef=exports.getValidate=void 0;var ref_error_1=require_ref_error();var code_1=require_code2();var codegen_1=require_codegen();var names_1=require_names();var compile_1=require_compile();var util_1=require_util();var def={keyword:"$ref",schemaType:"string",code(cxt){const{gen,schema:$ref,it}=cxt;const{baseId,schemaEnv:env,validateName,opts,self}=it;const{root}=env;if(($ref==="#"||$ref==="#/")&&baseId===root.baseId)return callRootRef();const schOrEnv=compile_1.resolveRef.call(self,root,baseId,$ref);if(schOrEnv===void 0)throw new ref_error_1.default(it.opts.uriResolver,baseId,$ref);if(schOrEnv instanceof compile_1.SchemaEnv)return callValidate(schOrEnv);return inlineRefSchema(schOrEnv);function callRootRef(){if(env===root)return callRef(cxt,validateName,env,env.$async);const rootName=gen.scopeValue("root",{ref:root});return callRef(cxt,(0,codegen_1._)`${rootName}.validate`,root,root.$async)}function callValidate(sch){const v=getValidate(cxt,sch);callRef(cxt,v,sch,sch.$async)}function inlineRefSchema(sch){const schName=gen.scopeValue("schema",opts.code.source===true?{ref:sch,code:(0,codegen_1.stringify)(sch)}:{ref:sch});const valid=gen.name("valid");const schCxt=cxt.subschema({schema:sch,dataTypes:[],schemaPath:codegen_1.nil,topSchemaRef:schName,errSchemaPath:$ref},valid);cxt.mergeEvaluated(schCxt);cxt.ok(valid)}}};function getValidate(cxt,sch){const{gen}=cxt;return sch.validate?gen.scopeValue("validate",{ref:sch.validate}):(0,codegen_1._)`${gen.scopeValue("wrapper",{ref:sch})}.validate`}exports.getValidate=getValidate;function callRef(cxt,v,sch,$async){const{gen,it}=cxt;const{allErrors,schemaEnv:env,opts}=it;const passCxt=opts.passContext?names_1.default.this:codegen_1.nil;if($async)callAsyncRef();else callSyncRef();function callAsyncRef(){if(!env.$async)throw new Error("async schema referenced by sync schema");const valid=gen.let("valid");gen.try(()=>{gen.code((0,codegen_1._)`await ${(0,code_1.callValidateCode)(cxt,v,passCxt)}`);addEvaluatedFrom(v);if(!allErrors)gen.assign(valid,true)},e=>{gen.if((0,codegen_1._)`!(${e} instanceof ${it.ValidationError})`,()=>gen.throw(e));addErrorsFrom(e);if(!allErrors)gen.assign(valid,false)});cxt.ok(valid)}function callSyncRef(){cxt.result((0,code_1.callValidateCode)(cxt,v,passCxt),()=>addEvaluatedFrom(v),()=>addErrorsFrom(v))}function addErrorsFrom(source){const errs=(0,codegen_1._)`${source}.errors`;gen.assign(names_1.default.vErrors,(0,codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`);gen.assign(names_1.default.errors,(0,codegen_1._)`${names_1.default.vErrors}.length`)}function addEvaluatedFrom(source){var _a3;if(!it.opts.unevaluated)return;const schEvaluated=(_a3=sch===null||sch===void 0?void 0:sch.validate)===null||_a3===void 0?void 0:_a3.evaluated;if(it.props!==true){if(schEvaluated&&!schEvaluated.dynamicProps){if(schEvaluated.props!==void 0){it.props=util_1.mergeEvaluated.props(gen,schEvaluated.props,it.props)}}else{const props=gen.var("props",(0,codegen_1._)`${source}.evaluated.props`);it.props=util_1.mergeEvaluated.props(gen,props,it.props,codegen_1.Name)}}if(it.items!==true){if(schEvaluated&&!schEvaluated.dynamicItems){if(schEvaluated.items!==void 0){it.items=util_1.mergeEvaluated.items(gen,schEvaluated.items,it.items)}}else{const items=gen.var("items",(0,codegen_1._)`${source}.evaluated.items`);it.items=util_1.mergeEvaluated.items(gen,items,it.items,codegen_1.Name)}}}}exports.callRef=callRef;exports.default=def}});var require_core2=__commonJS({"node_modules/ajv/dist/vocabularies/core/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var id_1=require_id();var ref_1=require_ref();var core=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",id_1.default,ref_1.default];exports.default=core}});var require_limitNumber=__commonJS({"node_modules/ajv/dist/vocabularies/validation/limitNumber.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var ops=codegen_1.operators;var KWDs={maximum:{okStr:"<=",ok:ops.LTE,fail:ops.GT},minimum:{okStr:">=",ok:ops.GTE,fail:ops.LT},exclusiveMaximum:{okStr:"<",ok:ops.LT,fail:ops.GTE},exclusiveMinimum:{okStr:">",ok:ops.GT,fail:ops.LTE}};var error2={message:({keyword,schemaCode})=>(0,codegen_1.str)`must be ${KWDs[keyword].okStr} ${schemaCode}`,params:({keyword,schemaCode})=>(0,codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}`};var def={keyword:Object.keys(KWDs),type:"number",schemaType:"number",$data:true,error:error2,code(cxt){const{keyword,data,schemaCode}=cxt;cxt.fail$data((0,codegen_1._)`${data} ${KWDs[keyword].fail} ${schemaCode} || isNaN(${data})`)}};exports.default=def}});var require_multipleOf=__commonJS({"node_modules/ajv/dist/vocabularies/validation/multipleOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var error2={message:({schemaCode})=>(0,codegen_1.str)`must be multiple of ${schemaCode}`,params:({schemaCode})=>(0,codegen_1._)`{multipleOf: ${schemaCode}}`};var def={keyword:"multipleOf",type:"number",schemaType:"number",$data:true,error:error2,code(cxt){const{gen,data,schemaCode,it}=cxt;const prec=it.opts.multipleOfPrecision;const res=gen.let("res");const invalid=prec?(0,codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}`:(0,codegen_1._)`${res} !== parseInt(${res})`;cxt.fail$data((0,codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`)}};exports.default=def}});var require_ucs2length=__commonJS({"node_modules/ajv/dist/runtime/ucs2length.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});function ucs2length(str){const len=str.length;let length=0;let pos=0;let value;while(pos=55296&&value<=56319&&pos(0,codegen_1._)`{limit: ${schemaCode}}`};var def={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:true,error:error2,code(cxt){const{keyword,data,schemaCode,it}=cxt;const op=keyword==="maxLength"?codegen_1.operators.GT:codegen_1.operators.LT;const len=it.opts.unicode===false?(0,codegen_1._)`${data}.length`:(0,codegen_1._)`${(0,util_1.useFunc)(cxt.gen,ucs2length_1.default)}(${data})`;cxt.fail$data((0,codegen_1._)`${len} ${op} ${schemaCode}`)}};exports.default=def}});var require_pattern=__commonJS({"node_modules/ajv/dist/vocabularies/validation/pattern.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var util_1=require_util();var codegen_1=require_codegen();var error2={message:({schemaCode})=>(0,codegen_1.str)`must match pattern "${schemaCode}"`,params:({schemaCode})=>(0,codegen_1._)`{pattern: ${schemaCode}}`};var def={keyword:"pattern",type:"string",schemaType:"string",$data:true,error:error2,code(cxt){const{gen,data,$data,schema,schemaCode,it}=cxt;const u=it.opts.unicodeRegExp?"u":"";if($data){const{regExp}=it.opts.code;const regExpCode=regExp.code==="new RegExp"?(0,codegen_1._)`new RegExp`:(0,util_1.useFunc)(gen,regExp);const valid=gen.let("valid");gen.try(()=>gen.assign(valid,(0,codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`),()=>gen.assign(valid,false));cxt.fail$data((0,codegen_1._)`!${valid}`)}else{const regExp=(0,code_1.usePattern)(cxt,schema);cxt.fail$data((0,codegen_1._)`!${regExp}.test(${data})`)}}};exports.default=def}});var require_limitProperties=__commonJS({"node_modules/ajv/dist/vocabularies/validation/limitProperties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var error2={message({keyword,schemaCode}){const comp=keyword==="maxProperties"?"more":"fewer";return(0,codegen_1.str)`must NOT have ${comp} than ${schemaCode} properties`},params:({schemaCode})=>(0,codegen_1._)`{limit: ${schemaCode}}`};var def={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:true,error:error2,code(cxt){const{keyword,data,schemaCode}=cxt;const op=keyword==="maxProperties"?codegen_1.operators.GT:codegen_1.operators.LT;cxt.fail$data((0,codegen_1._)`Object.keys(${data}).length ${op} ${schemaCode}`)}};exports.default=def}});var require_required=__commonJS({"node_modules/ajv/dist/vocabularies/validation/required.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var codegen_1=require_codegen();var util_1=require_util();var error2={message:({params:{missingProperty}})=>(0,codegen_1.str)`must have required property '${missingProperty}'`,params:({params:{missingProperty}})=>(0,codegen_1._)`{missingProperty: ${missingProperty}}`};var def={keyword:"required",type:"object",schemaType:"array",$data:true,error:error2,code(cxt){const{gen,schema,schemaCode,data,$data,it}=cxt;const{opts}=it;if(!$data&&schema.length===0)return;const useLoop=schema.length>=opts.loopRequired;if(it.allErrors)allErrorsMode();else exitOnErrorMode();if(opts.strictRequired){const props=cxt.parentSchema.properties;const{definedProperties}=cxt.it;for(const requiredKey of schema){if((props===null||props===void 0?void 0:props[requiredKey])===void 0&&!definedProperties.has(requiredKey)){const schemaPath=it.schemaEnv.baseId+it.errSchemaPath;const msg=`required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`;(0,util_1.checkStrictMode)(it,msg,it.opts.strictRequired)}}}function allErrorsMode(){if(useLoop||$data){cxt.block$data(codegen_1.nil,loopAllRequired)}else{for(const prop of schema){(0,code_1.checkReportMissingProp)(cxt,prop)}}}function exitOnErrorMode(){const missing=gen.let("missing");if(useLoop||$data){const valid=gen.let("valid",true);cxt.block$data(valid,()=>loopUntilMissing(missing,valid));cxt.ok(valid)}else{gen.if((0,code_1.checkMissingProp)(cxt,schema,missing));(0,code_1.reportMissingProp)(cxt,missing);gen.else()}}function loopAllRequired(){gen.forOf("prop",schemaCode,prop=>{cxt.setParams({missingProperty:prop});gen.if((0,code_1.noPropertyInData)(gen,data,prop,opts.ownProperties),()=>cxt.error())})}function loopUntilMissing(missing,valid){cxt.setParams({missingProperty:missing});gen.forOf(missing,schemaCode,()=>{gen.assign(valid,(0,code_1.propertyInData)(gen,data,missing,opts.ownProperties));gen.if((0,codegen_1.not)(valid),()=>{cxt.error();gen.break()})},codegen_1.nil)}}};exports.default=def}});var require_limitItems=__commonJS({"node_modules/ajv/dist/vocabularies/validation/limitItems.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var error2={message({keyword,schemaCode}){const comp=keyword==="maxItems"?"more":"fewer";return(0,codegen_1.str)`must NOT have ${comp} than ${schemaCode} items`},params:({schemaCode})=>(0,codegen_1._)`{limit: ${schemaCode}}`};var def={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:true,error:error2,code(cxt){const{keyword,data,schemaCode}=cxt;const op=keyword==="maxItems"?codegen_1.operators.GT:codegen_1.operators.LT;cxt.fail$data((0,codegen_1._)`${data}.length ${op} ${schemaCode}`)}};exports.default=def}});var require_equal=__commonJS({"node_modules/ajv/dist/runtime/equal.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var equal=require_fast_deep_equal();equal.code='require("ajv/dist/runtime/equal").default';exports.default=equal}});var require_uniqueItems=__commonJS({"node_modules/ajv/dist/vocabularies/validation/uniqueItems.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var dataType_1=require_dataType();var codegen_1=require_codegen();var util_1=require_util();var equal_1=require_equal();var error2={message:({params:{i,j}})=>(0,codegen_1.str)`must NOT have duplicate items (items ## ${j} and ${i} are identical)`,params:({params:{i,j}})=>(0,codegen_1._)`{i: ${i}, j: ${j}}`};var def={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:true,error:error2,code(cxt){const{gen,data,$data,schema,parentSchema,schemaCode,it}=cxt;if(!$data&&!schema)return;const valid=gen.let("valid");const itemTypes=parentSchema.items?(0,dataType_1.getSchemaTypes)(parentSchema.items):[];cxt.block$data(valid,validateUniqueItems,(0,codegen_1._)`${schemaCode} === false`);cxt.ok(valid);function validateUniqueItems(){const i=gen.let("i",(0,codegen_1._)`${data}.length`);const j=gen.let("j");cxt.setParams({i,j});gen.assign(valid,true);gen.if((0,codegen_1._)`${i} > 1`,()=>(canOptimize()?loopN:loopN2)(i,j))}function canOptimize(){return itemTypes.length>0&&!itemTypes.some(t=>t==="object"||t==="array")}function loopN(i,j){const item=gen.name("item");const wrongType=(0,dataType_1.checkDataTypes)(itemTypes,item,it.opts.strictNumbers,dataType_1.DataType.Wrong);const indices=gen.const("indices",(0,codegen_1._)`{}`);gen.for((0,codegen_1._)`;${i}--;`,()=>{gen.let(item,(0,codegen_1._)`${data}[${i}]`);gen.if(wrongType,(0,codegen_1._)`continue`);if(itemTypes.length>1)gen.if((0,codegen_1._)`typeof ${item} == "string"`,(0,codegen_1._)`${item} += "_"`);gen.if((0,codegen_1._)`typeof ${indices}[${item}] == "number"`,()=>{gen.assign(j,(0,codegen_1._)`${indices}[${item}]`);cxt.error();gen.assign(valid,false).break()}).code((0,codegen_1._)`${indices}[${item}] = ${i}`)})}function loopN2(i,j){const eql=(0,util_1.useFunc)(gen,equal_1.default);const outer=gen.name("outer");gen.label(outer).for((0,codegen_1._)`;${i}--;`,()=>gen.for((0,codegen_1._)`${j} = ${i}; ${j}--;`,()=>gen.if((0,codegen_1._)`${eql}(${data}[${i}], ${data}[${j}])`,()=>{cxt.error();gen.assign(valid,false).break(outer)})))}}};exports.default=def}});var require_const=__commonJS({"node_modules/ajv/dist/vocabularies/validation/const.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var equal_1=require_equal();var error2={message:"must be equal to constant",params:({schemaCode})=>(0,codegen_1._)`{allowedValue: ${schemaCode}}`};var def={keyword:"const",$data:true,error:error2,code(cxt){const{gen,data,$data,schemaCode,schema}=cxt;if($data||schema&&typeof schema=="object"){cxt.fail$data((0,codegen_1._)`!${(0,util_1.useFunc)(gen,equal_1.default)}(${data}, ${schemaCode})`)}else{cxt.fail((0,codegen_1._)`${schema} !== ${data}`)}}};exports.default=def}});var require_enum=__commonJS({"node_modules/ajv/dist/vocabularies/validation/enum.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var equal_1=require_equal();var error2={message:"must be equal to one of the allowed values",params:({schemaCode})=>(0,codegen_1._)`{allowedValues: ${schemaCode}}`};var def={keyword:"enum",schemaType:"array",$data:true,error:error2,code(cxt){const{gen,data,$data,schema,schemaCode,it}=cxt;if(!$data&&schema.length===0)throw new Error("enum must have non-empty array");const useLoop=schema.length>=it.opts.loopEnum;let eql;const getEql=()=>eql!==null&&eql!==void 0?eql:eql=(0,util_1.useFunc)(gen,equal_1.default);let valid;if(useLoop||$data){valid=gen.let("valid");cxt.block$data(valid,loopEnum)}else{if(!Array.isArray(schema))throw new Error("ajv implementation error");const vSchema=gen.const("vSchema",schemaCode);valid=(0,codegen_1.or)(...schema.map((_x,i)=>equalCode(vSchema,i)))}cxt.pass(valid);function loopEnum(){gen.assign(valid,false);gen.forOf("v",schemaCode,v=>gen.if((0,codegen_1._)`${getEql()}(${data}, ${v})`,()=>gen.assign(valid,true).break()))}function equalCode(vSchema,i){const sch=schema[i];return typeof sch==="object"&&sch!==null?(0,codegen_1._)`${getEql()}(${data}, ${vSchema}[${i}])`:(0,codegen_1._)`${data} === ${sch}`}}};exports.default=def}});var require_validation=__commonJS({"node_modules/ajv/dist/vocabularies/validation/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var limitNumber_1=require_limitNumber();var multipleOf_1=require_multipleOf();var limitLength_1=require_limitLength();var pattern_1=require_pattern();var limitProperties_1=require_limitProperties();var required_1=require_required();var limitItems_1=require_limitItems();var uniqueItems_1=require_uniqueItems();var const_1=require_const();var enum_1=require_enum();var validation=[limitNumber_1.default,multipleOf_1.default,limitLength_1.default,pattern_1.default,limitProperties_1.default,required_1.default,limitItems_1.default,uniqueItems_1.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},const_1.default,enum_1.default];exports.default=validation}});var require_additionalItems=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/additionalItems.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.validateAdditionalItems=void 0;var codegen_1=require_codegen();var util_1=require_util();var error2={message:({params:{len}})=>(0,codegen_1.str)`must NOT have more than ${len} items`,params:({params:{len}})=>(0,codegen_1._)`{limit: ${len}}`};var def={keyword:"additionalItems",type:"array",schemaType:["boolean","object"],before:"uniqueItems",error:error2,code(cxt){const{parentSchema,it}=cxt;const{items}=parentSchema;if(!Array.isArray(items)){(0,util_1.checkStrictMode)(it,'"additionalItems" is ignored when "items" is not an array of schemas');return}validateAdditionalItems(cxt,items)}};function validateAdditionalItems(cxt,items){const{gen,schema,data,keyword,it}=cxt;it.items=true;const len=gen.const("len",(0,codegen_1._)`${data}.length`);if(schema===false){cxt.setParams({len:items.length});cxt.pass((0,codegen_1._)`${len} <= ${items.length}`)}else if(typeof schema=="object"&&!(0,util_1.alwaysValidSchema)(it,schema)){const valid=gen.var("valid",(0,codegen_1._)`${len} <= ${items.length}`);gen.if((0,codegen_1.not)(valid),()=>validateItems(valid));cxt.ok(valid)}function validateItems(valid){gen.forRange("i",items.length,len,i=>{cxt.subschema({keyword,dataProp:i,dataPropType:util_1.Type.Num},valid);if(!it.allErrors)gen.if((0,codegen_1.not)(valid),()=>gen.break())})}}exports.validateAdditionalItems=validateAdditionalItems;exports.default=def}});var require_items=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/items.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.validateTuple=void 0;var codegen_1=require_codegen();var util_1=require_util();var code_1=require_code2();var def={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(cxt){const{schema,it}=cxt;if(Array.isArray(schema))return validateTuple(cxt,"additionalItems",schema);it.items=true;if((0,util_1.alwaysValidSchema)(it,schema))return;cxt.ok((0,code_1.validateArray)(cxt))}};function validateTuple(cxt,extraItems,schArr=cxt.schema){const{gen,parentSchema,data,keyword,it}=cxt;checkStrictTuple(parentSchema);if(it.opts.unevaluated&&schArr.length&&it.items!==true){it.items=util_1.mergeEvaluated.items(gen,schArr.length,it.items)}const valid=gen.name("valid");const len=gen.const("len",(0,codegen_1._)`${data}.length`);schArr.forEach((sch,i)=>{if((0,util_1.alwaysValidSchema)(it,sch))return;gen.if((0,codegen_1._)`${len} > ${i}`,()=>cxt.subschema({keyword,schemaProp:i,dataProp:i},valid));cxt.ok(valid)});function checkStrictTuple(sch){const{opts,errSchemaPath}=it;const l=schArr.length;const fullTuple=l===sch.minItems&&(l===sch.maxItems||sch[extraItems]===false);if(opts.strictTuples&&!fullTuple){const msg=`"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"`;(0,util_1.checkStrictMode)(it,msg,opts.strictTuples)}}}exports.validateTuple=validateTuple;exports.default=def}});var require_prefixItems=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/prefixItems.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var items_1=require_items();var def={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:cxt=>(0,items_1.validateTuple)(cxt,"items")};exports.default=def}});var require_items2020=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/items2020.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var code_1=require_code2();var additionalItems_1=require_additionalItems();var error2={message:({params:{len}})=>(0,codegen_1.str)`must NOT have more than ${len} items`,params:({params:{len}})=>(0,codegen_1._)`{limit: ${len}}`};var def={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:error2,code(cxt){const{schema,parentSchema,it}=cxt;const{prefixItems}=parentSchema;it.items=true;if((0,util_1.alwaysValidSchema)(it,schema))return;if(prefixItems)(0,additionalItems_1.validateAdditionalItems)(cxt,prefixItems);else cxt.ok((0,code_1.validateArray)(cxt))}};exports.default=def}});var require_contains=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/contains.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:({params:{min,max}})=>max===void 0?(0,codegen_1.str)`must contain at least ${min} valid item(s)`:(0,codegen_1.str)`must contain at least ${min} and no more than ${max} valid item(s)`,params:({params:{min,max}})=>max===void 0?(0,codegen_1._)`{minContains: ${min}}`:(0,codegen_1._)`{minContains: ${min}, maxContains: ${max}}`};var def={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:true,error:error2,code(cxt){const{gen,schema,parentSchema,data,it}=cxt;let min;let max;const{minContains,maxContains}=parentSchema;if(it.opts.next){min=minContains===void 0?1:minContains;max=maxContains}else{min=1}const len=gen.const("len",(0,codegen_1._)`${data}.length`);cxt.setParams({min,max});if(max===void 0&&min===0){(0,util_1.checkStrictMode)(it,`"minContains" == 0 without "maxContains": "contains" keyword ignored`);return}if(max!==void 0&&min>max){(0,util_1.checkStrictMode)(it,`"minContains" > "maxContains" is always invalid`);cxt.fail();return}if((0,util_1.alwaysValidSchema)(it,schema)){let cond=(0,codegen_1._)`${len} >= ${min}`;if(max!==void 0)cond=(0,codegen_1._)`${cond} && ${len} <= ${max}`;cxt.pass(cond);return}it.items=true;const valid=gen.name("valid");if(max===void 0&&min===1){validateItems(valid,()=>gen.if(valid,()=>gen.break()))}else if(min===0){gen.let(valid,true);if(max!==void 0)gen.if((0,codegen_1._)`${data}.length > 0`,validateItemsWithCount)}else{gen.let(valid,false);validateItemsWithCount()}cxt.result(valid,()=>cxt.reset());function validateItemsWithCount(){const schValid=gen.name("_valid");const count=gen.let("count",0);validateItems(schValid,()=>gen.if(schValid,()=>checkLimits(count)))}function validateItems(_valid,block){gen.forRange("i",0,len,i=>{cxt.subschema({keyword:"contains",dataProp:i,dataPropType:util_1.Type.Num,compositeRule:true},_valid);block()})}function checkLimits(count){gen.code((0,codegen_1._)`${count}++`);if(max===void 0){gen.if((0,codegen_1._)`${count} >= ${min}`,()=>gen.assign(valid,true).break())}else{gen.if((0,codegen_1._)`${count} > ${max}`,()=>gen.assign(valid,false).break());if(min===1)gen.assign(valid,true);else gen.if((0,codegen_1._)`${count} >= ${min}`,()=>gen.assign(valid,true))}}}};exports.default=def}});var require_dependencies=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/dependencies.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.validateSchemaDeps=exports.validatePropertyDeps=exports.error=void 0;var codegen_1=require_codegen();var util_1=require_util();var code_1=require_code2();exports.error={message:({params:{property,depsCount,deps}})=>{const property_ies=depsCount===1?"property":"properties";return(0,codegen_1.str)`must have ${property_ies} ${deps} when property ${property} is present`},params:({params:{property,depsCount,deps,missingProperty}})=>(0,codegen_1._)`{property: ${property}, missingProperty: ${missingProperty}, depsCount: ${depsCount}, - deps: ${deps}}`};var def={keyword:"dependencies",type:"object",schemaType:"object",error:exports.error,code(cxt){const[propDeps,schDeps]=splitDependencies(cxt);validatePropertyDeps(cxt,propDeps);validateSchemaDeps(cxt,schDeps)}};function splitDependencies({schema}){const propertyDeps={};const schemaDeps={};for(const key in schema){if(key==="__proto__")continue;const deps=Array.isArray(schema[key])?propertyDeps:schemaDeps;deps[key]=schema[key]}return[propertyDeps,schemaDeps]}function validatePropertyDeps(cxt,propertyDeps=cxt.schema){const{gen,data,it}=cxt;if(Object.keys(propertyDeps).length===0)return;const missing=gen.let("missing");for(const prop in propertyDeps){const deps=propertyDeps[prop];if(deps.length===0)continue;const hasProperty=(0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties);cxt.setParams({property:prop,depsCount:deps.length,deps:deps.join(", ")});if(it.allErrors){gen.if(hasProperty,()=>{for(const depProp of deps){(0,code_1.checkReportMissingProp)(cxt,depProp)}})}else{gen.if((0,codegen_1._)`${hasProperty} && (${(0,code_1.checkMissingProp)(cxt,deps,missing)})`);(0,code_1.reportMissingProp)(cxt,missing);gen.else()}}}exports.validatePropertyDeps=validatePropertyDeps;function validateSchemaDeps(cxt,schemaDeps=cxt.schema){const{gen,data,keyword,it}=cxt;const valid=gen.name("valid");for(const prop in schemaDeps){if((0,util_1.alwaysValidSchema)(it,schemaDeps[prop]))continue;gen.if((0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties),()=>{const schCxt=cxt.subschema({keyword,schemaProp:prop},valid);cxt.mergeValidEvaluated(schCxt,valid)},()=>gen.var(valid,true));cxt.ok(valid)}}exports.validateSchemaDeps=validateSchemaDeps;exports.default=def}});var require_propertyNames=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/propertyNames.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:"property name must be valid",params:({params})=>(0,codegen_1._)`{propertyName: ${params.propertyName}}`};var def={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:error2,code(cxt){const{gen,schema,data,it}=cxt;if((0,util_1.alwaysValidSchema)(it,schema))return;const valid=gen.name("valid");gen.forIn("key",data,key=>{cxt.setParams({propertyName:key});cxt.subschema({keyword:"propertyNames",data:key,dataTypes:["string"],propertyName:key,compositeRule:true},valid);gen.if((0,codegen_1.not)(valid),()=>{cxt.error(true);if(!it.allErrors)gen.break()})});cxt.ok(valid)}};exports.default=def}});var require_additionalProperties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var codegen_1=require_codegen();var names_1=require_names();var util_1=require_util();var error2={message:"must NOT have additional properties",params:({params})=>(0,codegen_1._)`{additionalProperty: ${params.additionalProperty}}`};var def={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:true,trackErrors:true,error:error2,code(cxt){const{gen,schema,parentSchema,data,errsCount,it}=cxt;if(!errsCount)throw new Error("ajv implementation error");const{allErrors,opts}=it;it.props=true;if(opts.removeAdditional!=="all"&&(0,util_1.alwaysValidSchema)(it,schema))return;const props=(0,code_1.allSchemaProperties)(parentSchema.properties);const patProps=(0,code_1.allSchemaProperties)(parentSchema.patternProperties);checkAdditionalProperties();cxt.ok((0,codegen_1._)`${errsCount} === ${names_1.default.errors}`);function checkAdditionalProperties(){gen.forIn("key",data,key=>{if(!props.length&&!patProps.length)additionalPropertyCode(key);else gen.if(isAdditional(key),()=>additionalPropertyCode(key))})}function isAdditional(key){let definedProp;if(props.length>8){const propsSchema=(0,util_1.schemaRefOrVal)(it,parentSchema.properties,"properties");definedProp=(0,code_1.isOwnProperty)(gen,propsSchema,key)}else if(props.length){definedProp=(0,codegen_1.or)(...props.map(p=>(0,codegen_1._)`${key} === ${p}`))}else{definedProp=codegen_1.nil}if(patProps.length){definedProp=(0,codegen_1.or)(definedProp,...patProps.map(p=>(0,codegen_1._)`${(0,code_1.usePattern)(cxt,p)}.test(${key})`))}return(0,codegen_1.not)(definedProp)}function deleteAdditional(key){gen.code((0,codegen_1._)`delete ${data}[${key}]`)}function additionalPropertyCode(key){if(opts.removeAdditional==="all"||opts.removeAdditional&&schema===false){deleteAdditional(key);return}if(schema===false){cxt.setParams({additionalProperty:key});cxt.error();if(!allErrors)gen.break();return}if(typeof schema=="object"&&!(0,util_1.alwaysValidSchema)(it,schema)){const valid=gen.name("valid");if(opts.removeAdditional==="failing"){applyAdditionalSchema(key,valid,false);gen.if((0,codegen_1.not)(valid),()=>{cxt.reset();deleteAdditional(key)})}else{applyAdditionalSchema(key,valid);if(!allErrors)gen.if((0,codegen_1.not)(valid),()=>gen.break())}}}function applyAdditionalSchema(key,valid,errors){const subschema={keyword:"additionalProperties",dataProp:key,dataPropType:util_1.Type.Str};if(errors===false){Object.assign(subschema,{compositeRule:true,createErrors:false,allErrors:false})}cxt.subschema(subschema,valid)}}};exports.default=def}});var require_properties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/properties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var validate_1=require_validate();var code_1=require_code2();var util_1=require_util();var additionalProperties_1=require_additionalProperties();var def={keyword:"properties",type:"object",schemaType:"object",code(cxt){const{gen,schema,parentSchema,data,it}=cxt;if(it.opts.removeAdditional==="all"&&parentSchema.additionalProperties===void 0){additionalProperties_1.default.code(new validate_1.KeywordCxt(it,additionalProperties_1.default,"additionalProperties"))}const allProps=(0,code_1.allSchemaProperties)(schema);for(const prop of allProps){it.definedProperties.add(prop)}if(it.opts.unevaluated&&allProps.length&&it.props!==true){it.props=util_1.mergeEvaluated.props(gen,(0,util_1.toHash)(allProps),it.props)}const properties=allProps.filter(p=>!(0,util_1.alwaysValidSchema)(it,schema[p]));if(properties.length===0)return;const valid=gen.name("valid");for(const prop of properties){if(hasDefault(prop)){applyPropertySchema(prop)}else{gen.if((0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties));applyPropertySchema(prop);if(!it.allErrors)gen.else().var(valid,true);gen.endIf()}cxt.it.definedProperties.add(prop);cxt.ok(valid)}function hasDefault(prop){return it.opts.useDefaults&&!it.compositeRule&&schema[prop].default!==void 0}function applyPropertySchema(prop){cxt.subschema({keyword:"properties",schemaProp:prop,dataProp:prop},valid)}}};exports.default=def}});var require_patternProperties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/patternProperties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var codegen_1=require_codegen();var util_1=require_util();var util_2=require_util();var def={keyword:"patternProperties",type:"object",schemaType:"object",code(cxt){const{gen,schema,data,parentSchema,it}=cxt;const{opts}=it;const patterns=(0,code_1.allSchemaProperties)(schema);const alwaysValidPatterns=patterns.filter(p=>(0,util_1.alwaysValidSchema)(it,schema[p]));if(patterns.length===0||alwaysValidPatterns.length===patterns.length&&(!it.opts.unevaluated||it.props===true)){return}const checkProperties=opts.strictSchema&&!opts.allowMatchingProperties&&parentSchema.properties;const valid=gen.name("valid");if(it.props!==true&&!(it.props instanceof codegen_1.Name)){it.props=(0,util_2.evaluatedPropsToName)(gen,it.props)}const{props}=it;validatePatternProperties();function validatePatternProperties(){for(const pat of patterns){if(checkProperties)checkMatchingProperties(pat);if(it.allErrors){validateProperties(pat)}else{gen.var(valid,true);validateProperties(pat);gen.if(valid)}}}function checkMatchingProperties(pat){for(const prop in checkProperties){if(new RegExp(pat).test(prop)){(0,util_1.checkStrictMode)(it,`property ${prop} matches pattern ${pat} (use allowMatchingProperties)`)}}}function validateProperties(pat){gen.forIn("key",data,key=>{gen.if((0,codegen_1._)`${(0,code_1.usePattern)(cxt,pat)}.test(${key})`,()=>{const alwaysValid=alwaysValidPatterns.includes(pat);if(!alwaysValid){cxt.subschema({keyword:"patternProperties",schemaProp:pat,dataProp:key,dataPropType:util_2.Type.Str},valid)}if(it.opts.unevaluated&&props!==true){gen.assign((0,codegen_1._)`${props}[${key}]`,true)}else if(!alwaysValid&&!it.allErrors){gen.if((0,codegen_1.not)(valid),()=>gen.break())}})})}}};exports.default=def}});var require_not=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/not.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:"not",schemaType:["object","boolean"],trackErrors:true,code(cxt){const{gen,schema,it}=cxt;if((0,util_1.alwaysValidSchema)(it,schema)){cxt.fail();return}const valid=gen.name("valid");cxt.subschema({keyword:"not",compositeRule:true,createErrors:false,allErrors:false},valid);cxt.failResult(valid,()=>cxt.reset(),()=>cxt.error())},error:{message:"must NOT be valid"}};exports.default=def}});var require_anyOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/anyOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var def={keyword:"anyOf",schemaType:"array",trackErrors:true,code:code_1.validateUnion,error:{message:"must match a schema in anyOf"}};exports.default=def}});var require_oneOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/oneOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:"must match exactly one schema in oneOf",params:({params})=>(0,codegen_1._)`{passingSchemas: ${params.passing}}`};var def={keyword:"oneOf",schemaType:"array",trackErrors:true,error:error2,code(cxt){const{gen,schema,parentSchema,it}=cxt;if(!Array.isArray(schema))throw new Error("ajv implementation error");if(it.opts.discriminator&&parentSchema.discriminator)return;const schArr=schema;const valid=gen.let("valid",false);const passing=gen.let("passing",null);const schValid=gen.name("_valid");cxt.setParams({passing});gen.block(validateOneOf);cxt.result(valid,()=>cxt.reset(),()=>cxt.error(true));function validateOneOf(){schArr.forEach((sch,i)=>{let schCxt;if((0,util_1.alwaysValidSchema)(it,sch)){gen.var(schValid,true)}else{schCxt=cxt.subschema({keyword:"oneOf",schemaProp:i,compositeRule:true},schValid)}if(i>0){gen.if((0,codegen_1._)`${schValid} && ${valid}`).assign(valid,false).assign(passing,(0,codegen_1._)`[${passing}, ${i}]`).else()}gen.if(schValid,()=>{gen.assign(valid,true);gen.assign(passing,i);if(schCxt)cxt.mergeEvaluated(schCxt,codegen_1.Name)})})}}};exports.default=def}});var require_allOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/allOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:"allOf",schemaType:"array",code(cxt){const{gen,schema,it}=cxt;if(!Array.isArray(schema))throw new Error("ajv implementation error");const valid=gen.name("valid");schema.forEach((sch,i)=>{if((0,util_1.alwaysValidSchema)(it,sch))return;const schCxt=cxt.subschema({keyword:"allOf",schemaProp:i},valid);cxt.ok(valid);cxt.mergeEvaluated(schCxt)})}};exports.default=def}});var require_if=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/if.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:({params})=>(0,codegen_1.str)`must match "${params.ifClause}" schema`,params:({params})=>(0,codegen_1._)`{failingKeyword: ${params.ifClause}}`};var def={keyword:"if",schemaType:["object","boolean"],trackErrors:true,error:error2,code(cxt){const{gen,parentSchema,it}=cxt;if(parentSchema.then===void 0&&parentSchema.else===void 0){(0,util_1.checkStrictMode)(it,'"if" without "then" and "else" is ignored')}const hasThen=hasSchema(it,"then");const hasElse=hasSchema(it,"else");if(!hasThen&&!hasElse)return;const valid=gen.let("valid",true);const schValid=gen.name("_valid");validateIf();cxt.reset();if(hasThen&&hasElse){const ifClause=gen.let("ifClause");cxt.setParams({ifClause});gen.if(schValid,validateClause("then",ifClause),validateClause("else",ifClause))}else if(hasThen){gen.if(schValid,validateClause("then"))}else{gen.if((0,codegen_1.not)(schValid),validateClause("else"))}cxt.pass(valid,()=>cxt.error(true));function validateIf(){const schCxt=cxt.subschema({keyword:"if",compositeRule:true,createErrors:false,allErrors:false},schValid);cxt.mergeEvaluated(schCxt)}function validateClause(keyword,ifClause){return()=>{const schCxt=cxt.subschema({keyword},schValid);gen.assign(valid,schValid);cxt.mergeValidEvaluated(schCxt,valid);if(ifClause)gen.assign(ifClause,(0,codegen_1._)`${keyword}`);else cxt.setParams({ifClause:keyword})}}}};function hasSchema(it,keyword){const schema=it.schema[keyword];return schema!==void 0&&!(0,util_1.alwaysValidSchema)(it,schema)}exports.default=def}});var require_thenElse=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/thenElse.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:["then","else"],schemaType:["object","boolean"],code({keyword,parentSchema,it}){if(parentSchema.if===void 0)(0,util_1.checkStrictMode)(it,`"${keyword}" without "if" is ignored`)}};exports.default=def}});var require_applicator=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var additionalItems_1=require_additionalItems();var prefixItems_1=require_prefixItems();var items_1=require_items();var items2020_1=require_items2020();var contains_1=require_contains();var dependencies_1=require_dependencies();var propertyNames_1=require_propertyNames();var additionalProperties_1=require_additionalProperties();var properties_1=require_properties();var patternProperties_1=require_patternProperties();var not_1=require_not();var anyOf_1=require_anyOf();var oneOf_1=require_oneOf();var allOf_1=require_allOf();var if_1=require_if();var thenElse_1=require_thenElse();function getApplicator(draft2020=false){const applicator=[not_1.default,anyOf_1.default,oneOf_1.default,allOf_1.default,if_1.default,thenElse_1.default,propertyNames_1.default,additionalProperties_1.default,dependencies_1.default,properties_1.default,patternProperties_1.default];if(draft2020)applicator.push(prefixItems_1.default,items2020_1.default);else applicator.push(additionalItems_1.default,items_1.default);applicator.push(contains_1.default);return applicator}exports.default=getApplicator}});var require_format=__commonJS({"node_modules/ajv/dist/vocabularies/format/format.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var error2={message:({schemaCode})=>(0,codegen_1.str)`must match format "${schemaCode}"`,params:({schemaCode})=>(0,codegen_1._)`{format: ${schemaCode}}`};var def={keyword:"format",type:["number","string"],schemaType:"string",$data:true,error:error2,code(cxt,ruleType){const{gen,data,$data,schema,schemaCode,it}=cxt;const{opts,errSchemaPath,schemaEnv,self}=it;if(!opts.validateFormats)return;if($data)validate$DataFormat();else validateFormat();function validate$DataFormat(){const fmts=gen.scopeValue("formats",{ref:self.formats,code:opts.code.formats});const fDef=gen.const("fDef",(0,codegen_1._)`${fmts}[${schemaCode}]`);const fType=gen.let("fType");const format=gen.let("format");gen.if((0,codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`,()=>gen.assign(fType,(0,codegen_1._)`${fDef}.type || "string"`).assign(format,(0,codegen_1._)`${fDef}.validate`),()=>gen.assign(fType,(0,codegen_1._)`"string"`).assign(format,fDef));cxt.fail$data((0,codegen_1.or)(unknownFmt(),invalidFmt()));function unknownFmt(){if(opts.strictSchema===false)return codegen_1.nil;return(0,codegen_1._)`${schemaCode} && !${format}`}function invalidFmt(){const callFormat=schemaEnv.$async?(0,codegen_1._)`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))`:(0,codegen_1._)`${format}(${data})`;const validData=(0,codegen_1._)`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`;return(0,codegen_1._)`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`}}function validateFormat(){const formatDef=self.formats[schema];if(!formatDef){unknownFormat();return}if(formatDef===true)return;const[fmtType,format,fmtRef]=getFormat(formatDef);if(fmtType===ruleType)cxt.pass(validCondition());function unknownFormat(){if(opts.strictSchema===false){self.logger.warn(unknownMsg());return}throw new Error(unknownMsg());function unknownMsg(){return`unknown format "${schema}" ignored in schema at path "${errSchemaPath}"`}}function getFormat(fmtDef){const code=fmtDef instanceof RegExp?(0,codegen_1.regexpCode)(fmtDef):opts.code.formats?(0,codegen_1._)`${opts.code.formats}${(0,codegen_1.getProperty)(schema)}`:void 0;const fmt=gen.scopeValue("formats",{key:schema,ref:fmtDef,code});if(typeof fmtDef=="object"&&!(fmtDef instanceof RegExp)){return[fmtDef.type||"string",fmtDef.validate,(0,codegen_1._)`${fmt}.validate`]}return["string",fmtDef,fmt]}function validCondition(){if(typeof formatDef=="object"&&!(formatDef instanceof RegExp)&&formatDef.async){if(!schemaEnv.$async)throw new Error("async format in sync schema");return(0,codegen_1._)`await ${fmtRef}(${data})`}return typeof format=="function"?(0,codegen_1._)`${fmtRef}(${data})`:(0,codegen_1._)`${fmtRef}.test(${data})`}}}};exports.default=def}});var require_format2=__commonJS({"node_modules/ajv/dist/vocabularies/format/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var format_1=require_format();var format=[format_1.default];exports.default=format}});var require_metadata=__commonJS({"node_modules/ajv/dist/vocabularies/metadata.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.contentVocabulary=exports.metadataVocabulary=void 0;exports.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"];exports.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]}});var require_draft7=__commonJS({"node_modules/ajv/dist/vocabularies/draft7.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var core_1=require_core2();var validation_1=require_validation();var applicator_1=require_applicator();var format_1=require_format2();var metadata_1=require_metadata();var draft7Vocabularies=[core_1.default,validation_1.default,(0,applicator_1.default)(),format_1.default,metadata_1.metadataVocabulary,metadata_1.contentVocabulary];exports.default=draft7Vocabularies}});var require_types=__commonJS({"node_modules/ajv/dist/vocabularies/discriminator/types.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DiscrError=void 0;var DiscrError;(function(DiscrError2){DiscrError2["Tag"]="tag";DiscrError2["Mapping"]="mapping"})(DiscrError||(exports.DiscrError=DiscrError={}))}});var require_discriminator=__commonJS({"node_modules/ajv/dist/vocabularies/discriminator/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var types_1=require_types();var compile_1=require_compile();var ref_error_1=require_ref_error();var util_1=require_util();var error2={message:({params:{discrError,tagName}})=>discrError===types_1.DiscrError.Tag?`tag "${tagName}" must be string`:`value of tag "${tagName}" must be in oneOf`,params:({params:{discrError,tag,tagName}})=>(0,codegen_1._)`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`};var def={keyword:"discriminator",type:"object",schemaType:"object",error:error2,code(cxt){const{gen,data,schema,parentSchema,it}=cxt;const{oneOf}=parentSchema;if(!it.opts.discriminator){throw new Error("discriminator: requires discriminator option")}const tagName=schema.propertyName;if(typeof tagName!="string")throw new Error("discriminator: requires propertyName");if(schema.mapping)throw new Error("discriminator: mapping is not supported");if(!oneOf)throw new Error("discriminator: requires oneOf keyword");const valid=gen.let("valid",false);const tag=gen.const("tag",(0,codegen_1._)`${data}${(0,codegen_1.getProperty)(tagName)}`);gen.if((0,codegen_1._)`typeof ${tag} == "string"`,()=>validateMapping(),()=>cxt.error(false,{discrError:types_1.DiscrError.Tag,tag,tagName}));cxt.ok(valid);function validateMapping(){const mapping=getMapping();gen.if(false);for(const tagValue in mapping){gen.elseIf((0,codegen_1._)`${tag} === ${tagValue}`);gen.assign(valid,applyTagSchema(mapping[tagValue]))}gen.else();cxt.error(false,{discrError:types_1.DiscrError.Mapping,tag,tagName});gen.endIf()}function applyTagSchema(schemaProp){const _valid=gen.name("valid");const schCxt=cxt.subschema({keyword:"oneOf",schemaProp},_valid);cxt.mergeEvaluated(schCxt,codegen_1.Name);return _valid}function getMapping(){var _a3;const oneOfMapping={};const topRequired=hasRequired(parentSchema);let tagRequired=true;for(let i=0;ithis.addVocabulary(v));if(this.opts.discriminator)this.addKeyword(discriminator_1.default)}_addDefaultMetaSchema(){super._addDefaultMetaSchema();if(!this.opts.meta)return;const metaSchema=this.opts.$data?this.$dataMetaSchema(draft7MetaSchema,META_SUPPORT_DATA):draft7MetaSchema;this.addMetaSchema(metaSchema,META_SCHEMA_ID,false);this.refs["http://json-schema.org/schema"]=META_SCHEMA_ID}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(META_SCHEMA_ID)?META_SCHEMA_ID:void 0)}};exports.Ajv=Ajv2;module.exports=exports=Ajv2;module.exports.Ajv=Ajv2;Object.defineProperty(exports,"__esModule",{value:true});exports.default=Ajv2;var validate_1=require_validate();Object.defineProperty(exports,"KeywordCxt",{enumerable:true,get:function(){return validate_1.KeywordCxt}});var codegen_1=require_codegen();Object.defineProperty(exports,"_",{enumerable:true,get:function(){return codegen_1._}});Object.defineProperty(exports,"str",{enumerable:true,get:function(){return codegen_1.str}});Object.defineProperty(exports,"stringify",{enumerable:true,get:function(){return codegen_1.stringify}});Object.defineProperty(exports,"nil",{enumerable:true,get:function(){return codegen_1.nil}});Object.defineProperty(exports,"Name",{enumerable:true,get:function(){return codegen_1.Name}});Object.defineProperty(exports,"CodeGen",{enumerable:true,get:function(){return codegen_1.CodeGen}});var validation_error_1=require_validation_error();Object.defineProperty(exports,"ValidationError",{enumerable:true,get:function(){return validation_error_1.default}});var ref_error_1=require_ref_error();Object.defineProperty(exports,"MissingRefError",{enumerable:true,get:function(){return ref_error_1.default}})}});var require_formats=__commonJS({"node_modules/ajv-formats/dist/formats.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.formatNames=exports.fastFormats=exports.fullFormats=void 0;function fmtDef(validate,compare){return{validate,compare}}exports.fullFormats={date:fmtDef(date4,compareDate),time:fmtDef(getTime(true),compareTime),"date-time":fmtDef(getDateTime(true),compareDateTime),"iso-time":fmtDef(getTime(),compareIsoTime),"iso-date-time":fmtDef(getDateTime(),compareIsoDateTime),duration:/^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/,uri,"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,url:/^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,ipv6:/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i,regex,uuid:/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,"json-pointer":/^(?:\/(?:[^~/]|~0|~1)*)*$/,"json-pointer-uri-fragment":/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,"relative-json-pointer":/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,byte,int32:{type:"number",validate:validateInt32},int64:{type:"number",validate:validateInt64},float:{type:"number",validate:validateNumber},double:{type:"number",validate:validateNumber},password:true,binary:true};exports.fastFormats={...exports.fullFormats,date:fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d$/,compareDate),time:fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,compareTime),"date-time":fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,compareDateTime),"iso-time":fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,compareIsoTime),"iso-date-time":fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,compareIsoDateTime),uri:/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i};exports.formatNames=Object.keys(exports.fullFormats);function isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}var DATE=/^(\d\d\d\d)-(\d\d)-(\d\d)$/;var DAYS=[0,31,28,31,30,31,30,31,31,30,31,30,31];function date4(str){const matches=DATE.exec(str);if(!matches)return false;const year=+matches[1];const month=+matches[2];const day=+matches[3];return month>=1&&month<=12&&day>=1&&day<=(month===2&&isLeapYear(year)?29:DAYS[month])}function compareDate(d1,d2){if(!(d1&&d2))return void 0;if(d1>d2)return 1;if(d123||tzM>59||strictTimeZone&&!tz)return false;if(hr<=23&&min<=59&&sec<60)return true;const utcMin=min-tzM*tzSign;const utcHr=hr-tzH*tzSign-(utcMin<0?1:0);return(utcHr===23||utcHr===-1)&&(utcMin===59||utcMin===-1)&&sec<61}}function compareTime(s1,s2){if(!(s1&&s2))return void 0;const t1=new Date("2020-01-01T"+s1).valueOf();const t2=new Date("2020-01-01T"+s2).valueOf();if(!(t1&&t2))return void 0;return t1-t2}function compareIsoTime(t1,t2){if(!(t1&&t2))return void 0;const a1=TIME.exec(t1);const a2=TIME.exec(t2);if(!(a1&&a2))return void 0;t1=a1[1]+a1[2]+a1[3];t2=a2[1]+a2[2]+a2[3];if(t1>t2)return 1;if(t1=MIN_INT32}function validateInt64(value){return Number.isInteger(value)}function validateNumber(){return true}var Z_ANCHOR=/[^\\]\\Z/;function regex(str){if(Z_ANCHOR.test(str))return false;try{new RegExp(str);return true}catch(e){return false}}}});var require_limit=__commonJS({"node_modules/ajv-formats/dist/limit.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.formatLimitDefinition=void 0;var ajv_1=require_ajv();var codegen_1=require_codegen();var ops=codegen_1.operators;var KWDs={formatMaximum:{okStr:"<=",ok:ops.LTE,fail:ops.GT},formatMinimum:{okStr:">=",ok:ops.GTE,fail:ops.LT},formatExclusiveMaximum:{okStr:"<",ok:ops.LT,fail:ops.GTE},formatExclusiveMinimum:{okStr:">",ok:ops.GT,fail:ops.LTE}};var error2={message:({keyword,schemaCode})=>(0,codegen_1.str)`should be ${KWDs[keyword].okStr} ${schemaCode}`,params:({keyword,schemaCode})=>(0,codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}`};exports.formatLimitDefinition={keyword:Object.keys(KWDs),type:"string",schemaType:"string",$data:true,error:error2,code(cxt){const{gen,data,schemaCode,keyword,it}=cxt;const{opts,self}=it;if(!opts.validateFormats)return;const fCxt=new ajv_1.KeywordCxt(it,self.RULES.all.format.definition,"format");if(fCxt.$data)validate$DataFormat();else validateFormat();function validate$DataFormat(){const fmts=gen.scopeValue("formats",{ref:self.formats,code:opts.code.formats});const fmt=gen.const("fmt",(0,codegen_1._)`${fmts}[${fCxt.schemaCode}]`);cxt.fail$data((0,codegen_1.or)((0,codegen_1._)`typeof ${fmt} != "object"`,(0,codegen_1._)`${fmt} instanceof RegExp`,(0,codegen_1._)`typeof ${fmt}.compare != "function"`,compareCode(fmt)))}function validateFormat(){const format=fCxt.schema;const fmtDef=self.formats[format];if(!fmtDef||fmtDef===true)return;if(typeof fmtDef!="object"||fmtDef instanceof RegExp||typeof fmtDef.compare!="function"){throw new Error(`"${keyword}": format "${format}" does not define "compare" function`)}const fmt=gen.scopeValue("formats",{key:format,ref:fmtDef,code:opts.code.formats?(0,codegen_1._)`${opts.code.formats}${(0,codegen_1.getProperty)(format)}`:void 0});cxt.fail$data(compareCode(fmt))}function compareCode(fmt){return(0,codegen_1._)`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`}},dependencies:["format"]};var formatLimitPlugin=ajv=>{ajv.addKeyword(exports.formatLimitDefinition);return ajv};exports.default=formatLimitPlugin}});var require_dist=__commonJS({"node_modules/ajv-formats/dist/index.js"(exports,module){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var formats_1=require_formats();var limit_1=require_limit();var codegen_1=require_codegen();var fullName=new codegen_1.Name("fullFormats");var fastName=new codegen_1.Name("fastFormats");var formatsPlugin=(ajv,opts={keywords:true})=>{if(Array.isArray(opts)){addFormats(ajv,opts,formats_1.fullFormats,fullName);return ajv}const[formats,exportName]=opts.mode==="fast"?[formats_1.fastFormats,fastName]:[formats_1.fullFormats,fullName];const list=opts.formats||formats_1.formatNames;addFormats(ajv,list,formats,exportName);if(opts.keywords)(0,limit_1.default)(ajv);return ajv};formatsPlugin.get=(name,mode="full")=>{const formats=mode==="fast"?formats_1.fastFormats:formats_1.fullFormats;const f=formats[name];if(!f)throw new Error(`Unknown format "${name}"`);return f};function addFormats(ajv,list,fs,exportName){var _a3;var _b;(_a3=(_b=ajv.opts.code).formats)!==null&&_a3!==void 0?_a3:_b.formats=(0,codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;for(const f of list)ajv.addFormat(f,fs[f])}module.exports=exports=formatsPlugin;Object.defineProperty(exports,"__esModule",{value:true});exports.default=formatsPlugin}});var require_content_type=__commonJS({"node_modules/content-type/index.js"(exports){"use strict";var PARAM_REGEXP=/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;var TEXT_REGEXP=/^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;var TOKEN_REGEXP=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;var QESC_REGEXP=/\\([\u000b\u0020-\u00ff])/g;var QUOTE_REGEXP=/([\\"])/g;var TYPE_REGEXP=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;exports.format=format;exports.parse=parse3;function format(obj){if(!obj||typeof obj!=="object"){throw new TypeError("argument obj is required")}var parameters=obj.parameters;var type=obj.type;if(!type||!TYPE_REGEXP.test(type)){throw new TypeError("invalid type")}var string4=type;if(parameters&&typeof parameters==="object"){var param;var params=Object.keys(parameters).sort();for(var i=0;i0&&!TEXT_REGEXP.test(str)){throw new TypeError("invalid parameter value")}return'"'+str.replace(QUOTE_REGEXP,"\\$1")+'"'}function ContentType(type){this.parameters=Object.create(null);this.type=type}}});var _a;var NEVER=Object.freeze({status:"aborted"});function $constructor(name,initializer3,params){function init(inst,def){if(!inst._zod){Object.defineProperty(inst,"_zod",{value:{def,constr:_,traits:new Set},enumerable:false})}if(inst._zod.traits.has(name)){return}inst._zod.traits.add(name);initializer3(inst,def);const proto=_.prototype;const keys=Object.keys(proto);for(let i=0;i{if(params?.Parent&&inst instanceof params.Parent)return true;return inst?._zod?.traits?.has(name)}});Object.defineProperty(_,"name",{value:name});return _}var $ZodAsyncError=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};var $ZodEncodeError=class extends Error{constructor(name){super(`Encountered unidirectional transform during encode: ${name}`);this.name="ZodEncodeError"}};(_a=globalThis).__zod_globalConfig??(_a.__zod_globalConfig={});var globalConfig=globalThis.__zod_globalConfig;function config(newConfig){if(newConfig)Object.assign(globalConfig,newConfig);return globalConfig}var util_exports={};__export(util_exports,{BIGINT_FORMAT_RANGES:()=>BIGINT_FORMAT_RANGES,Class:()=>Class,NUMBER_FORMAT_RANGES:()=>NUMBER_FORMAT_RANGES,aborted:()=>aborted,allowsEval:()=>allowsEval,assert:()=>assert,assertEqual:()=>assertEqual,assertIs:()=>assertIs,assertNever:()=>assertNever,assertNotEqual:()=>assertNotEqual,assignProp:()=>assignProp,base64ToUint8Array:()=>base64ToUint8Array,base64urlToUint8Array:()=>base64urlToUint8Array,cached:()=>cached,captureStackTrace:()=>captureStackTrace,cleanEnum:()=>cleanEnum,cleanRegex:()=>cleanRegex,clone:()=>clone,cloneDef:()=>cloneDef,createTransparentProxy:()=>createTransparentProxy,defineLazy:()=>defineLazy,esc:()=>esc,escapeRegex:()=>escapeRegex,explicitlyAborted:()=>explicitlyAborted,extend:()=>extend,finalizeIssue:()=>finalizeIssue,floatSafeRemainder:()=>floatSafeRemainder,getElementAtPath:()=>getElementAtPath,getEnumValues:()=>getEnumValues,getLengthableOrigin:()=>getLengthableOrigin,getParsedType:()=>getParsedType,getSizableOrigin:()=>getSizableOrigin,hexToUint8Array:()=>hexToUint8Array,isObject:()=>isObject,isPlainObject:()=>isPlainObject,issue:()=>issue,joinValues:()=>joinValues,jsonStringifyReplacer:()=>jsonStringifyReplacer,merge:()=>merge,mergeDefs:()=>mergeDefs,normalizeParams:()=>normalizeParams,nullish:()=>nullish,numKeys:()=>numKeys,objectClone:()=>objectClone,omit:()=>omit,optionalKeys:()=>optionalKeys,parsedType:()=>parsedType,partial:()=>partial,pick:()=>pick,prefixIssues:()=>prefixIssues,primitiveTypes:()=>primitiveTypes,promiseAllObject:()=>promiseAllObject,propertyKeyTypes:()=>propertyKeyTypes,randomString:()=>randomString,required:()=>required,safeExtend:()=>safeExtend,shallowClone:()=>shallowClone,slugify:()=>slugify,stringifyPrimitive:()=>stringifyPrimitive,uint8ArrayToBase64:()=>uint8ArrayToBase64,uint8ArrayToBase64url:()=>uint8ArrayToBase64url,uint8ArrayToHex:()=>uint8ArrayToHex,unwrapMessage:()=>unwrapMessage});function assertEqual(val){return val}function assertNotEqual(val){return val}function assertIs(_arg){}function assertNever(_x){throw new Error("Unexpected value in exhaustive check")}function assert(_){}function getEnumValues(entries){const numericValues=Object.values(entries).filter(v=>typeof v==="number");const values=Object.entries(entries).filter(([k,_])=>numericValues.indexOf(+k)===-1).map(([_,v])=>v);return values}function joinValues(array2,separator="|"){return array2.map(val=>stringifyPrimitive(val)).join(separator)}function jsonStringifyReplacer(_,value){if(typeof value==="bigint")return value.toString();return value}function cached(getter){const set=false;return{get value(){if(!set){const value=getter();Object.defineProperty(this,"value",{value});return value}throw new Error("cached value already set")}}}function nullish(input){return input===null||input===void 0}function cleanRegex(source){const start=source.startsWith("^")?1:0;const end=source.endsWith("$")?source.length-1:source.length;return source.slice(start,end)}function floatSafeRemainder(val,step){const ratio=val/step;const roundedRatio=Math.round(ratio);const tolerance=Number.EPSILON*Math.max(Math.abs(ratio),1);if(Math.abs(ratio-roundedRatio)acc?.[key],obj)}function promiseAllObject(promisesObj){const keys=Object.keys(promisesObj);const promises=keys.map(key=>promisesObj[key]);return Promise.all(promises).then(results=>{const resolvedObj={};for(let i=0;i{};function isObject(data){return typeof data==="object"&&data!==null&&!Array.isArray(data)}var allowsEval=cached(()=>{if(globalConfig.jitless){return false}if(typeof navigator!=="undefined"&&navigator?.userAgent?.includes("Cloudflare")){return false}try{const F=Function;new F("");return true}catch(_){return false}});function isPlainObject(o){if(isObject(o)===false)return false;const ctor=o.constructor;if(ctor===void 0)return true;if(typeof ctor!=="function")return true;const prot=ctor.prototype;if(isObject(prot)===false)return false;if(Object.prototype.hasOwnProperty.call(prot,"isPrototypeOf")===false){return false}return true}function shallowClone(o){if(isPlainObject(o))return{...o};if(Array.isArray(o))return[...o];if(o instanceof Map)return new Map(o);if(o instanceof Set)return new Set(o);return o}function numKeys(data){let keyCount=0;for(const key in data){if(Object.prototype.hasOwnProperty.call(data,key)){keyCount++}}return keyCount}var getParsedType=data=>{const t=typeof data;switch(t){case"undefined":return"undefined";case"string":return"string";case"number":return Number.isNaN(data)?"nan":"number";case"boolean":return"boolean";case"function":return"function";case"bigint":return"bigint";case"symbol":return"symbol";case"object":if(Array.isArray(data)){return"array"}if(data===null){return"null"}if(data.then&&typeof data.then==="function"&&data.catch&&typeof data.catch==="function"){return"promise"}if(typeof Map!=="undefined"&&data instanceof Map){return"map"}if(typeof Set!=="undefined"&&data instanceof Set){return"set"}if(typeof Date!=="undefined"&&data instanceof Date){return"date"}if(typeof File!=="undefined"&&data instanceof File){return"file"}return"object";default:throw new Error(`Unknown data type: ${t}`)}};var propertyKeyTypes=new Set(["string","number","symbol"]);var primitiveTypes=new Set(["string","number","bigint","boolean","symbol","undefined"]);function escapeRegex(str){return str.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function clone(inst,def,params){const cl=new inst._zod.constr(def??inst._zod.def);if(!def||params?.parent)cl._zod.parent=inst;return cl}function normalizeParams(_params){const params=_params;if(!params)return{};if(typeof params==="string")return{error:()=>params};if(params?.message!==void 0){if(params?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");params.error=params.message}delete params.message;if(typeof params.error==="string")return{...params,error:()=>params.error};return params}function createTransparentProxy(getter){let target;return new Proxy({},{get(_,prop,receiver){target??(target=getter());return Reflect.get(target,prop,receiver)},set(_,prop,value,receiver){target??(target=getter());return Reflect.set(target,prop,value,receiver)},has(_,prop){target??(target=getter());return Reflect.has(target,prop)},deleteProperty(_,prop){target??(target=getter());return Reflect.deleteProperty(target,prop)},ownKeys(_){target??(target=getter());return Reflect.ownKeys(target)},getOwnPropertyDescriptor(_,prop){target??(target=getter());return Reflect.getOwnPropertyDescriptor(target,prop)},defineProperty(_,prop,descriptor){target??(target=getter());return Reflect.defineProperty(target,prop,descriptor)}})}function stringifyPrimitive(value){if(typeof value==="bigint")return value.toString()+"n";if(typeof value==="string")return`"${value}"`;return`${value}`}function optionalKeys(shape){return Object.keys(shape).filter(k=>{return shape[k]._zod.optin==="optional"&&shape[k]._zod.optout==="optional"})}var NUMBER_FORMAT_RANGES={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};var BIGINT_FORMAT_RANGES={int64:[BigInt("-9223372036854775808"),BigInt("9223372036854775807")],uint64:[BigInt(0),BigInt("18446744073709551615")]};function pick(schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".pick() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const newShape={};for(const key in mask){if(!(key in currDef.shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;newShape[key]=currDef.shape[key]}assignProp(this,"shape",newShape);return newShape},checks:[]});return clone(schema,def)}function omit(schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".omit() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const newShape={...schema._zod.def.shape};for(const key in mask){if(!(key in currDef.shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;delete newShape[key]}assignProp(this,"shape",newShape);return newShape},checks:[]});return clone(schema,def)}function extend(schema,shape){if(!isPlainObject(shape)){throw new Error("Invalid input to extend: expected a plain object")}const checks=schema._zod.def.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){const existingShape=schema._zod.def.shape;for(const key in shape){if(Object.getOwnPropertyDescriptor(existingShape,key)!==void 0){throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}}}const def=mergeDefs(schema._zod.def,{get shape(){const _shape={...schema._zod.def.shape,...shape};assignProp(this,"shape",_shape);return _shape}});return clone(schema,def)}function safeExtend(schema,shape){if(!isPlainObject(shape)){throw new Error("Invalid input to safeExtend: expected a plain object")}const def=mergeDefs(schema._zod.def,{get shape(){const _shape={...schema._zod.def.shape,...shape};assignProp(this,"shape",_shape);return _shape}});return clone(schema,def)}function merge(a,b){if(a._zod.def.checks?.length){throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.")}const def=mergeDefs(a._zod.def,{get shape(){const _shape={...a._zod.def.shape,...b._zod.def.shape};assignProp(this,"shape",_shape);return _shape},get catchall(){return b._zod.def.catchall},checks:b._zod.def.checks??[]});return clone(a,def)}function partial(Class2,schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".partial() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const oldShape=schema._zod.def.shape;const shape={...oldShape};if(mask){for(const key in mask){if(!(key in oldShape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;shape[key]=Class2?new Class2({type:"optional",innerType:oldShape[key]}):oldShape[key]}}else{for(const key in oldShape){shape[key]=Class2?new Class2({type:"optional",innerType:oldShape[key]}):oldShape[key]}}assignProp(this,"shape",shape);return shape},checks:[]});return clone(schema,def)}function required(Class2,schema,mask){const def=mergeDefs(schema._zod.def,{get shape(){const oldShape=schema._zod.def.shape;const shape={...oldShape};if(mask){for(const key in mask){if(!(key in shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;shape[key]=new Class2({type:"nonoptional",innerType:oldShape[key]})}}else{for(const key in oldShape){shape[key]=new Class2({type:"nonoptional",innerType:oldShape[key]})}}assignProp(this,"shape",shape);return shape}});return clone(schema,def)}function aborted(x,startIndex=0){if(x.aborted===true)return true;for(let i=startIndex;i{var _a3;(_a3=iss).path??(_a3.path=[]);iss.path.unshift(path);return iss})}function unwrapMessage(message){return typeof message==="string"?message:message?.message}function finalizeIssue(iss,ctx,config2){const message=iss.message?iss.message:unwrapMessage(iss.inst?._zod.def?.error?.(iss))??unwrapMessage(ctx?.error?.(iss))??unwrapMessage(config2.customError?.(iss))??unwrapMessage(config2.localeError?.(iss))??"Invalid input";const{inst:_inst,continue:_continue,input:_input,...rest}=iss;rest.path??(rest.path=[]);rest.message=message;if(ctx?.reportInput){rest.input=_input}return rest}function getSizableOrigin(input){if(input instanceof Set)return"set";if(input instanceof Map)return"map";if(input instanceof File)return"file";return"unknown"}function getLengthableOrigin(input){if(Array.isArray(input))return"array";if(typeof input==="string")return"string";return"unknown"}function parsedType(data){const t=typeof data;switch(t){case"number":{return Number.isNaN(data)?"nan":"number"}case"object":{if(data===null){return"null"}if(Array.isArray(data)){return"array"}const obj=data;if(obj&&Object.getPrototypeOf(obj)!==Object.prototype&&"constructor"in obj&&obj.constructor){return obj.constructor.name}}}return t}function issue(...args){const[iss,input,inst]=args;if(typeof iss==="string"){return{message:iss,code:"custom",input,inst}}return{...iss}}function cleanEnum(obj){return Object.entries(obj).filter(([k,_])=>{return Number.isNaN(Number.parseInt(k,10))}).map(el=>el[1])}function base64ToUint8Array(base642){const binaryString=atob(base642);const bytes=new Uint8Array(binaryString.length);for(let i=0;ib.toString(16).padStart(2,"0")).join("")}var Class=class{constructor(..._args){}};var initializer=(inst,def)=>{inst.name="$ZodError";Object.defineProperty(inst,"_zod",{value:inst._zod,enumerable:false});Object.defineProperty(inst,"issues",{value:def,enumerable:false});inst.message=JSON.stringify(def,jsonStringifyReplacer,2);Object.defineProperty(inst,"toString",{value:()=>inst.message,enumerable:false})};var $ZodError=$constructor("$ZodError",initializer);var $ZodRealError=$constructor("$ZodError",initializer,{Parent:Error});function flattenError(error2,mapper=issue2=>issue2.message){const fieldErrors={};const formErrors=[];for(const sub of error2.issues){if(sub.path.length>0){fieldErrors[sub.path[0]]=fieldErrors[sub.path[0]]||[];fieldErrors[sub.path[0]].push(mapper(sub))}else{formErrors.push(mapper(sub))}}return{formErrors,fieldErrors}}function formatError(error2,mapper=issue2=>issue2.message){const fieldErrors={_errors:[]};const processError=(error3,path=[])=>{for(const issue2 of error3.issues){if(issue2.code==="invalid_union"&&issue2.errors.length){issue2.errors.map(issues=>processError({issues},[...path,...issue2.path]))}else if(issue2.code==="invalid_key"){processError({issues:issue2.issues},[...path,...issue2.path])}else if(issue2.code==="invalid_element"){processError({issues:issue2.issues},[...path,...issue2.path])}else{const fullpath=[...path,...issue2.path];if(fullpath.length===0){fieldErrors._errors.push(mapper(issue2))}else{let curr=fieldErrors;let i=0;while(i(schema,value,_ctx,_params)=>{const ctx=_ctx?{..._ctx,async:false}:{async:false};const result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise){throw new $ZodAsyncError}if(result.issues.length){const e=new(_params?.Err??_Err)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())));captureStackTrace(e,_params?.callee);throw e}return result.value};var _parseAsync=_Err=>async(schema,value,_ctx,params)=>{const ctx=_ctx?{..._ctx,async:true}:{async:true};let result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise)result=await result;if(result.issues.length){const e=new(params?.Err??_Err)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())));captureStackTrace(e,params?.callee);throw e}return result.value};var _safeParse=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,async:false}:{async:false};const result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise){throw new $ZodAsyncError}return result.issues.length?{success:false,error:new(_Err??$ZodError)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())))}:{success:true,data:result.value}};var safeParse=_safeParse($ZodRealError);var _safeParseAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,async:true}:{async:true};let result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise)result=await result;return result.issues.length?{success:false,error:new _Err(result.issues.map(iss=>finalizeIssue(iss,ctx,config())))}:{success:true,data:result.value}};var safeParseAsync=_safeParseAsync($ZodRealError);var _encode=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _parse(_Err)(schema,value,ctx)};var _decode=_Err=>(schema,value,_ctx)=>{return _parse(_Err)(schema,value,_ctx)};var _encodeAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _parseAsync(_Err)(schema,value,ctx)};var _decodeAsync=_Err=>async(schema,value,_ctx)=>{return _parseAsync(_Err)(schema,value,_ctx)};var _safeEncode=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _safeParse(_Err)(schema,value,ctx)};var _safeDecode=_Err=>(schema,value,_ctx)=>{return _safeParse(_Err)(schema,value,_ctx)};var _safeEncodeAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _safeParseAsync(_Err)(schema,value,ctx)};var _safeDecodeAsync=_Err=>async(schema,value,_ctx)=>{return _safeParseAsync(_Err)(schema,value,_ctx)};var cuid=/^[cC][0-9a-z]{6,}$/;var cuid2=/^[0-9a-z]+$/;var ulid=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;var xid=/^[0-9a-vA-V]{20}$/;var ksuid=/^[A-Za-z0-9]{27}$/;var nanoid=/^[a-zA-Z0-9_-]{21}$/;var duration=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;var guid=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;var uuid=version2=>{if(!version2)return/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`)};var email=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/;var _emoji=`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;function emoji(){return new RegExp(_emoji,"u")}var ipv4=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;var ipv6=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;var cidrv4=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;var cidrv6=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;var base64=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;var base64url=/^[A-Za-z0-9_-]*$/;var httpProtocol=/^https?$/;var e164=/^\+[1-9]\d{6,14}$/;var dateSource=`(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;var date=new RegExp(`^${dateSource}$`);function timeSource(args){const hhmm=`(?:[01]\\d|2[0-3]):[0-5]\\d`;const regex=typeof args.precision==="number"?args.precision===-1?`${hhmm}`:args.precision===0?`${hhmm}:[0-5]\\d`:`${hhmm}:[0-5]\\d\\.\\d{${args.precision}}`:`${hhmm}(?::[0-5]\\d(?:\\.\\d+)?)?`;return regex}function time(args){return new RegExp(`^${timeSource(args)}$`)}function datetime(args){const time3=timeSource({precision:args.precision});const opts=["Z"];if(args.local)opts.push("");if(args.offset)opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);const timeRegex=`${time3}(?:${opts.join("|")})`;return new RegExp(`^${dateSource}T(?:${timeRegex})$`)}var string=params=>{const regex=params?`[\\s\\S]{${params?.minimum??0},${params?.maximum??""}}`:`[\\s\\S]*`;return new RegExp(`^${regex}$`)};var bigint=/^-?\d+n?$/;var integer=/^-?\d+$/;var number=/^-?\d+(?:\.\d+)?$/;var boolean=/^(?:true|false)$/i;var _null=/^null$/i;var lowercase=/^[^A-Z]*$/;var uppercase=/^[^a-z]*$/;var $ZodCheck=$constructor("$ZodCheck",(inst,def)=>{var _a3;inst._zod??(inst._zod={});inst._zod.def=def;(_a3=inst._zod).onattach??(_a3.onattach=[])});var numericOriginMap={number:"number",bigint:"bigint",object:"date"};var $ZodCheckLessThan=$constructor("$ZodCheckLessThan",(inst,def)=>{$ZodCheck.init(inst,def);const origin=numericOriginMap[typeof def.value];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;const curr=(def.inclusive?bag.maximum:bag.exclusiveMaximum)??Number.POSITIVE_INFINITY;if(def.value{if(def.inclusive?payload.value<=def.value:payload.value{$ZodCheck.init(inst,def);const origin=numericOriginMap[typeof def.value];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;const curr=(def.inclusive?bag.minimum:bag.exclusiveMinimum)??Number.NEGATIVE_INFINITY;if(def.value>curr){if(def.inclusive)bag.minimum=def.value;else bag.exclusiveMinimum=def.value}});inst._zod.check=payload=>{if(def.inclusive?payload.value>=def.value:payload.value>def.value){return}payload.issues.push({origin,code:"too_small",minimum:typeof def.value==="object"?def.value.getTime():def.value,input:payload.value,inclusive:def.inclusive,inst,continue:!def.abort})}});var $ZodCheckMultipleOf=$constructor("$ZodCheckMultipleOf",(inst,def)=>{$ZodCheck.init(inst,def);inst._zod.onattach.push(inst2=>{var _a3;(_a3=inst2._zod.bag).multipleOf??(_a3.multipleOf=def.value)});inst._zod.check=payload=>{if(typeof payload.value!==typeof def.value)throw new Error("Cannot mix number and bigint in multiple_of check.");const isMultiple=typeof payload.value==="bigint"?payload.value%def.value===BigInt(0):floatSafeRemainder(payload.value,def.value)===0;if(isMultiple)return;payload.issues.push({origin:typeof payload.value,code:"not_multiple_of",divisor:def.value,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckNumberFormat=$constructor("$ZodCheckNumberFormat",(inst,def)=>{$ZodCheck.init(inst,def);def.format=def.format||"float64";const isInt=def.format?.includes("int");const origin=isInt?"int":"number";const[minimum,maximum]=NUMBER_FORMAT_RANGES[def.format];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.format=def.format;bag.minimum=minimum;bag.maximum=maximum;if(isInt)bag.pattern=integer});inst._zod.check=payload=>{const input=payload.value;if(isInt){if(!Number.isInteger(input)){payload.issues.push({expected:origin,format:def.format,code:"invalid_type",continue:false,input,inst});return}if(!Number.isSafeInteger(input)){if(input>0){payload.issues.push({input,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst,origin,inclusive:true,continue:!def.abort})}else{payload.issues.push({input,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst,origin,inclusive:true,continue:!def.abort})}return}}if(inputmaximum){payload.issues.push({origin:"number",input,code:"too_big",maximum,inclusive:true,inst,continue:!def.abort})}}});var $ZodCheckMaxLength=$constructor("$ZodCheckMaxLength",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const curr=inst2._zod.bag.maximum??Number.POSITIVE_INFINITY;if(def.maximum{const input=payload.value;const length=input.length;if(length<=def.maximum)return;const origin=getLengthableOrigin(input);payload.issues.push({origin,code:"too_big",maximum:def.maximum,inclusive:true,input,inst,continue:!def.abort})}});var $ZodCheckMinLength=$constructor("$ZodCheckMinLength",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const curr=inst2._zod.bag.minimum??Number.NEGATIVE_INFINITY;if(def.minimum>curr)inst2._zod.bag.minimum=def.minimum});inst._zod.check=payload=>{const input=payload.value;const length=input.length;if(length>=def.minimum)return;const origin=getLengthableOrigin(input);payload.issues.push({origin,code:"too_small",minimum:def.minimum,inclusive:true,input,inst,continue:!def.abort})}});var $ZodCheckLengthEquals=$constructor("$ZodCheckLengthEquals",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.minimum=def.length;bag.maximum=def.length;bag.length=def.length});inst._zod.check=payload=>{const input=payload.value;const length=input.length;if(length===def.length)return;const origin=getLengthableOrigin(input);const tooBig=length>def.length;payload.issues.push({origin,...tooBig?{code:"too_big",maximum:def.length}:{code:"too_small",minimum:def.length},inclusive:true,exact:true,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckStringFormat=$constructor("$ZodCheckStringFormat",(inst,def)=>{var _a3,_b;$ZodCheck.init(inst,def);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.format=def.format;if(def.pattern){bag.patterns??(bag.patterns=new Set);bag.patterns.add(def.pattern)}});if(def.pattern)(_a3=inst._zod).check??(_a3.check=payload=>{def.pattern.lastIndex=0;if(def.pattern.test(payload.value))return;payload.issues.push({origin:"string",code:"invalid_format",format:def.format,input:payload.value,...def.pattern?{pattern:def.pattern.toString()}:{},inst,continue:!def.abort})});else(_b=inst._zod).check??(_b.check=()=>{})});var $ZodCheckRegex=$constructor("$ZodCheckRegex",(inst,def)=>{$ZodCheckStringFormat.init(inst,def);inst._zod.check=payload=>{def.pattern.lastIndex=0;if(def.pattern.test(payload.value))return;payload.issues.push({origin:"string",code:"invalid_format",format:"regex",input:payload.value,pattern:def.pattern.toString(),inst,continue:!def.abort})}});var $ZodCheckLowerCase=$constructor("$ZodCheckLowerCase",(inst,def)=>{def.pattern??(def.pattern=lowercase);$ZodCheckStringFormat.init(inst,def)});var $ZodCheckUpperCase=$constructor("$ZodCheckUpperCase",(inst,def)=>{def.pattern??(def.pattern=uppercase);$ZodCheckStringFormat.init(inst,def)});var $ZodCheckIncludes=$constructor("$ZodCheckIncludes",(inst,def)=>{$ZodCheck.init(inst,def);const escapedRegex=escapeRegex(def.includes);const pattern=new RegExp(typeof def.position==="number"?`^.{${def.position}}${escapedRegex}`:escapedRegex);def.pattern=pattern;inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.includes(def.includes,def.position))return;payload.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:def.includes,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckStartsWith=$constructor("$ZodCheckStartsWith",(inst,def)=>{$ZodCheck.init(inst,def);const pattern=new RegExp(`^${escapeRegex(def.prefix)}.*`);def.pattern??(def.pattern=pattern);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.startsWith(def.prefix))return;payload.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:def.prefix,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckEndsWith=$constructor("$ZodCheckEndsWith",(inst,def)=>{$ZodCheck.init(inst,def);const pattern=new RegExp(`.*${escapeRegex(def.suffix)}$`);def.pattern??(def.pattern=pattern);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.endsWith(def.suffix))return;payload.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:def.suffix,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckOverwrite=$constructor("$ZodCheckOverwrite",(inst,def)=>{$ZodCheck.init(inst,def);inst._zod.check=payload=>{payload.value=def.tx(payload.value)}});var Doc=class{constructor(args=[]){this.content=[];this.indent=0;if(this)this.args=args}indented(fn){this.indent+=1;fn(this);this.indent-=1}write(arg){if(typeof arg==="function"){arg(this,{execution:"sync"});arg(this,{execution:"async"});return}const content=arg;const lines=content.split("\n").filter(x=>x);const minIndent=Math.min(...lines.map(x=>x.length-x.trimStart().length));const dedented=lines.map(x=>x.slice(minIndent)).map(x=>" ".repeat(this.indent*2)+x);for(const line of dedented){this.content.push(line)}}compile(){const F=Function;const args=this?.args;const content=this?.content??[``];const lines=[...content.map(x=>` ${x}`)];return new F(...args,lines.join("\n"))}};var version={major:4,minor:4,patch:3};var $ZodType=$constructor("$ZodType",(inst,def)=>{var _a3;inst??(inst={});inst._zod.def=def;inst._zod.bag=inst._zod.bag||{};inst._zod.version=version;const checks=[...inst._zod.def.checks??[]];if(inst._zod.traits.has("$ZodCheck")){checks.unshift(inst)}for(const ch of checks){for(const fn of ch._zod.onattach){fn(inst)}}if(checks.length===0){(_a3=inst._zod).deferred??(_a3.deferred=[]);inst._zod.deferred?.push(()=>{inst._zod.run=inst._zod.parse})}else{const runChecks=(payload,checks2,ctx)=>{let isAborted=aborted(payload);let asyncResult;for(const ch of checks2){if(ch._zod.def.when){if(explicitlyAborted(payload))continue;const shouldRun=ch._zod.def.when(payload);if(!shouldRun)continue}else if(isAborted){continue}const currLen=payload.issues.length;const _=ch._zod.check(payload);if(_ instanceof Promise&&ctx?.async===false){throw new $ZodAsyncError}if(asyncResult||_ instanceof Promise){asyncResult=(asyncResult??Promise.resolve()).then(async()=>{await _;const nextLen=payload.issues.length;if(nextLen===currLen)return;if(!isAborted)isAborted=aborted(payload,currLen)})}else{const nextLen=payload.issues.length;if(nextLen===currLen)continue;if(!isAborted)isAborted=aborted(payload,currLen)}}if(asyncResult){return asyncResult.then(()=>{return payload})}return payload};const handleCanaryResult=(canary,payload,ctx)=>{if(aborted(canary)){canary.aborted=true;return canary}const checkResult=runChecks(payload,checks,ctx);if(checkResult instanceof Promise){if(ctx.async===false)throw new $ZodAsyncError;return checkResult.then(checkResult2=>inst._zod.parse(checkResult2,ctx))}return inst._zod.parse(checkResult,ctx)};inst._zod.run=(payload,ctx)=>{if(ctx.skipChecks){return inst._zod.parse(payload,ctx)}if(ctx.direction==="backward"){const canary=inst._zod.parse({value:payload.value,issues:[]},{...ctx,skipChecks:true});if(canary instanceof Promise){return canary.then(canary2=>{return handleCanaryResult(canary2,payload,ctx)})}return handleCanaryResult(canary,payload,ctx)}const result=inst._zod.parse(payload,ctx);if(result instanceof Promise){if(ctx.async===false)throw new $ZodAsyncError;return result.then(result2=>runChecks(result2,checks,ctx))}return runChecks(result,checks,ctx)}}defineLazy(inst,"~standard",()=>({validate:value=>{try{const r=safeParse(inst,value);return r.success?{value:r.data}:{issues:r.error?.issues}}catch(_){return safeParseAsync(inst,value).then(r=>r.success?{value:r.data}:{issues:r.error?.issues})}},vendor:"zod",version:1}))});var $ZodString=$constructor("$ZodString",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=[...inst?._zod.bag?.patterns??[]].pop()??string(inst._zod.bag);inst._zod.parse=(payload,_)=>{if(def.coerce)try{payload.value=String(payload.value)}catch(_2){}if(typeof payload.value==="string")return payload;payload.issues.push({expected:"string",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodStringFormat=$constructor("$ZodStringFormat",(inst,def)=>{$ZodCheckStringFormat.init(inst,def);$ZodString.init(inst,def)});var $ZodGUID=$constructor("$ZodGUID",(inst,def)=>{def.pattern??(def.pattern=guid);$ZodStringFormat.init(inst,def)});var $ZodUUID=$constructor("$ZodUUID",(inst,def)=>{if(def.version){const versionMap={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8};const v=versionMap[def.version];if(v===void 0)throw new Error(`Invalid UUID version: "${def.version}"`);def.pattern??(def.pattern=uuid(v))}else def.pattern??(def.pattern=uuid());$ZodStringFormat.init(inst,def)});var $ZodEmail=$constructor("$ZodEmail",(inst,def)=>{def.pattern??(def.pattern=email);$ZodStringFormat.init(inst,def)});var $ZodURL=$constructor("$ZodURL",(inst,def)=>{$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{try{const trimmed=payload.value.trim();if(!def.normalize&&def.protocol?.source===httpProtocol.source){if(!/^https?:\/\//i.test(trimmed)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid URL format",input:payload.value,inst,continue:!def.abort});return}}const url2=new URL(trimmed);if(def.hostname){def.hostname.lastIndex=0;if(!def.hostname.test(url2.hostname)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:def.hostname.source,input:payload.value,inst,continue:!def.abort})}}if(def.protocol){def.protocol.lastIndex=0;if(!def.protocol.test(url2.protocol.endsWith(":")?url2.protocol.slice(0,-1):url2.protocol)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:def.protocol.source,input:payload.value,inst,continue:!def.abort})}}if(def.normalize){payload.value=url2.href}else{payload.value=trimmed}return}catch(_){payload.issues.push({code:"invalid_format",format:"url",input:payload.value,inst,continue:!def.abort})}}});var $ZodEmoji=$constructor("$ZodEmoji",(inst,def)=>{def.pattern??(def.pattern=emoji());$ZodStringFormat.init(inst,def)});var $ZodNanoID=$constructor("$ZodNanoID",(inst,def)=>{def.pattern??(def.pattern=nanoid);$ZodStringFormat.init(inst,def)});var $ZodCUID=$constructor("$ZodCUID",(inst,def)=>{def.pattern??(def.pattern=cuid);$ZodStringFormat.init(inst,def)});var $ZodCUID2=$constructor("$ZodCUID2",(inst,def)=>{def.pattern??(def.pattern=cuid2);$ZodStringFormat.init(inst,def)});var $ZodULID=$constructor("$ZodULID",(inst,def)=>{def.pattern??(def.pattern=ulid);$ZodStringFormat.init(inst,def)});var $ZodXID=$constructor("$ZodXID",(inst,def)=>{def.pattern??(def.pattern=xid);$ZodStringFormat.init(inst,def)});var $ZodKSUID=$constructor("$ZodKSUID",(inst,def)=>{def.pattern??(def.pattern=ksuid);$ZodStringFormat.init(inst,def)});var $ZodISODateTime=$constructor("$ZodISODateTime",(inst,def)=>{def.pattern??(def.pattern=datetime(def));$ZodStringFormat.init(inst,def)});var $ZodISODate=$constructor("$ZodISODate",(inst,def)=>{def.pattern??(def.pattern=date);$ZodStringFormat.init(inst,def)});var $ZodISOTime=$constructor("$ZodISOTime",(inst,def)=>{def.pattern??(def.pattern=time(def));$ZodStringFormat.init(inst,def)});var $ZodISODuration=$constructor("$ZodISODuration",(inst,def)=>{def.pattern??(def.pattern=duration);$ZodStringFormat.init(inst,def)});var $ZodIPv4=$constructor("$ZodIPv4",(inst,def)=>{def.pattern??(def.pattern=ipv4);$ZodStringFormat.init(inst,def);inst._zod.bag.format=`ipv4`});var $ZodIPv6=$constructor("$ZodIPv6",(inst,def)=>{def.pattern??(def.pattern=ipv6);$ZodStringFormat.init(inst,def);inst._zod.bag.format=`ipv6`;inst._zod.check=payload=>{try{new URL(`http://[${payload.value}]`)}catch{payload.issues.push({code:"invalid_format",format:"ipv6",input:payload.value,inst,continue:!def.abort})}}});var $ZodCIDRv4=$constructor("$ZodCIDRv4",(inst,def)=>{def.pattern??(def.pattern=cidrv4);$ZodStringFormat.init(inst,def)});var $ZodCIDRv6=$constructor("$ZodCIDRv6",(inst,def)=>{def.pattern??(def.pattern=cidrv6);$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{const parts=payload.value.split("/");try{if(parts.length!==2)throw new Error;const[address,prefix]=parts;if(!prefix)throw new Error;const prefixNum=Number(prefix);if(`${prefixNum}`!==prefix)throw new Error;if(prefixNum<0||prefixNum>128)throw new Error;new URL(`http://[${address}]`)}catch{payload.issues.push({code:"invalid_format",format:"cidrv6",input:payload.value,inst,continue:!def.abort})}}});function isValidBase64(data){if(data==="")return true;if(/\s/.test(data))return false;if(data.length%4!==0)return false;try{atob(data);return true}catch{return false}}var $ZodBase64=$constructor("$ZodBase64",(inst,def)=>{def.pattern??(def.pattern=base64);$ZodStringFormat.init(inst,def);inst._zod.bag.contentEncoding="base64";inst._zod.check=payload=>{if(isValidBase64(payload.value))return;payload.issues.push({code:"invalid_format",format:"base64",input:payload.value,inst,continue:!def.abort})}});function isValidBase64URL(data){if(!base64url.test(data))return false;const base642=data.replace(/[-_]/g,c=>c==="-"?"+":"/");const padded=base642.padEnd(Math.ceil(base642.length/4)*4,"=");return isValidBase64(padded)}var $ZodBase64URL=$constructor("$ZodBase64URL",(inst,def)=>{def.pattern??(def.pattern=base64url);$ZodStringFormat.init(inst,def);inst._zod.bag.contentEncoding="base64url";inst._zod.check=payload=>{if(isValidBase64URL(payload.value))return;payload.issues.push({code:"invalid_format",format:"base64url",input:payload.value,inst,continue:!def.abort})}});var $ZodE164=$constructor("$ZodE164",(inst,def)=>{def.pattern??(def.pattern=e164);$ZodStringFormat.init(inst,def)});function isValidJWT(token,algorithm=null){try{const tokensParts=token.split(".");if(tokensParts.length!==3)return false;const[header]=tokensParts;if(!header)return false;const parsedHeader=JSON.parse(atob(header));if("typ"in parsedHeader&&parsedHeader?.typ!=="JWT")return false;if(!parsedHeader.alg)return false;if(algorithm&&(!("alg"in parsedHeader)||parsedHeader.alg!==algorithm))return false;return true}catch{return false}}var $ZodJWT=$constructor("$ZodJWT",(inst,def)=>{$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{if(isValidJWT(payload.value,def.alg))return;payload.issues.push({code:"invalid_format",format:"jwt",input:payload.value,inst,continue:!def.abort})}});var $ZodNumber=$constructor("$ZodNumber",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=inst._zod.bag.pattern??number;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=Number(payload.value)}catch(_){}const input=payload.value;if(typeof input==="number"&&!Number.isNaN(input)&&Number.isFinite(input)){return payload}const received=typeof input==="number"?Number.isNaN(input)?"NaN":!Number.isFinite(input)?"Infinity":void 0:void 0;payload.issues.push({expected:"number",code:"invalid_type",input,inst,...received?{received}:{}});return payload}});var $ZodNumberFormat=$constructor("$ZodNumberFormat",(inst,def)=>{$ZodCheckNumberFormat.init(inst,def);$ZodNumber.init(inst,def)});var $ZodBoolean=$constructor("$ZodBoolean",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=boolean;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=Boolean(payload.value)}catch(_){}const input=payload.value;if(typeof input==="boolean")return payload;payload.issues.push({expected:"boolean",code:"invalid_type",input,inst});return payload}});var $ZodBigInt=$constructor("$ZodBigInt",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=bigint;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=BigInt(payload.value)}catch(_){}if(typeof payload.value==="bigint")return payload;payload.issues.push({expected:"bigint",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodNull=$constructor("$ZodNull",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=_null;inst._zod.values=new Set([null]);inst._zod.parse=(payload,_ctx)=>{const input=payload.value;if(input===null)return payload;payload.issues.push({expected:"null",code:"invalid_type",input,inst});return payload}});var $ZodAny=$constructor("$ZodAny",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=payload=>payload});var $ZodUnknown=$constructor("$ZodUnknown",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=payload=>payload});var $ZodNever=$constructor("$ZodNever",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,_ctx)=>{payload.issues.push({expected:"never",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodDate=$constructor("$ZodDate",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,_ctx)=>{if(def.coerce){try{payload.value=new Date(payload.value)}catch(_err){}}const input=payload.value;const isDate=input instanceof Date;const isValidDate=isDate&&!Number.isNaN(input.getTime());if(isValidDate)return payload;payload.issues.push({expected:"date",code:"invalid_type",input,...isDate?{received:"Invalid Date"}:{},inst});return payload}});function handleArrayResult(result,final,index){if(result.issues.length){final.issues.push(...prefixIssues(index,result.issues))}final.value[index]=result.value}var $ZodArray=$constructor("$ZodArray",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,ctx)=>{const input=payload.value;if(!Array.isArray(input)){payload.issues.push({expected:"array",code:"invalid_type",input,inst});return payload}payload.value=Array(input.length);const proms=[];for(let i=0;ihandleArrayResult(result2,payload,i)))}else{handleArrayResult(result,payload,i)}}if(proms.length){return Promise.all(proms).then(()=>payload)}return payload}});function handlePropertyResult(result,final,key,input,isOptionalIn,isOptionalOut){const isPresent=key in input;if(result.issues.length){if(isOptionalIn&&isOptionalOut&&!isPresent){return}final.issues.push(...prefixIssues(key,result.issues))}if(!isPresent&&!isOptionalIn){if(!result.issues.length){final.issues.push({code:"invalid_type",expected:"nonoptional",input:void 0,path:[key]})}return}if(result.value===void 0){if(isPresent){final.value[key]=void 0}}else{final.value[key]=result.value}}function normalizeDef(def){const keys=Object.keys(def.shape);for(const k of keys){if(!def.shape?.[k]?._zod?.traits?.has("$ZodType")){throw new Error(`Invalid element at key "${k}": expected a Zod schema`)}}const okeys=optionalKeys(def.shape);return{...def,keys,keySet:new Set(keys),numKeys:keys.length,optionalKeys:new Set(okeys)}}function handleCatchall(proms,input,payload,ctx,def,inst){const unrecognized=[];const keySet=def.keySet;const _catchall=def.catchall._zod;const t=_catchall.def.type;const isOptionalIn=_catchall.optin==="optional";const isOptionalOut=_catchall.optout==="optional";for(const key in input){if(key==="__proto__")continue;if(keySet.has(key))continue;if(t==="never"){unrecognized.push(key);continue}const r=_catchall.run({value:input[key],issues:[]},ctx);if(r instanceof Promise){proms.push(r.then(r2=>handlePropertyResult(r2,payload,key,input,isOptionalIn,isOptionalOut)))}else{handlePropertyResult(r,payload,key,input,isOptionalIn,isOptionalOut)}}if(unrecognized.length){payload.issues.push({code:"unrecognized_keys",keys:unrecognized,input,inst})}if(!proms.length)return payload;return Promise.all(proms).then(()=>{return payload})}var $ZodObject=$constructor("$ZodObject",(inst,def)=>{$ZodType.init(inst,def);const desc=Object.getOwnPropertyDescriptor(def,"shape");if(!desc?.get){const sh=def.shape;Object.defineProperty(def,"shape",{get:()=>{const newSh={...sh};Object.defineProperty(def,"shape",{value:newSh});return newSh}})}const _normalized=cached(()=>normalizeDef(def));defineLazy(inst._zod,"propValues",()=>{const shape=def.shape;const propValues={};for(const key in shape){const field=shape[key]._zod;if(field.values){propValues[key]??(propValues[key]=new Set);for(const v of field.values)propValues[key].add(v)}}return propValues});const isObject2=isObject;const catchall=def.catchall;let value;inst._zod.parse=(payload,ctx)=>{value??(value=_normalized.value);const input=payload.value;if(!isObject2(input)){payload.issues.push({expected:"object",code:"invalid_type",input,inst});return payload}payload.value={};const proms=[];const shape=value.shape;for(const key of value.keys){const el=shape[key];const isOptionalIn=el._zod.optin==="optional";const isOptionalOut=el._zod.optout==="optional";const r=el._zod.run({value:input[key],issues:[]},ctx);if(r instanceof Promise){proms.push(r.then(r2=>handlePropertyResult(r2,payload,key,input,isOptionalIn,isOptionalOut)))}else{handlePropertyResult(r,payload,key,input,isOptionalIn,isOptionalOut)}}if(!catchall){return proms.length?Promise.all(proms).then(()=>payload):payload}return handleCatchall(proms,input,payload,ctx,_normalized.value,inst)}});var $ZodObjectJIT=$constructor("$ZodObjectJIT",(inst,def)=>{$ZodObject.init(inst,def);const superParse=inst._zod.parse;const _normalized=cached(()=>normalizeDef(def));const generateFastpass=shape=>{const doc=new Doc(["shape","payload","ctx"]);const normalized=_normalized.value;const parseStr=key=>{const k=esc(key);return`shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`};doc.write(`const input = payload.value;`);const ids=Object.create(null);let counter=0;for(const key of normalized.keys){ids[key]=`key_${counter++}`}doc.write(`const newResult = {};`);for(const key of normalized.keys){const id=ids[key];const k=esc(key);const schema=shape[key];const isOptionalIn=schema?._zod?.optin==="optional";const isOptionalOut=schema?._zod?.optout==="optional";doc.write(`const ${id} = ${parseStr(key)};`);if(isOptionalIn&&isOptionalOut){doc.write(` + deps: ${deps}}`};var def={keyword:"dependencies",type:"object",schemaType:"object",error:exports.error,code(cxt){const[propDeps,schDeps]=splitDependencies(cxt);validatePropertyDeps(cxt,propDeps);validateSchemaDeps(cxt,schDeps)}};function splitDependencies({schema}){const propertyDeps={};const schemaDeps={};for(const key in schema){if(key==="__proto__")continue;const deps=Array.isArray(schema[key])?propertyDeps:schemaDeps;deps[key]=schema[key]}return[propertyDeps,schemaDeps]}function validatePropertyDeps(cxt,propertyDeps=cxt.schema){const{gen,data,it}=cxt;if(Object.keys(propertyDeps).length===0)return;const missing=gen.let("missing");for(const prop in propertyDeps){const deps=propertyDeps[prop];if(deps.length===0)continue;const hasProperty=(0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties);cxt.setParams({property:prop,depsCount:deps.length,deps:deps.join(", ")});if(it.allErrors){gen.if(hasProperty,()=>{for(const depProp of deps){(0,code_1.checkReportMissingProp)(cxt,depProp)}})}else{gen.if((0,codegen_1._)`${hasProperty} && (${(0,code_1.checkMissingProp)(cxt,deps,missing)})`);(0,code_1.reportMissingProp)(cxt,missing);gen.else()}}}exports.validatePropertyDeps=validatePropertyDeps;function validateSchemaDeps(cxt,schemaDeps=cxt.schema){const{gen,data,keyword,it}=cxt;const valid=gen.name("valid");for(const prop in schemaDeps){if((0,util_1.alwaysValidSchema)(it,schemaDeps[prop]))continue;gen.if((0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties),()=>{const schCxt=cxt.subschema({keyword,schemaProp:prop},valid);cxt.mergeValidEvaluated(schCxt,valid)},()=>gen.var(valid,true));cxt.ok(valid)}}exports.validateSchemaDeps=validateSchemaDeps;exports.default=def}});var require_propertyNames=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/propertyNames.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:"property name must be valid",params:({params})=>(0,codegen_1._)`{propertyName: ${params.propertyName}}`};var def={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:error2,code(cxt){const{gen,schema,data,it}=cxt;if((0,util_1.alwaysValidSchema)(it,schema))return;const valid=gen.name("valid");gen.forIn("key",data,key=>{cxt.setParams({propertyName:key});cxt.subschema({keyword:"propertyNames",data:key,dataTypes:["string"],propertyName:key,compositeRule:true},valid);gen.if((0,codegen_1.not)(valid),()=>{cxt.error(true);if(!it.allErrors)gen.break()})});cxt.ok(valid)}};exports.default=def}});var require_additionalProperties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var codegen_1=require_codegen();var names_1=require_names();var util_1=require_util();var error2={message:"must NOT have additional properties",params:({params})=>(0,codegen_1._)`{additionalProperty: ${params.additionalProperty}}`};var def={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:true,trackErrors:true,error:error2,code(cxt){const{gen,schema,parentSchema,data,errsCount,it}=cxt;if(!errsCount)throw new Error("ajv implementation error");const{allErrors,opts}=it;it.props=true;if(opts.removeAdditional!=="all"&&(0,util_1.alwaysValidSchema)(it,schema))return;const props=(0,code_1.allSchemaProperties)(parentSchema.properties);const patProps=(0,code_1.allSchemaProperties)(parentSchema.patternProperties);checkAdditionalProperties();cxt.ok((0,codegen_1._)`${errsCount} === ${names_1.default.errors}`);function checkAdditionalProperties(){gen.forIn("key",data,key=>{if(!props.length&&!patProps.length)additionalPropertyCode(key);else gen.if(isAdditional(key),()=>additionalPropertyCode(key))})}function isAdditional(key){let definedProp;if(props.length>8){const propsSchema=(0,util_1.schemaRefOrVal)(it,parentSchema.properties,"properties");definedProp=(0,code_1.isOwnProperty)(gen,propsSchema,key)}else if(props.length){definedProp=(0,codegen_1.or)(...props.map(p=>(0,codegen_1._)`${key} === ${p}`))}else{definedProp=codegen_1.nil}if(patProps.length){definedProp=(0,codegen_1.or)(definedProp,...patProps.map(p=>(0,codegen_1._)`${(0,code_1.usePattern)(cxt,p)}.test(${key})`))}return(0,codegen_1.not)(definedProp)}function deleteAdditional(key){gen.code((0,codegen_1._)`delete ${data}[${key}]`)}function additionalPropertyCode(key){if(opts.removeAdditional==="all"||opts.removeAdditional&&schema===false){deleteAdditional(key);return}if(schema===false){cxt.setParams({additionalProperty:key});cxt.error();if(!allErrors)gen.break();return}if(typeof schema=="object"&&!(0,util_1.alwaysValidSchema)(it,schema)){const valid=gen.name("valid");if(opts.removeAdditional==="failing"){applyAdditionalSchema(key,valid,false);gen.if((0,codegen_1.not)(valid),()=>{cxt.reset();deleteAdditional(key)})}else{applyAdditionalSchema(key,valid);if(!allErrors)gen.if((0,codegen_1.not)(valid),()=>gen.break())}}}function applyAdditionalSchema(key,valid,errors){const subschema={keyword:"additionalProperties",dataProp:key,dataPropType:util_1.Type.Str};if(errors===false){Object.assign(subschema,{compositeRule:true,createErrors:false,allErrors:false})}cxt.subschema(subschema,valid)}}};exports.default=def}});var require_properties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/properties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var validate_1=require_validate();var code_1=require_code2();var util_1=require_util();var additionalProperties_1=require_additionalProperties();var def={keyword:"properties",type:"object",schemaType:"object",code(cxt){const{gen,schema,parentSchema,data,it}=cxt;if(it.opts.removeAdditional==="all"&&parentSchema.additionalProperties===void 0){additionalProperties_1.default.code(new validate_1.KeywordCxt(it,additionalProperties_1.default,"additionalProperties"))}const allProps=(0,code_1.allSchemaProperties)(schema);for(const prop of allProps){it.definedProperties.add(prop)}if(it.opts.unevaluated&&allProps.length&&it.props!==true){it.props=util_1.mergeEvaluated.props(gen,(0,util_1.toHash)(allProps),it.props)}const properties=allProps.filter(p=>!(0,util_1.alwaysValidSchema)(it,schema[p]));if(properties.length===0)return;const valid=gen.name("valid");for(const prop of properties){if(hasDefault(prop)){applyPropertySchema(prop)}else{gen.if((0,code_1.propertyInData)(gen,data,prop,it.opts.ownProperties));applyPropertySchema(prop);if(!it.allErrors)gen.else().var(valid,true);gen.endIf()}cxt.it.definedProperties.add(prop);cxt.ok(valid)}function hasDefault(prop){return it.opts.useDefaults&&!it.compositeRule&&schema[prop].default!==void 0}function applyPropertySchema(prop){cxt.subschema({keyword:"properties",schemaProp:prop,dataProp:prop},valid)}}};exports.default=def}});var require_patternProperties=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/patternProperties.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var codegen_1=require_codegen();var util_1=require_util();var util_2=require_util();var def={keyword:"patternProperties",type:"object",schemaType:"object",code(cxt){const{gen,schema,data,parentSchema,it}=cxt;const{opts}=it;const patterns=(0,code_1.allSchemaProperties)(schema);const alwaysValidPatterns=patterns.filter(p=>(0,util_1.alwaysValidSchema)(it,schema[p]));if(patterns.length===0||alwaysValidPatterns.length===patterns.length&&(!it.opts.unevaluated||it.props===true)){return}const checkProperties=opts.strictSchema&&!opts.allowMatchingProperties&&parentSchema.properties;const valid=gen.name("valid");if(it.props!==true&&!(it.props instanceof codegen_1.Name)){it.props=(0,util_2.evaluatedPropsToName)(gen,it.props)}const{props}=it;validatePatternProperties();function validatePatternProperties(){for(const pat of patterns){if(checkProperties)checkMatchingProperties(pat);if(it.allErrors){validateProperties(pat)}else{gen.var(valid,true);validateProperties(pat);gen.if(valid)}}}function checkMatchingProperties(pat){for(const prop in checkProperties){if(new RegExp(pat).test(prop)){(0,util_1.checkStrictMode)(it,`property ${prop} matches pattern ${pat} (use allowMatchingProperties)`)}}}function validateProperties(pat){gen.forIn("key",data,key=>{gen.if((0,codegen_1._)`${(0,code_1.usePattern)(cxt,pat)}.test(${key})`,()=>{const alwaysValid=alwaysValidPatterns.includes(pat);if(!alwaysValid){cxt.subschema({keyword:"patternProperties",schemaProp:pat,dataProp:key,dataPropType:util_2.Type.Str},valid)}if(it.opts.unevaluated&&props!==true){gen.assign((0,codegen_1._)`${props}[${key}]`,true)}else if(!alwaysValid&&!it.allErrors){gen.if((0,codegen_1.not)(valid),()=>gen.break())}})})}}};exports.default=def}});var require_not=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/not.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:"not",schemaType:["object","boolean"],trackErrors:true,code(cxt){const{gen,schema,it}=cxt;if((0,util_1.alwaysValidSchema)(it,schema)){cxt.fail();return}const valid=gen.name("valid");cxt.subschema({keyword:"not",compositeRule:true,createErrors:false,allErrors:false},valid);cxt.failResult(valid,()=>cxt.reset(),()=>cxt.error())},error:{message:"must NOT be valid"}};exports.default=def}});var require_anyOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/anyOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var code_1=require_code2();var def={keyword:"anyOf",schemaType:"array",trackErrors:true,code:code_1.validateUnion,error:{message:"must match a schema in anyOf"}};exports.default=def}});var require_oneOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/oneOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:"must match exactly one schema in oneOf",params:({params})=>(0,codegen_1._)`{passingSchemas: ${params.passing}}`};var def={keyword:"oneOf",schemaType:"array",trackErrors:true,error:error2,code(cxt){const{gen,schema,parentSchema,it}=cxt;if(!Array.isArray(schema))throw new Error("ajv implementation error");if(it.opts.discriminator&&parentSchema.discriminator)return;const schArr=schema;const valid=gen.let("valid",false);const passing=gen.let("passing",null);const schValid=gen.name("_valid");cxt.setParams({passing});gen.block(validateOneOf);cxt.result(valid,()=>cxt.reset(),()=>cxt.error(true));function validateOneOf(){schArr.forEach((sch,i)=>{let schCxt;if((0,util_1.alwaysValidSchema)(it,sch)){gen.var(schValid,true)}else{schCxt=cxt.subschema({keyword:"oneOf",schemaProp:i,compositeRule:true},schValid)}if(i>0){gen.if((0,codegen_1._)`${schValid} && ${valid}`).assign(valid,false).assign(passing,(0,codegen_1._)`[${passing}, ${i}]`).else()}gen.if(schValid,()=>{gen.assign(valid,true);gen.assign(passing,i);if(schCxt)cxt.mergeEvaluated(schCxt,codegen_1.Name)})})}}};exports.default=def}});var require_allOf=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/allOf.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:"allOf",schemaType:"array",code(cxt){const{gen,schema,it}=cxt;if(!Array.isArray(schema))throw new Error("ajv implementation error");const valid=gen.name("valid");schema.forEach((sch,i)=>{if((0,util_1.alwaysValidSchema)(it,sch))return;const schCxt=cxt.subschema({keyword:"allOf",schemaProp:i},valid);cxt.ok(valid);cxt.mergeEvaluated(schCxt)})}};exports.default=def}});var require_if=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/if.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var util_1=require_util();var error2={message:({params})=>(0,codegen_1.str)`must match "${params.ifClause}" schema`,params:({params})=>(0,codegen_1._)`{failingKeyword: ${params.ifClause}}`};var def={keyword:"if",schemaType:["object","boolean"],trackErrors:true,error:error2,code(cxt){const{gen,parentSchema,it}=cxt;if(parentSchema.then===void 0&&parentSchema.else===void 0){(0,util_1.checkStrictMode)(it,'"if" without "then" and "else" is ignored')}const hasThen=hasSchema(it,"then");const hasElse=hasSchema(it,"else");if(!hasThen&&!hasElse)return;const valid=gen.let("valid",true);const schValid=gen.name("_valid");validateIf();cxt.reset();if(hasThen&&hasElse){const ifClause=gen.let("ifClause");cxt.setParams({ifClause});gen.if(schValid,validateClause("then",ifClause),validateClause("else",ifClause))}else if(hasThen){gen.if(schValid,validateClause("then"))}else{gen.if((0,codegen_1.not)(schValid),validateClause("else"))}cxt.pass(valid,()=>cxt.error(true));function validateIf(){const schCxt=cxt.subschema({keyword:"if",compositeRule:true,createErrors:false,allErrors:false},schValid);cxt.mergeEvaluated(schCxt)}function validateClause(keyword,ifClause){return()=>{const schCxt=cxt.subschema({keyword},schValid);gen.assign(valid,schValid);cxt.mergeValidEvaluated(schCxt,valid);if(ifClause)gen.assign(ifClause,(0,codegen_1._)`${keyword}`);else cxt.setParams({ifClause:keyword})}}}};function hasSchema(it,keyword){const schema=it.schema[keyword];return schema!==void 0&&!(0,util_1.alwaysValidSchema)(it,schema)}exports.default=def}});var require_thenElse=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/thenElse.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var util_1=require_util();var def={keyword:["then","else"],schemaType:["object","boolean"],code({keyword,parentSchema,it}){if(parentSchema.if===void 0)(0,util_1.checkStrictMode)(it,`"${keyword}" without "if" is ignored`)}};exports.default=def}});var require_applicator=__commonJS({"node_modules/ajv/dist/vocabularies/applicator/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var additionalItems_1=require_additionalItems();var prefixItems_1=require_prefixItems();var items_1=require_items();var items2020_1=require_items2020();var contains_1=require_contains();var dependencies_1=require_dependencies();var propertyNames_1=require_propertyNames();var additionalProperties_1=require_additionalProperties();var properties_1=require_properties();var patternProperties_1=require_patternProperties();var not_1=require_not();var anyOf_1=require_anyOf();var oneOf_1=require_oneOf();var allOf_1=require_allOf();var if_1=require_if();var thenElse_1=require_thenElse();function getApplicator(draft2020=false){const applicator=[not_1.default,anyOf_1.default,oneOf_1.default,allOf_1.default,if_1.default,thenElse_1.default,propertyNames_1.default,additionalProperties_1.default,dependencies_1.default,properties_1.default,patternProperties_1.default];if(draft2020)applicator.push(prefixItems_1.default,items2020_1.default);else applicator.push(additionalItems_1.default,items_1.default);applicator.push(contains_1.default);return applicator}exports.default=getApplicator}});var require_format=__commonJS({"node_modules/ajv/dist/vocabularies/format/format.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var error2={message:({schemaCode})=>(0,codegen_1.str)`must match format "${schemaCode}"`,params:({schemaCode})=>(0,codegen_1._)`{format: ${schemaCode}}`};var def={keyword:"format",type:["number","string"],schemaType:"string",$data:true,error:error2,code(cxt,ruleType){const{gen,data,$data,schema,schemaCode,it}=cxt;const{opts,errSchemaPath,schemaEnv,self}=it;if(!opts.validateFormats)return;if($data)validate$DataFormat();else validateFormat();function validate$DataFormat(){const fmts=gen.scopeValue("formats",{ref:self.formats,code:opts.code.formats});const fDef=gen.const("fDef",(0,codegen_1._)`${fmts}[${schemaCode}]`);const fType=gen.let("fType");const format=gen.let("format");gen.if((0,codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`,()=>gen.assign(fType,(0,codegen_1._)`${fDef}.type || "string"`).assign(format,(0,codegen_1._)`${fDef}.validate`),()=>gen.assign(fType,(0,codegen_1._)`"string"`).assign(format,fDef));cxt.fail$data((0,codegen_1.or)(unknownFmt(),invalidFmt()));function unknownFmt(){if(opts.strictSchema===false)return codegen_1.nil;return(0,codegen_1._)`${schemaCode} && !${format}`}function invalidFmt(){const callFormat=schemaEnv.$async?(0,codegen_1._)`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))`:(0,codegen_1._)`${format}(${data})`;const validData=(0,codegen_1._)`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`;return(0,codegen_1._)`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`}}function validateFormat(){const formatDef=self.formats[schema];if(!formatDef){unknownFormat();return}if(formatDef===true)return;const[fmtType,format,fmtRef]=getFormat(formatDef);if(fmtType===ruleType)cxt.pass(validCondition());function unknownFormat(){if(opts.strictSchema===false){self.logger.warn(unknownMsg());return}throw new Error(unknownMsg());function unknownMsg(){return`unknown format "${schema}" ignored in schema at path "${errSchemaPath}"`}}function getFormat(fmtDef){const code=fmtDef instanceof RegExp?(0,codegen_1.regexpCode)(fmtDef):opts.code.formats?(0,codegen_1._)`${opts.code.formats}${(0,codegen_1.getProperty)(schema)}`:void 0;const fmt=gen.scopeValue("formats",{key:schema,ref:fmtDef,code});if(typeof fmtDef=="object"&&!(fmtDef instanceof RegExp)){return[fmtDef.type||"string",fmtDef.validate,(0,codegen_1._)`${fmt}.validate`]}return["string",fmtDef,fmt]}function validCondition(){if(typeof formatDef=="object"&&!(formatDef instanceof RegExp)&&formatDef.async){if(!schemaEnv.$async)throw new Error("async format in sync schema");return(0,codegen_1._)`await ${fmtRef}(${data})`}return typeof format=="function"?(0,codegen_1._)`${fmtRef}(${data})`:(0,codegen_1._)`${fmtRef}.test(${data})`}}}};exports.default=def}});var require_format2=__commonJS({"node_modules/ajv/dist/vocabularies/format/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var format_1=require_format();var format=[format_1.default];exports.default=format}});var require_metadata=__commonJS({"node_modules/ajv/dist/vocabularies/metadata.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.contentVocabulary=exports.metadataVocabulary=void 0;exports.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"];exports.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]}});var require_draft7=__commonJS({"node_modules/ajv/dist/vocabularies/draft7.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var core_1=require_core2();var validation_1=require_validation();var applicator_1=require_applicator();var format_1=require_format2();var metadata_1=require_metadata();var draft7Vocabularies=[core_1.default,validation_1.default,(0,applicator_1.default)(),format_1.default,metadata_1.metadataVocabulary,metadata_1.contentVocabulary];exports.default=draft7Vocabularies}});var require_types=__commonJS({"node_modules/ajv/dist/vocabularies/discriminator/types.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DiscrError=void 0;var DiscrError;(function(DiscrError2){DiscrError2["Tag"]="tag";DiscrError2["Mapping"]="mapping"})(DiscrError||(exports.DiscrError=DiscrError={}))}});var require_discriminator=__commonJS({"node_modules/ajv/dist/vocabularies/discriminator/index.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var codegen_1=require_codegen();var types_1=require_types();var compile_1=require_compile();var ref_error_1=require_ref_error();var util_1=require_util();var error2={message:({params:{discrError,tagName}})=>discrError===types_1.DiscrError.Tag?`tag "${tagName}" must be string`:`value of tag "${tagName}" must be in oneOf`,params:({params:{discrError,tag,tagName}})=>(0,codegen_1._)`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`};var def={keyword:"discriminator",type:"object",schemaType:"object",error:error2,code(cxt){const{gen,data,schema,parentSchema,it}=cxt;const{oneOf}=parentSchema;if(!it.opts.discriminator){throw new Error("discriminator: requires discriminator option")}const tagName=schema.propertyName;if(typeof tagName!="string")throw new Error("discriminator: requires propertyName");if(schema.mapping)throw new Error("discriminator: mapping is not supported");if(!oneOf)throw new Error("discriminator: requires oneOf keyword");const valid=gen.let("valid",false);const tag=gen.const("tag",(0,codegen_1._)`${data}${(0,codegen_1.getProperty)(tagName)}`);gen.if((0,codegen_1._)`typeof ${tag} == "string"`,()=>validateMapping(),()=>cxt.error(false,{discrError:types_1.DiscrError.Tag,tag,tagName}));cxt.ok(valid);function validateMapping(){const mapping=getMapping();gen.if(false);for(const tagValue in mapping){gen.elseIf((0,codegen_1._)`${tag} === ${tagValue}`);gen.assign(valid,applyTagSchema(mapping[tagValue]))}gen.else();cxt.error(false,{discrError:types_1.DiscrError.Mapping,tag,tagName});gen.endIf()}function applyTagSchema(schemaProp){const _valid=gen.name("valid");const schCxt=cxt.subschema({keyword:"oneOf",schemaProp},_valid);cxt.mergeEvaluated(schCxt,codegen_1.Name);return _valid}function getMapping(){var _a3;const oneOfMapping={};const topRequired=hasRequired(parentSchema);let tagRequired=true;for(let i=0;ithis.addVocabulary(v));if(this.opts.discriminator)this.addKeyword(discriminator_1.default)}_addDefaultMetaSchema(){super._addDefaultMetaSchema();if(!this.opts.meta)return;const metaSchema=this.opts.$data?this.$dataMetaSchema(draft7MetaSchema,META_SUPPORT_DATA):draft7MetaSchema;this.addMetaSchema(metaSchema,META_SCHEMA_ID,false);this.refs["http://json-schema.org/schema"]=META_SCHEMA_ID}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(META_SCHEMA_ID)?META_SCHEMA_ID:void 0)}};exports.Ajv=Ajv2;module.exports=exports=Ajv2;module.exports.Ajv=Ajv2;Object.defineProperty(exports,"__esModule",{value:true});exports.default=Ajv2;var validate_1=require_validate();Object.defineProperty(exports,"KeywordCxt",{enumerable:true,get:function(){return validate_1.KeywordCxt}});var codegen_1=require_codegen();Object.defineProperty(exports,"_",{enumerable:true,get:function(){return codegen_1._}});Object.defineProperty(exports,"str",{enumerable:true,get:function(){return codegen_1.str}});Object.defineProperty(exports,"stringify",{enumerable:true,get:function(){return codegen_1.stringify}});Object.defineProperty(exports,"nil",{enumerable:true,get:function(){return codegen_1.nil}});Object.defineProperty(exports,"Name",{enumerable:true,get:function(){return codegen_1.Name}});Object.defineProperty(exports,"CodeGen",{enumerable:true,get:function(){return codegen_1.CodeGen}});var validation_error_1=require_validation_error();Object.defineProperty(exports,"ValidationError",{enumerable:true,get:function(){return validation_error_1.default}});var ref_error_1=require_ref_error();Object.defineProperty(exports,"MissingRefError",{enumerable:true,get:function(){return ref_error_1.default}})}});var require_formats=__commonJS({"node_modules/ajv-formats/dist/formats.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.formatNames=exports.fastFormats=exports.fullFormats=void 0;function fmtDef(validate,compare){return{validate,compare}}exports.fullFormats={date:fmtDef(date4,compareDate),time:fmtDef(getTime(true),compareTime),"date-time":fmtDef(getDateTime(true),compareDateTime),"iso-time":fmtDef(getTime(),compareIsoTime),"iso-date-time":fmtDef(getDateTime(),compareIsoDateTime),duration:/^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/,uri,"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,url:/^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,ipv6:/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i,regex,uuid:/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,"json-pointer":/^(?:\/(?:[^~/]|~0|~1)*)*$/,"json-pointer-uri-fragment":/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,"relative-json-pointer":/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,byte,int32:{type:"number",validate:validateInt32},int64:{type:"number",validate:validateInt64},float:{type:"number",validate:validateNumber},double:{type:"number",validate:validateNumber},password:true,binary:true};exports.fastFormats={...exports.fullFormats,date:fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d$/,compareDate),time:fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,compareTime),"date-time":fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,compareDateTime),"iso-time":fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,compareIsoTime),"iso-date-time":fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,compareIsoDateTime),uri:/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i};exports.formatNames=Object.keys(exports.fullFormats);function isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}var DATE=/^(\d\d\d\d)-(\d\d)-(\d\d)$/;var DAYS=[0,31,28,31,30,31,30,31,31,30,31,30,31];function date4(str){const matches=DATE.exec(str);if(!matches)return false;const year=+matches[1];const month=+matches[2];const day=+matches[3];return month>=1&&month<=12&&day>=1&&day<=(month===2&&isLeapYear(year)?29:DAYS[month])}function compareDate(d1,d2){if(!(d1&&d2))return void 0;if(d1>d2)return 1;if(d123||tzM>59||strictTimeZone&&!tz)return false;if(hr<=23&&min<=59&&sec<60)return true;const utcMin=min-tzM*tzSign;const utcHr=hr-tzH*tzSign-(utcMin<0?1:0);return(utcHr===23||utcHr===-1)&&(utcMin===59||utcMin===-1)&&sec<61}}function compareTime(s1,s2){if(!(s1&&s2))return void 0;const t1=new Date("2020-01-01T"+s1).valueOf();const t2=new Date("2020-01-01T"+s2).valueOf();if(!(t1&&t2))return void 0;return t1-t2}function compareIsoTime(t1,t2){if(!(t1&&t2))return void 0;const a1=TIME.exec(t1);const a2=TIME.exec(t2);if(!(a1&&a2))return void 0;t1=a1[1]+a1[2]+a1[3];t2=a2[1]+a2[2]+a2[3];if(t1>t2)return 1;if(t1=MIN_INT32}function validateInt64(value){return Number.isInteger(value)}function validateNumber(){return true}var Z_ANCHOR=/[^\\]\\Z/;function regex(str){if(Z_ANCHOR.test(str))return false;try{new RegExp(str);return true}catch(e){return false}}}});var require_limit=__commonJS({"node_modules/ajv-formats/dist/limit.js"(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.formatLimitDefinition=void 0;var ajv_1=require_ajv();var codegen_1=require_codegen();var ops=codegen_1.operators;var KWDs={formatMaximum:{okStr:"<=",ok:ops.LTE,fail:ops.GT},formatMinimum:{okStr:">=",ok:ops.GTE,fail:ops.LT},formatExclusiveMaximum:{okStr:"<",ok:ops.LT,fail:ops.GTE},formatExclusiveMinimum:{okStr:">",ok:ops.GT,fail:ops.LTE}};var error2={message:({keyword,schemaCode})=>(0,codegen_1.str)`should be ${KWDs[keyword].okStr} ${schemaCode}`,params:({keyword,schemaCode})=>(0,codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}`};exports.formatLimitDefinition={keyword:Object.keys(KWDs),type:"string",schemaType:"string",$data:true,error:error2,code(cxt){const{gen,data,schemaCode,keyword,it}=cxt;const{opts,self}=it;if(!opts.validateFormats)return;const fCxt=new ajv_1.KeywordCxt(it,self.RULES.all.format.definition,"format");if(fCxt.$data)validate$DataFormat();else validateFormat();function validate$DataFormat(){const fmts=gen.scopeValue("formats",{ref:self.formats,code:opts.code.formats});const fmt=gen.const("fmt",(0,codegen_1._)`${fmts}[${fCxt.schemaCode}]`);cxt.fail$data((0,codegen_1.or)((0,codegen_1._)`typeof ${fmt} != "object"`,(0,codegen_1._)`${fmt} instanceof RegExp`,(0,codegen_1._)`typeof ${fmt}.compare != "function"`,compareCode(fmt)))}function validateFormat(){const format=fCxt.schema;const fmtDef=self.formats[format];if(!fmtDef||fmtDef===true)return;if(typeof fmtDef!="object"||fmtDef instanceof RegExp||typeof fmtDef.compare!="function"){throw new Error(`"${keyword}": format "${format}" does not define "compare" function`)}const fmt=gen.scopeValue("formats",{key:format,ref:fmtDef,code:opts.code.formats?(0,codegen_1._)`${opts.code.formats}${(0,codegen_1.getProperty)(format)}`:void 0});cxt.fail$data(compareCode(fmt))}function compareCode(fmt){return(0,codegen_1._)`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`}},dependencies:["format"]};var formatLimitPlugin=ajv=>{ajv.addKeyword(exports.formatLimitDefinition);return ajv};exports.default=formatLimitPlugin}});var require_dist=__commonJS({"node_modules/ajv-formats/dist/index.js"(exports,module){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var formats_1=require_formats();var limit_1=require_limit();var codegen_1=require_codegen();var fullName=new codegen_1.Name("fullFormats");var fastName=new codegen_1.Name("fastFormats");var formatsPlugin=(ajv,opts={keywords:true})=>{if(Array.isArray(opts)){addFormats(ajv,opts,formats_1.fullFormats,fullName);return ajv}const[formats,exportName]=opts.mode==="fast"?[formats_1.fastFormats,fastName]:[formats_1.fullFormats,fullName];const list=opts.formats||formats_1.formatNames;addFormats(ajv,list,formats,exportName);if(opts.keywords)(0,limit_1.default)(ajv);return ajv};formatsPlugin.get=(name,mode="full")=>{const formats=mode==="fast"?formats_1.fastFormats:formats_1.fullFormats;const f=formats[name];if(!f)throw new Error(`Unknown format "${name}"`);return f};function addFormats(ajv,list,fs,exportName){var _a3;var _b;(_a3=(_b=ajv.opts.code).formats)!==null&&_a3!==void 0?_a3:_b.formats=(0,codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;for(const f of list)ajv.addFormat(f,fs[f])}module.exports=exports=formatsPlugin;Object.defineProperty(exports,"__esModule",{value:true});exports.default=formatsPlugin}});var require_content_type=__commonJS({"node_modules/content-type/index.js"(exports){"use strict";var PARAM_REGEXP=/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;var TEXT_REGEXP=/^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;var TOKEN_REGEXP=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;var QESC_REGEXP=/\\([\u000b\u0020-\u00ff])/g;var QUOTE_REGEXP=/([\\"])/g;var TYPE_REGEXP=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;exports.format=format;exports.parse=parse3;function format(obj){if(!obj||typeof obj!=="object"){throw new TypeError("argument obj is required")}var parameters=obj.parameters;var type=obj.type;if(!type||!TYPE_REGEXP.test(type)){throw new TypeError("invalid type")}var string4=type;if(parameters&&typeof parameters==="object"){var param;var params=Object.keys(parameters).sort();for(var i=0;i0&&!TEXT_REGEXP.test(str)){throw new TypeError("invalid parameter value")}return'"'+str.replace(QUOTE_REGEXP,"\\$1")+'"'}function ContentType(type){this.parameters=Object.create(null);this.type=type}}});import{realpathSync}from"node:fs";import{fileURLToPath}from"node:url";var _a;var NEVER=Object.freeze({status:"aborted"});function $constructor(name,initializer3,params){function init(inst,def){if(!inst._zod){Object.defineProperty(inst,"_zod",{value:{def,constr:_,traits:new Set},enumerable:false})}if(inst._zod.traits.has(name)){return}inst._zod.traits.add(name);initializer3(inst,def);const proto=_.prototype;const keys=Object.keys(proto);for(let i=0;i{if(params?.Parent&&inst instanceof params.Parent)return true;return inst?._zod?.traits?.has(name)}});Object.defineProperty(_,"name",{value:name});return _}var $ZodAsyncError=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};var $ZodEncodeError=class extends Error{constructor(name){super(`Encountered unidirectional transform during encode: ${name}`);this.name="ZodEncodeError"}};(_a=globalThis).__zod_globalConfig??(_a.__zod_globalConfig={});var globalConfig=globalThis.__zod_globalConfig;function config(newConfig){if(newConfig)Object.assign(globalConfig,newConfig);return globalConfig}var util_exports={};__export(util_exports,{BIGINT_FORMAT_RANGES:()=>BIGINT_FORMAT_RANGES,Class:()=>Class,NUMBER_FORMAT_RANGES:()=>NUMBER_FORMAT_RANGES,aborted:()=>aborted,allowsEval:()=>allowsEval,assert:()=>assert,assertEqual:()=>assertEqual,assertIs:()=>assertIs,assertNever:()=>assertNever,assertNotEqual:()=>assertNotEqual,assignProp:()=>assignProp,base64ToUint8Array:()=>base64ToUint8Array,base64urlToUint8Array:()=>base64urlToUint8Array,cached:()=>cached,captureStackTrace:()=>captureStackTrace,cleanEnum:()=>cleanEnum,cleanRegex:()=>cleanRegex,clone:()=>clone,cloneDef:()=>cloneDef,createTransparentProxy:()=>createTransparentProxy,defineLazy:()=>defineLazy,esc:()=>esc,escapeRegex:()=>escapeRegex,explicitlyAborted:()=>explicitlyAborted,extend:()=>extend,finalizeIssue:()=>finalizeIssue,floatSafeRemainder:()=>floatSafeRemainder,getElementAtPath:()=>getElementAtPath,getEnumValues:()=>getEnumValues,getLengthableOrigin:()=>getLengthableOrigin,getParsedType:()=>getParsedType,getSizableOrigin:()=>getSizableOrigin,hexToUint8Array:()=>hexToUint8Array,isObject:()=>isObject,isPlainObject:()=>isPlainObject,issue:()=>issue,joinValues:()=>joinValues,jsonStringifyReplacer:()=>jsonStringifyReplacer,merge:()=>merge,mergeDefs:()=>mergeDefs,normalizeParams:()=>normalizeParams,nullish:()=>nullish,numKeys:()=>numKeys,objectClone:()=>objectClone,omit:()=>omit,optionalKeys:()=>optionalKeys,parsedType:()=>parsedType,partial:()=>partial,pick:()=>pick,prefixIssues:()=>prefixIssues,primitiveTypes:()=>primitiveTypes,promiseAllObject:()=>promiseAllObject,propertyKeyTypes:()=>propertyKeyTypes,randomString:()=>randomString,required:()=>required,safeExtend:()=>safeExtend,shallowClone:()=>shallowClone,slugify:()=>slugify,stringifyPrimitive:()=>stringifyPrimitive,uint8ArrayToBase64:()=>uint8ArrayToBase64,uint8ArrayToBase64url:()=>uint8ArrayToBase64url,uint8ArrayToHex:()=>uint8ArrayToHex,unwrapMessage:()=>unwrapMessage});function assertEqual(val){return val}function assertNotEqual(val){return val}function assertIs(_arg){}function assertNever(_x){throw new Error("Unexpected value in exhaustive check")}function assert(_){}function getEnumValues(entries){const numericValues=Object.values(entries).filter(v=>typeof v==="number");const values=Object.entries(entries).filter(([k,_])=>numericValues.indexOf(+k)===-1).map(([_,v])=>v);return values}function joinValues(array2,separator="|"){return array2.map(val=>stringifyPrimitive(val)).join(separator)}function jsonStringifyReplacer(_,value){if(typeof value==="bigint")return value.toString();return value}function cached(getter){const set=false;return{get value(){if(!set){const value=getter();Object.defineProperty(this,"value",{value});return value}throw new Error("cached value already set")}}}function nullish(input){return input===null||input===void 0}function cleanRegex(source){const start=source.startsWith("^")?1:0;const end=source.endsWith("$")?source.length-1:source.length;return source.slice(start,end)}function floatSafeRemainder(val,step){const ratio=val/step;const roundedRatio=Math.round(ratio);const tolerance=Number.EPSILON*Math.max(Math.abs(ratio),1);if(Math.abs(ratio-roundedRatio)acc?.[key],obj)}function promiseAllObject(promisesObj){const keys=Object.keys(promisesObj);const promises=keys.map(key=>promisesObj[key]);return Promise.all(promises).then(results=>{const resolvedObj={};for(let i=0;i{};function isObject(data){return typeof data==="object"&&data!==null&&!Array.isArray(data)}var allowsEval=cached(()=>{if(globalConfig.jitless){return false}if(typeof navigator!=="undefined"&&navigator?.userAgent?.includes("Cloudflare")){return false}try{const F=Function;new F("");return true}catch(_){return false}});function isPlainObject(o){if(isObject(o)===false)return false;const ctor=o.constructor;if(ctor===void 0)return true;if(typeof ctor!=="function")return true;const prot=ctor.prototype;if(isObject(prot)===false)return false;if(Object.prototype.hasOwnProperty.call(prot,"isPrototypeOf")===false){return false}return true}function shallowClone(o){if(isPlainObject(o))return{...o};if(Array.isArray(o))return[...o];if(o instanceof Map)return new Map(o);if(o instanceof Set)return new Set(o);return o}function numKeys(data){let keyCount=0;for(const key in data){if(Object.prototype.hasOwnProperty.call(data,key)){keyCount++}}return keyCount}var getParsedType=data=>{const t=typeof data;switch(t){case"undefined":return"undefined";case"string":return"string";case"number":return Number.isNaN(data)?"nan":"number";case"boolean":return"boolean";case"function":return"function";case"bigint":return"bigint";case"symbol":return"symbol";case"object":if(Array.isArray(data)){return"array"}if(data===null){return"null"}if(data.then&&typeof data.then==="function"&&data.catch&&typeof data.catch==="function"){return"promise"}if(typeof Map!=="undefined"&&data instanceof Map){return"map"}if(typeof Set!=="undefined"&&data instanceof Set){return"set"}if(typeof Date!=="undefined"&&data instanceof Date){return"date"}if(typeof File!=="undefined"&&data instanceof File){return"file"}return"object";default:throw new Error(`Unknown data type: ${t}`)}};var propertyKeyTypes=new Set(["string","number","symbol"]);var primitiveTypes=new Set(["string","number","bigint","boolean","symbol","undefined"]);function escapeRegex(str){return str.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function clone(inst,def,params){const cl=new inst._zod.constr(def??inst._zod.def);if(!def||params?.parent)cl._zod.parent=inst;return cl}function normalizeParams(_params){const params=_params;if(!params)return{};if(typeof params==="string")return{error:()=>params};if(params?.message!==void 0){if(params?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");params.error=params.message}delete params.message;if(typeof params.error==="string")return{...params,error:()=>params.error};return params}function createTransparentProxy(getter){let target;return new Proxy({},{get(_,prop,receiver){target??(target=getter());return Reflect.get(target,prop,receiver)},set(_,prop,value,receiver){target??(target=getter());return Reflect.set(target,prop,value,receiver)},has(_,prop){target??(target=getter());return Reflect.has(target,prop)},deleteProperty(_,prop){target??(target=getter());return Reflect.deleteProperty(target,prop)},ownKeys(_){target??(target=getter());return Reflect.ownKeys(target)},getOwnPropertyDescriptor(_,prop){target??(target=getter());return Reflect.getOwnPropertyDescriptor(target,prop)},defineProperty(_,prop,descriptor){target??(target=getter());return Reflect.defineProperty(target,prop,descriptor)}})}function stringifyPrimitive(value){if(typeof value==="bigint")return value.toString()+"n";if(typeof value==="string")return`"${value}"`;return`${value}`}function optionalKeys(shape){return Object.keys(shape).filter(k=>{return shape[k]._zod.optin==="optional"&&shape[k]._zod.optout==="optional"})}var NUMBER_FORMAT_RANGES={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};var BIGINT_FORMAT_RANGES={int64:[BigInt("-9223372036854775808"),BigInt("9223372036854775807")],uint64:[BigInt(0),BigInt("18446744073709551615")]};function pick(schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".pick() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const newShape={};for(const key in mask){if(!(key in currDef.shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;newShape[key]=currDef.shape[key]}assignProp(this,"shape",newShape);return newShape},checks:[]});return clone(schema,def)}function omit(schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".omit() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const newShape={...schema._zod.def.shape};for(const key in mask){if(!(key in currDef.shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;delete newShape[key]}assignProp(this,"shape",newShape);return newShape},checks:[]});return clone(schema,def)}function extend(schema,shape){if(!isPlainObject(shape)){throw new Error("Invalid input to extend: expected a plain object")}const checks=schema._zod.def.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){const existingShape=schema._zod.def.shape;for(const key in shape){if(Object.getOwnPropertyDescriptor(existingShape,key)!==void 0){throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}}}const def=mergeDefs(schema._zod.def,{get shape(){const _shape={...schema._zod.def.shape,...shape};assignProp(this,"shape",_shape);return _shape}});return clone(schema,def)}function safeExtend(schema,shape){if(!isPlainObject(shape)){throw new Error("Invalid input to safeExtend: expected a plain object")}const def=mergeDefs(schema._zod.def,{get shape(){const _shape={...schema._zod.def.shape,...shape};assignProp(this,"shape",_shape);return _shape}});return clone(schema,def)}function merge(a,b){if(a._zod.def.checks?.length){throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.")}const def=mergeDefs(a._zod.def,{get shape(){const _shape={...a._zod.def.shape,...b._zod.def.shape};assignProp(this,"shape",_shape);return _shape},get catchall(){return b._zod.def.catchall},checks:b._zod.def.checks??[]});return clone(a,def)}function partial(Class2,schema,mask){const currDef=schema._zod.def;const checks=currDef.checks;const hasChecks=checks&&checks.length>0;if(hasChecks){throw new Error(".partial() cannot be used on object schemas containing refinements")}const def=mergeDefs(schema._zod.def,{get shape(){const oldShape=schema._zod.def.shape;const shape={...oldShape};if(mask){for(const key in mask){if(!(key in oldShape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;shape[key]=Class2?new Class2({type:"optional",innerType:oldShape[key]}):oldShape[key]}}else{for(const key in oldShape){shape[key]=Class2?new Class2({type:"optional",innerType:oldShape[key]}):oldShape[key]}}assignProp(this,"shape",shape);return shape},checks:[]});return clone(schema,def)}function required(Class2,schema,mask){const def=mergeDefs(schema._zod.def,{get shape(){const oldShape=schema._zod.def.shape;const shape={...oldShape};if(mask){for(const key in mask){if(!(key in shape)){throw new Error(`Unrecognized key: "${key}"`)}if(!mask[key])continue;shape[key]=new Class2({type:"nonoptional",innerType:oldShape[key]})}}else{for(const key in oldShape){shape[key]=new Class2({type:"nonoptional",innerType:oldShape[key]})}}assignProp(this,"shape",shape);return shape}});return clone(schema,def)}function aborted(x,startIndex=0){if(x.aborted===true)return true;for(let i=startIndex;i{var _a3;(_a3=iss).path??(_a3.path=[]);iss.path.unshift(path);return iss})}function unwrapMessage(message){return typeof message==="string"?message:message?.message}function finalizeIssue(iss,ctx,config2){const message=iss.message?iss.message:unwrapMessage(iss.inst?._zod.def?.error?.(iss))??unwrapMessage(ctx?.error?.(iss))??unwrapMessage(config2.customError?.(iss))??unwrapMessage(config2.localeError?.(iss))??"Invalid input";const{inst:_inst,continue:_continue,input:_input,...rest}=iss;rest.path??(rest.path=[]);rest.message=message;if(ctx?.reportInput){rest.input=_input}return rest}function getSizableOrigin(input){if(input instanceof Set)return"set";if(input instanceof Map)return"map";if(input instanceof File)return"file";return"unknown"}function getLengthableOrigin(input){if(Array.isArray(input))return"array";if(typeof input==="string")return"string";return"unknown"}function parsedType(data){const t=typeof data;switch(t){case"number":{return Number.isNaN(data)?"nan":"number"}case"object":{if(data===null){return"null"}if(Array.isArray(data)){return"array"}const obj=data;if(obj&&Object.getPrototypeOf(obj)!==Object.prototype&&"constructor"in obj&&obj.constructor){return obj.constructor.name}}}return t}function issue(...args){const[iss,input,inst]=args;if(typeof iss==="string"){return{message:iss,code:"custom",input,inst}}return{...iss}}function cleanEnum(obj){return Object.entries(obj).filter(([k,_])=>{return Number.isNaN(Number.parseInt(k,10))}).map(el=>el[1])}function base64ToUint8Array(base642){const binaryString=atob(base642);const bytes=new Uint8Array(binaryString.length);for(let i=0;ib.toString(16).padStart(2,"0")).join("")}var Class=class{constructor(..._args){}};var initializer=(inst,def)=>{inst.name="$ZodError";Object.defineProperty(inst,"_zod",{value:inst._zod,enumerable:false});Object.defineProperty(inst,"issues",{value:def,enumerable:false});inst.message=JSON.stringify(def,jsonStringifyReplacer,2);Object.defineProperty(inst,"toString",{value:()=>inst.message,enumerable:false})};var $ZodError=$constructor("$ZodError",initializer);var $ZodRealError=$constructor("$ZodError",initializer,{Parent:Error});function flattenError(error2,mapper=issue2=>issue2.message){const fieldErrors={};const formErrors=[];for(const sub of error2.issues){if(sub.path.length>0){fieldErrors[sub.path[0]]=fieldErrors[sub.path[0]]||[];fieldErrors[sub.path[0]].push(mapper(sub))}else{formErrors.push(mapper(sub))}}return{formErrors,fieldErrors}}function formatError(error2,mapper=issue2=>issue2.message){const fieldErrors={_errors:[]};const processError=(error3,path=[])=>{for(const issue2 of error3.issues){if(issue2.code==="invalid_union"&&issue2.errors.length){issue2.errors.map(issues=>processError({issues},[...path,...issue2.path]))}else if(issue2.code==="invalid_key"){processError({issues:issue2.issues},[...path,...issue2.path])}else if(issue2.code==="invalid_element"){processError({issues:issue2.issues},[...path,...issue2.path])}else{const fullpath=[...path,...issue2.path];if(fullpath.length===0){fieldErrors._errors.push(mapper(issue2))}else{let curr=fieldErrors;let i=0;while(i(schema,value,_ctx,_params)=>{const ctx=_ctx?{..._ctx,async:false}:{async:false};const result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise){throw new $ZodAsyncError}if(result.issues.length){const e=new(_params?.Err??_Err)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())));captureStackTrace(e,_params?.callee);throw e}return result.value};var _parseAsync=_Err=>async(schema,value,_ctx,params)=>{const ctx=_ctx?{..._ctx,async:true}:{async:true};let result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise)result=await result;if(result.issues.length){const e=new(params?.Err??_Err)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())));captureStackTrace(e,params?.callee);throw e}return result.value};var _safeParse=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,async:false}:{async:false};const result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise){throw new $ZodAsyncError}return result.issues.length?{success:false,error:new(_Err??$ZodError)(result.issues.map(iss=>finalizeIssue(iss,ctx,config())))}:{success:true,data:result.value}};var safeParse=_safeParse($ZodRealError);var _safeParseAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,async:true}:{async:true};let result=schema._zod.run({value,issues:[]},ctx);if(result instanceof Promise)result=await result;return result.issues.length?{success:false,error:new _Err(result.issues.map(iss=>finalizeIssue(iss,ctx,config())))}:{success:true,data:result.value}};var safeParseAsync=_safeParseAsync($ZodRealError);var _encode=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _parse(_Err)(schema,value,ctx)};var _decode=_Err=>(schema,value,_ctx)=>{return _parse(_Err)(schema,value,_ctx)};var _encodeAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _parseAsync(_Err)(schema,value,ctx)};var _decodeAsync=_Err=>async(schema,value,_ctx)=>{return _parseAsync(_Err)(schema,value,_ctx)};var _safeEncode=_Err=>(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _safeParse(_Err)(schema,value,ctx)};var _safeDecode=_Err=>(schema,value,_ctx)=>{return _safeParse(_Err)(schema,value,_ctx)};var _safeEncodeAsync=_Err=>async(schema,value,_ctx)=>{const ctx=_ctx?{..._ctx,direction:"backward"}:{direction:"backward"};return _safeParseAsync(_Err)(schema,value,ctx)};var _safeDecodeAsync=_Err=>async(schema,value,_ctx)=>{return _safeParseAsync(_Err)(schema,value,_ctx)};var cuid=/^[cC][0-9a-z]{6,}$/;var cuid2=/^[0-9a-z]+$/;var ulid=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;var xid=/^[0-9a-vA-V]{20}$/;var ksuid=/^[A-Za-z0-9]{27}$/;var nanoid=/^[a-zA-Z0-9_-]{21}$/;var duration=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;var guid=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;var uuid=version2=>{if(!version2)return/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`)};var email=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/;var _emoji=`^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;function emoji(){return new RegExp(_emoji,"u")}var ipv4=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;var ipv6=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;var cidrv4=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;var cidrv6=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;var base64=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;var base64url=/^[A-Za-z0-9_-]*$/;var httpProtocol=/^https?$/;var e164=/^\+[1-9]\d{6,14}$/;var dateSource=`(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;var date=new RegExp(`^${dateSource}$`);function timeSource(args){const hhmm=`(?:[01]\\d|2[0-3]):[0-5]\\d`;const regex=typeof args.precision==="number"?args.precision===-1?`${hhmm}`:args.precision===0?`${hhmm}:[0-5]\\d`:`${hhmm}:[0-5]\\d\\.\\d{${args.precision}}`:`${hhmm}(?::[0-5]\\d(?:\\.\\d+)?)?`;return regex}function time(args){return new RegExp(`^${timeSource(args)}$`)}function datetime(args){const time3=timeSource({precision:args.precision});const opts=["Z"];if(args.local)opts.push("");if(args.offset)opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);const timeRegex=`${time3}(?:${opts.join("|")})`;return new RegExp(`^${dateSource}T(?:${timeRegex})$`)}var string=params=>{const regex=params?`[\\s\\S]{${params?.minimum??0},${params?.maximum??""}}`:`[\\s\\S]*`;return new RegExp(`^${regex}$`)};var bigint=/^-?\d+n?$/;var integer=/^-?\d+$/;var number=/^-?\d+(?:\.\d+)?$/;var boolean=/^(?:true|false)$/i;var _null=/^null$/i;var lowercase=/^[^A-Z]*$/;var uppercase=/^[^a-z]*$/;var $ZodCheck=$constructor("$ZodCheck",(inst,def)=>{var _a3;inst._zod??(inst._zod={});inst._zod.def=def;(_a3=inst._zod).onattach??(_a3.onattach=[])});var numericOriginMap={number:"number",bigint:"bigint",object:"date"};var $ZodCheckLessThan=$constructor("$ZodCheckLessThan",(inst,def)=>{$ZodCheck.init(inst,def);const origin=numericOriginMap[typeof def.value];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;const curr=(def.inclusive?bag.maximum:bag.exclusiveMaximum)??Number.POSITIVE_INFINITY;if(def.value{if(def.inclusive?payload.value<=def.value:payload.value{$ZodCheck.init(inst,def);const origin=numericOriginMap[typeof def.value];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;const curr=(def.inclusive?bag.minimum:bag.exclusiveMinimum)??Number.NEGATIVE_INFINITY;if(def.value>curr){if(def.inclusive)bag.minimum=def.value;else bag.exclusiveMinimum=def.value}});inst._zod.check=payload=>{if(def.inclusive?payload.value>=def.value:payload.value>def.value){return}payload.issues.push({origin,code:"too_small",minimum:typeof def.value==="object"?def.value.getTime():def.value,input:payload.value,inclusive:def.inclusive,inst,continue:!def.abort})}});var $ZodCheckMultipleOf=$constructor("$ZodCheckMultipleOf",(inst,def)=>{$ZodCheck.init(inst,def);inst._zod.onattach.push(inst2=>{var _a3;(_a3=inst2._zod.bag).multipleOf??(_a3.multipleOf=def.value)});inst._zod.check=payload=>{if(typeof payload.value!==typeof def.value)throw new Error("Cannot mix number and bigint in multiple_of check.");const isMultiple=typeof payload.value==="bigint"?payload.value%def.value===BigInt(0):floatSafeRemainder(payload.value,def.value)===0;if(isMultiple)return;payload.issues.push({origin:typeof payload.value,code:"not_multiple_of",divisor:def.value,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckNumberFormat=$constructor("$ZodCheckNumberFormat",(inst,def)=>{$ZodCheck.init(inst,def);def.format=def.format||"float64";const isInt=def.format?.includes("int");const origin=isInt?"int":"number";const[minimum,maximum]=NUMBER_FORMAT_RANGES[def.format];inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.format=def.format;bag.minimum=minimum;bag.maximum=maximum;if(isInt)bag.pattern=integer});inst._zod.check=payload=>{const input=payload.value;if(isInt){if(!Number.isInteger(input)){payload.issues.push({expected:origin,format:def.format,code:"invalid_type",continue:false,input,inst});return}if(!Number.isSafeInteger(input)){if(input>0){payload.issues.push({input,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst,origin,inclusive:true,continue:!def.abort})}else{payload.issues.push({input,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst,origin,inclusive:true,continue:!def.abort})}return}}if(inputmaximum){payload.issues.push({origin:"number",input,code:"too_big",maximum,inclusive:true,inst,continue:!def.abort})}}});var $ZodCheckMaxLength=$constructor("$ZodCheckMaxLength",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const curr=inst2._zod.bag.maximum??Number.POSITIVE_INFINITY;if(def.maximum{const input=payload.value;const length=input.length;if(length<=def.maximum)return;const origin=getLengthableOrigin(input);payload.issues.push({origin,code:"too_big",maximum:def.maximum,inclusive:true,input,inst,continue:!def.abort})}});var $ZodCheckMinLength=$constructor("$ZodCheckMinLength",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const curr=inst2._zod.bag.minimum??Number.NEGATIVE_INFINITY;if(def.minimum>curr)inst2._zod.bag.minimum=def.minimum});inst._zod.check=payload=>{const input=payload.value;const length=input.length;if(length>=def.minimum)return;const origin=getLengthableOrigin(input);payload.issues.push({origin,code:"too_small",minimum:def.minimum,inclusive:true,input,inst,continue:!def.abort})}});var $ZodCheckLengthEquals=$constructor("$ZodCheckLengthEquals",(inst,def)=>{var _a3;$ZodCheck.init(inst,def);(_a3=inst._zod.def).when??(_a3.when=payload=>{const val=payload.value;return!nullish(val)&&val.length!==void 0});inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.minimum=def.length;bag.maximum=def.length;bag.length=def.length});inst._zod.check=payload=>{const input=payload.value;const length=input.length;if(length===def.length)return;const origin=getLengthableOrigin(input);const tooBig=length>def.length;payload.issues.push({origin,...tooBig?{code:"too_big",maximum:def.length}:{code:"too_small",minimum:def.length},inclusive:true,exact:true,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckStringFormat=$constructor("$ZodCheckStringFormat",(inst,def)=>{var _a3,_b;$ZodCheck.init(inst,def);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.format=def.format;if(def.pattern){bag.patterns??(bag.patterns=new Set);bag.patterns.add(def.pattern)}});if(def.pattern)(_a3=inst._zod).check??(_a3.check=payload=>{def.pattern.lastIndex=0;if(def.pattern.test(payload.value))return;payload.issues.push({origin:"string",code:"invalid_format",format:def.format,input:payload.value,...def.pattern?{pattern:def.pattern.toString()}:{},inst,continue:!def.abort})});else(_b=inst._zod).check??(_b.check=()=>{})});var $ZodCheckRegex=$constructor("$ZodCheckRegex",(inst,def)=>{$ZodCheckStringFormat.init(inst,def);inst._zod.check=payload=>{def.pattern.lastIndex=0;if(def.pattern.test(payload.value))return;payload.issues.push({origin:"string",code:"invalid_format",format:"regex",input:payload.value,pattern:def.pattern.toString(),inst,continue:!def.abort})}});var $ZodCheckLowerCase=$constructor("$ZodCheckLowerCase",(inst,def)=>{def.pattern??(def.pattern=lowercase);$ZodCheckStringFormat.init(inst,def)});var $ZodCheckUpperCase=$constructor("$ZodCheckUpperCase",(inst,def)=>{def.pattern??(def.pattern=uppercase);$ZodCheckStringFormat.init(inst,def)});var $ZodCheckIncludes=$constructor("$ZodCheckIncludes",(inst,def)=>{$ZodCheck.init(inst,def);const escapedRegex=escapeRegex(def.includes);const pattern=new RegExp(typeof def.position==="number"?`^.{${def.position}}${escapedRegex}`:escapedRegex);def.pattern=pattern;inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.includes(def.includes,def.position))return;payload.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:def.includes,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckStartsWith=$constructor("$ZodCheckStartsWith",(inst,def)=>{$ZodCheck.init(inst,def);const pattern=new RegExp(`^${escapeRegex(def.prefix)}.*`);def.pattern??(def.pattern=pattern);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.startsWith(def.prefix))return;payload.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:def.prefix,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckEndsWith=$constructor("$ZodCheckEndsWith",(inst,def)=>{$ZodCheck.init(inst,def);const pattern=new RegExp(`.*${escapeRegex(def.suffix)}$`);def.pattern??(def.pattern=pattern);inst._zod.onattach.push(inst2=>{const bag=inst2._zod.bag;bag.patterns??(bag.patterns=new Set);bag.patterns.add(pattern)});inst._zod.check=payload=>{if(payload.value.endsWith(def.suffix))return;payload.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:def.suffix,input:payload.value,inst,continue:!def.abort})}});var $ZodCheckOverwrite=$constructor("$ZodCheckOverwrite",(inst,def)=>{$ZodCheck.init(inst,def);inst._zod.check=payload=>{payload.value=def.tx(payload.value)}});var Doc=class{constructor(args=[]){this.content=[];this.indent=0;if(this)this.args=args}indented(fn){this.indent+=1;fn(this);this.indent-=1}write(arg){if(typeof arg==="function"){arg(this,{execution:"sync"});arg(this,{execution:"async"});return}const content=arg;const lines=content.split("\n").filter(x=>x);const minIndent=Math.min(...lines.map(x=>x.length-x.trimStart().length));const dedented=lines.map(x=>x.slice(minIndent)).map(x=>" ".repeat(this.indent*2)+x);for(const line of dedented){this.content.push(line)}}compile(){const F=Function;const args=this?.args;const content=this?.content??[``];const lines=[...content.map(x=>` ${x}`)];return new F(...args,lines.join("\n"))}};var version={major:4,minor:4,patch:3};var $ZodType=$constructor("$ZodType",(inst,def)=>{var _a3;inst??(inst={});inst._zod.def=def;inst._zod.bag=inst._zod.bag||{};inst._zod.version=version;const checks=[...inst._zod.def.checks??[]];if(inst._zod.traits.has("$ZodCheck")){checks.unshift(inst)}for(const ch of checks){for(const fn of ch._zod.onattach){fn(inst)}}if(checks.length===0){(_a3=inst._zod).deferred??(_a3.deferred=[]);inst._zod.deferred?.push(()=>{inst._zod.run=inst._zod.parse})}else{const runChecks=(payload,checks2,ctx)=>{let isAborted=aborted(payload);let asyncResult;for(const ch of checks2){if(ch._zod.def.when){if(explicitlyAborted(payload))continue;const shouldRun=ch._zod.def.when(payload);if(!shouldRun)continue}else if(isAborted){continue}const currLen=payload.issues.length;const _=ch._zod.check(payload);if(_ instanceof Promise&&ctx?.async===false){throw new $ZodAsyncError}if(asyncResult||_ instanceof Promise){asyncResult=(asyncResult??Promise.resolve()).then(async()=>{await _;const nextLen=payload.issues.length;if(nextLen===currLen)return;if(!isAborted)isAborted=aborted(payload,currLen)})}else{const nextLen=payload.issues.length;if(nextLen===currLen)continue;if(!isAborted)isAborted=aborted(payload,currLen)}}if(asyncResult){return asyncResult.then(()=>{return payload})}return payload};const handleCanaryResult=(canary,payload,ctx)=>{if(aborted(canary)){canary.aborted=true;return canary}const checkResult=runChecks(payload,checks,ctx);if(checkResult instanceof Promise){if(ctx.async===false)throw new $ZodAsyncError;return checkResult.then(checkResult2=>inst._zod.parse(checkResult2,ctx))}return inst._zod.parse(checkResult,ctx)};inst._zod.run=(payload,ctx)=>{if(ctx.skipChecks){return inst._zod.parse(payload,ctx)}if(ctx.direction==="backward"){const canary=inst._zod.parse({value:payload.value,issues:[]},{...ctx,skipChecks:true});if(canary instanceof Promise){return canary.then(canary2=>{return handleCanaryResult(canary2,payload,ctx)})}return handleCanaryResult(canary,payload,ctx)}const result=inst._zod.parse(payload,ctx);if(result instanceof Promise){if(ctx.async===false)throw new $ZodAsyncError;return result.then(result2=>runChecks(result2,checks,ctx))}return runChecks(result,checks,ctx)}}defineLazy(inst,"~standard",()=>({validate:value=>{try{const r=safeParse(inst,value);return r.success?{value:r.data}:{issues:r.error?.issues}}catch(_){return safeParseAsync(inst,value).then(r=>r.success?{value:r.data}:{issues:r.error?.issues})}},vendor:"zod",version:1}))});var $ZodString=$constructor("$ZodString",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=[...inst?._zod.bag?.patterns??[]].pop()??string(inst._zod.bag);inst._zod.parse=(payload,_)=>{if(def.coerce)try{payload.value=String(payload.value)}catch(_2){}if(typeof payload.value==="string")return payload;payload.issues.push({expected:"string",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodStringFormat=$constructor("$ZodStringFormat",(inst,def)=>{$ZodCheckStringFormat.init(inst,def);$ZodString.init(inst,def)});var $ZodGUID=$constructor("$ZodGUID",(inst,def)=>{def.pattern??(def.pattern=guid);$ZodStringFormat.init(inst,def)});var $ZodUUID=$constructor("$ZodUUID",(inst,def)=>{if(def.version){const versionMap={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8};const v=versionMap[def.version];if(v===void 0)throw new Error(`Invalid UUID version: "${def.version}"`);def.pattern??(def.pattern=uuid(v))}else def.pattern??(def.pattern=uuid());$ZodStringFormat.init(inst,def)});var $ZodEmail=$constructor("$ZodEmail",(inst,def)=>{def.pattern??(def.pattern=email);$ZodStringFormat.init(inst,def)});var $ZodURL=$constructor("$ZodURL",(inst,def)=>{$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{try{const trimmed=payload.value.trim();if(!def.normalize&&def.protocol?.source===httpProtocol.source){if(!/^https?:\/\//i.test(trimmed)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid URL format",input:payload.value,inst,continue:!def.abort});return}}const url2=new URL(trimmed);if(def.hostname){def.hostname.lastIndex=0;if(!def.hostname.test(url2.hostname)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:def.hostname.source,input:payload.value,inst,continue:!def.abort})}}if(def.protocol){def.protocol.lastIndex=0;if(!def.protocol.test(url2.protocol.endsWith(":")?url2.protocol.slice(0,-1):url2.protocol)){payload.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:def.protocol.source,input:payload.value,inst,continue:!def.abort})}}if(def.normalize){payload.value=url2.href}else{payload.value=trimmed}return}catch(_){payload.issues.push({code:"invalid_format",format:"url",input:payload.value,inst,continue:!def.abort})}}});var $ZodEmoji=$constructor("$ZodEmoji",(inst,def)=>{def.pattern??(def.pattern=emoji());$ZodStringFormat.init(inst,def)});var $ZodNanoID=$constructor("$ZodNanoID",(inst,def)=>{def.pattern??(def.pattern=nanoid);$ZodStringFormat.init(inst,def)});var $ZodCUID=$constructor("$ZodCUID",(inst,def)=>{def.pattern??(def.pattern=cuid);$ZodStringFormat.init(inst,def)});var $ZodCUID2=$constructor("$ZodCUID2",(inst,def)=>{def.pattern??(def.pattern=cuid2);$ZodStringFormat.init(inst,def)});var $ZodULID=$constructor("$ZodULID",(inst,def)=>{def.pattern??(def.pattern=ulid);$ZodStringFormat.init(inst,def)});var $ZodXID=$constructor("$ZodXID",(inst,def)=>{def.pattern??(def.pattern=xid);$ZodStringFormat.init(inst,def)});var $ZodKSUID=$constructor("$ZodKSUID",(inst,def)=>{def.pattern??(def.pattern=ksuid);$ZodStringFormat.init(inst,def)});var $ZodISODateTime=$constructor("$ZodISODateTime",(inst,def)=>{def.pattern??(def.pattern=datetime(def));$ZodStringFormat.init(inst,def)});var $ZodISODate=$constructor("$ZodISODate",(inst,def)=>{def.pattern??(def.pattern=date);$ZodStringFormat.init(inst,def)});var $ZodISOTime=$constructor("$ZodISOTime",(inst,def)=>{def.pattern??(def.pattern=time(def));$ZodStringFormat.init(inst,def)});var $ZodISODuration=$constructor("$ZodISODuration",(inst,def)=>{def.pattern??(def.pattern=duration);$ZodStringFormat.init(inst,def)});var $ZodIPv4=$constructor("$ZodIPv4",(inst,def)=>{def.pattern??(def.pattern=ipv4);$ZodStringFormat.init(inst,def);inst._zod.bag.format=`ipv4`});var $ZodIPv6=$constructor("$ZodIPv6",(inst,def)=>{def.pattern??(def.pattern=ipv6);$ZodStringFormat.init(inst,def);inst._zod.bag.format=`ipv6`;inst._zod.check=payload=>{try{new URL(`http://[${payload.value}]`)}catch{payload.issues.push({code:"invalid_format",format:"ipv6",input:payload.value,inst,continue:!def.abort})}}});var $ZodCIDRv4=$constructor("$ZodCIDRv4",(inst,def)=>{def.pattern??(def.pattern=cidrv4);$ZodStringFormat.init(inst,def)});var $ZodCIDRv6=$constructor("$ZodCIDRv6",(inst,def)=>{def.pattern??(def.pattern=cidrv6);$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{const parts=payload.value.split("/");try{if(parts.length!==2)throw new Error;const[address,prefix]=parts;if(!prefix)throw new Error;const prefixNum=Number(prefix);if(`${prefixNum}`!==prefix)throw new Error;if(prefixNum<0||prefixNum>128)throw new Error;new URL(`http://[${address}]`)}catch{payload.issues.push({code:"invalid_format",format:"cidrv6",input:payload.value,inst,continue:!def.abort})}}});function isValidBase64(data){if(data==="")return true;if(/\s/.test(data))return false;if(data.length%4!==0)return false;try{atob(data);return true}catch{return false}}var $ZodBase64=$constructor("$ZodBase64",(inst,def)=>{def.pattern??(def.pattern=base64);$ZodStringFormat.init(inst,def);inst._zod.bag.contentEncoding="base64";inst._zod.check=payload=>{if(isValidBase64(payload.value))return;payload.issues.push({code:"invalid_format",format:"base64",input:payload.value,inst,continue:!def.abort})}});function isValidBase64URL(data){if(!base64url.test(data))return false;const base642=data.replace(/[-_]/g,c=>c==="-"?"+":"/");const padded=base642.padEnd(Math.ceil(base642.length/4)*4,"=");return isValidBase64(padded)}var $ZodBase64URL=$constructor("$ZodBase64URL",(inst,def)=>{def.pattern??(def.pattern=base64url);$ZodStringFormat.init(inst,def);inst._zod.bag.contentEncoding="base64url";inst._zod.check=payload=>{if(isValidBase64URL(payload.value))return;payload.issues.push({code:"invalid_format",format:"base64url",input:payload.value,inst,continue:!def.abort})}});var $ZodE164=$constructor("$ZodE164",(inst,def)=>{def.pattern??(def.pattern=e164);$ZodStringFormat.init(inst,def)});function isValidJWT(token,algorithm=null){try{const tokensParts=token.split(".");if(tokensParts.length!==3)return false;const[header]=tokensParts;if(!header)return false;const parsedHeader=JSON.parse(atob(header));if("typ"in parsedHeader&&parsedHeader?.typ!=="JWT")return false;if(!parsedHeader.alg)return false;if(algorithm&&(!("alg"in parsedHeader)||parsedHeader.alg!==algorithm))return false;return true}catch{return false}}var $ZodJWT=$constructor("$ZodJWT",(inst,def)=>{$ZodStringFormat.init(inst,def);inst._zod.check=payload=>{if(isValidJWT(payload.value,def.alg))return;payload.issues.push({code:"invalid_format",format:"jwt",input:payload.value,inst,continue:!def.abort})}});var $ZodNumber=$constructor("$ZodNumber",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=inst._zod.bag.pattern??number;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=Number(payload.value)}catch(_){}const input=payload.value;if(typeof input==="number"&&!Number.isNaN(input)&&Number.isFinite(input)){return payload}const received=typeof input==="number"?Number.isNaN(input)?"NaN":!Number.isFinite(input)?"Infinity":void 0:void 0;payload.issues.push({expected:"number",code:"invalid_type",input,inst,...received?{received}:{}});return payload}});var $ZodNumberFormat=$constructor("$ZodNumberFormat",(inst,def)=>{$ZodCheckNumberFormat.init(inst,def);$ZodNumber.init(inst,def)});var $ZodBoolean=$constructor("$ZodBoolean",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=boolean;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=Boolean(payload.value)}catch(_){}const input=payload.value;if(typeof input==="boolean")return payload;payload.issues.push({expected:"boolean",code:"invalid_type",input,inst});return payload}});var $ZodBigInt=$constructor("$ZodBigInt",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=bigint;inst._zod.parse=(payload,_ctx)=>{if(def.coerce)try{payload.value=BigInt(payload.value)}catch(_){}if(typeof payload.value==="bigint")return payload;payload.issues.push({expected:"bigint",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodNull=$constructor("$ZodNull",(inst,def)=>{$ZodType.init(inst,def);inst._zod.pattern=_null;inst._zod.values=new Set([null]);inst._zod.parse=(payload,_ctx)=>{const input=payload.value;if(input===null)return payload;payload.issues.push({expected:"null",code:"invalid_type",input,inst});return payload}});var $ZodAny=$constructor("$ZodAny",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=payload=>payload});var $ZodUnknown=$constructor("$ZodUnknown",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=payload=>payload});var $ZodNever=$constructor("$ZodNever",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,_ctx)=>{payload.issues.push({expected:"never",code:"invalid_type",input:payload.value,inst});return payload}});var $ZodDate=$constructor("$ZodDate",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,_ctx)=>{if(def.coerce){try{payload.value=new Date(payload.value)}catch(_err){}}const input=payload.value;const isDate=input instanceof Date;const isValidDate=isDate&&!Number.isNaN(input.getTime());if(isValidDate)return payload;payload.issues.push({expected:"date",code:"invalid_type",input,...isDate?{received:"Invalid Date"}:{},inst});return payload}});function handleArrayResult(result,final,index){if(result.issues.length){final.issues.push(...prefixIssues(index,result.issues))}final.value[index]=result.value}var $ZodArray=$constructor("$ZodArray",(inst,def)=>{$ZodType.init(inst,def);inst._zod.parse=(payload,ctx)=>{const input=payload.value;if(!Array.isArray(input)){payload.issues.push({expected:"array",code:"invalid_type",input,inst});return payload}payload.value=Array(input.length);const proms=[];for(let i=0;ihandleArrayResult(result2,payload,i)))}else{handleArrayResult(result,payload,i)}}if(proms.length){return Promise.all(proms).then(()=>payload)}return payload}});function handlePropertyResult(result,final,key,input,isOptionalIn,isOptionalOut){const isPresent=key in input;if(result.issues.length){if(isOptionalIn&&isOptionalOut&&!isPresent){return}final.issues.push(...prefixIssues(key,result.issues))}if(!isPresent&&!isOptionalIn){if(!result.issues.length){final.issues.push({code:"invalid_type",expected:"nonoptional",input:void 0,path:[key]})}return}if(result.value===void 0){if(isPresent){final.value[key]=void 0}}else{final.value[key]=result.value}}function normalizeDef(def){const keys=Object.keys(def.shape);for(const k of keys){if(!def.shape?.[k]?._zod?.traits?.has("$ZodType")){throw new Error(`Invalid element at key "${k}": expected a Zod schema`)}}const okeys=optionalKeys(def.shape);return{...def,keys,keySet:new Set(keys),numKeys:keys.length,optionalKeys:new Set(okeys)}}function handleCatchall(proms,input,payload,ctx,def,inst){const unrecognized=[];const keySet=def.keySet;const _catchall=def.catchall._zod;const t=_catchall.def.type;const isOptionalIn=_catchall.optin==="optional";const isOptionalOut=_catchall.optout==="optional";for(const key in input){if(key==="__proto__")continue;if(keySet.has(key))continue;if(t==="never"){unrecognized.push(key);continue}const r=_catchall.run({value:input[key],issues:[]},ctx);if(r instanceof Promise){proms.push(r.then(r2=>handlePropertyResult(r2,payload,key,input,isOptionalIn,isOptionalOut)))}else{handlePropertyResult(r,payload,key,input,isOptionalIn,isOptionalOut)}}if(unrecognized.length){payload.issues.push({code:"unrecognized_keys",keys:unrecognized,input,inst})}if(!proms.length)return payload;return Promise.all(proms).then(()=>{return payload})}var $ZodObject=$constructor("$ZodObject",(inst,def)=>{$ZodType.init(inst,def);const desc=Object.getOwnPropertyDescriptor(def,"shape");if(!desc?.get){const sh=def.shape;Object.defineProperty(def,"shape",{get:()=>{const newSh={...sh};Object.defineProperty(def,"shape",{value:newSh});return newSh}})}const _normalized=cached(()=>normalizeDef(def));defineLazy(inst._zod,"propValues",()=>{const shape=def.shape;const propValues={};for(const key in shape){const field=shape[key]._zod;if(field.values){propValues[key]??(propValues[key]=new Set);for(const v of field.values)propValues[key].add(v)}}return propValues});const isObject2=isObject;const catchall=def.catchall;let value;inst._zod.parse=(payload,ctx)=>{value??(value=_normalized.value);const input=payload.value;if(!isObject2(input)){payload.issues.push({expected:"object",code:"invalid_type",input,inst});return payload}payload.value={};const proms=[];const shape=value.shape;for(const key of value.keys){const el=shape[key];const isOptionalIn=el._zod.optin==="optional";const isOptionalOut=el._zod.optout==="optional";const r=el._zod.run({value:input[key],issues:[]},ctx);if(r instanceof Promise){proms.push(r.then(r2=>handlePropertyResult(r2,payload,key,input,isOptionalIn,isOptionalOut)))}else{handlePropertyResult(r,payload,key,input,isOptionalIn,isOptionalOut)}}if(!catchall){return proms.length?Promise.all(proms).then(()=>payload):payload}return handleCatchall(proms,input,payload,ctx,_normalized.value,inst)}});var $ZodObjectJIT=$constructor("$ZodObjectJIT",(inst,def)=>{$ZodObject.init(inst,def);const superParse=inst._zod.parse;const _normalized=cached(()=>normalizeDef(def));const generateFastpass=shape=>{const doc=new Doc(["shape","payload","ctx"]);const normalized=_normalized.value;const parseStr=key=>{const k=esc(key);return`shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`};doc.write(`const input = payload.value;`);const ids=Object.create(null);let counter=0;for(const key of normalized.keys){ids[key]=`key_${counter++}`}doc.write(`const newResult = {};`);for(const key of normalized.keys){const id=ids[key];const k=esc(key);const schema=shape[key];const isOptionalIn=schema?._zod?.optin==="optional";const isOptionalOut=schema?._zod?.optout==="optional";doc.write(`const ${id} = ${parseStr(key)};`);if(isOptionalIn&&isOptionalOut){doc.write(` if (${id}.issues.length) { if (${k} in input) { payload.issues = payload.issues.concat(${id}.issues.map(iss => ({ @@ -74,8 +74,8 @@ ${value}`,dataLines++}else isEventPrefix(chunk,searchIndex,firstCharCode)?eventT `,searchIndex)}return chunk.slice(searchIndex)}for(;searchIndex20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected the request (HTTP ${error2.httpStatus})`:"MCP endpoint rejected the request";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function normalizeMcpSdkError(error2){return error2 instanceof McpError&&error2.code===ErrorCode.RequestTimeout?new ToolDiscoveryRuntimeError("timeout"):error2}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} -`)}async function callMcpSdk(operation){try{return await operation()}catch(error2){throw normalizeMcpSdkError(error2)}}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>callMcpSdk(()=>client.connect(transport,requestOptions)),loadPage:cursor=>callMcpSdk(async()=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)}),hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}await main(); +${value}`,dataLines++;break;case"id":id=value.includes("\0")?void 0:value;break;case"retry":/^\d+$/.test(value)?onRetry(parseInt(value,10)):onError(new ParseError(`Invalid \`retry\` value: "${value}"`,{type:"invalid-retry",value,line}));break;default:onError(new ParseError(`Unknown field "${field.length>20?`${field.slice(0,20)}\u2026`:field}"`,{type:"unknown-field",field,value,line}));break}}function dispatchEvent(){dataLines>0&&onEvent({id,event:eventType,data}),id=void 0,data="",dataLines=0,eventType=void 0}function reset(options={}){if(options.consume&&pendingFragments.length>0){const incompleteLine=pendingFragments.join("");parseLine(incompleteLine,0,incompleteLine.length)}isFirstChunk=true,id=void 0,data="",dataLines=0,eventType=void 0,pendingFragments.length=0,pendingFragmentsLength=0,terminated=false}return{feed,reset}}function isDataPrefix(chunk,i,firstCharCode){return firstCharCode===100&&chunk.charCodeAt(i+1)===97&&chunk.charCodeAt(i+2)===116&&chunk.charCodeAt(i+3)===97&&chunk.charCodeAt(i+4)===58}function isEventPrefix(chunk,i,firstCharCode){return firstCharCode===101&&chunk.charCodeAt(i+1)===118&&chunk.charCodeAt(i+2)===101&&chunk.charCodeAt(i+3)===110&&chunk.charCodeAt(i+4)===116&&chunk.charCodeAt(i+5)===58}var EventSourceParserStream=class extends TransformStream{constructor({onError,onRetry,onComment,maxBufferSize}={}){let parser;super({start(controller){parser=createParser({onEvent:event=>{controller.enqueue(event)},onError(error2){typeof onError=="function"&&onError(error2),(onError==="terminate"||error2.type==="max-buffer-size-exceeded")&&controller.error(error2)},onRetry,onComment,maxBufferSize})},transform(chunk){parser.feed(chunk)}})}};var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2};var StreamableHTTPError=class extends Error{constructor(code,message){super(`Streamable HTTP error: ${message}`);this.code=code}};var StreamableHTTPClientTransport=class{constructor(url2,opts){this._hasCompletedAuthFlow=false;this._url=url2;this._resourceMetadataUrl=void 0;this._scope=void 0;this._requestInit=opts?.requestInit;this._authProvider=opts?.authProvider;this._fetch=opts?.fetch;this._fetchWithInit=createFetchWithInit(opts?.fetch,opts?.requestInit);this._sessionId=opts?.sessionId;this._reconnectionOptions=opts?.reconnectionOptions??DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS}async _authThenStart(){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}let result;try{result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(error2){this.onerror?.(error2);throw error2}if(result!=="AUTHORIZED"){throw new UnauthorizedError}return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){const headers={};if(this._authProvider){const tokens=await this._authProvider.tokens();if(tokens){headers["Authorization"]=`Bearer ${tokens.access_token}`}}if(this._sessionId){headers["mcp-session-id"]=this._sessionId}if(this._protocolVersion){headers["mcp-protocol-version"]=this._protocolVersion}const extraHeaders=normalizeHeaders(this._requestInit?.headers);return new Headers({...headers,...extraHeaders})}async _startOrAuthSse(options){const{resumptionToken}=options;try{const headers=await this._commonHeaders();headers.set("Accept","text/event-stream");if(resumptionToken){headers.set("last-event-id",resumptionToken)}const response=await(this._fetch??fetch)(this._url,{method:"GET",headers,signal:this._abortController?.signal});if(!response.ok){await response.body?.cancel();if(response.status===401&&this._authProvider){return await this._authThenStart()}if(response.status===405){return}throw new StreamableHTTPError(response.status,`Failed to open SSE stream: ${response.statusText}`)}this._handleSseStream(response.body,options,true)}catch(error2){this.onerror?.(error2);throw error2}}_getNextReconnectionDelay(attempt){if(this._serverRetryMs!==void 0){return this._serverRetryMs}const initialDelay=this._reconnectionOptions.initialReconnectionDelay;const growFactor=this._reconnectionOptions.reconnectionDelayGrowFactor;const maxDelay=this._reconnectionOptions.maxReconnectionDelay;return Math.min(initialDelay*Math.pow(growFactor,attempt),maxDelay)}_scheduleReconnection(options,attemptCount=0){const maxRetries=this._reconnectionOptions.maxRetries;if(attemptCount>=maxRetries){this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));return}const delay=this._getNextReconnectionDelay(attemptCount);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(options).catch(error2=>{this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error2 instanceof Error?error2.message:String(error2)}`));this._scheduleReconnection(options,attemptCount+1)})},delay)}_handleSseStream(stream,options,isReconnectable){if(!stream){return}const{onresumptiontoken,replayMessageId}=options;let lastEventId;let hasPrimingEvent=false;let receivedResponse=false;const processStream=async()=>{try{const reader=stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({onRetry:retryMs=>{this._serverRetryMs=retryMs}})).getReader();while(true){const{value:event,done}=await reader.read();if(done){break}if(event.id){lastEventId=event.id;hasPrimingEvent=true;onresumptiontoken?.(event.id)}if(!event.data){continue}if(!event.event||event.event==="message"){try{const message=JSONRPCMessageSchema.parse(JSON.parse(event.data));if(isJSONRPCResultResponse(message)){receivedResponse=true;if(replayMessageId!==void 0){message.id=replayMessageId}}this.onmessage?.(message)}catch(error2){this.onerror?.(error2)}}}const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}}catch(error2){this.onerror?.(new Error(`SSE stream disconnected: ${error2}`));const canResume=isReconnectable||hasPrimingEvent;const needsReconnect=canResume&&!receivedResponse;if(needsReconnect&&this._abortController&&!this._abortController.signal.aborted){try{this._scheduleReconnection({resumptionToken:lastEventId,onresumptiontoken,replayMessageId},0)}catch(error3){this.onerror?.(new Error(`Failed to reconnect: ${error3 instanceof Error?error3.message:String(error3)}`))}}}};processStream()}async start(){if(this._abortController){throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.")}this._abortController=new AbortController}async finishAuth(authorizationCode){if(!this._authProvider){throw new UnauthorizedError("No auth provider")}const result=await auth(this._authProvider,{serverUrl:this._url,authorizationCode,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError("Failed to authorize")}}async close(){if(this._reconnectionTimeout){clearTimeout(this._reconnectionTimeout);this._reconnectionTimeout=void 0}this._abortController?.abort();this.onclose?.()}async send(message,options){try{const{resumptionToken,onresumptiontoken}=options||{};if(resumptionToken){this._startOrAuthSse({resumptionToken,replayMessageId:isJSONRPCRequest(message)?message.id:void 0}).catch(err=>this.onerror?.(err));return}const headers=await this._commonHeaders();headers.set("content-type","application/json");headers.set("accept","application/json, text/event-stream");const init={...this._requestInit,method:"POST",headers,body:JSON.stringify(message),signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);const sessionId=response.headers.get("mcp-session-id");if(sessionId){this._sessionId=sessionId}if(!response.ok){const text=await response.text().catch(()=>null);if(response.status===401&&this._authProvider){if(this._hasCompletedAuthFlow){throw new StreamableHTTPError(401,"Server returned 401 after successful authentication")}const{resourceMetadataUrl,scope}=extractWWWAuthenticateParams(response);this._resourceMetadataUrl=resourceMetadataUrl;this._scope=scope;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit});if(result!=="AUTHORIZED"){throw new UnauthorizedError}this._hasCompletedAuthFlow=true;return this.send(message)}if(response.status===403&&this._authProvider){const{resourceMetadataUrl,scope,error:error2}=extractWWWAuthenticateParams(response);if(error2==="insufficient_scope"){const wwwAuthHeader=response.headers.get("WWW-Authenticate");if(this._lastUpscopingHeader===wwwAuthHeader){throw new StreamableHTTPError(403,"Server returned 403 after trying upscoping")}if(scope){this._scope=scope}if(resourceMetadataUrl){this._resourceMetadataUrl=resourceMetadataUrl}this._lastUpscopingHeader=wwwAuthHeader??void 0;const result=await auth(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch});if(result!=="AUTHORIZED"){throw new UnauthorizedError}return this.send(message)}}throw new StreamableHTTPError(response.status,`Error POSTing to endpoint: ${text}`)}this._hasCompletedAuthFlow=false;this._lastUpscopingHeader=void 0;if(response.status===202){await response.body?.cancel();if(isInitializedNotification(message)){this._startOrAuthSse({resumptionToken:void 0}).catch(err=>this.onerror?.(err))}return}const messages=Array.isArray(message)?message:[message];const hasRequests=messages.filter(msg=>"method"in msg&&"id"in msg&&msg.id!==void 0).length>0;const contentType2=response.headers.get("content-type");const responseMediaType=mediaTypeEssence(contentType2);if(hasRequests){if(responseMediaType==="text/event-stream"){this._handleSseStream(response.body,{onresumptiontoken},false)}else if(responseMediaType==="application/json"){const data=await response.json();const responseMessages=Array.isArray(data)?data.map(msg=>JSONRPCMessageSchema.parse(msg)):[JSONRPCMessageSchema.parse(data)];for(const msg of responseMessages){this.onmessage?.(msg)}}else{await response.body?.cancel();throw new StreamableHTTPError(-1,`Unexpected content type: ${contentType2}`)}}else{await response.body?.cancel()}}catch(error2){this.onerror?.(error2);throw error2}}get sessionId(){return this._sessionId}async terminateSession(){if(!this._sessionId){return}try{const headers=await this._commonHeaders();const init={...this._requestInit,method:"DELETE",headers,signal:this._abortController?.signal};const response=await(this._fetch??fetch)(this._url,init);await response.body?.cancel();if(!response.ok&&response.status!==405){throw new StreamableHTTPError(response.status,`Failed to terminate session: ${response.statusText}`)}this._sessionId=void 0}catch(error2){this.onerror?.(error2);throw error2}}setProtocolVersion(version2){this._protocolVersion=version2}get protocolVersion(){return this._protocolVersion}async resumeStream(lastEventId,options){await this._startOrAuthSse({resumptionToken:lastEventId,onresumptiontoken:options?.onresumptiontoken})}};var MCP_TOOL_DISCOVERY_PROTOCOL=2;var MCP_TOOL_DISCOVERY_LIMITS={maxTotalTimeMs:1e4,maxRequestTimeMs:5e3,maxResponseBytes:1048576,maxPages:20,maxTools:500,maxCursorBytes:2048,maxToolNameBytes:256};function parseMcpToolDiscoveryArguments(args){if(args.length!==4||args[0]!=="--url"||args[2]!=="--credential-env"){throw new Error("invalid arguments")}const url2=new URL(args[1]);const credentialEnv=args[3];if(url2.protocol!=="https:"||url2.username!==""||url2.password!==""||url2.hash!==""||!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)){throw new Error("invalid arguments")}return{url:url2,credentialEnv}}function buildMcpToolDiscoveryAuthorizationPlaceholder(credentialEnv,runtimeValue){if(!/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u.test(credentialEnv)||runtimeValue===void 0){return null}const escapedCredentialEnv=credentialEnv.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&");const placeholderPattern=new RegExp(`^openshell:resolve:env:(?:v[0-9]{1,20}_)?${escapedCredentialEnv}$`,"u");return placeholderPattern.test(runtimeValue)?`Bearer ${runtimeValue}`:null}function normalizeMcpToolPage(page){return{tools:page.tools,...page.nextCursor!==void 0?{nextCursor:page.nextCursor}:{}}}var ToolDiscoveryRuntimeError=class extends Error{code;httpStatus;constructor(code,httpStatus){super(code);this.name="ToolDiscoveryRuntimeError";this.code=code;this.httpStatus=httpStatus}};function utf8Bytes(value){return new TextEncoder().encode(value).byteLength}function compareNames(left,right){return leftright?1:0}var UNSAFE_PROTOCOL_TEXT=/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/u;function validToolName(name){return typeof name==="string"&&name.length>0&&utf8Bytes(name)<=MCP_TOOL_DISCOVERY_LIMITS.maxToolNameBytes&&!UNSAFE_PROTOCOL_TEXT.test(name)}function validateCursor(cursor){return typeof cursor==="string"&&cursor.length>0&&utf8Bytes(cursor)<=MCP_TOOL_DISCOVERY_LIMITS.maxCursorBytes&&!UNSAFE_PROTOCOL_TEXT.test(cursor)}function truncatedResult(tools,detail){const sorted=[...tools].sort(compareNames);return{ok:false,count:sorted.length,tools:sorted,truncated:true,detail,failedStage:"tool-discovery",failureClass:"tool-operation"}}async function enumerateMcpToolNames(loadPage){const names=[];const seenNames=new Set;const seenCursors=new Set;let cursor;for(let pageNumber=1;pageNumber<=MCP_TOOL_DISCOVERY_LIMITS.maxPages;pageNumber+=1){const page=await loadPage(cursor);if(!page||!Array.isArray(page.tools)){throw new ToolDiscoveryRuntimeError("invalid-response")}for(const tool of page.tools){if(!tool||!validToolName(tool.name)||seenNames.has(tool.name)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenNames.add(tool.name);if(names.lengthMCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit`)}const sorted=[...names].sort(compareNames);return{ok:true,count:sorted.length,tools:sorted,truncated:false}}if(!validateCursor(nextCursor)||seenCursors.has(nextCursor)){throw new ToolDiscoveryRuntimeError("invalid-response")}seenCursors.add(nextCursor);cursor=nextCursor;if(seenNames.size>=MCP_TOOL_DISCOVERY_LIMITS.maxTools){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxTools}-tool safety limit before pagination completed`)}if(pageNumber===MCP_TOOL_DISCOVERY_LIMITS.maxPages){return truncatedResult(names,`tool discovery reached the ${MCP_TOOL_DISCOVERY_LIMITS.maxPages}-page safety limit`)}}throw new ToolDiscoveryRuntimeError("invalid-response")}async function runMcpToolDiscoverySession(session){let failedStage="initialization";try{await session.connect();failedStage="tool-discovery";session.publishResult(await enumerateMcpToolNames(session.loadPage))}catch(error2){session.publishResult(mcpToolDiscoveryFailure(error2,failedStage))}finally{if(session.hasSession()){try{await session.terminateSession()}catch{}}try{await session.close()}catch{}}}function combinedSignal(left,right){return left?AbortSignal.any([left,right]):right}function boundedFetchError(error2,deadlineSignal){return deadlineSignal.aborted||error2 instanceof Error&&error2.name==="AbortError"?new ToolDiscoveryRuntimeError("timeout"):new ToolDiscoveryRuntimeError("connection")}function createBoundedMcpFetch(fetchImpl,deadlineSignal){let responseBytes=0;return async(input,init={})=>{let response;try{response=await fetchImpl(input,{...init,redirect:"manual",signal:combinedSignal(init.signal,deadlineSignal)})}catch(error2){throw boundedFetchError(error2,deadlineSignal)}if(response.status>=300&&response.status<400){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("redirect")}if(response.status<200||response.status>=300){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("http-error",response.status)}const contentLength=response.headers.get("content-length");if(contentLength!==null&&/^\d+$/u.test(contentLength)){const declaredBytes=Number(contentLength);if(!Number.isSafeInteger(declaredBytes)||responseBytes+declaredBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await response.body?.cancel();throw new ToolDiscoveryRuntimeError("response-too-large")}}if(!response.body)return response;const reader=response.body.getReader();const boundedBody=new ReadableStream({async pull(controller){try{const{value,done}=await reader.read();if(done){controller.close();return}responseBytes+=value.byteLength;if(responseBytes>MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes){await reader.cancel();controller.error(new ToolDiscoveryRuntimeError("response-too-large"));return}controller.enqueue(value)}catch(error2){controller.error(boundedFetchError(error2,deadlineSignal))}},cancel(reason){return reader.cancel(reason)}});return new Response(boundedBody,{status:response.status,statusText:response.statusText,headers:response.headers})}}function safeToolDiscoveryErrorDetail(error2){if(error2 instanceof ToolDiscoveryRuntimeError){switch(error2.code){case"connection":return"MCP endpoint connection failed";case"http-error":return typeof error2.httpStatus==="number"?`MCP endpoint rejected the request (HTTP ${error2.httpStatus})`:"MCP endpoint rejected the request";case"invalid-response":return"MCP endpoint returned an invalid tool-list response";case"redirect":return"MCP endpoint redirect was rejected";case"response-too-large":return`MCP responses exceeded the ${MCP_TOOL_DISCOVERY_LIMITS.maxResponseBytes}-byte safety limit`;case"timeout":return`MCP request timed out after ${MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs/1e3}s`}}return"MCP request failed"}function mcpToolDiscoveryFailure(error2,failedStage){let failureClass;if(error2 instanceof ToolDiscoveryRuntimeError){if(error2.code==="connection"||error2.code==="timeout"){failureClass="connection"}else if(error2.code==="http-error"&&(error2.httpStatus===401||error2.httpStatus===403)){failureClass="authentication"}else{failureClass="protocol"}}else{failureClass=failedStage==="initialization"?"protocol":"tool-operation"}return{ok:false,count:0,tools:[],truncated:false,detail:safeToolDiscoveryErrorDetail(error2),failedStage,failureClass}}function writeResult(result){process.stdout.write(`${JSON.stringify({protocol:MCP_TOOL_DISCOVERY_PROTOCOL,...result})} +`)}function normalizeMcpSdkError(error2){return error2 instanceof McpError&&error2.code===ErrorCode.RequestTimeout?new ToolDiscoveryRuntimeError("timeout"):error2}async function callMcpSdk(operation){try{return await operation()}catch(error2){throw normalizeMcpSdkError(error2)}}async function main(){let runtimeArguments;try{runtimeArguments=parseMcpToolDiscoveryArguments(process.argv.slice(2))}catch{writeResult({ok:false,count:0,tools:[],truncated:false,detail:"tool discovery received invalid runtime arguments",failedStage:"preflight",failureClass:"precondition"});return}const deadlineSignal=AbortSignal.timeout(MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs);const boundedFetch=createBoundedMcpFetch(globalThis.fetch,deadlineSignal);const authorization=buildMcpToolDiscoveryAuthorizationPlaceholder(runtimeArguments.credentialEnv,process.env[runtimeArguments.credentialEnv]);if(!authorization){writeResult({ok:false,count:0,tools:[],truncated:false,detail:"managed MCP credential placeholder is unavailable",failedStage:"preflight",failureClass:"precondition"});return}const transport=new StreamableHTTPClientTransport(runtimeArguments.url,{fetch:boundedFetch,requestInit:{headers:{authorization},redirect:"manual"},reconnectionOptions:{maxReconnectionDelay:1,initialReconnectionDelay:1,reconnectionDelayGrowFactor:1,maxRetries:0}});const client=new Client({name:"nemoclaw-mcp-tool-discovery",version:"1.0.0"},{capabilities:{}});const requestOptions={signal:deadlineSignal,timeout:MCP_TOOL_DISCOVERY_LIMITS.maxRequestTimeMs,maxTotalTimeout:MCP_TOOL_DISCOVERY_LIMITS.maxTotalTimeMs};await runMcpToolDiscoverySession({connect:()=>callMcpSdk(()=>client.connect(transport,requestOptions)),loadPage:cursor=>callMcpSdk(async()=>{const page=await client.listTools(cursor?{cursor}:void 0,requestOptions);return normalizeMcpToolPage(page)}),hasSession:()=>Boolean(transport.sessionId),terminateSession:()=>transport.terminateSession(),close:()=>client.close(),publishResult:writeResult})}var entrypointPath=process.argv[1];if(entrypointPath&&realpathSync(fileURLToPath(import.meta.url))===realpathSync(entrypointPath)){await main()}export{normalizeMcpSdkError}; /*! Bundled license information: content-type/index.js: diff --git a/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts b/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts index a142ed84918..3d858660d32 100644 --- a/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts +++ b/tools/mcp-tool-discovery-runtime/streamable-http-client.test.ts @@ -19,7 +19,7 @@ import { normalizeMcpToolPage, runMcpToolDiscoverySession, } from "./tool-discovery-core.ts"; -import { normalizeMcpSdkError } from "./mcp-sdk-error.ts"; +import { normalizeMcpSdkError } from "./mcp-tool-discovery.ts"; test("classifies only the SDK request-timeout code as a remote request timeout (#10944)", () => { const timeout = mcpToolDiscoveryFailure(