{"version":3,"file":"server-R9UkCn3v.mjs","names":[],"sources":["../src/workers/paths.ts","../src/workers/routing.ts","../src/workers/run-env.ts","../src/workers/proxy/server.ts"],"sourcesContent":["/**\n * paths.ts — where worker run artefacts live.\n *\n * Everything a run writes (event mirror, status file, pane registry, routing\n * state, the empty MCP config) sits under one logDir so the whole tree is\n * disposable and configurable: `workers.logDir`, default ~/.claude/logs/workers.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { expandHome, DEFAULT_LOG_DIR, type WorkersConfig } from \"./config.js\";\nimport { paiHomePath, resolvePaiFile } from \"../config/pai-home.js\";\n\n/**\n * Absolute logDir for the given config. An explicit `logDir` in workers.yaml\n * is honored verbatim (the user chose it). Otherwise this resolves like every\n * other PAI_HOME file: PAI_HOME/logs/workers if it exists, else the old\n * ~/.claude/logs/workers (with a one-time notice), else the new path.\n */\nexport function workersLogDir(config: WorkersConfig): string {\n  const expanded = expandHome(config.logDir);\n  if (config.logDir !== DEFAULT_LOG_DIR) return expanded;\n  return resolvePaiFile(paiHomePath(\"logs\", \"workers\"), [expanded], \"pai config migrate --logs\");\n}\n\n/** The strict empty MCP config for headless runs, written on demand. */\nexport function ensureNoMcpConfig(logDir: string): string {\n  const path = noMcpConfigPath(logDir);\n  if (!existsSync(path)) {\n    mkdirSync(logDir, { recursive: true });\n    writeFileSync(path, '{ \"mcpServers\": {} }\\n', \"utf8\");\n  }\n  return path;\n}\n\nexport function statusPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.status`);\n}\n\nexport function eventsPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.jsonl`);\n}\n\nexport function ledgerPath(logDir: string): string {\n  return join(logDir, \"ledger.log\");\n}\n\nexport function routingStatePath(logDir: string): string {\n  return join(logDir, \"routing-state.json\");\n}\n\nexport function panesDir(logDir: string): string {\n  return join(logDir, \"panes\");\n}\n\n/**\n * The strict empty MCP config handed to headless workers. Written into the\n * logDir on demand (never into the user's vendor config directory — this is\n * PAI state, not vendor state).\n */\nexport function noMcpConfigPath(logDir: string): string {\n  return join(logDir, \"no-mcp.json\");\n}\n","/**\n * routing.ts — provider selection: flag > class > active (possibly \"auto\").\n *\n * Auto-routing walks `workers.routing.order` — or the class's own `order` —\n * and takes the first provider that is enabled, out of cooldown, (when it\n * defines a quotaProbe) under its quotaSkipAt threshold, and — when the class\n * constrains it — within `maxCostTier` and carrying all `requireTags`. A run\n * that dies of a quota/rate error puts its provider in cooldown for\n * cooldownMinutes; when that happens before the first tool call and\n * retryOnQuota is set, the runner restarts the same task on the next provider\n * (ledger: WORKER-REROUTE).\n *\n * An explicit --provider or a class mapping that pins a provider always\n * bypasses all of this.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n  type WorkerEngine,\n  type WorkerProvider,\n  type WorkersConfig,\n  type ProviderTag,\n  WORKER_CLASSES,\n  WorkersConfigError,\n  classModelCapability,\n  providerCostTier,\n  getProviderOrNative,\n  resolveCapability,\n} from \"./config.js\";\nimport { routingStatePath } from \"./paths.js\";\n\nconst QUOTA_SKIP_DEFAULT = 95;\nconst PROBE_TIMEOUT_MS = 10_000;\n\nexport interface RoutingState {\n  /** provider name → ISO timestamp when its cooldown ends */\n  cooldowns: Record<string, string>;\n}\n\nexport function readRoutingState(logDir: string): RoutingState {\n  const path = routingStatePath(logDir);\n  if (!existsSync(path)) return { cooldowns: {} };\n  try {\n    const parsed = JSON.parse(readFileSync(path, \"utf8\")) as RoutingState;\n    return { cooldowns: parsed.cooldowns ?? {} };\n  } catch {\n    return { cooldowns: {} };\n  }\n}\n\nexport function writeRoutingState(logDir: string, state: RoutingState): void {\n  const path = routingStatePath(logDir);\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tmp = `${path}.tmp`;\n  writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n  renameSync(tmp, path);\n}\n\nexport function cooldownRemaining(\n  state: RoutingState,\n  provider: string,\n  now: Date = new Date()\n): number {\n  const end = state.cooldowns[provider];\n  if (!end) return 0;\n  const ms = Date.parse(end) - now.getTime();\n  return ms > 0 ? ms : 0;\n}\n\nexport function setCooldown(\n  logDir: string,\n  provider: string,\n  minutes: number,\n  now: Date = new Date()\n): void {\n  const state = readRoutingState(logDir);\n  state.cooldowns[provider] = new Date(now.getTime() + minutes * 60_000).toISOString();\n  writeRoutingState(logDir, state);\n}\n\nexport function clearCooldown(logDir: string, provider: string): void {\n  const state = readRoutingState(logDir);\n  if (!(provider in state.cooldowns)) return;\n  delete state.cooldowns[provider];\n  writeRoutingState(logDir, state);\n}\n\n/**\n * Run a provider's quotaProbe and return its percentage (0–100), or null when\n * there is no probe or it printed nothing usable. A probe must never break\n * routing: failures read as \"unknown\", not as \"full\".\n */\nexport function probeQuota(provider: WorkerProvider): number | null {\n  if (!provider.quotaProbe) return null;\n  try {\n    const out = execFileSync(\"/bin/sh\", [\"-c\", provider.quotaProbe], {\n      timeout: PROBE_TIMEOUT_MS,\n      encoding: \"utf8\",\n      stdio: [\"ignore\", \"pipe\", \"ignore\"],\n    });\n    const m = out.match(/(\\d+(?:\\.\\d+)?)/);\n    if (!m) return null;\n    return Math.min(100, Math.max(0, Math.round(parseFloat(m[1]))));\n  } catch {\n    return null;\n  }\n}\n\nexport function quotaSkipThreshold(provider: WorkerProvider): number {\n  return provider.quotaSkipAt ?? QUOTA_SKIP_DEFAULT;\n}\n\n/** Does this provider exceed its quota threshold right now? */\nexport function quotaExceeded(provider: WorkerProvider): boolean {\n  const used = probeQuota(provider);\n  return used !== null && used >= quotaSkipThreshold(provider);\n}\n\nexport interface ResolvedTarget {\n  providerName: string;\n  provider: WorkerProvider;\n  /** Model alias from the class (\"glm/fast\" → \"fast\"), null = provider default. */\n  modelAlias: string | null;\n  /** MCP servers (or set names) from the class target, null when it sets none. */\n  classMcp: string[] | null;\n  /** How the provider was chosen — names the bypass rule for rerouting. */\n  via: \"flag\" | \"class\" | \"active\" | \"auto\";\n}\n\nexport class NoProviderError extends WorkersConfigError {}\n\nfunction mustExist(config: WorkersConfig, name: string): WorkerProvider {\n  const p = getProviderOrNative(config, name);\n  if (!p) {\n    throw new NoProviderError(\n      `no worker provider named \"${name}\". Configured: ` +\n        `${Object.keys(config.providers).join(\", \") || \"(none)\"}.` +\n        `\\nAdd one with: pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n    );\n  }\n  return p;\n}\n\nfunction mustBeRunnable(name: string, p: WorkerProvider): WorkerProvider {\n  if (!p.enabled) {\n    throw new NoProviderError(\n      `provider \"${name}\" is disabled. Enable it with: pai worker providers enable ${name}`\n    );\n  }\n  return p;\n}\n\n/** Why one provider of a routing order did not qualify (message fragment). */\nfunction exclusionReason(\n  config: WorkersConfig,\n  state: RoutingState,\n  name: string,\n  cls?: { maxCostTier?: number; requireTags?: string[] }\n): string | null {\n  const p = getProviderOrNative(config, name);\n  if (!p) return \"not configured\";\n  if (!p.enabled) return \"disabled\";\n  if (cooldownRemaining(state, name) > 0) return \"cooldown\";\n  if (quotaExceeded(p)) return \"quota\";\n  const tier = providerCostTier(p);\n  if (cls?.maxCostTier !== undefined && tier > cls.maxCostTier) {\n    return `cost tier ${tier} > max ${cls.maxCostTier}`;\n  }\n  if (cls?.requireTags?.length) {\n    const have = p.tags ?? [];\n    const missing = cls.requireTags.filter((t) => !have.includes(t as ProviderTag));\n    if (missing.length) return `missing tags: ${missing.join(\", \")}`;\n  }\n  return null;\n}\n\n/**\n * Resolve which provider (and model alias) a run uses.\n *\n * @param flagProvider --provider value, highest precedence\n * @param className    --class value (the old --role), looked up in workers.classes\n */\nexport function resolveTarget(\n  config: WorkersConfig,\n  logDir: string,\n  opts: { flagProvider?: string; className?: string } = {}\n): ResolvedTarget {\n  if (opts.flagProvider) {\n    return {\n      providerName: opts.flagProvider,\n      provider: mustBeRunnable(opts.flagProvider, mustExist(config, opts.flagProvider)),\n      modelAlias: null,\n      classMcp: null,\n      via: \"flag\",\n    };\n  }\n\n  // class constraints (present whether or not the target pins a provider)\n  const clsTarget = opts.className ? config.classes[opts.className] : undefined;\n  const cls =\n    typeof clsTarget === \"object\" && clsTarget !== null ? clsTarget : undefined;\n\n  if (opts.className && clsTarget !== undefined && typeof clsTarget !== \"object\") {\n    const [name, alias] = clsTarget.split(\"/\");\n    return {\n      providerName: name,\n      provider: mustBeRunnable(name, mustExist(config, name)),\n      modelAlias: alias ?? null,\n      classMcp: null,\n      via: \"class\",\n    };\n  }\n  if (opts.className && cls?.provider) {\n    const provider = mustBeRunnable(cls.provider, mustExist(config, cls.provider));\n    return {\n      providerName: cls.provider,\n      provider,\n      modelAlias: null,\n      classMcp: cls.mcp ?? null,\n      via: \"class\",\n    };\n  }\n  if (opts.className && clsTarget === undefined) {\n    // a standard class may simply be unconfigured (it then routes like a run\n    // without a class); anything else is a typo and must not pass silently\n    if (!(WORKER_CLASSES as readonly string[]).includes(opts.className)) {\n      throw new NoProviderError(\n        `no class named \"${opts.className}\". Standard classes: ${WORKER_CLASSES.join(\", \")}.` +\n          `Defined: ${Object.keys(config.classes).join(\", \") || \"(none)\"}.` +\n          `\\nSet one with: pai worker classes set ${opts.className}=<provider[/alias]>`\n      );\n    }\n  }\n\n  if (config.active !== \"auto\") {\n    const name = config.active;\n    if (!name) {\n      throw new NoProviderError(\n        `no active worker provider. Add one with: ` +\n          `pai worker providers add <name> --base-url <url> --key-file <path> --model <model>` +\n          `\\n(or point one that exists at it: pai worker providers use <name>)`\n      );\n    }\n    return {\n      providerName: name,\n      provider: mustBeRunnable(name, mustExist(config, name)),\n      modelAlias: null,\n      classMcp: null,\n      via: \"active\",\n    };\n  }\n\n  // auto: first provider in (class or global) order that is enabled,\n  // cooled-down-free, under quota and within the class constraints\n  const state = readRoutingState(logDir);\n  const order = cls?.order ?? config.routing.order;\n  const excluded: string[] = [];\n  for (const name of order) {\n    const p = getProviderOrNative(config, name);\n    const why = exclusionReason(config, state, name, cls);\n    if (why) {\n      excluded.push(`${name}: ${why}`);\n      continue;\n    }\n    // exclusionReason already returned null (no exclusion) above, which is\n    // only possible when getProviderOrNative found a real provider\n    return { providerName: name, provider: p!, modelAlias: null, classMcp: cls?.mcp ?? null, via: \"auto\" };\n  }\n  throw new NoProviderError(\n    `auto-routing found no usable provider${opts.className ? ` for class \"${opts.className}\"` : \"\"} ` +\n      `(order: [${order.join(\", \")}]).` +\n      `${excluded.length ? `\\nExcluded: ${excluded.join(\"; \")}.` : \"\"}` +\n      `\\nClear a cooldown with: pai worker providers enable <name>; widen the class with: pai worker classes set ${opts.className ?? \"<class>\"}=<target>`\n  );\n}\n\n/**\n * Next provider after `from` in auto order, applying the same filters.\n * Used by rerouting; null when the order is exhausted.\n */\nexport function nextAutoProvider(\n  config: WorkersConfig,\n  logDir: string,\n  from: string,\n  now: Date = new Date()\n): string | null {\n  const state = readRoutingState(logDir);\n  const order = config.routing.order;\n  const start = order.indexOf(from);\n  for (let i = start + 1; i < order.length; i++) {\n    const name = order[i];\n    const p = getProviderOrNative(config, name);\n    if (!p || !p.enabled) continue;\n    const end = state.cooldowns[name];\n    if (end && Date.parse(end) > now.getTime()) continue;\n    if (quotaExceeded(p)) continue;\n    return name;\n  }\n  return null;\n}\n\n/**\n * Was this failure a quota/rate failure? Detected from the result text the\n * endpoint produced (HTTP status is not visible in the stream events).\n */\nexport function isQuotaFailure(resultText: string): boolean {\n  return /usage limit reached|rate limit|quota/i.test(resultText);\n}\n\nexport interface CapabilityRunResolution {\n  providerName: string;\n  provider: WorkerProvider;\n  model: string;\n  engine: WorkerEngine;\n  fellBack: boolean;\n  capability: string;\n}\n\n/**\n * Whether a run should resolve through `resolveCapability` instead of\n * `resolveTarget`: an explicit `--capability` always does. A `--class`\n * whose implied capability is not \"default\" (e.g. `image`) does too, but\n * only when the class itself names no provider — a class already pinned to\n * one (`image: glm/image`) always wins unchanged, same as any other class.\n */\nexport function capabilityForRun(\n  config: WorkersConfig,\n  opts: { capabilityFlag?: string; className?: string }\n): string | null {\n  if (opts.capabilityFlag) return opts.capabilityFlag;\n  if (!opts.className) return null;\n  const capability = classModelCapability(opts.className);\n  if (capability === \"default\") return null;\n  const clsTarget = config.classes[opts.className];\n  const namesProvider = typeof clsTarget === \"string\" ? true : Boolean(clsTarget?.provider);\n  return namesProvider ? null : capability;\n}\n\n/**\n * resolveCapability, turned into a runnable provider (NoProviderError-shaped\n * on failure). Unlike resolveCapability itself — which falls back to the\n * active provider's default model so `describeCapabilities` has something to\n * report — an actual run refuses the fallback: silently running a normal\n * model on a prompt written for a capability it does not have (an image\n * prompt fed to a text model, say) produces a confusing non-answer instead\n * of a clear error naming the config key to set.\n */\nexport function resolveCapabilityRun(config: WorkersConfig, capability: string): CapabilityRunResolution {\n  const r = resolveCapability(config, capability);\n  if (r.fellBack) {\n    throw new NoProviderError(\n      `no provider declares the \"${capability}\" capability — configure one with: ` +\n        `pai worker capability ${capability} <provider>[,<provider>…], or ` +\n        `pai worker model ${capability} <model-id> --provider <name>`\n    );\n  }\n  // \"image\" specifically promises real pixel output — a provider whose only\n  // claim to it is a `models.image` entry from the classes-based idiom\n  // (`image: glm/image`, a plain model choice on an unchanged claude/codex\n  // engine) cannot deliver that through this cross-provider run path, so a\n  // run refuses it rather than spawning claude on a prompt it cannot answer.\n  if (capability === \"image\" && r.engine !== \"image\") {\n    throw new NoProviderError(\n      `provider \"${r.provider}\" has no image engine (it would run \"${r.model}\" on its normal ` +\n        `${r.engine} engine, not generate an image) — configure an engine=image provider with: ` +\n        `pai worker providers add <name> --engine image --url <url> --key <key> --model <id>, then ` +\n        `pai worker capability image <name>`\n    );\n  }\n  const provider = getProviderOrNative(config, r.provider);\n  if (!provider) {\n    throw new NoProviderError(`capability \"${capability}\" resolved to unknown provider \"${r.provider}\"`);\n  }\n  return {\n    providerName: r.provider,\n    provider,\n    model: r.model,\n    engine: r.engine,\n    fellBack: r.fellBack,\n    capability,\n  };\n}\n","/**\n * run-env.ts — the provider environment every Claude Code spawn must run with.\n *\n * Extracted from run.ts so daemon-side background spawns (session summaries,\n * context handovers, KG extraction) go through the exact same env the worker\n * runner uses, instead of importing the whole runner into the daemon.\n */\n\nimport { resolveModelCapability, resolveProviderKey, type WorkerProvider } from \"./config.js\";\n\n/**\n * Session-identity variables the SPAWNING session leaves in the environment.\n * Inherited, they make a headless child attach to the spawner's messaging\n * socket instead of standing on its own — and a worktree-mode child then\n * starts with its core tools (Bash/Read/…) deferred out of its reach, left\n * with nothing but the tool-registry search (2026-09-18). The child is its\n * own session, so none of these may cross the spawn boundary.\n */\nconst SESSION_IDENTITY_VARS = [\n  \"CLAUDE_CODE_MESSAGING_SOCKET\",\n  \"CLAUDE_CODE_MESSAGING_TOKEN\",\n  \"CLAUDE_CODE_SESSION_ID\",\n  \"CLAUDE_CODE_CHILD_SESSION\",\n  \"CLAUDE_PID\",\n  \"CLAUDECODE\",\n] as const;\n\n/**\n * Harness settings the spawner's environment carries that a headless worker\n * must not keep. ENABLE_TOOL_SEARCH reaches every pai process through the\n * user settings' env block, so a pai spawned from inside a Claude session\n * passes it on to worker children; inherited, the headless child starts with\n * every core tool deferred out of reach — only ToolSearch remains callable,\n * granted tools included (2026-09-18, verified by stripping exactly this one\n * var). Interactive runs set it deliberately below.\n */\nconst HEADLESS_STRIP_VARS = [\"ENABLE_TOOL_SEARCH\"] as const;\n\n/**\n * Provider-specific env vars a native-Anthropic run must never carry — not\n * set by this function, and stripped even when a parent shell exported them\n * (e.g. this worker was itself spawned from a GLM-configured environment).\n */\nconst NATIVE_STRIP_VARS = [\n  \"ANTHROPIC_BASE_URL\",\n  \"ANTHROPIC_AUTH_TOKEN\",\n  \"ANTHROPIC_DEFAULT_HAIKU_MODEL\",\n  \"ANTHROPIC_DEFAULT_SONNET_MODEL\",\n  \"ANTHROPIC_DEFAULT_OPUS_MODEL\",\n] as const;\n\n/** Environment for a run through `provider`. Caller's env minus the Anthropic key. */\nexport function buildRunEnv(\n  provider: WorkerProvider,\n  headless: boolean,\n  proxyUrl?: string\n): NodeJS.ProcessEnv {\n  const env: NodeJS.ProcessEnv = { ...process.env };\n  delete env.ANTHROPIC_API_KEY;\n  if (headless) {\n    for (const k of SESSION_IDENTITY_VARS) delete env[k];\n    for (const k of HEADLESS_STRIP_VARS) delete env[k];\n  }\n\n  if (provider.native) {\n    // Plain Claude Code on its own OAuth/Max-plan login: no base URL, no\n    // token, no provider-specific model pins — and none inherited from a\n    // shell that had a different provider exported into it either.\n    for (const k of NATIVE_STRIP_VARS) delete env[k];\n    env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = \"1\";\n    if (headless) {\n      env.PAI_WORKER = \"1\";\n    } else {\n      env.ENABLE_TOOL_SEARCH = \"true\";\n    }\n    return env;\n  }\n\n  let token = \"local\";\n  if (proxyUrl) {\n    // openai-protocol provider: the proxy holds the real key; the runner only\n    // needs a placeholder so Claude Code sends an auth header at all\n    env.ANTHROPIC_BASE_URL = proxyUrl;\n  } else {\n    const resolved = resolveProviderKey(provider);\n    if (resolved) token = resolved;\n    env.ANTHROPIC_BASE_URL = provider.baseUrl;\n  }\n\n  env.ANTHROPIC_AUTH_TOKEN = token;\n  env.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolveModelCapability(provider, \"fast\");\n  env.ANTHROPIC_DEFAULT_SONNET_MODEL = provider.models.default;\n  env.ANTHROPIC_DEFAULT_OPUS_MODEL = provider.models.default;\n  for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n  env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = \"1\";\n  if (headless) {\n    env.PAI_WORKER = \"1\";\n  } else {\n    env.ENABLE_TOOL_SEARCH = \"true\";\n  }\n  return env;\n}\n","/**\n * server.ts — the PAI worker proxy: Anthropic Messages API on the front,\n * OpenAI Chat Completions on the back, loopback only.\n *\n * One proxy serves every OpenAI-protocol provider: Claude Code points\n * ANTHROPIC_BASE_URL at `http://127.0.0.1:8797/<provider>` and the provider\n * name in the path selects the upstream (`upstreamUrl` + `keyFile` from the\n * workers config, re-read per request so config edits apply without a\n * restart). `run` starts the proxy on demand (detached, pid file under the\n * logDir) and `pai worker proxy stop` stops it again.\n *\n * All translation lives in translate.ts; this file is only HTTP plumbing.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer, type Server } from \"node:http\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readWorkersSection, resolveProviderKey, type WorkerProvider } from \"../config.js\";\nimport { workersLogDir } from \"../paths.js\";\nimport {\n  anthropicError,\n  anthropicToOpenAi,\n  openAiToAnthropic,\n  OpenAiStreamTranslator,\n  type AnthropicRequest,\n} from \"./translate.js\";\n\nexport const DEFAULT_PROXY_PORT = 8797;\nconst HOST = \"127.0.0.1\";\n\n/** How providers are looked up — swapped out by the tests. */\nexport type ProviderResolver = () => Record<string, WorkerProvider>;\n\nconst configProviders: ProviderResolver = () => readWorkersSection().workers.providers;\n\nexport function proxyPidPath(logDir: string): string {\n  return join(logDir, \"proxy.pid\");\n}\n\n// ---------------------------------------------------------------------------\n// HTTP server\n// ---------------------------------------------------------------------------\n\ninterface ProxyServerOptions {\n  port?: number;\n  resolveProvider?: ProviderResolver;\n  /** Test hook: called with every (translated) upstream request body. */\n  onUpstreamRequest?: (url: string, body: unknown) => void;\n}\n\n/**\n * Create (but not start) the proxy server. Routes:\n *   GET  /healthz              → 200 ok\n *   POST /<provider>/v1/messages (and /<provider>/messages) → translated call\n */\nexport function createProxyServer(opts: ProxyServerOptions = {}): Server {\n  const resolveProvider = opts.resolveProvider ?? configProviders;\n  return createServer((req, res) => {\n    const url = new URL(req.url ?? \"/\", `http://${HOST}`);\n    if (req.method === \"GET\" && url.pathname === \"/healthz\") {\n      res.writeHead(200, { \"content-type\": \"text/plain\" });\n      res.end(\"ok\");\n      return;\n    }\n    const m = url.pathname.match(/^\\/([a-zA-Z0-9_-]+)\\/(v1\\/)?messages$/);\n    if (req.method !== \"POST\" || !m) {\n      res.writeHead(404, { \"content-type\": \"application/json\" });\n      res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));\n      return;\n    }\n    const providerName = m[1];\n    const chunks: Buffer[] = [];\n    req.on(\"data\", (c: Buffer) => chunks.push(c));\n    req.on(\"end\", () => {\n      void handleMessages(providerName, Buffer.concat(chunks).toString(\"utf8\"), resolveProvider, res, opts);\n    });\n    req.on(\"error\", () => {\n      res.writeHead(400, { \"content-type\": \"application/json\" });\n      res.end(JSON.stringify(anthropicError(400, \"request body read failed\")));\n    });\n  });\n}\n\nasync function handleMessages(\n  providerName: string,\n  bodyText: string,\n  resolveProvider: ProviderResolver,\n  res: import(\"node:http\").ServerResponse,\n  opts: ProxyServerOptions\n): Promise<void> {\n  const fail = (status: number, message: string) => {\n    res.writeHead(status, { \"content-type\": \"application/json\" });\n    res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));\n  };\n  let reqBody: AnthropicRequest;\n  try {\n    reqBody = JSON.parse(bodyText) as AnthropicRequest;\n  } catch {\n    fail(400, \"request body is not valid JSON\");\n    return;\n  }\n  const provider = resolveProvider()[providerName];\n  if (!provider) {\n    fail(404, `no worker provider named \"${providerName}\"`);\n    return;\n  }\n  if (provider.protocol !== \"openai\" || !provider.upstreamUrl) {\n    fail(400, `provider \"${providerName}\" is not an openai-protocol provider`);\n    return;\n  }\n\n  const model = provider.models.default;\n  const openaiBody = anthropicToOpenAi(reqBody, model);\n  opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);\n\n  let upstream: Response;\n  try {\n    const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n    let token: string | null;\n    try {\n      token = resolveProviderKey(provider);\n    } catch (e) {\n      fail(500, e instanceof Error ? e.message : String(e));\n      return;\n    }\n    if (token) headers.authorization = `Bearer ${token}`;\n    upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {\n      method: \"POST\",\n      headers,\n      body: JSON.stringify(openaiBody),\n    });\n  } catch (e) {\n    fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);\n    return;\n  }\n\n  if (!upstream.ok) {\n    const text = await upstream.text().catch(() => \"\");\n    res.writeHead(upstream.status, { \"content-type\": \"application/json\" });\n    res.end(JSON.stringify(anthropicError(upstream.status, text)));\n    return;\n  }\n\n  if (reqBody.stream) {\n    res.writeHead(200, {\n      \"content-type\": \"text/event-stream\",\n      \"cache-control\": \"no-cache\",\n      connection: \"keep-alive\",\n    });\n    const translator = new OpenAiStreamTranslator(model);\n    let buf = \"\";\n    if (!upstream.body) {\n      res.end(translator.finish());\n      return;\n    }\n    const reader = upstream.body.getReader();\n    try {\n      for (;;) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        buf += new TextDecoder().decode(value, { stream: true });\n        let nl: number;\n        while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n          const line = buf.slice(0, nl).trim();\n          buf = buf.slice(nl + 1);\n          if (!line.startsWith(\"data:\")) continue;\n          const payload = line.slice(5).trim();\n          if (payload === \"[DONE]\") {\n            res.write(translator.finish());\n          } else {\n            try {\n              res.write(translator.feed(JSON.parse(payload) as Parameters<typeof translator.feed>[0]));\n            } catch {\n              // skip a malformed chunk rather than kill the stream\n            }\n          }\n        }\n      }\n    } finally {\n      res.write(translator.finish()); // no-op when [DONE] already finished it\n      res.end();\n    }\n    return;\n  }\n\n  const json = (await upstream.json().catch(() => null)) as unknown;\n  if (!json) {\n    fail(502, \"upstream returned a non-JSON body\");\n    return;\n  }\n  res.writeHead(200, { \"content-type\": \"application/json\" });\n  res.end(JSON.stringify(openAiToAnthropic(json as Parameters<typeof openAiToAnthropic>[0], model)));\n}\n\n// ---------------------------------------------------------------------------\n// Start / stop\n// ---------------------------------------------------------------------------\n\n/** Listen on host:port (loopback). Resolves with the bound port. */\nexport function listenProxy(server: Server, port = DEFAULT_PROXY_PORT): Promise<number> {\n  return new Promise((resolve, reject) => {\n    server.once(\"error\", reject);\n    server.listen(port, HOST, () => {\n      const addr = server.address();\n      resolve(typeof addr === \"object\" && addr ? addr.port : port);\n    });\n  });\n}\n\n/** True when something already accepts connections on the port. */\nexport function proxyListening(port = DEFAULT_PROXY_PORT, timeoutMs = 400): Promise<boolean> {\n  return new Promise((resolve) => {\n    const sock = connect({ port, host: HOST });\n    const done = (ok: boolean) => {\n      sock.removeAllListeners();\n      sock.destroy();\n      resolve(ok);\n    };\n    sock.setTimeout(timeoutMs, () => done(false));\n    sock.once(\"connect\", () => done(true));\n    sock.once(\"error\", () => done(false));\n  });\n}\n\n/**\n * Where the standalone proxy artefact lives, found from this module's own\n * location (dist/cli bundle → ../hooks/, dev checkout → <repo>/dist/hooks).\n */\nexport function standaloneProxyPath(): string | null {\n  let dir = dirname(fileURLToPath(import.meta.url));\n  for (let i = 0; i < 6; i++) {\n    for (const cand of [\n      join(dir, \"worker-proxy.mjs\"),\n      join(dir, \"hooks\", \"worker-proxy.mjs\"),\n      join(dir, \"dist\", \"hooks\", \"worker-proxy.mjs\"),\n    ]) {\n      if (existsSync(cand)) return cand;\n    }\n    const parent = dirname(dir);\n    if (parent === dir) break;\n    dir = parent;\n  }\n  return null;\n}\n\n/**\n * Ensure a proxy is listening on the port; start the detached standalone when\n * nothing answers. Returns the base URL (`http://127.0.0.1:<port>`).\n */\nexport async function ensureProxyRunning(port: number, logDir: string): Promise<string> {\n  if (await proxyListening(port)) return `http://${HOST}:${port}`;\n  const script = standaloneProxyPath();\n  if (!script) {\n    throw new Error(\n      `the PAI worker proxy is not running and its build was not found — ` +\n        `run \\`bun run build\\` (or start one with: pai worker proxy --port ${port})`\n    );\n  }\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  const child = spawn(process.execPath, [script, \"--port\", String(port)], {\n    detached: true,\n    stdio: \"ignore\",\n  });\n  child.unref();\n  writeFileSync(proxyPidPath(logDir), `${child.pid}\\n`, \"utf8\");\n  for (let i = 0; i < 40; i++) {\n    if (await proxyListening(port)) return `http://${HOST}:${port}`;\n    await new Promise((r) => setTimeout(r, 250));\n  }\n  throw new Error(`the PAI worker proxy did not come up on port ${port} (see ${proxyPidPath(logDir)})`);\n}\n\n/** `pai worker proxy stop`: SIGTERM the pid from the pid file. */\nexport function stopProxy(logDir: string): string {\n  const path = proxyPidPath(logDir);\n  if (!existsSync(path)) return \"no proxy pid file — nothing to stop\";\n  const pid = parseInt(readFileSync(path, \"utf8\").trim(), 10);\n  unlinkSync(path);\n  if (!Number.isFinite(pid)) return \"stale proxy pid file removed\";\n  try {\n    process.kill(pid, \"SIGTERM\");\n  } catch {\n    return `proxy pid ${pid} was not running (pid file removed)`;\n  }\n  return `proxy pid ${pid} stopped`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,QAA+B;CAC3D,MAAM,WAAW,WAAW,OAAO,OAAO;AAC1C,KAAI,OAAO,WAAW,gBAAiB,QAAO;AAC9C,QAAO,eAAe,YAAY,QAAQ,UAAU,EAAE,CAAC,SAAS,EAAE,4BAA4B;;;AAIhG,SAAgB,kBAAkB,QAAwB;CACxD,MAAM,OAAO,gBAAgB,OAAO;AACpC,KAAI,CAAC,WAAW,KAAK,EAAE;AACrB,YAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;AACtC,gBAAc,MAAM,4BAA0B,OAAO;;AAEvD,QAAO;;AAGT,SAAgB,WAAW,QAAgB,IAAoB;AAC7D,QAAO,KAAK,QAAQ,GAAG,GAAG,SAAS;;AAGrC,SAAgB,WAAW,QAAgB,IAAoB;AAC7D,QAAO,KAAK,QAAQ,GAAG,GAAG,QAAQ;;AAGpC,SAAgB,WAAW,QAAwB;AACjD,QAAO,KAAK,QAAQ,aAAa;;AAGnC,SAAgB,iBAAiB,QAAwB;AACvD,QAAO,KAAK,QAAQ,qBAAqB;;AAG3C,SAAgB,SAAS,QAAwB;AAC/C,QAAO,KAAK,QAAQ,QAAQ;;;;;;;AAQ9B,SAAgB,gBAAgB,QAAwB;AACtD,QAAO,KAAK,QAAQ,cAAc;;;;;;;;;;;;;;;;;;;;AC5BpC,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAOzB,SAAgB,iBAAiB,QAA8B;CAC7D,MAAM,OAAO,iBAAiB,OAAO;AACrC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE,WAAW,EAAE,EAAE;AAC/C,KAAI;AAEF,SAAO,EAAE,WADM,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC,CAC1B,aAAa,EAAE,EAAE;SACtC;AACN,SAAO,EAAE,WAAW,EAAE,EAAE;;;AAI5B,SAAgB,kBAAkB,QAAgB,OAA2B;CAC3E,MAAM,OAAO,iBAAiB,OAAO;CACrC,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACzD,MAAM,MAAM,GAAG,KAAK;AACpB,eAAc,KAAK,KAAK,UAAU,OAAO,MAAM,EAAE,GAAG,MAAM,OAAO;AACjE,YAAW,KAAK,KAAK;;AAGvB,SAAgB,kBACd,OACA,UACA,sBAAY,IAAI,MAAM,EACd;CACR,MAAM,MAAM,MAAM,UAAU;AAC5B,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,KAAK,KAAK,MAAM,IAAI,GAAG,IAAI,SAAS;AAC1C,QAAO,KAAK,IAAI,KAAK;;AAGvB,SAAgB,YACd,QACA,UACA,SACA,sBAAY,IAAI,MAAM,EAChB;CACN,MAAM,QAAQ,iBAAiB,OAAO;AACtC,OAAM,UAAU,YAAY,IAAI,KAAK,IAAI,SAAS,GAAG,UAAU,IAAO,CAAC,aAAa;AACpF,mBAAkB,QAAQ,MAAM;;AAGlC,SAAgB,cAAc,QAAgB,UAAwB;CACpE,MAAM,QAAQ,iBAAiB,OAAO;AACtC,KAAI,EAAE,YAAY,MAAM,WAAY;AACpC,QAAO,MAAM,UAAU;AACvB,mBAAkB,QAAQ,MAAM;;;;;;;AAQlC,SAAgB,WAAW,UAAyC;AAClE,KAAI,CAAC,SAAS,WAAY,QAAO;AACjC,KAAI;EAMF,MAAM,IALM,aAAa,WAAW,CAAC,MAAM,SAAS,WAAW,EAAE;GAC/D,SAAS;GACT,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CACY,MAAM,kBAAkB;AACtC,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;SACzD;AACN,SAAO;;;AAIX,SAAgB,mBAAmB,UAAkC;AACnE,QAAO,SAAS,eAAe;;;AAIjC,SAAgB,cAAc,UAAmC;CAC/D,MAAM,OAAO,WAAW,SAAS;AACjC,QAAO,SAAS,QAAQ,QAAQ,mBAAmB,SAAS;;AAc9D,IAAa,kBAAb,cAAqC,mBAAmB;AAExD,SAAS,UAAU,QAAuB,MAA8B;CACtE,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,KAAI,CAAC,EACH,OAAM,IAAI,gBACR,6BAA6B,KAAK,iBAC7B,OAAO,KAAK,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,SAAS,qGAE3D;AAEH,QAAO;;AAGT,SAAS,eAAe,MAAc,GAAmC;AACvE,KAAI,CAAC,EAAE,QACL,OAAM,IAAI,gBACR,aAAa,KAAK,6DAA6D,OAChF;AAEH,QAAO;;;AAIT,SAAS,gBACP,QACA,OACA,MACA,KACe;CACf,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,KAAI,CAAC,EAAG,QAAO;AACf,KAAI,CAAC,EAAE,QAAS,QAAO;AACvB,KAAI,kBAAkB,OAAO,KAAK,GAAG,EAAG,QAAO;AAC/C,KAAI,cAAc,EAAE,CAAE,QAAO;CAC7B,MAAM,OAAO,iBAAiB,EAAE;AAChC,KAAI,KAAK,gBAAgB,UAAa,OAAO,IAAI,YAC/C,QAAO,aAAa,KAAK,SAAS,IAAI;AAExC,KAAI,KAAK,aAAa,QAAQ;EAC5B,MAAM,OAAO,EAAE,QAAQ,EAAE;EACzB,MAAM,UAAU,IAAI,YAAY,QAAQ,MAAM,CAAC,KAAK,SAAS,EAAiB,CAAC;AAC/E,MAAI,QAAQ,OAAQ,QAAO,iBAAiB,QAAQ,KAAK,KAAK;;AAEhE,QAAO;;;;;;;;AAST,SAAgB,cACd,QACA,QACA,OAAsD,EAAE,EACxC;AAChB,KAAI,KAAK,aACP,QAAO;EACL,cAAc,KAAK;EACnB,UAAU,eAAe,KAAK,cAAc,UAAU,QAAQ,KAAK,aAAa,CAAC;EACjF,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CAIH,MAAM,YAAY,KAAK,YAAY,OAAO,QAAQ,KAAK,aAAa;CACpE,MAAM,MACJ,OAAO,cAAc,YAAY,cAAc,OAAO,YAAY;AAEpE,KAAI,KAAK,aAAa,cAAc,UAAa,OAAO,cAAc,UAAU;EAC9E,MAAM,CAAC,MAAM,SAAS,UAAU,MAAM,IAAI;AAC1C,SAAO;GACL,cAAc;GACd,UAAU,eAAe,MAAM,UAAU,QAAQ,KAAK,CAAC;GACvD,YAAY,SAAS;GACrB,UAAU;GACV,KAAK;GACN;;AAEH,KAAI,KAAK,aAAa,KAAK,UAAU;EACnC,MAAM,WAAW,eAAe,IAAI,UAAU,UAAU,QAAQ,IAAI,SAAS,CAAC;AAC9E,SAAO;GACL,cAAc,IAAI;GAClB;GACA,YAAY;GACZ,UAAU,IAAI,OAAO;GACrB,KAAK;GACN;;AAEH,KAAI,KAAK,aAAa,cAAc,QAGlC;MAAI,CAAE,eAAqC,SAAS,KAAK,UAAU,CACjE,OAAM,IAAI,gBACR,mBAAmB,KAAK,UAAU,uBAAuB,eAAe,KAAK,KAAK,CAAC,YACrE,OAAO,KAAK,OAAO,QAAQ,CAAC,KAAK,KAAK,IAAI,SAAS,0CACrB,KAAK,UAAU,qBAC5D;;AAIL,KAAI,OAAO,WAAW,QAAQ;EAC5B,MAAM,OAAO,OAAO;AACpB,MAAI,CAAC,KACH,OAAM,IAAI,gBACR,iMAGD;AAEH,SAAO;GACL,cAAc;GACd,UAAU,eAAe,MAAM,UAAU,QAAQ,KAAK,CAAC;GACvD,YAAY;GACZ,UAAU;GACV,KAAK;GACN;;CAKH,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,QAAQ,KAAK,SAAS,OAAO,QAAQ;CAC3C,MAAM,WAAqB,EAAE;AAC7B,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,IAAI,oBAAoB,QAAQ,KAAK;EAC3C,MAAM,MAAM,gBAAgB,QAAQ,OAAO,MAAM,IAAI;AACrD,MAAI,KAAK;AACP,YAAS,KAAK,GAAG,KAAK,IAAI,MAAM;AAChC;;AAIF,SAAO;GAAE,cAAc;GAAM,UAAU;GAAI,YAAY;GAAM,UAAU,KAAK,OAAO;GAAM,KAAK;GAAQ;;AAExG,OAAM,IAAI,gBACR,wCAAwC,KAAK,YAAY,eAAe,KAAK,UAAU,KAAK,GAAG,YACjF,MAAM,KAAK,KAAK,CAAC,KAC1B,SAAS,SAAS,eAAe,SAAS,KAAK,KAAK,CAAC,KAAK,+GACgD,KAAK,aAAa,UAAU,WAC5I;;;;;;AAOH,SAAgB,iBACd,QACA,QACA,MACA,sBAAY,IAAI,MAAM,EACP;CACf,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,QAAQ,OAAO,QAAQ;CAC7B,MAAM,QAAQ,MAAM,QAAQ,KAAK;AACjC,MAAK,IAAI,IAAI,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK;EAC7C,MAAM,OAAO,MAAM;EACnB,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,MAAI,CAAC,KAAK,CAAC,EAAE,QAAS;EACtB,MAAM,MAAM,MAAM,UAAU;AAC5B,MAAI,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI,SAAS,CAAE;AAC5C,MAAI,cAAc,EAAE,CAAE;AACtB,SAAO;;AAET,QAAO;;;;;;AAOT,SAAgB,eAAe,YAA6B;AAC1D,QAAO,wCAAwC,KAAK,WAAW;;;;;;;;;AAmBjE,SAAgB,iBACd,QACA,MACe;AACf,KAAI,KAAK,eAAgB,QAAO,KAAK;AACrC,KAAI,CAAC,KAAK,UAAW,QAAO;CAC5B,MAAM,aAAa,qBAAqB,KAAK,UAAU;AACvD,KAAI,eAAe,UAAW,QAAO;CACrC,MAAM,YAAY,OAAO,QAAQ,KAAK;AAEtC,SADsB,OAAO,cAAc,WAAW,OAAO,QAAQ,WAAW,SAAS,IAClE,OAAO;;;;;;;;;;;AAYhC,SAAgB,qBAAqB,QAAuB,YAA6C;CACvG,MAAM,IAAI,kBAAkB,QAAQ,WAAW;AAC/C,KAAI,EAAE,SACJ,OAAM,IAAI,gBACR,6BAA6B,WAAW,2DACb,WAAW,iDAChB,WAAW,+BAClC;AAOH,KAAI,eAAe,WAAW,EAAE,WAAW,QACzC,OAAM,IAAI,gBACR,aAAa,EAAE,SAAS,uCAAuC,EAAE,MAAM,kBAClE,EAAE,OAAO,yMAGf;CAEH,MAAM,WAAW,oBAAoB,QAAQ,EAAE,SAAS;AACxD,KAAI,CAAC,SACH,OAAM,IAAI,gBAAgB,eAAe,WAAW,kCAAkC,EAAE,SAAS,GAAG;AAEtG,QAAO;EACL,cAAc,EAAE;EAChB;EACA,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,UAAU,EAAE;EACZ;EACD;;;;;;;;;;;;;;;;;;;;AC7WH,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAM,sBAAsB,CAAC,qBAAqB;;;;;;AAOlD,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACD;;AAGD,SAAgB,YACd,UACA,UACA,UACmB;CACnB,MAAM,MAAyB,EAAE,GAAG,QAAQ,KAAK;AACjD,QAAO,IAAI;AACX,KAAI,UAAU;AACZ,OAAK,MAAM,KAAK,sBAAuB,QAAO,IAAI;AAClD,OAAK,MAAM,KAAK,oBAAqB,QAAO,IAAI;;AAGlD,KAAI,SAAS,QAAQ;AAInB,OAAK,MAAM,KAAK,kBAAmB,QAAO,IAAI;AAC9C,MAAI,2CAA2C;AAC/C,MAAI,SACF,KAAI,aAAa;MAEjB,KAAI,qBAAqB;AAE3B,SAAO;;CAGT,IAAI,QAAQ;AACZ,KAAI,SAGF,KAAI,qBAAqB;MACpB;EACL,MAAM,WAAW,mBAAmB,SAAS;AAC7C,MAAI,SAAU,SAAQ;AACtB,MAAI,qBAAqB,SAAS;;AAGpC,KAAI,uBAAuB;AAC3B,KAAI,gCAAgC,uBAAuB,UAAU,OAAO;AAC5E,KAAI,iCAAiC,SAAS,OAAO;AACrD,KAAI,+BAA+B,SAAS,OAAO;AACnD,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,SAAS,IAAI,CAAE,KAAI,KAAK;AAC5D,KAAI,2CAA2C;AAC/C,KAAI,SACF,KAAI,aAAa;KAEjB,KAAI,qBAAqB;AAE3B,QAAO;;;;;;;;;;;;;;;;;;ACtET,MAAa,qBAAqB;AAClC,MAAM,OAAO;AAOb,SAAgB,aAAa,QAAwB;AACnD,QAAO,KAAK,QAAQ,YAAY;;;AA8KlC,SAAgB,eAAe,OAAO,oBAAoB,YAAY,KAAuB;AAC3F,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,OAAO,QAAQ;GAAE;GAAM,MAAM;GAAM,CAAC;EAC1C,MAAM,QAAQ,OAAgB;AAC5B,QAAK,oBAAoB;AACzB,QAAK,SAAS;AACd,WAAQ,GAAG;;AAEb,OAAK,WAAW,iBAAiB,KAAK,MAAM,CAAC;AAC7C,OAAK,KAAK,iBAAiB,KAAK,KAAK,CAAC;AACtC,OAAK,KAAK,eAAe,KAAK,MAAM,CAAC;GACrC;;;;;;AAOJ,SAAgB,sBAAqC;CACnD,IAAI,MAAM,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,OAAK,MAAM,QAAQ;GACjB,KAAK,KAAK,mBAAmB;GAC7B,KAAK,KAAK,SAAS,mBAAmB;GACtC,KAAK,KAAK,QAAQ,SAAS,mBAAmB;GAC/C,CACC,KAAI,WAAW,KAAK,CAAE,QAAO;EAE/B,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,QAAM;;AAER,QAAO;;;;;;AAOT,eAAsB,mBAAmB,MAAc,QAAiC;AACtF,KAAI,MAAM,eAAe,KAAK,CAAE,QAAO,UAAU,KAAK,GAAG;CACzD,MAAM,SAAS,qBAAqB;AACpC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,uIACuE,KAAK,GAC7E;AAEH,KAAI,CAAC,WAAW,OAAO,CAAE,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;CAC/D,MAAM,QAAQ,MAAM,QAAQ,UAAU;EAAC;EAAQ;EAAU,OAAO,KAAK;EAAC,EAAE;EACtE,UAAU;EACV,OAAO;EACR,CAAC;AACF,OAAM,OAAO;AACb,eAAc,aAAa,OAAO,EAAE,GAAG,MAAM,IAAI,KAAK,OAAO;AAC7D,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,MAAI,MAAM,eAAe,KAAK,CAAE,QAAO,UAAU,KAAK,GAAG;AACzD,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;;AAE9C,OAAM,IAAI,MAAM,gDAAgD,KAAK,QAAQ,aAAa,OAAO,CAAC,GAAG;;;AAIvG,SAAgB,UAAU,QAAwB;CAChD,MAAM,OAAO,aAAa,OAAO;AACjC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;CAC9B,MAAM,MAAM,SAAS,aAAa,MAAM,OAAO,CAAC,MAAM,EAAE,GAAG;AAC3D,YAAW,KAAK;AAChB,KAAI,CAAC,OAAO,SAAS,IAAI,CAAE,QAAO;AAClC,KAAI;AACF,UAAQ,KAAK,KAAK,UAAU;SACtB;AACN,SAAO,aAAa,IAAI;;AAE1B,QAAO,aAAa,IAAI"}