@carrierllc/mcp 0.9.2 → 0.10.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/README.md +63 -5
- package/dist/{chunk-4XHSOF62.js → chunk-DUAENMJE.js} +325 -4
- package/dist/chunk-DUAENMJE.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1575 -1401
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/plugin/carrier/README.md +1 -1
- package/dist/chunk-4XHSOF62.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/lib/deploy-targets.ts","../src/cli/lib/exec.ts","../src/cli/lib/brand.ts","../src/cli/lib/storefront-secrets.ts","../src/cli/lib/site.ts","../src/cli/lib/clerk.ts","../src/cli/lib/clerk-cli.ts","../src/cli/lib/verify-storefront.ts","../../../packages/screens/src/model.ts","../../../packages/brand/src/tokens.ts","../../../packages/screens/src/render-html.ts","../../../packages/screens/src/render-tui.ts","../../../packages/screens/src/builders.ts","../package.json","../src/version.ts","../../../packages/ocs-client/src/rate-floor.ts","../../../packages/ocs-client/src/rate-governor.ts","../../../packages/ocs-client/src/ocs-param-shapes.ts","../../../packages/ocs-client/src/client.ts","../../../packages/ocs-client/src/ocs-params.ts","../../../packages/ocs-client/src/list-subscriber.ts","../../../packages/ocs-client/src/router-catalog.ts","../../../packages/ocs-client/src/ocs-array.ts","../../../packages/ocs-client/src/analysis.ts","../../../packages/ocs-client/src/usage.ts","../src/lib/storefront-logo.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { mkdtemp, rm, writeFile as writeFileMode } from \"node:fs/promises\";\nimport { exists, writeFile } from \"./fsx.js\";\nimport { run, which } from \"./exec.js\";\n\n/**\n * Multi-target storefront deployment.\n *\n * The storefront is a Next.js app that ships with an OpenNext/Cloudflare config,\n * so Cloudflare is the native target and stays the default. Vercel runs Next.js\n * natively too. Netlify and Fly need a config file the template does not carry,\n * so they are supported by writing that config first — see `ensureConfig`.\n * Anything we cannot honestly deploy reports `ready: false` with the reason,\n * rather than shelling out and failing halfway.\n */\n\nexport type TargetId = \"cloudflare\" | \"vercel\" | \"netlify\" | \"fly\";\n\nexport const TARGET_IDS: readonly TargetId[] = [\"cloudflare\", \"vercel\", \"netlify\", \"fly\"] as const;\n\nexport function isTargetId(v: string): v is TargetId {\n return (TARGET_IDS as readonly string[]).includes(v);\n}\n\nexport interface TargetStatus {\n id: TargetId;\n label: string;\n /** CLI resolvable on PATH, directly or via npx. */\n installed: boolean;\n /** CLI reports an authenticated session. */\n authenticated: boolean;\n /** Project carries this target's config file. */\n configured: boolean;\n /** Deployable right now. */\n ready: boolean;\n /** Why not ready, when it is not. */\n reason?: string;\n /** Account identity, when the CLI reports one. */\n account?: string;\n}\n\nexport interface DeployOutcome {\n ok: boolean;\n target: TargetId;\n projectName: string;\n url?: string;\n reason?: string;\n}\n\ninterface TargetDef {\n id: TargetId;\n label: string;\n bin: string;\n /** npx package when the binary is not installed globally. Empty = no npx path. */\n npxPkg: string;\n /** Config files that mark a project as set up for this target. */\n markers: string[];\n /** package.json script that produces this target's build artifact. */\n buildScript: string;\n /** Artifact required before deploying, relative to the storefront root. */\n artifact?: string;\n /** Argv that reports the logged-in identity. */\n whoami: string[];\n /** Command to run to log in, shown in guidance. */\n loginHint: string;\n /**\n * Read the account identity out of the CLI's output. Given stdout and stderr\n * separately, because several of these CLIs print a version banner to stderr\n * that would otherwise be mistaken for the answer.\n */\n parseAccount?: (stdout: string, stderr: string) => string | undefined;\n deployArgs: (projectName: string) => string[];\n /** Write config this target needs but the template does not ship. */\n ensureConfig?: (storefront: string, projectName: string) => Promise<void>;\n /** Argv that sets one secret. Value goes on stdin when `stdin` is set. */\n secretArgs: (key: string, value: string, projectName: string) => { args: string[]; stdin?: string };\n}\n\nconst DEFS: Record<TargetId, TargetDef> = {\n cloudflare: {\n id: \"cloudflare\",\n label: \"Cloudflare Workers\",\n bin: \"wrangler\",\n npxPkg: \"wrangler\",\n markers: [\"wrangler.jsonc\", \"wrangler.json\", \"wrangler.toml\"],\n buildScript: \"cf:build\",\n artifact: join(\".open-next\", \"worker.js\"),\n whoami: [\"whoami\"],\n loginHint: \"wrangler login\",\n // wrangler writes this mid-sentence, so the trailing period is not part of it.\n parseAccount: (stdout, stderr) =>\n `${stdout}${stderr}`.match(/associated with the email\\s+(\\S+?)[.,]?(?:\\s|$)/)?.[1],\n deployArgs: (name) => [\"deploy\", \"--name\", name],\n secretArgs: (key, value, name) => ({ args: [\"secret\", \"put\", key, \"--name\", name], stdin: value }),\n },\n vercel: {\n id: \"vercel\",\n label: \"Vercel\",\n bin: \"vercel\",\n npxPkg: \"vercel\",\n markers: [\"vercel.json\", \".vercel\"],\n buildScript: \"build\",\n whoami: [\"whoami\"],\n loginHint: \"vercel login\",\n // The username is the only thing on stdout; the version banner goes to stderr.\n parseAccount: (stdout) =>\n stdout\n .trim()\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith(\">\") && !/^Vercel CLI/i.test(l))\n .pop(),\n deployArgs: () => [\"deploy\", \"--prod\", \"--yes\"],\n secretArgs: (key, value) => ({ args: [\"env\", \"add\", key, \"production\", \"--force\"], stdin: value }),\n },\n netlify: {\n id: \"netlify\",\n label: \"Netlify\",\n bin: \"netlify\",\n npxPkg: \"netlify-cli\",\n markers: [\"netlify.toml\"],\n buildScript: \"build\",\n whoami: [\"status\"],\n loginHint: \"netlify login\",\n parseAccount: (stdout, stderr) => `${stdout}${stderr}`.match(/Email:\\s*(\\S+)/)?.[1],\n deployArgs: () => [\"deploy\", \"--build\", \"--prod\"],\n ensureConfig: async (storefront) => {\n const path = join(storefront, \"netlify.toml\");\n if (await exists(path)) return;\n // Next.js on Netlify is only supported through the official plugin.\n await writeFile(\n path,\n [\n \"# Written by @carrierllc/mcp\",\n \"[build]\",\n ' command = \"npm run build\"',\n ' publish = \".next\"',\n \"\",\n \"[[plugins]]\",\n ' package = \"@netlify/plugin-nextjs\"',\n \"\",\n ].join(\"\\n\"),\n );\n },\n secretArgs: (key, value) => ({ args: [\"env:set\", key, value] }),\n },\n fly: {\n id: \"fly\",\n label: \"Fly.io\",\n bin: \"flyctl\",\n npxPkg: \"\",\n markers: [\"fly.toml\"],\n buildScript: \"build\",\n whoami: [\"auth\", \"whoami\"],\n loginHint: \"flyctl auth login\",\n parseAccount: (out) => out.trim().split(\"\\n\").pop()?.trim(),\n deployArgs: () => [\"deploy\", \"--now\"],\n ensureConfig: async (storefront, projectName) => {\n const toml = join(storefront, \"fly.toml\");\n if (!(await exists(toml))) {\n await writeFile(\n toml,\n [\n \"# Written by @carrierllc/mcp\",\n `app = \"${projectName}\"`,\n \"\",\n \"[build]\",\n ' dockerfile = \"Dockerfile\"',\n \"\",\n \"[http_service]\",\n \" internal_port = 3000\",\n \" force_https = true\",\n \" auto_stop_machines = true\",\n \" auto_start_machines = true\",\n \"\",\n ].join(\"\\n\"),\n );\n }\n const dockerfile = join(storefront, \"Dockerfile\");\n if (!(await exists(dockerfile))) {\n // Fly runs a container, so the Next server needs one — the template has none.\n await writeFile(\n dockerfile,\n [\n \"# Written by @carrierllc/mcp\",\n \"FROM node:22-slim AS build\",\n \"WORKDIR /app\",\n \"COPY package*.json ./\",\n \"RUN npm install\",\n \"COPY . .\",\n \"RUN npm run build\",\n \"\",\n \"FROM node:22-slim\",\n \"WORKDIR /app\",\n \"ENV NODE_ENV=production PORT=3000\",\n \"COPY --from=build /app ./\",\n \"EXPOSE 3000\",\n 'CMD [\"npm\", \"run\", \"start\"]',\n \"\",\n ].join(\"\\n\"),\n );\n }\n },\n secretArgs: (key, value) => ({ args: [\"secrets\", \"set\", `${key}=${value}`] }),\n },\n};\n\nexport function targetLabel(id: TargetId): string {\n return DEFS[id].label;\n}\n\n/**\n * The name this project already deploys under, if it declares one.\n *\n * Cloudflare identifies a Worker by name, so deriving the name from the brand\n * instead of the project config does not rename a deployment — it creates a\n * second one and abandons the first, along with its routes, custom domain and\n * secrets. Observed: a storefront configured as `storefront` redeployed as\n * `bananas`, leaving the live site untouched and broken.\n *\n * So the config wins whenever it has a name, exactly as a configured host wins\n * in `rankTargets`.\n */\nexport async function configuredProjectName(\n id: TargetId,\n storefront: string,\n): Promise<string | undefined> {\n if (id !== \"cloudflare\") return undefined;\n for (const marker of DEFS.cloudflare.markers) {\n const path = join(storefront, marker);\n if (!(await exists(path))) continue;\n const body = await readFileText(path);\n // JSONC — a comment-tolerant match beats a parse that would choke on them.\n const name = body.match(/^\\s*\"?name\"?\\s*:\\s*\"([^\"]+)\"/m)?.[1];\n if (name) return name;\n }\n return undefined;\n}\n\n/**\n * Route a custom domain to the Worker.\n *\n * `custom_domain: true` makes Cloudflare provision the DNS record and the\n * certificate itself, which is the only reason this is worth automating. It has\n * to live in wrangler config — there is no CLI flag for it.\n *\n * Requires the domain's zone to be on the same Cloudflare account. We do not\n * check that here; a deploy against a zone the account does not hold fails with\n * Cloudflare's own error, which says more than a guess would.\n */\nexport async function setCustomDomain(storefront: string, domain: string): Promise<boolean> {\n const clean = domain.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\");\n if (!clean || !clean.includes(\".\")) return false;\n\n const path = join(storefront, \"wrangler.jsonc\");\n if (!(await exists(path))) return false;\n const body = await readFileText(path);\n if (body.includes(`\"pattern\": \"${clean}\"`)) return true; // already routed\n\n // JSONC, so this is a targeted textual insert rather than a parse/serialise\n // round-trip that would strip the file's comments.\n const routes = ` \"routes\": [\\n { \"pattern\": \"${clean}\", \"custom_domain\": true }\\n ],\\n`;\n const anchor = body.indexOf(`\"main\"`);\n if (anchor === -1) return false;\n const lineStart = body.lastIndexOf(\"\\n\", anchor) + 1;\n const patched = body.slice(0, lineStart) + routes + body.slice(lineStart);\n await writeFile(path, patched);\n return true;\n}\n\nasync function readFileText(path: string): Promise<string> {\n const { readFile } = await import(\"./fsx.js\");\n return readFile(path, \"utf8\");\n}\n\n/**\n * Revert to the previously deployed version.\n *\n * Only Cloudflare is wired: it is the one host here with a single-command\n * instant rollback. Everywhere else this reports false so the caller says\n * \"left as-is\" rather than implying a revert that never happened.\n */\nexport async function rollback(\n id: TargetId,\n storefront: string,\n projectName: string,\n): Promise<{ ok: boolean; reason?: string }> {\n if (id !== \"cloudflare\") {\n return { ok: false, reason: `${DEFS[id].label} has no one-command rollback — revert manually.` };\n }\n const def = DEFS[id];\n const resolved = await resolveBin(def);\n if (!resolved) return { ok: false, reason: \"wrangler not found.\" };\n const r = await run(\n resolved.bin,\n [...resolved.prefix, \"rollback\", \"--name\", projectName, \"--yes\"],\n { cwd: storefront, timeoutMs: 300_000 },\n );\n return r.ok\n ? { ok: true }\n : { ok: false, reason: `${r.stderr || r.stdout}`.trim().split(\"\\n\").slice(-2).join(\" \").slice(0, 300) };\n}\n\n/** Exposed so the account parsers can be tested against real CLI output. */\nexport function parseAccountForTest(\n id: TargetId,\n stdout: string,\n stderr: string,\n): string | undefined {\n return DEFS[id].parseAccount?.(stdout, stderr);\n}\n\nexport function loginHint(id: TargetId): string {\n return DEFS[id].loginHint;\n}\n\n/** How to invoke a target's CLI: direct binary, npx, or not at all. */\nasync function resolveBin(def: TargetDef): Promise<{ bin: string; prefix: string[] } | undefined> {\n if (await which(def.bin)) return { bin: def.bin, prefix: [] };\n if (def.npxPkg && (await which(\"npx\"))) return { bin: \"npx\", prefix: [def.npxPkg] };\n return undefined;\n}\n\n/** Probe one target: installed, authenticated, and configured for this project. */\nexport async function probeTarget(id: TargetId, storefront: string): Promise<TargetStatus> {\n const def = DEFS[id];\n let configured = false;\n for (const marker of def.markers) {\n if (await exists(join(storefront, marker))) {\n configured = true;\n break;\n }\n }\n\n const resolved = await resolveBin(def);\n if (!resolved) {\n return {\n id,\n label: def.label,\n installed: false,\n authenticated: false,\n configured,\n ready: false,\n reason: def.npxPkg\n ? `${def.bin} not found — install it, or make npx available.`\n : `${def.bin} not found — install the Fly CLI (brew install flyctl).`,\n };\n }\n\n const who = await run(resolved.bin, [...resolved.prefix, ...def.whoami], {\n cwd: storefront,\n timeoutMs: 60_000,\n });\n if (!who.ok) {\n return {\n id,\n label: def.label,\n installed: true,\n authenticated: false,\n configured,\n ready: false,\n reason: `${def.bin} is not logged in — run \\`${def.loginHint}\\`.`,\n };\n }\n\n return {\n id,\n label: def.label,\n installed: true,\n authenticated: true,\n configured,\n ready: true,\n account: def.parseAccount?.(who.stdout, who.stderr),\n };\n}\n\n/** Probe every target. Order is stable; ranking is a separate concern. */\nexport async function probeAll(storefront: string): Promise<TargetStatus[]> {\n return Promise.all(TARGET_IDS.map((id) => probeTarget(id, storefront)));\n}\n\n/**\n * Rank the deployable targets.\n *\n * A target the project is already configured for wins, because that config was a\n * deliberate choice. Otherwise fall back to declaration order, which puts\n * Cloudflare first — the one the template is actually built for.\n */\nexport function rankTargets(statuses: TargetStatus[]): TargetStatus[] {\n return statuses\n .filter((s) => s.ready)\n .sort((a, b) => {\n if (a.configured !== b.configured) return a.configured ? -1 : 1;\n return TARGET_IDS.indexOf(a.id) - TARGET_IDS.indexOf(b.id);\n });\n}\n\n/** The build script this target needs (`cf:build` for Cloudflare, else `build`). */\nexport function buildScriptFor(id: TargetId): string {\n return DEFS[id].buildScript;\n}\n\n/** True when the target needs a build artifact that is not on disk yet. */\nexport async function artifactMissing(id: TargetId, storefront: string): Promise<boolean> {\n const artifact = DEFS[id].artifact;\n if (!artifact) return false;\n return !(await exists(join(storefront, artifact)));\n}\n\n/** Best-effort deployed URL, read back out of the target CLI's own output. */\nexport function parseDeployedUrl(output: string): string | undefined {\n return output.match(\n /https:\\/\\/[^\\s\"']+\\.(?:workers\\.dev|vercel\\.app|netlify\\.app|fly\\.dev)[^\\s\"']*/,\n )?.[0];\n}\n\n/** Push one secret to a target. Never logs the value. */\nexport async function putSecret(\n id: TargetId,\n storefront: string,\n projectName: string,\n key: string,\n value: string,\n): Promise<boolean> {\n const def = DEFS[id];\n const resolved = await resolveBin(def);\n if (!resolved) return false;\n const { args, stdin } = def.secretArgs(key, value, projectName);\n const r = await run(resolved.bin, [...resolved.prefix, ...args], {\n cwd: storefront,\n timeoutMs: 120_000,\n stdin,\n });\n return r.ok;\n}\n\nexport interface StagedSecrets {\n /** Keys that are now guaranteed to be live when the deploy lands. */\n staged: string[];\n failed: string[];\n /** Passed to `deployTo` so Cloudflare can ship them in the same version. */\n secretsFile?: string;\n /** Called after the deploy, success or not. */\n cleanup: () => Promise<void>;\n}\n\n/**\n * Put secrets in place *before or with* the code, never after.\n *\n * Pushing secrets after a successful deploy leaves a window where the new code\n * is serving without them. That window is not theoretical: it is why a\n * storefront deployed green and then returned 500 on every catalog page.\n *\n * Each host has its own way of closing it:\n * Cloudflare — `--secrets-file` on `wrangler deploy`, one atomic version.\n * Fly — `secrets set --stage`, applied by the deploy that follows.\n * Vercel — `env add`, picked up by the next deployment.\n * Netlify — `env:set`, picked up by the next build.\n */\nexport async function stageSecrets(\n id: TargetId,\n storefront: string,\n projectName: string,\n secrets: Record<string, string>,\n): Promise<StagedSecrets> {\n const entries = Object.entries(secrets).filter(([, v]) => v?.trim());\n const noop: StagedSecrets = { staged: [], failed: [], cleanup: async () => {} };\n if (entries.length === 0) return noop;\n\n const def = DEFS[id];\n const resolved = await resolveBin(def);\n if (!resolved) return { staged: [], failed: entries.map(([k]) => k), cleanup: async () => {} };\n\n if (id === \"cloudflare\") {\n // A file rather than N `secret put` calls: one API call, one version, and no\n // interval where the Worker is live without them.\n const dir = await mkdtemp(join(tmpdir(), \"carrier-secrets-\"));\n const file = join(dir, \".env\");\n const body = entries.map(([k, v]) => `${k}=${v}`).join(\"\\n\");\n // 0600: this is the one place secret values touch disk.\n await writeFileMode(file, `${body}\\n`, { mode: 0o600 });\n return {\n staged: entries.map(([k]) => k),\n failed: [],\n secretsFile: file,\n cleanup: async () => {\n await rm(dir, { recursive: true, force: true });\n },\n };\n }\n\n if (id === \"fly\") {\n // --stage defers the machine update to the deploy, avoiding a second restart.\n const args = [\"secrets\", \"set\", \"--stage\", ...entries.map(([k, v]) => `${k}=${v}`)];\n const r = await run(resolved.bin, [...resolved.prefix, ...args], {\n cwd: storefront,\n timeoutMs: 180_000,\n });\n return {\n staged: r.ok ? entries.map(([k]) => k) : [],\n failed: r.ok ? [] : entries.map(([k]) => k),\n cleanup: async () => {},\n };\n }\n\n // Vercel and Netlify: set them first, the following deploy picks them up.\n const staged: string[] = [];\n const failed: string[] = [];\n for (const [key, value] of entries) {\n const ok = await putSecret(id, storefront, projectName, key, value);\n (ok ? staged : failed).push(key);\n }\n return { staged, failed, cleanup: async () => {} };\n}\n\n/** Deploy the storefront to one target. Assumes the build already ran. */\nexport async function deployTo(\n id: TargetId,\n storefront: string,\n projectName: string,\n opts: { secretsFile?: string } = {},\n): Promise<DeployOutcome> {\n const def = DEFS[id];\n const resolved = await resolveBin(def);\n if (!resolved) {\n return { ok: false, target: id, projectName, reason: `${def.bin} not found.` };\n }\n await def.ensureConfig?.(storefront, projectName);\n\n if (await artifactMissing(id, storefront)) {\n return {\n ok: false,\n target: id,\n projectName,\n reason: `No ${def.artifact} build found — run the build step first.`,\n };\n }\n\n const args = [...resolved.prefix, ...def.deployArgs(projectName)];\n // Cloudflare can carry the secrets in the same version as the code.\n if (id === \"cloudflare\" && opts.secretsFile) args.push(\"--secrets-file\", opts.secretsFile);\n\n // Captured rather than inherited, so the deployed URL can be parsed back out.\n const r = await run(resolved.bin, args, {\n cwd: storefront,\n timeoutMs: 900_000,\n });\n const combined = `${r.stdout}${r.stderr}`;\n if (!r.ok) {\n const tail = combined.trim().split(\"\\n\").slice(-3).join(\" \").slice(0, 400);\n return { ok: false, target: id, projectName, reason: tail || `${def.bin} deploy failed.` };\n }\n return { ok: true, target: id, projectName, url: parseDeployedUrl(combined) };\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface RunResult {\n ok: boolean;\n code: number | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * A binary name safe to hand to `spawn` with `shell: false`.\n *\n * `shell: false` already keeps `spawn` from invoking a shell, so classic\n * metacharacter injection (`;`, `|`, backticks, `$()`) is not reachable\n * through it. This check is a second, explicit boundary: every caller in this\n * codebase passes a literal binary name (`wrangler`, `vercel`, `npx`, ...), so\n * a `cmd` containing a path separator, whitespace, or a shell metacharacter is\n * rejected before it reaches `spawn` rather than trusted implicitly.\n */\nconst SAFE_COMMAND = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;\n\nfunction isSafeCommand(cmd: string): boolean {\n return SAFE_COMMAND.test(cmd);\n}\n\n/** Run a command, capturing output. Never throws — inspect `.ok`. */\nexport function run(\n cmd: string,\n args: string[],\n opts: { cwd?: string; timeoutMs?: number; stdin?: string } = {},\n): Promise<RunResult> {\n if (!isSafeCommand(cmd)) {\n return Promise.resolve({\n ok: false,\n code: null,\n stdout: \"\",\n stderr: `refusing to run unsafe command: ${cmd}`,\n });\n }\n return new Promise((resolve) => {\n const child = spawn(cmd, args, { cwd: opts.cwd, shell: false });\n // Secret values are piped in rather than passed as argv, so they never\n // appear in the process table or in shell history.\n if (opts.stdin !== undefined) {\n child.stdin?.on(\"error\", () => {\n /* closed early — the close handler reports the real failure */\n });\n child.stdin?.end(opts.stdin);\n }\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n const finish = (result: RunResult) => {\n if (settled) return;\n settled = true;\n resolve(result);\n };\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (opts.timeoutMs && opts.timeoutMs > 0) {\n timer = setTimeout(() => {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* ignore */\n }\n finish({\n ok: false,\n code: null,\n stdout,\n stderr: stderr || `timeout after ${opts.timeoutMs}ms`,\n });\n }, opts.timeoutMs);\n }\n child.stdout?.on(\"data\", (d) => (stdout += d.toString()));\n child.stderr?.on(\"data\", (d) => (stderr += d.toString()));\n child.on(\"error\", () => {\n if (timer) clearTimeout(timer);\n finish({ ok: false, code: null, stdout, stderr });\n });\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n finish({ ok: code === 0, code, stdout, stderr });\n });\n });\n}\n\n/** Run a command inheriting stdio (for long, user-visible tasks like installs/builds). */\nexport function runInherit(cmd: string, args: string[], opts: { cwd?: string } = {}): Promise<RunResult> {\n if (!isSafeCommand(cmd)) {\n return Promise.resolve({\n ok: false,\n code: null,\n stdout: \"\",\n stderr: `refusing to run unsafe command: ${cmd}`,\n });\n }\n return new Promise((resolve) => {\n const child = spawn(cmd, args, { cwd: opts.cwd, shell: false, stdio: \"inherit\" });\n child.on(\"error\", () => resolve({ ok: false, code: null, stdout: \"\", stderr: \"\" }));\n child.on(\"close\", (code) => resolve({ ok: code === 0, code, stdout: \"\", stderr: \"\" }));\n });\n}\n\n/** True if a binary is resolvable on PATH. */\nexport async function which(bin: string): Promise<boolean> {\n const probe = process.platform === \"win32\" ? \"where\" : \"which\";\n const r = await run(probe, [bin]);\n return r.ok && r.stdout.trim().length > 0;\n}\n","/**\n * Brand config — the single white-label surface for the storefront.\n * Carrier defaults; overridable interactively or via `carrier site create --brand`.\n */\nexport interface Brand {\n name: string;\n legalName: string;\n tagline: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n supportWhatsapp: string;\n colors: {\n /** Page background (deepest ink). */\n bg: string;\n /** Primary accent. */\n accent: string;\n /** Accent pressed/dark. */\n accentDark: string;\n /** Primary text. */\n text: string;\n };\n social: {\n x?: string;\n linkedin?: string;\n instagram?: string;\n tiktok?: string;\n };\n /** MCP control-plane endpoint the storefront's API talks to. */\n carrierApiUrl: string;\n}\n\nexport const CARRIER_BRAND: Brand = {\n name: \"Carrier\",\n legalName: \"Lifecycle Innovations Limited\",\n tagline: \"Programmable connectivity, on demand.\",\n domain: \"carrier.llc\",\n supportEmail: \"support@carrier.llc\",\n supportUrl: \"https://carrier.llc/help\",\n supportWhatsapp: \"+17864604829\",\n colors: {\n bg: \"#080C16\",\n accent: \"#FF6B35\",\n accentDark: \"#D9461C\",\n text: \"#F5F1EA\",\n },\n social: {\n x: \"@carrier_llc\",\n instagram: \"@carrier.llc\",\n tiktok: \"@carrier.llc\",\n },\n carrierApiUrl: \"https://api.carrier.llc\",\n};\n\nconst CARRIER_ACCENT_LIGHT = \"#FFB088\";\nconst CARRIER_ACCENT_GRADIENT_START = \"#FF7A45\";\n\nfunction hexToRgb(hex: string): [number, number, number] | null {\n const h = hex.replace(/^#/, \"\");\n if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;\n return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];\n}\n\nfunction rgbToHex(r: number, g: number, b: number, lower = false): string {\n const fmt = (n: number) =>\n Math.max(0, Math.min(255, Math.round(n)))\n .toString(16)\n .padStart(2, \"0\");\n const out = `#${fmt(r)}${fmt(g)}${fmt(b)}`;\n return lower ? out : out.toUpperCase();\n}\n\nfunction mixHexWithWhite(hex: string, whiteRatio: number): string {\n const rgb = hexToRgb(hex);\n if (!rgb) return hex;\n const mix = (n: number) => n + (255 - n) * whiteRatio;\n return rgbToHex(mix(rgb[0]), mix(rgb[1]), mix(rgb[2]));\n}\n\nfunction darkenHex(hex: string, factor: number): string {\n const rgb = hexToRgb(hex);\n if (!rgb) return hex;\n const scale = (n: number) => n * factor;\n return rgbToHex(scale(rgb[0]), scale(rgb[1]), scale(rgb[2]), true);\n}\n\n/** Derive a darker pressed-state accent; keeps the Carrier default pair when accent is unchanged. */\nexport function deriveAccentDark(accent: string, seed: Brand = CARRIER_BRAND): string {\n if (accent.toUpperCase() === seed.colors.accent.toUpperCase()) return seed.colors.accentDark;\n const rgb = hexToRgb(accent);\n if (!rgb) return accent;\n const scale = (n: number) => Math.max(0, Math.min(255, Math.round(n * 0.75)));\n return rgbToHex(scale(rgb[0]), scale(rgb[1]), scale(rgb[2]));\n}\n\n/** Derive a lighter accent for glows and `--brand-accent-light`. */\nexport function deriveAccentLight(accent: string, seed: Brand = CARRIER_BRAND): string {\n if (accent.toUpperCase() === seed.colors.accent.toUpperCase()) return CARRIER_ACCENT_LIGHT;\n return mixHexWithWhite(accent, 0.45);\n}\n\n/** Derive the lighter hero-gradient stop paired with accent. */\nexport function deriveAccentGradientStart(accent: string, seed: Brand = CARRIER_BRAND): string {\n if (accent.toUpperCase() === seed.colors.accent.toUpperCase()) return CARRIER_ACCENT_GRADIENT_START;\n return mixHexWithWhite(accent, 0.08);\n}\n\n/** Tree-wide hex swaps for accent-derived template colors (gradients, mango scale, CSS vars). */\nexport function deriveAccentPaletteSubs(accent: string, accentDark: string): Array<[string, string]> {\n if (accent.toUpperCase() === CARRIER_BRAND.colors.accent.toUpperCase()) return [];\n const accentLight = deriveAccentLight(accent);\n const gradientStart = deriveAccentGradientStart(accent);\n return [\n [\"#FF6B35\", accent],\n [\"#D9461C\", accentDark],\n [\"#FFB088\", accentLight],\n [\"#FF7A45\", gradientStart],\n [\"#fff4ef\", mixHexWithWhite(accent, 0.94).toLowerCase()],\n [\"#ffe0d0\", mixHexWithWhite(accent, 0.85).toLowerCase()],\n [\"#ffbfa0\", mixHexWithWhite(accent, 0.7).toLowerCase()],\n [\"#ff9970\", mixHexWithWhite(accent, 0.55).toLowerCase()],\n [\"#ff7d4d\", mixHexWithWhite(accent, 0.4).toLowerCase()],\n [\"#b33a17\", darkenHex(accentDark, 0.75)],\n [\"#8a2d12\", darkenHex(accentDark, 0.58)],\n [\"#5e1e0c\", darkenHex(accentDark, 0.4)],\n [\"#3a1107\", darkenHex(accentDark, 0.25)],\n ];\n}\n\n/** Serialize a brand to the `src/brand.config.ts` the storefront imports. */\nexport function renderBrandConfig(brand: Brand): string {\n return `// Auto-generated by @carrierllc/mcp — edit freely to re-brand this storefront.\n// This is the single white-label surface: name, copy, colors, domain, support.\nexport interface Brand {\n name: string;\n legalName: string;\n tagline: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n colors: { bg: string; accent: string; accentDark: string; text: string };\n social: { x?: string; linkedin?: string };\n carrierApiUrl: string;\n}\n\nexport const brand: Brand = ${JSON.stringify(brand, null, 2)};\n\nexport default brand;\n`;\n}\n\n/** Serialize a brand to a `.env.local` for the storefront. */\nexport function renderEnv(brand: Brand): string {\n return [\n `# Generated by @carrierllc/mcp`,\n `NEXT_PUBLIC_BRAND_NAME=${JSON.stringify(brand.name)}`,\n `NEXT_PUBLIC_CARRIER_API_URL=${JSON.stringify(brand.carrierApiUrl)}`,\n `# Public site origin. Used to build absolute URLs for guest checkout.`,\n `# \\`carrier site deploy\\` writes the deployed URL back here.`,\n `NEXT_PUBLIC_APP_URL=`,\n `# Required whenever NEXT_PUBLIC_CARRIER_API_URL points at a live origin:`,\n `# the catalog client throws without it, so / and /shop return 500.`,\n `# \\`carrier site deploy\\` fills this in and pushes it to the deploy target.`,\n `CARRIER_API_KEY=`,\n `# Guest checkout calls Stripe server-side. Without this, checkout fails at`,\n `# request time while every other page keeps working.`,\n `STRIPE_SECRET_KEY=`,\n `# Clerk — \\`carrier site clerk\\` fills these in, or paste them from dashboard.clerk.com`,\n `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=`,\n `CLERK_SECRET_KEY=`,\n ``,\n ].join(\"\\n\");\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { exists, readFile, writeFile } from \"./fsx.js\";\n\n/**\n * Every environment key the storefront template consumes, and how it is delivered.\n *\n * This is the single source of truth. `template-env-coverage.test.js` greps the\n * template for `process.env.X` and fails when a key is missing here, because this\n * exact gap has now shipped three times: CARRIER_API_KEY (site down),\n * STRIPE_SECRET_KEY and NEXT_PUBLIC_APP_URL (guest checkout silently degraded).\n *\n * \"secret\" — server-side only. Must be pushed to the deploy target.\n * \"public\" — NEXT_PUBLIC_*, inlined by Next at build time. Must be in\n * .env.local *before* the build, and pushing it post-deploy is useless.\n *\n * Clerk's keys are listed even though the template never names them in\n * `process.env` — @clerk/nextjs reads them from the environment itself.\n */\nexport const TEMPLATE_ENV_KEYS = {\n NEXT_PUBLIC_BRAND_NAME: \"public\",\n NEXT_PUBLIC_CARRIER_API_URL: \"public\",\n NEXT_PUBLIC_APP_URL: \"public\",\n NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: \"public\",\n CARRIER_API_KEY: \"secret\",\n CLERK_SECRET_KEY: \"secret\",\n STRIPE_SECRET_KEY: \"secret\",\n} as const;\n\nexport type TemplateEnvKey = keyof typeof TEMPLATE_ENV_KEYS;\n\nfunction keysOfKind(kind: \"secret\" | \"public\"): TemplateEnvKey[] {\n return (Object.keys(TEMPLATE_ENV_KEYS) as TemplateEnvKey[]).filter(\n (k) => TEMPLATE_ENV_KEYS[k] === kind,\n );\n}\n\nexport const RUNTIME_SECRET_KEYS: readonly TemplateEnvKey[] = keysOfKind(\"secret\");\n\n/** Written into .env.local and inlined at build time. */\nexport const PUBLIC_ENV_KEYS: readonly TemplateEnvKey[] = keysOfKind(\"public\");\n\nexport type SecretMap = Record<string, string>;\n\n/** Parse a dotenv file. Ignores comments; strips surrounding quotes. */\nexport function parseEnvFile(body: string): SecretMap {\n const out: SecretMap = {};\n for (const raw of body.split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = line.slice(0, eq).trim();\n let value = line.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"') && value.length > 1) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\") && value.length > 1)\n ) {\n value = value.slice(1, -1);\n }\n if (value) out[key] = value;\n }\n return out;\n}\n\nasync function readEnvFile(path: string): Promise<SecretMap> {\n if (!(await exists(path))) return {};\n try {\n return parseEnvFile(await readFile(path, \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/**\n * Collect candidate values for one key, nearest source first.\n *\n * The storefront's own `.env.local` wins because it is the most specific, then\n * the live process env, then the operator's `~/.env`. Values are never logged.\n */\nexport async function discoverSecrets(\n storefront: string,\n env: Record<string, string | undefined> = process.env,\n home: string = homedir(),\n): Promise<SecretMap> {\n const local = await readEnvFile(join(storefront, \".env.local\"));\n const global = await readEnvFile(join(home, \".env\"));\n\n const merged: SecretMap = {};\n const keys = [...RUNTIME_SECRET_KEYS, ...PUBLIC_ENV_KEYS];\n for (const key of keys) {\n const value = local[key]?.trim() || env[key]?.trim() || global[key]?.trim();\n if (value) merged[key] = value;\n }\n return merged;\n}\n\n/**\n * Merge values into a storefront `.env.local`, preserving comments and order.\n *\n * An existing non-empty value is only replaced when `overwrite` names that key —\n * re-running a deploy must not clobber keys the operator edited by hand.\n */\nexport async function mergeEnvLocal(\n storefront: string,\n updates: SecretMap,\n opts: { overwrite?: string[] } = {},\n): Promise<string[]> {\n const path = join(storefront, \".env.local\");\n const overwrite = new Set(opts.overwrite ?? []);\n const original = (await exists(path)) ? await readFile(path, \"utf8\") : \"\";\n const lines = original ? original.split(\"\\n\") : [];\n const written: string[] = [];\n\n for (const [key, value] of Object.entries(updates)) {\n if (!value) continue;\n const index = lines.findIndex((l) => l.trim().startsWith(`${key}=`));\n if (index === -1) {\n lines.push(`${key}=${value}`);\n written.push(key);\n continue;\n }\n const current = lines[index].slice(lines[index].indexOf(\"=\") + 1).trim();\n const isEmpty = current === \"\" || current === '\"\"' || current === \"''\";\n if (isEmpty || overwrite.has(key)) {\n lines[index] = `${key}=${value}`;\n written.push(key);\n }\n }\n\n if (written.length === 0) return [];\n const body = lines.join(\"\\n\").replace(/\\n{3,}$/, \"\\n\");\n await writeFile(path, body.endsWith(\"\\n\") ? body : `${body}\\n`);\n return written;\n}\n\n/** True when the storefront points at a live Carrier origin and so needs a key. */\nexport function needsCarrierKey(secrets: SecretMap): boolean {\n const url = secrets.NEXT_PUBLIC_CARRIER_API_URL?.trim();\n return Boolean(url) && !secrets.CARRIER_API_KEY?.trim();\n}\n","import { join } from \"node:path\";\nimport { Brand, CARRIER_BRAND } from \"./brand.js\";\nimport { exists, readFile } from \"./fsx.js\";\nimport { runInherit, which } from \"./exec.js\";\nimport {\n buildScriptFor,\n configuredProjectName,\n deployTo,\n probeAll,\n putSecret,\n rankTargets,\n setCustomDomain,\n stageSecrets,\n type StagedSecrets,\n type TargetId,\n type TargetStatus,\n} from \"./deploy-targets.js\";\nimport { discoverSecrets, RUNTIME_SECRET_KEYS } from \"./storefront-secrets.js\";\n\nfunction slug(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40) || \"storefront\";\n}\n\n/** Install storefront deps. Uses npm to stay independent of the pnpm workspace. */\nexport async function installDeps(target: string): Promise<boolean> {\n const pkgMgr = (await which(\"pnpm\")) ? \"pnpm\" : \"npm\";\n const r = await runInherit(pkgMgr, [\"install\"], { cwd: target });\n return r.ok;\n}\n\n/**\n * Build the storefront for a deploy target.\n *\n * Cloudflare needs `cf:build`, which runs `next build` through OpenNext and\n * emits the `.open-next/worker.js` bundle. Every other target ships the plain\n * Next output, so they use `build`.\n */\nexport async function buildSite(target: string, forTarget: TargetId = \"cloudflare\"): Promise<boolean> {\n const pkgMgr = (await which(\"pnpm\")) ? \"pnpm\" : \"npm\";\n const r = await runInherit(pkgMgr, [\"run\", buildScriptFor(forTarget)], { cwd: target });\n return r.ok;\n}\n\n/** Read white-label name from a scaffolded storefront (falls back to Carrier defaults). */\nexport async function loadStorefrontBrand(\n target: string,\n overrides?: { name?: string },\n): Promise<Brand> {\n const configPath = join(target, \"src\", \"brand.config.ts\");\n if (!(await exists(configPath))) {\n return { ...CARRIER_BRAND, ...overrides };\n }\n const src = await readFile(configPath, \"utf8\");\n const pick = (field: string, fallback: string): string => {\n const m = src.match(new RegExp(`\\\\b${field}:\\\\s*(?:[^\"\\\\n]*\\\\?\\\\?\\\\s*)?\"([^\"]*)\"`));\n return m?.[1] ?? fallback;\n };\n return {\n ...CARRIER_BRAND,\n name: overrides?.name ?? pick(\"name\", CARRIER_BRAND.name),\n domain: pick(\"domain\", CARRIER_BRAND.domain),\n supportEmail: pick(\"supportEmail\", CARRIER_BRAND.supportEmail),\n supportUrl: pick(\"supportUrl\", CARRIER_BRAND.supportUrl),\n tagline: pick(\"tagline\", CARRIER_BRAND.tagline),\n legalName: pick(\"legalName\", CARRIER_BRAND.legalName),\n colors: {\n ...CARRIER_BRAND.colors,\n accent: pick(\"accent\", CARRIER_BRAND.colors.accent),\n accentDark: pick(\"accentDark\", CARRIER_BRAND.colors.accentDark),\n bg: pick(\"bg\", CARRIER_BRAND.colors.bg),\n text: pick(\"text\", CARRIER_BRAND.colors.text),\n },\n carrierApiUrl: CARRIER_BRAND.carrierApiUrl,\n };\n}\n\n/**\n * Deploy the storefront to whichever target is available on this box.\n *\n * `preferred` forces one target and fails loudly if it is not ready, because a\n * silent fallback to a different host is not something a deploy should decide.\n * With no preference, the ranked list wins: a target the project is already\n * configured for, else Cloudflare, which is what the template is built for.\n *\n * Runtime secrets are pushed after a successful deploy — see `pushSecrets`. The\n * previous Cloudflare-only path never did this, which is why a deployed\n * storefront returned 500 on every page that reads the catalog.\n */\nexport async function deploySite(\n target: string,\n brand: Brand,\n opts: {\n preferred?: TargetId;\n pushSecrets?: boolean;\n env?: Record<string, string | undefined>;\n customDomain?: boolean;\n } = {},\n): Promise<DeploySiteResult> {\n const statuses = await probeAll(target);\n\n let chosen: TargetStatus | undefined;\n if (opts.preferred) {\n const wanted = statuses.find((s) => s.id === opts.preferred);\n if (!wanted?.ready) {\n return {\n ok: false,\n projectName: slug(brand.name),\n statuses,\n reason:\n wanted?.reason ??\n `${opts.preferred} is not available on this machine.`,\n };\n }\n chosen = wanted;\n } else {\n chosen = rankTargets(statuses)[0];\n }\n\n if (!chosen) {\n return {\n ok: false,\n projectName: slug(brand.name),\n statuses,\n reason:\n \"No deploy target is ready. \" +\n statuses.map((s) => `${s.label}: ${s.reason ?? \"unavailable\"}`).join(\" | \"),\n };\n }\n\n // The project's own config names the deployment. Falling back to the brand\n // slug would deploy a second Worker and leave the real one untouched.\n const projectName = (await configuredProjectName(chosen.id, target)) ?? slug(brand.name);\n\n // Cloudflare provisions DNS and the certificate itself when the route is\n // marked as a custom domain, so wire it before deploying.\n if (opts.customDomain && chosen.id === \"cloudflare\" && brand.domain) {\n await setCustomDomain(target, brand.domain);\n }\n\n // Secrets go in place BEFORE the code, so there is never a window where the\n // new deployment is live without them.\n let staged: StagedSecrets = { staged: [], failed: [], cleanup: async () => {} };\n if (opts.pushSecrets !== false) {\n const discovered = await discoverSecrets(target, opts.env ?? process.env);\n const runtime: Record<string, string> = {};\n for (const key of RUNTIME_SECRET_KEYS) {\n const value = discovered[key];\n if (value) runtime[key] = value;\n }\n staged = await stageSecrets(chosen.id, target, projectName, runtime);\n }\n\n try {\n const outcome = await deployTo(chosen.id, target, projectName, {\n secretsFile: staged.secretsFile,\n });\n if (!outcome.ok) {\n return { ok: false, projectName, statuses, target: chosen.id, reason: outcome.reason };\n }\n return {\n ok: true,\n projectName,\n statuses,\n target: chosen.id,\n url: outcome.url,\n secrets: { pushed: staged.staged, failed: staged.failed },\n };\n } finally {\n await staged.cleanup();\n }\n}\n\nexport interface DeploySiteResult {\n ok: boolean;\n projectName: string;\n statuses: TargetStatus[];\n target?: TargetId;\n url?: string;\n reason?: string;\n secrets?: { pushed: string[]; failed: string[] };\n}\n\n/**\n * Push the server-side secrets a deployed storefront needs.\n *\n * Only keys that actually resolve to a value are pushed; a missing key is left\n * to the caller to report, because inventing one would be worse than a clear gap.\n */\nexport async function pushSecrets(\n targetId: TargetId,\n storefront: string,\n projectName: string,\n env: Record<string, string | undefined> = process.env,\n): Promise<{ pushed: string[]; failed: string[] }> {\n const secrets = await discoverSecrets(storefront, env);\n const pushed: string[] = [];\n const failed: string[] = [];\n for (const key of RUNTIME_SECRET_KEYS) {\n const value = secrets[key];\n if (!value) continue;\n const ok = await putSecret(targetId, storefront, projectName, key, value);\n (ok ? pushed : failed).push(key);\n }\n return { pushed, failed };\n}\n\nexport { slug };\n","/**\n * Clerk provisioning for a scaffolded storefront.\n *\n * Creating a Clerk application is only possible through the Platform API, whose\n * `platform_api_access_token` is issued to partners/resellers — it is not\n * self-serve. So provisioning runs in three tiers, best first:\n *\n * 1. `platform` — CLERK_PLATFORM_API_KEY present: create a real application\n * and read its instance keys back. Fully automatic.\n * 2. `discovered` — reuse a pk_/sk_ pair already on the box (env or .env).\n * Automatic, but reuses an existing instance.\n * 3. `manual` — nothing to go on: return the exact dashboard steps.\n *\n * Every tier ends at the same place: a publishable/secret pair that the caller\n * writes into `.env.local` and pushes to the deploy target. Instance settings\n * (allowed origins, redirect URLs) are then applied over the Backend API, which\n * works with an ordinary secret key regardless of which tier produced it.\n *\n * Endpoints below are taken from Clerk's published OpenAPI specs\n * (github.com/clerk/openapi-specs): `platform/beta.yml` and `bapi/2026-05-12.yml`.\n */\n\nconst CLERK_API_BASE = \"https://api.clerk.com/v1\";\n\nexport type ClerkTier = \"platform\" | \"cli-keyless\" | \"discovered\" | \"manual\";\n\nexport interface ClerkCredentials {\n publishableKey: string;\n secretKey: string;\n tier: ClerkTier;\n applicationId?: string;\n instanceId?: string;\n}\n\nexport interface ClerkProvisionResult {\n ok: boolean;\n tier: ClerkTier;\n credentials?: ClerkCredentials;\n /** Why the tier failed, or why we fell through to a lower one. */\n reason?: string;\n /** Operator-facing steps, populated for the `manual` tier. */\n guidance?: string[];\n}\n\nexport interface ClerkConfigureResult {\n ok: boolean;\n applied: string[];\n failed: Array<{ step: string; reason: string }>;\n}\n\ntype Env = Record<string, string | undefined>;\n\nconst PUBLISHABLE_KEYS = [\n \"CLERK_PUBLISHABLE_KEY\",\n \"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY\",\n] as const;\nconst SECRET_KEYS = [\"CLERK_SECRET_KEY\"] as const;\n\nfunction firstNonEmpty(env: Env, names: readonly string[]): string | undefined {\n for (const name of names) {\n const value = env[name]?.trim();\n if (value) return value;\n }\n return undefined;\n}\n\n/** Shape check only — never proves the key is live. */\nexport function looksPublishable(key: string): boolean {\n return /^pk_(test|live)_/.test(key);\n}\n\nexport function looksSecret(key: string): boolean {\n return /^sk_(test|live)_/.test(key);\n}\n\nasync function clerkFetch(\n path: string,\n token: string,\n init: { method?: string; body?: unknown } = {},\n): Promise<{ ok: boolean; status: number; json?: unknown; error?: string }> {\n try {\n const res = await fetch(`${CLERK_API_BASE}${path}`, {\n method: init.method ?? \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n });\n const text = await res.text();\n let json: unknown;\n try {\n json = text ? JSON.parse(text) : undefined;\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n return { ok: false, status: res.status, json, error: clerkError(json) ?? text.slice(0, 300) };\n }\n return { ok: true, status: res.status, json };\n } catch (e: unknown) {\n return { ok: false, status: 0, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** Clerk returns `{ errors: [{ message, long_message }] }` on failure. */\nfunction clerkError(json: unknown): string | undefined {\n if (typeof json !== \"object\" || json === null) return undefined;\n const errors = (json as { errors?: unknown }).errors;\n if (!Array.isArray(errors) || errors.length === 0) return undefined;\n const first = errors[0] as { message?: string; long_message?: string };\n return first.long_message ?? first.message;\n}\n\ninterface PlatformInstance {\n instance_id: string;\n environment_type: \"development\" | \"production\";\n secret_key?: string;\n publishable_key?: string;\n}\n\n/**\n * Tier 1 — create a real Clerk application via the Platform API.\n *\n * `POST /platform/applications` returns the application with its instances,\n * each carrying `publishable_key` / `secret_key`.\n */\nexport async function createClerkApplication(\n platformToken: string,\n opts: { name: string; domain?: string; production?: boolean },\n): Promise<ClerkProvisionResult> {\n const body: Record<string, unknown> = {\n name: opts.name,\n environment_types: opts.production ? [\"development\", \"production\"] : [\"development\"],\n };\n if (opts.domain) body.domain = opts.domain;\n\n const res = await clerkFetch(\"/platform/applications\", platformToken, {\n method: \"POST\",\n body,\n });\n if (!res.ok) {\n return {\n ok: false,\n tier: \"platform\",\n reason:\n res.status === 401 || res.status === 403\n ? `Clerk Platform API rejected the token (HTTP ${res.status}). Check CLERK_PLATFORM_API_KEY.`\n : `Clerk Platform API error: ${res.error ?? `HTTP ${res.status}`}`,\n };\n }\n\n const payload = res.json as\n | { application_id?: string; instances?: PlatformInstance[] }\n | undefined;\n const instances = payload?.instances ?? [];\n // Prefer production when it was requested and came back with keys.\n const wanted = opts.production ? \"production\" : \"development\";\n const instance =\n instances.find((i) => i.environment_type === wanted && i.secret_key && i.publishable_key) ??\n instances.find((i) => i.secret_key && i.publishable_key);\n\n if (!instance?.secret_key || !instance.publishable_key) {\n return {\n ok: false,\n tier: \"platform\",\n reason:\n \"Clerk created the application but returned no instance keys. Read them with \" +\n \"GET /platform/applications?include_secret_keys=true.\",\n };\n }\n\n return {\n ok: true,\n tier: \"platform\",\n credentials: {\n publishableKey: instance.publishable_key,\n secretKey: instance.secret_key,\n tier: \"platform\",\n applicationId: payload?.application_id,\n instanceId: instance.instance_id,\n },\n };\n}\n\n/**\n * Tier 2 — mint a keyless application with the Clerk CLI.\n *\n * `clerk init` run while signed out provisions a claimable application and\n * writes real dev keys, with no account and no credential of any kind. That is\n * the only way to create a Clerk application without a partner token: the\n * published spec exposes `/platform/accountless_applications/claim` but no\n * corresponding create endpoint, so there is nothing to call directly.\n *\n * The CLI also rewrites source. Verified against this template: it wraps\n * `layout.tsx` in a second `<ClerkProvider>` even though `Providers.tsx`\n * already mounts one with our appearance config, reindents the file, and drops\n * the trailing newline. So everything it touches is snapshotted and restored;\n * only `.env.local` is kept. `--no-skills` suppresses a global agent-skills\n * install that otherwise lands outside the project entirely.\n */\nexport async function mintKeylessClerkApp(\n storefront: string,\n deps: ClerkCliDeps,\n): Promise<ClerkProvisionResult> {\n if (!(await deps.which(\"npx\"))) {\n return { ok: false, tier: \"cli-keyless\", reason: \"npx not found — cannot run the Clerk CLI.\" };\n }\n\n const restore = await deps.snapshot(storefront);\n try {\n const r = await deps.run(\n \"npx\",\n [\"--yes\", \"clerk@latest\", \"init\", \"--framework\", \"next\", \"--keyless\", \"--no-skills\", \"-y\"],\n { cwd: storefront, timeoutMs: 600_000 },\n );\n // Read the keys before restoring, since .env.local is what we are keeping.\n const envPath = `${storefront}/.env.local`;\n const body = (await deps.readFileIfExists(envPath)) ?? \"\";\n const publishableKey = matchEnv(body, \"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY\");\n const secretKey = matchEnv(body, \"CLERK_SECRET_KEY\");\n\n if (!publishableKey || !secretKey) {\n const tail = `${r.stdout}${r.stderr}`.trim().split(\"\\n\").slice(-2).join(\" \").slice(0, 300);\n return {\n ok: false,\n tier: \"cli-keyless\",\n reason: `Clerk CLI did not produce keys${tail ? `: ${tail}` : \".\"}`,\n };\n }\n if (!looksPublishable(publishableKey) || !looksSecret(secretKey)) {\n return { ok: false, tier: \"cli-keyless\", reason: \"Clerk CLI wrote keys in an unexpected format.\" };\n }\n\n return {\n ok: true,\n tier: \"cli-keyless\",\n credentials: { publishableKey, secretKey, tier: \"cli-keyless\" },\n };\n } finally {\n // Undo every source edit the CLI made; keep only .env.local.\n await restore();\n }\n}\n\n/**\n * Pull production keys for an already-linked application.\n *\n * A keyless or development instance is not something to point a real storefront\n * at, so this is opt-in and separate. It needs an authenticated CLI session\n * (`clerk auth login`) and a linked app — the keys land in `.env.local`.\n */\nexport async function pullProductionKeys(\n storefront: string,\n deps: ClerkCliDeps,\n): Promise<ClerkProvisionResult> {\n if (!(await deps.which(\"npx\"))) {\n return { ok: false, tier: \"platform\", reason: \"npx not found — cannot run the Clerk CLI.\" };\n }\n const r = await deps.run(\"npx\", [\"--yes\", \"clerk@latest\", \"env\", \"pull\", \"--instance\", \"prod\"], {\n cwd: storefront,\n timeoutMs: 300_000,\n });\n if (!r.ok) {\n const tail = `${r.stdout}${r.stderr}`.trim().split(\"\\n\").slice(-2).join(\" \").slice(0, 300);\n return {\n ok: false,\n tier: \"platform\",\n reason: `Could not pull production keys${tail ? `: ${tail}` : \".\"} Run \\`clerk auth login\\` and link the app first.`,\n };\n }\n const body = (await deps.readFileIfExists(`${storefront}/.env.local`)) ?? \"\";\n const publishableKey = matchEnv(body, \"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY\");\n const secretKey = matchEnv(body, \"CLERK_SECRET_KEY\");\n if (!publishableKey || !secretKey) {\n return { ok: false, tier: \"platform\", reason: \"Clerk CLI reported success but wrote no keys.\" };\n }\n return {\n ok: true,\n tier: \"platform\",\n credentials: { publishableKey, secretKey, tier: \"platform\" },\n };\n}\n\nfunction matchEnv(body: string, key: string): string | undefined {\n const line = body.split(\"\\n\").find((l) => l.trim().startsWith(`${key}=`));\n const value = line?.slice(line.indexOf(\"=\") + 1).trim();\n return value || undefined;\n}\n\n/**\n * The bits of the outside world `mintKeylessClerkApp` needs.\n *\n * Injected rather than imported so the tier is testable without spawning a real\n * CLI or writing to a real storefront.\n */\nexport interface ClerkCliDeps {\n which(bin: string): Promise<boolean>;\n run(\n cmd: string,\n args: string[],\n opts: { cwd?: string; timeoutMs?: number },\n ): Promise<{ ok: boolean; stdout: string; stderr: string }>;\n readFileIfExists(path: string): Promise<string | undefined>;\n /** Snapshot everything the CLI may rewrite; returns the restore function. */\n snapshot(storefront: string): Promise<() => Promise<void>>;\n}\n\n/** Tier 3 — a usable pk_/sk_ pair already present in the environment. */\nexport function discoverClerkCredentials(env: Env): ClerkCredentials | undefined {\n const publishableKey = firstNonEmpty(env, PUBLISHABLE_KEYS);\n const secretKey = firstNonEmpty(env, SECRET_KEYS);\n if (!publishableKey || !secretKey) return undefined;\n if (!looksPublishable(publishableKey) || !looksSecret(secretKey)) return undefined;\n return { publishableKey, secretKey, tier: \"discovered\" };\n}\n\nconst MANUAL_STEPS = [\n \"Open https://dashboard.clerk.com and create an application.\",\n \"Copy the Publishable key (pk_...) and Secret key (sk_...) from API keys.\",\n \"Put them in the storefront's .env.local as NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY.\",\n \"Re-run `carrier site deploy` — the keys are pushed to the deploy target automatically.\",\n];\n\n/**\n * Run the tiers in order and return the first that produces credentials.\n *\n * Never throws: a failed higher tier degrades to the next one, carrying its\n * reason forward so the operator can see why the better path did not run.\n */\nexport async function provisionClerk(opts: {\n name: string;\n domain?: string;\n production?: boolean;\n env: Env;\n /** Skip every creating tier; only reuse credentials that already exist. */\n noCreate?: boolean;\n /** Storefront directory — required for the keyless tier. */\n storefront?: string;\n /** Omit to disable the keyless tier (it needs to run a CLI). */\n cli?: ClerkCliDeps;\n}): Promise<ClerkProvisionResult> {\n const platformToken = firstNonEmpty(opts.env, [\n \"CLERK_PLATFORM_API_KEY\",\n \"CLERK_PLATFORM_TOKEN\",\n ]);\n\n const notes: string[] = [];\n\n if (platformToken && !opts.noCreate) {\n const created = await createClerkApplication(platformToken, {\n name: opts.name,\n domain: opts.domain,\n production: opts.production,\n });\n if (created.ok) return created;\n notes.push(created.reason ?? \"Clerk Platform API call failed.\");\n }\n\n // Reusing keys that already exist beats minting a throwaway app, so the\n // keyless tier only runs when there is nothing to reuse.\n const discovered = discoverClerkCredentials(opts.env);\n if (discovered) {\n return {\n ok: true,\n tier: \"discovered\",\n credentials: discovered,\n reason: notes.length ? notes.join(\" \") : undefined,\n };\n }\n\n if (opts.cli && opts.storefront && !opts.noCreate) {\n const minted = await mintKeylessClerkApp(opts.storefront, opts.cli);\n if (minted.ok) {\n return { ...minted, reason: notes.length ? notes.join(\" \") : undefined };\n }\n notes.push(minted.reason ?? \"Clerk CLI keyless provisioning failed.\");\n }\n\n if (!platformToken) {\n notes.push(\n \"No CLERK_PLATFORM_API_KEY set, so a new Clerk application cannot be created through the \" +\n \"Platform API (it is a partner surface, not self-serve).\",\n );\n }\n\n return {\n ok: false,\n tier: \"manual\",\n reason: notes.join(\" \"),\n guidance: MANUAL_STEPS,\n };\n}\n\n/**\n * Apply instance settings over the Backend API.\n *\n * Works with any secret key, whichever tier produced it:\n * PATCH /instance — allowed_origins\n * POST /redirect_urls — one call per URL\n *\n * Partial success is normal (a URL may already be whitelisted), so each step is\n * reported separately rather than collapsing to a single boolean.\n */\nexport async function configureClerkInstance(\n secretKey: string,\n opts: { allowedOrigins?: string[]; redirectUrls?: string[] },\n): Promise<ClerkConfigureResult> {\n const applied: string[] = [];\n const failed: Array<{ step: string; reason: string }> = [];\n\n const origins = dedupe(opts.allowedOrigins ?? []);\n if (origins.length > 0) {\n const res = await clerkFetch(\"/instance\", secretKey, {\n method: \"PATCH\",\n body: { allowed_origins: origins },\n });\n if (res.ok) applied.push(`allowed_origins (${origins.length})`);\n else failed.push({ step: \"allowed_origins\", reason: res.error ?? `HTTP ${res.status}` });\n }\n\n for (const url of dedupe(opts.redirectUrls ?? [])) {\n const res = await clerkFetch(\"/redirect_urls\", secretKey, { method: \"POST\", body: { url } });\n if (res.ok) applied.push(`redirect_url ${url}`);\n else failed.push({ step: `redirect_url ${url}`, reason: res.error ?? `HTTP ${res.status}` });\n }\n\n return { ok: failed.length === 0, applied, failed };\n}\n\nfunction dedupe(values: string[]): string[] {\n return [...new Set(values.map((v) => v.trim()).filter(Boolean))];\n}\n\n/** Origins and redirect URLs a storefront needs, given where it ended up. */\nexport function storefrontClerkUrls(deployedUrl?: string, domain?: string): {\n allowedOrigins: string[];\n redirectUrls: string[];\n} {\n const bases = dedupe([\n deployedUrl?.replace(/\\/$/, \"\") ?? \"\",\n domain ? `https://${domain.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\")}` : \"\",\n \"http://localhost:3000\",\n ]);\n return {\n allowedOrigins: bases,\n redirectUrls: bases.flatMap((b) => [b, `${b}/checkout/success`, `${b}/dashboard`]),\n };\n}\n\nexport { MANUAL_STEPS };\n","import { join } from \"node:path\";\nimport { cp, mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { exists, readFile } from \"./fsx.js\";\nimport { run, which } from \"./exec.js\";\nimport type { ClerkCliDeps } from \"./clerk.js\";\n\n/**\n * Real wiring for the Clerk CLI tier.\n *\n * The snapshot is the important part. `clerk init` edits application source —\n * verified against this template, it wraps `layout.tsx` in a second\n * `<ClerkProvider>` on top of the one `Providers.tsx` already mounts. Two\n * nested providers is not a harmless duplicate: the outer one carries no\n * appearance config, so brand theming silently changes.\n *\n * So: copy the directories the CLI is known to touch, run it, then put them\n * back. `.env.local` is what we actually want and lives outside the snapshot,\n * so it survives.\n */\n\n/** Directories the CLI may rewrite. Cheap to copy; excludes node_modules. */\nconst SNAPSHOT_PATHS = [\"src\", \"package.json\", \"next.config.mjs\", \"middleware.ts\"];\n\nexport function clerkCliDeps(): ClerkCliDeps {\n return {\n which,\n run: async (cmd, args, opts) => {\n const r = await run(cmd, args, opts);\n return { ok: r.ok, stdout: r.stdout, stderr: r.stderr };\n },\n readFileIfExists: async (path) => {\n if (!(await exists(path))) return undefined;\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return undefined;\n }\n },\n snapshot: async (storefront: string) => {\n const backup = await mkdtemp(join(tmpdir(), \"carrier-clerk-snap-\"));\n const saved: string[] = [];\n for (const rel of SNAPSHOT_PATHS) {\n const src = join(storefront, rel);\n if (!(await exists(src))) continue;\n await cp(src, join(backup, rel), { recursive: true });\n saved.push(rel);\n }\n return async () => {\n try {\n for (const rel of saved) {\n const target = join(storefront, rel);\n await rm(target, { recursive: true, force: true });\n await cp(join(backup, rel), target, { recursive: true });\n }\n } finally {\n await rm(backup, { recursive: true, force: true });\n }\n };\n },\n };\n}\n","import { discoverSecrets, needsCarrierKey, type SecretMap } from \"./storefront-secrets.js\";\n\n/**\n * Does the deployed storefront actually serve?\n *\n * Nothing asked this before, which is the whole reason a storefront could build\n * green, deploy green, and return 500 on every catalog page. A deploy that is\n * not verified is not finished.\n *\n * Deliberately tiny — a handful of unconditional HTTP assertions covering the\n * paths a working storefront cannot fail. It is a smoke test, not a test suite.\n */\n\nexport interface ProbeResult {\n path: string;\n status: number;\n ok: boolean;\n /** Set when the body was checked for content, not just status. */\n bodyOk?: boolean;\n error?: string;\n}\n\nexport type Diagnosis =\n | \"carrier-key-missing\"\n | \"clerk-keys-missing\"\n | \"empty-catalog\"\n | \"not-deployed\"\n | \"unclassified\"\n | \"healthy\";\n\nexport interface VerifyResult {\n ok: boolean;\n url: string;\n probes: ProbeResult[];\n diagnosis: Diagnosis;\n /** One line an operator can act on. */\n summary: string;\n /** Whether a deterministic repair exists for this diagnosis. */\n repairable: boolean;\n}\n\n/** Paths a working storefront cannot fail, plus what to look for in the body. */\nconst PROBES: Array<{ path: string; expectBody?: RegExp }> = [\n { path: \"/\" },\n { path: \"/shop\", expectBody: /·/ }, // plan names render as \"Visit · Calm\"\n { path: \"/help\" },\n];\n\nasync function probe(base: string, path: string, expectBody?: RegExp): Promise<ProbeResult> {\n // Cache-bust: an edge-cached 200 would hide a broken origin, and an edge-cached\n // 500 would survive the very repair we just made.\n const url = `${base.replace(/\\/$/, \"\")}${path}${path.includes(\"?\") ? \"&\" : \"?\"}_v=${Date.now()}`;\n try {\n const res = await fetch(url, { redirect: \"manual\" });\n const status = res.status;\n // 3xx on an auth-gated path is a working app, not a failure.\n const ok = status < 400;\n if (!expectBody || !ok) return { path, status, ok };\n const body = await res.text();\n return { path, status, ok, bodyOk: expectBody.test(body) };\n } catch (e: unknown) {\n return { path, status: 0, ok: false, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/**\n * Classify a failing probe set.\n *\n * The mapping is deliberately narrow. Each row corresponds to a failure that has\n * actually happened; anything else is reported as `unclassified` rather than\n * guessed at, because a confident wrong diagnosis triggers a wrong repair.\n */\nexport function diagnose(probes: ProbeResult[], secrets: SecretMap): Diagnosis {\n const by = (p: string) => probes.find((x) => x.path === p);\n const root = by(\"/\");\n const shop = by(\"/shop\");\n const help = by(\"/help\");\n\n if (probes.every((p) => p.ok && p.bodyOk !== false)) return \"healthy\";\n\n // Nothing resolved at all — wrong URL, or the deploy never landed.\n if (probes.every((p) => p.status === 0)) return \"not-deployed\";\n\n // The signature of today's outage: catalog pages 500 while a page that does\n // not read the catalog is fine.\n const catalogDown = root?.status === 500 && shop?.status === 500;\n if (catalogDown && help?.ok) {\n return needsCarrierKey(secrets) ? \"carrier-key-missing\" : \"unclassified\";\n }\n\n // Shop renders but no plan names — upstream returned nothing sellable.\n if (shop?.ok && shop.bodyOk === false) return \"empty-catalog\";\n\n // Auth-only breakage: public pages fine, protected/auth routes erroring.\n if (root?.ok && shop?.ok && probes.some((p) => p.status >= 500)) return \"clerk-keys-missing\";\n\n if (probes.every((p) => p.status === 404)) return \"not-deployed\";\n\n return \"unclassified\";\n}\n\nconst SUMMARIES: Record<Diagnosis, string> = {\n healthy: \"All probes returned a working page.\",\n \"carrier-key-missing\":\n \"Catalog pages return 500 while non-catalog pages work — CARRIER_API_KEY is missing or invalid on the host.\",\n \"clerk-keys-missing\": \"Public pages work but auth routes error — Clerk keys are missing on the host.\",\n \"empty-catalog\":\n \"/shop renders but no plans are in it — the Carrier catalog returned nothing sellable. Not a deploy problem.\",\n \"not-deployed\": \"Nothing served at the deployed URL — the deploy did not land, or the URL is wrong.\",\n unclassified: \"The storefront is not serving correctly and the symptom matches no known cause.\",\n};\n\n/** Only these have a deterministic fix worth attempting. */\nconst REPAIRABLE: ReadonlySet<Diagnosis> = new Set<Diagnosis>([\n \"carrier-key-missing\",\n \"clerk-keys-missing\",\n]);\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Probe a deployed storefront and classify the result.\n *\n * A hostname that has just been created can 404 for a few seconds before it\n * routes, so `not-deployed` is retried rather than believed first time — a\n * false \"the deploy did not land\" is worse than waiting. Real failures\n * (500s, an empty catalog) are conclusions, not races, so they return at once.\n */\nexport async function verifyStorefront(\n url: string,\n storefront: string,\n env: Record<string, string | undefined> = process.env,\n opts: { attempts?: number; delayMs?: number } = {},\n): Promise<VerifyResult> {\n const attempts = Math.max(1, opts.attempts ?? 4);\n const delayMs = opts.delayMs ?? 5_000;\n const secrets = await discoverSecrets(storefront, env);\n\n let probes: ProbeResult[] = [];\n let diagnosis: Diagnosis = \"unclassified\";\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n probes = [];\n for (const { path, expectBody } of PROBES) {\n probes.push(await probe(url, path, expectBody));\n }\n diagnosis = diagnose(probes, secrets);\n if (diagnosis !== \"not-deployed\" || attempt === attempts) break;\n await sleep(delayMs);\n }\n\n const ok = diagnosis === \"healthy\";\n return {\n ok,\n url,\n probes,\n diagnosis,\n summary: SUMMARIES[diagnosis],\n repairable: !ok && REPAIRABLE.has(diagnosis),\n };\n}\n\n/** Human-readable probe line, e.g. \"/shop 500\". */\nexport function formatProbes(probes: ProbeResult[]): string {\n return probes\n .map((p) => {\n const status = p.status === 0 ? \"unreachable\" : String(p.status);\n const body = p.bodyOk === false ? \" (no plans)\" : \"\";\n return `${p.path} ${status}${body}`;\n })\n .join(\" \");\n}\n\n/**\n * What a repair for this diagnosis needs to do.\n *\n * Returned as a description rather than executed here, so the caller owns the\n * rebuild/redeploy cycle and this module stays free of process spawning.\n */\nexport interface RepairPlan {\n diagnosis: Diagnosis;\n /** Re-run Clerk provisioning before rebuilding. */\n runClerk: boolean;\n /** A rebuild is required because a NEXT_PUBLIC_* value is inlined. */\n rebuild: boolean;\n note: string;\n}\n\nexport function repairPlanFor(diagnosis: Diagnosis): RepairPlan | undefined {\n if (diagnosis === \"carrier-key-missing\") {\n return {\n diagnosis,\n runClerk: false,\n // CARRIER_API_KEY is read at request time, so re-staging and redeploying\n // is enough; no rebuild needed.\n rebuild: false,\n note: \"Re-resolve CARRIER_API_KEY, stage it, and redeploy.\",\n };\n }\n if (diagnosis === \"clerk-keys-missing\") {\n return {\n diagnosis,\n runClerk: true,\n // The publishable key is inlined at build time, so this one must rebuild.\n rebuild: true,\n note: \"Provision Clerk, rebuild so the publishable key is inlined, and redeploy.\",\n };\n }\n return undefined;\n}\n\nexport { REPAIRABLE };\n","/**\n * The screen model.\n *\n * A screen is declared once and rendered twice: as an HTML panel for an MCP App\n * (`ui://` resource) and as a TUI screen in the terminal. Neither renderer may\n * add data of its own — if a value is not in the model, it does not appear on\n * either surface. That is what keeps the two honest about showing the same\n * thing, which the previous hand-rolled panels did not: `fleet_health_app`\n * served an empty dashboard for months while `fleet_health` reported a full\n * fleet, because they were two separate implementations of the same report.\n *\n * Sections are deliberately few and boring. A screen is a status surface, not a\n * layout engine — anything that cannot be said with a metric, a table, a bar, a\n * key/value list or a note probably should not be on a dashboard.\n */\n\nexport type Tone = \"ok\" | \"info\" | \"warn\" | \"critical\" | \"muted\";\n\n/** A single headline number. */\nexport interface Metric {\n label: string;\n value: string | number;\n /** Small qualifier under the value, e.g. \"of 100\" or \"+3 today\". */\n hint?: string;\n tone?: Tone;\n}\n\nexport interface Bar {\n label: string;\n value: number;\n /** Defaults to the largest value in the group. */\n max?: number;\n hint?: string;\n tone?: Tone;\n}\n\nexport interface KeyValue {\n label: string;\n value: string;\n tone?: Tone;\n}\n\nexport type Cell = string | number;\n\nexport interface TableSection {\n kind: \"table\";\n title?: string;\n columns: string[];\n rows: Cell[][];\n /** Shown when `rows` is empty — say why, never render a bare empty table. */\n empty?: string;\n /** Column indices to right-align (numbers). */\n numeric?: number[];\n}\n\nexport type Section =\n | { kind: \"metrics\"; title?: string; items: Metric[] }\n | { kind: \"bars\"; title?: string; items: Bar[]; empty?: string }\n | { kind: \"keyvalue\"; title?: string; items: KeyValue[] }\n | { kind: \"note\"; tone: Tone; text: string; title?: string }\n | TableSection;\n\n/** A suggested follow-up, rendered as a button in HTML and a hint line in TUI. */\nexport interface Action {\n label: string;\n /** The tool or CLI command this action maps to. */\n command: string;\n description?: string;\n}\n\nexport interface Screen {\n /** Stable id — also the ui:// resource slug. */\n id: string;\n title: string;\n subtitle?: string;\n sections: Section[];\n actions?: Action[];\n /** Rendered in the footer; set by the caller, never by a renderer. */\n footer?: string;\n}\n\n/** A metric that conveys nothing: zero, a dash, or blank. */\nfunction isBlankValue(value: string | number): boolean {\n const text = String(value).trim();\n return text === \"\" || text === \"—\" || text === \"-\" || Number(text) === 0;\n}\n\n/**\n * True when a screen carries no actual data.\n *\n * Judged on values, not structure. A grid of six metric cards all reading `0`\n * is structurally full and informationally empty, and it is exactly the shape\n * that hid the fleet bug for months: the panel looked like a working dashboard\n * reporting a quiet fleet, when in fact no data had arrived at all.\n *\n * A `note` always counts as content — an error or warning is real information,\n * and must never be replaced by the generic empty state.\n */\nexport function isEmptyScreen(screen: Screen): boolean {\n return screen.sections.every((section) => {\n switch (section.kind) {\n case \"metrics\":\n return section.items.every((m) => isBlankValue(m.value));\n case \"bars\":\n return section.items.every((b) => b.value === 0);\n case \"keyvalue\":\n return section.items.every((kv) => isBlankValue(kv.value));\n case \"table\":\n return section.rows.length === 0;\n case \"note\":\n return false;\n }\n });\n}\n\n/** Every metric across a screen, for tests and summaries. */\nexport function screenMetrics(screen: Screen): Metric[] {\n return screen.sections.flatMap((s) => (s.kind === \"metrics\" ? s.items : []));\n}\n","/**\n * Carrier design tokens — single source of truth.\n * Source: apps/landing-mcp/marketing/CARRIER-BRAND.md §7 Visual identity\n *\n * Surface elevation model (claude.ai-inspired, adapted for Carrier dark theme):\n * surface-0 = base page background #080C16\n * surface-1 = raised card / panel rgba(15, 20, 34, 0.80)\n * surface-2 = elevated popover rgba(22, 28, 46, 0.90)\n * surface-3 = overlay / sheet rgba(31, 38, 56, 0.95)\n * Elevation is expressed via alpha blending on the ink palette, not drop-shadows.\n */\n\nexport const colors = {\n /** Base page background — deepest ink */\n backgroundDark: \"#080C16\",\n backgroundDarkOklch: \"oklch(0.09 0.02 260)\",\n\n /** Surface elevation ladder — alpha blends, no drop-shadows */\n surface0: \"#080C16\",\n surface1: \"rgba(15, 20, 34, 0.80)\",\n surface2: \"rgba(22, 28, 46, 0.90)\",\n surface3: \"rgba(31, 38, 56, 0.95)\",\n\n /** Legacy alias — kept for backward compat */\n surfaceDark: \"#0F1422\",\n surfaceCard: \"rgba(15, 20, 34, 0.75)\",\n\n /** Borders */\n borderCard: \"#1F2638\",\n borderMuted: \"rgba(255, 255, 255, 0.07)\",\n borderSubtle: \"rgba(255, 255, 255, 0.04)\",\n\n /** Brand accent — Carrier Flame orange (do not replace with violet) */\n accentFlame: \"#FF6B35\",\n accentEmber: \"#D9461C\",\n accentSpark: \"#FFB088\",\n\n /** Legacy violet/fuchsia — used on esimmcp co-brand surface only */\n accentViolet: \"#a78bfa\",\n accentFuchsia: \"#e879f9\",\n\n /** Text hierarchy — tight ratio, generous contrast */\n textPrimary: \"#F5F1EA\",\n textSecondary: \"#C9CCD6\",\n textMuted: \"#8A92A8\",\n textFaint: \"#5A6278\",\n\n /** Status — do not deviate */\n statusSuccess: \"#10b981\",\n statusWarning: \"#f59e0b\",\n statusError: \"#ef4444\",\n\n /** Light mode equivalents */\n light: {\n background: \"#ffffff\",\n backgroundSecondary: \"#f8fafc\",\n surfaceCard: \"rgba(248, 250, 252, 0.9)\",\n borderCard: \"#e2e8f0\",\n textPrimary: \"#0f172a\",\n textSecondary: \"#475569\",\n textMuted: \"#94a3b8\",\n },\n} as const;\n\nexport const gradients = {\n /**\n * Primary CTA button gradient — flame orange, subtle, claude.ai-style.\n * Direction 160deg reads left-to-right on wide buttons without feeling gaudy.\n */\n buttonPrimary: \"linear-gradient(160deg, #FF7A45 0%, #FF6B35 55%, #E85D28 100%)\",\n buttonPrimaryHover: \"linear-gradient(160deg, #FF8555 0%, #FF7340 55%, #F06630 100%)\",\n\n /** Ghost button — ultra-subtle surface highlight */\n buttonGhost: \"linear-gradient(160deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)\",\n buttonGhostHover: \"linear-gradient(160deg, rgba(255,255,255,0.09) 0%, rgba(255,255,255,0.05) 100%)\",\n\n /** Co-brand accent (esimmcp violet→fuchsia) */\n accentLinear: \"linear-gradient(135deg, #a78bfa 0%, #e879f9 100%)\",\n accentLinearDeg: (deg: number) =>\n `linear-gradient(${deg}deg, #a78bfa 0%, #e879f9 100%)`,\n accentText: \"linear-gradient(135deg, #a78bfa 0%, #e879f9 100%)\",\n\n /** Flame text gradient */\n flameText: \"linear-gradient(135deg, #FF6B35 0%, #FFB088 100%)\",\n\n /** Subtle glow — flame tint */\n glowFlame: \"radial-gradient(ellipse at center, rgba(255, 107, 53, 0.15) 0%, transparent 70%)\",\n /** Subtle glow — violet (esimmcp) */\n glowViolet: \"radial-gradient(ellipse at center, rgba(167, 139, 250, 0.15) 0%, transparent 70%)\",\n} as const;\n\nexport const typography = {\n fontSans: \"var(--font-sans, Inter), system-ui, -apple-system, sans-serif\",\n fontMono: \"var(--font-mono, 'JetBrains Mono'), ui-monospace, monospace\",\n fontSerif: \"var(--font-serif, Georgia), 'Times New Roman', serif\",\n\n /** Weights — only these three, per brand guide */\n weightRegular: \"400\",\n weightBold: \"700\",\n weightBlack: \"900\",\n\n /** Line heights */\n lineHeightHeadline: \"1.05\",\n lineHeightSubheading: \"1.2\",\n lineHeightBody: \"1.5\",\n\n /** Letter spacing — tight on big headlines */\n trackingTight: \"-0.04em\",\n trackingNormal: \"0em\",\n trackingWide: \"0.05em\",\n\n /** Type scale (rem) */\n scale: {\n xs: \"0.75rem\",\n sm: \"0.875rem\",\n base: \"1rem\",\n lg: \"1.125rem\",\n xl: \"1.25rem\",\n \"2xl\": \"1.5rem\",\n \"3xl\": \"1.875rem\",\n \"4xl\": \"2.25rem\",\n \"5xl\": \"3rem\",\n \"6xl\": \"3.75rem\",\n \"7xl\": \"4.5rem\",\n },\n} as const;\n\nexport const spacing = {\n /** Section vertical padding — 80px / 112px */\n sectionSm: \"5rem\",\n sectionLg: \"7rem\",\n\n /** Content max-width */\n contentMax: \"72rem\",\n contentNarrow: \"48rem\",\n contentWide: \"90rem\",\n} as const;\n\nexport const radius = {\n sm: \"0.375rem\",\n md: \"0.5rem\",\n lg: \"0.75rem\",\n xl: \"1rem\",\n \"2xl\": \"1.5rem\",\n full: \"9999px\",\n} as const;\n\nexport const motion = {\n /** Duration */\n durationFast: \"150ms\",\n durationBase: \"250ms\",\n durationSlow: \"400ms\",\n durationSlower: \"700ms\",\n\n /** Easing */\n easingDefault: \"cubic-bezier(0.4, 0, 0.2, 1)\",\n easingIn: \"cubic-bezier(0.4, 0, 1, 1)\",\n easingOut: \"cubic-bezier(0, 0, 0.2, 1)\",\n easingSpring: \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n\n /** Common transitions */\n transitionBase: \"all 250ms cubic-bezier(0.4, 0, 0.2, 1)\",\n transitionFast: \"all 150ms cubic-bezier(0.4, 0, 0.2, 1)\",\n transitionColor: \"color 150ms cubic-bezier(0.4, 0, 0.2, 1), background-color 150ms cubic-bezier(0.4, 0, 0.2, 1), border-color 150ms cubic-bezier(0.4, 0, 0.2, 1)\",\n} as const;\n\nexport const shadows = {\n /**\n * Elevation expressed as glow, not drop-shadow — claude.ai principle.\n * No box-shadow: 0 4px 6px rgba(0,0,0,0.x) on interactive elements.\n */\n flameGlow: \"0 0 32px rgba(255, 107, 53, 0.18)\",\n flameGlowStrong: \"0 0 48px rgba(255, 107, 53, 0.28)\",\n cardGlow: \"0 0 40px rgba(255, 107, 53, 0.10)\",\n cardGlowStrong: \"0 0 60px rgba(255, 107, 53, 0.18)\",\n innerHighlight: \"inset 0 1px 0 rgba(255, 255, 255, 0.06)\",\n innerHighlightStrong: \"inset 0 1px 0 rgba(255, 255, 255, 0.12)\",\n\n /** Legacy violet glow (esimmcp surface) */\n cardGlowViolet: \"0 0 40px rgba(167, 139, 250, 0.12)\",\n} as const;\n\nexport const tokens = {\n colors,\n gradients,\n typography,\n spacing,\n radius,\n motion,\n shadows,\n} as const;\n\nexport type Tokens = typeof tokens;\n","import { colors, radius, typography } from \"@carrier/brand/tokens\";\nimport { isEmptyScreen, type Bar, type Screen, type Section, type Tone } from \"./model.js\";\n\n/**\n * Render a screen as a self-contained HTML panel for an MCP App.\n *\n * Self-contained is a hard requirement: the panel runs in a sandboxed iframe\n * with no network access, so every style is inline and there are no external\n * fonts, scripts or images. Colours come from `@carrier/brand` rather than\n * being retyped, which is the whole reason the previous hand-rolled panels\n * looked like three unrelated products.\n */\n\nconst TONE_COLOR: Record<Tone, string> = {\n ok: colors.statusSuccess,\n info: colors.accentFlame,\n warn: colors.statusWarning,\n critical: colors.statusError,\n muted: colors.textMuted,\n};\n\n/** Escape text for HTML. Every value on a panel passes through here. */\nexport function esc(value: unknown): string {\n return String(value)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n// `fallback` is widened to string: the brand tokens are `as const`, so an\n// inferred default would narrow this to one literal hex and reject every other.\nfunction toneColor(tone: Tone | undefined, fallback: string = colors.textPrimary): string {\n return tone ? TONE_COLOR[tone] : fallback;\n}\n\nfunction metricsHtml(items: { label: string; value: string | number; hint?: string; tone?: Tone }[]): string {\n const cards = items\n .map(\n (m) => `\n <div style=\"background:${colors.surface1};border:1px solid ${colors.borderCard};border-radius:${radius.lg};padding:16px 18px;min-width:0\">\n <div style=\"font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:${colors.textMuted}\">${esc(m.label)}</div>\n <div style=\"font-size:28px;font-weight:600;margin-top:6px;color:${toneColor(m.tone)};line-height:1.1\">${esc(m.value)}</div>\n ${m.hint ? `<div style=\"font-size:12px;color:${colors.textFaint};margin-top:4px\">${esc(m.hint)}</div>` : \"\"}\n </div>`,\n )\n .join(\"\");\n return `<div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px\">${cards}</div>`;\n}\n\nfunction barsHtml(items: Bar[], empty?: string): string {\n if (items.length === 0) return emptyHtml(empty ?? \"Nothing to show.\");\n const ceiling = Math.max(...items.map((b) => b.max ?? b.value), 1);\n const rows = items\n .map((b) => {\n const pct = Math.max(0, Math.min(100, ((b.value / (b.max ?? ceiling)) || 0) * 100));\n return `\n <div style=\"margin-bottom:10px\">\n <div style=\"display:flex;justify-content:space-between;font-size:13px;color:${colors.textSecondary};margin-bottom:4px\">\n <span>${esc(b.label)}</span>\n <span style=\"color:${colors.textMuted}\">${esc(b.hint ?? b.value)}</span>\n </div>\n <div style=\"height:8px;background:${colors.surface2};border-radius:${radius.full};overflow:hidden\">\n <div style=\"height:100%;width:${pct.toFixed(1)}%;background:${toneColor(b.tone, colors.accentFlame)}\"></div>\n </div>\n </div>`;\n })\n .join(\"\");\n return `<div>${rows}</div>`;\n}\n\nfunction tableHtml(section: Extract<Section, { kind: \"table\" }>): string {\n if (section.rows.length === 0) return emptyHtml(section.empty ?? \"No rows.\");\n const numeric = new Set(section.numeric ?? []);\n const head = section.columns\n .map(\n (c, i) =>\n `<th style=\"text-align:${numeric.has(i) ? \"right\" : \"left\"};padding:8px 12px;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:${colors.textMuted};border-bottom:1px solid ${colors.borderCard};white-space:nowrap\">${esc(c)}</th>`,\n )\n .join(\"\");\n const body = section.rows\n .map(\n (row) =>\n `<tr>${row\n .map(\n (cell, i) =>\n `<td style=\"text-align:${numeric.has(i) ? \"right\" : \"left\"};padding:8px 12px;font-size:13px;color:${colors.textSecondary};border-bottom:1px solid ${colors.borderSubtle};white-space:nowrap\">${esc(cell)}</td>`,\n )\n .join(\"\")}</tr>`,\n )\n .join(\"\");\n // Wide tables scroll inside their own container; the page never scrolls sideways.\n return `<div style=\"overflow-x:auto\"><table style=\"width:100%;border-collapse:collapse\">${\n `<thead><tr>${head}</tr></thead>`\n }<tbody>${body}</tbody></table></div>`;\n}\n\nfunction keyValueHtml(items: { label: string; value: string; tone?: Tone }[]): string {\n const rows = items\n .map(\n (kv) => `\n <div style=\"display:flex;justify-content:space-between;gap:16px;padding:7px 0;border-bottom:1px solid ${colors.borderSubtle}\">\n <span style=\"font-size:13px;color:${colors.textMuted}\">${esc(kv.label)}</span>\n <span style=\"font-size:13px;color:${toneColor(kv.tone, colors.textPrimary)};text-align:right\">${esc(kv.value)}</span>\n </div>`,\n )\n .join(\"\");\n return `<div>${rows}</div>`;\n}\n\nfunction noteHtml(tone: Tone, text: string): string {\n const c = toneColor(tone, colors.accentFlame);\n return `<div style=\"border-left:3px solid ${c};background:${colors.surface1};padding:10px 14px;border-radius:${radius.md};font-size:13px;color:${colors.textSecondary}\">${esc(text)}</div>`;\n}\n\nfunction emptyHtml(text: string): string {\n return `<div style=\"padding:14px;border:1px dashed ${colors.borderCard};border-radius:${radius.md};font-size:13px;color:${colors.textMuted}\">${esc(text)}</div>`;\n}\n\nfunction sectionHtml(section: Section): string {\n const title = \"title\" in section && section.title\n ? `<h2 style=\"font-size:13px;font-weight:600;letter-spacing:.04em;color:${colors.textPrimary};margin:0 0 10px\">${esc(section.title)}</h2>`\n : \"\";\n let body: string;\n switch (section.kind) {\n case \"metrics\":\n body = metricsHtml(section.items);\n break;\n case \"bars\":\n body = barsHtml(section.items, section.empty);\n break;\n case \"table\":\n body = tableHtml(section);\n break;\n case \"keyvalue\":\n body = keyValueHtml(section.items);\n break;\n case \"note\":\n body = noteHtml(section.tone, section.text);\n break;\n }\n return `<section style=\"margin-bottom:22px\">${title}${body}</section>`;\n}\n\n/** Render a complete, self-contained HTML document for a screen. */\nexport function renderHtml(screen: Screen): string {\n const sections = isEmptyScreen(screen)\n ? emptyHtml(\n \"No data came back for this view. That is a real result, not a loading state — check the account scope and credentials.\",\n )\n : screen.sections.map(sectionHtml).join(\"\");\n\n const actions = screen.actions?.length\n ? `<section style=\"margin-top:6px;display:flex;flex-wrap:wrap;gap:8px\">${screen.actions\n .map(\n (a) =>\n `<span title=\"${esc(a.description ?? a.command)}\" style=\"font-size:12px;color:${colors.textSecondary};background:${colors.surface2};border:1px solid ${colors.borderCard};border-radius:${radius.full};padding:6px 12px\">${esc(a.label)} <code style=\"color:${colors.textFaint}\">${esc(a.command)}</code></span>`,\n )\n .join(\"\")}</section>`\n : \"\";\n\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>${esc(screen.title)}</title></head>\n<body style=\"margin:0;background:${colors.surface0};color:${colors.textPrimary};font-family:${typography.fontSans};padding:20px\">\n <header style=\"margin-bottom:20px\">\n <h1 style=\"font-size:18px;font-weight:600;margin:0;letter-spacing:-.01em\">${esc(screen.title)}</h1>\n ${screen.subtitle ? `<p style=\"margin:4px 0 0;font-size:13px;color:${colors.textMuted}\">${esc(screen.subtitle)}</p>` : \"\"}\n </header>\n ${sections}\n ${actions}\n ${screen.footer ? `<footer style=\"margin-top:18px;font-size:11px;color:${colors.textFaint}\">${esc(screen.footer)}</footer>` : \"\"}\n</body></html>`;\n}\n","import { isEmptyScreen, type Bar, type Cell, type Screen, type Section, type Tone } from \"./model.js\";\n\n/**\n * Render a screen as terminal output.\n *\n * Deliberately dependency-free: colour is emitted as raw ANSI so this package\n * stays importable from a Worker bundle, where a terminal-colour library has no\n * business being. `color: false` produces plain text for pipes, CI and tests —\n * tests assert on content, never on escape codes.\n *\n * The layout is width-aware but never reflows text: a dashboard that rewraps\n * differently every run is impossible to diff between two runs, which is most\n * of what a terminal dashboard is for.\n */\n\nexport interface TuiOptions {\n /** Terminal width. Defaults to 80, clamped to a readable range. */\n width?: number;\n color?: boolean;\n}\n\nconst ANSI: Record<Tone | \"reset\" | \"bold\" | \"dim\", string> = {\n ok: \"\u001b[32m\",\n info: \"\u001b[38;5;209m\", // Carrier flame, nearest 256-colour\n warn: \"\u001b[33m\",\n critical: \"\u001b[31m\",\n muted: \"\u001b[90m\",\n reset: \"\u001b[0m\",\n bold: \"\u001b[1m\",\n dim: \"\u001b[2m\",\n};\n\nfunction paint(text: string, code: string, color: boolean): string {\n return color ? `${code}${text}${ANSI.reset}` : text;\n}\n\n/** Printable width, ignoring ANSI escapes. */\nexport function displayWidth(text: string): number {\n return text.replace(/\u001b\\[[0-9;]*m/g, \"\").length;\n}\n\nfunction pad(text: string, width: number, align: \"left\" | \"right\"): string {\n const gap = Math.max(0, width - displayWidth(text));\n return align === \"right\" ? \" \".repeat(gap) + text : text + \" \".repeat(gap);\n}\n\nfunction heading(text: string, color: boolean): string {\n return paint(text.toUpperCase(), ANSI.bold, color);\n}\n\nfunction metricsTui(\n items: { label: string; value: string | number; hint?: string; tone?: Tone }[],\n width: number,\n color: boolean,\n): string[] {\n if (items.length === 0) return [];\n // One metric per line: a grid that reflows with terminal width cannot be\n // diffed between runs, and these are meant to be watched.\n const labelWidth = Math.min(\n 28,\n Math.max(...items.map((m) => m.label.length)),\n );\n return items.map((m) => {\n const label = paint(pad(m.label, labelWidth, \"left\"), ANSI.muted, color);\n const value = paint(String(m.value), m.tone ? ANSI[m.tone] : ANSI.bold, color);\n const hint = m.hint ? paint(` ${m.hint}`, ANSI.dim, color) : \"\";\n return ` ${label} ${value}${hint}`.slice(0, width + 64);\n });\n}\n\nfunction barsTui(items: Bar[], width: number, color: boolean, empty?: string): string[] {\n if (items.length === 0) return [` ${paint(empty ?? \"Nothing to show.\", ANSI.muted, color)}`];\n const ceiling = Math.max(...items.map((b) => b.max ?? b.value), 1);\n const labelWidth = Math.min(24, Math.max(...items.map((b) => b.label.length)));\n const barWidth = Math.max(10, Math.min(40, width - labelWidth - 22));\n return items.map((b) => {\n const ratio = (b.value / (b.max ?? ceiling)) || 0;\n const filled = Math.max(0, Math.min(barWidth, Math.round(ratio * barWidth)));\n const bar = paint(\"█\".repeat(filled), b.tone ? ANSI[b.tone] : ANSI.info, color) +\n paint(\"░\".repeat(barWidth - filled), ANSI.dim, color);\n const label = paint(pad(b.label, labelWidth, \"left\"), ANSI.muted, color);\n const value = paint(String(b.hint ?? b.value), ANSI.dim, color);\n return ` ${label} ${bar} ${value}`;\n });\n}\n\nfunction tableTui(\n section: Extract<Section, { kind: \"table\" }>,\n width: number,\n color: boolean,\n): string[] {\n if (section.rows.length === 0) {\n return [` ${paint(section.empty ?? \"No rows.\", ANSI.muted, color)}`];\n }\n const numeric = new Set(section.numeric ?? []);\n const cols = section.columns.length;\n const widths = Array.from({ length: cols }, (_, i) =>\n Math.max(\n section.columns[i]?.length ?? 0,\n ...section.rows.map((r) => String(r[i] ?? \"\").length),\n ),\n );\n // Trim the widest column first when the table overflows, so narrow\n // identifier columns stay intact.\n let total = widths.reduce((a, b) => a + b + 2, 2);\n while (total > width && Math.max(...widths) > 8) {\n const widest = widths.indexOf(Math.max(...widths));\n widths[widest] -= 1;\n total -= 1;\n }\n\n const clip = (cell: Cell, i: number): string => {\n const text = String(cell ?? \"\");\n return text.length > widths[i] ? `${text.slice(0, Math.max(1, widths[i] - 1))}…` : text;\n };\n\n const header =\n \" \" +\n section.columns\n .map((c, i) => paint(pad(clip(c, i), widths[i], numeric.has(i) ? \"right\" : \"left\"), ANSI.muted, color))\n .join(\" \");\n const rule = \" \" + paint(widths.map((w) => \"─\".repeat(w)).join(\" \"), ANSI.dim, color);\n const body = section.rows.map(\n (row) =>\n \" \" +\n row\n .map((cell, i) => pad(clip(cell, i), widths[i], numeric.has(i) ? \"right\" : \"left\"))\n .join(\" \"),\n );\n return [header, rule, ...body];\n}\n\nfunction keyValueTui(\n items: { label: string; value: string; tone?: Tone }[],\n color: boolean,\n): string[] {\n if (items.length === 0) return [];\n const labelWidth = Math.min(30, Math.max(...items.map((kv) => kv.label.length)));\n return items.map(\n (kv) =>\n ` ${paint(pad(kv.label, labelWidth, \"left\"), ANSI.muted, color)} ${paint(kv.value, kv.tone ? ANSI[kv.tone] : ANSI.reset, color)}`,\n );\n}\n\nconst NOTE_PREFIX: Record<Tone, string> = {\n ok: \"ok\",\n info: \"note\",\n warn: \"warning\",\n critical: \"critical\",\n muted: \"note\",\n};\n\nfunction sectionTui(section: Section, width: number, color: boolean): string[] {\n const lines: string[] = [];\n if (\"title\" in section && section.title) lines.push(heading(section.title, color));\n switch (section.kind) {\n case \"metrics\":\n lines.push(...metricsTui(section.items, width, color));\n break;\n case \"bars\":\n lines.push(...barsTui(section.items, width, color, section.empty));\n break;\n case \"table\":\n lines.push(...tableTui(section, width, color));\n break;\n case \"keyvalue\":\n lines.push(...keyValueTui(section.items, color));\n break;\n case \"note\":\n lines.push(\n ` ${paint(`${NOTE_PREFIX[section.tone]}:`, ANSI[section.tone], color)} ${section.text}`,\n );\n break;\n }\n lines.push(\"\");\n return lines;\n}\n\n/** Render a screen as terminal text. */\nexport function renderTui(screen: Screen, opts: TuiOptions = {}): string {\n const width = Math.max(40, Math.min(160, opts.width ?? 80));\n const color = opts.color ?? true;\n\n const lines: string[] = [];\n lines.push(paint(screen.title, ANSI.bold, color));\n if (screen.subtitle) lines.push(paint(screen.subtitle, ANSI.muted, color));\n lines.push(paint(\"─\".repeat(width), ANSI.dim, color));\n lines.push(\"\");\n\n if (isEmptyScreen(screen)) {\n lines.push(\n ` ${paint(\"No data came back for this view.\", ANSI.warn, color)} That is a real result, not a`,\n \" loading state — check the account scope and credentials.\",\n \"\",\n );\n } else {\n for (const section of screen.sections) lines.push(...sectionTui(section, width, color));\n }\n\n if (screen.actions?.length) {\n lines.push(heading(\"next\", color));\n for (const a of screen.actions) {\n lines.push(` ${paint(a.command, ANSI.info, color)} ${paint(a.label, ANSI.dim, color)}`);\n }\n lines.push(\"\");\n }\n if (screen.footer) lines.push(paint(screen.footer, ANSI.dim, color));\n\n return lines.join(\"\\n\");\n}\n","import type { Screen, Section, Tone } from \"./model.js\";\n\n/**\n * Screen builders, one per tool domain.\n *\n * Every builder is pure: already-extracted data in, a `Screen` out. No fetching,\n * no unwrapping of API envelopes. That division is the point — `fleet_health_app`\n * broke precisely because it re-implemented extraction that a working tool\n * already did, and drifted. Callers pass the output of the proven extractors\n * (`extractEsimStatusCounts`, `extractUsageTimeline`, …); builders only shape it.\n */\n\nconst pct = (part: number, total: number): string =>\n total > 0 ? `${((part / total) * 100).toFixed(1)}%` : \"0%\";\n\nconst money = (value: number, currency = \"\"): string =>\n `${currency}${value.toFixed(2)}`.trim();\n\n/** Bytes → human units. Fleet data arrives in bytes and MB inconsistently. */\nexport function humanBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return \"0 MB\";\n const gb = bytes / 1_073_741_824;\n if (gb >= 1) return `${gb.toFixed(gb >= 10 ? 0 : 1)} GB`;\n return `${(bytes / 1_048_576).toFixed(0)} MB`;\n}\n\n// ── Fleet ────────────────────────────────────────────────────────────────────\n\nexport interface FleetAccount {\n name: string;\n balance: number;\n active: number;\n suspended: number;\n inventory: number;\n other: number;\n packageOnly?: boolean;\n}\n\nexport interface FleetInput {\n active: number;\n suspended: number;\n inventory: number;\n other: number;\n accounts: FleetAccount[];\n /** Set when an underlying call failed, so the screen can say so. */\n unavailable?: string[];\n}\n\nexport function fleetScreen(input: FleetInput): Screen {\n const total = input.active + input.suspended + input.inventory + input.other;\n const lowBalance = input.accounts.filter((a) => a.balance < 10);\n const criticals = lowBalance.filter((a) => a.packageOnly && a.balance === 0);\n\n const sections: Section[] = [\n {\n kind: \"metrics\",\n items: [\n { label: \"Total eSIMs\", value: total },\n { label: \"Active\", value: input.active, hint: pct(input.active, total), tone: \"ok\" },\n { label: \"Inventory\", value: input.inventory, hint: pct(input.inventory, total) },\n {\n label: \"Suspended\",\n value: input.suspended,\n hint: pct(input.suspended, total),\n tone: input.suspended > input.active * 0.1 ? \"warn\" : undefined,\n },\n { label: \"Accounts\", value: input.accounts.length },\n {\n label: \"Low balance\",\n value: lowBalance.length,\n tone: lowBalance.length > 0 ? \"warn\" : \"ok\",\n },\n ],\n },\n ];\n\n // Only chart the per-account split when it is actually populated. The\n // account list call carries balances and flags but no status breakdown, so\n // drawing this from it yields a row of empty bars that looks like a fleet\n // with nothing in it — a chart that misinforms is worse than no chart.\n const hasPerAccountCounts = input.accounts.some(\n (a) => a.active + a.suspended + a.inventory + a.other > 0,\n );\n if (hasPerAccountCounts) {\n sections.push({\n kind: \"bars\",\n title: \"eSIMs by account\",\n items: [...input.accounts]\n .sort((a, b) => b.active + b.inventory - (a.active + a.inventory))\n .slice(0, 10)\n .map((a) => ({\n label: a.name,\n value: a.active + a.suspended + a.inventory + a.other,\n hint: `${a.active} active`,\n tone: a.active > 0 ? (\"ok\" as Tone) : (\"muted\" as Tone),\n })),\n });\n }\n\n sections.push({\n kind: \"table\",\n title: \"Accounts\",\n columns: [\"Account\", \"Balance\", \"Active\", \"Inventory\"],\n numeric: [1, 2, 3],\n empty: \"No accounts under this reseller.\",\n rows: input.accounts.map((a) => [a.name, money(a.balance), a.active, a.inventory]),\n });\n\n if (criticals.length > 0) {\n sections.push({\n kind: \"note\",\n tone: \"critical\",\n text: `${criticals.length} package-only account(s) at zero balance — packages cannot be assigned until topped up: ${criticals\n .map((a) => a.name)\n .join(\", \")}.`,\n });\n }\n for (const missing of input.unavailable ?? []) {\n sections.push({ kind: \"note\", tone: \"warn\", text: `Unavailable: ${missing}` });\n }\n\n return {\n id: \"fleet-health\",\n title: \"Fleet health\",\n subtitle: `${total} eSIMs across ${input.accounts.length} account(s) · ${pct(input.active, total)} utilisation`,\n sections,\n actions: [\n { label: \"Top up an account\", command: \"wallet_topup_checkout\" },\n { label: \"Per-account eSIM status\", command: \"esim_status_per_account\" },\n ],\n };\n}\n\n// ── Subscribers ──────────────────────────────────────────────────────────────\n\nexport interface SubscriberRow {\n iccid: string;\n msisdn?: string;\n status: string;\n account?: string;\n dataUsedBytes?: number;\n}\n\nexport function subscribersScreen(rows: SubscriberRow[], opts: { account?: string } = {}): Screen {\n const byStatus = new Map<string, number>();\n for (const r of rows) byStatus.set(r.status, (byStatus.get(r.status) ?? 0) + 1);\n\n return {\n id: \"subscribers\",\n title: \"Subscribers\",\n subtitle: opts.account ? `Account ${opts.account} · ${rows.length} shown` : `${rows.length} shown`,\n sections: [\n {\n kind: \"metrics\",\n items: [\n { label: \"Listed\", value: rows.length },\n ...[...byStatus.entries()].map(([status, count]) => ({\n label: status,\n value: count,\n tone: status.toLowerCase() === \"active\" ? (\"ok\" as Tone) : undefined,\n })),\n ],\n },\n {\n kind: \"table\",\n title: \"Records\",\n columns: [\"ICCID\", \"MSISDN\", \"Status\", \"Account\", \"Data used\"],\n numeric: [4],\n empty: \"No subscribers matched. Widen the filter or check the account scope.\",\n rows: rows.map((r) => [\n r.iccid,\n r.msisdn ?? \"—\",\n r.status,\n r.account ?? \"—\",\n r.dataUsedBytes === undefined ? \"—\" : humanBytes(r.dataUsedBytes),\n ]),\n },\n ],\n actions: [\n { label: \"Diagnose one\", command: \"diagnose_subscriber\" },\n { label: \"Usage detail\", command: \"subscriber_usage\" },\n ],\n };\n}\n\n// ── Usage ────────────────────────────────────────────────────────────────────\n\nexport interface UsageInput {\n subject: string;\n timeline: Array<{ date: string; bytes: number }>;\n countries?: Array<{ country: string; bytes: number }>;\n totalBytes?: number;\n}\n\nexport function usageScreen(input: UsageInput): Screen {\n const total = input.totalBytes ?? input.timeline.reduce((sum, p) => sum + p.bytes, 0);\n const peak = input.timeline.reduce(\n (best, p) => (p.bytes > best.bytes ? p : best),\n { date: \"—\", bytes: 0 },\n );\n\n const sections: Section[] = [\n {\n kind: \"metrics\",\n items: [\n { label: \"Total\", value: humanBytes(total) },\n { label: \"Days\", value: input.timeline.length },\n { label: \"Peak day\", value: humanBytes(peak.bytes), hint: peak.date },\n {\n label: \"Daily average\",\n value: humanBytes(input.timeline.length ? total / input.timeline.length : 0),\n },\n ],\n },\n {\n kind: \"bars\",\n title: \"Daily usage\",\n empty: \"No usage recorded in this window.\",\n items: input.timeline.map((p) => ({\n label: p.date,\n value: p.bytes,\n hint: humanBytes(p.bytes),\n })),\n },\n ];\n\n if (input.countries?.length) {\n sections.push({\n kind: \"bars\",\n title: \"By country\",\n items: input.countries\n .slice(0, 10)\n .map((c) => ({ label: c.country, value: c.bytes, hint: humanBytes(c.bytes) })),\n });\n }\n\n return {\n id: \"usage\",\n title: \"Usage\",\n subtitle: input.subject,\n sections,\n actions: [{ label: \"Project depletion\", command: \"usage_projection\" }],\n };\n}\n\n// ── Packages ─────────────────────────────────────────────────────────────────\n\nexport interface PackageRow {\n name: string;\n id: string | number;\n dataLimitBytes?: number;\n validityDays?: number;\n price?: number;\n recurring?: boolean;\n}\n\nexport function packagesScreen(rows: PackageRow[]): Screen {\n return {\n id: \"packages\",\n title: \"Package catalog\",\n subtitle: `${rows.length} template(s)`,\n sections: [\n {\n kind: \"metrics\",\n items: [\n { label: \"Templates\", value: rows.length },\n { label: \"Recurring\", value: rows.filter((r) => r.recurring).length },\n ],\n },\n {\n kind: \"table\",\n title: \"Templates\",\n columns: [\"Name\", \"ID\", \"Data\", \"Validity\", \"Price\"],\n numeric: [2, 3, 4],\n empty: \"No package templates visible to this account.\",\n rows: rows.map((r) => [\n r.name,\n String(r.id),\n r.dataLimitBytes === undefined ? \"—\" : humanBytes(r.dataLimitBytes),\n r.validityDays === undefined ? \"—\" : `${r.validityDays}d`,\n r.price === undefined ? \"—\" : money(r.price),\n ]),\n },\n ],\n actions: [{ label: \"Assign to a subscriber\", command: \"assign_package\" }],\n };\n}\n\n// ── Billing ──────────────────────────────────────────────────────────────────\n\nexport interface BillingInput {\n currency?: string;\n balance?: number;\n pending?: number;\n events: Array<{ date: string; description: string; amount: number }>;\n}\n\nexport function billingScreen(input: BillingInput): Screen {\n const spend = input.events.reduce((sum, e) => sum + (e.amount > 0 ? e.amount : 0), 0);\n return {\n id: \"billing\",\n title: \"Billing\",\n subtitle: `${input.events.length} recent event(s)`,\n sections: [\n {\n kind: \"metrics\",\n items: [\n {\n label: \"Balance\",\n value: input.balance === undefined ? \"—\" : money(input.balance, input.currency),\n tone: (input.balance ?? 0) < 10 ? \"warn\" : \"ok\",\n },\n {\n label: \"Pending\",\n value: input.pending === undefined ? \"—\" : money(input.pending, input.currency),\n },\n { label: \"Recent spend\", value: money(spend, input.currency) },\n ],\n },\n {\n kind: \"table\",\n title: \"Recent events\",\n columns: [\"Date\", \"Description\", \"Amount\"],\n numeric: [2],\n empty: \"No billing events in this window.\",\n rows: input.events.map((e) => [e.date, e.description, money(e.amount, input.currency)]),\n },\n ],\n actions: [{ label: \"Check payouts\", command: \"stripe_connect_payouts\" }],\n };\n}\n\n// ── Wallet ───────────────────────────────────────────────────────────────────\n\nexport interface WalletInput {\n balance: number;\n currency?: string;\n autoTopupEnabled?: boolean;\n threshold?: number;\n credits?: number;\n}\n\nexport function walletScreen(input: WalletInput): Screen {\n const low = input.balance < (input.threshold ?? 10);\n return {\n id: \"wallet\",\n title: \"Wallet\",\n subtitle: input.autoTopupEnabled ? \"Auto top-up is on\" : \"Auto top-up is off\",\n sections: [\n {\n kind: \"metrics\",\n items: [\n {\n label: \"Balance\",\n value: money(input.balance, input.currency),\n tone: low ? \"warn\" : \"ok\",\n },\n ...(input.credits === undefined\n ? []\n : [{ label: \"Credits\", value: input.credits }]),\n ],\n },\n {\n kind: \"keyvalue\",\n title: \"Settings\",\n items: [\n { label: \"Auto top-up\", value: input.autoTopupEnabled ? \"enabled\" : \"disabled\" },\n {\n label: \"Threshold\",\n value: input.threshold === undefined ? \"—\" : money(input.threshold, input.currency),\n },\n ],\n },\n ...(low\n ? ([\n {\n kind: \"note\",\n tone: \"warn\",\n text: \"Balance is under the top-up threshold. Package assignment fails at zero on package-only accounts.\",\n },\n ] as Section[])\n : []),\n ],\n actions: [\n { label: \"Top up\", command: \"wallet_topup_checkout\" },\n { label: \"Configure auto top-up\", command: \"wallet_auto_topup\" },\n ],\n };\n}\n\n// ── Greenzone ────────────────────────────────────────────────────────────────\n\nexport function greenzoneScreen(entries: Array<{ value: string; note?: string }>): Screen {\n return {\n id: \"greenzone\",\n title: \"Greenzone whitelist\",\n subtitle: `${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"}`,\n sections: [\n { kind: \"metrics\", items: [{ label: \"Entries\", value: entries.length }] },\n {\n kind: \"table\",\n title: \"Whitelisted\",\n columns: [\"Value\", \"Note\"],\n empty: \"Whitelist is empty — every destination follows the default policy.\",\n rows: entries.map((e) => [e.value, e.note ?? \"—\"]),\n },\n ],\n actions: [\n { label: \"Add an entry\", command: \"greenzone_whitelist_add\" },\n { label: \"Remove an entry\", command: \"greenzone_whitelist_remove\" },\n ],\n };\n}\n\n// ── Storefront ───────────────────────────────────────────────────────────────\n\nexport interface StorefrontInput {\n brand: string;\n url?: string;\n target?: string;\n verified?: boolean | null;\n diagnosis?: string;\n probes?: Array<{ path: string; status: number; ok: boolean }>;\n secretsStaged?: string[];\n secretsMissing?: string[];\n plans?: number;\n}\n\nexport function storefrontScreen(input: StorefrontInput): Screen {\n const sections: Section[] = [\n {\n kind: \"metrics\",\n items: [\n {\n label: \"Status\",\n value: input.verified === true ? \"serving\" : input.verified === false ? \"broken\" : \"unknown\",\n tone: input.verified === true ? \"ok\" : input.verified === false ? \"critical\" : \"muted\",\n },\n { label: \"Host\", value: input.target ?? \"—\" },\n { label: \"Plans\", value: input.plans ?? \"—\" },\n { label: \"Secrets\", value: input.secretsStaged?.length ?? 0 },\n ],\n },\n {\n kind: \"keyvalue\",\n title: \"Deployment\",\n items: [\n { label: \"Brand\", value: input.brand },\n { label: \"URL\", value: input.url ?? \"not deployed\" },\n ...(input.diagnosis ? [{ label: \"Diagnosis\", value: input.diagnosis, tone: \"warn\" as Tone }] : []),\n ],\n },\n ];\n\n if (input.probes?.length) {\n sections.push({\n kind: \"table\",\n title: \"Probes\",\n columns: [\"Path\", \"Status\"],\n numeric: [1],\n rows: input.probes.map((p) => [p.path, p.status === 0 ? \"unreachable\" : p.status]),\n });\n }\n if (input.secretsMissing?.length) {\n sections.push({\n kind: \"note\",\n tone: \"critical\",\n text: `Missing runtime secret(s): ${input.secretsMissing.join(\", \")}. The build and deploy both succeed without them and the site fails at request time.`,\n });\n }\n\n return {\n id: \"storefront\",\n title: \"Storefront\",\n subtitle: input.url ?? input.brand,\n sections,\n actions: [\n { label: \"Deploy\", command: \"carrier site deploy\" },\n { label: \"Check hosts\", command: \"carrier site targets\" },\n ],\n };\n}\n","{\n \"name\": \"@carrierllc/mcp\",\n \"version\": \"0.9.2\",\n \"description\": \"Carrier MCP \\u2014 natural-language control of MVNO/eSIM fleets via eSIMVault OCS. Stdio mode for direct integration with Claude Desktop, Cursor, Windsurf, and MCP-compatible clients. Ships the `carrier` CLI (plugin install + white-label eSIM storefront scaffold).\",\n \"license\": \"MIT\",\n \"author\": \"Carrier (Lifecycle Innovations Limited)\",\n \"homepage\": \"https://mcp.carrier.llc\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Lifecycle-Innovations-Limited/carrier.llc\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"plugin\",\n \"README.md\",\n \"templates\",\n \"!templates/**/node_modules\",\n \"!templates/**/.open-next\",\n \"!templates/**/.next\",\n \"!templates/**/.turbo\",\n \"!templates/**/.vercel\",\n \"!templates/**/.wrangler\",\n \"!templates/**/*.tsbuildinfo\",\n \"!templates/**/.env.local\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"prepublishOnly\": \"pnpm run build\",\n \"build:test\": \"tsup --config tsup.test.config.ts\",\n \"test\": \"pnpm run build && pnpm run build:test && node --test test/*.test.js\",\n \"check:pack\": \"node scripts/check-pack-size.mjs\",\n \"generate:domains\": \"node scripts/generate-domains.mjs\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"^0.7.0\",\n \"@clerk/backend\": \"^3.14.0\",\n \"@modelcontextprotocol/ext-apps\": \"^1.7.5\",\n \"@modelcontextprotocol/sdk\": \"^1.29.0\",\n \"@noble/hashes\": \"^1.8.0\",\n \"@sentry/cloudflare\": \"^10.65.0\",\n \"aws4fetch\": \"^1.0.20\",\n \"commander\": \"^15.0.0\",\n \"picocolors\": \"^1.1.1\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@cloudflare/workers-types\": \"^4.20260521.1\",\n \"@types/node\": \"^26.1.2\",\n \"@typescript-eslint/eslint-plugin\": \"^8.59.4\",\n \"@typescript-eslint/parser\": \"^8.67.0\",\n \"eslint\": \"^9.39.4\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"@carrier/ai\": \"workspace:*\",\n \"@carrier/ocs-client\": \"workspace:*\",\n \"@carrier/ocs-spec\": \"workspace:*\",\n \"@carrier/screens\": \"workspace:*\"\n },\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"esim\",\n \"mvno\",\n \"carrier\",\n \"fleet-management\",\n \"esimvault\"\n ],\n \"engines\": {\n \"node\": \">=22\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"bin\": {\n \"carrier-mcp\": \"dist/index.js\",\n \"carrier\": \"dist/cli.js\"\n }\n}\n","// JSON modules are default-only, so this pulls the manifest in and reads one field.\nimport pkg from \"../package.json\" with { type: \"json\" };\n\n/**\n * Single source of truth for the published version.\n *\n * The CLI banner, `carrier --version`, the stdio MCP server's clientInfo and the\n * `carrier ask` handshake all read this. It used to be typed out in four places,\n * which is how seven commits shipped under a stale version string. Bump\n * package.json and every surface follows.\n */\nexport const CARRIER_VERSION: string = pkg.version;\n","/**\n * Global sliding-window rate floor for the OCS client.\n *\n * Scoped per resellerId (derived from the API token). Lives on a module-level\n * Map so it is shared across all OcsClient instances within one Worker isolate.\n *\n * Enforces ≤ 10 calls per 1000ms per reseller key.\n * Uses a sliding-window log: tracks the last 10 call timestamps.\n * Before the 11th call, waits until the oldest recorded call is at least\n * 1000ms in the past — guaranteeing ≤10 in any 1000ms span.\n *\n * Uses a serialised promise-chain (gate) so concurrent callers queue up\n * atomically. Without serialisation, multiple callers could read the same\n * window state simultaneously and all proceed, defeating the floor.\n *\n * Set env var RATE_FLOOR_DISABLED=true to bypass (never do this in production).\n */\n\nconst RATE_LIMIT = 10; // max calls per window\nconst WINDOW_MS = 1000; // window size in ms\n\ninterface WindowState {\n /** Timestamps of the last RATE_LIMIT calls (oldest first). */\n log: number[];\n /** Serialisation gate: every acquire chains onto this promise. */\n gate: Promise<void>;\n}\n\n// Module-level — shared across all OcsClient instances per isolate.\nconst windows = new Map<string, WindowState>();\n\nfunction getWindowState(key: string): WindowState {\n let s = windows.get(key);\n if (!s) {\n s = { log: [], gate: Promise.resolve() };\n windows.set(key, s);\n }\n return s;\n}\n\n/**\n * Acquire one slot for the given reseller key, awaiting if necessary.\n * No-op when RATE_FLOOR_DISABLED=true.\n */\nexport async function acquireToken(resellerId: string): Promise<void> {\n // Allow env-based bypass for integration/load tests that need to spike.\n // Tests asserting the floor works must NOT set this flag.\n // Resolve `process` via globalThis so TS doesn't need @types/node\n // (Workers tsconfig excludes Node types; runtime guard handles both envs).\n const proc = (globalThis as Record<string, unknown>)[\"process\"] as\n | { env?: Record<string, string | undefined> }\n | undefined;\n if (\n proc?.env?.[\"RATE_FLOOR_DISABLED\"] === \"true\" ||\n (globalThis as Record<string, unknown>)[\"RATE_FLOOR_DISABLED\"] === \"true\"\n ) {\n return;\n }\n\n const state = getWindowState(resellerId);\n\n // Chain onto the existing gate so only one acquire runs at a time.\n const ticket = state.gate.then(async () => {\n while (true) {\n const now = Date.now();\n\n if (state.log.length < RATE_LIMIT) {\n state.log.push(now);\n return;\n }\n\n const oldest = state.log[0]!;\n const age = now - oldest;\n\n if (age >= WINDOW_MS) {\n state.log.shift();\n state.log.push(now);\n return;\n }\n\n const waitMs = WINDOW_MS - age + 1;\n await new Promise<void>((r) => setTimeout(r, waitMs));\n }\n });\n\n // Advance the gate so the next caller queues behind this one.\n state.gate = ticket;\n\n return ticket;\n}\n\n/** Exposed for testing: reset all window state. */\nexport function _resetBucketsForTest(): void {\n windows.clear();\n}\n","/**\n * Per-endpoint rate-limit governor for Bridge4IP / OCS API calls.\n *\n * LIMITS (per reseller, confirmed by Bridge4IP):\n * Global: 600 / min\n * modifySubscriberMobilePlan: 300 / min\n * AffectRecurringPackageToSub: 300 / min\n * AffectSubscriberFakePhoneNumber: 300 / min\n * AffectSubscriberRealPhoneNumber: 300 / min\n * EsimStatusPerAccount: 300 / min\n * GetSubscriberActivePeriod: 300 / min\n * HlrGetBitrate / HlrSetBitrate: 300 / min\n * ListSubscriber: 300 / min\n * ListSubscriberPrepaidPackages: 300 / min\n * MoveSubscriberRangeToAccount: 300 / min\n * PushSteeringToSubs: 300 / min\n * SetSubscriberTrafficRestrictions:300 / min\n * ListLocationZoneElement: 150 / min\n * SendMtSms: 150 / min\n * SubscriberUsageOverPeriod: 100 / min ← tight\n * SubscriberNetworkEventsOverPeriod: 30 / min ← VERY TIGHT\n * getSingleSubscriber (TBD): 300 / min (assumed; alert on 429)\n * getSubscriberLocationByCellId (TBD): 300 / min (assumed; alert on 429)\n *\n * CAPACITY RESERVATION:\n * 80% of each bucket is reserved for interactive (MCP tool) calls.\n * 20% is reserved for batch / cron callers (ocs-bridge polls).\n * Callers pass `priority: \"interactive\" | \"batch\"` to acquireEndpointSlot().\n * Interactive callers draw from the full bucket capacity.\n * Batch callers can only draw up to 20% of the per-minute limit per minute.\n *\n * ALGORITHM:\n * Sliding-window log per (resellerId, endpoint) key.\n * Separate batch-window log per key to enforce the 20% ceiling.\n * Serialised promise-chain gate prevents TOCTOU races within a single isolate.\n * (CF Workers: mcp-server DO serialises per-session; ocs-bridge uses KV for cross-isolate.)\n *\n * NEVER LEAK MONEY:\n * Set RATE_FLOOR_DISABLED=true ONLY in test environments.\n * Any process-global flag bypasses all per-endpoint governors.\n */\n\nexport type CallPriority = \"interactive\" | \"batch\";\n\n// ---------------------------------------------------------------------------\n// Endpoint limit table\n// ---------------------------------------------------------------------------\n\n/** Per-minute cap for a given OCS method (camelCase). Default: 300/min. */\nconst ENDPOINT_LIMITS_PER_MIN: Record<string, number> = {\n // Global sentinel (keyed as \"__global__\")\n __global__: 600,\n\n // 30/min — tightest endpoint\n subscriberNetworkEventsOverPeriod: 30,\n\n // 100/min\n subscriberUsageOverPeriod: 100,\n\n // 150/min\n listLocationZoneElement: 150,\n sendMtSms: 150,\n\n // 300/min (explicit; also the fallback default)\n modifySubscriberMobilePlan: 300,\n affectRecurringPackageToSubscriber: 300,\n affectSubscriberFakePhoneNumber: 300,\n affectSubscriberRealPhoneNumber: 300,\n esimStatusPerAccount: 300,\n getSubscriberActivePeriod: 300,\n hlrGetBitrate: 300,\n hlrSetBitrate: 300,\n listSubscriber: 300,\n listSubscriberPrepaidPackages: 300,\n moveSubscriberRangeToAccount: 300,\n pushSteeringToSubs: 300,\n setSubscriberTrafficRestrictions: 300,\n getSingleSubscriber: 300,\n getSubscriberLocationByCellId: 300,\n};\n\nconst DEFAULT_LIMIT_PER_MIN = 300;\nconst WINDOW_MS = 60_000; // 1 minute\n\n/** Wall-clock throttle per endpoint bucket — avoids JSON log spam on every OCS call. */\nconst METRIC_EMIT_MIN_INTERVAL_MS = 10_000;\n\n/** Fraction of capacity reserved for batch callers (20%). */\nconst BATCH_FRACTION = 0.2;\n\n/** Non-reversible id for metrics only (callers may pass API tokens as reseller keys). */\nfunction resellerKeyHash(resellerKey: string | undefined): string {\n if (!resellerKey) return \"00000000\";\n let h = 5381;\n for (let i = 0; i < resellerKey.length; i++) {\n h = (((h << 5) + h) ^ resellerKey.charCodeAt(i)) >>> 0;\n }\n return h.toString(16).padStart(8, \"0\");\n}\n\n// ---------------------------------------------------------------------------\n// Prometheus-style metric emission\n// ---------------------------------------------------------------------------\n\n/**\n * Emit a structured metric line for external scraping / CF Analytics Engine.\n * Format: metric{labels} value timestamp_ms\n */\nfunction emitMetric(\n endpoint: string,\n resellerKey: string,\n callsInWindow: number,\n limitPerMin: number,\n): void {\n const pct = Math.round((callsInWindow / limitPerMin) * 100);\n const reseller_hash = resellerKeyHash(resellerKey);\n // Structured log: picked up by CF Workers Tail / BetterStack / Prometheus remote-write.\n console.log(\n JSON.stringify({\n metric: \"carrier_ocs_calls_per_min\",\n endpoint,\n reseller_hash,\n calls_in_window: callsInWindow,\n limit_per_min: limitPerMin,\n utilisation_pct: pct,\n alert: pct >= 80,\n ts: Date.now(),\n }),\n );\n if (pct >= 80) {\n console.warn(\n `[rate-governor] ALERT: ${endpoint} at ${pct}% capacity (${callsInWindow}/${limitPerMin} per min) reseller=${reseller_hash}`,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Window state\n// ---------------------------------------------------------------------------\n\ninterface EndpointWindowState {\n /** All call timestamps in the current sliding window (oldest first). */\n log: number[];\n /** Batch-only call timestamps (subset of log). */\n batchLog: number[];\n /** Serialisation gate — one acquire at a time. */\n gate: Promise<void>;\n /** Last `emitMetric` wall time for this bucket (throttles log volume). */\n lastMetricEmitAt: number;\n /**\n * When true, the next sample at ≥80% utilisation should emit immediately\n * (edge-trigger) so alerts are not delayed by the interval throttle.\n */\n metricBelowHighUtil: boolean;\n}\n\n// Keyed as `${resellerId}:${endpoint}` (or `${resellerId}:__global__`)\nconst _endpointWindows = new Map<string, EndpointWindowState>();\n\nfunction _getEndpointState(key: string): EndpointWindowState {\n let s = _endpointWindows.get(key);\n if (!s) {\n s = {\n log: [],\n batchLog: [],\n gate: Promise.resolve(),\n lastMetricEmitAt: 0,\n metricBelowHighUtil: true,\n };\n _endpointWindows.set(key, s);\n }\n return s;\n}\n\nfunction _prune(log: number[], windowStart: number): number[] {\n // Remove entries older than the window boundary.\n let i = 0;\n while (i < log.length && log[i]! <= windowStart) i++;\n return i > 0 ? log.slice(i) : log;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Acquire one OCS call slot for the given (resellerId, endpoint) pair.\n *\n * Enforces:\n * 1. Per-endpoint limit (from ENDPOINT_LIMITS_PER_MIN table).\n * 2. Global 600/min limit across all endpoints for this reseller.\n * 3. Batch callers capped at 20% of each bucket.\n *\n * Emits throttled `carrier_ocs_calls_per_min` JSON metrics (and warns at ≥80%\n * utilisation when a sample is emitted).\n *\n * No-op when RATE_FLOOR_DISABLED=true (never set in production).\n */\nexport async function acquireEndpointSlot(\n resellerId: string,\n endpoint: string,\n priority: CallPriority = \"interactive\",\n): Promise<void> {\n // Env bypass — test environments only.\n const proc = (globalThis as Record<string, unknown>)[\"process\"] as\n | { env?: Record<string, string | undefined> }\n | undefined;\n if (\n proc?.env?.[\"RATE_FLOOR_DISABLED\"] === \"true\" ||\n (globalThis as Record<string, unknown>)[\"RATE_FLOOR_DISABLED\"] === \"true\"\n ) {\n return;\n }\n\n // Global first: avoid holding a tight endpoint slot while waiting on the 600/min bucket.\n await _acquireOneSlot(resellerId, \"__global__\", priority);\n await _acquireOneSlot(resellerId, endpoint, priority);\n}\n\nasync function _acquireOneSlot(\n resellerId: string,\n endpoint: string,\n priority: CallPriority,\n): Promise<void> {\n const bucketKey = `${resellerId}:${endpoint}`;\n const limitPerMin = ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;\n const batchCap = Math.floor(limitPerMin * BATCH_FRACTION);\n\n const state = _getEndpointState(bucketKey);\n\n const ticket = state.gate.then(async () => {\n while (true) {\n const now = Date.now();\n const windowStart = now - WINDOW_MS;\n\n // Prune expired entries.\n state.log = _prune(state.log, windowStart);\n state.batchLog = _prune(state.batchLog, windowStart);\n\n const totalInWindow = state.log.length;\n const batchInWindow = state.batchLog.length;\n\n // Emit metric on a wall-clock interval; also emit immediately when util\n // first crosses ≥80% after having been below (so alerts are not delayed).\n if (totalInWindow > 0 && endpoint !== \"__global__\") {\n const utilPct = Math.round((totalInWindow / limitPerMin) * 100);\n const intervalOk =\n now - state.lastMetricEmitAt >= METRIC_EMIT_MIN_INTERVAL_MS;\n const enteredHighUtil = utilPct >= 80 && state.metricBelowHighUtil;\n if (intervalOk || enteredHighUtil) {\n emitMetric(endpoint, resellerId, totalInWindow, limitPerMin);\n state.lastMetricEmitAt = now;\n if (utilPct >= 80) {\n state.metricBelowHighUtil = false;\n }\n }\n if (utilPct < 80) {\n state.metricBelowHighUtil = true;\n }\n }\n\n // Check capacity:\n // - Global cap: can't exceed limitPerMin regardless of priority.\n // - Batch cap: batch callers additionally can't exceed batchCap.\n const globalFull = totalInWindow >= limitPerMin;\n const batchFull = priority === \"batch\" && batchInWindow >= batchCap;\n\n if (!globalFull && !batchFull) {\n // Slot available — record call.\n state.log.push(now);\n if (priority === \"batch\") {\n state.batchLog.push(now);\n }\n return;\n }\n\n // Determine how long to wait.\n let waitMs: number;\n if (globalFull) {\n // Wait until oldest entry in the full window ages out.\n const oldest = state.log[0]!;\n waitMs = WINDOW_MS - (now - oldest) + 1;\n } else {\n // Batch cap hit — wait until oldest batch entry ages out.\n const oldestBatch = state.batchLog[0]!;\n waitMs = WINDOW_MS - (now - oldestBatch) + 1;\n }\n\n await new Promise<void>((r) => setTimeout(r, waitMs));\n }\n });\n\n state.gate = ticket;\n return ticket;\n}\n\n/**\n * Returns the per-minute limit for a given OCS method.\n * Exported for testing and monitoring.\n */\nexport function getLimitForEndpoint(endpoint: string): number {\n return ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;\n}\n\n/**\n * Read-only counts for the current sliding window (same keying as acquireEndpointSlot).\n * Scans timestamps without mutating state — safe for status tools alongside acquires.\n */\nexport function getRateLimitWindowCounts(\n resellerKey: string,\n endpoint: string,\n): { calls_in_window: number; batch_calls_in_window: number } {\n const bucketKey = `${resellerKey}:${endpoint}`;\n const state = _endpointWindows.get(bucketKey);\n if (!state) {\n return { calls_in_window: 0, batch_calls_in_window: 0 };\n }\n const windowStart = Date.now() - WINDOW_MS;\n let calls_in_window = 0;\n for (const ts of state.log) {\n if (ts > windowStart) calls_in_window++;\n }\n let batch_calls_in_window = 0;\n for (const ts of state.batchLog) {\n if (ts > windowStart) batch_calls_in_window++;\n }\n return { calls_in_window, batch_calls_in_window };\n}\n\n/** Exposed for testing: reset all endpoint window state. */\nexport function _resetGovernorForTest(): void {\n _endpointWindows.clear();\n}\n","/**\n * OCS parameter-shape registry.\n *\n * WHY THIS FILE EXISTS\n * The OCS JSON-RPC surface is not uniform. Some methods take a params OBJECT\n * (`{ \"listPrepaidPackageTemplate\": { \"resellerId\": 1 } }`) and some take a\n * BARE INTEGER (`{ \"listDetailedLocationZone\": 1 }`). Sending the wrong one\n * does not degrade gracefully: OCS rejects the entire request with\n *\n * Cannot deserialize value of type java.lang.Integer from Object value\n *\n * and the Console renders a dead panel with no data and no error.\n *\n * This bug class has now shipped THREE times:\n * 1. `:zoneId` vs `:locationZoneId` (PR #659)\n * 2. `listDetailedLocationZone` receiving `{}` (network.ts, intelligence.ts)\n * 3. `listSponsor` receiving `{}` — same ternary, found only by auditing\n * every sibling call site rather than fixing the one that was reported.\n *\n * Prose comments at each call site did not stop (2) or (3). A single pinned\n * list plus a test that reads it is the enforcement.\n *\n * SOURCES (both required to add an entry)\n * - docs/research/bridge4ip-ocs-api/endpoints-full.md — the request example\n * shows a bare number vs an object.\n * - apps/mcp-server/src/tools.ts \"Fix #12/#13/#14/#15\" — these shapes were\n * proven against the live OCS server, not inferred.\n *\n * NOTE ON packages/ocs-spec/ocs-methods.json\n * Do NOT read param shape from that file. Its `params: {}` means \"no named\n * parameters documented\", which is true for BOTH a bare-int method and a\n * genuinely argument-less one. It cannot distinguish the two, so using it as\n * the shape source is how a bare-int method gets `{}` again.\n */\n\n/**\n * Methods whose sole parameter is a bare integer RESELLER id.\n * Callers must pass `resolveOcsResellerId(...)`'s number, never an object.\n */\nexport const OCS_BARE_INT_RESELLER_METHODS = [\n \"listSponsor\",\n \"listSteeringList\",\n \"listDetailedDestinationList\",\n \"listDetailedLocationZone\",\n \"getCustomerTariff\",\n] as const;\n\n/**\n * Methods whose sole parameter is a bare integer that is NOT a reseller id.\n * Kept separate so nobody \"helpfully\" feeds them a reseller id.\n */\nexport const OCS_BARE_INT_OTHER_METHODS = {\n listLocationZoneElement: \"locationZoneId\",\n listDestinationListPrefix: \"destinationListId\",\n listVoipTariffRule: \"voipPlanId\",\n getSimProviderStatus: \"simId\",\n deleteSubscriberPackage: \"packageId\",\n} as const;\n\nexport type OcsBareIntResellerMethod =\n (typeof OCS_BARE_INT_RESELLER_METHODS)[number];\n\nexport function isBareIntResellerMethod(\n method: string,\n): method is OcsBareIntResellerMethod {\n return (OCS_BARE_INT_RESELLER_METHODS as readonly string[]).includes(method);\n}\n\n/**\n * Every method whose sole parameter is a bare integer, regardless of what that\n * integer identifies.\n */\nexport function isBareIntMethod(method: string): boolean {\n return (\n isBareIntResellerMethod(method) ||\n Object.prototype.hasOwnProperty.call(OCS_BARE_INT_OTHER_METHODS, method)\n );\n}\n\n/** What the bare integer identifies, for a precise error message. */\nexport function bareIntIdName(method: string): string {\n if (isBareIntResellerMethod(method)) return \"resellerId\";\n return (\n (OCS_BARE_INT_OTHER_METHODS as Record<string, string>)[method] ?? \"id\"\n );\n}\n\n/**\n * Throws when a bare-int method is about to be sent an object.\n *\n * WHY THIS IS A RUNTIME CHECK AND NOT ONLY A TEST\n * A pinned list that only tests read is documentation: it cannot stop a NEW\n * call site written tomorrow, and it did not stop three of them. Three\n * methods in this very registry (listLocationZoneElement,\n * listDestinationListPrefix, deleteSubscriberPackage) were still being called\n * with objects on 2026-08-20 and reproduced the Jackson error against live\n * production OCS while the registry sat here, correct and unconsumed.\n *\n * Enforcing at the single serialisation chokepoint covers every surface at\n * once — API, MCP server, MCP stdio, CLI — including call sites nobody has\n * written yet. That is the property a grep guard cannot have.\n *\n * The thrown error names the expected id, so the failure is self-explaining\n * rather than surfacing as an opaque OCS deserialiser message four layers\n * away from the mistake.\n */\nexport function assertBareIntParam(method: string, params: unknown): void {\n if (!isBareIntMethod(method)) return;\n if (typeof params === \"number\") return;\n // A numeric string still serialises as a JSON string, which OCS also rejects.\n const shape =\n params === null\n ? \"null\"\n : Array.isArray(params)\n ? \"array\"\n : typeof params === \"object\"\n ? \"object\"\n : typeof params;\n throw new TypeError(\n `OCS method \"${method}\" takes a BARE INTEGER ${bareIntIdName(method)}, ` +\n `but received ${shape}. Sending an object makes OCS reject the whole ` +\n `request with \"Cannot deserialize value of type java.lang.Integer from ` +\n `Object value\", which surfaces in the UI as an empty panel. ` +\n `Pass the id itself, e.g. call(\"${method}\", 1170).`,\n );\n}\n","/**\n * @carrier/ocs-client — OCS API client (Workers-compatible).\n *\n * Shared between apps/mcp-server and apps/api.\n * All requests POST to `${baseUrl}/v1?token=<api_key>` with body `{ methodName: params }`.\n * Responses: `{ status: { code, msg }, methodName: { ...data } }`.\n *\n * Money-leak guardrails (NEVER LEAK MONEY):\n * - Per-second floor: ≤10 calls/sec/reseller (see rate-floor.ts).\n * - Per-endpoint per-minute governor: enforces Bridge4IP limits per endpoint\n * (30–600/min depending on method) with 80/20 interactive/batch split (see rate-governor.ts).\n * On 429 / OCS error code 100 (TRAFFIC_CONTROL_LIMIT_EXCEEDED): exponential backoff + log.\n * - Specific error codes 12, 17, 100, 10001-10004 carry structured messages.\n */\n\nimport { acquireToken } from \"./rate-floor.js\";\nimport { acquireEndpointSlot, type CallPriority } from \"./rate-governor.js\";\nimport { OCS_V1_METHODS, RESPONSE_SCHEMAS } from \"./schemas.js\";\nimport { assertBareIntParam } from \"./ocs-param-shapes.js\";\n\nexport interface OcsStatus {\n code: number;\n msg: string;\n}\n\nexport interface OcsResponse<T = Record<string, unknown>> {\n status: OcsStatus;\n [method: string]: T | OcsStatus;\n}\n\nexport interface OcsApiErrorDetails {\n /** For create_location_zone validation errors (codes 10001-10004). */\n invalidTadigs?: string[];\n existingLZ?: unknown;\n [key: string]: unknown;\n}\n\nexport class OcsApiError extends Error {\n public readonly details: OcsApiErrorDetails;\n\n constructor(\n public readonly code: number,\n message: string,\n public readonly method: string,\n details: OcsApiErrorDetails = {},\n ) {\n super(`[${method}] OCS error ${code}: ${message}`);\n this.name = \"OcsApiError\";\n this.details = details;\n }\n}\n\n/**\n * Thrown when `call()` is invoked with a method that is NOT one of the\n * known-valid OCS v1 methods (see `OCS_V1_METHODS`). Catches phantom methods\n * like `createAccount` / `createSubscriber` at dev time — these are not OCS\n * methods and the server silently returns a 200 + error-2 envelope for them.\n * Only thrown in strict mode (`{ strictMethods: true }`); otherwise `call()`\n * warns once and proceeds.\n */\nexport class OcsUnknownMethodError extends Error {\n constructor(public readonly method: string) {\n super(\n `[${method}] is not a known OCS v1 method. ` +\n `It is not in the verified set of ${OCS_V1_METHODS.size} methods ` +\n `(createAccount/createSubscriber do not exist on the OCS). ` +\n `If this is a genuinely new method, add it to OCS_V1_METHODS or ` +\n `construct the client without { strictMethods: true }.`,\n );\n this.name = \"OcsUnknownMethodError\";\n }\n}\n\n/**\n * Thrown when a response payload that has a pinned schema in\n * `RESPONSE_SCHEMAS` fails shape validation — i.e. the OCS returned a 200 with\n * status.code 0 but a body shaped differently than the contract expects. This\n * surfaces provider drift loudly instead of returning a mis-shaped object that\n * blows up far away from the boundary. Skip per-client with\n * `{ validateResponses: false }` for forward-compat.\n */\n/** A single contract-validation failure: where it failed and why. */\nexport interface OcsContractIssue {\n /** Dotted path into the payload (e.g. `reseller.0.account`); `<root>` for top-level. */\n path: string;\n /** Human-readable reason from the underlying Zod issue. */\n message: string;\n}\n\nexport class OcsContractError extends Error {\n constructor(\n public readonly method: string,\n /** Structured list of what failed — inspect programmatically, don't parse the message. */\n public readonly issues: OcsContractIssue[],\n public readonly payload: unknown,\n ) {\n super(\n `[${method}] OCS response failed contract validation: ${OcsContractError.summarize(issues)}`,\n );\n this.name = \"OcsContractError\";\n }\n\n /** One-line, human-readable join of the issues — for logs and the error message. */\n get summary(): string {\n return OcsContractError.summarize(this.issues);\n }\n\n private static summarize(issues: OcsContractIssue[]): string {\n return issues.map((i) => `${i.path}: ${i.message}`).join(\"; \");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error code message routing\n// ---------------------------------------------------------------------------\n\nfunction ocsErrorMessage(\n code: number,\n rawMsg: string,\n rawBody: Record<string, unknown>,\n): { message: string; details: OcsApiErrorDetails } {\n switch (code) {\n case 12:\n return {\n message:\n \"OCS resource is read-only (code 12). The package/subscriber/account state \" +\n \"does not allow this mutation. Often returned for end-of-life subscribers or \" +\n \"finalized invoices.\",\n details: {},\n };\n case 17:\n return {\n message:\n \"Subscriber is end-of-life (code 17). It can no longer be mutated. \" +\n \"Use modify_subscriber_status to revive before further changes.\",\n details: {},\n };\n default:\n if (code >= 10001 && code <= 10004) {\n // create_location_zone validation — preserve invalidTadigs / existingLZ\n const details: OcsApiErrorDetails = {};\n if (rawBody[\"invalidTadigs\"] !== undefined) {\n details.invalidTadigs = rawBody[\"invalidTadigs\"] as string[];\n }\n if (rawBody[\"existingLZ\"] !== undefined) {\n details.existingLZ = rawBody[\"existingLZ\"];\n }\n return { message: rawMsg || \"Location zone validation failed\", details };\n }\n return { message: rawMsg || \"Unknown error\", details: {} };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Retry with exponential backoff + jitter\n// ---------------------------------------------------------------------------\n\nconst BACKOFF_BASE_MS = 500;\nconst BACKOFF_MAX_MS = 4_000;\nconst BACKOFF_MAX_RETRIES = 3;\nconst BACKOFF_JITTER = 0.25; // ±25%\n\n/**\n * Exported so callers (e.g. intelligence.ts) can wrap per-iteration OCS calls.\n * Strategy: 3 retries, exponential base 500ms, ±25% jitter, capped at 4s.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= BACKOFF_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n const base = Math.min(BACKOFF_BASE_MS * Math.pow(2, attempt - 1), BACKOFF_MAX_MS);\n const jitter = base * BACKOFF_JITTER * (2 * Math.random() - 1);\n const delay = Math.max(0, Math.round(base + jitter));\n await new Promise<void>((r) => setTimeout(r, delay));\n }\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n // Only retry on code 100 (TRAFFIC_CONTROL_LIMIT_EXCEEDED) or network errors.\n if (err instanceof OcsApiError && err.code !== 100) {\n throw err;\n }\n // Contract failures are deterministic — don't waste retries on them.\n if (err instanceof OcsContractError) {\n throw err;\n }\n }\n }\n throw lastError;\n}\n\n// ---------------------------------------------------------------------------\n// OCS Client\n// ---------------------------------------------------------------------------\n\nconst REQUEST_TIMEOUT_MS = 20_000;\n\nexport interface OcsClientOptions {\n /**\n * Enforce the known-method guard: when true, calling a method not in\n * `OCS_V1_METHODS` throws `OcsUnknownMethodError`. Default **false**\n * (permissive) — the repo currently calls a number of method names that\n * aren't in the live-verified OCS v1 set (a separate reconciliation effort),\n * so enforcing here would break existing call sites. Opt in (`true`) for\n * dev/tests or once the codebase's method vocabulary is reconciled. An\n * unrecognized method is warned once regardless, so the signal isn't lost.\n */\n strictMethods?: boolean;\n /**\n * Run response-shape validation against `RESPONSE_SCHEMAS`. Default true. A\n * mismatch WARNS once (does not throw) unless `strictValidation` is set. Set\n * false to skip validation entirely (e.g. consumers that do their own\n * shape-tolerant parsing).\n */\n validateResponses?: boolean;\n /**\n * Throw `OcsContractError` on a response-shape mismatch instead of warning.\n * Default false. Enable in dev/CI/tests to fail loudly on provider drift;\n * left off in prod so an un-sampled shape variant can't break live traffic.\n */\n strictValidation?: boolean;\n}\n\nexport class OcsClient {\n private readonly baseUrl: string;\n private readonly _strictMethods: boolean;\n private readonly _validateResponses: boolean;\n private readonly _strictValidation: boolean;\n // Per-instance (NOT static): a static set would dedup across requests in a\n // reused Worker isolate and silently swallow recurring contract-drift warnings.\n private readonly _warnedMethods = new Set<string>();\n private readonly _warnedValidation = new Set<string>();\n\n /**\n * @param baseUrl OCS base URL without trailing slash (e.g. https://ocs.esimvault.cloud).\n * @param _defaultToken Default API token; identifies the reseller for rate-floor keying.\n * @param _priority Call priority: \"interactive\" (MCP tool calls, 80% capacity) or\n * \"batch\" (cron/poll callers, 20% capacity). Default: \"interactive\".\n * @param options Contract-enforcement opt-outs (see `OcsClientOptions`).\n */\n constructor(\n baseUrl: string,\n private readonly _defaultToken?: string,\n private readonly _priority: CallPriority = \"interactive\",\n options: OcsClientOptions = {},\n ) {\n // Trimmed by index, not by `/\\/+$/`. That regex backtracks polynomially on\n // a long run of slashes (CodeQL js/polynomial-redos), and this constructor\n // is now reachable from `@carrier/onboarding` with a base URL that comes\n // from Worker config — still trusted, but the cheap fix is cheaper than\n // the argument.\n let end = baseUrl.length;\n while (end > 0 && baseUrl.charCodeAt(end - 1) === 47 /* \"/\" */) end--;\n this.baseUrl = baseUrl.slice(0, end);\n this._strictMethods = options.strictMethods ?? false;\n this._validateResponses = options.validateResponses ?? true;\n this._strictValidation = options.strictValidation ?? false;\n }\n\n async call<T = Record<string, unknown>>(\n method: string,\n params: Record<string, unknown> | number | string = {},\n token?: string,\n ): Promise<T> {\n // Method guard. The live-verified OCS v1 set (OCS_V1_METHODS) does not (yet)\n // cover every name the repo calls, so by default we WARN once rather than\n // throw — surfacing phantom methods (createAccount/createSubscriber, etc.)\n // without breaking existing call sites. Strict mode (opt-in) throws.\n if (!OCS_V1_METHODS.has(method)) {\n if (this._strictMethods) throw new OcsUnknownMethodError(method);\n if (!this._warnedMethods.has(method)) {\n this._warnedMethods.add(method);\n console.warn(\n `[ocs-client] method \"${method}\" is not in the verified OCS v1 set — ` +\n `it may not exist on this OCS instance (see OCS_V1_METHODS).`,\n );\n }\n }\n\n const tok = token ?? this._defaultToken;\n if (!tok) {\n throw new OcsApiError(-1, \"No OCS API token provided\", method);\n }\n\n const url = `${this.baseUrl}/v1?token=${tok}`;\n // Shape gate. Some OCS methods take a bare integer id; sending an object\n // makes OCS reject the entire request with an opaque Jackson deserialiser\n // error that the UI renders as an empty panel. Fail here — at the one place\n // every surface serialises through — with a message that names the mistake.\n assertBareIntParam(method, params);\n const body = JSON.stringify({ [method]: params });\n\n return retryWithBackoff(async () => {\n // Layer 1 — Per-second floor: ≤10 calls/sec keyed by token.\n await acquireToken(tok);\n\n // Layer 2 — Per-endpoint per-minute governor (each retryable attempt consumes one slot).\n await acquireEndpointSlot(tok, method, this._priority);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (err) {\n // Network error — retryWithBackoff will retry on non-OcsApiError\n throw err;\n }\n\n if (res.status >= 500) {\n // Treat 5xx as retryable network-layer errors (not OcsApiError, so backoff retries)\n throw new Error(`HTTP ${res.status} ${res.statusText}`);\n }\n\n if (!res.ok) {\n throw new OcsApiError(\n res.status,\n `HTTP ${res.status} ${res.statusText}`,\n method,\n );\n }\n\n const json = (await res.json()) as OcsResponse<T>;\n\n if (json.status?.code !== 0) {\n const ocsCode = json.status?.code ?? -1;\n const rawMsg = json.status?.msg ?? \"Unknown error\";\n // Pass full json body for error-detail extraction (e.g. invalidTadigs)\n const rawBody = json as unknown as Record<string, unknown>;\n const { message, details } = ocsErrorMessage(ocsCode, rawMsg, rawBody);\n throw new OcsApiError(ocsCode, message, method, details);\n }\n\n // Resolve the response payload, honoring the live-OCS key remaps.\n let payload: T;\n if (method === \"getCustomerTariff\" && json[\"listTariffRule\"] !== undefined) {\n // Fix #14: getCustomerTariff response keyed as \"listTariffRule\"\n payload = json[\"listTariffRule\"] as T;\n } else if (method === \"getSubscriberLocationByCellId\") {\n const byMethod = json[method] as T | undefined;\n if (byMethod !== undefined) {\n // Present null is a valid payload; do not fall through to envelope fallback.\n payload = byMethod;\n } else if (json[\"subscriberLocation\"] !== undefined) {\n // GeoSense: live OCS returns coordinates under \"subscriberLocation\", not the method name\n payload = json[\"subscriberLocation\"] as T;\n } else {\n payload = (json[method] as T) ?? (json as unknown as T);\n }\n } else {\n payload = (json[method] as T) ?? (json as unknown as T);\n }\n\n // Contract validation: if this method has a pinned response schema, assert\n // the payload shape. A mismatch = provider drift. By default we WARN once\n // (surfacing the drift without breaking the call — the live shapes are a\n // sampled snapshot, so we don't hard-fail prod traffic on an un-sampled\n // variant). Opt into `{ strictValidation: true }` to throw OcsContractError.\n if (this._validateResponses) {\n const schema = RESPONSE_SCHEMAS[method];\n if (schema) {\n const parsed = schema.safeParse(payload);\n if (!parsed.success) {\n const issues: OcsContractIssue[] = parsed.error.issues.map((i) => ({\n path: i.path.join(\".\") || \"<root>\",\n message: i.message,\n }));\n if (this._strictValidation) {\n throw new OcsContractError(method, issues, payload);\n }\n if (!this._warnedValidation.has(method)) {\n this._warnedValidation.add(method);\n console.warn(`[ocs-client] ${new OcsContractError(method, issues, payload).message}`);\n }\n }\n }\n }\n\n return payload;\n });\n }\n}\n","/**\n * Shared OCS request builders + response parsers.\n *\n * Console, REST API, MCP, and CLI MUST call these instead of inventing\n * per-surface shapes. Live eSIMVault OCS is picky:\n * - getSubscriberLocation takes a flat SubscriberId ({ iccid }), NOT\n * { subscriber: { iccid } } (OCS error 2 \"Unrecognized field subscriber\").\n * - subscriberUsageOverPeriod takes { subscriber: { iccid }, period }\n * and a max inclusive window of 7 days. Wider windows 400/empty.\n * - Usage bytes live in quantityPerType[\"33\"]; daily rollup may sit at\n * data.subsPeriodUsages OR data.usages[0].subsPeriodUsages.\n */\n\nexport const OCS_MAX_USAGE_WINDOW_DAYS = 7;\nexport const OCS_DATA_USAGE_TYPE = \"33\";\n\nexport function clampUsagePeriod(\n start: string,\n end: string,\n maxDays = OCS_MAX_USAGE_WINDOW_DAYS,\n): { start: string; end: string } {\n const endMs = Date.parse(`${end}T00:00:00Z`);\n const startMs = Date.parse(`${start}T00:00:00Z`);\n if (Number.isNaN(endMs) || Number.isNaN(startMs) || startMs > endMs) {\n return { start, end };\n }\n const minStartMs = endMs - (maxDays - 1) * 86_400_000;\n if (startMs < minStartMs) {\n return { start: new Date(minStartMs).toISOString().slice(0, 10), end };\n }\n return { start, end };\n}\n\n/** Inclusive last-N-days window ending today (UTC). days=7 → start = today-6. */\nexport function lastNDaysPeriod(days = OCS_MAX_USAGE_WINDOW_DAYS, now = new Date()): { start: string; end: string } {\n const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n const start = new Date(end);\n start.setUTCDate(end.getUTCDate() - (days - 1));\n return {\n start: start.toISOString().slice(0, 10),\n end: end.toISOString().slice(0, 10),\n };\n}\n\nexport function locationParams(iccid: string): { iccid: string } {\n return { iccid };\n}\n\n/** Flat SubscriberId — same shape as location. Active period rejects a nested subscriber wrapper. */\nexport function subscriberIdParams(iccid: string): { iccid: string } {\n return { iccid };\n}\n\nexport interface RecurringPackageOptions {\n activationAtFirstUse?: boolean;\n startTimeUTC?: string;\n}\n\nexport function recurringPackageParams(\n iccid: string,\n packageTemplateId: number,\n options: RecurringPackageOptions = {},\n): Record<string, unknown> {\n if (options.activationAtFirstUse !== undefined && options.startTimeUTC !== undefined) {\n throw new Error(\"activationAtFirstUse cannot be combined with startTimeUTC\");\n }\n return {\n subscriber: { iccid },\n packageTemplateId,\n ...(options.startTimeUTC !== undefined\n ? { startTimeUTC: options.startTimeUTC }\n : { activationAtFirstUse: options.activationAtFirstUse ?? true }),\n };\n}\n\nexport function networkEventsOverPeriodParams(\n iccid: string,\n start: string,\n end: string,\n): { subscriber: { iccid: string }; period: { start: string; end: string } } {\n return usageOverPeriodParams(iccid, start, end);\n}\n\nexport type OcsRadioType = \"2G\" | \"3G\" | \"4G\" | \"5G\" | \"NB-IoT\";\n\nexport function radioTypeFromRat(rat: unknown): OcsRadioType | undefined {\n const s = String(rat ?? \"\").toUpperCase();\n if (!s) return undefined;\n if (s.includes(\"NB\")) return \"NB-IoT\";\n if (s.includes(\"5G\") || s.includes(\"NR\")) return \"5G\";\n if (s.includes(\"4G\") || s.includes(\"LTE\")) return \"4G\";\n if (s.includes(\"3G\") || s.includes(\"UMTS\") || s.includes(\"HSPA\") || s.includes(\"WCDMA\")) return \"3G\";\n if (s.includes(\"2G\") || s.includes(\"GSM\") || s.includes(\"GPRS\") || s.includes(\"EDGE\")) return \"2G\";\n return undefined;\n}\n\nexport interface CellTuple {\n radioType: OcsRadioType;\n mcc: number;\n mnc: number;\n lac: number;\n cellId?: number;\n}\n\nexport function cellTupleFromNetworkInfo(networkInfo: unknown): CellTuple | undefined {\n if (!networkInfo || typeof networkInfo !== \"object\") return undefined;\n const rec = networkInfo as Record<string, unknown>;\n const radioType = radioTypeFromRat(rec.lastRat ?? rec.rat ?? rec.radioType);\n const mcc = Number(rec.lastMcc ?? rec.mcc);\n const mnc = Number(rec.lastMnc ?? rec.mnc);\n const lac = Number(rec.lastLac ?? rec.lac);\n const cellIdRaw = rec.lastCellId ?? rec.cellId;\n if (!radioType || !Number.isFinite(mcc) || !Number.isFinite(mnc) || !Number.isFinite(lac)) return undefined;\n const out: CellTuple = { radioType, mcc, mnc, lac };\n const cellId = Number(cellIdRaw);\n if (Number.isFinite(cellId) && cellId > 0) out.cellId = cellId;\n return out;\n}\n\nexport function cellLocationParams(cell: CellTuple): {\n radioType: OcsRadioType;\n mcc: number;\n mnc: number;\n lac: number;\n cellId?: number;\n} {\n const params: { radioType: OcsRadioType; mcc: number; mnc: number; lac: number; cellId?: number } = {\n radioType: cell.radioType,\n mcc: cell.mcc,\n mnc: cell.mnc,\n lac: cell.lac,\n };\n if (cell.cellId != null) params.cellId = cell.cellId;\n return params;\n}\n\nexport function usageOverPeriodParams(\n iccid: string,\n start: string,\n end: string,\n): { subscriber: { iccid: string }; period: { start: string; end: string } } {\n const period = clampUsagePeriod(start, end);\n return { subscriber: { iccid }, period };\n}\n\n/**\n * Pull the IMSI out of a `getSingleSubscriber` record.\n *\n * Live OCS returns the IMSI under `imsiList[]` (and `multiImsi[]`), never as a\n * bare top-level `imsi` property. Reading `record.imsi` directly is always\n * `undefined`, which is why every HLR/SMS tool that needed an IMSI failed with\n * \"Could not resolve IMSI for ICCID <n>\" against a perfectly healthy SIM.\n *\n * Verified 2026-08-19 on ICCID 8948010000020241113: `imsiList[0].imsi` =\n * 260010189628101, and `hlrGetBitrate { imsi }` then answers UNLIMITED.\n *\n * Accepts either the unwrapped record or the `{ getSingleSubscriber: {...} }`\n * envelope, since call sites differ on which one they hold.\n */\nexport function imsiFromSubscriberRecord(record: unknown): string | undefined {\n if (!record || typeof record !== \"object\") return undefined;\n const rec = record as Record<string, unknown>;\n const inner =\n rec.getSingleSubscriber && typeof rec.getSingleSubscriber === \"object\"\n ? (rec.getSingleSubscriber as Record<string, unknown>)\n : rec;\n\n const direct = inner.imsi;\n if (typeof direct === \"string\" && direct.length > 0) return direct;\n\n for (const key of [\"imsiList\", \"multiImsi\"] as const) {\n const list = inner[key];\n if (!Array.isArray(list)) continue;\n for (const entry of list) {\n if (!entry || typeof entry !== \"object\") continue;\n const value = (entry as Record<string, unknown>).imsi;\n if (typeof value === \"string\" && value.length > 0) return value;\n }\n }\n return undefined;\n}\n\n\nexport interface UsageDayPoint {\n date: string;\n mb: number;\n}\n\nexport interface UsageCountrySlice {\n country: string;\n mb: number;\n operators: { name: string; mb: number }[];\n}\n\nexport function mbFromBytes(bytes: number | undefined): number {\n if (!bytes) return 0;\n return Math.round(bytes / 1_048_576);\n}\n\nexport function dataBytesFromQuantity(qty: unknown): number {\n if (!qty || typeof qty !== \"object\") return 0;\n const rec = qty as Record<string, unknown>;\n const raw = rec[OCS_DATA_USAGE_TYPE] ?? rec[33];\n return typeof raw === \"number\" ? raw : 0;\n}\n\nfunction periodRows(data: Record<string, unknown> | undefined): unknown[] {\n if (!data) return [];\n if (Array.isArray(data.subsPeriodUsages) && data.subsPeriodUsages.length) return data.subsPeriodUsages;\n const usages = Array.isArray(data.usages) ? data.usages : [];\n return usages.flatMap((u) => {\n const rec = u as { subsPeriodUsages?: unknown[] };\n return Array.isArray(rec.subsPeriodUsages) ? rec.subsPeriodUsages : [];\n });\n}\n\n/** Accepts the REST `{ data }` envelope or the raw OCS usage object. */\nexport function extractUsageTimeline(json: unknown): UsageDayPoint[] {\n const root = (json ?? {}) as Record<string, unknown>;\n const data = (root.data ?? root.usage ?? root.subscriberUsageOverPeriod ?? root) as Record<string, unknown>;\n if (Array.isArray(data)) {\n return data\n .map((entry: { date?: string; day?: string; mb?: number; bytes?: number }) => ({\n date: entry.date ?? entry.day ?? \"\",\n mb: entry.mb ?? mbFromBytes(entry.bytes),\n }))\n .filter((p) => p.date);\n }\n if (!data || typeof data !== \"object\") return [];\n\n const periods = periodRows(data);\n if (periods.length > 0) {\n return periods\n .map((p) => {\n const rec = p as { day?: string; date?: string; total?: { quantityPerType?: unknown } };\n return {\n date: rec.day ?? rec.date ?? \"\",\n mb: mbFromBytes(dataBytesFromQuantity(rec.total?.quantityPerType)),\n };\n })\n .filter((p) => p.date);\n }\n\n const sessions = Array.isArray(data.usages)\n ? (data.usages as { subsDailyUsages?: unknown[]; usageDateUtc?: string; quantity?: number }[]).flatMap((u) =>\n Array.isArray(u.subsDailyUsages) && u.subsDailyUsages.length ? u.subsDailyUsages : [u],\n )\n : [];\n if (sessions.length === 0) return [];\n const byDay = new Map<string, number>();\n for (const s of sessions) {\n const rec = s as { day?: string; usageDateUtc?: string; quantity?: number; total?: { quantityPerType?: unknown } };\n const day = rec.day ?? (typeof rec.usageDateUtc === \"string\" ? rec.usageDateUtc.slice(0, 10) : \"\");\n if (!day) continue;\n const bytes =\n typeof rec.quantity === \"number\" ? rec.quantity : dataBytesFromQuantity(rec.total?.quantityPerType);\n byDay.set(day, (byDay.get(day) ?? 0) + bytes);\n }\n return [...byDay.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, bytes]) => ({ date, mb: mbFromBytes(bytes) }));\n}\n\nexport function extractUsageBreakdown(json: unknown): { totalMb: number; countries: UsageCountrySlice[] } {\n const root = (json ?? {}) as Record<string, unknown>;\n const data = (root.data ?? root.usage ?? root.subscriberUsageOverPeriod ?? root) as Record<string, unknown> | undefined;\n const totalObj =\n data && typeof data === \"object\"\n ? ((data.total ?? (Array.isArray(data.usages) ? (data.usages[0] as { total?: unknown })?.total : undefined)) as\n | { quantityPerType?: unknown; quantityPerCountry?: unknown[] }\n | undefined)\n : undefined;\n const totalMb = mbFromBytes(dataBytesFromQuantity(totalObj?.quantityPerType));\n const rows = (Array.isArray(totalObj?.quantityPerCountry) ? totalObj.quantityPerCountry : []) as {\n name?: string;\n qty?: number;\n quantityPerOperator?: { name?: string; qty?: number }[];\n }[];\n const countries: UsageCountrySlice[] = rows.map((c) => ({\n country: c.name ?? \"Unknown\",\n mb: mbFromBytes(typeof c.qty === \"number\" ? c.qty : 0),\n operators: (c.quantityPerOperator ?? []).map((o) => ({\n name: o.name ?? \"Unknown\",\n mb: mbFromBytes(typeof o.qty === \"number\" ? o.qty : 0),\n })),\n }));\n return { totalMb, countries };\n}\n\nexport function parseSubscriberLocation(json: unknown): {\n latitude: number;\n longitude: number;\n accuracy?: number;\n at?: string;\n} | undefined {\n const root = (json ?? {}) as Record<string, unknown>;\n const loc = (root.subscriberLocation ??\n (root.data as Record<string, unknown> | undefined)?.subscriberLocation ??\n root.data ??\n root) as Record<string, unknown> | undefined;\n if (!loc || typeof loc !== \"object\") return undefined;\n const lat = loc.latitude;\n const lng = loc.longitude;\n if (typeof lat !== \"number\" || typeof lng !== \"number\") return undefined;\n const out: { latitude: number; longitude: number; accuracy?: number; at?: string } = {\n latitude: lat,\n longitude: lng,\n };\n if (typeof loc.accuracy === \"number\") out.accuracy = loc.accuracy;\n const at = typeof loc.dateTime === \"string\" ? loc.dateTime : typeof loc.at === \"string\" ? loc.at : undefined;\n if (at) out.at = at;\n return out;\n}\n\nexport interface NetworkEvent {\n timestamp: string;\n type: string;\n description: string;\n country?: string;\n operator?: string;\n mcc?: number;\n mnc?: number;\n protocol?: string;\n}\n\nfunction flattenNetworkEventGroups(payload: unknown): unknown[] {\n if (Array.isArray(payload)) return payload;\n if (!payload || typeof payload !== \"object\") return [];\n const rec = payload as Record<string, unknown>;\n if (Array.isArray(rec.events)) return rec.events;\n const groups = [\"mapAnswer\", \"s6aAnswer\", \"gyAnswer\", \"camelAnswer\", \"mocAnswer\", \"mtcAnswer\"] as const;\n const out: unknown[] = [];\n for (const key of groups) {\n const group = rec[key];\n if (group && typeof group === \"object\" && Array.isArray((group as { events?: unknown[] }).events)) {\n out.push(...((group as { events: unknown[] }).events));\n }\n }\n return out;\n}\n\nexport function extractNetworkEvents(json: unknown): NetworkEvent[] {\n const root = (json ?? {}) as Record<string, unknown>;\n const payload = root.subscriberNetworkEventsOverPeriod ?? root.data ?? root.events ?? root;\n const rows = flattenNetworkEventGroups(payload);\n return rows\n .map((row) => {\n const rec = (row ?? {}) as Record<string, unknown>;\n const timestamp = String(rec.eventTime ?? rec.timestamp ?? rec.time ?? rec.dateTime ?? \"\");\n const longCode = typeof rec.longCode === \"string\" ? rec.longCode : undefined;\n const shortCode = typeof rec.shortCode === \"string\" ? rec.shortCode : undefined;\n const type = String(rec.eventType ?? rec.type ?? longCode ?? shortCode ?? \"network\");\n const country = typeof rec.countryName === \"string\" ? rec.countryName : typeof rec.country === \"string\" ? rec.country : undefined;\n const operator = typeof rec.operator === \"string\" ? rec.operator : undefined;\n const mcc = typeof rec.mcc === \"number\" ? rec.mcc : undefined;\n const mnc = typeof rec.mnc === \"number\" ? rec.mnc : undefined;\n const protocol = typeof rec.protocol === \"string\" ? rec.protocol : undefined;\n const result = typeof rec.result === \"string\" ? rec.result : undefined;\n const parts = [type, operator, country, result].filter(Boolean);\n const event: NetworkEvent = {\n timestamp,\n type,\n description: parts.join(\" · \") || \"Network event\",\n };\n if (country) event.country = country;\n if (operator) event.operator = operator;\n if (mcc != null) event.mcc = mcc;\n if (mnc != null) event.mnc = mnc;\n if (protocol) event.protocol = protocol;\n return event;\n })\n .filter((e) => e.timestamp)\n .sort((a, b) => b.timestamp.localeCompare(a.timestamp));\n}\n\nexport function parseActivePeriod(json: unknown): { start?: string; end?: string } | undefined {\n const root = (json ?? {}) as Record<string, unknown>;\n const payload = (root.getSubscriberActivePeriod ?? root.data ?? root) as Record<string, unknown>;\n const period = (payload.period ?? payload) as Record<string, unknown>;\n const start = typeof period.start === \"string\" ? period.start : undefined;\n const end = typeof period.end === \"string\" ? period.end : undefined;\n if (!start && !end) return undefined;\n const out: { start?: string; end?: string } = {};\n if (start) out.start = start;\n if (end) out.end = end;\n return out;\n}\n\nexport interface EsimStatusCounts {\n active: number;\n suspended: number;\n inventory: number;\n other: number;\n total: number;\n}\n\nfunction emptyEsimStatusCounts(): EsimStatusCounts {\n return { active: 0, suspended: 0, inventory: 0, other: 0, total: 0 };\n}\n\nfunction addEsimStatusRow(acc: EsimStatusCounts, rec: Record<string, unknown>): void {\n const count = Number(rec.count ?? 0);\n if (!Number.isFinite(count) || count === 0) return;\n // Live OCS (account 5259, 2026-08-18): statusNum 0=Free, 2=Affected.\n // Do not trust statusNum 2 as Suspended. Prefer statusStr when present.\n const str = String(rec.statusStr ?? rec.name ?? rec.status ?? \"\").toUpperCase();\n if (str === \"ACTIVATED\" || str === \"ACTIVE\" || str === \"AFFECTED\") {\n acc.active += count;\n return;\n }\n if (str === \"SUSPENDED\") {\n acc.suspended += count;\n return;\n }\n if (str === \"FREE\" || str === \"INVENTORY\" || str === \"NOT_ACTIVATED\" || str === \"AVAILABLE\") {\n acc.inventory += count;\n return;\n }\n const num = rec.statusNum;\n if (typeof num === \"number\") {\n if (num === 1) acc.active += count;\n else if (num === 0) acc.inventory += count;\n else if (num === 2) acc.suspended += count;\n else acc.other += count;\n return;\n }\n acc.other += count;\n}\n\n/**\n * Unwrap esimStatusPerAccount.\n * Live shape: { account: [{ sponsor: [{ esim: { status: [{ statusNum, statusStr, count }] } }] }] }\n * statusNum: 0 Free / 1 Activated / 2 Suspended / 3+ other.\n */\nexport function extractEsimStatusCounts(json: unknown): EsimStatusCounts {\n const acc = emptyEsimStatusCounts();\n const root = (json ?? {}) as Record<string, unknown>;\n const payload = (root.data ?? root.esimStatusPerAccount ?? root) as Record<string, unknown> | unknown[];\n const accounts: unknown[] = Array.isArray(payload)\n ? payload\n : Array.isArray((payload as Record<string, unknown>).account)\n ? ((payload as Record<string, unknown>).account as unknown[])\n : [];\n\n for (const account of accounts) {\n const rec = (account ?? {}) as Record<string, unknown>;\n const sponsors = Array.isArray(rec.sponsor) ? rec.sponsor : [];\n if (sponsors.length > 0) {\n for (const sp of sponsors) {\n const esim = ((sp as Record<string, unknown>)?.esim ?? {}) as Record<string, unknown>;\n const statuses = Array.isArray(esim.status) ? esim.status : [];\n for (const row of statuses) addEsimStatusRow(acc, (row ?? {}) as Record<string, unknown>);\n }\n continue;\n }\n acc.active += Number(rec.active ?? 0);\n acc.suspended += Number(rec.suspended ?? 0);\n acc.inventory += Number(rec.inventory ?? rec.notActivated ?? rec.free ?? 0);\n acc.other += Number(rec.other ?? rec.terminated ?? 0);\n }\n\n acc.total = acc.active + acc.suspended + acc.inventory + acc.other;\n return acc;\n}\n\n/**\n * Params for `esimStatusPerAccount`.\n *\n * OCS rejects a payload carrying BOTH keys with error 2\n * (\"Cannot provide both 'resellerId' and 'accountId'\"), so an explicit\n * accountId must suppress the reseller default rather than accompany it.\n * Verified live 2026-08-19 against account 5259 / reseller 1170.\n *\n * Accepts the historical bare-accountId form as well as the\n * `{ accountId?, resellerId? }` form the MCP/CLI reseller-default path needs.\n */\nexport function esimStatusPerAccountParams(accountId: number): { accountId: number };\nexport function esimStatusPerAccountParams(args: {\n accountId: number;\n resellerId?: number;\n}): { accountId: number };\nexport function esimStatusPerAccountParams(args: { resellerId: number }): { resellerId: number };\nexport function esimStatusPerAccountParams(args: {\n accountId?: number;\n resellerId?: number;\n}): { accountId: number } | { resellerId: number };\nexport function esimStatusPerAccountParams(\n args: number | { accountId?: number; resellerId?: number },\n): { accountId: number } | { resellerId: number } {\n if (typeof args === \"number\") return { accountId: args };\n if (args.accountId !== undefined) return { accountId: args.accountId };\n if (args.resellerId !== undefined) return { resellerId: args.resellerId };\n throw new Error(\"esimStatusPerAccountParams needs an accountId or a resellerId\");\n}\n\n/**\n * Data allowance used to express an \"unlimited\" package template.\n *\n * OCS has no unlimited flag. Templates were previously created with\n * `databyte: 0`, which reads as unlimited to a human but is a literal zero to\n * the charging engine — and, critically, to the throttling engine.\n *\n * `throttlingThreshold1Perc` is a percentage OF THE BUNDLE and\n * `throttlingThreshold1Limit` is the throttled speed in Kbps. With\n * `databyte: 0` the trigger point is `perc% x 0 = 0`, so an \"unlimited, full\n * speed until N, then slow\" plan has no computable full-speed window at all,\n * and every storefront that reads `databyte` renders \"0 MB\".\n *\n * Unlimited is therefore a real, high ceiling and the throttle is configured\n * separately through the throttling fields.\n */\nexport const UNLIMITED_DATA_BYTES = 100 * 1024 * 1024 * 1024;\n\n/** Bytes per MB, for turning a throttle trigger in MB into a bundle percentage. */\nconst BYTES_PER_MB = 1_048_576;\n\n/** Field names OCS accepts for the data allowance on a package template. */\nconst TEMPLATE_DATA_KEYS = [\"databyte\", \"dataBytes\", \"dataLimit\"] as const;\n\nfunction readTemplateDataBytes(t: Record<string, unknown>): number | undefined {\n for (const key of TEMPLATE_DATA_KEYS) {\n const v = t[key];\n if (typeof v === \"number\" && Number.isFinite(v)) return v;\n }\n return undefined;\n}\n\n/**\n * Percentage of `totalBytes` that `mb` megabytes represents, clamped to 1..100\n * and rounded, because OCS only accepts whole percentages.\n */\nexport function throttlePercentFromMb(mb: number, totalBytes = UNLIMITED_DATA_BYTES): number {\n if (!Number.isFinite(mb) || mb <= 0 || totalBytes <= 0) return 100;\n const pct = Math.round(((mb * BYTES_PER_MB) / totalBytes) * 100);\n return Math.min(100, Math.max(1, pct));\n}\n\n/**\n * Throttled-speed values OCS accepts for `throttlingThreshold1Limit` /\n * `throttlingThreshold2Limit`, in Kbps.\n *\n * This is a BITRATE enum, the same family as `hlrSetBitrate` (KB_32…KB_102400) —\n * NOT a data allowance. Reading it as megabytes is the mistake that produced the\n * current Mango catalog: the bitrate field was filled in as if it were the\n * bundle, and `databyte` was left at 0. Validating against this list is what\n * catches that inversion at the call site instead of in production.\n */\nexport const OCS_THROTTLE_KBPS = [\n 128, 256, 384, 512, 1024, 3072, 5120, 7680, 10240, 20480, 51200, 102400,\n] as const;\n\n/**\n * `recurringPeriodicityType` values OCS accepts.\n *\n * Per the vendor docs (docs.esimvault.cloud/ocs-api, createPrepaidPackageTemplate\n * and modifyPPTRecurring): \"Recurring periodicity type. Possible values:\n * 0=Daily, 1=Weekly, 2=Monthly.\"\n *\n * There is no 3. The live Mango v2 templates were created with\n * `recurringPeriodicityType: 3` and an internal comment describing it as\n * \"3=MONTHLY\", so their recurrence is configured with a value outside the\n * documented range. Verified 2026-08-20: OCS echoes this field back exactly as\n * sent (a template created with 0 reads back as 0), so the 3 is not a\n * read-side encoding difference.\n */\nexport const OCS_PERIODICITY = { DAILY: 0, WEEKLY: 1, MONTHLY: 2 } as const;\n\nconst OCS_PERIODICITY_VALUES = [0, 1, 2] as const;\n\n/**\n * Ceiling multipliers applied to the full-speed window. Each one divides 100\n * exactly, so the resulting trigger percentage is a whole number and OCS never\n * has to round it. Ordered smallest-first: the first that clears the\n * exhaustion check wins, keeping the bundle no larger than it needs to be.\n */\nconst CEILING_MULTIPLIERS = [20, 25, 50, 100] as const;\n\nconst SECONDS_PER_DAY = 86_400;\n\n/**\n * Bytes a subscriber could pull at `kbps` for `periodDays` straight.\n *\n * Network bitrates are decimal (1 kbps = 1000 bits/s), so this deliberately\n * does not use the 1024-based BYTES_PER_MB.\n */\nexport function throttledBytesPerPeriod(kbps: number, periodDays = 1): number {\n if (!Number.isFinite(kbps) || kbps <= 0) return 0;\n return (kbps * 1000 * SECONDS_PER_DAY * Math.max(1, periodDays)) / 8;\n}\n\n/**\n * Pick a bundle ceiling for an unlimited plan.\n *\n * Two things have to hold at once:\n * 1. The full-speed window must be an exact whole percentage of the ceiling,\n * because that percentage is the only way OCS can express the trigger.\n * 2. The bundle must be unreachable. A plan sold as unlimited must not run out,\n * so the ceiling has to clear the window PLUS everything a subscriber could\n * draw at the throttled rate for the rest of the period.\n *\n * Returns the ceiling in bytes and the whole-number trigger percentage.\n */\nexport function unlimitedCeilingFor(\n windowMb: number,\n throttleKbps?: number,\n periodDays = 1,\n): { ceilingBytes: number; percent: number } {\n const windowBytes = windowMb * BYTES_PER_MB;\n const unreachableAbove = windowBytes + throttledBytesPerPeriod(throttleKbps ?? 0, periodDays);\n\n for (const multiplier of CEILING_MULTIPLIERS) {\n const ceilingBytes = windowBytes * multiplier;\n if (ceilingBytes > unreachableAbove) {\n return { ceilingBytes, percent: Math.round(100 / multiplier) };\n }\n }\n\n // Even a 100x ceiling could not outrun the throttled rate. Fall back to the\n // widest representable spread rather than silently shipping a bundle the\n // subscriber can drain.\n const last = CEILING_MULTIPLIERS[CEILING_MULTIPLIERS.length - 1]!;\n return { ceilingBytes: windowBytes * last, percent: Math.round(100 / last) };\n}\n\n/**\n * Normalize a package-template payload before it goes to OCS.\n *\n * Callers describe the product the way it is sold and the OCS fields are derived\n * from that:\n *\n * - `throttleAfterMb` — the full-speed window, e.g. 500 for \"500 MB/day\".\n * - `throttleKbps` — the speed after the window, e.g. 128. Must be an OCS\n * bitrate ({@link OCS_THROTTLE_KBPS}).\n * - `periodDays` — the package period; 1 for a daily bucket.\n *\n * An unlimited template (explicit `unlimited: true`, or a zero/absent allowance)\n * never gets a 0 allowance. Its ceiling is sized so the window lands on a whole\n * trigger percentage AND the bundle cannot be drained even at the throttled rate\n * for the whole period — a plan sold as unlimited must not run out.\n *\n * Returns a new object; the input is not mutated.\n */\nexport function normalizePackageTemplate(\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...template };\n\n const unlimitedFlag = out.unlimited === true;\n const current = readTemplateDataBytes(out);\n const isUnlimited = unlimitedFlag || current === undefined || current === 0;\n\n const throttleAfterMb = typeof out.throttleAfterMb === \"number\" ? out.throttleAfterMb : undefined;\n const throttleKbps = typeof out.throttleKbps === \"number\" ? out.throttleKbps : undefined;\n const periodDays = typeof out.perioddays === \"number\" ? out.perioddays : 1;\n\n if (throttleKbps !== undefined && !OCS_THROTTLE_KBPS.includes(throttleKbps as never)) {\n throw new Error(\n `throttleKbps ${throttleKbps} is not an OCS bitrate. ` +\n `Allowed: ${OCS_THROTTLE_KBPS.join(\", \")}. ` +\n `This field is a SPEED in Kbps, not a data allowance.`,\n );\n }\n\n const periodicity = out.recurringPeriodicityType;\n if (periodicity !== undefined && !OCS_PERIODICITY_VALUES.includes(periodicity as never)) {\n throw new Error(\n `recurringPeriodicityType ${periodicity} is out of range. ` +\n `Allowed: 0=Daily, 1=Weekly, 2=Monthly. ` +\n `(The live v2 Mango templates use 3, which is not a valid value.)`,\n );\n }\n\n // A daily or weekly recurring template must say how many packages to grant:\n // OCS only lets nbOccurrence default for MONTHLY templates.\n if (\n out.recurring === true &&\n (periodicity === OCS_PERIODICITY.DAILY || periodicity === OCS_PERIODICITY.WEEKLY) &&\n out.nbOccurrence === undefined\n ) {\n throw new Error(\n \"nbOccurrence is required for a daily or weekly recurring template; \" +\n \"OCS only treats it as optional for monthly templates.\",\n );\n }\n\n if (throttleKbps !== undefined) out.throttlingThreshold1Limit = throttleKbps;\n delete out.throttleKbps;\n\n if (isUnlimited) {\n let ceiling = UNLIMITED_DATA_BYTES;\n if (throttleAfterMb !== undefined && throttleAfterMb > 0) {\n const sized = unlimitedCeilingFor(throttleAfterMb, throttleKbps, periodDays);\n ceiling = sized.ceilingBytes;\n out.throttlingThreshold1Perc = sized.percent;\n }\n // Write the ceiling to whichever key the caller used, defaulting to the\n // OCS-native `databyte`, so no stale 0 is left behind on a sibling key.\n for (const key of TEMPLATE_DATA_KEYS) {\n if (key in out) out[key] = ceiling;\n }\n if (!TEMPLATE_DATA_KEYS.some((k) => k in out)) out.databyte = ceiling;\n }\n delete out.unlimited;\n\n if (throttleAfterMb !== undefined) {\n // A finite bundle keeps the plain percentage-of-bundle reading; an unlimited\n // one already had its percentage set alongside the ceiling above.\n if (!isUnlimited) {\n const total = readTemplateDataBytes(out) ?? UNLIMITED_DATA_BYTES;\n out.throttlingThreshold1Perc = throttlePercentFromMb(throttleAfterMb, total);\n }\n out.throttlingActive = out.throttlingActive ?? true;\n }\n delete out.throttleAfterMb;\n\n return out;\n}\n\n/**\n * Normalize a PARTIAL template update (`modify_template_core`).\n *\n * Unlike {@link normalizePackageTemplate} this never injects an allowance the\n * caller did not send — a rename must stay a rename. It only rewrites an\n * allowance that is explicitly present and zero, and expands `throttleAfterMb`.\n *\n * Returns a new object; the input is not mutated.\n */\nexport function normalizePackageTemplateChanges(\n changes: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...changes };\n\n const explicitlyUnlimited = out.unlimited === true;\n for (const key of TEMPLATE_DATA_KEYS) {\n if (!(key in out)) continue;\n if (out[key] === 0 || explicitlyUnlimited) out[key] = UNLIMITED_DATA_BYTES;\n }\n if (explicitlyUnlimited && !TEMPLATE_DATA_KEYS.some((k) => k in out)) {\n out.databyte = UNLIMITED_DATA_BYTES;\n }\n delete out.unlimited;\n\n const throttleAfterMb = out.throttleAfterMb;\n if (typeof throttleAfterMb === \"number\") {\n // Percentages are relative to the bundle; without one in this payload the\n // unlimited ceiling is the only sane basis.\n const total = readTemplateDataBytes(out) ?? UNLIMITED_DATA_BYTES;\n out.throttlingThreshold1Perc = throttlePercentFromMb(throttleAfterMb, total);\n }\n delete out.throttleAfterMb;\n\n return out;\n}\n\n/**\n * True when a template's allowance is the unlimited sentinel or larger.\n * Storefronts should render these as \"Unlimited\" plus the throttle terms,\n * never as a raw byte count.\n */\nexport function isUnlimitedDataBytes(bytes: number | undefined | null): boolean {\n return typeof bytes === \"number\" && bytes >= UNLIMITED_DATA_BYTES;\n}\n\n/** Surfaces that must stay aligned on these builders. Used by the contract test. */\nexport const OCS_PARITY_SURFACES = [\"api\", \"console\", \"mcp-stdio\", \"mcp-server\", \"cli\"] as const;\n","/**\n * OCS `listSubscriber` — the request contract and the response shape, in one\n * place both MCP surfaces import.\n *\n * WHY THIS FILE EXISTS\n * `apps/mcp-server` and `apps/mcp-stdio` each held their own copy of this\n * logic, and the copies disagreed on the same tool call:\n *\n * Worker : imsiPrefix / iccidPrefix / msisdnPrefix, status+offset+limit\n * applied client-side\n * CLI : imsi / iccid / msisdn, status+offset FORWARDED to OCS\n *\n * OCS accepts exactly five properties and rejects the whole request\n * otherwise (\"Unrecognized field status ... 5 known properties\"), so a status\n * filter the Worker quietly ignored would error outright on the CLI.\n *\n * Both copies also guarded their filters on `Array.isArray(raw)`. The real\n * response is an ENVELOPE, so that guard was never true and `status`,\n * `offset` and `limit` all silently did nothing. Measured on the live fleet:\n * `limit: 2` returned 100 rows, 208,903 characters — while the tool's own\n * description told callers to always set `limit` to avoid unbounded fetches.\n *\n * This is the same failure class as ocs-param-shapes.ts, which exists because\n * \"prose comments at each call site did not stop it\". One shared\n * implementation is the enforcement.\n */\n\n/**\n * The ONLY properties OCS `listSubscriber` accepts.\n *\n * Anything else fails the entire request. Proven against the live server, not\n * inferred from ocs-methods.json (see the note in ocs-param-shapes.ts about why\n * that file cannot answer this).\n */\nexport const OCS_LIST_SUBSCRIBER_PARAMS = [\n \"accountId\",\n \"activationCode\",\n \"imsiPrefix\",\n \"iccidPrefix\",\n \"msisdnPrefix\",\n] as const;\n\n/** Caller-facing filters, as the MCP tool exposes them. */\nexport interface ListSubscriberArgs {\n imsi?: string;\n iccid?: string;\n activationCode?: string;\n accountId?: number;\n msisdn?: string;\n status?: string;\n offset?: number;\n limit?: number;\n}\n\n/**\n * Map tool args to OCS params.\n *\n * `status`, `offset` and `limit` are deliberately absent from the result: OCS\n * cannot take them, and they are applied to the returned rows instead.\n *\n * The *Prefix names matter — OCS matches them as PREFIXES, not exact values. A\n * full ICCID still matches, being a prefix of itself, but a partial value\n * legitimately returns several rows.\n */\nexport function buildListSubscriberParams(\n args: ListSubscriberArgs,\n): Record<string, unknown> {\n const params: Record<string, unknown> = {};\n if (args.imsi) params.imsiPrefix = args.imsi;\n if (args.iccid) params.iccidPrefix = args.iccid;\n if (args.activationCode) params.activationCode = args.activationCode;\n if (args.accountId !== undefined) params.accountId = args.accountId;\n if (args.msisdn) params.msisdnPrefix = args.msisdn;\n return params;\n}\n\n/**\n * Read a subscriber row's status name.\n *\n * OCS returns `status` as an ARRAY of records whose own `status` field is the\n * name (\"Active\" / \"Inventory\" / \"Inactive\" / \"End of Life\"), not a plain\n * string. Comparing the array against a string silently matches nothing, so a\n * filter written against `row.status` quietly returns zero rows.\n */\nexport function extractSubscriberStatus(row: unknown): string | null {\n if (row === null || typeof row !== \"object\") return null;\n const raw = (row as Record<string, unknown>).status;\n if (typeof raw === \"string\" && raw.length > 0) return raw;\n if (Array.isArray(raw)) {\n for (const entry of raw) {\n if (entry && typeof entry === \"object\") {\n const name = (entry as Record<string, unknown>).status;\n if (typeof name === \"string\" && name.length > 0) return name;\n }\n }\n }\n return null;\n}\n\n/**\n * Pull subscriber rows out of whatever shape listSubscriber returned.\n *\n * Live shape is `{hasMore, nbFound, subscriberList: [...]}`. Returns\n * `rows: null` for anything unrecognised so callers pass the payload through\n * rather than inventing an empty list.\n */\nexport function unwrapSubscriberList(raw: unknown): {\n rows: unknown[] | null;\n envelope: Record<string, unknown> | null;\n} {\n if (Array.isArray(raw)) return { rows: raw, envelope: null };\n if (raw !== null && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n if (Array.isArray(obj.subscriberList)) {\n return { rows: obj.subscriberList as unknown[], envelope: obj };\n }\n }\n return { rows: null, envelope: null };\n}\n\n/**\n * Put filtered rows back into the caller's original shape, with honest counts.\n *\n * `nbFound` becomes the number of rows that MATCHED the filters — not OCS's\n * original total and not the page size. Reporting `nbFound: 100` beside 2 rows\n * is how a truncated result gets read as a complete one.\n *\n * Adds `truncated` + `note` when rows were dropped, naming the params that\n * would narrow the request: the same never-silent rule as bounded-results.ts.\n */\nexport function rewrapSubscriberList(\n raw: unknown,\n envelope: Record<string, unknown> | null,\n rows: unknown[] | null,\n filtered: unknown[] | null,\n matched: number | undefined,\n): unknown {\n if (rows === null || filtered === null) return raw;\n\n const total = matched ?? filtered.length;\n const dropped = total - filtered.length;\n\n // Caller originally got a bare array; keep giving them one.\n if (envelope === null) return filtered;\n\n const out: Record<string, unknown> = {\n ...envelope,\n subscriberList: filtered,\n nbFound: total,\n hasMore: filtered.length < total,\n };\n\n if (dropped > 0) {\n out.truncated = true;\n out.note =\n `${dropped} of ${total} matching subscribers omitted by offset/limit. ` +\n `Narrow with \\`status\\`, \\`accountId\\` or \\`iccid\\`, or page with \\`offset\\`.`;\n }\n\n return out;\n}\n\n/**\n * The whole client-side filter pipeline: status -> offset -> limit, re-wrapped.\n *\n * Both MCP surfaces call this, so they cannot drift on filter ORDER either —\n * offset-before-limit is what a caller paging through results expects.\n */\nexport function applySubscriberFilters(\n raw: unknown,\n args: ListSubscriberArgs,\n): unknown {\n const { rows, envelope } = unwrapSubscriberList(raw);\n let filtered = rows;\n\n if (filtered && args.status) {\n const wanted = String(args.status).trim().toLowerCase();\n filtered = filtered.filter((row) => {\n const status = extractSubscriberStatus(row);\n return status !== null && status.toLowerCase() === wanted;\n });\n }\n\n const matched = filtered?.length;\n\n if (filtered && typeof args.offset === \"number\" && args.offset > 0) {\n filtered = filtered.slice(args.offset);\n }\n if (filtered && typeof args.limit === \"number\" && args.limit >= 0) {\n filtered = filtered.slice(0, args.limit);\n }\n\n return rewrapSubscriberList(raw, envelope, rows, filtered, matched);\n}\n","/**\n * The tool catalog `carrier_ask` routes against — shared by both MCP surfaces.\n *\n * WHY THIS IS SHARED\n * `carrier_ask` exists twice: apps/mcp-server (the hosted Worker) and\n * apps/mcp-stdio (the published CLI, which is what `carrier ask` actually\n * runs). Fixing the Worker's router in #731 did nothing for the CLI, because\n * the CLI holds its own 430-line hardcoded catalog. Same lesson as\n * list-subscriber.ts and ocs-param-shapes.ts: the fix has to live in one\n * place or it only ever lands on one surface.\n *\n * WHY THE ROUTER WAS WRONG\n * It chose from short hand-written hints and, on the Worker, a generated stub\n * for anything not hand-written:\n *\n * \"Carrier MCP tool `high_cost_subscribers` (high cost subscribers).\n * Required scope: read. Use when the user intent clearly matches this\n * tool name or its domain.\"\n *\n * That is name matching. Measured against the live fleet:\n *\n * \"what is the total cost of the mbs of my users\"\n * -> high_cost_subscribers (a >80%-utilisation report, not a total)\n * \"which reseller sits directly above Lifecycle Innovations\"\n * -> list_reseller_accounts (answer is get_reseller_info.parentName)\n * \"what is the VoIP charging plan name for this reseller\"\n * -> a tool returning a bare {status: OK}\n *\n * Every one returned real data about a different question, which is worse\n * than returning nothing: it reads as an answer.\n *\n * Meanwhile the tools carry descriptions written for exactly this job, and 70\n * of 123 carry an explicit \"Do NOT use this to X — use Y instead\" naming the\n * tool that IS right. The router never saw them.\n */\n\n/** Per-process registry of the descriptions tools registered with. */\nconst descriptions = new Map<string, string>();\n\n/** Called from each app's registerTool wrapper. */\nexport function recordToolDescription(name: string, description: string): void {\n if (description) descriptions.set(name, description);\n}\n\nexport function getToolDescription(name: string): string | undefined {\n return descriptions.get(name);\n}\n\nexport function recordedToolCount(): number {\n return descriptions.size;\n}\n\n/** Test seam — never called in production. */\nexport function resetToolDescriptions(): void {\n descriptions.clear();\n}\n\n/**\n * Trim a description to what the router needs to choose correctly.\n *\n * All 123 full descriptions are ~63 KB (~15.8k tokens) per routing call. Most\n * of each is parameter and return detail the router does not use — it picks a\n * tool, it does not call one. What it does need is the opening statement of\n * purpose and, above all, the \"Do NOT\" steers, since those are exactly the\n * near-miss pairs it was confusing.\n *\n * Keeps the first two sentences plus every sentence containing a Do NOT steer.\n * Splitting on \". \" followed by a capital keeps \"e.g.\" and decimals intact.\n */\nexport function routerDescription(full: string | undefined): string | undefined {\n if (!full) return undefined;\n\n const sentences = full\n .split(/(?<=\\.)\\s+(?=[A-Z`'\"])/)\n .map((s) => s.trim())\n .filter(Boolean);\n\n const kept = [\n ...sentences.slice(0, 2),\n ...sentences.slice(2).filter((s) => /\\bDo NOT\\b/i.test(s)),\n ];\n\n return kept.length ? kept.join(\" \") : full;\n}\n\nexport interface RouterTool {\n name: string;\n description: string;\n input_schema: { type: \"object\"; properties: Record<string, unknown>; required?: string[] };\n}\n\nexport interface BuildRouterCatalogOptions {\n /** Every routable tool name. */\n toolNames: Iterable<string>;\n /** Hand-written entries: richer input hints, and a fallback description. */\n curated: readonly RouterTool[];\n /** The clarify sentinel, always offered first. */\n clarifyTool: RouterTool;\n /** Scope lookup, used only for the last-resort stub text. */\n getScope: (name: string) => string;\n}\n\n/**\n * Build the catalog, preferring each tool's OWN registered description.\n *\n * Order of preference per tool:\n * 1. the recorded description, trimmed — what the tool actually does\n * 2. the curated hand-written hint — for tools not registered on this\n * surface (docs-only entries such as stripe_proxy_call)\n * 3. a name-only stub — last resort\n *\n * (2) and (3) used to be the only sources. Curated input_schema hints are kept\n * either way: they tell the router which params are worth extracting.\n *\n * Call this per route, not at module scope — descriptions are only populated\n * once tools have registered.\n */\nexport function buildRouterCatalog(opts: BuildRouterCatalogOptions): RouterTool[] {\n const curatedByName = new Map(opts.curated.map((t) => [t.name, t]));\n const tools: RouterTool[] = [];\n\n for (const name of [...opts.toolNames].sort()) {\n if (name === \"carrier_ask\" || name === \"carrier_clarify\") continue;\n\n const curated = curatedByName.get(name);\n const real = routerDescription(getToolDescription(name));\n\n tools.push({\n name,\n description:\n real ??\n curated?.description ??\n `Carrier MCP tool \\`${name}\\`. Required scope: ${opts.getScope(name)}. ` +\n `Use when the user intent clearly matches this tool name or its domain.`,\n input_schema: curated?.input_schema ?? { type: \"object\", properties: {} },\n });\n }\n\n return [opts.clarifyTool, ...tools];\n}\n\n/**\n * Shared router rules.\n *\n * The original prompt had no way to decline: rule 1 said pick the best match\n * and clarify was discouraged, so a near-miss always won over saying nothing.\n * Rules 7-9 are the corrective, and they are the reason the three live failures\n * above should now come back as clarify rather than as confident wrong data.\n */\nexport const ROUTER_RULES = `Rules:\n1. Pick the single best-matching tool. Never pick carrier_ask (the router itself).\n2. Extract any parameters mentioned in the intent (ICCID, account IDs, amounts, etc.) as the tool's input.\n3. Use carrier_clarify when the intent is ambiguous between several tools, AND when no tool actually answers the question. Returning nothing useful is correct and expected; a near-miss is not.\n4. DESTRUCTIVE tools are flagged in their descriptions — still pick them if they match; the safety layer handles the confirm flow.\n5. Only include params explicitly mentioned in the intent.\n6. Context fields (iccid, account_id, reseller_id) from the routing context take precedence.\n7. A tool answers the question only if its description says it does. Do not infer capability from the tool's NAME: several names share words with unrelated questions, and picking on the name alone has produced confidently wrong answers.\n8. Read the \"Do NOT use this to …\" steers in a description as hard exclusions. They exist because that tool is the common wrong answer for a neighbouring question, and they name the tool to use instead.\n9. Prefer carrier_clarify over a tool that would return real data about a different question. Answering \"which subscribers erode margin\" when the user asked \"what do my users' megabytes cost in total\" is worse than admitting the gap, because the output looks like an answer.`;\n","/**\n * Read a list out of an OCS response, whatever envelope it arrived in.\n *\n * WHY THIS EXISTS\n * ---------------\n * OCS list methods do not return arrays. Each wraps its rows in a differently\n * named envelope:\n *\n * listSubscriber { hasMore, nbFound, subscriberList: [...] }\n * listPrepaidPackageTemplate { template: [...] }\n * listDetailedLocationZone { listDetailedLocationZone | locationZone | zone | zones: [...] }\n * listResellerAccount { reseller: [ { account: [...] } ] }\n *\n * Code that tests `Array.isArray(result.data)` against any of these gets false\n * and silently does nothing. That single mistake has now been found in six\n * places and cost real behaviour every time:\n *\n * list_subscribers status/offset/limit all inert; limit:2 returned 100 rows\n * high_cost_subscribers could not flag a single subscriber, ever\n * optimize_package usage section never rendered\n * churn_risk dead in BOTH branches of its usage factor\n * marketing_intelligence fleet distribution and catalogue sections empty\n *\n * `bounded-results.ts:105-110` already carried the zone key list; this\n * generalises it so every caller shares one reader instead of each inventing a\n * guard that happens to be wrong.\n *\n * NOT a schema validator. It answers exactly one question — \"where are the\n * rows?\" — and returns [] when there are none, so callers can treat an empty\n * fleet and a missing envelope the same way without a special case.\n */\n\n/** Envelope keys per OCS method, most specific first. */\nexport const OCS_LIST_KEYS = {\n subscribers: [\"subscriberList\"],\n templates: [\"template\", \"prepaidPackageTemplate\"],\n zones: [\"listDetailedLocationZone\", \"locationZone\", \"zone\", \"zones\"],\n resellers: [\"reseller\"],\n packages: [\"prepaidPackage\", \"package\"],\n} as const;\n\nexport type OcsListKind = keyof typeof OCS_LIST_KEYS;\n\n/**\n * Extract the row array from an OCS response.\n *\n * Accepts a bare array unchanged, so recorded fixtures and any older OCS\n * version that really did return one keep working.\n *\n * @param data the raw OCS response\n * @param keys envelope keys to try, in order. Pass an OcsListKind for the\n * known method families, or an explicit list for anything else.\n */\nexport function ocsArray<T = unknown>(\n data: unknown,\n keys: OcsListKind | readonly string[],\n): T[] {\n if (Array.isArray(data)) return data as T[];\n if (data === null || typeof data !== \"object\") return [];\n\n const candidates: readonly string[] =\n typeof keys === \"string\" ? OCS_LIST_KEYS[keys] : keys;\n\n const obj = data as Record<string, unknown>;\n for (const key of candidates) {\n const v = obj[key];\n if (Array.isArray(v)) return v as T[];\n }\n return [];\n}\n\n/**\n * Row count without materialising the array.\n *\n * Prefer this over `ocsArray(...).length` where only the count matters — it\n * reads the same envelope and makes the intent obvious at the call site.\n */\nexport function ocsCount(data: unknown, keys: OcsListKind | readonly string[]): number {\n return ocsArray(data, keys).length;\n}\n\n/**\n * Read a subscriber's ICCID from a listSubscriber row.\n *\n * WHY THIS IS NOT `row.iccid`\n * ---------------------------\n * A listSubscriber row has NO root `iccid`. Captured from the live fleet, its\n * keys are:\n *\n * account, accountId, activationDate, allowedData, allowedMoc, allowedMosms,\n * allowedMtc, allowedMtsms, balance, batchId, hotspotBlocked, imsiList,\n * multiImsi, phoneNumberList, prepaid, reseller, resellerId, sim, status,\n * subscriberId, useAccountForCharging\n *\n * The ICCID lives at `imsiList[0].iccid`. `sim` looks like the obvious home and\n * is not — it carries activationCode, esim, pin/puk and smdpServer, but no\n * ICCID.\n *\n * `String(row.iccid ?? \"\")` therefore yields \"\", and every downstream\n * per-subscriber OCS call made with that empty string returns nothing. That is\n * why high_cost_subscribers still flagged nobody after its usage-envelope bug\n * was fixed: it was asking OCS about a subscriber with no identifier.\n *\n * Returns \"\" when no ICCID can be found, so callers can skip a row instead of\n * calling OCS with a blank identifier.\n */\nexport function subscriberIccid(row: unknown): string {\n if (row === null || typeof row !== \"object\") return \"\";\n const r = row as Record<string, unknown>;\n\n // Canonical location.\n const imsiList = r.imsiList;\n if (Array.isArray(imsiList)) {\n for (const entry of imsiList) {\n if (entry && typeof entry === \"object\") {\n const v = (entry as Record<string, unknown>).iccid;\n if (typeof v === \"string\" && v.length > 0) return v;\n if (typeof v === \"number\") return String(v);\n }\n }\n }\n\n // Shapes seen elsewhere: getSingleSubscriber nests differently, and some\n // recorded fixtures are flattened. Accept those rather than returning \"\".\n for (const key of [\"iccid\", \"ICCID\"]) {\n const v = r[key];\n if (typeof v === \"string\" && v.length > 0) return v;\n if (typeof v === \"number\") return String(v);\n }\n const sim = r.sim;\n if (sim && typeof sim === \"object\") {\n const v = (sim as Record<string, unknown>).iccid;\n if (typeof v === \"string\" && v.length > 0) return v;\n if (typeof v === \"number\") return String(v);\n }\n\n return \"\";\n}\n","/**\n * Model-backed analysis of pre-computed fleet facts.\n *\n * WHAT THIS IS FOR\n * ----------------\n * The intelligence composites compute real numbers by fanning out OCS calls —\n * usage, utilisation, cost per GB, days to exhaustion. That part is accurate and\n * must stay deterministic. What they lacked was judgement: they ended in a\n * hardcoded verdict sentence, which is how \"high_cost_subscribers\" answered a\n * question about total data cost with \"Fleet margins look healthy.\"\n *\n * This adds the judgement and nothing else. The model receives facts it did not\n * compute and cannot change, and returns prose about them.\n *\n * THE NUMBERS RULE, AND HOW IT IS ENFORCED\n * ----------------------------------------\n * A model figure presented as a measurement is the worst failure this layer\n * could have — worse than no analysis, because it looks like data. Three\n * mechanisms, in order of strength:\n *\n * 1. STRUCTURE. The model answers through a tool schema whose fields are all\n * strings and enums. There is no numeric field for it to fill, so it\n * cannot return a figure as data.\n * 2. SUBSTITUTION. To cite a number it emits a placeholder — {balance} — and\n * we replace it with the Worker-formatted `display` string. The rendered\n * figure therefore always comes from the computed Fact.\n * 3. VALIDATION. Any bare digit left in the model's prose is rejected and the\n * call falls back to the deterministic verdict. Cheap, and it catches a\n * model that ignores (2).\n *\n * NEVER THROWS\n * ------------\n * Every failure — disabled, no credentials, wrong tier, out of budget, rate\n * limited, timeout, malformed output, validation failure — returns the\n * deterministic verdict with a `reason`. Callers have one code path, and a\n * Bedrock outage degrades the report rather than breaking the tool.\n */\n\n/** A pre-computed number or label. Computed in the Worker, never by the model. */\nexport interface Fact {\n /** Stable slug the model cites as {key}. */\n key: string;\n /** Human label, e.g. \"Daily average\". */\n label: string;\n value: number | string | boolean | null;\n unit: \"bytes\" | \"days\" | \"pct\" | \"count\" | \"eur\" | \"kbps\" | \"iso8601\" | \"text\";\n /** Worker-formatted for display, e.g. \"2.14 GB\". This is what gets rendered. */\n display: string;\n}\n\n/** The verdict today's threshold code produces. Always the fallback. */\nexport interface DeterministicVerdict {\n severity: string;\n findings: string[];\n actions: string[];\n}\n\nexport interface AnalysisRequest {\n kind: string;\n /** The only tenant-derived content that reaches the model. */\n facts: Fact[];\n /** Pseudonymised labels only: { subscriber: \"SUB-7f3a1c9e\" }. Never a raw ICCID. */\n entities?: Record<string, string>;\n deterministic: DeterministicVerdict;\n maxOutputTokens?: number;\n}\n\nexport type AnalysisPath = \"model\" | \"deterministic\";\n\nexport type FallbackReason =\n | \"disabled\"\n | \"no_credentials\"\n | \"tier\"\n | \"budget_exhausted\"\n | \"rate_limited\"\n | \"timeout\"\n | \"error\"\n | \"no_facts\"\n | \"malformed\"\n | \"invented_number\";\n\nexport interface AnalysisResult {\n path: AnalysisPath;\n reason?: FallbackReason;\n headline: string;\n assessment: string;\n recommendations: { action: string; why: string; priority: \"now\" | \"soon\" | \"watch\" }[];\n /** Fact keys the model actually cited, for auditing what it looked at. */\n usedFactKeys: string[];\n latencyMs?: number;\n}\n\n/**\n * The tool the model must answer through.\n *\n * Every field is a string or enum. There is deliberately no number anywhere —\n * that is mechanism (1). Adding a numeric field here would defeat the whole\n * design, so the shape is asserted by a test.\n */\nexport const ANALYSIS_TOOL_SCHEMA = {\n name: \"report_analysis\",\n description:\n \"Report your judgement of the supplied fleet facts. Cite any figure as a \" +\n \"{placeholder} using the fact's key — never write the number yourself.\",\n input_schema: {\n type: \"object\" as const,\n properties: {\n headline: {\n type: \"string\",\n description: \"One sentence, the single most important thing. Cite figures as {key}.\",\n },\n assessment: {\n type: \"string\",\n description:\n \"2-4 sentences explaining what the facts mean and why. Cite figures as {key}. \" +\n \"Say plainly when the facts do not support a conclusion.\",\n },\n recommendations: {\n type: \"array\",\n description: \"Concrete actions. Empty array when none is warranted.\",\n items: {\n type: \"object\",\n properties: {\n action: { type: \"string\", description: \"What to do. Cite figures as {key}.\" },\n why: { type: \"string\", description: \"Which fact drives it. Cite figures as {key}.\" },\n priority: { type: \"string\", enum: [\"now\", \"soon\", \"watch\"] },\n },\n required: [\"action\", \"why\", \"priority\"],\n },\n },\n used_fact_keys: {\n type: \"array\",\n description: \"Keys of the facts you actually used.\",\n items: { type: \"string\" },\n },\n },\n required: [\"headline\", \"assessment\", \"recommendations\", \"used_fact_keys\"],\n },\n} as const;\n\nexport const ANALYSIS_SYSTEM_PROMPT = `You are a telecom fleet analyst. You are given facts that have ALREADY been measured. Your job is judgement, not arithmetic.\n\nRules:\n1. NEVER write a number, percentage, or quantity yourself. To cite one, use its placeholder: {key}. The system substitutes the measured value.\n2. Only cite facts that appear in the supplied list. Do not infer a figure that was not given to you.\n3. If the facts do not support a conclusion, say so plainly. \"There is not enough usage data to judge this\" is a correct and useful answer.\n4. Do not restate the facts as a list — the caller already has them. Explain what they MEAN.\n5. Prefer no recommendation over a generic one. An empty recommendations array is fine.\n6. Be concise and specific to this fleet. No filler.`;\n\n/** Replace {key} with the Fact's display string — mechanism (2). */\nexport function substituteFacts(text: string, facts: Fact[]): string {\n const byKey = new Map(facts.map((f) => [f.key, f.display]));\n return text.replace(/\\{([a-z0-9_]+)\\}/gi, (whole, key: string) => {\n const display = byKey.get(key);\n return display ?? whole;\n });\n}\n\n/**\n * Reject prose that still carries a bare number after substitution — mechanism (3).\n *\n * Runs AFTER substitution, so legitimate figures are already `display` strings\n * from computed Facts. Anything numeric left over was written by the model.\n *\n * Deliberately tolerant of ordinals and small list markers (\"top 3\", \"1.\"),\n * which carry no measurement claim. Everything else is treated as invented.\n */\nexport function containsInventedNumber(rendered: string, facts: Fact[]): boolean {\n const displays = facts.map((f) => f.display).filter(Boolean);\n let stripped = rendered;\n for (const d of displays) {\n stripped = stripped.split(d).join(\" \");\n }\n // Allow: a bare small integer used as an ordinal or list index.\n stripped = stripped.replace(/\\b(?:top|first|next|last)\\s+\\d{1,2}\\b/gi, \" \");\n stripped = stripped.replace(/(^|\\s)\\d{1,2}[.)]\\s/g, \" \");\n return /\\d/.test(stripped);\n}\n\nfunction fromDeterministic(\n req: AnalysisRequest,\n reason: FallbackReason,\n): AnalysisResult {\n const d = req.deterministic;\n return {\n path: \"deterministic\",\n reason,\n headline: d.findings[0] ?? d.severity,\n assessment: d.findings.join(\" \"),\n recommendations: d.actions.map((action) => ({\n action,\n why: \"From the measured thresholds.\",\n priority: \"soon\" as const,\n })),\n usedFactKeys: [],\n };\n}\n\n/** Minimal shape of a Bedrock tool-use response, so this module stays transport-free. */\nexport interface ModelInvoker {\n (payload: {\n system: string;\n userMessage: string;\n tool: typeof ANALYSIS_TOOL_SCHEMA;\n maxTokens: number;\n }): Promise<{ toolInput: Record<string, unknown> } | null>;\n}\n\n/**\n * Run the analysis, falling back to the deterministic verdict on ANY failure.\n *\n * `invoke` is injected so this module needs no AWS credentials, no fetch and no\n * Worker bindings — which is what lets both MCP surfaces and the tests share it.\n */\nexport async function analyzeWithModel(\n req: AnalysisRequest,\n invoke: ModelInvoker | null,\n opts: { enabled?: boolean; reason?: FallbackReason } = {},\n): Promise<AnalysisResult> {\n if (opts.reason) return fromDeterministic(req, opts.reason);\n if (opts.enabled === false) return fromDeterministic(req, \"disabled\");\n if (!invoke) return fromDeterministic(req, \"no_credentials\");\n if (req.facts.length === 0) return fromDeterministic(req, \"no_facts\");\n\n const started = Date.now();\n let out: { toolInput: Record<string, unknown> } | null;\n try {\n out = await invoke({\n system: ANALYSIS_SYSTEM_PROMPT,\n userMessage: JSON.stringify({\n kind: req.kind,\n entities: req.entities ?? {},\n facts: req.facts.map((f) => ({\n key: f.key,\n label: f.label,\n value: f.value,\n unit: f.unit,\n })),\n deterministic_verdict: req.deterministic,\n }),\n tool: ANALYSIS_TOOL_SCHEMA,\n maxTokens: req.maxOutputTokens ?? 700,\n });\n } catch (err) {\n const rateLimited =\n typeof err === \"object\" && err !== null && \"isRateLimit\" in err;\n return fromDeterministic(req, rateLimited ? \"rate_limited\" : \"error\");\n }\n\n if (!out?.toolInput) return fromDeterministic(req, \"malformed\");\n\n const t = out.toolInput as {\n headline?: unknown;\n assessment?: unknown;\n recommendations?: unknown;\n used_fact_keys?: unknown;\n };\n if (typeof t.headline !== \"string\" || typeof t.assessment !== \"string\") {\n return fromDeterministic(req, \"malformed\");\n }\n\n const headline = substituteFacts(t.headline, req.facts);\n const assessment = substituteFacts(t.assessment, req.facts);\n\n const rawRecs = Array.isArray(t.recommendations) ? t.recommendations : [];\n const recommendations = rawRecs\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === \"object\")\n .map((r) => ({\n action: substituteFacts(String(r.action ?? \"\"), req.facts),\n why: substituteFacts(String(r.why ?? \"\"), req.facts),\n priority:\n r.priority === \"now\" || r.priority === \"watch\"\n ? (r.priority as \"now\" | \"watch\")\n : (\"soon\" as const),\n }))\n .filter((r) => r.action.length > 0);\n\n // Mechanism (3): anything numeric that is not a substituted display value was\n // written by the model. Reject the whole result rather than render part of it.\n const surfaces = [headline, assessment, ...recommendations.flatMap((r) => [r.action, r.why])];\n if (surfaces.some((s) => containsInventedNumber(s, req.facts))) {\n return fromDeterministic(req, \"invented_number\");\n }\n\n return {\n path: \"model\",\n headline,\n assessment,\n recommendations,\n usedFactKeys: Array.isArray(t.used_fact_keys)\n ? t.used_fact_keys.filter((k): k is string => typeof k === \"string\")\n : [],\n latencyMs: Date.now() - started,\n };\n}\n","/**\n * Read data usage out of an OCS `subscriberUsageOverPeriod` response.\n *\n * WHY THIS IS SHARED\n * ------------------\n * Three surfaces read this response and all three read it differently:\n *\n * apps/mcp-server guarded on Array.isArray(usage.data) -> never true\n * apps/mcp-stdio same guard, same result\n * apps/api reads `data.totalBytes` -> field does not exist\n *\n * The live response, captured from the fleet, is:\n *\n * {\n * total: { cost, parentResellerCost, resellerCost, subscriberCost },\n * usages: [ { subscriberId, subsPeriodUsages: [\n * { day, total: { quantityPerType: { \"33\": <bytes> } } } ] } ]\n * }\n *\n * There is a `total`, but it carries COST fields — no byte count. So\n * `data.totalBytes ?? 0` yields 0 for every subscriber, and a ranking built on\n * it ranks everything equal at zero.\n *\n * The data quantity type is the string \"33\". Not \"DATA\" — a readable-looking\n * key silently matches nothing, which is how a first draft of the mcp-server\n * test asserted the wrong thing.\n */\n\n/** OCS quantity type code for data. Voice and SMS use other codes. */\nexport const OCS_DATA_QUANTITY_TYPE = \"33\";\n\nexport interface DailyUsage {\n date: string;\n bytes: number;\n}\n\nfunction readBytes(entry: Record<string, unknown>): number {\n const total = entry.total;\n if (total !== null && typeof total === \"object\") {\n const perType = (total as Record<string, unknown>).quantityPerType;\n if (perType !== null && typeof perType === \"object\") {\n const v = (perType as Record<string, unknown>)[OCS_DATA_QUANTITY_TYPE];\n if (v !== undefined) return Number(v) || 0;\n }\n }\n // Legacy/flat shapes, kept so recorded fixtures and older OCS keep working.\n return Number(entry.dataBytes ?? entry.dataVolume ?? entry.totalData ?? 0) || 0;\n}\n\n/**\n * Daily rows from a usage response, in the order OCS returned them.\n *\n * Accepts the nested envelope and the legacy flat array. Returns [] for\n * anything else, so an absent response and a zero-usage subscriber look the\n * same to callers — which is correct, since both mean \"nothing to report\".\n */\nexport function extractDailyUsage(data: unknown): DailyUsage[] {\n const out: DailyUsage[] = [];\n if (data === null || typeof data !== \"object\") return out;\n\n if (Array.isArray(data)) {\n for (const entry of data) {\n if (entry === null || typeof entry !== \"object\") continue;\n const e = entry as Record<string, unknown>;\n out.push({ date: String(e.date ?? e.day ?? \"?\"), bytes: readBytes(e) });\n }\n return out;\n }\n\n const usages = (data as Record<string, unknown>).usages;\n if (!Array.isArray(usages)) return out;\n for (const usage of usages) {\n if (usage === null || typeof usage !== \"object\") continue;\n const periods = (usage as Record<string, unknown>).subsPeriodUsages;\n if (!Array.isArray(periods)) continue;\n for (const period of periods) {\n if (period === null || typeof period !== \"object\") continue;\n const p = period as Record<string, unknown>;\n out.push({ date: String(p.day ?? p.date ?? \"?\"), bytes: readBytes(p) });\n }\n }\n return out;\n}\n\n/** Total data bytes over the window. This is what `totalBytes` was meant to be. */\nexport function totalUsageBytes(data: unknown): number {\n return extractDailyUsage(data).reduce((sum, r) => sum + r.bytes, 0);\n}\n\n/** Mean daily bytes, or 0 when there are no rows. */\nexport function averageDailyBytes(data: unknown): number {\n const rows = extractDailyUsage(data);\n return rows.length === 0 ? 0 : rows.reduce((s, r) => s + r.bytes, 0) / rows.length;\n}\n\n/**\n * Total cost over the window, read from the `total` object that DOES exist.\n *\n * Separate from bytes on purpose: conflating the two is what produced a\n * `totalBytes` read against a cost-only object in the first place.\n */\nexport function totalUsageCost(data: unknown): number {\n if (data === null || typeof data !== \"object\") return 0;\n const total = (data as Record<string, unknown>).total;\n if (total === null || typeof total !== \"object\") return 0;\n return Number((total as Record<string, unknown>).cost ?? 0) || 0;\n}\n","/**\n * Storefront logo generator.\n *\n * Works on any box that has the Carrier CLI or MCP — no image-API key required.\n * Produces a distinctive SVG mark from brand name + accent. If an OpenAI key is\n * available (worker secret or local env), optionally returns a raster PNG too.\n */\n\nexport type LogoMark = {\n svg: string;\n filename: string;\n source: \"svg\" | \"openai\";\n pngBase64?: string;\n};\n\nconst OPENAI_KEY_NAMES = [\n \"OPENAI_API_KEY\",\n \"OPENAI_KEY\",\n \"CARRIER_OPENAI_API_KEY\",\n] as const;\n\nexport function firstImageKey(env: Record<string, string | undefined> = process.env): string | undefined {\n for (const name of OPENAI_KEY_NAMES) {\n const v = env[name];\n if (v && v.trim() && !v.startsWith(\"__\") && !v.includes(\"PLACEHOLD\")) return v.trim();\n }\n return undefined;\n}\n\nfunction clampHex(hex: string, fallback: string): string {\n const h = hex.trim();\n return /^#[0-9a-fA-F]{6}$/.test(h) ? h.toUpperCase() : fallback;\n}\n\nfunction hashName(name: string): number {\n let h = 2166136261;\n for (let i = 0; i < name.length; i++) {\n h ^= name.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return h >>> 0;\n}\n\nfunction mix(hex: string, toward: string, t: number): string {\n const parse = (x: string) => [\n parseInt(x.slice(1, 3), 16),\n parseInt(x.slice(3, 5), 16),\n parseInt(x.slice(5, 7), 16),\n ] as const;\n const a = parse(hex);\n const b = parse(toward);\n const ch = (i: number) =>\n Math.round(a[i] + (b[i] - a[i]) * t)\n .toString(16)\n .padStart(2, \"0\");\n return `#${ch(0)}${ch(1)}${ch(2)}`;\n}\n\n/** Distinctive 512×512 SVG mark — letter + geometric seal from the brand name. */\nexport function generateStorefrontLogoSvg(\n name: string,\n accent = \"#FF6B35\",\n): string {\n const brand = (name.trim() || \"C\").slice(0, 24);\n const letter = brand[0]!.toUpperCase();\n const accentHex = clampHex(accent, \"#FF6B35\");\n const deep = mix(accentHex, \"#1A1812\", 0.45);\n const light = mix(accentHex, \"#FFF8EB\", 0.35);\n const h = hashName(brand);\n const style = h % 4;\n const uid = `lg${(h % 1_000_000).toString(16)}`;\n\n let seal = \"\";\n if (style === 0) {\n seal = `<rect x=\"56\" y=\"56\" width=\"400\" height=\"400\" rx=\"96\" fill=\"url(#${uid}g)\"/>`;\n } else if (style === 1) {\n seal = `<circle cx=\"256\" cy=\"256\" r=\"196\" fill=\"url(#${uid}g)\"/>`;\n } else if (style === 2) {\n seal = `<path d=\"M256 48 L432 152 V360 L256 464 L80 360 V152 Z\" fill=\"url(#${uid}g)\"/>`;\n } else {\n seal = `<path d=\"M256 64 C320 64 400 96 424 176 C448 256 424 352 344 416 C280 464 232 464 168 416 C88 352 64 256 88 176 C112 96 192 64 256 64 Z\" fill=\"url(#${uid}g)\"/>`;\n }\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\" role=\"img\" aria-label=\"${escapeXml(brand)} logo\">\n <defs>\n <linearGradient id=\"${uid}g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"${light}\"/>\n <stop offset=\"55%\" stop-color=\"${accentHex}\"/>\n <stop offset=\"100%\" stop-color=\"${deep}\"/>\n </linearGradient>\n </defs>\n <rect width=\"512\" height=\"512\" rx=\"96\" fill=\"#FAFAF7\"/>\n ${seal}\n <text x=\"256\" y=\"300\" text-anchor=\"middle\" font-family=\"Georgia, 'Times New Roman', serif\" font-size=\"220\" font-weight=\"700\" fill=\"#FFF8EB\">${escapeXml(letter)}</text>\n</svg>\n`;\n}\n\nfunction escapeXml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\n\nexport function logoPrompt(name: string, accent: string, tagline?: string): string {\n return [\n `App icon / brand mark for \"${name}\", a travel eSIM storefront.`,\n tagline ? `Tagline: ${tagline}.` : \"\",\n `Flat vector, single centered symbol, no letters unless they are a refined monogram of \"${name[0] ?? \"C\"}\".`,\n `Color: ${accent} on warm cream #FAFAF7. Soft squircle icon, premium, simple, high contrast.`,\n `No photorealism, no mockups, no shadows of devices, no watermarks.`,\n ]\n .filter(Boolean)\n .join(\" \");\n}\n\n/** Best-effort OpenAI image. Returns undefined on any failure. */\nexport async function tryOpenAiLogoPng(\n name: string,\n accent: string,\n apiKey: string,\n tagline?: string,\n): Promise<string | undefined> {\n try {\n const res = await fetch(\"https://api.openai.com/v1/images/generations\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: \"dall-e-3\",\n prompt: logoPrompt(name, accent, tagline),\n size: \"1024x1024\",\n quality: \"hd\",\n response_format: \"b64_json\",\n n: 1,\n }),\n });\n if (!res.ok) return undefined;\n const json = (await res.json()) as { data?: Array<{ b64_json?: string }> };\n return json.data?.[0]?.b64_json;\n } catch {\n return undefined;\n }\n}\n\nexport async function generateStorefrontLogo(\n input: { name: string; accent?: string; tagline?: string; env?: Record<string, string | undefined> },\n): Promise<LogoMark> {\n const name = input.name.trim() || \"Carrier\";\n const accent = clampHex(input.accent ?? \"#FF6B35\", \"#FF6B35\");\n const svg = generateStorefrontLogoSvg(name, accent);\n const key = firstImageKey(input.env ?? (typeof process !== \"undefined\" ? process.env : {}));\n if (key) {\n const png = await tryOpenAiLogoPng(name, accent, key, input.tagline);\n if (png) {\n return { svg, filename: \"logo.svg\", source: \"openai\", pngBase64: png };\n }\n }\n return { svg, filename: \"logo.svg\", source: \"svg\" };\n}\n"],"mappings":";;;;;;;AAAA,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,SAAS,IAAI,aAAa,qBAAqB;;;ACFxD,SAAS,aAAa;AAmBtB,IAAM,eAAe;AAErB,SAAS,cAAc,KAAsB;AAC3C,SAAO,aAAa,KAAK,GAAG;AAC9B;AAGO,SAAS,IACd,KACA,MACA,OAA6D,CAAC,GAC1C;AACpB,MAAI,CAAC,cAAc,GAAG,GAAG;AACvB,WAAO,QAAQ,QAAQ;AAAA,MACrB,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,mCAAmC,GAAG;AAAA,IAChD,CAAC;AAAA,EACH;AACA,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,MAAM,KAAK,MAAM,EAAE,KAAK,KAAK,KAAK,OAAO,MAAM,CAAC;AAG9D,QAAI,KAAK,UAAU,QAAW;AAC5B,YAAM,OAAO,GAAG,SAAS,MAAM;AAAA,MAE/B,CAAC;AACD,YAAM,OAAO,IAAI,KAAK,KAAK;AAAA,IAC7B;AACA,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,UAAU;AACd,UAAM,SAAS,CAAC,WAAsB;AACpC,UAAI,QAAS;AACb,gBAAU;AACV,cAAQ,MAAM;AAAA,IAChB;AACA,QAAI;AACJ,QAAI,KAAK,aAAa,KAAK,YAAY,GAAG;AACxC,cAAQ,WAAW,MAAM;AACvB,YAAI;AACF,gBAAM,KAAK,SAAS;AAAA,QACtB,QAAQ;AAAA,QAER;AACA,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,MAAM;AAAA,UACN;AAAA,UACA,QAAQ,UAAU,iBAAiB,KAAK,SAAS;AAAA,QACnD,CAAC;AAAA,MACH,GAAG,KAAK,SAAS;AAAA,IACnB;AACA,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAO,UAAU,EAAE,SAAS,CAAE;AACxD,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAO,UAAU,EAAE,SAAS,CAAE;AACxD,UAAM,GAAG,SAAS,MAAM;AACtB,UAAI,MAAO,cAAa,KAAK;AAC7B,aAAO,EAAE,IAAI,OAAO,MAAM,MAAM,QAAQ,OAAO,CAAC;AAAA,IAClD,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,UAAI,MAAO,cAAa,KAAK;AAC7B,aAAO,EAAE,IAAI,SAAS,GAAG,MAAM,QAAQ,OAAO,CAAC;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AACH;AAGO,SAAS,WAAW,KAAa,MAAgB,OAAyB,CAAC,GAAuB;AACvG,MAAI,CAAC,cAAc,GAAG,GAAG;AACvB,WAAO,QAAQ,QAAQ;AAAA,MACrB,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,mCAAmC,GAAG;AAAA,IAChD,CAAC;AAAA,EACH;AACA,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,MAAM,KAAK,MAAM,EAAE,KAAK,KAAK,KAAK,OAAO,OAAO,OAAO,UAAU,CAAC;AAChF,UAAM,GAAG,SAAS,MAAM,QAAQ,EAAE,IAAI,OAAO,MAAM,MAAM,QAAQ,IAAI,QAAQ,GAAG,CAAC,CAAC;AAClF,UAAM,GAAG,SAAS,CAAC,SAAS,QAAQ,EAAE,IAAI,SAAS,GAAG,MAAM,QAAQ,IAAI,QAAQ,GAAG,CAAC,CAAC;AAAA,EACvF,CAAC;AACH;AAGA,eAAsB,MAAM,KAA+B;AACzD,QAAMA,SAAQ,QAAQ,aAAa,UAAU,UAAU;AACvD,QAAM,IAAI,MAAM,IAAIA,QAAO,CAAC,GAAG,CAAC;AAChC,SAAO,EAAE,MAAM,EAAE,OAAO,KAAK,EAAE,SAAS;AAC1C;;;ADzFO,IAAM,aAAkC,CAAC,cAAc,UAAU,WAAW,KAAK;AAEjF,SAAS,WAAW,GAA0B;AACnD,SAAQ,WAAiC,SAAS,CAAC;AACrD;AAwDA,IAAM,OAAoC;AAAA,EACxC,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,CAAC,kBAAkB,iBAAiB,eAAe;AAAA,IAC5D,aAAa;AAAA,IACb,UAAU,KAAK,cAAc,WAAW;AAAA,IACxC,QAAQ,CAAC,QAAQ;AAAA,IACjB,WAAW;AAAA;AAAA,IAEX,cAAc,CAAC,QAAQ,WACrB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,iDAAiD,IAAI,CAAC;AAAA,IACnF,YAAY,CAAC,SAAS,CAAC,UAAU,UAAU,IAAI;AAAA,IAC/C,YAAY,CAAC,KAAK,OAAO,UAAU,EAAE,MAAM,CAAC,UAAU,OAAO,KAAK,UAAU,IAAI,GAAG,OAAO,MAAM;AAAA,EAClG;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,CAAC,eAAe,SAAS;AAAA,IAClC,aAAa;AAAA,IACb,QAAQ,CAAC,QAAQ;AAAA,IACjB,WAAW;AAAA;AAAA,IAEX,cAAc,CAAC,WACb,OACG,KAAK,EACL,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,eAAe,KAAK,CAAC,CAAC,EAChE,IAAI;AAAA,IACT,YAAY,MAAM,CAAC,UAAU,UAAU,OAAO;AAAA,IAC9C,YAAY,CAAC,KAAK,WAAW,EAAE,MAAM,CAAC,OAAO,OAAO,KAAK,cAAc,SAAS,GAAG,OAAO,MAAM;AAAA,EAClG;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,CAAC,cAAc;AAAA,IACxB,aAAa;AAAA,IACb,QAAQ,CAAC,QAAQ;AAAA,IACjB,WAAW;AAAA,IACX,cAAc,CAAC,QAAQ,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,gBAAgB,IAAI,CAAC;AAAA,IAClF,YAAY,MAAM,CAAC,UAAU,WAAW,QAAQ;AAAA,IAChD,cAAc,OAAO,eAAe;AAClC,YAAM,OAAO,KAAK,YAAY,cAAc;AAC5C,UAAI,MAAM,OAAO,IAAI,EAAG;AAExB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,EAAE,KAAK,IAAI;AAAA,MACb;AAAA,IACF;AAAA,IACA,YAAY,CAAC,KAAK,WAAW,EAAE,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,KAAK;AAAA,IACH,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,CAAC,UAAU;AAAA,IACpB,aAAa;AAAA,IACb,QAAQ,CAAC,QAAQ,QAAQ;AAAA,IACzB,WAAW;AAAA,IACX,cAAc,CAAC,QAAQ,IAAI,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,GAAG,KAAK;AAAA,IAC1D,YAAY,MAAM,CAAC,UAAU,OAAO;AAAA,IACpC,cAAc,OAAO,YAAY,gBAAgB;AAC/C,YAAM,OAAO,KAAK,YAAY,UAAU;AACxC,UAAI,CAAE,MAAM,OAAO,IAAI,GAAI;AACzB,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,YACE;AAAA,YACA,UAAU,WAAW;AAAA,YACrB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,EAAE,KAAK,IAAI;AAAA,QACb;AAAA,MACF;AACA,YAAM,aAAa,KAAK,YAAY,YAAY;AAChD,UAAI,CAAE,MAAM,OAAO,UAAU,GAAI;AAE/B,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,EAAE,KAAK,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,IACA,YAAY,CAAC,KAAK,WAAW,EAAE,MAAM,CAAC,WAAW,OAAO,GAAG,GAAG,IAAI,KAAK,EAAE,EAAE;AAAA,EAC7E;AACF;AAkBA,eAAsB,sBACpB,IACA,YAC6B;AAC7B,MAAI,OAAO,aAAc,QAAO;AAChC,aAAW,UAAU,KAAK,WAAW,SAAS;AAC5C,UAAM,OAAO,KAAK,YAAY,MAAM;AACpC,QAAI,CAAE,MAAM,OAAO,IAAI,EAAI;AAC3B,UAAM,OAAO,MAAM,aAAa,IAAI;AAEpC,UAAM,OAAO,KAAK,MAAM,+BAA+B,IAAI,CAAC;AAC5D,QAAI,KAAM,QAAO;AAAA,EACnB;AACA,SAAO;AACT;AAaA,eAAsB,gBAAgB,YAAoB,QAAkC;AAC1F,QAAM,QAAQ,OAAO,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,OAAO,EAAE;AAClE,MAAI,CAAC,SAAS,CAAC,MAAM,SAAS,GAAG,EAAG,QAAO;AAE3C,QAAM,OAAO,KAAK,YAAY,gBAAgB;AAC9C,MAAI,CAAE,MAAM,OAAO,IAAI,EAAI,QAAO;AAClC,QAAM,OAAO,MAAM,aAAa,IAAI;AACpC,MAAI,KAAK,SAAS,eAAe,KAAK,GAAG,EAAG,QAAO;AAInD,QAAM,SAAS;AAAA,oBAAoC,KAAK;AAAA;AAAA;AACxD,QAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,MAAI,WAAW,GAAI,QAAO;AAC1B,QAAM,YAAY,KAAK,YAAY,MAAM,MAAM,IAAI;AACnD,QAAM,UAAU,KAAK,MAAM,GAAG,SAAS,IAAI,SAAS,KAAK,MAAM,SAAS;AACxE,QAAM,UAAU,MAAM,OAAO;AAC7B,SAAO;AACT;AAEA,eAAe,aAAa,MAA+B;AACzD,QAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,mBAAU;AAC5C,SAAOA,UAAS,MAAM,MAAM;AAC9B;AASA,eAAsB,SACpB,IACA,YACA,aAC2C;AAC3C,MAAI,OAAO,cAAc;AACvB,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,KAAK,EAAE,EAAE,KAAK,uDAAkD;AAAA,EACjG;AACA,QAAM,MAAM,KAAK,EAAE;AACnB,QAAM,WAAW,MAAM,WAAW,GAAG;AACrC,MAAI,CAAC,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,sBAAsB;AACjE,QAAM,IAAI,MAAM;AAAA,IACd,SAAS;AAAA,IACT,CAAC,GAAG,SAAS,QAAQ,YAAY,UAAU,aAAa,OAAO;AAAA,IAC/D,EAAE,KAAK,YAAY,WAAW,IAAQ;AAAA,EACxC;AACA,SAAO,EAAE,KACL,EAAE,IAAI,KAAK,IACX,EAAE,IAAI,OAAO,QAAQ,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE;AAC1G;AAgBA,eAAe,WAAW,KAAwE;AAChG,MAAI,MAAM,MAAM,IAAI,GAAG,EAAG,QAAO,EAAE,KAAK,IAAI,KAAK,QAAQ,CAAC,EAAE;AAC5D,MAAI,IAAI,UAAW,MAAM,MAAM,KAAK,EAAI,QAAO,EAAE,KAAK,OAAO,QAAQ,CAAC,IAAI,MAAM,EAAE;AAClF,SAAO;AACT;AAGA,eAAsB,YAAY,IAAc,YAA2C;AACzF,QAAM,MAAM,KAAK,EAAE;AACnB,MAAI,aAAa;AACjB,aAAW,UAAU,IAAI,SAAS;AAChC,QAAI,MAAM,OAAO,KAAK,YAAY,MAAM,CAAC,GAAG;AAC1C,mBAAa;AACb;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,WAAW,GAAG;AACrC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,MACL;AAAA,MACA,OAAO,IAAI;AAAA,MACX,WAAW;AAAA,MACX,eAAe;AAAA,MACf;AAAA,MACA,OAAO;AAAA,MACP,QAAQ,IAAI,SACR,GAAG,IAAI,GAAG,yDACV,GAAG,IAAI,GAAG;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,IAAI,SAAS,KAAK,CAAC,GAAG,SAAS,QAAQ,GAAG,IAAI,MAAM,GAAG;AAAA,IACvE,KAAK;AAAA,IACL,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,WAAO;AAAA,MACL;AAAA,MACA,OAAO,IAAI;AAAA,MACX,WAAW;AAAA,MACX,eAAe;AAAA,MACf;AAAA,MACA,OAAO;AAAA,MACP,QAAQ,GAAG,IAAI,GAAG,kCAA6B,IAAI,SAAS;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,IAAI;AAAA,IACX,WAAW;AAAA,IACX,eAAe;AAAA,IACf;AAAA,IACA,OAAO;AAAA,IACP,SAAS,IAAI,eAAe,IAAI,QAAQ,IAAI,MAAM;AAAA,EACpD;AACF;AAGA,eAAsB,SAAS,YAA6C;AAC1E,SAAO,QAAQ,IAAI,WAAW,IAAI,CAAC,OAAO,YAAY,IAAI,UAAU,CAAC,CAAC;AACxE;AASO,SAAS,YAAY,UAA0C;AACpE,SAAO,SACJ,OAAO,CAAC,MAAM,EAAE,KAAK,EACrB,KAAK,CAAC,GAAG,MAAM;AACd,QAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,aAAa,KAAK;AAC9D,WAAO,WAAW,QAAQ,EAAE,EAAE,IAAI,WAAW,QAAQ,EAAE,EAAE;AAAA,EAC3D,CAAC;AACL;AAGO,SAAS,eAAe,IAAsB;AACnD,SAAO,KAAK,EAAE,EAAE;AAClB;AAGA,eAAsB,gBAAgB,IAAc,YAAsC;AACxF,QAAM,WAAW,KAAK,EAAE,EAAE;AAC1B,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,CAAE,MAAM,OAAO,KAAK,YAAY,QAAQ,CAAC;AAClD;AAGO,SAAS,iBAAiB,QAAoC;AACnE,SAAO,OAAO;AAAA,IACZ;AAAA,EACF,IAAI,CAAC;AACP;AAGA,eAAsB,UACpB,IACA,YACA,aACA,KACA,OACkB;AAClB,QAAM,MAAM,KAAK,EAAE;AACnB,QAAM,WAAW,MAAM,WAAW,GAAG;AACrC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,EAAE,MAAM,MAAM,IAAI,IAAI,WAAW,KAAK,OAAO,WAAW;AAC9D,QAAM,IAAI,MAAM,IAAI,SAAS,KAAK,CAAC,GAAG,SAAS,QAAQ,GAAG,IAAI,GAAG;AAAA,IAC/D,KAAK;AAAA,IACL,WAAW;AAAA,IACX;AAAA,EACF,CAAC;AACD,SAAO,EAAE;AACX;AAyBA,eAAsB,aACpB,IACA,YACA,aACA,SACwB;AACxB,QAAM,UAAU,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,KAAK,CAAC;AACnE,QAAM,OAAsB,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,YAAY;AAAA,EAAC,EAAE;AAC9E,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,MAAM,KAAK,EAAE;AACnB,QAAM,WAAW,MAAM,WAAW,GAAG;AACrC,MAAI,CAAC,SAAU,QAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,YAAY;AAAA,EAAC,EAAE;AAE7F,MAAI,OAAO,cAAc;AAGvB,UAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,GAAG,kBAAkB,CAAC;AAC5D,UAAM,OAAO,KAAK,KAAK,MAAM;AAC7B,UAAM,OAAO,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAE3D,UAAM,cAAc,MAAM,GAAG,IAAI;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACtD,WAAO;AAAA,MACL,QAAQ,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,MAC9B,QAAQ,CAAC;AAAA,MACT,aAAa;AAAA,MACb,SAAS,YAAY;AACnB,cAAM,GAAG,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,OAAO;AAEhB,UAAM,OAAO,CAAC,WAAW,OAAO,WAAW,GAAG,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AAClF,UAAM,IAAI,MAAM,IAAI,SAAS,KAAK,CAAC,GAAG,SAAS,QAAQ,GAAG,IAAI,GAAG;AAAA,MAC/D,KAAK;AAAA,MACL,WAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,MACL,QAAQ,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AAAA,MAC1C,QAAQ,EAAE,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,MAC1C,SAAS,YAAY;AAAA,MAAC;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,SAAmB,CAAC;AAC1B,QAAM,SAAmB,CAAC;AAC1B,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,UAAM,KAAK,MAAM,UAAU,IAAI,YAAY,aAAa,KAAK,KAAK;AAClE,KAAC,KAAK,SAAS,QAAQ,KAAK,GAAG;AAAA,EACjC;AACA,SAAO,EAAE,QAAQ,QAAQ,SAAS,YAAY;AAAA,EAAC,EAAE;AACnD;AAGA,eAAsB,SACpB,IACA,YACA,aACA,OAAiC,CAAC,GACV;AACxB,QAAM,MAAM,KAAK,EAAE;AACnB,QAAM,WAAW,MAAM,WAAW,GAAG;AACrC,MAAI,CAAC,UAAU;AACb,WAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,aAAa,QAAQ,GAAG,IAAI,GAAG,cAAc;AAAA,EAC/E;AACA,QAAM,IAAI,eAAe,YAAY,WAAW;AAEhD,MAAI,MAAM,gBAAgB,IAAI,UAAU,GAAG;AACzC,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA,QAAQ,MAAM,IAAI,QAAQ;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,OAAO,CAAC,GAAG,SAAS,QAAQ,GAAG,IAAI,WAAW,WAAW,CAAC;AAEhE,MAAI,OAAO,gBAAgB,KAAK,YAAa,MAAK,KAAK,kBAAkB,KAAK,WAAW;AAGzF,QAAM,IAAI,MAAM,IAAI,SAAS,KAAK,MAAM;AAAA,IACtC,KAAK;AAAA,IACL,WAAW;AAAA,EACb,CAAC;AACD,QAAM,WAAW,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM;AACvC,MAAI,CAAC,EAAE,IAAI;AACT,UAAM,OAAO,SAAS,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG;AACzE,WAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,aAAa,QAAQ,QAAQ,GAAG,IAAI,GAAG,kBAAkB;AAAA,EAC3F;AACA,SAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,aAAa,KAAK,iBAAiB,QAAQ,EAAE;AAC9E;;;AE1gBO,IAAM,gBAAuB;AAAA,EAClC,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,MAAM;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,GAAG;AAAA,IACH,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAAA,EACA,eAAe;AACjB;AAEA,IAAM,uBAAuB;AAC7B,IAAM,gCAAgC;AAEtC,SAAS,SAAS,KAA8C;AAC9D,QAAM,IAAI,IAAI,QAAQ,MAAM,EAAE;AAC9B,MAAI,CAAC,mBAAmB,KAAK,CAAC,EAAG,QAAO;AACxC,SAAO,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;AAC/F;AAEA,SAAS,SAAS,GAAW,GAAW,GAAW,QAAQ,OAAe;AACxE,QAAM,MAAM,CAAC,MACX,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EACrC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AACpB,QAAM,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACxC,SAAO,QAAQ,MAAM,IAAI,YAAY;AACvC;AAEA,SAAS,gBAAgB,KAAa,YAA4B;AAChE,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAMC,OAAM,CAAC,MAAc,KAAK,MAAM,KAAK;AAC3C,SAAO,SAASA,KAAI,IAAI,CAAC,CAAC,GAAGA,KAAI,IAAI,CAAC,CAAC,GAAGA,KAAI,IAAI,CAAC,CAAC,CAAC;AACvD;AAEA,SAAS,UAAU,KAAa,QAAwB;AACtD,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,QAAQ,CAAC,MAAc,IAAI;AACjC,SAAO,SAAS,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI;AACnE;AAGO,SAAS,iBAAiB,QAAgB,OAAc,eAAuB;AACpF,MAAI,OAAO,YAAY,MAAM,KAAK,OAAO,OAAO,YAAY,EAAG,QAAO,KAAK,OAAO;AAClF,QAAM,MAAM,SAAS,MAAM;AAC3B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,QAAQ,CAAC,MAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC;AAC5E,SAAO,SAAS,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC;AAC7D;AAGO,SAAS,kBAAkB,QAAgB,OAAc,eAAuB;AACrF,MAAI,OAAO,YAAY,MAAM,KAAK,OAAO,OAAO,YAAY,EAAG,QAAO;AACtE,SAAO,gBAAgB,QAAQ,IAAI;AACrC;AAGO,SAAS,0BAA0B,QAAgB,OAAc,eAAuB;AAC7F,MAAI,OAAO,YAAY,MAAM,KAAK,OAAO,OAAO,YAAY,EAAG,QAAO;AACtE,SAAO,gBAAgB,QAAQ,IAAI;AACrC;AAGO,SAAS,wBAAwB,QAAgB,YAA6C;AACnG,MAAI,OAAO,YAAY,MAAM,cAAc,OAAO,OAAO,YAAY,EAAG,QAAO,CAAC;AAChF,QAAM,cAAc,kBAAkB,MAAM;AAC5C,QAAM,gBAAgB,0BAA0B,MAAM;AACtD,SAAO;AAAA,IACL,CAAC,WAAW,MAAM;AAAA,IAClB,CAAC,WAAW,UAAU;AAAA,IACtB,CAAC,WAAW,WAAW;AAAA,IACvB,CAAC,WAAW,aAAa;AAAA,IACzB,CAAC,WAAW,gBAAgB,QAAQ,IAAI,EAAE,YAAY,CAAC;AAAA,IACvD,CAAC,WAAW,gBAAgB,QAAQ,IAAI,EAAE,YAAY,CAAC;AAAA,IACvD,CAAC,WAAW,gBAAgB,QAAQ,GAAG,EAAE,YAAY,CAAC;AAAA,IACtD,CAAC,WAAW,gBAAgB,QAAQ,IAAI,EAAE,YAAY,CAAC;AAAA,IACvD,CAAC,WAAW,gBAAgB,QAAQ,GAAG,EAAE,YAAY,CAAC;AAAA,IACtD,CAAC,WAAW,UAAU,YAAY,IAAI,CAAC;AAAA,IACvC,CAAC,WAAW,UAAU,YAAY,IAAI,CAAC;AAAA,IACvC,CAAC,WAAW,UAAU,YAAY,GAAG,CAAC;AAAA,IACtC,CAAC,WAAW,UAAU,YAAY,IAAI,CAAC;AAAA,EACzC;AACF;AAyBO,SAAS,UAAU,OAAsB;AAC9C,SAAO;AAAA,IACL;AAAA,IACA,0BAA0B,KAAK,UAAU,MAAM,IAAI,CAAC;AAAA,IACpD,+BAA+B,KAAK,UAAU,MAAM,aAAa,CAAC;AAAA,IAClE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC5KA,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;AAkBd,IAAM,oBAAoB;AAAA,EAC/B,wBAAwB;AAAA,EACxB,6BAA6B;AAAA,EAC7B,qBAAqB;AAAA,EACrB,mCAAmC;AAAA,EACnC,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,mBAAmB;AACrB;AAIA,SAAS,WAAW,MAA6C;AAC/D,SAAQ,OAAO,KAAK,iBAAiB,EAAuB;AAAA,IAC1D,CAAC,MAAM,kBAAkB,CAAC,MAAM;AAAA,EAClC;AACF;AAEO,IAAM,sBAAiD,WAAW,QAAQ;AAG1E,IAAM,kBAA6C,WAAW,QAAQ;AAKtE,SAAS,aAAa,MAAyB;AACpD,QAAM,MAAiB,CAAC;AACxB,aAAW,OAAO,KAAK,MAAM,IAAI,GAAG;AAClC,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AACnC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,MAAM,EAAG;AACb,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,QAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,QACG,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,KAC/D,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAChE;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AACA,QAAI,MAAO,KAAI,GAAG,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEA,eAAe,YAAY,MAAkC;AAC3D,MAAI,CAAE,MAAM,OAAO,IAAI,EAAI,QAAO,CAAC;AACnC,MAAI;AACF,WAAO,aAAa,MAAM,SAAS,MAAM,MAAM,CAAC;AAAA,EAClD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAQA,eAAsB,gBACpB,YACA,MAA0C,QAAQ,KAClD,OAAe,QAAQ,GACH;AACpB,QAAM,QAAQ,MAAM,YAAYC,MAAK,YAAY,YAAY,CAAC;AAC9D,QAAM,SAAS,MAAM,YAAYA,MAAK,MAAM,MAAM,CAAC;AAEnD,QAAM,SAAoB,CAAC;AAC3B,QAAM,OAAO,CAAC,GAAG,qBAAqB,GAAG,eAAe;AACxD,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,MAAM,GAAG,GAAG,KAAK,KAAK,IAAI,GAAG,GAAG,KAAK,KAAK,OAAO,GAAG,GAAG,KAAK;AAC1E,QAAI,MAAO,QAAO,GAAG,IAAI;AAAA,EAC3B;AACA,SAAO;AACT;AAQA,eAAsB,cACpB,YACA,SACA,OAAiC,CAAC,GACf;AACnB,QAAM,OAAOA,MAAK,YAAY,YAAY;AAC1C,QAAM,YAAY,IAAI,IAAI,KAAK,aAAa,CAAC,CAAC;AAC9C,QAAM,WAAY,MAAM,OAAO,IAAI,IAAK,MAAM,SAAS,MAAM,MAAM,IAAI;AACvE,QAAM,QAAQ,WAAW,SAAS,MAAM,IAAI,IAAI,CAAC;AACjD,QAAM,UAAoB,CAAC;AAE3B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,MAAM,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,GAAG,GAAG,CAAC;AACnE,QAAI,UAAU,IAAI;AAChB,YAAM,KAAK,GAAG,GAAG,IAAI,KAAK,EAAE;AAC5B,cAAQ,KAAK,GAAG;AAChB;AAAA,IACF;AACA,UAAM,UAAU,MAAM,KAAK,EAAE,MAAM,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC,EAAE,KAAK;AACvE,UAAM,UAAU,YAAY,MAAM,YAAY,QAAQ,YAAY;AAClE,QAAI,WAAW,UAAU,IAAI,GAAG,GAAG;AACjC,YAAM,KAAK,IAAI,GAAG,GAAG,IAAI,KAAK;AAC9B,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,QAAM,OAAO,MAAM,KAAK,IAAI,EAAE,QAAQ,WAAW,IAAI;AACrD,QAAM,UAAU,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,GAAG,IAAI;AAAA,CAAI;AAC9D,SAAO;AACT;AAGO,SAAS,gBAAgB,SAA6B;AAC3D,QAAM,MAAM,QAAQ,6BAA6B,KAAK;AACtD,SAAO,QAAQ,GAAG,KAAK,CAAC,QAAQ,iBAAiB,KAAK;AACxD;;;AC5IA,SAAS,QAAAC,aAAY;AAmBrB,SAAS,KAAK,GAAmB;AAC/B,SAAO,EACJ,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,KAAK;AACrB;AAGA,eAAsB,YAAY,QAAkC;AAClE,QAAM,SAAU,MAAM,MAAM,MAAM,IAAK,SAAS;AAChD,QAAM,IAAI,MAAM,WAAW,QAAQ,CAAC,SAAS,GAAG,EAAE,KAAK,OAAO,CAAC;AAC/D,SAAO,EAAE;AACX;AASA,eAAsB,UAAU,QAAgB,YAAsB,cAAgC;AACpG,QAAM,SAAU,MAAM,MAAM,MAAM,IAAK,SAAS;AAChD,QAAM,IAAI,MAAM,WAAW,QAAQ,CAAC,OAAO,eAAe,SAAS,CAAC,GAAG,EAAE,KAAK,OAAO,CAAC;AACtF,SAAO,EAAE;AACX;AAGA,eAAsB,oBACpB,QACA,WACgB;AAChB,QAAM,aAAaC,MAAK,QAAQ,OAAO,iBAAiB;AACxD,MAAI,CAAE,MAAM,OAAO,UAAU,GAAI;AAC/B,WAAO,EAAE,GAAG,eAAe,GAAG,UAAU;AAAA,EAC1C;AACA,QAAM,MAAM,MAAM,SAAS,YAAY,MAAM;AAC7C,QAAM,OAAO,CAAC,OAAe,aAA6B;AACxD,UAAM,IAAI,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,uCAAuC,CAAC;AAClF,WAAO,IAAI,CAAC,KAAK;AAAA,EACnB;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,WAAW,QAAQ,KAAK,QAAQ,cAAc,IAAI;AAAA,IACxD,QAAQ,KAAK,UAAU,cAAc,MAAM;AAAA,IAC3C,cAAc,KAAK,gBAAgB,cAAc,YAAY;AAAA,IAC7D,YAAY,KAAK,cAAc,cAAc,UAAU;AAAA,IACvD,SAAS,KAAK,WAAW,cAAc,OAAO;AAAA,IAC9C,WAAW,KAAK,aAAa,cAAc,SAAS;AAAA,IACpD,QAAQ;AAAA,MACN,GAAG,cAAc;AAAA,MACjB,QAAQ,KAAK,UAAU,cAAc,OAAO,MAAM;AAAA,MAClD,YAAY,KAAK,cAAc,cAAc,OAAO,UAAU;AAAA,MAC9D,IAAI,KAAK,MAAM,cAAc,OAAO,EAAE;AAAA,MACtC,MAAM,KAAK,QAAQ,cAAc,OAAO,IAAI;AAAA,IAC9C;AAAA,IACA,eAAe,cAAc;AAAA,EAC/B;AACF;AAcA,eAAsB,WACpB,QACA,OACA,OAKI,CAAC,GACsB;AAC3B,QAAM,WAAW,MAAM,SAAS,MAAM;AAEtC,MAAI;AACJ,MAAI,KAAK,WAAW;AAClB,UAAM,SAAS,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS;AAC3D,QAAI,CAAC,QAAQ,OAAO;AAClB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,aAAa,KAAK,MAAM,IAAI;AAAA,QAC5B;AAAA,QACA,QACE,QAAQ,UACR,GAAG,KAAK,SAAS;AAAA,MACrB;AAAA,IACF;AACA,aAAS;AAAA,EACX,OAAO;AACL,aAAS,YAAY,QAAQ,EAAE,CAAC;AAAA,EAClC;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,aAAa,KAAK,MAAM,IAAI;AAAA,MAC5B;AAAA,MACA,QACE,gCACA,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,KAAK,EAAE,UAAU,aAAa,EAAE,EAAE,KAAK,KAAK;AAAA,IAC9E;AAAA,EACF;AAIA,QAAM,cAAe,MAAM,sBAAsB,OAAO,IAAI,MAAM,KAAM,KAAK,MAAM,IAAI;AAIvF,MAAI,KAAK,gBAAgB,OAAO,OAAO,gBAAgB,MAAM,QAAQ;AACnE,UAAM,gBAAgB,QAAQ,MAAM,MAAM;AAAA,EAC5C;AAIA,MAAI,SAAwB,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,YAAY;AAAA,EAAC,EAAE;AAC9E,MAAI,KAAK,gBAAgB,OAAO;AAC9B,UAAM,aAAa,MAAM,gBAAgB,QAAQ,KAAK,OAAO,QAAQ,GAAG;AACxE,UAAM,UAAkC,CAAC;AACzC,eAAW,OAAO,qBAAqB;AACrC,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,MAAO,SAAQ,GAAG,IAAI;AAAA,IAC5B;AACA,aAAS,MAAM,aAAa,OAAO,IAAI,QAAQ,aAAa,OAAO;AAAA,EACrE;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,OAAO,IAAI,QAAQ,aAAa;AAAA,MAC7D,aAAa,OAAO;AAAA,IACtB,CAAC;AACD,QAAI,CAAC,QAAQ,IAAI;AACf,aAAO,EAAE,IAAI,OAAO,aAAa,UAAU,QAAQ,OAAO,IAAI,QAAQ,QAAQ,OAAO;AAAA,IACvF;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,KAAK,QAAQ;AAAA,MACb,SAAS,EAAE,QAAQ,OAAO,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAC1D;AAAA,EACF,UAAE;AACA,UAAM,OAAO,QAAQ;AAAA,EACvB;AACF;;;ACxJA,IAAM,iBAAiB;AA8BvB,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AACF;AACA,IAAM,cAAc,CAAC,kBAAkB;AAEvC,SAAS,cAAc,KAAU,OAA8C;AAC7E,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,IAAI,IAAI,GAAG,KAAK;AAC9B,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAsB;AACrD,SAAO,mBAAmB,KAAK,GAAG;AACpC;AAEO,SAAS,YAAY,KAAsB;AAChD,SAAO,mBAAmB,KAAK,GAAG;AACpC;AAEA,eAAe,WACb,MACA,OACA,OAA4C,CAAC,GAC6B;AAC1E,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,cAAc,GAAG,IAAI,IAAI;AAAA,MAClD,QAAQ,KAAK,UAAU;AAAA,MACvB,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,SAAS,SAAY,SAAY,KAAK,UAAU,KAAK,IAAI;AAAA,IACtE,CAAC;AACD,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI;AACJ,QAAI;AACF,aAAO,OAAO,KAAK,MAAM,IAAI,IAAI;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,QAAQ,MAAM,OAAO,WAAW,IAAI,KAAK,KAAK,MAAM,GAAG,GAAG,EAAE;AAAA,IAC9F;AACA,WAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAC9C,SAAS,GAAY;AACnB,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,EAAE;AAAA,EACnF;AACF;AAGA,SAAS,WAAW,MAAmC;AACrD,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,SAAU,KAA8B;AAC9C,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,EAAG,QAAO;AAC1D,QAAM,QAAQ,OAAO,CAAC;AACtB,SAAO,MAAM,gBAAgB,MAAM;AACrC;AAeA,eAAsB,uBACpB,eACA,MAC+B;AAC/B,QAAM,OAAgC;AAAA,IACpC,MAAM,KAAK;AAAA,IACX,mBAAmB,KAAK,aAAa,CAAC,eAAe,YAAY,IAAI,CAAC,aAAa;AAAA,EACrF;AACA,MAAI,KAAK,OAAQ,MAAK,SAAS,KAAK;AAEpC,QAAM,MAAM,MAAM,WAAW,0BAA0B,eAAe;AAAA,IACpE,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QACE,IAAI,WAAW,OAAO,IAAI,WAAW,MACjC,+CAA+C,IAAI,MAAM,qCACzD,6BAA6B,IAAI,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,IACtE;AAAA,EACF;AAEA,QAAM,UAAU,IAAI;AAGpB,QAAM,YAAY,SAAS,aAAa,CAAC;AAEzC,QAAM,SAAS,KAAK,aAAa,eAAe;AAChD,QAAM,WACJ,UAAU,KAAK,CAAC,MAAM,EAAE,qBAAqB,UAAU,EAAE,cAAc,EAAE,eAAe,KACxF,UAAU,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,eAAe;AAEzD,MAAI,CAAC,UAAU,cAAc,CAAC,SAAS,iBAAiB;AACtD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QACE;AAAA,IAEJ;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,MACX,gBAAgB,SAAS;AAAA,MACzB,WAAW,SAAS;AAAA,MACpB,MAAM;AAAA,MACN,eAAe,SAAS;AAAA,MACxB,YAAY,SAAS;AAAA,IACvB;AAAA,EACF;AACF;AAkBA,eAAsB,oBACpB,YACA,MAC+B;AAC/B,MAAI,CAAE,MAAM,KAAK,MAAM,KAAK,GAAI;AAC9B,WAAO,EAAE,IAAI,OAAO,MAAM,eAAe,QAAQ,iDAA4C;AAAA,EAC/F;AAEA,QAAM,UAAU,MAAM,KAAK,SAAS,UAAU;AAC9C,MAAI;AACF,UAAM,IAAI,MAAM,KAAK;AAAA,MACnB;AAAA,MACA,CAAC,SAAS,gBAAgB,QAAQ,eAAe,QAAQ,aAAa,eAAe,IAAI;AAAA,MACzF,EAAE,KAAK,YAAY,WAAW,IAAQ;AAAA,IACxC;AAEA,UAAM,UAAU,GAAG,UAAU;AAC7B,UAAM,OAAQ,MAAM,KAAK,iBAAiB,OAAO,KAAM;AACvD,UAAM,iBAAiB,SAAS,MAAM,mCAAmC;AACzE,UAAM,YAAY,SAAS,MAAM,kBAAkB;AAEnD,QAAI,CAAC,kBAAkB,CAAC,WAAW;AACjC,YAAM,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG;AACzF,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,QAAQ,iCAAiC,OAAO,KAAK,IAAI,KAAK,GAAG;AAAA,MACnE;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB,cAAc,KAAK,CAAC,YAAY,SAAS,GAAG;AAChE,aAAO,EAAE,IAAI,OAAO,MAAM,eAAe,QAAQ,gDAAgD;AAAA,IACnG;AAEA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aAAa,EAAE,gBAAgB,WAAW,MAAM,cAAc;AAAA,IAChE;AAAA,EACF,UAAE;AAEA,UAAM,QAAQ;AAAA,EAChB;AACF;AAyCA,SAAS,SAAS,MAAc,KAAiC;AAC/D,QAAM,OAAO,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,GAAG,GAAG,CAAC;AACxE,QAAM,QAAQ,MAAM,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC,EAAE,KAAK;AACtD,SAAO,SAAS;AAClB;AAqBO,SAAS,yBAAyB,KAAwC;AAC/E,QAAM,iBAAiB,cAAc,KAAK,gBAAgB;AAC1D,QAAM,YAAY,cAAc,KAAK,WAAW;AAChD,MAAI,CAAC,kBAAkB,CAAC,UAAW,QAAO;AAC1C,MAAI,CAAC,iBAAiB,cAAc,KAAK,CAAC,YAAY,SAAS,EAAG,QAAO;AACzE,SAAO,EAAE,gBAAgB,WAAW,MAAM,aAAa;AACzD;AAEA,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,eAAsB,eAAe,MAWH;AAChC,QAAM,gBAAgB,cAAc,KAAK,KAAK;AAAA,IAC5C;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,QAAkB,CAAC;AAEzB,MAAI,iBAAiB,CAAC,KAAK,UAAU;AACnC,UAAM,UAAU,MAAM,uBAAuB,eAAe;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,IACnB,CAAC;AACD,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,KAAK,QAAQ,UAAU,iCAAiC;AAAA,EAChE;AAIA,QAAM,aAAa,yBAAyB,KAAK,GAAG;AACpD,MAAI,YAAY;AACd,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,QAAQ,MAAM,SAAS,MAAM,KAAK,GAAG,IAAI;AAAA,IAC3C;AAAA,EACF;AAEA,MAAI,KAAK,OAAO,KAAK,cAAc,CAAC,KAAK,UAAU;AACjD,UAAM,SAAS,MAAM,oBAAoB,KAAK,YAAY,KAAK,GAAG;AAClE,QAAI,OAAO,IAAI;AACb,aAAO,EAAE,GAAG,QAAQ,QAAQ,MAAM,SAAS,MAAM,KAAK,GAAG,IAAI,OAAU;AAAA,IACzE;AACA,UAAM,KAAK,OAAO,UAAU,wCAAwC;AAAA,EACtE;AAEA,MAAI,CAAC,eAAe;AAClB,UAAM;AAAA,MACJ;AAAA,IAEF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ,MAAM,KAAK,GAAG;AAAA,IACtB,UAAU;AAAA,EACZ;AACF;AAYA,eAAsB,uBACpB,WACA,MAC+B;AAC/B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAkD,CAAC;AAEzD,QAAM,UAAU,OAAO,KAAK,kBAAkB,CAAC,CAAC;AAChD,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,MAAM,MAAM,WAAW,aAAa,WAAW;AAAA,MACnD,QAAQ;AAAA,MACR,MAAM,EAAE,iBAAiB,QAAQ;AAAA,IACnC,CAAC;AACD,QAAI,IAAI,GAAI,SAAQ,KAAK,oBAAoB,QAAQ,MAAM,GAAG;AAAA,QACzD,QAAO,KAAK,EAAE,MAAM,mBAAmB,QAAQ,IAAI,SAAS,QAAQ,IAAI,MAAM,GAAG,CAAC;AAAA,EACzF;AAEA,aAAW,OAAO,OAAO,KAAK,gBAAgB,CAAC,CAAC,GAAG;AACjD,UAAM,MAAM,MAAM,WAAW,kBAAkB,WAAW,EAAE,QAAQ,QAAQ,MAAM,EAAE,IAAI,EAAE,CAAC;AAC3F,QAAI,IAAI,GAAI,SAAQ,KAAK,gBAAgB,GAAG,EAAE;AAAA,QACzC,QAAO,KAAK,EAAE,MAAM,gBAAgB,GAAG,IAAI,QAAQ,IAAI,SAAS,QAAQ,IAAI,MAAM,GAAG,CAAC;AAAA,EAC7F;AAEA,SAAO,EAAE,IAAI,OAAO,WAAW,GAAG,SAAS,OAAO;AACpD;AAEA,SAAS,OAAO,QAA4B;AAC1C,SAAO,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AACjE;AAGO,SAAS,oBAAoB,aAAsB,QAGxD;AACA,QAAM,QAAQ,OAAO;AAAA,IACnB,aAAa,QAAQ,OAAO,EAAE,KAAK;AAAA,IACnC,SAAS,WAAW,OAAO,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,OAAO,EAAE,CAAC,KAAK;AAAA,IAC9E;AAAA,EACF,CAAC;AACD,SAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,cAAc,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,qBAAqB,GAAG,CAAC,YAAY,CAAC;AAAA,EACnF;AACF;;;AChcA,SAAS,QAAAC,aAAY;AACrB,SAAS,IAAI,WAAAC,UAAS,MAAAC,WAAU;AAChC,SAAS,UAAAC,eAAc;AAoBvB,IAAM,iBAAiB,CAAC,OAAO,gBAAgB,mBAAmB,eAAe;AAE1E,SAAS,eAA6B;AAC3C,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO,KAAK,MAAM,SAAS;AAC9B,YAAM,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI;AACnC,aAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,QAAQ,QAAQ,EAAE,OAAO;AAAA,IACxD;AAAA,IACA,kBAAkB,OAAO,SAAS;AAChC,UAAI,CAAE,MAAM,OAAO,IAAI,EAAI,QAAO;AAClC,UAAI;AACF,eAAO,MAAM,SAAS,MAAM,MAAM;AAAA,MACpC,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,UAAU,OAAO,eAAuB;AACtC,YAAM,SAAS,MAAMC,SAAQC,MAAKC,QAAO,GAAG,qBAAqB,CAAC;AAClE,YAAM,QAAkB,CAAC;AACzB,iBAAW,OAAO,gBAAgB;AAChC,cAAM,MAAMD,MAAK,YAAY,GAAG;AAChC,YAAI,CAAE,MAAM,OAAO,GAAG,EAAI;AAC1B,cAAM,GAAG,KAAKA,MAAK,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,cAAM,KAAK,GAAG;AAAA,MAChB;AACA,aAAO,YAAY;AACjB,YAAI;AACF,qBAAW,OAAO,OAAO;AACvB,kBAAM,SAASA,MAAK,YAAY,GAAG;AACnC,kBAAME,IAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACjD,kBAAM,GAAGF,MAAK,QAAQ,GAAG,GAAG,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,UACzD;AAAA,QACF,UAAE;AACA,gBAAME,IAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACnBA,IAAM,SAAuD;AAAA,EAC3D,EAAE,MAAM,IAAI;AAAA,EACZ,EAAE,MAAM,SAAS,YAAY,IAAI;AAAA;AAAA,EACjC,EAAE,MAAM,QAAQ;AAClB;AAEA,eAAe,MAAM,MAAc,MAAc,YAA2C;AAG1F,QAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,KAAK,SAAS,GAAG,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;AAC9F,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,UAAU,SAAS,CAAC;AACnD,UAAM,SAAS,IAAI;AAEnB,UAAM,KAAK,SAAS;AACpB,QAAI,CAAC,cAAc,CAAC,GAAI,QAAO,EAAE,MAAM,QAAQ,GAAG;AAClD,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,WAAO,EAAE,MAAM,QAAQ,IAAI,QAAQ,WAAW,KAAK,IAAI,EAAE;AAAA,EAC3D,SAAS,GAAY;AACnB,WAAO,EAAE,MAAM,QAAQ,GAAG,IAAI,OAAO,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,EAAE;AAAA,EACzF;AACF;AASO,SAAS,SAAS,QAAuB,SAA+B;AAC7E,QAAM,KAAK,CAAC,MAAc,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC;AACzD,QAAM,OAAO,GAAG,GAAG;AACnB,QAAM,OAAO,GAAG,OAAO;AACvB,QAAM,OAAO,GAAG,OAAO;AAEvB,MAAI,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,EAAG,QAAO;AAG5D,MAAI,OAAO,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAAG,QAAO;AAIhD,QAAM,cAAc,MAAM,WAAW,OAAO,MAAM,WAAW;AAC7D,MAAI,eAAe,MAAM,IAAI;AAC3B,WAAO,gBAAgB,OAAO,IAAI,wBAAwB;AAAA,EAC5D;AAGA,MAAI,MAAM,MAAM,KAAK,WAAW,MAAO,QAAO;AAG9C,MAAI,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,EAAG,QAAO;AAExE,MAAI,OAAO,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,EAAG,QAAO;AAElD,SAAO;AACT;AAEA,IAAM,YAAuC;AAAA,EAC3C,SAAS;AAAA,EACT,uBACE;AAAA,EACF,sBAAsB;AAAA,EACtB,iBACE;AAAA,EACF,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAGA,IAAM,aAAqC,oBAAI,IAAe;AAAA,EAC5D;AAAA,EACA;AACF,CAAC;AAED,IAAM,QAAQ,CAAC,OAA8B,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAUjF,eAAsB,iBACpB,KACA,YACA,MAA0C,QAAQ,KAClD,OAAgD,CAAC,GAC1B;AACvB,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC;AAC/C,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,UAAU,MAAM,gBAAgB,YAAY,GAAG;AAErD,MAAI,SAAwB,CAAC;AAC7B,MAAI,YAAuB;AAE3B,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,aAAS,CAAC;AACV,eAAW,EAAE,MAAM,WAAW,KAAK,QAAQ;AACzC,aAAO,KAAK,MAAM,MAAM,KAAK,MAAM,UAAU,CAAC;AAAA,IAChD;AACA,gBAAY,SAAS,QAAQ,OAAO;AACpC,QAAI,cAAc,kBAAkB,YAAY,SAAU;AAC1D,UAAM,MAAM,OAAO;AAAA,EACrB;AAEA,QAAM,KAAK,cAAc;AACzB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,SAAS;AAAA,IAC5B,YAAY,CAAC,MAAM,WAAW,IAAI,SAAS;AAAA,EAC7C;AACF;AAGO,SAAS,aAAa,QAA+B;AAC1D,SAAO,OACJ,IAAI,CAAC,MAAM;AACV,UAAM,SAAS,EAAE,WAAW,IAAI,gBAAgB,OAAO,EAAE,MAAM;AAC/D,UAAM,OAAO,EAAE,WAAW,QAAQ,gBAAgB;AAClD,WAAO,GAAG,EAAE,IAAI,IAAI,MAAM,GAAG,IAAI;AAAA,EACnC,CAAC,EACA,KAAK,IAAI;AACd;AAiBO,SAAS,cAAc,WAA8C;AAC1E,MAAI,cAAc,uBAAuB;AACvC,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA;AAAA;AAAA,MAGV,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,cAAc,sBAAsB;AACtC,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA;AAAA,MAEV,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;;;AC/HA,SAAS,aAAa,OAAiC;AACrD,QAAM,OAAO,OAAO,KAAK,EAAE,KAAK;AAChC,SAAO,SAAS,MAAM,SAAS,YAAO,SAAS,OAAO,OAAO,IAAI,MAAM;AACzE;AAaO,SAAS,cAAc,QAAyB;AACrD,SAAO,OAAO,SAAS,MAAM,CAAC,YAAY;AACxC,YAAQ,QAAQ,MAAM;MACpB,KAAK;AACH,eAAO,QAAQ,MAAM,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC;MACzD,KAAK;AACH,eAAO,QAAQ,MAAM,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;MACjD,KAAK;AACH,eAAO,QAAQ,MAAM,MAAM,CAAC,OAAO,aAAa,GAAG,KAAK,CAAC;MAC3D,KAAK;AACH,eAAO,QAAQ,KAAK,WAAW;MACjC,KAAK;AACH,eAAO;IACX;EACF,CAAC;AACH;ACrGO,IAAM,SAAS;;EAEpB,gBAAgB;EAChB,qBAAqB;;EAGrB,UAAU;EACV,UAAU;EACV,UAAU;EACV,UAAU;;EAGV,aAAa;EACb,aAAa;;EAGb,YAAY;EACZ,aAAa;EACb,cAAc;;EAGd,aAAa;EACb,aAAa;EACb,aAAa;;EAGb,cAAc;EACd,eAAe;;EAGf,aAAa;EACb,eAAe;EACf,WAAW;EACX,WAAW;;EAGX,eAAe;EACf,eAAe;EACf,aAAa;;EAGb,OAAO;IACL,YAAY;IACZ,qBAAqB;IACrB,aAAa;IACb,YAAY;IACZ,aAAa;IACb,eAAe;IACf,WAAW;EACb;AACF;AA6BO,IAAM,aAAa;EACxB,UAAU;EACV,UAAU;EACV,WAAW;;EAGX,eAAe;EACf,YAAY;EACZ,aAAa;;EAGb,oBAAoB;EACpB,sBAAsB;EACtB,gBAAgB;;EAGhB,eAAe;EACf,gBAAgB;EAChB,cAAc;;EAGd,OAAO;IACL,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;EACT;AACF;AAaO,IAAM,SAAS;EACpB,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,OAAO;EACP,MAAM;AACR;ACpIA,IAAM,aAAmC;EACvC,IAAI,OAAO;EACX,MAAM,OAAO;EACb,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,OAAO,OAAO;AAChB;AAGO,SAAS,IAAI,OAAwB;AAC1C,SAAO,OAAO,KAAK,EAChB,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;AAIA,SAAS,UAAU,MAAwB,WAAmB,OAAO,aAAqB;AACxF,SAAO,OAAO,WAAW,IAAI,IAAI;AACnC;AAEA,SAAS,YAAY,OAAwF;AAC3G,QAAM,QAAQ,MACX;IACC,CAAC,MAAM;+BACkB,OAAO,QAAQ,qBAAqB,OAAO,UAAU,kBAAkB,OAAO,EAAE;yFACtB,OAAO,SAAS,KAAK,IAAI,EAAE,KAAK,CAAC;0EAChD,UAAU,EAAE,IAAI,CAAC,qBAAqB,IAAI,EAAE,KAAK,CAAC;UAClH,EAAE,OAAO,oCAAoC,OAAO,SAAS,oBAAoB,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE;;EAE/G,EACC,KAAK,EAAE;AACV,SAAO,+FAA+F,KAAK;AAC7G;AAEA,SAAS,SAAS,OAAc,OAAwB;AACtD,MAAI,MAAM,WAAW,EAAG,QAAO,UAAU,SAAS,kBAAkB;AACpE,QAAM,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC;AACjE,QAAM,OAAO,MACV,IAAI,CAAC,MAAM;AACV,UAAMC,OAAM,KAAK,IAAI,GAAG,KAAK,IAAI,MAAO,EAAE,SAAS,EAAE,OAAO,YAAa,KAAK,GAAG,CAAC;AAClF,WAAO;;sFAEyE,OAAO,aAAa;kBACxF,IAAI,EAAE,KAAK,CAAC;+BACC,OAAO,SAAS,KAAK,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC;;4CAE9B,OAAO,QAAQ,kBAAkB,OAAO,IAAI;0CAC9CA,KAAI,QAAQ,CAAC,CAAC,gBAAgB,UAAU,EAAE,MAAM,OAAO,WAAW,CAAC;;;EAGzG,CAAC,EACA,KAAK,EAAE;AACV,SAAO,QAAQ,IAAI;AACrB;AAEA,SAAS,UAAU,SAAsD;AACvE,MAAI,QAAQ,KAAK,WAAW,EAAG,QAAO,UAAU,QAAQ,SAAS,UAAU;AAC3E,QAAM,UAAU,IAAI,IAAI,QAAQ,WAAW,CAAC,CAAC;AAC7C,QAAM,OAAO,QAAQ,QAClB;IACC,CAAC,GAAG,MACF,yBAAyB,QAAQ,IAAI,CAAC,IAAI,UAAU,MAAM,wFAAwF,OAAO,SAAS,4BAA4B,OAAO,UAAU,wBAAwB,IAAI,CAAC,CAAC;EACjP,EACC,KAAK,EAAE;AACV,QAAM,OAAO,QAAQ,KAClB;IACC,CAAC,QACC,OAAO,IACJ;MACC,CAAC,MAAM,MACL,yBAAyB,QAAQ,IAAI,CAAC,IAAI,UAAU,MAAM,0CAA0C,OAAO,aAAa,4BAA4B,OAAO,YAAY,wBAAwB,IAAI,IAAI,CAAC;IAC5M,EACC,KAAK,EAAE,CAAC;EACf,EACC,KAAK,EAAE;AAEV,SAAO,mFACL,cAAc,IAAI,eACpB,UAAU,IAAI;AAChB;AAEA,SAAS,aAAa,OAAgE;AACpF,QAAM,OAAO,MACV;IACC,CAAC,OAAO;8GACgG,OAAO,YAAY;4CACrF,OAAO,SAAS,KAAK,IAAI,GAAG,KAAK,CAAC;4CAClC,UAAU,GAAG,MAAM,OAAO,WAAW,CAAC,sBAAsB,IAAI,GAAG,KAAK,CAAC;;EAEjH,EACC,KAAK,EAAE;AACV,SAAO,QAAQ,IAAI;AACrB;AAEA,SAAS,SAAS,MAAY,MAAsB;AAClD,QAAM,IAAI,UAAU,MAAM,OAAO,WAAW;AAC5C,SAAO,qCAAqC,CAAC,eAAe,OAAO,QAAQ,oCAAoC,OAAO,EAAE,yBAAyB,OAAO,aAAa,KAAK,IAAI,IAAI,CAAC;AACrL;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,8CAA8C,OAAO,UAAU,kBAAkB,OAAO,EAAE,yBAAyB,OAAO,SAAS,KAAK,IAAI,IAAI,CAAC;AAC1J;AAEA,SAAS,YAAY,SAA0B;AAC7C,QAAM,QAAQ,WAAW,WAAW,QAAQ,QACxC,wEAAwE,OAAO,WAAW,qBAAqB,IAAI,QAAQ,KAAK,CAAC,UACjI;AACJ,MAAI;AACJ,UAAQ,QAAQ,MAAM;IACpB,KAAK;AACH,aAAO,YAAY,QAAQ,KAAK;AAChC;IACF,KAAK;AACH,aAAO,SAAS,QAAQ,OAAO,QAAQ,KAAK;AAC5C;IACF,KAAK;AACH,aAAO,UAAU,OAAO;AACxB;IACF,KAAK;AACH,aAAO,aAAa,QAAQ,KAAK;AACjC;IACF,KAAK;AACH,aAAO,SAAS,QAAQ,MAAM,QAAQ,IAAI;AAC1C;EACJ;AACA,SAAO,uCAAuC,KAAK,GAAG,IAAI;AAC5D;AAGO,SAAS,WAAW,QAAwB;AACjD,QAAM,WAAW,cAAc,MAAM,IACjC;IACE;EACF,IACA,OAAO,SAAS,IAAI,WAAW,EAAE,KAAK,EAAE;AAE5C,QAAM,UAAU,OAAO,SAAS,SAC5B,uEAAuE,OAAO,QAC3E;IACC,CAAC,MACC,gBAAgB,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,iCAAiC,OAAO,aAAa,eAAe,OAAO,QAAQ,qBAAqB,OAAO,UAAU,kBAAkB,OAAO,IAAI,sBAAsB,IAAI,EAAE,KAAK,CAAC,uBAAuB,OAAO,SAAS,KAAK,IAAI,EAAE,OAAO,CAAC;EACrS,EACC,KAAK,EAAE,CAAC,eACX;AAEJ,SAAO;;SAEA,IAAI,OAAO,KAAK,CAAC;mCACS,OAAO,QAAQ,UAAU,OAAO,WAAW,gBAAgB,WAAW,QAAQ;;gFAEjC,IAAI,OAAO,KAAK,CAAC;MAC3F,OAAO,WAAW,iDAAiD,OAAO,SAAS,KAAK,IAAI,OAAO,QAAQ,CAAC,SAAS,EAAE;;IAEzH,QAAQ;IACR,OAAO;IACP,OAAO,SAAS,uDAAuD,OAAO,SAAS,KAAK,IAAI,OAAO,MAAM,CAAC,cAAc,EAAE;;AAElI;ACzJA,IAAM,OAAwD;EAC5D,IAAI;EACJ,MAAM;;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,OAAO;EACP,MAAM;EACN,KAAK;AACP;AAEA,SAAS,MAAM,MAAc,MAAc,OAAwB;AACjE,SAAO,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK;AACjD;AAGO,SAAS,aAAa,MAAsB;AACjD,SAAO,KAAK,QAAQ,gBAAgB,EAAE,EAAE;AAC1C;AAEA,SAAS,IAAI,MAAc,OAAe,OAAiC;AACzE,QAAM,MAAM,KAAK,IAAI,GAAG,QAAQ,aAAa,IAAI,CAAC;AAClD,SAAO,UAAU,UAAU,IAAI,OAAO,GAAG,IAAI,OAAO,OAAO,IAAI,OAAO,GAAG;AAC3E;AAEA,SAAS,QAAQ,MAAc,OAAwB;AACrD,SAAO,MAAM,KAAK,YAAY,GAAG,KAAK,MAAM,KAAK;AACnD;AAEA,SAAS,WACP,OACA,OACA,OACU;AACV,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAGhC,QAAM,aAAa,KAAK;IACtB;IACA,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;EAC9C;AACA,SAAO,MAAM,IAAI,CAAC,MAAM;AACtB,UAAM,QAAQ,MAAM,IAAI,EAAE,OAAO,YAAY,MAAM,GAAG,KAAK,OAAO,KAAK;AACvE,UAAM,QAAQ,MAAM,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,KAAK,EAAE,IAAI,IAAI,KAAK,MAAM,KAAK;AAC7E,UAAM,OAAO,EAAE,OAAO,MAAM,KAAK,EAAE,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI;AAC9D,WAAO,KAAK,KAAK,KAAK,KAAK,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,EAAE;EAC1D,CAAC;AACH;AAEA,SAAS,QAAQ,OAAc,OAAe,OAAgB,OAA0B;AACtF,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC,KAAK,MAAM,SAAS,oBAAoB,KAAK,OAAO,KAAK,CAAC,EAAE;AAC5F,QAAM,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC;AACjE,QAAM,aAAa,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC;AAC7E,QAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,aAAa,EAAE,CAAC;AACnE,SAAO,MAAM,IAAI,CAAC,MAAM;AACtB,UAAM,QAAS,EAAE,SAAS,EAAE,OAAO,YAAa;AAChD,UAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,KAAK,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAC3E,UAAM,MAAM,MAAM,SAAI,OAAO,MAAM,GAAG,EAAE,OAAO,KAAK,EAAE,IAAI,IAAI,KAAK,MAAM,KAAK,IAC5E,MAAM,SAAI,OAAO,WAAW,MAAM,GAAG,KAAK,KAAK,KAAK;AACtD,UAAM,QAAQ,MAAM,IAAI,EAAE,OAAO,YAAY,MAAM,GAAG,KAAK,OAAO,KAAK;AACvE,UAAM,QAAQ,MAAM,OAAO,EAAE,QAAQ,EAAE,KAAK,GAAG,KAAK,KAAK,KAAK;AAC9D,WAAO,KAAK,KAAK,KAAK,GAAG,KAAK,KAAK;EACrC,CAAC;AACH;AAEA,SAAS,SACP,SACA,OACA,OACU;AACV,MAAI,QAAQ,KAAK,WAAW,GAAG;AAC7B,WAAO,CAAC,KAAK,MAAM,QAAQ,SAAS,YAAY,KAAK,OAAO,KAAK,CAAC,EAAE;EACtE;AACA,QAAM,UAAU,IAAI,IAAI,QAAQ,WAAW,CAAC,CAAC;AAC7C,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,MAAM;IAAK,EAAE,QAAQ,KAAK;IAAG,CAAC,GAAG,MAC9C,KAAK;MACH,QAAQ,QAAQ,CAAC,GAAG,UAAU;MAC9B,GAAG,QAAQ,KAAK,IAAI,CAAC,MAAM,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,MAAM;IACtD;EACF;AAGA,MAAI,QAAQ,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC;AAChD,SAAO,QAAQ,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,GAAG;AAC/C,UAAM,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG,MAAM,CAAC;AACjD,WAAO,MAAM,KAAK;AAClB,aAAS;EACX;AAEA,QAAM,OAAO,CAAC,MAAY,MAAsB;AAC9C,UAAM,OAAO,OAAO,QAAQ,EAAE;AAC9B,WAAO,KAAK,SAAS,OAAO,CAAC,IAAI,GAAG,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,WAAM;EACrF;AAEA,QAAM,SACJ,OACA,QAAQ,QACL,IAAI,CAAC,GAAG,MAAM,MAAM,IAAI,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,IAAI,CAAC,IAAI,UAAU,MAAM,GAAG,KAAK,OAAO,KAAK,CAAC,EACrG,KAAK,IAAI;AACd,QAAM,OAAO,OAAO,MAAM,OAAO,IAAI,CAAC,MAAM,SAAI,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK;AACtF,QAAM,OAAO,QAAQ,KAAK;IACxB,CAAC,QACC,OACA,IACG,IAAI,CAAC,MAAM,MAAM,IAAI,KAAK,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,IAAI,CAAC,IAAI,UAAU,MAAM,CAAC,EACjF,KAAK,IAAI;EAChB;AACA,SAAO,CAAC,QAAQ,MAAM,GAAG,IAAI;AAC/B;AAEA,SAAS,YACP,OACA,OACU;AACV,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,QAAM,aAAa,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC;AAC/E,SAAO,MAAM;IACX,CAAC,OACC,KAAK,MAAM,IAAI,GAAG,OAAO,YAAY,MAAM,GAAG,KAAK,OAAO,KAAK,CAAC,KAAK,MAAM,GAAG,OAAO,GAAG,OAAO,KAAK,GAAG,IAAI,IAAI,KAAK,OAAO,KAAK,CAAC;EACrI;AACF;AAEA,IAAM,cAAoC;EACxC,IAAI;EACJ,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;AACT;AAEA,SAAS,WAAW,SAAkB,OAAe,OAA0B;AAC7E,QAAM,QAAkB,CAAC;AACzB,MAAI,WAAW,WAAW,QAAQ,MAAO,OAAM,KAAK,QAAQ,QAAQ,OAAO,KAAK,CAAC;AACjF,UAAQ,QAAQ,MAAM;IACpB,KAAK;AACH,YAAM,KAAK,GAAG,WAAW,QAAQ,OAAO,OAAO,KAAK,CAAC;AACrD;IACF,KAAK;AACH,YAAM,KAAK,GAAG,QAAQ,QAAQ,OAAO,OAAO,OAAO,QAAQ,KAAK,CAAC;AACjE;IACF,KAAK;AACH,YAAM,KAAK,GAAG,SAAS,SAAS,OAAO,KAAK,CAAC;AAC7C;IACF,KAAK;AACH,YAAM,KAAK,GAAG,YAAY,QAAQ,OAAO,KAAK,CAAC;AAC/C;IACF,KAAK;AACH,YAAM;QACJ,KAAK,MAAM,GAAG,YAAY,QAAQ,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,KAAK,CAAC,IAAI,QAAQ,IAAI;MACxF;AACA;EACJ;AACA,QAAM,KAAK,EAAE;AACb,SAAO;AACT;AAGO,SAAS,UAAU,QAAgB,OAAmB,CAAC,GAAW;AACvE,QAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;AAC1D,QAAM,QAAQ,KAAK,SAAS;AAE5B,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,KAAK,CAAC;AAChD,MAAI,OAAO,SAAU,OAAM,KAAK,MAAM,OAAO,UAAU,KAAK,OAAO,KAAK,CAAC;AACzE,QAAM,KAAK,MAAM,SAAI,OAAO,KAAK,GAAG,KAAK,KAAK,KAAK,CAAC;AACpD,QAAM,KAAK,EAAE;AAEb,MAAI,cAAc,MAAM,GAAG;AACzB,UAAM;MACJ,KAAK,MAAM,oCAAoC,KAAK,MAAM,KAAK,CAAC;MAChE;MACA;IACF;EACF,OAAO;AACL,eAAW,WAAW,OAAO,SAAU,OAAM,KAAK,GAAG,WAAW,SAAS,OAAO,KAAK,CAAC;EACxF;AAEA,MAAI,OAAO,SAAS,QAAQ;AAC1B,UAAM,KAAK,QAAQ,QAAQ,KAAK,CAAC;AACjC,eAAW,KAAK,OAAO,SAAS;AAC9B,YAAM,KAAK,KAAK,MAAM,EAAE,SAAS,KAAK,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,OAAO,KAAK,KAAK,KAAK,CAAC,EAAE;IAC1F;AACA,UAAM,KAAK,EAAE;EACf;AACA,MAAI,OAAO,OAAQ,OAAM,KAAK,MAAM,OAAO,QAAQ,KAAK,KAAK,KAAK,CAAC;AAEnE,SAAO,MAAM,KAAK,IAAI;AACxB;ACrMA,IAAM,MAAM,CAAC,MAAc,UACzB,QAAQ,IAAI,IAAK,OAAO,QAAS,KAAK,QAAQ,CAAC,CAAC,MAAM;AAExD,IAAM,QAAQ,CAAC,OAAe,WAAW,OACvC,GAAG,QAAQ,GAAG,MAAM,QAAQ,CAAC,CAAC,GAAG,KAAK;AAGjC,SAAS,WAAW,OAAuB;AAChD,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAClD,QAAM,KAAK,QAAQ;AACnB,MAAI,MAAM,EAAG,QAAO,GAAG,GAAG,QAAQ,MAAM,KAAK,IAAI,CAAC,CAAC;AACnD,SAAO,IAAI,QAAQ,SAAW,QAAQ,CAAC,CAAC;AAC1C;AAwBO,SAAS,YAAY,OAA2B;AACrD,QAAM,QAAQ,MAAM,SAAS,MAAM,YAAY,MAAM,YAAY,MAAM;AACvE,QAAM,aAAa,MAAM,SAAS,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE;AAC9D,QAAM,YAAY,WAAW,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,YAAY,CAAC;AAE3E,QAAM,WAAsB;IAC1B;MACE,MAAM;MACN,OAAO;QACL,EAAE,OAAO,eAAe,OAAO,MAAM;QACrC,EAAE,OAAO,UAAU,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM,QAAQ,KAAK,GAAG,MAAM,KAAK;QACnF,EAAE,OAAO,aAAa,OAAO,MAAM,WAAW,MAAM,IAAI,MAAM,WAAW,KAAK,EAAE;QAChF;UACE,OAAO;UACP,OAAO,MAAM;UACb,MAAM,IAAI,MAAM,WAAW,KAAK;UAChC,MAAM,MAAM,YAAY,MAAM,SAAS,MAAM,SAAS;QACxD;QACA,EAAE,OAAO,YAAY,OAAO,MAAM,SAAS,OAAO;QAClD;UACE,OAAO;UACP,OAAO,WAAW;UAClB,MAAM,WAAW,SAAS,IAAI,SAAS;QACzC;MACF;IACF;EACF;AAMA,QAAM,sBAAsB,MAAM,SAAS;IACzC,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ;EAC1D;AACA,MAAI,qBAAqB;AACvB,aAAS,KAAK;MACZ,MAAM;MACN,OAAO;MACP,OAAO,CAAC,GAAG,MAAM,QAAQ,EACtB,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAChE,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,OAAO;QACX,OAAO,EAAE;QACT,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE;QAChD,MAAM,GAAG,EAAE,MAAM;QACjB,MAAM,EAAE,SAAS,IAAK,OAAiB;MACzC,EAAE;IACN,CAAC;EACH;AAEA,WAAS,KAAK;IACZ,MAAM;IACN,OAAO;IACP,SAAS,CAAC,WAAW,WAAW,UAAU,WAAW;IACrD,SAAS,CAAC,GAAG,GAAG,CAAC;IACjB,OAAO;IACP,MAAM,MAAM,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,EAAE,OAAO,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC;EACnF,CAAC;AAED,MAAI,UAAU,SAAS,GAAG;AACxB,aAAS,KAAK;MACZ,MAAM;MACN,MAAM;MACN,MAAM,GAAG,UAAU,MAAM,gGAA2F,UACjH,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,IAAI,CAAC;IACf,CAAC;EACH;AACA,aAAW,WAAW,MAAM,eAAe,CAAC,GAAG;AAC7C,aAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,gBAAgB,OAAO,GAAG,CAAC;EAC/E;AAEA,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,GAAG,KAAK,iBAAiB,MAAM,SAAS,MAAM,oBAAiB,IAAI,MAAM,QAAQ,KAAK,CAAC;IACjG;IACA,SAAS;MACP,EAAE,OAAO,qBAAqB,SAAS,wBAAwB;MAC/D,EAAE,OAAO,2BAA2B,SAAS,0BAA0B;IACzE;EACF;AACF;AAYO,SAAS,kBAAkB,MAAuB,OAA6B,CAAC,GAAW;AAChG,QAAM,WAAW,oBAAI,IAAoB;AACzC,aAAW,KAAK,KAAM,UAAS,IAAI,EAAE,SAAS,SAAS,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;AAE9E,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,KAAK,UAAU,WAAW,KAAK,OAAO,SAAM,KAAK,MAAM,WAAW,GAAG,KAAK,MAAM;IAC1F,UAAU;MACR;QACE,MAAM;QACN,OAAO;UACL,EAAE,OAAO,UAAU,OAAO,KAAK,OAAO;UACtC,GAAG,CAAC,GAAG,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YACnD,OAAO;YACP,OAAO;YACP,MAAM,OAAO,YAAY,MAAM,WAAY,OAAgB;UAC7D,EAAE;QACJ;MACF;MACA;QACE,MAAM;QACN,OAAO;QACP,SAAS,CAAC,SAAS,UAAU,UAAU,WAAW,WAAW;QAC7D,SAAS,CAAC,CAAC;QACX,OAAO;QACP,MAAM,KAAK,IAAI,CAAC,MAAM;UACpB,EAAE;UACF,EAAE,UAAU;UACZ,EAAE;UACF,EAAE,WAAW;UACb,EAAE,kBAAkB,SAAY,WAAM,WAAW,EAAE,aAAa;QAClE,CAAC;MACH;IACF;IACA,SAAS;MACP,EAAE,OAAO,gBAAgB,SAAS,sBAAsB;MACxD,EAAE,OAAO,gBAAgB,SAAS,mBAAmB;IACvD;EACF;AACF;AAWO,SAAS,YAAY,OAA2B;AACrD,QAAM,QAAQ,MAAM,cAAc,MAAM,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACpF,QAAM,OAAO,MAAM,SAAS;IAC1B,CAAC,MAAM,MAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI;IACzC,EAAE,MAAM,UAAK,OAAO,EAAE;EACxB;AAEA,QAAM,WAAsB;IAC1B;MACE,MAAM;MACN,OAAO;QACL,EAAE,OAAO,SAAS,OAAO,WAAW,KAAK,EAAE;QAC3C,EAAE,OAAO,QAAQ,OAAO,MAAM,SAAS,OAAO;QAC9C,EAAE,OAAO,YAAY,OAAO,WAAW,KAAK,KAAK,GAAG,MAAM,KAAK,KAAK;QACpE;UACE,OAAO;UACP,OAAO,WAAW,MAAM,SAAS,SAAS,QAAQ,MAAM,SAAS,SAAS,CAAC;QAC7E;MACF;IACF;IACA;MACE,MAAM;MACN,OAAO;MACP,OAAO;MACP,OAAO,MAAM,SAAS,IAAI,CAAC,OAAO;QAChC,OAAO,EAAE;QACT,OAAO,EAAE;QACT,MAAM,WAAW,EAAE,KAAK;MAC1B,EAAE;IACJ;EACF;AAEA,MAAI,MAAM,WAAW,QAAQ;AAC3B,aAAS,KAAK;MACZ,MAAM;MACN,OAAO;MACP,OAAO,MAAM,UACV,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,OAAO,EAAE,OAAO,MAAM,WAAW,EAAE,KAAK,EAAE,EAAE;IACjF,CAAC;EACH;AAEA,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,MAAM;IAChB;IACA,SAAS,CAAC,EAAE,OAAO,qBAAqB,SAAS,mBAAmB,CAAC;EACvE;AACF;AAaO,SAAS,eAAe,MAA4B;AACzD,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,GAAG,KAAK,MAAM;IACxB,UAAU;MACR;QACE,MAAM;QACN,OAAO;UACL,EAAE,OAAO,aAAa,OAAO,KAAK,OAAO;UACzC,EAAE,OAAO,aAAa,OAAO,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO;QACtE;MACF;MACA;QACE,MAAM;QACN,OAAO;QACP,SAAS,CAAC,QAAQ,MAAM,QAAQ,YAAY,OAAO;QACnD,SAAS,CAAC,GAAG,GAAG,CAAC;QACjB,OAAO;QACP,MAAM,KAAK,IAAI,CAAC,MAAM;UACpB,EAAE;UACF,OAAO,EAAE,EAAE;UACX,EAAE,mBAAmB,SAAY,WAAM,WAAW,EAAE,cAAc;UAClE,EAAE,iBAAiB,SAAY,WAAM,GAAG,EAAE,YAAY;UACtD,EAAE,UAAU,SAAY,WAAM,MAAM,EAAE,KAAK;QAC7C,CAAC;MACH;IACF;IACA,SAAS,CAAC,EAAE,OAAO,0BAA0B,SAAS,iBAAiB,CAAC;EAC1E;AACF;AAWO,SAAS,cAAc,OAA6B;AACzD,QAAM,QAAQ,MAAM,OAAO,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AACpF,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,GAAG,MAAM,OAAO,MAAM;IAChC,UAAU;MACR;QACE,MAAM;QACN,OAAO;UACL;YACE,OAAO;YACP,OAAO,MAAM,YAAY,SAAY,WAAM,MAAM,MAAM,SAAS,MAAM,QAAQ;YAC9E,OAAO,MAAM,WAAW,KAAK,KAAK,SAAS;UAC7C;UACA;YACE,OAAO;YACP,OAAO,MAAM,YAAY,SAAY,WAAM,MAAM,MAAM,SAAS,MAAM,QAAQ;UAChF;UACA,EAAE,OAAO,gBAAgB,OAAO,MAAM,OAAO,MAAM,QAAQ,EAAE;QAC/D;MACF;MACA;QACE,MAAM;QACN,OAAO;QACP,SAAS,CAAC,QAAQ,eAAe,QAAQ;QACzC,SAAS,CAAC,CAAC;QACX,OAAO;QACP,MAAM,MAAM,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,aAAa,MAAM,EAAE,QAAQ,MAAM,QAAQ,CAAC,CAAC;MACxF;IACF;IACA,SAAS,CAAC,EAAE,OAAO,iBAAiB,SAAS,yBAAyB,CAAC;EACzE;AACF;AAYO,SAAS,aAAa,OAA4B;AACvD,QAAM,MAAM,MAAM,WAAW,MAAM,aAAa;AAChD,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,MAAM,mBAAmB,sBAAsB;IACzD,UAAU;MACR;QACE,MAAM;QACN,OAAO;UACL;YACE,OAAO;YACP,OAAO,MAAM,MAAM,SAAS,MAAM,QAAQ;YAC1C,MAAM,MAAM,SAAS;UACvB;UACA,GAAI,MAAM,YAAY,SAClB,CAAC,IACD,CAAC,EAAE,OAAO,WAAW,OAAO,MAAM,QAAQ,CAAC;QACjD;MACF;MACA;QACE,MAAM;QACN,OAAO;QACP,OAAO;UACL,EAAE,OAAO,eAAe,OAAO,MAAM,mBAAmB,YAAY,WAAW;UAC/E;YACE,OAAO;YACP,OAAO,MAAM,cAAc,SAAY,WAAM,MAAM,MAAM,WAAW,MAAM,QAAQ;UACpF;QACF;MACF;MACA,GAAI,MACC;QACC;UACE,MAAM;UACN,MAAM;UACN,MAAM;QACR;MACF,IACA,CAAC;IACP;IACA,SAAS;MACP,EAAE,OAAO,UAAU,SAAS,wBAAwB;MACpD,EAAE,OAAO,yBAAyB,SAAS,oBAAoB;IACjE;EACF;AACF;AAIO,SAAS,gBAAgB,SAA0D;AACxF,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,GAAG,QAAQ,MAAM,QAAQ,QAAQ,WAAW,IAAI,MAAM,KAAK;IACrE,UAAU;MACR,EAAE,MAAM,WAAW,OAAO,CAAC,EAAE,OAAO,WAAW,OAAO,QAAQ,OAAO,CAAC,EAAE;MACxE;QACE,MAAM;QACN,OAAO;QACP,SAAS,CAAC,SAAS,MAAM;QACzB,OAAO;QACP,MAAM,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,QAAG,CAAC;MACnD;IACF;IACA,SAAS;MACP,EAAE,OAAO,gBAAgB,SAAS,0BAA0B;MAC5D,EAAE,OAAO,mBAAmB,SAAS,6BAA6B;IACpE;EACF;AACF;AAgBO,SAAS,iBAAiB,OAAgC;AAC/D,QAAM,WAAsB;IAC1B;MACE,MAAM;MACN,OAAO;QACL;UACE,OAAO;UACP,OAAO,MAAM,aAAa,OAAO,YAAY,MAAM,aAAa,QAAQ,WAAW;UACnF,MAAM,MAAM,aAAa,OAAO,OAAO,MAAM,aAAa,QAAQ,aAAa;QACjF;QACA,EAAE,OAAO,QAAQ,OAAO,MAAM,UAAU,SAAI;QAC5C,EAAE,OAAO,SAAS,OAAO,MAAM,SAAS,SAAI;QAC5C,EAAE,OAAO,WAAW,OAAO,MAAM,eAAe,UAAU,EAAE;MAC9D;IACF;IACA;MACE,MAAM;MACN,OAAO;MACP,OAAO;QACL,EAAE,OAAO,SAAS,OAAO,MAAM,MAAM;QACrC,EAAE,OAAO,OAAO,OAAO,MAAM,OAAO,eAAe;QACnD,GAAI,MAAM,YAAY,CAAC,EAAE,OAAO,aAAa,OAAO,MAAM,WAAW,MAAM,OAAe,CAAC,IAAI,CAAC;MAClG;IACF;EACF;AAEA,MAAI,MAAM,QAAQ,QAAQ;AACxB,aAAS,KAAK;MACZ,MAAM;MACN,OAAO;MACP,SAAS,CAAC,QAAQ,QAAQ;MAC1B,SAAS,CAAC,CAAC;MACX,MAAM,MAAM,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,CAAC;IACnF,CAAC;EACH;AACA,MAAI,MAAM,gBAAgB,QAAQ;AAChC,aAAS,KAAK;MACZ,MAAM;MACN,MAAM;MACN,MAAM,8BAA8B,MAAM,eAAe,KAAK,IAAI,CAAC;IACrE,CAAC;EACH;AAEA,SAAO;IACL,IAAI;IACJ,OAAO;IACP,UAAU,MAAM,OAAO,MAAM;IAC7B;IACA,SAAS;MACP,EAAE,OAAO,UAAU,SAAS,sBAAsB;MAClD,EAAE,OAAO,eAAe,SAAS,uBAAuB;IAC1D;EACF;AACF;;;ACjeA;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,SAAW;AAAA,EACX,QAAU;AAAA,EACV,UAAY;AAAA,EACZ,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,EACT;AAAA,EACA,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,OAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,cAAc;AAAA,IACd,MAAQ;AAAA,IACR,gBAAkB;AAAA,IAClB,cAAc;AAAA,IACd,MAAQ;AAAA,IACR,cAAc;AAAA,IACd,oBAAoB;AAAA,EACtB;AAAA,EACA,cAAgB;AAAA,IACd,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,kCAAkC;AAAA,IAClC,6BAA6B;AAAA,IAC7B,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,WAAa;AAAA,IACb,WAAa;AAAA,IACb,YAAc;AAAA,IACd,KAAO;AAAA,EACT;AAAA,EACA,iBAAmB;AAAA,IACjB,6BAA6B;AAAA,IAC7B,eAAe;AAAA,IACf,oCAAoC;AAAA,IACpC,6BAA6B;AAAA,IAC7B,QAAU;AAAA,IACV,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,eAAe;AAAA,IACf,uBAAuB;AAAA,IACvB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,EACtB;AAAA,EACA,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,eAAiB;AAAA,IACf,QAAU;AAAA,EACZ;AAAA,EACA,KAAO;AAAA,IACL,eAAe;AAAA,IACf,SAAW;AAAA,EACb;AACF;;;ACtEO,IAAM,kBAA0B,gBAAI;;;AEsC3C,IAAM,0BAAkD;;EAEtD,YAAY;;EAGZ,mCAAmC;;EAGnC,2BAA2B;;EAG3B,yBAAyB;EACzB,WAAW;;EAGX,4BAA4B;EAC5B,oCAAoC;EACpC,iCAAiC;EACjC,iCAAiC;EACjC,sBAAsB;EACtB,2BAA2B;EAC3B,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,+BAA+B;EAC/B,8BAA8B;EAC9B,oBAAoB;EACpB,kCAAkC;EAClC,qBAAqB;EACrB,+BAA+B;AACjC;AAEA,IAAM,wBAAwB;AAC9B,IAAMC,aAAY;AAGlB,IAAM,8BAA8B;AAGpC,IAAM,iBAAiB;AAGvB,SAAS,gBAAgB,aAAyC;AAChE,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAO,KAAK,KAAK,IAAK,YAAY,WAAW,CAAC,OAAO;EACvD;AACA,SAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACvC;AAUA,SAAS,WACP,UACA,aACA,eACA,aACM;AACN,QAAMC,OAAM,KAAK,MAAO,gBAAgB,cAAe,GAAG;AAC1D,QAAM,gBAAgB,gBAAgB,WAAW;AAEjD,UAAQ;IACN,KAAK,UAAU;MACb,QAAQ;MACR;MACA;MACA,iBAAiB;MACjB,eAAe;MACf,iBAAiBA;MACjB,OAAOA,QAAO;MACd,IAAI,KAAK,IAAI;IACf,CAAC;EACH;AACA,MAAIA,QAAO,IAAI;AACb,YAAQ;MACN,0BAA0B,QAAQ,OAAOA,IAAG,eAAe,aAAa,IAAI,WAAW,sBAAsB,aAAa;IAC5H;EACF;AACF;AAuBA,IAAM,mBAAmB,oBAAI,IAAiC;AAE9D,SAAS,kBAAkB,KAAkC;AAC3D,MAAI,IAAI,iBAAiB,IAAI,GAAG;AAChC,MAAI,CAAC,GAAG;AACN,QAAI;MACF,KAAK,CAAC;MACN,UAAU,CAAC;MACX,MAAM,QAAQ,QAAQ;MACtB,kBAAkB;MAClB,qBAAqB;IACvB;AACA,qBAAiB,IAAI,KAAK,CAAC;EAC7B;AACA,SAAO;AACT;AAEA,SAAS,OAAO,KAAe,aAA+B;AAE5D,MAAI,IAAI;AACR,SAAO,IAAI,IAAI,UAAU,IAAI,CAAC,KAAM,YAAa;AACjD,SAAO,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI;AAChC;AAmBA,eAAsB,oBACpB,YACA,UACA,WAAyB,eACV;AAEf,QAAM,OAAQ,WAAuC,SAAS;AAG9D,MACE,MAAM,MAAM,qBAAqB,MAAM,UACtC,WAAuC,qBAAqB,MAAM,QACnE;AACA;EACF;AAGA,QAAM,gBAAgB,YAAY,cAAc,QAAQ;AACxD,QAAM,gBAAgB,YAAY,UAAU,QAAQ;AACtD;AAEA,eAAe,gBACb,YACA,UACA,UACe;AACf,QAAM,YAAY,GAAG,UAAU,IAAI,QAAQ;AAC3C,QAAM,cAAc,wBAAwB,QAAQ,KAAK;AACzD,QAAM,WAAW,KAAK,MAAM,cAAc,cAAc;AAExD,QAAM,QAAQ,kBAAkB,SAAS;AAEzC,QAAM,SAAS,MAAM,KAAK,KAAK,YAAY;AACzC,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,cAAc,MAAMD;AAG1B,YAAM,MAAM,OAAO,MAAM,KAAK,WAAW;AACzC,YAAM,WAAW,OAAO,MAAM,UAAU,WAAW;AAEnD,YAAM,gBAAgB,MAAM,IAAI;AAChC,YAAM,gBAAgB,MAAM,SAAS;AAIrC,UAAI,gBAAgB,KAAK,aAAa,cAAc;AAClD,cAAM,UAAU,KAAK,MAAO,gBAAgB,cAAe,GAAG;AAC9D,cAAM,aACJ,MAAM,MAAM,oBAAoB;AAClC,cAAM,kBAAkB,WAAW,MAAM,MAAM;AAC/C,YAAI,cAAc,iBAAiB;AACjC,qBAAW,UAAU,YAAY,eAAe,WAAW;AAC3D,gBAAM,mBAAmB;AACzB,cAAI,WAAW,IAAI;AACjB,kBAAM,sBAAsB;UAC9B;QACF;AACA,YAAI,UAAU,IAAI;AAChB,gBAAM,sBAAsB;QAC9B;MACF;AAKA,YAAM,aAAa,iBAAiB;AACpC,YAAM,YAAY,aAAa,WAAW,iBAAiB;AAE3D,UAAI,CAAC,cAAc,CAAC,WAAW;AAE7B,cAAM,IAAI,KAAK,GAAG;AAClB,YAAI,aAAa,SAAS;AACxB,gBAAM,SAAS,KAAK,GAAG;QACzB;AACA;MACF;AAGA,UAAI;AACJ,UAAI,YAAY;AAEd,cAAM,SAAS,MAAM,IAAI,CAAC;AAC1B,iBAASA,cAAa,MAAM,UAAU;MACxC,OAAO;AAEL,cAAM,cAAc,MAAM,SAAS,CAAC;AACpC,iBAASA,cAAa,MAAM,eAAe;MAC7C;AAEA,YAAM,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;IACtD;EACF,CAAC;AAED,QAAM,OAAO;AACb,SAAO;AACT;AAMO,SAAS,oBAAoB,UAA0B;AAC5D,SAAO,wBAAwB,QAAQ,KAAK;AAC9C;AAMO,SAAS,yBACd,aACA,UAC4D;AAC5D,QAAM,YAAY,GAAG,WAAW,IAAI,QAAQ;AAC5C,QAAM,QAAQ,iBAAiB,IAAI,SAAS;AAC5C,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,iBAAiB,GAAG,uBAAuB,EAAE;EACxD;AACA,QAAM,cAAc,KAAK,IAAI,IAAIA;AACjC,MAAI,kBAAkB;AACtB,aAAW,MAAM,MAAM,KAAK;AAC1B,QAAI,KAAK,YAAa;EACxB;AACA,MAAI,wBAAwB;AAC5B,aAAW,MAAM,MAAM,UAAU;AAC/B,QAAI,KAAK,YAAa;EACxB;AACA,SAAO,EAAE,iBAAiB,sBAAsB;AAClD;;;AG1TO,IAAM,4BAA4B;AAGlC,SAAS,iBACd,OACA,KACA,UAAU,2BACsB;AAChC,QAAM,QAAQ,KAAK,MAAM,GAAG,GAAG,YAAY;AAC3C,QAAM,UAAU,KAAK,MAAM,GAAG,KAAK,YAAY;AAC/C,MAAI,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM,OAAO,KAAK,UAAU,OAAO;AACnE,WAAO,EAAE,OAAO,IAAI;EACtB;AACA,QAAM,aAAa,SAAS,UAAU,KAAK;AAC3C,MAAI,UAAU,YAAY;AACxB,WAAO,EAAE,OAAO,IAAI,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAG,IAAI;EACvE;AACA,SAAO,EAAE,OAAO,IAAI;AACtB;AAGO,SAAS,gBAAgB,OAAO,2BAA2B,MAAM,oBAAI,KAAK,GAAmC;AAClH,QAAM,MAAM,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,GAAG,IAAI,WAAW,CAAC,CAAC;AACxF,QAAM,QAAQ,IAAI,KAAK,GAAG;AAC1B,QAAM,WAAW,IAAI,WAAW,KAAK,OAAO,EAAE;AAC9C,SAAO;IACL,OAAO,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE;IACtC,KAAK,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE;EACpC;AACF;AAEO,SAAS,eAAe,OAAkC;AAC/D,SAAO,EAAE,MAAM;AACjB;AAGO,SAAS,mBAAmB,OAAkC;AACnE,SAAO,EAAE,MAAM;AACjB;AAOO,SAAS,uBACd,OACA,mBACA,UAAmC,CAAC,GACX;AACzB,MAAI,QAAQ,yBAAyB,UAAa,QAAQ,iBAAiB,QAAW;AACpF,UAAM,IAAI,MAAM,2DAA2D;EAC7E;AACA,SAAO;IACL,YAAY,EAAE,MAAM;IACpB;IACA,GAAI,QAAQ,iBAAiB,SACzB,EAAE,cAAc,QAAQ,aAAa,IACrC,EAAE,sBAAsB,QAAQ,wBAAwB,KAAK;EACnE;AACF;AAEO,SAAS,8BACd,OACA,OACA,KAC2E;AAC3E,SAAO,sBAAsB,OAAO,OAAO,GAAG;AAChD;AAuDO,SAAS,sBACd,OACA,OACA,KAC2E;AAC3E,QAAM,SAAS,iBAAiB,OAAO,GAAG;AAC1C,SAAO,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;AACzC;AAgBO,SAAS,yBAAyB,QAAqC;AAC5E,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,MAAM;AACZ,QAAM,QACJ,IAAI,uBAAuB,OAAO,IAAI,wBAAwB,WACzD,IAAI,sBACL;AAEN,QAAM,SAAS,MAAM;AACrB,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAAG,QAAO;AAE5D,aAAW,OAAO,CAAC,YAAY,WAAW,GAAY;AACpD,UAAM,OAAO,MAAM,GAAG;AACtB,QAAI,CAAC,MAAM,QAAQ,IAAI,EAAG;AAC1B,eAAW,SAAS,MAAM;AACxB,UAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,YAAM,QAAS,MAAkC;AACjD,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;IAC5D;EACF;AACA,SAAO;AACT;AAwNA,SAAS,wBAA0C;AACjD,SAAO,EAAE,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,EAAE;AACrE;AAEA,SAAS,iBAAiB,KAAuB,KAAoC;AACnF,QAAM,QAAQ,OAAO,IAAI,SAAS,CAAC;AACnC,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,UAAU,EAAG;AAG5C,QAAM,MAAM,OAAO,IAAI,aAAa,IAAI,QAAQ,IAAI,UAAU,EAAE,EAAE,YAAY;AAC9E,MAAI,QAAQ,eAAe,QAAQ,YAAY,QAAQ,YAAY;AACjE,QAAI,UAAU;AACd;EACF;AACA,MAAI,QAAQ,aAAa;AACvB,QAAI,aAAa;AACjB;EACF;AACA,MAAI,QAAQ,UAAU,QAAQ,eAAe,QAAQ,mBAAmB,QAAQ,aAAa;AAC3F,QAAI,aAAa;AACjB;EACF;AACA,QAAM,MAAM,IAAI;AAChB,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI,QAAQ,EAAG,KAAI,UAAU;aACpB,QAAQ,EAAG,KAAI,aAAa;aAC5B,QAAQ,EAAG,KAAI,aAAa;QAChC,KAAI,SAAS;AAClB;EACF;AACA,MAAI,SAAS;AACf;AAOO,SAAS,wBAAwB,MAAiC;AACvE,QAAM,MAAM,sBAAsB;AAClC,QAAM,OAAQ,QAAQ,CAAC;AACvB,QAAM,UAAW,KAAK,QAAQ,KAAK,wBAAwB;AAC3D,QAAM,WAAsB,MAAM,QAAQ,OAAO,IAC7C,UACA,MAAM,QAAS,QAAoC,OAAO,IACtD,QAAoC,UACtC,CAAC;AAEP,aAAW,WAAW,UAAU;AAC9B,UAAM,MAAO,WAAW,CAAC;AACzB,UAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,CAAC;AAC7D,QAAI,SAAS,SAAS,GAAG;AACvB,iBAAW,MAAM,UAAU;AACzB,cAAM,OAAS,IAAgC,QAAQ,CAAC;AACxD,cAAM,WAAW,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AAC7D,mBAAW,OAAO,SAAU,kBAAiB,KAAM,OAAO,CAAC,CAA6B;MAC1F;AACA;IACF;AACA,QAAI,UAAU,OAAO,IAAI,UAAU,CAAC;AACpC,QAAI,aAAa,OAAO,IAAI,aAAa,CAAC;AAC1C,QAAI,aAAa,OAAO,IAAI,aAAa,IAAI,gBAAgB,IAAI,QAAQ,CAAC;AAC1E,QAAI,SAAS,OAAO,IAAI,SAAS,IAAI,cAAc,CAAC;EACtD;AAEA,MAAI,QAAQ,IAAI,SAAS,IAAI,YAAY,IAAI,YAAY,IAAI;AAC7D,SAAO;AACT;AAuBO,SAAS,2BACd,MACgD;AAChD,MAAI,OAAO,SAAS,SAAU,QAAO,EAAE,WAAW,KAAK;AACvD,MAAI,KAAK,cAAc,OAAW,QAAO,EAAE,WAAW,KAAK,UAAU;AACrE,MAAI,KAAK,eAAe,OAAW,QAAO,EAAE,YAAY,KAAK,WAAW;AACxE,QAAM,IAAI,MAAM,+DAA+D;AACjF;AAkBO,IAAM,uBAAuB,MAAM,OAAO,OAAO;AAGxD,IAAM,eAAe;AAGrB,IAAM,qBAAqB,CAAC,YAAY,aAAa,WAAW;AAEhE,SAAS,sBAAsB,GAAgD;AAC7E,aAAW,OAAO,oBAAoB;AACpC,UAAM,IAAI,EAAE,GAAG;AACf,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,EAAG,QAAO;EAC1D;AACA,SAAO;AACT;AAMO,SAAS,sBAAsB,IAAY,aAAa,sBAA8B;AAC3F,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,MAAM,KAAK,cAAc,EAAG,QAAO;AAC/D,QAAME,OAAM,KAAK,MAAQ,KAAK,eAAgB,aAAc,GAAG;AAC/D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGA,IAAG,CAAC;AACvC;AAYO,IAAM,oBAAoB;EAC/B;EAAK;EAAK;EAAK;EAAK;EAAM;EAAM;EAAM;EAAM;EAAO;EAAO;EAAO;AACnE;AAgBO,IAAM,kBAAkB,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,EAAE;AAEjE,IAAM,yBAAyB,CAAC,GAAG,GAAG,CAAC;AAQvC,IAAM,sBAAsB,CAAC,IAAI,IAAI,IAAI,GAAG;AAE5C,IAAM,kBAAkB;AAQjB,SAAS,wBAAwB,MAAc,aAAa,GAAW;AAC5E,MAAI,CAAC,OAAO,SAAS,IAAI,KAAK,QAAQ,EAAG,QAAO;AAChD,SAAQ,OAAO,MAAO,kBAAkB,KAAK,IAAI,GAAG,UAAU,IAAK;AACrE;AAcO,SAAS,oBACd,UACA,cACA,aAAa,GAC8B;AAC3C,QAAM,cAAc,WAAW;AAC/B,QAAM,mBAAmB,cAAc,wBAAwB,gBAAgB,GAAG,UAAU;AAE5F,aAAW,cAAc,qBAAqB;AAC5C,UAAM,eAAe,cAAc;AACnC,QAAI,eAAe,kBAAkB;AACnC,aAAO,EAAE,cAAc,SAAS,KAAK,MAAM,MAAM,UAAU,EAAE;IAC/D;EACF;AAKA,QAAM,OAAO,oBAAoB,oBAAoB,SAAS,CAAC;AAC/D,SAAO,EAAE,cAAc,cAAc,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI,EAAE;AAC7E;AAoBO,SAAS,yBACd,UACyB;AACzB,QAAM,MAA+B,EAAE,GAAG,SAAS;AAEnD,QAAM,gBAAgB,IAAI,cAAc;AACxC,QAAM,UAAU,sBAAsB,GAAG;AACzC,QAAM,cAAc,iBAAiB,YAAY,UAAa,YAAY;AAE1E,QAAM,kBAAkB,OAAO,IAAI,oBAAoB,WAAW,IAAI,kBAAkB;AACxF,QAAM,eAAe,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;AAC/E,QAAM,aAAa,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AAEzE,MAAI,iBAAiB,UAAa,CAAC,kBAAkB,SAAS,YAAqB,GAAG;AACpF,UAAM,IAAI;MACR,gBAAgB,YAAY,oCACd,kBAAkB,KAAK,IAAI,CAAC;IAE5C;EACF;AAEA,QAAM,cAAc,IAAI;AACxB,MAAI,gBAAgB,UAAa,CAAC,uBAAuB,SAAS,WAAoB,GAAG;AACvF,UAAM,IAAI;MACR,4BAA4B,WAAW;IAGzC;EACF;AAIA,MACE,IAAI,cAAc,SACjB,gBAAgB,gBAAgB,SAAS,gBAAgB,gBAAgB,WAC1E,IAAI,iBAAiB,QACrB;AACA,UAAM,IAAI;MACR;IAEF;EACF;AAEA,MAAI,iBAAiB,OAAW,KAAI,4BAA4B;AAChE,SAAO,IAAI;AAEX,MAAI,aAAa;AACf,QAAI,UAAU;AACd,QAAI,oBAAoB,UAAa,kBAAkB,GAAG;AACxD,YAAM,QAAQ,oBAAoB,iBAAiB,cAAc,UAAU;AAC3E,gBAAU,MAAM;AAChB,UAAI,2BAA2B,MAAM;IACvC;AAGA,eAAW,OAAO,oBAAoB;AACpC,UAAI,OAAO,IAAK,KAAI,GAAG,IAAI;IAC7B;AACA,QAAI,CAAC,mBAAmB,KAAK,CAAC,MAAM,KAAK,GAAG,EAAG,KAAI,WAAW;EAChE;AACA,SAAO,IAAI;AAEX,MAAI,oBAAoB,QAAW;AAGjC,QAAI,CAAC,aAAa;AAChB,YAAM,QAAQ,sBAAsB,GAAG,KAAK;AAC5C,UAAI,2BAA2B,sBAAsB,iBAAiB,KAAK;IAC7E;AACA,QAAI,mBAAmB,IAAI,oBAAoB;EACjD;AACA,SAAO,IAAI;AAEX,SAAO;AACT;AAWO,SAAS,gCACd,SACyB;AACzB,QAAM,MAA+B,EAAE,GAAG,QAAQ;AAElD,QAAM,sBAAsB,IAAI,cAAc;AAC9C,aAAW,OAAO,oBAAoB;AACpC,QAAI,EAAE,OAAO,KAAM;AACnB,QAAI,IAAI,GAAG,MAAM,KAAK,oBAAqB,KAAI,GAAG,IAAI;EACxD;AACA,MAAI,uBAAuB,CAAC,mBAAmB,KAAK,CAAC,MAAM,KAAK,GAAG,GAAG;AACpE,QAAI,WAAW;EACjB;AACA,SAAO,IAAI;AAEX,QAAM,kBAAkB,IAAI;AAC5B,MAAI,OAAO,oBAAoB,UAAU;AAGvC,UAAM,QAAQ,sBAAsB,GAAG,KAAK;AAC5C,QAAI,2BAA2B,sBAAsB,iBAAiB,KAAK;EAC7E;AACA,SAAO,IAAI;AAEX,SAAO;AACT;;;AChrBO,SAAS,0BACd,MACyB;AACzB,QAAM,SAAkC,CAAC;AACzC,MAAI,KAAK,KAAM,QAAO,aAAa,KAAK;AACxC,MAAI,KAAK,MAAO,QAAO,cAAc,KAAK;AAC1C,MAAI,KAAK,eAAgB,QAAO,iBAAiB,KAAK;AACtD,MAAI,KAAK,cAAc,OAAW,QAAO,YAAY,KAAK;AAC1D,MAAI,KAAK,OAAQ,QAAO,eAAe,KAAK;AAC5C,SAAO;AACT;AAUO,SAAS,wBAAwB,KAA6B;AACnE,MAAI,QAAQ,QAAQ,OAAO,QAAQ,SAAU,QAAO;AACpD,QAAM,MAAO,IAAgC;AAC7C,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,QAAO;AACtD,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,eAAW,SAAS,KAAK;AACvB,UAAI,SAAS,OAAO,UAAU,UAAU;AACtC,cAAM,OAAQ,MAAkC;AAChD,YAAI,OAAO,SAAS,YAAY,KAAK,SAAS,EAAG,QAAO;MAC1D;IACF;EACF;AACA,SAAO;AACT;AASO,SAAS,qBAAqB,KAGnC;AACA,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,EAAE,MAAM,KAAK,UAAU,KAAK;AAC3D,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,UAAM,MAAM;AACZ,QAAI,MAAM,QAAQ,IAAI,cAAc,GAAG;AACrC,aAAO,EAAE,MAAM,IAAI,gBAA6B,UAAU,IAAI;IAChE;EACF;AACA,SAAO,EAAE,MAAM,MAAM,UAAU,KAAK;AACtC;AAYO,SAAS,qBACd,KACA,UACA,MACA,UACA,SACS;AACT,MAAI,SAAS,QAAQ,aAAa,KAAM,QAAO;AAE/C,QAAM,QAAQ,WAAW,SAAS;AAClC,QAAM,UAAU,QAAQ,SAAS;AAGjC,MAAI,aAAa,KAAM,QAAO;AAE9B,QAAM,MAA+B;IACnC,GAAG;IACH,gBAAgB;IAChB,SAAS;IACT,SAAS,SAAS,SAAS;EAC7B;AAEA,MAAI,UAAU,GAAG;AACf,QAAI,YAAY;AAChB,QAAI,OACF,GAAG,OAAO,OAAO,KAAK;EAE1B;AAEA,SAAO;AACT;AAQO,SAAS,uBACd,KACA,MACS;AACT,QAAM,EAAE,MAAM,SAAS,IAAI,qBAAqB,GAAG;AACnD,MAAI,WAAW;AAEf,MAAI,YAAY,KAAK,QAAQ;AAC3B,UAAM,SAAS,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,YAAY;AACtD,eAAW,SAAS,OAAO,CAAC,QAAQ;AAClC,YAAM,SAAS,wBAAwB,GAAG;AAC1C,aAAO,WAAW,QAAQ,OAAO,YAAY,MAAM;IACrD,CAAC;EACH;AAEA,QAAM,UAAU,UAAU;AAE1B,MAAI,YAAY,OAAO,KAAK,WAAW,YAAY,KAAK,SAAS,GAAG;AAClE,eAAW,SAAS,MAAM,KAAK,MAAM;EACvC;AACA,MAAI,YAAY,OAAO,KAAK,UAAU,YAAY,KAAK,SAAS,GAAG;AACjE,eAAW,SAAS,MAAM,GAAG,KAAK,KAAK;EACzC;AAEA,SAAO,qBAAqB,KAAK,UAAU,MAAM,UAAU,OAAO;AACpE;AC5JA,IAAM,eAAe,oBAAI,IAAoB;AAGtC,SAAS,sBAAsB,MAAc,aAA2B;AAC7E,MAAI,YAAa,cAAa,IAAI,MAAM,WAAW;AACrD;AAEO,SAAS,mBAAmB,MAAkC;AACnE,SAAO,aAAa,IAAI,IAAI;AAC9B;AAuBO,SAAS,kBAAkB,MAA8C;AAC9E,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,YAAY,KACf,MAAM,wBAAwB,EAC9B,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,QAAM,OAAO;IACX,GAAG,UAAU,MAAM,GAAG,CAAC;IACvB,GAAG,UAAU,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,cAAc,KAAK,CAAC,CAAC;EAC3D;AAEA,SAAO,KAAK,SAAS,KAAK,KAAK,GAAG,IAAI;AACxC;AAkCO,SAAS,mBAAmB,MAA+C;AAChF,QAAM,gBAAgB,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAClE,QAAM,QAAsB,CAAC;AAE7B,aAAW,QAAQ,CAAC,GAAG,KAAK,SAAS,EAAE,KAAK,GAAG;AAC7C,QAAI,SAAS,iBAAiB,SAAS,kBAAmB;AAE1D,UAAM,UAAU,cAAc,IAAI,IAAI;AACtC,UAAM,OAAO,kBAAkB,mBAAmB,IAAI,CAAC;AAEvD,UAAM,KAAK;MACT;MACA,aACE,QACA,SAAS,eACT,sBAAsB,IAAI,uBAAuB,KAAK,SAAS,IAAI,CAAC;MAEtE,cAAc,SAAS,gBAAgB,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;IAC1E,CAAC;EACH;AAEA,SAAO,CAAC,KAAK,aAAa,GAAG,KAAK;AACpC;AAUO,IAAM,eAAe;;;;;;;;;;;;AItI5B,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,cAAc,MAA0C,QAAQ,KAAyB;AACvG,aAAW,QAAQ,kBAAkB;AACnC,UAAM,IAAI,IAAI,IAAI;AAClB,QAAI,KAAK,EAAE,KAAK,KAAK,CAAC,EAAE,WAAW,IAAI,KAAK,CAAC,EAAE,SAAS,WAAW,EAAG,QAAO,EAAE,KAAK;AAAA,EACtF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAa,UAA0B;AACvD,QAAM,IAAI,IAAI,KAAK;AACnB,SAAO,oBAAoB,KAAK,CAAC,IAAI,EAAE,YAAY,IAAI;AACzD;AAEA,SAAS,SAAS,MAAsB;AACtC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,SAAK,KAAK,WAAW,CAAC;AACtB,QAAI,KAAK,KAAK,GAAG,QAAQ;AAAA,EAC3B;AACA,SAAO,MAAM;AACf;AAEA,SAAS,IAAI,KAAa,QAAgB,GAAmB;AAC3D,QAAM,QAAQ,CAAC,MAAc;AAAA,IAC3B,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC1B,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC1B,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EAC5B;AACA,QAAM,IAAI,MAAM,GAAG;AACnB,QAAM,IAAI,MAAM,MAAM;AACtB,QAAM,KAAK,CAAC,MACV,KAAK,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,EAChC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AACpB,SAAO,IAAI,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AAClC;AAGO,SAAS,0BACd,MACA,SAAS,WACD;AACR,QAAM,SAAS,KAAK,KAAK,KAAK,KAAK,MAAM,GAAG,EAAE;AAC9C,QAAM,SAAS,MAAM,CAAC,EAAG,YAAY;AACrC,QAAM,YAAY,SAAS,QAAQ,SAAS;AAC5C,QAAM,OAAO,IAAI,WAAW,WAAW,IAAI;AAC3C,QAAM,QAAQ,IAAI,WAAW,WAAW,IAAI;AAC5C,QAAM,IAAI,SAAS,KAAK;AACxB,QAAM,QAAQ,IAAI;AAClB,QAAM,MAAM,MAAM,IAAI,KAAW,SAAS,EAAE,CAAC;AAE7C,MAAI,OAAO;AACX,MAAI,UAAU,GAAG;AACf,WAAO,mEAAmE,GAAG;AAAA,EAC/E,WAAW,UAAU,GAAG;AACtB,WAAO,gDAAgD,GAAG;AAAA,EAC5D,WAAW,UAAU,GAAG;AACtB,WAAO,sEAAsE,GAAG;AAAA,EAClF,OAAO;AACL,WAAO,uJAAuJ,GAAG;AAAA,EACnK;AAEA,SAAO;AAAA,gHACuG,UAAU,KAAK,CAAC;AAAA;AAAA,0BAEtG,GAAG;AAAA,sCACS,KAAK;AAAA,uCACJ,SAAS;AAAA,wCACR,IAAI;AAAA;AAAA;AAAA;AAAA,IAIxC,IAAI;AAAA,gJACwI,UAAU,MAAM,CAAC;AAAA;AAAA;AAGjK;AAEA,SAAS,UAAU,GAAmB;AACpC,SAAO,EAAE,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ;AACpG;AAEO,SAAS,WAAW,MAAc,QAAgB,SAA0B;AACjF,SAAO;AAAA,IACL,8BAA8B,IAAI;AAAA,IAClC,UAAU,YAAY,OAAO,MAAM;AAAA,IACnC,0FAA0F,KAAK,CAAC,KAAK,GAAG;AAAA,IACxG,UAAU,MAAM;AAAA,IAChB;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AAGA,eAAsB,iBACpB,MACA,QACA,QACA,SAC6B;AAC7B,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,gDAAgD;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ,WAAW,MAAM,QAAQ,OAAO;AAAA,QACxC,MAAM;AAAA,QACN,SAAS;AAAA,QACT,iBAAiB;AAAA,QACjB,GAAG;AAAA,MACL,CAAC;AAAA,IACH,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,KAAK,OAAO,CAAC,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,uBACpB,OACmB;AACnB,QAAM,OAAO,MAAM,KAAK,KAAK,KAAK;AAClC,QAAM,SAAS,SAAS,MAAM,UAAU,WAAW,SAAS;AAC5D,QAAM,MAAM,0BAA0B,MAAM,MAAM;AAClD,QAAM,MAAM,cAAc,MAAM,QAAQ,OAAO,YAAY,cAAc,QAAQ,MAAM,CAAC,EAAE;AAC1F,MAAI,KAAK;AACP,UAAM,MAAM,MAAM,iBAAiB,MAAM,QAAQ,KAAK,MAAM,OAAO;AACnE,QAAI,KAAK;AACP,aAAO,EAAE,KAAK,UAAU,YAAY,QAAQ,UAAU,WAAW,IAAI;AAAA,IACvE;AAAA,EACF;AACA,SAAO,EAAE,KAAK,UAAU,YAAY,QAAQ,MAAM;AACpD;","names":["probe","readFile","mix","join","join","join","join","join","mkdtemp","rm","tmpdir","mkdtemp","join","tmpdir","rm","pct","WINDOW_MS","pct","pct"]}
|