paratix 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/output.ts","../src/outputFormatting.ts","../src/signalOrchestration.ts"],"sourcesContent":["/* eslint-disable max-lines -- CLI output rendering is intentionally kept together */\nimport pc from \"picocolors\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\nimport { fitAnimatedModuleLine, formatDisplayModule } from \"./outputFormatting.js\"\nimport { CommandError } from \"./sshHelpers.js\"\n\nconst MODULE_NAME_WIDTH = 56\nconst MIN_MODULE_NAME_WIDTH = 12\nconst OUTPUT_INDENT_UNIT = \" \"\nconst SPINNER_FRAME_INTERVAL_MS = 80\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst STATUS_ICONS: Record<DisplayStatus, string> = {\n changed: pc.yellow(\"\\u21ba\"),\n failed: pc.red(\"\\u2717\"),\n ok: pc.green(\"\\u2713\"),\n skipped: pc.dim(\"\\u2298\"),\n waiting: pc.cyan(\"\\u23f8\"),\n}\n\nconst CLI_HEADER_LINES = [\n \" _ _ \",\n \" | | (_) \",\n \" _ __ __ _ _ __ __ _| |_ ___ __\",\n \" | '_ \\\\ / _` | '__/ _` | __| \\\\ \\\\/ /\",\n \" | |_) | (_| | | | (_| | |_| |> < \",\n \" | .__/ \\\\__,_|_| \\\\__,_|\\\\__|_/_/\\\\_\\\\\",\n \" | | \",\n \" |_| \",\n]\n\ntype ActiveSpinner = {\n detail?: string\n frameIndex: number\n interval: NodeJS.Timeout\n}\n\nlet activeSpinner: ActiveSpinner | null = null\nlet recipeOutputDepth = -1\n\nexport function renderCliHeader(version: string): string {\n const versionText = pc.dim(`v${version}`)\n return `${pc.cyan(CLI_HEADER_LINES.join(\"\\n\"))}${versionText}\\n`\n}\n\nexport function printCliHeader(version: string): void {\n console.log(renderCliHeader(version))\n}\n\nfunction supportsAnimatedModuleOutput(): boolean {\n return (\n process.stdout.isTTY &&\n typeof process.stdout.clearLine === \"function\" &&\n typeof process.stdout.cursorTo === \"function\"\n )\n}\n\nfunction getModuleIcon(status: DisplayStatus, waitingFrame?: string): string {\n return status === \"waiting\" ? pc.cyan(waitingFrame ?? \"|\") : STATUS_ICONS[status]\n}\n\nfunction getModuleStatusText(status: DisplayStatus): string {\n switch (status) {\n case \"changed\": {\n return pc.yellow(status)\n }\n case \"failed\": {\n return pc.red(status)\n }\n case \"ok\": {\n return pc.green(status)\n }\n case \"skipped\": {\n return pc.dim(status)\n }\n case \"waiting\": {\n return pc.cyan(\"running\")\n }\n }\n}\n\nfunction getCurrentOutputDepth(): number {\n return Math.max(recipeOutputDepth, 0)\n}\n\nfunction getModuleIndent(): string {\n if (recipeOutputDepth < 0) {\n return OUTPUT_INDENT_UNIT\n }\n\n return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2)\n}\n\nfunction getRecipeHeaderIndent(): string {\n return recipeOutputDepth < 0 ? \"\" : OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1)\n}\n\nfunction getErrorIndent(): string {\n return `${getModuleIndent()}│ `\n}\n\nfunction getContinuationIndent(): string {\n return `${getModuleIndent()} `\n}\n\nexport async function withRecipeOutputScope<T>(\n scopedOperation: () => Promise<T> | T\n): Promise<T> {\n recipeOutputDepth += 1\n try {\n return await scopedOperation()\n } finally {\n recipeOutputDepth -= 1\n }\n}\n\nfunction renderModuleLine(parameters: {\n detail?: string\n name: string\n status: DisplayStatus\n waitingFrame?: string\n}): string {\n const { detail, name, status, waitingFrame } = parameters\n const indent = getModuleIndent()\n const icon = getModuleIcon(status, waitingFrame)\n const statusText = getModuleStatusText(status)\n const detailSuffix = detail == null ? \"\" : ` ${pc.dim(detail)}`\n const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - indent.length, MIN_MODULE_NAME_WIDTH)\n return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`\n}\n\nfunction writeAnimatedModuleLine(line: string): void {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n process.stdout.write(fitAnimatedModuleLine(line, process.stdout.columns))\n}\n\nfunction stopAnimatedModuleLine(clearCurrentLine = false): void {\n if (activeSpinner == null) return\n\n clearInterval(activeSpinner.interval)\n activeSpinner = null\n\n if (clearCurrentLine && supportsAnimatedModuleOutput()) {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n }\n}\n\nexport function startModuleSpinner(name: string, detail?: string): void {\n if (!supportsAnimatedModuleOutput()) return\n\n stopAnimatedModuleLine()\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail,\n name,\n status: \"waiting\",\n terminalColumns: process.stdout.columns,\n })\n\n const spinner: ActiveSpinner = {\n detail: displayModule.detail,\n frameIndex: 0,\n interval: setInterval(() => {\n spinner.frameIndex = (spinner.frameIndex + 1) % SPINNER_FRAMES.length\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: spinner.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[spinner.frameIndex],\n })\n )\n }, SPINNER_FRAME_INTERVAL_MS),\n }\n\n activeSpinner = spinner\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: displayModule.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[0],\n })\n )\n}\n\nexport function resetLiveOutputForTests(): void {\n stopAnimatedModuleLine()\n recipeOutputDepth = -1\n}\n\n/**\n * Print a bold, colored header line marking the start of a recipe run.\n * @param name - The recipe or server name to display.\n */\nexport function printRecipeHeader(name: string): void {\n stopAnimatedModuleLine(true)\n const header = pc.bold(pc.blue(`[${name}]`))\n console.log(`${getRecipeHeaderIndent()}${header}`)\n}\n\nexport function printRunContext(parameters: {\n dryRun: boolean\n host: string\n name: string\n ports: number[]\n}): void {\n const mode = parameters.dryRun ? pc.yellow(\"dry-run\") : pc.green(\"apply\")\n const ports = parameters.ports.join(\", \")\n console.log(\n pc.dim(`Run ${parameters.name} · host ${parameters.host} · ports ${ports} · mode ${mode}`)\n )\n}\n\n/**\n * Print a single module result row with a status icon, name, and colored status label.\n *\n * @param name - The module name shown in the left column.\n * @param status - One of the known status strings (`ok`, `changed`, `skipped`, `failed`).\n * @param detail - Optional short detail appended in dim text after the status.\n */\nexport function printModuleResult(name: string, status: DisplayStatus, detail?: string): void {\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail,\n name,\n status,\n terminalColumns: process.stdout.columns,\n })\n const line = renderModuleLine({\n detail: displayModule.detail,\n name: displayModule.name,\n status,\n })\n if (supportsAnimatedModuleOutput() && activeSpinner != null) {\n stopAnimatedModuleLine()\n writeAnimatedModuleLine(line)\n process.stdout.write(\"\\n\")\n for (const detailLine of displayModule.detailLines) {\n process.stdout.write(`${getContinuationIndent()}${pc.dim(detailLine)}\\n`)\n }\n return\n }\n\n console.log(line)\n for (const detailLine of displayModule.detailLines) {\n console.log(`${getContinuationIndent()}${pc.dim(detailLine)}`)\n }\n}\n\n/**\n * Print captured stderr and stdout from a failed command in a red bordered block.\n * Outputs nothing if both streams are empty.\n *\n * @param stdout - Captured standard output of the failed command.\n * @param stderr - Captured standard error of the failed command.\n */\nexport function printCommandError(stdout: string, stderr: string): void {\n const lines: string[] = []\n if (stderr.trim()) {\n lines.push(...stderr.trim().split(\"\\n\"))\n }\n if (stdout.trim()) {\n lines.push(...stdout.trim().split(\"\\n\"))\n }\n if (lines.length > 0) {\n console.error(pc.red(`${getErrorIndent()}Error output:`))\n for (const line of lines) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\n/**\n * Print the full (untruncated) stdout and stderr of a failed command.\n * Used in verbose mode to show the complete output that was truncated in the error message.\n *\n * @param stdout - Full standard output of the failed command.\n * @param stderr - Full standard error of the failed command.\n */\nexport function printVerboseCommandError(stdout: string, stderr: string): void {\n if (stderr.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stderr:`))\n for (const line of stderr.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n if (stdout.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stdout:`))\n for (const line of stdout.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\nfunction printVerboseErrorBlock(label: string, content: string): void {\n if (!content.trim()) {\n return\n }\n\n console.error(pc.red(`${getErrorIndent()}${label}`))\n for (const line of content.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n}\n\nfunction getErrorCause(error: Error): unknown {\n return (error as { cause?: unknown } & Error).cause\n}\n\nfunction printVerboseErrorCause(cause: unknown, depth: number): void {\n const label = `Cause ${depth}:`\n if (cause instanceof Error) {\n const stack = cause.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(cause)\n printVerboseErrorBlock(label, stackOrMessage)\n const nestedCause = getErrorCause(cause)\n if (nestedCause !== undefined) {\n printVerboseErrorCause(nestedCause, depth + 1)\n }\n return\n }\n\n printVerboseErrorBlock(label, String(cause))\n}\n\nfunction printVerboseGenericError(error: Error): void {\n const stack = error.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(error)\n printVerboseErrorBlock(\"Full stack:\", stackOrMessage)\n const cause = getErrorCause(error)\n if (cause !== undefined) {\n printVerboseErrorCause(cause, 1)\n }\n}\n\n/**\n * Print the error message of a failed command and, when verbose mode is active\n * and the error is a {@link CommandError}, the full untruncated output.\n *\n * @param error - The caught error value.\n * @param verbose - Whether to show full stdout/stderr.\n */\nexport function printCommandFailure(error: unknown, verbose: boolean): void {\n if (verbose && error instanceof CommandError) {\n // Print only the exit-code line, skip the truncated output and hint\n const summaryLine = error.message.split(\"\\n\")[0]\n printCommandError(\"\", summaryLine)\n printVerboseCommandError(error.fullStdout, error.fullStderr)\n return\n }\n\n printCommandError(\"\", String(error))\n if (verbose && error instanceof Error) {\n printVerboseGenericError(error)\n }\n}\n\n/**\n * Print a run summary line with counts for each status category.\n *\n * @param stats - Aggregated counts from the completed run.\n * @param stats.changed - Number of modules that changed state.\n * @param stats.ok - Number of modules already in desired state.\n * @param stats.skipped - Number of modules skipped.\n * @param stats.failed - Number of modules that failed.\n * @param stats.signals - Number of signals triggered.\n */\nexport function printSummary(stats: {\n changed: number\n failed: number\n ok: number\n signals: number\n skipped: number\n}): void {\n const parts = [\n pc.yellow(`${stats.changed} changed`),\n pc.green(`${stats.ok} ok`),\n pc.dim(`${stats.skipped} skipped`),\n stats.failed > 0 ? pc.red(`${stats.failed} failed`) : `${stats.failed} failed`,\n pc.cyan(`${stats.signals} signals triggered`),\n ]\n console.log(`\\n${parts.join(pc.dim(\" \\u00b7 \"))}`)\n}\n","import { stripVTControlCharacters } from \"node:util\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst PACKAGE_MODULE_SUFFIX_LENGTH = 2\nconst PACKAGE_COLUMN_GAP_WIDTH = 2\nconst DEFAULT_TERMINAL_COLUMNS = 100\nconst MIN_PACKAGE_COLUMNS_WIDTH = 24\nconst MIN_PACKAGE_COLUMN_WIDTH = 18\nconst MIN_ANIMATED_LINE_COLUMNS = 8\n\nexport type DisplayModule = {\n detail?: string\n detailLines: string[]\n name: string\n}\n\nfunction splitPackageModuleName(name: string): { packages: string[]; summaryName: string } | null {\n for (const prefix of [\"package.installed: \", \"package.absent: \"]) {\n if (!name.startsWith(prefix)) continue\n\n const packages = name\n .slice(prefix.length)\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean)\n if (packages.length === 0) return null\n\n return {\n packages,\n summaryName: prefix.slice(0, -PACKAGE_MODULE_SUFFIX_LENGTH),\n }\n }\n\n return null\n}\n\nfunction getPackageColumns(parameters: {\n continuationIndentWidth: number\n packages: string[]\n terminalColumns?: number\n}): string[] {\n if (parameters.packages.length <= 1) return parameters.packages\n\n const availableWidth = Math.max(\n (parameters.terminalColumns ?? DEFAULT_TERMINAL_COLUMNS) - parameters.continuationIndentWidth,\n MIN_PACKAGE_COLUMNS_WIDTH\n )\n const widestPackage = Math.max(...parameters.packages.map((entry) => entry.length))\n const columnWidth = Math.max(widestPackage + PACKAGE_COLUMN_GAP_WIDTH, MIN_PACKAGE_COLUMN_WIDTH)\n const columnCount = Math.max(Math.floor(availableWidth / columnWidth), 1)\n const rowCount = Math.ceil(parameters.packages.length / columnCount)\n\n return Array.from({ length: rowCount }, (_row, rowIndex) =>\n Array.from({ length: columnCount }, (_column, columnIndex) => {\n const packageIndex = rowIndex + columnIndex * rowCount\n const packageName = parameters.packages.at(packageIndex)\n if (packageName == null) return \"\"\n const isLastVisibleColumn =\n columnIndex === columnCount - 1 || packageIndex + rowCount >= parameters.packages.length\n return isLastVisibleColumn ? packageName : packageName.padEnd(columnWidth)\n })\n .filter(Boolean)\n .join(\"\")\n )\n}\n\nfunction getPackageSummaryDetail(packageCount: number, detail?: string): string {\n const packageLabel = packageCount === 1 ? \"1 package\" : `${packageCount} packages`\n return detail == null ? packageLabel : `${packageLabel} · ${detail}`\n}\n\nexport function fitAnimatedModuleLine(line: string, columns?: number): string {\n if (columns === undefined || columns < MIN_ANIMATED_LINE_COLUMNS) return line\n\n const plainLine = stripVTControlCharacters(line)\n if (plainLine.length < columns) return line\n\n return `${plainLine.slice(0, columns - 1)}\\u2026`\n}\n\nexport function formatDisplayModule(parameters: {\n continuationIndentWidth: number\n detail?: string\n name: string\n status: DisplayStatus\n terminalColumns?: number\n}): DisplayModule {\n const packageModule = splitPackageModuleName(parameters.name)\n if (packageModule == null) {\n return {\n detail: parameters.detail,\n detailLines: [],\n name: parameters.name,\n }\n }\n\n return {\n detail: getPackageSummaryDetail(packageModule.packages.length, parameters.detail),\n detailLines:\n parameters.status === \"waiting\"\n ? []\n : getPackageColumns({\n continuationIndentWidth: parameters.continuationIndentWidth,\n packages: packageModule.packages,\n terminalColumns: parameters.terminalColumns,\n }),\n name: packageModule.summaryName,\n }\n}\n","import type {\n Environment,\n Module,\n ModuleStatus,\n OrchestrationStep,\n SshConnection,\n} from \"./types.js\"\n\nimport { assertValidModuleMetaEntries, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport { printCommandFailure, printModuleResult, startModuleSpinner } from \"./output.js\"\n\nexport type SignalHooks = {\n onSignalFinished?: (status: ModuleStatus) => void\n onSignalStarted?: () => void\n}\n\nexport type SignalRunStatus = \"changed\" | \"failed\"\n\ntype SignalRunParameters = {\n environment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}\n\nfunction handleSignalResult(parameters: {\n hooks?: SignalHooks\n result: Awaited<ReturnType<Module[\"apply\"]>>\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { hooks, result, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, result.status)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n hooks?.onSignalFinished?.(result.status)\n return result.status === \"failed\" ? \"failed\" : \"changed\"\n}\n\nfunction handleSignalFailure(parameters: {\n error: unknown\n hooks?: SignalHooks\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { error, hooks, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, \"failed\")\n printCommandFailure(error, verbose)\n hooks?.onSignalFinished?.(\"failed\")\n return \"failed\"\n}\n\nasync function applySignalMeta(parameters: {\n currentEnvironment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n result: Awaited<ReturnType<Module[\"apply\"]>>\n}): Promise<Environment> {\n assertValidModuleMetaEntries(parameters.result.meta)\n const nextEnvironment = await mergeEnvironmentFromMeta(\n parameters.currentEnvironment,\n parameters.result.meta\n )\n await parameters.onSignalStep?.({\n env: nextEnvironment,\n meta: parameters.result.meta,\n status: parameters.result.status,\n })\n return nextEnvironment\n}\n\nasync function runOneSignal(parameters: {\n currentEnvironment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n signal: Module\n ssh: null | SshConnection\n verbose: boolean\n}): Promise<{ nextEnvironment: Environment; status: SignalRunStatus }> {\n const connection = parameters.signal.local === true ? null : parameters.ssh\n startModuleSpinner(`signal: ${parameters.signal.name}`)\n const result = await parameters.signal.apply(connection, parameters.currentEnvironment)\n const nextEnvironment = await applySignalMeta({\n currentEnvironment: parameters.currentEnvironment,\n onSignalStep: parameters.onSignalStep,\n result,\n })\n return {\n nextEnvironment,\n status: handleSignalResult({\n hooks: parameters.hooks,\n result,\n signalName: parameters.signal.name,\n verbose: parameters.verbose,\n }),\n }\n}\n\nexport async function runSignalModules(parameters: SignalRunParameters): Promise<SignalRunStatus> {\n const getShutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let currentEnvironment = parameters.environment\n let status: SignalRunStatus = \"changed\"\n\n for (const signal of parameters.signals) {\n if (getShutdownSignal() != null) break\n parameters.hooks?.onSignalStarted?.()\n try {\n // eslint-disable-next-line no-await-in-loop\n const signalStep = await runOneSignal({\n currentEnvironment,\n hooks: parameters.hooks,\n onSignalStep: parameters.onSignalStep,\n signal,\n ssh: parameters.ssh,\n verbose,\n })\n currentEnvironment = signalStep.nextEnvironment\n if (signalStep.status === \"failed\") status = \"failed\"\n } catch (error) {\n status = handleSignalFailure({\n error,\n hooks: parameters.hooks,\n signalName: signal.name,\n verbose,\n })\n }\n }\n\n return status\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sshHelpers.ts","../src/serverDefinitionValidation.ts","../src/meta.ts","../src/ssh.ts","../src/knownHosts.ts","../src/sftp.ts","../src/terminal.ts","../src/environment.ts"],"sourcesContent":["import type { Client, ClientChannel, ConnectConfig } from \"ssh2\"\n\nimport type { ExecOptions, ExecResult } from \"./types.js\"\n\n/**\n * Validate that a file mode string is a valid octal permission (e.g. \"644\", \"0755\").\n *\n * @param mode - The mode string to validate.\n * @throws {Error} If the mode is not a 3- or 4-digit octal string.\n */\nexport function validateMode(mode: string): void {\n if (!/^[0-7]{3,4}$/v.test(mode)) {\n throw new Error(\n `Invalid file mode '${mode}': expected a 3- or 4-digit octal string (e.g. \"644\", \"0755\")`\n )\n }\n}\n\n/**\n * Safely quote a string for use in a POSIX shell command.\n * Wraps the value in single quotes and escapes any embedded single quotes.\n *\n * @param s - The string to quote.\n * @returns The shell-safe quoted string.\n */\nexport function shellQuote(s: string): string {\n return `'${s.replaceAll(\"'\", \"'\\\\''\")}'`\n}\n\nconst CONNECTION_TIMEOUT = 10_000\n\n/**\n * Maximum number of characters included in a {@link CommandError} message\n * before the output is truncated. Output beyond this limit is still available\n * on {@link CommandError.fullStdout} and {@link CommandError.fullStderr}.\n */\nexport const MAX_OUTPUT_LENGTH = 500\n\nfunction truncateOutput(text: string): string {\n let count = 0\n let sliceEnd = 0\n for (const char of text) {\n if (count >= MAX_OUTPUT_LENGTH) return `${text.slice(0, sliceEnd)}…(truncated)`\n sliceEnd += char.length\n count++\n }\n return text\n}\n\nfunction codepointLengthExceeds(text: string, limit: number): boolean {\n let count = 0\n for (const _char of text) {\n count++\n if (count > limit) return true\n }\n return false\n}\n\n/**\n * Error thrown when a remote command exits with a non-zero exit code.\n *\n * The `message` contains a truncated summary of stdout and stderr\n * (up to {@link MAX_OUTPUT_LENGTH} characters each). The full, untruncated\n * output is available on {@link CommandError.fullStdout} and {@link CommandError.fullStderr} for use\n * in verbose error reporting.\n *\n * @example\n * ```ts\n * try {\n * await ssh.exec(\"exit 1\")\n * } catch (error) {\n * if (error instanceof CommandError) {\n * console.error(error.fullStderr)\n * }\n * }\n * ```\n */\nexport class CommandError extends Error {\n /** Full, untruncated standard error of the failed command. */\n public readonly fullStderr: string\n /** Full, untruncated standard output of the failed command. */\n public readonly fullStdout: string\n\n public constructor(message: string, fullStdout: string, fullStderr: string) {\n super(message)\n this.name = \"CommandError\"\n this.fullStdout = fullStdout\n this.fullStderr = fullStderr\n Error.captureStackTrace(this, CommandError)\n }\n}\n\nexport type StreamOutputParameters = {\n command: string\n options: ExecOptions\n reject: (reason: Error) => void\n resolve: (value: ExecResult) => void\n secrets?: SecretSource[]\n stream: ClientChannel\n timer: ReturnType<typeof setTimeout>\n}\n\nexport type SecretSource = (() => string) | string\n\n/** Placeholder used when redacting secrets from output. */\nconst REDACTED = \"[REDACTED]\"\n\nfunction resolveSecret(secret: SecretSource): string {\n return typeof secret === \"function\" ? secret() : secret\n}\n\nfunction getSecretVariants(secrets: SecretSource[]): string[] {\n const variants = new Set<string>()\n for (const source of secrets) {\n const secret = resolveSecret(source)\n if (secret.length === 0) continue\n if (secret.includes(REDACTED)) {\n throw new Error(`Secret must not contain the redaction placeholder \"${REDACTED}\"`)\n }\n\n variants.add(secret)\n\n const encoded = encodeURIComponent(secret)\n // eslint-disable-next-line security/detect-possible-timing-attacks -- not a secret comparison, just checking if URL encoding changed the string\n if (encoded !== secret) variants.add(encoded)\n\n const quoted = shellQuote(secret)\n // eslint-disable-next-line security/detect-possible-timing-attacks -- not a secret comparison, just checking if shell quoting changed the string\n if (quoted !== secret) variants.add(quoted)\n }\n return [...variants].sort((a, b) => b.length - a.length)\n}\n\nfunction createLazyVariantResolver(secrets: SecretSource[]): {\n getMaxLength: () => number\n mask: (text: string) => string\n} {\n let variants: null | string[] = null\n\n const getVariants = (): string[] => {\n variants ??= getSecretVariants(secrets)\n return variants\n }\n\n return {\n getMaxLength(): number {\n return Math.max(0, ...getVariants().map((variant) => variant.length))\n },\n mask(text: string): string {\n let masked = text\n for (const variant of getVariants()) {\n masked = masked.replaceAll(variant, REDACTED)\n }\n return masked\n },\n }\n}\n\nexport function maskSecrets(text: string, secrets: SecretSource[]): string {\n let masked = text\n const variants = getSecretVariants(secrets)\n for (const variant of variants) {\n masked = masked.replaceAll(variant, REDACTED)\n }\n return masked\n}\n\n/**\n * Create a sliding-window masker that buffers up to `maxSecretLength - 1`\n * characters so that secrets split across chunk boundaries are still masked\n * in live output.\n *\n * @param write - Callback that receives masked text for live output.\n * @param secrets - List of secret strings to mask.\n * @returns An object with `push` (feed new data) and `flush` (emit remaining buffer).\n */\nexport function createStreamMasker(\n write: (text: string) => void,\n secrets: SecretSource[]\n): { flush: () => void; push: (chunk: string) => void } {\n const resolver = createLazyVariantResolver(secrets)\n let overlap: null | number = null\n const getOverlap = (): number => {\n overlap ??= Math.max(0, resolver.getMaxLength() - 1)\n return overlap\n }\n\n let pending = \"\"\n\n return {\n flush(): void {\n if (pending.length > 0) {\n write(resolver.mask(pending))\n pending = \"\"\n }\n },\n push(chunk: string): void {\n const currentOverlap = getOverlap()\n if (currentOverlap === 0) {\n write(resolver.mask(chunk))\n return\n }\n pending += chunk\n if (pending.length <= currentOverlap) return\n // Mask the whole buffer first so secrets fully contained in\n // pending are replaced before the split. The overlap is then\n // taken from the *masked* result — safe because maskSecrets()\n // rejects any secret that contains the redaction placeholder.\n const masked = resolver.mask(pending)\n if (masked.length <= currentOverlap) {\n pending = masked\n return\n }\n write(masked.slice(0, -currentOverlap))\n pending = masked.slice(-currentOverlap)\n },\n }\n}\n\nfunction writeStdout(t: string): void {\n process.stdout.write(t)\n}\nfunction writeStderr(t: string): void {\n process.stderr.write(t)\n}\n\n/**\n * Normalize ssh2 close-event exit codes. ssh2 may pass `undefined` even though\n * its TypeScript type says `number`; treat that as a successful zero exit code.\n *\n * @param code - Exit code from the ssh2 `close` event.\n * @returns A normalized numeric exit code.\n */\nexport function normalizeSshCloseCode(code: number | undefined): number {\n return code ?? 0\n}\n\n/**\n * Wire up event listeners on an ssh2 stream to collect stdout/stderr\n * and resolve or reject the promise when the stream closes.\n *\n * @param parameters - Stream collection parameters.\n */\nexport function collectStreamOutput(parameters: StreamOutputParameters): void {\n const { command, options, reject, resolve, stream, timer } = parameters\n let stdout = \"\"\n let stderr = \"\"\n\n const secrets = parameters.secrets ?? []\n const stdoutMasker = createStreamMasker((text) => {\n stdout += text\n if (!options.silent) writeStdout(text)\n }, secrets)\n const stderrMasker = createStreamMasker((text) => {\n stderr += text\n if (!options.silent) writeStderr(text)\n }, secrets)\n\n stream.on(\"data\", (data: Buffer) => {\n stdoutMasker.push(data.toString())\n })\n stream.stderr.on(\"data\", (data: Buffer) => {\n stderrMasker.push(data.toString())\n })\n stream.on(\"error\", (error: Error) => {\n clearTimeout(timer)\n stdoutMasker.flush()\n stderrMasker.flush()\n reject(error)\n })\n stream.on(\"close\", (code: number) => {\n clearTimeout(timer)\n stdoutMasker.flush()\n stderrMasker.flush()\n const exitCode = normalizeSshCloseCode(code)\n if (exitCode !== 0 && options.ignoreExitCode !== true) {\n const wasTruncated =\n codepointLengthExceeds(stdout, MAX_OUTPUT_LENGTH) ||\n codepointLengthExceeds(stderr, MAX_OUTPUT_LENGTH)\n const hint = wasTruncated ? \"\\n(use --verbose for full output)\" : \"\"\n reject(\n new CommandError(\n `Command failed with exit code ${exitCode}: ${maskSecrets(command, secrets)}\\nstdout: ${truncateOutput(stdout)}\\nstderr: ${truncateOutput(stderr)}${hint}`,\n stdout,\n stderr\n )\n )\n return\n }\n resolve({ code: exitCode, stderr, stdout })\n })\n}\n\n/** Parameters for a single SSH connection attempt on one port. */\nexport type ConnectParameters = {\n /** Path to the SSH agent socket (e.g. `SSH_AUTH_SOCK`). Used when no `privateKey` is provided. */\n agent?: string\n /** Forward the local SSH agent to the remote host during this session. */\n agentForward?: boolean\n /** The ssh2 `Client` instance to connect with. */\n client: Client\n /** Hostname or IP address of the remote host. */\n host: string\n /** Optional host key verifier callback for known_hosts checking. */\n hostVerifier?: (key: Buffer) => boolean\n /** Password for keyboard-interactive or password authentication. */\n password?: string\n /** Port to connect on. */\n port: number\n /** PEM-encoded private key content. Mutually exclusive with `agent`. */\n privateKey?: Buffer | string\n /** Username to authenticate as. */\n username: string\n}\n\n/**\n * Build the ssh2 `ConnectConfig` from the connection parameters.\n *\n * @param parameters - Connection parameters.\n * @returns The populated config object.\n */\nfunction buildConnectConfig(parameters: ConnectParameters): ConnectConfig {\n const { agent, agentForward, host, hostVerifier, password, port, privateKey, username } =\n parameters\n const connectConfig: ConnectConfig = {\n host,\n port,\n readyTimeout: CONNECTION_TIMEOUT,\n username,\n }\n if (privateKey != null) {\n connectConfig.privateKey = privateKey\n }\n if (agent != null) {\n connectConfig.agent = agent\n }\n if (agentForward === true) {\n connectConfig.agentForward = true\n }\n if (typeof password === \"string\") {\n connectConfig.password = password\n connectConfig.tryKeyboard = true\n }\n if (hostVerifier != null) {\n connectConfig.hostVerifier = hostVerifier\n }\n return connectConfig\n}\n\n/**\n * Attempt a single SSH connection on a specific port.\n *\n * @param parameters - Connection parameters.\n */\nexport async function tryConnectOnPort(parameters: ConnectParameters): Promise<void> {\n const { client, port } = parameters\n const connectConfig = buildConnectConfig(parameters)\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n client.end()\n reject(new Error(`Connection timeout on port ${port}`))\n }, CONNECTION_TIMEOUT)\n\n client.on(\"ready\", () => {\n clearTimeout(timeout)\n resolve()\n })\n client.on(\"error\", (error: Error) => {\n clearTimeout(timeout)\n reject(error)\n })\n\n client.connect(connectConfig)\n })\n}\n","import type { SshConfig } from \"./types.js\"\n\nconst STRICT_HOST_KEY_ERROR = `Invalid property 'ssh.strictHostKeyChecking' (expected \"accept-new\", \"no\", or \"yes\")`\nconst MAX_TCP_PORT = 65_535\n\ntype NumberValidationOptions = {\n integer?: boolean\n positive?: boolean\n}\n\nfunction describeType(value: unknown): string {\n return value === null ? \"null\" : typeof value\n}\n\nfunction isHostKeyMode(value: string): boolean {\n return value === \"accept-new\" || value === \"no\" || value === \"yes\"\n}\n\nfunction isRecord(value: object): value is Record<string, unknown> {\n return Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null\n}\n\nfunction collectOptionalBooleanErrors(\n object: Record<string, unknown>,\n key: string,\n errors: string[]\n): void {\n if (!(key in object) || object[key] == null) return\n if (typeof object[key] !== \"boolean\") {\n errors.push(\n `Invalid property 'ssh.${key}' (expected boolean, got ${describeType(object[key])})`\n )\n }\n}\n\nfunction collectOptionalNumberErrors(parameters: {\n errors: string[]\n key: string\n object: Record<string, unknown>\n options?: NumberValidationOptions\n}): void {\n const { errors, key, object, options } = parameters\n if (!(key in object) || object[key] == null) return\n const value = object[key]\n const validatedNumber = validateNumberValue(value)\n if (validatedNumber == null) {\n errors.push(`Invalid property 'ssh.${key}' (expected number, got ${describeType(value)})`)\n return\n }\n if (options?.integer === true && !Number.isInteger(validatedNumber)) {\n errors.push(`Property 'ssh.${key}' must be an integer`)\n return\n }\n if (options?.positive === true && validatedNumber <= 0) {\n errors.push(`Property 'ssh.${key}' must be greater than 0`)\n }\n}\n\nfunction validateNumberValue(value: unknown): null | number {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null\n}\n\nexport function isValidTcpPort(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 1 && value <= MAX_TCP_PORT\n}\n\nfunction collectOptionalStringErrors(\n object: Record<string, unknown>,\n key: string,\n errors: string[]\n): void {\n if (!(key in object) || object[key] == null) return\n if (typeof object[key] !== \"string\") {\n errors.push(`Invalid property 'ssh.${key}' (expected string, got ${describeType(object[key])})`)\n return\n }\n if (object[key].length === 0) {\n errors.push(`Property 'ssh.${key}' must not be an empty string`)\n }\n}\n\nfunction collectPortsErrors(ssh: Record<string, unknown>, errors: string[]): void {\n if (!(\"ports\" in ssh)) {\n errors.push(\"Missing property 'ssh.ports' (expected array)\")\n return\n }\n if (!Array.isArray(ssh.ports)) {\n errors.push(`Invalid property 'ssh.ports' (expected array, got ${describeType(ssh.ports)})`)\n return\n }\n if (ssh.ports.length === 0) {\n errors.push(\"Property 'ssh.ports' must not be empty\")\n return\n }\n for (const [index, port] of ssh.ports.entries()) {\n if (!isValidTcpPort(port)) {\n errors.push(`Property 'ssh.ports[${index}]' must be an integer between 1 and 65535`)\n }\n }\n}\n\nfunction collectRequiredUserErrors(ssh: Record<string, unknown>, errors: string[]): void {\n if (!(\"user\" in ssh)) {\n errors.push(\"Missing property 'ssh.user' (expected string)\")\n return\n }\n if (typeof ssh.user !== \"string\") {\n errors.push(`Invalid property 'ssh.user' (expected string, got ${describeType(ssh.user)})`)\n return\n }\n if (ssh.user.length === 0) {\n errors.push(\"Property 'ssh.user' must not be an empty string\")\n }\n}\n\nfunction collectStrictHostKeyCheckingErrors(ssh: Record<string, unknown>, errors: string[]): void {\n if (!(\"strictHostKeyChecking\" in ssh) || ssh.strictHostKeyChecking == null) return\n if (typeof ssh.strictHostKeyChecking !== \"string\" || !isHostKeyMode(ssh.strictHostKeyChecking)) {\n errors.push(STRICT_HOST_KEY_ERROR)\n }\n}\n\nfunction collectOptionalSshFieldErrors(ssh: Record<string, unknown>, errors: string[]): void {\n collectOptionalStringErrors(ssh, \"privateKey\", errors)\n collectOptionalStringErrors(ssh, \"expectedHostFingerprint\", errors)\n collectOptionalStringErrors(ssh, \"expectedHostPublicKey\", errors)\n collectOptionalBooleanErrors(ssh, \"agentForward\", errors)\n collectOptionalBooleanErrors(ssh, \"passwordFallback\", errors)\n collectOptionalNumberErrors({\n errors,\n key: \"reconnectTimeout\",\n object: ssh,\n options: { positive: true },\n })\n collectOptionalNumberErrors({\n errors,\n key: \"maxReconnectAttempts\",\n object: ssh,\n options: { integer: true, positive: true },\n })\n collectStrictHostKeyCheckingErrors(ssh, errors)\n}\n\nexport function collectSshConfigErrors(value: unknown): string[] {\n const errors: string[] = []\n if (value === null) {\n errors.push(\"Invalid property 'ssh' (expected object, got null)\")\n return errors\n }\n if (typeof value !== \"object\") {\n errors.push(`Invalid property 'ssh' (expected object, got ${describeType(value)})`)\n return errors\n }\n if (!isRecord(value)) {\n errors.push(\"Invalid property 'ssh' (expected object, got object)\")\n return errors\n }\n const ssh = value\n collectPortsErrors(ssh, errors)\n collectRequiredUserErrors(ssh, errors)\n collectOptionalSshFieldErrors(ssh, errors)\n return errors\n}\n\nfunction normalizeServerDefinitionSshError(error: string): string {\n const mappedErrors: Record<string, string> = {\n \"Missing property 'ssh.user' (expected string)\": \"ssh.user is required\",\n \"Property 'ssh.expectedHostFingerprint' must not be an empty string\":\n \"ssh.expectedHostFingerprint must not be an empty string\",\n \"Property 'ssh.expectedHostPublicKey' must not be an empty string\":\n \"ssh.expectedHostPublicKey must not be an empty string\",\n \"Property 'ssh.ports' must not be empty\": \"ssh.ports must not be empty\",\n \"Property 'ssh.privateKey' must not be an empty string\":\n \"ssh.privateKey must not be an empty string\",\n \"Property 'ssh.user' must not be an empty string\": \"ssh.user is required\",\n [STRICT_HOST_KEY_ERROR]: \"ssh.strictHostKeyChecking must be one of accept-new, no, yes\",\n }\n return mappedErrors[error] ?? error\n}\n\nexport function validateSshConfig(ssh: SshConfig): void {\n const errors = collectSshConfigErrors(ssh)\n if (errors.length === 0) return\n throw new Error(`ServerDefinition: ${normalizeServerDefinitionSshError(errors[0])}`)\n}\n","import type {\n Environment,\n EnvironmentMetaEntry,\n MetaEnvironmentValue,\n ModuleMetaEntry,\n SshdPortMetaEntry,\n SystemHostMetaEntry,\n SystemRebootMetaEntry,\n} from \"./types.js\"\n\nimport { isValidTcpPort } from \"./serverDefinitionValidation.js\"\n\nconst SYSTEM_HOST_KIND = \"system.host\"\nconst SYSTEM_REBOOT_KIND = \"system.reboot\"\n\nexport type BooleanEnvironmentMetaEntry = { valueType: \"boolean\" } & EnvironmentMetaEntry\nexport type LazyEnvironmentMetaEntry = EnvironmentMetaEntry\nexport type NumberEnvironmentMetaEntry = { valueType: \"number\" } & EnvironmentMetaEntry\nexport type StringEnvironmentMetaEntry = { valueType: \"string\" } & EnvironmentMetaEntry\n\nfunction hasValidMetaName(name: unknown): name is string {\n return typeof name === \"string\" && name.length > 0\n}\n\nfunction inferMetaValueType(\n value: MetaEnvironmentValue,\n explicitValueType?: \"boolean\" | \"number\" | \"string\"\n): \"boolean\" | \"number\" | \"string\" {\n if (explicitValueType != null) return explicitValueType\n if (typeof value === \"boolean\") return \"boolean\"\n if (typeof value === \"number\") return \"number\"\n return \"string\"\n}\n\nfunction normalizeMetaValueResolver(\n value: MetaEnvironmentValue\n): () => Promise<boolean | number | string> {\n if (typeof value === \"function\") {\n return async () => {\n await Promise.resolve()\n return value()\n }\n }\n return async () => {\n await Promise.resolve()\n return value\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null\n}\n\nfunction assertValidEnvironmentMetaEntry(candidate: Record<string, unknown>): void {\n if (!hasValidMetaName(candidate.name)) {\n throw new TypeError(\"Invalid env meta entry: name must be a non-empty string\")\n }\n\n if (typeof candidate.resolve !== \"function\") {\n throw new TypeError(\"Invalid env meta entry: resolve must be a function returning a Promise\")\n }\n\n if (\n candidate.valueType !== \"boolean\" &&\n candidate.valueType !== \"number\" &&\n candidate.valueType !== \"string\"\n ) {\n throw new TypeError(\"Invalid env meta entry: valueType must be boolean, number, or string\")\n }\n}\n\nfunction assertValidSshdPortMetaEntry(candidate: Record<string, unknown>): void {\n if (!isValidTcpPort(candidate.port)) {\n throw new TypeError(\"Invalid sshd.port meta entry: port must be an integer between 1 and 65535\")\n }\n}\n\nfunction assertValidSystemHostMetaEntry(candidate: Record<string, unknown>): void {\n if (typeof candidate.host !== \"string\" || candidate.host.length === 0) {\n throw new TypeError(\"Invalid system.host meta entry: host must be a non-empty string\")\n }\n}\n\nexport function environmentMeta(\n name: string,\n value: MetaEnvironmentValue,\n valueType?: \"boolean\" | \"number\" | \"string\"\n): EnvironmentMetaEntry {\n if (!hasValidMetaName(name)) {\n throw new TypeError(\"Meta env entry name must be a non-empty string\")\n }\n return {\n kind: \"env\",\n name,\n resolve: normalizeMetaValueResolver(value),\n valueType: inferMetaValueType(value, valueType),\n }\n}\n\nexport function sshdPortMeta(port: number): SshdPortMetaEntry {\n if (!isValidTcpPort(port)) {\n throw new TypeError(\"Meta entry sshd.port requires an integer port between 1 and 65535\")\n }\n return { kind: \"sshd.port\", port }\n}\n\nexport function systemHostMeta(host: string): SystemHostMetaEntry {\n if (host.length === 0) {\n throw new TypeError(\"Meta entry system.host requires a non-empty host\")\n }\n return { host, kind: SYSTEM_HOST_KIND }\n}\n\nexport function systemRebootMeta(): SystemRebootMetaEntry {\n return { kind: SYSTEM_REBOOT_KIND }\n}\n\nexport const meta = {\n env: environmentMeta,\n sshdPort: sshdPortMeta,\n systemHost: systemHostMeta,\n systemReboot: systemRebootMeta,\n} as const\n\nexport function isEnvironmentMetaEntry(entry: ModuleMetaEntry): entry is EnvironmentMetaEntry {\n return entry.kind === \"env\"\n}\n\nexport function isStringEnvironmentMetaEntry(\n entry: ModuleMetaEntry\n): entry is StringEnvironmentMetaEntry {\n return isEnvironmentMetaEntry(entry) && entry.valueType === \"string\"\n}\n\nexport function isNumberEnvironmentMetaEntry(\n entry: ModuleMetaEntry\n): entry is NumberEnvironmentMetaEntry {\n return isEnvironmentMetaEntry(entry) && entry.valueType === \"number\"\n}\n\nexport function isBooleanEnvironmentMetaEntry(\n entry: ModuleMetaEntry\n): entry is BooleanEnvironmentMetaEntry {\n return isEnvironmentMetaEntry(entry) && entry.valueType === \"boolean\"\n}\n\nexport function isLazyEnvironmentMetaEntry(\n entry: ModuleMetaEntry\n): entry is LazyEnvironmentMetaEntry {\n return isEnvironmentMetaEntry(entry)\n}\n\nexport function isSshdPortMetaEntry(entry: ModuleMetaEntry): entry is SshdPortMetaEntry {\n return entry.kind === \"sshd.port\"\n}\n\nexport function isSystemHostMetaEntry(entry: ModuleMetaEntry): entry is SystemHostMetaEntry {\n return entry.kind === SYSTEM_HOST_KIND\n}\n\nexport function isSystemRebootMetaEntry(entry: ModuleMetaEntry): entry is SystemRebootMetaEntry {\n return entry.kind === SYSTEM_REBOOT_KIND\n}\n\nexport function assertValidModuleMetaEntry(entry: unknown): asserts entry is ModuleMetaEntry {\n if (!isRecord(entry)) {\n throw new TypeError(`Invalid meta entry: expected object, got ${typeof entry}`)\n }\n\n switch (entry.kind) {\n case \"env\": {\n assertValidEnvironmentMetaEntry(entry)\n return\n }\n case \"sshd.port\": {\n assertValidSshdPortMetaEntry(entry)\n return\n }\n case SYSTEM_HOST_KIND: {\n assertValidSystemHostMetaEntry(entry)\n return\n }\n case SYSTEM_REBOOT_KIND: {\n return\n }\n default: {\n throw new TypeError(`Invalid meta entry kind: ${String(entry.kind)}`)\n }\n }\n}\n\nexport function assertValidModuleMetaEntries(entries: ModuleMetaEntry[] | undefined): void {\n if (entries == null) return\n for (const entry of entries) {\n assertValidModuleMetaEntry(entry)\n }\n}\n\nexport async function mergeEnvironmentFromMeta(\n environment: Environment,\n entries: ModuleMetaEntry[] | undefined\n): Promise<Environment> {\n if (entries == null || entries.length === 0) {\n await Promise.resolve()\n return environment\n }\n\n const nextEnvironment = { ...environment }\n for (const entry of entries) {\n if (!isEnvironmentMetaEntry(entry)) continue\n nextEnvironment[entry.name] = async () => entry.resolve()\n }\n await Promise.resolve()\n return nextEnvironment\n}\n\nexport function environmentToMetaEntries(environment: Environment): ModuleMetaEntry[] {\n return Object.entries(environment).map(([name, value]) => environmentMeta(name, value))\n}\n\nexport function diffEnvironmentToMetaEntries(\n original: Environment,\n current: Environment\n): ModuleMetaEntry[] | undefined {\n const entries: ModuleMetaEntry[] = []\n for (const key of Object.keys(current)) {\n if (!(key in original) || current[key] !== original[key]) {\n entries.push(environmentMeta(key, current[key]))\n }\n }\n return entries.length === 0 ? undefined : entries\n}\n","/* eslint-disable max-lines */\nimport { randomUUID, timingSafeEqual } from \"node:crypto\"\nimport { unlinkSync, writeFileSync } from \"node:fs\"\nimport { readFile, stat } from \"node:fs/promises\"\nimport { homedir, tmpdir } from \"node:os\"\nimport { join, posix } from \"node:path\"\nimport { Client, type ClientChannel } from \"ssh2\"\n\nimport type { ExecOptions, ExecResult, SshConfig, SshConnection } from \"./types.js\"\n\nimport { buildHostVerifier, extractAlgoFromKey, HostKeyVerificationError } from \"./knownHosts.js\"\nimport { sftpDownload, sftpUpload } from \"./sftp.js\"\nimport {\n collectStreamOutput,\n maskSecrets,\n normalizeSshCloseCode,\n type SecretSource,\n shellQuote,\n tryConnectOnPort,\n validateMode,\n} from \"./sshHelpers.js\"\nimport { promptTerminal } from \"./terminal.js\"\n\nexport { shellQuote, validateMode }\n\n/**\n * Validate that a path returned by `mktemp` matches the expected paratix pattern.\n *\n * @param directory - The target directory in which the temp file must be created.\n * @param path - The raw `mktemp` output to validate.\n * @param prefix - The expected Paratix temp-file prefix.\n * @returns The validated path.\n * @throws {Error} When the path does not match the expected pattern.\n */\nfunction validateMktempPath(directory: string, path: string, prefix: string): string {\n const normalizedDirectory = directory === \"/\" ? \"\" : directory\n const expectedPrefix = `${normalizedDirectory}/${prefix}.`\n if (!path.startsWith(expectedPrefix) || path.includes(\"\\n\") || path.endsWith(\"/\")) {\n throw new Error(`Unexpected mktemp output: ${path}`)\n }\n return path\n}\n\nfunction expandHomePath(path: string): string {\n if (path === \"~\") return homedir()\n if (path.startsWith(\"~/\")) return join(homedir(), path.slice(2))\n return path\n}\n\nfunction resolveWriteFileMode(\n remotePath: string,\n options: { mode?: string } | null | undefined\n): string {\n if (options?.mode == null) {\n throw new Error(\n `[ssh.writeFile: ${remotePath}] missing options.mode; pass { mode: \"0644\" } or another explicit file mode`\n )\n }\n\n try {\n validateMode(options.mode)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(\n `[ssh.writeFile: ${remotePath}] invalid options.mode \"${options.mode}\": ${reason}`,\n { cause: error }\n )\n }\n\n return options.mode\n}\n\nconst COMMAND_TIMEOUT = 120_000\nconst DEFAULT_MAX_RECONNECT_ATTEMPTS = 10\nconst DEFAULT_RECONNECT_TIMEOUT = 120_000\nconst JITTER_BASE = 0.75\nconst JITTER_RANGE = 0.5\nconst RECONNECT_BASE_DELAY = 1000\nconst RECONNECT_MAX_DELAY = 30_000\n\ntype AuthMethod = \"agent\" | \"password\" | \"privateKey\" | null\ntype PromptOptions = { abortSignal?: AbortSignal }\n\ntype SshRuntimeState = {\n host: string\n ports: number[]\n}\n\nexport class SshConnectionImpl implements SshConnection {\n private agentSocket: null | string = null\n private authMethod: AuthMethod = null\n /**\n * Cached sudo password stored as a Buffer so it can be actively zeroed\n * after use via `buffer.fill(0)`.\n *\n * **Limitations:** Buffer zeroing in JavaScript/V8 only reduces the window\n * for potential memory leaks — it cannot eliminate them entirely. The GC may\n * create internal copies. The masking pipeline only materializes a string\n * from this buffer on actual output/error paths. This is a best-effort\n * mitigation, not a guarantee.\n */\n private cachedSudoPassword: Buffer | null = null\n private client: Client | null = null\n private readonly config: SshConfig\n private connectedPort = 0\n private readonly pendingRejects = new Set<(reason: Error) => void>()\n private pinnedHostKey: Buffer | null = null\n private promptAbortSignal: AbortSignal | undefined\n private readonly runtime: SshRuntimeState\n private sudoProbePromise: null | Promise<void> = null\n private sudoReady = false\n private verifiedHostKey: Buffer | null = null\n\n public constructor(host: string, config: SshConfig) {\n this.runtime = {\n host,\n ports: [...config.ports],\n }\n this.config = {\n ...config,\n ports: [...config.ports],\n }\n if (\n config.sudoPassword != null &&\n (config.sudoPassword.includes(\"\\n\") || config.sudoPassword.includes(\"\\r\"))\n ) {\n throw new Error(\"Sudo password must not contain newline characters\")\n }\n this.cachedSudoPassword = config.sudoPassword == null ? null : Buffer.from(config.sudoPassword)\n }\n\n public addPort(port: number): void {\n if (!this.runtime.ports.includes(port)) this.runtime.ports.push(port)\n }\n\n /**\n * Establish the SSH connection using the configured credentials.\n *\n * Authentication strategy (in order):\n * 1. If `privateKey` is set: connect with the key, optionally falling back to\n * password authentication when `passwordFallback` is enabled.\n * 2. If `privateKey` is omitted: connect via the SSH agent identified by\n * `SSH_AUTH_SOCK`, optionally falling back to password authentication\n * when `passwordFallback` is enabled. Throws if the environment variable\n * is not set.\n *\n * @param options - Optional prompt behavior for interactive password fallback.\n * @throws {Error} When no port in `config.ports` accepts the connection.\n */\n public async connect(options?: PromptOptions): Promise<void> {\n this.promptAbortSignal = options?.abortSignal\n if (this.config.privateKey == null) {\n await this.connectViaAgent(options)\n return\n }\n await this.connectViaPrivateKey(options)\n }\n\n public disconnect(): void {\n this.clearCachedPassword()\n this.disconnectTransport()\n }\n\n public async downloadFile(remotePath: string, localPath: string): Promise<void> {\n const client = this.ensureClient()\n let sourcePath = remotePath\n try {\n if (this.config.user !== \"root\") {\n sourcePath = await this.createRemoteTempPath(\n \"mktemp /tmp/paratix-download.XXXXXX\",\n \"paratix-download\"\n )\n await this.exec(`cat ${shellQuote(remotePath)} > ${shellQuote(sourcePath)}`, {\n silent: true,\n })\n }\n await sftpDownload(client, sourcePath, localPath)\n } finally {\n if (sourcePath !== remotePath) {\n try {\n await this.cleanupRemoteTempFile(sourcePath)\n } catch (cleanupError) {\n process.stderr.write(\n `Warning: failed to remove temp file ${sourcePath}: ${maskSecrets(String(cleanupError), this.buildSecrets())}\\n`\n )\n }\n }\n }\n }\n\n public async exec(command: string, options: ExecOptions = {}): Promise<ExecResult> {\n await this.ensureSudoReady()\n return this.execPrepared(command, options)\n }\n\n public async exists(remotePath: string): Promise<boolean> {\n return this.test(`[ -e ${shellQuote(remotePath)} ]`)\n }\n\n public getConnectionInfo(): ReturnType<SshConnection[\"getConnectionInfo\"]> {\n return {\n agentSocket: this.authMethod === \"agent\" ? (this.agentSocket ?? undefined) : undefined,\n authMethod: this.authMethod ?? undefined,\n host: this.runtime.host,\n port: this.connectedPort,\n privateKeyPath:\n this.authMethod === \"privateKey\" && this.config.privateKey != null\n ? expandHomePath(this.config.privateKey)\n : undefined,\n user: this.config.user,\n verifiedHostPublicKey:\n this.verifiedHostKey == null\n ? undefined\n : `${extractAlgoFromKey(this.verifiedHostKey)} ${this.verifiedHostKey.toString(\"base64\")}`,\n }\n }\n\n public async lines(command: string): Promise<string[]> {\n const out = await this.output(command)\n return out === \"\" ? [] : out.split(\"\\n\")\n }\n\n public async output(command: string): Promise<string> {\n const result = await this.exec(command, { silent: true })\n return result.stdout.trim()\n }\n\n /**\n * Probe whether passwordless sudo is available. If not, prompt the user\n * for a password and cache it for the remainder of the run.\n *\n * @param options - Optional prompt behavior for the interactive sudo password prompt.\n */\n public async probeSudo(options?: PromptOptions): Promise<void> {\n this.promptAbortSignal = options?.abortSignal ?? this.promptAbortSignal\n if (this.isSudoReadyWithoutProbe()) {\n this.sudoReady = true\n return\n }\n await this.ensureSudoInstalled()\n if (await this.hasPasswordlessSudo()) {\n this.sudoReady = true\n return\n }\n await this.promptAndCacheSudoPassword()\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.output(`cat ${shellQuote(remotePath)}`)\n }\n\n public async reconnect(): Promise<void> {\n const timeout = this.config.reconnectTimeout ?? DEFAULT_RECONNECT_TIMEOUT\n const maxAttempts = this.config.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS\n const deadline = Date.now() + timeout\n let attempt = 0\n\n while (Date.now() < deadline && attempt < maxAttempts) {\n try {\n this.disconnectTransport()\n // eslint-disable-next-line no-await-in-loop\n await this.connect()\n return\n } catch (error) {\n if (\n error instanceof HostKeyVerificationError ||\n (error instanceof Error && error.message === \"SSH connection closed\")\n )\n throw error\n const jitter =\n Math.min(RECONNECT_BASE_DELAY * 2 ** attempt, RECONNECT_MAX_DELAY) *\n (JITTER_BASE + Math.random() * JITTER_RANGE)\n const delay = Math.min(jitter, Math.max(0, deadline - Date.now()))\n // eslint-disable-next-line no-await-in-loop\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay)\n })\n attempt++\n }\n }\n throw new Error(\n `Failed to reconnect to ${this.runtime.host} after ${attempt} attempts (timeout: ${timeout}ms)`\n )\n }\n\n public removePort(port: number): void {\n this.runtime.ports = this.runtime.ports.filter((candidate) => candidate !== port)\n }\n\n public async sha256(remotePath: string): Promise<null | string> {\n const exists = await this.test(`[ -f ${shellQuote(remotePath)} ]`)\n if (!exists) return null\n const out = await this.output(`sha256sum ${shellQuote(remotePath)}`)\n return out.split(/\\s+/v)[0] ?? null\n }\n\n public async test(command: string): Promise<boolean> {\n try {\n const result = await this.exec(command, { ignoreExitCode: true, silent: true })\n return result.code === 0\n } catch {\n return false\n }\n }\n\n public updateHost(host: string): void {\n this.runtime.host = host\n }\n\n public async uploadFile(\n localPath: string,\n remotePath: string,\n options?: { mode?: string }\n ): Promise<void> {\n const client = this.ensureClient()\n const temporaryPath = await this.createRemoteWritableTempPath(remotePath, \"paratix-upload\")\n const temporaryMode = options?.mode ?? \"0600\"\n try {\n await sftpUpload(client, localPath, temporaryPath)\n await this.setRemoteTempMode(temporaryPath, temporaryMode)\n await this.finalizeRemoteTempFile(temporaryPath, remotePath, temporaryMode)\n } finally {\n try {\n await this.cleanupRemoteTempFile(temporaryPath)\n } catch (cleanupError) {\n process.stderr.write(\n `Warning: failed to remove temp file ${temporaryPath}: ${maskSecrets(String(cleanupError), this.buildSecrets())}\\n`\n )\n }\n }\n }\n\n /**\n * Write a string to a remote file atomically via write-to-temp + mv.\n *\n * The content is first written to a local temporary file, uploaded via SFTP\n * to a remote temporary file, then moved to the final destination with `mv`.\n * This ensures the target file is never left in a half-written state.\n *\n * @param remotePath - Destination path on the remote host.\n * @param content - The string content to write.\n * @param options - Settings for the remote write.\n * @param options.mode - File mode to set via `chmod` on the temp file before moving (e.g. `\"0644\"`).\n */\n public async writeFile(\n remotePath: string,\n content: string,\n options: { mode: string }\n ): Promise<void> {\n const client = this.ensureClient()\n const localTemporary = join(tmpdir(), `paratix-write-${randomUUID()}`)\n const remoteTemporary = await this.createRemoteWritableTempPath(remotePath, \"paratix-write\")\n const temporaryMode = resolveWriteFileMode(\n remotePath,\n options as { mode?: string } | null | undefined\n )\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n writeFileSync(localTemporary, content, { mode: 0o600 })\n await sftpUpload(client, localTemporary, remoteTemporary)\n await this.setRemoteTempMode(remoteTemporary, temporaryMode)\n await this.finalizeRemoteTempFile(remoteTemporary, remotePath, temporaryMode)\n } finally {\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n unlinkSync(localTemporary)\n } catch {\n // local cleanup is best-effort\n }\n try {\n await this.cleanupRemoteTempFile(remoteTemporary)\n } catch (cleanupError) {\n process.stderr.write(\n `Warning: failed to remove temp file ${remoteTemporary}: ${maskSecrets(String(cleanupError), this.buildSecrets())}\\n`\n )\n }\n }\n }\n\n private buildEnvPrefix(environment?: Record<string, string>): string {\n if (environment == null) return \"\"\n for (const key of Object.keys(environment)) {\n if (!/^[A-Za-z_]\\w*$/v.test(key)) {\n throw new Error(`Invalid environment variable name: ${key}`)\n }\n }\n const pairs = Object.entries(environment).map(([k, v]) => `${k}=${shellQuote(v)}`)\n return `${pairs.join(\" \")} `\n }\n\n private buildSecrets(extra?: string[]): SecretSource[] {\n const cachedPasswordSecret =\n this.cachedSudoPassword == null ? [] : [() => this.cachedSudoPassword?.toString(\"utf8\") ?? \"\"]\n return [...cachedPasswordSecret, ...(extra ?? [])]\n }\n\n private async cacheAndValidateSudoPassword(password: string): Promise<void> {\n if (password.includes(\"\\n\") || password.includes(\"\\r\")) {\n throw new Error(\"Sudo password must not contain newline characters\")\n }\n this.cachedSudoPassword = Buffer.from(password)\n try {\n await this.execPrepared(\"true\", { silent: true, timeout: 10_000 })\n this.sudoReady = true\n } catch (error) {\n const masked = maskSecrets(String(error), [password])\n this.clearCachedPassword()\n throw new Error(`Sudo authentication failed: ${masked}`, { cause: error })\n }\n }\n\n private async cleanupRemoteTempFile(remotePath: string): Promise<void> {\n const cleanup =\n this.config.user === \"root\"\n ? this.exec(`rm -f ${shellQuote(remotePath)}`, { silent: true })\n : this.execWithoutSudo(`rm -f ${shellQuote(remotePath)}`)\n await cleanup\n }\n\n private clearCachedPassword(): void {\n if (this.cachedSudoPassword != null) {\n this.cachedSudoPassword.fill(0)\n this.cachedSudoPassword = null\n }\n }\n\n private async connectViaAgent(options?: PromptOptions): Promise<void> {\n const agent = process.env.SSH_AUTH_SOCK\n if (agent == null || agent.length === 0) {\n if (await this.tryPasswordFallback(options)) return\n if (this.config.passwordFallback) {\n throw new Error(\n `Failed to connect to ${this.runtime.host} on ports: ${this.runtime.ports.join(\", \")}`\n )\n }\n throw new Error(\"No privateKey configured and SSH_AUTH_SOCK is not set\")\n }\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- agent is validated from SSH_AUTH_SOCK env var\n await stat(agent)\n } catch {\n throw new Error(`SSH_AUTH_SOCK points to non-existent path: ${agent}`)\n }\n if (await this.tryConnectOnPorts(undefined, undefined, agent)) {\n this.agentSocket = agent\n this.authMethod = \"agent\"\n return\n }\n if (await this.tryPasswordFallback(options, agent)) return\n throw new Error(\n `Could not connect to ${this.runtime.host} via SSH agent on ports ${this.runtime.ports.join(\", \")}`\n )\n }\n\n private async connectViaPrivateKey(options?: PromptOptions): Promise<void> {\n const privateKeyPath = this.config.privateKey\n if (privateKeyPath == null) {\n throw new Error(\"connectViaPrivateKey requires config.privateKey\")\n }\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const privateKey = await readFile(expandHomePath(privateKeyPath))\n try {\n if (await this.tryConnectOnPorts(privateKey)) {\n this.authMethod = \"privateKey\"\n return\n }\n if (this.config.passwordFallback) {\n const password = await promptTerminal(\n `Password for ${this.config.user}@${this.runtime.host}: `,\n true,\n options\n )\n if (await this.tryConnectOnPorts(privateKey, password)) {\n this.authMethod = \"password\"\n return\n }\n }\n throw new Error(\n `Failed to connect to ${this.runtime.host} on ports: ${this.runtime.ports.join(\", \")}`\n )\n } finally {\n privateKey.fill(0)\n }\n }\n\n private async createRemoteTempPath(command: string, prefix: string): Promise<string> {\n const path =\n this.config.user === \"root\"\n ? await this.output(command)\n : await this.outputWithoutSudo(command)\n return validateMktempPath(\"/tmp\", path, prefix)\n }\n\n private async createRemoteTempPathInDestination(\n remotePath: string,\n prefix: string\n ): Promise<string> {\n const directory = posix.dirname(remotePath)\n const template = `${directory}/${prefix}.XXXXXX`\n const command = `mktemp ${shellQuote(template)}`\n const path =\n this.config.user === \"root\"\n ? await this.output(command)\n : await this.outputWithoutSudo(command)\n return validateMktempPath(directory, path, prefix)\n }\n\n private async createRemoteWritableTempPath(remotePath: string, prefix: string): Promise<string> {\n if (this.config.user === \"root\") {\n return this.createRemoteTempPathInDestination(remotePath, prefix)\n }\n return this.createRemoteTempPath(`mktemp '/tmp/${prefix}.XXXXXX'`, prefix)\n }\n\n private createSettledCallbacks<T>(\n resolve: (value: T) => void,\n reject: (reason: Error) => void\n ): { wrappedReject: (reason: Error) => void; wrappedResolve: (value: T) => void } {\n let settled = false\n const wrappedReject = (reason: Error): void => {\n if (settled) return\n settled = true\n this.pendingRejects.delete(wrappedReject)\n reject(reason)\n }\n const wrappedResolve = (value: T): void => {\n if (settled) return\n settled = true\n this.pendingRejects.delete(wrappedReject)\n resolve(value)\n }\n this.pendingRejects.add(wrappedReject)\n return { wrappedReject, wrappedResolve }\n }\n\n /** Tear down the SSH transport without touching the cached sudo password. */\n private disconnectTransport(): void {\n if (this.client) {\n this.client.end()\n this.client = null\n }\n const error = new Error(\"SSH connection closed\")\n for (const rejectFunction of this.pendingRejects) {\n rejectFunction(error)\n }\n this.pendingRejects.clear()\n }\n\n private ensureClient(): Client {\n if (!this.client) throw new Error(\"SSH not connected\")\n return this.client\n }\n\n /** Verify that `sudo` is available on the remote host. */\n private async ensureSudoInstalled(): Promise<void> {\n const result = await this.execRaw(\"command -v sudo\")\n if (result.exitCode !== 0) {\n throw new Error(\"sudo is not installed on the remote host\")\n }\n }\n\n private async ensureSudoReady(): Promise<void> {\n if (this.config.user === \"root\" || this.sudoReady) return\n if (this.cachedSudoPassword != null) {\n this.sudoReady = true\n return\n }\n if (this.sudoProbePromise != null) {\n await this.sudoProbePromise\n return\n }\n this.sudoProbePromise = this.probeSudo({ abortSignal: this.promptAbortSignal }).finally(() => {\n this.sudoProbePromise = null\n })\n await this.sudoProbePromise\n }\n\n private async execPrepared(command: string, options: ExecOptions = {}): Promise<ExecResult> {\n const client = this.ensureClient()\n const environmentPrefix = this.buildEnvPrefix(options.env)\n const { command: cmd, needsPassword } = this.sudoCommand(command, environmentPrefix)\n return new Promise((resolve, reject) => {\n const { wrappedReject, wrappedResolve } = this.createSettledCallbacks<ExecResult>(\n resolve,\n reject\n )\n const timeout = options.timeout ?? COMMAND_TIMEOUT\n const secrets = this.buildSecrets(options.secrets)\n let activeStream: ClientChannel | null = null\n const timer = setTimeout(() => {\n activeStream?.close()\n wrappedReject(\n new Error(`Command timed out after ${timeout}ms: ${maskSecrets(command, secrets)}`)\n )\n }, timeout)\n client.exec(cmd, (error: Error | undefined, stream: ClientChannel) => {\n if (error) {\n clearTimeout(timer)\n wrappedReject(error)\n return\n }\n activeStream = stream\n collectStreamOutput({\n command,\n options,\n reject: wrappedReject,\n resolve: wrappedResolve,\n secrets,\n stream,\n timer,\n })\n if (needsPassword && this.cachedSudoPassword != null) {\n this.writeSudoPassword(stream)\n }\n })\n })\n }\n\n /**\n * Execute a command directly over the SSH transport without sudo wrapping.\n * Used only by {@link probeSudo} to check whether `sudo` is installed.\n *\n * @param command - The raw shell command to run.\n * @returns The exit code and captured stdout.\n */\n private async execRaw(command: string): Promise<{ exitCode: number; stdout: string }> {\n const client = this.ensureClient()\n return new Promise((resolve, reject) => {\n const { wrappedReject, wrappedResolve } = this.createSettledCallbacks<{\n exitCode: number\n stdout: string\n }>(resolve, reject)\n let activeStream: ClientChannel | null = null\n const timer = setTimeout(() => {\n activeStream?.close()\n wrappedReject(new Error(`Command timed out after ${COMMAND_TIMEOUT}ms: ${command}`))\n }, COMMAND_TIMEOUT)\n client.exec(command, (error: Error | undefined, stream: ClientChannel) => {\n if (error) {\n clearTimeout(timer)\n wrappedReject(error)\n return\n }\n activeStream = stream\n const chunks: Buffer[] = []\n stream.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk)\n })\n stream.on(\"close\", (code: number) => {\n clearTimeout(timer)\n wrappedResolve({\n exitCode: normalizeSshCloseCode(code),\n stdout: Buffer.concat(chunks).toString(\"utf8\"),\n })\n })\n stream.stderr.on(\"data\", () => {\n // discard stderr\n })\n })\n })\n }\n\n private async execWithoutSudo(command: string): Promise<void> {\n const result = await this.execRaw(command)\n if (result.exitCode !== 0) {\n throw new Error(`Command failed (exit code ${result.exitCode}): ${command}`)\n }\n }\n\n private async finalizeRemoteTempFile(\n temporaryPath: string,\n remotePath: string,\n mode: string\n ): Promise<void> {\n validateMode(mode)\n if (this.config.user === \"root\") {\n await this.exec(`mv ${shellQuote(temporaryPath)} ${shellQuote(remotePath)}`, {\n silent: true,\n })\n return\n }\n\n const directory = posix.dirname(remotePath)\n const basename = posix.basename(remotePath)\n const finalTemplate = `${directory}/.${basename}.paratix.XXXXXX`\n const finalizeScript = `\ntarget_owner=$(stat -c '%u:%g' ${shellQuote(remotePath)} 2>/dev/null || printf '0:0')\ntarget_temp=''\ncleanup() {\n if [ -n \"$target_temp\" ]; then\n rm -f \"$target_temp\"\n fi\n}\ntrap cleanup EXIT\ntarget_temp=$(mktemp ${shellQuote(finalTemplate)})\nmv ${shellQuote(temporaryPath)} \"$target_temp\"\nchmod ${shellQuote(mode)} \"$target_temp\"\nchown \"$target_owner\" \"$target_temp\"\nmv \"$target_temp\" ${shellQuote(remotePath)}\ntrap - EXIT\n`\n await this.exec(finalizeScript, { silent: true })\n }\n\n private async hasPasswordlessSudo(): Promise<boolean> {\n try {\n await this.execPrepared(\"true\", { silent: true, timeout: 10_000 })\n return true\n } catch {\n return false\n }\n }\n\n private isSudoReadyWithoutProbe(): boolean {\n return this.config.user === \"root\" || this.cachedSudoPassword != null\n }\n\n private async outputWithoutSudo(command: string): Promise<string> {\n const result = await this.execRaw(command)\n if (result.exitCode !== 0) {\n throw new Error(`Command failed (exit code ${result.exitCode}): ${command}`)\n }\n return result.stdout.trim()\n }\n\n private async promptAndCacheSudoPassword(): Promise<void> {\n const password = await promptTerminal(\n `[sudo] password for ${this.config.user}@${this.runtime.host}: `,\n true,\n { abortSignal: this.promptAbortSignal }\n )\n await this.cacheAndValidateSudoPassword(password)\n }\n\n private async setRemoteTempMode(remotePath: string, mode: string): Promise<void> {\n validateMode(mode)\n const command = `chmod ${shellQuote(mode)} ${shellQuote(remotePath)}`\n if (this.config.user === \"root\") {\n await this.exec(command, { silent: true })\n return\n }\n await this.execWithoutSudo(command)\n }\n\n /**\n * Build the sudo-wrapped command string for execution.\n *\n * @param command - The raw command to execute.\n * @param environmentPrefix - Optional env var prefix string.\n * @returns An object with the final command and whether a password must be written to stdin.\n */\n private sudoCommand(\n command: string,\n environmentPrefix = \"\"\n ): { command: string; needsPassword: boolean } {\n if (this.config.user === \"root\") {\n return { command: `${environmentPrefix}${command}`, needsPassword: false }\n }\n const quoted = shellQuote(`${environmentPrefix}${command}`)\n if (this.cachedSudoPassword != null) {\n return { command: `SUDO_PROMPT='' sudo -S bash -c ${quoted}`, needsPassword: true }\n }\n return { command: `sudo bash -c ${quoted}`, needsPassword: false }\n }\n\n /**\n * Iterate over `config.ports` and attempt a connection on each one.\n *\n * @param privateKey - PEM-encoded private key content, or `undefined` when using agent auth.\n * @param password - Optional password for keyboard-interactive fallback.\n * @param agent - SSH agent socket path (e.g. `SSH_AUTH_SOCK`). Used when `privateKey` is absent.\n * @returns `true` if a port connected successfully, `false` if all ports failed.\n */\n private async tryConnectOnPorts(\n privateKey?: Buffer | string,\n password?: string,\n agent?: string\n ): Promise<boolean> {\n const mode = this.config.strictHostKeyChecking ?? \"yes\"\n for (const port of this.runtime.ports) {\n try {\n const client = new Client()\n const verifier = buildHostVerifier(\n mode,\n { host: this.runtime.host, port },\n {\n expectedHostFingerprint: this.config.expectedHostFingerprint,\n expectedHostPublicKey: this.config.expectedHostPublicKey,\n }\n )\n const wrappedVerifier = this.wrapHostVerifier(verifier.hostVerifier)\n // eslint-disable-next-line no-await-in-loop\n await tryConnectOnPort({\n agent,\n agentForward: this.config.agentForward,\n client,\n host: this.runtime.host,\n hostVerifier: wrappedVerifier,\n password,\n port,\n privateKey,\n username: this.config.user,\n })\n // Ensure the host key is persisted to disk before returning\n // eslint-disable-next-line no-await-in-loop\n if (verifier.pendingPersist != null) await verifier.pendingPersist\n client.on(\"close\", () => {\n const error = new Error(\"SSH connection closed unexpectedly\")\n for (const rejectFunction of this.pendingRejects) {\n rejectFunction(error)\n }\n this.pendingRejects.clear()\n })\n this.client = client\n this.connectedPort = port\n return true\n } catch (error) {\n if (error instanceof HostKeyVerificationError) throw error\n // Try next port\n }\n }\n return false\n }\n\n private async tryPasswordFallback(options?: PromptOptions, agent?: string): Promise<boolean> {\n if (!this.config.passwordFallback) return false\n const password = await promptTerminal(\n `Password for ${this.config.user}@${this.runtime.host}: `,\n true,\n options\n )\n if (!(await this.tryConnectOnPorts(undefined, password, agent))) return false\n this.authMethod = \"password\"\n return true\n }\n\n /**\n * Wrap a host-key verifier to pin the accepted key on first connection and\n * reject key changes on subsequent connections (reconnects).\n *\n * @param original - The original verifier from `buildHostVerifier`, if any.\n * @returns A verifier that enforces host-key pinning.\n */\n private wrapHostVerifier(original?: (key: Buffer) => boolean): (key: Buffer) => boolean {\n return (key: Buffer): boolean => {\n if (\n this.pinnedHostKey != null &&\n (this.pinnedHostKey.length !== key.length || !timingSafeEqual(this.pinnedHostKey, key))\n ) {\n this.clearCachedPassword()\n throw new HostKeyVerificationError(\n `HOST KEY CHANGED on reconnect to ${this.runtime.host}: ` +\n \"the remote host key does not match the key from the initial connection. \" +\n \"This could indicate a man-in-the-middle attack.\"\n )\n }\n if (original != null) {\n const accepted = original(key)\n if (!accepted) return false\n this.verifiedHostKey ??= Buffer.from(key)\n }\n this.pinnedHostKey ??= Buffer.from(key)\n return true\n }\n }\n\n /**\n * Write the cached sudo password followed by a newline to the given stream.\n *\n * @param stream - The SSH channel to write the password to.\n */\n private writeSudoPassword(stream: ClientChannel): void {\n stream.write(this.cachedSudoPassword)\n stream.write(\"\\n\")\n }\n}\n","import { createHash, createHmac, timingSafeEqual } from \"node:crypto\"\nimport { readFileSync } from \"node:fs\"\nimport { appendFile, mkdir } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nimport { shellQuote } from \"./sshHelpers.js\"\n\ntype HostVerifierOptions = {\n expectedHostFingerprint?: string\n expectedHostPublicKey?: string\n}\n\nexport type HostVerifierResult = {\n hostVerifier?: (key: Buffer) => boolean\n pendingPersist?: Promise<void>\n}\n\ntype HostLocation = {\n host: string\n port: number\n}\n\n/** Thrown when a remote host key does not match the expected key in known_hosts. */\nexport class HostKeyVerificationError extends Error {\n public constructor(message: string) {\n super(message)\n this.name = \"HostKeyVerificationError\"\n Error.captureStackTrace(this, HostKeyVerificationError)\n }\n}\n\n/** A parsed entry from a known_hosts file. */\nexport type KnownHostEntry = {\n /** Algorithm name as stored in the file (e.g. `\"ssh-ed25519\"`). */\n algo: string\n /** Host pattern as stored in the file (plain hostname or `[host]:port` notation). */\n host: string\n /** Raw public key bytes decoded from the Base64 field. */\n key: Buffer\n /** Optional OpenSSH marker such as `@revoked`. */\n marker?: string\n}\n\n/** Minimum number of whitespace-separated fields in a valid known_hosts line. */\nconst MIN_KNOWN_HOSTS_FIELDS = 3\n\n/** The default SSH port used by OpenSSH. */\nconst DEFAULT_SSH_PORT = 22\n\n/** Byte size of the uint32 length prefix in SSH wire format. */\nconst UINT32_SIZE = 4\nconst HASHED_HOST_PARTS = 4\n\n/**\n * In-memory cache for accepted host keys that could not be persisted to disk.\n * Keyed by the formatted host needle (e.g. `\"example.com\"` or `\"[example.com]:2222\"`).\n */\nconst inMemoryHostKeys = new Map<string, Buffer>()\n\n/**\n * Clear the in-memory host key cache. Intended for use in tests.\n */\nexport function clearHostKeyCache(): void {\n inMemoryHostKeys.clear()\n}\n\nfunction parseKnownHostsLine(line: string): KnownHostEntry[] {\n const parts = line.split(/\\s+/v)\n const offset = parts[0]?.startsWith(\"@\") ? 1 : 0\n if (parts.length < MIN_KNOWN_HOSTS_FIELDS + offset) return []\n\n const marker = offset === 1 ? parts[0] : undefined\n const hostsPart = parts[offset]\n const algo = parts[offset + 1]\n const base64Key = parts[offset + 2]\n\n const key = Buffer.from(base64Key, \"base64\")\n return hostsPart.split(\",\").map((host) => ({ algo, host, key, marker }))\n}\n\n/**\n * Parse the contents of an OpenSSH `known_hosts` file into structured entries.\n *\n * - Blank lines and comment lines (starting with `#`) are skipped.\n * - Hosts separated by commas produce one entry per hostname.\n *\n * @param content - The raw file content.\n * @returns An array of parsed entries.\n */\nexport function parseKnownHosts(content: string): KnownHostEntry[] {\n const entries: KnownHostEntry[] = []\n for (const raw of content.split(\"\\n\")) {\n const line = raw.trim()\n if (line.length === 0 || line.startsWith(\"#\")) continue\n entries.push(...parseKnownHostsLine(line))\n }\n return entries\n}\n\n/**\n * Format the host lookup needle for known_hosts matching.\n *\n * For port 22 the plain hostname is returned. For non-standard ports the\n * bracketed `[host]:port` notation is used, matching OpenSSH behavior.\n *\n * @param host - The hostname or IP.\n * @param port - The SSH port.\n * @returns The formatted needle string.\n */\nfunction formatHostNeedle(host: string, port: number): string {\n return port === DEFAULT_SSH_PORT ? host : `[${host}]:${port}`\n}\n\n/**\n * Look up a host key in the parsed known_hosts entries.\n *\n * @param pattern - The stored OpenSSH hashed host pattern.\n * @param needle - The formatted host lookup needle.\n * @returns Whether the hashed entry matches the target host.\n */\nfunction matchesHashedHost(pattern: string, needle: string): boolean {\n if (!pattern.startsWith(\"|1|\")) return false\n\n const parts = pattern.split(\"|\")\n if (parts.length !== HASHED_HOST_PARTS || parts[1] !== \"1\") return false\n\n const salt = Buffer.from(parts[2] ?? \"\", \"base64\")\n const expectedHash = Buffer.from(parts[3] ?? \"\", \"base64\")\n if (salt.length === 0 || expectedHash.length === 0) return false\n\n const actualHash = createHmac(\"sha1\", salt).update(needle).digest()\n return actualHash.length === expectedHash.length && timingSafeEqual(actualHash, expectedHash)\n}\n\nfunction findMatchingEntries(\n entries: KnownHostEntry[],\n host: string,\n port: number\n): KnownHostEntry[] {\n const needle = formatHostNeedle(host, port)\n return entries.filter((entry) => entry.host === needle || matchesHashedHost(entry.host, needle))\n}\n\nfunction lookupHostEntry(\n entries: KnownHostEntry[],\n host: string,\n port: number\n): KnownHostEntry | null {\n const match = findMatchingEntries(entries, host, port).find(\n (entry) => entry.marker !== \"@revoked\"\n )\n return match ?? null\n}\n\nfunction findRevokedEntry(entries: KnownHostEntry[], key: Buffer): KnownHostEntry | undefined {\n return entries.find(\n (entry) =>\n entry.marker === \"@revoked\" &&\n entry.key.length === key.length &&\n timingSafeEqual(entry.key, key)\n )\n}\n\nfunction throwHostKeyMismatch(host: string, presentedKey: Buffer, existingKey?: Buffer): never {\n const presentedAlgo = extractAlgoFromKey(presentedKey)\n const knownHostsDetails =\n existingKey == null\n ? \"remote host key does not match the key in known_hosts. \"\n : `remote host key (${presentedAlgo}) does not match the key in known_hosts (${extractAlgoFromKey(existingKey)}). `\n throw new HostKeyVerificationError(\n `HOST KEY VERIFICATION FAILED for ${host}: ${knownHostsDetails}` +\n \"This could indicate a man-in-the-middle attack.\"\n )\n}\n\nfunction verifyHostKeyAgainstKnownEntries(parameters: {\n cachedKey: Buffer | null\n fileEntries: KnownHostEntry[]\n host: string\n key: Buffer\n}): boolean {\n const { cachedKey, fileEntries, host, key } = parameters\n const revokedKey = findRevokedEntry(fileEntries, key)\n if (revokedKey != null) {\n throw new HostKeyVerificationError(\n `HOST KEY VERIFICATION FAILED for ${host}: remote host key (${extractAlgoFromKey(revokedKey.key)}) is marked as revoked in known_hosts.`\n )\n }\n\n const nonRevokedEntries = fileEntries.filter((entry) => entry.marker !== \"@revoked\")\n const matchingEntry = nonRevokedEntries.find(\n (entry) => entry.key.length === key.length && timingSafeEqual(entry.key, key)\n )\n if (matchingEntry != null) return true\n if (cachedKey?.length === key.length && timingSafeEqual(cachedKey, key)) {\n return true\n }\n\n const firstNonRevokedKey = nonRevokedEntries.at(0)?.key\n if (firstNonRevokedKey != null) throwHostKeyMismatch(host, key, firstNonRevokedKey)\n if (cachedKey != null) throwHostKeyMismatch(host, key, cachedKey)\n return false\n}\n\nexport function lookupHostKey(\n entries: KnownHostEntry[],\n host: string,\n port: number\n): Buffer | null {\n return lookupHostEntry(entries, host, port)?.key ?? null\n}\n\n/**\n * Extract the algorithm name from an SSH public key in wire format.\n *\n * The SSH wire format starts with a `uint32` length prefix followed by the\n * algorithm name as an ASCII string.\n *\n * @param keyBuffer - The raw public key buffer.\n * @returns The algorithm name (e.g. `\"ssh-ed25519\"`).\n */\nexport function extractAlgoFromKey(keyBuffer: Buffer): string {\n if (keyBuffer.length < UINT32_SIZE) {\n throw new Error(\"Invalid SSH key buffer: too short to contain algorithm length\")\n }\n const algoLength = keyBuffer.readUInt32BE(0)\n if (algoLength === 0 || UINT32_SIZE + algoLength > keyBuffer.length) {\n throw new Error(\"Invalid SSH key buffer: algorithm length exceeds buffer size\")\n }\n return keyBuffer.subarray(UINT32_SIZE, UINT32_SIZE + algoLength).toString(\"ascii\")\n}\n\n/**\n * Compute the SHA256 fingerprint of an SSH public key in OpenSSH format.\n *\n * The result matches the fingerprint shown by `ssh-keygen -l`, e.g.\n * `SHA256:AbCdEf...`. Trailing `=` padding characters are stripped from the\n * base64 digest to conform to the OpenSSH fingerprint representation.\n *\n * @param key - The raw public key buffer (SSH wire format).\n * @returns The fingerprint string prefixed with `SHA256:`.\n */\nexport function computeFingerprint(key: Buffer): string {\n const hash = createHash(\"sha256\").update(key).digest(\"base64\")\n // Remove trailing '=' padding to match OpenSSH format\n return `SHA256:${hash.replaceAll(\"=\", \"\")}`\n}\n\n/**\n * Append a new host key entry to `~/.ssh/known_hosts`.\n *\n * Creates the `~/.ssh` directory (mode `0o700`) and the file itself if they\n * do not exist yet.\n *\n * @param host - The hostname or IP.\n * @param port - The SSH port.\n * @param keyBuffer - The raw public key buffer.\n */\nexport async function appendHostKey(host: string, port: number, keyBuffer: Buffer): Promise<void> {\n const hostLabel = formatHostNeedle(host, port)\n const algo = extractAlgoFromKey(keyBuffer)\n const base64Key = keyBuffer.toString(\"base64\")\n const line = `${hostLabel} ${algo} ${base64Key}\\n`\n\n const sshDirectory = join(homedir(), \".ssh\")\n const filePath = join(sshDirectory, \"known_hosts\")\n\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await mkdir(sshDirectory, { mode: 0o700, recursive: true })\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n await appendFile(filePath, line, { mode: 0o644 })\n}\n\n/**\n * Read and parse `~/.ssh/known_hosts`, returning an empty array on failure.\n *\n * @returns The parsed entries.\n */\nfunction loadKnownHostEntries(): KnownHostEntry[] {\n const filePath = join(homedir(), \".ssh\", \"known_hosts\")\n let content = \"\"\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n content = readFileSync(filePath, \"utf8\")\n } catch {\n // File may not exist yet — treat as empty\n }\n return parseKnownHosts(content)\n}\n\n/**\n * Accept an unknown host key, warn to stderr, and persist it to `~/.ssh/known_hosts`.\n *\n * The key is immediately stored in the in-memory cache so subsequent connections\n * within the same process succeed even if the disk write fails. If writing to\n * disk fails, a recovery hint with the equivalent `ssh-keyscan` command is\n * printed to stderr.\n *\n * @param host - The hostname or IP of the remote host.\n * @param port - The SSH port of the remote host.\n * @param key - The raw public key buffer presented by the remote host.\n * @returns A promise that resolves once the key has been written to disk (or the write error has been handled).\n */\nasync function acceptAndPersistHostKey(host: string, port: number, key: Buffer): Promise<void> {\n try {\n const algo = extractAlgoFromKey(key)\n const fingerprint = computeFingerprint(key)\n process.stderr.write(\n `WARNING: Permanently added '${host}' (${algo}) to the list of known hosts. ` +\n `Fingerprint: ${fingerprint}\\n`\n )\n } catch {\n process.stderr.write(`WARNING: Permanently added '${host}' to the list of known hosts.\\n`)\n }\n inMemoryHostKeys.set(formatHostNeedle(host, port), key)\n try {\n await appendHostKey(host, port, key)\n } catch (error: unknown) {\n const keyscanArguments =\n port === DEFAULT_SSH_PORT ? shellQuote(host) : `-p ${port} ${shellQuote(host)}`\n process.stderr.write(\n `WARNING: Could not persist host key for ${host} — ` +\n `the key is cached in memory for this session. ` +\n `To persist it, ensure ~/.ssh/ is writable or run: ` +\n `ssh-keyscan ${keyscanArguments} >> ~/.ssh/known_hosts. ` +\n `${String(error)}\\n`\n )\n }\n}\n\nfunction normalizePinnedPublicKey(publicKey: string): string {\n const parts = publicKey.trim().split(/\\s+/v)\n if (parts.length < 2) {\n throw new Error(\"Expected host public key must use the format '<algorithm> <base64>'\")\n }\n const [algorithm, key] = parts\n return `${algorithm} ${key}`\n}\n\nfunction formatPresentedPublicKey(key: Buffer): string {\n return `${extractAlgoFromKey(key)} ${key.toString(\"base64\")}`\n}\n\nfunction hasPinnedHostTrustAnchor(options?: HostVerifierOptions): boolean {\n return options?.expectedHostFingerprint != null || options?.expectedHostPublicKey != null\n}\n\nfunction verifyPinnedHostKey(host: string, key: Buffer, options: HostVerifierOptions): void {\n const normalizedExpectedPublicKey =\n options.expectedHostPublicKey == null\n ? null\n : normalizePinnedPublicKey(options.expectedHostPublicKey)\n const expectedFingerprint = options.expectedHostFingerprint ?? null\n const presentedPublicKey = formatPresentedPublicKey(key)\n const presentedFingerprint = computeFingerprint(key)\n\n if (\n normalizedExpectedPublicKey === presentedPublicKey ||\n expectedFingerprint === presentedFingerprint\n ) {\n return\n }\n\n throw new HostKeyVerificationError(\n `HOST KEY VERIFICATION FAILED for ${host}: the remote host key does not match the configured trust anchor.`\n )\n}\n\n/**\n * Build the `hostVerifier` callback for an ssh2 `ConnectConfig`.\n *\n * Behavior by mode:\n * - `\"no\"` — returns an empty object (no verification, ssh2 default).\n * - `\"accept-new\"` — accepts unknown keys and appends them to `~/.ssh/known_hosts`;\n * throws if a known key does not match.\n * - `\"yes\"` — throws for both unknown keys and mismatched keys.\n *\n * @param mode - The host key verification strategy.\n * @param location - The target host and SSH port.\n * @param options - Optional pinned trust anchors for the remote host.\n * @returns An object with `hostVerifier` set (or empty for mode `\"no\"`).\n * @throws {Error} When a known host key does not match the presented key (all modes except `\"no\"`).\n * @throws {Error} When no known_hosts entry exists for the host and mode is `\"yes\"`.\n */\nexport function buildHostVerifier(\n mode: \"accept-new\" | \"no\" | \"yes\",\n location: HostLocation,\n options: HostVerifierOptions = {}\n): HostVerifierResult {\n const { host, port } = location\n if (mode === \"no\" && !hasPinnedHostTrustAnchor(options)) return {}\n\n const entries = loadKnownHostEntries()\n const fileEntries = findMatchingEntries(entries, host, port)\n const cachedKey = inMemoryHostKeys.get(formatHostNeedle(host, port)) ?? null\n\n const result: { hostVerifier: (key: Buffer) => boolean } & HostVerifierResult = {\n hostVerifier(key: Buffer): boolean {\n if (\n mode !== \"no\" &&\n verifyHostKeyAgainstKnownEntries({ cachedKey, fileEntries, host, key })\n ) {\n if (hasPinnedHostTrustAnchor(options)) verifyPinnedHostKey(host, key, options)\n return true\n }\n if (hasPinnedHostTrustAnchor(options)) {\n verifyPinnedHostKey(host, key, options)\n return true\n }\n if (mode === \"yes\") {\n throw new HostKeyVerificationError(\n `Host key for ${host} not found in known_hosts. ` +\n 'Set strictHostKeyChecking to \"accept-new\" for explicit TOFU or configure ssh.expectedHostFingerprint / ssh.expectedHostPublicKey.'\n )\n }\n if (mode === \"no\") return true\n // mode === \"accept-new\": accept and persist\n result.pendingPersist = acceptAndPersistHostKey(host, port, key)\n return true\n },\n }\n return result\n}\n","import type { Readable, Writable } from \"node:stream\"\nimport type { Client, SFTPWrapper } from \"ssh2\"\n\nimport { randomUUID } from \"node:crypto\"\nimport { createReadStream, createWriteStream, renameSync, unlinkSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\n\n/** Default timeout for SFTP transfers in milliseconds (2 minutes). */\nexport const SFTP_TIMEOUT = 120_000\n\n/**\n * Wire up stream event handlers with a timeout guard, then pipe.\n *\n * @param options - Stream piping options including timeout configuration.\n * @param options.readStream - The source stream to read from.\n * @param options.reject - Promise reject callback.\n * @param options.resolve - Promise resolve callback.\n * @param options.sftp - The SFTP wrapper to close on completion.\n * @param options.timeout - Maximum time in ms before the transfer is aborted.\n * @param options.timeoutMessage - Error message to use when the transfer times out.\n * @param options.writeStream - The destination stream to write to.\n */\nfunction wireStreams(options: {\n readStream: Readable\n reject: (reason: Error) => void\n resolve: () => void\n sftp: SFTPWrapper\n timeout: number\n timeoutMessage: string\n writeStream: Writable\n}): void {\n const { readStream, reject, resolve, sftp, timeout, timeoutMessage, writeStream } = options\n\n let settled = false\n\n const timer = setTimeout(() => {\n if (settled) return\n settled = true\n readStream.destroy()\n writeStream.destroy()\n sftp.end()\n reject(new Error(timeoutMessage))\n }, timeout)\n\n writeStream.on(\"close\", () => {\n clearTimeout(timer)\n if (settled) return\n settled = true\n sftp.end()\n resolve()\n })\n writeStream.on(\"error\", (writeError: Error) => {\n clearTimeout(timer)\n if (settled) return\n settled = true\n readStream.destroy()\n if (typeof writeStream.destroy === \"function\") writeStream.destroy()\n sftp.end()\n reject(writeError)\n })\n readStream.on(\"error\", (readError: Error) => {\n clearTimeout(timer)\n if (settled) return\n settled = true\n if (typeof readStream.destroy === \"function\") readStream.destroy()\n if (typeof writeStream.destroy === \"function\") writeStream.destroy()\n sftp.end()\n reject(readError)\n })\n readStream.pipe(writeStream)\n}\n\n/**\n * Transfer a remote file to a local path via SFTP.\n *\n * @param client - The connected ssh2 client.\n * @param remotePath - Source path on the remote host.\n * @param localPath - Destination path on the local filesystem.\n * @param timeout - Maximum time in ms before the transfer is aborted.\n */\n// eslint-disable-next-line max-params -- timeout parameter extends the existing signature\nexport async function sftpDownload(\n client: Client,\n remotePath: string,\n localPath: string,\n timeout = SFTP_TIMEOUT\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const temporaryPath = join(dirname(localPath), `.paratix-download-${randomUUID()}.tmp`)\n let shouldCleanupTemporaryFile = false\n\n const rejectWithCleanup = (reason: Error): void => {\n if (shouldCleanupTemporaryFile) {\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n unlinkSync(temporaryPath)\n } catch {\n // Best effort cleanup: preserve the original transfer error.\n }\n }\n reject(reason)\n }\n\n client.sftp((error, sftp) => {\n if (error) {\n rejectWithCleanup(error)\n return\n }\n\n const readStream = sftp.createReadStream(remotePath)\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const writeStream = createWriteStream(temporaryPath, { mode: 0o600 })\n shouldCleanupTemporaryFile = true\n\n wireStreams({\n readStream,\n reject: rejectWithCleanup,\n resolve: () => {\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n renameSync(temporaryPath, localPath)\n resolve()\n } catch (finalizeError) {\n rejectWithCleanup(\n finalizeError instanceof Error\n ? finalizeError\n : new Error(`Failed to finalize SFTP download: ${String(finalizeError)}`)\n )\n }\n },\n sftp,\n timeout,\n timeoutMessage: `SFTP download timed out after ${timeout}ms: ${remotePath}`,\n writeStream,\n })\n })\n })\n}\n\n/**\n * Transfer a local file to a remote path via SFTP.\n *\n * @param client - The connected ssh2 client.\n * @param localPath - Source path on the local filesystem.\n * @param remotePath - Destination path on the remote host.\n * @param timeout - Maximum time in ms before the transfer is aborted.\n */\n// eslint-disable-next-line max-params -- timeout parameter extends the existing signature\nexport async function sftpUpload(\n client: Client,\n localPath: string,\n remotePath: string,\n timeout = SFTP_TIMEOUT\n): Promise<void> {\n return new Promise((resolve, reject) => {\n client.sftp((error, sftp) => {\n if (error) {\n reject(error)\n return\n }\n\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const readStream = createReadStream(localPath)\n const writeStream = sftp.createWriteStream(remotePath, { mode: 0o600 })\n\n wireStreams({\n readStream,\n reject,\n resolve,\n sftp,\n timeout,\n timeoutMessage: `SFTP upload timed out after ${timeout}ms: ${remotePath}`,\n writeStream,\n })\n })\n })\n}\n","import { createInterface } from \"node:readline\"\n\nfunction normalizePromptAbortReason(reason: unknown): Error {\n return reason instanceof Error ? reason : new Error(String(reason))\n}\n\nfunction installHiddenPromptOutput(rl: ReturnType<typeof createInterface>, question: string): void {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- readline internal API for password masking\n const rlInternal = rl as unknown as {\n _writeToOutput: (text: string) => void\n output: NodeJS.WritableStream\n }\n rlInternal._writeToOutput = (text: string) => {\n if (text.includes(question)) {\n rlInternal.output.write(text)\n }\n }\n}\n\nfunction createPromptAbortHandler(parameters: {\n abortSignal?: AbortSignal\n cleanup: () => void\n hidden: boolean\n reject: (reason?: unknown) => void\n rl: ReturnType<typeof createInterface>\n setSettled: () => void\n settled: () => boolean\n}): () => void {\n return () => {\n if (parameters.settled()) return\n parameters.setSettled()\n parameters.cleanup()\n parameters.rl.close()\n if (parameters.hidden) process.stderr.write(\"\\n\")\n parameters.reject(\n normalizePromptAbortReason(\n parameters.abortSignal?.reason ?? new Error(\"Terminal prompt aborted\")\n )\n )\n }\n}\n\n/**\n * Prompt the user for input on the terminal.\n * When `hidden` is true the typed characters are not echoed (for passwords).\n *\n * @param question - The prompt text shown to the user.\n * @param hidden - Whether to suppress character echo (password mode).\n * @param options - Optional prompt behavior.\n * @param options.abortSignal - Optional abort signal that cancels the active prompt.\n * @returns The entered string.\n */\nexport async function promptTerminal(\n question: string,\n hidden = false,\n options?: { abortSignal?: AbortSignal }\n): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stderr })\n const abortSignal = options?.abortSignal\n\n if (hidden) installHiddenPromptOutput(rl, question)\n\n return new Promise((resolve, reject) => {\n let settled = false\n\n const cleanup = (): void => {\n abortSignal?.removeEventListener(\"abort\", rejectPrompt)\n }\n\n const resolvePrompt = (answer: string): void => {\n if (settled) return\n settled = true\n cleanup()\n rl.close()\n if (hidden) process.stderr.write(\"\\n\")\n resolve(answer)\n }\n\n const rejectPrompt = createPromptAbortHandler({\n abortSignal,\n cleanup,\n hidden,\n reject,\n rl,\n setSettled: () => {\n settled = true\n },\n settled: () => settled,\n })\n\n if (abortSignal?.aborted === true) {\n rejectPrompt()\n return\n }\n\n abortSignal?.addEventListener(\"abort\", rejectPrompt, { once: true })\n\n rl.question(question, (answer) => {\n resolvePrompt(answer)\n })\n })\n}\n","import { readFile } from \"node:fs/promises\"\n\nimport type { Environment } from \"./types.js\"\n\n/**\n * Resolve a single env key to its concrete value.\n * Lazy function values are awaited; plain primitive values are returned as-is.\n *\n * @param environment - The env map to look up the key in.\n * @param key - The key to resolve.\n * @returns The resolved primitive value.\n * @throws {Error} When the key is not present in `environment`.\n */\nexport async function resolveEnvironment(\n environment: Environment,\n key: string\n): Promise<boolean | number | string> {\n const value = environment[key]\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Record index may be undefined at runtime\n if (value === undefined) {\n throw new Error(`Env key \"${key}\" is not defined`)\n }\n if (typeof value === \"function\") {\n return value()\n }\n return value\n}\n\n/**\n * Parse a `.env` file and return its contents as an {@link Environment} map.\n * Blank lines and lines starting with `#` are ignored.\n * Surrounding single or double quotes are stripped from values.\n * Double-quoted values support escape sequences (`\\\\n`, `\\\\\"`, `\\\\\\\\`).\n * Unquoted values support inline comments (`value # comment`, `value\\t# comment`).\n *\n * @param filePath - Absolute path to the `.env` file.\n * @returns The parsed env map.\n */\nexport async function loadDotEnvironment(filePath: string): Promise<Environment> {\n // eslint-disable-next-line security/detect-non-literal-fs-filename\n const content = await readFile(filePath, \"utf8\")\n const environment: Environment = {}\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue\n const eqIndex = trimmed.indexOf(\"=\")\n if (eqIndex === -1) continue\n\n const key = trimmed.slice(0, eqIndex).trim()\n environment[key] = processValue(trimmed.slice(eqIndex + 1).trim())\n }\n\n return environment\n}\n\nfunction processValue(raw: string): string {\n if (raw.length >= 2 && raw.startsWith('\"') && raw.endsWith('\"')) {\n // Double-quoted: strip quotes and process escape sequences.\n // Use \\0 as sentinel for escaped backslashes — safe because .env files never contain null bytes.\n return raw\n .slice(1, -1)\n .replaceAll(\"\\\\\\\\\", \"\\0\")\n .replaceAll(\"\\\\n\", \"\\n\")\n .replaceAll('\\\\\"', '\"')\n .replaceAll(\"\\0\", \"\\\\\")\n }\n if (raw.length >= 2 && raw.startsWith(\"'\") && raw.endsWith(\"'\")) {\n // Single-quoted: strip quotes, keep value literal\n return raw.slice(1, -1)\n }\n // Unquoted: strip inline comments after any whitespace before #\n const commentIndex = raw.search(/\\s#/v)\n return commentIndex === -1 ? raw : raw.slice(0, commentIndex).trimEnd()\n}\n\n/**\n * Merge multiple env maps left-to-right into a new object.\n * Later entries overwrite earlier ones for the same key.\n * `undefined` entries are silently skipped.\n *\n * @param environments - One or more env maps to merge.\n * @returns The merged env map.\n */\nexport function mergeEnvironment(...environments: Array<Environment | undefined>): Environment {\n const result: Environment = {}\n for (const environment of environments) {\n if (environment != null) {\n Object.assign(result, environment)\n }\n }\n return result\n}\n\n/**\n * Like {@link resolveEnvironment} but always returns a string.\n * Numbers are converted via `String()`.\n *\n * @param environment - The env map to look up the key in.\n * @param key - The key to resolve.\n * @returns The resolved value as a string.\n */\nexport async function resolveEnvironmentAsString(\n environment: Environment,\n key: string\n): Promise<string> {\n const value = await resolveEnvironment(environment, key)\n return String(value)\n}\n"],"mappings":";AAUO,SAAS,aAAa,MAAoB;AAC/C,MAAI,CAAC,WAAC,gBAAa,GAAC,EAAC,KAAK,IAAI,GAAG;AAC/B,UAAM,IAAI;AAAA,MACR,sBAAsB,IAAI;AAAA,IAC5B;AAAA,EACF;AACF;AASO,SAAS,WAAW,GAAmB;AAC5C,SAAO,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;AACvC;AAEA,IAAM,qBAAqB;AAOpB,IAAM,oBAAoB;AAEjC,SAAS,eAAe,MAAsB;AAC5C,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,aAAW,QAAQ,MAAM;AACvB,QAAI,SAAS,kBAAmB,QAAO,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC;AACjE,gBAAY,KAAK;AACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB,MAAc,OAAwB;AACpE,MAAI,QAAQ;AACZ,aAAW,SAAS,MAAM;AACxB;AACA,QAAI,QAAQ,MAAO,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAqBO,IAAM,eAAN,MAAM,sBAAqB,MAAM;AAAA;AAAA,EAEtB;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,SAAiB,YAAoB,YAAoB;AAC1E,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,aAAa;AAClB,UAAM,kBAAkB,MAAM,aAAY;AAAA,EAC5C;AACF;AAeA,IAAM,WAAW;AAEjB,SAAS,cAAc,QAA8B;AACnD,SAAO,OAAO,WAAW,aAAa,OAAO,IAAI;AACnD;AAEA,SAAS,kBAAkB,SAAmC;AAC5D,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,UAAU,SAAS;AAC5B,UAAM,SAAS,cAAc,MAAM;AACnC,QAAI,OAAO,WAAW,EAAG;AACzB,QAAI,OAAO,SAAS,QAAQ,GAAG;AAC7B,YAAM,IAAI,MAAM,sDAAsD,QAAQ,GAAG;AAAA,IACnF;AAEA,aAAS,IAAI,MAAM;AAEnB,UAAM,UAAU,mBAAmB,MAAM;AAEzC,QAAI,YAAY,OAAQ,UAAS,IAAI,OAAO;AAE5C,UAAM,SAAS,WAAW,MAAM;AAEhC,QAAI,WAAW,OAAQ,UAAS,IAAI,MAAM;AAAA,EAC5C;AACA,SAAO,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AACzD;AAEA,SAAS,0BAA0B,SAGjC;AACA,MAAI,WAA4B;AAEhC,QAAM,cAAc,MAAgB;AAClC,iBAAa,kBAAkB,OAAO;AACtC,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,eAAuB;AACrB,aAAO,KAAK,IAAI,GAAG,GAAG,YAAY,EAAE,IAAI,CAAC,YAAY,QAAQ,MAAM,CAAC;AAAA,IACtE;AAAA,IACA,KAAK,MAAsB;AACzB,UAAI,SAAS;AACb,iBAAW,WAAW,YAAY,GAAG;AACnC,iBAAS,OAAO,WAAW,SAAS,QAAQ;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,YAAY,MAAc,SAAiC;AACzE,MAAI,SAAS;AACb,QAAM,WAAW,kBAAkB,OAAO;AAC1C,aAAW,WAAW,UAAU;AAC9B,aAAS,OAAO,WAAW,SAAS,QAAQ;AAAA,EAC9C;AACA,SAAO;AACT;AAWO,SAAS,mBACd,OACA,SACsD;AACtD,QAAM,WAAW,0BAA0B,OAAO;AAClD,MAAI,UAAyB;AAC7B,QAAM,aAAa,MAAc;AAC/B,gBAAY,KAAK,IAAI,GAAG,SAAS,aAAa,IAAI,CAAC;AACnD,WAAO;AAAA,EACT;AAEA,MAAI,UAAU;AAEd,SAAO;AAAA,IACL,QAAc;AACZ,UAAI,QAAQ,SAAS,GAAG;AACtB,cAAM,SAAS,KAAK,OAAO,CAAC;AAC5B,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,KAAK,OAAqB;AACxB,YAAM,iBAAiB,WAAW;AAClC,UAAI,mBAAmB,GAAG;AACxB,cAAM,SAAS,KAAK,KAAK,CAAC;AAC1B;AAAA,MACF;AACA,iBAAW;AACX,UAAI,QAAQ,UAAU,eAAgB;AAKtC,YAAM,SAAS,SAAS,KAAK,OAAO;AACpC,UAAI,OAAO,UAAU,gBAAgB;AACnC,kBAAU;AACV;AAAA,MACF;AACA,YAAM,OAAO,MAAM,GAAG,CAAC,cAAc,CAAC;AACtC,gBAAU,OAAO,MAAM,CAAC,cAAc;AAAA,IACxC;AAAA,EACF;AACF;AAEA,SAAS,YAAY,GAAiB;AACpC,UAAQ,OAAO,MAAM,CAAC;AACxB;AACA,SAAS,YAAY,GAAiB;AACpC,UAAQ,OAAO,MAAM,CAAC;AACxB;AASO,SAAS,sBAAsB,MAAkC;AACtE,SAAO,QAAQ;AACjB;AAQO,SAAS,oBAAoB,YAA0C;AAC5E,QAAM,EAAE,SAAS,SAAS,QAAQ,SAAS,QAAQ,MAAM,IAAI;AAC7D,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,QAAM,UAAU,WAAW,WAAW,CAAC;AACvC,QAAM,eAAe,mBAAmB,CAAC,SAAS;AAChD,cAAU;AACV,QAAI,CAAC,QAAQ,OAAQ,aAAY,IAAI;AAAA,EACvC,GAAG,OAAO;AACV,QAAM,eAAe,mBAAmB,CAAC,SAAS;AAChD,cAAU;AACV,QAAI,CAAC,QAAQ,OAAQ,aAAY,IAAI;AAAA,EACvC,GAAG,OAAO;AAEV,SAAO,GAAG,QAAQ,CAAC,SAAiB;AAClC,iBAAa,KAAK,KAAK,SAAS,CAAC;AAAA,EACnC,CAAC;AACD,SAAO,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACzC,iBAAa,KAAK,KAAK,SAAS,CAAC;AAAA,EACnC,CAAC;AACD,SAAO,GAAG,SAAS,CAAC,UAAiB;AACnC,iBAAa,KAAK;AAClB,iBAAa,MAAM;AACnB,iBAAa,MAAM;AACnB,WAAO,KAAK;AAAA,EACd,CAAC;AACD,SAAO,GAAG,SAAS,CAAC,SAAiB;AACnC,iBAAa,KAAK;AAClB,iBAAa,MAAM;AACnB,iBAAa,MAAM;AACnB,UAAM,WAAW,sBAAsB,IAAI;AAC3C,QAAI,aAAa,KAAK,QAAQ,mBAAmB,MAAM;AACrD,YAAM,eACJ,uBAAuB,QAAQ,iBAAiB,KAChD,uBAAuB,QAAQ,iBAAiB;AAClD,YAAM,OAAO,eAAe,sCAAsC;AAClE;AAAA,QACE,IAAI;AAAA,UACF,iCAAiC,QAAQ,KAAK,YAAY,SAAS,OAAO,CAAC;AAAA,UAAa,eAAe,MAAM,CAAC;AAAA,UAAa,eAAe,MAAM,CAAC,GAAG,IAAI;AAAA,UACxJ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AACA,YAAQ,EAAE,MAAM,UAAU,QAAQ,OAAO,CAAC;AAAA,EAC5C,CAAC;AACH;AA8BA,SAAS,mBAAmB,YAA8C;AACxE,QAAM,EAAE,OAAO,cAAc,MAAM,cAAc,UAAU,MAAM,YAAY,SAAS,IACpF;AACF,QAAM,gBAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,EACF;AACA,MAAI,cAAc,MAAM;AACtB,kBAAc,aAAa;AAAA,EAC7B;AACA,MAAI,SAAS,MAAM;AACjB,kBAAc,QAAQ;AAAA,EACxB;AACA,MAAI,iBAAiB,MAAM;AACzB,kBAAc,eAAe;AAAA,EAC/B;AACA,MAAI,OAAO,aAAa,UAAU;AAChC,kBAAc,WAAW;AACzB,kBAAc,cAAc;AAAA,EAC9B;AACA,MAAI,gBAAgB,MAAM;AACxB,kBAAc,eAAe;AAAA,EAC/B;AACA,SAAO;AACT;AAOA,eAAsB,iBAAiB,YAA8C;AACnF,QAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,QAAM,gBAAgB,mBAAmB,UAAU;AACnD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,IAAI;AACX,aAAO,IAAI,MAAM,8BAA8B,IAAI,EAAE,CAAC;AAAA,IACxD,GAAG,kBAAkB;AAErB,WAAO,GAAG,SAAS,MAAM;AACvB,mBAAa,OAAO;AACpB,cAAQ;AAAA,IACV,CAAC;AACD,WAAO,GAAG,SAAS,CAAC,UAAiB;AACnC,mBAAa,OAAO;AACpB,aAAO,KAAK;AAAA,IACd,CAAC;AAED,WAAO,QAAQ,aAAa;AAAA,EAC9B,CAAC;AACH;;;ACpXA,IAAM,wBAAwB;AAC9B,IAAM,eAAe;AAOrB,SAAS,aAAa,OAAwB;AAC5C,SAAO,UAAU,OAAO,SAAS,OAAO;AAC1C;AAEA,SAAS,cAAc,OAAwB;AAC7C,SAAO,UAAU,gBAAgB,UAAU,QAAQ,UAAU;AAC/D;AAEA,SAAS,SAAS,OAAiD;AACjE,SAAO,OAAO,eAAe,KAAK,MAAM,OAAO,aAAa,OAAO,eAAe,KAAK,MAAM;AAC/F;AAEA,SAAS,6BACP,QACA,KACA,QACM;AACN,MAAI,EAAE,OAAO,WAAW,OAAO,GAAG,KAAK,KAAM;AAC7C,MAAI,OAAO,OAAO,GAAG,MAAM,WAAW;AACpC,WAAO;AAAA,MACL,yBAAyB,GAAG,4BAA4B,aAAa,OAAO,GAAG,CAAC,CAAC;AAAA,IACnF;AAAA,EACF;AACF;AAEA,SAAS,4BAA4B,YAK5B;AACP,QAAM,EAAE,QAAQ,KAAK,QAAQ,QAAQ,IAAI;AACzC,MAAI,EAAE,OAAO,WAAW,OAAO,GAAG,KAAK,KAAM;AAC7C,QAAM,QAAQ,OAAO,GAAG;AACxB,QAAM,kBAAkB,oBAAoB,KAAK;AACjD,MAAI,mBAAmB,MAAM;AAC3B,WAAO,KAAK,yBAAyB,GAAG,2BAA2B,aAAa,KAAK,CAAC,GAAG;AACzF;AAAA,EACF;AACA,MAAI,SAAS,YAAY,QAAQ,CAAC,OAAO,UAAU,eAAe,GAAG;AACnE,WAAO,KAAK,iBAAiB,GAAG,sBAAsB;AACtD;AAAA,EACF;AACA,MAAI,SAAS,aAAa,QAAQ,mBAAmB,GAAG;AACtD,WAAO,KAAK,iBAAiB,GAAG,0BAA0B;AAAA,EAC5D;AACF;AAEA,SAAS,oBAAoB,OAA+B;AAC1D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEO,SAAS,eAAe,OAAiC;AAC9D,SAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,KAAK,SAAS;AACxF;AAEA,SAAS,4BACP,QACA,KACA,QACM;AACN,MAAI,EAAE,OAAO,WAAW,OAAO,GAAG,KAAK,KAAM;AAC7C,MAAI,OAAO,OAAO,GAAG,MAAM,UAAU;AACnC,WAAO,KAAK,yBAAyB,GAAG,2BAA2B,aAAa,OAAO,GAAG,CAAC,CAAC,GAAG;AAC/F;AAAA,EACF;AACA,MAAI,OAAO,GAAG,EAAE,WAAW,GAAG;AAC5B,WAAO,KAAK,iBAAiB,GAAG,+BAA+B;AAAA,EACjE;AACF;AAEA,SAAS,mBAAmB,KAA8B,QAAwB;AAChF,MAAI,EAAE,WAAW,MAAM;AACrB,WAAO,KAAK,+CAA+C;AAC3D;AAAA,EACF;AACA,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,GAAG;AAC7B,WAAO,KAAK,qDAAqD,aAAa,IAAI,KAAK,CAAC,GAAG;AAC3F;AAAA,EACF;AACA,MAAI,IAAI,MAAM,WAAW,GAAG;AAC1B,WAAO,KAAK,wCAAwC;AACpD;AAAA,EACF;AACA,aAAW,CAAC,OAAO,IAAI,KAAK,IAAI,MAAM,QAAQ,GAAG;AAC/C,QAAI,CAAC,eAAe,IAAI,GAAG;AACzB,aAAO,KAAK,uBAAuB,KAAK,2CAA2C;AAAA,IACrF;AAAA,EACF;AACF;AAEA,SAAS,0BAA0B,KAA8B,QAAwB;AACvF,MAAI,EAAE,UAAU,MAAM;AACpB,WAAO,KAAK,+CAA+C;AAC3D;AAAA,EACF;AACA,MAAI,OAAO,IAAI,SAAS,UAAU;AAChC,WAAO,KAAK,qDAAqD,aAAa,IAAI,IAAI,CAAC,GAAG;AAC1F;AAAA,EACF;AACA,MAAI,IAAI,KAAK,WAAW,GAAG;AACzB,WAAO,KAAK,iDAAiD;AAAA,EAC/D;AACF;AAEA,SAAS,mCAAmC,KAA8B,QAAwB;AAChG,MAAI,EAAE,2BAA2B,QAAQ,IAAI,yBAAyB,KAAM;AAC5E,MAAI,OAAO,IAAI,0BAA0B,YAAY,CAAC,cAAc,IAAI,qBAAqB,GAAG;AAC9F,WAAO,KAAK,qBAAqB;AAAA,EACnC;AACF;AAEA,SAAS,8BAA8B,KAA8B,QAAwB;AAC3F,8BAA4B,KAAK,cAAc,MAAM;AACrD,8BAA4B,KAAK,2BAA2B,MAAM;AAClE,8BAA4B,KAAK,yBAAyB,MAAM;AAChE,+BAA6B,KAAK,gBAAgB,MAAM;AACxD,+BAA6B,KAAK,oBAAoB,MAAM;AAC5D,8BAA4B;AAAA,IAC1B;AAAA,IACA,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,EAAE,UAAU,KAAK;AAAA,EAC5B,CAAC;AACD,8BAA4B;AAAA,IAC1B;AAAA,IACA,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,EAAE,SAAS,MAAM,UAAU,KAAK;AAAA,EAC3C,CAAC;AACD,qCAAmC,KAAK,MAAM;AAChD;AAEO,SAAS,uBAAuB,OAA0B;AAC/D,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU,MAAM;AAClB,WAAO,KAAK,oDAAoD;AAChE,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,KAAK,gDAAgD,aAAa,KAAK,CAAC,GAAG;AAClF,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,KAAK,sDAAsD;AAClE,WAAO;AAAA,EACT;AACA,QAAM,MAAM;AACZ,qBAAmB,KAAK,MAAM;AAC9B,4BAA0B,KAAK,MAAM;AACrC,gCAA8B,KAAK,MAAM;AACzC,SAAO;AACT;AAEA,SAAS,kCAAkC,OAAuB;AAChE,QAAM,eAAuC;AAAA,IAC3C,iDAAiD;AAAA,IACjD,sEACE;AAAA,IACF,oEACE;AAAA,IACF,0CAA0C;AAAA,IAC1C,yDACE;AAAA,IACF,mDAAmD;AAAA,IACnD,CAAC,qBAAqB,GAAG;AAAA,EAC3B;AACA,SAAO,aAAa,KAAK,KAAK;AAChC;AAEO,SAAS,kBAAkB,KAAsB;AACtD,QAAM,SAAS,uBAAuB,GAAG;AACzC,MAAI,OAAO,WAAW,EAAG;AACzB,QAAM,IAAI,MAAM,qBAAqB,kCAAkC,OAAO,CAAC,CAAC,CAAC,EAAE;AACrF;;;AC5KA,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAO3B,SAAS,iBAAiB,MAA+B;AACvD,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS;AACnD;AAEA,SAAS,mBACP,OACA,mBACiC;AACjC,MAAI,qBAAqB,KAAM,QAAO;AACtC,MAAI,OAAO,UAAU,UAAW,QAAO;AACvC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO;AACT;AAEA,SAAS,2BACP,OAC0C;AAC1C,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAO,YAAY;AACjB,YAAM,QAAQ,QAAQ;AACtB,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACA,SAAO,YAAY;AACjB,UAAM,QAAQ,QAAQ;AACtB,WAAO;AAAA,EACT;AACF;AAEA,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,gCAAgC,WAA0C;AACjF,MAAI,CAAC,iBAAiB,UAAU,IAAI,GAAG;AACrC,UAAM,IAAI,UAAU,yDAAyD;AAAA,EAC/E;AAEA,MAAI,OAAO,UAAU,YAAY,YAAY;AAC3C,UAAM,IAAI,UAAU,wEAAwE;AAAA,EAC9F;AAEA,MACE,UAAU,cAAc,aACxB,UAAU,cAAc,YACxB,UAAU,cAAc,UACxB;AACA,UAAM,IAAI,UAAU,sEAAsE;AAAA,EAC5F;AACF;AAEA,SAAS,6BAA6B,WAA0C;AAC9E,MAAI,CAAC,eAAe,UAAU,IAAI,GAAG;AACnC,UAAM,IAAI,UAAU,2EAA2E;AAAA,EACjG;AACF;AAEA,SAAS,+BAA+B,WAA0C;AAChF,MAAI,OAAO,UAAU,SAAS,YAAY,UAAU,KAAK,WAAW,GAAG;AACrE,UAAM,IAAI,UAAU,iEAAiE;AAAA,EACvF;AACF;AAEO,SAAS,gBACd,MACA,OACA,WACsB;AACtB,MAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B,UAAM,IAAI,UAAU,gDAAgD;AAAA,EACtE;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,SAAS,2BAA2B,KAAK;AAAA,IACzC,WAAW,mBAAmB,OAAO,SAAS;AAAA,EAChD;AACF;AAEO,SAAS,aAAa,MAAiC;AAC5D,MAAI,CAAC,eAAe,IAAI,GAAG;AACzB,UAAM,IAAI,UAAU,mEAAmE;AAAA,EACzF;AACA,SAAO,EAAE,MAAM,aAAa,KAAK;AACnC;AAEO,SAAS,eAAe,MAAmC;AAChE,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,UAAU,kDAAkD;AAAA,EACxE;AACA,SAAO,EAAE,MAAM,MAAM,iBAAiB;AACxC;AAEO,SAAS,mBAA0C;AACxD,SAAO,EAAE,MAAM,mBAAmB;AACpC;AAEO,IAAM,OAAO;AAAA,EAClB,KAAK;AAAA,EACL,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,cAAc;AAChB;AAEO,SAAS,uBAAuB,OAAuD;AAC5F,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,6BACd,OACqC;AACrC,SAAO,uBAAuB,KAAK,KAAK,MAAM,cAAc;AAC9D;AAEO,SAAS,6BACd,OACqC;AACrC,SAAO,uBAAuB,KAAK,KAAK,MAAM,cAAc;AAC9D;AAEO,SAAS,8BACd,OACsC;AACtC,SAAO,uBAAuB,KAAK,KAAK,MAAM,cAAc;AAC9D;AAEO,SAAS,2BACd,OACmC;AACnC,SAAO,uBAAuB,KAAK;AACrC;AAEO,SAAS,oBAAoB,OAAoD;AACtF,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,sBAAsB,OAAsD;AAC1F,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,wBAAwB,OAAwD;AAC9F,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,2BAA2B,OAAkD;AAC3F,MAAI,CAACA,UAAS,KAAK,GAAG;AACpB,UAAM,IAAI,UAAU,4CAA4C,OAAO,KAAK,EAAE;AAAA,EAChF;AAEA,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,OAAO;AACV,sCAAgC,KAAK;AACrC;AAAA,IACF;AAAA,IACA,KAAK,aAAa;AAChB,mCAA6B,KAAK;AAClC;AAAA,IACF;AAAA,IACA,KAAK,kBAAkB;AACrB,qCAA+B,KAAK;AACpC;AAAA,IACF;AAAA,IACA,KAAK,oBAAoB;AACvB;AAAA,IACF;AAAA,IACA,SAAS;AACP,YAAM,IAAI,UAAU,4BAA4B,OAAO,MAAM,IAAI,CAAC,EAAE;AAAA,IACtE;AAAA,EACF;AACF;AAEO,SAAS,6BAA6B,SAA8C;AACzF,MAAI,WAAW,KAAM;AACrB,aAAW,SAAS,SAAS;AAC3B,+BAA2B,KAAK;AAAA,EAClC;AACF;AAEA,eAAsB,yBACpB,aACA,SACsB;AACtB,MAAI,WAAW,QAAQ,QAAQ,WAAW,GAAG;AAC3C,UAAM,QAAQ,QAAQ;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,EAAE,GAAG,YAAY;AACzC,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,uBAAuB,KAAK,EAAG;AACpC,oBAAgB,MAAM,IAAI,IAAI,YAAY,MAAM,QAAQ;AAAA,EAC1D;AACA,QAAM,QAAQ,QAAQ;AACtB,SAAO;AACT;AAEO,SAAS,yBAAyB,aAA6C;AACpF,SAAO,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,gBAAgB,MAAM,KAAK,CAAC;AACxF;AAEO,SAAS,6BACd,UACA,SAC+B;AAC/B,QAAM,UAA6B,CAAC;AACpC,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,EAAE,OAAO,aAAa,QAAQ,GAAG,MAAM,SAAS,GAAG,GAAG;AACxD,cAAQ,KAAK,gBAAgB,KAAK,QAAQ,GAAG,CAAC,CAAC;AAAA,IACjD;AAAA,EACF;AACA,SAAO,QAAQ,WAAW,IAAI,SAAY;AAC5C;;;ACtOA,SAAS,cAAAC,aAAY,mBAAAC,wBAAuB;AAC5C,SAAS,cAAAC,aAAY,qBAAqB;AAC1C,SAAS,UAAU,YAAY;AAC/B,SAAS,WAAAC,UAAS,cAAc;AAChC,SAAS,QAAAC,OAAM,aAAa;AAC5B,SAAS,cAAkC;;;ACN3C,SAAS,YAAY,YAAY,uBAAuB;AACxD,SAAS,oBAAoB;AAC7B,SAAS,YAAY,aAAa;AAClC,SAAS,eAAe;AACxB,SAAS,YAAY;AAoBd,IAAM,2BAAN,MAAM,kCAAiC,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAClC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,UAAM,kBAAkB,MAAM,yBAAwB;AAAA,EACxD;AACF;AAeA,IAAM,yBAAyB;AAG/B,IAAM,mBAAmB;AAGzB,IAAM,cAAc;AACpB,IAAM,oBAAoB;AAM1B,IAAM,mBAAmB,oBAAI,IAAoB;AASjD,SAAS,oBAAoB,MAAgC;AAC3D,QAAM,QAAQ,KAAK,MAAM,WAAC,QAAI,GAAC;AAC/B,QAAM,SAAS,MAAM,CAAC,GAAG,WAAW,GAAG,IAAI,IAAI;AAC/C,MAAI,MAAM,SAAS,yBAAyB,OAAQ,QAAO,CAAC;AAE5D,QAAM,SAAS,WAAW,IAAI,MAAM,CAAC,IAAI;AACzC,QAAM,YAAY,MAAM,MAAM;AAC9B,QAAM,OAAO,MAAM,SAAS,CAAC;AAC7B,QAAM,YAAY,MAAM,SAAS,CAAC;AAElC,QAAM,MAAM,OAAO,KAAK,WAAW,QAAQ;AAC3C,SAAO,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,MAAM,KAAK,OAAO,EAAE;AACzE;AAWO,SAAS,gBAAgB,SAAmC;AACjE,QAAM,UAA4B,CAAC;AACnC,aAAW,OAAO,QAAQ,MAAM,IAAI,GAAG;AACrC,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,KAAK,WAAW,KAAK,KAAK,WAAW,GAAG,EAAG;AAC/C,YAAQ,KAAK,GAAG,oBAAoB,IAAI,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAYA,SAAS,iBAAiB,MAAc,MAAsB;AAC5D,SAAO,SAAS,mBAAmB,OAAO,IAAI,IAAI,KAAK,IAAI;AAC7D;AASA,SAAS,kBAAkB,SAAiB,QAAyB;AACnE,MAAI,CAAC,QAAQ,WAAW,KAAK,EAAG,QAAO;AAEvC,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,WAAW,qBAAqB,MAAM,CAAC,MAAM,IAAK,QAAO;AAEnE,QAAM,OAAO,OAAO,KAAK,MAAM,CAAC,KAAK,IAAI,QAAQ;AACjD,QAAM,eAAe,OAAO,KAAK,MAAM,CAAC,KAAK,IAAI,QAAQ;AACzD,MAAI,KAAK,WAAW,KAAK,aAAa,WAAW,EAAG,QAAO;AAE3D,QAAM,aAAa,WAAW,QAAQ,IAAI,EAAE,OAAO,MAAM,EAAE,OAAO;AAClE,SAAO,WAAW,WAAW,aAAa,UAAU,gBAAgB,YAAY,YAAY;AAC9F;AAEA,SAAS,oBACP,SACA,MACA,MACkB;AAClB,QAAM,SAAS,iBAAiB,MAAM,IAAI;AAC1C,SAAO,QAAQ,OAAO,CAAC,UAAU,MAAM,SAAS,UAAU,kBAAkB,MAAM,MAAM,MAAM,CAAC;AACjG;AAaA,SAAS,iBAAiB,SAA2B,KAAyC;AAC5F,SAAO,QAAQ;AAAA,IACb,CAAC,UACC,MAAM,WAAW,cACjB,MAAM,IAAI,WAAW,IAAI,UACzB,gBAAgB,MAAM,KAAK,GAAG;AAAA,EAClC;AACF;AAEA,SAAS,qBAAqB,MAAc,cAAsB,aAA6B;AAC7F,QAAM,gBAAgB,mBAAmB,YAAY;AACrD,QAAM,oBACJ,eAAe,OACX,4DACA,oBAAoB,aAAa,4CAA4C,mBAAmB,WAAW,CAAC;AAClH,QAAM,IAAI;AAAA,IACR,oCAAoC,IAAI,KAAK,iBAAiB;AAAA,EAEhE;AACF;AAEA,SAAS,iCAAiC,YAK9B;AACV,QAAM,EAAE,WAAW,aAAa,MAAM,IAAI,IAAI;AAC9C,QAAM,aAAa,iBAAiB,aAAa,GAAG;AACpD,MAAI,cAAc,MAAM;AACtB,UAAM,IAAI;AAAA,MACR,oCAAoC,IAAI,sBAAsB,mBAAmB,WAAW,GAAG,CAAC;AAAA,IAClG;AAAA,EACF;AAEA,QAAM,oBAAoB,YAAY,OAAO,CAAC,UAAU,MAAM,WAAW,UAAU;AACnF,QAAM,gBAAgB,kBAAkB;AAAA,IACtC,CAAC,UAAU,MAAM,IAAI,WAAW,IAAI,UAAU,gBAAgB,MAAM,KAAK,GAAG;AAAA,EAC9E;AACA,MAAI,iBAAiB,KAAM,QAAO;AAClC,MAAI,WAAW,WAAW,IAAI,UAAU,gBAAgB,WAAW,GAAG,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,kBAAkB,GAAG,CAAC,GAAG;AACpD,MAAI,sBAAsB,KAAM,sBAAqB,MAAM,KAAK,kBAAkB;AAClF,MAAI,aAAa,KAAM,sBAAqB,MAAM,KAAK,SAAS;AAChE,SAAO;AACT;AAmBO,SAAS,mBAAmB,WAA2B;AAC5D,MAAI,UAAU,SAAS,aAAa;AAClC,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,QAAM,aAAa,UAAU,aAAa,CAAC;AAC3C,MAAI,eAAe,KAAK,cAAc,aAAa,UAAU,QAAQ;AACnE,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AACA,SAAO,UAAU,SAAS,aAAa,cAAc,UAAU,EAAE,SAAS,OAAO;AACnF;AAYO,SAAS,mBAAmB,KAAqB;AACtD,QAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,QAAQ;AAE7D,SAAO,UAAU,KAAK,WAAW,KAAK,EAAE,CAAC;AAC3C;AAYA,eAAsB,cAAc,MAAc,MAAc,WAAkC;AAChG,QAAM,YAAY,iBAAiB,MAAM,IAAI;AAC7C,QAAM,OAAO,mBAAmB,SAAS;AACzC,QAAM,YAAY,UAAU,SAAS,QAAQ;AAC7C,QAAM,OAAO,GAAG,SAAS,IAAI,IAAI,IAAI,SAAS;AAAA;AAE9C,QAAM,eAAe,KAAK,QAAQ,GAAG,MAAM;AAC3C,QAAM,WAAW,KAAK,cAAc,aAAa;AAGjD,QAAM,MAAM,cAAc,EAAE,MAAM,KAAO,WAAW,KAAK,CAAC;AAE1D,QAAM,WAAW,UAAU,MAAM,EAAE,MAAM,IAAM,CAAC;AAClD;AAOA,SAAS,uBAAyC;AAChD,QAAM,WAAW,KAAK,QAAQ,GAAG,QAAQ,aAAa;AACtD,MAAI,UAAU;AACd,MAAI;AAEF,cAAU,aAAa,UAAU,MAAM;AAAA,EACzC,QAAQ;AAAA,EAER;AACA,SAAO,gBAAgB,OAAO;AAChC;AAeA,eAAe,wBAAwB,MAAc,MAAc,KAA4B;AAC7F,MAAI;AACF,UAAM,OAAO,mBAAmB,GAAG;AACnC,UAAM,cAAc,mBAAmB,GAAG;AAC1C,YAAQ,OAAO;AAAA,MACb,+BAA+B,IAAI,MAAM,IAAI,8CAC3B,WAAW;AAAA;AAAA,IAC/B;AAAA,EACF,QAAQ;AACN,YAAQ,OAAO,MAAM,+BAA+B,IAAI;AAAA,CAAiC;AAAA,EAC3F;AACA,mBAAiB,IAAI,iBAAiB,MAAM,IAAI,GAAG,GAAG;AACtD,MAAI;AACF,UAAM,cAAc,MAAM,MAAM,GAAG;AAAA,EACrC,SAAS,OAAgB;AACvB,UAAM,mBACJ,SAAS,mBAAmB,WAAW,IAAI,IAAI,MAAM,IAAI,IAAI,WAAW,IAAI,CAAC;AAC/E,YAAQ,OAAO;AAAA,MACb,2CAA2C,IAAI,uHAG9B,gBAAgB,2BAC5B,OAAO,KAAK,CAAC;AAAA;AAAA,IACpB;AAAA,EACF;AACF;AAEA,SAAS,yBAAyB,WAA2B;AAC3D,QAAM,QAAQ,UAAU,KAAK,EAAE,MAAM,WAAC,QAAI,GAAC;AAC3C,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI,MAAM,qEAAqE;AAAA,EACvF;AACA,QAAM,CAAC,WAAW,GAAG,IAAI;AACzB,SAAO,GAAG,SAAS,IAAI,GAAG;AAC5B;AAEA,SAAS,yBAAyB,KAAqB;AACrD,SAAO,GAAG,mBAAmB,GAAG,CAAC,IAAI,IAAI,SAAS,QAAQ,CAAC;AAC7D;AAEA,SAAS,yBAAyB,SAAwC;AACxE,SAAO,SAAS,2BAA2B,QAAQ,SAAS,yBAAyB;AACvF;AAEA,SAAS,oBAAoB,MAAc,KAAa,SAAoC;AAC1F,QAAM,8BACJ,QAAQ,yBAAyB,OAC7B,OACA,yBAAyB,QAAQ,qBAAqB;AAC5D,QAAM,sBAAsB,QAAQ,2BAA2B;AAC/D,QAAM,qBAAqB,yBAAyB,GAAG;AACvD,QAAM,uBAAuB,mBAAmB,GAAG;AAEnD,MACE,gCAAgC,sBAChC,wBAAwB,sBACxB;AACA;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,oCAAoC,IAAI;AAAA,EAC1C;AACF;AAkBO,SAAS,kBACd,MACA,UACA,UAA+B,CAAC,GACZ;AACpB,QAAM,EAAE,MAAM,KAAK,IAAI;AACvB,MAAI,SAAS,QAAQ,CAAC,yBAAyB,OAAO,EAAG,QAAO,CAAC;AAEjE,QAAM,UAAU,qBAAqB;AACrC,QAAM,cAAc,oBAAoB,SAAS,MAAM,IAAI;AAC3D,QAAM,YAAY,iBAAiB,IAAI,iBAAiB,MAAM,IAAI,CAAC,KAAK;AAExE,QAAM,SAA0E;AAAA,IAC9E,aAAa,KAAsB;AACjC,UACE,SAAS,QACT,iCAAiC,EAAE,WAAW,aAAa,MAAM,IAAI,CAAC,GACtE;AACA,YAAI,yBAAyB,OAAO,EAAG,qBAAoB,MAAM,KAAK,OAAO;AAC7E,eAAO;AAAA,MACT;AACA,UAAI,yBAAyB,OAAO,GAAG;AACrC,4BAAoB,MAAM,KAAK,OAAO;AACtC,eAAO;AAAA,MACT;AACA,UAAI,SAAS,OAAO;AAClB,cAAM,IAAI;AAAA,UACR,gBAAgB,IAAI;AAAA,QAEtB;AAAA,MACF;AACA,UAAI,SAAS,KAAM,QAAO;AAE1B,aAAO,iBAAiB,wBAAwB,MAAM,MAAM,GAAG;AAC/D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACpaA,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB,mBAAmB,YAAY,kBAAkB;AAC5E,SAAS,SAAS,QAAAC,aAAY;AAGvB,IAAM,eAAe;AAc5B,SAAS,YAAY,SAQZ;AACP,QAAM,EAAE,YAAY,QAAQ,SAAS,MAAM,SAAS,gBAAgB,YAAY,IAAI;AAEpF,MAAI,UAAU;AAEd,QAAM,QAAQ,WAAW,MAAM;AAC7B,QAAI,QAAS;AACb,cAAU;AACV,eAAW,QAAQ;AACnB,gBAAY,QAAQ;AACpB,SAAK,IAAI;AACT,WAAO,IAAI,MAAM,cAAc,CAAC;AAAA,EAClC,GAAG,OAAO;AAEV,cAAY,GAAG,SAAS,MAAM;AAC5B,iBAAa,KAAK;AAClB,QAAI,QAAS;AACb,cAAU;AACV,SAAK,IAAI;AACT,YAAQ;AAAA,EACV,CAAC;AACD,cAAY,GAAG,SAAS,CAAC,eAAsB;AAC7C,iBAAa,KAAK;AAClB,QAAI,QAAS;AACb,cAAU;AACV,eAAW,QAAQ;AACnB,QAAI,OAAO,YAAY,YAAY,WAAY,aAAY,QAAQ;AACnE,SAAK,IAAI;AACT,WAAO,UAAU;AAAA,EACnB,CAAC;AACD,aAAW,GAAG,SAAS,CAAC,cAAqB;AAC3C,iBAAa,KAAK;AAClB,QAAI,QAAS;AACb,cAAU;AACV,QAAI,OAAO,WAAW,YAAY,WAAY,YAAW,QAAQ;AACjE,QAAI,OAAO,YAAY,YAAY,WAAY,aAAY,QAAQ;AACnE,SAAK,IAAI;AACT,WAAO,SAAS;AAAA,EAClB,CAAC;AACD,aAAW,KAAK,WAAW;AAC7B;AAWA,eAAsB,aACpB,QACA,YACA,WACA,UAAU,cACK;AACf,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,gBAAgBA,MAAK,QAAQ,SAAS,GAAG,qBAAqB,WAAW,CAAC,MAAM;AACtF,QAAI,6BAA6B;AAEjC,UAAM,oBAAoB,CAAC,WAAwB;AACjD,UAAI,4BAA4B;AAC9B,YAAI;AAEF,qBAAW,aAAa;AAAA,QAC1B,QAAQ;AAAA,QAER;AAAA,MACF;AACA,aAAO,MAAM;AAAA,IACf;AAEA,WAAO,KAAK,CAAC,OAAO,SAAS;AAC3B,UAAI,OAAO;AACT,0BAAkB,KAAK;AACvB;AAAA,MACF;AAEA,YAAM,aAAa,KAAK,iBAAiB,UAAU;AAEnD,YAAM,cAAc,kBAAkB,eAAe,EAAE,MAAM,IAAM,CAAC;AACpE,mCAA6B;AAE7B,kBAAY;AAAA,QACV;AAAA,QACA,QAAQ;AAAA,QACR,SAAS,MAAM;AACb,cAAI;AAEF,uBAAW,eAAe,SAAS;AACnC,oBAAQ;AAAA,UACV,SAAS,eAAe;AACtB;AAAA,cACE,yBAAyB,QACrB,gBACA,IAAI,MAAM,qCAAqC,OAAO,aAAa,CAAC,EAAE;AAAA,YAC5E;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,QACA,gBAAgB,iCAAiC,OAAO,OAAO,UAAU;AAAA,QACzE;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;AAWA,eAAsB,WACpB,QACA,WACA,YACA,UAAU,cACK;AACf,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAO,KAAK,CAAC,OAAO,SAAS;AAC3B,UAAI,OAAO;AACT,eAAO,KAAK;AACZ;AAAA,MACF;AAGA,YAAM,aAAa,iBAAiB,SAAS;AAC7C,YAAM,cAAc,KAAK,kBAAkB,YAAY,EAAE,MAAM,IAAM,CAAC;AAEtE,kBAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,gBAAgB,+BAA+B,OAAO,OAAO,UAAU;AAAA,QACvE;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;;;AChLA,SAAS,uBAAuB;AAEhC,SAAS,2BAA2B,QAAwB;AAC1D,SAAO,kBAAkB,QAAQ,SAAS,IAAI,MAAM,OAAO,MAAM,CAAC;AACpE;AAEA,SAAS,0BAA0B,IAAwC,UAAwB;AAEjG,QAAM,aAAa;AAInB,aAAW,iBAAiB,CAAC,SAAiB;AAC5C,QAAI,KAAK,SAAS,QAAQ,GAAG;AAC3B,iBAAW,OAAO,MAAM,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,yBAAyB,YAQnB;AACb,SAAO,MAAM;AACX,QAAI,WAAW,QAAQ,EAAG;AAC1B,eAAW,WAAW;AACtB,eAAW,QAAQ;AACnB,eAAW,GAAG,MAAM;AACpB,QAAI,WAAW,OAAQ,SAAQ,OAAO,MAAM,IAAI;AAChD,eAAW;AAAA,MACT;AAAA,QACE,WAAW,aAAa,UAAU,IAAI,MAAM,yBAAyB;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACF;AAYA,eAAsB,eACpB,UACA,SAAS,OACT,SACiB;AACjB,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,QAAM,cAAc,SAAS;AAE7B,MAAI,OAAQ,2BAA0B,IAAI,QAAQ;AAElD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,UAAU;AAEd,UAAM,UAAU,MAAY;AAC1B,mBAAa,oBAAoB,SAAS,YAAY;AAAA,IACxD;AAEA,UAAM,gBAAgB,CAAC,WAAyB;AAC9C,UAAI,QAAS;AACb,gBAAU;AACV,cAAQ;AACR,SAAG,MAAM;AACT,UAAI,OAAQ,SAAQ,OAAO,MAAM,IAAI;AACrC,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,eAAe,yBAAyB;AAAA,MAC5C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,MAAM;AAChB,kBAAU;AAAA,MACZ;AAAA,MACA,SAAS,MAAM;AAAA,IACjB,CAAC;AAED,QAAI,aAAa,YAAY,MAAM;AACjC,mBAAa;AACb;AAAA,IACF;AAEA,iBAAa,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAEnE,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,oBAAc,MAAM;AAAA,IACtB,CAAC;AAAA,EACH,CAAC;AACH;;;AHnEA,SAAS,mBAAmB,WAAmB,MAAc,QAAwB;AACnF,QAAM,sBAAsB,cAAc,MAAM,KAAK;AACrD,QAAM,iBAAiB,GAAG,mBAAmB,IAAI,MAAM;AACvD,MAAI,CAAC,KAAK,WAAW,cAAc,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,GAAG,GAAG;AACjF,UAAM,IAAI,MAAM,6BAA6B,IAAI,EAAE;AAAA,EACrD;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAsB;AAC5C,MAAI,SAAS,IAAK,QAAOC,SAAQ;AACjC,MAAI,KAAK,WAAW,IAAI,EAAG,QAAOC,MAAKD,SAAQ,GAAG,KAAK,MAAM,CAAC,CAAC;AAC/D,SAAO;AACT;AAEA,SAAS,qBACP,YACA,SACQ;AACR,MAAI,SAAS,QAAQ,MAAM;AACzB,UAAM,IAAI;AAAA,MACR,mBAAmB,UAAU;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI;AACF,iBAAa,QAAQ,IAAI;AAAA,EAC3B,SAAS,OAAO;AACd,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,UAAM,IAAI;AAAA,MACR,mBAAmB,UAAU,2BAA2B,QAAQ,IAAI,MAAM,MAAM;AAAA,MAChF,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,QAAQ;AACjB;AAEA,IAAM,kBAAkB;AACxB,IAAM,iCAAiC;AACvC,IAAM,4BAA4B;AAClC,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAUrB,IAAM,oBAAN,MAAiD;AAAA,EAC9C,cAA6B;AAAA,EAC7B,aAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWzB,qBAAoC;AAAA,EACpC,SAAwB;AAAA,EACf;AAAA,EACT,gBAAgB;AAAA,EACP,iBAAiB,oBAAI,IAA6B;AAAA,EAC3D,gBAA+B;AAAA,EAC/B;AAAA,EACS;AAAA,EACT,mBAAyC;AAAA,EACzC,YAAY;AAAA,EACZ,kBAAiC;AAAA,EAElC,YAAY,MAAc,QAAmB;AAClD,SAAK,UAAU;AAAA,MACb;AAAA,MACA,OAAO,CAAC,GAAG,OAAO,KAAK;AAAA,IACzB;AACA,SAAK,SAAS;AAAA,MACZ,GAAG;AAAA,MACH,OAAO,CAAC,GAAG,OAAO,KAAK;AAAA,IACzB;AACA,QACE,OAAO,gBAAgB,SACtB,OAAO,aAAa,SAAS,IAAI,KAAK,OAAO,aAAa,SAAS,IAAI,IACxE;AACA,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AACA,SAAK,qBAAqB,OAAO,gBAAgB,OAAO,OAAO,OAAO,KAAK,OAAO,YAAY;AAAA,EAChG;AAAA,EAEO,QAAQ,MAAoB;AACjC,QAAI,CAAC,KAAK,QAAQ,MAAM,SAAS,IAAI,EAAG,MAAK,QAAQ,MAAM,KAAK,IAAI;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAa,QAAQ,SAAwC;AAC3D,SAAK,oBAAoB,SAAS;AAClC,QAAI,KAAK,OAAO,cAAc,MAAM;AAClC,YAAM,KAAK,gBAAgB,OAAO;AAClC;AAAA,IACF;AACA,UAAM,KAAK,qBAAqB,OAAO;AAAA,EACzC;AAAA,EAEO,aAAmB;AACxB,SAAK,oBAAoB;AACzB,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEA,MAAa,aAAa,YAAoB,WAAkC;AAC9E,UAAM,SAAS,KAAK,aAAa;AACjC,QAAI,aAAa;AACjB,QAAI;AACF,UAAI,KAAK,OAAO,SAAS,QAAQ;AAC/B,qBAAa,MAAM,KAAK;AAAA,UACtB;AAAA,UACA;AAAA,QACF;AACA,cAAM,KAAK,KAAK,OAAO,WAAW,UAAU,CAAC,MAAM,WAAW,UAAU,CAAC,IAAI;AAAA,UAC3E,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,YAAM,aAAa,QAAQ,YAAY,SAAS;AAAA,IAClD,UAAE;AACA,UAAI,eAAe,YAAY;AAC7B,YAAI;AACF,gBAAM,KAAK,sBAAsB,UAAU;AAAA,QAC7C,SAAS,cAAc;AACrB,kBAAQ,OAAO;AAAA,YACb,uCAAuC,UAAU,KAAK,YAAY,OAAO,YAAY,GAAG,KAAK,aAAa,CAAC,CAAC;AAAA;AAAA,UAC9G;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,KAAK,SAAiB,UAAuB,CAAC,GAAwB;AACjF,UAAM,KAAK,gBAAgB;AAC3B,WAAO,KAAK,aAAa,SAAS,OAAO;AAAA,EAC3C;AAAA,EAEA,MAAa,OAAO,YAAsC;AACxD,WAAO,KAAK,KAAK,QAAQ,WAAW,UAAU,CAAC,IAAI;AAAA,EACrD;AAAA,EAEO,oBAAoE;AACzE,WAAO;AAAA,MACL,aAAa,KAAK,eAAe,UAAW,KAAK,eAAe,SAAa;AAAA,MAC7E,YAAY,KAAK,cAAc;AAAA,MAC/B,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK;AAAA,MACX,gBACE,KAAK,eAAe,gBAAgB,KAAK,OAAO,cAAc,OAC1D,eAAe,KAAK,OAAO,UAAU,IACrC;AAAA,MACN,MAAM,KAAK,OAAO;AAAA,MAClB,uBACE,KAAK,mBAAmB,OACpB,SACA,GAAG,mBAAmB,KAAK,eAAe,CAAC,IAAI,KAAK,gBAAgB,SAAS,QAAQ,CAAC;AAAA,IAC9F;AAAA,EACF;AAAA,EAEA,MAAa,MAAM,SAAoC;AACrD,UAAM,MAAM,MAAM,KAAK,OAAO,OAAO;AACrC,WAAO,QAAQ,KAAK,CAAC,IAAI,IAAI,MAAM,IAAI;AAAA,EACzC;AAAA,EAEA,MAAa,OAAO,SAAkC;AACpD,UAAM,SAAS,MAAM,KAAK,KAAK,SAAS,EAAE,QAAQ,KAAK,CAAC;AACxD,WAAO,OAAO,OAAO,KAAK;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,UAAU,SAAwC;AAC7D,SAAK,oBAAoB,SAAS,eAAe,KAAK;AACtD,QAAI,KAAK,wBAAwB,GAAG;AAClC,WAAK,YAAY;AACjB;AAAA,IACF;AACA,UAAM,KAAK,oBAAoB;AAC/B,QAAI,MAAM,KAAK,oBAAoB,GAAG;AACpC,WAAK,YAAY;AACjB;AAAA,IACF;AACA,UAAM,KAAK,2BAA2B;AAAA,EACxC;AAAA,EAEA,MAAa,SAAS,YAAqC;AACzD,WAAO,KAAK,OAAO,OAAO,WAAW,UAAU,CAAC,EAAE;AAAA,EACpD;AAAA,EAEA,MAAa,YAA2B;AACtC,UAAM,UAAU,KAAK,OAAO,oBAAoB;AAChD,UAAM,cAAc,KAAK,OAAO,wBAAwB;AACxD,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,QAAI,UAAU;AAEd,WAAO,KAAK,IAAI,IAAI,YAAY,UAAU,aAAa;AACrD,UAAI;AACF,aAAK,oBAAoB;AAEzB,cAAM,KAAK,QAAQ;AACnB;AAAA,MACF,SAAS,OAAO;AACd,YACE,iBAAiB,4BAChB,iBAAiB,SAAS,MAAM,YAAY;AAE7C,gBAAM;AACR,cAAM,SACJ,KAAK,IAAI,uBAAuB,KAAK,SAAS,mBAAmB,KAChE,cAAc,KAAK,OAAO,IAAI;AACjC,cAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC;AAEjE,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,qBAAW,SAAS,KAAK;AAAA,QAC3B,CAAC;AACD;AAAA,MACF;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR,0BAA0B,KAAK,QAAQ,IAAI,UAAU,OAAO,uBAAuB,OAAO;AAAA,IAC5F;AAAA,EACF;AAAA,EAEO,WAAW,MAAoB;AACpC,SAAK,QAAQ,QAAQ,KAAK,QAAQ,MAAM,OAAO,CAAC,cAAc,cAAc,IAAI;AAAA,EAClF;AAAA,EAEA,MAAa,OAAO,YAA4C;AAC9D,UAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,WAAW,UAAU,CAAC,IAAI;AACjE,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,MAAM,MAAM,KAAK,OAAO,aAAa,WAAW,UAAU,CAAC,EAAE;AACnE,WAAO,IAAI,MAAM,WAAC,QAAI,GAAC,GAAE,CAAC,KAAK;AAAA,EACjC;AAAA,EAEA,MAAa,KAAK,SAAmC;AACnD,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAAK,SAAS,EAAE,gBAAgB,MAAM,QAAQ,KAAK,CAAC;AAC9E,aAAO,OAAO,SAAS;AAAA,IACzB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEO,WAAW,MAAoB;AACpC,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEA,MAAa,WACX,WACA,YACA,SACe;AACf,UAAM,SAAS,KAAK,aAAa;AACjC,UAAM,gBAAgB,MAAM,KAAK,6BAA6B,YAAY,gBAAgB;AAC1F,UAAM,gBAAgB,SAAS,QAAQ;AACvC,QAAI;AACF,YAAM,WAAW,QAAQ,WAAW,aAAa;AACjD,YAAM,KAAK,kBAAkB,eAAe,aAAa;AACzD,YAAM,KAAK,uBAAuB,eAAe,YAAY,aAAa;AAAA,IAC5E,UAAE;AACA,UAAI;AACF,cAAM,KAAK,sBAAsB,aAAa;AAAA,MAChD,SAAS,cAAc;AACrB,gBAAQ,OAAO;AAAA,UACb,uCAAuC,aAAa,KAAK,YAAY,OAAO,YAAY,GAAG,KAAK,aAAa,CAAC,CAAC;AAAA;AAAA,QACjH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAa,UACX,YACA,SACA,SACe;AACf,UAAM,SAAS,KAAK,aAAa;AACjC,UAAM,iBAAiBC,MAAK,OAAO,GAAG,iBAAiBC,YAAW,CAAC,EAAE;AACrE,UAAM,kBAAkB,MAAM,KAAK,6BAA6B,YAAY,eAAe;AAC3F,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,IACF;AACA,QAAI;AAEF,oBAAc,gBAAgB,SAAS,EAAE,MAAM,IAAM,CAAC;AACtD,YAAM,WAAW,QAAQ,gBAAgB,eAAe;AACxD,YAAM,KAAK,kBAAkB,iBAAiB,aAAa;AAC3D,YAAM,KAAK,uBAAuB,iBAAiB,YAAY,aAAa;AAAA,IAC9E,UAAE;AACA,UAAI;AAEF,QAAAC,YAAW,cAAc;AAAA,MAC3B,QAAQ;AAAA,MAER;AACA,UAAI;AACF,cAAM,KAAK,sBAAsB,eAAe;AAAA,MAClD,SAAS,cAAc;AACrB,gBAAQ,OAAO;AAAA,UACb,uCAAuC,eAAe,KAAK,YAAY,OAAO,YAAY,GAAG,KAAK,aAAa,CAAC,CAAC;AAAA;AAAA,QACnH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAe,aAA8C;AACnE,QAAI,eAAe,KAAM,QAAO;AAChC,eAAW,OAAO,OAAO,KAAK,WAAW,GAAG;AAC1C,UAAI,CAAC,WAAC,mBAAe,GAAC,EAAC,KAAK,GAAG,GAAG;AAChC,cAAM,IAAI,MAAM,sCAAsC,GAAG,EAAE;AAAA,MAC7D;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE;AACjF,WAAO,GAAG,MAAM,KAAK,GAAG,CAAC;AAAA,EAC3B;AAAA,EAEQ,aAAa,OAAkC;AACrD,UAAM,uBACJ,KAAK,sBAAsB,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,oBAAoB,SAAS,MAAM,KAAK,EAAE;AAC/F,WAAO,CAAC,GAAG,sBAAsB,GAAI,SAAS,CAAC,CAAE;AAAA,EACnD;AAAA,EAEA,MAAc,6BAA6B,UAAiC;AAC1E,QAAI,SAAS,SAAS,IAAI,KAAK,SAAS,SAAS,IAAI,GAAG;AACtD,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AACA,SAAK,qBAAqB,OAAO,KAAK,QAAQ;AAC9C,QAAI;AACF,YAAM,KAAK,aAAa,QAAQ,EAAE,QAAQ,MAAM,SAAS,IAAO,CAAC;AACjE,WAAK,YAAY;AAAA,IACnB,SAAS,OAAO;AACd,YAAM,SAAS,YAAY,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC;AACpD,WAAK,oBAAoB;AACzB,YAAM,IAAI,MAAM,+BAA+B,MAAM,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,IAC3E;AAAA,EACF;AAAA,EAEA,MAAc,sBAAsB,YAAmC;AACrE,UAAM,UACJ,KAAK,OAAO,SAAS,SACjB,KAAK,KAAK,SAAS,WAAW,UAAU,CAAC,IAAI,EAAE,QAAQ,KAAK,CAAC,IAC7D,KAAK,gBAAgB,SAAS,WAAW,UAAU,CAAC,EAAE;AAC5D,UAAM;AAAA,EACR;AAAA,EAEQ,sBAA4B;AAClC,QAAI,KAAK,sBAAsB,MAAM;AACnC,WAAK,mBAAmB,KAAK,CAAC;AAC9B,WAAK,qBAAqB;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,SAAwC;AACpE,UAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAI,SAAS,QAAQ,MAAM,WAAW,GAAG;AACvC,UAAI,MAAM,KAAK,oBAAoB,OAAO,EAAG;AAC7C,UAAI,KAAK,OAAO,kBAAkB;AAChC,cAAM,IAAI;AAAA,UACR,wBAAwB,KAAK,QAAQ,IAAI,cAAc,KAAK,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,QACtF;AAAA,MACF;AACA,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AACA,QAAI;AAEF,YAAM,KAAK,KAAK;AAAA,IAClB,QAAQ;AACN,YAAM,IAAI,MAAM,8CAA8C,KAAK,EAAE;AAAA,IACvE;AACA,QAAI,MAAM,KAAK,kBAAkB,QAAW,QAAW,KAAK,GAAG;AAC7D,WAAK,cAAc;AACnB,WAAK,aAAa;AAClB;AAAA,IACF;AACA,QAAI,MAAM,KAAK,oBAAoB,SAAS,KAAK,EAAG;AACpD,UAAM,IAAI;AAAA,MACR,wBAAwB,KAAK,QAAQ,IAAI,2BAA2B,KAAK,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,IACnG;AAAA,EACF;AAAA,EAEA,MAAc,qBAAqB,SAAwC;AACzE,UAAM,iBAAiB,KAAK,OAAO;AACnC,QAAI,kBAAkB,MAAM;AAC1B,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAEA,UAAM,aAAa,MAAM,SAAS,eAAe,cAAc,CAAC;AAChE,QAAI;AACF,UAAI,MAAM,KAAK,kBAAkB,UAAU,GAAG;AAC5C,aAAK,aAAa;AAClB;AAAA,MACF;AACA,UAAI,KAAK,OAAO,kBAAkB;AAChC,cAAM,WAAW,MAAM;AAAA,UACrB,gBAAgB,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI;AAAA,UACrD;AAAA,UACA;AAAA,QACF;AACA,YAAI,MAAM,KAAK,kBAAkB,YAAY,QAAQ,GAAG;AACtD,eAAK,aAAa;AAClB;AAAA,QACF;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,QAAQ,IAAI,cAAc,KAAK,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,MACtF;AAAA,IACF,UAAE;AACA,iBAAW,KAAK,CAAC;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAc,qBAAqB,SAAiB,QAAiC;AACnF,UAAM,OACJ,KAAK,OAAO,SAAS,SACjB,MAAM,KAAK,OAAO,OAAO,IACzB,MAAM,KAAK,kBAAkB,OAAO;AAC1C,WAAO,mBAAmB,QAAQ,MAAM,MAAM;AAAA,EAChD;AAAA,EAEA,MAAc,kCACZ,YACA,QACiB;AACjB,UAAM,YAAY,MAAM,QAAQ,UAAU;AAC1C,UAAM,WAAW,GAAG,SAAS,IAAI,MAAM;AACvC,UAAM,UAAU,UAAU,WAAW,QAAQ,CAAC;AAC9C,UAAM,OACJ,KAAK,OAAO,SAAS,SACjB,MAAM,KAAK,OAAO,OAAO,IACzB,MAAM,KAAK,kBAAkB,OAAO;AAC1C,WAAO,mBAAmB,WAAW,MAAM,MAAM;AAAA,EACnD;AAAA,EAEA,MAAc,6BAA6B,YAAoB,QAAiC;AAC9F,QAAI,KAAK,OAAO,SAAS,QAAQ;AAC/B,aAAO,KAAK,kCAAkC,YAAY,MAAM;AAAA,IAClE;AACA,WAAO,KAAK,qBAAqB,gBAAgB,MAAM,YAAY,MAAM;AAAA,EAC3E;AAAA,EAEQ,uBACN,SACA,QACgF;AAChF,QAAI,UAAU;AACd,UAAM,gBAAgB,CAAC,WAAwB;AAC7C,UAAI,QAAS;AACb,gBAAU;AACV,WAAK,eAAe,OAAO,aAAa;AACxC,aAAO,MAAM;AAAA,IACf;AACA,UAAM,iBAAiB,CAAC,UAAmB;AACzC,UAAI,QAAS;AACb,gBAAU;AACV,WAAK,eAAe,OAAO,aAAa;AACxC,cAAQ,KAAK;AAAA,IACf;AACA,SAAK,eAAe,IAAI,aAAa;AACrC,WAAO,EAAE,eAAe,eAAe;AAAA,EACzC;AAAA;AAAA,EAGQ,sBAA4B;AAClC,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,IAAI;AAChB,WAAK,SAAS;AAAA,IAChB;AACA,UAAM,QAAQ,IAAI,MAAM,uBAAuB;AAC/C,eAAW,kBAAkB,KAAK,gBAAgB;AAChD,qBAAe,KAAK;AAAA,IACtB;AACA,SAAK,eAAe,MAAM;AAAA,EAC5B;AAAA,EAEQ,eAAuB;AAC7B,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,mBAAmB;AACrD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAc,sBAAqC;AACjD,UAAM,SAAS,MAAM,KAAK,QAAQ,iBAAiB;AACnD,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,OAAO,SAAS,UAAU,KAAK,UAAW;AACnD,QAAI,KAAK,sBAAsB,MAAM;AACnC,WAAK,YAAY;AACjB;AAAA,IACF;AACA,QAAI,KAAK,oBAAoB,MAAM;AACjC,YAAM,KAAK;AACX;AAAA,IACF;AACA,SAAK,mBAAmB,KAAK,UAAU,EAAE,aAAa,KAAK,kBAAkB,CAAC,EAAE,QAAQ,MAAM;AAC5F,WAAK,mBAAmB;AAAA,IAC1B,CAAC;AACD,UAAM,KAAK;AAAA,EACb;AAAA,EAEA,MAAc,aAAa,SAAiB,UAAuB,CAAC,GAAwB;AAC1F,UAAM,SAAS,KAAK,aAAa;AACjC,UAAM,oBAAoB,KAAK,eAAe,QAAQ,GAAG;AACzD,UAAM,EAAE,SAAS,KAAK,cAAc,IAAI,KAAK,YAAY,SAAS,iBAAiB;AACnF,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,EAAE,eAAe,eAAe,IAAI,KAAK;AAAA,QAC7C;AAAA,QACA;AAAA,MACF;AACA,YAAM,UAAU,QAAQ,WAAW;AACnC,YAAM,UAAU,KAAK,aAAa,QAAQ,OAAO;AACjD,UAAI,eAAqC;AACzC,YAAM,QAAQ,WAAW,MAAM;AAC7B,sBAAc,MAAM;AACpB;AAAA,UACE,IAAI,MAAM,2BAA2B,OAAO,OAAO,YAAY,SAAS,OAAO,CAAC,EAAE;AAAA,QACpF;AAAA,MACF,GAAG,OAAO;AACV,aAAO,KAAK,KAAK,CAAC,OAA0B,WAA0B;AACpE,YAAI,OAAO;AACT,uBAAa,KAAK;AAClB,wBAAc,KAAK;AACnB;AAAA,QACF;AACA,uBAAe;AACf,4BAAoB;AAAA,UAClB;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AACD,YAAI,iBAAiB,KAAK,sBAAsB,MAAM;AACpD,eAAK,kBAAkB,MAAM;AAAA,QAC/B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,QAAQ,SAAgE;AACpF,UAAM,SAAS,KAAK,aAAa;AACjC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,EAAE,eAAe,eAAe,IAAI,KAAK,uBAG5C,SAAS,MAAM;AAClB,UAAI,eAAqC;AACzC,YAAM,QAAQ,WAAW,MAAM;AAC7B,sBAAc,MAAM;AACpB,sBAAc,IAAI,MAAM,2BAA2B,eAAe,OAAO,OAAO,EAAE,CAAC;AAAA,MACrF,GAAG,eAAe;AAClB,aAAO,KAAK,SAAS,CAAC,OAA0B,WAA0B;AACxE,YAAI,OAAO;AACT,uBAAa,KAAK;AAClB,wBAAc,KAAK;AACnB;AAAA,QACF;AACA,uBAAe;AACf,cAAM,SAAmB,CAAC;AAC1B,eAAO,GAAG,QAAQ,CAAC,UAAkB;AACnC,iBAAO,KAAK,KAAK;AAAA,QACnB,CAAC;AACD,eAAO,GAAG,SAAS,CAAC,SAAiB;AACnC,uBAAa,KAAK;AAClB,yBAAe;AAAA,YACb,UAAU,sBAAsB,IAAI;AAAA,YACpC,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAAA,UAC/C,CAAC;AAAA,QACH,CAAC;AACD,eAAO,OAAO,GAAG,QAAQ,MAAM;AAAA,QAE/B,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,gBAAgB,SAAgC;AAC5D,UAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,MAAM,6BAA6B,OAAO,QAAQ,MAAM,OAAO,EAAE;AAAA,IAC7E;AAAA,EACF;AAAA,EAEA,MAAc,uBACZ,eACA,YACA,MACe;AACf,iBAAa,IAAI;AACjB,QAAI,KAAK,OAAO,SAAS,QAAQ;AAC/B,YAAM,KAAK,KAAK,MAAM,WAAW,aAAa,CAAC,IAAI,WAAW,UAAU,CAAC,IAAI;AAAA,QAC3E,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,QAAQ,UAAU;AAC1C,UAAM,WAAW,MAAM,SAAS,UAAU;AAC1C,UAAM,gBAAgB,GAAG,SAAS,KAAK,QAAQ;AAC/C,UAAM,iBAAiB;AAAA,iCACM,WAAW,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAQhC,WAAW,aAAa,CAAC;AAAA,KAC3C,WAAW,aAAa,CAAC;AAAA,QACtB,WAAW,IAAI,CAAC;AAAA;AAAA,oBAEJ,WAAW,UAAU,CAAC;AAAA;AAAA;AAGtC,UAAM,KAAK,KAAK,gBAAgB,EAAE,QAAQ,KAAK,CAAC;AAAA,EAClD;AAAA,EAEA,MAAc,sBAAwC;AACpD,QAAI;AACF,YAAM,KAAK,aAAa,QAAQ,EAAE,QAAQ,MAAM,SAAS,IAAO,CAAC;AACjE,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,0BAAmC;AACzC,WAAO,KAAK,OAAO,SAAS,UAAU,KAAK,sBAAsB;AAAA,EACnE;AAAA,EAEA,MAAc,kBAAkB,SAAkC;AAChE,UAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,MAAM,6BAA6B,OAAO,QAAQ,MAAM,OAAO,EAAE;AAAA,IAC7E;AACA,WAAO,OAAO,OAAO,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAc,6BAA4C;AACxD,UAAM,WAAW,MAAM;AAAA,MACrB,uBAAuB,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI;AAAA,MAC5D;AAAA,MACA,EAAE,aAAa,KAAK,kBAAkB;AAAA,IACxC;AACA,UAAM,KAAK,6BAA6B,QAAQ;AAAA,EAClD;AAAA,EAEA,MAAc,kBAAkB,YAAoB,MAA6B;AAC/E,iBAAa,IAAI;AACjB,UAAM,UAAU,SAAS,WAAW,IAAI,CAAC,IAAI,WAAW,UAAU,CAAC;AACnE,QAAI,KAAK,OAAO,SAAS,QAAQ;AAC/B,YAAM,KAAK,KAAK,SAAS,EAAE,QAAQ,KAAK,CAAC;AACzC;AAAA,IACF;AACA,UAAM,KAAK,gBAAgB,OAAO;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,YACN,SACA,oBAAoB,IACyB;AAC7C,QAAI,KAAK,OAAO,SAAS,QAAQ;AAC/B,aAAO,EAAE,SAAS,GAAG,iBAAiB,GAAG,OAAO,IAAI,eAAe,MAAM;AAAA,IAC3E;AACA,UAAM,SAAS,WAAW,GAAG,iBAAiB,GAAG,OAAO,EAAE;AAC1D,QAAI,KAAK,sBAAsB,MAAM;AACnC,aAAO,EAAE,SAAS,kCAAkC,MAAM,IAAI,eAAe,KAAK;AAAA,IACpF;AACA,WAAO,EAAE,SAAS,gBAAgB,MAAM,IAAI,eAAe,MAAM;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,kBACZ,YACA,UACA,OACkB;AAClB,UAAM,OAAO,KAAK,OAAO,yBAAyB;AAClD,eAAW,QAAQ,KAAK,QAAQ,OAAO;AACrC,UAAI;AACF,cAAM,SAAS,IAAI,OAAO;AAC1B,cAAM,WAAW;AAAA,UACf;AAAA,UACA,EAAE,MAAM,KAAK,QAAQ,MAAM,KAAK;AAAA,UAChC;AAAA,YACE,yBAAyB,KAAK,OAAO;AAAA,YACrC,uBAAuB,KAAK,OAAO;AAAA,UACrC;AAAA,QACF;AACA,cAAM,kBAAkB,KAAK,iBAAiB,SAAS,YAAY;AAEnE,cAAM,iBAAiB;AAAA,UACrB;AAAA,UACA,cAAc,KAAK,OAAO;AAAA,UAC1B;AAAA,UACA,MAAM,KAAK,QAAQ;AAAA,UACnB,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,KAAK,OAAO;AAAA,QACxB,CAAC;AAGD,YAAI,SAAS,kBAAkB,KAAM,OAAM,SAAS;AACpD,eAAO,GAAG,SAAS,MAAM;AACvB,gBAAM,QAAQ,IAAI,MAAM,oCAAoC;AAC5D,qBAAW,kBAAkB,KAAK,gBAAgB;AAChD,2BAAe,KAAK;AAAA,UACtB;AACA,eAAK,eAAe,MAAM;AAAA,QAC5B,CAAC;AACD,aAAK,SAAS;AACd,aAAK,gBAAgB;AACrB,eAAO;AAAA,MACT,SAAS,OAAO;AACd,YAAI,iBAAiB,yBAA0B,OAAM;AAAA,MAEvD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,oBAAoB,SAAyB,OAAkC;AAC3F,QAAI,CAAC,KAAK,OAAO,iBAAkB,QAAO;AAC1C,UAAM,WAAW,MAAM;AAAA,MACrB,gBAAgB,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI;AAAA,MACrD;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAE,MAAM,KAAK,kBAAkB,QAAW,UAAU,KAAK,EAAI,QAAO;AACxE,SAAK,aAAa;AAClB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAiB,UAA+D;AACtF,WAAO,CAAC,QAAyB;AAC/B,UACE,KAAK,iBAAiB,SACrB,KAAK,cAAc,WAAW,IAAI,UAAU,CAACC,iBAAgB,KAAK,eAAe,GAAG,IACrF;AACA,aAAK,oBAAoB;AACzB,cAAM,IAAI;AAAA,UACR,oCAAoC,KAAK,QAAQ,IAAI;AAAA,QAGvD;AAAA,MACF;AACA,UAAI,YAAY,MAAM;AACpB,cAAM,WAAW,SAAS,GAAG;AAC7B,YAAI,CAAC,SAAU,QAAO;AACtB,aAAK,oBAAoB,OAAO,KAAK,GAAG;AAAA,MAC1C;AACA,WAAK,kBAAkB,OAAO,KAAK,GAAG;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,kBAAkB,QAA6B;AACrD,WAAO,MAAM,KAAK,kBAAkB;AACpC,WAAO,MAAM,IAAI;AAAA,EACnB;AACF;;;AI32BA,SAAS,YAAAC,iBAAgB;AAazB,eAAsB,mBACpB,aACA,KACoC;AACpC,QAAM,QAAQ,YAAY,GAAG;AAE7B,MAAI,UAAU,QAAW;AACvB,UAAM,IAAI,MAAM,YAAY,GAAG,kBAAkB;AAAA,EACnD;AACA,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAYA,eAAsB,mBAAmB,UAAwC;AAE/E,QAAM,UAAU,MAAMA,UAAS,UAAU,MAAM;AAC/C,QAAM,cAA2B,CAAC;AAElC,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,MAAM,QAAQ,WAAW,GAAG,EAAG;AAC/C,UAAM,UAAU,QAAQ,QAAQ,GAAG;AACnC,QAAI,YAAY,GAAI;AAEpB,UAAM,MAAM,QAAQ,MAAM,GAAG,OAAO,EAAE,KAAK;AAC3C,gBAAY,GAAG,IAAI,aAAa,QAAQ,MAAM,UAAU,CAAC,EAAE,KAAK,CAAC;AAAA,EACnE;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,KAAqB;AACzC,MAAI,IAAI,UAAU,KAAK,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAG;AAG/D,WAAO,IACJ,MAAM,GAAG,EAAE,EACX,WAAW,QAAQ,IAAI,EACvB,WAAW,OAAO,IAAI,EACtB,WAAW,OAAO,GAAG,EACrB,WAAW,MAAM,IAAI;AAAA,EAC1B;AACA,MAAI,IAAI,UAAU,KAAK,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAG;AAE/D,WAAO,IAAI,MAAM,GAAG,EAAE;AAAA,EACxB;AAEA,QAAM,eAAe,IAAI,OAAO,WAAC,QAAI,GAAC;AACtC,SAAO,iBAAiB,KAAK,MAAM,IAAI,MAAM,GAAG,YAAY,EAAE,QAAQ;AACxE;AAUO,SAAS,oBAAoB,cAA2D;AAC7F,QAAM,SAAsB,CAAC;AAC7B,aAAW,eAAe,cAAc;AACtC,QAAI,eAAe,MAAM;AACvB,aAAO,OAAO,QAAQ,WAAW;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;","names":["isRecord","randomUUID","timingSafeEqual","unlinkSync","homedir","join","join","homedir","join","randomUUID","unlinkSync","timingSafeEqual","readFile"]}