@kindlm/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +52 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/kindlm.js +57 -0
- package/dist/kindlm.js.map +1 -0
- package/package.json +49 -8
- package/bin/index.js +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/validate.ts","../src/utils/file-reader.ts","../src/commands/test.ts","../src/utils/run-tests.ts","../src/utils/http.ts","../src/utils/spinner.ts","../src/utils/command-executor.ts","../src/utils/last-run.ts","../src/utils/pdf-renderer.ts","../src/commands/baseline.ts","../src/utils/baseline-io.ts","../src/commands/login.ts","../src/cloud/auth.ts","../src/cloud/client.ts","../src/commands/upload.ts","../src/utils/git.ts","../src/utils/env.ts","../src/cloud/upload.ts","../src/commands/trace.ts","../src/utils/trace-server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerValidateCommand } from \"./commands/validate.js\";\nimport { registerTestCommand } from \"./commands/test.js\";\nimport { registerBaselineCommand } from \"./commands/baseline.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerUploadCommand } from \"./commands/upload.js\";\nimport { registerTraceCommand } from \"./commands/trace.js\";\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"kindlm\")\n .description(\"AI agent behavioral regression testing\")\n .version(\"0.0.0\");\n\n registerInitCommand(program);\n registerValidateCommand(program);\n registerTestCommand(program);\n registerBaselineCommand(program);\n registerLoginCommand(program);\n registerUploadCommand(program);\n registerTraceCommand(program);\n\n return program;\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport chalk from \"chalk\";\n\nconst TEMPLATE = `kindlm: 1\nproject: my-project\n\nsuite:\n name: my-agent-tests\n description: Behavioral tests for my AI agent\n\nproviders:\n openai:\n apiKeyEnv: OPENAI_API_KEY\n\nmodels:\n - id: gpt-4o\n provider: openai\n model: gpt-4o\n params:\n temperature: 0\n maxTokens: 1024\n\nprompts:\n greeting:\n system: You are a helpful assistant.\n user: \"{{message}}\"\n\ntests:\n - name: basic-greeting\n prompt: greeting\n vars:\n message: Hello, how are you?\n expect:\n output:\n contains:\n - hello\n guardrails:\n pii:\n enabled: true\n\ngates:\n passRateMin: 0.95\n\ndefaults:\n repeat: 1\n concurrency: 4\n timeoutMs: 60000\n`;\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Create a kindlm.yaml template\")\n .option(\"--force\", \"Overwrite existing kindlm.yaml\")\n .action((options: { force?: boolean }) => {\n const filePath = resolve(process.cwd(), \"kindlm.yaml\");\n\n if (existsSync(filePath) && !options.force) {\n console.error(chalk.red(\"kindlm.yaml already exists. Use --force to overwrite.\"));\n process.exit(1);\n }\n\n writeFileSync(filePath, TEMPLATE, \"utf-8\");\n console.log(chalk.green(\"Created kindlm.yaml\"));\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` 1. Edit ${chalk.bold(\"kindlm.yaml\")} with your test configuration`);\n console.log(` 2. Set your API key: ${chalk.bold(\"export OPENAI_API_KEY=sk-...\")}`);\n console.log(` 3. Run tests: ${chalk.bold(\"kindlm test\")}`);\n });\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { parseConfig } from \"@kindlm/core\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\n\nexport function registerValidateCommand(program: Command): void {\n program\n .command(\"validate\")\n .description(\"Validate kindlm.yaml configuration\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action((options: { config: string }) => {\n const configPath = resolve(process.cwd(), options.config);\n const configDir = dirname(configPath);\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const result = parseConfig(yamlContent, { configDir, fileReader });\n\n if (!result.success) {\n console.error(chalk.red(\"Validation failed:\"));\n const details = result.error.details;\n if (details && Array.isArray(details[\"errors\"])) {\n for (const e of details[\"errors\"] as string[]) {\n console.error(chalk.red(` - ${e}`));\n }\n } else {\n console.error(chalk.red(` ${result.error.message}`));\n }\n process.exit(1);\n }\n\n const config = result.data;\n console.log(chalk.green(\"Config is valid!\"));\n console.log(\"\");\n console.log(` Suite: ${chalk.bold(config.suite.name)}`);\n console.log(` Tests: ${chalk.bold(String(config.tests.length))}`);\n console.log(` Models: ${chalk.bold(String(config.models.length))}`);\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport type { FileReader, Result } from \"@kindlm/core\";\n\nexport function createNodeFileReader(): FileReader {\n return {\n readFile(path: string): Result<string> {\n try {\n return { success: true, data: readFileSync(path, \"utf-8\") };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"CONFIG_FILE_REF_ERROR\",\n message: `Cannot read file: ${path}: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n evaluateGates,\n createPrettyReporter,\n createJsonReporter,\n createJunitReporter,\n createComplianceReporter,\n ProviderError,\n} from \"@kindlm/core\";\nimport type { Colorize, KindlmError } from \"@kindlm/core\";\nimport { runTests } from \"../utils/run-tests.js\";\nimport { saveLastRun, computeConfigHash } from \"../utils/last-run.js\";\nimport { renderCompliancePdf } from \"../utils/pdf-renderer.js\";\n\ninterface TestOptions {\n suite?: string;\n compliance?: boolean;\n reporter: string;\n runs?: string;\n gate?: string;\n config: string;\n pdf?: string;\n}\n\nexport function registerTestCommand(program: Command): void {\n program\n .command(\"test\")\n .description(\"Run test suites\")\n .option(\"-s, --suite <name>\", \"Run a specific suite\")\n .option(\"--compliance\", \"Generate compliance report\")\n .option(\"--reporter <type>\", \"Output format: pretty, json, junit\", \"pretty\")\n .option(\"--runs <count>\", \"Override run count\")\n .option(\"--gate <percent>\", \"Fail if pass rate below threshold\")\n .option(\"--pdf <path>\", \"Export compliance report as PDF (requires --compliance)\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action(async (options: TestOptions) => {\n try {\n const { runnerResult, config, yamlContent } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n gate: options.gate ? parseFloat(options.gate) : undefined,\n });\n\n const { runResult: result, aggregated } = runnerResult;\n\n // Evaluate gates\n const gateEvaluation = evaluateGates(config.gates, aggregated);\n\n // Select + generate report\n const reporter = selectReporter(options.reporter);\n const report = reporter.generate(result, gateEvaluation);\n console.log(report.content);\n\n // Compliance report\n if (options.compliance) {\n const complianceReporter = createComplianceReporter();\n const complianceReport = complianceReporter.generate(result, gateEvaluation);\n console.log(\"\");\n console.log(complianceReport.content);\n\n // PDF export\n if (options.pdf) {\n const pdfPath = await renderCompliancePdf(complianceReport.content, options.pdf);\n console.log(\"\");\n console.log(chalk.green(`PDF report saved to ${pdfPath}`));\n }\n }\n\n // Cache last run for upload\n try {\n saveLastRun({\n runnerResult,\n suiteName: config.suite.name,\n configHash: computeConfigHash(yamlContent),\n timestamp: new Date().toISOString(),\n });\n } catch {\n // Non-fatal — don't block exit on cache failure\n }\n\n // Exit code\n const allPassed = result.failed === 0 && result.errored === 0 && gateEvaluation.passed;\n process.exit(allPassed ? 0 : 1);\n } catch (e) {\n if (e instanceof ProviderError) {\n const prefix = e.code === \"TIMEOUT\"\n ? \"Provider timeout\"\n : e.code === \"NETWORK_ERROR\"\n ? \"Network error\"\n : e.code === \"AUTH_FAILED\"\n ? \"Authentication failed\"\n : e.code === \"RATE_LIMITED\"\n ? \"Rate limited\"\n : `Provider error (${e.code})`;\n console.error(chalk.red(`${prefix}: ${e.message}`));\n if (e.retryable) {\n console.error(chalk.yellow(\"This error may be transient. Try again or increase --timeout.\"));\n }\n } else if (isKindlmError(e)) {\n const isConfig = e.code.startsWith(\"CONFIG_\");\n const label = isConfig ? \"Config error\" : \"Error\";\n console.error(chalk.red(`${label}: ${e.message}`));\n } else if (e instanceof Error && e.name === \"AbortError\") {\n console.error(chalk.red(\"Request timed out. Check network connectivity or increase timeout.\"));\n } else {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n }\n process.exit(1);\n }\n });\n}\n\nconst chalkColorize: Colorize = {\n bold: (t) => chalk.bold(t),\n red: (t) => chalk.red(t),\n green: (t) => chalk.green(t),\n yellow: (t) => chalk.yellow(t),\n cyan: (t) => chalk.cyan(t),\n dim: (t) => chalk.dim(t),\n greenBold: (t) => chalk.green.bold(t),\n redBold: (t) => chalk.red.bold(t),\n};\n\nfunction selectReporter(type: string) {\n switch (type) {\n case \"json\":\n return createJsonReporter();\n case \"junit\":\n return createJunitReporter();\n case \"pretty\":\n default:\n return createPrettyReporter(chalkColorize);\n }\n}\n\nfunction isKindlmError(e: unknown): e is KindlmError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n \"code\" in e &&\n \"message\" in e &&\n typeof (e as KindlmError).code === \"string\" &&\n typeof (e as KindlmError).message === \"string\"\n );\n}\n","/* eslint-disable no-console */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n createProvider,\n createRunner,\n} from \"@kindlm/core\";\nimport type {\n ProviderAdapter,\n KindLMConfig,\n ProgressEvent,\n RunnerResult,\n BaselineData,\n} from \"@kindlm/core\";\nimport { createHttpClient } from \"./http.js\";\nimport { createSpinner } from \"./spinner.js\";\nimport { createNodeFileReader } from \"./file-reader.js\";\nimport { createNodeCommandExecutor } from \"./command-executor.js\";\n\nexport interface RunTestsOptions {\n configPath: string;\n runs?: number;\n gate?: number;\n baselineData?: BaselineData;\n}\n\nexport interface RunTestsResult {\n config: KindLMConfig;\n runnerResult: RunnerResult;\n configDir: string;\n yamlContent: string;\n}\n\nconst MAX_CONFIG_SIZE = 1_048_576; // 1MB\n\nexport async function runTests(options: RunTestsOptions): Promise<RunTestsResult> {\n const spinner = createSpinner();\n\n // SIGINT handler: stop spinner, print partial results, exit 130\n let interrupted = false;\n const sigintHandler = () => {\n if (interrupted) process.exit(130);\n interrupted = true;\n spinner.stop();\n console.error(chalk.yellow(\"\\nInterrupted. Exiting...\"));\n process.exit(130);\n };\n process.on(\"SIGINT\", sigintHandler);\n\n try {\n return await runTestsInner(options, spinner);\n } finally {\n process.removeListener(\"SIGINT\", sigintHandler);\n }\n}\n\nasync function runTestsInner(\n options: RunTestsOptions,\n spinner: ReturnType<typeof createSpinner>,\n): Promise<RunTestsResult> {\n // 1. Read config\n const configPath = resolve(process.cwd(), options.configPath);\n const configDir = dirname(configPath);\n\n // Check file size before reading\n try {\n const stat = statSync(configPath);\n if (stat.size > MAX_CONFIG_SIZE) {\n console.error(chalk.red(`Config file exceeds 1MB limit (${(stat.size / 1_048_576).toFixed(1)}MB): ${configPath}`));\n process.exit(1);\n }\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n // 2. Parse + validate\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, { configDir, fileReader });\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const config: KindLMConfig = parseResult.data;\n\n // 3. Apply CLI overrides\n if (options.runs !== undefined) {\n config.defaults.repeat = options.runs;\n }\n if (options.gate !== undefined) {\n if (!config.gates) {\n config.gates = { passRateMin: options.gate / 100 } as KindLMConfig[\"gates\"];\n } else {\n config.gates.passRateMin = options.gate / 100;\n }\n }\n\n // 4. Resolve API keys + create provider adapters\n const httpClient = createHttpClient();\n const adapters = new Map<string, ProviderAdapter>();\n\n const providers = config.providers as Record<string, { apiKeyEnv?: string; baseUrl?: string; organization?: string } | undefined>;\n for (const [name, providerConfig] of Object.entries(providers)) {\n if (!providerConfig) continue;\n\n let apiKey = \"\";\n if (providerConfig.apiKeyEnv) {\n const key = process.env[providerConfig.apiKeyEnv];\n if (!key) {\n console.error(chalk.red(`Missing environment variable: ${providerConfig.apiKeyEnv}`));\n process.exit(1);\n }\n apiKey = key.trim();\n } else if (name !== \"ollama\") {\n console.error(chalk.red(`Provider \"${name}\" requires apiKeyEnv to be configured`));\n process.exit(1);\n }\n\n let adapter: ProviderAdapter;\n try {\n adapter = createProvider(name, httpClient);\n } catch (cause) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n console.error(chalk.red(`Failed to create provider \"${name}\": ${msg}`));\n process.exit(1);\n }\n\n await adapter.initialize({\n apiKey,\n baseUrl: providerConfig.baseUrl,\n organization: providerConfig.organization,\n timeoutMs: config.defaults.timeoutMs,\n maxRetries: 2,\n });\n\n adapters.set(name, adapter);\n }\n\n // 5. Create + run\n let completedTests = 0;\n const totalTests = countExecutionUnits(config);\n\n const onProgress = (event: ProgressEvent) => {\n if (event.type === \"test_start\") {\n spinner.start(`Running ${event.test} [${event.model}] (${completedTests}/${totalTests})`);\n } else if (event.type === \"test_complete\") {\n completedTests++;\n }\n };\n\n // Check if any tests use command mode\n const hasCommandTests = config.tests.some((t) => t.command);\n const commandExecutor = hasCommandTests ? createNodeCommandExecutor() : undefined;\n\n const runner = createRunner(config, {\n adapters,\n configDir,\n fileReader,\n onProgress,\n baselineData: options.baselineData,\n commandExecutor,\n });\n\n const runResult = await runner.run();\n spinner.stop();\n\n if (!runResult.success) {\n console.error(chalk.red(`Run failed: ${runResult.error.message}`));\n process.exit(1);\n }\n\n return {\n config,\n runnerResult: runResult.data,\n configDir,\n yamlContent,\n };\n}\n\nfunction countExecutionUnits(config: KindLMConfig): number {\n let count = 0;\n for (const test of config.tests) {\n if (test.skip) continue;\n const repeat = test.repeat ?? config.defaults.repeat;\n if (test.command) {\n count += repeat;\n } else {\n const modelCount = test.models?.length ?? config.models.length;\n count += modelCount * repeat;\n }\n }\n return count;\n}\n","import type { HttpClient, HttpRequestInit, HttpResponse } from \"@kindlm/core\";\n\nexport function createHttpClient(): HttpClient {\n return {\n async fetch(url: string, init: HttpRequestInit): Promise<HttpResponse> {\n const controller = new AbortController();\n const timeoutId = init.timeoutMs\n ? setTimeout(() => controller.abort(), init.timeoutMs)\n : undefined;\n\n try {\n const response = await globalThis.fetch(url, {\n method: init.method,\n headers: init.headers,\n body: init.body,\n signal: controller.signal,\n });\n\n return {\n ok: response.ok,\n status: response.status,\n json: () => response.json() as Promise<unknown>,\n };\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n },\n };\n}\n","import ora, { type Ora } from \"ora\";\n\nexport interface Spinner {\n start(text: string): void;\n succeed(text: string): void;\n fail(text: string): void;\n stop(): void;\n}\n\nexport function createSpinner(): Spinner {\n let instance: Ora | undefined;\n\n return {\n start(text: string): void {\n instance = ora(text).start();\n },\n succeed(text: string): void {\n instance?.succeed(text);\n instance = undefined;\n },\n fail(text: string): void {\n instance?.fail(text);\n instance = undefined;\n },\n stop(): void {\n instance?.stop();\n instance = undefined;\n },\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { ok, err } from \"@kindlm/core\";\nimport type { CommandExecutor, CommandExecuteOptions, RawCommandOutput, Result } from \"@kindlm/core\";\n\nexport function createNodeCommandExecutor(): CommandExecutor {\n return {\n async execute(command: string, options: CommandExecuteOptions): Promise<Result<RawCommandOutput>> {\n return new Promise((resolve) => {\n const child = spawn(\"sh\", [\"-c\", command], {\n cwd: options.cwd,\n env: { ...process.env, ...options.env },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk: Buffer) => stdoutChunks.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => stderrChunks.push(chunk));\n\n const timer = setTimeout(() => {\n child.kill(\"SIGTERM\");\n setTimeout(() => {\n if (!child.killed) child.kill(\"SIGKILL\");\n }, 1000);\n }, options.timeoutMs);\n\n child.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n\n if (signal === \"SIGTERM\" || signal === \"SIGKILL\") {\n resolve(err({\n code: \"PROVIDER_TIMEOUT\",\n message: `Command timed out after ${options.timeoutMs}ms`,\n }));\n return;\n }\n\n resolve(ok({\n stdout: Buffer.concat(stdoutChunks).toString(\"utf-8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf-8\"),\n exitCode: code ?? 1,\n }));\n });\n\n child.on(\"error\", (e) => {\n clearTimeout(timer);\n resolve(err({\n code: \"UNKNOWN_ERROR\",\n message: `Failed to spawn command: ${e.message}`,\n }));\n });\n });\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport type { RunnerResult } from \"@kindlm/core\";\n\nexport interface LastRunData {\n runnerResult: RunnerResult;\n suiteName: string;\n configHash: string;\n timestamp: string;\n}\n\nfunction getLastRunPath(): string {\n return join(process.cwd(), \".kindlm\", \"last-run.json\");\n}\n\nexport function saveLastRun(data: LastRunData): void {\n const filePath = getLastRunPath();\n const dir = join(process.cwd(), \".kindlm\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n\n // Strip individual run data (outputText, full assertions) to reduce file size.\n // Pre-extract failure messages since upload needs them.\n const slimmed: LastRunData = {\n ...data,\n runnerResult: {\n ...data.runnerResult,\n aggregated: data.runnerResult.aggregated.map((agg) => {\n const failureMessages = agg.runs\n .flatMap((r) => r.assertions.filter((a) => !a.passed).map((a) => a.failureMessage))\n .filter((m): m is string => m !== undefined);\n\n return {\n ...agg,\n failureMessages,\n runs: [],\n };\n }),\n },\n };\n\n writeFileSync(filePath, JSON.stringify(slimmed), { mode: 0o600 });\n}\n\nexport function loadLastRun(): LastRunData | null {\n try {\n const raw = readFileSync(getLastRunPath(), \"utf-8\");\n const parsed = JSON.parse(raw) as LastRunData;\n if (\n parsed.runnerResult?.runResult &&\n Array.isArray(parsed.runnerResult.aggregated) &&\n typeof parsed.suiteName === \"string\" &&\n typeof parsed.configHash === \"string\" &&\n typeof parsed.timestamp === \"string\"\n ) {\n return parsed;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function computeConfigHash(yamlContent: string): string {\n return createHash(\"sha256\").update(yamlContent).digest(\"hex\");\n}\n","// @ts-expect-error pdfkit has no type declarations\nimport PDFDocument from \"pdfkit\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\ninterface Section {\n heading: string;\n headingLevel: number;\n body: string;\n}\n\nfunction parseSections(markdown: string): Section[] {\n const lines = markdown.split(\"\\n\");\n const sections: Section[] = [];\n let currentHeading = \"\";\n let currentLevel = 2;\n let currentBody: string[] = [];\n\n for (const line of lines) {\n const headingMatch = line.match(/^(#{2,4})\\s+(.+)$/);\n if (headingMatch && headingMatch[1]?.length === 2) {\n if (currentHeading || currentBody.length > 0) {\n sections.push({ heading: currentHeading, headingLevel: currentLevel, body: currentBody.join(\"\\n\").trim() });\n }\n currentHeading = headingMatch[2]?.trim() ?? \"\";\n currentLevel = 2;\n currentBody = [];\n } else {\n currentBody.push(line);\n }\n }\n\n if (currentHeading || currentBody.length > 0) {\n sections.push({ heading: currentHeading, headingLevel: currentLevel, body: currentBody.join(\"\\n\").trim() });\n }\n\n return sections;\n}\n\nfunction extractTitle(markdown: string): string {\n const match = markdown.match(/^# (.+)$/m);\n return match?.[1]?.trim() ?? \"KindLM Compliance Report\";\n}\n\nfunction extractHash(markdown: string): string | null {\n const match = markdown.match(/SHA-256:\\s*`([a-f0-9]+)`/i);\n return match?.[1] ?? null;\n}\n\nfunction headingSize(level: number): number {\n switch (level) {\n case 2: return 18;\n case 3: return 15;\n case 4: return 13;\n default: return 13;\n }\n}\n\ninterface TableRow {\n cells: string[];\n}\n\nfunction parseTable(lines: string[]): { header: TableRow; rows: TableRow[] } | null {\n if (lines.length < 2) return null;\n const headerLine = lines[0] ?? \"\";\n const separatorLine = lines[1] ?? \"\";\n if (!headerLine.includes(\"|\") || !separatorLine.match(/^\\s*\\|[-:\\s|]+\\|\\s*$/)) return null;\n\n const parseCells = (line: string): string[] =>\n line.split(\"|\").slice(1, -1).map((c) => c.trim());\n\n const header: TableRow = { cells: parseCells(headerLine) };\n const rows: TableRow[] = [];\n for (let i = 2; i < lines.length; i++) {\n const row = lines[i] ?? \"\";\n if (!row.includes(\"|\")) break;\n rows.push({ cells: parseCells(row) });\n }\n return { header, rows };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PDFDoc = any;\n\nfunction ensureSpace(doc: PDFDoc, needed: number): void {\n const bottomMargin = doc.page.margins.bottom as number;\n const available = (doc.page.height as number) - bottomMargin - 30 - (doc.y as number);\n if (available < needed) {\n doc.addPage();\n renderPageHeader(doc);\n }\n}\n\nfunction renderPageHeader(doc: PDFDoc): void {\n const timestamp = new Date().toISOString();\n doc.fontSize(8).font(\"Helvetica\").fillColor(\"#a8a29e\").text(\"KindLM Compliance Report\", 60, 40);\n doc.text(timestamp, 60, 40, { align: \"right\" });\n doc.moveDown(3);\n}\n\nfunction renderPageFooter(doc: PDFDoc): void {\n doc.fontSize(8).font(\"Helvetica\").fillColor(\"#a8a29e\").text(\n \"Generated by KindLM · kindlm.com\",\n 60,\n (doc.page.height as number) - 50,\n { align: \"center\", width: (doc.page.width as number) - 120 },\n );\n}\n\nfunction renderTable(\n doc: PDFDoc,\n table: { header: TableRow; rows: TableRow[] },\n contentWidth: number,\n): void {\n const colCount = table.header.cells.length;\n const colWidth = contentWidth / colCount;\n const leftMargin = doc.page.margins.left as number;\n const rowHeight = 18;\n\n ensureSpace(doc, rowHeight * 2);\n\n const drawRow = (cells: string[], bold: boolean, bgColor?: string) => {\n const y = doc.y as number;\n if (bgColor) {\n doc.save();\n doc.rect(leftMargin, y - 2, contentWidth, rowHeight).fill(bgColor);\n doc.restore();\n }\n for (let i = 0; i < cells.length; i++) {\n const x = leftMargin + i * colWidth;\n doc.fontSize(8)\n .font(bold ? \"Helvetica-Bold\" : \"Courier\")\n .fillColor(\"#44403c\")\n .text(cells[i] ?? \"\", x + 4, y, { width: colWidth - 8, height: rowHeight, lineBreak: false });\n }\n doc.y = y + rowHeight;\n };\n\n drawRow(table.header.cells, true, \"#f5f5f4\");\n for (const row of table.rows) {\n ensureSpace(doc, rowHeight);\n drawRow(row.cells, false);\n }\n}\n\nexport async function renderCompliancePdf(\n markdownContent: string,\n outputPath: string,\n): Promise<string> {\n await mkdir(dirname(outputPath), { recursive: true });\n\n return new Promise((resolve, reject) => {\n const doc = new PDFDocument({\n size: \"A4\",\n margins: { top: 72, bottom: 72, left: 60, right: 60 },\n info: {\n Title: \"KindLM EU AI Act Compliance Report\",\n Author: \"KindLM\",\n Creator: \"KindLM CLI\",\n },\n });\n\n const stream = createWriteStream(outputPath);\n doc.pipe(stream);\n\n const contentWidth = (doc.page.width as number) - (doc.page.margins.left as number) - (doc.page.margins.right as number);\n const title = extractTitle(markdownContent);\n const hash = extractHash(markdownContent);\n\n // --- Title Page ---\n doc.moveDown(6);\n doc.fontSize(28).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(title, { align: \"center\", width: contentWidth });\n doc.moveDown(0.5);\n doc.fontSize(14).font(\"Helvetica\").fillColor(\"#57534e\").text(\"EU AI Act Annex IV Documentation\", { align: \"center\", width: contentWidth });\n doc.moveDown(1);\n doc.fontSize(10).fillColor(\"#a8a29e\").text(`Generated: ${new Date().toISOString()}`, { align: \"center\", width: contentWidth });\n\n if (hash) {\n doc.moveDown(0.3);\n doc.fontSize(9).font(\"Courier\").fillColor(\"#78716c\").text(`SHA-256: ${hash}`, { align: \"center\", width: contentWidth });\n }\n\n doc.moveDown(2);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#6366f1\").text(\"kindlm.com\", { align: \"center\", link: \"https://kindlm.com\", width: contentWidth });\n\n // --- Content Pages ---\n const sections = parseSections(markdownContent);\n\n for (const section of sections) {\n doc.addPage();\n renderPageHeader(doc);\n\n if (section.heading) {\n const size = headingSize(section.headingLevel);\n doc.fontSize(size).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(section.heading, { width: contentWidth });\n doc.moveDown(0.5);\n doc.moveTo(60, doc.y).lineTo(60 + contentWidth, doc.y).strokeColor(\"#e7e5e4\").lineWidth(1).stroke();\n doc.moveDown(0.8);\n }\n\n const bodyLines = section.body.split(\"\\n\");\n let inCodeBlock = false;\n let i = 0;\n while (i < bodyLines.length) {\n const line = bodyLines[i] ?? \"\";\n\n if (line.startsWith(\"```\")) {\n inCodeBlock = !inCodeBlock;\n if (inCodeBlock) {\n ensureSpace(doc, 30);\n }\n i++;\n continue;\n }\n\n if (inCodeBlock) {\n ensureSpace(doc, 14);\n const codeY = doc.y as number;\n doc.save();\n doc.rect(doc.page.margins.left, codeY - 2, contentWidth, 14).fill(\"#f5f5f4\");\n doc.restore();\n doc.fontSize(9).font(\"Courier\").fillColor(\"#44403c\").text(line, { width: contentWidth });\n i++;\n continue;\n }\n\n if (!line.trim()) {\n doc.moveDown(0.4);\n i++;\n continue;\n }\n\n // Table detection: look ahead for separator line\n const nextLine = bodyLines[i + 1] ?? \"\";\n if (line.includes(\"|\") && i + 1 < bodyLines.length && nextLine.match(/^\\s*\\|[-:\\s|]+\\|\\s*$/)) {\n const tableLines: string[] = [];\n let j = i;\n while (j < bodyLines.length && (bodyLines[j] ?? \"\").includes(\"|\")) {\n tableLines.push(bodyLines[j] ?? \"\");\n j++;\n }\n const table = parseTable(tableLines);\n if (table) {\n renderTable(doc, table, contentWidth);\n i = j;\n continue;\n }\n }\n\n // Skip standalone separator lines\n if (line.match(/^\\s*\\|[-:]+/) || line.match(/^---+$/)) {\n i++;\n continue;\n }\n\n // Subheadings (### and ####)\n const subheadingMatch = line.match(/^(#{3,4})\\s+(.+)$/);\n if (subheadingMatch?.[1] && subheadingMatch[2]) {\n const level = subheadingMatch[1].length;\n const size = headingSize(level);\n ensureSpace(doc, size + 10);\n doc.moveDown(0.3);\n doc.fontSize(size).font(\"Helvetica-Bold\").fillColor(\"#1c1917\").text(subheadingMatch[2].trim(), { width: contentWidth });\n doc.moveDown(0.3);\n i++;\n continue;\n }\n\n // Bullet points\n if (line.match(/^\\s*[-*] /)) {\n ensureSpace(doc, 14);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#44403c\").text(line.trim(), { indent: 12, width: contentWidth - 12 });\n i++;\n continue;\n }\n\n // Regular text\n ensureSpace(doc, 14);\n doc.fontSize(10).font(\"Helvetica\").fillColor(\"#44403c\").text(line.trim(), { width: contentWidth });\n i++;\n }\n\n renderPageFooter(doc);\n }\n\n doc.end();\n\n stream.on(\"finish\", () => resolve(outputPath));\n stream.on(\"error\", reject);\n });\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { resolve, dirname, join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n readBaseline,\n writeBaseline,\n listBaselines,\n buildBaselineData,\n compareBaseline,\n deserializeBaseline,\n} from \"@kindlm/core\";\nimport { runTests } from \"../utils/run-tests.js\";\nimport { createFileBaselineIO } from \"../utils/baseline-io.js\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\n\ninterface BaselineSetOptions {\n config: string;\n runs?: string;\n}\n\ninterface BaselineCompareOptions {\n config: string;\n runs?: string;\n}\n\ninterface BaselineListOptions {\n config: string;\n}\n\nexport function registerBaselineCommand(program: Command): void {\n const baseline = program\n .command(\"baseline\")\n .description(\"Manage test baselines\");\n\n baseline\n .command(\"set\")\n .description(\"Save current results as baseline\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .option(\"--runs <count>\", \"Override run count\")\n .action(async (options: BaselineSetOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n // Run tests\n const { config, runnerResult } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n });\n\n const { aggregated } = runnerResult;\n\n // Build + write baseline\n const baselineData = buildBaselineData(\n config.suite.name,\n aggregated,\n new Date().toISOString(),\n );\n\n const writeResult = writeBaseline(baselineData, io);\n if (!writeResult.success) {\n console.error(chalk.red(`Failed to save baseline: ${writeResult.error.message}`));\n process.exit(1);\n }\n\n const testCount = Object.keys(baselineData.results).length;\n console.log(\"\");\n console.log(\n chalk.green(`Baseline saved for suite \"${config.suite.name}\" (${testCount} test${testCount === 1 ? \"\" : \"s\"})`),\n );\n console.log(chalk.dim(` Location: ${kindlmDir}/baselines/`));\n process.exit(0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n\n baseline\n .command(\"compare\")\n .description(\"Compare latest against baseline\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .option(\"--runs <count>\", \"Override run count\")\n .action(async (options: BaselineCompareOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n // We need the suite name from config before running tests.\n // Parse config minimally to get the suite name for baseline lookup.\n const configPath = resolve(process.cwd(), options.config);\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, {\n configDir,\n fileReader,\n });\n\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const suiteName = parseResult.data.suite.name;\n\n // Load baseline — fail fast if missing\n const baselineResult = readBaseline(suiteName, io);\n if (!baselineResult.success) {\n if (baselineResult.error.code === \"BASELINE_NOT_FOUND\") {\n console.error(chalk.red(`No baseline found for suite \"${suiteName}\". Run \\`kindlm baseline set\\` first.`));\n } else {\n console.error(chalk.red(`Failed to read baseline: ${baselineResult.error.message}`));\n }\n process.exit(1);\n }\n\n const baselineData = baselineResult.data;\n\n // Run tests with baseline injected for drift assertions\n const { runnerResult } = await runTests({\n configPath: options.config,\n runs: options.runs ? parseInt(options.runs, 10) : undefined,\n baselineData,\n });\n\n const { aggregated } = runnerResult;\n\n // Build current baseline data for comparison\n const currentData = buildBaselineData(\n suiteName,\n aggregated,\n new Date().toISOString(),\n );\n\n // Compare\n const comparison = compareBaseline(baselineData, currentData.results);\n\n // Print comparison report\n console.log(\"\");\n console.log(chalk.bold(`Baseline comparison for \"${suiteName}\"`));\n console.log(chalk.dim(` Baseline from: ${baselineData.createdAt}`));\n console.log(\"\");\n\n if (comparison.regressions.length > 0) {\n console.log(chalk.red.bold(` Regressions (${comparison.regressions.length}):`));\n for (const r of comparison.regressions) {\n console.log(chalk.red(` ${r.testName}: ${formatPercent(r.baselinePassRate)} → ${formatPercent(r.currentPassRate)}`));\n if (r.newFailureCodes.length > 0) {\n console.log(chalk.red(` New failures: ${r.newFailureCodes.join(\", \")}`));\n }\n }\n console.log(\"\");\n }\n\n if (comparison.improvements.length > 0) {\n console.log(chalk.green.bold(` Improvements (${comparison.improvements.length}):`));\n for (const imp of comparison.improvements) {\n console.log(chalk.green(` ${imp.testName}: ${formatPercent(imp.baselinePassRate)} → ${formatPercent(imp.currentPassRate)}`));\n }\n console.log(\"\");\n }\n\n if (comparison.unchanged.length > 0) {\n console.log(chalk.dim(` Unchanged (${comparison.unchanged.length}):`));\n for (const u of comparison.unchanged) {\n console.log(chalk.dim(` ${u.testName}: ${formatPercent(u.passRate)}`));\n }\n console.log(\"\");\n }\n\n if (comparison.newTests.length > 0) {\n console.log(chalk.cyan(` New tests (${comparison.newTests.length}):`));\n for (const t of comparison.newTests) {\n console.log(chalk.cyan(` ${t}`));\n }\n console.log(\"\");\n }\n\n if (comparison.removedTests.length > 0) {\n console.log(chalk.yellow(` Removed tests (${comparison.removedTests.length}):`));\n for (const t of comparison.removedTests) {\n console.log(chalk.yellow(` ${t}`));\n }\n console.log(\"\");\n }\n\n // Exit 1 if any regressions\n process.exit(comparison.regressions.length > 0 ? 1 : 0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n\n baseline\n .command(\"list\")\n .description(\"List saved baselines\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"kindlm.yaml\")\n .action((options: BaselineListOptions) => {\n try {\n const configDir = dirname(resolve(process.cwd(), options.config));\n const kindlmDir = join(configDir, \".kindlm\");\n const io = createFileBaselineIO(kindlmDir);\n\n const listResult = listBaselines(io);\n if (!listResult.success) {\n console.error(chalk.red(`Failed to list baselines: ${listResult.error.message}`));\n process.exit(1);\n }\n\n const names = listResult.data;\n\n if (names.length === 0) {\n console.log(chalk.dim(\"No baselines saved yet. Run `kindlm baseline set` to create one.\"));\n process.exit(0);\n }\n\n console.log(chalk.bold(\"Saved baselines:\"));\n console.log(\"\");\n\n for (const name of names) {\n const readResult = io.read(name);\n if (!readResult.success) {\n console.log(` ${name} ${chalk.dim(\"(unreadable)\")}`);\n continue;\n }\n\n const parsed = deserializeBaseline(readResult.data);\n if (!parsed.success) {\n console.log(` ${name} ${chalk.dim(\"(corrupt)\")}`);\n continue;\n }\n\n const testCount = Object.keys(parsed.data.results).length;\n console.log(` ${chalk.cyan(parsed.data.suiteName)} — ${testCount} test${testCount === 1 ? \"\" : \"s\"}, saved ${chalk.dim(parsed.data.createdAt)}`);\n }\n\n process.exit(0);\n } catch (e) {\n console.error(chalk.red(`Error: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nfunction formatPercent(rate: number): string {\n return `${(rate * 100).toFixed(1)}%`;\n}\n","import { readFileSync, writeFileSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { BaselineIO } from \"@kindlm/core\";\nimport type { Result } from \"@kindlm/core\";\n\nfunction sanitizeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport function createFileBaselineIO(kindlmDir: string): BaselineIO {\n const baselinesDir = join(kindlmDir, \"baselines\");\n\n return {\n read(suiteName: string): Result<string> {\n const filePath = join(baselinesDir, `${sanitizeFilename(suiteName)}.json`);\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return { success: true, data: content };\n } catch {\n return {\n success: false,\n error: {\n code: \"BASELINE_NOT_FOUND\",\n message: `No baseline found for suite \"${suiteName}\" at ${filePath}`,\n },\n };\n }\n },\n\n write(suiteName: string, content: string): Result<void> {\n try {\n mkdirSync(baselinesDir, { recursive: true });\n const filePath = join(baselinesDir, `${sanitizeFilename(suiteName)}.json`);\n writeFileSync(filePath, content, \"utf-8\");\n return { success: true, data: undefined };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"UNKNOWN_ERROR\",\n message: `Failed to write baseline: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n\n list(): Result<string[]> {\n try {\n mkdirSync(baselinesDir, { recursive: true });\n const files = readdirSync(baselinesDir);\n const names = files\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n return { success: true, data: names };\n } catch (e) {\n return {\n success: false,\n error: {\n code: \"UNKNOWN_ERROR\",\n message: `Failed to list baselines: ${e instanceof Error ? e.message : String(e)}`,\n },\n };\n }\n },\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { Writable } from \"node:stream\";\nimport chalk from \"chalk\";\nimport { loadToken, saveToken, clearToken } from \"../cloud/auth.js\";\nimport { createCloudClient, getCloudUrl, CloudApiError } from \"../cloud/client.js\";\n\ninterface LoginOptions {\n token?: string;\n status?: boolean;\n logout?: boolean;\n}\n\nexport function registerLoginCommand(program: Command): void {\n program\n .command(\"login\")\n .description(\"Authenticate with KindLM Cloud\")\n .option(\"-t, --token <token>\", \"API token (skips interactive prompt)\")\n .option(\"--status\", \"Show current authentication status\")\n .option(\"--logout\", \"Remove stored credentials\")\n .action(async (options: LoginOptions) => {\n try {\n if (options.logout) {\n clearToken();\n console.log(chalk.green(\"Logged out. Credentials removed.\"));\n return;\n }\n\n if (options.status) {\n await showStatus();\n return;\n }\n\n const token = options.token ?? process.env[\"KINDLM_API_TOKEN\"] ?? (await promptForToken());\n\n if (!token.startsWith(\"klm_\")) {\n console.error(chalk.red(\"Invalid token format. KindLM tokens start with \\\"klm_\\\".\"));\n process.exit(1);\n }\n\n // Validate token against Cloud API\n const client = createCloudClient(getCloudUrl(), token);\n try {\n await client.get(\"/v1/auth/tokens\");\n } catch (e) {\n if (e instanceof CloudApiError && e.status === 401) {\n console.error(chalk.red(\"Invalid or expired token.\"));\n process.exit(1);\n }\n throw e;\n }\n\n saveToken(token);\n console.log(chalk.green(\"Authenticated successfully. Token saved.\"));\n } catch (e) {\n console.error(chalk.red(`Login failed: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nasync function showStatus(): Promise<void> {\n const token = loadToken();\n if (!token) {\n console.log(chalk.yellow(\"Not authenticated. Run \\\"kindlm login\\\" to authenticate.\"));\n return;\n }\n\n const client = createCloudClient(getCloudUrl(), token);\n try {\n await client.get(\"/v1/auth/tokens\");\n console.log(chalk.green(\"Authenticated.\"));\n console.log(` Cloud URL: ${getCloudUrl()}`);\n } catch (e) {\n if (e instanceof CloudApiError && e.status === 401) {\n console.log(chalk.yellow(\"Stored token is invalid or expired. Run \\\"kindlm login\\\" to re-authenticate.\"));\n } else {\n console.log(chalk.yellow(`Cannot reach Cloud API: ${e instanceof Error ? e.message : String(e)}`));\n }\n }\n}\n\nfunction promptForToken(): Promise<string> {\n return new Promise((resolve, reject) => {\n // Muted output stream suppresses echo of typed characters\n const muted = new Writable({\n write(_chunk, _encoding, callback) {\n callback();\n },\n });\n\n process.stderr.write(\"Paste your KindLM API token: \");\n const rl = createInterface({ input: process.stdin, output: muted, terminal: true });\n\n rl.question(\"\", (answer) => {\n rl.close();\n process.stderr.write(\"\\n\");\n const trimmed = answer.trim();\n if (!trimmed) {\n reject(new Error(\"No token provided\"));\n return;\n }\n resolve(trimmed);\n });\n });\n}\n","import { readFileSync, writeFileSync, mkdirSync, unlinkSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\ninterface CredentialsFile {\n token: string;\n savedAt: string;\n}\n\nexport function getCredentialsPath(): string {\n return join(homedir(), \".kindlm\", \"credentials\");\n}\n\nexport function loadToken(): string | null {\n try {\n const raw = readFileSync(getCredentialsPath(), \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<CredentialsFile>;\n if (typeof parsed.token === \"string\" && parsed.token.length > 0) {\n return parsed.token;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function saveToken(token: string): void {\n const filePath = getCredentialsPath();\n const dir = join(homedir(), \".kindlm\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const data: CredentialsFile = { token, savedAt: new Date().toISOString() };\n writeFileSync(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n chmodSync(filePath, 0o600);\n}\n\nexport function clearToken(): void {\n try {\n unlinkSync(getCredentialsPath());\n } catch {\n // File doesn't exist — nothing to clear\n }\n}\n","const DEFAULT_CLOUD_URL = \"https://api.kindlm.com\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst RETRY_DELAY_MS = 1_000;\nconst MAX_RETRIES = 1;\n\nexport class CloudApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = \"CloudApiError\";\n this.status = status;\n }\n}\n\nexport interface CloudClient {\n baseUrl: string;\n get<T>(path: string): Promise<T>;\n post<T>(path: string, body: unknown): Promise<T>;\n patch<T>(path: string, body: unknown): Promise<T>;\n delete(path: string): Promise<void>;\n}\n\nexport function getCloudUrl(): string {\n const url = process.env[\"KINDLM_CLOUD_URL\"] ?? DEFAULT_CLOUD_URL;\n if (url.startsWith(\"http://\") && !isLocalhost(url)) {\n throw new Error(\n `Refusing to use insecure HTTP for Cloud API: ${url}. Use HTTPS or target localhost for development.`,\n );\n }\n return url;\n}\n\nfunction isLocalhost(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.hostname === \"localhost\" || parsed.hostname === \"127.0.0.1\" || parsed.hostname === \"::1\";\n } catch {\n return false;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function createCloudClient(baseUrl: string, token: string): CloudClient {\n async function request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${baseUrl}${path}`;\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n const init: RequestInit = { method, headers };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(body);\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n await sleep(RETRY_DELAY_MS);\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n init.signal = controller.signal;\n\n try {\n const response = await fetch(url, init);\n\n if (!response.ok) {\n // Retry on 5xx\n if (response.status >= 500 && attempt < MAX_RETRIES) {\n lastError = new CloudApiError(response.status, `HTTP ${response.status}`);\n continue;\n }\n\n let message = `HTTP ${response.status}`;\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n try {\n const errorBody = (await response.json()) as { error?: string };\n if (errorBody.error) {\n message = errorBody.error;\n }\n } catch {\n // ignore parse errors\n }\n }\n throw new CloudApiError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n // Validate response content-type is JSON before parsing\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) {\n throw new CloudApiError(\n response.status,\n `Expected JSON response but got content-type: ${contentType}`,\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n if (error instanceof CloudApiError) throw error;\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Retry on network errors (not CloudApiErrors)\n if (attempt < MAX_RETRIES) continue;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n throw lastError ?? new Error(\"Request failed\");\n }\n\n return {\n baseUrl,\n get: <T>(path: string) => request<T>(\"GET\", path),\n post: <T>(path: string, body: unknown) => request<T>(\"POST\", path, body),\n patch: <T>(path: string, body: unknown) => request<T>(\"PATCH\", path, body),\n delete: (path: string) => request<void>(\"DELETE\", path),\n };\n}\n","/* eslint-disable no-console */\nimport type { Command } from \"commander\";\nimport { basename } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { loadToken } from \"../cloud/auth.js\";\nimport { createCloudClient, getCloudUrl } from \"../cloud/client.js\";\nimport { loadLastRun } from \"../utils/last-run.js\";\nimport { getGitInfo } from \"../utils/git.js\";\nimport { detectCI } from \"../utils/env.js\";\nimport { uploadResults } from \"../cloud/upload.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\n\ninterface UploadCommandOptions {\n token?: string;\n project?: string;\n}\n\nexport function registerUploadCommand(program: Command): void {\n program\n .command(\"upload\")\n .description(\"Push last run results to KindLM Cloud\")\n .option(\"-t, --token <token>\", \"API token (overrides stored token)\")\n .option(\"-p, --project <name>\", \"Project name\")\n .action(async (options: UploadCommandOptions) => {\n try {\n const token = options.token ?? process.env[\"KINDLM_API_TOKEN\"] ?? loadToken();\n if (!token) {\n console.error(chalk.red(\"Not authenticated. Run \\\"kindlm login\\\" first or pass --token.\"));\n process.exit(1);\n }\n\n const lastRun = loadLastRun();\n if (!lastRun) {\n console.error(chalk.red(\"No test run found. Run \\\"kindlm test\\\" first.\"));\n process.exit(1);\n }\n\n const gitInfo = getGitInfo();\n const ciEnv = detectCI();\n const projectName = options.project ?? resolveProjectName();\n\n const client = createCloudClient(getCloudUrl(), token);\n const spinner = createSpinner();\n spinner.start(\"Uploading results to KindLM Cloud...\");\n\n try {\n const result = await uploadResults(client, lastRun.runnerResult, {\n projectName,\n suiteName: lastRun.suiteName,\n configHash: lastRun.configHash,\n commitSha: ciEnv.commitSha ?? gitInfo.commitSha ?? undefined,\n branch: ciEnv.branch ?? gitInfo.branch ?? undefined,\n environment: ciEnv.isCI ? \"ci\" : \"local\",\n triggeredBy: ciEnv.name ?? \"local\",\n });\n\n spinner.succeed(\"Uploaded successfully.\");\n console.log(` Run ID: ${result.runId}`);\n console.log(` Project: ${projectName}`);\n console.log(` Suite: ${lastRun.suiteName}`);\n } catch (e) {\n spinner.fail(\"Upload failed.\");\n throw e;\n }\n } catch (e) {\n console.error(chalk.red(`Upload failed: ${e instanceof Error ? e.message : String(e)}`));\n process.exit(1);\n }\n });\n}\n\nfunction extractRepoName(remoteUrl: string): string | null {\n // HTTPS: https://github.com/org/repo.git\n try {\n const parsed = new URL(remoteUrl);\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n if (last) return last.replace(/\\.git$/, \"\");\n } catch {\n // Not a standard URL — try SSH format\n }\n\n // SSH: git@github.com:org/repo.git\n const sshMatch = remoteUrl.match(/^[\\w.-]+@[\\w.-]+:(.+?)(?:\\.git)?$/);\n if (sshMatch?.[1]) {\n const segments = sshMatch[1].split(\"/\");\n return segments[segments.length - 1] ?? null;\n }\n\n return null;\n}\n\nfunction resolveProjectName(): string {\n try {\n const remote = execSync(\"git remote get-url origin\", { encoding: \"utf-8\" }).trim();\n const name = extractRepoName(remote);\n if (name) return name;\n } catch {\n // Not in a git repo or no remote\n }\n return basename(process.cwd());\n}\n","import { execSync } from \"node:child_process\";\n\nexport interface GitInfo {\n commitSha: string | null;\n branch: string | null;\n dirty: boolean;\n}\n\nexport function getGitInfo(): GitInfo {\n try {\n const commitSha = execSync(\"git rev-parse HEAD\", { encoding: \"utf-8\" }).trim() || null;\n const branch = execSync(\"git rev-parse --abbrev-ref HEAD\", { encoding: \"utf-8\" }).trim() || null;\n const status = execSync(\"git status --porcelain\", { encoding: \"utf-8\" }).trim();\n const dirty = status.length > 0;\n return { commitSha, branch, dirty };\n } catch {\n return { commitSha: null, branch: null, dirty: false };\n }\n}\n","export interface CIEnvironment {\n name: string | null;\n isCI: boolean;\n commitSha: string | null;\n branch: string | null;\n}\n\nexport function detectCI(): CIEnvironment {\n if (process.env[\"GITHUB_ACTIONS\"]) {\n return {\n name: \"github_actions\",\n isCI: true,\n commitSha: process.env[\"GITHUB_SHA\"] ?? null,\n branch: process.env[\"GITHUB_REF_NAME\"] ?? null,\n };\n }\n\n if (process.env[\"GITLAB_CI\"]) {\n return {\n name: \"gitlab_ci\",\n isCI: true,\n commitSha: process.env[\"CI_COMMIT_SHA\"] ?? null,\n branch: process.env[\"CI_COMMIT_BRANCH\"] ?? null,\n };\n }\n\n if (process.env[\"CI\"]) {\n return {\n name: null,\n isCI: true,\n commitSha: null,\n branch: null,\n };\n }\n\n return {\n name: null,\n isCI: false,\n commitSha: null,\n branch: null,\n };\n}\n","import type { CloudClient } from \"./client.js\";\nimport type { RunnerResult } from \"@kindlm/core\";\nimport type { AggregatedTestResult } from \"@kindlm/core\";\n\nfunction e(segment: string): string {\n return encodeURIComponent(segment);\n}\n\nexport interface UploadOptions {\n projectName: string;\n suiteName: string;\n configHash: string;\n commitSha?: string;\n branch?: string;\n environment?: string;\n triggeredBy?: string;\n}\n\ninterface CloudProject {\n id: string;\n name: string;\n}\n\ninterface CloudSuite {\n id: string;\n name: string;\n}\n\ninterface CloudRun {\n id: string;\n}\n\nexport interface UploadResult {\n runId: string;\n projectId: string;\n}\n\nexport async function uploadResults(\n client: CloudClient,\n runnerResult: RunnerResult,\n options: UploadOptions,\n): Promise<UploadResult> {\n // 1. Find or create project\n const projectId = await findOrCreateProject(client, options.projectName);\n\n // 2. Find or create suite\n const suiteId = await findOrCreateSuite(client, projectId, options.suiteName, options.configHash);\n\n // 3. Create run\n const run = await client.post<CloudRun>(`/v1/runs/${e(projectId)}/runs`, {\n suiteId,\n commitSha: options.commitSha,\n branch: options.branch,\n environment: options.environment,\n triggeredBy: options.triggeredBy,\n });\n\n // 4. Batch insert results (chunks of 50 to avoid payload limits)\n const results = mapAggregatedResults(runnerResult.aggregated);\n const BATCH_SIZE = 50;\n for (let i = 0; i < results.length; i += BATCH_SIZE) {\n const batch = results.slice(i, i + BATCH_SIZE);\n await client.post(`/v1/results/${e(run.id)}/results`, { results: batch });\n }\n\n // 5. Compute run-level metrics and finalize\n const { runResult } = runnerResult;\n const passRate = runResult.totalTests > 0\n ? runResult.passed / runResult.totalTests\n : 0;\n\n const modelIds = new Set(runnerResult.aggregated.map((a) => a.modelId));\n const judgeScores = runnerResult.aggregated\n .map((a) => a.assertionScores[\"judge\"]?.mean)\n .filter((s): s is number => s !== undefined);\n const judgeAvgScore = judgeScores.length > 0\n ? judgeScores.reduce((a, b) => a + b, 0) / judgeScores.length\n : undefined;\n\n const latencies = runnerResult.aggregated.map((a) => a.latencyAvgMs);\n const latencyAvgMs = latencies.length > 0\n ? latencies.reduce((a, b) => a + b, 0) / latencies.length\n : undefined;\n\n const totalCost = runnerResult.aggregated.reduce((sum, a) => sum + a.totalCostUsd, 0);\n const costEstimateUsd = totalCost > 0 ? totalCost : undefined;\n\n await client.patch(`/v1/runs/${e(run.id)}`, {\n status: \"completed\",\n passRate,\n testCount: runResult.totalTests,\n modelCount: modelIds.size,\n judgeAvgScore,\n latencyAvgMs,\n costEstimateUsd,\n finishedAt: new Date().toISOString(),\n });\n\n return { runId: run.id, projectId };\n}\n\nasync function findOrCreateProject(client: CloudClient, name: string): Promise<string> {\n const { projects } = await client.get<{ projects: CloudProject[] }>(\"/v1/projects\");\n const existing = projects.find((p) => p.name === name);\n if (existing) return existing.id;\n\n const created = await client.post<CloudProject>(\"/v1/projects\", { name });\n return created.id;\n}\n\nasync function findOrCreateSuite(\n client: CloudClient,\n projectId: string,\n name: string,\n configHash: string,\n): Promise<string> {\n const { suites } = await client.get<{ suites: CloudSuite[] }>(\n `/v1/suites/${e(projectId)}/suites`,\n );\n const existing = suites.find((s) => s.name === name);\n if (existing) return existing.id;\n\n const created = await client.post<CloudSuite>(`/v1/suites/${e(projectId)}/suites`, {\n name,\n configHash,\n });\n return created.id;\n}\n\ninterface CloudTestResult {\n testCaseName: string;\n modelId: string;\n passed: number;\n passRate: number;\n runCount: number;\n judgeAvg: number | null;\n driftScore: number | null;\n latencyAvgMs: number | null;\n costUsd: number | null;\n totalTokens: number | null;\n failureCodes: string | null;\n failureMessages: string | null;\n assertionScores: string | null;\n}\n\nexport function mapAggregatedResults(aggregated: AggregatedTestResult[]): CloudTestResult[] {\n return aggregated.map((agg) => {\n // Extract failure messages from runs, or use pre-computed value from cache\n const cached = agg as AggregatedTestResult & { failureMessages?: string[] };\n const failureMessages = agg.runs.length > 0\n ? agg.runs\n .flatMap((r) => r.assertions.filter((a) => !a.passed).map((a) => a.failureMessage))\n .filter((m): m is string => m !== undefined)\n : (cached.failureMessages ?? []);\n\n return {\n testCaseName: agg.testCaseName,\n modelId: agg.modelId,\n passed: agg.passed ? 1 : 0,\n passRate: agg.passRate,\n runCount: agg.runCount,\n judgeAvg: agg.assertionScores[\"judge\"]?.mean ?? null,\n driftScore: agg.assertionScores[\"drift\"]?.mean ?? null,\n latencyAvgMs: agg.latencyAvgMs ?? null,\n costUsd: agg.totalCostUsd ?? null,\n totalTokens: agg.totalTokens ?? null,\n failureCodes: agg.failureCodes.length > 0 ? JSON.stringify(agg.failureCodes) : null,\n failureMessages: failureMessages.length > 0 ? JSON.stringify(failureMessages) : null,\n assertionScores: Object.keys(agg.assertionScores).length > 0\n ? JSON.stringify(agg.assertionScores)\n : null,\n };\n });\n}\n","/* eslint-disable no-console */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport type { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport {\n parseConfig,\n createProvider,\n filterSpans,\n mapSpansToResult,\n buildContextFromTrace,\n createAssertionsFromExpect,\n evaluateGates,\n} from \"@kindlm/core\";\nimport type {\n KindLMConfig,\n ProviderAdapter,\n TraceConfig,\n AssertionResult,\n} from \"@kindlm/core\";\nimport { createTraceServer } from \"../utils/trace-server.js\";\nimport { createSpinner } from \"../utils/spinner.js\";\nimport { createNodeFileReader } from \"../utils/file-reader.js\";\nimport { createHttpClient } from \"../utils/http.js\";\n\nexport function registerTraceCommand(program: Command): void {\n program\n .command(\"trace\")\n .description(\"Ingest OpenTelemetry traces and run assertions against them\")\n .option(\"-c, --config <path>\", \"Config file path\", \"kindlm.yaml\")\n .option(\"--port <port>\", \"OTLP HTTP port\", \"4318\")\n .option(\"--command <cmd>\", \"Command to spawn (traces are collected while it runs)\")\n .option(\"--timeout <ms>\", \"Timeout in ms to wait for traces\", \"30000\")\n .option(\"--reporter <type>\", \"Report format: pretty, json, junit\", \"pretty\")\n .action(async (opts: {\n config: string;\n port: string;\n command?: string;\n timeout: string;\n reporter: string;\n }) => {\n const spinner = createSpinner();\n\n try {\n // 1. Read and parse config\n const configPath = resolve(process.cwd(), opts.config);\n const configDir = dirname(configPath);\n\n try {\n const stat = statSync(configPath);\n if (stat.size > 1_048_576) {\n console.error(chalk.red(\"Config file exceeds 1MB limit\"));\n process.exit(1);\n }\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n let yamlContent: string;\n try {\n yamlContent = readFileSync(configPath, \"utf-8\");\n } catch {\n console.error(chalk.red(`Config file not found: ${configPath}`));\n process.exit(1);\n }\n\n const fileReader = createNodeFileReader();\n const parseResult = parseConfig(yamlContent, { configDir, fileReader });\n if (!parseResult.success) {\n console.error(chalk.red(`Config validation failed: ${parseResult.error.message}`));\n process.exit(1);\n }\n\n const config: KindLMConfig = parseResult.data;\n const traceConfig: TraceConfig = config.trace ?? {\n port: parseInt(opts.port, 10),\n timeoutMs: parseInt(opts.timeout, 10),\n spanMapping: {\n outputTextAttr: \"gen_ai.completion.0.content\",\n modelAttr: \"gen_ai.response.model\",\n systemAttr: \"gen_ai.system\",\n inputTokensAttr: \"gen_ai.usage.input_tokens\",\n outputTokensAttr: \"gen_ai.usage.output_tokens\",\n },\n };\n\n const port = parseInt(opts.port, 10) || traceConfig.port;\n const timeoutMs = parseInt(opts.timeout, 10) || traceConfig.timeoutMs;\n\n // 2. Start trace server\n const traceServer = createTraceServer(port);\n await traceServer.start();\n spinner.start(`Listening for OTLP traces on port ${port}...`);\n\n // 3. Optionally spawn command\n if (opts.command) {\n const child = spawn(\"sh\", [\"-c\", opts.command], {\n cwd: configDir,\n env: {\n ...process.env,\n OTEL_EXPORTER_OTLP_ENDPOINT: `http://localhost:${port}`,\n OTEL_EXPORTER_OTLP_PROTOCOL: \"http/json\",\n },\n stdio: \"inherit\",\n });\n\n child.on(\"error\", (e) => {\n spinner.fail(`Command failed: ${e.message}`);\n });\n }\n\n // 4. Wait for traces\n const spans = await traceServer.waitForSpans({ timeoutMs });\n await traceServer.stop();\n\n if (spans.length === 0) {\n spinner.fail(\"No traces received\");\n process.exit(1);\n }\n\n spinner.succeed(`Received ${spans.length} spans`);\n\n // 5. Filter and map spans\n const filtered = filterSpans(spans, traceConfig.spanFilter);\n const mappingResult = mapSpansToResult(filtered, traceConfig.spanMapping);\n\n // 6. Resolve judge adapter if needed\n const httpClient = createHttpClient();\n const adapters = new Map<string, ProviderAdapter>();\n const providers = config.providers as Record<string, { apiKeyEnv?: string; baseUrl?: string; organization?: string } | undefined>;\n\n for (const [name, providerConfig] of Object.entries(providers)) {\n if (!providerConfig) continue;\n let apiKey = \"\";\n if (providerConfig.apiKeyEnv) {\n const key = process.env[providerConfig.apiKeyEnv];\n if (key) apiKey = key.trim();\n }\n if (!apiKey && name !== \"ollama\") continue;\n\n try {\n const adapter = createProvider(name, httpClient);\n await adapter.initialize({\n apiKey,\n baseUrl: providerConfig.baseUrl,\n organization: providerConfig.organization,\n timeoutMs: config.defaults.timeoutMs,\n maxRetries: 2,\n });\n adapters.set(name, adapter);\n } catch {\n // Skip providers that fail to initialize\n }\n }\n\n const judgeModelId = config.defaults.judgeModel ?? config.models[0]?.id;\n const judgeModelConfig = config.models.find((m) => m.id === judgeModelId);\n const judgeAdapter = judgeModelConfig ? adapters.get(judgeModelConfig.provider) : undefined;\n\n // 7. Build context and evaluate assertions for each test\n const context = buildContextFromTrace(mappingResult, {\n configDir,\n judgeAdapter,\n judgeModel: judgeModelConfig?.model,\n });\n\n const testResults: { testName: string; assertions: AssertionResult[] }[] = [];\n\n for (const test of config.tests) {\n if (test.skip) continue;\n\n const assertions = createAssertionsFromExpect(test.expect);\n const allResults: AssertionResult[] = [];\n for (const assertion of assertions) {\n const results = await assertion.evaluate(context);\n allResults.push(...results);\n }\n testResults.push({ testName: test.name, assertions: allResults });\n }\n\n // 8. Report results\n const totalAssertions = testResults.reduce((s, t) => s + t.assertions.length, 0);\n const passedAssertions = testResults.reduce(\n (s, t) => s + t.assertions.filter((a) => a.passed).length,\n 0,\n );\n const failedAssertions = totalAssertions - passedAssertions;\n\n console.log();\n console.log(chalk.bold(\"Trace Test Results\"));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n for (const { testName, assertions } of testResults) {\n const allPassed = assertions.every((a) => a.passed);\n const icon = allPassed ? chalk.green(\"✓\") : chalk.red(\"✗\");\n console.log(`${icon} ${testName}`);\n\n for (const a of assertions) {\n const aIcon = a.passed ? chalk.green(\" ✓\") : chalk.red(\" ✗\");\n const label = a.failureMessage ? `${a.label}: ${a.failureMessage}` : a.label;\n console.log(`${aIcon} ${label}`);\n }\n }\n\n console.log();\n console.log(\n `${chalk.bold(\"Total:\")} ${passedAssertions} passed, ${failedAssertions} failed out of ${totalAssertions} assertions`,\n );\n\n // 9. Evaluate gates\n // Build minimal aggregated results for gate evaluation\n const gateEval = evaluateGates(config.gates, []);\n if (!gateEval.passed) {\n for (const g of gateEval.gates.filter((g) => !g.passed)) {\n console.log(chalk.red(`Gate failed: ${g.message}`));\n }\n }\n\n // 10. Exit code\n process.exit(failedAssertions > 0 ? 1 : 0);\n } catch (e) {\n spinner.fail(`Trace command failed: ${e instanceof Error ? e.message : String(e)}`);\n process.exit(1);\n }\n });\n}\n","import { createServer } from \"node:http\";\nimport type { Server, IncomingMessage, ServerResponse } from \"node:http\";\nimport { parseOtlpPayload } from \"@kindlm/core\";\nimport type { ParsedSpan } from \"@kindlm/core\";\n\nexport interface TraceServer {\n start(): Promise<void>;\n stop(): Promise<void>;\n getSpans(): ParsedSpan[];\n waitForSpans(options: { timeoutMs: number }): Promise<ParsedSpan[]>;\n}\n\nexport function createTraceServer(port: number): TraceServer {\n const collectedSpans: ParsedSpan[] = [];\n let server: Server | null = null;\n let spanListeners: (() => void)[] = [];\n\n function notifyListeners() {\n for (const listener of spanListeners) {\n listener();\n }\n }\n\n function handleRequest(req: IncomingMessage, res: ServerResponse) {\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method !== \"POST\" || req.url !== \"/v1/traces\") {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n try {\n const body = Buffer.concat(chunks).toString(\"utf-8\");\n const parsed = JSON.parse(body) as unknown;\n const result = parseOtlpPayload(parsed);\n\n if (result.success) {\n collectedSpans.push(...result.data);\n notifyListeners();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ partialSuccess: {} }));\n } else {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: result.error.message }));\n }\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n }\n });\n }\n\n return {\n start() {\n return new Promise<void>((resolve, reject) => {\n server = createServer(handleRequest);\n server.on(\"error\", reject);\n server.listen(port, () => resolve());\n });\n },\n\n stop() {\n return new Promise<void>((resolve) => {\n if (server) {\n server.close(() => resolve());\n } else {\n resolve();\n }\n });\n },\n\n getSpans() {\n return [...collectedSpans];\n },\n\n waitForSpans({ timeoutMs }) {\n return new Promise<ParsedSpan[]>((resolve) => {\n if (collectedSpans.length > 0) {\n resolve([...collectedSpans]);\n return;\n }\n\n const timer = setTimeout(() => {\n spanListeners = spanListeners.filter((l) => l !== onSpan);\n resolve([...collectedSpans]);\n }, timeoutMs);\n\n const onSpan = () => {\n clearTimeout(timer);\n spanListeners = spanListeners.filter((l) => l !== onSpan);\n // Wait a short period for any remaining spans to arrive\n setTimeout(() => resolve([...collectedSpans]), 500);\n };\n spanListeners.push(onSpan);\n });\n },\n };\n}\n"],"mappings":"AAAA,OAAS,WAAAA,OAAe,YCExB,OAAS,cAAAC,GAAY,iBAAAC,OAAqB,KAC1C,OAAS,WAAAC,OAAe,OACxB,OAAOC,MAAW,QAElB,IAAMC,GAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CV,SAASC,GAAoBC,EAAwB,CAC1DA,EACG,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,OAAO,UAAW,gCAAgC,EAClD,OAAQC,GAAiC,CACxC,IAAMC,EAAWN,GAAQ,QAAQ,IAAI,EAAG,aAAa,EAEjDF,GAAWQ,CAAQ,GAAK,CAACD,EAAQ,QACnC,QAAQ,MAAMJ,EAAM,IAAI,uDAAuD,CAAC,EAChF,QAAQ,KAAK,CAAC,GAGhBF,GAAcO,EAAUJ,GAAU,OAAO,EACzC,QAAQ,IAAID,EAAM,MAAM,qBAAqB,CAAC,EAC9C,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAI,aAAa,EACzB,QAAQ,IAAI,aAAaA,EAAM,KAAK,aAAa,CAAC,+BAA+B,EACjF,QAAQ,IAAI,0BAA0BA,EAAM,KAAK,8BAA8B,CAAC,EAAE,EAClF,QAAQ,IAAI,mBAAmBA,EAAM,KAAK,aAAa,CAAC,EAAE,CAC5D,CAAC,CACL,CCvEA,OAAS,gBAAAM,OAAoB,KAC7B,OAAS,WAAAC,GAAS,WAAAC,OAAe,OACjC,OAAOC,MAAW,QAClB,OAAS,eAAAC,OAAmB,eCL5B,OAAS,gBAAAC,OAAoB,KAGtB,SAASC,GAAmC,CACjD,MAAO,CACL,SAASC,EAA8B,CACrC,GAAI,CACF,MAAO,CAAE,QAAS,GAAM,KAAMF,GAAaE,EAAM,OAAO,CAAE,CAC5D,OAASC,EAAG,CACV,MAAO,CACL,QAAS,GACT,MAAO,CACL,KAAM,wBACN,QAAS,qBAAqBD,CAAI,KAAKC,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EACnF,CACF,CACF,CACF,CACF,CACF,CDXO,SAASC,GAAwBC,EAAwB,CAC9DA,EACG,QAAQ,UAAU,EAClB,YAAY,oCAAoC,EAChD,OAAO,sBAAuB,sBAAuB,aAAa,EAClE,OAAQC,GAAgC,CACvC,IAAMC,EAAaC,GAAQ,QAAQ,IAAI,EAAGF,EAAQ,MAAM,EAClDG,EAAYC,GAAQH,CAAU,EAEhCI,EACJ,GAAI,CACFA,EAAcC,GAAaL,EAAY,OAAO,CAChD,MAAQ,CACN,QAAQ,MAAMM,EAAM,IAAI,0BAA0BN,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAMO,EAAaC,EAAqB,EAClCC,EAASC,GAAYN,EAAa,CAAE,UAAAF,EAAW,WAAAK,CAAW,CAAC,EAEjE,GAAI,CAACE,EAAO,QAAS,CACnB,QAAQ,MAAMH,EAAM,IAAI,oBAAoB,CAAC,EAC7C,IAAMK,EAAUF,EAAO,MAAM,QAC7B,GAAIE,GAAW,MAAM,QAAQA,EAAQ,MAAS,EAC5C,QAAWC,KAAKD,EAAQ,OACtB,QAAQ,MAAML,EAAM,IAAI,OAAOM,CAAC,EAAE,CAAC,OAGrC,QAAQ,MAAMN,EAAM,IAAI,KAAKG,EAAO,MAAM,OAAO,EAAE,CAAC,EAEtD,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAMI,EAASJ,EAAO,KACtB,QAAQ,IAAIH,EAAM,MAAM,kBAAkB,CAAC,EAC3C,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAI,aAAaA,EAAM,KAAKO,EAAO,MAAM,IAAI,CAAC,EAAE,EACxD,QAAQ,IAAI,aAAaP,EAAM,KAAK,OAAOO,EAAO,MAAM,MAAM,CAAC,CAAC,EAAE,EAClE,QAAQ,IAAI,aAAaP,EAAM,KAAK,OAAOO,EAAO,OAAO,MAAM,CAAC,CAAC,EAAE,CACrE,CAAC,CACL,CE9CA,OAAOC,MAAW,QAClB,OACE,iBAAAC,GACA,wBAAAC,GACA,sBAAAC,GACA,uBAAAC,GACA,4BAAAC,GACA,iBAAAC,OACK,eCTP,OAAS,gBAAAC,GAAc,YAAAC,OAAgB,KACvC,OAAS,WAAAC,GAAS,WAAAC,OAAe,OACjC,OAAOC,MAAW,QAClB,OACE,eAAAC,GACA,kBAAAC,GACA,gBAAAC,OACK,eCNA,SAASC,GAA+B,CAC7C,MAAO,CACL,MAAM,MAAMC,EAAaC,EAA8C,CACrE,IAAMC,EAAa,IAAI,gBACjBC,EAAYF,EAAK,UACnB,WAAW,IAAMC,EAAW,MAAM,EAAGD,EAAK,SAAS,EACnD,OAEJ,GAAI,CACF,IAAMG,EAAW,MAAM,WAAW,MAAMJ,EAAK,CAC3C,OAAQC,EAAK,OACb,QAASA,EAAK,QACd,KAAMA,EAAK,KACX,OAAQC,EAAW,MACrB,CAAC,EAED,MAAO,CACL,GAAIE,EAAS,GACb,OAAQA,EAAS,OACjB,KAAM,IAAMA,EAAS,KAAK,CAC5B,CACF,QAAE,CACID,IAAc,QAAW,aAAaA,CAAS,CACrD,CACF,CACF,CACF,CC5BA,OAAOE,OAAuB,MASvB,SAASC,GAAyB,CACvC,IAAIC,EAEJ,MAAO,CACL,MAAMC,EAAoB,CACxBD,EAAWF,GAAIG,CAAI,EAAE,MAAM,CAC7B,EACA,QAAQA,EAAoB,CAC1BD,GAAU,QAAQC,CAAI,EACtBD,EAAW,MACb,EACA,KAAKC,EAAoB,CACvBD,GAAU,KAAKC,CAAI,EACnBD,EAAW,MACb,EACA,MAAa,CACXA,GAAU,KAAK,EACfA,EAAW,MACb,CACF,CACF,CC7BA,OAAS,SAAAE,OAAa,gBACtB,OAAS,MAAAC,GAAI,OAAAC,OAAW,eAGjB,SAASC,IAA6C,CAC3D,MAAO,CACL,MAAM,QAAQC,EAAiBC,EAAmE,CAChG,OAAO,IAAI,QAASC,GAAY,CAC9B,IAAMC,EAAQP,GAAM,KAAM,CAAC,KAAMI,CAAO,EAAG,CACzC,IAAKC,EAAQ,IACb,IAAK,CAAE,GAAG,QAAQ,IAAK,GAAGA,EAAQ,GAAI,EACtC,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EAEKG,EAAyB,CAAC,EAC1BC,EAAyB,CAAC,EAEhCF,EAAM,OAAO,GAAG,OAASG,GAAkBF,EAAa,KAAKE,CAAK,CAAC,EACnEH,EAAM,OAAO,GAAG,OAASG,GAAkBD,EAAa,KAAKC,CAAK,CAAC,EAEnE,IAAMC,EAAQ,WAAW,IAAM,CAC7BJ,EAAM,KAAK,SAAS,EACpB,WAAW,IAAM,CACVA,EAAM,QAAQA,EAAM,KAAK,SAAS,CACzC,EAAG,GAAI,CACT,EAAGF,EAAQ,SAAS,EAEpBE,EAAM,GAAG,QAAS,CAACK,EAAMC,IAAW,CAGlC,GAFA,aAAaF,CAAK,EAEdE,IAAW,WAAaA,IAAW,UAAW,CAChDP,EAAQJ,GAAI,CACV,KAAM,mBACN,QAAS,2BAA2BG,EAAQ,SAAS,IACvD,CAAC,CAAC,EACF,MACF,CAEAC,EAAQL,GAAG,CACT,OAAQ,OAAO,OAAOO,CAAY,EAAE,SAAS,OAAO,EACpD,OAAQ,OAAO,OAAOC,CAAY,EAAE,SAAS,OAAO,EACpD,SAAUG,GAAQ,CACpB,CAAC,CAAC,CACJ,CAAC,EAEDL,EAAM,GAAG,QAAUO,GAAM,CACvB,aAAaH,CAAK,EAClBL,EAAQJ,GAAI,CACV,KAAM,gBACN,QAAS,4BAA4BY,EAAE,OAAO,EAChD,CAAC,CAAC,CACJ,CAAC,CACH,CAAC,CACH,CACF,CACF,CHpBA,IAAMC,GAAkB,QAExB,eAAsBC,EAASC,EAAmD,CAChF,IAAMC,EAAUC,EAAc,EAG1BC,EAAc,GACZC,EAAgB,IAAM,CACtBD,GAAa,QAAQ,KAAK,GAAG,EACjCA,EAAc,GACdF,EAAQ,KAAK,EACb,QAAQ,MAAMI,EAAM,OAAO;AAAA,wBAA2B,CAAC,EACvD,QAAQ,KAAK,GAAG,CAClB,EACA,QAAQ,GAAG,SAAUD,CAAa,EAElC,GAAI,CACF,OAAO,MAAME,GAAcN,EAASC,CAAO,CAC7C,QAAE,CACA,QAAQ,eAAe,SAAUG,CAAa,CAChD,CACF,CAEA,eAAeE,GACbN,EACAC,EACyB,CAEzB,IAAMM,EAAaC,GAAQ,QAAQ,IAAI,EAAGR,EAAQ,UAAU,EACtDS,EAAYC,GAAQH,CAAU,EAGpC,GAAI,CACF,IAAMI,EAAOC,GAASL,CAAU,EAC5BI,EAAK,KAAOb,KACd,QAAQ,MAAMO,EAAM,IAAI,mCAAmCM,EAAK,KAAO,SAAW,QAAQ,CAAC,CAAC,QAAQJ,CAAU,EAAE,CAAC,EACjH,QAAQ,KAAK,CAAC,EAElB,MAAQ,CACN,QAAQ,MAAMF,EAAM,IAAI,0BAA0BE,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAIM,EACJ,GAAI,CACFA,EAAcC,GAAaP,EAAY,OAAO,CAChD,MAAQ,CACN,QAAQ,MAAMF,EAAM,IAAI,0BAA0BE,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAGA,IAAMQ,EAAaC,EAAqB,EAClCC,EAAcC,GAAYL,EAAa,CAAE,UAAAJ,EAAW,WAAAM,CAAW,CAAC,EACjEE,EAAY,UACf,QAAQ,MAAMZ,EAAM,IAAI,6BAA6BY,EAAY,MAAM,OAAO,EAAE,CAAC,EACjF,QAAQ,KAAK,CAAC,GAGhB,IAAME,EAAuBF,EAAY,KAGrCjB,EAAQ,OAAS,SACnBmB,EAAO,SAAS,OAASnB,EAAQ,MAE/BA,EAAQ,OAAS,SACdmB,EAAO,MAGVA,EAAO,MAAM,YAAcnB,EAAQ,KAAO,IAF1CmB,EAAO,MAAQ,CAAE,YAAanB,EAAQ,KAAO,GAAI,GAOrD,IAAMoB,EAAaC,EAAiB,EAC9BC,EAAW,IAAI,IAEfC,EAAYJ,EAAO,UACzB,OAAW,CAACK,EAAMC,CAAc,IAAK,OAAO,QAAQF,CAAS,EAAG,CAC9D,GAAI,CAACE,EAAgB,SAErB,IAAIC,EAAS,GACb,GAAID,EAAe,UAAW,CAC5B,IAAME,EAAM,QAAQ,IAAIF,EAAe,SAAS,EAC3CE,IACH,QAAQ,MAAMtB,EAAM,IAAI,iCAAiCoB,EAAe,SAAS,EAAE,CAAC,EACpF,QAAQ,KAAK,CAAC,GAEhBC,EAASC,EAAI,KAAK,CACpB,MAAWH,IAAS,WAClB,QAAQ,MAAMnB,EAAM,IAAI,aAAamB,CAAI,uCAAuC,CAAC,EACjF,QAAQ,KAAK,CAAC,GAGhB,IAAII,EACJ,GAAI,CACFA,EAAUC,GAAeL,EAAMJ,CAAU,CAC3C,OAASU,EAAO,CACd,IAAMC,EAAMD,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACjE,QAAQ,MAAMzB,EAAM,IAAI,8BAA8BmB,CAAI,MAAMO,CAAG,EAAE,CAAC,EACtE,QAAQ,KAAK,CAAC,CAChB,CAEA,MAAMH,EAAQ,WAAW,CACvB,OAAAF,EACA,QAASD,EAAe,QACxB,aAAcA,EAAe,aAC7B,UAAWN,EAAO,SAAS,UAC3B,WAAY,CACd,CAAC,EAEDG,EAAS,IAAIE,EAAMI,CAAO,CAC5B,CAGA,IAAII,EAAiB,EACfC,EAAaC,GAAoBf,CAAM,EAEvCgB,EAAcC,GAAyB,CACvCA,EAAM,OAAS,aACjBnC,EAAQ,MAAM,WAAWmC,EAAM,IAAI,KAAKA,EAAM,KAAK,MAAMJ,CAAc,IAAIC,CAAU,GAAG,EAC/EG,EAAM,OAAS,iBACxBJ,GAEJ,EAIMK,EADkBlB,EAAO,MAAM,KAAMmB,GAAMA,EAAE,OAAO,EAChBC,GAA0B,EAAI,OAWlEC,EAAY,MATHC,GAAatB,EAAQ,CAClC,SAAAG,EACA,UAAAb,EACA,WAAAM,EACA,WAAAoB,EACA,aAAcnC,EAAQ,aACtB,gBAAAqC,CACF,CAAC,EAE8B,IAAI,EACnC,OAAApC,EAAQ,KAAK,EAERuC,EAAU,UACb,QAAQ,MAAMnC,EAAM,IAAI,eAAemC,EAAU,MAAM,OAAO,EAAE,CAAC,EACjE,QAAQ,KAAK,CAAC,GAGT,CACL,OAAArB,EACA,aAAcqB,EAAU,KACxB,UAAA/B,EACA,YAAAI,CACF,CACF,CAEA,SAASqB,GAAoBf,EAA8B,CACzD,IAAIuB,EAAQ,EACZ,QAAWC,KAAQxB,EAAO,MAAO,CAC/B,GAAIwB,EAAK,KAAM,SACf,IAAMC,EAASD,EAAK,QAAUxB,EAAO,SAAS,OAC9C,GAAIwB,EAAK,QACPD,GAASE,MACJ,CACL,IAAMC,EAAaF,EAAK,QAAQ,QAAUxB,EAAO,OAAO,OACxDuB,GAASG,EAAaD,CACxB,CACF,CACA,OAAOF,CACT,CI3MA,OAAS,gBAAAI,GAAc,iBAAAC,GAAe,aAAAC,OAAiB,KACvD,OAAS,QAAAC,OAAY,OACrB,OAAS,cAAAC,OAAkB,SAU3B,SAASC,IAAyB,CAChC,OAAOF,GAAK,QAAQ,IAAI,EAAG,UAAW,eAAe,CACvD,CAEO,SAASG,GAAYC,EAAyB,CACnD,IAAMC,EAAWH,GAAe,EAC1BI,EAAMN,GAAK,QAAQ,IAAI,EAAG,SAAS,EACzCD,GAAUO,EAAK,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EAI/C,IAAMC,EAAuB,CAC3B,GAAGH,EACH,aAAc,CACZ,GAAGA,EAAK,aACR,WAAYA,EAAK,aAAa,WAAW,IAAKI,GAAQ,CACpD,IAAMC,EAAkBD,EAAI,KACzB,QAASE,GAAMA,EAAE,WAAW,OAAQC,GAAM,CAACA,EAAE,MAAM,EAAE,IAAKA,GAAMA,EAAE,cAAc,CAAC,EACjF,OAAQC,GAAmBA,IAAM,MAAS,EAE7C,MAAO,CACL,GAAGJ,EACH,gBAAAC,EACA,KAAM,CAAC,CACT,CACF,CAAC,CACH,CACF,EAEAX,GAAcO,EAAU,KAAK,UAAUE,CAAO,EAAG,CAAE,KAAM,GAAM,CAAC,CAClE,CAEO,SAASM,IAAkC,CAChD,GAAI,CACF,IAAMC,EAAMjB,GAAaK,GAAe,EAAG,OAAO,EAC5Ca,EAAS,KAAK,MAAMD,CAAG,EAC7B,OACEC,EAAO,cAAc,WACrB,MAAM,QAAQA,EAAO,aAAa,UAAU,GAC5C,OAAOA,EAAO,WAAc,UAC5B,OAAOA,EAAO,YAAe,UAC7B,OAAOA,EAAO,WAAc,SAErBA,EAEF,IACT,MAAQ,CACN,OAAO,IACT,CACF,CAEO,SAASC,GAAkBC,EAA6B,CAC7D,OAAOhB,GAAW,QAAQ,EAAE,OAAOgB,CAAW,EAAE,OAAO,KAAK,CAC9D,CChEA,OAAOC,OAAiB,SACxB,OAAS,qBAAAC,OAAyB,KAClC,OAAS,SAAAC,OAAa,cACtB,OAAS,WAAAC,OAAe,OAQxB,SAASC,GAAcC,EAA6B,CAClD,IAAMC,EAAQD,EAAS,MAAM;AAAA,CAAI,EAC3BE,EAAsB,CAAC,EACzBC,EAAiB,GACjBC,EAAe,EACfC,EAAwB,CAAC,EAE7B,QAAWC,KAAQL,EAAO,CACxB,IAAMM,EAAeD,EAAK,MAAM,mBAAmB,EAC/CC,GAAgBA,EAAa,CAAC,GAAG,SAAW,IAC1CJ,GAAkBE,EAAY,OAAS,IACzCH,EAAS,KAAK,CAAE,QAASC,EAAgB,aAAcC,EAAc,KAAMC,EAAY,KAAK;AAAA,CAAI,EAAE,KAAK,CAAE,CAAC,EAE5GF,EAAiBI,EAAa,CAAC,GAAG,KAAK,GAAK,GAC5CH,EAAe,EACfC,EAAc,CAAC,GAEfA,EAAY,KAAKC,CAAI,CAEzB,CAEA,OAAIH,GAAkBE,EAAY,OAAS,IACzCH,EAAS,KAAK,CAAE,QAASC,EAAgB,aAAcC,EAAc,KAAMC,EAAY,KAAK;AAAA,CAAI,EAAE,KAAK,CAAE,CAAC,EAGrGH,CACT,CAEA,SAASM,GAAaR,EAA0B,CAE9C,OADcA,EAAS,MAAM,WAAW,IACzB,CAAC,GAAG,KAAK,GAAK,0BAC/B,CAEA,SAASS,GAAYT,EAAiC,CAEpD,OADcA,EAAS,MAAM,2BAA2B,IACzC,CAAC,GAAK,IACvB,CAEA,SAASU,GAAYC,EAAuB,CAC1C,OAAQA,EAAO,CACb,IAAK,GAAG,MAAO,IACf,IAAK,GAAG,MAAO,IACf,IAAK,GAAG,MAAO,IACf,QAAS,MAAO,GAClB,CACF,CAMA,SAASC,GAAWX,EAAgE,CAClF,GAAIA,EAAM,OAAS,EAAG,OAAO,KAC7B,IAAMY,EAAaZ,EAAM,CAAC,GAAK,GACzBa,EAAgBb,EAAM,CAAC,GAAK,GAClC,GAAI,CAACY,EAAW,SAAS,GAAG,GAAK,CAACC,EAAc,MAAM,sBAAsB,EAAG,OAAO,KAEtF,IAAMC,EAAcT,GAClBA,EAAK,MAAM,GAAG,EAAE,MAAM,EAAG,EAAE,EAAE,IAAKU,GAAMA,EAAE,KAAK,CAAC,EAE5CC,EAAmB,CAAE,MAAOF,EAAWF,CAAU,CAAE,EACnDK,EAAmB,CAAC,EAC1B,QAAS,EAAI,EAAG,EAAIjB,EAAM,OAAQ,IAAK,CACrC,IAAMkB,EAAMlB,EAAM,CAAC,GAAK,GACxB,GAAI,CAACkB,EAAI,SAAS,GAAG,EAAG,MACxBD,EAAK,KAAK,CAAE,MAAOH,EAAWI,CAAG,CAAE,CAAC,CACtC,CACA,MAAO,CAAE,OAAAF,EAAQ,KAAAC,CAAK,CACxB,CAKA,SAASE,EAAYC,EAAaC,EAAsB,CACtD,IAAMC,EAAeF,EAAI,KAAK,QAAQ,OACnBA,EAAI,KAAK,OAAoBE,EAAe,GAAMF,EAAI,EACzDC,IACdD,EAAI,QAAQ,EACZG,GAAiBH,CAAG,EAExB,CAEA,SAASG,GAAiBH,EAAmB,CAC3C,IAAMI,EAAY,IAAI,KAAK,EAAE,YAAY,EACzCJ,EAAI,SAAS,CAAC,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KAAK,2BAA4B,GAAI,EAAE,EAC9FA,EAAI,KAAKI,EAAW,GAAI,GAAI,CAAE,MAAO,OAAQ,CAAC,EAC9CJ,EAAI,SAAS,CAAC,CAChB,CAEA,SAASK,GAAiBL,EAAmB,CAC3CA,EAAI,SAAS,CAAC,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KACrD,sCACA,GACCA,EAAI,KAAK,OAAoB,GAC9B,CAAE,MAAO,SAAU,MAAQA,EAAI,KAAK,MAAmB,GAAI,CAC7D,CACF,CAEA,SAASM,GACPN,EACAO,EACAC,EACM,CACN,IAAMC,EAAWF,EAAM,OAAO,MAAM,OAC9BG,EAAWF,EAAeC,EAC1BE,EAAaX,EAAI,KAAK,QAAQ,KAC9BY,EAAY,GAElBb,EAAYC,EAAKY,EAAY,CAAC,EAE9B,IAAMC,EAAU,CAACC,EAAiBC,EAAeC,IAAqB,CACpE,IAAMC,EAAIjB,EAAI,EACVgB,IACFhB,EAAI,KAAK,EACTA,EAAI,KAAKW,EAAYM,EAAI,EAAGT,EAAcI,CAAS,EAAE,KAAKI,CAAO,EACjEhB,EAAI,QAAQ,GAEd,QAASkB,EAAI,EAAGA,EAAIJ,EAAM,OAAQI,IAAK,CACrC,IAAMC,EAAIR,EAAaO,EAAIR,EAC3BV,EAAI,SAAS,CAAC,EACX,KAAKe,EAAO,iBAAmB,SAAS,EACxC,UAAU,SAAS,EACnB,KAAKD,EAAMI,CAAC,GAAK,GAAIC,EAAI,EAAGF,EAAG,CAAE,MAAOP,EAAW,EAAG,OAAQE,EAAW,UAAW,EAAM,CAAC,CAChG,CACAZ,EAAI,EAAIiB,EAAIL,CACd,EAEAC,EAAQN,EAAM,OAAO,MAAO,GAAM,SAAS,EAC3C,QAAWT,KAAOS,EAAM,KACtBR,EAAYC,EAAKY,CAAS,EAC1BC,EAAQf,EAAI,MAAO,EAAK,CAE5B,CAEA,eAAsBsB,GACpBC,EACAC,EACiB,CACjB,aAAM9C,GAAMC,GAAQ6C,CAAU,EAAG,CAAE,UAAW,EAAK,CAAC,EAE7C,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,IAAMxB,EAAM,IAAI1B,GAAY,CAC1B,KAAM,KACN,QAAS,CAAE,IAAK,GAAI,OAAQ,GAAI,KAAM,GAAI,MAAO,EAAG,EACpD,KAAM,CACJ,MAAO,qCACP,OAAQ,SACR,QAAS,YACX,CACF,CAAC,EAEKmD,EAASlD,GAAkB+C,CAAU,EAC3CtB,EAAI,KAAKyB,CAAM,EAEf,IAAMjB,EAAgBR,EAAI,KAAK,MAAoBA,EAAI,KAAK,QAAQ,KAAmBA,EAAI,KAAK,QAAQ,MAClG0B,EAAQvC,GAAakC,CAAe,EACpCM,EAAOvC,GAAYiC,CAAe,EAGxCrB,EAAI,SAAS,CAAC,EACdA,EAAI,SAAS,EAAE,EAAE,KAAK,gBAAgB,EAAE,UAAU,SAAS,EAAE,KAAK0B,EAAO,CAAE,MAAO,SAAU,MAAOlB,CAAa,CAAC,EACjHR,EAAI,SAAS,EAAG,EAChBA,EAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KAAK,mCAAoC,CAAE,MAAO,SAAU,MAAOQ,CAAa,CAAC,EACzIR,EAAI,SAAS,CAAC,EACdA,EAAI,SAAS,EAAE,EAAE,UAAU,SAAS,EAAE,KAAK,cAAc,IAAI,KAAK,EAAE,YAAY,CAAC,GAAI,CAAE,MAAO,SAAU,MAAOQ,CAAa,CAAC,EAEzHmB,IACF3B,EAAI,SAAS,EAAG,EAChBA,EAAI,SAAS,CAAC,EAAE,KAAK,SAAS,EAAE,UAAU,SAAS,EAAE,KAAK,YAAY2B,CAAI,GAAI,CAAE,MAAO,SAAU,MAAOnB,CAAa,CAAC,GAGxHR,EAAI,SAAS,CAAC,EACdA,EAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KAAK,aAAc,CAAE,MAAO,SAAU,KAAM,qBAAsB,MAAOQ,CAAa,CAAC,EAG/I,IAAM3B,EAAWH,GAAc2C,CAAe,EAE9C,QAAWO,KAAW/C,EAAU,CAI9B,GAHAmB,EAAI,QAAQ,EACZG,GAAiBH,CAAG,EAEhB4B,EAAQ,QAAS,CACnB,IAAMC,EAAOxC,GAAYuC,EAAQ,YAAY,EAC7C5B,EAAI,SAAS6B,CAAI,EAAE,KAAK,gBAAgB,EAAE,UAAU,SAAS,EAAE,KAAKD,EAAQ,QAAS,CAAE,MAAOpB,CAAa,CAAC,EAC5GR,EAAI,SAAS,EAAG,EAChBA,EAAI,OAAO,GAAIA,EAAI,CAAC,EAAE,OAAO,GAAKQ,EAAcR,EAAI,CAAC,EAAE,YAAY,SAAS,EAAE,UAAU,CAAC,EAAE,OAAO,EAClGA,EAAI,SAAS,EAAG,CAClB,CAEA,IAAM8B,EAAYF,EAAQ,KAAK,MAAM;AAAA,CAAI,EACrCG,EAAc,GACdb,EAAI,EACR,KAAOA,EAAIY,EAAU,QAAQ,CAC3B,IAAM7C,EAAO6C,EAAUZ,CAAC,GAAK,GAE7B,GAAIjC,EAAK,WAAW,KAAK,EAAG,CAC1B8C,EAAc,CAACA,EACXA,GACFhC,EAAYC,EAAK,EAAE,EAErBkB,IACA,QACF,CAEA,GAAIa,EAAa,CACfhC,EAAYC,EAAK,EAAE,EACnB,IAAMgC,EAAQhC,EAAI,EAClBA,EAAI,KAAK,EACTA,EAAI,KAAKA,EAAI,KAAK,QAAQ,KAAMgC,EAAQ,EAAGxB,EAAc,EAAE,EAAE,KAAK,SAAS,EAC3ER,EAAI,QAAQ,EACZA,EAAI,SAAS,CAAC,EAAE,KAAK,SAAS,EAAE,UAAU,SAAS,EAAE,KAAKf,EAAM,CAAE,MAAOuB,CAAa,CAAC,EACvFU,IACA,QACF,CAEA,GAAI,CAACjC,EAAK,KAAK,EAAG,CAChBe,EAAI,SAAS,EAAG,EAChBkB,IACA,QACF,CAGA,IAAMe,EAAWH,EAAUZ,EAAI,CAAC,GAAK,GACrC,GAAIjC,EAAK,SAAS,GAAG,GAAKiC,EAAI,EAAIY,EAAU,QAAUG,EAAS,MAAM,sBAAsB,EAAG,CAC5F,IAAMC,EAAuB,CAAC,EAC1BC,EAAIjB,EACR,KAAOiB,EAAIL,EAAU,SAAWA,EAAUK,CAAC,GAAK,IAAI,SAAS,GAAG,GAC9DD,EAAW,KAAKJ,EAAUK,CAAC,GAAK,EAAE,EAClCA,IAEF,IAAM5B,EAAQhB,GAAW2C,CAAU,EACnC,GAAI3B,EAAO,CACTD,GAAYN,EAAKO,EAAOC,CAAY,EACpCU,EAAIiB,EACJ,QACF,CACF,CAGA,GAAIlD,EAAK,MAAM,aAAa,GAAKA,EAAK,MAAM,QAAQ,EAAG,CACrDiC,IACA,QACF,CAGA,IAAMkB,EAAkBnD,EAAK,MAAM,mBAAmB,EACtD,GAAImD,IAAkB,CAAC,GAAKA,EAAgB,CAAC,EAAG,CAC9C,IAAM9C,EAAQ8C,EAAgB,CAAC,EAAE,OAC3BP,EAAOxC,GAAYC,CAAK,EAC9BS,EAAYC,EAAK6B,EAAO,EAAE,EAC1B7B,EAAI,SAAS,EAAG,EAChBA,EAAI,SAAS6B,CAAI,EAAE,KAAK,gBAAgB,EAAE,UAAU,SAAS,EAAE,KAAKO,EAAgB,CAAC,EAAE,KAAK,EAAG,CAAE,MAAO5B,CAAa,CAAC,EACtHR,EAAI,SAAS,EAAG,EAChBkB,IACA,QACF,CAGA,GAAIjC,EAAK,MAAM,WAAW,EAAG,CAC3Bc,EAAYC,EAAK,EAAE,EACnBA,EAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KAAKf,EAAK,KAAK,EAAG,CAAE,OAAQ,GAAI,MAAOuB,EAAe,EAAG,CAAC,EAClHU,IACA,QACF,CAGAnB,EAAYC,EAAK,EAAE,EACnBA,EAAI,SAAS,EAAE,EAAE,KAAK,WAAW,EAAE,UAAU,SAAS,EAAE,KAAKf,EAAK,KAAK,EAAG,CAAE,MAAOuB,CAAa,CAAC,EACjGU,GACF,CAEAb,GAAiBL,CAAG,CACtB,CAEAA,EAAI,IAAI,EAERyB,EAAO,GAAG,SAAU,IAAMF,EAAQD,CAAU,CAAC,EAC7CG,EAAO,GAAG,QAASD,CAAM,CAC3B,CAAC,CACH,CNzQO,SAASa,GAAoBC,EAAwB,CAC1DA,EACG,QAAQ,MAAM,EACd,YAAY,iBAAiB,EAC7B,OAAO,qBAAsB,sBAAsB,EACnD,OAAO,eAAgB,4BAA4B,EACnD,OAAO,oBAAqB,qCAAsC,QAAQ,EAC1E,OAAO,iBAAkB,oBAAoB,EAC7C,OAAO,mBAAoB,mCAAmC,EAC9D,OAAO,eAAgB,yDAAyD,EAChF,OAAO,sBAAuB,sBAAuB,aAAa,EAClE,OAAO,MAAOC,GAAyB,CACtC,GAAI,CACF,GAAM,CAAE,aAAAC,EAAc,OAAAC,EAAQ,YAAAC,CAAY,EAAI,MAAMC,EAAS,CAC3D,WAAYJ,EAAQ,OACpB,KAAMA,EAAQ,KAAO,SAASA,EAAQ,KAAM,EAAE,EAAI,OAClD,KAAMA,EAAQ,KAAO,WAAWA,EAAQ,IAAI,EAAI,MAClD,CAAC,EAEK,CAAE,UAAWK,EAAQ,WAAAC,CAAW,EAAIL,EAGpCM,EAAiBC,GAAcN,EAAO,MAAOI,CAAU,EAIvDG,EADWC,GAAeV,EAAQ,QAAQ,EACxB,SAASK,EAAQE,CAAc,EAIvD,GAHA,QAAQ,IAAIE,EAAO,OAAO,EAGtBT,EAAQ,WAAY,CAEtB,IAAMW,EADqBC,GAAyB,EACR,SAASP,EAAQE,CAAc,EAK3E,GAJA,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAII,EAAiB,OAAO,EAGhCX,EAAQ,IAAK,CACf,IAAMa,EAAU,MAAMC,GAAoBH,EAAiB,QAASX,EAAQ,GAAG,EAC/E,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAIe,EAAM,MAAM,uBAAuBF,CAAO,EAAE,CAAC,CAC3D,CACF,CAGA,GAAI,CACFG,GAAY,CACV,aAAAf,EACA,UAAWC,EAAO,MAAM,KACxB,WAAYe,GAAkBd,CAAW,EACzC,UAAW,IAAI,KAAK,EAAE,YAAY,CACpC,CAAC,CACH,MAAQ,CAER,CAGA,IAAMe,EAAYb,EAAO,SAAW,GAAKA,EAAO,UAAY,GAAKE,EAAe,OAChF,QAAQ,KAAKW,EAAY,EAAI,CAAC,CAChC,OAASC,EAAG,CACV,GAAIA,aAAaC,GAAe,CAC9B,IAAMC,EAASF,EAAE,OAAS,UACtB,mBACAA,EAAE,OAAS,gBACT,gBACAA,EAAE,OAAS,cACT,wBACAA,EAAE,OAAS,eACT,eACA,mBAAmBA,EAAE,IAAI,IACnC,QAAQ,MAAMJ,EAAM,IAAI,GAAGM,CAAM,KAAKF,EAAE,OAAO,EAAE,CAAC,EAC9CA,EAAE,WACJ,QAAQ,MAAMJ,EAAM,OAAO,+DAA+D,CAAC,CAE/F,SAAWO,GAAcH,CAAC,EAAG,CAE3B,IAAMI,EADWJ,EAAE,KAAK,WAAW,SAAS,EACnB,eAAiB,QAC1C,QAAQ,MAAMJ,EAAM,IAAI,GAAGQ,CAAK,KAAKJ,EAAE,OAAO,EAAE,CAAC,CACnD,MAAWA,aAAa,OAASA,EAAE,OAAS,aAC1C,QAAQ,MAAMJ,EAAM,IAAI,oEAAoE,CAAC,EAE7F,QAAQ,MAAMA,EAAM,IAAI,UAAUI,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EAEjF,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,CACL,CAEA,IAAMK,GAA0B,CAC9B,KAAOC,GAAMV,EAAM,KAAKU,CAAC,EACzB,IAAMA,GAAMV,EAAM,IAAIU,CAAC,EACvB,MAAQA,GAAMV,EAAM,MAAMU,CAAC,EAC3B,OAASA,GAAMV,EAAM,OAAOU,CAAC,EAC7B,KAAOA,GAAMV,EAAM,KAAKU,CAAC,EACzB,IAAMA,GAAMV,EAAM,IAAIU,CAAC,EACvB,UAAYA,GAAMV,EAAM,MAAM,KAAKU,CAAC,EACpC,QAAUA,GAAMV,EAAM,IAAI,KAAKU,CAAC,CAClC,EAEA,SAASf,GAAegB,EAAc,CACpC,OAAQA,EAAM,CACZ,IAAK,OACH,OAAOC,GAAmB,EAC5B,IAAK,QACH,OAAOC,GAAoB,EAE7B,QACE,OAAOC,GAAqBL,EAAa,CAC7C,CACF,CAEA,SAASF,GAAc,EAA8B,CACnD,OACE,OAAO,GAAM,UACb,IAAM,MACN,SAAU,GACV,YAAa,GACb,OAAQ,EAAkB,MAAS,UACnC,OAAQ,EAAkB,SAAY,QAE1C,COhJA,OAAS,WAAAQ,EAAS,WAAAC,EAAS,QAAAC,MAAY,OACvC,OAAS,gBAAAC,OAAoB,KAC7B,OAAOC,MAAW,QAClB,OACE,eAAAC,GACA,gBAAAC,GACA,iBAAAC,GACA,iBAAAC,GACA,qBAAAC,GACA,mBAAAC,GACA,uBAAAC,OACK,eCbP,OAAS,gBAAAC,GAAc,iBAAAC,GAAe,aAAAC,GAAW,eAAAC,OAAmB,KACpE,OAAS,QAAAC,MAAY,OAIrB,SAASC,GAAiBC,EAAsB,CAC9C,OAAOA,EAAK,QAAQ,kBAAmB,GAAG,CAC5C,CAEO,SAASC,EAAqBC,EAA+B,CAClE,IAAMC,EAAeL,EAAKI,EAAW,WAAW,EAEhD,MAAO,CACL,KAAKE,EAAmC,CACtC,IAAMC,EAAWP,EAAKK,EAAc,GAAGJ,GAAiBK,CAAS,CAAC,OAAO,EACzE,GAAI,CAEF,MAAO,CAAE,QAAS,GAAM,KADRV,GAAaW,EAAU,OAAO,CACR,CACxC,MAAQ,CACN,MAAO,CACL,QAAS,GACT,MAAO,CACL,KAAM,qBACN,QAAS,gCAAgCD,CAAS,QAAQC,CAAQ,EACpE,CACF,CACF,CACF,EAEA,MAAMD,EAAmBE,EAA+B,CACtD,GAAI,CACFV,GAAUO,EAAc,CAAE,UAAW,EAAK,CAAC,EAC3C,IAAME,EAAWP,EAAKK,EAAc,GAAGJ,GAAiBK,CAAS,CAAC,OAAO,EACzE,OAAAT,GAAcU,EAAUC,EAAS,OAAO,EACjC,CAAE,QAAS,GAAM,KAAM,MAAU,CAC1C,OAASC,EAAG,CACV,MAAO,CACL,QAAS,GACT,MAAO,CACL,KAAM,gBACN,QAAS,6BAA6BA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAClF,CACF,CACF,CACF,EAEA,MAAyB,CACvB,GAAI,CACF,OAAAX,GAAUO,EAAc,CAAE,UAAW,EAAK,CAAC,EAKpC,CAAE,QAAS,GAAM,KAJVN,GAAYM,CAAY,EAEnC,OAAQK,GAAMA,EAAE,SAAS,OAAO,CAAC,EACjC,IAAKA,GAAMA,EAAE,QAAQ,UAAW,EAAE,CAAC,CACF,CACtC,OAASD,EAAG,CACV,MAAO,CACL,QAAS,GACT,MAAO,CACL,KAAM,gBACN,QAAS,6BAA6BA,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAClF,CACF,CACF,CACF,CACF,CACF,CDjCO,SAASE,GAAwBC,EAAwB,CAC9D,IAAMC,EAAWD,EACd,QAAQ,UAAU,EAClB,YAAY,uBAAuB,EAEtCC,EACG,QAAQ,KAAK,EACb,YAAY,kCAAkC,EAC9C,OAAO,sBAAuB,sBAAuB,aAAa,EAClE,OAAO,iBAAkB,oBAAoB,EAC7C,OAAO,MAAOC,GAAgC,CAC7C,GAAI,CACF,IAAMC,EAAYC,EAAQC,EAAQ,QAAQ,IAAI,EAAGH,EAAQ,MAAM,CAAC,EAC1DI,EAAYC,EAAKJ,EAAW,SAAS,EACrCK,EAAKC,EAAqBH,CAAS,EAGnC,CAAE,OAAAI,EAAQ,aAAAC,CAAa,EAAI,MAAMC,EAAS,CAC9C,WAAYV,EAAQ,OACpB,KAAMA,EAAQ,KAAO,SAASA,EAAQ,KAAM,EAAE,EAAI,MACpD,CAAC,EAEK,CAAE,WAAAW,CAAW,EAAIF,EAGjBG,EAAeC,GACnBL,EAAO,MAAM,KACbG,EACA,IAAI,KAAK,EAAE,YAAY,CACzB,EAEMG,EAAcC,GAAcH,EAAcN,CAAE,EAC7CQ,EAAY,UACf,QAAQ,MAAME,EAAM,IAAI,4BAA4BF,EAAY,MAAM,OAAO,EAAE,CAAC,EAChF,QAAQ,KAAK,CAAC,GAGhB,IAAMG,EAAY,OAAO,KAAKL,EAAa,OAAO,EAAE,OACpD,QAAQ,IAAI,EAAE,EACd,QAAQ,IACNI,EAAM,MAAM,6BAA6BR,EAAO,MAAM,IAAI,MAAMS,CAAS,QAAQA,IAAc,EAAI,GAAK,GAAG,GAAG,CAChH,EACA,QAAQ,IAAID,EAAM,IAAI,eAAeZ,CAAS,aAAa,CAAC,EAC5D,QAAQ,KAAK,CAAC,CAChB,OAASc,EAAG,CACV,QAAQ,MAAMF,EAAM,IAAI,UAAUE,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EAC/E,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,EAEHnB,EACG,QAAQ,SAAS,EACjB,YAAY,iCAAiC,EAC7C,OAAO,sBAAuB,sBAAuB,aAAa,EAClE,OAAO,iBAAkB,oBAAoB,EAC7C,OAAO,MAAOC,GAAoC,CACjD,GAAI,CACF,IAAMC,EAAYC,EAAQC,EAAQ,QAAQ,IAAI,EAAGH,EAAQ,MAAM,CAAC,EAC1DI,EAAYC,EAAKJ,EAAW,SAAS,EACrCK,EAAKC,EAAqBH,CAAS,EAInCe,EAAahB,EAAQ,QAAQ,IAAI,EAAGH,EAAQ,MAAM,EACpDoB,EACJ,GAAI,CACFA,EAAcC,GAAaF,EAAY,OAAO,CAChD,MAAQ,CACN,QAAQ,MAAMH,EAAM,IAAI,0BAA0BG,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAMG,EAAaC,EAAqB,EAClCC,EAAcC,GAAYL,EAAa,CAC3C,UAAAnB,EACA,WAAAqB,CACF,CAAC,EAEIE,EAAY,UACf,QAAQ,MAAMR,EAAM,IAAI,6BAA6BQ,EAAY,MAAM,OAAO,EAAE,CAAC,EACjF,QAAQ,KAAK,CAAC,GAGhB,IAAME,EAAYF,EAAY,KAAK,MAAM,KAGnCG,EAAiBC,GAAaF,EAAWpB,CAAE,EAC5CqB,EAAe,UACdA,EAAe,MAAM,OAAS,qBAChC,QAAQ,MAAMX,EAAM,IAAI,gCAAgCU,CAAS,uCAAuC,CAAC,EAEzG,QAAQ,MAAMV,EAAM,IAAI,4BAA4BW,EAAe,MAAM,OAAO,EAAE,CAAC,EAErF,QAAQ,KAAK,CAAC,GAGhB,IAAMf,EAAee,EAAe,KAG9B,CAAE,aAAAlB,CAAa,EAAI,MAAMC,EAAS,CACtC,WAAYV,EAAQ,OACpB,KAAMA,EAAQ,KAAO,SAASA,EAAQ,KAAM,EAAE,EAAI,OAClD,aAAAY,CACF,CAAC,EAEK,CAAE,WAAAD,CAAW,EAAIF,EAGjBoB,EAAchB,GAClBa,EACAf,EACA,IAAI,KAAK,EAAE,YAAY,CACzB,EAGMmB,EAAaC,GAAgBnB,EAAciB,EAAY,OAAO,EAQpE,GALA,QAAQ,IAAI,EAAE,EACd,QAAQ,IAAIb,EAAM,KAAK,4BAA4BU,CAAS,GAAG,CAAC,EAChE,QAAQ,IAAIV,EAAM,IAAI,oBAAoBJ,EAAa,SAAS,EAAE,CAAC,EACnE,QAAQ,IAAI,EAAE,EAEVkB,EAAW,YAAY,OAAS,EAAG,CACrC,QAAQ,IAAId,EAAM,IAAI,KAAK,kBAAkBc,EAAW,YAAY,MAAM,IAAI,CAAC,EAC/E,QAAWE,KAAKF,EAAW,YACzB,QAAQ,IAAId,EAAM,IAAI,OAAOgB,EAAE,QAAQ,KAAKC,EAAcD,EAAE,gBAAgB,CAAC,WAAMC,EAAcD,EAAE,eAAe,CAAC,EAAE,CAAC,EAClHA,EAAE,gBAAgB,OAAS,GAC7B,QAAQ,IAAIhB,EAAM,IAAI,uBAAuBgB,EAAE,gBAAgB,KAAK,IAAI,CAAC,EAAE,CAAC,EAGhF,QAAQ,IAAI,EAAE,CAChB,CAEA,GAAIF,EAAW,aAAa,OAAS,EAAG,CACtC,QAAQ,IAAId,EAAM,MAAM,KAAK,mBAAmBc,EAAW,aAAa,MAAM,IAAI,CAAC,EACnF,QAAWI,KAAOJ,EAAW,aAC3B,QAAQ,IAAId,EAAM,MAAM,OAAOkB,EAAI,QAAQ,KAAKD,EAAcC,EAAI,gBAAgB,CAAC,WAAMD,EAAcC,EAAI,eAAe,CAAC,EAAE,CAAC,EAEhI,QAAQ,IAAI,EAAE,CAChB,CAEA,GAAIJ,EAAW,UAAU,OAAS,EAAG,CACnC,QAAQ,IAAId,EAAM,IAAI,gBAAgBc,EAAW,UAAU,MAAM,IAAI,CAAC,EACtE,QAAWK,KAAKL,EAAW,UACzB,QAAQ,IAAId,EAAM,IAAI,OAAOmB,EAAE,QAAQ,KAAKF,EAAcE,EAAE,QAAQ,CAAC,EAAE,CAAC,EAE1E,QAAQ,IAAI,EAAE,CAChB,CAEA,GAAIL,EAAW,SAAS,OAAS,EAAG,CAClC,QAAQ,IAAId,EAAM,KAAK,gBAAgBc,EAAW,SAAS,MAAM,IAAI,CAAC,EACtE,QAAWM,KAAKN,EAAW,SACzB,QAAQ,IAAId,EAAM,KAAK,OAAOoB,CAAC,EAAE,CAAC,EAEpC,QAAQ,IAAI,EAAE,CAChB,CAEA,GAAIN,EAAW,aAAa,OAAS,EAAG,CACtC,QAAQ,IAAId,EAAM,OAAO,oBAAoBc,EAAW,aAAa,MAAM,IAAI,CAAC,EAChF,QAAWM,KAAKN,EAAW,aACzB,QAAQ,IAAId,EAAM,OAAO,OAAOoB,CAAC,EAAE,CAAC,EAEtC,QAAQ,IAAI,EAAE,CAChB,CAGA,QAAQ,KAAKN,EAAW,YAAY,OAAS,EAAI,EAAI,CAAC,CACxD,OAASZ,EAAG,CACV,QAAQ,MAAMF,EAAM,IAAI,UAAUE,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EAC/E,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,EAEHnB,EACG,QAAQ,MAAM,EACd,YAAY,sBAAsB,EAClC,OAAO,sBAAuB,sBAAuB,aAAa,EAClE,OAAQC,GAAiC,CACxC,GAAI,CACF,IAAMC,EAAYC,EAAQC,EAAQ,QAAQ,IAAI,EAAGH,EAAQ,MAAM,CAAC,EAC1DI,EAAYC,EAAKJ,EAAW,SAAS,EACrCK,EAAKC,EAAqBH,CAAS,EAEnCiC,EAAaC,GAAchC,CAAE,EAC9B+B,EAAW,UACd,QAAQ,MAAMrB,EAAM,IAAI,6BAA6BqB,EAAW,MAAM,OAAO,EAAE,CAAC,EAChF,QAAQ,KAAK,CAAC,GAGhB,IAAME,EAAQF,EAAW,KAErBE,EAAM,SAAW,IACnB,QAAQ,IAAIvB,EAAM,IAAI,kEAAkE,CAAC,EACzF,QAAQ,KAAK,CAAC,GAGhB,QAAQ,IAAIA,EAAM,KAAK,kBAAkB,CAAC,EAC1C,QAAQ,IAAI,EAAE,EAEd,QAAWwB,KAAQD,EAAO,CACxB,IAAME,EAAanC,EAAG,KAAKkC,CAAI,EAC/B,GAAI,CAACC,EAAW,QAAS,CACvB,QAAQ,IAAI,KAAKD,CAAI,IAAIxB,EAAM,IAAI,cAAc,CAAC,EAAE,EACpD,QACF,CAEA,IAAM0B,EAASC,GAAoBF,EAAW,IAAI,EAClD,GAAI,CAACC,EAAO,QAAS,CACnB,QAAQ,IAAI,KAAKF,CAAI,IAAIxB,EAAM,IAAI,WAAW,CAAC,EAAE,EACjD,QACF,CAEA,IAAMC,EAAY,OAAO,KAAKyB,EAAO,KAAK,OAAO,EAAE,OACnD,QAAQ,IAAI,KAAK1B,EAAM,KAAK0B,EAAO,KAAK,SAAS,CAAC,WAAMzB,CAAS,QAAQA,IAAc,EAAI,GAAK,GAAG,WAAWD,EAAM,IAAI0B,EAAO,KAAK,SAAS,CAAC,EAAE,CAClJ,CAEA,QAAQ,KAAK,CAAC,CAChB,OAASxB,EAAG,CACV,QAAQ,MAAMF,EAAM,IAAI,UAAUE,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EAC/E,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,CACL,CAEA,SAASe,EAAcW,EAAsB,CAC3C,MAAO,IAAIA,EAAO,KAAK,QAAQ,CAAC,CAAC,GACnC,CEjQA,OAAS,mBAAAC,OAAuB,WAChC,OAAS,YAAAC,OAAgB,SACzB,OAAOC,MAAW,QCJlB,OAAS,gBAAAC,GAAc,iBAAAC,GAAe,aAAAC,GAAW,cAAAC,GAAY,aAAAC,OAAiB,KAC9E,OAAS,QAAAC,OAAY,OACrB,OAAS,WAAAC,OAAe,KAOjB,SAASC,GAA6B,CAC3C,OAAOF,GAAKC,GAAQ,EAAG,UAAW,aAAa,CACjD,CAEO,SAASE,GAA2B,CACzC,GAAI,CACF,IAAMC,EAAMT,GAAaO,EAAmB,EAAG,OAAO,EAChDG,EAAS,KAAK,MAAMD,CAAG,EAC7B,OAAI,OAAOC,EAAO,OAAU,UAAYA,EAAO,MAAM,OAAS,EACrDA,EAAO,MAET,IACT,MAAQ,CACN,OAAO,IACT,CACF,CAEO,SAASC,GAAUC,EAAqB,CAC7C,IAAMC,EAAWN,EAAmB,EAC9BO,EAAMT,GAAKC,GAAQ,EAAG,SAAS,EACrCJ,GAAUY,EAAK,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EAC/C,IAAMC,EAAwB,CAAE,MAAAH,EAAO,QAAS,IAAI,KAAK,EAAE,YAAY,CAAE,EACzEX,GAAcY,EAAU,KAAK,UAAUE,EAAM,KAAM,CAAC,EAAG,CAAE,KAAM,GAAM,CAAC,EACtEX,GAAUS,EAAU,GAAK,CAC3B,CAEO,SAASG,IAAmB,CACjC,GAAI,CACFb,GAAWI,EAAmB,CAAC,CACjC,MAAQ,CAER,CACF,CCzCA,IAAMU,GAAoB,yBAKnB,IAAMC,EAAN,cAA4B,KAAM,CAC9B,OAET,YAAYC,EAAgBC,EAAiB,CAC3C,MAAMA,CAAO,EACb,KAAK,KAAO,gBACZ,KAAK,OAASD,CAChB,CACF,EAUO,SAASE,GAAsB,CACpC,IAAMC,EAAM,QAAQ,IAAI,kBAAuBC,GAC/C,GAAID,EAAI,WAAW,SAAS,GAAK,CAACE,GAAYF,CAAG,EAC/C,MAAM,IAAI,MACR,gDAAgDA,CAAG,kDACrD,EAEF,OAAOA,CACT,CAEA,SAASE,GAAYF,EAAsB,CACzC,GAAI,CACF,IAAMG,EAAS,IAAI,IAAIH,CAAG,EAC1B,OAAOG,EAAO,WAAa,aAAeA,EAAO,WAAa,aAAeA,EAAO,WAAa,KACnG,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASC,GAAMC,EAA2B,CACxC,OAAO,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,CACzD,CAEO,SAASE,EAAkBC,EAAiBC,EAA4B,CAC7E,eAAeC,EAAWC,EAAgBC,EAAcC,EAA4B,CAClF,IAAMb,EAAM,GAAGQ,CAAO,GAAGI,CAAI,GACvBE,EAAkC,CACtC,cAAe,UAAUL,CAAK,EAChC,EAEMM,EAAoB,CAAE,OAAAJ,EAAQ,QAAAG,CAAQ,EACxCD,IAAS,SACXC,EAAQ,cAAc,EAAI,mBAC1BC,EAAK,KAAO,KAAK,UAAUF,CAAI,GAGjC,IAAIG,EAEJ,QAASC,EAAU,EAAGA,GAAW,EAAaA,IAAW,CACnDA,EAAU,GACZ,MAAMb,GAAM,GAAc,EAG5B,IAAMc,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAM,EAAG,GAAkB,EACzEH,EAAK,OAASG,EAAW,OAEzB,GAAI,CACF,IAAME,EAAW,MAAM,MAAMpB,EAAKe,CAAI,EAEtC,GAAI,CAACK,EAAS,GAAI,CAEhB,GAAIA,EAAS,QAAU,KAAOH,EAAU,EAAa,CACnDD,EAAY,IAAIpB,EAAcwB,EAAS,OAAQ,QAAQA,EAAS,MAAM,EAAE,EACxE,QACF,CAEA,IAAItB,EAAU,QAAQsB,EAAS,MAAM,GAErC,IADoBA,EAAS,QAAQ,IAAI,cAAc,GAAK,IAC5C,SAAS,kBAAkB,EACzC,GAAI,CACF,IAAMC,EAAa,MAAMD,EAAS,KAAK,EACnCC,EAAU,QACZvB,EAAUuB,EAAU,MAExB,MAAQ,CAER,CAEF,MAAM,IAAIzB,EAAcwB,EAAS,OAAQtB,CAAO,CAClD,CAEA,GAAIsB,EAAS,SAAW,IACtB,OAIF,IAAME,EAAcF,EAAS,QAAQ,IAAI,cAAc,GAAK,GAC5D,GAAI,CAACE,EAAY,SAAS,kBAAkB,EAC1C,MAAM,IAAI1B,EACRwB,EAAS,OACT,gDAAgDE,CAAW,EAC7D,EAGF,OAAQ,MAAMF,EAAS,KAAK,CAC9B,OAASG,EAAO,CACd,GAAIA,aAAiB3B,EAAe,MAAM2B,EAI1C,GAHAP,EAAYO,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EAGhEN,EAAU,EAAa,QAC7B,QAAE,CACA,aAAaE,CAAS,CACxB,CACF,CAEA,MAAMH,GAAa,IAAI,MAAM,gBAAgB,CAC/C,CAEA,MAAO,CACL,QAAAR,EACA,IAASI,GAAiBF,EAAW,MAAOE,CAAI,EAChD,KAAM,CAAIA,EAAcC,IAAkBH,EAAW,OAAQE,EAAMC,CAAI,EACvE,MAAO,CAAID,EAAcC,IAAkBH,EAAW,QAASE,EAAMC,CAAI,EACzE,OAASD,GAAiBF,EAAc,SAAUE,CAAI,CACxD,CACF,CFpHO,SAASY,GAAqBC,EAAwB,CAC3DA,EACG,QAAQ,OAAO,EACf,YAAY,gCAAgC,EAC5C,OAAO,sBAAuB,sCAAsC,EACpE,OAAO,WAAY,oCAAoC,EACvD,OAAO,WAAY,2BAA2B,EAC9C,OAAO,MAAOC,GAA0B,CACvC,GAAI,CACF,GAAIA,EAAQ,OAAQ,CAClBC,GAAW,EACX,QAAQ,IAAIC,EAAM,MAAM,kCAAkC,CAAC,EAC3D,MACF,CAEA,GAAIF,EAAQ,OAAQ,CAClB,MAAMG,GAAW,EACjB,MACF,CAEA,IAAMC,EAAQJ,EAAQ,OAAS,QAAQ,IAAI,kBAAwB,MAAMK,GAAe,EAEnFD,EAAM,WAAW,MAAM,IAC1B,QAAQ,MAAMF,EAAM,IAAI,wDAA0D,CAAC,EACnF,QAAQ,KAAK,CAAC,GAIhB,IAAMI,EAASC,EAAkBC,EAAY,EAAGJ,CAAK,EACrD,GAAI,CACF,MAAME,EAAO,IAAI,iBAAiB,CACpC,OAASG,EAAG,CACV,MAAIA,aAAaC,GAAiBD,EAAE,SAAW,MAC7C,QAAQ,MAAMP,EAAM,IAAI,2BAA2B,CAAC,EACpD,QAAQ,KAAK,CAAC,GAEVO,CACR,CAEAE,GAAUP,CAAK,EACf,QAAQ,IAAIF,EAAM,MAAM,0CAA0C,CAAC,CACrE,OAASO,EAAG,CACV,QAAQ,MAAMP,EAAM,IAAI,iBAAiBO,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EACtF,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,CACL,CAEA,eAAeN,IAA4B,CACzC,IAAMC,EAAQQ,EAAU,EACxB,GAAI,CAACR,EAAO,CACV,QAAQ,IAAIF,EAAM,OAAO,wDAA0D,CAAC,EACpF,MACF,CAEA,IAAMI,EAASC,EAAkBC,EAAY,EAAGJ,CAAK,EACrD,GAAI,CACF,MAAME,EAAO,IAAI,iBAAiB,EAClC,QAAQ,IAAIJ,EAAM,MAAM,gBAAgB,CAAC,EACzC,QAAQ,IAAI,gBAAgBM,EAAY,CAAC,EAAE,CAC7C,OAASC,EAAG,CACNA,aAAaC,GAAiBD,EAAE,SAAW,IAC7C,QAAQ,IAAIP,EAAM,OAAO,4EAA8E,CAAC,EAExG,QAAQ,IAAIA,EAAM,OAAO,2BAA2BO,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,CAErG,CACF,CAEA,SAASJ,IAAkC,CACzC,OAAO,IAAI,QAAQ,CAACQ,EAASC,IAAW,CAEtC,IAAMC,EAAQ,IAAIC,GAAS,CACzB,MAAMC,EAAQC,EAAWC,EAAU,CACjCA,EAAS,CACX,CACF,CAAC,EAED,QAAQ,OAAO,MAAM,+BAA+B,EACpD,IAAMC,EAAKC,GAAgB,CAAE,MAAO,QAAQ,MAAO,OAAQN,EAAO,SAAU,EAAK,CAAC,EAElFK,EAAG,SAAS,GAAKE,GAAW,CAC1BF,EAAG,MAAM,EACT,QAAQ,OAAO,MAAM;AAAA,CAAI,EACzB,IAAMG,EAAUD,EAAO,KAAK,EAC5B,GAAI,CAACC,EAAS,CACZT,EAAO,IAAI,MAAM,mBAAmB,CAAC,EACrC,MACF,CACAD,EAAQU,CAAO,CACjB,CAAC,CACH,CAAC,CACH,CGxGA,OAAS,YAAAC,OAAgB,OACzB,OAAS,YAAAC,OAAgB,gBACzB,OAAOC,OAAW,QCJlB,OAAS,YAAAC,MAAgB,gBAQlB,SAASC,IAAsB,CACpC,GAAI,CACF,IAAMC,EAAYF,EAAS,qBAAsB,CAAE,SAAU,OAAQ,CAAC,EAAE,KAAK,GAAK,KAC5EG,EAASH,EAAS,kCAAmC,CAAE,SAAU,OAAQ,CAAC,EAAE,KAAK,GAAK,KAEtFI,EADSJ,EAAS,yBAA0B,CAAE,SAAU,OAAQ,CAAC,EAAE,KAAK,EACzD,OAAS,EAC9B,MAAO,CAAE,UAAAE,EAAW,OAAAC,EAAQ,MAAAC,CAAM,CACpC,MAAQ,CACN,MAAO,CAAE,UAAW,KAAM,OAAQ,KAAM,MAAO,EAAM,CACvD,CACF,CCXO,SAASC,IAA0B,CACxC,OAAI,QAAQ,IAAI,eACP,CACL,KAAM,iBACN,KAAM,GACN,UAAW,QAAQ,IAAI,YAAiB,KACxC,OAAQ,QAAQ,IAAI,iBAAsB,IAC5C,EAGE,QAAQ,IAAI,UACP,CACL,KAAM,YACN,KAAM,GACN,UAAW,QAAQ,IAAI,eAAoB,KAC3C,OAAQ,QAAQ,IAAI,kBAAuB,IAC7C,EAGE,QAAQ,IAAI,GACP,CACL,KAAM,KACN,KAAM,GACN,UAAW,KACX,OAAQ,IACV,EAGK,CACL,KAAM,KACN,KAAM,GACN,UAAW,KACX,OAAQ,IACV,CACF,CCrCA,SAASC,EAAEC,EAAyB,CAClC,OAAO,mBAAmBA,CAAO,CACnC,CA+BA,eAAsBC,GACpBC,EACAC,EACAC,EACuB,CAEvB,IAAMC,EAAY,MAAMC,GAAoBJ,EAAQE,EAAQ,WAAW,EAGjEG,EAAU,MAAMC,GAAkBN,EAAQG,EAAWD,EAAQ,UAAWA,EAAQ,UAAU,EAG1FK,EAAM,MAAMP,EAAO,KAAe,YAAYH,EAAEM,CAAS,CAAC,QAAS,CACvE,QAAAE,EACA,UAAWH,EAAQ,UACnB,OAAQA,EAAQ,OAChB,YAAaA,EAAQ,YACrB,YAAaA,EAAQ,WACvB,CAAC,EAGKM,EAAUC,GAAqBR,EAAa,UAAU,EACtDS,EAAa,GACnB,QAASC,EAAI,EAAGA,EAAIH,EAAQ,OAAQG,GAAKD,EAAY,CACnD,IAAME,EAAQJ,EAAQ,MAAMG,EAAGA,EAAID,CAAU,EAC7C,MAAMV,EAAO,KAAK,eAAeH,EAAEU,EAAI,EAAE,CAAC,WAAY,CAAE,QAASK,CAAM,CAAC,CAC1E,CAGA,GAAM,CAAE,UAAAC,CAAU,EAAIZ,EAChBa,EAAWD,EAAU,WAAa,EACpCA,EAAU,OAASA,EAAU,WAC7B,EAEEE,EAAW,IAAI,IAAId,EAAa,WAAW,IAAKe,GAAMA,EAAE,OAAO,CAAC,EAChEC,EAAchB,EAAa,WAC9B,IAAKe,GAAMA,EAAE,gBAAgB,OAAU,IAAI,EAC3C,OAAQE,GAAmBA,IAAM,MAAS,EACvCC,EAAgBF,EAAY,OAAS,EACvCA,EAAY,OAAO,CAACD,EAAGI,IAAMJ,EAAII,EAAG,CAAC,EAAIH,EAAY,OACrD,OAEEI,EAAYpB,EAAa,WAAW,IAAKe,GAAMA,EAAE,YAAY,EAC7DM,EAAeD,EAAU,OAAS,EACpCA,EAAU,OAAO,CAACL,EAAGI,IAAMJ,EAAII,EAAG,CAAC,EAAIC,EAAU,OACjD,OAEEE,EAAYtB,EAAa,WAAW,OAAO,CAACuB,EAAKR,IAAMQ,EAAMR,EAAE,aAAc,CAAC,EAC9ES,EAAkBF,EAAY,EAAIA,EAAY,OAEpD,aAAMvB,EAAO,MAAM,YAAYH,EAAEU,EAAI,EAAE,CAAC,GAAI,CAC1C,OAAQ,YACR,SAAAO,EACA,UAAWD,EAAU,WACrB,WAAYE,EAAS,KACrB,cAAAI,EACA,aAAAG,EACA,gBAAAG,EACA,WAAY,IAAI,KAAK,EAAE,YAAY,CACrC,CAAC,EAEM,CAAE,MAAOlB,EAAI,GAAI,UAAAJ,CAAU,CACpC,CAEA,eAAeC,GAAoBJ,EAAqB0B,EAA+B,CACrF,GAAM,CAAE,SAAAC,CAAS,EAAI,MAAM3B,EAAO,IAAkC,cAAc,EAC5E4B,EAAWD,EAAS,KAAME,GAAMA,EAAE,OAASH,CAAI,EACrD,OAAIE,EAAiBA,EAAS,IAEd,MAAM5B,EAAO,KAAmB,eAAgB,CAAE,KAAA0B,CAAK,CAAC,GACzD,EACjB,CAEA,eAAepB,GACbN,EACAG,EACAuB,EACAI,EACiB,CACjB,GAAM,CAAE,OAAAC,CAAO,EAAI,MAAM/B,EAAO,IAC9B,cAAcH,EAAEM,CAAS,CAAC,SAC5B,EACMyB,EAAWG,EAAO,KAAM,GAAM,EAAE,OAASL,CAAI,EACnD,OAAIE,EAAiBA,EAAS,IAEd,MAAM5B,EAAO,KAAiB,cAAcH,EAAEM,CAAS,CAAC,UAAW,CACjF,KAAAuB,EACA,WAAAI,CACF,CAAC,GACc,EACjB,CAkBO,SAASrB,GAAqBuB,EAAuD,CAC1F,OAAOA,EAAW,IAAKC,GAAQ,CAE7B,IAAMC,EAASD,EACTE,EAAkBF,EAAI,KAAK,OAAS,EACtCA,EAAI,KACD,QAAS,GAAM,EAAE,WAAW,OAAQ,GAAM,CAAC,EAAE,MAAM,EAAE,IAAK,GAAM,EAAE,cAAc,CAAC,EACjF,OAAQG,GAAmBA,IAAM,MAAS,EAC5CF,EAAO,iBAAmB,CAAC,EAEhC,MAAO,CACL,aAAcD,EAAI,aAClB,QAASA,EAAI,QACb,OAAQA,EAAI,OAAS,EAAI,EACzB,SAAUA,EAAI,SACd,SAAUA,EAAI,SACd,SAAUA,EAAI,gBAAgB,OAAU,MAAQ,KAChD,WAAYA,EAAI,gBAAgB,OAAU,MAAQ,KAClD,aAAcA,EAAI,cAAgB,KAClC,QAASA,EAAI,cAAgB,KAC7B,YAAaA,EAAI,aAAe,KAChC,aAAcA,EAAI,aAAa,OAAS,EAAI,KAAK,UAAUA,EAAI,YAAY,EAAI,KAC/E,gBAAiBE,EAAgB,OAAS,EAAI,KAAK,UAAUA,CAAe,EAAI,KAChF,gBAAiB,OAAO,KAAKF,EAAI,eAAe,EAAE,OAAS,EACvD,KAAK,UAAUA,EAAI,eAAe,EAClC,IACN,CACF,CAAC,CACH,CH3JO,SAASI,GAAsBC,EAAwB,CAC5DA,EACG,QAAQ,QAAQ,EAChB,YAAY,uCAAuC,EACnD,OAAO,sBAAuB,oCAAoC,EAClE,OAAO,uBAAwB,cAAc,EAC7C,OAAO,MAAOC,GAAkC,CAC/C,GAAI,CACF,IAAMC,EAAQD,EAAQ,OAAS,QAAQ,IAAI,kBAAuBE,EAAU,EACvED,IACH,QAAQ,MAAME,GAAM,IAAI,8DAAgE,CAAC,EACzF,QAAQ,KAAK,CAAC,GAGhB,IAAMC,EAAUC,GAAY,EACvBD,IACH,QAAQ,MAAMD,GAAM,IAAI,6CAA+C,CAAC,EACxE,QAAQ,KAAK,CAAC,GAGhB,IAAMG,EAAUC,GAAW,EACrBC,EAAQC,GAAS,EACjBC,EAAcV,EAAQ,SAAWW,GAAmB,EAEpDC,EAASC,EAAkBC,EAAY,EAAGb,CAAK,EAC/Cc,EAAUC,EAAc,EAC9BD,EAAQ,MAAM,sCAAsC,EAEpD,GAAI,CACF,IAAME,EAAS,MAAMC,GAAcN,EAAQR,EAAQ,aAAc,CAC/D,YAAAM,EACA,UAAWN,EAAQ,UACnB,WAAYA,EAAQ,WACpB,UAAWI,EAAM,WAAaF,EAAQ,WAAa,OACnD,OAAQE,EAAM,QAAUF,EAAQ,QAAU,OAC1C,YAAaE,EAAM,KAAO,KAAO,QACjC,YAAaA,EAAM,MAAQ,OAC7B,CAAC,EAEDO,EAAQ,QAAQ,wBAAwB,EACxC,QAAQ,IAAI,cAAcE,EAAO,KAAK,EAAE,EACxC,QAAQ,IAAI,cAAcP,CAAW,EAAE,EACvC,QAAQ,IAAI,cAAcN,EAAQ,SAAS,EAAE,CAC/C,OAASe,EAAG,CACV,MAAAJ,EAAQ,KAAK,gBAAgB,EACvBI,CACR,CACF,OAASA,EAAG,CACV,QAAQ,MAAMhB,GAAM,IAAI,kBAAkBgB,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,CAAC,EACvF,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,CACL,CAEA,SAASC,GAAgBC,EAAkC,CAEzD,GAAI,CAEF,IAAMC,EADS,IAAI,IAAID,CAAS,EACR,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EACpDE,EAAOD,EAASA,EAAS,OAAS,CAAC,EACzC,GAAIC,EAAM,OAAOA,EAAK,QAAQ,SAAU,EAAE,CAC5C,MAAQ,CAER,CAGA,IAAMC,EAAWH,EAAU,MAAM,mCAAmC,EACpE,GAAIG,IAAW,CAAC,EAAG,CACjB,IAAMF,EAAWE,EAAS,CAAC,EAAE,MAAM,GAAG,EACtC,OAAOF,EAASA,EAAS,OAAS,CAAC,GAAK,IAC1C,CAEA,OAAO,IACT,CAEA,SAASX,IAA6B,CACpC,GAAI,CACF,IAAMc,EAASC,GAAS,4BAA6B,CAAE,SAAU,OAAQ,CAAC,EAAE,KAAK,EAC3EC,EAAOP,GAAgBK,CAAM,EACnC,GAAIE,EAAM,OAAOA,CACnB,MAAQ,CAER,CACA,OAAOC,GAAS,QAAQ,IAAI,CAAC,CAC/B,CIrGA,OAAS,gBAAAC,GAAc,YAAAC,OAAgB,KACvC,OAAS,WAAAC,GAAS,WAAAC,OAAe,OACjC,OAAS,SAAAC,OAAa,gBAEtB,OAAOC,MAAW,QAClB,OACE,eAAAC,GACA,kBAAAC,GACA,eAAAC,GACA,oBAAAC,GACA,yBAAAC,GACA,8BAAAC,GACA,iBAAAC,OACK,eCdP,OAAS,gBAAAC,OAAoB,OAE7B,OAAS,oBAAAC,OAAwB,eAU1B,SAASC,GAAkBC,EAA2B,CAC3D,IAAMC,EAA+B,CAAC,EAClCC,EAAwB,KACxBC,EAAgC,CAAC,EAErC,SAASC,GAAkB,CACzB,QAAWC,KAAYF,EACrBE,EAAS,CAEb,CAEA,SAASC,EAAcC,EAAsBC,EAAqB,CAMhE,GAJAA,EAAI,UAAU,8BAA+B,GAAG,EAChDA,EAAI,UAAU,+BAAgC,eAAe,EAC7DA,EAAI,UAAU,+BAAgC,cAAc,EAExDD,EAAI,SAAW,UAAW,CAC5BC,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,EACR,MACF,CAEA,GAAID,EAAI,SAAW,QAAUA,EAAI,MAAQ,aAAc,CACrDC,EAAI,UAAU,IAAK,CAAE,eAAgB,kBAAmB,CAAC,EACzDA,EAAI,IAAI,KAAK,UAAU,CAAE,MAAO,WAAY,CAAC,CAAC,EAC9C,MACF,CAEA,IAAMC,EAAmB,CAAC,EAC1BF,EAAI,GAAG,OAASG,GAAkBD,EAAO,KAAKC,CAAK,CAAC,EACpDH,EAAI,GAAG,MAAO,IAAM,CAClB,GAAI,CACF,IAAMI,EAAO,OAAO,OAAOF,CAAM,EAAE,SAAS,OAAO,EAC7CG,EAAS,KAAK,MAAMD,CAAI,EACxBE,EAASf,GAAiBc,CAAM,EAElCC,EAAO,SACTZ,EAAe,KAAK,GAAGY,EAAO,IAAI,EAClCT,EAAgB,EAChBI,EAAI,UAAU,IAAK,CAAE,eAAgB,kBAAmB,CAAC,EACzDA,EAAI,IAAI,KAAK,UAAU,CAAE,eAAgB,CAAC,CAAE,CAAC,CAAC,IAE9CA,EAAI,UAAU,IAAK,CAAE,eAAgB,kBAAmB,CAAC,EACzDA,EAAI,IAAI,KAAK,UAAU,CAAE,MAAOK,EAAO,MAAM,OAAQ,CAAC,CAAC,EAE3D,MAAQ,CACNL,EAAI,UAAU,IAAK,CAAE,eAAgB,kBAAmB,CAAC,EACzDA,EAAI,IAAI,KAAK,UAAU,CAAE,MAAO,cAAe,CAAC,CAAC,CACnD,CACF,CAAC,CACH,CAEA,MAAO,CACL,OAAQ,CACN,OAAO,IAAI,QAAc,CAACM,EAASC,IAAW,CAC5Cb,EAASL,GAAaS,CAAa,EACnCJ,EAAO,GAAG,QAASa,CAAM,EACzBb,EAAO,OAAOF,EAAM,IAAMc,EAAQ,CAAC,CACrC,CAAC,CACH,EAEA,MAAO,CACL,OAAO,IAAI,QAAeA,GAAY,CAChCZ,EACFA,EAAO,MAAM,IAAMY,EAAQ,CAAC,EAE5BA,EAAQ,CAEZ,CAAC,CACH,EAEA,UAAW,CACT,MAAO,CAAC,GAAGb,CAAc,CAC3B,EAEA,aAAa,CAAE,UAAAe,CAAU,EAAG,CAC1B,OAAO,IAAI,QAAuBF,GAAY,CAC5C,GAAIb,EAAe,OAAS,EAAG,CAC7Ba,EAAQ,CAAC,GAAGb,CAAc,CAAC,EAC3B,MACF,CAEA,IAAMgB,EAAQ,WAAW,IAAM,CAC7Bd,EAAgBA,EAAc,OAAQe,GAAMA,IAAMC,CAAM,EACxDL,EAAQ,CAAC,GAAGb,CAAc,CAAC,CAC7B,EAAGe,CAAS,EAENG,EAAS,IAAM,CACnB,aAAaF,CAAK,EAClBd,EAAgBA,EAAc,OAAQe,GAAMA,IAAMC,CAAM,EAExD,WAAW,IAAML,EAAQ,CAAC,GAAGb,CAAc,CAAC,EAAG,GAAG,CACpD,EACAE,EAAc,KAAKgB,CAAM,CAC3B,CAAC,CACH,CACF,CACF,CDpFO,SAASC,GAAqBC,EAAwB,CAC3DA,EACG,QAAQ,OAAO,EACf,YAAY,6DAA6D,EACzE,OAAO,sBAAuB,mBAAoB,aAAa,EAC/D,OAAO,gBAAiB,iBAAkB,MAAM,EAChD,OAAO,kBAAmB,uDAAuD,EACjF,OAAO,iBAAkB,mCAAoC,OAAO,EACpE,OAAO,oBAAqB,qCAAsC,QAAQ,EAC1E,OAAO,MAAOC,GAMT,CACJ,IAAMC,EAAUC,EAAc,EAE9B,GAAI,CAEF,IAAMC,EAAaC,GAAQ,QAAQ,IAAI,EAAGJ,EAAK,MAAM,EAC/CK,EAAYC,GAAQH,CAAU,EAEpC,GAAI,CACWI,GAASJ,CAAU,EACvB,KAAO,UACd,QAAQ,MAAMK,EAAM,IAAI,+BAA+B,CAAC,EACxD,QAAQ,KAAK,CAAC,EAElB,MAAQ,CACN,QAAQ,MAAMA,EAAM,IAAI,0BAA0BL,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAIM,EACJ,GAAI,CACFA,EAAcC,GAAaP,EAAY,OAAO,CAChD,MAAQ,CACN,QAAQ,MAAMK,EAAM,IAAI,0BAA0BL,CAAU,EAAE,CAAC,EAC/D,QAAQ,KAAK,CAAC,CAChB,CAEA,IAAMQ,EAAaC,EAAqB,EAClCC,EAAcC,GAAYL,EAAa,CAAE,UAAAJ,EAAW,WAAAM,CAAW,CAAC,EACjEE,EAAY,UACf,QAAQ,MAAML,EAAM,IAAI,6BAA6BK,EAAY,MAAM,OAAO,EAAE,CAAC,EACjF,QAAQ,KAAK,CAAC,GAGhB,IAAME,EAAuBF,EAAY,KACnCG,EAA2BD,EAAO,OAAS,CAC/C,KAAM,SAASf,EAAK,KAAM,EAAE,EAC5B,UAAW,SAASA,EAAK,QAAS,EAAE,EACpC,YAAa,CACX,eAAgB,8BAChB,UAAW,wBACX,WAAY,gBACZ,gBAAiB,4BACjB,iBAAkB,4BACpB,CACF,EAEMiB,EAAO,SAASjB,EAAK,KAAM,EAAE,GAAKgB,EAAY,KAC9CE,EAAY,SAASlB,EAAK,QAAS,EAAE,GAAKgB,EAAY,UAGtDG,EAAcC,GAAkBH,CAAI,EAC1C,MAAME,EAAY,MAAM,EACxBlB,EAAQ,MAAM,qCAAqCgB,CAAI,KAAK,EAGxDjB,EAAK,SACOqB,GAAM,KAAM,CAAC,KAAMrB,EAAK,OAAO,EAAG,CAC9C,IAAKK,EACL,IAAK,CACH,GAAG,QAAQ,IACX,4BAA6B,oBAAoBY,CAAI,GACrD,4BAA6B,WAC/B,EACA,MAAO,SACT,CAAC,EAEK,GAAG,QAAUK,GAAM,CACvBrB,EAAQ,KAAK,mBAAmBqB,EAAE,OAAO,EAAE,CAC7C,CAAC,EAIH,IAAMC,EAAQ,MAAMJ,EAAY,aAAa,CAAE,UAAAD,CAAU,CAAC,EAC1D,MAAMC,EAAY,KAAK,EAEnBI,EAAM,SAAW,IACnBtB,EAAQ,KAAK,oBAAoB,EACjC,QAAQ,KAAK,CAAC,GAGhBA,EAAQ,QAAQ,YAAYsB,EAAM,MAAM,QAAQ,EAGhD,IAAMC,EAAWC,GAAYF,EAAOP,EAAY,UAAU,EACpDU,EAAgBC,GAAiBH,EAAUR,EAAY,WAAW,EAGlEY,EAAaC,EAAiB,EAC9BC,EAAW,IAAI,IACfC,EAAYhB,EAAO,UAEzB,OAAW,CAACiB,EAAMC,CAAc,IAAK,OAAO,QAAQF,CAAS,EAAG,CAC9D,GAAI,CAACE,EAAgB,SACrB,IAAIC,EAAS,GACb,GAAID,EAAe,UAAW,CAC5B,IAAME,EAAM,QAAQ,IAAIF,EAAe,SAAS,EAC5CE,IAAKD,EAASC,EAAI,KAAK,EAC7B,CACA,GAAI,GAACD,GAAUF,IAAS,UAExB,GAAI,CACF,IAAMI,EAAUC,GAAeL,EAAMJ,CAAU,EAC/C,MAAMQ,EAAQ,WAAW,CACvB,OAAAF,EACA,QAASD,EAAe,QACxB,aAAcA,EAAe,aAC7B,UAAWlB,EAAO,SAAS,UAC3B,WAAY,CACd,CAAC,EACDe,EAAS,IAAIE,EAAMI,CAAO,CAC5B,MAAQ,CAER,CACF,CAEA,IAAME,EAAevB,EAAO,SAAS,YAAcA,EAAO,OAAO,CAAC,GAAG,GAC/DwB,EAAmBxB,EAAO,OAAO,KAAMyB,GAAMA,EAAE,KAAOF,CAAY,EAClEG,EAAeF,EAAmBT,EAAS,IAAIS,EAAiB,QAAQ,EAAI,OAG5EG,EAAUC,GAAsBjB,EAAe,CACnD,UAAArB,EACA,aAAAoC,EACA,WAAYF,GAAkB,KAChC,CAAC,EAEKK,EAAqE,CAAC,EAE5E,QAAWC,KAAQ9B,EAAO,MAAO,CAC/B,GAAI8B,EAAK,KAAM,SAEf,IAAMC,EAAaC,GAA2BF,EAAK,MAAM,EACnDG,EAAgC,CAAC,EACvC,QAAWC,KAAaH,EAAY,CAClC,IAAMI,EAAU,MAAMD,EAAU,SAASP,CAAO,EAChDM,EAAW,KAAK,GAAGE,CAAO,CAC5B,CACAN,EAAY,KAAK,CAAE,SAAUC,EAAK,KAAM,WAAYG,CAAW,CAAC,CAClE,CAGA,IAAMG,GAAkBP,EAAY,OAAO,CAACQ,EAAGC,IAAMD,EAAIC,EAAE,WAAW,OAAQ,CAAC,EACzEC,GAAmBV,EAAY,OACnC,CAACQ,EAAGC,IAAMD,EAAIC,EAAE,WAAW,OAAQE,GAAMA,EAAE,MAAM,EAAE,OACnD,CACF,EACMC,GAAmBL,GAAkBG,GAE3C,QAAQ,IAAI,EACZ,QAAQ,IAAI9C,EAAM,KAAK,oBAAoB,CAAC,EAC5C,QAAQ,IAAIA,EAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC,EAErC,OAAW,CAAE,SAAAiD,EAAU,WAAAX,CAAW,IAAKF,EAAa,CAElD,IAAMc,EADYZ,EAAW,MAAOS,GAAMA,EAAE,MAAM,EACzB/C,EAAM,MAAM,QAAG,EAAIA,EAAM,IAAI,QAAG,EACzD,QAAQ,IAAI,GAAGkD,CAAI,IAAID,CAAQ,EAAE,EAEjC,QAAWF,KAAKT,EAAY,CAC1B,IAAMa,GAAQJ,EAAE,OAAS/C,EAAM,MAAM,UAAK,EAAIA,EAAM,IAAI,UAAK,EACvDoD,GAAQL,EAAE,eAAiB,GAAGA,EAAE,KAAK,KAAKA,EAAE,cAAc,GAAKA,EAAE,MACvE,QAAQ,IAAI,GAAGI,EAAK,IAAIC,EAAK,EAAE,CACjC,CACF,CAEA,QAAQ,IAAI,EACZ,QAAQ,IACN,GAAGpD,EAAM,KAAK,QAAQ,CAAC,IAAI8C,EAAgB,YAAYE,EAAgB,kBAAkBL,EAAe,aAC1G,EAIA,IAAMU,GAAWC,GAAc/C,EAAO,MAAO,CAAC,CAAC,EAC/C,GAAI,CAAC8C,GAAS,OACZ,QAAWE,KAAKF,GAAS,MAAM,OAAQE,GAAM,CAACA,EAAE,MAAM,EACpD,QAAQ,IAAIvD,EAAM,IAAI,gBAAgBuD,EAAE,OAAO,EAAE,CAAC,EAKtD,QAAQ,KAAKP,GAAmB,EAAI,EAAI,CAAC,CAC3C,OAASlC,EAAG,CACVrB,EAAQ,KAAK,yBAAyBqB,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,EAAE,EAClF,QAAQ,KAAK,CAAC,CAChB,CACF,CAAC,CACL,CpB1NO,SAAS0C,IAAyB,CACvC,IAAMC,EAAU,IAAIC,GAEpB,OAAAD,EACG,KAAK,QAAQ,EACb,YAAY,wCAAwC,EACpD,QAAQ,OAAO,EAElBE,GAAoBF,CAAO,EAC3BG,GAAwBH,CAAO,EAC/BI,GAAoBJ,CAAO,EAC3BK,GAAwBL,CAAO,EAC/BM,GAAqBN,CAAO,EAC5BO,GAAsBP,CAAO,EAC7BQ,GAAqBR,CAAO,EAErBA,CACT","names":["Command","existsSync","writeFileSync","resolve","chalk","TEMPLATE","registerInitCommand","program","options","filePath","readFileSync","resolve","dirname","chalk","parseConfig","readFileSync","createNodeFileReader","path","e","registerValidateCommand","program","options","configPath","resolve","configDir","dirname","yamlContent","readFileSync","chalk","fileReader","createNodeFileReader","result","parseConfig","details","e","config","chalk","evaluateGates","createPrettyReporter","createJsonReporter","createJunitReporter","createComplianceReporter","ProviderError","readFileSync","statSync","resolve","dirname","chalk","parseConfig","createProvider","createRunner","createHttpClient","url","init","controller","timeoutId","response","ora","createSpinner","instance","text","spawn","ok","err","createNodeCommandExecutor","command","options","resolve","child","stdoutChunks","stderrChunks","chunk","timer","code","signal","e","MAX_CONFIG_SIZE","runTests","options","spinner","createSpinner","interrupted","sigintHandler","chalk","runTestsInner","configPath","resolve","configDir","dirname","stat","statSync","yamlContent","readFileSync","fileReader","createNodeFileReader","parseResult","parseConfig","config","httpClient","createHttpClient","adapters","providers","name","providerConfig","apiKey","key","adapter","createProvider","cause","msg","completedTests","totalTests","countExecutionUnits","onProgress","event","commandExecutor","t","createNodeCommandExecutor","runResult","createRunner","count","test","repeat","modelCount","readFileSync","writeFileSync","mkdirSync","join","createHash","getLastRunPath","saveLastRun","data","filePath","dir","slimmed","agg","failureMessages","r","a","m","loadLastRun","raw","parsed","computeConfigHash","yamlContent","PDFDocument","createWriteStream","mkdir","dirname","parseSections","markdown","lines","sections","currentHeading","currentLevel","currentBody","line","headingMatch","extractTitle","extractHash","headingSize","level","parseTable","headerLine","separatorLine","parseCells","c","header","rows","row","ensureSpace","doc","needed","bottomMargin","renderPageHeader","timestamp","renderPageFooter","renderTable","table","contentWidth","colCount","colWidth","leftMargin","rowHeight","drawRow","cells","bold","bgColor","y","i","x","renderCompliancePdf","markdownContent","outputPath","resolve","reject","stream","title","hash","section","size","bodyLines","inCodeBlock","codeY","nextLine","tableLines","j","subheadingMatch","registerTestCommand","program","options","runnerResult","config","yamlContent","runTests","result","aggregated","gateEvaluation","evaluateGates","report","selectReporter","complianceReport","createComplianceReporter","pdfPath","renderCompliancePdf","chalk","saveLastRun","computeConfigHash","allPassed","e","ProviderError","prefix","isKindlmError","label","chalkColorize","t","type","createJsonReporter","createJunitReporter","createPrettyReporter","resolve","dirname","join","readFileSync","chalk","parseConfig","readBaseline","writeBaseline","listBaselines","buildBaselineData","compareBaseline","deserializeBaseline","readFileSync","writeFileSync","mkdirSync","readdirSync","join","sanitizeFilename","name","createFileBaselineIO","kindlmDir","baselinesDir","suiteName","filePath","content","e","f","registerBaselineCommand","program","baseline","options","configDir","dirname","resolve","kindlmDir","join","io","createFileBaselineIO","config","runnerResult","runTests","aggregated","baselineData","buildBaselineData","writeResult","writeBaseline","chalk","testCount","e","configPath","yamlContent","readFileSync","fileReader","createNodeFileReader","parseResult","parseConfig","suiteName","baselineResult","readBaseline","currentData","comparison","compareBaseline","r","formatPercent","imp","u","t","listResult","listBaselines","names","name","readResult","parsed","deserializeBaseline","rate","createInterface","Writable","chalk","readFileSync","writeFileSync","mkdirSync","unlinkSync","chmodSync","join","homedir","getCredentialsPath","loadToken","raw","parsed","saveToken","token","filePath","dir","data","clearToken","DEFAULT_CLOUD_URL","CloudApiError","status","message","getCloudUrl","url","DEFAULT_CLOUD_URL","isLocalhost","parsed","sleep","ms","resolve","createCloudClient","baseUrl","token","request","method","path","body","headers","init","lastError","attempt","controller","timeoutId","response","errorBody","contentType","error","registerLoginCommand","program","options","clearToken","chalk","showStatus","token","promptForToken","client","createCloudClient","getCloudUrl","e","CloudApiError","saveToken","loadToken","resolve","reject","muted","Writable","_chunk","_encoding","callback","rl","createInterface","answer","trimmed","basename","execSync","chalk","execSync","getGitInfo","commitSha","branch","dirty","detectCI","e","segment","uploadResults","client","runnerResult","options","projectId","findOrCreateProject","suiteId","findOrCreateSuite","run","results","mapAggregatedResults","BATCH_SIZE","i","batch","runResult","passRate","modelIds","a","judgeScores","s","judgeAvgScore","b","latencies","latencyAvgMs","totalCost","sum","costEstimateUsd","name","projects","existing","p","configHash","suites","aggregated","agg","cached","failureMessages","m","registerUploadCommand","program","options","token","loadToken","chalk","lastRun","loadLastRun","gitInfo","getGitInfo","ciEnv","detectCI","projectName","resolveProjectName","client","createCloudClient","getCloudUrl","spinner","createSpinner","result","uploadResults","e","extractRepoName","remoteUrl","segments","last","sshMatch","remote","execSync","name","basename","readFileSync","statSync","resolve","dirname","spawn","chalk","parseConfig","createProvider","filterSpans","mapSpansToResult","buildContextFromTrace","createAssertionsFromExpect","evaluateGates","createServer","parseOtlpPayload","createTraceServer","port","collectedSpans","server","spanListeners","notifyListeners","listener","handleRequest","req","res","chunks","chunk","body","parsed","result","resolve","reject","timeoutMs","timer","l","onSpan","registerTraceCommand","program","opts","spinner","createSpinner","configPath","resolve","configDir","dirname","statSync","chalk","yamlContent","readFileSync","fileReader","createNodeFileReader","parseResult","parseConfig","config","traceConfig","port","timeoutMs","traceServer","createTraceServer","spawn","e","spans","filtered","filterSpans","mappingResult","mapSpansToResult","httpClient","createHttpClient","adapters","providers","name","providerConfig","apiKey","key","adapter","createProvider","judgeModelId","judgeModelConfig","m","judgeAdapter","context","buildContextFromTrace","testResults","test","assertions","createAssertionsFromExpect","allResults","assertion","results","totalAssertions","s","t","passedAssertions","a","failedAssertions","testName","icon","aIcon","label","gateEval","evaluateGates","g","createProgram","program","Command","registerInitCommand","registerValidateCommand","registerTestCommand","registerBaselineCommand","registerLoginCommand","registerUploadCommand","registerTraceCommand"]}
|
package/dist/kindlm.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as yn}from"commander";import{existsSync as _e,writeFileSync as De}from"fs";import{resolve as je}from"path";import H from"chalk";var Fe=`kindlm: 1
|
|
3
|
+
project: my-project
|
|
4
|
+
|
|
5
|
+
suite:
|
|
6
|
+
name: my-agent-tests
|
|
7
|
+
description: Behavioral tests for my AI agent
|
|
8
|
+
|
|
9
|
+
providers:
|
|
10
|
+
openai:
|
|
11
|
+
apiKeyEnv: OPENAI_API_KEY
|
|
12
|
+
|
|
13
|
+
models:
|
|
14
|
+
- id: gpt-4o
|
|
15
|
+
provider: openai
|
|
16
|
+
model: gpt-4o
|
|
17
|
+
params:
|
|
18
|
+
temperature: 0
|
|
19
|
+
maxTokens: 1024
|
|
20
|
+
|
|
21
|
+
prompts:
|
|
22
|
+
greeting:
|
|
23
|
+
system: You are a helpful assistant.
|
|
24
|
+
user: "{{message}}"
|
|
25
|
+
|
|
26
|
+
tests:
|
|
27
|
+
- name: basic-greeting
|
|
28
|
+
prompt: greeting
|
|
29
|
+
vars:
|
|
30
|
+
message: Hello, how are you?
|
|
31
|
+
expect:
|
|
32
|
+
output:
|
|
33
|
+
contains:
|
|
34
|
+
- hello
|
|
35
|
+
guardrails:
|
|
36
|
+
pii:
|
|
37
|
+
enabled: true
|
|
38
|
+
|
|
39
|
+
gates:
|
|
40
|
+
passRateMin: 0.95
|
|
41
|
+
|
|
42
|
+
defaults:
|
|
43
|
+
repeat: 1
|
|
44
|
+
concurrency: 4
|
|
45
|
+
timeoutMs: 60000
|
|
46
|
+
`;function se(e){e.command("init").description("Create a kindlm.yaml template").option("--force","Overwrite existing kindlm.yaml").action(t=>{let n=je(process.cwd(),"kindlm.yaml");_e(n)&&!t.force&&(console.error(H.red("kindlm.yaml already exists. Use --force to overwrite.")),process.exit(1)),De(n,Fe,"utf-8"),console.log(H.green("Created kindlm.yaml")),console.log(""),console.log("Next steps:"),console.log(` 1. Edit ${H.bold("kindlm.yaml")} with your test configuration`),console.log(` 2. Set your API key: ${H.bold("export OPENAI_API_KEY=sk-...")}`),console.log(` 3. Run tests: ${H.bold("kindlm test")}`)})}import{readFileSync as Be}from"fs";import{resolve as Ue,dirname as Ke}from"path";import M from"chalk";import{parseConfig as ze}from"@kindlm/core";import{readFileSync as He}from"fs";function A(){return{readFile(e){try{return{success:!0,data:He(e,"utf-8")}}catch(t){return{success:!1,error:{code:"CONFIG_FILE_REF_ERROR",message:`Cannot read file: ${e}: ${t instanceof Error?t.message:String(t)}`}}}}}}function ie(e){e.command("validate").description("Validate kindlm.yaml configuration").option("-c, --config <path>","Path to config file","kindlm.yaml").action(t=>{let n=Ue(process.cwd(),t.config),o=Ke(n),r;try{r=Be(n,"utf-8")}catch{console.error(M.red(`Config file not found: ${n}`)),process.exit(1)}let a=A(),i=ze(r,{configDir:o,fileReader:a});if(!i.success){console.error(M.red("Validation failed:"));let c=i.error.details;if(c&&Array.isArray(c.errors))for(let m of c.errors)console.error(M.red(` - ${m}`));else console.error(M.red(` ${i.error.message}`));process.exit(1)}let s=i.data;console.log(M.green("Config is valid!")),console.log(""),console.log(` Suite: ${M.bold(s.suite.name)}`),console.log(` Tests: ${M.bold(String(s.tests.length))}`),console.log(` Models: ${M.bold(String(s.models.length))}`)})}import w from"chalk";import{evaluateGates as Ct,createPrettyReporter as vt,createJsonReporter as Rt,createJunitReporter as wt,createComplianceReporter as St,ProviderError as Tt}from"@kindlm/core";import{readFileSync as Ye,statSync as Xe}from"fs";import{resolve as qe,dirname as Ve}from"path";import $ from"chalk";import{parseConfig as Ze,createProvider as Qe,createRunner as et}from"@kindlm/core";function G(){return{async fetch(e,t){let n=new AbortController,o=t.timeoutMs?setTimeout(()=>n.abort(),t.timeoutMs):void 0;try{let r=await globalThis.fetch(e,{method:t.method,headers:t.headers,body:t.body,signal:n.signal});return{ok:r.ok,status:r.status,json:()=>r.json()}}finally{o!==void 0&&clearTimeout(o)}}}}import Ge from"ora";function D(){let e;return{start(t){e=Ge(t).start()},succeed(t){e?.succeed(t),e=void 0},fail(t){e?.fail(t),e=void 0},stop(){e?.stop(),e=void 0}}}import{spawn as Je}from"child_process";import{ok as We,err as ae}from"@kindlm/core";function ce(){return{async execute(e,t){return new Promise(n=>{let o=Je("sh",["-c",e],{cwd:t.cwd,env:{...process.env,...t.env},stdio:["ignore","pipe","pipe"]}),r=[],a=[];o.stdout.on("data",s=>r.push(s)),o.stderr.on("data",s=>a.push(s));let i=setTimeout(()=>{o.kill("SIGTERM"),setTimeout(()=>{o.killed||o.kill("SIGKILL")},1e3)},t.timeoutMs);o.on("close",(s,c)=>{if(clearTimeout(i),c==="SIGTERM"||c==="SIGKILL"){n(ae({code:"PROVIDER_TIMEOUT",message:`Command timed out after ${t.timeoutMs}ms`}));return}n(We({stdout:Buffer.concat(r).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:s??1}))}),o.on("error",s=>{clearTimeout(i),n(ae({code:"UNKNOWN_ERROR",message:`Failed to spawn command: ${s.message}`}))})})}}}var tt=1048576;async function B(e){let t=D(),n=!1,o=()=>{n&&process.exit(130),n=!0,t.stop(),console.error($.yellow(`
|
|
47
|
+
Interrupted. Exiting...`)),process.exit(130)};process.on("SIGINT",o);try{return await nt(e,t)}finally{process.removeListener("SIGINT",o)}}async function nt(e,t){let n=qe(process.cwd(),e.configPath),o=Ve(n);try{let p=Xe(n);p.size>tt&&(console.error($.red(`Config file exceeds 1MB limit (${(p.size/1048576).toFixed(1)}MB): ${n}`)),process.exit(1))}catch{console.error($.red(`Config file not found: ${n}`)),process.exit(1)}let r;try{r=Ye(n,"utf-8")}catch{console.error($.red(`Config file not found: ${n}`)),process.exit(1)}let a=A(),i=Ze(r,{configDir:o,fileReader:a});i.success||(console.error($.red(`Config validation failed: ${i.error.message}`)),process.exit(1));let s=i.data;e.runs!==void 0&&(s.defaults.repeat=e.runs),e.gate!==void 0&&(s.gates?s.gates.passRateMin=e.gate/100:s.gates={passRateMin:e.gate/100});let c=G(),m=new Map,u=s.providers;for(let[p,b]of Object.entries(u)){if(!b)continue;let L="";if(b.apiKeyEnv){let O=process.env[b.apiKeyEnv];O||(console.error($.red(`Missing environment variable: ${b.apiKeyEnv}`)),process.exit(1)),L=O.trim()}else p!=="ollama"&&(console.error($.red(`Provider "${p}" requires apiKeyEnv to be configured`)),process.exit(1));let F;try{F=Qe(p,c)}catch(O){let _=O instanceof Error?O.message:String(O);console.error($.red(`Failed to create provider "${p}": ${_}`)),process.exit(1)}await F.initialize({apiKey:L,baseUrl:b.baseUrl,organization:b.organization,timeoutMs:s.defaults.timeoutMs,maxRetries:2}),m.set(p,F)}let f=0,y=ot(s),d=p=>{p.type==="test_start"?t.start(`Running ${p.test} [${p.model}] (${f}/${y})`):p.type==="test_complete"&&f++},S=s.tests.some(p=>p.command)?ce():void 0,l=await et(s,{adapters:m,configDir:o,fileReader:a,onProgress:d,baselineData:e.baselineData,commandExecutor:S}).run();return t.stop(),l.success||(console.error($.red(`Run failed: ${l.error.message}`)),process.exit(1)),{config:s,runnerResult:l.data,configDir:o,yamlContent:r}}function ot(e){let t=0;for(let n of e.tests){if(n.skip)continue;let o=n.repeat??e.defaults.repeat;if(n.command)t+=o;else{let r=n.models?.length??e.models.length;t+=r*o}}return t}import{readFileSync as rt,writeFileSync as st,mkdirSync as it}from"fs";import{join as le}from"path";import{createHash as at}from"crypto";function de(){return le(process.cwd(),".kindlm","last-run.json")}function me(e){let t=de(),n=le(process.cwd(),".kindlm");it(n,{recursive:!0,mode:448});let o={...e,runnerResult:{...e.runnerResult,aggregated:e.runnerResult.aggregated.map(r=>{let a=r.runs.flatMap(i=>i.assertions.filter(s=>!s.passed).map(s=>s.failureMessage)).filter(i=>i!==void 0);return{...r,failureMessages:a,runs:[]}})}};st(t,JSON.stringify(o),{mode:384})}function ue(){try{let e=rt(de(),"utf-8"),t=JSON.parse(e);return t.runnerResult?.runResult&&Array.isArray(t.runnerResult.aggregated)&&typeof t.suiteName=="string"&&typeof t.configHash=="string"&&typeof t.timestamp=="string"?t:null}catch{return null}}function fe(e){return at("sha256").update(e).digest("hex")}import ct from"pdfkit";import{createWriteStream as lt}from"fs";import{mkdir as dt}from"fs/promises";import{dirname as mt}from"path";function ut(e){let t=e.split(`
|
|
48
|
+
`),n=[],o="",r=2,a=[];for(let i of t){let s=i.match(/^(#{2,4})\s+(.+)$/);s&&s[1]?.length===2?((o||a.length>0)&&n.push({heading:o,headingLevel:r,body:a.join(`
|
|
49
|
+
`).trim()}),o=s[2]?.trim()??"",r=2,a=[]):a.push(i)}return(o||a.length>0)&&n.push({heading:o,headingLevel:r,body:a.join(`
|
|
50
|
+
`).trim()}),n}function ft(e){return e.match(/^# (.+)$/m)?.[1]?.trim()??"KindLM Compliance Report"}function pt(e){return e.match(/SHA-256:\s*`([a-f0-9]+)`/i)?.[1]??null}function pe(e){switch(e){case 2:return 18;case 3:return 15;case 4:return 13;default:return 13}}function gt(e){if(e.length<2)return null;let t=e[0]??"",n=e[1]??"";if(!t.includes("|")||!n.match(/^\s*\|[-:\s|]+\|\s*$/))return null;let o=i=>i.split("|").slice(1,-1).map(s=>s.trim()),r={cells:o(t)},a=[];for(let i=2;i<e.length;i++){let s=e[i]??"";if(!s.includes("|"))break;a.push({cells:o(s)})}return{header:r,rows:a}}function N(e,t){let n=e.page.margins.bottom;e.page.height-n-30-e.y<t&&(e.addPage(),ge(e))}function ge(e){let t=new Date().toISOString();e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("KindLM Compliance Report",60,40),e.text(t,60,40,{align:"right"}),e.moveDown(3)}function ht(e){e.fontSize(8).font("Helvetica").fillColor("#a8a29e").text("Generated by KindLM \xB7 kindlm.com",60,e.page.height-50,{align:"center",width:e.page.width-120})}function yt(e,t,n){let o=t.header.cells.length,r=n/o,a=e.page.margins.left,i=18;N(e,i*2);let s=(c,m,u)=>{let f=e.y;u&&(e.save(),e.rect(a,f-2,n,i).fill(u),e.restore());for(let y=0;y<c.length;y++){let d=a+y*r;e.fontSize(8).font(m?"Helvetica-Bold":"Courier").fillColor("#44403c").text(c[y]??"",d+4,f,{width:r-8,height:i,lineBreak:!1})}e.y=f+i};s(t.header.cells,!0,"#f5f5f4");for(let c of t.rows)N(e,i),s(c.cells,!1)}async function he(e,t){return await dt(mt(t),{recursive:!0}),new Promise((n,o)=>{let r=new ct({size:"A4",margins:{top:72,bottom:72,left:60,right:60},info:{Title:"KindLM EU AI Act Compliance Report",Author:"KindLM",Creator:"KindLM CLI"}}),a=lt(t);r.pipe(a);let i=r.page.width-r.page.margins.left-r.page.margins.right,s=ft(e),c=pt(e);r.moveDown(6),r.fontSize(28).font("Helvetica-Bold").fillColor("#1c1917").text(s,{align:"center",width:i}),r.moveDown(.5),r.fontSize(14).font("Helvetica").fillColor("#57534e").text("EU AI Act Annex IV Documentation",{align:"center",width:i}),r.moveDown(1),r.fontSize(10).fillColor("#a8a29e").text(`Generated: ${new Date().toISOString()}`,{align:"center",width:i}),c&&(r.moveDown(.3),r.fontSize(9).font("Courier").fillColor("#78716c").text(`SHA-256: ${c}`,{align:"center",width:i})),r.moveDown(2),r.fontSize(10).font("Helvetica").fillColor("#6366f1").text("kindlm.com",{align:"center",link:"https://kindlm.com",width:i});let m=ut(e);for(let u of m){if(r.addPage(),ge(r),u.heading){let C=pe(u.headingLevel);r.fontSize(C).font("Helvetica-Bold").fillColor("#1c1917").text(u.heading,{width:i}),r.moveDown(.5),r.moveTo(60,r.y).lineTo(60+i,r.y).strokeColor("#e7e5e4").lineWidth(1).stroke(),r.moveDown(.8)}let f=u.body.split(`
|
|
51
|
+
`),y=!1,d=0;for(;d<f.length;){let C=f[d]??"";if(C.startsWith("```")){y=!y,y&&N(r,30),d++;continue}if(y){N(r,14);let l=r.y;r.save(),r.rect(r.page.margins.left,l-2,i,14).fill("#f5f5f4"),r.restore(),r.fontSize(9).font("Courier").fillColor("#44403c").text(C,{width:i}),d++;continue}if(!C.trim()){r.moveDown(.4),d++;continue}let S=f[d+1]??"";if(C.includes("|")&&d+1<f.length&&S.match(/^\s*\|[-:\s|]+\|\s*$/)){let l=[],p=d;for(;p<f.length&&(f[p]??"").includes("|");)l.push(f[p]??""),p++;let b=gt(l);if(b){yt(r,b,i),d=p;continue}}if(C.match(/^\s*\|[-:]+/)||C.match(/^---+$/)){d++;continue}let h=C.match(/^(#{3,4})\s+(.+)$/);if(h?.[1]&&h[2]){let l=h[1].length,p=pe(l);N(r,p+10),r.moveDown(.3),r.fontSize(p).font("Helvetica-Bold").fillColor("#1c1917").text(h[2].trim(),{width:i}),r.moveDown(.3),d++;continue}if(C.match(/^\s*[-*] /)){N(r,14),r.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{indent:12,width:i-12}),d++;continue}N(r,14),r.fontSize(10).font("Helvetica").fillColor("#44403c").text(C.trim(),{width:i}),d++}ht(r)}r.end(),a.on("finish",()=>n(t)),a.on("error",o)})}function ye(e){e.command("test").description("Run test suites").option("-s, --suite <name>","Run a specific suite").option("--compliance","Generate compliance report").option("--reporter <type>","Output format: pretty, json, junit","pretty").option("--runs <count>","Override run count").option("--gate <percent>","Fail if pass rate below threshold").option("--pdf <path>","Export compliance report as PDF (requires --compliance)").option("-c, --config <path>","Path to config file","kindlm.yaml").action(async t=>{try{let{runnerResult:n,config:o,yamlContent:r}=await B({configPath:t.config,runs:t.runs?parseInt(t.runs,10):void 0,gate:t.gate?parseFloat(t.gate):void 0}),{runResult:a,aggregated:i}=n,s=Ct(o.gates,i),m=xt(t.reporter).generate(a,s);if(console.log(m.content),t.compliance){let y=St().generate(a,s);if(console.log(""),console.log(y.content),t.pdf){let d=await he(y.content,t.pdf);console.log(""),console.log(w.green(`PDF report saved to ${d}`))}}try{me({runnerResult:n,suiteName:o.suite.name,configHash:fe(r),timestamp:new Date().toISOString()})}catch{}let u=a.failed===0&&a.errored===0&&s.passed;process.exit(u?0:1)}catch(n){if(n instanceof Tt){let o=n.code==="TIMEOUT"?"Provider timeout":n.code==="NETWORK_ERROR"?"Network error":n.code==="AUTH_FAILED"?"Authentication failed":n.code==="RATE_LIMITED"?"Rate limited":`Provider error (${n.code})`;console.error(w.red(`${o}: ${n.message}`)),n.retryable&&console.error(w.yellow("This error may be transient. Try again or increase --timeout."))}else if(kt(n)){let r=n.code.startsWith("CONFIG_")?"Config error":"Error";console.error(w.red(`${r}: ${n.message}`))}else n instanceof Error&&n.name==="AbortError"?console.error(w.red("Request timed out. Check network connectivity or increase timeout.")):console.error(w.red(`Error: ${n instanceof Error?n.message:String(n)}`));process.exit(1)}})}var bt={bold:e=>w.bold(e),red:e=>w.red(e),green:e=>w.green(e),yellow:e=>w.yellow(e),cyan:e=>w.cyan(e),dim:e=>w.dim(e),greenBold:e=>w.green.bold(e),redBold:e=>w.red.bold(e)};function xt(e){switch(e){case"json":return Rt();case"junit":return wt();default:return vt(bt)}}function kt(e){return typeof e=="object"&&e!==null&&"code"in e&&"message"in e&&typeof e.code=="string"&&typeof e.message=="string"}import{resolve as W,dirname as q,join as V}from"path";import{readFileSync as It}from"fs";import g from"chalk";import{parseConfig as Ot,readBaseline as At,writeBaseline as Mt,listBaselines as Nt,buildBaselineData as Re,compareBaseline as Lt,deserializeBaseline as _t}from"@kindlm/core";import{readFileSync as Pt,writeFileSync as $t,mkdirSync as Ce,readdirSync as Et}from"fs";import{join as X}from"path";function ve(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function J(e){let t=X(e,"baselines");return{read(n){let o=X(t,`${ve(n)}.json`);try{return{success:!0,data:Pt(o,"utf-8")}}catch{return{success:!1,error:{code:"BASELINE_NOT_FOUND",message:`No baseline found for suite "${n}" at ${o}`}}}},write(n,o){try{Ce(t,{recursive:!0});let r=X(t,`${ve(n)}.json`);return $t(r,o,"utf-8"),{success:!0,data:void 0}}catch(r){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to write baseline: ${r instanceof Error?r.message:String(r)}`}}}},list(){try{return Ce(t,{recursive:!0}),{success:!0,data:Et(t).filter(r=>r.endsWith(".json")).map(r=>r.replace(/\.json$/,""))}}catch(n){return{success:!1,error:{code:"UNKNOWN_ERROR",message:`Failed to list baselines: ${n instanceof Error?n.message:String(n)}`}}}}}}function we(e){let t=e.command("baseline").description("Manage test baselines");t.command("set").description("Save current results as baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),{config:i,runnerResult:s}=await B({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0}),{aggregated:c}=s,m=Re(i.suite.name,c,new Date().toISOString()),u=Mt(m,a);u.success||(console.error(g.red(`Failed to save baseline: ${u.error.message}`)),process.exit(1));let f=Object.keys(m.results).length;console.log(""),console.log(g.green(`Baseline saved for suite "${i.suite.name}" (${f} test${f===1?"":"s"})`)),console.log(g.dim(` Location: ${r}/baselines/`)),process.exit(0)}catch(o){console.error(g.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}),t.command("compare").description("Compare latest against baseline").option("-c, --config <path>","Path to config file","kindlm.yaml").option("--runs <count>","Override run count").action(async n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),i=W(process.cwd(),n.config),s;try{s=It(i,"utf-8")}catch{console.error(g.red(`Config file not found: ${i}`)),process.exit(1)}let c=A(),m=Ot(s,{configDir:o,fileReader:c});m.success||(console.error(g.red(`Config validation failed: ${m.error.message}`)),process.exit(1));let u=m.data.suite.name,f=At(u,a);f.success||(f.error.code==="BASELINE_NOT_FOUND"?console.error(g.red(`No baseline found for suite "${u}". Run \`kindlm baseline set\` first.`)):console.error(g.red(`Failed to read baseline: ${f.error.message}`)),process.exit(1));let y=f.data,{runnerResult:d}=await B({configPath:n.config,runs:n.runs?parseInt(n.runs,10):void 0,baselineData:y}),{aggregated:C}=d,S=Re(u,C,new Date().toISOString()),h=Lt(y,S.results);if(console.log(""),console.log(g.bold(`Baseline comparison for "${u}"`)),console.log(g.dim(` Baseline from: ${y.createdAt}`)),console.log(""),h.regressions.length>0){console.log(g.red.bold(` Regressions (${h.regressions.length}):`));for(let l of h.regressions)console.log(g.red(` ${l.testName}: ${U(l.baselinePassRate)} \u2192 ${U(l.currentPassRate)}`)),l.newFailureCodes.length>0&&console.log(g.red(` New failures: ${l.newFailureCodes.join(", ")}`));console.log("")}if(h.improvements.length>0){console.log(g.green.bold(` Improvements (${h.improvements.length}):`));for(let l of h.improvements)console.log(g.green(` ${l.testName}: ${U(l.baselinePassRate)} \u2192 ${U(l.currentPassRate)}`));console.log("")}if(h.unchanged.length>0){console.log(g.dim(` Unchanged (${h.unchanged.length}):`));for(let l of h.unchanged)console.log(g.dim(` ${l.testName}: ${U(l.passRate)}`));console.log("")}if(h.newTests.length>0){console.log(g.cyan(` New tests (${h.newTests.length}):`));for(let l of h.newTests)console.log(g.cyan(` ${l}`));console.log("")}if(h.removedTests.length>0){console.log(g.yellow(` Removed tests (${h.removedTests.length}):`));for(let l of h.removedTests)console.log(g.yellow(` ${l}`));console.log("")}process.exit(h.regressions.length>0?1:0)}catch(o){console.error(g.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}}),t.command("list").description("List saved baselines").option("-c, --config <path>","Path to config file","kindlm.yaml").action(n=>{try{let o=q(W(process.cwd(),n.config)),r=V(o,".kindlm"),a=J(r),i=Nt(a);i.success||(console.error(g.red(`Failed to list baselines: ${i.error.message}`)),process.exit(1));let s=i.data;s.length===0&&(console.log(g.dim("No baselines saved yet. Run `kindlm baseline set` to create one.")),process.exit(0)),console.log(g.bold("Saved baselines:")),console.log("");for(let c of s){let m=a.read(c);if(!m.success){console.log(` ${c} ${g.dim("(unreadable)")}`);continue}let u=_t(m.data);if(!u.success){console.log(` ${c} ${g.dim("(corrupt)")}`);continue}let f=Object.keys(u.data.results).length;console.log(` ${g.cyan(u.data.suiteName)} \u2014 ${f} test${f===1?"":"s"}, saved ${g.dim(u.data.createdAt)}`)}process.exit(0)}catch(o){console.error(g.red(`Error: ${o instanceof Error?o.message:String(o)}`)),process.exit(1)}})}function U(e){return`${(e*100).toFixed(1)}%`}import{createInterface as Gt}from"readline";import{Writable as Jt}from"stream";import I from"chalk";import{readFileSync as Dt,writeFileSync as jt,mkdirSync as Ft,unlinkSync as Ht,chmodSync as Bt}from"fs";import{join as Se}from"path";import{homedir as Te}from"os";function Z(){return Se(Te(),".kindlm","credentials")}function Y(){try{let e=Dt(Z(),"utf-8"),t=JSON.parse(e);return typeof t.token=="string"&&t.token.length>0?t.token:null}catch{return null}}function be(e){let t=Z(),n=Se(Te(),".kindlm");Ft(n,{recursive:!0,mode:448});let o={token:e,savedAt:new Date().toISOString()};jt(t,JSON.stringify(o,null,2),{mode:384}),Bt(t,384)}function xe(){try{Ht(Z())}catch{}}var Ut="https://api.kindlm.com";var E=class extends Error{status;constructor(t,n){super(n),this.name="CloudApiError",this.status=t}};function j(){let e=process.env.KINDLM_CLOUD_URL??Ut;if(e.startsWith("http://")&&!Kt(e))throw new Error(`Refusing to use insecure HTTP for Cloud API: ${e}. Use HTTPS or target localhost for development.`);return e}function Kt(e){try{let t=new URL(e);return t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="::1"}catch{return!1}}function zt(e){return new Promise(t=>setTimeout(t,e))}function K(e,t){async function n(o,r,a){let i=`${e}${r}`,s={Authorization:`Bearer ${t}`},c={method:o,headers:s};a!==void 0&&(s["Content-Type"]="application/json",c.body=JSON.stringify(a));let m;for(let u=0;u<=1;u++){u>0&&await zt(1e3);let f=new AbortController,y=setTimeout(()=>f.abort(),3e4);c.signal=f.signal;try{let d=await fetch(i,c);if(!d.ok){if(d.status>=500&&u<1){m=new E(d.status,`HTTP ${d.status}`);continue}let S=`HTTP ${d.status}`;if((d.headers.get("content-type")??"").includes("application/json"))try{let l=await d.json();l.error&&(S=l.error)}catch{}throw new E(d.status,S)}if(d.status===204)return;let C=d.headers.get("content-type")??"";if(!C.includes("application/json"))throw new E(d.status,`Expected JSON response but got content-type: ${C}`);return await d.json()}catch(d){if(d instanceof E)throw d;if(m=d instanceof Error?d:new Error(String(d)),u<1)continue}finally{clearTimeout(y)}}throw m??new Error("Request failed")}return{baseUrl:e,get:o=>n("GET",o),post:(o,r)=>n("POST",o,r),patch:(o,r)=>n("PATCH",o,r),delete:o=>n("DELETE",o)}}function ke(e){e.command("login").description("Authenticate with KindLM Cloud").option("-t, --token <token>","API token (skips interactive prompt)").option("--status","Show current authentication status").option("--logout","Remove stored credentials").action(async t=>{try{if(t.logout){xe(),console.log(I.green("Logged out. Credentials removed."));return}if(t.status){await Wt();return}let n=t.token??process.env.KINDLM_API_TOKEN??await Yt();n.startsWith("klm_")||(console.error(I.red('Invalid token format. KindLM tokens start with "klm_".')),process.exit(1));let o=K(j(),n);try{await o.get("/v1/auth/tokens")}catch(r){throw r instanceof E&&r.status===401&&(console.error(I.red("Invalid or expired token.")),process.exit(1)),r}be(n),console.log(I.green("Authenticated successfully. Token saved."))}catch(n){console.error(I.red(`Login failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}async function Wt(){let e=Y();if(!e){console.log(I.yellow('Not authenticated. Run "kindlm login" to authenticate.'));return}let t=K(j(),e);try{await t.get("/v1/auth/tokens"),console.log(I.green("Authenticated.")),console.log(` Cloud URL: ${j()}`)}catch(n){n instanceof E&&n.status===401?console.log(I.yellow('Stored token is invalid or expired. Run "kindlm login" to re-authenticate.')):console.log(I.yellow(`Cannot reach Cloud API: ${n instanceof Error?n.message:String(n)}`))}}function Yt(){return new Promise((e,t)=>{let n=new Jt({write(r,a,i){i()}});process.stderr.write("Paste your KindLM API token: ");let o=Gt({input:process.stdin,output:n,terminal:!0});o.question("",r=>{o.close(),process.stderr.write(`
|
|
52
|
+
`);let a=r.trim();if(!a){t(new Error("No token provided"));return}e(a)})})}import{basename as Zt}from"path";import{execSync as Qt}from"child_process";import ee from"chalk";import{execSync as Q}from"child_process";function Pe(){try{let e=Q("git rev-parse HEAD",{encoding:"utf-8"}).trim()||null,t=Q("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim()||null,o=Q("git status --porcelain",{encoding:"utf-8"}).trim().length>0;return{commitSha:e,branch:t,dirty:o}}catch{return{commitSha:null,branch:null,dirty:!1}}}function $e(){return process.env.GITHUB_ACTIONS?{name:"github_actions",isCI:!0,commitSha:process.env.GITHUB_SHA??null,branch:process.env.GITHUB_REF_NAME??null}:process.env.GITLAB_CI?{name:"gitlab_ci",isCI:!0,commitSha:process.env.CI_COMMIT_SHA??null,branch:process.env.CI_COMMIT_BRANCH??null}:process.env.CI?{name:null,isCI:!0,commitSha:null,branch:null}:{name:null,isCI:!1,commitSha:null,branch:null}}function z(e){return encodeURIComponent(e)}async function Ee(e,t,n){let o=await Xt(e,n.projectName),r=await qt(e,o,n.suiteName,n.configHash),a=await e.post(`/v1/runs/${z(o)}/runs`,{suiteId:r,commitSha:n.commitSha,branch:n.branch,environment:n.environment,triggeredBy:n.triggeredBy}),i=Vt(t.aggregated),s=50;for(let l=0;l<i.length;l+=s){let p=i.slice(l,l+s);await e.post(`/v1/results/${z(a.id)}/results`,{results:p})}let{runResult:c}=t,m=c.totalTests>0?c.passed/c.totalTests:0,u=new Set(t.aggregated.map(l=>l.modelId)),f=t.aggregated.map(l=>l.assertionScores.judge?.mean).filter(l=>l!==void 0),y=f.length>0?f.reduce((l,p)=>l+p,0)/f.length:void 0,d=t.aggregated.map(l=>l.latencyAvgMs),C=d.length>0?d.reduce((l,p)=>l+p,0)/d.length:void 0,S=t.aggregated.reduce((l,p)=>l+p.totalCostUsd,0),h=S>0?S:void 0;return await e.patch(`/v1/runs/${z(a.id)}`,{status:"completed",passRate:m,testCount:c.totalTests,modelCount:u.size,judgeAvgScore:y,latencyAvgMs:C,costEstimateUsd:h,finishedAt:new Date().toISOString()}),{runId:a.id,projectId:o}}async function Xt(e,t){let{projects:n}=await e.get("/v1/projects"),o=n.find(a=>a.name===t);return o?o.id:(await e.post("/v1/projects",{name:t})).id}async function qt(e,t,n,o){let{suites:r}=await e.get(`/v1/suites/${z(t)}/suites`),a=r.find(s=>s.name===n);return a?a.id:(await e.post(`/v1/suites/${z(t)}/suites`,{name:n,configHash:o})).id}function Vt(e){return e.map(t=>{let n=t,o=t.runs.length>0?t.runs.flatMap(r=>r.assertions.filter(a=>!a.passed).map(a=>a.failureMessage)).filter(r=>r!==void 0):n.failureMessages??[];return{testCaseName:t.testCaseName,modelId:t.modelId,passed:t.passed?1:0,passRate:t.passRate,runCount:t.runCount,judgeAvg:t.assertionScores.judge?.mean??null,driftScore:t.assertionScores.drift?.mean??null,latencyAvgMs:t.latencyAvgMs??null,costUsd:t.totalCostUsd??null,totalTokens:t.totalTokens??null,failureCodes:t.failureCodes.length>0?JSON.stringify(t.failureCodes):null,failureMessages:o.length>0?JSON.stringify(o):null,assertionScores:Object.keys(t.assertionScores).length>0?JSON.stringify(t.assertionScores):null}})}function Ie(e){e.command("upload").description("Push last run results to KindLM Cloud").option("-t, --token <token>","API token (overrides stored token)").option("-p, --project <name>","Project name").action(async t=>{try{let n=t.token??process.env.KINDLM_API_TOKEN??Y();n||(console.error(ee.red('Not authenticated. Run "kindlm login" first or pass --token.')),process.exit(1));let o=ue();o||(console.error(ee.red('No test run found. Run "kindlm test" first.')),process.exit(1));let r=Pe(),a=$e(),i=t.project??tn(),s=K(j(),n),c=D();c.start("Uploading results to KindLM Cloud...");try{let m=await Ee(s,o.runnerResult,{projectName:i,suiteName:o.suiteName,configHash:o.configHash,commitSha:a.commitSha??r.commitSha??void 0,branch:a.branch??r.branch??void 0,environment:a.isCI?"ci":"local",triggeredBy:a.name??"local"});c.succeed("Uploaded successfully."),console.log(` Run ID: ${m.runId}`),console.log(` Project: ${i}`),console.log(` Suite: ${o.suiteName}`)}catch(m){throw c.fail("Upload failed."),m}}catch(n){console.error(ee.red(`Upload failed: ${n instanceof Error?n.message:String(n)}`)),process.exit(1)}})}function en(e){try{let o=new URL(e).pathname.split("/").filter(Boolean),r=o[o.length-1];if(r)return r.replace(/\.git$/,"")}catch{}let t=e.match(/^[\w.-]+@[\w.-]+:(.+?)(?:\.git)?$/);if(t?.[1]){let n=t[1].split("/");return n[n.length-1]??null}return null}function tn(){try{let e=Qt("git remote get-url origin",{encoding:"utf-8"}).trim(),t=en(e);if(t)return t}catch{}return Zt(process.cwd())}import{readFileSync as rn,statSync as sn}from"fs";import{resolve as an,dirname as cn}from"path";import{spawn as ln}from"child_process";import T from"chalk";import{parseConfig as dn,createProvider as mn,filterSpans as un,mapSpansToResult as fn,buildContextFromTrace as pn,createAssertionsFromExpect as gn,evaluateGates as hn}from"@kindlm/core";import{createServer as nn}from"http";import{parseOtlpPayload as on}from"@kindlm/core";function Oe(e){let t=[],n=null,o=[];function r(){for(let i of o)i()}function a(i,s){if(s.setHeader("Access-Control-Allow-Origin","*"),s.setHeader("Access-Control-Allow-Methods","POST, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),i.method==="OPTIONS"){s.writeHead(204),s.end();return}if(i.method!=="POST"||i.url!=="/v1/traces"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not found"}));return}let c=[];i.on("data",m=>c.push(m)),i.on("end",()=>{try{let m=Buffer.concat(c).toString("utf-8"),u=JSON.parse(m),f=on(u);f.success?(t.push(...f.data),r(),s.writeHead(200,{"Content-Type":"application/json"}),s.end(JSON.stringify({partialSuccess:{}}))):(s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:f.error.message})))}catch{s.writeHead(400,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Invalid JSON"}))}})}return{start(){return new Promise((i,s)=>{n=nn(a),n.on("error",s),n.listen(e,()=>i())})},stop(){return new Promise(i=>{n?n.close(()=>i()):i()})},getSpans(){return[...t]},waitForSpans({timeoutMs:i}){return new Promise(s=>{if(t.length>0){s([...t]);return}let c=setTimeout(()=>{o=o.filter(u=>u!==m),s([...t])},i),m=()=>{clearTimeout(c),o=o.filter(u=>u!==m),setTimeout(()=>s([...t]),500)};o.push(m)})}}}function Ae(e){e.command("trace").description("Ingest OpenTelemetry traces and run assertions against them").option("-c, --config <path>","Config file path","kindlm.yaml").option("--port <port>","OTLP HTTP port","4318").option("--command <cmd>","Command to spawn (traces are collected while it runs)").option("--timeout <ms>","Timeout in ms to wait for traces","30000").option("--reporter <type>","Report format: pretty, json, junit","pretty").action(async t=>{let n=D();try{let o=an(process.cwd(),t.config),r=cn(o);try{sn(o).size>1048576&&(console.error(T.red("Config file exceeds 1MB limit")),process.exit(1))}catch{console.error(T.red(`Config file not found: ${o}`)),process.exit(1)}let a;try{a=rn(o,"utf-8")}catch{console.error(T.red(`Config file not found: ${o}`)),process.exit(1)}let i=A(),s=dn(a,{configDir:r,fileReader:i});s.success||(console.error(T.red(`Config validation failed: ${s.error.message}`)),process.exit(1));let c=s.data,m=c.trace??{port:parseInt(t.port,10),timeoutMs:parseInt(t.timeout,10),spanMapping:{outputTextAttr:"gen_ai.completion.0.content",modelAttr:"gen_ai.response.model",systemAttr:"gen_ai.system",inputTokensAttr:"gen_ai.usage.input_tokens",outputTokensAttr:"gen_ai.usage.output_tokens"}},u=parseInt(t.port,10)||m.port,f=parseInt(t.timeout,10)||m.timeoutMs,y=Oe(u);await y.start(),n.start(`Listening for OTLP traces on port ${u}...`),t.command&&ln("sh",["-c",t.command],{cwd:r,env:{...process.env,OTEL_EXPORTER_OTLP_ENDPOINT:`http://localhost:${u}`,OTEL_EXPORTER_OTLP_PROTOCOL:"http/json"},stdio:"inherit"}).on("error",R=>{n.fail(`Command failed: ${R.message}`)});let d=await y.waitForSpans({timeoutMs:f});await y.stop(),d.length===0&&(n.fail("No traces received"),process.exit(1)),n.succeed(`Received ${d.length} spans`);let C=un(d,m.spanFilter),S=fn(C,m.spanMapping),h=G(),l=new Map,p=c.providers;for(let[v,R]of Object.entries(p)){if(!R)continue;let x="";if(R.apiKeyEnv){let k=process.env[R.apiKeyEnv];k&&(x=k.trim())}if(!(!x&&v!=="ollama"))try{let k=mn(v,h);await k.initialize({apiKey:x,baseUrl:R.baseUrl,organization:R.organization,timeoutMs:c.defaults.timeoutMs,maxRetries:2}),l.set(v,k)}catch{}}let b=c.defaults.judgeModel??c.models[0]?.id,L=c.models.find(v=>v.id===b),F=L?l.get(L.provider):void 0,O=pn(S,{configDir:r,judgeAdapter:F,judgeModel:L?.model}),_=[];for(let v of c.tests){if(v.skip)continue;let R=gn(v.expect),x=[];for(let k of R){let P=await k.evaluate(O);x.push(...P)}_.push({testName:v.name,assertions:x})}let te=_.reduce((v,R)=>v+R.assertions.length,0),ne=_.reduce((v,R)=>v+R.assertions.filter(x=>x.passed).length,0),oe=te-ne;console.log(),console.log(T.bold("Trace Test Results")),console.log(T.dim("\u2500".repeat(50)));for(let{testName:v,assertions:R}of _){let k=R.every(P=>P.passed)?T.green("\u2713"):T.red("\u2717");console.log(`${k} ${v}`);for(let P of R){let Ne=P.passed?T.green(" \u2713"):T.red(" \u2717"),Le=P.failureMessage?`${P.label}: ${P.failureMessage}`:P.label;console.log(`${Ne} ${Le}`)}}console.log(),console.log(`${T.bold("Total:")} ${ne} passed, ${oe} failed out of ${te} assertions`);let re=hn(c.gates,[]);if(!re.passed)for(let v of re.gates.filter(R=>!R.passed))console.log(T.red(`Gate failed: ${v.message}`));process.exit(oe>0?1:0)}catch(o){n.fail(`Trace command failed: ${o instanceof Error?o.message:String(o)}`),process.exit(1)}})}function Me(){let e=new yn;return e.name("kindlm").description("AI agent behavioral regression testing").version("0.0.0"),se(e),ie(e),ye(e),we(e),ke(e),Ie(e),Ae(e),e}process.on("unhandledRejection",e=>{let t=e instanceof Error?e.message:String(e);process.stderr.write(`
|
|
53
|
+
Unhandled error: ${t}
|
|
54
|
+
`),process.exit(1)});process.on("uncaughtException",e=>{process.stderr.write(`
|
|
55
|
+
Fatal error: ${e.message}
|
|
56
|
+
`),process.exit(1)});Me().parse(process.argv);
|
|
57
|
+
//# sourceMappingURL=kindlm.js.map
|