@carrierllc/mcp 0.13.35 → 0.13.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
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/ocs-params.ts","../../../packages/ocs-client/src/schemas.ts","../../../packages/ocs-client/src/timeout-budgets.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-spec/src/mcc-iso.ts","../../../packages/ocs-client/src/list-subscriber.ts","../../../packages/ocs-client/src/default-account.ts","../../../packages/ocs-client/src/router-catalog.ts","../../../packages/ocs-client/src/ocs-array.ts","../../../packages/ocs-client/src/ocs-summary.ts","../../../packages/ocs-client/src/analysis.ts","../../../packages/ocs-client/src/usage.ts","../../../packages/ocs-client/src/intelligence.ts","../../../packages/ocs-client/src/diagnose-shapes.ts","../../../packages/ocs-client/src/gy-country-history.ts","../../../packages/ocs-client/src/network-default.ts","../../../packages/ocs-client/src/network-default-service.ts","../../../packages/ocs-client/src/catalog-create-authorization.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\n/**\n * Stamp the Worker name into wrangler config.\n *\n * JSONC, so this is a targeted textual rewrite of the `name` field rather than\n * a parse/serialise round-trip that would strip the file's comments - the same\n * approach as `setCustomDomain`. Returns false when there is no wrangler config\n * or no name field to rewrite.\n */\nexport async function setWorkerName(storefront: string, name: string): Promise<boolean> {\n const path = join(storefront, \"wrangler.jsonc\");\n if (!(await exists(path))) return false;\n const body = await readFileText(path);\n const re = /^\\s*\"?name\"?\\s*:\\s*\"[^\"]*\"/m;\n if (!re.test(body)) return false;\n const patched = body.replace(re, (m) => m.replace(/\"[^\"]*\"\\s*$/, JSON.stringify(name)));\n if (patched === body) return false;\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 /** One sentence for <meta name=\"description\"> and social cards. */\n description: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n supportWhatsapp: string;\n /** Display name on transactional mail. */\n emailFromName: string;\n /** Mailbox transactional mail is sent from. The address is this plus `domain`. */\n emailFromMailbox: string;\n /** Public origin of the storefront, no trailing slash. */\n appUrl: 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 description: \"Data that works in 200+ countries. Instant QR activation. Human support.\",\n domain: \"carrier.llc\",\n supportEmail: \"support@carrier.llc\",\n supportUrl: \"https://carrier.llc/help\",\n supportWhatsapp: \"+17864604829\",\n emailFromName: \"Carrier\",\n emailFromMailbox: \"hello\",\n appUrl: \"https://carrier.llc\",\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, the accent 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 description: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n emailFromName: string;\n emailFromMailbox: string;\n appUrl: 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 `# Optional. With a publishable key the payment form is embedded in this`,\n `# site's own checkout page; without one the customer is sent to Stripe's`,\n `# hosted page and back. Both take money — the embedded one loses fewer`,\n `# people on the way.`,\n `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=`,\n `# Optional. Scopes the catalog to one hosted shop. Empty keeps the unscoped template list.`,\n `NEXT_PUBLIC_STOREFRONT_SLUG=`,\n `# Optional. Sends the eSIM QR code by email once an order is ready.`,\n `# \\`carrier site email\\` authenticates you at Resend in the browser and`,\n `# fills this in. Without it the storefront still sells and activates; the`,\n `# activation page just says email delivery is not set up.`,\n `# The from-address is composed from brand.config.ts (emailFromMailbox +`,\n `# domain), so verify that domain at resend.com/domains first.`,\n `RESEND_API_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 // Optional. Present: checkout is embedded in the storefront's own page.\n // Absent: checkout redirects to Stripe's hosted page, which still works.\n NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: \"public\",\n // Optional. Present: the scaffold catalog is scoped to one hosted shop.\n // Absent: the catalog client calls the unscoped template list.\n NEXT_PUBLIC_STOREFRONT_SLUG: \"public\",\n CARRIER_API_KEY: \"secret\",\n CLERK_SECRET_KEY: \"secret\",\n STRIPE_SECRET_KEY: \"secret\",\n // Optional. Present: the storefront emails the QR code to whoever paid.\n // Absent: the activation page says email delivery is not set up, and every\n // install route it shows on screen still works.\n RESEND_API_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 setWorkerName,\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 * The worker name the template ships in wrangler.jsonc. Deploying under it by\n * accident can clobber an unrelated Worker that already exists on the account,\n * so deploys that would land here are refused unless the operator opts in\n * (CAR-313).\n */\nexport const SCAFFOLD_DEFAULT_WORKER_NAME = \"storefront\";\n\n/**\n * Resolve the name a deploy runs under.\n *\n * Precedence: an explicit `--name` flag wins over everything - it is the\n * operator's deliberate choice. Then the project's own wrangler config, then\n * the slugged brand name. The old code let the wrangler config silently swallow\n * an explicit `--name`, so `carrier site deploy --name foo` deployed under the\n * scaffold default instead (CAR-313).\n */\nexport function resolveDeployName(opts: {\n explicitName?: string;\n configuredName?: string | undefined;\n brandName: string;\n}): string {\n if (opts.explicitName) return slug(opts.explicitName);\n return opts.configuredName ?? slug(opts.brandName);\n}\n\n/**\n * Whether this deploy must be refused unless `--force` is passed: the resolved\n * name is the scaffold default. The guard keys on the resolved name only — an\n * explicit `--name` is no exemption, because `--name storefront`, or a name\n * that slugifies to it (e.g. `--name \"!!!\"`), would otherwise land on the\n * default Worker without the operator acknowledging the overwrite risk.\n */\nexport function needsForceForDefaultName(opts: {\n projectName: string;\n force?: boolean;\n}): boolean {\n return !opts.force && opts.projectName === SCAFFOLD_DEFAULT_WORKER_NAME;\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 /** Explicit `--name` from the CLI: the operator's deliberate choice, wins over wrangler config. */\n explicitName?: string;\n /** Acknowledge the scaffold-default-name risk and deploy anyway. */\n force?: 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: resolveDeployName({ explicitName: opts.explicitName, brandName: 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: resolveDeployName({ explicitName: opts.explicitName, brandName: 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 // An explicit --name is the operator's deliberate choice and wins over the\n // wrangler config (CAR-313). Otherwise the project's own config names the\n // deployment: falling back to the brand slug would deploy a second Worker and\n // leave the real one untouched.\n const projectName = resolveDeployName({\n explicitName: opts.explicitName,\n configuredName: await configuredProjectName(chosen.id, target),\n brandName: brand.name,\n });\n\n // Deploying under the scaffold default \"storefront\" by accident can clobber\n // an unrelated Worker that already exists on the account. Any deploy that\n // resolves to the default — explicit --name included — needs --force.\n if (needsForceForDefaultName({ projectName, force: opts.force })) {\n return {\n ok: false,\n projectName,\n statuses,\n target: chosen.id,\n reason:\n 'Refusing to deploy under the scaffold default worker name \"storefront\" - ' +\n \"a different Worker may already use it on this account. Pass --name <worker> \" +\n 'to pick a name, or --force to deploy as \"storefront\" anyway.',\n };\n }\n\n // Persist an explicit name into wrangler config so later deploys without the\n // flag stay on the same Worker (JSONC-safe rewrite, same as setCustomDomain).\n if (opts.explicitName && chosen.id === \"cloudflare\") {\n await setWorkerName(target, projectName);\n }\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, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\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.13.35\",\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 \"type\": \"module\",\n \"carrierCompatibility\": {\n \"claudeOAuthOwnerVersion\": \"2.1.277\",\n \"claudeOAuthOwnerVersions\": [\"2.1.277\", \"2.1.296\"]\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"plugin\",\n \"shell\",\n \"native\",\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-concurrency=1 --test test/*.test.js && pnpm run test:oauth-repair\",\n \"test:oauth-repair\": \"node --experimental-transform-types --test-concurrency=1 --test test/oauth-repair.test.mjs test/claude-owner.test.mjs test/current-owner.test.mjs\",\n \"test:oauth-shell\": \"node scripts/check-oauth-shell.mjs\",\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.18.1\",\n \"@modelcontextprotocol/ext-apps\": \"^1.7.5\",\n \"@modelcontextprotocol/sdk\": \"^1.31.0\",\n \"@noble/hashes\": \"^2.3.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\": \"^5.20260903.1\",\n \"@types/node\": \"^26.1.2\",\n \"@typescript-eslint/eslint-plugin\": \"^8.59.4\",\n \"@typescript-eslint/parser\": \"^8.69.0\",\n \"eslint\": \"^10.9.1\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"@carrierllc/sdk\": \"workspace:*\",\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 * 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\n/**\n * `modifySubscriberStatus` body.\n *\n * `subscriber` is a common.SubscriberId object. A bare ICCID string is rejected\n * live with OCS error 2 \"Invalid JSON content\" / \"Cannot construct instance of\n * SubscriberId from String value\". The same object is what affectPackageToSubscriber\n * already sends (`{ subscriber: { iccid } }`).\n */\nexport function modifySubscriberStatusParams(\n iccid: string,\n newStatus: string,\n): { subscriber: { iccid: string }; newStatus: string } {\n return { subscriber: { iccid }, newStatus };\n}\n\n/**\n * Nest a bare ICCID that a caller still put on `subscriber`.\n *\n * An object is already a SubscriberId and is left alone. Numbers and other\n * shapes pass through so this can sit on the serialisation chokepoint without\n * rewriting unrelated methods.\n */\nexport function nestSubscriberIdParam<T>(params: T): T {\n if (!params || typeof params !== \"object\" || Array.isArray(params)) return params;\n const record = params as Record<string, unknown>;\n if (typeof record[\"subscriber\"] !== \"string\") return params;\n return { ...record, subscriber: { iccid: record[\"subscriber\"] } } as T;\n}\n\n/**\n * Confirmation for `modifySubscriberPrepaidPackageStatus`.\n *\n * The write succeeds and OCS returns no package body (the method key is null\n * or absent). Callers were promised an updated package record. This object is\n * that record: `updated` means the status envelope was success, and `status`\n * is `ACTIVE` or `INACTIVE` for the `active` flag that was sent.\n */\nexport interface PackageStatusConfirmation {\n updated: true;\n subsPrepaidPackageId?: number;\n active?: boolean;\n status?: \"ACTIVE\" | \"INACTIVE\";\n}\n\nexport function packageStatusConfirmation(params: unknown): PackageStatusConfirmation {\n const confirmation: PackageStatusConfirmation = { updated: true };\n if (!params || typeof params !== \"object\" || Array.isArray(params)) return confirmation;\n const record = params as Record<string, unknown>;\n const id = record[\"subsPrepaidPackageId\"];\n if (typeof id === \"number\" && Number.isFinite(id)) confirmation.subsPrepaidPackageId = id;\n if (typeof record[\"active\"] === \"boolean\") {\n confirmation.active = record[\"active\"];\n confirmation.status = record[\"active\"] ? \"ACTIVE\" : \"INACTIVE\";\n }\n return confirmation;\n}\n\n/**\n * Successful OCS calls sometimes carry no method body. `null` then looks like\n * a failure. Two methods have a documented stand-in:\n *\n * - `listSteeringList` → `[]` (an empty catalog, not an error)\n * - `modifySubscriberPrepaidPackageStatus` → {@link packageStatusConfirmation}\n *\n * Any other method, including `getSimProviderStatus`, keeps `null`.\n */\nexport function normaliseAbsentOcsPayload<T>(method: string, payload: T, params: unknown): T {\n if (payload !== null && payload !== undefined) return payload;\n if (method === \"listSteeringList\") return [] as T;\n if (method === \"modifySubscriberPrepaidPackageStatus\") {\n return packageStatusConfirmation(params) as T;\n }\n return payload;\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\n/**\n * `subscriberId` on modifySubscriberMobilePlan / modifySubscriberVoipPlan is a\n * SubscriberId OBJECT, not a bare id.\n *\n * The name reads like a number and that is exactly the trap. Probed live\n * 2026-08-28:\n *\n * {\"modifySubscriberMobilePlan\":{\"subscriberId\":{\"__carrierParamProbe__\":1}}}\n * -> Unrecognized field ... (class srv.ocs.backend.lib.json.api.common.SubscriberId),\n * 6 known properties: \"multiImsi\", \"iccid\", \"imsi\", \"activationCode\",\n * \"msisdn\", \"subscriberId\"\n *\n * That is the same common.SubscriberId class cleanSubscriberAllPackages and\n * listSubscriberPrepaidPackages take, so an ICCID identifies the subscriber\n * directly and NO id lookup is needed. Bridge4IP documents the same shape\n * (docs/research/bridge4ip-ocs-api/endpoints-full.md §3.10/3.11:\n * `{\"subscriberId\": {\"subscriberId\": 1000}, \"mobilePlanId\": 10}`) and calls\n * the doubled key a quirk of their own docs.\n *\n * Writing the ICCID as a BARE `subscriberId` would be the dangerous fix: the\n * inner `subscriberId` is an internal numeric id in a different id space, so a\n * 20-digit ICCID landing there could address another record entirely. Nesting\n * keeps the identifier in the field that means ICCID.\n */\nexport function subscriberPlanParams(\n iccid: string,\n): { subscriberId: { iccid: string } } {\n return { subscriberId: { iccid } };\n}\n\n/**\n * Limit fields OCS accepts inside `modifySubscriberPrepaidPackageLimits`'s\n * `newLimits` object, mapped from the names callers already use.\n *\n * Probed live 2026-08-28 by pointing the probe sentinel at the SUB-object —\n * the same Jackson trick works one level down:\n *\n * {\"modifySubscriberPrepaidPackageLimits\":{\"newLimits\":{\"__carrierParamProbe__\":1}}}\n * -> Unrecognized field ... (class Request$SubscriberPrepaidPackageLimits),\n * 5 known properties: \"mtcSecond\", \"dataByte\", \"mocSecond\", \"moSms\", \"mtSms\"\n *\n * Matches Bridge4IP's documented example exactly (endpoints-full.md §3.4).\n * The tool's own description advertises `dataLimit`, so the aliases are the\n * published contract and are mapped rather than broken.\n */\nexport const OCS_PREPAID_PACKAGE_LIMIT_FIELDS = [\n \"dataByte\",\n \"mocSecond\",\n \"mtcSecond\",\n \"moSms\",\n \"mtSms\",\n] as const;\n\nconst LIMIT_ALIASES: Record<string, (typeof OCS_PREPAID_PACKAGE_LIMIT_FIELDS)[number]> = {\n dataByte: \"dataByte\",\n dataBytes: \"dataByte\",\n dataLimit: \"dataByte\",\n databyte: \"dataByte\",\n mocSecond: \"mocSecond\",\n mocSeconds: \"mocSecond\",\n voiceOutLimit: \"mocSecond\",\n mtcSecond: \"mtcSecond\",\n mtcSeconds: \"mtcSecond\",\n voiceInLimit: \"mtcSecond\",\n moSms: \"moSms\",\n moSmsNumber: \"moSms\",\n smsOutLimit: \"moSms\",\n mtSms: \"mtSms\",\n mtSmsNumber: \"mtSms\",\n smsInLimit: \"mtSms\",\n};\n\n/**\n * Build the `newLimits` sub-object.\n *\n * Unknown keys THROW rather than being dropped. Silently discarding a limit a\n * caller asked for would report success on a package whose allowance never\n * changed, which is worse than the rejection this replaces: today OCS refuses\n * the whole call, so nothing is written and nothing is claimed.\n *\n * `null` is preserved — OCS reads it as \"leave this limit unchanged\".\n */\nexport function prepaidPackageLimits(\n limits: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const unknown: string[] = [];\n for (const [key, value] of Object.entries(limits)) {\n const target = LIMIT_ALIASES[key];\n if (!target) {\n unknown.push(key);\n continue;\n }\n out[target] = value;\n }\n if (unknown.length > 0) {\n throw new Error(\n `Unknown package limit field(s): ${unknown.join(\", \")}. ` +\n `OCS accepts only ${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(\", \")} ` +\n `inside newLimits (aliases: ${Object.keys(LIMIT_ALIASES).join(\", \")}).`,\n );\n }\n if (Object.keys(out).length === 0) {\n throw new Error(\n `No package limits to change. Provide at least one of ` +\n `${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(\", \")}.`,\n );\n }\n return out;\n}\n\n/**\n * Coerce a caller's date into the `java.time.LocalDateTime` OCS parses.\n *\n * `newExpirationDate` is a LocalDateTime and a date-only string is REJECTED —\n * verified live 2026-08-28 against packageId -1 (a row that cannot exist, so\n * the parse verdict is the only thing observed):\n *\n * \"2026-06-01\" -> code 2, DateTimeParseException at index 10\n * \"2026-06-01T23:59:59\" -> code 6, \"Subscriber package not found\"\n *\n * Code 6 means the payload parsed and business logic ran, which is the pass.\n *\n * `bareDate` decides what a date-only string means, and the two answers are\n * not interchangeable. An EXPIRY widened to 00:00:00 silently cuts the\n * caller's last day off the package, so expiries end the day. An ACTIVATION\n * pushed to 23:59:59 silently costs the caller the whole first day, so\n * activations start it. Defaulting to \"end\" keeps every existing expiry call\n * site behaving exactly as before.\n */\nexport function ocsLocalDateTime(value: string, bareDate: \"start\" | \"end\" = \"end\"): string {\n const trimmed = value.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n return bareDate === \"start\" ? `${trimmed}T00:00:00` : `${trimmed}T23:59:59`;\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$/.test(trimmed)) return `${trimmed}:00`;\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(trimmed)) {\n // Strip a trailing zone designator: LocalDateTime has no offset and OCS\n // throws on one.\n return trimmed.replace(/(?:Z|[+-]\\d{2}:?\\d{2})$/, \"\").slice(0, 19);\n }\n throw new Error(\n `\"${value}\" is not a date OCS can read. Use YYYY-MM-DD or ` +\n `YYYY-MM-DDTHH:mm:ss — this field is a java.time.LocalDateTime and a ` +\n `zone offset is rejected.`,\n );\n}\n\n/**\n * The active-period window OCS currently holds for one subscriber package.\n *\n * Read from a `listSubscriberPrepaidPackages` response, whose rows carry the\n * live window as `tsactivationutc` / `tsexpirationutc` — already in the\n * `YYYY-MM-DDTHH:mm:ss` LocalDateTime form the write side wants, so the values\n * round-trip without reformatting.\n *\n * Verified live 2026-08-28 on ICCID 8948010000020241170, package 208596510:\n * tsactivationutc \"2026-08-27T21:15:38\", tsexpirationutc \"2027-08-27T21:15:38\".\n *\n * Returns undefined for a package that is not on this subscriber, so a caller\n * refuses instead of writing a window onto a row it never read.\n */\nexport function activePeriodFromPackages(\n listResponse: unknown,\n packageId: number,\n): { activation?: string; expiration?: string } | undefined {\n const root = (listResponse ?? {}) as Record<string, unknown>;\n const payload = (root.listSubscriberPrepaidPackages ?? root.data ?? root) as\n | Record<string, unknown>\n | undefined;\n const packages = Array.isArray(payload?.packages) ? payload.packages : [];\n for (const entry of packages) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;\n if (Number(id) !== packageId) continue;\n const activation = rec.tsactivationutc ?? rec.tsActivationUtc;\n const expiration = rec.tsexpirationutc ?? rec.tsExpirationUtc;\n const window: { activation?: string; expiration?: string } = {};\n if (typeof activation === \"string\" && activation) window.activation = activation;\n if (typeof expiration === \"string\" && expiration) window.expiration = expiration;\n return window;\n }\n return undefined;\n}\n\n/** Sent when a caller supplies no comment. OCS requires the field to be present. */\nexport const OCS_ACTIVE_PERIOD_DEFAULT_COMMENT = \"Active period changed via Carrier\";\n\nexport interface ActivePeriodChange {\n /** New start of the window. Bare dates mean 00:00:00. */\n startDate?: string;\n /** New end of the window. Bare dates mean 23:59:59. */\n endDate?: string;\n /** Audit note stored on the OCS record. */\n comment?: string;\n /** The window OCS holds today, used to fill whichever bound the caller omitted. */\n current?: { activation?: string; expiration?: string };\n}\n\n/**\n * Build `modifySubscriberPrepaidPackageActivePeriod` params.\n *\n * OCS accepts exactly four field names here and REQUIRES all four. Every other\n * shape fails the whole request, which is how this route 502'd on every call\n * it ever served. Verified live 2026-08-28 against ocs.esimvault.cloud:\n *\n * {subscriber, packageId, startDate, endDate} -> code 2, Unrecognized field \"subscriber\"\n * {packageId, startDate, endDate} -> code 2, Unrecognized field \"startDate\"\n * {packageId, newActivationDateUtc} -> code 2, Missing 'newExpirationDateUtc'\n * {packageId, newExpirationDateUtc} -> code 2, Missing 'newActivationDateUtc'\n * {packageId, both dates} -> code 2, Missing 'comment'\n * {packageId, both dates, comment} -> code 6, Subscriber package not found\n *\n * Code 6 is the pass: the payload parsed and OCS ran the lookup. Note the\n * third and fourth lines — \"both dates are mandatory\" is why a partial change\n * cannot simply omit a bound. The caller must read the live window and pass it\n * as `current`, or this throws rather than inventing one.\n *\n * `packageId` is the SUBSCRIBER package id (`subscriberprepaidpackageid`), not\n * the template id. Verified live on ICCID 8948010000020241170: template\n * 2895241 answers code 6 \"Subscriber package not found\", subscriber package\n * 208596510 answers code 14 \"Can only modify package configured to be\n * activated at first use\" — a business rule, so that id reached the row.\n */\nexport function packageActivePeriodParams(\n packageId: number,\n change: ActivePeriodChange,\n): {\n packageId: number;\n newActivationDateUtc: string;\n newExpirationDateUtc: string;\n comment: string;\n} {\n if (change.startDate === undefined && change.endDate === undefined) {\n throw new Error(\"Provide startDate and/or endDate to change a package's active period.\");\n }\n const activation =\n change.startDate !== undefined\n ? ocsLocalDateTime(change.startDate, \"start\")\n : change.current?.activation;\n const expiration =\n change.endDate !== undefined\n ? ocsLocalDateTime(change.endDate, \"end\")\n : change.current?.expiration;\n\n for (const [field, value, omitted] of [\n [\"newActivationDateUtc\", activation, \"startDate\"],\n [\"newExpirationDateUtc\", expiration, \"endDate\"],\n ] as const) {\n if (value !== undefined) continue;\n throw new Error(\n `OCS requires both bounds on an active-period change and rejects the ` +\n `whole request with \"Missing '${field}'\" when one is absent. ` +\n `Supply ${omitted}, or read the package's current window first ` +\n `(activePeriodFromPackages) and pass it as \\`current\\`.`,\n );\n }\n\n return {\n packageId,\n // Already LocalDateTime-shaped when it came from `current`; normalising\n // again is a no-op and keeps a hand-passed value honest.\n newActivationDateUtc: ocsLocalDateTime(activation as string, \"start\"),\n newExpirationDateUtc: ocsLocalDateTime(expiration as string, \"end\"),\n comment: change.comment?.trim() || OCS_ACTIVE_PERIOD_DEFAULT_COMMENT,\n };\n}\n\n\n/**\n * Resolve a subscriber package id to the recurring subscription that produced\n * it, for `stopResumeSubsRecurringPackage`.\n *\n * These are three DIFFERENT id spaces, which is why a rename was never the\n * fix. One live package on ICCID 8948010000020241105 (2026-08-28):\n *\n * subscriberprepaidpackageid 204532344 <- what list_subscriber_packages returns\n * recurringPackage 11683296 <- what OCS wants as `recurringId`\n * templateId 3848928 <- stops EVERY subscriber on the template\n *\n * Passing the package id as `recurringId` would address an unrelated recurring\n * subscription; passing it as `templateId` would stop the template fleet-wide.\n *\n * Returns undefined when the package is not on the subscriber or carries no\n * `recurringPackage` (a one-off package has none), so the caller can refuse\n * instead of guessing.\n */\nexport function recurringIdForPackage(\n listResponse: unknown,\n packageId: number,\n): number | undefined {\n const root = (listResponse ?? {}) as Record<string, unknown>;\n const payload = (root.listSubscriberPrepaidPackages ?? root.data ?? root) as\n | Record<string, unknown>\n | undefined;\n const packages = Array.isArray(payload?.packages) ? payload.packages : [];\n for (const entry of packages) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;\n if (Number(id) !== packageId) continue;\n const recurring = rec.recurringPackage ?? rec.recurringId;\n const value = Number(recurring);\n return Number.isFinite(value) && value > 0 ? value : undefined;\n }\n return undefined;\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\nconst NETWORK_EVENT_GROUPS = [\"mapAnswer\", \"s6aAnswer\", \"gyAnswer\", \"camelAnswer\", \"mocAnswer\", \"mtcAnswer\"] as const;\n\nfunction hasDirectNetworkEvents(rec: Record<string, unknown>): boolean {\n if (Array.isArray(rec.events)) return true;\n return NETWORK_EVENT_GROUPS.some((key) => {\n const group = rec[key];\n return Boolean(\n group && typeof group === \"object\" && Array.isArray((group as { events?: unknown[] }).events),\n );\n });\n}\n\n/**\n * The API returns `{ data: <OCS body> }`. The OCS body nests protocol groups\n * under `subscriberNetworkEventsOverPeriod`. Either wrapper, or both, has to\n * come off before the groups are readable.\n */\nfunction networkEventPayload(json: unknown): unknown {\n let current: unknown = json;\n for (let depth = 0; depth < 4; depth += 1) {\n if (!current || typeof current !== \"object\" || Array.isArray(current)) return current;\n const rec = current as Record<string, unknown>;\n if (hasDirectNetworkEvents(rec)) return current;\n const nested = rec.subscriberNetworkEventsOverPeriod;\n if (nested && typeof nested === \"object\") {\n current = nested;\n continue;\n }\n if (\"data\" in rec) {\n current = rec.data;\n continue;\n }\n return current;\n }\n return current;\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 out: unknown[] = [];\n for (const key of NETWORK_EVENT_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 rows = flattenNetworkEventGroups(networkEventPayload(json));\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 for (const row of extractEsimStatusByAccount(json)) {\n acc.active += row.active;\n acc.suspended += row.suspended;\n acc.inventory += row.inventory;\n acc.other += row.other;\n }\n acc.total = acc.active + acc.suspended + acc.inventory + acc.other;\n return acc;\n}\n\n/** eSIM status counts for one OCS account. `id`/`name` are absent when OCS omitted them. */\nexport interface AccountEsimStatusCounts extends EsimStatusCounts {\n id?: number;\n name?: string;\n}\n\nfunction accountRows(json: unknown): unknown[] {\n const root = (json ?? {}) as Record<string, unknown>;\n const payload = (root.data ?? root.esimStatusPerAccount ?? root) as Record<string, unknown> | unknown[];\n if (Array.isArray(payload)) return payload;\n return Array.isArray((payload as Record<string, unknown>).account)\n ? ((payload as Record<string, unknown>).account as unknown[])\n : [];\n}\n\n/**\n * The same unwrap as `extractEsimStatusCounts`, kept per account.\n *\n * The fleet panel used to show `inventory: 0` for every account because only\n * the fleet-wide total was extracted, so a per-account availability figure\n * could not match OCS (CAR-280). One walker feeds both views, so the total is\n * by construction the sum of the rows.\n */\nexport function extractEsimStatusByAccount(json: unknown): AccountEsimStatusCounts[] {\n const out: AccountEsimStatusCounts[] = [];\n for (const account of accountRows(json)) {\n const rec = (account ?? {}) as Record<string, unknown>;\n const row: AccountEsimStatusCounts = emptyEsimStatusCounts();\n const id = Number(rec.id ?? rec.accountId);\n if (rec.id !== undefined || rec.accountId !== undefined) {\n if (Number.isFinite(id)) row.id = id;\n }\n if (typeof rec.name === \"string\") row.name = rec.name;\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 status of statuses) addEsimStatusRow(row, (status ?? {}) as Record<string, unknown>);\n }\n } else {\n row.active += Number(rec.active ?? 0);\n row.suspended += Number(rec.suspended ?? 0);\n row.inventory += Number(rec.inventory ?? rec.notActivated ?? rec.free ?? 0);\n row.other += Number(rec.other ?? rec.terminated ?? 0);\n }\n row.total = row.active + row.suspended + row.inventory + row.other;\n out.push(row);\n }\n return out;\n}\n\n/** One bundle whose displayed availability disagrees with OCS. */\nexport interface InventoryDeviation {\n key: string;\n displayed: number;\n actual: number | null;\n}\n\n/**\n * Carrier-versus-OCS inventory diff (CAR-280 / C2).\n *\n * Pass/fail is \"zero deviation\": every key in `displayed` must equal the OCS\n * count. A key OCS does not report is a deviation (`actual: null`), never\n * silently skipped, because a shop showing stock OCS does not have is exactly\n * the failure the test exists to catch.\n */\nexport function diffInventory(\n displayed: Record<string, number>,\n actual: Record<string, number>,\n): { ok: boolean; sampled: number; deviations: InventoryDeviation[] } {\n const keys = Object.keys(displayed);\n const deviations: InventoryDeviation[] = [];\n for (const key of keys) {\n const shown = displayed[key]!;\n const real = Object.prototype.hasOwnProperty.call(actual, key) ? actual[key]! : null;\n if (real === null || real !== shown) deviations.push({ key, displayed: shown, actual: real });\n }\n return { ok: keys.length > 0 && deviations.length === 0, sampled: keys.length, deviations };\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 * `hlrSetBitrate` `limit` values. Bridge4IP documents these strings\n * (KB_32 … KB_102400, plus UNLIMITED), and the MCP tool already sends them.\n * A bare Kbps number is not on that model.\n *\n * Wider than {@link OCS_THROTTLE_KBPS}: package-template throttling omits\n * 32, 64, and 2048, and has no unlimited token.\n */\nexport const OCS_HLR_BITRATE_LIMITS = [\n \"KB_32\",\n \"KB_64\",\n \"KB_128\",\n \"KB_256\",\n \"KB_384\",\n \"KB_512\",\n \"KB_1024\",\n \"KB_2048\",\n \"KB_3072\",\n \"KB_5120\",\n \"KB_7680\",\n \"KB_10240\",\n \"KB_20480\",\n \"KB_51200\",\n \"KB_102400\",\n \"UNLIMITED\",\n] as const;\n\nconst OCS_HLR_BITRATE_SET = new Set<string>(OCS_HLR_BITRATE_LIMITS);\n\n/**\n * Map a console Kbps figure or an already-correct token onto `hlrSetBitrate`'s\n * `limit`. Anything else is refused so a free-typed number cannot reach OCS.\n */\nexport function ocsHlrBitrate(raw: unknown): string | { error: string } {\n const token = hlrBitrateToken(raw);\n if (token !== undefined && OCS_HLR_BITRATE_SET.has(token)) return token;\n return {\n error:\n \"limit must be an HLR bitrate in Kbps (32, 64, 128, 256, 384, 512, 1024, 2048, 3072, 5120, 7680, 10240, 20480, 51200, 102400) or UNLIMITED\",\n };\n}\n\n/**\n * MCP `hlr_set_bitrate` takes bits per second. OCS `limit` is the KB_ enum.\n * 256000 bps is KB_256. 0 is that tool's \"remove the cap\" and means UNLIMITED.\n * A figure that is not an exact enum step is refused.\n */\nexport function ocsHlrBitrateFromBps(raw: number): string | { error: string } {\n if (raw === 0) return \"UNLIMITED\";\n if (!Number.isInteger(raw) || raw < 0 || raw % 1000 !== 0) {\n return {\n error:\n \"bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED\",\n };\n }\n const mapped = ocsHlrBitrate(raw / 1000);\n if (typeof mapped === \"string\") return mapped;\n return {\n error:\n \"bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED\",\n };\n}\n\nfunction hlrBitrateToken(raw: unknown): string | undefined {\n if (typeof raw === \"number\" && Number.isInteger(raw) && raw > 0) return `KB_${raw}`;\n if (typeof raw !== \"string\") return undefined;\n const key = raw.trim().toUpperCase().replace(/\\s+/g, \"_\");\n if (key === \"UNLIMITED\") return \"UNLIMITED\";\n if (key.startsWith(\"KB_\")) return key;\n if (/^\\d+$/.test(key)) return `KB_${key}`;\n return undefined;\n}\n\n/**\n * `changeSimStatus` `newStatus` is Affected or Free.\n *\n * Console sends Enable|Disable. MCP sends ENABLED|DISABLED. Both mean the\n * same pair. DELETED is not on this method.\n */\nexport function ocsSimStatus(raw: string): \"Affected\" | \"Free\" | { error: string } {\n const key = raw.trim().toLowerCase();\n if (key === \"enable\" || key === \"enabled\" || key === \"affected\") return \"Affected\";\n if (key === \"disable\" || key === \"disabled\" || key === \"free\") return \"Free\";\n if (key === \"deleted\") {\n return {\n error: \"DELETED is not a changeSimStatus value. newStatus must be Enable or Disable\",\n };\n }\n return { error: \"newStatus must be Enable or Disable\" };\n}\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/**\n * OCS `moveSubscriberRangeToAccount` wire shape.\n *\n * Live OCS (probed 2026-08-24) accepts exactly five fields: destAccount,\n * srcAccountId, rangeType, rangeStart, rangeEnd. REST used to forward\n * toAccountId / iccidStart / iccidEnd, which OCS rejects with code 2\n * (\"Unrecognized field toAccountId\") — eSIMMY hit that 502 on\n * POST /v1/accounts/{accountId}/move-subscribers. MCP was rebuilt onto\n * this shape on 2026-08-28; REST and Console aliases map through here\n * so a later surface cannot invent a sixth name.\n */\nexport type MoveSubscriberRangeToAccountParams = {\n srcAccountId: number;\n destAccount: number;\n rangeType: \"IMSI\" | \"ICCID\";\n rangeStart: string;\n rangeEnd: string;\n};\n\nconst RANGE_TYPES = new Set([\"IMSI\", \"ICCID\"]);\n\nfunction positiveAccountId(value: unknown): number | undefined {\n if (typeof value === \"number\") {\n return Number.isInteger(value) && value > 0 ? value : undefined;\n }\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n return Number.isInteger(n) && n > 0 ? n : undefined;\n }\n return undefined;\n}\n\nfunction rangeToken(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const t = value.trim();\n return t.length > 0 ? t : undefined;\n}\n\n/**\n * Map any published alias onto the five OCS fields.\n *\n * REST historically took `{ fromAccountId, iccidStart, iccidEnd }` with the\n * destination in the path. MCP takes `{ srcAccountId, destAccount, rangeType,\n * rangeStart, rangeEnd }`. eSIMMY sent destAccount in the body and the source\n * in the path. All three must produce the same OCS body, and none of\n * toAccountId / iccidStart / iccidEnd / fromAccountId may leave this function.\n */\nexport function moveSubscriberRangeToAccountParams(input: {\n srcAccountId?: unknown;\n destAccount?: unknown;\n fromAccountId?: unknown;\n toAccountId?: unknown;\n rangeType?: unknown;\n rangeStart?: unknown;\n rangeEnd?: unknown;\n iccidStart?: unknown;\n iccidEnd?: unknown;\n pathAccountId?: unknown;\n}): MoveSubscriberRangeToAccountParams | { error: string } {\n const destFromBody = positiveAccountId(input.destAccount ?? input.toAccountId);\n const srcFromBody = positiveAccountId(input.srcAccountId ?? input.fromAccountId);\n const pathId = positiveAccountId(input.pathAccountId);\n\n const destAccount = destFromBody ?? pathId;\n const srcAccountId =\n srcFromBody ?? (destFromBody !== undefined && pathId !== undefined && pathId !== destFromBody ? pathId : undefined);\n\n if (srcAccountId === undefined || destAccount === undefined) {\n return { error: \"srcAccountId and destAccount required\" };\n }\n if (srcAccountId === destAccount) {\n return { error: \"srcAccountId and destAccount must differ\" };\n }\n\n const rangeStart = rangeToken(input.rangeStart ?? input.iccidStart);\n const rangeEnd = rangeToken(input.rangeEnd ?? input.iccidEnd);\n if (!rangeStart || !rangeEnd) {\n return { error: \"rangeStart and rangeEnd required\" };\n }\n if (rangeStart > rangeEnd) {\n return { error: \"rangeStart must not be higher than rangeEnd\" };\n }\n\n const rawType =\n typeof input.rangeType === \"string\" && input.rangeType.trim() !== \"\"\n ? input.rangeType.trim().toUpperCase()\n : input.iccidStart !== undefined || input.iccidEnd !== undefined\n ? \"ICCID\"\n : undefined;\n if (!rawType || !RANGE_TYPES.has(rawType)) {\n return { error: \"rangeType must be IMSI or ICCID\" };\n }\n\n return {\n srcAccountId,\n destAccount,\n rangeType: rawType as \"IMSI\" | \"ICCID\",\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * `createLocationZone` accepts locationZoneName, tadigList, and networkProfileId.\n *\n * `name` and `tadigs` are aliases. ISO countries and MCC-MNC operators are not\n * TADIG codes, and this function does not invent a TADIG from either of them.\n * An explicit canonical key wins over its alias.\n */\nexport type LocationZoneCreateParams = {\n locationZoneName: string;\n tadigList: string[];\n networkProfileId: number;\n};\n\nconst LOCATION_ZONE_KEYS = new Set([\"locationZoneName\", \"name\", \"tadigList\", \"tadigs\", \"networkProfileId\"]);\n\nfunction zoneRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction zoneName(raw: unknown): string | { error: string } {\n if (raw === undefined || raw === null || raw === \"\") return { error: \"locationZoneName is required\" };\n if (typeof raw !== \"string\") return { error: \"locationZoneName must be a string\" };\n const name = raw.trim();\n if (!name) return { error: \"locationZoneName is required\" };\n return name;\n}\n\nfunction zoneTadigs(raw: unknown): string[] | { error: string } {\n if (raw === undefined || raw === null || raw === \"\") return { error: \"tadigList is required\" };\n const parts = typeof raw === \"string\" ? raw.split(/[\\s,]+/) : Array.isArray(raw) ? raw : null;\n if (!parts) return { error: \"tadigList must be a list of TADIG codes\" };\n const list: string[] = [];\n for (const part of parts) {\n if (typeof part !== \"string\") return { error: \"tadigList must be a list of TADIG codes\" };\n const code = part.trim();\n if (code) list.push(code);\n }\n if (list.length === 0) return { error: \"tadigList is required\" };\n return list;\n}\n\nfunction zoneProfile(raw: unknown): number | undefined {\n if (typeof raw === \"number\" && Number.isInteger(raw) && raw > 0) return raw;\n if (typeof raw === \"string\" && /^\\d+$/.test(raw.trim())) {\n const n = Number(raw.trim());\n return n > 0 ? n : undefined;\n }\n return undefined;\n}\n\nexport function locationZoneCreateParams(\n input: Record<string, unknown> | string,\n): LocationZoneCreateParams | { error: string } {\n const parsed = zoneRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"countries\" in body || \"operators\" in body) {\n return {\n error:\n \"createLocationZone has no countries or operators field. Send tadigList (TADIG codes) and networkProfileId.\",\n };\n }\n for (const key of Object.keys(body)) {\n if (!LOCATION_ZONE_KEYS.has(key)) {\n return { error: \"createLocationZone accepts locationZoneName, tadigList, and networkProfileId\" };\n }\n }\n const name = zoneName(\"locationZoneName\" in body ? body.locationZoneName : body.name);\n if (typeof name !== \"string\") return name;\n const tadigList = zoneTadigs(\"tadigList\" in body ? body.tadigList : body.tadigs);\n if (!Array.isArray(tadigList)) return tadigList;\n const networkProfileId = zoneProfile(body.networkProfileId);\n if (networkProfileId === undefined) {\n return { error: \"networkProfileId must be a positive integer\" };\n }\n return { locationZoneName: name, tadigList, networkProfileId };\n}\n\n/**\n * `createPrepaidPackageTemplate` property names from the live probe.\n * Aliases are mapped onto these. `price` and `description` are not in the set.\n */\nexport const PACKAGE_TEMPLATE_CREATE_FIELDS = [\n \"cost\",\n \"databyte\",\n \"deleted\",\n \"destinationzoneid\",\n \"esimSponsor\",\n \"hotspotBlocked\",\n \"locationzoneid\",\n \"mocsecond\",\n \"mosmsnumber\",\n \"mtcsecond\",\n \"mtsmsnumber\",\n \"nbOccurrence\",\n \"pckSubPrePaidPackagesDataSlicesTmpl\",\n \"perioddays\",\n \"picture\",\n \"prepaidpackagetemplateid\",\n \"prepaidpackagetemplatename\",\n \"priority\",\n \"rdbDestinationZones\",\n \"rdbLocationZones\",\n \"recurring\",\n \"recurringPeriodicityFrequency\",\n \"recurringPeriodicityType\",\n \"reportUnitsPreviousPackage\",\n \"reseller\",\n \"resellerid\",\n \"sponsors\",\n \"throttleTillEnd\",\n \"throttlingActive\",\n \"throttlingErrorAction\",\n \"throttlingThreshold1Limit\",\n \"throttlingThreshold1Perc\",\n \"throttlingThreshold2Limit\",\n \"throttlingThreshold2Perc\",\n \"throttlingThreshold3Limit\",\n \"throttlingThreshold3Perc\",\n \"throttlingThreshold4Limit\",\n \"throttlingThreshold4Perc\",\n \"uiEndAvailibilityPeriod\",\n \"uiStartAvailablePeriod\",\n \"uiVisible\",\n \"userUiName\",\n] as const;\n\nconst PACKAGE_TEMPLATE_CREATE_FIELD_SET = new Set<string>(PACKAGE_TEMPLATE_CREATE_FIELDS);\n\n/** Caller spellings that OCS rejects until they are renamed. Canonical key wins. */\nexport const PACKAGE_TEMPLATE_CREATE_ALIASES: Record<string, string> = {\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n dataLimit: \"databyte\",\n dataBytes: \"databyte\",\n validityDays: \"perioddays\",\n periodDays: \"perioddays\",\n locationZoneId: \"locationzoneid\",\n destinationZoneId: \"destinationzoneid\",\n resellerId: \"resellerid\",\n};\n\nfunction templateRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction templateName(raw: unknown): string | { error: string } {\n if (typeof raw !== \"string\") return { error: \"prepaidpackagetemplatename must be a string\" };\n const name = raw.trim();\n if (!name) return { error: \"prepaidpackagetemplatename is required\" };\n return name;\n}\n\n/**\n * Map a create-template body onto the fields `createPrepaidPackageTemplate` accepts.\n *\n * Does not invent an allowance. A missing data field stays missing; the MCP\n * tool runs {@link normalizePackageTemplate} first when it wants the unlimited\n * ceiling. `price` and `description` are refused. An explicit OCS key wins\n * over its alias.\n */\nexport function packageTemplateCreateParams(\n input: Record<string, unknown> | string,\n): Record<string, unknown> | { error: string } {\n const parsed = templateRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"price\" in body) return { error: \"createPrepaidPackageTemplate has no price field\" };\n if (\"description\" in body) return { error: \"createPrepaidPackageTemplate has no description field\" };\n\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) out[key] = value;\n }\n for (const [key, value] of Object.entries(body)) {\n const target = PACKAGE_TEMPLATE_CREATE_ALIASES[key];\n if (!target) {\n if (!PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) {\n return { error: `createPrepaidPackageTemplate does not accept ${key}` };\n }\n continue;\n }\n if (target in out) continue;\n if (target === \"prepaidpackagetemplatename\") {\n const name = templateName(value);\n if (typeof name !== \"string\") return name;\n out[target] = name;\n continue;\n }\n out[target] = value;\n }\n if (\"prepaidpackagetemplatename\" in out) {\n const name = templateName(out[\"prepaidpackagetemplatename\"]);\n if (typeof name !== \"string\") return name;\n out[\"prepaidpackagetemplatename\"] = name;\n }\n return out;\n}\n\n/**\n * OCS createPrepaidPackageTemplate fails with code 2 \"Missing 'resellerid'\"\n * when the body has no reseller. The caller rarely knows it, so fill it from\n * the token owner's own reseller. An explicit `resellerid` always wins.\n */\nexport async function withTemplateResellerId(\n params: Record<string, unknown>,\n resolveId: () => Promise<number>,\n): Promise<Record<string, unknown>> {\n const existing = params[\"resellerid\"];\n if (typeof existing === \"number\" && Number.isFinite(existing)) return params;\n return { ...params, resellerid: await resolveId() };\n}\n\n/**\n * MCP `create_package_template` entry. Normalizes the sold-plan shortcuts\n * (`unlimited`, `throttleAfterMb`, `throttleKbps`) first, then keeps only the\n * fields `createPrepaidPackageTemplate` accepts. The REST route must call\n * {@link packageTemplateCreateParams} directly so a name-only body does not\n * grow an unlimited allowance.\n */\nexport function packageTemplateCreateFromToolJson(\n template: string,\n): Record<string, unknown> | { error: string } {\n let parsed: unknown;\n try {\n parsed = JSON.parse(template);\n } catch {\n return { error: \"template must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { error: \"template must be a JSON object\" };\n }\n let normalized: Record<string, unknown>;\n try {\n normalized = normalizePackageTemplate(parsed as Record<string, unknown>);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"template could not be normalized\";\n return { error: message };\n }\n return packageTemplateCreateParams(normalized);\n}\n\n/**\n * `modifyPPTCore` accepts every create field plus `changeSponsor`.\n * `modifyPPTRecurring` and `modifyPPTThrottling` accept the create set only.\n */\nexport type PackageTemplateModifyMethod =\n | \"modifyPPTCore\"\n | \"modifyPPTRecurring\"\n | \"modifyPPTThrottling\";\n\nconst PACKAGE_TEMPLATE_MODIFY_EXTRA: Record<PackageTemplateModifyMethod, readonly string[]> = {\n modifyPPTCore: [\"changeSponsor\"],\n modifyPPTRecurring: [],\n modifyPPTThrottling: [],\n};\n\n/** Path identity. The handler sets `templateId` after this function returns. */\nconst PACKAGE_TEMPLATE_MODIFY_IDENTITY = new Set([\"templateId\", \"prepaidpackagetemplateid\"]);\n\nexport const PACKAGE_TEMPLATE_MODIFY_ALIASES: Record<string, string> = {\n ...PACKAGE_TEMPLATE_CREATE_ALIASES,\n periodicity: \"recurringPeriodicityType\",\n occurrences: \"nbOccurrence\",\n firstThresholdPercent: \"throttlingThreshold1Perc\",\n firstThresholdLimitKbps: \"throttlingThreshold1Limit\",\n errorAction: \"throttlingErrorAction\",\n};\n\n/** Accepted OCS names for one modifyPPT* method, before aliases. */\nexport function packageTemplateModifyFieldNames(\n method: PackageTemplateModifyMethod,\n): readonly string[] {\n return [...PACKAGE_TEMPLATE_CREATE_FIELDS, ...PACKAGE_TEMPLATE_MODIFY_EXTRA[method]];\n}\n\nfunction changesRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction periodicityValue(raw: unknown): number | { error: string } {\n if (typeof raw === \"number\" && OCS_PERIODICITY_VALUES.includes(raw as never)) return raw;\n if (typeof raw === \"string\") {\n const key = raw.trim().toLowerCase();\n if (key === \"daily\" || key === \"0\") return OCS_PERIODICITY.DAILY;\n if (key === \"weekly\" || key === \"1\") return OCS_PERIODICITY.WEEKLY;\n if (key === \"monthly\" || key === \"2\") return OCS_PERIODICITY.MONTHLY;\n }\n return { error: \"recurringPeriodicityType must be 0 (daily), 1 (weekly), or 2 (monthly)\" };\n}\n\nfunction throttlingErrorActionValue(raw: unknown): number | { error: string } {\n if (typeof raw === \"number\" && Number.isInteger(raw)) return raw;\n if (typeof raw === \"string\" && /^-?\\d+$/.test(raw.trim())) return Number(raw.trim());\n if (typeof raw === \"string\" && raw.trim().toLowerCase() === \"continue_unthrottled\") {\n return {\n error: \"throttlingErrorAction is an integer. continue_unthrottled is not a modifyPPT value\",\n };\n }\n return { error: \"throttlingErrorAction is an integer\" };\n}\n\nfunction applyTemplateField(\n out: Record<string, unknown>,\n target: string,\n value: unknown,\n): { error: string } | undefined {\n if (target === \"prepaidpackagetemplatename\") {\n const name = templateName(value);\n if (typeof name !== \"string\") return name;\n out[target] = name;\n return undefined;\n }\n if (target === \"recurringPeriodicityType\") {\n const period = periodicityValue(value);\n if (typeof period !== \"number\") return period;\n out[target] = period;\n return undefined;\n }\n if (target === \"throttlingErrorAction\") {\n const action = throttlingErrorActionValue(value);\n if (typeof action !== \"number\") return action;\n out[target] = action;\n return undefined;\n }\n out[target] = value;\n return undefined;\n}\n\n/**\n * Map a partial template update onto the fields the modifyPPT* method accepts.\n *\n * Does not invent an allowance. `templateId` and `prepaidpackagetemplateid`\n * are dropped so the caller can set the path id last. `price` and\n * `description` are refused. `periodicity` daily/weekly/monthly becomes\n * `recurringPeriodicityType` 0/1/2. `continue_unthrottled` is refused:\n * `throttlingErrorAction` is an integer. An explicit OCS key wins over its alias.\n */\nexport function packageTemplateModifyParams(\n input: Record<string, unknown> | string,\n method: PackageTemplateModifyMethod,\n): Record<string, unknown> | { error: string } {\n const parsed = changesRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"price\" in body) return { error: `${method} has no price field` };\n if (\"description\" in body) return { error: `${method} has no description field` };\n\n const accepted = new Set<string>([\n ...PACKAGE_TEMPLATE_CREATE_FIELDS,\n ...PACKAGE_TEMPLATE_MODIFY_EXTRA[method],\n ]);\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key) || !accepted.has(key)) continue;\n const applied = applyTemplateField(out, key, value);\n if (applied) return applied;\n }\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key)) continue;\n const target = PACKAGE_TEMPLATE_MODIFY_ALIASES[key];\n if (!target) {\n if (!accepted.has(key)) return { error: `${method} does not accept ${key}` };\n continue;\n }\n if (target in out) continue;\n const applied = applyTemplateField(out, target, value);\n if (applied) return applied;\n }\n if (Object.keys(out).length === 0) {\n return { error: \"at least one template field required\" };\n }\n return out;\n}\n\n/**\n * MCP modify-template entry. Core runs {@link normalizePackageTemplateChanges}\n * first so a zero allowance the caller sent becomes the unlimited ceiling, and\n * a rename stays a rename. Recurring and throttling do not.\n */\nexport function packageTemplateModifyFromToolJson(\n changes: string,\n method: PackageTemplateModifyMethod,\n): Record<string, unknown> | { error: string } {\n let parsed: unknown;\n try {\n parsed = JSON.parse(changes);\n } catch {\n return { error: \"changes must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { error: \"changes must be a JSON object\" };\n }\n let body = parsed as Record<string, unknown>;\n if (method === \"modifyPPTCore\") {\n try {\n body = normalizePackageTemplateChanges(body);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"changes could not be normalized\";\n return { error: message };\n }\n }\n return packageTemplateModifyParams(body, method);\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 * @carrier/ocs-client — Zod schemas for REST API request/response validation.\n *\n * Used by apps/api route definitions. Shared here so mcp-server can\n * reference the same shapes if needed.\n */\n\nimport { z } from \"zod\";\nimport { OCS_HLR_BITRATE_LIMITS } from \"./ocs-params.js\";\n\n// ---------------------------------------------------------------------------\n// OCS provider contract — known-valid v1 methods + response shape schemas.\n//\n// Ground truth captured via a live-stg smoke against eSIMVault OCS. These pin\n// the request/response contract so shape drift (the createAccount /\n// getResellerInfo / nested listResellerAccount bugs the smoke found) fails\n// loudly at the client boundary instead of being silently mis-read.\n//\n// Response schemas are deliberately LENIENT: objects `.passthrough()` so the\n// provider can add fields without breaking us; we only assert the KEY fields\n// and their exact names (the drift that actually bit us).\n// ---------------------------------------------------------------------------\n\n/**\n * The valid OCS v1 methods, verified against live-stg — the enumerated set\n * below is authoritative (\"the ONLY ones\"). This is the ONLY set the server\n * recognizes — notably there is NO `createAccount` or\n * `createSubscriber` (smoke returned OCS error 2 \"Unrecognized field\"). The\n * client guards against unknown methods using this set so phantom methods are\n * caught at dev time rather than silently round-tripping to a 200+error.\n */\nexport const OCS_V1_METHODS: ReadonlySet<string> = new Set([\n \"getResellerInfo\",\n \"listResellerAccount\",\n \"listPrepaidPackageTemplate\",\n \"listSubscriberPrepaidPackages\",\n \"listSubscriber\",\n \"getSingleSubscriber\",\n \"getSimProviderStatus\",\n \"getSubscriberLocation\",\n \"getSubscriberLocationByCellId\",\n \"esimStatusPerAccount\",\n \"subscriberUsageOverPeriod\",\n \"affectRecurringPackageToSubscriber\",\n \"deleteSubscriberPackage\",\n \"modifyAccountBalance\",\n \"modifySubscriberBalance\",\n \"modifySubscriberContactInfo\",\n \"modifySubscriberMobilePlan\",\n \"modifySubscriberSteeringList\",\n \"modifySubscriberVoipPlan\",\n \"modifySubscriberPrepaidPackageActivePeriod\",\n \"modifySubscriberPrepaidPackageLimits\",\n \"modifySubscriberPrepaidPackageStatus\",\n \"modifyPPTCore\",\n \"modifyPPTRecurring\",\n \"createPrepaidPackageTemplate\",\n \"createLocationZone\",\n \"changeNetworkProfileOfLocationZone\",\n \"hlrGetBitrate\",\n \"hlrSetBitrate\",\n \"listSponsor\",\n \"listDestinationListPrefix\",\n \"listDetailedLocationZone\",\n \"listLocationZoneElement\",\n \"listSubscriberVoipTariff\",\n \"listVoipTariffRule\",\n \"affectSubscriberFakePhoneNumber\",\n \"affectSubscriberRealPhoneNumber\",\n \"getCustomerTariff\",\n \"resetSubsGzCounter\",\n \"sendMtSms\",\n]);\n\n/**\n * The proven-phantom methods: `createAccount` and `createSubscriber`. A live-stg\n * smoke showed the OCS has NO account/subscriber creation method — it answers\n * HTTP 200 with `status.code 2 \"Unrecognized field\"` for these names. They are\n * therefore rejected UNCONDITIONALLY by `OcsClient.call` (pre-fetch, regardless\n * of `strictMethods` / `validateResponses` / `strictValidation`), unlike every\n * other unknown method which keeps the warn-once pass-through default. Kept as a\n * separate set on purpose so `OCS_V1_METHODS` (the allowed, live-verified set)\n * is never expanded to accommodate a phantom name.\n */\nexport const OCS_FORBIDDEN_METHODS: ReadonlySet<string> = new Set([\n \"createAccount\",\n \"createSubscriber\",\n]);\n\n/**\n * OCS response envelope. Every response is `{ status: { code, msg }, [method]: <data> }`.\n * `status.code !== 0` signals an OCS-level error (HTTP is always 200). The\n * client checks status before this validation runs; this schema exists so\n * callers and tests can assert the envelope shape independently.\n */\nexport const OcsResponseEnvelope = z\n .object({\n status: z\n .object({\n code: z.number(),\n // Required to match the `OcsStatus` interface in client.ts ({ code, msg }).\n msg: z.string(),\n })\n .passthrough(),\n })\n .passthrough();\n\n// --- getResellerInfo --------------------------------------------------------\n// Live shape: { id, name, parentId, parentName, balance, cdrFolder?,\n// trafficInfo?, chargingInfo?, contactInfo? }. NOT reseller_id/reseller_name.\nexport const GetResellerInfoResponse = z\n .object({\n id: z.number(),\n name: z.string(),\n parentId: z.number(),\n parentName: z.string(),\n // Production eSIMVault returns decimal money as a JSON string (\"495.52\").\n // Staging fixtures used a number, so accept both exact provider variants.\n balance: z.union([z.number(), z.string().regex(/^-?\\d+(?:\\.\\d+)?$/)]),\n cdrFolder: z.string().optional(),\n trafficInfo: z.object({}).passthrough().optional(),\n chargingInfo: z.object({}).passthrough().optional(),\n contactInfo: z.object({}).passthrough().optional(),\n })\n .passthrough();\n\n// --- listResellerAccount ----------------------------------------------------\n// Live shape is NESTED: { reseller: [ { id, name, account: [ {…} ] } ] }.\nexport const ListResellerAccountResponse = z\n .object({\n reseller: z.array(\n z\n .object({\n id: z.number(),\n name: z.string(),\n account: z.array(z.object({}).passthrough()),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n// --- listPrepaidPackageTemplate ---------------------------------------------\n// Live shape: { template: [ { prepaidpackagetemplateid, prepaidpackagetemplatename,\n// resellerid, locationzoneid, … } ] }.\nexport const ListPrepaidPackageTemplateResponse = z\n .object({\n template: z.array(\n z\n .object({\n prepaidpackagetemplateid: z.number(),\n prepaidpackagetemplatename: z.string(),\n resellerid: z.number(),\n locationzoneid: z.number(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n// --- getSingleSubscriber ----------------------------------------------------\n// Live shape: `status` is an ARRAY; status[0].status is a named string\n// (\"Active\"/\"Inventory\"/\"Inactive\"/\"End of Life\"/possibly others). Do NOT\n// hardcode a closed enum — assert a non-empty, alpha-leading string.\nconst SubscriberStatusName = z\n .string()\n .regex(/^[A-Za-z]/, \"subscriber status must be a non-empty alpha-leading name\");\n\nexport const GetSingleSubscriberResponse = z\n .object({\n status: z\n .array(\n z\n .object({\n status: SubscriberStatusName,\n })\n .passthrough(),\n )\n .min(1, \"getSingleSubscriber.status must be a non-empty array\"),\n })\n .passthrough();\n\n/**\n * Method → response-shape schema. Keyed by OCS method name; only the read\n * methods we depend on are pinned. Methods absent from this map skip shape\n * validation (write/admin mutations return only the status envelope).\n */\nexport const RESPONSE_SCHEMAS: Record<string, z.ZodType> = {\n getResellerInfo: GetResellerInfoResponse,\n listResellerAccount: ListResellerAccountResponse,\n listPrepaidPackageTemplate: ListPrepaidPackageTemplateResponse,\n getSingleSubscriber: GetSingleSubscriberResponse,\n};\n\n/**\n * Schema-derived payload types for consumers that need response typing without\n * re-declaring the shapes. Pure type exports (no runtime surface), so importing\n * them does not require a zod dependency in the consuming package. Inferred from\n * the schemas above, so they can never drift from the contract they document.\n */\nexport type GetResellerInfoData = z.infer<typeof GetResellerInfoResponse>;\nexport type ListResellerAccountData = z.infer<typeof ListResellerAccountResponse>;\n\n// ---------------------------------------------------------------------------\n// Common\n// ---------------------------------------------------------------------------\n\nexport const IccidParam = z.object({ iccid: z.string().min(1) });\nexport const AccountIdParam = z.object({ accountId: z.coerce.number().int().positive() });\nexport const PackageIdParam = z.object({ packageId: z.coerce.number().int().positive() });\nexport const TemplateIdParam = z.object({ templateId: z.coerce.number().int().positive() });\nexport const ResellerIdParam = z.object({ resellerId: z.coerce.number().int().positive() });\n\nexport const DateRangeBody = z.object({\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"must be YYYY-MM-DD\"),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"must be YYYY-MM-DD\"),\n});\n\nexport const ErrorResponse = z.object({\n error: z.string(),\n code: z.number().optional(),\n retryAfter: z.number().optional(),\n});\n\nexport const OkResponse = z.object({\n ok: z.boolean(),\n data: z.unknown().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Subscriber\n// ---------------------------------------------------------------------------\n\nexport const ModifySubscriberStatusBody = z.object({\n newStatus: z.enum([\"active\", \"suspended\", \"deactivated\"]),\n});\n\nexport const ModifySubscriberBalanceBody = z.object({\n amount: z.number().optional(),\n setBalance: z.number().optional(),\n}).refine((v) => v.amount !== undefined || v.setBalance !== undefined, {\n message: \"Provide either amount (delta) or setBalance (absolute)\",\n});\n\nexport const ModifySubscriberContactInfoBody = z.object({\n name: z.string().optional(),\n mail: z.string().email().optional(),\n phone: z.string().optional(),\n});\n\nexport const SetTrafficRestrictionsBody = z.object({\n blockData: z.boolean().optional(),\n blockVoice: z.boolean().optional(),\n blockSms: z.boolean().optional(),\n blockRoaming: z.boolean().optional(),\n});\n\nexport const ModifySteeringListBody = z.object({\n steeringListId: z.number().int().positive(),\n});\n\nexport const HlrSetBitrateBody = z.object({\n limit: z.enum(OCS_HLR_BITRATE_LIMITS),\n});\n\n// ---------------------------------------------------------------------------\n// Packages\n// ---------------------------------------------------------------------------\n\nexport const AssignPackageBody = z.object({\n packageTemplateId: z.number().int().positive(),\n startDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n});\n\nexport const AssignRecurringPackageBody = z.object({\n packageTemplateId: z.number().int().positive(),\n});\n\nexport const ModifyPackageLimitsBody = z.object({\n dataLimitMb: z.number().positive().optional(),\n voiceLimitMin: z.number().nonnegative().optional(),\n smsLimit: z.number().nonnegative().optional(),\n});\n\nexport const ModifyPackageExpiryBody = z.object({\n expiryDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n});\n\nexport const ModifyPackageStatusBody = z.object({\n status: z.enum([\"active\", \"paused\"]),\n});\n\nexport const StopResumeRecurringBody = z.object({\n action: z.enum([\"stop\", \"resume\"]),\n});\n\nexport const CreatePackageTemplateBody = z.object({\n name: z.string().min(1),\n dataLimitMb: z.number().positive(),\n validityDays: z.number().int().positive(),\n price: z.number().nonnegative().optional(),\n recurring: z.boolean().optional(),\n});\n\nexport const ModifyTemplateCoreBody = z.object({\n name: z.string().optional(),\n dataLimitMb: z.number().positive().optional(),\n validityDays: z.number().int().positive().optional(),\n});\n\nexport const ModifyTemplateRecurringBody = z.object({\n billingCycleDays: z.number().int().positive().optional(),\n autoRenew: z.boolean().optional(),\n});\n\nexport const ModifyTemplateThrottlingBody = z.object({\n throttleSpeedKbps: z.number().int().nonnegative(),\n});\n\n// ---------------------------------------------------------------------------\n// Accounts\n// ---------------------------------------------------------------------------\n\nexport const ModifyAccountBalanceBody = z.object({\n amount: z.number(),\n // adapt and add are a signed delta. set replaces the balance. subtract\n // negates a positive amount. The API drops mode before the OCS call.\n mode: z.enum([\"adapt\", \"set\", \"add\", \"subtract\"]).default(\"adapt\"),\n});\n\nexport const MoveSubscribersBody = z.object({\n fromAccountId: z.number().int().positive(),\n iccidStart: z.string().min(1),\n iccidEnd: z.string().min(1),\n});\n\n// ---------------------------------------------------------------------------\n// Location zones\n// ---------------------------------------------------------------------------\n\nexport const CreateLocationZoneBody = z.object({\n name: z.string().min(1),\n countries: z.array(z.string().length(2)).min(1),\n});\n\n// ---------------------------------------------------------------------------\n// SMS\n// ---------------------------------------------------------------------------\n\nexport const SendSmsBody = z.object({\n text: z.string().min(1).max(160),\n senderId: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Change SIM status\n// ---------------------------------------------------------------------------\n\nexport const ChangeSimStatusBody = z.object({\n newStatus: z.enum([\"Enable\", \"Disable\"]),\n});\n\n// ---------------------------------------------------------------------------\n// Intelligence composites\n// ---------------------------------------------------------------------------\n\nexport const DiagnoseSubscriberBody = z.object({\n iccid: z.string().min(1),\n});\n\nexport const FleetHealthQuery = z.object({\n accountId: z.coerce.number().int().positive().optional(),\n});\n\nexport const ChurnRiskQuery = z.object({\n daysSinceUsage: z.coerce.number().int().positive().default(14),\n limit: z.coerce.number().int().positive().max(500).default(50),\n});\n\nexport const DetectUsageAnomaliesBody = z.object({\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n thresholdMultiplier: z.number().positive().default(2.5),\n});\n\nexport const OptimizePackageBody = z.object({\n iccid: z.string().min(1),\n});\n\nexport const AuditNetworkCoverageBody = z.object({\n locationZoneId: z.number().int().positive().optional(),\n});\n\nexport const MarketingIntelligenceQuery = z.object({\n segment: z.enum([\"low_usage\", \"high_usage\", \"expiring_soon\", \"churned\"]).optional(),\n});\n\nexport const HighCostSubscribersQuery = z.object({\n limit: z.coerce.number().int().positive().max(200).default(20),\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Webhooks\n// ---------------------------------------------------------------------------\n\nexport const WebhookEventBody = z.record(z.string(), z.unknown());\n\n// ---------------------------------------------------------------------------\n// Health\n// ---------------------------------------------------------------------------\n\nexport const HealthResponse = z.object({\n ok: z.literal(true),\n version: z.string(),\n build_sha: z.string(),\n});\n","/**\n * Per-method OCS request budgets — the single source of truth for every OCS\n * client copy in this repo (#663 follow-up).\n *\n * #663 gave every OCS call one flat 20 s deadline and made a breach\n * non-retriable. Both halves of that are wrong against the live upstream, and\n * the numbers say so. Measured against reseller 1170 (parent Bridge4IP 246) on\n * 2026-08-25, three consecutive byte-identical requests each:\n *\n * listSubscriber {accountId:5259} 12.6 s | 0.7 s | 13.6 s (202 KB)\n * listPrepaidPackageTemplate {} 17.1 s | 9.0 s | 0.7 s ( 64 KB)\n * getCustomerTariff 1170 88 s – 128 s ( 3.4 MB)\n *\n * Re-measured the same day at 20:43Z, 25 runs each, and the picture changed in\n * a way that matters:\n *\n * listSubscriber p50 0.5 s p95 0.7 s max 0.7 s (202 KB)\n * listPrepaidPackageTemplate p50 0.3 s p95 2.5 s max 2.8 s ( 64 KB)\n * getCustomerTariff p50 2.8 s p95 3.3 s max 3.3 s (3,439,729 B)\n *\n * Byte-identical response, 3,439,729 B both times, delivered in 2.8 s here and\n * in 88-128 s earlier. So the slowness is a property of the upstream's *state*,\n * not of the payload: OCS has fast windows and slow windows, and everything\n * inside a slow window is slow together. The size only sets how far a slow\n * window can stretch a given call, which is why the 3.4 MB method degrades to\n * two minutes while a 64 KB one degrades to seventeen seconds.\n *\n * That is a resilience problem end to end, so both fixes are budgets, not repairs.\n *\n * 1. `getCustomerTariff` needs a deadline above its worst window, and there is\n * no smaller request to ask for instead. OCS takes the method as a bare\n * integer (`assertBareIntParam`) and the REST route demonstrably ignores\n * `?country=` and `?trafficType=` — both return byte-identical bodies, which\n * is why `bounded-results.ts` filters in-process rather than pushing the\n * filter down. That projection (3.4 MB -> ~232 KB) already bounds what a\n * model is handed, and matters twice over given #810/#811 (Haiku miscounted\n * a 55-object dump) — but it runs *after* the body arrives, so it cannot\n * shorten the transfer by a millisecond. Hence 180 s, 1.4x the worst\n * observation. One attempt, not three: a slow window lasts far longer than a\n * backoff, so a retry lands in the same window and turns a three-minute\n * failure into a nine-minute one. In a fast window the budget never fires.\n *\n * 2. Everything else gets a per-attempt deadline above every observation (30 s,\n * 1.75x the slowest) and a retry when it is breached anyway. Here a retry is\n * worth it: these calls are short enough that a second attempt costs seconds\n * rather than minutes, and the spread within a window is wide enough (0.7 s\n * vs 13.6 s, byte-identical) that the next attempt often lands on the fast\n * side. That is what turns a 20-fold spread into a stable p95.\n *\n * `totalMs` is the contract the caller can rely on: whatever the retries do,\n * the call either answers or throws inside that budget. It is checked before\n * every attempt and before every backoff sleep, so the budget is a ceiling\n * rather than an aspiration.\n *\n * Env overrides (unchanged semantics for `OCS_TIMEOUT_MS`, which raises the\n * per-attempt deadline for every method):\n * OCS_TIMEOUT_MS per-attempt deadline, all methods\n * OCS_TOTAL_TIMEOUT_MS total budget, all methods\n */\n\nexport interface OcsBudget {\n /** Deadline for a single HTTP attempt, including the body read. */\n attemptMs: number;\n /** Hard cap on attempts, backoff sleeps included in the total budget. */\n maxAttempts: number;\n /** Wall-clock ceiling for the whole call. The caller-facing guarantee. */\n totalMs: number;\n}\n\n/**\n * 30 s per attempt is 1.75x the slowest healthy observation (17.1 s), so the\n * jittery tail lands inside the first attempt. 75 s total leaves room for two\n * full attempts plus backoff, which is where the resilience actually comes\n * from.\n */\nexport const DEFAULT_OCS_BUDGET: OcsBudget = {\n attemptMs: 30_000,\n maxAttempts: 3,\n totalMs: 75_000,\n};\n\n/**\n * Methods whose measured behaviour does not fit the default. Keyed by the OCS\n * method name, not the MCP tool name, because the budget is a property of the\n * upstream call.\n */\nexport const OCS_METHOD_BUDGETS: Readonly<Record<string, OcsBudget>> = {\n // 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One\n // attempt: payload size does not vary, so a retry only doubles the wait.\n getCustomerTariff: { attemptMs: 180_000, maxAttempts: 1, totalMs: 180_000 },\n};\n\n/** Below this there is not enough budget left for an attempt to be worth starting. */\nexport const MIN_ATTEMPT_BUDGET_MS = 1_000;\n\nconst BACKOFF_BASE_MS = 500;\n\n/**\n * Raised when a call exhausts its budget.\n *\n * Deliberately NOT an OcsApiError: it carries no OCS status code, and the\n * OCS-error branch of a retry loop must never swallow it.\n */\nexport class OcsTimeoutError extends Error {\n constructor(\n public readonly method: string,\n /** The per-attempt deadline that was breached. */\n public readonly timeoutMs: number,\n /** The total budget for the call, when one applied. */\n public readonly budgetMs: number = timeoutMs,\n /** How many HTTP attempts were made before giving up. */\n public readonly attempts: number = 1,\n ) {\n super(\n `[${method}] OCS request timed out after ${timeoutMs}ms` +\n (attempts > 1 ? ` on each of ${attempts} attempts` : \"\") +\n (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : \"\") +\n `. The upstream did not deliver a complete response in time — usually an ` +\n `oversized payload or a wedged upstream. Narrow the request, or raise ` +\n `OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`,\n );\n this.name = \"OcsTimeoutError\";\n }\n}\n\nfunction envInt(name: string): number | undefined {\n const proc = (globalThis as Record<string, unknown>)[\"process\"] as\n | { env?: Record<string, string | undefined> }\n | undefined;\n const parsed = Number(proc?.env?.[name]);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/**\n * Resolve the budget for a method: table entry or default, then env overrides.\n *\n * Read on every call rather than cached at module load, because\n * `tests/integration/setup-live-ocs.ts` and individual tests set\n * `OCS_TIMEOUT_MS` at runtime and expect it to take effect.\n */\nexport function ocsBudgetFor(method: string): OcsBudget {\n const base = OCS_METHOD_BUDGETS[method] ?? DEFAULT_OCS_BUDGET;\n const attemptMs = envInt(\"OCS_TIMEOUT_MS\") ?? base.attemptMs;\n // A total budget below the per-attempt deadline would cut the first attempt\n // short, which is never what an override meant.\n const totalMs = Math.max(envInt(\"OCS_TOTAL_TIMEOUT_MS\") ?? base.totalMs, attemptMs);\n return { attemptMs, maxAttempts: base.maxAttempts, totalMs };\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n const timer = setTimeout(finish, ms);\n function finish(): void {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", finish);\n resolve();\n }\n signal.addEventListener(\"abort\", finish, { once: true });\n });\n}\n\n/**\n * Race `work` against a wall-clock deadline.\n *\n * A plain `setTimeout` on purpose: `AbortSignal.timeout()` is native and is not\n * patched by vitest's fake timers, so a deadline built on it cannot be tested\n * without burning real seconds. The signal is still handed to `fetch` so the\n * socket is torn down rather than leaked — but this race, not the signal, is\n * the guarantee, because a signal does not bound the body read at all.\n */\nasync function withDeadline<T>(work: Promise<T>, deadline: OcsTimeoutError, ms: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n work,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(deadline), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\n/**\n * Run one OCS call under its budget, retrying only on a deadline breach.\n *\n * `attempt` receives an `AbortSignal` that fires when its own per-attempt\n * deadline expires or the total budget runs out; pass it to `fetch`. Anything\n * `attempt` throws that is not an `OcsTimeoutError` propagates untouched, so\n * OCS status-code handling stays where it already lives.\n *\n * Modelled on `sendWithRetry` in packages/carrier-ai/src/bedrock.ts (#807):\n * full jitter, sleeps bound to the same signal as the work, and every attempt\n * and sleep checked against the deadline *before* it starts — so a retry can\n * never run past the budget the caller was promised.\n */\nexport async function runWithBudget<T>(\n method: string,\n attempt: (signal: AbortSignal, attemptMs: number) => Promise<T>,\n budget: OcsBudget = ocsBudgetFor(method),\n): Promise<T> {\n const deadlineAt = Date.now() + budget.totalMs;\n const budgetController = new AbortController();\n const budgetTimer = setTimeout(() => budgetController.abort(), budget.totalMs);\n\n let attempts = 0;\n let lastTimeout: OcsTimeoutError | undefined;\n\n try {\n for (let i = 0; i < budget.maxAttempts; i++) {\n const remaining = deadlineAt - Date.now();\n if (remaining < MIN_ATTEMPT_BUDGET_MS) break;\n\n // Never let an attempt outlive the total budget.\n const attemptMs = Math.min(budget.attemptMs, remaining);\n attempts++;\n\n const attemptController = new AbortController();\n const onBudgetAbort = () => attemptController.abort();\n budgetController.signal.addEventListener(\"abort\", onBudgetAbort, { once: true });\n const attemptTimer = setTimeout(() => attemptController.abort(), attemptMs);\n const breach = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);\n\n try {\n return await withDeadline(\n attempt(attemptController.signal, attemptMs),\n breach,\n attemptMs,\n );\n } catch (err) {\n if (!(err instanceof OcsTimeoutError)) throw err;\n lastTimeout = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);\n } finally {\n clearTimeout(attemptTimer);\n budgetController.signal.removeEventListener(\"abort\", onBudgetAbort);\n // The response body is fully read by the time an attempt resolves, so\n // aborting here only tears down a socket nobody is still reading.\n attemptController.abort();\n }\n\n if (i === budget.maxAttempts - 1) break;\n const backoff = Math.random() * BACKOFF_BASE_MS * 2 ** i;\n if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;\n await sleep(backoff, budgetController.signal);\n }\n } finally {\n clearTimeout(budgetTimer);\n }\n\n throw (\n lastTimeout ??\n new OcsTimeoutError(method, budget.attemptMs, budget.totalMs, Math.max(attempts, 1))\n );\n}\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 // stderr is observable in Workers and safe for MCP stdio. stdout is the\n // JSON-RPC protocol in the npm server: one metric line there corrupts the\n // stream and makes clients report \"Failed to parse JSONRPC message\".\n console.error(\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\n/**\n * Per-method FIELD-NAME translation, MCP/public name → OCS wire name.\n *\n * A sibling of the shape bug above, with the same failure mode: OCS rejects the\n * whole request rather than ignoring a field it does not know, so one wrong key\n * costs the entire call.\n *\n * getResellerInfo({ resellerId: 1170 })\n * → code 2, Unrecognized field \"resellerId\" ... (one known property: \"id\")\n *\n * WHY TRANSLATE RATHER THAN RENAME THE TOOL PARAMETER\n * `resellerId` is the honest domain name and is the published contract of\n * `@carrierllc/mcp` (npm, 0.9.2). OCS's `id` is a terse backend field whose\n * meaning is only clear from the method it sits on. Renaming the tool\n * parameter would break every existing caller to make the public surface\n * worse, so the mapping lives here instead — at the one place every surface\n * serialises through, which is also what makes it cover call sites nobody has\n * written yet.\n *\n * SCOPE\n * Keyed by method, never global: `id` means something different on every OCS\n * method, so a repo-wide `resellerId → id` rewrite would corrupt the calls it\n * did not intend to fix.\n *\n * VERIFIED against live OCS (ocs.esimvault.cloud), 2026-08-24:\n * getResellerInfo {resellerId:1170} → code 2, unrecognized field\n * getResellerInfo {id:1170} → code 0, Lifecycle Innovations, parent Bridge4IP (246)\n *\n * modifyPPTCore / modifyPPTRecurring / modifyPPTThrottling — OCS names the\n * template id `prepaidpackagetemplateid` on all three modifyPPT* methods (see\n * packages/ocs-spec/ocs-accepted-params.json); the tools send `templateId`.\n * packageTemplateModifyParams emits the other OCS names and strips a body\n * prepaidpackagetemplateid so this rename of templateId is the one that lands.\n * Core still renames a leftover name, volume, or validityDays. An explicit\n * target wins.\n */\nexport const OCS_PARAM_RENAMES: Record<string, Record<string, string>> = {\n getResellerInfo: { resellerId: \"id\" },\n modifyPPTCore: {\n templateId: \"prepaidpackagetemplateid\",\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n validityDays: \"perioddays\",\n },\n modifyPPTRecurring: { templateId: \"prepaidpackagetemplateid\" },\n modifyPPTThrottling: { templateId: \"prepaidpackagetemplateid\" },\n // Console NL-create and the MCP tool both advertise `name` / `volume` /\n // `validityDays`. OCS createPrepaidPackageTemplate rejects unknown keys\n // (Jackson code 2) — live failure was Unrecognized field \"name\".\n createPrepaidPackageTemplate: {\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n validityDays: \"perioddays\",\n dataLimit: \"databyte\",\n },\n createLocationZone: { name: \"locationZoneName\" },\n};\n\n/**\n * Applies the registry to one request's params.\n *\n * Returns the input untouched when there is nothing to rename, so the common\n * path allocates nothing. An explicitly-supplied target key always wins: a\n * caller that already speaks OCS (tools-backlog.ts sends `id` directly) must\n * not have its correct value clobbered by a translated one.\n */\nexport function applyParamRenames<T>(method: string, params: T): T {\n const renames = OCS_PARAM_RENAMES[method];\n if (!renames || !params || typeof params !== \"object\" || Array.isArray(params)) {\n return params;\n }\n const record = params as Record<string, unknown>;\n let out: Record<string, unknown> | undefined;\n for (const [from, to] of Object.entries(renames)) {\n if (!Object.prototype.hasOwnProperty.call(record, from)) continue;\n out ??= { ...record };\n if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];\n delete out[from];\n }\n return (out ?? params) as T;\n}\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/**\n * modifyAccountBalance — Sam's decision (2026-08-28) on the last of the\n * seven param-drift KNOWN_BROKEN entries left semantic after the six-tool\n * fix on 2026-08-27.\n *\n * WHY `mode` NEVER REACHES THE WIRE\n * The tool's public contract keeps `mode: 'adapt' | 'set'` — that part is\n * not the defect. What's wrong is sending `mode` itself to OCS: `mode` is\n * not a property this method knows about, so every call from the old\n * handler was rejected outright before it could move a cent.\n *\n * THE MAPPING\n * 'adapt' sends a signed delta under `amount` (negative to subtract),\n * never `setBalance`. 'set' sends the target value under `setBalance` and\n * omits `amount` entirely. Matches Bridge4IP's own documented contract\n * (docs/research/bridge4ip-ocs-api/endpoints-full.md §1.2) and the sibling\n * `modifySubscriberBalance` method exactly — neither needs a direction\n * classifier field.\n *\n * A `type: 'credit'|'debit'` FIELD WAS TRIED AND DROPPED\n * A param-enumeration probe (packages/ocs-spec/ocs-accepted-params.json,\n * 2026-08-24) listed `type` as a known property on the OCS model, so an\n * earlier version of this fix sent `type` alongside `amount`. That probe\n * technique surfaces every field the backend's Java model knows about,\n * not just the documented/supported API surface — it cannot distinguish a\n * real input from a vestigial or internal one. Live-tested 2026-08-28\n * directly against OCS (zero-amount, balance-0 account, so no state\n * change either way): `type` absent, `type: \"credit\"`, `type: \"debit\"`,\n * and `type: \"some_bogus_value\"` all produced an identical response. Not\n * required, value doesn't matter — dropped in favour of the documented,\n * sibling-consistent shape.\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\";\n\n// The per-second floor's buckets are module state. Tests that assert rate-floor\n// behaviour have to clear them between cases, and they now do that against THIS\n// client rather than an app-local copy of it — so the helper has to be reachable\n// from the package's public surface. `rate-floor.ts` itself stays unexported:\n// `acquireToken` is an internal chokepoint and nothing outside should call it.\nexport { _resetBucketsForTest } from \"./rate-floor.js\";\nimport { OCS_FORBIDDEN_METHODS, OCS_V1_METHODS, RESPONSE_SCHEMAS } from \"./schemas.js\";\nimport { assertBareIntParam, applyParamRenames } from \"./ocs-param-shapes.js\";\nimport { nestSubscriberIdParam, normaliseAbsentOcsPayload } from \"./ocs-params.js\";\nimport { OcsTimeoutError, ocsBudgetFor, runWithBudget } from \"./timeout-budgets.js\";\n\n// Package assignment/release can have succeeded before its response is lost.\n// Both inventory issuance, ICCID top-ups and rollback use these names. Never\n// replay them at either transport retry layer; callers retain the unknown\n// outcome for reconciliation rather than treating a timeout as non-acceptance.\nconst SINGLE_ATTEMPT_METHODS = new Set([\"affectPackageToSubscriber\", \"addSubscriberPackage\", \"deleteSubscriberPackage\"]);\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`).\n *\n * Two distinct policies:\n * - `createAccount` / `createSubscriber` (`OCS_FORBIDDEN_METHODS`) are proven\n * phantom methods — the OCS has no account/subscriber creation and answers\n * 200 + error 2 \"Unrecognized field\". They are ALWAYS rejected, pre-fetch and\n * regardless of client options.\n * - Any other unknown method throws only in strict mode (`{ strictMethods:\n * true }`); otherwise `call()` 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 (OCS_FORBIDDEN_METHODS.has(method)\n ? `${method} is a proven phantom method and is always rejected regardless of client options.`\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;\n// OCS code 100 is a provider traffic-control response, not an application error.\n// Its enforcement window can outlast the normal network-error retry cadence.\nconst TRAFFIC_CONTROL_BACKOFF_BASE_MS = 1_000;\nconst TRAFFIC_CONTROL_BACKOFF_MAX_MS = 16_000;\nconst TRAFFIC_CONTROL_MAX_RETRIES = 6;\nconst BACKOFF_JITTER = 0.25; // ±25%\n/**\n * Exported so callers (e.g. intelligence.ts) can wrap per-iteration OCS calls.\n * Network failures get 3 retries (500ms exponential, capped at 4s). OCS code 100\n * gets 6 retries (1s exponential, capped at 16s) because provider traffic-control\n * windows routinely outlast a short network retry burst.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: unknown;\n let maxRetries = BACKOFF_MAX_RETRIES;\n let backoffBaseMs = BACKOFF_BASE_MS;\n let backoffMaxMs = BACKOFF_MAX_MS;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const base = Math.min(backoffBaseMs * Math.pow(2, attempt - 1), backoffMaxMs);\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 if (err instanceof OcsApiError) {\n // Code 100 is a transient provider traffic-control response. It needs a\n // longer retry window than network failures; preserve all other OCS errors.\n if (err.code !== 100) throw err;\n maxRetries = TRAFFIC_CONTROL_MAX_RETRIES;\n backoffBaseMs = TRAFFIC_CONTROL_BACKOFF_BASE_MS;\n backoffMaxMs = TRAFFIC_CONTROL_BACKOFF_MAX_MS;\n }\n // Contract failures are deterministic — don't waste retries on them.\n if (err instanceof OcsContractError) {\n throw err;\n }\n // Timeout retries belong to runWithBudget, which is the only layer that\n // knows the total budget. Retrying here as well would multiply the\n // ceiling the caller was promised by four.\n if (err instanceof OcsTimeoutError) {\n throw err;\n }\n }\n }\n throw lastError;\n}\n\n/**\n * True when an error is an abort raised by our own deadline signal.\n *\n * PORTED FROM apps/mcp-server/src/client.ts ON 2026-08-28, AND THAT IS THE POINT.\n * The #663 request-budget work landed on the Worker's private copy of this\n * client and never reached this package or the stdio copy. The consequence was\n * not cosmetic: without this mapping an abort raised by our OWN deadline comes\n * out of `fetch` as a plain `Error`, which `retryWithBackoff` classifies as a\n * retriable network error and runs three more times — multiplying the total\n * wall-clock ceiling the caller was promised. `runWithBudget` is the only layer\n * that knows the total budget, so the timeout has to surface as the\n * non-retriable `OcsTimeoutError` for its accounting to hold.\n *\n * The Worker's own tests (apps/mcp-server/tests/ocs-budgets.test.ts) caught\n * this the moment the Worker was pointed at the shared client — which is the\n * whole argument for having one implementation: the gap existed for as long as\n * the copies did, and closing the fork is what surfaced it.\n */\nfunction isAbortError(err: unknown): boolean {\n return (\n err instanceof Error &&\n (err.name === \"AbortError\" ||\n err.name === \"TimeoutError\" ||\n /aborted|timed? ?out/i.test(err.message))\n );\n}\n\n// ---------------------------------------------------------------------------\n// OCS Client\n// ---------------------------------------------------------------------------\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 * This flag has no effect on `OCS_FORBIDDEN_METHODS` (createAccount /\n * createSubscriber): those proven phantom methods are rejected\n * unconditionally, with or without it.\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 // Hard gate for the proven phantom methods (P5): createAccount and\n // createSubscriber do not exist on the OCS (live smoke: 200 + error 2\n // \"Unrecognized field\"). They are rejected UNCONDITIONALLY — pre-fetch and\n // regardless of strictMethods / validateResponses / strictValidation — so\n // they can never be silently reintroduced behind a permissive client.\n if (OCS_FORBIDDEN_METHODS.has(method)) {\n throw new OcsUnknownMethodError(method);\n }\n // Method guard for everything else. The live-verified OCS v1 set\n // (OCS_V1_METHODS) does not (yet) cover every name the repo calls, so by\n // default we WARN once rather than throw — surfacing possibly-phantom\n // methods 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 // Field-name gate, same chokepoint and same reason. OCS rejects the whole\n // request on an unknown key, so a tool advertising `resellerId` where OCS\n // wants `id` errors mid-loop and the model gives up or invents an answer.\n // Translating here keeps the published tool schema stable across every\n // surface — MCP server, stdio, API, CLI — and future call sites.\n const wireParams = applyParamRenames(method, params);\n // modifySubscriberStatus.subscriber is a SubscriberId object. A bare ICCID\n // string is OCS error 2. Nesting here covers MCP, REST, the CLI, and any\n // rollback that still stored the string.\n const shapedParams =\n method === \"modifySubscriberStatus\" ? nestSubscriberIdParam(wireParams) : wireParams;\n const body = JSON.stringify({ [method]: shapedParams });\n\n const singleAttempt = SINGLE_ATTEMPT_METHODS.has(method);\n const send = 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 // Layer 3 — Request budget: per-method deadline, retry-on-timeout, and a\n // total wall-clock ceiling. See timeout-budgets.ts for the measurements.\n return runWithBudget<T>(method, async (signal, attemptMs) => {\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n signal,\n });\n } catch (err) {\n if (isAbortError(err)) {\n // Our own deadline fired at the socket level — surface it as the\n // non-retriable timeout rather than as a retriable network error.\n throw new OcsTimeoutError(method, attemptMs);\n }\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 // The body read is inside the deadline too: #663's hang was a response\n // that started but never completed, which a headers-only timeout misses.\n let json: OcsResponse<T>;\n try {\n json = (await res.json()) as OcsResponse<T>;\n } catch (err) {\n if (isAbortError(err)) {\n throw new OcsTimeoutError(method, attemptMs);\n }\n throw err;\n }\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 if (json[method] !== undefined) {\n payload = json[method] as T;\n } else if (Object.keys(json).some((k) => k !== \"status\")) {\n // OCS keyed the response under something other than the method name\n // (e.g. getSingleSubscriber -> {status, subscriber}). Callers that\n // read a differently-named top-level key off the raw envelope (see\n // ocs-scoping.ts's subscriberAccountId) depend on this fallback —\n // narrow it to \"envelope carries more than just status\" so it does\n // not also catch the success-with-nothing-but-status case below.\n payload = json as unknown as T;\n } else {\n // OCS answered status.code 0 (success) and the envelope carries\n // NOTHING beyond that status — observed live for getSimProviderStatus\n // (`{status:{code:0,msg:\"OK\"}}` and nothing else). The old fallback\n // `json as unknown as T` handed the caller that bare envelope, so\n // `payload.status` (an `{code,msg}` object) got read as if it were\n // the method's actual field of the same name — e.g.\n // diagnose_subscriber's `String(sim.data.status)` on that object\n // produced the literal string \"[object Object]\" and was reported as\n // a critical finding. A success envelope with nothing else means OCS\n // has no data to report for this call; treat it as an absent\n // payload so every downstream `if (data)` / `data?.field` guard\n // degrades safely instead of reading envelope plumbing as data.\n payload = null as T;\n }\n\n // A success envelope with a null body is not an error for the two\n // methods that document a stand-in (empty steering list, package\n // status confirmation). Other methods stay null.\n payload = normaliseAbsentOcsPayload(method, payload, shapedParams);\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 }, singleAttempt ? { ...ocsBudgetFor(method), maxAttempts: 1 } : undefined);\n };\n return singleAttempt ? send() : retryWithBackoff(send);\n }\n}\n","/**\n * ITU-T E.212 Mobile Country Code (MCC) → ISO 3166-1 alpha-2 mapping.\n *\n * Source: ITU-T E.212 (11/2019) + Wikipedia \"Mobile country code\" article.\n * Used by detect_country_entry intelligence composite and mango.talk country-watch cron.\n */\nexport const MCC_TO_ISO: Record<number, string> = {\n // Europe\n 202: \"GR\", // Greece\n 204: \"NL\", // Netherlands\n 206: \"BE\", // Belgium\n 208: \"FR\", // France\n 212: \"MC\", // Monaco\n 213: \"AD\", // Andorra\n 214: \"ES\", // Spain\n 216: \"HU\", // Hungary\n 218: \"BA\", // Bosnia and Herzegovina\n 219: \"HR\", // Croatia\n 220: \"RS\", // Serbia\n 221: \"XK\", // Kosovo\n 222: \"IT\", // Italy\n 225: \"VA\", // Vatican City\n 226: \"RO\", // Romania\n 228: \"CH\", // Switzerland\n 230: \"CZ\", // Czech Republic\n 231: \"SK\", // Slovakia\n 232: \"AT\", // Austria\n 234: \"GB\", // United Kingdom\n 235: \"GB\", // United Kingdom\n 238: \"DK\", // Denmark\n 240: \"SE\", // Sweden\n 242: \"NO\", // Norway\n 244: \"FI\", // Finland\n 246: \"LT\", // Lithuania\n 247: \"LV\", // Latvia\n 248: \"EE\", // Estonia\n 250: \"RU\", // Russia\n 255: \"UA\", // Ukraine\n 257: \"BY\", // Belarus\n 259: \"MD\", // Moldova\n 260: \"PL\", // Poland\n 262: \"DE\", // Germany\n 266: \"GI\", // Gibraltar\n 268: \"PT\", // Portugal\n 270: \"LU\", // Luxembourg\n 272: \"IE\", // Ireland\n 274: \"IS\", // Iceland\n 276: \"AL\", // Albania\n 278: \"MT\", // Malta\n 280: \"CY\", // Cyprus\n 282: \"GE\", // Georgia\n 283: \"AM\", // Armenia\n 284: \"BG\", // Bulgaria\n 286: \"TR\", // Turkey\n 288: \"FO\", // Faroe Islands\n 289: \"GE\", // Abkhazia (Georgia)\n 290: \"GL\", // Greenland\n 292: \"SM\", // San Marino\n 293: \"SI\", // Slovenia\n 294: \"MK\", // North Macedonia\n 295: \"LI\", // Liechtenstein\n 297: \"ME\", // Montenegro\n\n // Commonwealth of Independent States / Former USSR\n 401: \"KZ\", // Kazakhstan\n 402: \"BT\", // Bhutan\n 404: \"IN\", // India\n 405: \"IN\", // India\n 406: \"IN\", // India\n 410: \"PK\", // Pakistan\n 412: \"AF\", // Afghanistan\n 413: \"LK\", // Sri Lanka\n 414: \"MM\", // Myanmar\n 415: \"LB\", // Lebanon\n 416: \"JO\", // Jordan\n 417: \"SY\", // Syria\n 418: \"IQ\", // Iraq\n 419: \"KW\", // Kuwait\n 420: \"SA\", // Saudi Arabia\n 421: \"YE\", // Yemen\n 422: \"OM\", // Oman\n 424: \"AE\", // United Arab Emirates\n 425: \"IL\", // Israel\n 426: \"BH\", // Bahrain\n 427: \"QA\", // Qatar\n 428: \"MN\", // Mongolia\n 429: \"NP\", // Nepal\n 430: \"AE\", // United Arab Emirates\n 431: \"AE\", // United Arab Emirates\n 432: \"IR\", // Iran\n 434: \"UZ\", // Uzbekistan\n 436: \"TJ\", // Tajikistan\n 437: \"KG\", // Kyrgyzstan\n 438: \"TM\", // Turkmenistan\n 440: \"JP\", // Japan\n 441: \"JP\", // Japan\n 450: \"KR\", // South Korea\n 452: \"VN\", // Vietnam\n 454: \"HK\", // Hong Kong\n 455: \"MO\", // Macau\n 456: \"KH\", // Cambodia\n 457: \"LA\", // Laos\n 460: \"CN\", // China\n 461: \"CN\", // China\n 466: \"TW\", // Taiwan\n 467: \"KP\", // North Korea\n 470: \"BD\", // Bangladesh\n 472: \"MV\", // Maldives\n 502: \"MY\", // Malaysia\n 505: \"AU\", // Australia\n 510: \"ID\", // Indonesia\n 514: \"TL\", // East Timor\n 515: \"PH\", // Philippines\n 520: \"TH\", // Thailand\n 525: \"SG\", // Singapore\n 528: \"BN\", // Brunei\n 530: \"NZ\", // New Zealand\n 536: \"NR\", // Nauru\n 537: \"PG\", // Papua New Guinea\n 539: \"TO\", // Tonga\n 540: \"SB\", // Solomon Islands\n 541: \"VU\", // Vanuatu\n 542: \"FJ\", // Fiji\n 544: \"AS\", // American Samoa\n 545: \"KI\", // Kiribati\n 546: \"NC\", // New Caledonia\n 547: \"PF\", // French Polynesia\n 548: \"CK\", // Cook Islands\n 549: \"WS\", // Samoa\n 550: \"FM\", // Micronesia\n 551: \"MH\", // Marshall Islands\n 552: \"PW\", // Palau\n 553: \"TV\", // Tuvalu\n 555: \"NU\", // Niue\n\n // Africa (ITU-T E.212)\n 602: \"EG\", // Egypt\n 603: \"DZ\", // Algeria\n 604: \"MA\", // Morocco\n 605: \"TN\", // Tunisia\n 606: \"LY\", // Libya\n 607: \"GM\", // Gambia\n 608: \"SN\", // Senegal\n 609: \"MR\", // Mauritania\n 610: \"ML\", // Mali\n 611: \"GN\", // Guinea\n 612: \"CI\", // Côte d'Ivoire\n 613: \"BF\", // Burkina Faso\n 614: \"NE\", // Niger\n 615: \"TG\", // Togo\n 616: \"BJ\", // Benin\n 617: \"MU\", // Mauritius\n 618: \"LR\", // Liberia\n 619: \"SL\", // Sierra Leone\n 620: \"GH\", // Ghana\n 621: \"NG\", // Nigeria\n 622: \"TD\", // Chad\n 623: \"CF\", // Central African Republic\n 624: \"CM\", // Cameroon\n 625: \"CV\", // Cape Verde\n 626: \"ST\", // São Tomé and Príncipe\n 627: \"GQ\", // Equatorial Guinea\n 628: \"GA\", // Gabon\n 629: \"CG\", // Republic of the Congo\n 630: \"CD\", // Democratic Republic of the Congo\n 631: \"AO\", // Angola\n 632: \"GW\", // Guinea-Bissau\n 633: \"SC\", // Seychelles\n 634: \"SD\", // Sudan\n 635: \"RW\", // Rwanda\n 636: \"ET\", // Ethiopia\n 637: \"SO\", // Somalia\n 638: \"DJ\", // Djibouti\n 639: \"KE\", // Kenya\n 640: \"TZ\", // Tanzania\n 641: \"UG\", // Uganda\n 642: \"BI\", // Burundi\n 643: \"MZ\", // Mozambique\n 645: \"ZM\", // Zambia\n 646: \"MG\", // Madagascar\n 647: \"RE\", // Réunion / French Indian Ocean\n 648: \"ZW\", // Zimbabwe\n 649: \"NA\", // Namibia\n 650: \"MW\", // Malawi\n 651: \"LS\", // Lesotho\n 652: \"BW\", // Botswana\n 653: \"SZ\", // Eswatini\n 654: \"KM\", // Comoros\n 655: \"ZA\", // South Africa\n 657: \"ER\", // Eritrea\n 658: \"SH\", // Saint Helena\n 659: \"SS\", // South Sudan\n 702: \"BZ\", // Belize\n 704: \"GT\", // Guatemala\n 706: \"SV\", // El Salvador\n 708: \"HN\", // Honduras\n 710: \"NI\", // Nicaragua\n 712: \"CR\", // Costa Rica\n 714: \"PA\", // Panama\n 716: \"PE\", // Peru\n 722: \"AR\", // Argentina\n 724: \"BR\", // Brazil\n 730: \"CL\", // Chile\n 732: \"CO\", // Colombia\n 734: \"VE\", // Venezuela\n 736: \"BO\", // Bolivia\n 738: \"GY\", // Guyana\n 740: \"EC\", // Ecuador\n 742: \"GF\", // French Guiana\n 744: \"PY\", // Paraguay\n 746: \"SR\", // Suriname\n 748: \"UY\", // Uruguay\n 750: \"FK\", // Falkland Islands\n\n // North America, Caribbean\n 302: \"CA\", // Canada\n 308: \"PM\", // Saint Pierre and Miquelon\n 310: \"US\", // United States\n 311: \"US\", // United States\n 312: \"US\", // United States\n 313: \"US\", // United States\n 314: \"US\", // United States\n 315: \"US\", // United States\n 316: \"US\", // United States\n 330: \"PR\", // Puerto Rico\n 332: \"VI\", // U.S. Virgin Islands\n 334: \"MX\", // Mexico\n 338: \"JM\", // Jamaica\n 340: \"GP\", // Guadeloupe\n 342: \"BB\", // Barbados\n 344: \"AG\", // Antigua and Barbuda\n 346: \"KY\", // Cayman Islands\n 348: \"VG\", // British Virgin Islands\n 350: \"BM\", // Bermuda\n 352: \"GD\", // Grenada\n 354: \"MS\", // Montserrat\n 356: \"KN\", // Saint Kitts and Nevis\n 358: \"LC\", // Saint Lucia\n 360: \"VC\", // Saint Vincent and the Grenadines\n 362: \"CW\", // Curaçao / Netherlands Antilles\n 363: \"AW\", // Aruba\n 364: \"BS\", // Bahamas\n 365: \"AI\", // Anguilla\n 366: \"DM\", // Dominica\n 368: \"CU\", // Cuba\n 370: \"DO\", // Dominican Republic\n 372: \"HT\", // Haiti\n 374: \"TT\", // Trinidad and Tobago\n 376: \"TC\", // Turks and Caicos Islands\n\n // Special / Test MCCs\n 999: \"XX\", // Test network\n 901: \"XX\", // International / satellite\n};\n\n/**\n * Resolve an MCC (Mobile Country Code, ITU-T E.212) to ISO 3166-1 alpha-2.\n * Returns null for unknown MCCs.\n */\nexport function mccToIso(mcc: number | string | null | undefined): string | null {\n if (mcc == null) return null;\n let n: number;\n if (typeof mcc === \"string\") {\n const trimmed = mcc.trim();\n if (trimmed === \"\" || !/^\\d+$/.test(trimmed)) return null;\n n = parseInt(trimmed, 10);\n } else {\n n = mcc;\n }\n if (!Number.isFinite(n)) return null;\n return MCC_TO_ISO[n] ?? null;\n}\n\n/**\n * Reverse: get all MCCs for a given ISO 3166-1 alpha-2 country code.\n */\nexport function isoToMccs(iso: string): number[] {\n const upper = iso.toUpperCase();\n return Object.entries(MCC_TO_ISO)\n .filter(([, v]) => v === upper)\n .map(([k]) => Number(k));\n}\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 *\n * The array is a HISTORY: closed periods carry an `endDate`, the current one\n * does not. OCS happens to return it newest-first today, but that is ordering\n * we do not control, so prefer the open period explicitly and only fall back\n * to the first readable entry. A subscriber whose history begins with a closed\n * \"Manually Suspended\" period is Active, and must not read as suspended.\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)) return null;\n\n let fallback: string | null = null;\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const name = rec.status;\n if (typeof name !== \"string\" || name.length === 0) continue;\n\n const endDate = rec.endDate;\n const open = endDate === undefined || endDate === null || endDate === \"\";\n if (open) return name;\n if (fallback === null) fallback = name;\n }\n return fallback;\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 * Default OCS account for a BYO / reseller-token org — one implementation that\n * the REST API, the MCP Worker and the stdio CLI all import.\n *\n * WHY THIS FILE EXISTS\n * OCS `listSubscriber` refuses a request with no search key (HTTP 200 +\n * `{\"code\":2,\"msg\":\"Missing one search key\"}`) and it takes NO `resellerId`,\n * so there is no \"list the whole reseller\" call. The Console fleet page asks\n * for `/v1/subscribers?page=1&limit=50` with no filter. A BYO org that\n * connected an OCS token but never got a `preferred_account_id` in KV\n * (eSIMMY, 2026-09-29) therefore got a 400 and a dead Fleet page.\n *\n * Instead of asking the customer to fix KV by hand, Carrier now looks the\n * account up itself: `listResellerAccount` (accepts `resellerId` only) lists\n * the accounts under the token's reseller, and we pick one.\n *\n * WHICH ACCOUNT\n * - exactly one account: that one, no ambiguity.\n * - several: the LOWEST id. OCS carries no \"main account\" flag, so the oldest\n * account is the only deterministic choice. Callers get `accountCount` back\n * so a partial view can be labelled as partial instead of passing for the\n * whole fleet.\n *\n * NEVER A QUIET EMPTY LIST\n * Every failure is a typed result the caller turns into an error response.\n * Nothing here returns \"no subscribers\" for \"could not find out\".\n */\n\n/** Keys OCS accepts as a `listSubscriber` search key. */\nexport const OCS_SUBSCRIBER_SEARCH_KEYS = [\n \"accountId\",\n \"iccid\",\n \"imsi\",\n \"msisdn\",\n \"activationCode\",\n \"iccidPrefix\",\n \"imsiPrefix\",\n \"msisdnPrefix\",\n] as const;\n\n/** True when `params` carries at least one key OCS accepts as a subscriber search key. */\nexport function hasSubscriberSearchKey(params: Record<string, unknown>): boolean {\n return OCS_SUBSCRIBER_SEARCH_KEYS.some((key) => {\n const v = params[key];\n return v !== undefined && v !== null && String(v).trim() !== \"\";\n });\n}\n\nexport interface OcsAccountRef {\n id: number;\n name?: string;\n}\n\n/**\n * Flatten a `listResellerAccount` response.\n *\n * Live OCS nests accounts under `reseller[].account`; older snapshots had a\n * top-level `account` array or a bare array. A payload can carry both nested\n * and top-level accounts, so they are concatenated rather than `??`-chained.\n */\nexport function flattenResellerAccounts(raw: unknown): OcsAccountRef[] {\n const records: unknown[] = [];\n if (Array.isArray(raw)) {\n records.push(...raw);\n } else if (raw !== null && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n if (Array.isArray(obj[\"reseller\"])) {\n for (const entry of obj[\"reseller\"] as unknown[]) {\n const accounts = (entry as Record<string, unknown> | null)?.[\"account\"];\n if (Array.isArray(accounts)) records.push(...accounts);\n }\n }\n if (Array.isArray(obj[\"account\"])) records.push(...(obj[\"account\"] as unknown[]));\n }\n\n const seen = new Set<number>();\n const out: OcsAccountRef[] = [];\n for (const rec of records) {\n if (rec === null || typeof rec !== \"object\") continue;\n const r = rec as Record<string, unknown>;\n const id = Number(r[\"id\"] ?? r[\"accountId\"]);\n if (!Number.isFinite(id) || seen.has(id)) continue;\n seen.add(id);\n out.push(typeof r[\"name\"] === \"string\" ? { id, name: r[\"name\"] } : { id });\n }\n return out;\n}\n\nexport interface DefaultAccountPick {\n accountId: number;\n name?: string;\n /** How many accounts sit under the reseller. More than 1 means this is a partial view. */\n accountCount: number;\n}\n\n/** Pick the default account: the only one, or the lowest id. */\nexport function pickDefaultAccount(accounts: readonly OcsAccountRef[]): DefaultAccountPick | undefined {\n if (accounts.length === 0) return undefined;\n const first = [...accounts].sort((a, b) => a.id - b.id)[0]!;\n return {\n accountId: first.id,\n ...(first.name !== undefined ? { name: first.name } : {}),\n accountCount: accounts.length,\n };\n}\n\nexport type DefaultAccountResult =\n | ({ ok: true } & DefaultAccountPick)\n | { ok: false; reason: \"no_accounts\" | \"lookup_failed\"; message: string };\n\n/** Optional short-lived memo so a Fleet page load does not pay two OCS calls every time. */\nexport interface DefaultAccountCache {\n get(): Promise<DefaultAccountPick | null>;\n put(pick: DefaultAccountPick): Promise<void>;\n}\n\n/** Cache TTL. Short on purpose: an account created in OCS shows up within the hour. */\nexport const DEFAULT_ACCOUNT_CACHE_TTL_SECONDS = 3600;\n\n/**\n * KV key for the memo. The reseller id is part of it so reconnecting a\n * different OCS token cannot serve the previous reseller's account.\n */\nexport function defaultAccountCacheKey(orgId: string, resellerId?: string | number): string {\n return `default-account:${orgId}:${resellerId === undefined || resellerId === \"\" ? \"unknown\" : String(resellerId)}`;\n}\n\nexport const NO_ACCOUNTS_MESSAGE =\n \"OCS requires a subscriber search key, and no default account could be chosen because \" +\n \"this reseller has no accounts yet. Create an account in OCS, or pass accountId / \" +\n \"set preferred_account_id once one exists.\";\n\nexport function lookupFailedMessage(detail: string): string {\n return (\n \"OCS requires a subscriber search key, and this organization's default account could not be \" +\n `looked up (${detail}). Pass accountId, or set preferred_account_id, or retry.`\n );\n}\n\nexport interface ResolveDefaultAccountOptions {\n /** One OCS call. Must throw when OCS or the transport fails. */\n call: (method: string, params: Record<string, unknown>) => Promise<unknown>;\n /** KV `ocs_reseller_id`, when known. Looked up with getResellerInfo otherwise. */\n resellerId?: string | number;\n cache?: DefaultAccountCache;\n}\n\nfunction errorText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Resolve the default account for a reseller-token org.\n *\n * Read-only against OCS (`getResellerInfo`, `listResellerAccount`). The only\n * write is the optional cache.\n */\nexport async function resolveDefaultAccount(\n opts: ResolveDefaultAccountOptions,\n): Promise<DefaultAccountResult> {\n const cached = await opts.cache?.get().catch(() => null);\n if (cached && Number.isFinite(cached.accountId)) return { ok: true, ...cached };\n\n let resellerId: number | undefined;\n const given = Number(opts.resellerId);\n if (opts.resellerId !== undefined && opts.resellerId !== \"\" && Number.isFinite(given)) {\n resellerId = given;\n } else {\n try {\n const info = (await opts.call(\"getResellerInfo\", {})) as { id?: unknown } | null;\n const id = Number(info?.id);\n if (Number.isFinite(id)) resellerId = id;\n } catch {\n // Fall through: listResellerAccount below reports the real failure.\n }\n }\n\n let raw: unknown;\n try {\n raw = await opts.call(\"listResellerAccount\", resellerId !== undefined ? { resellerId } : {});\n } catch (err) {\n return { ok: false, reason: \"lookup_failed\", message: lookupFailedMessage(errorText(err)) };\n }\n\n const pick = pickDefaultAccount(flattenResellerAccounts(raw));\n if (!pick) return { ok: false, reason: \"no_accounts\", message: NO_ACCOUNTS_MESSAGE };\n\n await opts.cache?.put(pick).catch(() => undefined);\n return { ok: true, ...pick };\n}\n\n/**\n * Human note for a response that was scoped by a resolved default account.\n * Names the account and, when there are others, says the view is partial.\n */\nexport function defaultAccountNote(pick: DefaultAccountPick): string {\n const label = pick.name ? `${pick.accountId} (${pick.name})` : String(pick.accountId);\n return pick.accountCount > 1\n ? `No account was specified, so this shows account ${label} only — the lowest of ${pick.accountCount} accounts under this reseller. ` +\n \"Pass accountId to see another account.\"\n : `No account was specified, so this shows account ${label}, the only account under this reseller.`;\n}\n\nexport interface ScopeListSubscriberOptions {\n /** Params already built from the caller's filters (and, for managed orgs, already pinned). */\n params: Record<string, unknown>;\n /** Managed orgs are pinned elsewhere; nothing is resolved for them. */\n managed?: boolean;\n /** KV `preferred_account_id`, when set. Always beats a lookup. */\n preferredAccountId?: string;\n resellerId?: string | number;\n call: ResolveDefaultAccountOptions[\"call\"];\n cache?: DefaultAccountCache;\n}\n\nexport type ScopeListSubscriberResult =\n | { ok: true; params: Record<string, unknown>; defaultAccount?: DefaultAccountPick & { note: string } }\n | { ok: false; reason: \"no_accounts\" | \"lookup_failed\"; message: string };\n\n/**\n * The one rule for \"this listSubscriber has no search key\", shared by the MCP\n * Worker and the stdio CLI so Console, CLI and connector behave the same:\n *\n * caller key > KV preferred_account_id > looked-up default account > error\n */\nexport async function scopeListSubscriberParams(\n opts: ScopeListSubscriberOptions,\n): Promise<ScopeListSubscriberResult> {\n if (opts.managed || hasSubscriberSearchKey(opts.params)) return { ok: true, params: opts.params };\n\n const preferred = Number(opts.preferredAccountId);\n if (opts.preferredAccountId !== undefined && opts.preferredAccountId.trim() !== \"\" && Number.isFinite(preferred)) {\n return { ok: true, params: { ...opts.params, accountId: preferred } };\n }\n\n const resolved = await resolveDefaultAccount({\n call: opts.call,\n ...(opts.resellerId !== undefined ? { resellerId: opts.resellerId } : {}),\n ...(opts.cache ? { cache: opts.cache } : {}),\n });\n if (!resolved.ok) return resolved;\n const { ok: _ok, ...pick } = resolved;\n return {\n ok: true,\n params: { ...opts.params, accountId: pick.accountId },\n defaultAccount: { ...pick, note: defaultAccountNote(pick) },\n };\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\", \"packages\"],\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 * Server-side counting for OCS list results — the arithmetic the model is not\n * asked to do.\n *\n * WHY THIS EXISTS\n * ---------------\n * `list_package_templates` returns all 55 templates, untruncated and verified\n * against live OCS. Asked \"how many are there?\", Haiku answered 42; asked how\n * many of those are recurring, it answered 18. Live truth is 55 and 36.\n * Neither wrong number is a subset of anything — it is a model tallying a\n * 55-object JSON dump by eye, which is not a job a language model does\n * reliably and not a job it should be given when the server is holding the\n * array.\n *\n * So the server counts. Every list tool returning an OCS envelope emits a\n * `summary` block ahead of its rows: the row total, plus a tally of the few\n * low-cardinality fields a caller actually aggregates on. The model reads a\n * number instead of deriving one.\n *\n * WHY NOT A COUNT TOOL\n * --------------------\n * A `count_package_templates` tool would fix exactly these two questions, add\n * a 123rd entry to a catalogue whose size already costs tool-selection\n * accuracy, and only ever answer the counting question someone thought to\n * build. It would also be a second code path to the same OCS data, which is a\n * second chance to get tenant scoping wrong. Attaching the count to the read\n * that already happened costs no tool, no extra OCS call, and inherits that\n * read's scoping exactly.\n *\n * TENANCY\n * -------\n * `ocsListSummary` is pure: it counts the rows handed to it and fetches\n * nothing. Call it on the payload AFTER `scopedOcsCall` has filtered it and\n * the summary describes precisely what the caller is allowed to see. Counting\n * before scoping would leak fleet size across tenants — there is no code path\n * in this module that can reach OCS to do that.\n */\n\nimport { ocsArray, OCS_LIST_KEYS, type OcsListKind } from \"./ocs-array.js\";\n\n/** Distinct values a facet may have before only its cardinality is reported. */\nexport const FACET_TALLY_MAX = 25;\n\nexport interface OcsFacetSpec {\n /** Key this facet appears under in `summary.breakdown`. */\n readonly label: string;\n /** Dotted paths into a row, tried in order; the first defined value wins. */\n readonly paths: readonly string[];\n}\n\n/**\n * Facets per list kind. Deliberately short: a facet earns its place when a\n * human would plausibly ask \"how many X are Y\", and only for fields whose\n * value set is small enough to print.\n *\n * The template paths are the OCS field names as captured off the live fleet\n * (`tests/fixtures/response-shapes/list_package_templates.json`) — `recurring`\n * is a root boolean, and the zone name lives nested under `rdbLocationZones`,\n * not at the root beside `locationzoneid`.\n */\nexport const OCS_LIST_FACETS: Record<OcsListKind, readonly OcsFacetSpec[]> = {\n templates: [\n { label: \"recurring\", paths: [\"recurring\"] },\n {\n label: \"locationZone\",\n paths: [\n \"rdbLocationZones.locationzonename\",\n \"locationzonename\",\n \"locationzoneid\",\n ],\n },\n ],\n subscribers: [{ label: \"status\", paths: [\"status\"] }],\n packages: [\n { label: \"status\", paths: [\"status\"] },\n { label: \"recurring\", paths: [\"recurring\"] },\n ],\n zones: [],\n resellers: [],\n};\n\n/**\n * Child collections to count inside each row.\n *\n * `listResellerAccount` nests the accounts under each reseller, so a bare row\n * count answers \"how many resellers\", which is 1, when every real question\n * about that tool is about the 2 accounts underneath. Shipping `total: 1` with\n * \"use these figures directly\" in front of that payload is a worse lie than\n * saying nothing: it was measured breaking two eval questions that had been\n * passing. Counting the nesting is the fix — this is the documented trap of\n * that tool, so the summary is the right place to disarm it.\n */\nexport const OCS_LIST_NESTED: Partial<Record<OcsListKind, { label: string; path: string }>> = {\n resellers: { label: \"accounts\", path: \"account\" },\n};\n\nexport interface OcsFacetSummary {\n /** Distinct values seen across the rows. */\n distinct: number;\n /** Rows per value — omitted when `distinct` exceeds FACET_TALLY_MAX. */\n counts?: Record<string, number>;\n /** Rows where the field was absent or null. Omitted when zero. */\n missing?: number;\n}\n\nexport interface OcsListSummary {\n /**\n * Rows in the envelope, counted by the server. Authoritative for what is in\n * this result: use it, do not recount the rows below. When `moreAvailable`\n * is true it is the size of this page, not of the fleet.\n */\n total: number;\n /**\n * True when OCS says it holds rows this result does not carry — `hasMore`,\n * or an `nbFound` larger than the rows returned. Present only when true.\n *\n * Without this the note would tell a paged `list_subscribers` result that it\n * is complete, which is exactly the kind of confidently wrong number this\n * whole change exists to stop producing.\n */\n moreAvailable?: true;\n /** OCS `nbFound`: rows matching upstream, before paging. Present when OCS reports it. */\n upstreamTotal?: number;\n /**\n * Rows inside the rows, where the envelope nests — `{ accounts: 2 }` for\n * listResellerAccount. When this is present it is usually the number the\n * question is actually about, and `total` is not.\n */\n nested?: Record<string, number>;\n breakdown?: Record<string, OcsFacetSummary>;\n note: string;\n}\n\n/** OCS paging hints, when the envelope carries them (listSubscriber does). */\nfunction pagingHints(data: unknown): { hasMore: boolean; nbFound: number | undefined } {\n if (data === null || typeof data !== \"object\" || Array.isArray(data)) {\n return { hasMore: false, nbFound: undefined };\n }\n const obj = data as Record<string, unknown>;\n return {\n hasMore: obj.hasMore === true,\n nbFound: typeof obj.nbFound === \"number\" ? obj.nbFound : undefined,\n };\n}\n\nfunction readPath(row: Record<string, unknown>, path: string): unknown {\n let cur: unknown = row;\n for (const seg of path.split(\".\")) {\n if (cur === null || typeof cur !== \"object\") return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/** Facet key for a raw value, or null when the value is not summarisable. */\nfunction facetKey(v: unknown): string | null {\n if (v === null || v === undefined) return null;\n if (typeof v === \"boolean\" || typeof v === \"number\") return String(v);\n if (typeof v === \"string\") return v;\n return null;\n}\n\n/**\n * Count an OCS list envelope: row total plus a tally of the declared facets.\n *\n * Pure — it counts what it is handed. See the tenancy note above.\n */\nexport function ocsListSummary(\n data: unknown,\n kind: OcsListKind,\n facets: readonly OcsFacetSpec[] = OCS_LIST_FACETS[kind],\n): OcsListSummary {\n const rows = ocsArray<Record<string, unknown>>(data, kind);\n\n const breakdown: Record<string, OcsFacetSummary> = {};\n for (const facet of facets) {\n const counts = new Map<string, number>();\n let missing = 0;\n\n for (const row of rows) {\n if (row === null || typeof row !== \"object\") {\n missing += 1;\n continue;\n }\n let key: string | null = null;\n for (const path of facet.paths) {\n key = facetKey(readPath(row, path));\n if (key !== null) break;\n }\n if (key === null) {\n missing += 1;\n continue;\n }\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n\n if (counts.size === 0 && missing === 0) continue;\n\n const entry: OcsFacetSummary = { distinct: counts.size };\n if (counts.size > 0 && counts.size <= FACET_TALLY_MAX) {\n // Sorted, so the same fleet renders the same block every call and\n // \"first alphabetically\" is answerable by reading rather than re-sorting.\n entry.counts = Object.fromEntries(\n [...counts].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n );\n }\n if (missing > 0) entry.missing = missing;\n breakdown[facet.label] = entry;\n }\n\n const { hasMore, nbFound } = pagingHints(data);\n const more = hasMore || (nbFound !== undefined && nbFound > rows.length);\n\n const summary: OcsListSummary = {\n total: rows.length,\n note:\n `Counted server-side: ${rows.length} row${rows.length === 1 ? \"\" : \"s\"} in this result. ` +\n \"Use these figures directly rather than recounting the rows below. \" +\n (more\n ? \"This is ONE PAGE — OCS holds more rows than are returned here\" +\n (nbFound !== undefined ? ` (${nbFound} match upstream)` : \"\") +\n \", so do not report `total` as a fleet-wide figure; narrow the filters or page for the rest.\"\n : \"Nothing was truncated: these are all the rows OCS returned.\"),\n };\n\n if (more) {\n summary.moreAvailable = true;\n if (nbFound !== undefined) summary.upstreamTotal = nbFound;\n }\n\n const nest = OCS_LIST_NESTED[kind];\n if (nest) {\n const n = rows.reduce((acc, row) => {\n const child = row && typeof row === \"object\" ? readPath(row, nest.path) : undefined;\n return acc + (Array.isArray(child) ? child.length : 0);\n }, 0);\n summary.nested = { [nest.label]: n };\n summary.note +=\n ` This envelope NESTS: \\`total\\` counts the outer rows, while there are ${n} ` +\n `${nest.label} inside them under \\`${nest.path}\\`. A question about ${nest.label} ` +\n `is answered by that figure, not by \\`total\\`.`;\n }\n\n if (Object.keys(breakdown).length > 0) {\n summary.breakdown = breakdown;\n summary.note +=\n \" `breakdown` gives, per field, how many distinct values occur and how many rows carry each \" +\n \"(`missing` counts rows where the field is absent).\";\n }\n\n return summary;\n}\n\n/**\n * The OCS payload with its `summary` in front of it.\n *\n * Additive and summary-first: the envelope and every row are untouched, and a\n * model reading the result meets the count before the rows rather than after.\n */\nexport function withOcsListSummary<T>(\n data: T,\n kind: OcsListKind,\n facets?: readonly OcsFacetSpec[],\n): T & { summary: OcsListSummary } {\n const summary = ocsListSummary(data, kind, facets);\n\n if (data !== null && typeof data === \"object\" && !Array.isArray(data)) {\n return { summary, ...(data as object) } as T & { summary: OcsListSummary };\n }\n\n // A bare array (recorded fixtures, older OCS) has nowhere to hang a key.\n // Give it the envelope it should have had rather than dropping the count.\n return { summary, [OCS_LIST_KEYS[kind][0]]: data } as unknown as T & {\n summary: OcsListSummary;\n };\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 * NEVER SILENT, EITHER\n * --------------------\n * Degrading gracefully was right. Degrading INVISIBLY is what let Carrier ship\n * an AI product that never once ran in production: the deployed IAM user could\n * not invoke the configured inference profile, so every call fell back, returned\n * HTTP 200 and told nobody for two months.\n *\n * So a fallback is a reportable event, not only a return value. `onFallback`\n * hands the caller the reason, the underlying error, and whether the cause is\n * PERMANENT — an IAM denial fails identically on every future call, a throttle\n * does not. Treating those alike is how a real outage hides inside normal noise.\n *\n * The observer stays transport-free and is wrapped in try/catch, so this module\n * still needs no Sentry, no AWS and no Worker bindings, and a broken observer\n * cannot break the never-throws contract above.\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 /** IAM refused the call. Permanent: it fails identically next time. */\n | \"access_denied\"\n /** The configured model id cannot be invoked as configured. Permanent. */\n | \"model_unavailable\"\n | \"tier\"\n | \"budget_exhausted\"\n | \"rate_limited\"\n | \"timeout\"\n | \"error\"\n | \"no_facts\"\n | \"malformed\"\n | \"invented_number\";\n\n/**\n * Reasons that describe a broken deployment rather than a normal condition.\n *\n * This distinction is the point. `tier` and `budget_exhausted` are the product\n * working as sold; `rate_limited` is weather. These three mean nobody is getting\n * the AI they paid for and no future call will fix itself, so an operator should\n * be paged on them and on nothing else.\n *\n * `no_credentials` is in the list because it is only reachable once the feature\n * flag is on: a deployment that has enabled the model path and has no AWS keys\n * is misconfigured, not merely idle.\n */\nexport const PERMANENT_FALLBACK_REASONS: readonly FallbackReason[] = [\n \"no_credentials\",\n \"access_denied\",\n \"model_unavailable\",\n];\n\nexport function isPermanentFallback(reason: FallbackReason | undefined): boolean {\n return reason !== undefined && PERMANENT_FALLBACK_REASONS.includes(reason);\n}\n\n/**\n * Reasons worth a warning rather than a page.\n *\n * These are per-call defects — Bedrock failed, timed out, or answered something\n * unusable. One is a blip; a sustained rate of them is an incident. Everything\n * not in this list and not in PERMANENT_FALLBACK_REASONS is the product\n * behaving as sold and reaches Sentry never, because paging on `tier` or\n * `budget_exhausted` is how the alert that matters gets muted.\n *\n * It lives beside PERMANENT_FALLBACK_REASONS so the MCP composites and the REST\n * API grade a fallback the same way. Two surfaces with two severity tables is\n * how one of them ends up paging on a throttle.\n */\nexport const SENTRY_WARN_FALLBACK_REASONS: readonly FallbackReason[] = [\n \"error\",\n \"timeout\",\n \"malformed\",\n \"invented_number\",\n];\n\n/** True when `reason` deserves a Sentry event at all, at any level. */\nexport function shouldReportFallback(reason: FallbackReason, permanent: boolean): boolean {\n return permanent || SENTRY_WARN_FALLBACK_REASONS.includes(reason);\n}\n\n/** What `onFallback` is told. Enough to report without re-deriving anything. */\nexport interface FallbackEvent {\n /** The composite that asked for analysis, e.g. \"high_cost_subscribers\". */\n kind: string;\n reason: FallbackReason;\n /** True when the cause is a misconfiguration, per PERMANENT_FALLBACK_REASONS. */\n permanent: boolean;\n /** The thrown value, when the fallback came from a failed call. */\n error?: unknown;\n}\n\n/**\n * Map a thrown value to the reason that describes it.\n *\n * Rate limiting is checked first because @carrier/ai marks it explicitly\n * (`isRateLimit`), and mistaking a throttle for a misconfiguration is exactly\n * the false alarm that gets an alert muted.\n *\n * The two permanent buckets are separated deliberately. `access_denied` means\n * this credential may not call Bedrock and is fixed in IAM. `model_unavailable`\n * means this model id cannot be invoked the way it is configured — the shape of\n * the June 2026 outage, where an inference-profile-only model was set as\n * BEDROCK_MODEL_ID — and is fixed in config. Sending an operator to the wrong\n * console makes an outage longer, not shorter.\n */\nexport function classifyModelError(err: unknown): FallbackReason {\n if (typeof err !== \"object\" || err === null) return \"error\";\n if (\"isRateLimit\" in err) return \"rate_limited\";\n\n const name = \"name\" in err ? String((err as { name: unknown }).name) : \"\";\n const message = err instanceof Error ? err.message : String(err);\n const status =\n \"status\" in err ? Number((err as { status: unknown }).status) : Number.NaN;\n\n if (\n name === \"AbortError\" ||\n name === \"TimeoutError\" ||\n /timed? ?out|aborted/i.test(message)\n ) {\n return \"timeout\";\n }\n if (\n status === 403 ||\n /AccessDenied|not authori[sz]ed|UnrecognizedClient|InvalidSignature|ExpiredToken|InvalidClientTokenId/i.test(\n message,\n )\n ) {\n return \"access_denied\";\n }\n if (\n status === 404 ||\n /ValidationException|ResourceNotFound|inference profile|on-demand throughput/i.test(\n message,\n )\n ) {\n return \"model_unavailable\";\n }\n return \"error\";\n}\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 *\n * Also catches a placeholder `substituteFacts` could not resolve. The model\n * is only allowed to cite keys from the supplied fact list; a `{key}` that\n * survives substitution means it cited a key that does not exist (a typo, a\n * hallucinated key name, or a stale prompt/fact-set mismatch). Such a key\n * carries no digits of its own — `{sampled_subscribers}` — so the bare-number\n * check above never sees it, and the raw template text was shipped straight\n * to the console verified live 2026-09-01. An unresolved placeholder is\n * exactly the \"model wrote something the server did not verify\" case this\n * guard exists to catch, so it is treated the same as an invented number.\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 if (/\\d/.test(stripped)) return true;\n return /\\{[a-z0-9_]+\\}/i.test(rendered);\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\nexport interface AnalyzeOptions {\n enabled?: boolean;\n reason?: FallbackReason;\n /**\n * Called once for every deterministic return, including the gate reasons.\n *\n * Every path, not only the failures: a surface that reports nothing when it\n * refuses on tier or budget cannot answer \"how often does the model actually\n * run?\", which is the question that went unasked for two months. The caller\n * decides which reasons deserve an alert and which only a counter — see\n * `isPermanentFallback`.\n *\n * Anything this throws is swallowed. Observability must never be able to turn\n * a degraded report into a broken tool.\n */\n onFallback?: (event: FallbackEvent) => void;\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: AnalyzeOptions = {},\n): Promise<AnalysisResult> {\n /** Report, then return. One helper so no fallback path can forget to report. */\n const fall = (reason: FallbackReason, error?: unknown): AnalysisResult => {\n if (opts.onFallback) {\n try {\n opts.onFallback({\n kind: req.kind,\n reason,\n permanent: isPermanentFallback(reason),\n ...(error !== undefined ? { error } : {}),\n });\n } catch {\n // See AnalyzeOptions.onFallback: a broken observer is not an outage.\n }\n }\n return fromDeterministic(req, reason);\n };\n\n if (opts.reason) return fall(opts.reason);\n if (opts.enabled === false) return fall(\"disabled\");\n if (!invoke) return fall(\"no_credentials\");\n if (req.facts.length === 0) return fall(\"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 // The classifier replaces a two-way rate-limited/error split that collapsed\n // an IAM denial, a bad model id, a timeout and a genuine transient into one\n // indistinguishable \"error\". That collapse is why the June outage looked\n // like noise.\n return fall(classifyModelError(err), err);\n }\n\n if (!out?.toolInput) return fall(\"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 fall(\"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 fall(\"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/** Only non-array records can carry an explicit measurement. */\nfunction usageRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction measuredRowBytes(row: unknown): number | null {\n if (!usageRecord(row)) return null;\n let value: unknown;\n if (usageRecord(row.total) && \"quantityPerType\" in row.total) {\n const perType = row.total.quantityPerType;\n if (!usageRecord(perType) || !Object.hasOwn(perType, OCS_DATA_QUANTITY_TYPE)) return null;\n value = perType[OCS_DATA_QUANTITY_TYPE];\n } else {\n const key = [\"dataBytes\", \"dataVolume\", \"totalData\"].find((key) => Object.hasOwn(row, key));\n if (key === undefined) return null;\n value = row[key];\n }\n // Decimal numeric strings are supported by the existing reader; avoid Number's\n // coercion of blank strings, booleans, arrays and non-decimal literals to bytes.\n if (typeof value === \"string\") {\n if (!/^(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(value)) return null;\n value = Number(value);\n }\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\n/**\n * Complete measured bytes, or null when the response cannot prove usage.\n * Empty envelopes/periods have no explicit byte measurement, so are unknown.\n * Unlike the legacy readers, one malformed row invalidates the whole window.\n */\nexport function measuredUsageBytes(data: unknown): number | null {\n let rows: unknown[];\n if (Array.isArray(data)) {\n rows = data;\n } else {\n if (!usageRecord(data) || !Array.isArray(data.usages) || data.usages.length === 0) return null;\n rows = [];\n for (const usage of data.usages) {\n if (!usageRecord(usage) || !Array.isArray(usage.subsPeriodUsages) || usage.subsPeriodUsages.length === 0) return null;\n rows.push(...usage.subsPeriodUsages);\n }\n }\n if (rows.length === 0) return null;\n let sum = 0;\n for (const row of rows) {\n const bytes = measuredRowBytes(row);\n if (bytes === null) return null;\n sum += bytes;\n if (!Number.isFinite(sum)) return null;\n }\n return sum;\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 * Shared building blocks for the nine intelligence composites.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * `apps/mcp-server` and `apps/mcp-stdio` each carried a full copy of\n * `intelligence.ts`. The copies were 675 changed lines apart, and every fix\n * measured against the live fleet landed on the Worker only. Three of those\n * fixes were still missing from the published npm package:\n *\n * 1. ACTIVE filter. OCS returns `status` as an ARRAY of records, so\n * `String(row.status).toUpperCase() === \"ACTIVE\"` matches nothing and the\n * fleet reads as empty. The Worker moved to `extractSubscriberStatus`;\n * the CLI did not.\n *\n * 2. Usage window. `subscriberUsageOverPeriod` counts the range INCLUSIVELY\n * and caps it at 7 days, so a 7-day offset asks for 8 and the call fails.\n * `lastNDaysPeriod` in ocs-params.ts has expressed this correctly all\n * along; not one intelligence call site on either surface used it.\n *\n * 3. Usage parsing. The response is a nested envelope, not a flat array, so\n * an `Array.isArray(usage.data)` guard is never true and the whole block\n * is skipped without an error to explain it. `extractDailyUsage` in\n * usage.ts is the correct reader; the CLI never called it.\n *\n * Prose comments at each call site did not stop any of this — the same lesson\n * ocs-param-shapes.ts and list-subscriber.ts were written to record. One\n * shared implementation is the enforcement.\n */\n\nimport { extractSubscriberStatus } from \"./list-subscriber.js\";\n\n/** Whole days from now until `dateStr`, rounded up. Negative once past. */\nexport function daysUntil(dateStr: string): number {\n const now = new Date();\n const target = new Date(dateStr);\n return Math.ceil((target.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/** Human-readable byte count for report tables. */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`;\n}\n\n/**\n * Is this subscriber row ACTIVE?\n *\n * Goes through {@link extractSubscriberStatus} because OCS nests the name\n * inside an array. Comparing the raw field against a string returns false for\n * every row, which reads as an empty fleet rather than as an error.\n */\nexport function isActiveSubscriber(row: unknown): boolean {\n return (extractSubscriberStatus(row) ?? \"\").toUpperCase() === \"ACTIVE\";\n}\n\n/** The ACTIVE rows of an aggregated subscriber list. */\nexport function filterActiveSubscribers<T>(rows: T[]): T[] {\n return rows.filter((row) => isActiveSubscriber(row));\n}\n\n/**\n * Unwrap `{subscriberList: [...]}` or accept a bare array.\n *\n * Distinct from `unwrapSubscriberList` in list-subscriber.ts, which reports\n * whether it recognised the shape so callers can pass an unknown payload\n * through untouched. Here an unrecognised payload contributes no rows, which\n * is what an aggregating fan-out wants.\n */\nexport function subscriberRows(raw: unknown): Record<string, unknown>[] {\n if (Array.isArray(raw)) return raw as Record<string, unknown>[];\n const list = (raw as { subscriberList?: Record<string, unknown>[] } | null)?.subscriberList;\n return Array.isArray(list) ? list : [];\n}\n\n/**\n * Read the reseller's own balance from either OcsClient's direct payload or\n * the raw OCS response envelope. Money arrives as a decimal string in live\n * production, while staging fixtures use a number.\n */\nexport function extractResellerBalance(raw: unknown): number | null {\n const direct = raw as { balance?: unknown } | null;\n const enveloped = raw as { getResellerInfo?: { balance?: unknown } } | null;\n const value = direct?.balance ?? enveloped?.getResellerInfo?.balance;\n if (value === undefined || value === null || value === \"\") return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n","/**\n * Response shapes used by `diagnose_subscriber`.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * diagnose_subscriber read five OCS responses as if they were flat objects and\n * arrays. None of them are. Captured live from the Mango fleet (2026-08-29,\n * ICCID 8948010000020241170), every one of those reads was wrong:\n *\n * - `subscriber.status` is a status HISTORY array, not a string, so\n * `String(status).toUpperCase()` produced \"[OBJECT OBJECT],[OBJECT OBJECT]\",\n * which never equals \"ACTIVE\" — every healthy subscriber was reported\n * CRITICAL with \"OCS status is [OBJECT OBJECT] (not ACTIVE)\".\n * - `listSubscriberPrepaidPackages` returns `{ packages: [...] }`, and its\n * rows carry `active` / `useddatabyte` / `pckdatabyte` / `tsexpirationutc`,\n * not `status` / `dataUsed` / `dataLimit` / `expirationDate`.\n * - `subscriberNetworkEventsOverPeriod` returns an object of per-protocol\n * answer arrays (`s6aAnswer`, `gyAnswer`, `mapAnswer`, ...), not one array,\n * so the `Array.isArray` guard skipped the whole check and the tool never\n * looked at the events that actually explain an outage.\n * - the sim id lives at `sim.id`, not `simId`, so the SIM-provider check\n * never ran.\n * - the IMSI lives at `imsiList[].imsi`, not `imsi`, so the HLR check never\n * ran (`imsiFromSubscriberRecord` already knew this; diagnose did not use it).\n *\n * Two of those five turned into false CRITICALs; the other three silently made\n * the diagnosis blind. The shape knowledge belongs here next to `ocsArray` and\n * `extractSubscriberStatus` rather than duplicated in each MCP app.\n */\n\nimport { ocsArray } from \"./ocs-array.js\";\n\n/** Envelope keys seen for listSubscriberPrepaidPackages, most specific first. */\nexport const PREPAID_PACKAGE_KEYS = [\"prepaidPackage\", \"packages\", \"package\"] as const;\n\n/** Per-protocol answer arrays in a subscriberNetworkEventsOverPeriod response. */\nexport const NETWORK_EVENT_KEYS = [\n \"s6aAnswer\",\n \"gyAnswer\",\n \"mapAnswer\",\n \"camelAnswer\",\n \"mocAnswer\",\n \"mtcAnswer\",\n] as const;\n\nfunction positiveSimId(raw: unknown): number | undefined {\n const n = typeof raw === \"number\" ? raw : typeof raw === \"string\" && raw.trim() !== \"\" ? Number(raw) : NaN;\n return Number.isSafeInteger(n) && n > 0 ? n : undefined;\n}\n\n/**\n * Read a subscriber's SIM id.\n *\n * The live `getSingleSubscriber` record nests it at `sim.id`. Older payloads\n * may expose `simId` or `sim_id`, and some callers still wrap the record under\n * `subscriber`. The subscriber's own `id` is a different OCS entity — never\n * use it. `Number(undefined)` is `NaN`, so a missing id must stay `undefined`\n * rather than being forwarded to `getSimProviderStatus` or `changeSimStatus`.\n */\nexport function subscriberSimId(record: unknown): number | undefined {\n if (!record || typeof record !== \"object\") return undefined;\n const rec = record as Record<string, unknown>;\n\n const sim = rec.sim;\n if (sim && typeof sim === \"object\") {\n const simRec = sim as Record<string, unknown>;\n const fromSim = positiveSimId(simRec.id) ?? positiveSimId(simRec.simId) ?? positiveSimId(simRec.sim_id);\n if (fromSim !== undefined) return fromSim;\n }\n\n const flat = positiveSimId(rec.simId) ?? positiveSimId(rec.sim_id);\n if (flat !== undefined) return flat;\n\n const nested = rec.subscriber;\n if (nested && typeof nested === \"object\" && nested !== record) return subscriberSimId(nested);\n return undefined;\n}\n\n/**\n * Read the current SIM provider status from a `getSimProviderStatus` response.\n *\n * The live response is an ARRAY of status periods, each with `status`,\n * `start`, `end`, `profileType` and `statusModification` (CAR-300). Reading\n * `data.simStatus ?? data.status` off it yields `undefined` for every SIM, so\n * callers never saw a status at all.\n *\n * The array is a history. Select the latest period covering now: its start\n * must not be in the future and its end must not have passed. Missing bounds\n * are open; naive OCS timestamps are UTC. Expired or unreadable dated periods\n * are not current. A bare object with `simStatus` or `status` is still accepted. Returns\n * `undefined` when nothing readable is found, so \"no data\" never reads as a\n * status.\n */\nfunction simProviderStatusName(rec: unknown): string | undefined {\n if (!rec || typeof rec !== \"object\") return undefined;\n const r = rec as Record<string, unknown>;\n for (const key of [\"simStatus\", \"status\"] as const) {\n const v = r[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v.trim();\n }\n return undefined;\n}\n\n/** The current provider record, retaining profile metadata from the same period. */\nexport function currentSimProviderPeriod(data: unknown, now: Date = new Date()): Record<string, unknown> | undefined {\n if (!Array.isArray(data)) {\n return simProviderStatusName(data) === undefined ? undefined : data as Record<string, unknown>;\n }\n\n const bound = (raw: unknown): number | undefined => {\n if (raw === undefined || raw === null || raw === \"\") return undefined;\n if (typeof raw === \"number\") return raw;\n if (typeof raw !== \"string\") return NaN;\n const stamp = raw.trim();\n const hasZone = /(?:Z|[+-]\\d{2}:?\\d{2})$/.test(stamp);\n return Date.parse(hasZone ? stamp : `${stamp}Z`);\n };\n const atNow = now.getTime();\n if (!Number.isFinite(atNow)) return undefined;\n let current: { record: Record<string, unknown>; at: number } | undefined;\n for (const entry of data) {\n const status = simProviderStatusName(entry);\n if (status === undefined) continue;\n const rec = entry as Record<string, unknown>;\n const start = bound(rec.start);\n const end = bound(rec.end);\n if (start !== undefined && (!Number.isFinite(start) || start > atNow)) continue;\n if (end !== undefined && (!Number.isFinite(end) || end <= atNow)) continue;\n const at = start ?? -Infinity;\n if (!current || at > current.at) current = { record: rec, at };\n }\n return current?.record;\n}\n\n/** Current provider state, never a scheduled or expired historical state. */\nexport function extractSimProviderStatus(data: unknown, now: Date = new Date()): string | undefined {\n return simProviderStatusName(currentSimProviderPeriod(data, now));\n}\n\n/** Package rows from a listSubscriberPrepaidPackages response. */\nexport function prepaidPackageRows(data: unknown): Record<string, unknown>[] {\n return ocsArray<Record<string, unknown>>(data, PREPAID_PACKAGE_KEYS);\n}\n\n/**\n * Does the OCS row CLAIM to be active?\n *\n * This is the raw flag only. It is NOT the same question as \"does this package\n * still grant anything\" — see `isActivePackage`.\n */\nexport function isFlaggedActive(pkg: Record<string, unknown>): boolean {\n if (typeof pkg.active === \"boolean\") return pkg.active;\n const status = pkg.status;\n if (typeof status === \"string\") return status.toUpperCase() === \"ACTIVE\";\n return false;\n}\n\n/**\n * Has this package passed its expiry?\n *\n * Rows with no readable expiry are NOT treated as expired: an unparseable or\n * absent timestamp is missing information, and silently deciding \"expired\"\n * on missing data would hide live packages the same way `active` hid dead\n * ones. Unknown stays live and is a separate problem to surface.\n */\nexport function isExpiredPackage(\n pkg: Record<string, unknown>,\n now: Date = new Date(),\n): boolean {\n const expiry = packageExpiry(pkg);\n if (!expiry) return false;\n // OCS sends naive UTC stamps (\"2026-08-21T20:03:53\") with no zone marker.\n // Date.parse treats those as LOCAL time, which shifts the verdict by the\n // host offset and makes the same row expired on one box and live on another.\n const hasZone = /(?:Z|[+-]\\d{2}:?\\d{2})$/.test(expiry);\n const ms = Date.parse(hasZone ? expiry : `${expiry}Z`);\n if (!Number.isFinite(ms)) return false;\n return ms <= now.getTime();\n}\n\n/**\n * Is this package actually granting anything right now?\n *\n * `active` IS NOT VALIDITY. Measured live on 2026-09-07 (Mango SIM ...121):\n * package 204532407 expired 2026-08-21 and still carried `active: true`\n * seventeen days later. Filtering on the flag alone therefore counts dead\n * packages as allowance, which is exactly how a subscriber hits\n * EXPIRED_ENTITLEMENT while the console reports a healthy active package.\n *\n * A package is live only when the flag says active AND the expiry has not\n * passed. Both conditions, every time.\n */\nexport function isActivePackage(\n pkg: Record<string, unknown>,\n now: Date = new Date(),\n): boolean {\n return isFlaggedActive(pkg) && !isExpiredPackage(pkg, now);\n}\n\n/**\n * Rows OCS calls active that have in fact expired.\n *\n * These are the silent ones: they inflate the active count and mask a\n * subscriber with no real allowance. Surface them, never just drop them.\n */\nexport function staleActivePackages(\n pkgs: Record<string, unknown>[],\n now: Date = new Date(),\n): Record<string, unknown>[] {\n return pkgs.filter((p) => isFlaggedActive(p) && isExpiredPackage(p, now));\n}\n\nfunction firstNumber(pkg: Record<string, unknown>, keys: readonly string[]): number {\n for (const key of keys) {\n const v = pkg[key];\n if (typeof v === \"number\" && Number.isFinite(v)) return v;\n if (typeof v === \"string\" && v.trim() !== \"\" && Number.isFinite(Number(v))) return Number(v);\n }\n return 0;\n}\n\n/** Bytes consumed on a package. */\nexport function packageDataUsed(pkg: Record<string, unknown>): number {\n return firstNumber(pkg, [\"useddatabyte\", \"dataUsed\", \"dataConsumed\"]);\n}\n\n/** Byte allowance of a package. 0 means unmetered or unknown. */\nexport function packageDataLimit(pkg: Record<string, unknown>): number {\n return firstNumber(pkg, [\"pckdatabyte\", \"dataLimit\", \"dataAllowance\"]);\n}\n\n/** Expiry timestamp of a package, or \"\" when absent. */\nexport function packageExpiry(pkg: Record<string, unknown>): string {\n for (const key of [\"tsexpirationutc\", \"expirationDate\", \"endDate\"] as const) {\n const v = pkg[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v;\n }\n return \"\";\n}\n\n/** Human label for a package — the template name when the row has no name. */\nexport function packageName(pkg: Record<string, unknown>): string {\n if (typeof pkg.name === \"string\" && pkg.name !== \"\") return pkg.name;\n\n const tpl = pkg.packageTemplate;\n if (tpl && typeof tpl === \"object\") {\n const tplName = (tpl as Record<string, unknown>).prepaidpackagetemplatename;\n if (typeof tplName === \"string\" && tplName !== \"\") return tplName;\n }\n\n for (const key of [\"templateId\", \"packageTemplateId\", \"subscriberprepaidpackageid\"] as const) {\n const v = pkg[key];\n if (v !== undefined && v !== null) return String(v);\n }\n return \"unknown\";\n}\n\n/** Flatten a subscriberNetworkEventsOverPeriod response into one event list. */\nexport function networkEventRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) return data as Record<string, unknown>[];\n if (!data || typeof data !== \"object\") return [];\n\n const obj = data as Record<string, unknown>;\n const rows: Record<string, unknown>[] = [];\n for (const key of NETWORK_EVENT_KEYS) {\n // Each answer key holds an array that is itself sometimes an array of\n // arrays (one inner array per OCS page), so flatten one level.\n for (const entry of ocsArray<unknown>(obj[key], [])) {\n if (Array.isArray(entry)) {\n for (const inner of entry) {\n if (inner && typeof inner === \"object\") rows.push(inner as Record<string, unknown>);\n }\n } else if (entry && typeof entry === \"object\") {\n rows.push(entry as Record<string, unknown>);\n }\n }\n }\n return rows;\n}\n\n/** What the network events say went wrong. */\nexport interface NetworkEventSummary {\n /** Total events across every protocol leg. */\n total: number;\n /** Gy answers refusing quota because the bundle is out of credit. */\n creditLimitReached: number;\n /** S6a ULR rejected 5004 — roaming not allowed on that operator. */\n roamingNotAllowed: number;\n /** MAP legs that timed out — 2G/3G signalling not completing. */\n mapTimeouts: number;\n /** Distinct operators seen, for spotting steering ping-pong. */\n operators: string[];\n /** Newest event timestamp, or \"\" when there are none. */\n lastEventTime: string;\n}\n\nfunction eventTime(row: Record<string, unknown>): string {\n for (const key of [\"eventTime\", \"timestamp\", \"date\"] as const) {\n const v = row[key];\n if (typeof v === \"string\" && v !== \"\") return v;\n }\n return \"\";\n}\n\n/**\n * Reduce raw network events to the few signals that explain an outage.\n *\n * These are exactly the signals that were sitting unread in the response while\n * the tool reported a bogus status finding instead.\n */\nexport function summarizeNetworkEvents(rows: Record<string, unknown>[]): NetworkEventSummary {\n const operators = new Set<string>();\n let creditLimitReached = 0;\n let roamingNotAllowed = 0;\n let mapTimeouts = 0;\n let lastEventTime = \"\";\n\n for (const row of rows) {\n const operator = row.operator;\n if (typeof operator === \"string\" && operator !== \"\" && operator !== \"N/A\") operators.add(operator);\n\n const rre = row.rreResponse;\n if (typeof rre === \"string\" && rre.includes(\"CreditLimitReached\")) creditLimitReached++;\n\n if (String(row.code ?? \"\") === \"5004\") roamingNotAllowed++;\n\n if (row.protocol === \"MAP\" && String(row.result ?? \"\").toLowerCase().includes(\"timeout\")) mapTimeouts++;\n\n const t = eventTime(row);\n if (t > lastEventTime) lastEventTime = t;\n }\n\n return {\n total: rows.length,\n creditLimitReached,\n roamingNotAllowed,\n mapTimeouts,\n operators: [...operators].sort(),\n lastEventTime,\n };\n}\n","/**\n * Reconstruct a subscriber's country history from OCS Gy charging events.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * `subscriber_country_history` used to read `country.entered` events from the\n * OCS ring buffer, which only ever contains what the Bridge4IP Relay LU webhook\n * pushed into it. Relay is off for reseller 1170 (`relayGy: false`,\n * `relayLU: false`, read via `getResellerInfo`), and the buffer holds 24 hours\n * at best, so the tool answered \"no cross-border events\" for subscribers that\n * had demonstrably crossed a border.\n *\n * The same answer is already in the OCS itself. `subscriberNetworkEventsOverPeriod`\n * returns `gyAnswer.events` — one Diameter Gy charging record per session\n * Init/Update/Term — and every record carries its own `countryAlpha2`,\n * `countryName`, `operator` and `userLocationInfoMCC`. That is a complete\n * location trail for the queried period, with no webhook and no ring buffer.\n *\n * SHAPE FACTS, captured live 2026-09-06 from ICCID 8948010000020241196\n * (reseller 1170, period 2026-09-01..2026-09-06, 36 Gy events, HTTP 200,\n * `status.code === 0`):\n *\n * - `usedUnits` is a STRING. Observed values across those 36 records were\n * \"0\" (29x), \"232\", \"4267\" and — this is the one that bites — \"N/A\" (5x).\n * Every Init and most Term records carry \"N/A\". `Number(\"N/A\")` is NaN, so\n * a naive sum poisons the whole total to NaN and a naive `> 0` test reads\n * as false. Both are handled by `gyUsedBytes`.\n * - `userLocationInfoMCC` is a STRING too (\"206\", not 206).\n * - `countryAlpha2` is LOWER-case (\"be\", \"nl\"). Callers expect ISO 3166-1\n * alpha-2 upper-case, so it is upper-cased and, when absent, derived from\n * the MCC via `mccToIso`.\n * - Session boundaries interleave. At the NL to BE crossing the BE `Init` was\n * stamped 2026-09-03T10:19:05.951+02:00 and the NL `Term` that closed the\n * old session 2026-09-03T10:19:05.955+02:00 — the Term is 4 ms LATER than\n * the Init that replaced it. Sorting purely by time therefore produces a\n * phantom one-event NL stay wedged between two BE stays. A `Term` records\n * where a session ENDED, not a new arrival, so `collapseGyStays` drops a\n * run made only of `Term` events when the runs on both sides are the same\n * country, then re-collapses. Without that pass the live capture yields\n * four stays (NL, BE, NL, BE) instead of the two that actually happened.\n *\n * It lives here, next to `networkEventRows` and `summarizeNetworkEvents` in\n * `diagnose-shapes.ts`, because those parse the other legs of this same\n * response.\n *\n * Everything here is pure. No network, no env, no OCS client.\n */\n\nimport { mccToIso } from \"@carrier/ocs-spec/mcc-iso\";\n\n/** One Gy charging record, as it arrives under `gyAnswer.events`. */\nexport interface GyEvent {\n eventTime?: unknown;\n countryAlpha2?: unknown;\n countryName?: unknown;\n operator?: unknown;\n userLocationInfoMCC?: unknown;\n userLocationInfoMNC?: unknown;\n usedUnits?: unknown;\n requestType?: unknown;\n [key: string]: unknown;\n}\n\n/** A continuous period spent in one country. */\nexport interface CountryStay {\n /** ISO 3166-1 alpha-2, upper-case. */\n country: string;\n /** OCS `countryName` for the stay, or null when no record carried one. */\n country_name: string | null;\n /** Timestamp of the first Gy record in this country, as OCS wrote it. */\n first_seen: string;\n /** Timestamp of the last Gy record in this country. */\n last_seen: string;\n /** Distinct serving operators seen during the stay, sorted. */\n operators: string[];\n /** Distinct MCCs seen during the stay, sorted. */\n mccs: string[];\n /** Gy records that make up this stay. */\n event_count: number;\n /** Bytes charged during the stay. \"N/A\" records count as 0, never NaN. */\n used_bytes: number;\n}\n\n/** The whole reconstruction for one subscriber. */\nexport interface GyCountryHistory {\n stays: CountryStay[];\n /** Country changes between consecutive stays. */\n transitions: { from: string; to: string; at: string }[];\n /** Gy records used (those carrying both a timestamp and a country). */\n events_considered: number;\n /** Gy records skipped for want of a timestamp or a resolvable country. */\n events_skipped: number;\n /**\n * Records dropped as session-teardown artefacts — a `Term` stamped after the\n * `Init` that replaced it. Counted here so nothing disappears silently;\n * `events_considered` minus this equals the events across `stays`.\n */\n teardown_artifacts_dropped: number;\n}\n\n/**\n * Pull `gyAnswer.events` out of a `subscriberNetworkEventsOverPeriod` response.\n *\n * Accepts the raw OCS envelope (`{ status, subscriberNetworkEventsOverPeriod }`),\n * the unwrapped payload, or a bare `{ gyAnswer }`, because call sites in this\n * repo unwrap to different depths.\n */\nexport function extractGyEvents(response: unknown): GyEvent[] {\n if (!response || typeof response !== \"object\") return [];\n const root = response as Record<string, unknown>;\n\n const candidates: unknown[] = [\n root.subscriberNetworkEventsOverPeriod,\n root.data,\n root,\n ];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== \"object\") continue;\n const gy = (candidate as Record<string, unknown>).gyAnswer;\n if (Array.isArray(gy)) {\n return gy.filter((e): e is GyEvent => !!e && typeof e === \"object\");\n }\n if (gy && typeof gy === \"object\") {\n const events = (gy as Record<string, unknown>).events;\n if (Array.isArray(events)) {\n return events.filter((e): e is GyEvent => !!e && typeof e === \"object\");\n }\n }\n }\n return [];\n}\n\n/**\n * Bytes charged on one Gy record.\n *\n * `usedUnits` is a string of bytes, and the OCS writes the literal \"N/A\" on\n * Init records and on most Terms. Returning 0 for anything non-numeric is what\n * keeps a period total from collapsing to NaN.\n */\nexport function gyUsedBytes(event: GyEvent): number {\n const raw = event.usedUnits;\n if (typeof raw === \"number\") return Number.isFinite(raw) ? raw : 0;\n if (typeof raw !== \"string\") return 0;\n const trimmed = raw.trim();\n if (trimmed === \"\" || trimmed.toUpperCase() === \"N/A\") return 0;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : 0;\n}\n\n/** ISO alpha-2 for a Gy record: `countryAlpha2` first, MCC as the fallback. */\nexport function gyCountryIso(event: GyEvent): string | null {\n const alpha2 = event.countryAlpha2;\n if (typeof alpha2 === \"string\" && /^[A-Za-z]{2}$/.test(alpha2.trim())) {\n return alpha2.trim().toUpperCase();\n }\n const mcc = event.userLocationInfoMCC;\n if (typeof mcc === \"string\" || typeof mcc === \"number\") return mccToIso(mcc);\n return null;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() !== \"\" ? value.trim() : null;\n}\n\nfunction meaningful(value: unknown): string | null {\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n const s = str(value);\n return s && s.toUpperCase() !== \"N/A\" ? s : null;\n}\n\ninterface NormalisedEvent {\n time: string;\n country: string;\n countryName: string | null;\n operator: string | null;\n mcc: string | null;\n requestType: string;\n bytes: number;\n}\n\nfunction normalise(events: GyEvent[]): { rows: NormalisedEvent[]; skipped: number } {\n const rows: NormalisedEvent[] = [];\n let skipped = 0;\n\n for (const event of events) {\n const time = str(event.eventTime);\n const country = gyCountryIso(event);\n if (!time || !country) {\n skipped++;\n continue;\n }\n rows.push({\n time,\n country,\n countryName: meaningful(event.countryName),\n operator: meaningful(event.operator),\n mcc: meaningful(event.userLocationInfoMCC),\n requestType: str(event.requestType) ?? \"\",\n bytes: gyUsedBytes(event),\n });\n }\n\n // Oldest first. Timestamps are ISO-8601 with an offset, so compare them as\n // instants — a +02:00 record and a Z record from the same moment must not\n // swap places just because their strings sort differently.\n rows.sort((a, b) => {\n const ta = Date.parse(a.time);\n const tb = Date.parse(b.time);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n return a.time.localeCompare(b.time);\n });\n\n return { rows, skipped };\n}\n\nfunction runToStay(run: NormalisedEvent[]): CountryStay {\n const operators = new Set<string>();\n const mccs = new Set<string>();\n let countryName: string | null = null;\n let bytes = 0;\n\n for (const row of run) {\n if (row.operator) operators.add(row.operator);\n if (row.mcc) mccs.add(row.mcc);\n if (!countryName && row.countryName) countryName = row.countryName;\n bytes += row.bytes;\n }\n\n const first = run[0]!;\n const last = run[run.length - 1]!;\n\n return {\n country: first.country,\n country_name: countryName,\n first_seen: first.time,\n last_seen: last.time,\n operators: [...operators].sort(),\n mccs: [...mccs].sort(),\n event_count: run.length,\n used_bytes: bytes,\n };\n}\n\nfunction collapseRuns(rows: NormalisedEvent[]): NormalisedEvent[][] {\n const runs: NormalisedEvent[][] = [];\n for (const row of rows) {\n const current = runs[runs.length - 1];\n if (current && current[0]!.country === row.country) current.push(row);\n else runs.push([row]);\n }\n return runs;\n}\n\n/**\n * Collapse a chronological event list into country stays.\n *\n * Two passes. The first merges consecutive same-country records. The second\n * removes the session-teardown artefact described in the file header: a run\n * made entirely of `Term` records, sitting between two runs of one and the\n * same other country, is the tail of a session that has already been replaced,\n * not a visit. It is dropped and its neighbours re-collapsed.\n */\nexport function collapseGyStays(events: GyEvent[]): GyCountryHistory {\n const { rows, skipped } = normalise(events);\n if (rows.length === 0) {\n return {\n stays: [],\n transitions: [],\n events_considered: 0,\n events_skipped: skipped,\n teardown_artifacts_dropped: 0,\n };\n }\n\n let runs = collapseRuns(rows);\n let teardownDropped = 0;\n\n for (let changed = true; changed; ) {\n changed = false;\n for (let i = 1; i < runs.length - 1; i++) {\n const run = runs[i]!;\n if (!run.every((r) => r.requestType.toLowerCase() === \"term\")) continue;\n if (runs[i - 1]![0]!.country !== runs[i + 1]![0]!.country) continue;\n\n teardownDropped += run.length;\n runs = collapseRuns(runs.flatMap((r, idx) => (idx === i ? [] : r)));\n changed = true;\n break;\n }\n }\n\n const stays = runs.map(runToStay);\n const transitions = stays.slice(1).map((stay, idx) => ({\n from: stays[idx]!.country,\n to: stay.country,\n at: stay.first_seen,\n }));\n\n return {\n stays,\n transitions,\n events_considered: rows.length,\n events_skipped: skipped,\n teardown_artifacts_dropped: teardownDropped,\n };\n}\n\n/** One call: raw OCS response in, country history out. */\nexport function gyCountryHistoryFromResponse(response: unknown): GyCountryHistory {\n return collapseGyStays(extractGyEvents(response));\n}\n","/**\n * Network default: which networks a SIM may use, chosen per country.\n *\n * WHY THIS EXISTS\n * ---------------\n * Nothing in provisioning, package templates or activation ever sets a steering\n * list. A SIM with no list is allowed on every network OCS has for the country,\n * so a customer with five networks in Canada gets all five and the phone picks\n * one, not necessarily the cheapest. The wish: the default is \"cheapest available\n * network\", and the customer can override it.\n *\n * This module is PURE. No I/O, no clock unless `now` is passed, no KV. The API\n * route, the MCP tool and the CLI all call these functions, so the three surfaces\n * cannot disagree about what \"cheapest\" means. `tests/network-default-contract`\n * pins that.\n *\n * PRECEDENCE (first hit wins)\n * sim override > package override > org default > system default\n * The system default is `cheapest_available`. It applies to NEW assignments\n * only: nothing here touches a SIM that already has a list.\n *\n * WHAT \"CHEAPEST\" MEANS\n * - lowest `datarate` > 0 in the tariff, per network (OCS: currency per MB),\n * - among rules that are really in force (not hidden, not `active: false`,\n * inside their start/end window at `now`),\n * - ties keep every tied network (redundancy, deterministic order),\n * - only compared inside one currency; a mixed-currency country is not\n * comparable and falls back.\n * This is the wholesale data rate, not the end price a customer pays. Whether\n * that is the right basis is an owner decision, see the design note.\n *\n * NEVER SILENT\n * ------------\n * If the choice cannot be made (no tariff, nothing available, mixed currencies,\n * or an override that is invalid) the plan falls back to `allow_all` and says so\n * in `warnings` with a stable `code`. `fellBackToAll` is true. A caller must show\n * that; it must not present a fallback as a cheapest choice.\n */\n\nexport const NETWORK_DEFAULT_MODES = [\"cheapest_available\", \"all\", \"custom\"] as const;\nexport type NetworkDefaultMode = (typeof NETWORK_DEFAULT_MODES)[number];\n\n/** The mode used when neither the org nor the SIM/package chose one. */\nexport const SYSTEM_NETWORK_DEFAULT: NetworkDefaultMode = \"cheapest_available\";\n\n/** A stored or supplied choice. `custom` needs an existing OCS steering list. */\nexport interface NetworkChoice {\n mode: NetworkDefaultMode;\n /** Required when mode is `custom`. Ignored otherwise. */\n steeringListId?: number;\n}\n\nexport type NetworkChoiceSource =\n | \"sim_override\"\n | \"package_override\"\n | \"org_default\"\n | \"system_default\";\n\nexport type NetworkPlanWarningCode =\n | \"NO_TARIFF_DATA\"\n | \"NO_AVAILABLE_NETWORK\"\n | \"MIXED_CURRENCY\"\n | \"INVALID_CHOICE\"\n | \"NO_COUNTRY\"\n | \"SINGLE_NETWORK\"\n | \"FETCH_FAILED\";\n\nexport interface NetworkPlanWarning {\n code: NetworkPlanWarningCode;\n /** ISO-3166 alpha-2, upper case, when the warning is about one country. */\n country?: string;\n message: string;\n}\n\n/** The tariff fields this module reads. A superset of what OCS sends is fine. */\nexport interface TariffRuleLike {\n operator?: {\n networkId?: number;\n countryIso2?: string;\n operatorName?: string;\n [k: string]: unknown;\n };\n networkid?: number;\n datarate?: number;\n currency?: { currencycode?: string; [k: string]: unknown };\n currencyid?: number;\n active?: boolean;\n hidden?: boolean;\n startdate?: string;\n enddate?: string;\n [k: string]: unknown;\n}\n\nexport interface CandidateNetwork {\n networkId: number;\n operatorName: string;\n /** Lowest positive datarate seen for this network, per MB, in `currency`. */\n datarate: number;\n currency: string;\n}\n\nexport interface CountryCheapest {\n country: string;\n /** Networks tied at the lowest rate. Empty when no choice could be made. */\n networks: CandidateNetwork[];\n /** How many distinct networks were in the running. */\n candidateCount: number;\n warnings: NetworkPlanWarning[];\n}\n\nconst isoOf = (r: TariffRuleLike): string =>\n String(r.operator?.countryIso2 ?? \"\").trim().toUpperCase();\n\nconst networkIdOf = (r: TariffRuleLike): number | undefined => {\n const n = r.operator?.networkId ?? r.networkid;\n return typeof n === \"number\" && Number.isFinite(n) ? n : undefined;\n};\n\n/** Normalise and de-duplicate ISO2 codes; drops anything that is not two letters. */\nexport function normaliseCountries(countries: readonly string[]): string[] {\n const out = new Set<string>();\n for (const c of countries) {\n const iso = String(c ?? \"\").trim().toUpperCase();\n if (/^[A-Z]{2}$/.test(iso)) out.add(iso);\n }\n return [...out].sort();\n}\n\n/**\n * Is this tariff rule really in force at `now`?\n *\n * `active: false` and `hidden: true` are excluded. A missing flag counts as in\n * force, because OCS omits fields it does not use. The start/end window is\n * checked too: the captured OCS fixture shows expired rules that still exist.\n * Unparseable dates are ignored rather than trusted either way.\n */\nexport function isRuleAvailable(rule: TariffRuleLike, now: Date): boolean {\n if (rule.active === false) return false;\n if (rule.hidden === true) return false;\n const t = now.getTime();\n if (rule.startdate) {\n const s = Date.parse(rule.startdate);\n if (Number.isFinite(s) && s > t) return false;\n }\n if (rule.enddate) {\n const e = Date.parse(rule.enddate);\n if (Number.isFinite(e) && e <= t) return false;\n }\n return true;\n}\n\n/**\n * Cheapest available network(s) for one country, from tariff rules.\n * Pure. Never throws. On any doubt it returns no networks plus a warning.\n */\nexport function cheapestAvailableForCountry(\n rules: readonly TariffRuleLike[] | null | undefined,\n country: string,\n now: Date,\n): CountryCheapest {\n const iso = String(country ?? \"\").trim().toUpperCase();\n if (!/^[A-Z]{2}$/.test(iso)) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n { code: \"NO_COUNTRY\", message: `\"${country}\" is not an ISO-3166 alpha-2 country code.` },\n ],\n };\n }\n if (!rules || rules.length === 0) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n {\n code: \"NO_TARIFF_DATA\",\n country: iso,\n message: `No tariff data was available, so ${iso} stays open to all networks. It was not narrowed to the cheapest.`,\n },\n ],\n };\n }\n\n const best = new Map<number, CandidateNetwork>();\n const currencies = new Set<string>();\n for (const r of rules) {\n if (isoOf(r) !== iso) continue;\n if (!isRuleAvailable(r, now)) continue;\n const rate = r.datarate;\n if (typeof rate !== \"number\" || !Number.isFinite(rate) || rate <= 0) continue;\n const id = networkIdOf(r);\n if (id === undefined) continue;\n const currency = String(\n r.currency?.currencycode ?? (r.currencyid !== undefined ? `id:${r.currencyid}` : \"?\"),\n );\n currencies.add(currency);\n const cur = best.get(id);\n if (!cur || rate < cur.datarate) {\n best.set(id, {\n networkId: id,\n operatorName: String(r.operator?.operatorName ?? `network ${id}`),\n datarate: rate,\n currency,\n });\n }\n }\n\n if (best.size === 0) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n {\n code: \"NO_AVAILABLE_NETWORK\",\n country: iso,\n message: `No network with a usable data rate was found for ${iso}, so it stays open to all networks. It was not narrowed to the cheapest.`,\n },\n ],\n };\n }\n if (currencies.size > 1) {\n return {\n country: iso,\n networks: [],\n candidateCount: best.size,\n warnings: [\n {\n code: \"MIXED_CURRENCY\",\n country: iso,\n message: `Rates for ${iso} are in more than one currency (${[...currencies].sort().join(\", \")}), so they cannot be compared. It stays open to all networks.`,\n },\n ],\n };\n }\n\n const min = Math.min(...[...best.values()].map((n) => n.datarate));\n const networks = [...best.values()]\n .filter((n) => n.datarate === min)\n .sort((a, b) => a.networkId - b.networkId);\n const warnings: NetworkPlanWarning[] = [];\n if (best.size === 1) {\n warnings.push({\n code: \"SINGLE_NETWORK\",\n country: iso,\n message: `Only one network is available for ${iso}, so \"cheapest\" and \"all\" are the same there.`,\n });\n }\n return { country: iso, networks, candidateCount: best.size, warnings };\n}\n\n/** Why a choice is unusable, or null when it is fine. */\nexport function validateNetworkChoice(choice: unknown): string | null {\n if (!choice || typeof choice !== \"object\") return \"A network choice must be an object.\";\n const c = choice as Record<string, unknown>;\n if (!(NETWORK_DEFAULT_MODES as readonly unknown[]).includes(c.mode)) {\n return `mode must be one of: ${NETWORK_DEFAULT_MODES.join(\", \")}.`;\n }\n if (c.mode === \"custom\") {\n const id = c.steeringListId;\n if (typeof id !== \"number\" || !Number.isInteger(id) || id <= 0) {\n return \"mode custom needs steeringListId, a positive integer from list_steering_lists.\";\n }\n }\n return null;\n}\n\nexport interface ResolveInput {\n /** Countries the SIM is expected to be used in. ISO2, any case. */\n countries: readonly string[];\n /** Tariff rules from get_tariff / getCustomerTariff. Null when unavailable. */\n tariffRules: readonly TariffRuleLike[] | null | undefined;\n simOverride?: NetworkChoice | null;\n packageOverride?: NetworkChoice | null;\n orgDefault?: NetworkChoice | null;\n now: Date;\n}\n\nexport type NetworkOutcome = \"restrict\" | \"allow_all\" | \"use_list\";\n\nexport interface NetworkPlan {\n source: NetworkChoiceSource;\n mode: NetworkDefaultMode;\n outcome: NetworkOutcome;\n /** Set when outcome is `use_list`. */\n steeringListId?: number;\n /** Union of the cheapest networks over all countries. Set when outcome is `restrict`. */\n networks: CandidateNetwork[];\n perCountry: CountryCheapest[];\n /** True when cheapest_available was asked for but at least one country could not be narrowed. */\n fellBackToAll: boolean;\n warnings: NetworkPlanWarning[];\n /** What the comparison is based on. Null when no comparison was made. */\n priceBasis: \"wholesale_datarate_per_mb\" | null;\n}\n\n/** First layer that carries a valid choice; an invalid one is reported, then skipped. */\nfunction pickChoice(\n input: ResolveInput,\n): { choice: NetworkChoice; source: NetworkChoiceSource; skipped: NetworkPlanWarning[] } {\n const layers: Array<[NetworkChoiceSource, NetworkChoice | null | undefined]> = [\n [\"sim_override\", input.simOverride],\n [\"package_override\", input.packageOverride],\n [\"org_default\", input.orgDefault],\n ];\n const skipped: NetworkPlanWarning[] = [];\n for (const [source, choice] of layers) {\n if (choice === null || choice === undefined) continue;\n const problem = validateNetworkChoice(choice);\n if (problem) {\n skipped.push({\n code: \"INVALID_CHOICE\",\n message: `The ${source.replace(\"_\", \" \")} is invalid and was ignored: ${problem}`,\n });\n continue;\n }\n return { choice, source, skipped };\n }\n return { choice: { mode: SYSTEM_NETWORK_DEFAULT }, source: \"system_default\", skipped };\n}\n\n/**\n * Decide what a SIM may use. See the module doc for precedence and fallbacks.\n *\n * `custom` and `all` never look at the tariff. Only `cheapest_available` does.\n */\nexport function resolveNetworkPlan(input: ResolveInput): NetworkPlan {\n const { choice, source, skipped } = pickChoice(input);\n const countries = normaliseCountries(input.countries);\n const warnings: NetworkPlanWarning[] = [...skipped];\n\n if (choice.mode === \"custom\") {\n return {\n source,\n mode: \"custom\",\n outcome: \"use_list\",\n steeringListId: choice.steeringListId as number, // validated by pickChoice\n networks: [],\n perCountry: [],\n fellBackToAll: false,\n warnings,\n priceBasis: null,\n };\n }\n if (choice.mode === \"all\") {\n return {\n source,\n mode: \"all\",\n outcome: \"allow_all\",\n networks: [],\n perCountry: [],\n fellBackToAll: false,\n warnings,\n priceBasis: null,\n };\n }\n\n // cheapest_available\n if (countries.length === 0) {\n warnings.push({\n code: \"NO_COUNTRY\",\n message: \"No valid country was given, so nothing could be narrowed. The SIM stays open to all networks.\",\n });\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"allow_all\",\n networks: [],\n perCountry: [],\n fellBackToAll: true,\n warnings,\n priceBasis: null,\n };\n }\n\n const perCountry = countries.map((c) => cheapestAvailableForCountry(input.tariffRules, c, input.now));\n for (const pc of perCountry) warnings.push(...pc.warnings);\n\n const byId = new Map<number, CandidateNetwork>();\n for (const pc of perCountry) for (const n of pc.networks) byId.set(n.networkId, n);\n const networks = [...byId.values()].sort((a, b) => a.networkId - b.networkId);\n const unresolved = perCountry.some((pc) => pc.networks.length === 0);\n\n if (networks.length === 0) {\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"allow_all\",\n networks: [],\n perCountry,\n fellBackToAll: true,\n warnings,\n priceBasis: null,\n };\n }\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"restrict\",\n networks,\n perCountry,\n fellBackToAll: unresolved,\n warnings,\n priceBasis: \"wholesale_datarate_per_mb\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Turning a plan into an assignment. Creating a steering list has NO OCS REST\n// method (it runs through a minutes-long browser agent), so the applier can\n// only assign a list that already exists. It never invents one silently.\n// ---------------------------------------------------------------------------\n\nexport interface SteeringListSummary {\n id: number;\n name: string;\n /** OCS network ids on the list; empty when the catalogue does not expose them. */\n networkIds: number[];\n}\n\n/** Read steering lists from the raw OCS/REST shape without trusting field names. */\nexport function steeringListsFromRaw(raw: unknown): SteeringListSummary[] {\n const arr = Array.isArray(raw)\n ? raw\n : raw && typeof raw === \"object\"\n ? ((raw as Record<string, unknown>).data ??\n (raw as Record<string, unknown>).steeringLists ??\n (raw as Record<string, unknown>).list ??\n [])\n : [];\n if (!Array.isArray(arr)) return [];\n const out: SteeringListSummary[] = [];\n for (const item of arr) {\n if (!item || typeof item !== \"object\") continue;\n const o = item as Record<string, unknown>;\n const id = Number(o.id ?? o.steeringListId ?? o.steeringlistid);\n if (!Number.isInteger(id) || id <= 0) continue;\n const nets = o.networks ?? o.networkList ?? o.operators ?? [];\n const networkIds = Array.isArray(nets)\n ? nets\n .map((n) => {\n if (typeof n === \"number\") return n;\n if (n && typeof n === \"object\") {\n const r = n as Record<string, unknown>;\n return Number(r.networkId ?? r.networkid ?? r.id);\n }\n return NaN;\n })\n .filter((n) => Number.isInteger(n))\n : [];\n out.push({ id, name: String(o.name ?? o.steeringListName ?? `list ${id}`), networkIds });\n }\n return out;\n}\n\nexport type AssignmentAction =\n | { action: \"assign\"; steeringListId: number; reason: string }\n | { action: \"needs_list\"; networks: CandidateNetwork[]; suggestedName: string; reason: string }\n | { action: \"leave_open\"; reason: string };\n\n/**\n * What to do with a plan, given the lists that exist. Pure.\n *\n * `restrict` needs a list whose network set equals the plan's networks exactly.\n * A superset would let the phone roam onto networks the plan excluded, and a\n * subset would cut coverage, so neither counts as a match.\n */\nexport function planAssignment(\n plan: NetworkPlan,\n lists: readonly SteeringListSummary[],\n): AssignmentAction {\n if (plan.outcome === \"allow_all\") {\n return {\n action: \"leave_open\",\n reason: plan.fellBackToAll\n ? \"Fell back to all networks; see warnings. No steering list is set.\"\n : \"The choice is all networks. No steering list is set.\",\n };\n }\n if (plan.outcome === \"use_list\") {\n const id = plan.steeringListId as number;\n const known = lists.some((l) => l.id === id);\n return known\n ? { action: \"assign\", steeringListId: id, reason: \"Chosen custom list exists.\" }\n : {\n action: \"leave_open\",\n reason: `Steering list ${id} was not found in this reseller's lists, so nothing is assigned.`,\n };\n }\n const want = new Set(plan.networks.map((n) => n.networkId));\n const match = lists.find(\n (l) => l.networkIds.length === want.size && l.networkIds.every((id) => want.has(id)),\n );\n if (match) {\n return {\n action: \"assign\",\n steeringListId: match.id,\n reason: `List \"${match.name}\" holds exactly the cheapest networks.`,\n };\n }\n const countries = plan.perCountry.filter((p) => p.networks.length > 0).map((p) => p.country);\n return {\n action: \"needs_list\",\n networks: plan.networks,\n suggestedName: `cheapest-${countries.join(\"-\").toLowerCase()}`,\n reason:\n \"No existing list holds exactly these networks. Creating one has no REST method; it needs the browser agent or the OCS dashboard.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// One report shape for every surface (REST, MCP worker, MCP stdio, CLI, Console).\n// Each surface only FETCHES raw inputs; this function decides and serialises.\n// ---------------------------------------------------------------------------\n\n/** OCS answers `{ listTariffRule: { rule: [...] } }`; the client may or may not unwrap it. */\nexport function tariffRulesFromRaw(raw: unknown): TariffRuleLike[] | null {\n if (Array.isArray(raw)) return raw as TariffRuleLike[];\n if (raw && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n for (const key of [\"rule\", \"listTariffRule\", \"rules\", \"data\"]) {\n const v = obj[key];\n if (Array.isArray(v)) return v as TariffRuleLike[];\n if (v && typeof v === \"object\") {\n const nested = (v as Record<string, unknown>).rule;\n if (Array.isArray(nested)) return nested as TariffRuleLike[];\n }\n }\n }\n return null;\n}\n\n/** Stored org setting: one KV record per org, deliberately NOT inside the auth record. */\nexport interface StoredNetworkDefault extends NetworkChoice {\n updatedAt: string;\n updatedBy?: string;\n}\n\nexport const NETWORK_DEFAULT_KEY_PREFIX = \"netdefault:\";\n\n/** KV key for an org, or `clerk_<user>` for a solo operator. Same partition the auth record uses. */\nexport function networkDefaultKey(partition: string): string {\n return `${NETWORK_DEFAULT_KEY_PREFIX}${partition}`;\n}\n\n/** Parse a stored record. Anything invalid reads as \"not set\", never as a partial choice. */\nexport function parseStoredNetworkDefault(raw: unknown): StoredNetworkDefault | null {\n if (!raw || typeof raw !== \"object\") return null;\n const r = raw as Record<string, unknown>;\n if (validateNetworkChoice(r) !== null) return null;\n const choice: StoredNetworkDefault = {\n mode: r.mode as NetworkDefaultMode,\n updatedAt: typeof r.updatedAt === \"string\" ? r.updatedAt : \"\",\n };\n if (r.mode === \"custom\") choice.steeringListId = r.steeringListId as number;\n if (typeof r.updatedBy === \"string\") choice.updatedBy = r.updatedBy;\n return choice;\n}\n\nexport interface NetworkPlanReportInput {\n countries: readonly string[];\n tariffRaw: unknown;\n steeringListsRaw: unknown;\n /** Set by the surface when reading the tariff failed. Turns into a FETCH_FAILED warning. */\n tariffError?: string | null;\n /** Set by the surface when reading the steering lists failed. */\n steeringListsError?: string | null;\n orgDefault?: NetworkChoice | null;\n packageOverride?: NetworkChoice | null;\n simOverride?: NetworkChoice | null;\n now: Date;\n}\n\nexport interface NetworkPlanReport {\n plan: NetworkPlan;\n assignment: AssignmentAction;\n /** Always false: this report only plans. Applying is a separate, confirmed write. */\n applied: false;\n /** One line a human can read without knowing the field names. */\n summary: string;\n}\n\nconst listNetworks = (ns: readonly CandidateNetwork[]): string =>\n ns.map((n) => `${n.operatorName} (${n.datarate} ${n.currency}/MB)`).join(\", \");\n\nexport function buildNetworkPlanReport(input: NetworkPlanReportInput): NetworkPlanReport {\n const plan = resolveNetworkPlan({\n countries: input.countries,\n tariffRules: input.tariffError ? null : tariffRulesFromRaw(input.tariffRaw),\n orgDefault: input.orgDefault ?? null,\n packageOverride: input.packageOverride ?? null,\n simOverride: input.simOverride ?? null,\n now: input.now,\n });\n\n // A failed read is reported even when the chosen mode never needed it, so a\n // broken upstream cannot hide behind a mode that happened not to look.\n if (input.tariffError && plan.mode === \"cheapest_available\") {\n plan.warnings.push({\n code: \"FETCH_FAILED\",\n message: `The tariff could not be read (${input.tariffError}).`,\n });\n }\n if (input.steeringListsError) {\n plan.warnings.push({\n code: \"FETCH_FAILED\",\n message: `The steering lists could not be read (${input.steeringListsError}).`,\n });\n }\n\n let assignment: AssignmentAction;\n if (input.steeringListsError && (plan.outcome === \"restrict\" || plan.outcome === \"use_list\")) {\n assignment = {\n action: \"leave_open\",\n reason: \"The steering lists could not be read, so no list can be matched or checked. Nothing is assigned.\",\n };\n } else {\n assignment = planAssignment(plan, steeringListsFromRaw(input.steeringListsRaw));\n }\n\n let summary: string;\n if (plan.outcome === \"restrict\") {\n summary = `Cheapest available: ${listNetworks(plan.networks)}.`;\n if (plan.fellBackToAll) summary += \" Some countries could not be narrowed and stay open to all networks.\";\n } else if (plan.outcome === \"use_list\") {\n summary = `Custom steering list ${plan.steeringListId}.`;\n } else if (plan.fellBackToAll) {\n summary = \"FELL BACK TO ALL NETWORKS: the cheapest network could not be determined. See warnings.\";\n } else {\n summary = \"All networks are allowed (chosen).\";\n }\n return { plan, assignment, applied: false, summary };\n}\n\n/**\n * KV partition for the stored org default. Mirrors how the auth record is keyed\n * (`org:<orgId>`, or `user:<userId>` for a solo operator) so every surface that\n * knows who the caller is reaches the same record.\n */\nexport function networkDefaultPartition(identity: { orgId?: string | undefined; sub: string }): string {\n if (identity.orgId) return identity.orgId;\n return `user:${identity.sub.replace(/^clerk_/, \"\")}`;\n}\n","/**\n * Network default: the one service every surface calls.\n *\n * REST (apps/api), MCP worker (apps/mcp-server), MCP stdio (apps/mcp-stdio) and\n * the Console proxy all run THESE functions. Each surface supplies only an I/O\n * adapter (how to call OCS, how to reach the stored org default), so the decision,\n * the validation, the warnings and the response shape cannot differ between them.\n * tests/network-default-contract.test.ts pins that no surface re-implements it.\n *\n * Nothing here writes to OCS. `setNetworkDefault` writes ONE stored record through\n * the adapter and touches no SIM.\n */\n\nimport {\n buildNetworkPlanReport,\n networkDefaultKey,\n NETWORK_DEFAULT_MODES,\n parseStoredNetworkDefault,\n steeringListsFromRaw,\n SYSTEM_NETWORK_DEFAULT,\n validateNetworkChoice,\n type NetworkChoice,\n type NetworkPlanReport,\n type NetworkPlanWarning,\n type StoredNetworkDefault,\n} from \"./network-default.js\";\n\n/** What a surface must provide. Throwing from `callOcs` means the OCS call failed. */\nexport interface NetworkDefaultIo {\n /** Read-only OCS calls only: getCustomerTariff, listSteeringList. */\n callOcs(method: \"getCustomerTariff\" | \"listSteeringList\", resellerId: number): Promise<unknown>;\n resolveResellerId(): Promise<number>;\n /**\n * The stored org default, raw. `undefined` means this surface has NO store\n * (local stdio): that is reported, never treated as \"nothing stored\".\n */\n readStored(): Promise<unknown> | undefined;\n /** Persist the record. Absent on surfaces without a store. */\n writeStored?(record: StoredNetworkDefault): Promise<void>;\n}\n\nexport type ServiceResult<T> = { ok: true; data: T } | { ok: false; status: 400 | 501 | 502; error: string; code: string };\n\nconst fail = (status: 400 | 501 | 502, code: string, error: string): { ok: false; status: 400 | 501 | 502; error: string; code: string } => ({\n ok: false,\n status,\n error,\n code,\n});\n\nconst msg = (e: unknown): string => (e instanceof Error ? e.message : String(e));\n\n/** `undefined`/`null` mean \"not given\". Anything else must be a valid choice. */\nexport function parseOptionalChoice(value: unknown, label: string): { choice: NetworkChoice | null } | { error: string } {\n if (value === undefined || value === null) return { choice: null };\n const problem = validateNetworkChoice(value);\n if (problem) return { error: `${label}: ${problem}` };\n const v = value as NetworkChoice;\n return { choice: v.mode === \"custom\" ? { mode: \"custom\", steeringListId: v.steeringListId as number } : { mode: v.mode } };\n}\n\nconst NO_STORE_WARNING: NetworkPlanWarning = {\n code: \"INVALID_CHOICE\",\n message:\n \"This surface has no stored org default (local stdio mode), so it could not be read. Only the system default and the overrides you pass apply here.\",\n};\n\nexport interface NetworkDefaultView {\n modes: typeof NETWORK_DEFAULT_MODES;\n systemDefault: typeof SYSTEM_NETWORK_DEFAULT;\n orgDefault: StoredNetworkDefault | null;\n effectiveMode: string;\n warnings: NetworkPlanWarning[];\n appliesTo: \"new_assignments_only\";\n}\n\nexport async function getNetworkDefault(io: NetworkDefaultIo): Promise<ServiceResult<NetworkDefaultView>> {\n const pending = io.readStored();\n const warnings: NetworkPlanWarning[] = [];\n let stored: StoredNetworkDefault | null = null;\n if (pending === undefined) {\n warnings.push(NO_STORE_WARNING);\n } else {\n const raw = await Promise.resolve(pending).catch(() => null);\n stored = parseStoredNetworkDefault(raw);\n if (raw !== null && raw !== undefined && stored === null) {\n warnings.push({\n code: \"INVALID_CHOICE\",\n message: \"The stored org default is unreadable and was ignored; the system default applies.\",\n });\n }\n }\n return {\n ok: true,\n data: {\n modes: NETWORK_DEFAULT_MODES,\n systemDefault: SYSTEM_NETWORK_DEFAULT,\n orgDefault: stored,\n effectiveMode: stored?.mode ?? SYSTEM_NETWORK_DEFAULT,\n warnings,\n appliesTo: \"new_assignments_only\",\n },\n };\n}\n\nexport async function setNetworkDefault(\n io: NetworkDefaultIo,\n body: unknown,\n actor: string,\n now: Date,\n): Promise<ServiceResult<{ orgDefault: StoredNetworkDefault; appliesTo: \"new_assignments_only\" }>> {\n const parsed = parseOptionalChoice(body, \"body\");\n if (\"error\" in parsed) return fail(400, \"VALIDATION_ERROR\", parsed.error);\n if (parsed.choice === null) return fail(400, \"VALIDATION_ERROR\", \"body: A network choice must be an object.\");\n const choice = parsed.choice;\n if (!io.writeStored) {\n return fail(501, \"NO_STORE\", \"This surface has no store for the org default. Set it through the REST API, the Console or the hosted MCP server.\");\n }\n\n // A custom list must exist for this reseller; refuse an id OCS does not know.\n if (choice.mode === \"custom\") {\n let listsRaw: unknown;\n try {\n listsRaw = await io.callOcs(\"listSteeringList\", await io.resolveResellerId());\n } catch (e) {\n return fail(502, \"OCS_ERROR\", msg(e));\n }\n if (!steeringListsFromRaw(listsRaw).some((l) => l.id === choice.steeringListId)) {\n return fail(400, \"VALIDATION_ERROR\", `Steering list ${choice.steeringListId} was not found. Pick one from list_steering_lists.`);\n }\n }\n\n const record: StoredNetworkDefault = { ...choice, updatedAt: now.toISOString(), updatedBy: actor };\n await io.writeStored(record);\n return { ok: true, data: { orgDefault: record, appliesTo: \"new_assignments_only\" } };\n}\n\nexport interface PlanRequest {\n countries?: unknown;\n simOverride?: unknown;\n packageOverride?: unknown;\n resellerId?: unknown;\n}\n\nexport async function planNetworkDefault(\n io: NetworkDefaultIo,\n req: PlanRequest | null | undefined,\n now: Date,\n): Promise<ServiceResult<NetworkPlanReport>> {\n if (!req || !Array.isArray(req.countries) || req.countries.length === 0) {\n return fail(400, \"VALIDATION_ERROR\", \"countries required: a non-empty array of ISO-3166 alpha-2 codes\");\n }\n const sim = parseOptionalChoice(req.simOverride, \"simOverride\");\n if (\"error\" in sim) return fail(400, \"VALIDATION_ERROR\", sim.error);\n const pkg = parseOptionalChoice(req.packageOverride, \"packageOverride\");\n if (\"error\" in pkg) return fail(400, \"VALIDATION_ERROR\", pkg.error);\n\n let resellerId: number;\n if (req.resellerId !== undefined && req.resellerId !== null) {\n resellerId = Number(req.resellerId);\n if (!Number.isInteger(resellerId) || resellerId <= 0) {\n return fail(400, \"VALIDATION_ERROR\", \"resellerId must be a positive integer\");\n }\n } else {\n try {\n resellerId = await io.resolveResellerId();\n } catch (e) {\n return fail(502, \"OCS_ERROR\", msg(e));\n }\n }\n\n const settle = async (method: \"getCustomerTariff\" | \"listSteeringList\") => {\n try {\n return { data: await io.callOcs(method, resellerId), error: null as string | null };\n } catch (e) {\n return { data: undefined, error: msg(e) };\n }\n };\n const [tariff, lists, view] = await Promise.all([settle(\"getCustomerTariff\"), settle(\"listSteeringList\"), getNetworkDefault(io)]);\n const orgDefault = view.ok && view.data.orgDefault\n ? view.data.orgDefault.mode === \"custom\"\n ? ({ mode: \"custom\", steeringListId: view.data.orgDefault.steeringListId as number } as NetworkChoice)\n : ({ mode: view.data.orgDefault.mode } as NetworkChoice)\n : null;\n\n const report = buildNetworkPlanReport({\n countries: req.countries.map((x) => String(x)),\n tariffRaw: tariff.data,\n tariffError: tariff.error,\n steeringListsRaw: lists.data,\n steeringListsError: lists.error,\n orgDefault,\n packageOverride: pkg.choice,\n simOverride: sim.choice,\n now,\n });\n // A surface without a store must say so on the plan too, not only on `get`.\n if (view.ok) report.plan.warnings.push(...view.data.warnings);\n return { ok: true, data: report };\n}\n\nexport { networkDefaultKey };\n","import { ocsArray } from \"./ocs-array.js\";\n\n/** Narrow server-side catalog grants. Account isolation never implies catalog authority. */\nexport type CatalogCapability = \"location_zone.create\" | \"package_template.create\";\nexport interface CatalogGrantDb {\n prepare(sql: string): { bind(...values: unknown[]): { first<T>(): Promise<T | null> } };\n}\nexport interface CatalogPrincipal {\n orgId?: string | undefined;\n resellerId?: unknown;\n managed?: boolean | undefined;\n db?: CatalogGrantDb | undefined;\n}\nexport interface CatalogDecision {\n orgId: string;\n resellerId: number | undefined;\n capability: CatalogCapability;\n allowed: boolean;\n}\n/** Resolve freshly against the credential actually sent, never caller request fields. */\nexport interface CatalogAuthorization {\n principal(token: string): Promise<CatalogPrincipal>;\n audit?(decision: CatalogDecision): void;\n}\nexport function catalogCapability(method: string): CatalogCapability | undefined {\n if (method === \"createLocationZone\") return \"location_zone.create\";\n if (method === \"createPrepaidPackageTemplate\") return \"package_template.create\";\n return undefined;\n}\nfunction positiveId(value: unknown): number | undefined {\n if (typeof value !== \"number\" && !(typeof value === \"string\" && /^[1-9]\\d*$/.test(value))) return undefined;\n const n = Number(value);\n return Number.isSafeInteger(n) && n > 0 ? n : undefined;\n}\n/**\n * Canonical `listNetworkProfile` rows.\n *\n * The shared client removes the method key, so callers see\n * `{ networkProfile: [...] }`. A raw envelope still nests that object under\n * `listNetworkProfile`. A bare array is the older recorded shape. Anything\n * else, including a row that is not an object, is unrecognised.\n */\nexport function networkProfileRows(raw: unknown): Record<string, unknown>[] | undefined {\n const body = raw !== null && typeof raw === \"object\" && !Array.isArray(raw) && \"listNetworkProfile\" in raw\n ? (raw as { listNetworkProfile?: unknown }).listNetworkProfile : raw;\n const recognised = Array.isArray(body)\n || (body !== null && typeof body === \"object\" && !Array.isArray(body)\n && (\"networkProfile\" in body || \"listNetworkProfile\" in body));\n if (!recognised) return undefined;\n const rows = ocsArray<unknown>(body, [\"networkProfile\"]);\n if (rows.some((row) => row === null || typeof row !== \"object\" || Array.isArray(row))) return undefined;\n return rows as Record<string, unknown>[];\n}\n/** Mutates only template resellerid after exact ownership proof; all errors deny. */\nexport async function authorizeCatalogCreate(\n authorization: CatalogAuthorization | undefined,\n token: string,\n method: string,\n params: Record<string, unknown> | null,\n listProfiles: (resellerId: number) => Promise<unknown>,\n): Promise<boolean> {\n const capability = catalogCapability(method);\n if (!capability || !authorization || !params) return false;\n let principal: CatalogPrincipal = {};\n let resellerId: number | undefined;\n let allowed = false;\n try {\n principal = await authorization.principal(token);\n resellerId = positiveId(principal.resellerId);\n if (principal.managed || !principal.orgId || !principal.db || resellerId === undefined) return false;\n const grant = await principal.db.prepare(\n \"SELECT 1 AS ok FROM org_ocs_catalog_grants WHERE org_id = ? AND reseller_id = ? AND capability = ?\",\n ).bind(principal.orgId, resellerId, capability).first<{ ok: number }>();\n if (grant?.ok !== 1) return false;\n if (capability === \"package_template.create\") {\n // No alternate identity spelling can override the lower-case OCS wire field.\n for (const key of [\"resellerid\", \"resellerId\", \"reseller_id\"]) {\n if (params[key] !== undefined && positiveId(params[key]) !== resellerId) return false;\n }\n params.resellerid = resellerId;\n allowed = true;\n } else {\n const profileId = positiveId(params.networkProfileId);\n if (profileId === undefined) return false;\n // Membership is the authenticated reseller-scoped list. A row resellerId\n // is optional; when the provider supplies one, it must match the grant.\n const rows = networkProfileRows(await listProfiles(resellerId));\n if (!rows) return false;\n const matches = rows.filter((row) => positiveId(row.id) === profileId);\n allowed = matches.length === 1 && matches.every((row) =>\n row.resellerId === undefined || positiveId(row.resellerId) === resellerId);\n }\n return allowed;\n } catch {\n return false;\n } finally {\n // Existing best-effort audit: no tokens, bodies, profile names or TADIGs.\n try { authorization.audit?.({ orgId: principal.orgId ?? \"\", resellerId, capability, allowed }); } catch { /* non-fatal telemetry */ }\n }\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.\n */\n\nexport type LogoMark = {\n svg: string;\n filename: string;\n source: \"svg\";\n pngBase64?: string;\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, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\");\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\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 return { svg, filename: \"logo.svg\", source: \"svg\" };\n}\n"],"mappings":"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A,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EAC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","names":["probe","readFile","mix","join","join","join","join","join","mkdtemp","rm","tmpdir","mkdtemp","join","tmpdir","rm","pct","str","pct","sleep","WINDOW_MS","pct","BACKOFF_BASE_MS","run"]}
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/ocs-params.ts","../../../packages/ocs-client/src/schemas.ts","../../../packages/ocs-client/src/timeout-budgets.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-spec/src/mcc-iso.ts","../../../packages/ocs-client/src/list-subscriber.ts","../../../packages/ocs-client/src/default-account.ts","../../../packages/ocs-client/src/router-catalog.ts","../../../packages/ocs-client/src/ocs-array.ts","../../../packages/ocs-client/src/ocs-summary.ts","../../../packages/ocs-client/src/analysis.ts","../../../packages/ocs-client/src/usage.ts","../../../packages/ocs-client/src/intelligence.ts","../../../packages/ocs-client/src/diagnose-shapes.ts","../../../packages/ocs-client/src/gy-country-history.ts","../../../packages/ocs-client/src/network-default.ts","../../../packages/ocs-client/src/network-default-service.ts","../../../packages/ocs-client/src/catalog-create-authorization.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\n/**\n * Stamp the Worker name into wrangler config.\n *\n * JSONC, so this is a targeted textual rewrite of the `name` field rather than\n * a parse/serialise round-trip that would strip the file's comments - the same\n * approach as `setCustomDomain`. Returns false when there is no wrangler config\n * or no name field to rewrite.\n */\nexport async function setWorkerName(storefront: string, name: string): Promise<boolean> {\n const path = join(storefront, \"wrangler.jsonc\");\n if (!(await exists(path))) return false;\n const body = await readFileText(path);\n const re = /^\\s*\"?name\"?\\s*:\\s*\"[^\"]*\"/m;\n if (!re.test(body)) return false;\n const patched = body.replace(re, (m) => m.replace(/\"[^\"]*\"\\s*$/, JSON.stringify(name)));\n if (patched === body) return false;\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 /** One sentence for <meta name=\"description\"> and social cards. */\n description: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n supportWhatsapp: string;\n /** Display name on transactional mail. */\n emailFromName: string;\n /** Mailbox transactional mail is sent from. The address is this plus `domain`. */\n emailFromMailbox: string;\n /** Public origin of the storefront, no trailing slash. */\n appUrl: 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 description: \"Data that works in 200+ countries. Instant QR activation. Human support.\",\n domain: \"carrier.llc\",\n supportEmail: \"support@carrier.llc\",\n supportUrl: \"https://carrier.llc/help\",\n supportWhatsapp: \"+17864604829\",\n emailFromName: \"Carrier\",\n emailFromMailbox: \"hello\",\n appUrl: \"https://carrier.llc\",\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, the accent 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 description: string;\n domain: string;\n supportEmail: string;\n supportUrl: string;\n emailFromName: string;\n emailFromMailbox: string;\n appUrl: 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 `# Optional. With a publishable key the payment form is embedded in this`,\n `# site's own checkout page; without one the customer is sent to Stripe's`,\n `# hosted page and back. Both take money — the embedded one loses fewer`,\n `# people on the way.`,\n `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=`,\n `# Optional. Scopes the catalog to one hosted shop. Empty keeps the unscoped template list.`,\n `NEXT_PUBLIC_STOREFRONT_SLUG=`,\n `# Optional. Sends the eSIM QR code by email once an order is ready.`,\n `# \\`carrier site email\\` authenticates you at Resend in the browser and`,\n `# fills this in. Without it the storefront still sells and activates; the`,\n `# activation page just says email delivery is not set up.`,\n `# The from-address is composed from brand.config.ts (emailFromMailbox +`,\n `# domain), so verify that domain at resend.com/domains first.`,\n `RESEND_API_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 // Optional. Present: checkout is embedded in the storefront's own page.\n // Absent: checkout redirects to Stripe's hosted page, which still works.\n NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: \"public\",\n // Optional. Present: the scaffold catalog is scoped to one hosted shop.\n // Absent: the catalog client calls the unscoped template list.\n NEXT_PUBLIC_STOREFRONT_SLUG: \"public\",\n CARRIER_API_KEY: \"secret\",\n CLERK_SECRET_KEY: \"secret\",\n STRIPE_SECRET_KEY: \"secret\",\n // Optional. Present: the storefront emails the QR code to whoever paid.\n // Absent: the activation page says email delivery is not set up, and every\n // install route it shows on screen still works.\n RESEND_API_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 setWorkerName,\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 * The worker name the template ships in wrangler.jsonc. Deploying under it by\n * accident can clobber an unrelated Worker that already exists on the account,\n * so deploys that would land here are refused unless the operator opts in\n * (CAR-313).\n */\nexport const SCAFFOLD_DEFAULT_WORKER_NAME = \"storefront\";\n\n/**\n * Resolve the name a deploy runs under.\n *\n * Precedence: an explicit `--name` flag wins over everything - it is the\n * operator's deliberate choice. Then the project's own wrangler config, then\n * the slugged brand name. The old code let the wrangler config silently swallow\n * an explicit `--name`, so `carrier site deploy --name foo` deployed under the\n * scaffold default instead (CAR-313).\n */\nexport function resolveDeployName(opts: {\n explicitName?: string;\n configuredName?: string | undefined;\n brandName: string;\n}): string {\n if (opts.explicitName) return slug(opts.explicitName);\n return opts.configuredName ?? slug(opts.brandName);\n}\n\n/**\n * Whether this deploy must be refused unless `--force` is passed: the resolved\n * name is the scaffold default. The guard keys on the resolved name only — an\n * explicit `--name` is no exemption, because `--name storefront`, or a name\n * that slugifies to it (e.g. `--name \"!!!\"`), would otherwise land on the\n * default Worker without the operator acknowledging the overwrite risk.\n */\nexport function needsForceForDefaultName(opts: {\n projectName: string;\n force?: boolean;\n}): boolean {\n return !opts.force && opts.projectName === SCAFFOLD_DEFAULT_WORKER_NAME;\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 /** Explicit `--name` from the CLI: the operator's deliberate choice, wins over wrangler config. */\n explicitName?: string;\n /** Acknowledge the scaffold-default-name risk and deploy anyway. */\n force?: 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: resolveDeployName({ explicitName: opts.explicitName, brandName: 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: resolveDeployName({ explicitName: opts.explicitName, brandName: 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 // An explicit --name is the operator's deliberate choice and wins over the\n // wrangler config (CAR-313). Otherwise the project's own config names the\n // deployment: falling back to the brand slug would deploy a second Worker and\n // leave the real one untouched.\n const projectName = resolveDeployName({\n explicitName: opts.explicitName,\n configuredName: await configuredProjectName(chosen.id, target),\n brandName: brand.name,\n });\n\n // Deploying under the scaffold default \"storefront\" by accident can clobber\n // an unrelated Worker that already exists on the account. Any deploy that\n // resolves to the default — explicit --name included — needs --force.\n if (needsForceForDefaultName({ projectName, force: opts.force })) {\n return {\n ok: false,\n projectName,\n statuses,\n target: chosen.id,\n reason:\n 'Refusing to deploy under the scaffold default worker name \"storefront\" - ' +\n \"a different Worker may already use it on this account. Pass --name <worker> \" +\n 'to pick a name, or --force to deploy as \"storefront\" anyway.',\n };\n }\n\n // Persist an explicit name into wrangler config so later deploys without the\n // flag stay on the same Worker (JSONC-safe rewrite, same as setCustomDomain).\n if (opts.explicitName && chosen.id === \"cloudflare\") {\n await setWorkerName(target, projectName);\n }\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, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\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.13.37\",\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 \"type\": \"module\",\n \"carrierCompatibility\": {\n \"claudeOAuthOwnerVersion\": \"2.1.277\",\n \"claudeOAuthOwnerVersions\": [\"2.1.277\", \"2.1.296\"]\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\",\n \"plugin\",\n \"shell\",\n \"native\",\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-concurrency=1 --test test/*.test.js && pnpm run test:oauth-repair\",\n \"test:oauth-repair\": \"node --experimental-transform-types --test-concurrency=1 --test test/oauth-repair.test.mjs test/claude-owner.test.mjs test/current-owner.test.mjs\",\n \"test:oauth-shell\": \"node scripts/check-oauth-shell.mjs\",\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.18.1\",\n \"@modelcontextprotocol/ext-apps\": \"^1.7.5\",\n \"@modelcontextprotocol/sdk\": \"^1.31.0\",\n \"@noble/hashes\": \"^2.3.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\": \"^5.20260903.1\",\n \"@types/node\": \"^26.1.2\",\n \"@typescript-eslint/eslint-plugin\": \"^8.59.4\",\n \"@typescript-eslint/parser\": \"^8.69.0\",\n \"eslint\": \"^10.9.1\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"@carrierllc/sdk\": \"workspace:*\",\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 * 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\n/**\n * `modifySubscriberStatus` body.\n *\n * `subscriber` is a common.SubscriberId object. A bare ICCID string is rejected\n * live with OCS error 2 \"Invalid JSON content\" / \"Cannot construct instance of\n * SubscriberId from String value\". The same object is what affectPackageToSubscriber\n * already sends (`{ subscriber: { iccid } }`).\n */\nexport function modifySubscriberStatusParams(\n iccid: string,\n newStatus: string,\n): { subscriber: { iccid: string }; newStatus: string } {\n return { subscriber: { iccid }, newStatus };\n}\n\n/**\n * Nest a bare ICCID that a caller still put on `subscriber`.\n *\n * An object is already a SubscriberId and is left alone. Numbers and other\n * shapes pass through so this can sit on the serialisation chokepoint without\n * rewriting unrelated methods.\n */\nexport function nestSubscriberIdParam<T>(params: T): T {\n if (!params || typeof params !== \"object\" || Array.isArray(params)) return params;\n const record = params as Record<string, unknown>;\n if (typeof record[\"subscriber\"] !== \"string\") return params;\n return { ...record, subscriber: { iccid: record[\"subscriber\"] } } as T;\n}\n\n/**\n * Confirmation for `modifySubscriberPrepaidPackageStatus`.\n *\n * The write succeeds and OCS returns no package body (the method key is null\n * or absent). Callers were promised an updated package record. This object is\n * that record: `updated` means the status envelope was success, and `status`\n * is `ACTIVE` or `INACTIVE` for the `active` flag that was sent.\n */\nexport interface PackageStatusConfirmation {\n updated: true;\n subsPrepaidPackageId?: number;\n active?: boolean;\n status?: \"ACTIVE\" | \"INACTIVE\";\n}\n\nexport function packageStatusConfirmation(params: unknown): PackageStatusConfirmation {\n const confirmation: PackageStatusConfirmation = { updated: true };\n if (!params || typeof params !== \"object\" || Array.isArray(params)) return confirmation;\n const record = params as Record<string, unknown>;\n const id = record[\"subsPrepaidPackageId\"];\n if (typeof id === \"number\" && Number.isFinite(id)) confirmation.subsPrepaidPackageId = id;\n if (typeof record[\"active\"] === \"boolean\") {\n confirmation.active = record[\"active\"];\n confirmation.status = record[\"active\"] ? \"ACTIVE\" : \"INACTIVE\";\n }\n return confirmation;\n}\n\n/**\n * Successful OCS calls sometimes carry no method body. `null` then looks like\n * a failure. Two methods have a documented stand-in:\n *\n * - `listSteeringList` → `[]` (an empty catalog, not an error)\n * - `modifySubscriberPrepaidPackageStatus` → {@link packageStatusConfirmation}\n *\n * Any other method, including `getSimProviderStatus`, keeps `null`.\n */\nexport function normaliseAbsentOcsPayload<T>(method: string, payload: T, params: unknown): T {\n if (payload !== null && payload !== undefined) return payload;\n if (method === \"listSteeringList\") return [] as T;\n if (method === \"modifySubscriberPrepaidPackageStatus\") {\n return packageStatusConfirmation(params) as T;\n }\n return payload;\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\n/**\n * `subscriberId` on modifySubscriberMobilePlan / modifySubscriberVoipPlan is a\n * SubscriberId OBJECT, not a bare id.\n *\n * The name reads like a number and that is exactly the trap. Probed live\n * 2026-08-28:\n *\n * {\"modifySubscriberMobilePlan\":{\"subscriberId\":{\"__carrierParamProbe__\":1}}}\n * -> Unrecognized field ... (class srv.ocs.backend.lib.json.api.common.SubscriberId),\n * 6 known properties: \"multiImsi\", \"iccid\", \"imsi\", \"activationCode\",\n * \"msisdn\", \"subscriberId\"\n *\n * That is the same common.SubscriberId class cleanSubscriberAllPackages and\n * listSubscriberPrepaidPackages take, so an ICCID identifies the subscriber\n * directly and NO id lookup is needed. Bridge4IP documents the same shape\n * (docs/research/bridge4ip-ocs-api/endpoints-full.md §3.10/3.11:\n * `{\"subscriberId\": {\"subscriberId\": 1000}, \"mobilePlanId\": 10}`) and calls\n * the doubled key a quirk of their own docs.\n *\n * Writing the ICCID as a BARE `subscriberId` would be the dangerous fix: the\n * inner `subscriberId` is an internal numeric id in a different id space, so a\n * 20-digit ICCID landing there could address another record entirely. Nesting\n * keeps the identifier in the field that means ICCID.\n */\nexport function subscriberPlanParams(\n iccid: string,\n): { subscriberId: { iccid: string } } {\n return { subscriberId: { iccid } };\n}\n\n/**\n * Limit fields OCS accepts inside `modifySubscriberPrepaidPackageLimits`'s\n * `newLimits` object, mapped from the names callers already use.\n *\n * Probed live 2026-08-28 by pointing the probe sentinel at the SUB-object —\n * the same Jackson trick works one level down:\n *\n * {\"modifySubscriberPrepaidPackageLimits\":{\"newLimits\":{\"__carrierParamProbe__\":1}}}\n * -> Unrecognized field ... (class Request$SubscriberPrepaidPackageLimits),\n * 5 known properties: \"mtcSecond\", \"dataByte\", \"mocSecond\", \"moSms\", \"mtSms\"\n *\n * Matches Bridge4IP's documented example exactly (endpoints-full.md §3.4).\n * The tool's own description advertises `dataLimit`, so the aliases are the\n * published contract and are mapped rather than broken.\n */\nexport const OCS_PREPAID_PACKAGE_LIMIT_FIELDS = [\n \"dataByte\",\n \"mocSecond\",\n \"mtcSecond\",\n \"moSms\",\n \"mtSms\",\n] as const;\n\nconst LIMIT_ALIASES: Record<string, (typeof OCS_PREPAID_PACKAGE_LIMIT_FIELDS)[number]> = {\n dataByte: \"dataByte\",\n dataBytes: \"dataByte\",\n dataLimit: \"dataByte\",\n databyte: \"dataByte\",\n mocSecond: \"mocSecond\",\n mocSeconds: \"mocSecond\",\n voiceOutLimit: \"mocSecond\",\n mtcSecond: \"mtcSecond\",\n mtcSeconds: \"mtcSecond\",\n voiceInLimit: \"mtcSecond\",\n moSms: \"moSms\",\n moSmsNumber: \"moSms\",\n smsOutLimit: \"moSms\",\n mtSms: \"mtSms\",\n mtSmsNumber: \"mtSms\",\n smsInLimit: \"mtSms\",\n};\n\n/**\n * Build the `newLimits` sub-object.\n *\n * Unknown keys THROW rather than being dropped. Silently discarding a limit a\n * caller asked for would report success on a package whose allowance never\n * changed, which is worse than the rejection this replaces: today OCS refuses\n * the whole call, so nothing is written and nothing is claimed.\n *\n * `null` is preserved — OCS reads it as \"leave this limit unchanged\".\n */\nexport function prepaidPackageLimits(\n limits: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const unknown: string[] = [];\n for (const [key, value] of Object.entries(limits)) {\n const target = LIMIT_ALIASES[key];\n if (!target) {\n unknown.push(key);\n continue;\n }\n out[target] = value;\n }\n if (unknown.length > 0) {\n throw new Error(\n `Unknown package limit field(s): ${unknown.join(\", \")}. ` +\n `OCS accepts only ${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(\", \")} ` +\n `inside newLimits (aliases: ${Object.keys(LIMIT_ALIASES).join(\", \")}).`,\n );\n }\n if (Object.keys(out).length === 0) {\n throw new Error(\n `No package limits to change. Provide at least one of ` +\n `${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(\", \")}.`,\n );\n }\n return out;\n}\n\n/**\n * Coerce a caller's date into the `java.time.LocalDateTime` OCS parses.\n *\n * `newExpirationDate` is a LocalDateTime and a date-only string is REJECTED —\n * verified live 2026-08-28 against packageId -1 (a row that cannot exist, so\n * the parse verdict is the only thing observed):\n *\n * \"2026-06-01\" -> code 2, DateTimeParseException at index 10\n * \"2026-06-01T23:59:59\" -> code 6, \"Subscriber package not found\"\n *\n * Code 6 means the payload parsed and business logic ran, which is the pass.\n *\n * `bareDate` decides what a date-only string means, and the two answers are\n * not interchangeable. An EXPIRY widened to 00:00:00 silently cuts the\n * caller's last day off the package, so expiries end the day. An ACTIVATION\n * pushed to 23:59:59 silently costs the caller the whole first day, so\n * activations start it. Defaulting to \"end\" keeps every existing expiry call\n * site behaving exactly as before.\n */\nexport function ocsLocalDateTime(value: string, bareDate: \"start\" | \"end\" = \"end\"): string {\n const trimmed = value.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n return bareDate === \"start\" ? `${trimmed}T00:00:00` : `${trimmed}T23:59:59`;\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$/.test(trimmed)) return `${trimmed}:00`;\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(trimmed)) {\n // Strip a trailing zone designator: LocalDateTime has no offset and OCS\n // throws on one.\n return trimmed.replace(/(?:Z|[+-]\\d{2}:?\\d{2})$/, \"\").slice(0, 19);\n }\n throw new Error(\n `\"${value}\" is not a date OCS can read. Use YYYY-MM-DD or ` +\n `YYYY-MM-DDTHH:mm:ss — this field is a java.time.LocalDateTime and a ` +\n `zone offset is rejected.`,\n );\n}\n\n/**\n * The active-period window OCS currently holds for one subscriber package.\n *\n * Read from a `listSubscriberPrepaidPackages` response, whose rows carry the\n * live window as `tsactivationutc` / `tsexpirationutc` — already in the\n * `YYYY-MM-DDTHH:mm:ss` LocalDateTime form the write side wants, so the values\n * round-trip without reformatting.\n *\n * Verified live 2026-08-28 on ICCID 8948010000020241170, package 208596510:\n * tsactivationutc \"2026-08-27T21:15:38\", tsexpirationutc \"2027-08-27T21:15:38\".\n *\n * Returns undefined for a package that is not on this subscriber, so a caller\n * refuses instead of writing a window onto a row it never read.\n */\nexport function activePeriodFromPackages(\n listResponse: unknown,\n packageId: number,\n): { activation?: string; expiration?: string } | undefined {\n const root = (listResponse ?? {}) as Record<string, unknown>;\n const payload = (root.listSubscriberPrepaidPackages ?? root.data ?? root) as\n | Record<string, unknown>\n | undefined;\n const packages = Array.isArray(payload?.packages) ? payload.packages : [];\n for (const entry of packages) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;\n if (Number(id) !== packageId) continue;\n const activation = rec.tsactivationutc ?? rec.tsActivationUtc;\n const expiration = rec.tsexpirationutc ?? rec.tsExpirationUtc;\n const window: { activation?: string; expiration?: string } = {};\n if (typeof activation === \"string\" && activation) window.activation = activation;\n if (typeof expiration === \"string\" && expiration) window.expiration = expiration;\n return window;\n }\n return undefined;\n}\n\n/** Sent when a caller supplies no comment. OCS requires the field to be present. */\nexport const OCS_ACTIVE_PERIOD_DEFAULT_COMMENT = \"Active period changed via Carrier\";\n\nexport interface ActivePeriodChange {\n /** New start of the window. Bare dates mean 00:00:00. */\n startDate?: string;\n /** New end of the window. Bare dates mean 23:59:59. */\n endDate?: string;\n /** Audit note stored on the OCS record. */\n comment?: string;\n /** The window OCS holds today, used to fill whichever bound the caller omitted. */\n current?: { activation?: string; expiration?: string };\n}\n\n/**\n * Build `modifySubscriberPrepaidPackageActivePeriod` params.\n *\n * OCS accepts exactly four field names here and REQUIRES all four. Every other\n * shape fails the whole request, which is how this route 502'd on every call\n * it ever served. Verified live 2026-08-28 against ocs.esimvault.cloud:\n *\n * {subscriber, packageId, startDate, endDate} -> code 2, Unrecognized field \"subscriber\"\n * {packageId, startDate, endDate} -> code 2, Unrecognized field \"startDate\"\n * {packageId, newActivationDateUtc} -> code 2, Missing 'newExpirationDateUtc'\n * {packageId, newExpirationDateUtc} -> code 2, Missing 'newActivationDateUtc'\n * {packageId, both dates} -> code 2, Missing 'comment'\n * {packageId, both dates, comment} -> code 6, Subscriber package not found\n *\n * Code 6 is the pass: the payload parsed and OCS ran the lookup. Note the\n * third and fourth lines — \"both dates are mandatory\" is why a partial change\n * cannot simply omit a bound. The caller must read the live window and pass it\n * as `current`, or this throws rather than inventing one.\n *\n * `packageId` is the SUBSCRIBER package id (`subscriberprepaidpackageid`), not\n * the template id. Verified live on ICCID 8948010000020241170: template\n * 2895241 answers code 6 \"Subscriber package not found\", subscriber package\n * 208596510 answers code 14 \"Can only modify package configured to be\n * activated at first use\" — a business rule, so that id reached the row.\n */\nexport function packageActivePeriodParams(\n packageId: number,\n change: ActivePeriodChange,\n): {\n packageId: number;\n newActivationDateUtc: string;\n newExpirationDateUtc: string;\n comment: string;\n} {\n if (change.startDate === undefined && change.endDate === undefined) {\n throw new Error(\"Provide startDate and/or endDate to change a package's active period.\");\n }\n const activation =\n change.startDate !== undefined\n ? ocsLocalDateTime(change.startDate, \"start\")\n : change.current?.activation;\n const expiration =\n change.endDate !== undefined\n ? ocsLocalDateTime(change.endDate, \"end\")\n : change.current?.expiration;\n\n for (const [field, value, omitted] of [\n [\"newActivationDateUtc\", activation, \"startDate\"],\n [\"newExpirationDateUtc\", expiration, \"endDate\"],\n ] as const) {\n if (value !== undefined) continue;\n throw new Error(\n `OCS requires both bounds on an active-period change and rejects the ` +\n `whole request with \"Missing '${field}'\" when one is absent. ` +\n `Supply ${omitted}, or read the package's current window first ` +\n `(activePeriodFromPackages) and pass it as \\`current\\`.`,\n );\n }\n\n return {\n packageId,\n // Already LocalDateTime-shaped when it came from `current`; normalising\n // again is a no-op and keeps a hand-passed value honest.\n newActivationDateUtc: ocsLocalDateTime(activation as string, \"start\"),\n newExpirationDateUtc: ocsLocalDateTime(expiration as string, \"end\"),\n comment: change.comment?.trim() || OCS_ACTIVE_PERIOD_DEFAULT_COMMENT,\n };\n}\n\n\n/**\n * Resolve a subscriber package id to the recurring subscription that produced\n * it, for `stopResumeSubsRecurringPackage`.\n *\n * These are three DIFFERENT id spaces, which is why a rename was never the\n * fix. One live package on ICCID 8948010000020241105 (2026-08-28):\n *\n * subscriberprepaidpackageid 204532344 <- what list_subscriber_packages returns\n * recurringPackage 11683296 <- what OCS wants as `recurringId`\n * templateId 3848928 <- stops EVERY subscriber on the template\n *\n * Passing the package id as `recurringId` would address an unrelated recurring\n * subscription; passing it as `templateId` would stop the template fleet-wide.\n *\n * Returns undefined when the package is not on the subscriber or carries no\n * `recurringPackage` (a one-off package has none), so the caller can refuse\n * instead of guessing.\n */\nexport function recurringIdForPackage(\n listResponse: unknown,\n packageId: number,\n): number | undefined {\n const root = (listResponse ?? {}) as Record<string, unknown>;\n const payload = (root.listSubscriberPrepaidPackages ?? root.data ?? root) as\n | Record<string, unknown>\n | undefined;\n const packages = Array.isArray(payload?.packages) ? payload.packages : [];\n for (const entry of packages) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;\n if (Number(id) !== packageId) continue;\n const recurring = rec.recurringPackage ?? rec.recurringId;\n const value = Number(recurring);\n return Number.isFinite(value) && value > 0 ? value : undefined;\n }\n return undefined;\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\nconst NETWORK_EVENT_GROUPS = [\"mapAnswer\", \"s6aAnswer\", \"gyAnswer\", \"camelAnswer\", \"mocAnswer\", \"mtcAnswer\"] as const;\n\nfunction hasDirectNetworkEvents(rec: Record<string, unknown>): boolean {\n if (Array.isArray(rec.events)) return true;\n return NETWORK_EVENT_GROUPS.some((key) => {\n const group = rec[key];\n return Boolean(\n group && typeof group === \"object\" && Array.isArray((group as { events?: unknown[] }).events),\n );\n });\n}\n\n/**\n * The API returns `{ data: <OCS body> }`. The OCS body nests protocol groups\n * under `subscriberNetworkEventsOverPeriod`. Either wrapper, or both, has to\n * come off before the groups are readable.\n */\nfunction networkEventPayload(json: unknown): unknown {\n let current: unknown = json;\n for (let depth = 0; depth < 4; depth += 1) {\n if (!current || typeof current !== \"object\" || Array.isArray(current)) return current;\n const rec = current as Record<string, unknown>;\n if (hasDirectNetworkEvents(rec)) return current;\n const nested = rec.subscriberNetworkEventsOverPeriod;\n if (nested && typeof nested === \"object\") {\n current = nested;\n continue;\n }\n if (\"data\" in rec) {\n current = rec.data;\n continue;\n }\n return current;\n }\n return current;\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 out: unknown[] = [];\n for (const key of NETWORK_EVENT_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 rows = flattenNetworkEventGroups(networkEventPayload(json));\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 for (const row of extractEsimStatusByAccount(json)) {\n acc.active += row.active;\n acc.suspended += row.suspended;\n acc.inventory += row.inventory;\n acc.other += row.other;\n }\n acc.total = acc.active + acc.suspended + acc.inventory + acc.other;\n return acc;\n}\n\n/** eSIM status counts for one OCS account. `id`/`name` are absent when OCS omitted them. */\nexport interface AccountEsimStatusCounts extends EsimStatusCounts {\n id?: number;\n name?: string;\n}\n\nfunction accountRows(json: unknown): unknown[] {\n const root = (json ?? {}) as Record<string, unknown>;\n const payload = (root.data ?? root.esimStatusPerAccount ?? root) as Record<string, unknown> | unknown[];\n if (Array.isArray(payload)) return payload;\n return Array.isArray((payload as Record<string, unknown>).account)\n ? ((payload as Record<string, unknown>).account as unknown[])\n : [];\n}\n\n/**\n * The same unwrap as `extractEsimStatusCounts`, kept per account.\n *\n * The fleet panel used to show `inventory: 0` for every account because only\n * the fleet-wide total was extracted, so a per-account availability figure\n * could not match OCS (CAR-280). One walker feeds both views, so the total is\n * by construction the sum of the rows.\n */\nexport function extractEsimStatusByAccount(json: unknown): AccountEsimStatusCounts[] {\n const out: AccountEsimStatusCounts[] = [];\n for (const account of accountRows(json)) {\n const rec = (account ?? {}) as Record<string, unknown>;\n const row: AccountEsimStatusCounts = emptyEsimStatusCounts();\n const id = Number(rec.id ?? rec.accountId);\n if (rec.id !== undefined || rec.accountId !== undefined) {\n if (Number.isFinite(id)) row.id = id;\n }\n if (typeof rec.name === \"string\") row.name = rec.name;\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 status of statuses) addEsimStatusRow(row, (status ?? {}) as Record<string, unknown>);\n }\n } else {\n row.active += Number(rec.active ?? 0);\n row.suspended += Number(rec.suspended ?? 0);\n row.inventory += Number(rec.inventory ?? rec.notActivated ?? rec.free ?? 0);\n row.other += Number(rec.other ?? rec.terminated ?? 0);\n }\n row.total = row.active + row.suspended + row.inventory + row.other;\n out.push(row);\n }\n return out;\n}\n\n/** One bundle whose displayed availability disagrees with OCS. */\nexport interface InventoryDeviation {\n key: string;\n displayed: number;\n actual: number | null;\n}\n\n/**\n * Carrier-versus-OCS inventory diff (CAR-280 / C2).\n *\n * Pass/fail is \"zero deviation\": every key in `displayed` must equal the OCS\n * count. A key OCS does not report is a deviation (`actual: null`), never\n * silently skipped, because a shop showing stock OCS does not have is exactly\n * the failure the test exists to catch.\n */\nexport function diffInventory(\n displayed: Record<string, number>,\n actual: Record<string, number>,\n): { ok: boolean; sampled: number; deviations: InventoryDeviation[] } {\n const keys = Object.keys(displayed);\n const deviations: InventoryDeviation[] = [];\n for (const key of keys) {\n const shown = displayed[key]!;\n const real = Object.prototype.hasOwnProperty.call(actual, key) ? actual[key]! : null;\n if (real === null || real !== shown) deviations.push({ key, displayed: shown, actual: real });\n }\n return { ok: keys.length > 0 && deviations.length === 0, sampled: keys.length, deviations };\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 * `hlrSetBitrate` `limit` values. Bridge4IP documents these strings\n * (KB_32 … KB_102400, plus UNLIMITED), and the MCP tool already sends them.\n * A bare Kbps number is not on that model.\n *\n * Wider than {@link OCS_THROTTLE_KBPS}: package-template throttling omits\n * 32, 64, and 2048, and has no unlimited token.\n */\nexport const OCS_HLR_BITRATE_LIMITS = [\n \"KB_32\",\n \"KB_64\",\n \"KB_128\",\n \"KB_256\",\n \"KB_384\",\n \"KB_512\",\n \"KB_1024\",\n \"KB_2048\",\n \"KB_3072\",\n \"KB_5120\",\n \"KB_7680\",\n \"KB_10240\",\n \"KB_20480\",\n \"KB_51200\",\n \"KB_102400\",\n \"UNLIMITED\",\n] as const;\n\nconst OCS_HLR_BITRATE_SET = new Set<string>(OCS_HLR_BITRATE_LIMITS);\n\n/**\n * Map a console Kbps figure or an already-correct token onto `hlrSetBitrate`'s\n * `limit`. Anything else is refused so a free-typed number cannot reach OCS.\n */\nexport function ocsHlrBitrate(raw: unknown): string | { error: string } {\n const token = hlrBitrateToken(raw);\n if (token !== undefined && OCS_HLR_BITRATE_SET.has(token)) return token;\n return {\n error:\n \"limit must be an HLR bitrate in Kbps (32, 64, 128, 256, 384, 512, 1024, 2048, 3072, 5120, 7680, 10240, 20480, 51200, 102400) or UNLIMITED\",\n };\n}\n\n/**\n * MCP `hlr_set_bitrate` takes bits per second. OCS `limit` is the KB_ enum.\n * 256000 bps is KB_256. 0 is that tool's \"remove the cap\" and means UNLIMITED.\n * A figure that is not an exact enum step is refused.\n */\nexport function ocsHlrBitrateFromBps(raw: number): string | { error: string } {\n if (raw === 0) return \"UNLIMITED\";\n if (!Number.isInteger(raw) || raw < 0 || raw % 1000 !== 0) {\n return {\n error:\n \"bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED\",\n };\n }\n const mapped = ocsHlrBitrate(raw / 1000);\n if (typeof mapped === \"string\") return mapped;\n return {\n error:\n \"bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED\",\n };\n}\n\nfunction hlrBitrateToken(raw: unknown): string | undefined {\n if (typeof raw === \"number\" && Number.isInteger(raw) && raw > 0) return `KB_${raw}`;\n if (typeof raw !== \"string\") return undefined;\n const key = raw.trim().toUpperCase().replace(/\\s+/g, \"_\");\n if (key === \"UNLIMITED\") return \"UNLIMITED\";\n if (key.startsWith(\"KB_\")) return key;\n if (/^\\d+$/.test(key)) return `KB_${key}`;\n return undefined;\n}\n\n/**\n * `changeSimStatus` `newStatus` is Affected or Free.\n *\n * Console sends Enable|Disable. MCP sends ENABLED|DISABLED. Both mean the\n * same pair. DELETED is not on this method.\n */\nexport function ocsSimStatus(raw: string): \"Affected\" | \"Free\" | { error: string } {\n const key = raw.trim().toLowerCase();\n if (key === \"enable\" || key === \"enabled\" || key === \"affected\") return \"Affected\";\n if (key === \"disable\" || key === \"disabled\" || key === \"free\") return \"Free\";\n if (key === \"deleted\") {\n return {\n error: \"DELETED is not a changeSimStatus value. newStatus must be Enable or Disable\",\n };\n }\n return { error: \"newStatus must be Enable or Disable\" };\n}\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/**\n * OCS `moveSubscriberRangeToAccount` wire shape.\n *\n * Live OCS (probed 2026-08-24) accepts exactly five fields: destAccount,\n * srcAccountId, rangeType, rangeStart, rangeEnd. REST used to forward\n * toAccountId / iccidStart / iccidEnd, which OCS rejects with code 2\n * (\"Unrecognized field toAccountId\") — eSIMMY hit that 502 on\n * POST /v1/accounts/{accountId}/move-subscribers. MCP was rebuilt onto\n * this shape on 2026-08-28; REST and Console aliases map through here\n * so a later surface cannot invent a sixth name.\n */\nexport type MoveSubscriberRangeToAccountParams = {\n srcAccountId: number;\n destAccount: number;\n rangeType: \"IMSI\" | \"ICCID\";\n rangeStart: string;\n rangeEnd: string;\n};\n\nconst RANGE_TYPES = new Set([\"IMSI\", \"ICCID\"]);\n\nfunction positiveAccountId(value: unknown): number | undefined {\n if (typeof value === \"number\") {\n return Number.isInteger(value) && value > 0 ? value : undefined;\n }\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n return Number.isInteger(n) && n > 0 ? n : undefined;\n }\n return undefined;\n}\n\nfunction rangeToken(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const t = value.trim();\n return t.length > 0 ? t : undefined;\n}\n\n/**\n * Map any published alias onto the five OCS fields.\n *\n * REST historically took `{ fromAccountId, iccidStart, iccidEnd }` with the\n * destination in the path. MCP takes `{ srcAccountId, destAccount, rangeType,\n * rangeStart, rangeEnd }`. eSIMMY sent destAccount in the body and the source\n * in the path. All three must produce the same OCS body, and none of\n * toAccountId / iccidStart / iccidEnd / fromAccountId may leave this function.\n */\nexport function moveSubscriberRangeToAccountParams(input: {\n srcAccountId?: unknown;\n destAccount?: unknown;\n fromAccountId?: unknown;\n toAccountId?: unknown;\n rangeType?: unknown;\n rangeStart?: unknown;\n rangeEnd?: unknown;\n iccidStart?: unknown;\n iccidEnd?: unknown;\n pathAccountId?: unknown;\n}): MoveSubscriberRangeToAccountParams | { error: string } {\n const destFromBody = positiveAccountId(input.destAccount ?? input.toAccountId);\n const srcFromBody = positiveAccountId(input.srcAccountId ?? input.fromAccountId);\n const pathId = positiveAccountId(input.pathAccountId);\n\n const destAccount = destFromBody ?? pathId;\n const srcAccountId =\n srcFromBody ?? (destFromBody !== undefined && pathId !== undefined && pathId !== destFromBody ? pathId : undefined);\n\n if (srcAccountId === undefined || destAccount === undefined) {\n return { error: \"srcAccountId and destAccount required\" };\n }\n if (srcAccountId === destAccount) {\n return { error: \"srcAccountId and destAccount must differ\" };\n }\n\n const rangeStart = rangeToken(input.rangeStart ?? input.iccidStart);\n const rangeEnd = rangeToken(input.rangeEnd ?? input.iccidEnd);\n if (!rangeStart || !rangeEnd) {\n return { error: \"rangeStart and rangeEnd required\" };\n }\n if (rangeStart > rangeEnd) {\n return { error: \"rangeStart must not be higher than rangeEnd\" };\n }\n\n const rawType =\n typeof input.rangeType === \"string\" && input.rangeType.trim() !== \"\"\n ? input.rangeType.trim().toUpperCase()\n : input.iccidStart !== undefined || input.iccidEnd !== undefined\n ? \"ICCID\"\n : undefined;\n if (!rawType || !RANGE_TYPES.has(rawType)) {\n return { error: \"rangeType must be IMSI or ICCID\" };\n }\n\n return {\n srcAccountId,\n destAccount,\n rangeType: rawType as \"IMSI\" | \"ICCID\",\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * `createLocationZone` accepts locationZoneName, tadigList, and networkProfileId.\n *\n * `name` and `tadigs` are aliases. ISO countries and MCC-MNC operators are not\n * TADIG codes, and this function does not invent a TADIG from either of them.\n * An explicit canonical key wins over its alias.\n */\nexport type LocationZoneCreateParams = {\n locationZoneName: string;\n tadigList: string[];\n networkProfileId: number;\n};\n\nconst LOCATION_ZONE_KEYS = new Set([\"locationZoneName\", \"name\", \"tadigList\", \"tadigs\", \"networkProfileId\"]);\n\nfunction zoneRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"zone must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction zoneName(raw: unknown): string | { error: string } {\n if (raw === undefined || raw === null || raw === \"\") return { error: \"locationZoneName is required\" };\n if (typeof raw !== \"string\") return { error: \"locationZoneName must be a string\" };\n const name = raw.trim();\n if (!name) return { error: \"locationZoneName is required\" };\n return name;\n}\n\nfunction zoneTadigs(raw: unknown): string[] | { error: string } {\n if (raw === undefined || raw === null || raw === \"\") return { error: \"tadigList is required\" };\n const parts = typeof raw === \"string\" ? raw.split(/[\\s,]+/) : Array.isArray(raw) ? raw : null;\n if (!parts) return { error: \"tadigList must be a list of TADIG codes\" };\n const list: string[] = [];\n for (const part of parts) {\n if (typeof part !== \"string\") return { error: \"tadigList must be a list of TADIG codes\" };\n const code = part.trim();\n if (code) list.push(code);\n }\n if (list.length === 0) return { error: \"tadigList is required\" };\n return list;\n}\n\nfunction zoneProfile(raw: unknown): number | undefined {\n if (typeof raw === \"number\" && Number.isInteger(raw) && raw > 0) return raw;\n if (typeof raw === \"string\" && /^\\d+$/.test(raw.trim())) {\n const n = Number(raw.trim());\n return n > 0 ? n : undefined;\n }\n return undefined;\n}\n\nexport function locationZoneCreateParams(\n input: Record<string, unknown> | string,\n): LocationZoneCreateParams | { error: string } {\n const parsed = zoneRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"countries\" in body || \"operators\" in body) {\n return {\n error:\n \"createLocationZone has no countries or operators field. Send tadigList (TADIG codes) and networkProfileId.\",\n };\n }\n for (const key of Object.keys(body)) {\n if (!LOCATION_ZONE_KEYS.has(key)) {\n return { error: \"createLocationZone accepts locationZoneName, tadigList, and networkProfileId\" };\n }\n }\n const name = zoneName(\"locationZoneName\" in body ? body.locationZoneName : body.name);\n if (typeof name !== \"string\") return name;\n const tadigList = zoneTadigs(\"tadigList\" in body ? body.tadigList : body.tadigs);\n if (!Array.isArray(tadigList)) return tadigList;\n const networkProfileId = zoneProfile(body.networkProfileId);\n if (networkProfileId === undefined) {\n return { error: \"networkProfileId must be a positive integer\" };\n }\n return { locationZoneName: name, tadigList, networkProfileId };\n}\n\n/**\n * `createPrepaidPackageTemplate` property names from the live probe.\n * Aliases are mapped onto these. `price` and `description` are not in the set.\n */\nexport const PACKAGE_TEMPLATE_CREATE_FIELDS = [\n \"cost\",\n \"databyte\",\n \"deleted\",\n \"destinationzoneid\",\n \"esimSponsor\",\n \"hotspotBlocked\",\n \"locationzoneid\",\n \"mocsecond\",\n \"mosmsnumber\",\n \"mtcsecond\",\n \"mtsmsnumber\",\n \"nbOccurrence\",\n \"pckSubPrePaidPackagesDataSlicesTmpl\",\n \"perioddays\",\n \"picture\",\n \"prepaidpackagetemplateid\",\n \"prepaidpackagetemplatename\",\n \"priority\",\n \"rdbDestinationZones\",\n \"rdbLocationZones\",\n \"recurring\",\n \"recurringPeriodicityFrequency\",\n \"recurringPeriodicityType\",\n \"reportUnitsPreviousPackage\",\n \"reseller\",\n \"resellerid\",\n \"sponsors\",\n \"throttleTillEnd\",\n \"throttlingActive\",\n \"throttlingErrorAction\",\n \"throttlingThreshold1Limit\",\n \"throttlingThreshold1Perc\",\n \"throttlingThreshold2Limit\",\n \"throttlingThreshold2Perc\",\n \"throttlingThreshold3Limit\",\n \"throttlingThreshold3Perc\",\n \"throttlingThreshold4Limit\",\n \"throttlingThreshold4Perc\",\n \"uiEndAvailibilityPeriod\",\n \"uiStartAvailablePeriod\",\n \"uiVisible\",\n \"userUiName\",\n] as const;\n\nconst PACKAGE_TEMPLATE_CREATE_FIELD_SET = new Set<string>(PACKAGE_TEMPLATE_CREATE_FIELDS);\n\n/** Caller spellings that OCS rejects until they are renamed. Canonical key wins. */\nexport const PACKAGE_TEMPLATE_CREATE_ALIASES: Record<string, string> = {\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n dataLimit: \"databyte\",\n dataBytes: \"databyte\",\n validityDays: \"perioddays\",\n periodDays: \"perioddays\",\n locationZoneId: \"locationzoneid\",\n destinationZoneId: \"destinationzoneid\",\n resellerId: \"resellerid\",\n};\n\nfunction templateRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"template must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction templateName(raw: unknown): string | { error: string } {\n if (typeof raw !== \"string\") return { error: \"prepaidpackagetemplatename must be a string\" };\n const name = raw.trim();\n if (!name) return { error: \"prepaidpackagetemplatename is required\" };\n return name;\n}\n\n/**\n * Map a create-template body onto the fields `createPrepaidPackageTemplate` accepts.\n *\n * Does not invent an allowance. A missing data field stays missing; the MCP\n * tool runs {@link normalizePackageTemplate} first when it wants the unlimited\n * ceiling. `price` and `description` are refused. An explicit OCS key wins\n * over its alias.\n */\nexport function packageTemplateCreateParams(\n input: Record<string, unknown> | string,\n): Record<string, unknown> | { error: string } {\n const parsed = templateRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"price\" in body) return { error: \"createPrepaidPackageTemplate has no price field\" };\n if (\"description\" in body) return { error: \"createPrepaidPackageTemplate has no description field\" };\n\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) out[key] = value;\n }\n for (const [key, value] of Object.entries(body)) {\n const target = PACKAGE_TEMPLATE_CREATE_ALIASES[key];\n if (!target) {\n if (!PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) {\n return { error: `createPrepaidPackageTemplate does not accept ${key}` };\n }\n continue;\n }\n if (target in out) continue;\n if (target === \"prepaidpackagetemplatename\") {\n const name = templateName(value);\n if (typeof name !== \"string\") return name;\n out[target] = name;\n continue;\n }\n out[target] = value;\n }\n if (\"prepaidpackagetemplatename\" in out) {\n const name = templateName(out[\"prepaidpackagetemplatename\"]);\n if (typeof name !== \"string\") return name;\n out[\"prepaidpackagetemplatename\"] = name;\n }\n return out;\n}\n\n/**\n * OCS createPrepaidPackageTemplate fails with code 2 \"Missing 'resellerid'\"\n * when the body has no reseller. The caller rarely knows it, so fill it from\n * the token owner's own reseller. An explicit `resellerid` always wins.\n */\nexport async function withTemplateResellerId(\n params: Record<string, unknown>,\n resolveId: () => Promise<number>,\n): Promise<Record<string, unknown>> {\n const existing = params[\"resellerid\"];\n if (typeof existing === \"number\" && Number.isFinite(existing)) return params;\n return { ...params, resellerid: await resolveId() };\n}\n\n/**\n * MCP `create_package_template` entry. Normalizes the sold-plan shortcuts\n * (`unlimited`, `throttleAfterMb`, `throttleKbps`) first, then keeps only the\n * fields `createPrepaidPackageTemplate` accepts. The REST route must call\n * {@link packageTemplateCreateParams} directly so a name-only body does not\n * grow an unlimited allowance.\n */\nexport function packageTemplateCreateFromToolJson(\n template: string,\n): Record<string, unknown> | { error: string } {\n let parsed: unknown;\n try {\n parsed = JSON.parse(template);\n } catch {\n return { error: \"template must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { error: \"template must be a JSON object\" };\n }\n let normalized: Record<string, unknown>;\n try {\n normalized = normalizePackageTemplate(parsed as Record<string, unknown>);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"template could not be normalized\";\n return { error: message };\n }\n return packageTemplateCreateParams(normalized);\n}\n\n/**\n * `modifyPPTCore` accepts every create field plus `changeSponsor`.\n * `modifyPPTRecurring` and `modifyPPTThrottling` accept the create set only.\n */\nexport type PackageTemplateModifyMethod =\n | \"modifyPPTCore\"\n | \"modifyPPTRecurring\"\n | \"modifyPPTThrottling\";\n\nconst PACKAGE_TEMPLATE_MODIFY_EXTRA: Record<PackageTemplateModifyMethod, readonly string[]> = {\n modifyPPTCore: [\"changeSponsor\"],\n modifyPPTRecurring: [],\n modifyPPTThrottling: [],\n};\n\n/** Path identity. The handler sets `templateId` after this function returns. */\nconst PACKAGE_TEMPLATE_MODIFY_IDENTITY = new Set([\"templateId\", \"prepaidpackagetemplateid\"]);\n\nexport const PACKAGE_TEMPLATE_MODIFY_ALIASES: Record<string, string> = {\n ...PACKAGE_TEMPLATE_CREATE_ALIASES,\n periodicity: \"recurringPeriodicityType\",\n occurrences: \"nbOccurrence\",\n firstThresholdPercent: \"throttlingThreshold1Perc\",\n firstThresholdLimitKbps: \"throttlingThreshold1Limit\",\n errorAction: \"throttlingErrorAction\",\n};\n\n/** Accepted OCS names for one modifyPPT* method, before aliases. */\nexport function packageTemplateModifyFieldNames(\n method: PackageTemplateModifyMethod,\n): readonly string[] {\n return [...PACKAGE_TEMPLATE_CREATE_FIELDS, ...PACKAGE_TEMPLATE_MODIFY_EXTRA[method]];\n}\n\nfunction changesRecord(\n input: Record<string, unknown> | string,\n): { ok: true; body: Record<string, unknown> } | { ok: false; error: string } {\n if (typeof input !== \"string\") {\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n return { ok: true, body: input };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(input);\n } catch {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { ok: false, error: \"changes must be a JSON object\" };\n }\n return { ok: true, body: parsed as Record<string, unknown> };\n}\n\nfunction periodicityValue(raw: unknown): number | { error: string } {\n if (typeof raw === \"number\" && OCS_PERIODICITY_VALUES.includes(raw as never)) return raw;\n if (typeof raw === \"string\") {\n const key = raw.trim().toLowerCase();\n if (key === \"daily\" || key === \"0\") return OCS_PERIODICITY.DAILY;\n if (key === \"weekly\" || key === \"1\") return OCS_PERIODICITY.WEEKLY;\n if (key === \"monthly\" || key === \"2\") return OCS_PERIODICITY.MONTHLY;\n }\n return { error: \"recurringPeriodicityType must be 0 (daily), 1 (weekly), or 2 (monthly)\" };\n}\n\nfunction throttlingErrorActionValue(raw: unknown): number | { error: string } {\n if (typeof raw === \"number\" && Number.isInteger(raw)) return raw;\n if (typeof raw === \"string\" && /^-?\\d+$/.test(raw.trim())) return Number(raw.trim());\n if (typeof raw === \"string\" && raw.trim().toLowerCase() === \"continue_unthrottled\") {\n return {\n error: \"throttlingErrorAction is an integer. continue_unthrottled is not a modifyPPT value\",\n };\n }\n return { error: \"throttlingErrorAction is an integer\" };\n}\n\nfunction applyTemplateField(\n out: Record<string, unknown>,\n target: string,\n value: unknown,\n): { error: string } | undefined {\n if (target === \"prepaidpackagetemplatename\") {\n const name = templateName(value);\n if (typeof name !== \"string\") return name;\n out[target] = name;\n return undefined;\n }\n if (target === \"recurringPeriodicityType\") {\n const period = periodicityValue(value);\n if (typeof period !== \"number\") return period;\n out[target] = period;\n return undefined;\n }\n if (target === \"throttlingErrorAction\") {\n const action = throttlingErrorActionValue(value);\n if (typeof action !== \"number\") return action;\n out[target] = action;\n return undefined;\n }\n out[target] = value;\n return undefined;\n}\n\n/**\n * Map a partial template update onto the fields the modifyPPT* method accepts.\n *\n * Does not invent an allowance. `templateId` and `prepaidpackagetemplateid`\n * are dropped so the caller can set the path id last. `price` and\n * `description` are refused. `periodicity` daily/weekly/monthly becomes\n * `recurringPeriodicityType` 0/1/2. `continue_unthrottled` is refused:\n * `throttlingErrorAction` is an integer. An explicit OCS key wins over its alias.\n */\nexport function packageTemplateModifyParams(\n input: Record<string, unknown> | string,\n method: PackageTemplateModifyMethod,\n): Record<string, unknown> | { error: string } {\n const parsed = changesRecord(input);\n if (!parsed.ok) return { error: parsed.error };\n const body = parsed.body;\n if (\"price\" in body) return { error: `${method} has no price field` };\n if (\"description\" in body) return { error: `${method} has no description field` };\n\n const accepted = new Set<string>([\n ...PACKAGE_TEMPLATE_CREATE_FIELDS,\n ...PACKAGE_TEMPLATE_MODIFY_EXTRA[method],\n ]);\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key) || !accepted.has(key)) continue;\n const applied = applyTemplateField(out, key, value);\n if (applied) return applied;\n }\n for (const [key, value] of Object.entries(body)) {\n if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key)) continue;\n const target = PACKAGE_TEMPLATE_MODIFY_ALIASES[key];\n if (!target) {\n if (!accepted.has(key)) return { error: `${method} does not accept ${key}` };\n continue;\n }\n if (target in out) continue;\n const applied = applyTemplateField(out, target, value);\n if (applied) return applied;\n }\n if (Object.keys(out).length === 0) {\n return { error: \"at least one template field required\" };\n }\n return out;\n}\n\n/**\n * MCP modify-template entry. Core runs {@link normalizePackageTemplateChanges}\n * first so a zero allowance the caller sent becomes the unlimited ceiling, and\n * a rename stays a rename. Recurring and throttling do not.\n */\nexport function packageTemplateModifyFromToolJson(\n changes: string,\n method: PackageTemplateModifyMethod,\n): Record<string, unknown> | { error: string } {\n let parsed: unknown;\n try {\n parsed = JSON.parse(changes);\n } catch {\n return { error: \"changes must be a JSON object\" };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { error: \"changes must be a JSON object\" };\n }\n let body = parsed as Record<string, unknown>;\n if (method === \"modifyPPTCore\") {\n try {\n body = normalizePackageTemplateChanges(body);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"changes could not be normalized\";\n return { error: message };\n }\n }\n return packageTemplateModifyParams(body, method);\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 * @carrier/ocs-client — Zod schemas for REST API request/response validation.\n *\n * Used by apps/api route definitions. Shared here so mcp-server can\n * reference the same shapes if needed.\n */\n\nimport { z } from \"zod\";\nimport { OCS_HLR_BITRATE_LIMITS } from \"./ocs-params.js\";\n\n// ---------------------------------------------------------------------------\n// OCS provider contract — known-valid v1 methods + response shape schemas.\n//\n// Ground truth captured via a live-stg smoke against eSIMVault OCS. These pin\n// the request/response contract so shape drift (the createAccount /\n// getResellerInfo / nested listResellerAccount bugs the smoke found) fails\n// loudly at the client boundary instead of being silently mis-read.\n//\n// Response schemas are deliberately LENIENT: objects `.passthrough()` so the\n// provider can add fields without breaking us; we only assert the KEY fields\n// and their exact names (the drift that actually bit us).\n// ---------------------------------------------------------------------------\n\n/**\n * The valid OCS v1 methods, verified against live-stg — the enumerated set\n * below is authoritative (\"the ONLY ones\"). This is the ONLY set the server\n * recognizes — notably there is NO `createAccount` or\n * `createSubscriber` (smoke returned OCS error 2 \"Unrecognized field\"). The\n * client guards against unknown methods using this set so phantom methods are\n * caught at dev time rather than silently round-tripping to a 200+error.\n */\nexport const OCS_V1_METHODS: ReadonlySet<string> = new Set([\n \"getResellerInfo\",\n \"listResellerAccount\",\n \"listPrepaidPackageTemplate\",\n \"listSubscriberPrepaidPackages\",\n \"listSubscriber\",\n \"getSingleSubscriber\",\n \"getSimProviderStatus\",\n \"getSubscriberLocation\",\n \"getSubscriberLocationByCellId\",\n \"esimStatusPerAccount\",\n \"subscriberUsageOverPeriod\",\n \"affectRecurringPackageToSubscriber\",\n \"deleteSubscriberPackage\",\n \"modifyAccountBalance\",\n \"modifySubscriberBalance\",\n \"modifySubscriberContactInfo\",\n \"modifySubscriberMobilePlan\",\n \"modifySubscriberSteeringList\",\n \"modifySubscriberVoipPlan\",\n \"modifySubscriberPrepaidPackageActivePeriod\",\n \"modifySubscriberPrepaidPackageLimits\",\n \"modifySubscriberPrepaidPackageStatus\",\n \"modifyPPTCore\",\n \"modifyPPTRecurring\",\n \"createPrepaidPackageTemplate\",\n \"createLocationZone\",\n \"changeNetworkProfileOfLocationZone\",\n \"hlrGetBitrate\",\n \"hlrSetBitrate\",\n \"listSponsor\",\n \"listDestinationListPrefix\",\n \"listDetailedLocationZone\",\n \"listLocationZoneElement\",\n \"listSubscriberVoipTariff\",\n \"listVoipTariffRule\",\n \"affectSubscriberFakePhoneNumber\",\n \"affectSubscriberRealPhoneNumber\",\n \"getCustomerTariff\",\n \"resetSubsGzCounter\",\n \"sendMtSms\",\n]);\n\n/**\n * The proven-phantom methods: `createAccount` and `createSubscriber`. A live-stg\n * smoke showed the OCS has NO account/subscriber creation method — it answers\n * HTTP 200 with `status.code 2 \"Unrecognized field\"` for these names. They are\n * therefore rejected UNCONDITIONALLY by `OcsClient.call` (pre-fetch, regardless\n * of `strictMethods` / `validateResponses` / `strictValidation`), unlike every\n * other unknown method which keeps the warn-once pass-through default. Kept as a\n * separate set on purpose so `OCS_V1_METHODS` (the allowed, live-verified set)\n * is never expanded to accommodate a phantom name.\n */\nexport const OCS_FORBIDDEN_METHODS: ReadonlySet<string> = new Set([\n \"createAccount\",\n \"createSubscriber\",\n]);\n\n/**\n * OCS response envelope. Every response is `{ status: { code, msg }, [method]: <data> }`.\n * `status.code !== 0` signals an OCS-level error (HTTP is always 200). The\n * client checks status before this validation runs; this schema exists so\n * callers and tests can assert the envelope shape independently.\n */\nexport const OcsResponseEnvelope = z\n .object({\n status: z\n .object({\n code: z.number(),\n // Required to match the `OcsStatus` interface in client.ts ({ code, msg }).\n msg: z.string(),\n })\n .passthrough(),\n })\n .passthrough();\n\n// --- getResellerInfo --------------------------------------------------------\n// Live shape: { id, name, parentId, parentName, balance, cdrFolder?,\n// trafficInfo?, chargingInfo?, contactInfo? }. NOT reseller_id/reseller_name.\nexport const GetResellerInfoResponse = z\n .object({\n id: z.number(),\n name: z.string(),\n parentId: z.number(),\n parentName: z.string(),\n // Production eSIMVault returns decimal money as a JSON string (\"495.52\").\n // Staging fixtures used a number, so accept both exact provider variants.\n balance: z.union([z.number(), z.string().regex(/^-?\\d+(?:\\.\\d+)?$/)]),\n cdrFolder: z.string().optional(),\n trafficInfo: z.object({}).passthrough().optional(),\n chargingInfo: z.object({}).passthrough().optional(),\n contactInfo: z.object({}).passthrough().optional(),\n })\n .passthrough();\n\n// --- listResellerAccount ----------------------------------------------------\n// Live shape is NESTED: { reseller: [ { id, name, account: [ {…} ] } ] }.\nexport const ListResellerAccountResponse = z\n .object({\n reseller: z.array(\n z\n .object({\n id: z.number(),\n name: z.string(),\n account: z.array(z.object({}).passthrough()),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n// --- listPrepaidPackageTemplate ---------------------------------------------\n// Live shape: { template: [ { prepaidpackagetemplateid, prepaidpackagetemplatename,\n// resellerid, locationzoneid, … } ] }.\nexport const ListPrepaidPackageTemplateResponse = z\n .object({\n template: z.array(\n z\n .object({\n prepaidpackagetemplateid: z.number(),\n prepaidpackagetemplatename: z.string(),\n resellerid: z.number(),\n locationzoneid: z.number(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n// --- getSingleSubscriber ----------------------------------------------------\n// Live shape: `status` is an ARRAY; status[0].status is a named string\n// (\"Active\"/\"Inventory\"/\"Inactive\"/\"End of Life\"/possibly others). Do NOT\n// hardcode a closed enum — assert a non-empty, alpha-leading string.\nconst SubscriberStatusName = z\n .string()\n .regex(/^[A-Za-z]/, \"subscriber status must be a non-empty alpha-leading name\");\n\nexport const GetSingleSubscriberResponse = z\n .object({\n status: z\n .array(\n z\n .object({\n status: SubscriberStatusName,\n })\n .passthrough(),\n )\n .min(1, \"getSingleSubscriber.status must be a non-empty array\"),\n })\n .passthrough();\n\n/**\n * Method → response-shape schema. Keyed by OCS method name; only the read\n * methods we depend on are pinned. Methods absent from this map skip shape\n * validation (write/admin mutations return only the status envelope).\n */\nexport const RESPONSE_SCHEMAS: Record<string, z.ZodType> = {\n getResellerInfo: GetResellerInfoResponse,\n listResellerAccount: ListResellerAccountResponse,\n listPrepaidPackageTemplate: ListPrepaidPackageTemplateResponse,\n getSingleSubscriber: GetSingleSubscriberResponse,\n};\n\n/**\n * Schema-derived payload types for consumers that need response typing without\n * re-declaring the shapes. Pure type exports (no runtime surface), so importing\n * them does not require a zod dependency in the consuming package. Inferred from\n * the schemas above, so they can never drift from the contract they document.\n */\nexport type GetResellerInfoData = z.infer<typeof GetResellerInfoResponse>;\nexport type ListResellerAccountData = z.infer<typeof ListResellerAccountResponse>;\n\n// ---------------------------------------------------------------------------\n// Common\n// ---------------------------------------------------------------------------\n\nexport const IccidParam = z.object({ iccid: z.string().min(1) });\nexport const AccountIdParam = z.object({ accountId: z.coerce.number().int().positive() });\nexport const PackageIdParam = z.object({ packageId: z.coerce.number().int().positive() });\nexport const TemplateIdParam = z.object({ templateId: z.coerce.number().int().positive() });\nexport const ResellerIdParam = z.object({ resellerId: z.coerce.number().int().positive() });\n\nexport const DateRangeBody = z.object({\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"must be YYYY-MM-DD\"),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"must be YYYY-MM-DD\"),\n});\n\nexport const ErrorResponse = z.object({\n error: z.string(),\n code: z.number().optional(),\n retryAfter: z.number().optional(),\n});\n\nexport const OkResponse = z.object({\n ok: z.boolean(),\n data: z.unknown().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Subscriber\n// ---------------------------------------------------------------------------\n\nexport const ModifySubscriberStatusBody = z.object({\n newStatus: z.enum([\"active\", \"suspended\", \"deactivated\"]),\n});\n\nexport const ModifySubscriberBalanceBody = z.object({\n amount: z.number().optional(),\n setBalance: z.number().optional(),\n}).refine((v) => v.amount !== undefined || v.setBalance !== undefined, {\n message: \"Provide either amount (delta) or setBalance (absolute)\",\n});\n\nexport const ModifySubscriberContactInfoBody = z.object({\n name: z.string().optional(),\n mail: z.string().email().optional(),\n phone: z.string().optional(),\n});\n\nexport const SetTrafficRestrictionsBody = z.object({\n blockData: z.boolean().optional(),\n blockVoice: z.boolean().optional(),\n blockSms: z.boolean().optional(),\n blockRoaming: z.boolean().optional(),\n});\n\nexport const ModifySteeringListBody = z.object({\n steeringListId: z.number().int().positive(),\n});\n\nexport const HlrSetBitrateBody = z.object({\n limit: z.enum(OCS_HLR_BITRATE_LIMITS),\n});\n\n// ---------------------------------------------------------------------------\n// Packages\n// ---------------------------------------------------------------------------\n\nexport const AssignPackageBody = z.object({\n packageTemplateId: z.number().int().positive(),\n startDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n});\n\nexport const AssignRecurringPackageBody = z.object({\n packageTemplateId: z.number().int().positive(),\n});\n\nexport const ModifyPackageLimitsBody = z.object({\n dataLimitMb: z.number().positive().optional(),\n voiceLimitMin: z.number().nonnegative().optional(),\n smsLimit: z.number().nonnegative().optional(),\n});\n\nexport const ModifyPackageExpiryBody = z.object({\n expiryDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n});\n\nexport const ModifyPackageStatusBody = z.object({\n status: z.enum([\"active\", \"paused\"]),\n});\n\nexport const StopResumeRecurringBody = z.object({\n action: z.enum([\"stop\", \"resume\"]),\n});\n\nexport const CreatePackageTemplateBody = z.object({\n name: z.string().min(1),\n dataLimitMb: z.number().positive(),\n validityDays: z.number().int().positive(),\n price: z.number().nonnegative().optional(),\n recurring: z.boolean().optional(),\n});\n\nexport const ModifyTemplateCoreBody = z.object({\n name: z.string().optional(),\n dataLimitMb: z.number().positive().optional(),\n validityDays: z.number().int().positive().optional(),\n});\n\nexport const ModifyTemplateRecurringBody = z.object({\n billingCycleDays: z.number().int().positive().optional(),\n autoRenew: z.boolean().optional(),\n});\n\nexport const ModifyTemplateThrottlingBody = z.object({\n throttleSpeedKbps: z.number().int().nonnegative(),\n});\n\n// ---------------------------------------------------------------------------\n// Accounts\n// ---------------------------------------------------------------------------\n\nexport const ModifyAccountBalanceBody = z.object({\n amount: z.number(),\n // adapt and add are a signed delta. set replaces the balance. subtract\n // negates a positive amount. The API drops mode before the OCS call.\n mode: z.enum([\"adapt\", \"set\", \"add\", \"subtract\"]).default(\"adapt\"),\n});\n\nexport const MoveSubscribersBody = z.object({\n fromAccountId: z.number().int().positive(),\n iccidStart: z.string().min(1),\n iccidEnd: z.string().min(1),\n});\n\n// ---------------------------------------------------------------------------\n// Location zones\n// ---------------------------------------------------------------------------\n\nexport const CreateLocationZoneBody = z.object({\n name: z.string().min(1),\n countries: z.array(z.string().length(2)).min(1),\n});\n\n// ---------------------------------------------------------------------------\n// SMS\n// ---------------------------------------------------------------------------\n\nexport const SendSmsBody = z.object({\n text: z.string().min(1).max(160),\n senderId: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Change SIM status\n// ---------------------------------------------------------------------------\n\nexport const ChangeSimStatusBody = z.object({\n newStatus: z.enum([\"Enable\", \"Disable\"]),\n});\n\n// ---------------------------------------------------------------------------\n// Intelligence composites\n// ---------------------------------------------------------------------------\n\nexport const DiagnoseSubscriberBody = z.object({\n iccid: z.string().min(1),\n});\n\nexport const FleetHealthQuery = z.object({\n accountId: z.coerce.number().int().positive().optional(),\n});\n\nexport const ChurnRiskQuery = z.object({\n daysSinceUsage: z.coerce.number().int().positive().default(14),\n limit: z.coerce.number().int().positive().max(500).default(50),\n});\n\nexport const DetectUsageAnomaliesBody = z.object({\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n thresholdMultiplier: z.number().positive().default(2.5),\n});\n\nexport const OptimizePackageBody = z.object({\n iccid: z.string().min(1),\n});\n\nexport const AuditNetworkCoverageBody = z.object({\n locationZoneId: z.number().int().positive().optional(),\n});\n\nexport const MarketingIntelligenceQuery = z.object({\n segment: z.enum([\"low_usage\", \"high_usage\", \"expiring_soon\", \"churned\"]).optional(),\n});\n\nexport const HighCostSubscribersQuery = z.object({\n limit: z.coerce.number().int().positive().max(200).default(20),\n start: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n end: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Webhooks\n// ---------------------------------------------------------------------------\n\nexport const WebhookEventBody = z.record(z.string(), z.unknown());\n\n// ---------------------------------------------------------------------------\n// Health\n// ---------------------------------------------------------------------------\n\nexport const HealthResponse = z.object({\n ok: z.literal(true),\n version: z.string(),\n build_sha: z.string(),\n});\n","/**\n * Per-method OCS request budgets — the single source of truth for every OCS\n * client copy in this repo (#663 follow-up).\n *\n * #663 gave every OCS call one flat 20 s deadline and made a breach\n * non-retriable. Both halves of that are wrong against the live upstream, and\n * the numbers say so. Measured against reseller 1170 (parent Bridge4IP 246) on\n * 2026-08-25, three consecutive byte-identical requests each:\n *\n * listSubscriber {accountId:5259} 12.6 s | 0.7 s | 13.6 s (202 KB)\n * listPrepaidPackageTemplate {} 17.1 s | 9.0 s | 0.7 s ( 64 KB)\n * getCustomerTariff 1170 88 s – 128 s ( 3.4 MB)\n *\n * Re-measured the same day at 20:43Z, 25 runs each, and the picture changed in\n * a way that matters:\n *\n * listSubscriber p50 0.5 s p95 0.7 s max 0.7 s (202 KB)\n * listPrepaidPackageTemplate p50 0.3 s p95 2.5 s max 2.8 s ( 64 KB)\n * getCustomerTariff p50 2.8 s p95 3.3 s max 3.3 s (3,439,729 B)\n *\n * Byte-identical response, 3,439,729 B both times, delivered in 2.8 s here and\n * in 88-128 s earlier. So the slowness is a property of the upstream's *state*,\n * not of the payload: OCS has fast windows and slow windows, and everything\n * inside a slow window is slow together. The size only sets how far a slow\n * window can stretch a given call, which is why the 3.4 MB method degrades to\n * two minutes while a 64 KB one degrades to seventeen seconds.\n *\n * That is a resilience problem end to end, so both fixes are budgets, not repairs.\n *\n * 1. `getCustomerTariff` needs a deadline above its worst window, and there is\n * no smaller request to ask for instead. OCS takes the method as a bare\n * integer (`assertBareIntParam`) and the REST route demonstrably ignores\n * `?country=` and `?trafficType=` — both return byte-identical bodies, which\n * is why `bounded-results.ts` filters in-process rather than pushing the\n * filter down. That projection (3.4 MB -> ~232 KB) already bounds what a\n * model is handed, and matters twice over given #810/#811 (Haiku miscounted\n * a 55-object dump) — but it runs *after* the body arrives, so it cannot\n * shorten the transfer by a millisecond. Hence 180 s, 1.4x the worst\n * observation. One attempt, not three: a slow window lasts far longer than a\n * backoff, so a retry lands in the same window and turns a three-minute\n * failure into a nine-minute one. In a fast window the budget never fires.\n *\n * 2. Everything else gets a per-attempt deadline above every observation (30 s,\n * 1.75x the slowest) and a retry when it is breached anyway. Here a retry is\n * worth it: these calls are short enough that a second attempt costs seconds\n * rather than minutes, and the spread within a window is wide enough (0.7 s\n * vs 13.6 s, byte-identical) that the next attempt often lands on the fast\n * side. That is what turns a 20-fold spread into a stable p95.\n *\n * `totalMs` is the contract the caller can rely on: whatever the retries do,\n * the call either answers or throws inside that budget. It is checked before\n * every attempt and before every backoff sleep, so the budget is a ceiling\n * rather than an aspiration.\n *\n * Env overrides (unchanged semantics for `OCS_TIMEOUT_MS`, which raises the\n * per-attempt deadline for every method):\n * OCS_TIMEOUT_MS per-attempt deadline, all methods\n * OCS_TOTAL_TIMEOUT_MS total budget, all methods\n */\n\nexport interface OcsBudget {\n /** Deadline for a single HTTP attempt, including the body read. */\n attemptMs: number;\n /** Hard cap on attempts, backoff sleeps included in the total budget. */\n maxAttempts: number;\n /** Wall-clock ceiling for the whole call. The caller-facing guarantee. */\n totalMs: number;\n}\n\n/**\n * 30 s per attempt is 1.75x the slowest healthy observation (17.1 s), so the\n * jittery tail lands inside the first attempt. 75 s total leaves room for two\n * full attempts plus backoff, which is where the resilience actually comes\n * from.\n */\nexport const DEFAULT_OCS_BUDGET: OcsBudget = {\n attemptMs: 30_000,\n maxAttempts: 3,\n totalMs: 75_000,\n};\n\n/**\n * Methods whose measured behaviour does not fit the default. Keyed by the OCS\n * method name, not the MCP tool name, because the budget is a property of the\n * upstream call.\n */\nexport const OCS_METHOD_BUDGETS: Readonly<Record<string, OcsBudget>> = {\n // 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One\n // attempt: payload size does not vary, so a retry only doubles the wait.\n getCustomerTariff: { attemptMs: 180_000, maxAttempts: 1, totalMs: 180_000 },\n};\n\n/** Below this there is not enough budget left for an attempt to be worth starting. */\nexport const MIN_ATTEMPT_BUDGET_MS = 1_000;\n\nconst BACKOFF_BASE_MS = 500;\n\n/**\n * Raised when a call exhausts its budget.\n *\n * Deliberately NOT an OcsApiError: it carries no OCS status code, and the\n * OCS-error branch of a retry loop must never swallow it.\n */\nexport class OcsTimeoutError extends Error {\n constructor(\n public readonly method: string,\n /** The per-attempt deadline that was breached. */\n public readonly timeoutMs: number,\n /** The total budget for the call, when one applied. */\n public readonly budgetMs: number = timeoutMs,\n /** How many HTTP attempts were made before giving up. */\n public readonly attempts: number = 1,\n ) {\n super(\n `[${method}] OCS request timed out after ${timeoutMs}ms` +\n (attempts > 1 ? ` on each of ${attempts} attempts` : \"\") +\n (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : \"\") +\n `. The upstream did not deliver a complete response in time — usually an ` +\n `oversized payload or a wedged upstream. Narrow the request, or raise ` +\n `OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`,\n );\n this.name = \"OcsTimeoutError\";\n }\n}\n\nfunction envInt(name: string): number | undefined {\n const proc = (globalThis as Record<string, unknown>)[\"process\"] as\n | { env?: Record<string, string | undefined> }\n | undefined;\n const parsed = Number(proc?.env?.[name]);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/**\n * Resolve the budget for a method: table entry or default, then env overrides.\n *\n * Read on every call rather than cached at module load, because\n * `tests/integration/setup-live-ocs.ts` and individual tests set\n * `OCS_TIMEOUT_MS` at runtime and expect it to take effect.\n */\nexport function ocsBudgetFor(method: string): OcsBudget {\n const base = OCS_METHOD_BUDGETS[method] ?? DEFAULT_OCS_BUDGET;\n const attemptMs = envInt(\"OCS_TIMEOUT_MS\") ?? base.attemptMs;\n // A total budget below the per-attempt deadline would cut the first attempt\n // short, which is never what an override meant.\n const totalMs = Math.max(envInt(\"OCS_TOTAL_TIMEOUT_MS\") ?? base.totalMs, attemptMs);\n return { attemptMs, maxAttempts: base.maxAttempts, totalMs };\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n const timer = setTimeout(finish, ms);\n function finish(): void {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", finish);\n resolve();\n }\n signal.addEventListener(\"abort\", finish, { once: true });\n });\n}\n\n/**\n * Race `work` against a wall-clock deadline.\n *\n * A plain `setTimeout` on purpose: `AbortSignal.timeout()` is native and is not\n * patched by vitest's fake timers, so a deadline built on it cannot be tested\n * without burning real seconds. The signal is still handed to `fetch` so the\n * socket is torn down rather than leaked — but this race, not the signal, is\n * the guarantee, because a signal does not bound the body read at all.\n */\nasync function withDeadline<T>(work: Promise<T>, deadline: OcsTimeoutError, ms: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n work,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(deadline), ms);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\n/**\n * Run one OCS call under its budget, retrying only on a deadline breach.\n *\n * `attempt` receives an `AbortSignal` that fires when its own per-attempt\n * deadline expires or the total budget runs out; pass it to `fetch`. Anything\n * `attempt` throws that is not an `OcsTimeoutError` propagates untouched, so\n * OCS status-code handling stays where it already lives.\n *\n * Modelled on `sendWithRetry` in packages/carrier-ai/src/bedrock.ts (#807):\n * full jitter, sleeps bound to the same signal as the work, and every attempt\n * and sleep checked against the deadline *before* it starts — so a retry can\n * never run past the budget the caller was promised.\n */\nexport async function runWithBudget<T>(\n method: string,\n attempt: (signal: AbortSignal, attemptMs: number) => Promise<T>,\n budget: OcsBudget = ocsBudgetFor(method),\n): Promise<T> {\n const deadlineAt = Date.now() + budget.totalMs;\n const budgetController = new AbortController();\n const budgetTimer = setTimeout(() => budgetController.abort(), budget.totalMs);\n\n let attempts = 0;\n let lastTimeout: OcsTimeoutError | undefined;\n\n try {\n for (let i = 0; i < budget.maxAttempts; i++) {\n const remaining = deadlineAt - Date.now();\n if (remaining < MIN_ATTEMPT_BUDGET_MS) break;\n\n // Never let an attempt outlive the total budget.\n const attemptMs = Math.min(budget.attemptMs, remaining);\n attempts++;\n\n const attemptController = new AbortController();\n const onBudgetAbort = () => attemptController.abort();\n budgetController.signal.addEventListener(\"abort\", onBudgetAbort, { once: true });\n const attemptTimer = setTimeout(() => attemptController.abort(), attemptMs);\n const breach = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);\n\n try {\n return await withDeadline(\n attempt(attemptController.signal, attemptMs),\n breach,\n attemptMs,\n );\n } catch (err) {\n if (!(err instanceof OcsTimeoutError)) throw err;\n lastTimeout = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);\n } finally {\n clearTimeout(attemptTimer);\n budgetController.signal.removeEventListener(\"abort\", onBudgetAbort);\n // The response body is fully read by the time an attempt resolves, so\n // aborting here only tears down a socket nobody is still reading.\n attemptController.abort();\n }\n\n if (i === budget.maxAttempts - 1) break;\n const backoff = Math.random() * BACKOFF_BASE_MS * 2 ** i;\n if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;\n await sleep(backoff, budgetController.signal);\n }\n } finally {\n clearTimeout(budgetTimer);\n }\n\n throw (\n lastTimeout ??\n new OcsTimeoutError(method, budget.attemptMs, budget.totalMs, Math.max(attempts, 1))\n );\n}\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 // stderr is observable in Workers and safe for MCP stdio. stdout is the\n // JSON-RPC protocol in the npm server: one metric line there corrupts the\n // stream and makes clients report \"Failed to parse JSONRPC message\".\n console.error(\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\n/**\n * Per-method FIELD-NAME translation, MCP/public name → OCS wire name.\n *\n * A sibling of the shape bug above, with the same failure mode: OCS rejects the\n * whole request rather than ignoring a field it does not know, so one wrong key\n * costs the entire call.\n *\n * getResellerInfo({ resellerId: 1170 })\n * → code 2, Unrecognized field \"resellerId\" ... (one known property: \"id\")\n *\n * WHY TRANSLATE RATHER THAN RENAME THE TOOL PARAMETER\n * `resellerId` is the honest domain name and is the published contract of\n * `@carrierllc/mcp` (npm, 0.9.2). OCS's `id` is a terse backend field whose\n * meaning is only clear from the method it sits on. Renaming the tool\n * parameter would break every existing caller to make the public surface\n * worse, so the mapping lives here instead — at the one place every surface\n * serialises through, which is also what makes it cover call sites nobody has\n * written yet.\n *\n * SCOPE\n * Keyed by method, never global: `id` means something different on every OCS\n * method, so a repo-wide `resellerId → id` rewrite would corrupt the calls it\n * did not intend to fix.\n *\n * VERIFIED against live OCS (ocs.esimvault.cloud), 2026-08-24:\n * getResellerInfo {resellerId:1170} → code 2, unrecognized field\n * getResellerInfo {id:1170} → code 0, Lifecycle Innovations, parent Bridge4IP (246)\n *\n * modifyPPTCore / modifyPPTRecurring / modifyPPTThrottling — OCS names the\n * template id `prepaidpackagetemplateid` on all three modifyPPT* methods (see\n * packages/ocs-spec/ocs-accepted-params.json); the tools send `templateId`.\n * packageTemplateModifyParams emits the other OCS names and strips a body\n * prepaidpackagetemplateid so this rename of templateId is the one that lands.\n * Core still renames a leftover name, volume, or validityDays. An explicit\n * target wins.\n */\nexport const OCS_PARAM_RENAMES: Record<string, Record<string, string>> = {\n getResellerInfo: { resellerId: \"id\" },\n modifyPPTCore: {\n templateId: \"prepaidpackagetemplateid\",\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n validityDays: \"perioddays\",\n },\n modifyPPTRecurring: { templateId: \"prepaidpackagetemplateid\" },\n modifyPPTThrottling: { templateId: \"prepaidpackagetemplateid\" },\n // Console NL-create and the MCP tool both advertise `name` / `volume` /\n // `validityDays`. OCS createPrepaidPackageTemplate rejects unknown keys\n // (Jackson code 2) — live failure was Unrecognized field \"name\".\n createPrepaidPackageTemplate: {\n name: \"prepaidpackagetemplatename\",\n volume: \"databyte\",\n validityDays: \"perioddays\",\n dataLimit: \"databyte\",\n },\n createLocationZone: { name: \"locationZoneName\" },\n};\n\n/**\n * Applies the registry to one request's params.\n *\n * Returns the input untouched when there is nothing to rename, so the common\n * path allocates nothing. An explicitly-supplied target key always wins: a\n * caller that already speaks OCS (tools-backlog.ts sends `id` directly) must\n * not have its correct value clobbered by a translated one.\n */\nexport function applyParamRenames<T>(method: string, params: T): T {\n const renames = OCS_PARAM_RENAMES[method];\n if (!renames || !params || typeof params !== \"object\" || Array.isArray(params)) {\n return params;\n }\n const record = params as Record<string, unknown>;\n let out: Record<string, unknown> | undefined;\n for (const [from, to] of Object.entries(renames)) {\n if (!Object.prototype.hasOwnProperty.call(record, from)) continue;\n out ??= { ...record };\n if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];\n delete out[from];\n }\n return (out ?? params) as T;\n}\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/**\n * modifyAccountBalance — Sam's decision (2026-08-28) on the last of the\n * seven param-drift KNOWN_BROKEN entries left semantic after the six-tool\n * fix on 2026-08-27.\n *\n * WHY `mode` NEVER REACHES THE WIRE\n * The tool's public contract keeps `mode: 'adapt' | 'set'` — that part is\n * not the defect. What's wrong is sending `mode` itself to OCS: `mode` is\n * not a property this method knows about, so every call from the old\n * handler was rejected outright before it could move a cent.\n *\n * THE MAPPING\n * 'adapt' sends a signed delta under `amount` (negative to subtract),\n * never `setBalance`. 'set' sends the target value under `setBalance` and\n * omits `amount` entirely. Matches Bridge4IP's own documented contract\n * (docs/research/bridge4ip-ocs-api/endpoints-full.md §1.2) and the sibling\n * `modifySubscriberBalance` method exactly — neither needs a direction\n * classifier field.\n *\n * A `type: 'credit'|'debit'` FIELD WAS TRIED AND DROPPED\n * A param-enumeration probe (packages/ocs-spec/ocs-accepted-params.json,\n * 2026-08-24) listed `type` as a known property on the OCS model, so an\n * earlier version of this fix sent `type` alongside `amount`. That probe\n * technique surfaces every field the backend's Java model knows about,\n * not just the documented/supported API surface — it cannot distinguish a\n * real input from a vestigial or internal one. Live-tested 2026-08-28\n * directly against OCS (zero-amount, balance-0 account, so no state\n * change either way): `type` absent, `type: \"credit\"`, `type: \"debit\"`,\n * and `type: \"some_bogus_value\"` all produced an identical response. Not\n * required, value doesn't matter — dropped in favour of the documented,\n * sibling-consistent shape.\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\";\n\n// The per-second floor's buckets are module state. Tests that assert rate-floor\n// behaviour have to clear them between cases, and they now do that against THIS\n// client rather than an app-local copy of it — so the helper has to be reachable\n// from the package's public surface. `rate-floor.ts` itself stays unexported:\n// `acquireToken` is an internal chokepoint and nothing outside should call it.\nexport { _resetBucketsForTest } from \"./rate-floor.js\";\nimport { OCS_FORBIDDEN_METHODS, OCS_V1_METHODS, RESPONSE_SCHEMAS } from \"./schemas.js\";\nimport { assertBareIntParam, applyParamRenames } from \"./ocs-param-shapes.js\";\nimport { nestSubscriberIdParam, normaliseAbsentOcsPayload } from \"./ocs-params.js\";\nimport { OcsTimeoutError, ocsBudgetFor, runWithBudget } from \"./timeout-budgets.js\";\n\n// Package assignment/release can have succeeded before its response is lost.\n// Both inventory issuance, ICCID top-ups and rollback use these names. Never\n// replay them at either transport retry layer; callers retain the unknown\n// outcome for reconciliation rather than treating a timeout as non-acceptance.\nconst SINGLE_ATTEMPT_METHODS = new Set([\"affectPackageToSubscriber\", \"addSubscriberPackage\", \"deleteSubscriberPackage\"]);\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`).\n *\n * Two distinct policies:\n * - `createAccount` / `createSubscriber` (`OCS_FORBIDDEN_METHODS`) are proven\n * phantom methods — the OCS has no account/subscriber creation and answers\n * 200 + error 2 \"Unrecognized field\". They are ALWAYS rejected, pre-fetch and\n * regardless of client options.\n * - Any other unknown method throws only in strict mode (`{ strictMethods:\n * true }`); otherwise `call()` 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 (OCS_FORBIDDEN_METHODS.has(method)\n ? `${method} is a proven phantom method and is always rejected regardless of client options.`\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;\n// OCS code 100 is a provider traffic-control response, not an application error.\n// Its enforcement window can outlast the normal network-error retry cadence.\nconst TRAFFIC_CONTROL_BACKOFF_BASE_MS = 1_000;\nconst TRAFFIC_CONTROL_BACKOFF_MAX_MS = 16_000;\nconst TRAFFIC_CONTROL_MAX_RETRIES = 6;\nconst BACKOFF_JITTER = 0.25; // ±25%\n/**\n * Exported so callers (e.g. intelligence.ts) can wrap per-iteration OCS calls.\n * Network failures get 3 retries (500ms exponential, capped at 4s). OCS code 100\n * gets 6 retries (1s exponential, capped at 16s) because provider traffic-control\n * windows routinely outlast a short network retry burst.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: unknown;\n let maxRetries = BACKOFF_MAX_RETRIES;\n let backoffBaseMs = BACKOFF_BASE_MS;\n let backoffMaxMs = BACKOFF_MAX_MS;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const base = Math.min(backoffBaseMs * Math.pow(2, attempt - 1), backoffMaxMs);\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 if (err instanceof OcsApiError) {\n // Code 100 is a transient provider traffic-control response. It needs a\n // longer retry window than network failures; preserve all other OCS errors.\n if (err.code !== 100) throw err;\n maxRetries = TRAFFIC_CONTROL_MAX_RETRIES;\n backoffBaseMs = TRAFFIC_CONTROL_BACKOFF_BASE_MS;\n backoffMaxMs = TRAFFIC_CONTROL_BACKOFF_MAX_MS;\n }\n // Contract failures are deterministic — don't waste retries on them.\n if (err instanceof OcsContractError) {\n throw err;\n }\n // Timeout retries belong to runWithBudget, which is the only layer that\n // knows the total budget. Retrying here as well would multiply the\n // ceiling the caller was promised by four.\n if (err instanceof OcsTimeoutError) {\n throw err;\n }\n }\n }\n throw lastError;\n}\n\n/**\n * True when an error is an abort raised by our own deadline signal.\n *\n * PORTED FROM apps/mcp-server/src/client.ts ON 2026-08-28, AND THAT IS THE POINT.\n * The #663 request-budget work landed on the Worker's private copy of this\n * client and never reached this package or the stdio copy. The consequence was\n * not cosmetic: without this mapping an abort raised by our OWN deadline comes\n * out of `fetch` as a plain `Error`, which `retryWithBackoff` classifies as a\n * retriable network error and runs three more times — multiplying the total\n * wall-clock ceiling the caller was promised. `runWithBudget` is the only layer\n * that knows the total budget, so the timeout has to surface as the\n * non-retriable `OcsTimeoutError` for its accounting to hold.\n *\n * The Worker's own tests (apps/mcp-server/tests/ocs-budgets.test.ts) caught\n * this the moment the Worker was pointed at the shared client — which is the\n * whole argument for having one implementation: the gap existed for as long as\n * the copies did, and closing the fork is what surfaced it.\n */\nfunction isAbortError(err: unknown): boolean {\n return (\n err instanceof Error &&\n (err.name === \"AbortError\" ||\n err.name === \"TimeoutError\" ||\n /aborted|timed? ?out/i.test(err.message))\n );\n}\n\n// ---------------------------------------------------------------------------\n// OCS Client\n// ---------------------------------------------------------------------------\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 * This flag has no effect on `OCS_FORBIDDEN_METHODS` (createAccount /\n * createSubscriber): those proven phantom methods are rejected\n * unconditionally, with or without it.\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 // Hard gate for the proven phantom methods (P5): createAccount and\n // createSubscriber do not exist on the OCS (live smoke: 200 + error 2\n // \"Unrecognized field\"). They are rejected UNCONDITIONALLY — pre-fetch and\n // regardless of strictMethods / validateResponses / strictValidation — so\n // they can never be silently reintroduced behind a permissive client.\n if (OCS_FORBIDDEN_METHODS.has(method)) {\n throw new OcsUnknownMethodError(method);\n }\n // Method guard for everything else. The live-verified OCS v1 set\n // (OCS_V1_METHODS) does not (yet) cover every name the repo calls, so by\n // default we WARN once rather than throw — surfacing possibly-phantom\n // methods 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 // Field-name gate, same chokepoint and same reason. OCS rejects the whole\n // request on an unknown key, so a tool advertising `resellerId` where OCS\n // wants `id` errors mid-loop and the model gives up or invents an answer.\n // Translating here keeps the published tool schema stable across every\n // surface — MCP server, stdio, API, CLI — and future call sites.\n const wireParams = applyParamRenames(method, params);\n // modifySubscriberStatus.subscriber is a SubscriberId object. A bare ICCID\n // string is OCS error 2. Nesting here covers MCP, REST, the CLI, and any\n // rollback that still stored the string.\n const shapedParams =\n method === \"modifySubscriberStatus\" ? nestSubscriberIdParam(wireParams) : wireParams;\n const body = JSON.stringify({ [method]: shapedParams });\n\n const singleAttempt = SINGLE_ATTEMPT_METHODS.has(method);\n const send = 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 // Layer 3 — Request budget: per-method deadline, retry-on-timeout, and a\n // total wall-clock ceiling. See timeout-budgets.ts for the measurements.\n return runWithBudget<T>(method, async (signal, attemptMs) => {\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n signal,\n });\n } catch (err) {\n if (isAbortError(err)) {\n // Our own deadline fired at the socket level — surface it as the\n // non-retriable timeout rather than as a retriable network error.\n throw new OcsTimeoutError(method, attemptMs);\n }\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 // The body read is inside the deadline too: #663's hang was a response\n // that started but never completed, which a headers-only timeout misses.\n let json: OcsResponse<T>;\n try {\n json = (await res.json()) as OcsResponse<T>;\n } catch (err) {\n if (isAbortError(err)) {\n throw new OcsTimeoutError(method, attemptMs);\n }\n throw err;\n }\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 if (json[method] !== undefined) {\n payload = json[method] as T;\n } else if (Object.keys(json).some((k) => k !== \"status\")) {\n // OCS keyed the response under something other than the method name\n // (e.g. getSingleSubscriber -> {status, subscriber}). Callers that\n // read a differently-named top-level key off the raw envelope (see\n // ocs-scoping.ts's subscriberAccountId) depend on this fallback —\n // narrow it to \"envelope carries more than just status\" so it does\n // not also catch the success-with-nothing-but-status case below.\n payload = json as unknown as T;\n } else {\n // OCS answered status.code 0 (success) and the envelope carries\n // NOTHING beyond that status — observed live for getSimProviderStatus\n // (`{status:{code:0,msg:\"OK\"}}` and nothing else). The old fallback\n // `json as unknown as T` handed the caller that bare envelope, so\n // `payload.status` (an `{code,msg}` object) got read as if it were\n // the method's actual field of the same name — e.g.\n // diagnose_subscriber's `String(sim.data.status)` on that object\n // produced the literal string \"[object Object]\" and was reported as\n // a critical finding. A success envelope with nothing else means OCS\n // has no data to report for this call; treat it as an absent\n // payload so every downstream `if (data)` / `data?.field` guard\n // degrades safely instead of reading envelope plumbing as data.\n payload = null as T;\n }\n\n // A success envelope with a null body is not an error for the two\n // methods that document a stand-in (empty steering list, package\n // status confirmation). Other methods stay null.\n payload = normaliseAbsentOcsPayload(method, payload, shapedParams);\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 }, singleAttempt ? { ...ocsBudgetFor(method), maxAttempts: 1 } : undefined);\n };\n return singleAttempt ? send() : retryWithBackoff(send);\n }\n}\n","/**\n * ITU-T E.212 Mobile Country Code (MCC) → ISO 3166-1 alpha-2 mapping.\n *\n * Source: ITU-T E.212 (11/2019) + Wikipedia \"Mobile country code\" article.\n * Used by detect_country_entry intelligence composite and mango.talk country-watch cron.\n */\nexport const MCC_TO_ISO: Record<number, string> = {\n // Europe\n 202: \"GR\", // Greece\n 204: \"NL\", // Netherlands\n 206: \"BE\", // Belgium\n 208: \"FR\", // France\n 212: \"MC\", // Monaco\n 213: \"AD\", // Andorra\n 214: \"ES\", // Spain\n 216: \"HU\", // Hungary\n 218: \"BA\", // Bosnia and Herzegovina\n 219: \"HR\", // Croatia\n 220: \"RS\", // Serbia\n 221: \"XK\", // Kosovo\n 222: \"IT\", // Italy\n 225: \"VA\", // Vatican City\n 226: \"RO\", // Romania\n 228: \"CH\", // Switzerland\n 230: \"CZ\", // Czech Republic\n 231: \"SK\", // Slovakia\n 232: \"AT\", // Austria\n 234: \"GB\", // United Kingdom\n 235: \"GB\", // United Kingdom\n 238: \"DK\", // Denmark\n 240: \"SE\", // Sweden\n 242: \"NO\", // Norway\n 244: \"FI\", // Finland\n 246: \"LT\", // Lithuania\n 247: \"LV\", // Latvia\n 248: \"EE\", // Estonia\n 250: \"RU\", // Russia\n 255: \"UA\", // Ukraine\n 257: \"BY\", // Belarus\n 259: \"MD\", // Moldova\n 260: \"PL\", // Poland\n 262: \"DE\", // Germany\n 266: \"GI\", // Gibraltar\n 268: \"PT\", // Portugal\n 270: \"LU\", // Luxembourg\n 272: \"IE\", // Ireland\n 274: \"IS\", // Iceland\n 276: \"AL\", // Albania\n 278: \"MT\", // Malta\n 280: \"CY\", // Cyprus\n 282: \"GE\", // Georgia\n 283: \"AM\", // Armenia\n 284: \"BG\", // Bulgaria\n 286: \"TR\", // Turkey\n 288: \"FO\", // Faroe Islands\n 289: \"GE\", // Abkhazia (Georgia)\n 290: \"GL\", // Greenland\n 292: \"SM\", // San Marino\n 293: \"SI\", // Slovenia\n 294: \"MK\", // North Macedonia\n 295: \"LI\", // Liechtenstein\n 297: \"ME\", // Montenegro\n\n // Commonwealth of Independent States / Former USSR\n 401: \"KZ\", // Kazakhstan\n 402: \"BT\", // Bhutan\n 404: \"IN\", // India\n 405: \"IN\", // India\n 406: \"IN\", // India\n 410: \"PK\", // Pakistan\n 412: \"AF\", // Afghanistan\n 413: \"LK\", // Sri Lanka\n 414: \"MM\", // Myanmar\n 415: \"LB\", // Lebanon\n 416: \"JO\", // Jordan\n 417: \"SY\", // Syria\n 418: \"IQ\", // Iraq\n 419: \"KW\", // Kuwait\n 420: \"SA\", // Saudi Arabia\n 421: \"YE\", // Yemen\n 422: \"OM\", // Oman\n 424: \"AE\", // United Arab Emirates\n 425: \"IL\", // Israel\n 426: \"BH\", // Bahrain\n 427: \"QA\", // Qatar\n 428: \"MN\", // Mongolia\n 429: \"NP\", // Nepal\n 430: \"AE\", // United Arab Emirates\n 431: \"AE\", // United Arab Emirates\n 432: \"IR\", // Iran\n 434: \"UZ\", // Uzbekistan\n 436: \"TJ\", // Tajikistan\n 437: \"KG\", // Kyrgyzstan\n 438: \"TM\", // Turkmenistan\n 440: \"JP\", // Japan\n 441: \"JP\", // Japan\n 450: \"KR\", // South Korea\n 452: \"VN\", // Vietnam\n 454: \"HK\", // Hong Kong\n 455: \"MO\", // Macau\n 456: \"KH\", // Cambodia\n 457: \"LA\", // Laos\n 460: \"CN\", // China\n 461: \"CN\", // China\n 466: \"TW\", // Taiwan\n 467: \"KP\", // North Korea\n 470: \"BD\", // Bangladesh\n 472: \"MV\", // Maldives\n 502: \"MY\", // Malaysia\n 505: \"AU\", // Australia\n 510: \"ID\", // Indonesia\n 514: \"TL\", // East Timor\n 515: \"PH\", // Philippines\n 520: \"TH\", // Thailand\n 525: \"SG\", // Singapore\n 528: \"BN\", // Brunei\n 530: \"NZ\", // New Zealand\n 536: \"NR\", // Nauru\n 537: \"PG\", // Papua New Guinea\n 539: \"TO\", // Tonga\n 540: \"SB\", // Solomon Islands\n 541: \"VU\", // Vanuatu\n 542: \"FJ\", // Fiji\n 544: \"AS\", // American Samoa\n 545: \"KI\", // Kiribati\n 546: \"NC\", // New Caledonia\n 547: \"PF\", // French Polynesia\n 548: \"CK\", // Cook Islands\n 549: \"WS\", // Samoa\n 550: \"FM\", // Micronesia\n 551: \"MH\", // Marshall Islands\n 552: \"PW\", // Palau\n 553: \"TV\", // Tuvalu\n 555: \"NU\", // Niue\n\n // Africa (ITU-T E.212)\n 602: \"EG\", // Egypt\n 603: \"DZ\", // Algeria\n 604: \"MA\", // Morocco\n 605: \"TN\", // Tunisia\n 606: \"LY\", // Libya\n 607: \"GM\", // Gambia\n 608: \"SN\", // Senegal\n 609: \"MR\", // Mauritania\n 610: \"ML\", // Mali\n 611: \"GN\", // Guinea\n 612: \"CI\", // Côte d'Ivoire\n 613: \"BF\", // Burkina Faso\n 614: \"NE\", // Niger\n 615: \"TG\", // Togo\n 616: \"BJ\", // Benin\n 617: \"MU\", // Mauritius\n 618: \"LR\", // Liberia\n 619: \"SL\", // Sierra Leone\n 620: \"GH\", // Ghana\n 621: \"NG\", // Nigeria\n 622: \"TD\", // Chad\n 623: \"CF\", // Central African Republic\n 624: \"CM\", // Cameroon\n 625: \"CV\", // Cape Verde\n 626: \"ST\", // São Tomé and Príncipe\n 627: \"GQ\", // Equatorial Guinea\n 628: \"GA\", // Gabon\n 629: \"CG\", // Republic of the Congo\n 630: \"CD\", // Democratic Republic of the Congo\n 631: \"AO\", // Angola\n 632: \"GW\", // Guinea-Bissau\n 633: \"SC\", // Seychelles\n 634: \"SD\", // Sudan\n 635: \"RW\", // Rwanda\n 636: \"ET\", // Ethiopia\n 637: \"SO\", // Somalia\n 638: \"DJ\", // Djibouti\n 639: \"KE\", // Kenya\n 640: \"TZ\", // Tanzania\n 641: \"UG\", // Uganda\n 642: \"BI\", // Burundi\n 643: \"MZ\", // Mozambique\n 645: \"ZM\", // Zambia\n 646: \"MG\", // Madagascar\n 647: \"RE\", // Réunion / French Indian Ocean\n 648: \"ZW\", // Zimbabwe\n 649: \"NA\", // Namibia\n 650: \"MW\", // Malawi\n 651: \"LS\", // Lesotho\n 652: \"BW\", // Botswana\n 653: \"SZ\", // Eswatini\n 654: \"KM\", // Comoros\n 655: \"ZA\", // South Africa\n 657: \"ER\", // Eritrea\n 658: \"SH\", // Saint Helena\n 659: \"SS\", // South Sudan\n 702: \"BZ\", // Belize\n 704: \"GT\", // Guatemala\n 706: \"SV\", // El Salvador\n 708: \"HN\", // Honduras\n 710: \"NI\", // Nicaragua\n 712: \"CR\", // Costa Rica\n 714: \"PA\", // Panama\n 716: \"PE\", // Peru\n 722: \"AR\", // Argentina\n 724: \"BR\", // Brazil\n 730: \"CL\", // Chile\n 732: \"CO\", // Colombia\n 734: \"VE\", // Venezuela\n 736: \"BO\", // Bolivia\n 738: \"GY\", // Guyana\n 740: \"EC\", // Ecuador\n 742: \"GF\", // French Guiana\n 744: \"PY\", // Paraguay\n 746: \"SR\", // Suriname\n 748: \"UY\", // Uruguay\n 750: \"FK\", // Falkland Islands\n\n // North America, Caribbean\n 302: \"CA\", // Canada\n 308: \"PM\", // Saint Pierre and Miquelon\n 310: \"US\", // United States\n 311: \"US\", // United States\n 312: \"US\", // United States\n 313: \"US\", // United States\n 314: \"US\", // United States\n 315: \"US\", // United States\n 316: \"US\", // United States\n 330: \"PR\", // Puerto Rico\n 332: \"VI\", // U.S. Virgin Islands\n 334: \"MX\", // Mexico\n 338: \"JM\", // Jamaica\n 340: \"GP\", // Guadeloupe\n 342: \"BB\", // Barbados\n 344: \"AG\", // Antigua and Barbuda\n 346: \"KY\", // Cayman Islands\n 348: \"VG\", // British Virgin Islands\n 350: \"BM\", // Bermuda\n 352: \"GD\", // Grenada\n 354: \"MS\", // Montserrat\n 356: \"KN\", // Saint Kitts and Nevis\n 358: \"LC\", // Saint Lucia\n 360: \"VC\", // Saint Vincent and the Grenadines\n 362: \"CW\", // Curaçao / Netherlands Antilles\n 363: \"AW\", // Aruba\n 364: \"BS\", // Bahamas\n 365: \"AI\", // Anguilla\n 366: \"DM\", // Dominica\n 368: \"CU\", // Cuba\n 370: \"DO\", // Dominican Republic\n 372: \"HT\", // Haiti\n 374: \"TT\", // Trinidad and Tobago\n 376: \"TC\", // Turks and Caicos Islands\n\n // Special / Test MCCs\n 999: \"XX\", // Test network\n 901: \"XX\", // International / satellite\n};\n\n/**\n * Resolve an MCC (Mobile Country Code, ITU-T E.212) to ISO 3166-1 alpha-2.\n * Returns null for unknown MCCs.\n */\nexport function mccToIso(mcc: number | string | null | undefined): string | null {\n if (mcc == null) return null;\n let n: number;\n if (typeof mcc === \"string\") {\n const trimmed = mcc.trim();\n if (trimmed === \"\" || !/^\\d+$/.test(trimmed)) return null;\n n = parseInt(trimmed, 10);\n } else {\n n = mcc;\n }\n if (!Number.isFinite(n)) return null;\n return MCC_TO_ISO[n] ?? null;\n}\n\n/**\n * Reverse: get all MCCs for a given ISO 3166-1 alpha-2 country code.\n */\nexport function isoToMccs(iso: string): number[] {\n const upper = iso.toUpperCase();\n return Object.entries(MCC_TO_ISO)\n .filter(([, v]) => v === upper)\n .map(([k]) => Number(k));\n}\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 *\n * The array is a HISTORY: closed periods carry an `endDate`, the current one\n * does not. OCS happens to return it newest-first today, but that is ordering\n * we do not control, so prefer the open period explicitly and only fall back\n * to the first readable entry. A subscriber whose history begins with a closed\n * \"Manually Suspended\" period is Active, and must not read as suspended.\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)) return null;\n\n let fallback: string | null = null;\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const rec = entry as Record<string, unknown>;\n const name = rec.status;\n if (typeof name !== \"string\" || name.length === 0) continue;\n\n const endDate = rec.endDate;\n const open = endDate === undefined || endDate === null || endDate === \"\";\n if (open) return name;\n if (fallback === null) fallback = name;\n }\n return fallback;\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 * Default OCS account for a BYO / reseller-token org — one implementation that\n * the REST API, the MCP Worker and the stdio CLI all import.\n *\n * WHY THIS FILE EXISTS\n * OCS `listSubscriber` refuses a request with no search key (HTTP 200 +\n * `{\"code\":2,\"msg\":\"Missing one search key\"}`) and it takes NO `resellerId`,\n * so there is no \"list the whole reseller\" call. The Console fleet page asks\n * for `/v1/subscribers?page=1&limit=50` with no filter. A BYO org that\n * connected an OCS token but never got a `preferred_account_id` in KV\n * (eSIMMY, 2026-09-29) therefore got a 400 and a dead Fleet page.\n *\n * Instead of asking the customer to fix KV by hand, Carrier now looks the\n * account up itself: `listResellerAccount` (accepts `resellerId` only) lists\n * the accounts under the token's reseller, and we pick one.\n *\n * WHICH ACCOUNT\n * - exactly one account: that one, no ambiguity.\n * - several: the LOWEST id. OCS carries no \"main account\" flag, so the oldest\n * account is the only deterministic choice. Callers get `accountCount` back\n * so a partial view can be labelled as partial instead of passing for the\n * whole fleet.\n *\n * NEVER A QUIET EMPTY LIST\n * Every failure is a typed result the caller turns into an error response.\n * Nothing here returns \"no subscribers\" for \"could not find out\".\n */\n\n/** Keys OCS accepts as a `listSubscriber` search key. */\nexport const OCS_SUBSCRIBER_SEARCH_KEYS = [\n \"accountId\",\n \"iccid\",\n \"imsi\",\n \"msisdn\",\n \"activationCode\",\n \"iccidPrefix\",\n \"imsiPrefix\",\n \"msisdnPrefix\",\n] as const;\n\n/** True when `params` carries at least one key OCS accepts as a subscriber search key. */\nexport function hasSubscriberSearchKey(params: Record<string, unknown>): boolean {\n return OCS_SUBSCRIBER_SEARCH_KEYS.some((key) => {\n const v = params[key];\n return v !== undefined && v !== null && String(v).trim() !== \"\";\n });\n}\n\nexport interface OcsAccountRef {\n id: number;\n name?: string;\n}\n\n/**\n * Flatten a `listResellerAccount` response.\n *\n * Live OCS nests accounts under `reseller[].account`; older snapshots had a\n * top-level `account` array or a bare array. A payload can carry both nested\n * and top-level accounts, so they are concatenated rather than `??`-chained.\n */\nexport function flattenResellerAccounts(raw: unknown): OcsAccountRef[] {\n const records: unknown[] = [];\n if (Array.isArray(raw)) {\n records.push(...raw);\n } else if (raw !== null && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n if (Array.isArray(obj[\"reseller\"])) {\n for (const entry of obj[\"reseller\"] as unknown[]) {\n const accounts = (entry as Record<string, unknown> | null)?.[\"account\"];\n if (Array.isArray(accounts)) records.push(...accounts);\n }\n }\n if (Array.isArray(obj[\"account\"])) records.push(...(obj[\"account\"] as unknown[]));\n }\n\n const seen = new Set<number>();\n const out: OcsAccountRef[] = [];\n for (const rec of records) {\n if (rec === null || typeof rec !== \"object\") continue;\n const r = rec as Record<string, unknown>;\n const id = Number(r[\"id\"] ?? r[\"accountId\"]);\n if (!Number.isFinite(id) || seen.has(id)) continue;\n seen.add(id);\n out.push(typeof r[\"name\"] === \"string\" ? { id, name: r[\"name\"] } : { id });\n }\n return out;\n}\n\nexport interface DefaultAccountPick {\n accountId: number;\n name?: string;\n /** How many accounts sit under the reseller. More than 1 means this is a partial view. */\n accountCount: number;\n}\n\n/** Pick the default account: the only one, or the lowest id. */\nexport function pickDefaultAccount(accounts: readonly OcsAccountRef[]): DefaultAccountPick | undefined {\n if (accounts.length === 0) return undefined;\n const first = [...accounts].sort((a, b) => a.id - b.id)[0]!;\n return {\n accountId: first.id,\n ...(first.name !== undefined ? { name: first.name } : {}),\n accountCount: accounts.length,\n };\n}\n\nexport type DefaultAccountResult =\n | ({ ok: true } & DefaultAccountPick)\n | { ok: false; reason: \"no_accounts\" | \"lookup_failed\"; message: string };\n\n/** Optional short-lived memo so a Fleet page load does not pay two OCS calls every time. */\nexport interface DefaultAccountCache {\n get(): Promise<DefaultAccountPick | null>;\n put(pick: DefaultAccountPick): Promise<void>;\n}\n\n/** Cache TTL. Short on purpose: an account created in OCS shows up within the hour. */\nexport const DEFAULT_ACCOUNT_CACHE_TTL_SECONDS = 3600;\n\n/**\n * KV key for the memo. The reseller id is part of it so reconnecting a\n * different OCS token cannot serve the previous reseller's account.\n */\nexport function defaultAccountCacheKey(orgId: string, resellerId?: string | number): string {\n return `default-account:${orgId}:${resellerId === undefined || resellerId === \"\" ? \"unknown\" : String(resellerId)}`;\n}\n\nexport const NO_ACCOUNTS_MESSAGE =\n \"OCS requires a subscriber search key, and no default account could be chosen because \" +\n \"this reseller has no accounts yet. Create an account in OCS, or pass accountId / \" +\n \"set preferred_account_id once one exists.\";\n\nexport function lookupFailedMessage(detail: string): string {\n return (\n \"OCS requires a subscriber search key, and this organization's default account could not be \" +\n `looked up (${detail}). Pass accountId, or set preferred_account_id, or retry.`\n );\n}\n\nexport interface ResolveDefaultAccountOptions {\n /** One OCS call. Must throw when OCS or the transport fails. */\n call: (method: string, params: Record<string, unknown>) => Promise<unknown>;\n /** KV `ocs_reseller_id`, when known. Looked up with getResellerInfo otherwise. */\n resellerId?: string | number;\n cache?: DefaultAccountCache;\n}\n\nfunction errorText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Resolve the default account for a reseller-token org.\n *\n * Read-only against OCS (`getResellerInfo`, `listResellerAccount`). The only\n * write is the optional cache.\n */\nexport async function resolveDefaultAccount(\n opts: ResolveDefaultAccountOptions,\n): Promise<DefaultAccountResult> {\n const cached = await opts.cache?.get().catch(() => null);\n if (cached && Number.isFinite(cached.accountId)) return { ok: true, ...cached };\n\n let resellerId: number | undefined;\n const given = Number(opts.resellerId);\n if (opts.resellerId !== undefined && opts.resellerId !== \"\" && Number.isFinite(given)) {\n resellerId = given;\n } else {\n try {\n const info = (await opts.call(\"getResellerInfo\", {})) as { id?: unknown } | null;\n const id = Number(info?.id);\n if (Number.isFinite(id)) resellerId = id;\n } catch {\n // Fall through: listResellerAccount below reports the real failure.\n }\n }\n\n let raw: unknown;\n try {\n raw = await opts.call(\"listResellerAccount\", resellerId !== undefined ? { resellerId } : {});\n } catch (err) {\n return { ok: false, reason: \"lookup_failed\", message: lookupFailedMessage(errorText(err)) };\n }\n\n const pick = pickDefaultAccount(flattenResellerAccounts(raw));\n if (!pick) return { ok: false, reason: \"no_accounts\", message: NO_ACCOUNTS_MESSAGE };\n\n await opts.cache?.put(pick).catch(() => undefined);\n return { ok: true, ...pick };\n}\n\n/**\n * Human note for a response that was scoped by a resolved default account.\n * Names the account and, when there are others, says the view is partial.\n */\nexport function defaultAccountNote(pick: DefaultAccountPick): string {\n const label = pick.name ? `${pick.accountId} (${pick.name})` : String(pick.accountId);\n return pick.accountCount > 1\n ? `No account was specified, so this shows account ${label} only — the lowest of ${pick.accountCount} accounts under this reseller. ` +\n \"Pass accountId to see another account.\"\n : `No account was specified, so this shows account ${label}, the only account under this reseller.`;\n}\n\nexport interface ScopeListSubscriberOptions {\n /** Params already built from the caller's filters (and, for managed orgs, already pinned). */\n params: Record<string, unknown>;\n /** Managed orgs are pinned elsewhere; nothing is resolved for them. */\n managed?: boolean;\n /** KV `preferred_account_id`, when set. Always beats a lookup. */\n preferredAccountId?: string;\n resellerId?: string | number;\n call: ResolveDefaultAccountOptions[\"call\"];\n cache?: DefaultAccountCache;\n}\n\nexport type ScopeListSubscriberResult =\n | { ok: true; params: Record<string, unknown>; defaultAccount?: DefaultAccountPick & { note: string } }\n | { ok: false; reason: \"no_accounts\" | \"lookup_failed\"; message: string };\n\n/**\n * The one rule for \"this listSubscriber has no search key\", shared by the MCP\n * Worker and the stdio CLI so Console, CLI and connector behave the same:\n *\n * caller key > KV preferred_account_id > looked-up default account > error\n */\nexport async function scopeListSubscriberParams(\n opts: ScopeListSubscriberOptions,\n): Promise<ScopeListSubscriberResult> {\n if (opts.managed || hasSubscriberSearchKey(opts.params)) return { ok: true, params: opts.params };\n\n const preferred = Number(opts.preferredAccountId);\n if (opts.preferredAccountId !== undefined && opts.preferredAccountId.trim() !== \"\" && Number.isFinite(preferred)) {\n return { ok: true, params: { ...opts.params, accountId: preferred } };\n }\n\n const resolved = await resolveDefaultAccount({\n call: opts.call,\n ...(opts.resellerId !== undefined ? { resellerId: opts.resellerId } : {}),\n ...(opts.cache ? { cache: opts.cache } : {}),\n });\n if (!resolved.ok) return resolved;\n const { ok: _ok, ...pick } = resolved;\n return {\n ok: true,\n params: { ...opts.params, accountId: pick.accountId },\n defaultAccount: { ...pick, note: defaultAccountNote(pick) },\n };\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\", \"packages\"],\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 * Server-side counting for OCS list results — the arithmetic the model is not\n * asked to do.\n *\n * WHY THIS EXISTS\n * ---------------\n * `list_package_templates` returns all 55 templates, untruncated and verified\n * against live OCS. Asked \"how many are there?\", Haiku answered 42; asked how\n * many of those are recurring, it answered 18. Live truth is 55 and 36.\n * Neither wrong number is a subset of anything — it is a model tallying a\n * 55-object JSON dump by eye, which is not a job a language model does\n * reliably and not a job it should be given when the server is holding the\n * array.\n *\n * So the server counts. Every list tool returning an OCS envelope emits a\n * `summary` block ahead of its rows: the row total, plus a tally of the few\n * low-cardinality fields a caller actually aggregates on. The model reads a\n * number instead of deriving one.\n *\n * WHY NOT A COUNT TOOL\n * --------------------\n * A `count_package_templates` tool would fix exactly these two questions, add\n * a 123rd entry to a catalogue whose size already costs tool-selection\n * accuracy, and only ever answer the counting question someone thought to\n * build. It would also be a second code path to the same OCS data, which is a\n * second chance to get tenant scoping wrong. Attaching the count to the read\n * that already happened costs no tool, no extra OCS call, and inherits that\n * read's scoping exactly.\n *\n * TENANCY\n * -------\n * `ocsListSummary` is pure: it counts the rows handed to it and fetches\n * nothing. Call it on the payload AFTER `scopedOcsCall` has filtered it and\n * the summary describes precisely what the caller is allowed to see. Counting\n * before scoping would leak fleet size across tenants — there is no code path\n * in this module that can reach OCS to do that.\n */\n\nimport { ocsArray, OCS_LIST_KEYS, type OcsListKind } from \"./ocs-array.js\";\n\n/** Distinct values a facet may have before only its cardinality is reported. */\nexport const FACET_TALLY_MAX = 25;\n\nexport interface OcsFacetSpec {\n /** Key this facet appears under in `summary.breakdown`. */\n readonly label: string;\n /** Dotted paths into a row, tried in order; the first defined value wins. */\n readonly paths: readonly string[];\n}\n\n/**\n * Facets per list kind. Deliberately short: a facet earns its place when a\n * human would plausibly ask \"how many X are Y\", and only for fields whose\n * value set is small enough to print.\n *\n * The template paths are the OCS field names as captured off the live fleet\n * (`tests/fixtures/response-shapes/list_package_templates.json`) — `recurring`\n * is a root boolean, and the zone name lives nested under `rdbLocationZones`,\n * not at the root beside `locationzoneid`.\n */\nexport const OCS_LIST_FACETS: Record<OcsListKind, readonly OcsFacetSpec[]> = {\n templates: [\n { label: \"recurring\", paths: [\"recurring\"] },\n {\n label: \"locationZone\",\n paths: [\n \"rdbLocationZones.locationzonename\",\n \"locationzonename\",\n \"locationzoneid\",\n ],\n },\n ],\n subscribers: [{ label: \"status\", paths: [\"status\"] }],\n packages: [\n { label: \"status\", paths: [\"status\"] },\n { label: \"recurring\", paths: [\"recurring\"] },\n ],\n zones: [],\n resellers: [],\n};\n\n/**\n * Child collections to count inside each row.\n *\n * `listResellerAccount` nests the accounts under each reseller, so a bare row\n * count answers \"how many resellers\", which is 1, when every real question\n * about that tool is about the 2 accounts underneath. Shipping `total: 1` with\n * \"use these figures directly\" in front of that payload is a worse lie than\n * saying nothing: it was measured breaking two eval questions that had been\n * passing. Counting the nesting is the fix — this is the documented trap of\n * that tool, so the summary is the right place to disarm it.\n */\nexport const OCS_LIST_NESTED: Partial<Record<OcsListKind, { label: string; path: string }>> = {\n resellers: { label: \"accounts\", path: \"account\" },\n};\n\nexport interface OcsFacetSummary {\n /** Distinct values seen across the rows. */\n distinct: number;\n /** Rows per value — omitted when `distinct` exceeds FACET_TALLY_MAX. */\n counts?: Record<string, number>;\n /** Rows where the field was absent or null. Omitted when zero. */\n missing?: number;\n}\n\nexport interface OcsListSummary {\n /**\n * Rows in the envelope, counted by the server. Authoritative for what is in\n * this result: use it, do not recount the rows below. When `moreAvailable`\n * is true it is the size of this page, not of the fleet.\n */\n total: number;\n /**\n * True when OCS says it holds rows this result does not carry — `hasMore`,\n * or an `nbFound` larger than the rows returned. Present only when true.\n *\n * Without this the note would tell a paged `list_subscribers` result that it\n * is complete, which is exactly the kind of confidently wrong number this\n * whole change exists to stop producing.\n */\n moreAvailable?: true;\n /** OCS `nbFound`: rows matching upstream, before paging. Present when OCS reports it. */\n upstreamTotal?: number;\n /**\n * Rows inside the rows, where the envelope nests — `{ accounts: 2 }` for\n * listResellerAccount. When this is present it is usually the number the\n * question is actually about, and `total` is not.\n */\n nested?: Record<string, number>;\n breakdown?: Record<string, OcsFacetSummary>;\n note: string;\n}\n\n/** OCS paging hints, when the envelope carries them (listSubscriber does). */\nfunction pagingHints(data: unknown): { hasMore: boolean; nbFound: number | undefined } {\n if (data === null || typeof data !== \"object\" || Array.isArray(data)) {\n return { hasMore: false, nbFound: undefined };\n }\n const obj = data as Record<string, unknown>;\n return {\n hasMore: obj.hasMore === true,\n nbFound: typeof obj.nbFound === \"number\" ? obj.nbFound : undefined,\n };\n}\n\nfunction readPath(row: Record<string, unknown>, path: string): unknown {\n let cur: unknown = row;\n for (const seg of path.split(\".\")) {\n if (cur === null || typeof cur !== \"object\") return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/** Facet key for a raw value, or null when the value is not summarisable. */\nfunction facetKey(v: unknown): string | null {\n if (v === null || v === undefined) return null;\n if (typeof v === \"boolean\" || typeof v === \"number\") return String(v);\n if (typeof v === \"string\") return v;\n return null;\n}\n\n/**\n * Count an OCS list envelope: row total plus a tally of the declared facets.\n *\n * Pure — it counts what it is handed. See the tenancy note above.\n */\nexport function ocsListSummary(\n data: unknown,\n kind: OcsListKind,\n facets: readonly OcsFacetSpec[] = OCS_LIST_FACETS[kind],\n): OcsListSummary {\n const rows = ocsArray<Record<string, unknown>>(data, kind);\n\n const breakdown: Record<string, OcsFacetSummary> = {};\n for (const facet of facets) {\n const counts = new Map<string, number>();\n let missing = 0;\n\n for (const row of rows) {\n if (row === null || typeof row !== \"object\") {\n missing += 1;\n continue;\n }\n let key: string | null = null;\n for (const path of facet.paths) {\n key = facetKey(readPath(row, path));\n if (key !== null) break;\n }\n if (key === null) {\n missing += 1;\n continue;\n }\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n\n if (counts.size === 0 && missing === 0) continue;\n\n const entry: OcsFacetSummary = { distinct: counts.size };\n if (counts.size > 0 && counts.size <= FACET_TALLY_MAX) {\n // Sorted, so the same fleet renders the same block every call and\n // \"first alphabetically\" is answerable by reading rather than re-sorting.\n entry.counts = Object.fromEntries(\n [...counts].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n );\n }\n if (missing > 0) entry.missing = missing;\n breakdown[facet.label] = entry;\n }\n\n const { hasMore, nbFound } = pagingHints(data);\n const more = hasMore || (nbFound !== undefined && nbFound > rows.length);\n\n const summary: OcsListSummary = {\n total: rows.length,\n note:\n `Counted server-side: ${rows.length} row${rows.length === 1 ? \"\" : \"s\"} in this result. ` +\n \"Use these figures directly rather than recounting the rows below. \" +\n (more\n ? \"This is ONE PAGE — OCS holds more rows than are returned here\" +\n (nbFound !== undefined ? ` (${nbFound} match upstream)` : \"\") +\n \", so do not report `total` as a fleet-wide figure; narrow the filters or page for the rest.\"\n : \"Nothing was truncated: these are all the rows OCS returned.\"),\n };\n\n if (more) {\n summary.moreAvailable = true;\n if (nbFound !== undefined) summary.upstreamTotal = nbFound;\n }\n\n const nest = OCS_LIST_NESTED[kind];\n if (nest) {\n const n = rows.reduce((acc, row) => {\n const child = row && typeof row === \"object\" ? readPath(row, nest.path) : undefined;\n return acc + (Array.isArray(child) ? child.length : 0);\n }, 0);\n summary.nested = { [nest.label]: n };\n summary.note +=\n ` This envelope NESTS: \\`total\\` counts the outer rows, while there are ${n} ` +\n `${nest.label} inside them under \\`${nest.path}\\`. A question about ${nest.label} ` +\n `is answered by that figure, not by \\`total\\`.`;\n }\n\n if (Object.keys(breakdown).length > 0) {\n summary.breakdown = breakdown;\n summary.note +=\n \" `breakdown` gives, per field, how many distinct values occur and how many rows carry each \" +\n \"(`missing` counts rows where the field is absent).\";\n }\n\n return summary;\n}\n\n/**\n * The OCS payload with its `summary` in front of it.\n *\n * Additive and summary-first: the envelope and every row are untouched, and a\n * model reading the result meets the count before the rows rather than after.\n */\nexport function withOcsListSummary<T>(\n data: T,\n kind: OcsListKind,\n facets?: readonly OcsFacetSpec[],\n): T & { summary: OcsListSummary } {\n const summary = ocsListSummary(data, kind, facets);\n\n if (data !== null && typeof data === \"object\" && !Array.isArray(data)) {\n return { summary, ...(data as object) } as T & { summary: OcsListSummary };\n }\n\n // A bare array (recorded fixtures, older OCS) has nowhere to hang a key.\n // Give it the envelope it should have had rather than dropping the count.\n return { summary, [OCS_LIST_KEYS[kind][0]]: data } as unknown as T & {\n summary: OcsListSummary;\n };\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 * NEVER SILENT, EITHER\n * --------------------\n * Degrading gracefully was right. Degrading INVISIBLY is what let Carrier ship\n * an AI product that never once ran in production: the deployed IAM user could\n * not invoke the configured inference profile, so every call fell back, returned\n * HTTP 200 and told nobody for two months.\n *\n * So a fallback is a reportable event, not only a return value. `onFallback`\n * hands the caller the reason, the underlying error, and whether the cause is\n * PERMANENT — an IAM denial fails identically on every future call, a throttle\n * does not. Treating those alike is how a real outage hides inside normal noise.\n *\n * The observer stays transport-free and is wrapped in try/catch, so this module\n * still needs no Sentry, no AWS and no Worker bindings, and a broken observer\n * cannot break the never-throws contract above.\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 /** IAM refused the call. Permanent: it fails identically next time. */\n | \"access_denied\"\n /** The configured model id cannot be invoked as configured. Permanent. */\n | \"model_unavailable\"\n | \"tier\"\n | \"budget_exhausted\"\n | \"rate_limited\"\n | \"timeout\"\n | \"error\"\n | \"no_facts\"\n | \"malformed\"\n | \"invented_number\";\n\n/**\n * Reasons that describe a broken deployment rather than a normal condition.\n *\n * This distinction is the point. `tier` and `budget_exhausted` are the product\n * working as sold; `rate_limited` is weather. These three mean nobody is getting\n * the AI they paid for and no future call will fix itself, so an operator should\n * be paged on them and on nothing else.\n *\n * `no_credentials` is in the list because it is only reachable once the feature\n * flag is on: a deployment that has enabled the model path and has no AWS keys\n * is misconfigured, not merely idle.\n */\nexport const PERMANENT_FALLBACK_REASONS: readonly FallbackReason[] = [\n \"no_credentials\",\n \"access_denied\",\n \"model_unavailable\",\n];\n\nexport function isPermanentFallback(reason: FallbackReason | undefined): boolean {\n return reason !== undefined && PERMANENT_FALLBACK_REASONS.includes(reason);\n}\n\n/**\n * Reasons worth a warning rather than a page.\n *\n * These are per-call defects — Bedrock failed, timed out, or answered something\n * unusable. One is a blip; a sustained rate of them is an incident. Everything\n * not in this list and not in PERMANENT_FALLBACK_REASONS is the product\n * behaving as sold and reaches Sentry never, because paging on `tier` or\n * `budget_exhausted` is how the alert that matters gets muted.\n *\n * It lives beside PERMANENT_FALLBACK_REASONS so the MCP composites and the REST\n * API grade a fallback the same way. Two surfaces with two severity tables is\n * how one of them ends up paging on a throttle.\n */\nexport const SENTRY_WARN_FALLBACK_REASONS: readonly FallbackReason[] = [\n \"error\",\n \"timeout\",\n \"malformed\",\n \"invented_number\",\n];\n\n/** True when `reason` deserves a Sentry event at all, at any level. */\nexport function shouldReportFallback(reason: FallbackReason, permanent: boolean): boolean {\n return permanent || SENTRY_WARN_FALLBACK_REASONS.includes(reason);\n}\n\n/** What `onFallback` is told. Enough to report without re-deriving anything. */\nexport interface FallbackEvent {\n /** The composite that asked for analysis, e.g. \"high_cost_subscribers\". */\n kind: string;\n reason: FallbackReason;\n /** True when the cause is a misconfiguration, per PERMANENT_FALLBACK_REASONS. */\n permanent: boolean;\n /** The thrown value, when the fallback came from a failed call. */\n error?: unknown;\n}\n\n/**\n * Map a thrown value to the reason that describes it.\n *\n * Rate limiting is checked first because @carrier/ai marks it explicitly\n * (`isRateLimit`), and mistaking a throttle for a misconfiguration is exactly\n * the false alarm that gets an alert muted.\n *\n * The two permanent buckets are separated deliberately. `access_denied` means\n * this credential may not call Bedrock and is fixed in IAM. `model_unavailable`\n * means this model id cannot be invoked the way it is configured — the shape of\n * the June 2026 outage, where an inference-profile-only model was set as\n * BEDROCK_MODEL_ID — and is fixed in config. Sending an operator to the wrong\n * console makes an outage longer, not shorter.\n */\nexport function classifyModelError(err: unknown): FallbackReason {\n if (typeof err !== \"object\" || err === null) return \"error\";\n if (\"isRateLimit\" in err) return \"rate_limited\";\n\n const name = \"name\" in err ? String((err as { name: unknown }).name) : \"\";\n const message = err instanceof Error ? err.message : String(err);\n const status =\n \"status\" in err ? Number((err as { status: unknown }).status) : Number.NaN;\n\n if (\n name === \"AbortError\" ||\n name === \"TimeoutError\" ||\n /timed? ?out|aborted/i.test(message)\n ) {\n return \"timeout\";\n }\n if (\n status === 403 ||\n /AccessDenied|not authori[sz]ed|UnrecognizedClient|InvalidSignature|ExpiredToken|InvalidClientTokenId/i.test(\n message,\n )\n ) {\n return \"access_denied\";\n }\n if (\n status === 404 ||\n /ValidationException|ResourceNotFound|inference profile|on-demand throughput/i.test(\n message,\n )\n ) {\n return \"model_unavailable\";\n }\n return \"error\";\n}\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 *\n * Also catches a placeholder `substituteFacts` could not resolve. The model\n * is only allowed to cite keys from the supplied fact list; a `{key}` that\n * survives substitution means it cited a key that does not exist (a typo, a\n * hallucinated key name, or a stale prompt/fact-set mismatch). Such a key\n * carries no digits of its own — `{sampled_subscribers}` — so the bare-number\n * check above never sees it, and the raw template text was shipped straight\n * to the console verified live 2026-09-01. An unresolved placeholder is\n * exactly the \"model wrote something the server did not verify\" case this\n * guard exists to catch, so it is treated the same as an invented number.\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 if (/\\d/.test(stripped)) return true;\n return /\\{[a-z0-9_]+\\}/i.test(rendered);\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\nexport interface AnalyzeOptions {\n enabled?: boolean;\n reason?: FallbackReason;\n /**\n * Called once for every deterministic return, including the gate reasons.\n *\n * Every path, not only the failures: a surface that reports nothing when it\n * refuses on tier or budget cannot answer \"how often does the model actually\n * run?\", which is the question that went unasked for two months. The caller\n * decides which reasons deserve an alert and which only a counter — see\n * `isPermanentFallback`.\n *\n * Anything this throws is swallowed. Observability must never be able to turn\n * a degraded report into a broken tool.\n */\n onFallback?: (event: FallbackEvent) => void;\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: AnalyzeOptions = {},\n): Promise<AnalysisResult> {\n /** Report, then return. One helper so no fallback path can forget to report. */\n const fall = (reason: FallbackReason, error?: unknown): AnalysisResult => {\n if (opts.onFallback) {\n try {\n opts.onFallback({\n kind: req.kind,\n reason,\n permanent: isPermanentFallback(reason),\n ...(error !== undefined ? { error } : {}),\n });\n } catch {\n // See AnalyzeOptions.onFallback: a broken observer is not an outage.\n }\n }\n return fromDeterministic(req, reason);\n };\n\n if (opts.reason) return fall(opts.reason);\n if (opts.enabled === false) return fall(\"disabled\");\n if (!invoke) return fall(\"no_credentials\");\n if (req.facts.length === 0) return fall(\"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 // The classifier replaces a two-way rate-limited/error split that collapsed\n // an IAM denial, a bad model id, a timeout and a genuine transient into one\n // indistinguishable \"error\". That collapse is why the June outage looked\n // like noise.\n return fall(classifyModelError(err), err);\n }\n\n if (!out?.toolInput) return fall(\"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 fall(\"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 fall(\"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/** Only non-array records can carry an explicit measurement. */\nfunction usageRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction measuredRowBytes(row: unknown): number | null {\n if (!usageRecord(row)) return null;\n let value: unknown;\n if (usageRecord(row.total) && \"quantityPerType\" in row.total) {\n const perType = row.total.quantityPerType;\n if (!usageRecord(perType) || !Object.hasOwn(perType, OCS_DATA_QUANTITY_TYPE)) return null;\n value = perType[OCS_DATA_QUANTITY_TYPE];\n } else {\n const key = [\"dataBytes\", \"dataVolume\", \"totalData\"].find((key) => Object.hasOwn(row, key));\n if (key === undefined) return null;\n value = row[key];\n }\n // Decimal numeric strings are supported by the existing reader; avoid Number's\n // coercion of blank strings, booleans, arrays and non-decimal literals to bytes.\n if (typeof value === \"string\") {\n if (!/^(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(value)) return null;\n value = Number(value);\n }\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\n/**\n * Complete measured bytes, or null when the response cannot prove usage.\n * Empty envelopes/periods have no explicit byte measurement, so are unknown.\n * Unlike the legacy readers, one malformed row invalidates the whole window.\n */\nexport function measuredUsageBytes(data: unknown): number | null {\n let rows: unknown[];\n if (Array.isArray(data)) {\n rows = data;\n } else {\n if (!usageRecord(data) || !Array.isArray(data.usages) || data.usages.length === 0) return null;\n rows = [];\n for (const usage of data.usages) {\n if (!usageRecord(usage) || !Array.isArray(usage.subsPeriodUsages) || usage.subsPeriodUsages.length === 0) return null;\n rows.push(...usage.subsPeriodUsages);\n }\n }\n if (rows.length === 0) return null;\n let sum = 0;\n for (const row of rows) {\n const bytes = measuredRowBytes(row);\n if (bytes === null) return null;\n sum += bytes;\n if (!Number.isFinite(sum)) return null;\n }\n return sum;\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 * Shared building blocks for the nine intelligence composites.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * `apps/mcp-server` and `apps/mcp-stdio` each carried a full copy of\n * `intelligence.ts`. The copies were 675 changed lines apart, and every fix\n * measured against the live fleet landed on the Worker only. Three of those\n * fixes were still missing from the published npm package:\n *\n * 1. ACTIVE filter. OCS returns `status` as an ARRAY of records, so\n * `String(row.status).toUpperCase() === \"ACTIVE\"` matches nothing and the\n * fleet reads as empty. The Worker moved to `extractSubscriberStatus`;\n * the CLI did not.\n *\n * 2. Usage window. `subscriberUsageOverPeriod` counts the range INCLUSIVELY\n * and caps it at 7 days, so a 7-day offset asks for 8 and the call fails.\n * `lastNDaysPeriod` in ocs-params.ts has expressed this correctly all\n * along; not one intelligence call site on either surface used it.\n *\n * 3. Usage parsing. The response is a nested envelope, not a flat array, so\n * an `Array.isArray(usage.data)` guard is never true and the whole block\n * is skipped without an error to explain it. `extractDailyUsage` in\n * usage.ts is the correct reader; the CLI never called it.\n *\n * Prose comments at each call site did not stop any of this — the same lesson\n * ocs-param-shapes.ts and list-subscriber.ts were written to record. One\n * shared implementation is the enforcement.\n */\n\nimport { extractSubscriberStatus } from \"./list-subscriber.js\";\n\n/** Whole days from now until `dateStr`, rounded up. Negative once past. */\nexport function daysUntil(dateStr: string): number {\n const now = new Date();\n const target = new Date(dateStr);\n return Math.ceil((target.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/** Human-readable byte count for report tables. */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`;\n}\n\n/**\n * Is this subscriber row ACTIVE?\n *\n * Goes through {@link extractSubscriberStatus} because OCS nests the name\n * inside an array. Comparing the raw field against a string returns false for\n * every row, which reads as an empty fleet rather than as an error.\n */\nexport function isActiveSubscriber(row: unknown): boolean {\n return (extractSubscriberStatus(row) ?? \"\").toUpperCase() === \"ACTIVE\";\n}\n\n/** The ACTIVE rows of an aggregated subscriber list. */\nexport function filterActiveSubscribers<T>(rows: T[]): T[] {\n return rows.filter((row) => isActiveSubscriber(row));\n}\n\n/**\n * Unwrap `{subscriberList: [...]}` or accept a bare array.\n *\n * Distinct from `unwrapSubscriberList` in list-subscriber.ts, which reports\n * whether it recognised the shape so callers can pass an unknown payload\n * through untouched. Here an unrecognised payload contributes no rows, which\n * is what an aggregating fan-out wants.\n */\nexport function subscriberRows(raw: unknown): Record<string, unknown>[] {\n if (Array.isArray(raw)) return raw as Record<string, unknown>[];\n const list = (raw as { subscriberList?: Record<string, unknown>[] } | null)?.subscriberList;\n return Array.isArray(list) ? list : [];\n}\n\n/**\n * Read the reseller's own balance from either OcsClient's direct payload or\n * the raw OCS response envelope. Money arrives as a decimal string in live\n * production, while staging fixtures use a number.\n */\nexport function extractResellerBalance(raw: unknown): number | null {\n const direct = raw as { balance?: unknown } | null;\n const enveloped = raw as { getResellerInfo?: { balance?: unknown } } | null;\n const value = direct?.balance ?? enveloped?.getResellerInfo?.balance;\n if (value === undefined || value === null || value === \"\") return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n","/**\n * Response shapes used by `diagnose_subscriber`.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * diagnose_subscriber read five OCS responses as if they were flat objects and\n * arrays. None of them are. Captured live from the Mango fleet (2026-08-29,\n * ICCID 8948010000020241170), every one of those reads was wrong:\n *\n * - `subscriber.status` is a status HISTORY array, not a string, so\n * `String(status).toUpperCase()` produced \"[OBJECT OBJECT],[OBJECT OBJECT]\",\n * which never equals \"ACTIVE\" — every healthy subscriber was reported\n * CRITICAL with \"OCS status is [OBJECT OBJECT] (not ACTIVE)\".\n * - `listSubscriberPrepaidPackages` returns `{ packages: [...] }`, and its\n * rows carry `active` / `useddatabyte` / `pckdatabyte` / `tsexpirationutc`,\n * not `status` / `dataUsed` / `dataLimit` / `expirationDate`.\n * - `subscriberNetworkEventsOverPeriod` returns an object of per-protocol\n * answer arrays (`s6aAnswer`, `gyAnswer`, `mapAnswer`, ...), not one array,\n * so the `Array.isArray` guard skipped the whole check and the tool never\n * looked at the events that actually explain an outage.\n * - the sim id lives at `sim.id`, not `simId`, so the SIM-provider check\n * never ran.\n * - the IMSI lives at `imsiList[].imsi`, not `imsi`, so the HLR check never\n * ran (`imsiFromSubscriberRecord` already knew this; diagnose did not use it).\n *\n * Two of those five turned into false CRITICALs; the other three silently made\n * the diagnosis blind. The shape knowledge belongs here next to `ocsArray` and\n * `extractSubscriberStatus` rather than duplicated in each MCP app.\n */\n\nimport { ocsArray } from \"./ocs-array.js\";\n\n/** Envelope keys seen for listSubscriberPrepaidPackages, most specific first. */\nexport const PREPAID_PACKAGE_KEYS = [\"prepaidPackage\", \"packages\", \"package\"] as const;\n\n/** Per-protocol answer arrays in a subscriberNetworkEventsOverPeriod response. */\nexport const NETWORK_EVENT_KEYS = [\n \"s6aAnswer\",\n \"gyAnswer\",\n \"mapAnswer\",\n \"camelAnswer\",\n \"mocAnswer\",\n \"mtcAnswer\",\n] as const;\n\nfunction positiveSimId(raw: unknown): number | undefined {\n const n = typeof raw === \"number\" ? raw : typeof raw === \"string\" && raw.trim() !== \"\" ? Number(raw) : NaN;\n return Number.isSafeInteger(n) && n > 0 ? n : undefined;\n}\n\n/**\n * Read a subscriber's SIM id.\n *\n * The live `getSingleSubscriber` record nests it at `sim.id`. Older payloads\n * may expose `simId` or `sim_id`, and some callers still wrap the record under\n * `subscriber`. The subscriber's own `id` is a different OCS entity — never\n * use it. `Number(undefined)` is `NaN`, so a missing id must stay `undefined`\n * rather than being forwarded to `getSimProviderStatus` or `changeSimStatus`.\n */\nexport function subscriberSimId(record: unknown): number | undefined {\n if (!record || typeof record !== \"object\") return undefined;\n const rec = record as Record<string, unknown>;\n\n const sim = rec.sim;\n if (sim && typeof sim === \"object\") {\n const simRec = sim as Record<string, unknown>;\n const fromSim = positiveSimId(simRec.id) ?? positiveSimId(simRec.simId) ?? positiveSimId(simRec.sim_id);\n if (fromSim !== undefined) return fromSim;\n }\n\n const flat = positiveSimId(rec.simId) ?? positiveSimId(rec.sim_id);\n if (flat !== undefined) return flat;\n\n const nested = rec.subscriber;\n if (nested && typeof nested === \"object\" && nested !== record) return subscriberSimId(nested);\n return undefined;\n}\n\n/**\n * Read the current SIM provider status from a `getSimProviderStatus` response.\n *\n * The live response is an ARRAY of status periods, each with `status`,\n * `start`, `end`, `profileType` and `statusModification` (CAR-300). Reading\n * `data.simStatus ?? data.status` off it yields `undefined` for every SIM, so\n * callers never saw a status at all.\n *\n * The array is a history. Select the latest period covering now: its start\n * must not be in the future and its end must not have passed. Missing bounds\n * are open; naive OCS timestamps are UTC. Expired or unreadable dated periods\n * are not current. A bare object with `simStatus` or `status` is still accepted. Returns\n * `undefined` when nothing readable is found, so \"no data\" never reads as a\n * status.\n */\nfunction simProviderStatusName(rec: unknown): string | undefined {\n if (!rec || typeof rec !== \"object\") return undefined;\n const r = rec as Record<string, unknown>;\n for (const key of [\"simStatus\", \"status\"] as const) {\n const v = r[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v.trim();\n }\n return undefined;\n}\n\n/** The current provider record, retaining profile metadata from the same period. */\nexport function currentSimProviderPeriod(data: unknown, now: Date = new Date()): Record<string, unknown> | undefined {\n if (!Array.isArray(data)) {\n return simProviderStatusName(data) === undefined ? undefined : data as Record<string, unknown>;\n }\n\n const bound = (raw: unknown): number | undefined => {\n if (raw === undefined || raw === null || raw === \"\") return undefined;\n if (typeof raw === \"number\") return raw;\n if (typeof raw !== \"string\") return NaN;\n const stamp = raw.trim();\n const hasZone = /(?:Z|[+-]\\d{2}:?\\d{2})$/.test(stamp);\n return Date.parse(hasZone ? stamp : `${stamp}Z`);\n };\n const atNow = now.getTime();\n if (!Number.isFinite(atNow)) return undefined;\n let current: { record: Record<string, unknown>; at: number } | undefined;\n for (const entry of data) {\n const status = simProviderStatusName(entry);\n if (status === undefined) continue;\n const rec = entry as Record<string, unknown>;\n const start = bound(rec.start);\n const end = bound(rec.end);\n if (start !== undefined && (!Number.isFinite(start) || start > atNow)) continue;\n if (end !== undefined && (!Number.isFinite(end) || end <= atNow)) continue;\n const at = start ?? -Infinity;\n if (!current || at > current.at) current = { record: rec, at };\n }\n return current?.record;\n}\n\n/** Current provider state, never a scheduled or expired historical state. */\nexport function extractSimProviderStatus(data: unknown, now: Date = new Date()): string | undefined {\n return simProviderStatusName(currentSimProviderPeriod(data, now));\n}\n\n/** Package rows from a listSubscriberPrepaidPackages response. */\nexport function prepaidPackageRows(data: unknown): Record<string, unknown>[] {\n return ocsArray<Record<string, unknown>>(data, PREPAID_PACKAGE_KEYS);\n}\n\n/**\n * Does the OCS row CLAIM to be active?\n *\n * This is the raw flag only. It is NOT the same question as \"does this package\n * still grant anything\" — see `isActivePackage`.\n */\nexport function isFlaggedActive(pkg: Record<string, unknown>): boolean {\n if (typeof pkg.active === \"boolean\") return pkg.active;\n const status = pkg.status;\n if (typeof status === \"string\") return status.toUpperCase() === \"ACTIVE\";\n return false;\n}\n\n/**\n * Has this package passed its expiry?\n *\n * Rows with no readable expiry are NOT treated as expired: an unparseable or\n * absent timestamp is missing information, and silently deciding \"expired\"\n * on missing data would hide live packages the same way `active` hid dead\n * ones. Unknown stays live and is a separate problem to surface.\n */\nexport function isExpiredPackage(\n pkg: Record<string, unknown>,\n now: Date = new Date(),\n): boolean {\n const expiry = packageExpiry(pkg);\n if (!expiry) return false;\n // OCS sends naive UTC stamps (\"2026-08-21T20:03:53\") with no zone marker.\n // Date.parse treats those as LOCAL time, which shifts the verdict by the\n // host offset and makes the same row expired on one box and live on another.\n const hasZone = /(?:Z|[+-]\\d{2}:?\\d{2})$/.test(expiry);\n const ms = Date.parse(hasZone ? expiry : `${expiry}Z`);\n if (!Number.isFinite(ms)) return false;\n return ms <= now.getTime();\n}\n\n/**\n * Is this package actually granting anything right now?\n *\n * `active` IS NOT VALIDITY. Measured live on 2026-09-07 (Mango SIM ...121):\n * package 204532407 expired 2026-08-21 and still carried `active: true`\n * seventeen days later. Filtering on the flag alone therefore counts dead\n * packages as allowance, which is exactly how a subscriber hits\n * EXPIRED_ENTITLEMENT while the console reports a healthy active package.\n *\n * A package is live only when the flag says active AND the expiry has not\n * passed. Both conditions, every time.\n */\nexport function isActivePackage(\n pkg: Record<string, unknown>,\n now: Date = new Date(),\n): boolean {\n return isFlaggedActive(pkg) && !isExpiredPackage(pkg, now);\n}\n\n/**\n * Rows OCS calls active that have in fact expired.\n *\n * These are the silent ones: they inflate the active count and mask a\n * subscriber with no real allowance. Surface them, never just drop them.\n */\nexport function staleActivePackages(\n pkgs: Record<string, unknown>[],\n now: Date = new Date(),\n): Record<string, unknown>[] {\n return pkgs.filter((p) => isFlaggedActive(p) && isExpiredPackage(p, now));\n}\n\nfunction firstNumber(pkg: Record<string, unknown>, keys: readonly string[]): number {\n for (const key of keys) {\n const v = pkg[key];\n if (typeof v === \"number\" && Number.isFinite(v)) return v;\n if (typeof v === \"string\" && v.trim() !== \"\" && Number.isFinite(Number(v))) return Number(v);\n }\n return 0;\n}\n\n/** Bytes consumed on a package. */\nexport function packageDataUsed(pkg: Record<string, unknown>): number {\n return firstNumber(pkg, [\"useddatabyte\", \"dataUsed\", \"dataConsumed\"]);\n}\n\n/** Byte allowance of a package. 0 means unmetered or unknown. */\nexport function packageDataLimit(pkg: Record<string, unknown>): number {\n return firstNumber(pkg, [\"pckdatabyte\", \"dataLimit\", \"dataAllowance\"]);\n}\n\n/** Expiry timestamp of a package, or \"\" when absent. */\nexport function packageExpiry(pkg: Record<string, unknown>): string {\n for (const key of [\"tsexpirationutc\", \"expirationDate\", \"endDate\"] as const) {\n const v = pkg[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v;\n }\n return \"\";\n}\n\n/** Human label for a package — the template name when the row has no name. */\nexport function packageName(pkg: Record<string, unknown>): string {\n if (typeof pkg.name === \"string\" && pkg.name !== \"\") return pkg.name;\n\n const tpl = pkg.packageTemplate;\n if (tpl && typeof tpl === \"object\") {\n const tplName = (tpl as Record<string, unknown>).prepaidpackagetemplatename;\n if (typeof tplName === \"string\" && tplName !== \"\") return tplName;\n }\n\n for (const key of [\"templateId\", \"packageTemplateId\", \"subscriberprepaidpackageid\"] as const) {\n const v = pkg[key];\n if (v !== undefined && v !== null) return String(v);\n }\n return \"unknown\";\n}\n\n/** Flatten a subscriberNetworkEventsOverPeriod response into one event list. */\nexport function networkEventRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) return data as Record<string, unknown>[];\n if (!data || typeof data !== \"object\") return [];\n\n const obj = data as Record<string, unknown>;\n const rows: Record<string, unknown>[] = [];\n for (const key of NETWORK_EVENT_KEYS) {\n // Each answer key holds an array that is itself sometimes an array of\n // arrays (one inner array per OCS page), so flatten one level.\n for (const entry of ocsArray<unknown>(obj[key], [])) {\n if (Array.isArray(entry)) {\n for (const inner of entry) {\n if (inner && typeof inner === \"object\") rows.push(inner as Record<string, unknown>);\n }\n } else if (entry && typeof entry === \"object\") {\n rows.push(entry as Record<string, unknown>);\n }\n }\n }\n return rows;\n}\n\n/** What the network events say went wrong. */\nexport interface NetworkEventSummary {\n /** Total events across every protocol leg. */\n total: number;\n /** Gy answers refusing quota because the bundle is out of credit. */\n creditLimitReached: number;\n /** S6a ULR rejected 5004 — roaming not allowed on that operator. */\n roamingNotAllowed: number;\n /** MAP legs that timed out — 2G/3G signalling not completing. */\n mapTimeouts: number;\n /** Distinct operators seen, for spotting steering ping-pong. */\n operators: string[];\n /** Newest event timestamp, or \"\" when there are none. */\n lastEventTime: string;\n}\n\nfunction eventTime(row: Record<string, unknown>): string {\n for (const key of [\"eventTime\", \"timestamp\", \"date\"] as const) {\n const v = row[key];\n if (typeof v === \"string\" && v !== \"\") return v;\n }\n return \"\";\n}\n\n/**\n * Reduce raw network events to the few signals that explain an outage.\n *\n * These are exactly the signals that were sitting unread in the response while\n * the tool reported a bogus status finding instead.\n */\nexport function summarizeNetworkEvents(rows: Record<string, unknown>[]): NetworkEventSummary {\n const operators = new Set<string>();\n let creditLimitReached = 0;\n let roamingNotAllowed = 0;\n let mapTimeouts = 0;\n let lastEventTime = \"\";\n\n for (const row of rows) {\n const operator = row.operator;\n if (typeof operator === \"string\" && operator !== \"\" && operator !== \"N/A\") operators.add(operator);\n\n const rre = row.rreResponse;\n if (typeof rre === \"string\" && rre.includes(\"CreditLimitReached\")) creditLimitReached++;\n\n if (String(row.code ?? \"\") === \"5004\") roamingNotAllowed++;\n\n if (row.protocol === \"MAP\" && String(row.result ?? \"\").toLowerCase().includes(\"timeout\")) mapTimeouts++;\n\n const t = eventTime(row);\n if (t > lastEventTime) lastEventTime = t;\n }\n\n return {\n total: rows.length,\n creditLimitReached,\n roamingNotAllowed,\n mapTimeouts,\n operators: [...operators].sort(),\n lastEventTime,\n };\n}\n","/**\n * Reconstruct a subscriber's country history from OCS Gy charging events.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * `subscriber_country_history` used to read `country.entered` events from the\n * OCS ring buffer, which only ever contains what the Bridge4IP Relay LU webhook\n * pushed into it. Relay is off for reseller 1170 (`relayGy: false`,\n * `relayLU: false`, read via `getResellerInfo`), and the buffer holds 24 hours\n * at best, so the tool answered \"no cross-border events\" for subscribers that\n * had demonstrably crossed a border.\n *\n * The same answer is already in the OCS itself. `subscriberNetworkEventsOverPeriod`\n * returns `gyAnswer.events` — one Diameter Gy charging record per session\n * Init/Update/Term — and every record carries its own `countryAlpha2`,\n * `countryName`, `operator` and `userLocationInfoMCC`. That is a complete\n * location trail for the queried period, with no webhook and no ring buffer.\n *\n * SHAPE FACTS, captured live 2026-09-06 from ICCID 8948010000020241196\n * (reseller 1170, period 2026-09-01..2026-09-06, 36 Gy events, HTTP 200,\n * `status.code === 0`):\n *\n * - `usedUnits` is a STRING. Observed values across those 36 records were\n * \"0\" (29x), \"232\", \"4267\" and — this is the one that bites — \"N/A\" (5x).\n * Every Init and most Term records carry \"N/A\". `Number(\"N/A\")` is NaN, so\n * a naive sum poisons the whole total to NaN and a naive `> 0` test reads\n * as false. Both are handled by `gyUsedBytes`.\n * - `userLocationInfoMCC` is a STRING too (\"206\", not 206).\n * - `countryAlpha2` is LOWER-case (\"be\", \"nl\"). Callers expect ISO 3166-1\n * alpha-2 upper-case, so it is upper-cased and, when absent, derived from\n * the MCC via `mccToIso`.\n * - Session boundaries interleave. At the NL to BE crossing the BE `Init` was\n * stamped 2026-09-03T10:19:05.951+02:00 and the NL `Term` that closed the\n * old session 2026-09-03T10:19:05.955+02:00 — the Term is 4 ms LATER than\n * the Init that replaced it. Sorting purely by time therefore produces a\n * phantom one-event NL stay wedged between two BE stays. A `Term` records\n * where a session ENDED, not a new arrival, so `collapseGyStays` drops a\n * run made only of `Term` events when the runs on both sides are the same\n * country, then re-collapses. Without that pass the live capture yields\n * four stays (NL, BE, NL, BE) instead of the two that actually happened.\n *\n * It lives here, next to `networkEventRows` and `summarizeNetworkEvents` in\n * `diagnose-shapes.ts`, because those parse the other legs of this same\n * response.\n *\n * Everything here is pure. No network, no env, no OCS client.\n */\n\nimport { mccToIso } from \"@carrier/ocs-spec/mcc-iso\";\n\n/** One Gy charging record, as it arrives under `gyAnswer.events`. */\nexport interface GyEvent {\n eventTime?: unknown;\n countryAlpha2?: unknown;\n countryName?: unknown;\n operator?: unknown;\n userLocationInfoMCC?: unknown;\n userLocationInfoMNC?: unknown;\n usedUnits?: unknown;\n requestType?: unknown;\n [key: string]: unknown;\n}\n\n/** A continuous period spent in one country. */\nexport interface CountryStay {\n /** ISO 3166-1 alpha-2, upper-case. */\n country: string;\n /** OCS `countryName` for the stay, or null when no record carried one. */\n country_name: string | null;\n /** Timestamp of the first Gy record in this country, as OCS wrote it. */\n first_seen: string;\n /** Timestamp of the last Gy record in this country. */\n last_seen: string;\n /** Distinct serving operators seen during the stay, sorted. */\n operators: string[];\n /** Distinct MCCs seen during the stay, sorted. */\n mccs: string[];\n /** Gy records that make up this stay. */\n event_count: number;\n /** Bytes charged during the stay. \"N/A\" records count as 0, never NaN. */\n used_bytes: number;\n}\n\n/** The whole reconstruction for one subscriber. */\nexport interface GyCountryHistory {\n stays: CountryStay[];\n /** Country changes between consecutive stays. */\n transitions: { from: string; to: string; at: string }[];\n /** Gy records used (those carrying both a timestamp and a country). */\n events_considered: number;\n /** Gy records skipped for want of a timestamp or a resolvable country. */\n events_skipped: number;\n /**\n * Records dropped as session-teardown artefacts — a `Term` stamped after the\n * `Init` that replaced it. Counted here so nothing disappears silently;\n * `events_considered` minus this equals the events across `stays`.\n */\n teardown_artifacts_dropped: number;\n}\n\n/**\n * Pull `gyAnswer.events` out of a `subscriberNetworkEventsOverPeriod` response.\n *\n * Accepts the raw OCS envelope (`{ status, subscriberNetworkEventsOverPeriod }`),\n * the unwrapped payload, or a bare `{ gyAnswer }`, because call sites in this\n * repo unwrap to different depths.\n */\nexport function extractGyEvents(response: unknown): GyEvent[] {\n if (!response || typeof response !== \"object\") return [];\n const root = response as Record<string, unknown>;\n\n const candidates: unknown[] = [\n root.subscriberNetworkEventsOverPeriod,\n root.data,\n root,\n ];\n for (const candidate of candidates) {\n if (!candidate || typeof candidate !== \"object\") continue;\n const gy = (candidate as Record<string, unknown>).gyAnswer;\n if (Array.isArray(gy)) {\n return gy.filter((e): e is GyEvent => !!e && typeof e === \"object\");\n }\n if (gy && typeof gy === \"object\") {\n const events = (gy as Record<string, unknown>).events;\n if (Array.isArray(events)) {\n return events.filter((e): e is GyEvent => !!e && typeof e === \"object\");\n }\n }\n }\n return [];\n}\n\n/**\n * Bytes charged on one Gy record.\n *\n * `usedUnits` is a string of bytes, and the OCS writes the literal \"N/A\" on\n * Init records and on most Terms. Returning 0 for anything non-numeric is what\n * keeps a period total from collapsing to NaN.\n */\nexport function gyUsedBytes(event: GyEvent): number {\n const raw = event.usedUnits;\n if (typeof raw === \"number\") return Number.isFinite(raw) ? raw : 0;\n if (typeof raw !== \"string\") return 0;\n const trimmed = raw.trim();\n if (trimmed === \"\" || trimmed.toUpperCase() === \"N/A\") return 0;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : 0;\n}\n\n/** ISO alpha-2 for a Gy record: `countryAlpha2` first, MCC as the fallback. */\nexport function gyCountryIso(event: GyEvent): string | null {\n const alpha2 = event.countryAlpha2;\n if (typeof alpha2 === \"string\" && /^[A-Za-z]{2}$/.test(alpha2.trim())) {\n return alpha2.trim().toUpperCase();\n }\n const mcc = event.userLocationInfoMCC;\n if (typeof mcc === \"string\" || typeof mcc === \"number\") return mccToIso(mcc);\n return null;\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() !== \"\" ? value.trim() : null;\n}\n\nfunction meaningful(value: unknown): string | null {\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n const s = str(value);\n return s && s.toUpperCase() !== \"N/A\" ? s : null;\n}\n\ninterface NormalisedEvent {\n time: string;\n country: string;\n countryName: string | null;\n operator: string | null;\n mcc: string | null;\n requestType: string;\n bytes: number;\n}\n\nfunction normalise(events: GyEvent[]): { rows: NormalisedEvent[]; skipped: number } {\n const rows: NormalisedEvent[] = [];\n let skipped = 0;\n\n for (const event of events) {\n const time = str(event.eventTime);\n const country = gyCountryIso(event);\n if (!time || !country) {\n skipped++;\n continue;\n }\n rows.push({\n time,\n country,\n countryName: meaningful(event.countryName),\n operator: meaningful(event.operator),\n mcc: meaningful(event.userLocationInfoMCC),\n requestType: str(event.requestType) ?? \"\",\n bytes: gyUsedBytes(event),\n });\n }\n\n // Oldest first. Timestamps are ISO-8601 with an offset, so compare them as\n // instants — a +02:00 record and a Z record from the same moment must not\n // swap places just because their strings sort differently.\n rows.sort((a, b) => {\n const ta = Date.parse(a.time);\n const tb = Date.parse(b.time);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n return a.time.localeCompare(b.time);\n });\n\n return { rows, skipped };\n}\n\nfunction runToStay(run: NormalisedEvent[]): CountryStay {\n const operators = new Set<string>();\n const mccs = new Set<string>();\n let countryName: string | null = null;\n let bytes = 0;\n\n for (const row of run) {\n if (row.operator) operators.add(row.operator);\n if (row.mcc) mccs.add(row.mcc);\n if (!countryName && row.countryName) countryName = row.countryName;\n bytes += row.bytes;\n }\n\n const first = run[0]!;\n const last = run[run.length - 1]!;\n\n return {\n country: first.country,\n country_name: countryName,\n first_seen: first.time,\n last_seen: last.time,\n operators: [...operators].sort(),\n mccs: [...mccs].sort(),\n event_count: run.length,\n used_bytes: bytes,\n };\n}\n\nfunction collapseRuns(rows: NormalisedEvent[]): NormalisedEvent[][] {\n const runs: NormalisedEvent[][] = [];\n for (const row of rows) {\n const current = runs[runs.length - 1];\n if (current && current[0]!.country === row.country) current.push(row);\n else runs.push([row]);\n }\n return runs;\n}\n\n/**\n * Collapse a chronological event list into country stays.\n *\n * Two passes. The first merges consecutive same-country records. The second\n * removes the session-teardown artefact described in the file header: a run\n * made entirely of `Term` records, sitting between two runs of one and the\n * same other country, is the tail of a session that has already been replaced,\n * not a visit. It is dropped and its neighbours re-collapsed.\n */\nexport function collapseGyStays(events: GyEvent[]): GyCountryHistory {\n const { rows, skipped } = normalise(events);\n if (rows.length === 0) {\n return {\n stays: [],\n transitions: [],\n events_considered: 0,\n events_skipped: skipped,\n teardown_artifacts_dropped: 0,\n };\n }\n\n let runs = collapseRuns(rows);\n let teardownDropped = 0;\n\n for (let changed = true; changed; ) {\n changed = false;\n for (let i = 1; i < runs.length - 1; i++) {\n const run = runs[i]!;\n if (!run.every((r) => r.requestType.toLowerCase() === \"term\")) continue;\n if (runs[i - 1]![0]!.country !== runs[i + 1]![0]!.country) continue;\n\n teardownDropped += run.length;\n runs = collapseRuns(runs.flatMap((r, idx) => (idx === i ? [] : r)));\n changed = true;\n break;\n }\n }\n\n const stays = runs.map(runToStay);\n const transitions = stays.slice(1).map((stay, idx) => ({\n from: stays[idx]!.country,\n to: stay.country,\n at: stay.first_seen,\n }));\n\n return {\n stays,\n transitions,\n events_considered: rows.length,\n events_skipped: skipped,\n teardown_artifacts_dropped: teardownDropped,\n };\n}\n\n/** One call: raw OCS response in, country history out. */\nexport function gyCountryHistoryFromResponse(response: unknown): GyCountryHistory {\n return collapseGyStays(extractGyEvents(response));\n}\n","/**\n * Network default: which networks a SIM may use, chosen per country.\n *\n * WHY THIS EXISTS\n * ---------------\n * Nothing in provisioning, package templates or activation ever sets a steering\n * list. A SIM with no list is allowed on every network OCS has for the country,\n * so a customer with five networks in Canada gets all five and the phone picks\n * one, not necessarily the cheapest. The wish: the default is \"cheapest available\n * network\", and the customer can override it.\n *\n * This module is PURE. No I/O, no clock unless `now` is passed, no KV. The API\n * route, the MCP tool and the CLI all call these functions, so the three surfaces\n * cannot disagree about what \"cheapest\" means. `tests/network-default-contract`\n * pins that.\n *\n * PRECEDENCE (first hit wins)\n * sim override > package override > org default > system default\n * The system default is `cheapest_available`. It applies to NEW assignments\n * only: nothing here touches a SIM that already has a list.\n *\n * WHAT \"CHEAPEST\" MEANS\n * - lowest `datarate` > 0 in the tariff, per network (OCS: currency per MB),\n * - among rules that are really in force (not hidden, not `active: false`,\n * inside their start/end window at `now`),\n * - ties keep every tied network (redundancy, deterministic order),\n * - only compared inside one currency; a mixed-currency country is not\n * comparable and falls back.\n * This is the wholesale data rate, not the end price a customer pays. Whether\n * that is the right basis is an owner decision, see the design note.\n *\n * NEVER SILENT\n * ------------\n * If the choice cannot be made (no tariff, nothing available, mixed currencies,\n * or an override that is invalid) the plan falls back to `allow_all` and says so\n * in `warnings` with a stable `code`. `fellBackToAll` is true. A caller must show\n * that; it must not present a fallback as a cheapest choice.\n */\n\nexport const NETWORK_DEFAULT_MODES = [\"cheapest_available\", \"all\", \"custom\"] as const;\nexport type NetworkDefaultMode = (typeof NETWORK_DEFAULT_MODES)[number];\n\n/** The mode used when neither the org nor the SIM/package chose one. */\nexport const SYSTEM_NETWORK_DEFAULT: NetworkDefaultMode = \"cheapest_available\";\n\n/** A stored or supplied choice. `custom` needs an existing OCS steering list. */\nexport interface NetworkChoice {\n mode: NetworkDefaultMode;\n /** Required when mode is `custom`. Ignored otherwise. */\n steeringListId?: number;\n}\n\nexport type NetworkChoiceSource =\n | \"sim_override\"\n | \"package_override\"\n | \"org_default\"\n | \"system_default\";\n\nexport type NetworkPlanWarningCode =\n | \"NO_TARIFF_DATA\"\n | \"NO_AVAILABLE_NETWORK\"\n | \"MIXED_CURRENCY\"\n | \"INVALID_CHOICE\"\n | \"NO_COUNTRY\"\n | \"SINGLE_NETWORK\"\n | \"FETCH_FAILED\";\n\nexport interface NetworkPlanWarning {\n code: NetworkPlanWarningCode;\n /** ISO-3166 alpha-2, upper case, when the warning is about one country. */\n country?: string;\n message: string;\n}\n\n/** The tariff fields this module reads. A superset of what OCS sends is fine. */\nexport interface TariffRuleLike {\n operator?: {\n networkId?: number;\n countryIso2?: string;\n operatorName?: string;\n [k: string]: unknown;\n };\n networkid?: number;\n datarate?: number;\n currency?: { currencycode?: string; [k: string]: unknown };\n currencyid?: number;\n active?: boolean;\n hidden?: boolean;\n startdate?: string;\n enddate?: string;\n [k: string]: unknown;\n}\n\nexport interface CandidateNetwork {\n networkId: number;\n operatorName: string;\n /** Lowest positive datarate seen for this network, per MB, in `currency`. */\n datarate: number;\n currency: string;\n}\n\nexport interface CountryCheapest {\n country: string;\n /** Networks tied at the lowest rate. Empty when no choice could be made. */\n networks: CandidateNetwork[];\n /** How many distinct networks were in the running. */\n candidateCount: number;\n warnings: NetworkPlanWarning[];\n}\n\nconst isoOf = (r: TariffRuleLike): string =>\n String(r.operator?.countryIso2 ?? \"\").trim().toUpperCase();\n\nconst networkIdOf = (r: TariffRuleLike): number | undefined => {\n const n = r.operator?.networkId ?? r.networkid;\n return typeof n === \"number\" && Number.isFinite(n) ? n : undefined;\n};\n\n/** Normalise and de-duplicate ISO2 codes; drops anything that is not two letters. */\nexport function normaliseCountries(countries: readonly string[]): string[] {\n const out = new Set<string>();\n for (const c of countries) {\n const iso = String(c ?? \"\").trim().toUpperCase();\n if (/^[A-Z]{2}$/.test(iso)) out.add(iso);\n }\n return [...out].sort();\n}\n\n/**\n * Is this tariff rule really in force at `now`?\n *\n * `active: false` and `hidden: true` are excluded. A missing flag counts as in\n * force, because OCS omits fields it does not use. The start/end window is\n * checked too: the captured OCS fixture shows expired rules that still exist.\n * Unparseable dates are ignored rather than trusted either way.\n */\nexport function isRuleAvailable(rule: TariffRuleLike, now: Date): boolean {\n if (rule.active === false) return false;\n if (rule.hidden === true) return false;\n const t = now.getTime();\n if (rule.startdate) {\n const s = Date.parse(rule.startdate);\n if (Number.isFinite(s) && s > t) return false;\n }\n if (rule.enddate) {\n const e = Date.parse(rule.enddate);\n if (Number.isFinite(e) && e <= t) return false;\n }\n return true;\n}\n\n/**\n * Cheapest available network(s) for one country, from tariff rules.\n * Pure. Never throws. On any doubt it returns no networks plus a warning.\n */\nexport function cheapestAvailableForCountry(\n rules: readonly TariffRuleLike[] | null | undefined,\n country: string,\n now: Date,\n): CountryCheapest {\n const iso = String(country ?? \"\").trim().toUpperCase();\n if (!/^[A-Z]{2}$/.test(iso)) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n { code: \"NO_COUNTRY\", message: `\"${country}\" is not an ISO-3166 alpha-2 country code.` },\n ],\n };\n }\n if (!rules || rules.length === 0) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n {\n code: \"NO_TARIFF_DATA\",\n country: iso,\n message: `No tariff data was available, so ${iso} stays open to all networks. It was not narrowed to the cheapest.`,\n },\n ],\n };\n }\n\n const best = new Map<number, CandidateNetwork>();\n const currencies = new Set<string>();\n for (const r of rules) {\n if (isoOf(r) !== iso) continue;\n if (!isRuleAvailable(r, now)) continue;\n const rate = r.datarate;\n if (typeof rate !== \"number\" || !Number.isFinite(rate) || rate <= 0) continue;\n const id = networkIdOf(r);\n if (id === undefined) continue;\n const currency = String(\n r.currency?.currencycode ?? (r.currencyid !== undefined ? `id:${r.currencyid}` : \"?\"),\n );\n currencies.add(currency);\n const cur = best.get(id);\n if (!cur || rate < cur.datarate) {\n best.set(id, {\n networkId: id,\n operatorName: String(r.operator?.operatorName ?? `network ${id}`),\n datarate: rate,\n currency,\n });\n }\n }\n\n if (best.size === 0) {\n return {\n country: iso,\n networks: [],\n candidateCount: 0,\n warnings: [\n {\n code: \"NO_AVAILABLE_NETWORK\",\n country: iso,\n message: `No network with a usable data rate was found for ${iso}, so it stays open to all networks. It was not narrowed to the cheapest.`,\n },\n ],\n };\n }\n if (currencies.size > 1) {\n return {\n country: iso,\n networks: [],\n candidateCount: best.size,\n warnings: [\n {\n code: \"MIXED_CURRENCY\",\n country: iso,\n message: `Rates for ${iso} are in more than one currency (${[...currencies].sort().join(\", \")}), so they cannot be compared. It stays open to all networks.`,\n },\n ],\n };\n }\n\n const min = Math.min(...[...best.values()].map((n) => n.datarate));\n const networks = [...best.values()]\n .filter((n) => n.datarate === min)\n .sort((a, b) => a.networkId - b.networkId);\n const warnings: NetworkPlanWarning[] = [];\n if (best.size === 1) {\n warnings.push({\n code: \"SINGLE_NETWORK\",\n country: iso,\n message: `Only one network is available for ${iso}, so \"cheapest\" and \"all\" are the same there.`,\n });\n }\n return { country: iso, networks, candidateCount: best.size, warnings };\n}\n\n/** Why a choice is unusable, or null when it is fine. */\nexport function validateNetworkChoice(choice: unknown): string | null {\n if (!choice || typeof choice !== \"object\") return \"A network choice must be an object.\";\n const c = choice as Record<string, unknown>;\n if (!(NETWORK_DEFAULT_MODES as readonly unknown[]).includes(c.mode)) {\n return `mode must be one of: ${NETWORK_DEFAULT_MODES.join(\", \")}.`;\n }\n if (c.mode === \"custom\") {\n const id = c.steeringListId;\n if (typeof id !== \"number\" || !Number.isInteger(id) || id <= 0) {\n return \"mode custom needs steeringListId, a positive integer from list_steering_lists.\";\n }\n }\n return null;\n}\n\nexport interface ResolveInput {\n /** Countries the SIM is expected to be used in. ISO2, any case. */\n countries: readonly string[];\n /** Tariff rules from get_tariff / getCustomerTariff. Null when unavailable. */\n tariffRules: readonly TariffRuleLike[] | null | undefined;\n simOverride?: NetworkChoice | null;\n packageOverride?: NetworkChoice | null;\n orgDefault?: NetworkChoice | null;\n now: Date;\n}\n\nexport type NetworkOutcome = \"restrict\" | \"allow_all\" | \"use_list\";\n\nexport interface NetworkPlan {\n source: NetworkChoiceSource;\n mode: NetworkDefaultMode;\n outcome: NetworkOutcome;\n /** Set when outcome is `use_list`. */\n steeringListId?: number;\n /** Union of the cheapest networks over all countries. Set when outcome is `restrict`. */\n networks: CandidateNetwork[];\n perCountry: CountryCheapest[];\n /** True when cheapest_available was asked for but at least one country could not be narrowed. */\n fellBackToAll: boolean;\n warnings: NetworkPlanWarning[];\n /** What the comparison is based on. Null when no comparison was made. */\n priceBasis: \"wholesale_datarate_per_mb\" | null;\n}\n\n/** First layer that carries a valid choice; an invalid one is reported, then skipped. */\nfunction pickChoice(\n input: ResolveInput,\n): { choice: NetworkChoice; source: NetworkChoiceSource; skipped: NetworkPlanWarning[] } {\n const layers: Array<[NetworkChoiceSource, NetworkChoice | null | undefined]> = [\n [\"sim_override\", input.simOverride],\n [\"package_override\", input.packageOverride],\n [\"org_default\", input.orgDefault],\n ];\n const skipped: NetworkPlanWarning[] = [];\n for (const [source, choice] of layers) {\n if (choice === null || choice === undefined) continue;\n const problem = validateNetworkChoice(choice);\n if (problem) {\n skipped.push({\n code: \"INVALID_CHOICE\",\n message: `The ${source.replace(\"_\", \" \")} is invalid and was ignored: ${problem}`,\n });\n continue;\n }\n return { choice, source, skipped };\n }\n return { choice: { mode: SYSTEM_NETWORK_DEFAULT }, source: \"system_default\", skipped };\n}\n\n/**\n * Decide what a SIM may use. See the module doc for precedence and fallbacks.\n *\n * `custom` and `all` never look at the tariff. Only `cheapest_available` does.\n */\nexport function resolveNetworkPlan(input: ResolveInput): NetworkPlan {\n const { choice, source, skipped } = pickChoice(input);\n const countries = normaliseCountries(input.countries);\n const warnings: NetworkPlanWarning[] = [...skipped];\n\n if (choice.mode === \"custom\") {\n return {\n source,\n mode: \"custom\",\n outcome: \"use_list\",\n steeringListId: choice.steeringListId as number, // validated by pickChoice\n networks: [],\n perCountry: [],\n fellBackToAll: false,\n warnings,\n priceBasis: null,\n };\n }\n if (choice.mode === \"all\") {\n return {\n source,\n mode: \"all\",\n outcome: \"allow_all\",\n networks: [],\n perCountry: [],\n fellBackToAll: false,\n warnings,\n priceBasis: null,\n };\n }\n\n // cheapest_available\n if (countries.length === 0) {\n warnings.push({\n code: \"NO_COUNTRY\",\n message: \"No valid country was given, so nothing could be narrowed. The SIM stays open to all networks.\",\n });\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"allow_all\",\n networks: [],\n perCountry: [],\n fellBackToAll: true,\n warnings,\n priceBasis: null,\n };\n }\n\n const perCountry = countries.map((c) => cheapestAvailableForCountry(input.tariffRules, c, input.now));\n for (const pc of perCountry) warnings.push(...pc.warnings);\n\n const byId = new Map<number, CandidateNetwork>();\n for (const pc of perCountry) for (const n of pc.networks) byId.set(n.networkId, n);\n const networks = [...byId.values()].sort((a, b) => a.networkId - b.networkId);\n const unresolved = perCountry.some((pc) => pc.networks.length === 0);\n\n if (networks.length === 0) {\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"allow_all\",\n networks: [],\n perCountry,\n fellBackToAll: true,\n warnings,\n priceBasis: null,\n };\n }\n return {\n source,\n mode: \"cheapest_available\",\n outcome: \"restrict\",\n networks,\n perCountry,\n fellBackToAll: unresolved,\n warnings,\n priceBasis: \"wholesale_datarate_per_mb\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Turning a plan into an assignment. Creating a steering list has NO OCS REST\n// method (it runs through a minutes-long browser agent), so the applier can\n// only assign a list that already exists. It never invents one silently.\n// ---------------------------------------------------------------------------\n\nexport interface SteeringListSummary {\n id: number;\n name: string;\n /** OCS network ids on the list; empty when the catalogue does not expose them. */\n networkIds: number[];\n}\n\n/** Read steering lists from the raw OCS/REST shape without trusting field names. */\nexport function steeringListsFromRaw(raw: unknown): SteeringListSummary[] {\n const arr = Array.isArray(raw)\n ? raw\n : raw && typeof raw === \"object\"\n ? ((raw as Record<string, unknown>).data ??\n (raw as Record<string, unknown>).steeringLists ??\n (raw as Record<string, unknown>).list ??\n [])\n : [];\n if (!Array.isArray(arr)) return [];\n const out: SteeringListSummary[] = [];\n for (const item of arr) {\n if (!item || typeof item !== \"object\") continue;\n const o = item as Record<string, unknown>;\n const id = Number(o.id ?? o.steeringListId ?? o.steeringlistid);\n if (!Number.isInteger(id) || id <= 0) continue;\n const nets = o.networks ?? o.networkList ?? o.operators ?? [];\n const networkIds = Array.isArray(nets)\n ? nets\n .map((n) => {\n if (typeof n === \"number\") return n;\n if (n && typeof n === \"object\") {\n const r = n as Record<string, unknown>;\n return Number(r.networkId ?? r.networkid ?? r.id);\n }\n return NaN;\n })\n .filter((n) => Number.isInteger(n))\n : [];\n out.push({ id, name: String(o.name ?? o.steeringListName ?? `list ${id}`), networkIds });\n }\n return out;\n}\n\nexport type AssignmentAction =\n | { action: \"assign\"; steeringListId: number; reason: string }\n | { action: \"needs_list\"; networks: CandidateNetwork[]; suggestedName: string; reason: string }\n | { action: \"leave_open\"; reason: string };\n\n/**\n * What to do with a plan, given the lists that exist. Pure.\n *\n * `restrict` needs a list whose network set equals the plan's networks exactly.\n * A superset would let the phone roam onto networks the plan excluded, and a\n * subset would cut coverage, so neither counts as a match.\n */\nexport function planAssignment(\n plan: NetworkPlan,\n lists: readonly SteeringListSummary[],\n): AssignmentAction {\n if (plan.outcome === \"allow_all\") {\n return {\n action: \"leave_open\",\n reason: plan.fellBackToAll\n ? \"Fell back to all networks; see warnings. No steering list is set.\"\n : \"The choice is all networks. No steering list is set.\",\n };\n }\n if (plan.outcome === \"use_list\") {\n const id = plan.steeringListId as number;\n const known = lists.some((l) => l.id === id);\n return known\n ? { action: \"assign\", steeringListId: id, reason: \"Chosen custom list exists.\" }\n : {\n action: \"leave_open\",\n reason: `Steering list ${id} was not found in this reseller's lists, so nothing is assigned.`,\n };\n }\n const want = new Set(plan.networks.map((n) => n.networkId));\n const match = lists.find(\n (l) => l.networkIds.length === want.size && l.networkIds.every((id) => want.has(id)),\n );\n if (match) {\n return {\n action: \"assign\",\n steeringListId: match.id,\n reason: `List \"${match.name}\" holds exactly the cheapest networks.`,\n };\n }\n const countries = plan.perCountry.filter((p) => p.networks.length > 0).map((p) => p.country);\n return {\n action: \"needs_list\",\n networks: plan.networks,\n suggestedName: `cheapest-${countries.join(\"-\").toLowerCase()}`,\n reason:\n \"No existing list holds exactly these networks. Creating one has no REST method; it needs the browser agent or the OCS dashboard.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// One report shape for every surface (REST, MCP worker, MCP stdio, CLI, Console).\n// Each surface only FETCHES raw inputs; this function decides and serialises.\n// ---------------------------------------------------------------------------\n\n/** OCS answers `{ listTariffRule: { rule: [...] } }`; the client may or may not unwrap it. */\nexport function tariffRulesFromRaw(raw: unknown): TariffRuleLike[] | null {\n if (Array.isArray(raw)) return raw as TariffRuleLike[];\n if (raw && typeof raw === \"object\") {\n const obj = raw as Record<string, unknown>;\n for (const key of [\"rule\", \"listTariffRule\", \"rules\", \"data\"]) {\n const v = obj[key];\n if (Array.isArray(v)) return v as TariffRuleLike[];\n if (v && typeof v === \"object\") {\n const nested = (v as Record<string, unknown>).rule;\n if (Array.isArray(nested)) return nested as TariffRuleLike[];\n }\n }\n }\n return null;\n}\n\n/** Stored org setting: one KV record per org, deliberately NOT inside the auth record. */\nexport interface StoredNetworkDefault extends NetworkChoice {\n updatedAt: string;\n updatedBy?: string;\n}\n\nexport const NETWORK_DEFAULT_KEY_PREFIX = \"netdefault:\";\n\n/** KV key for an org, or `clerk_<user>` for a solo operator. Same partition the auth record uses. */\nexport function networkDefaultKey(partition: string): string {\n return `${NETWORK_DEFAULT_KEY_PREFIX}${partition}`;\n}\n\n/** Parse a stored record. Anything invalid reads as \"not set\", never as a partial choice. */\nexport function parseStoredNetworkDefault(raw: unknown): StoredNetworkDefault | null {\n if (!raw || typeof raw !== \"object\") return null;\n const r = raw as Record<string, unknown>;\n if (validateNetworkChoice(r) !== null) return null;\n const choice: StoredNetworkDefault = {\n mode: r.mode as NetworkDefaultMode,\n updatedAt: typeof r.updatedAt === \"string\" ? r.updatedAt : \"\",\n };\n if (r.mode === \"custom\") choice.steeringListId = r.steeringListId as number;\n if (typeof r.updatedBy === \"string\") choice.updatedBy = r.updatedBy;\n return choice;\n}\n\nexport interface NetworkPlanReportInput {\n countries: readonly string[];\n tariffRaw: unknown;\n steeringListsRaw: unknown;\n /** Set by the surface when reading the tariff failed. Turns into a FETCH_FAILED warning. */\n tariffError?: string | null;\n /** Set by the surface when reading the steering lists failed. */\n steeringListsError?: string | null;\n orgDefault?: NetworkChoice | null;\n packageOverride?: NetworkChoice | null;\n simOverride?: NetworkChoice | null;\n now: Date;\n}\n\nexport interface NetworkPlanReport {\n plan: NetworkPlan;\n assignment: AssignmentAction;\n /** Always false: this report only plans. Applying is a separate, confirmed write. */\n applied: false;\n /** One line a human can read without knowing the field names. */\n summary: string;\n}\n\nconst listNetworks = (ns: readonly CandidateNetwork[]): string =>\n ns.map((n) => `${n.operatorName} (${n.datarate} ${n.currency}/MB)`).join(\", \");\n\nexport function buildNetworkPlanReport(input: NetworkPlanReportInput): NetworkPlanReport {\n const plan = resolveNetworkPlan({\n countries: input.countries,\n tariffRules: input.tariffError ? null : tariffRulesFromRaw(input.tariffRaw),\n orgDefault: input.orgDefault ?? null,\n packageOverride: input.packageOverride ?? null,\n simOverride: input.simOverride ?? null,\n now: input.now,\n });\n\n // A failed read is reported even when the chosen mode never needed it, so a\n // broken upstream cannot hide behind a mode that happened not to look.\n if (input.tariffError && plan.mode === \"cheapest_available\") {\n plan.warnings.push({\n code: \"FETCH_FAILED\",\n message: `The tariff could not be read (${input.tariffError}).`,\n });\n }\n if (input.steeringListsError) {\n plan.warnings.push({\n code: \"FETCH_FAILED\",\n message: `The steering lists could not be read (${input.steeringListsError}).`,\n });\n }\n\n let assignment: AssignmentAction;\n if (input.steeringListsError && (plan.outcome === \"restrict\" || plan.outcome === \"use_list\")) {\n assignment = {\n action: \"leave_open\",\n reason: \"The steering lists could not be read, so no list can be matched or checked. Nothing is assigned.\",\n };\n } else {\n assignment = planAssignment(plan, steeringListsFromRaw(input.steeringListsRaw));\n }\n\n let summary: string;\n if (plan.outcome === \"restrict\") {\n summary = `Cheapest available: ${listNetworks(plan.networks)}.`;\n if (plan.fellBackToAll) summary += \" Some countries could not be narrowed and stay open to all networks.\";\n } else if (plan.outcome === \"use_list\") {\n summary = `Custom steering list ${plan.steeringListId}.`;\n } else if (plan.fellBackToAll) {\n summary = \"FELL BACK TO ALL NETWORKS: the cheapest network could not be determined. See warnings.\";\n } else {\n summary = \"All networks are allowed (chosen).\";\n }\n return { plan, assignment, applied: false, summary };\n}\n\n/**\n * KV partition for the stored org default. Mirrors how the auth record is keyed\n * (`org:<orgId>`, or `user:<userId>` for a solo operator) so every surface that\n * knows who the caller is reaches the same record.\n */\nexport function networkDefaultPartition(identity: { orgId?: string | undefined; sub: string }): string {\n if (identity.orgId) return identity.orgId;\n return `user:${identity.sub.replace(/^clerk_/, \"\")}`;\n}\n","/**\n * Network default: the one service every surface calls.\n *\n * REST (apps/api), MCP worker (apps/mcp-server), MCP stdio (apps/mcp-stdio) and\n * the Console proxy all run THESE functions. Each surface supplies only an I/O\n * adapter (how to call OCS, how to reach the stored org default), so the decision,\n * the validation, the warnings and the response shape cannot differ between them.\n * tests/network-default-contract.test.ts pins that no surface re-implements it.\n *\n * Nothing here writes to OCS. `setNetworkDefault` writes ONE stored record through\n * the adapter and touches no SIM.\n */\n\nimport {\n buildNetworkPlanReport,\n networkDefaultKey,\n NETWORK_DEFAULT_MODES,\n parseStoredNetworkDefault,\n steeringListsFromRaw,\n SYSTEM_NETWORK_DEFAULT,\n validateNetworkChoice,\n type NetworkChoice,\n type NetworkPlanReport,\n type NetworkPlanWarning,\n type StoredNetworkDefault,\n} from \"./network-default.js\";\n\n/** What a surface must provide. Throwing from `callOcs` means the OCS call failed. */\nexport interface NetworkDefaultIo {\n /** Read-only OCS calls only: getCustomerTariff, listSteeringList. */\n callOcs(method: \"getCustomerTariff\" | \"listSteeringList\", resellerId: number): Promise<unknown>;\n resolveResellerId(): Promise<number>;\n /**\n * The stored org default, raw. `undefined` means this surface has NO store\n * (local stdio): that is reported, never treated as \"nothing stored\".\n */\n readStored(): Promise<unknown> | undefined;\n /** Persist the record. Absent on surfaces without a store. */\n writeStored?(record: StoredNetworkDefault): Promise<void>;\n}\n\nexport type ServiceResult<T> = { ok: true; data: T } | { ok: false; status: 400 | 501 | 502; error: string; code: string };\n\nconst fail = (status: 400 | 501 | 502, code: string, error: string): { ok: false; status: 400 | 501 | 502; error: string; code: string } => ({\n ok: false,\n status,\n error,\n code,\n});\n\nconst msg = (e: unknown): string => (e instanceof Error ? e.message : String(e));\n\n/** `undefined`/`null` mean \"not given\". Anything else must be a valid choice. */\nexport function parseOptionalChoice(value: unknown, label: string): { choice: NetworkChoice | null } | { error: string } {\n if (value === undefined || value === null) return { choice: null };\n const problem = validateNetworkChoice(value);\n if (problem) return { error: `${label}: ${problem}` };\n const v = value as NetworkChoice;\n return { choice: v.mode === \"custom\" ? { mode: \"custom\", steeringListId: v.steeringListId as number } : { mode: v.mode } };\n}\n\nconst NO_STORE_WARNING: NetworkPlanWarning = {\n code: \"INVALID_CHOICE\",\n message:\n \"This surface has no stored org default (local stdio mode), so it could not be read. Only the system default and the overrides you pass apply here.\",\n};\n\nexport interface NetworkDefaultView {\n modes: typeof NETWORK_DEFAULT_MODES;\n systemDefault: typeof SYSTEM_NETWORK_DEFAULT;\n orgDefault: StoredNetworkDefault | null;\n effectiveMode: string;\n warnings: NetworkPlanWarning[];\n appliesTo: \"new_assignments_only\";\n}\n\nexport async function getNetworkDefault(io: NetworkDefaultIo): Promise<ServiceResult<NetworkDefaultView>> {\n const pending = io.readStored();\n const warnings: NetworkPlanWarning[] = [];\n let stored: StoredNetworkDefault | null = null;\n if (pending === undefined) {\n warnings.push(NO_STORE_WARNING);\n } else {\n const raw = await Promise.resolve(pending).catch(() => null);\n stored = parseStoredNetworkDefault(raw);\n if (raw !== null && raw !== undefined && stored === null) {\n warnings.push({\n code: \"INVALID_CHOICE\",\n message: \"The stored org default is unreadable and was ignored; the system default applies.\",\n });\n }\n }\n return {\n ok: true,\n data: {\n modes: NETWORK_DEFAULT_MODES,\n systemDefault: SYSTEM_NETWORK_DEFAULT,\n orgDefault: stored,\n effectiveMode: stored?.mode ?? SYSTEM_NETWORK_DEFAULT,\n warnings,\n appliesTo: \"new_assignments_only\",\n },\n };\n}\n\nexport async function setNetworkDefault(\n io: NetworkDefaultIo,\n body: unknown,\n actor: string,\n now: Date,\n): Promise<ServiceResult<{ orgDefault: StoredNetworkDefault; appliesTo: \"new_assignments_only\" }>> {\n const parsed = parseOptionalChoice(body, \"body\");\n if (\"error\" in parsed) return fail(400, \"VALIDATION_ERROR\", parsed.error);\n if (parsed.choice === null) return fail(400, \"VALIDATION_ERROR\", \"body: A network choice must be an object.\");\n const choice = parsed.choice;\n if (!io.writeStored) {\n return fail(501, \"NO_STORE\", \"This surface has no store for the org default. Set it through the REST API, the Console or the hosted MCP server.\");\n }\n\n // A custom list must exist for this reseller; refuse an id OCS does not know.\n if (choice.mode === \"custom\") {\n let listsRaw: unknown;\n try {\n listsRaw = await io.callOcs(\"listSteeringList\", await io.resolveResellerId());\n } catch (e) {\n return fail(502, \"OCS_ERROR\", msg(e));\n }\n if (!steeringListsFromRaw(listsRaw).some((l) => l.id === choice.steeringListId)) {\n return fail(400, \"VALIDATION_ERROR\", `Steering list ${choice.steeringListId} was not found. Pick one from list_steering_lists.`);\n }\n }\n\n const record: StoredNetworkDefault = { ...choice, updatedAt: now.toISOString(), updatedBy: actor };\n await io.writeStored(record);\n return { ok: true, data: { orgDefault: record, appliesTo: \"new_assignments_only\" } };\n}\n\nexport interface PlanRequest {\n countries?: unknown;\n simOverride?: unknown;\n packageOverride?: unknown;\n resellerId?: unknown;\n}\n\nexport async function planNetworkDefault(\n io: NetworkDefaultIo,\n req: PlanRequest | null | undefined,\n now: Date,\n): Promise<ServiceResult<NetworkPlanReport>> {\n if (!req || !Array.isArray(req.countries) || req.countries.length === 0) {\n return fail(400, \"VALIDATION_ERROR\", \"countries required: a non-empty array of ISO-3166 alpha-2 codes\");\n }\n const sim = parseOptionalChoice(req.simOverride, \"simOverride\");\n if (\"error\" in sim) return fail(400, \"VALIDATION_ERROR\", sim.error);\n const pkg = parseOptionalChoice(req.packageOverride, \"packageOverride\");\n if (\"error\" in pkg) return fail(400, \"VALIDATION_ERROR\", pkg.error);\n\n let resellerId: number;\n if (req.resellerId !== undefined && req.resellerId !== null) {\n resellerId = Number(req.resellerId);\n if (!Number.isInteger(resellerId) || resellerId <= 0) {\n return fail(400, \"VALIDATION_ERROR\", \"resellerId must be a positive integer\");\n }\n } else {\n try {\n resellerId = await io.resolveResellerId();\n } catch (e) {\n return fail(502, \"OCS_ERROR\", msg(e));\n }\n }\n\n const settle = async (method: \"getCustomerTariff\" | \"listSteeringList\") => {\n try {\n return { data: await io.callOcs(method, resellerId), error: null as string | null };\n } catch (e) {\n return { data: undefined, error: msg(e) };\n }\n };\n const [tariff, lists, view] = await Promise.all([settle(\"getCustomerTariff\"), settle(\"listSteeringList\"), getNetworkDefault(io)]);\n const orgDefault = view.ok && view.data.orgDefault\n ? view.data.orgDefault.mode === \"custom\"\n ? ({ mode: \"custom\", steeringListId: view.data.orgDefault.steeringListId as number } as NetworkChoice)\n : ({ mode: view.data.orgDefault.mode } as NetworkChoice)\n : null;\n\n const report = buildNetworkPlanReport({\n countries: req.countries.map((x) => String(x)),\n tariffRaw: tariff.data,\n tariffError: tariff.error,\n steeringListsRaw: lists.data,\n steeringListsError: lists.error,\n orgDefault,\n packageOverride: pkg.choice,\n simOverride: sim.choice,\n now,\n });\n // A surface without a store must say so on the plan too, not only on `get`.\n if (view.ok) report.plan.warnings.push(...view.data.warnings);\n return { ok: true, data: report };\n}\n\nexport { networkDefaultKey };\n","import { ocsArray } from \"./ocs-array.js\";\n\n/** Narrow server-side catalog grants. Account isolation never implies catalog authority. */\nexport type CatalogCapability = \"location_zone.create\" | \"package_template.create\";\nexport interface CatalogGrantDb {\n prepare(sql: string): { bind(...values: unknown[]): { first<T>(): Promise<T | null> } };\n}\nexport interface CatalogPrincipal {\n orgId?: string | undefined;\n resellerId?: unknown;\n managed?: boolean | undefined;\n db?: CatalogGrantDb | undefined;\n}\nexport interface CatalogDecision {\n orgId: string;\n resellerId: number | undefined;\n capability: CatalogCapability;\n allowed: boolean;\n}\n/** Resolve freshly against the credential actually sent, never caller request fields. */\nexport interface CatalogAuthorization {\n principal(token: string): Promise<CatalogPrincipal>;\n audit?(decision: CatalogDecision): void;\n}\nexport function catalogCapability(method: string): CatalogCapability | undefined {\n if (method === \"createLocationZone\") return \"location_zone.create\";\n if (method === \"createPrepaidPackageTemplate\") return \"package_template.create\";\n return undefined;\n}\nfunction positiveId(value: unknown): number | undefined {\n if (typeof value !== \"number\" && !(typeof value === \"string\" && /^[1-9]\\d*$/.test(value))) return undefined;\n const n = Number(value);\n return Number.isSafeInteger(n) && n > 0 ? n : undefined;\n}\n/**\n * Canonical `listNetworkProfile` rows.\n *\n * The shared client removes the method key, so callers see\n * `{ networkProfile: [...] }`. A raw envelope still nests that object under\n * `listNetworkProfile`. A bare array is the older recorded shape. Anything\n * else, including a row that is not an object, is unrecognised.\n */\nexport function networkProfileRows(raw: unknown): Record<string, unknown>[] | undefined {\n const body = raw !== null && typeof raw === \"object\" && !Array.isArray(raw) && \"listNetworkProfile\" in raw\n ? (raw as { listNetworkProfile?: unknown }).listNetworkProfile : raw;\n const recognised = Array.isArray(body)\n || (body !== null && typeof body === \"object\" && !Array.isArray(body)\n && (\"networkProfile\" in body || \"listNetworkProfile\" in body));\n if (!recognised) return undefined;\n const rows = ocsArray<unknown>(body, [\"networkProfile\"]);\n if (rows.some((row) => row === null || typeof row !== \"object\" || Array.isArray(row))) return undefined;\n return rows as Record<string, unknown>[];\n}\n/** Mutates only template resellerid after exact ownership proof; all errors deny. */\nexport async function authorizeCatalogCreate(\n authorization: CatalogAuthorization | undefined,\n token: string,\n method: string,\n params: Record<string, unknown> | null,\n listProfiles: (resellerId: number) => Promise<unknown>,\n): Promise<boolean> {\n const capability = catalogCapability(method);\n if (!capability || !authorization || !params) return false;\n let principal: CatalogPrincipal = {};\n let resellerId: number | undefined;\n let allowed = false;\n try {\n principal = await authorization.principal(token);\n resellerId = positiveId(principal.resellerId);\n if (principal.managed || !principal.orgId || !principal.db || resellerId === undefined) return false;\n const grant = await principal.db.prepare(\n \"SELECT 1 AS ok FROM org_ocs_catalog_grants WHERE org_id = ? AND reseller_id = ? AND capability = ?\",\n ).bind(principal.orgId, resellerId, capability).first<{ ok: number }>();\n if (grant?.ok !== 1) return false;\n if (capability === \"package_template.create\") {\n // No alternate identity spelling can override the lower-case OCS wire field.\n for (const key of [\"resellerid\", \"resellerId\", \"reseller_id\"]) {\n if (params[key] !== undefined && positiveId(params[key]) !== resellerId) return false;\n }\n params.resellerid = resellerId;\n allowed = true;\n } else {\n const profileId = positiveId(params.networkProfileId);\n if (profileId === undefined) return false;\n // Membership is the authenticated reseller-scoped list. A row resellerId\n // is optional; when the provider supplies one, it must match the grant.\n const rows = networkProfileRows(await listProfiles(resellerId));\n if (!rows) return false;\n const matches = rows.filter((row) => positiveId(row.id) === profileId);\n allowed = matches.length === 1 && matches.every((row) =>\n row.resellerId === undefined || positiveId(row.resellerId) === resellerId);\n }\n return allowed;\n } catch {\n return false;\n } finally {\n // Existing best-effort audit: no tokens, bodies, profile names or TADIGs.\n try { authorization.audit?.({ orgId: principal.orgId ?? \"\", resellerId, capability, allowed }); } catch { /* non-fatal telemetry */ }\n }\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.\n */\n\nexport type LogoMark = {\n svg: string;\n filename: string;\n source: \"svg\";\n pngBase64?: string;\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, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\");\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\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 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;AAUA,eAAsB,cAAc,YAAoB,MAAgC;AACtF,QAAM,OAAO,KAAK,YAAY,gBAAgB;AAC9C,MAAI,CAAE,MAAM,OAAO,IAAI,EAAI,QAAO;AAClC,QAAM,OAAO,MAAM,aAAa,IAAI;AACpC,QAAM,KAAK;AACX,MAAI,CAAC,GAAG,KAAK,IAAI,EAAG,QAAO;AAC3B,QAAM,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,eAAe,KAAK,UAAU,IAAI,CAAC,CAAC;AACtF,MAAI,YAAY,KAAM,QAAO;AAC7B,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;;;AEthBO,IAAM,gBAAuB;AAAA,EAClC,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,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;AA6BO,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,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,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC1MA,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;AAkBd,IAAM,oBAAoB;AAAA,EAC/B,wBAAwB;AAAA,EACxB,6BAA6B;AAAA,EAC7B,qBAAqB;AAAA,EACrB,mCAAmC;AAAA;AAAA;AAAA,EAGnC,oCAAoC;AAAA;AAAA;AAAA,EAGpC,6BAA6B;AAAA,EAC7B,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA;AAAA;AAAA;AAAA,EAInB,gBAAgB;AAClB;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;;;ACtJA,SAAS,QAAAC,aAAY;AAoBrB,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;AAQO,IAAM,+BAA+B;AAWrC,SAAS,kBAAkB,MAIvB;AACT,MAAI,KAAK,aAAc,QAAO,KAAK,KAAK,YAAY;AACpD,SAAO,KAAK,kBAAkB,KAAK,KAAK,SAAS;AACnD;AASO,SAAS,yBAAyB,MAG7B;AACV,SAAO,CAAC,KAAK,SAAS,KAAK,gBAAgB;AAC7C;AAcA,eAAsB,WACpB,QACA,OACA,OASI,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,kBAAkB,EAAE,cAAc,KAAK,cAAc,WAAW,MAAM,KAAK,CAAC;AAAA,QACzF;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,kBAAkB,EAAE,cAAc,KAAK,cAAc,WAAW,MAAM,KAAK,CAAC;AAAA,MACzF;AAAA,MACA,QACE,gCACA,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,KAAK,EAAE,UAAU,aAAa,EAAE,EAAE,KAAK,KAAK;AAAA,IAC9E;AAAA,EACF;AAMA,QAAM,cAAc,kBAAkB;AAAA,IACpC,cAAc,KAAK;AAAA,IACnB,gBAAgB,MAAM,sBAAsB,OAAO,IAAI,MAAM;AAAA,IAC7D,WAAW,MAAM;AAAA,EACnB,CAAC;AAKD,MAAI,yBAAyB,EAAE,aAAa,OAAO,KAAK,MAAM,CAAC,GAAG;AAChE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,QACE;AAAA,IAGJ;AAAA,EACF;AAIA,MAAI,KAAK,gBAAgB,OAAO,OAAO,cAAc;AACnD,UAAM,cAAc,QAAQ,WAAW;AAAA,EACzC;AAIA,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;;;ACjOA,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,MAAQ;AAAA,EACR,sBAAwB;AAAA,IACtB,yBAA2B;AAAA,IAC3B,0BAA4B,CAAC,WAAW,SAAS;AAAA,EACnD;AAAA,EACA,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,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,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,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,mBAAmB;AAAA,IACnB,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;;;AC3EO,IAAM,kBAA0B,gBAAI;;;ACEpC,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;AAUO,SAAS,6BACd,OACA,WACsD;AACtD,SAAO,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU;AAC5C;AASO,SAAS,sBAAyB,QAAc;AACrD,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,QAAM,SAAS;AACf,MAAI,OAAO,OAAO,YAAY,MAAM,SAAU,QAAO;AACrD,SAAO,EAAE,GAAG,QAAQ,YAAY,EAAE,OAAO,OAAO,YAAY,EAAE,EAAE;AAClE;AAiBO,SAAS,0BAA0B,QAA4C;AACpF,QAAM,eAA0C,EAAE,SAAS,KAAK;AAChE,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,QAAM,SAAS;AACf,QAAM,KAAK,OAAO,sBAAsB;AACxC,MAAI,OAAO,OAAO,YAAY,OAAO,SAAS,EAAE,EAAG,cAAa,uBAAuB;AACvF,MAAI,OAAO,OAAO,QAAQ,MAAM,WAAW;AACzC,iBAAa,SAAS,OAAO,QAAQ;AACrC,iBAAa,SAAS,OAAO,QAAQ,IAAI,WAAW;EACtD;AACA,SAAO;AACT;AAWO,SAAS,0BAA6B,QAAgB,SAAY,QAAoB;AAC3F,MAAI,YAAY,QAAQ,YAAY,OAAW,QAAO;AACtD,MAAI,WAAW,mBAAoB,QAAO,CAAC;AAC3C,MAAI,WAAW,wCAAwC;AACrD,WAAO,0BAA0B,MAAM;EACzC;AACA,SAAO;AACT;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;AAgDO,IAAM,mCAAmC;EAC9C;EACA;EACA;EACA;EACA;AACF;AAEA,IAAM,gBAAmF;EACvF,UAAU;EACV,WAAW;EACX,WAAW;EACX,UAAU;EACV,WAAW;EACX,YAAY;EACZ,eAAe;EACf,WAAW;EACX,YAAY;EACZ,cAAc;EACd,OAAO;EACP,aAAa;EACb,aAAa;EACb,OAAO;EACP,aAAa;EACb,YAAY;AACd;AAYO,SAAS,qBACd,QACyB;AACzB,QAAM,MAA+B,CAAC;AACtC,QAAM,UAAoB,CAAC;AAC3B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAM,SAAS,cAAc,GAAG;AAChC,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,GAAG;AAChB;IACF;AACA,QAAI,MAAM,IAAI;EAChB;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;MACR,mCAAmC,QAAQ,KAAK,IAAI,CAAC,sBAC/B,iCAAiC,KAAK,IAAI,CAAC,+BACjC,OAAO,KAAK,aAAa,EAAE,KAAK,IAAI,CAAC;IACvE;EACF;AACA,MAAI,OAAO,KAAK,GAAG,EAAE,WAAW,GAAG;AACjC,UAAM,IAAI;MACR,wDACK,iCAAiC,KAAK,IAAI,CAAC;IAClD;EACF;AACA,SAAO;AACT;AAqBO,SAAS,iBAAiB,OAAe,WAA4B,OAAe;AACzF,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,sBAAsB,KAAK,OAAO,GAAG;AACvC,WAAO,aAAa,UAAU,GAAG,OAAO,cAAc,GAAG,OAAO;EAClE;AACA,MAAI,kCAAkC,KAAK,OAAO,EAAG,QAAO,GAAG,OAAO;AACtE,MAAI,uCAAuC,KAAK,OAAO,GAAG;AAGxD,WAAO,QAAQ,QAAQ,2BAA2B,EAAE,EAAE,MAAM,GAAG,EAAE;EACnE;AACA,QAAM,IAAI;IACR,IAAI,KAAK;EAGX;AACF;AAgBO,SAAS,yBACd,cACA,WAC0D;AAC1D,QAAM,OAAQ,gBAAgB,CAAC;AAC/B,QAAM,UAAW,KAAK,iCAAiC,KAAK,QAAQ;AAGpE,QAAM,WAAW,MAAM,QAAQ,SAAS,QAAQ,IAAI,QAAQ,WAAW,CAAC;AACxE,aAAW,SAAS,UAAU;AAC5B,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAM,MAAM;AACZ,UAAM,KAAK,IAAI,8BAA8B,IAAI;AACjD,QAAI,OAAO,EAAE,MAAM,UAAW;AAC9B,UAAM,aAAa,IAAI,mBAAmB,IAAI;AAC9C,UAAM,aAAa,IAAI,mBAAmB,IAAI;AAC9C,UAAM,SAAuD,CAAC;AAC9D,QAAI,OAAO,eAAe,YAAY,WAAY,QAAO,aAAa;AACtE,QAAI,OAAO,eAAe,YAAY,WAAY,QAAO,aAAa;AACtE,WAAO;EACT;AACA,SAAO;AACT;AAGO,IAAM,oCAAoC;AAsC1C,SAAS,0BACd,WACA,QAMA;AACA,MAAI,OAAO,cAAc,UAAa,OAAO,YAAY,QAAW;AAClE,UAAM,IAAI,MAAM,uEAAuE;EACzF;AACA,QAAM,aACJ,OAAO,cAAc,SACjB,iBAAiB,OAAO,WAAW,OAAO,IAC1C,OAAO,SAAS;AACtB,QAAM,aACJ,OAAO,YAAY,SACf,iBAAiB,OAAO,SAAS,KAAK,IACtC,OAAO,SAAS;AAEtB,aAAW,CAAC,OAAO,OAAO,OAAO,KAAK;IACpC,CAAC,wBAAwB,YAAY,WAAW;IAChD,CAAC,wBAAwB,YAAY,SAAS;EAChD,GAAY;AACV,QAAI,UAAU,OAAW;AACzB,UAAM,IAAI;MACR,oGACkC,KAAK,iCAC3B,OAAO;IAErB;EACF;AAEA,SAAO;IACL;;;IAGA,sBAAsB,iBAAiB,YAAsB,OAAO;IACpE,sBAAsB,iBAAiB,YAAsB,KAAK;IAClE,SAAS,OAAO,SAAS,KAAK,KAAK;EACrC;AACF;AAqBO,SAAS,sBACd,cACA,WACoB;AACpB,QAAM,OAAQ,gBAAgB,CAAC;AAC/B,QAAM,UAAW,KAAK,iCAAiC,KAAK,QAAQ;AAGpE,QAAM,WAAW,MAAM,QAAQ,SAAS,QAAQ,IAAI,QAAQ,WAAW,CAAC;AACxE,aAAW,SAAS,UAAU;AAC5B,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAM,MAAM;AACZ,UAAM,KAAK,IAAI,8BAA8B,IAAI;AACjD,QAAI,OAAO,EAAE,MAAM,UAAW;AAC9B,UAAM,YAAY,IAAI,oBAAoB,IAAI;AAC9C,UAAM,QAAQ,OAAO,SAAS;AAC9B,WAAO,OAAO,SAAS,KAAK,KAAK,QAAQ,IAAI,QAAQ;EACvD;AACA,SAAO;AACT;AAyPA,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,QAAMC,OAAM,OAAO,IAAI,aAAa,IAAI,QAAQ,IAAI,UAAU,EAAE,EAAE,YAAY;AAC9E,MAAIA,SAAQ,eAAeA,SAAQ,YAAYA,SAAQ,YAAY;AACjE,QAAI,UAAU;AACd;EACF;AACA,MAAIA,SAAQ,aAAa;AACvB,QAAI,aAAa;AACjB;EACF;AACA,MAAIA,SAAQ,UAAUA,SAAQ,eAAeA,SAAQ,mBAAmBA,SAAQ,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,aAAW,OAAO,2BAA2B,IAAI,GAAG;AAClD,QAAI,UAAU,IAAI;AAClB,QAAI,aAAa,IAAI;AACrB,QAAI,aAAa,IAAI;AACrB,QAAI,SAAS,IAAI;EACnB;AACA,MAAI,QAAQ,IAAI,SAAS,IAAI,YAAY,IAAI,YAAY,IAAI;AAC7D,SAAO;AACT;AAQA,SAAS,YAAY,MAA0B;AAC7C,QAAM,OAAQ,QAAQ,CAAC;AACvB,QAAM,UAAW,KAAK,QAAQ,KAAK,wBAAwB;AAC3D,MAAI,MAAM,QAAQ,OAAO,EAAG,QAAO;AACnC,SAAO,MAAM,QAAS,QAAoC,OAAO,IAC3D,QAAoC,UACtC,CAAC;AACP;AAUO,SAAS,2BAA2B,MAA0C;AACnF,QAAM,MAAiC,CAAC;AACxC,aAAW,WAAW,YAAY,IAAI,GAAG;AACvC,UAAM,MAAO,WAAW,CAAC;AACzB,UAAM,MAA+B,sBAAsB;AAC3D,UAAM,KAAK,OAAO,IAAI,MAAM,IAAI,SAAS;AACzC,QAAI,IAAI,OAAO,UAAa,IAAI,cAAc,QAAW;AACvD,UAAI,OAAO,SAAS,EAAE,EAAG,KAAI,KAAK;IACpC;AACA,QAAI,OAAO,IAAI,SAAS,SAAU,KAAI,OAAO,IAAI;AACjD,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,UAAU,SAAU,kBAAiB,KAAM,UAAU,CAAC,CAA6B;MAChG;IACF,OAAO;AACL,UAAI,UAAU,OAAO,IAAI,UAAU,CAAC;AACpC,UAAI,aAAa,OAAO,IAAI,aAAa,CAAC;AAC1C,UAAI,aAAa,OAAO,IAAI,aAAa,IAAI,gBAAgB,IAAI,QAAQ,CAAC;AAC1E,UAAI,SAAS,OAAO,IAAI,SAAS,IAAI,cAAc,CAAC;IACtD;AACA,QAAI,QAAQ,IAAI,SAAS,IAAI,YAAY,IAAI,YAAY,IAAI;AAC7D,QAAI,KAAK,GAAG;EACd;AACA,SAAO;AACT;AAoDO,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,QAAMC,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;AAUO,IAAM,yBAAyB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACF;AAEA,IAAM,sBAAsB,IAAI,IAAY,sBAAsB;AAM3D,SAAS,cAAc,KAA0C;AACtE,QAAM,QAAQ,gBAAgB,GAAG;AACjC,MAAI,UAAU,UAAa,oBAAoB,IAAI,KAAK,EAAG,QAAO;AAClE,SAAO;IACL,OACE;EACJ;AACF;AAOO,SAAS,qBAAqB,KAAyC;AAC5E,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,MAAM,QAAS,GAAG;AACzD,WAAO;MACL,OACE;IACJ;EACF;AACA,QAAM,SAAS,cAAc,MAAM,GAAI;AACvC,MAAI,OAAO,WAAW,SAAU,QAAO;AACvC,SAAO;IACL,OACE;EACJ;AACF;AAEA,SAAS,gBAAgB,KAAkC;AACzD,MAAI,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG,KAAK,MAAM,EAAG,QAAO,MAAM,GAAG;AACjF,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,MAAM,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ,QAAQ,GAAG;AACxD,MAAI,QAAQ,YAAa,QAAO;AAChC,MAAI,IAAI,WAAW,KAAK,EAAG,QAAO;AAClC,MAAI,QAAQ,KAAK,GAAG,EAAG,QAAO,MAAM,GAAG;AACvC,SAAO;AACT;AAQO,SAAS,aAAa,KAAsD;AACjF,QAAM,MAAM,IAAI,KAAK,EAAE,YAAY;AACnC,MAAI,QAAQ,YAAY,QAAQ,aAAa,QAAQ,WAAY,QAAO;AACxE,MAAI,QAAQ,aAAa,QAAQ,cAAc,QAAQ,OAAQ,QAAO;AACtE,MAAI,QAAQ,WAAW;AACrB,WAAO;MACL,OAAO;IACT;EACF;AACA,SAAO,EAAE,OAAO,sCAAsC;AACxD;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;AA8HA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,oBAAoB,QAAQ,aAAa,UAAU,kBAAkB,CAAC;AAE1G,SAAS,WACP,OAC4E;AAC5E,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE,aAAO,EAAE,IAAI,OAAO,OAAO,6BAA6B;IAC1D;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,MAAM;EACjC;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;EAC3B,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,OAAO,6BAA6B;EAC1D;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,OAAO,6BAA6B;EAC1D;AACA,SAAO,EAAE,IAAI,MAAM,MAAM,OAAkC;AAC7D;AAEA,SAAS,SAAS,KAA0C;AAC1D,MAAI,QAAQ,UAAa,QAAQ,QAAQ,QAAQ,GAAI,QAAO,EAAE,OAAO,+BAA+B;AACpG,MAAI,OAAO,QAAQ,SAAU,QAAO,EAAE,OAAO,oCAAoC;AACjF,QAAM,OAAO,IAAI,KAAK;AACtB,MAAI,CAAC,KAAM,QAAO,EAAE,OAAO,+BAA+B;AAC1D,SAAO;AACT;AAEA,SAAS,WAAW,KAA4C;AAC9D,MAAI,QAAQ,UAAa,QAAQ,QAAQ,QAAQ,GAAI,QAAO,EAAE,OAAO,wBAAwB;AAC7F,QAAM,QAAQ,OAAO,QAAQ,WAAW,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,GAAG,IAAI,MAAM;AACzF,MAAI,CAAC,MAAO,QAAO,EAAE,OAAO,0CAA0C;AACtE,QAAM,OAAiB,CAAC;AACxB,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,SAAU,QAAO,EAAE,OAAO,0CAA0C;AACxF,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,KAAM,MAAK,KAAK,IAAI;EAC1B;AACA,MAAI,KAAK,WAAW,EAAG,QAAO,EAAE,OAAO,wBAAwB;AAC/D,SAAO;AACT;AAEA,SAAS,YAAY,KAAkC;AACrD,MAAI,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG,KAAK,MAAM,EAAG,QAAO;AACxE,MAAI,OAAO,QAAQ,YAAY,QAAQ,KAAK,IAAI,KAAK,CAAC,GAAG;AACvD,UAAM,IAAI,OAAO,IAAI,KAAK,CAAC;AAC3B,WAAO,IAAI,IAAI,IAAI;EACrB;AACA,SAAO;AACT;AAEO,SAAS,yBACd,OAC8C;AAC9C,QAAM,SAAS,WAAW,KAAK;AAC/B,MAAI,CAAC,OAAO,GAAI,QAAO,EAAE,OAAO,OAAO,MAAM;AAC7C,QAAM,OAAO,OAAO;AACpB,MAAI,eAAe,QAAQ,eAAe,MAAM;AAC9C,WAAO;MACL,OACE;IACJ;EACF;AACA,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,CAAC,mBAAmB,IAAI,GAAG,GAAG;AAChC,aAAO,EAAE,OAAO,+EAA+E;IACjG;EACF;AACA,QAAM,OAAO,SAAS,sBAAsB,OAAO,KAAK,mBAAmB,KAAK,IAAI;AACpF,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,QAAM,YAAY,WAAW,eAAe,OAAO,KAAK,YAAY,KAAK,MAAM;AAC/E,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACtC,QAAM,mBAAmB,YAAY,KAAK,gBAAgB;AAC1D,MAAI,qBAAqB,QAAW;AAClC,WAAO,EAAE,OAAO,8CAA8C;EAChE;AACA,SAAO,EAAE,kBAAkB,MAAM,WAAW,iBAAiB;AAC/D;AAMO,IAAM,iCAAiC;EAC5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACF;AAEA,IAAM,oCAAoC,IAAI,IAAY,8BAA8B;AAGjF,IAAM,kCAA0D;EACrE,MAAM;EACN,QAAQ;EACR,WAAW;EACX,WAAW;EACX,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,mBAAmB;EACnB,YAAY;AACd;AAEA,SAAS,eACP,OAC4E;AAC5E,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE,aAAO,EAAE,IAAI,OAAO,OAAO,iCAAiC;IAC9D;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,MAAM;EACjC;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;EAC3B,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,OAAO,iCAAiC;EAC9D;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,OAAO,iCAAiC;EAC9D;AACA,SAAO,EAAE,IAAI,MAAM,MAAM,OAAkC;AAC7D;AAEA,SAAS,aAAa,KAA0C;AAC9D,MAAI,OAAO,QAAQ,SAAU,QAAO,EAAE,OAAO,8CAA8C;AAC3F,QAAM,OAAO,IAAI,KAAK;AACtB,MAAI,CAAC,KAAM,QAAO,EAAE,OAAO,yCAAyC;AACpE,SAAO;AACT;AAUO,SAAS,4BACd,OAC6C;AAC7C,QAAM,SAAS,eAAe,KAAK;AACnC,MAAI,CAAC,OAAO,GAAI,QAAO,EAAE,OAAO,OAAO,MAAM;AAC7C,QAAM,OAAO,OAAO;AACpB,MAAI,WAAW,KAAM,QAAO,EAAE,OAAO,kDAAkD;AACvF,MAAI,iBAAiB,KAAM,QAAO,EAAE,OAAO,wDAAwD;AAEnG,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,kCAAkC,IAAI,GAAG,EAAG,KAAI,GAAG,IAAI;EAC7D;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAM,SAAS,gCAAgC,GAAG;AAClD,QAAI,CAAC,QAAQ;AACX,UAAI,CAAC,kCAAkC,IAAI,GAAG,GAAG;AAC/C,eAAO,EAAE,OAAO,gDAAgD,GAAG,GAAG;MACxE;AACA;IACF;AACA,QAAI,UAAU,IAAK;AACnB,QAAI,WAAW,8BAA8B;AAC3C,YAAM,OAAO,aAAa,KAAK;AAC/B,UAAI,OAAO,SAAS,SAAU,QAAO;AACrC,UAAI,MAAM,IAAI;AACd;IACF;AACA,QAAI,MAAM,IAAI;EAChB;AACA,MAAI,gCAAgC,KAAK;AACvC,UAAM,OAAO,aAAa,IAAI,4BAA4B,CAAC;AAC3D,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,QAAI,4BAA4B,IAAI;EACtC;AACA,SAAO;AACT;AAOA,eAAsB,uBACpB,QACA,WACkC;AAClC,QAAM,WAAW,OAAO,YAAY;AACpC,MAAI,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,EAAG,QAAO;AACtE,SAAO,EAAE,GAAG,QAAQ,YAAY,MAAM,UAAU,EAAE;AACpD;AASO,SAAS,kCACd,UAC6C;AAC7C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,QAAQ;EAC9B,QAAQ;AACN,WAAO,EAAE,OAAO,iCAAiC;EACnD;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,OAAO,iCAAiC;EACnD;AACA,MAAI;AACJ,MAAI;AACF,iBAAa,yBAAyB,MAAiC;EACzE,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,WAAO,EAAE,OAAO,QAAQ;EAC1B;AACA,SAAO,4BAA4B,UAAU;AAC/C;AAWA,IAAM,gCAAwF;EAC5F,eAAe,CAAC,eAAe;EAC/B,oBAAoB,CAAC;EACrB,qBAAqB,CAAC;AACxB;AAGA,IAAM,mCAAmC,oBAAI,IAAI,CAAC,cAAc,0BAA0B,CAAC;AAEpF,IAAM,kCAA0D;EACrE,GAAG;EACH,aAAa;EACb,aAAa;EACb,uBAAuB;EACvB,yBAAyB;EACzB,aAAa;AACf;AASA,SAAS,cACP,OAC4E;AAC5E,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE,aAAO,EAAE,IAAI,OAAO,OAAO,gCAAgC;IAC7D;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,MAAM;EACjC;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;EAC3B,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,OAAO,gCAAgC;EAC7D;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,OAAO,gCAAgC;EAC7D;AACA,SAAO,EAAE,IAAI,MAAM,MAAM,OAAkC;AAC7D;AAEA,SAAS,iBAAiB,KAA0C;AAClE,MAAI,OAAO,QAAQ,YAAY,uBAAuB,SAAS,GAAY,EAAG,QAAO;AACrF,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,MAAM,IAAI,KAAK,EAAE,YAAY;AACnC,QAAI,QAAQ,WAAW,QAAQ,IAAK,QAAO,gBAAgB;AAC3D,QAAI,QAAQ,YAAY,QAAQ,IAAK,QAAO,gBAAgB;AAC5D,QAAI,QAAQ,aAAa,QAAQ,IAAK,QAAO,gBAAgB;EAC/D;AACA,SAAO,EAAE,OAAO,yEAAyE;AAC3F;AAEA,SAAS,2BAA2B,KAA0C;AAC5E,MAAI,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG,EAAG,QAAO;AAC7D,MAAI,OAAO,QAAQ,YAAY,UAAU,KAAK,IAAI,KAAK,CAAC,EAAG,QAAO,OAAO,IAAI,KAAK,CAAC;AACnF,MAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAE,YAAY,MAAM,wBAAwB;AAClF,WAAO;MACL,OAAO;IACT;EACF;AACA,SAAO,EAAE,OAAO,sCAAsC;AACxD;AAEA,SAAS,mBACP,KACA,QACA,OAC+B;AAC/B,MAAI,WAAW,8BAA8B;AAC3C,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,QAAI,MAAM,IAAI;AACd,WAAO;EACT;AACA,MAAI,WAAW,4BAA4B;AACzC,UAAM,SAAS,iBAAiB,KAAK;AACrC,QAAI,OAAO,WAAW,SAAU,QAAO;AACvC,QAAI,MAAM,IAAI;AACd,WAAO;EACT;AACA,MAAI,WAAW,yBAAyB;AACtC,UAAM,SAAS,2BAA2B,KAAK;AAC/C,QAAI,OAAO,WAAW,SAAU,QAAO;AACvC,QAAI,MAAM,IAAI;AACd,WAAO;EACT;AACA,MAAI,MAAM,IAAI;AACd,SAAO;AACT;AAWO,SAAS,4BACd,OACA,QAC6C;AAC7C,QAAM,SAAS,cAAc,KAAK;AAClC,MAAI,CAAC,OAAO,GAAI,QAAO,EAAE,OAAO,OAAO,MAAM;AAC7C,QAAM,OAAO,OAAO;AACpB,MAAI,WAAW,KAAM,QAAO,EAAE,OAAO,GAAG,MAAM,sBAAsB;AACpE,MAAI,iBAAiB,KAAM,QAAO,EAAE,OAAO,GAAG,MAAM,4BAA4B;AAEhF,QAAM,WAAW,oBAAI,IAAY;IAC/B,GAAG;IACH,GAAG,8BAA8B,MAAM;EACzC,CAAC;AACD,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,iCAAiC,IAAI,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,EAAG;AACrE,UAAM,UAAU,mBAAmB,KAAK,KAAK,KAAK;AAClD,QAAI,QAAS,QAAO;EACtB;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,iCAAiC,IAAI,GAAG,EAAG;AAC/C,UAAM,SAAS,gCAAgC,GAAG;AAClD,QAAI,CAAC,QAAQ;AACX,UAAI,CAAC,SAAS,IAAI,GAAG,EAAG,QAAO,EAAE,OAAO,GAAG,MAAM,oBAAoB,GAAG,GAAG;AAC3E;IACF;AACA,QAAI,UAAU,IAAK;AACnB,UAAM,UAAU,mBAAmB,KAAK,QAAQ,KAAK;AACrD,QAAI,QAAS,QAAO;EACtB;AACA,MAAI,OAAO,KAAK,GAAG,EAAE,WAAW,GAAG;AACjC,WAAO,EAAE,OAAO,uCAAuC;EACzD;AACA,SAAO;AACT;AAOO,SAAS,kCACd,SACA,QAC6C;AAC7C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;EAC7B,QAAQ;AACN,WAAO,EAAE,OAAO,gCAAgC;EAClD;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,OAAO,gCAAgC;EAClD;AACA,MAAI,OAAO;AACX,MAAI,WAAW,iBAAiB;AAC9B,QAAI;AACF,aAAO,gCAAgC,IAAI;IAC7C,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,aAAO,EAAE,OAAO,QAAQ;IAC1B;EACF;AACA,SAAO,4BAA4B,MAAM,MAAM;AACjD;;;AC31DA,SAAS,SAAS;AAwBX,IAAM,iBAAsC,oBAAI,IAAI;EACzD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACF,CAAC;AAYM,IAAM,wBAA6C,oBAAI,IAAI;EAChE;EACA;AACF,CAAC;AAQM,IAAM,sBAAsB,EAChC,OAAO;EACN,QAAQ,EACL,OAAO;IACN,MAAM,EAAE,OAAO;;IAEf,KAAK,EAAE,OAAO;EAChB,CAAC,EACA,YAAY;AACjB,CAAC,EACA,YAAY;AAKR,IAAM,0BAA0B,EACpC,OAAO;EACN,IAAI,EAAE,OAAO;EACb,MAAM,EAAE,OAAO;EACf,UAAU,EAAE,OAAO;EACnB,YAAY,EAAE,OAAO;;;EAGrB,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC,CAAC;EACpE,WAAW,EAAE,OAAO,EAAE,SAAS;EAC/B,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS;EACjD,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS;EAClD,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS;AACnD,CAAC,EACA,YAAY;AAIR,IAAM,8BAA8B,EACxC,OAAO;EACN,UAAU,EAAE;IACV,EACG,OAAO;MACN,IAAI,EAAE,OAAO;MACb,MAAM,EAAE,OAAO;MACf,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,CAAC;IAC7C,CAAC,EACA,YAAY;EACjB;AACF,CAAC,EACA,YAAY;AAKR,IAAM,qCAAqC,EAC/C,OAAO;EACN,UAAU,EAAE;IACV,EACG,OAAO;MACN,0BAA0B,EAAE,OAAO;MACnC,4BAA4B,EAAE,OAAO;MACrC,YAAY,EAAE,OAAO;MACrB,gBAAgB,EAAE,OAAO;IAC3B,CAAC,EACA,YAAY;EACjB;AACF,CAAC,EACA,YAAY;AAMf,IAAM,uBAAuB,EAC1B,OAAO,EACP,MAAM,aAAa,0DAA0D;AAEzE,IAAM,8BAA8B,EACxC,OAAO;EACN,QAAQ,EACL;IACC,EACG,OAAO;MACN,QAAQ;IACV,CAAC,EACA,YAAY;EACjB,EACC,IAAI,GAAG,sDAAsD;AAClE,CAAC,EACA,YAAY;AAOR,IAAM,mBAA8C;EACzD,iBAAiB;EACjB,qBAAqB;EACrB,4BAA4B;EAC5B,qBAAqB;AACvB;AAeO,IAAM,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AACxD,IAAM,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACjF,IAAM,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACjF,IAAM,kBAAkB,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACnF,IAAM,kBAAkB,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAEnF,IAAM,gBAAgB,EAAE,OAAO;EACpC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,oBAAoB;EACnE,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,oBAAoB;AACnE,CAAC;AAEM,IAAM,gBAAgB,EAAE,OAAO;EACpC,OAAO,EAAE,OAAO;EAChB,MAAM,EAAE,OAAO,EAAE,SAAS;EAC1B,YAAY,EAAE,OAAO,EAAE,SAAS;AAClC,CAAC;AAEM,IAAM,aAAa,EAAE,OAAO;EACjC,IAAI,EAAE,QAAQ;EACd,MAAM,EAAE,QAAQ,EAAE,SAAS;AAC7B,CAAC;AAMM,IAAM,6BAA6B,EAAE,OAAO;EACjD,WAAW,EAAE,KAAK,CAAC,UAAU,aAAa,aAAa,CAAC;AAC1D,CAAC;AAEM,IAAM,8BAA8B,EAAE,OAAO;EAClD,QAAQ,EAAE,OAAO,EAAE,SAAS;EAC5B,YAAY,EAAE,OAAO,EAAE,SAAS;AAClC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,UAAa,EAAE,eAAe,QAAW;EACrE,SAAS;AACX,CAAC;AAEM,IAAM,kCAAkC,EAAE,OAAO;EACtD,MAAM,EAAE,OAAO,EAAE,SAAS;EAC1B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS;EAClC,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAEM,IAAM,6BAA6B,EAAE,OAAO;EACjD,WAAW,EAAE,QAAQ,EAAE,SAAS;EAChC,YAAY,EAAE,QAAQ,EAAE,SAAS;EACjC,UAAU,EAAE,QAAQ,EAAE,SAAS;EAC/B,cAAc,EAAE,QAAQ,EAAE,SAAS;AACrC,CAAC;AAEM,IAAM,yBAAyB,EAAE,OAAO;EAC7C,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAC5C,CAAC;AAEM,IAAM,oBAAoB,EAAE,OAAO;EACxC,OAAO,EAAE,KAAK,sBAAsB;AACtC,CAAC;AAMM,IAAM,oBAAoB,EAAE,OAAO;EACxC,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;EAC7C,WAAW,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,SAAS;AAC9D,CAAC;AAEM,IAAM,6BAA6B,EAAE,OAAO;EACjD,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAC/C,CAAC;AAEM,IAAM,0BAA0B,EAAE,OAAO;EAC9C,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;EAC5C,eAAe,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS;EACjD,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS;AAC9C,CAAC;AAEM,IAAM,0BAA0B,EAAE,OAAO;EAC9C,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB;AACpD,CAAC;AAEM,IAAM,0BAA0B,EAAE,OAAO;EAC9C,QAAQ,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;AACrC,CAAC;AAEM,IAAM,0BAA0B,EAAE,OAAO;EAC9C,QAAQ,EAAE,KAAK,CAAC,QAAQ,QAAQ,CAAC;AACnC,CAAC;AAEM,IAAM,4BAA4B,EAAE,OAAO;EAChD,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;EACtB,aAAa,EAAE,OAAO,EAAE,SAAS;EACjC,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;EACxC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS;EACzC,WAAW,EAAE,QAAQ,EAAE,SAAS;AAClC,CAAC;AAEM,IAAM,yBAAyB,EAAE,OAAO;EAC7C,MAAM,EAAE,OAAO,EAAE,SAAS;EAC1B,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;EAC5C,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACrD,CAAC;AAEM,IAAM,8BAA8B,EAAE,OAAO;EAClD,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;EACvD,WAAW,EAAE,QAAQ,EAAE,SAAS;AAClC,CAAC;AAEM,IAAM,+BAA+B,EAAE,OAAO;EACnD,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAClD,CAAC;AAMM,IAAM,2BAA2B,EAAE,OAAO;EAC/C,QAAQ,EAAE,OAAO;;;EAGjB,MAAM,EAAE,KAAK,CAAC,SAAS,OAAO,OAAO,UAAU,CAAC,EAAE,QAAQ,OAAO;AACnE,CAAC;AAEM,IAAM,sBAAsB,EAAE,OAAO;EAC1C,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;EACzC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;EAC5B,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAC5B,CAAC;AAMM,IAAM,yBAAyB,EAAE,OAAO;EAC7C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;EACtB,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC;AAChD,CAAC;AAMM,IAAM,cAAc,EAAE,OAAO;EAClC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;EAC/B,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAMM,IAAM,sBAAsB,EAAE,OAAO;EAC1C,WAAW,EAAE,KAAK,CAAC,UAAU,SAAS,CAAC;AACzC,CAAC;AAMM,IAAM,yBAAyB,EAAE,OAAO;EAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAEM,IAAM,mBAAmB,EAAE,OAAO;EACvC,WAAW,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACzD,CAAC;AAEM,IAAM,iBAAiB,EAAE,OAAO;EACrC,gBAAgB,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE;EAC7D,OAAO,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAC/D,CAAC;AAEM,IAAM,2BAA2B,EAAE,OAAO;EAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB;EAC7C,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB;EAC3C,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,GAAG;AACxD,CAAC;AAEM,IAAM,sBAAsB,EAAE,OAAO;EAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAEM,IAAM,2BAA2B,EAAE,OAAO;EAC/C,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACvD,CAAC;AAEM,IAAM,6BAA6B,EAAE,OAAO;EACjD,SAAS,EAAE,KAAK,CAAC,aAAa,cAAc,iBAAiB,SAAS,CAAC,EAAE,SAAS;AACpF,CAAC;AAEM,IAAM,2BAA2B,EAAE,OAAO;EAC/C,OAAO,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;EAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,SAAS;EACxD,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,SAAS;AACxD,CAAC;AAMM,IAAM,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAMzD,IAAM,iBAAiB,EAAE,OAAO;EACrC,IAAI,EAAE,QAAQ,IAAI;EAClB,SAAS,EAAE,OAAO;EAClB,WAAW,EAAE,OAAO;AACtB,CAAC;;;ACtVM,IAAM,qBAAgC;EAC3C,WAAW;EACX,aAAa;EACb,SAAS;AACX;AAOO,IAAM,qBAA0D;;;EAGrE,mBAAmB,EAAE,WAAW,MAAS,aAAa,GAAG,SAAS,KAAQ;AAC5E;AAGO,IAAM,wBAAwB;AAErC,IAAM,kBAAkB;AAQjB,IAAM,kBAAN,cAA8B,MAAM;EACzC,YACkB,QAEA,WAEA,WAAmB,WAEnB,WAAmB,GACnC;AACA;MACE,IAAI,MAAM,iCAAiC,SAAS,QACjD,WAAW,IAAI,eAAe,QAAQ,cAAc,OACpD,WAAW,YAAY,YAAY,QAAQ,QAAQ,MACpD;IAGJ;AAfgB,SAAA,SAAA;AAEA,SAAA,YAAA;AAEA,SAAA,WAAA;AAEA,SAAA,WAAA;AAUhB,SAAK,OAAO;EACd;EAjBkB;EAEA;EAEA;EAEA;AAYpB;AAEA,SAAS,OAAO,MAAkC;AAChD,QAAM,OAAQ,WAAuC,SAAS;AAG9D,QAAM,SAAS,OAAO,MAAM,MAAM,IAAI,CAAC;AACvC,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAC1D;AASO,SAAS,aAAa,QAA2B;AACtD,QAAM,OAAO,mBAAmB,MAAM,KAAK;AAC3C,QAAM,YAAY,OAAO,gBAAgB,KAAK,KAAK;AAGnD,QAAM,UAAU,KAAK,IAAI,OAAO,sBAAsB,KAAK,KAAK,SAAS,SAAS;AAClF,SAAO,EAAE,WAAW,aAAa,KAAK,aAAa,QAAQ;AAC7D;AAEA,SAASC,OAAM,IAAY,QAAoC;AAC7D,SAAO,IAAI,QAAc,CAAC,YAAY;AACpC,UAAM,QAAQ,WAAW,QAAQ,EAAE;AACnC,aAAS,SAAe;AACtB,mBAAa,KAAK;AAClB,aAAO,oBAAoB,SAAS,MAAM;AAC1C,cAAQ;IACV;AACA,WAAO,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;EACzD,CAAC;AACH;AAWA,eAAe,aAAgB,MAAkB,UAA2B,IAAwB;AAClG,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;MACxB;MACA,IAAI,QAAe,CAAC,UAAU,WAAW;AACvC,gBAAQ,WAAW,MAAM,OAAO,QAAQ,GAAG,EAAE;MAC/C,CAAC;IACH,CAAC;EACH,UAAA;AACE,QAAI,UAAU,OAAW,cAAa,KAAK;EAC7C;AACF;AAeA,eAAsB,cACpB,QACA,SACA,SAAoB,aAAa,MAAM,GAC3B;AACZ,QAAM,aAAa,KAAK,IAAI,IAAI,OAAO;AACvC,QAAM,mBAAmB,IAAI,gBAAgB;AAC7C,QAAM,cAAc,WAAW,MAAM,iBAAiB,MAAM,GAAG,OAAO,OAAO;AAE7E,MAAI,WAAW;AACf,MAAI;AAEJ,MAAI;AACF,aAAS,IAAI,GAAG,IAAI,OAAO,aAAa,KAAK;AAC3C,YAAM,YAAY,aAAa,KAAK,IAAI;AACxC,UAAI,YAAY,sBAAuB;AAGvC,YAAM,YAAY,KAAK,IAAI,OAAO,WAAW,SAAS;AACtD;AAEA,YAAM,oBAAoB,IAAI,gBAAgB;AAC9C,YAAM,gBAAgB,MAAM,kBAAkB,MAAM;AACpD,uBAAiB,OAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AAC/E,YAAM,eAAe,WAAW,MAAM,kBAAkB,MAAM,GAAG,SAAS;AAC1E,YAAM,SAAS,IAAI,gBAAgB,QAAQ,WAAW,OAAO,SAAS,QAAQ;AAE9E,UAAI;AACF,eAAO,MAAM;UACX,QAAQ,kBAAkB,QAAQ,SAAS;UAC3C;UACA;QACF;MACF,SAAS,KAAK;AACZ,YAAI,EAAE,eAAe,iBAAkB,OAAM;AAC7C,sBAAc,IAAI,gBAAgB,QAAQ,WAAW,OAAO,SAAS,QAAQ;MAC/E,UAAA;AACE,qBAAa,YAAY;AACzB,yBAAiB,OAAO,oBAAoB,SAAS,aAAa;AAGlE,0BAAkB,MAAM;MAC1B;AAEA,UAAI,MAAM,OAAO,cAAc,EAAG;AAClC,YAAM,UAAU,KAAK,OAAO,IAAI,kBAAkB,KAAK;AACvD,UAAI,KAAK,IAAI,IAAI,UAAU,wBAAwB,WAAY;AAC/D,YAAMA,OAAM,SAAS,iBAAiB,MAAM;IAC9C;EACF,UAAA;AACE,iBAAa,WAAW;EAC1B;AAEA,QACE,eACA,IAAI,gBAAgB,QAAQ,OAAO,WAAW,OAAO,SAAS,KAAK,IAAI,UAAU,CAAC,CAAC;AAEvF;;;AC5OA,IAAM,aAAa;AACnB,IAAM,YAAY;AAUlB,IAAM,UAAU,oBAAI,IAAyB;AAE7C,SAAS,eAAe,KAA0B;AAChD,MAAI,IAAI,QAAQ,IAAI,GAAG;AACvB,MAAI,CAAC,GAAG;AACN,QAAI,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,QAAQ,EAAE;AACvC,YAAQ,IAAI,KAAK,CAAC;EACpB;AACA,SAAO;AACT;AAMA,eAAsB,aAAa,YAAmC;AAKpE,QAAM,OAAQ,WAAuC,SAAS;AAG9D,MACE,MAAM,MAAM,qBAAqB,MAAM,UACtC,WAAuC,qBAAqB,MAAM,QACnE;AACA;EACF;AAEA,QAAM,QAAQ,eAAe,UAAU;AAGvC,QAAM,SAAS,MAAM,KAAK,KAAK,YAAY;AACzC,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,IAAI;AAErB,UAAI,MAAM,IAAI,SAAS,YAAY;AACjC,cAAM,IAAI,KAAK,GAAG;AAClB;MACF;AAEA,YAAM,SAAS,MAAM,IAAI,CAAC;AAC1B,YAAM,MAAM,MAAM;AAElB,UAAI,OAAO,WAAW;AACpB,cAAM,IAAI,MAAM;AAChB,cAAM,IAAI,KAAK,GAAG;AAClB;MACF;AAEA,YAAM,SAAS,YAAY,MAAM;AACjC,YAAM,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;IACtD;EACF,CAAC;AAGD,QAAM,OAAO;AAEb,SAAO;AACT;ACxCA,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;AAKjD,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;ACnSO,IAAM,gCAAgC;EAC3C;EACA;EACA;EACA;EACA;AACF;AAMO,IAAM,6BAA6B;EACxC,yBAAyB;EACzB,2BAA2B;EAC3B,oBAAoB;EACpB,sBAAsB;EACtB,yBAAyB;AAC3B;AAsCO,IAAM,oBAA4D;EACvE,iBAAiB,EAAE,YAAY,KAAK;EACpC,eAAe;IACb,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,cAAc;EAChB;EACA,oBAAoB,EAAE,YAAY,2BAA2B;EAC7D,qBAAqB,EAAE,YAAY,2BAA2B;;;;EAI9D,8BAA8B;IAC5B,MAAM;IACN,QAAQ;IACR,cAAc;IACd,WAAW;EACb;EACA,oBAAoB,EAAE,MAAM,mBAAmB;AACjD;AAUO,SAAS,kBAAqB,QAAgB,QAAc;AACjE,QAAM,UAAU,kBAAkB,MAAM;AACxC,MAAI,CAAC,WAAW,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC9E,WAAO;EACT;AACA,QAAM,SAAS;AACf,MAAI;AACJ,aAAW,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,OAAO,GAAG;AAChD,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,EAAG;AACzD,YAAQ,EAAE,GAAG,OAAO;AACpB,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,EAAE,EAAG,KAAI,EAAE,IAAI,OAAO,IAAI;AAC5E,WAAO,IAAI,IAAI;EACjB;AACA,SAAQ,OAAO;AACjB;AAKO,SAAS,wBACd,QACoC;AACpC,SAAQ,8BAAoD,SAAS,MAAM;AAC7E;AAMO,SAAS,gBAAgB,QAAyB;AACvD,SACE,wBAAwB,MAAM,KAC9B,OAAO,UAAU,eAAe,KAAK,4BAA4B,MAAM;AAE3E;AAGO,SAAS,cAAc,QAAwB;AACpD,MAAI,wBAAwB,MAAM,EAAG,QAAO;AAC5C,SACG,2BAAsD,MAAM,KAAK;AAEtE;AAqBO,SAAS,mBAAmB,QAAgB,QAAuB;AACxE,MAAI,CAAC,gBAAgB,MAAM,EAAG;AAC9B,MAAI,OAAO,WAAW,SAAU;AAEhC,QAAM,QACJ,WAAW,OACP,SACA,MAAM,QAAQ,MAAM,IAClB,UACA,OAAO,WAAW,WAChB,WACA,OAAO;AACjB,QAAM,IAAI;IACR,eAAe,MAAM,0BAA0B,cAAc,MAAM,CAAC,kBAClD,KAAK,kNAGa,MAAM;EAC5C;AACF;AC9KA,IAAM,yBAAyB,oBAAI,IAAI,CAAC,6BAA6B,wBAAwB,yBAAyB,CAAC;AAmBhH,IAAM,cAAN,cAA0B,MAAM;EAGrC,YACkB,MAChB,SACgB,QAChB,UAA8B,CAAC,GAC/B;AACA,UAAM,IAAI,MAAM,eAAe,IAAI,KAAK,OAAO,EAAE;AALjC,SAAA,OAAA;AAEA,SAAA,SAAA;AAIhB,SAAK,OAAO;AACZ,SAAK,UAAU;EACjB;EARkB;EAEA;EALF;AAYlB;AAcO,IAAM,wBAAN,cAAoC,MAAM;EAC/C,YAA4B,QAAgB;AAC1C;MACE,IAAI,MAAM,oEAC4B,eAAe,IAAI,yEAEtD,sBAAsB,IAAI,MAAM,IAC7B,GAAG,MAAM,qFACT;IAER;AAT0B,SAAA,SAAA;AAU1B,SAAK,OAAO;EACd;EAX4B;AAY9B;AAkBO,IAAM,mBAAN,MAAM,0BAAyB,MAAM;EAC1C,YACkB,QAEA,QACA,SAChB;AACA;MACE,IAAI,MAAM,8CAA8C,kBAAiB,UAAU,MAAM,CAAC;IAC5F;AAPgB,SAAA,SAAA;AAEA,SAAA,SAAA;AACA,SAAA,UAAA;AAKhB,SAAK,OAAO;EACd;EATkB;EAEA;EACA;;EASlB,IAAI,UAAkB;AACpB,WAAO,kBAAiB,UAAU,KAAK,MAAM;EAC/C;EAEA,OAAe,UAAU,QAAoC;AAC3D,WAAO,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;EAC/D;AACF;AAMA,SAAS,gBACP,MACA,QACA,SACkD;AAClD,UAAQ,MAAM;IACZ,KAAK;AACH,aAAO;QACL,SACE;QAGF,SAAS,CAAC;MACZ;IACF,KAAK;AACH,aAAO;QACL,SACE;QAEF,SAAS,CAAC;MACZ;IACF;AACE,UAAI,QAAQ,SAAS,QAAQ,OAAO;AAElC,cAAM,UAA8B,CAAC;AACrC,YAAI,QAAQ,eAAe,MAAM,QAAW;AAC1C,kBAAQ,gBAAgB,QAAQ,eAAe;QACjD;AACA,YAAI,QAAQ,YAAY,MAAM,QAAW;AACvC,kBAAQ,aAAa,QAAQ,YAAY;QAC3C;AACA,eAAO,EAAE,SAAS,UAAU,mCAAmC,QAAQ;MACzE;AACA,aAAO,EAAE,SAAS,UAAU,iBAAiB,SAAS,CAAC,EAAE;EAC7D;AACF;AAMA,IAAME,mBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAG5B,IAAM,kCAAkC;AACxC,IAAM,iCAAiC;AACvC,IAAM,8BAA8B;AACpC,IAAM,iBAAiB;AAOvB,eAAsB,iBAAoB,IAAkC;AAC1E,MAAI;AACJ,MAAI,aAAa;AACjB,MAAI,gBAAgBA;AACpB,MAAI,eAAe;AAEnB,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,QAAI,UAAU,GAAG;AACf,YAAM,OAAO,KAAK,IAAI,gBAAgB,KAAK,IAAI,GAAG,UAAU,CAAC,GAAG,YAAY;AAC5E,YAAM,SAAS,OAAO,kBAAkB,IAAI,KAAK,OAAO,IAAI;AAC5D,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,CAAC;AACnD,YAAM,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;IACrD;AACA,QAAI;AACF,aAAO,MAAM,GAAG;IAClB,SAAS,KAAK;AACZ,kBAAY;AACZ,UAAI,eAAe,aAAa;AAG9B,YAAI,IAAI,SAAS,IAAK,OAAM;AAC5B,qBAAa;AACb,wBAAgB;AAChB,uBAAe;MACjB;AAEA,UAAI,eAAe,kBAAkB;AACnC,cAAM;MACR;AAIA,UAAI,eAAe,iBAAiB;AAClC,cAAM;MACR;IACF;EACF;AACA,QAAM;AACR;AAoBA,SAAS,aAAa,KAAuB;AAC3C,SACE,eAAe,UACd,IAAI,SAAS,gBACZ,IAAI,SAAS,kBACb,uBAAuB,KAAK,IAAI,OAAO;AAE7C;AAmCO,IAAM,YAAN,MAAgB;;;;;;;;EAiBrB,YACE,SACiB,eACA,YAA0B,eAC3C,UAA4B,CAAC,GAC7B;AAHiB,SAAA,gBAAA;AACA,SAAA,YAAA;AAQjB,QAAI,MAAM,QAAQ;AAClB,WAAO,MAAM,KAAK,QAAQ,WAAW,MAAM,CAAC,MAAM,GAAc;AAChE,SAAK,UAAU,QAAQ,MAAM,GAAG,GAAG;AACnC,SAAK,iBAAiB,QAAQ,iBAAiB;AAC/C,SAAK,qBAAqB,QAAQ,qBAAqB;AACvD,SAAK,oBAAoB,QAAQ,oBAAoB;EACvD;EAfmB;EACA;EAnBF;EACA;EACA;EACA;;;EAGA,iBAAiB,oBAAI,IAAY;EACjC,oBAAoB,oBAAI,IAAY;EA4BrD,MAAM,KACJ,QACA,SAAoD,CAAC,GACrD,OACY;AAMZ,QAAI,sBAAsB,IAAI,MAAM,GAAG;AACrC,YAAM,IAAI,sBAAsB,MAAM;IACxC;AAKA,QAAI,CAAC,eAAe,IAAI,MAAM,GAAG;AAC/B,UAAI,KAAK,eAAgB,OAAM,IAAI,sBAAsB,MAAM;AAC/D,UAAI,CAAC,KAAK,eAAe,IAAI,MAAM,GAAG;AACpC,aAAK,eAAe,IAAI,MAAM;AAC9B,gBAAQ;UACN,wBAAwB,MAAM;QAEhC;MACF;IACF;AAEA,UAAM,MAAM,SAAS,KAAK;AAC1B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,YAAY,IAAI,6BAA6B,MAAM;IAC/D;AAEA,UAAM,MAAM,GAAG,KAAK,OAAO,aAAa,GAAG;AAK3C,uBAAmB,QAAQ,MAAM;AAMjC,UAAM,aAAa,kBAAkB,QAAQ,MAAM;AAInD,UAAM,eACJ,WAAW,2BAA2B,sBAAsB,UAAU,IAAI;AAC5E,UAAM,OAAO,KAAK,UAAU,EAAE,CAAC,MAAM,GAAG,aAAa,CAAC;AAEtD,UAAM,gBAAgB,uBAAuB,IAAI,MAAM;AACvD,UAAM,OAAO,YAAY;AAEvB,YAAM,aAAa,GAAG;AAGtB,YAAM,oBAAoB,KAAK,QAAQ,KAAK,SAAS;AAIrD,aAAO,cAAiB,QAAQ,OAAO,QAAQ,cAAc;AAC3D,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,MAAM,KAAK;YACrB,QAAQ;YACR,SAAS,EAAE,gBAAgB,mBAAmB;YAC9C;YACA;UACF,CAAC;QACH,SAAS,KAAK;AACZ,cAAI,aAAa,GAAG,GAAG;AAGrB,kBAAM,IAAI,gBAAgB,QAAQ,SAAS;UAC7C;AAEA,gBAAM;QACR;AAEA,YAAI,IAAI,UAAU,KAAK;AAErB,gBAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;QACxD;AAEA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,IAAI;YACR,IAAI;YACJ,QAAQ,IAAI,MAAM,IAAI,IAAI,UAAU;YACpC;UACF;QACF;AAIA,YAAI;AACJ,YAAI;AACF,iBAAQ,MAAM,IAAI,KAAK;QACzB,SAAS,KAAK;AACZ,cAAI,aAAa,GAAG,GAAG;AACrB,kBAAM,IAAI,gBAAgB,QAAQ,SAAS;UAC7C;AACA,gBAAM;QACR;AAEA,YAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,gBAAM,UAAU,KAAK,QAAQ,QAAQ;AACrC,gBAAM,SAAS,KAAK,QAAQ,OAAO;AAEnC,gBAAM,UAAU;AAChB,gBAAM,EAAE,SAAS,QAAQ,IAAI,gBAAgB,SAAS,QAAQ,OAAO;AACrE,gBAAM,IAAI,YAAY,SAAS,SAAS,QAAQ,OAAO;QACzD;AAGA,YAAI;AACJ,YAAI,WAAW,uBAAuB,KAAK,gBAAgB,MAAM,QAAW;AAE1E,oBAAU,KAAK,gBAAgB;QACjC,WAAW,WAAW,iCAAiC;AACrD,gBAAM,WAAW,KAAK,MAAM;AAC5B,cAAI,aAAa,QAAW;AAE1B,sBAAU;UACZ,WAAW,KAAK,oBAAoB,MAAM,QAAW;AAEnD,sBAAU,KAAK,oBAAoB;UACrC,OAAO;AACL,sBAAW,KAAK,MAAM,KAAY;UACpC;QACF,WAAW,KAAK,MAAM,MAAM,QAAW;AACrC,oBAAU,KAAK,MAAM;QACvB,WAAW,OAAO,KAAK,IAAI,EAAE,KAAK,CAAC,MAAM,MAAM,QAAQ,GAAG;AAOxD,oBAAU;QACZ,OAAO;AAaL,oBAAU;QACZ;AAKA,kBAAU,0BAA0B,QAAQ,SAAS,YAAY;AAOjE,YAAI,KAAK,oBAAoB;AAC3B,gBAAM,SAAS,iBAAiB,MAAM;AACtC,cAAI,QAAQ;AACV,kBAAM,SAAS,OAAO,UAAU,OAAO;AACvC,gBAAI,CAAC,OAAO,SAAS;AACnB,oBAAM,SAA6B,OAAO,MAAM,OAAO,IAAI,CAAC,OAAO;gBACjE,MAAM,EAAE,KAAK,KAAK,GAAG,KAAK;gBAC1B,SAAS,EAAE;cACb,EAAE;AACF,kBAAI,KAAK,mBAAmB;AAC1B,sBAAM,IAAI,iBAAiB,QAAQ,QAAQ,OAAO;cACpD;AACA,kBAAI,CAAC,KAAK,kBAAkB,IAAI,MAAM,GAAG;AACvC,qBAAK,kBAAkB,IAAI,MAAM;AACjC,wBAAQ,KAAK,gBAAgB,IAAI,iBAAiB,QAAQ,QAAQ,OAAO,EAAE,OAAO,EAAE;cACtF;YACF;UACF;QACF;AAEA,eAAO;MACT,GAAG,gBAAgB,EAAE,GAAG,aAAa,MAAM,GAAG,aAAa,EAAE,IAAI,MAAS;IAC5E;AACA,WAAO,gBAAgB,KAAK,IAAI,iBAAiB,IAAI;EACvD;AACF;;;ACngBO,IAAM,aAAqC;AAAA;AAAA,EAEhD,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EAC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","names":["probe","readFile","mix","join","join","join","join","join","mkdtemp","rm","tmpdir","mkdtemp","join","tmpdir","rm","pct","str","pct","sleep","WINDOW_MS","pct","BACKOFF_BASE_MS","run"]}