olakai-cli 0.6.6 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/monitor/doctor.ts","../src/monitor/doctor-adapters.ts"],"sourcesContent":["/**\n * `olakai monitor doctor [--tool X] [--all] [--fix] [--json]` (OLA-244, S2).\n *\n * The end-to-end health check for local coding-agent monitoring. Where\n * `monitor status` answers \"is this workspace configured?\", `doctor`\n * answers \"is monitoring actually WORKING here, end-to-end?\" — by running\n * an ordered chain of checks per (workspace, tool):\n *\n * 1. registry-entry — is this install recorded in the machine registry?\n * 2. config-valid — does `.olakai/monitor-<tool>.json` parse + carry\n * agentId/apiKey/endpoint?\n * 3. hooks-installed — are the tool's hooks present at the right scope?\n * 4. api-key-valid — does the config's key authenticate?\n * 5. agent-exists — does the configured agent still resolve on the\n * backend?\n * 6. events-flowing — has the agent reported any event in the last 7d?\n *\n * Each check yields a structured `CheckResult` so `--json` can emit the\n * full picture and `--fix` can dispatch the registered repair for each\n * fixable failure. Repairs are idempotent + best-effort; doctor reports\n * what it fixed vs. what still needs a human.\n *\n * V-001 (verified): codex and cursor install their hooks GLOBALLY\n * (`~/.codex/config.toml`, `~/.cursor/hooks.json`), but attribute events\n * via the per-workspace `.olakai/monitor-<tool>.json`. When a global hook\n * fires in a workspace that has no such config, the plugin's `handleHook`\n * walks ancestors via `findConfiguredWorkspace`, finds nothing, and\n * silent-exits — so NO event is reported and the activity is unattributed.\n * doctor encodes this: for global-scope tools, a present hook + missing\n * config is reported as \"hook installed globally but this workspace has no\n * monitor config, so events here are not attributed.\"\n */\nimport * as os from \"node:os\";\nimport {\n getAgent,\n listActivity,\n listMyAgents,\n regenerateAgentApiKey,\n validateMonitoringApiKey,\n} from \"../lib/api.js\";\nimport { getValidToken } from \"../lib/auth.js\";\nimport { getPlugin, type ToolId } from \"./plugin.js\";\nimport { runMonitorInstall } from \"./install.js\";\nimport {\n getToolScope,\n readRegistry,\n reconcileCurrentWorkspace,\n upsertEntry,\n type RegistryEntry,\n type RegistryScope,\n} from \"./registry.js\";\nimport { findConfiguredWorkspace } from \"./paths.js\";\nimport { getDoctorToolAdapter, type DoctorToolConfig } from \"./doctor-adapters.js\";\n\n/** A single diagnostic check outcome. */\nexport type CheckStatus = \"ok\" | \"warn\" | \"fail\";\n\nexport interface CheckResult {\n /** Stable machine id (e.g. \"config-valid\"). */\n id: string;\n /** Human label for the checklist line. */\n label: string;\n status: CheckStatus;\n /** One-line human detail (the \"why\"). */\n detail: string;\n /** Whether `--fix` has a registered repair for this check. */\n fixable: boolean;\n}\n\n/** Per (workspace, tool) diagnostic report. */\nexport interface DoctorTargetReport {\n path: string;\n tool: ToolId;\n scope: RegistryScope;\n agentId?: string;\n checks: CheckResult[];\n /** Worst status across all checks. */\n overall: CheckStatus;\n}\n\nexport interface DoctorReport {\n targets: DoctorTargetReport[];\n /** Worst status across all targets (\"ok\" when there are no targets). */\n overall: CheckStatus;\n}\n\n/** A (workspace, tool) pairing to diagnose. */\nexport interface DoctorTarget {\n path: string;\n tool: ToolId;\n}\n\nconst SEVERITY: Record<CheckStatus, number> = { ok: 0, warn: 1, fail: 2 };\n\nfunction worst(a: CheckStatus, b: CheckStatus): CheckStatus {\n return SEVERITY[a] >= SEVERITY[b] ? a : b;\n}\n\n/** 7-day lookback window (ISO) for the events-flowing probe. */\nfunction sevenDaysAgoIso(now: Date = new Date()): string {\n return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();\n}\n\n/**\n * Result of running the check chain for one target, plus the resolved\n * config (so `--fix` can re-link without re-loading) and any backend\n * facts (agent presence, last event) the chain discovered.\n */\ninterface ChainOutcome {\n checks: CheckResult[];\n config: DoctorToolConfig | null;\n /** True only when EVERY check is ok — gates `lastVerifiedAt` writes. */\n allGreen: boolean;\n /** ISO timestamp of the most recent event, if events-flowing saw one. */\n lastEventAt?: string;\n}\n\n/**\n * Run the ordered check chain for a single (workspace, tool). Pure of\n * side effects beyond (a) the reconcile adoption in registry-entry and\n * (b) the `lastEventAt`/`lastVerifiedAt` registry write after a green\n * events-flowing probe — both intentional and best-effort.\n *\n * `homeDir` is injectable so tests never touch the real `~/.olakai`.\n */\nexport async function runCheckChain(\n target: DoctorTarget,\n opts: { homeDir?: string; now?: Date } = {},\n): Promise<ChainOutcome> {\n const homeDir = opts.homeDir ?? os.homedir();\n const now = opts.now ?? new Date();\n const tool = target.tool;\n const scope = getToolScope(tool);\n const adapter = getDoctorToolAdapter(tool);\n const checks: CheckResult[] = [];\n\n // 1) registry-entry --------------------------------------------------\n let registry = readRegistry(homeDir);\n let entry = registry.workspaces.find(\n (e) => e.path === target.path && e.tool === tool,\n );\n if (!entry) {\n // Try to adopt from disk (S1 reconcile) before declaring it missing.\n try {\n reconcileCurrentWorkspace(target.path, homeDir);\n } catch {\n // reconcile is best-effort; fall through to the re-read.\n }\n registry = readRegistry(homeDir);\n entry = registry.workspaces.find(\n (e) => e.path === target.path && e.tool === tool,\n );\n }\n if (entry) {\n checks.push({\n id: \"registry-entry\",\n label: \"Registry entry\",\n status: \"ok\",\n detail: `Recorded in the machine registry as \"${entry.agentName}\".`,\n fixable: false,\n });\n } else {\n checks.push({\n id: \"registry-entry\",\n label: \"Registry entry\",\n status: \"warn\",\n detail:\n \"No machine-registry entry for this workspace+tool (and none could be adopted from disk).\",\n fixable: true,\n });\n }\n\n // 2) config-valid ----------------------------------------------------\n const config = adapter.loadConfig(target.path);\n if (config && config.agentId && config.apiKey && config.monitoringEndpoint) {\n checks.push({\n id: \"config-valid\",\n label: \"Config file\",\n status: \"ok\",\n detail: `${adapter.configRelLabel} is present and valid (agent ${config.agentId}).`,\n fixable: false,\n });\n } else if (config) {\n checks.push({\n id: \"config-valid\",\n label: \"Config file\",\n status: \"fail\",\n detail: `${adapter.configRelLabel} is missing required fields (agentId/apiKey/endpoint).`,\n fixable: true,\n });\n } else {\n // V-001 messaging: for a global-scope tool, the absence of a\n // per-workspace config is the exact reason events here are dropped.\n const detail =\n scope === \"global\"\n ? `No ${adapter.configRelLabel} in this workspace. ${adapter.displayName} hooks are global, so any activity here is NOT attributed to an agent until you run 'olakai monitor init --tool ${tool}'.`\n : `No ${adapter.configRelLabel} — this workspace is not configured for monitoring.`;\n checks.push({\n id: \"config-valid\",\n label: \"Config file\",\n status: \"fail\",\n detail,\n fixable: false,\n });\n }\n\n // 3) hooks-installed -------------------------------------------------\n // Reuse the plugin's own status() so we don't re-parse settings files.\n let hooksConfigured = false;\n let hooksStatusError: string | null = null;\n try {\n const status = await getPlugin(tool).status({ projectRoot: target.path });\n hooksConfigured = status.hooksConfigured;\n } catch (err) {\n hooksStatusError = err instanceof Error ? err.message : String(err);\n }\n if (hooksStatusError) {\n checks.push({\n id: \"hooks-installed\",\n label: \"Hooks installed\",\n status: \"warn\",\n detail: `Couldn't determine hook status (${hooksStatusError}).`,\n fixable: true,\n });\n } else if (hooksConfigured) {\n if (scope === \"global\" && !config) {\n // V-001: hook present globally, but no workspace config to attribute to.\n checks.push({\n id: \"hooks-installed\",\n label: \"Hooks installed\",\n status: \"warn\",\n detail: `${adapter.displayName} hooks are installed globally (${adapter.hooksLabel}), but this workspace has no monitor config — events fired here silent-exit unattributed.`,\n fixable: false,\n });\n } else {\n checks.push({\n id: \"hooks-installed\",\n label: \"Hooks installed\",\n status: \"ok\",\n detail:\n scope === \"global\"\n ? `Installed globally at ${adapter.hooksLabel}.`\n : `Installed at ${adapter.hooksLabel}.`,\n fixable: true,\n });\n }\n } else {\n checks.push({\n id: \"hooks-installed\",\n label: \"Hooks installed\",\n status: \"fail\",\n detail: `${adapter.displayName} hooks are not installed (${adapter.hooksLabel}). The tool will not report any activity.`,\n fixable: true,\n });\n }\n\n // The remaining checks need a usable config. When config is absent we\n // can't authenticate, resolve the agent, or probe activity — emit warns\n // that point back at config-valid rather than spurious fails.\n if (!config || !config.agentId || !config.apiKey) {\n checks.push({\n id: \"api-key-valid\",\n label: \"API key valid\",\n status: \"warn\",\n detail: \"Skipped — no usable config to read the API key from.\",\n fixable: true,\n });\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"warn\",\n detail: \"Skipped — no agentId in config to resolve.\",\n fixable: false,\n });\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"warn\",\n detail: \"Skipped — no agentId in config to probe.\",\n fixable: false,\n });\n return { checks, config, allGreen: false };\n }\n\n // 4) api-key-valid ---------------------------------------------------\n const validation = await validateMonitoringApiKey(\n config.monitoringEndpoint,\n config.apiKey,\n );\n if (validation === null) {\n // null = invalid key OR network failure (the validator soft-fails\n // both to null). We can't distinguish, so this is fixable (re-link)\n // but we phrase it carefully.\n checks.push({\n id: \"api-key-valid\",\n label: \"API key valid\",\n status: \"fail\",\n detail:\n \"The configured API key did not authenticate (rejected, or the monitoring endpoint was unreachable).\",\n fixable: true,\n });\n } else {\n checks.push({\n id: \"api-key-valid\",\n label: \"API key valid\",\n status: \"ok\",\n detail: `Authenticates against ${config.monitoringEndpoint}.`,\n fixable: false,\n });\n }\n\n // 5) agent-exists ----------------------------------------------------\n let agentResolves = false;\n try {\n await getAgent(config.agentId);\n agentResolves = true;\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"ok\",\n detail: `Agent ${config.agentId} resolves on the backend.`,\n fixable: false,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // 404 maps to \"Agent not found\" in api.ts — treat as a hard fail\n // (recreatable). Permission errors are the common case for the\n // primary self-monitor persona: `GET /api/config/agents/:id`\n // requires ANALYST+, but coding agents are typically provisioned\n // by EMPLOYEE/USER. In that case fall back to the account lens\n // (`listMyAgents`, which is readable by any authenticated user) and\n // accept membership as verification. Any other error (network, 5xx)\n // stays a warn.\n if (/not found/i.test(message)) {\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"fail\",\n detail: `Agent ${config.agentId} no longer exists on the backend (404).`,\n fixable: true,\n });\n } else if (/permission|forbidden|analyst|403/i.test(message)) {\n // Self-owner fallback. If the configured agent is one *I* created,\n // verifying via the account lens is correct and equivalent to a\n // successful `getAgent` for the purposes of this check.\n try {\n const mine = await listMyAgents();\n if (mine.some((a) => a.id === config.agentId)) {\n agentResolves = true;\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"ok\",\n detail: `Agent ${config.agentId} exists on the backend (verified via account lens — you're the creator).`,\n fixable: false,\n });\n } else {\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"warn\",\n detail: `Agent ${config.agentId} isn't in your account-lens listing (run 'olakai agents mine'). The config may be stale or belong to another user.`,\n fixable: false,\n });\n }\n } catch (mineErr) {\n const mineMsg =\n mineErr instanceof Error ? mineErr.message : String(mineErr);\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"warn\",\n detail: `Couldn't verify agent ${config.agentId} (permission denied; account-lens lookup also failed: ${mineMsg}).`,\n fixable: false,\n });\n }\n } else {\n checks.push({\n id: \"agent-exists\",\n label: \"Agent exists\",\n status: \"warn\",\n detail: `Couldn't verify agent ${config.agentId} (${message}).`,\n fixable: false,\n });\n }\n }\n\n // 6) events-flowing --------------------------------------------------\n // V-002 (resolved): the metadata listing is readable by a normal CLI\n // user (ANALYST_READONLY_DEMO+); only `includeContent` needs ADMIN, so\n // we never pass it. A 403 is handled gracefully as a warn (token lacks\n // activity read) rather than a hard fail.\n let lastEventAt: string | undefined;\n if (!agentResolves) {\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"warn\",\n detail: \"Skipped — agent could not be resolved.\",\n fixable: false,\n });\n } else if (!getValidToken()) {\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"warn\",\n detail: \"Skipped — not logged in (run 'olakai login' to enable this check).\",\n fixable: false,\n });\n } else {\n try {\n const since = sevenDaysAgoIso(now);\n const activity = await listActivity({\n agentId: config.agentId,\n since,\n limit: 1,\n });\n if (activity.prompts.length > 0) {\n lastEventAt = activity.prompts[0].createdAt;\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"ok\",\n detail: `Most recent event ${lastEventAt} (within the last 7 days).`,\n fixable: false,\n });\n } else {\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"warn\",\n detail:\n \"No events in the last 7 days. This is expected if the workspace has been idle; if you've been active, check hooks + API key.\",\n fixable: false,\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Treat any permission/forbidden error as a soft warn so doctor\n // never hard-fails on a token that simply can't read activity.\n checks.push({\n id: \"events-flowing\",\n label: \"Events flowing\",\n status: \"warn\",\n detail: /403|forbidden|permission/i.test(message)\n ? \"Couldn't verify — your token lacks activity read access.\"\n : `Couldn't probe activity (${message}).`,\n fixable: false,\n });\n }\n }\n\n const allGreen = checks.every((c) => c.status === \"ok\");\n\n // Persist freshness signals to the registry (best-effort). lastEventAt\n // whenever we saw one; lastVerifiedAt only on a fully-green run.\n if (entry && (lastEventAt || allGreen)) {\n try {\n upsertEntry(\n {\n ...entry,\n ...(lastEventAt ? { lastEventAt } : {}),\n ...(allGreen ? { lastVerifiedAt: now.toISOString() } : {}),\n },\n homeDir,\n );\n } catch {\n // Registry is an index, not the source of truth — never fail doctor\n // on a write error.\n }\n }\n\n return { checks, config, allGreen, lastEventAt };\n}\n\n/** Outcome of attempting `--fix` on a single check. */\nexport interface FixResult {\n checkId: string;\n /** Did the repair succeed (fully resolve the failure)? */\n fixed: boolean;\n /** Human detail of what was done or why it still needs a human. */\n detail: string;\n}\n\n/**\n * Run the registered repairs for every fixable failed/warned check in\n * `checks`, in chain order. Idempotent and best-effort. Returns one\n * `FixResult` per repair attempted.\n *\n * `homeDir` is injectable for tests.\n */\nexport async function runFixes(\n target: DoctorTarget,\n outcome: ChainOutcome,\n opts: {\n homeDir?: string;\n interactive?: boolean;\n /** Opt-in to recreating a missing (404) agent. Off by default so\n * `doctor --fix` never silently provisions a duplicate agent on a\n * transient/false 404 — recreation is the job of `monitor repair`\n * or an explicit `--recreate-missing`. */\n recreateMissing?: boolean;\n } = {},\n): Promise<FixResult[]> {\n const homeDir = opts.homeDir ?? os.homedir();\n const tool = target.tool;\n const adapter = getDoctorToolAdapter(tool);\n const results: FixResult[] = [];\n\n // Index checks by id for ordered, gated repair dispatch.\n const byId = new Map<string, CheckResult>();\n for (const c of outcome.checks) byId.set(c.id, c);\n\n const needsFix = (id: string): boolean => {\n const c = byId.get(id);\n return Boolean(c && c.fixable && c.status !== \"ok\");\n };\n\n // -- registry-entry: adopt from disk via reconcile -------------------\n if (needsFix(\"registry-entry\")) {\n try {\n const adopted = reconcileCurrentWorkspace(target.path, homeDir);\n const has = adopted.some((e) => e.tool === tool);\n results.push({\n checkId: \"registry-entry\",\n fixed: has,\n detail: has\n ? \"Adopted the on-disk config into the machine registry.\"\n : \"Nothing to adopt — there's no on-disk config to register. Run 'olakai monitor init'.\",\n });\n } catch (err) {\n results.push({\n checkId: \"registry-entry\",\n fixed: false,\n detail: `Reconcile failed: ${err instanceof Error ? err.message : String(err)}`,\n });\n }\n }\n\n // -- agent-exists (404): recreate the self-monitor agent -------------\n // Recreating PROVISIONS A NEW backend agent and rotates the key, so it\n // is opt-in: `doctor --fix` never silently recreates (a transient or\n // false 404 could otherwise spawn a duplicate agent). Gated behind\n // `--recreate-missing`; `olakai monitor repair` (S3) is the heavier\n // entry point. Done before config/api-key re-link because recreate\n // rewrites the config with a brand-new agent + key, superseding a\n // stale re-link.\n if (needsFix(\"agent-exists\")) {\n if (!opts.recreateMissing) {\n results.push({\n checkId: \"agent-exists\",\n fixed: false,\n detail: `The configured agent no longer exists on the backend. Recreating it provisions a NEW agent — re-run with '--recreate-missing', or use 'olakai monitor repair --tool ${tool}'.`,\n });\n } else {\n try {\n const result = await runMonitorInstall(tool, {\n projectRoot: target.path,\n interactive: opts.interactive ?? false,\n });\n results.push({\n checkId: \"agent-exists\",\n fixed: true,\n detail: `Recreated the self-monitor agent \"${result.agentName}\" (${result.agentId}) and rewrote the config + registry.`,\n });\n // The recreate already wrote a fresh, valid config + key, so the\n // config-valid / api-key-valid repairs below are moot. Mark them\n // resolved-by-recreate to avoid a redundant key rotation.\n byId.delete(\"config-valid\");\n byId.delete(\"api-key-valid\");\n } catch (err) {\n results.push({\n checkId: \"agent-exists\",\n fixed: false,\n detail: `Could not recreate the agent: ${err instanceof Error ? err.message : String(err)}. This needs a human (check 'olakai login' and your role).`,\n });\n }\n }\n }\n\n // -- config-valid / api-key-valid: re-link by rotating the key -------\n // A single rotation fixes both, so coalesce them. We rotate only when\n // we have an agentId to rotate against.\n const relinkNeeded = needsFix(\"config-valid\") || needsFix(\"api-key-valid\");\n if (relinkNeeded) {\n const config = outcome.config;\n if (!config || !config.agentId) {\n results.push({\n checkId: needsFix(\"config-valid\") ? \"config-valid\" : \"api-key-valid\",\n fixed: false,\n detail:\n \"Can't re-link — no agentId in config. Run 'olakai monitor init' to set up this workspace.\",\n });\n } else {\n try {\n const rotated = await regenerateAgentApiKey(config.agentId);\n adapter.writeConfig(target.path, {\n agentId: config.agentId,\n apiKey: rotated.key,\n agentName: config.agentName,\n source: config.source,\n createdAt: config.createdAt ?? new Date().toISOString(),\n monitoringEndpoint: config.monitoringEndpoint,\n });\n const detail =\n \"Rotated the agent API key and rewrote the workspace config. Other workspaces using the old key must re-init.\";\n if (needsFix(\"config-valid\")) {\n results.push({ checkId: \"config-valid\", fixed: true, detail });\n }\n if (needsFix(\"api-key-valid\")) {\n results.push({ checkId: \"api-key-valid\", fixed: true, detail });\n }\n } catch (err) {\n const detail = `Key rotation failed: ${err instanceof Error ? err.message : String(err)}.`;\n if (needsFix(\"config-valid\")) {\n results.push({ checkId: \"config-valid\", fixed: false, detail });\n }\n if (needsFix(\"api-key-valid\")) {\n results.push({ checkId: \"api-key-valid\", fixed: false, detail });\n }\n }\n }\n }\n\n // -- hooks-installed: idempotent re-merge via the plugin's merge path -\n if (needsFix(\"hooks-installed\")) {\n try {\n adapter.repairHooks(target.path);\n results.push({\n checkId: \"hooks-installed\",\n fixed: true,\n detail: `Re-merged ${adapter.displayName} hooks (${adapter.hooksLabel}).`,\n });\n } catch (err) {\n results.push({\n checkId: \"hooks-installed\",\n fixed: false,\n detail: `Could not re-merge hooks: ${err instanceof Error ? err.message : String(err)}.`,\n });\n }\n }\n\n return results;\n}\n\n/**\n * Resolve the set of (workspace, tool) targets to diagnose.\n *\n * - `--all`: every registry entry on the machine (after reconciling the\n * current workspace so a freshly-installed cwd is included).\n * - otherwise: the single (resolved current-workspace root, tool). When\n * the cwd isn't inside a configured workspace we still return the cwd\n * itself so doctor can report the \"not configured\" state honestly.\n */\nexport function resolveTargets(\n opts: { all?: boolean; tool?: ToolId; cwd?: string; homeDir?: string },\n): DoctorTarget[] {\n const cwd = opts.cwd ?? process.cwd();\n const homeDir = opts.homeDir ?? os.homedir();\n\n if (opts.all) {\n try {\n reconcileCurrentWorkspace(cwd, homeDir);\n } catch {\n // best-effort\n }\n const registry = readRegistry(homeDir);\n return registry.workspaces.map((e) => ({ path: e.path, tool: e.tool }));\n }\n\n // Single-target mode requires a tool (the command resolves/prompts it\n // before calling here).\n const tool = opts.tool;\n if (!tool) return [];\n\n // Prefer the configured workspace root; fall back to cwd so doctor can\n // report \"not configured\" rather than silently producing no targets.\n const root = findConfiguredWorkspace(cwd, [tool]) ?? cwd;\n return [{ path: root, tool }];\n}\n\n/** Build the structured report for a list of targets (no fixes). */\nexport async function buildReport(\n targets: DoctorTarget[],\n opts: { homeDir?: string; now?: Date } = {},\n): Promise<{ report: DoctorReport; outcomes: Map<string, ChainOutcome> }> {\n const targetReports: DoctorTargetReport[] = [];\n const outcomes = new Map<string, ChainOutcome>();\n let overall: CheckStatus = \"ok\";\n\n for (const target of targets) {\n const outcome = await runCheckChain(target, opts);\n outcomes.set(targetKey(target), outcome);\n const targetOverall = outcome.checks.reduce<CheckStatus>(\n (acc, c) => worst(acc, c.status),\n \"ok\",\n );\n overall = worst(overall, targetOverall);\n\n const entry = findEntry(target, opts.homeDir);\n targetReports.push({\n path: target.path,\n tool: target.tool,\n scope: getToolScope(target.tool),\n agentId: outcome.config?.agentId ?? entry?.agentId,\n checks: outcome.checks,\n overall: targetOverall,\n });\n }\n\n return { report: { targets: targetReports, overall }, outcomes };\n}\n\nexport function targetKey(target: DoctorTarget): string {\n return `${target.path}::${target.tool}`;\n}\n\nfunction findEntry(\n target: DoctorTarget,\n homeDir?: string,\n): RegistryEntry | undefined {\n const registry = readRegistry(homeDir ?? os.homedir());\n return registry.workspaces.find(\n (e) => e.path === target.path && e.tool === target.tool,\n );\n}\n\n// ===========================================================================\n// Human-readable rendering\n// ===========================================================================\n\nconst ICON: Record<CheckStatus, string> = {\n ok: \"✓\", // ✓\n warn: \"⚠\", // ⚠\n fail: \"✗\", // ✗\n};\n\nexport function formatReport(report: DoctorReport): string {\n if (report.targets.length === 0) {\n return \"No monitored workspaces to check. Run 'olakai monitor init --tool <tool>' first.\";\n }\n const lines: string[] = [];\n for (const t of report.targets) {\n lines.push(`${t.tool} — ${t.path} [${t.scope}]`);\n for (const c of t.checks) {\n lines.push(` ${ICON[c.status]} ${c.label}: ${c.detail}`);\n }\n lines.push(\"\");\n }\n const summary =\n report.overall === \"ok\"\n ? \"All checks passed.\"\n : report.overall === \"warn\"\n ? \"Some checks need attention (warnings). Run with --fix to repair fixable issues.\"\n : \"Some checks failed. Run with --fix to repair fixable issues.\";\n lines.push(summary);\n return lines.join(\"\\n\").trimEnd();\n}\n\nexport function formatFixResults(\n target: DoctorTarget,\n fixes: FixResult[],\n): string {\n if (fixes.length === 0) {\n return `${target.tool} — ${target.path}: nothing to fix.`;\n }\n const lines: string[] = [`${target.tool} — ${target.path}:`];\n for (const f of fixes) {\n lines.push(` ${f.fixed ? ICON.ok : ICON.fail} ${f.checkId}: ${f.detail}`);\n }\n return lines.join(\"\\n\");\n}\n\n// ===========================================================================\n// Command entry point\n// ===========================================================================\n\nexport interface DoctorCommandOptions {\n tool?: ToolId;\n all?: boolean;\n fix?: boolean;\n json?: boolean;\n /** Opt-in to recreating a missing (404) agent during `--fix`. */\n recreateMissing?: boolean;\n /** Test seams. */\n cwd?: string;\n homeDir?: string;\n now?: Date;\n interactive?: boolean;\n}\n\nexport interface DoctorJsonResult {\n report: DoctorReport;\n fixes?: Array<{\n path: string;\n tool: ToolId;\n results: FixResult[];\n }>;\n}\n\n/**\n * Pure orchestrator: resolve targets, build the report, optionally run\n * fixes (and re-check after), and return the JSON-shaped result plus the\n * worst overall status. The Commander action wraps this with console\n * output + exit-code mapping.\n */\nexport async function runDoctor(\n options: DoctorCommandOptions,\n): Promise<DoctorJsonResult> {\n const targets = resolveTargets({\n all: options.all,\n tool: options.tool,\n cwd: options.cwd,\n homeDir: options.homeDir,\n });\n\n const { report, outcomes } = await buildReport(targets, {\n homeDir: options.homeDir,\n now: options.now,\n });\n\n if (!options.fix) {\n return { report };\n }\n\n const fixes: NonNullable<DoctorJsonResult[\"fixes\"]> = [];\n for (const target of targets) {\n const outcome = outcomes.get(targetKey(target));\n if (!outcome) continue;\n const results = await runFixes(target, outcome, {\n homeDir: options.homeDir,\n interactive: options.interactive,\n recreateMissing: options.recreateMissing,\n });\n if (results.length > 0) {\n fixes.push({ path: target.path, tool: target.tool, results });\n }\n }\n\n // Re-check after fixes so the returned report reflects the new state.\n const { report: afterReport } = await buildReport(targets, {\n homeDir: options.homeDir,\n now: options.now,\n });\n\n return { report: afterReport, fixes };\n}\n\n/** Map a report's worst status to a process exit code. */\nexport function exitCodeForStatus(status: CheckStatus): number {\n // Warnings are non-fatal (idle workspaces are normal); only hard\n // failures map to a non-zero exit so scripts can gate on real breakage.\n return status === \"fail\" ? 1 : 0;\n}\n\n/**\n * Render `runDoctor`'s result to the console. Separated from `runDoctor`\n * so the orchestration stays testable without capturing stdout.\n */\nexport function printDoctorResult(\n result: DoctorJsonResult,\n json: boolean,\n): void {\n if (json) {\n const payload: DoctorJsonResult = { report: result.report };\n if (result.fixes) payload.fixes = result.fixes;\n console.log(JSON.stringify(payload, null, 2));\n return;\n }\n\n console.log(formatReport(result.report));\n\n if (result.fixes && result.fixes.length > 0) {\n console.log(\"\");\n console.log(\"Repairs:\");\n for (const f of result.fixes) {\n console.log(formatFixResults({ path: f.path, tool: f.tool }, f.results));\n }\n }\n}\n\n// Re-export the adapter type surface so tests importing from doctor.ts\n// have everything in one place.\nexport type { DoctorToolConfig } from \"./doctor-adapters.js\";\n","/**\n * Per-tool adapters for `monitor doctor` (OLA-244, S2).\n *\n * `doctor.ts` is intentionally tool-agnostic: it runs the same check\n * chain for every tool and dispatches repairs through this adapter table.\n * Each adapter wraps the tool's EXISTING config load/write helpers and\n * its idempotent hook-merge path so doctor never re-implements (and never\n * drifts from) the install code.\n *\n * Why a separate module from the plugin contract: the `ToolMonitorPlugin`\n * interface (plugin.ts) exposes install/uninstall/status/handleHook but\n * NOT a config reader/writer or a hooks-only repair — those are internal\n * to each plugin's `config.ts`/`install.ts`. Rather than widen the plugin\n * contract for one consumer, doctor pulls the specific helpers it needs\n * here. If a future tool is added, its adapter is registered below.\n */\nimport {\n loadClaudeCodeConfig,\n writeClaudeCodeConfig,\n getClaudeCodeConfigPath,\n} from \"./plugins/claude-code/config.js\";\nimport {\n getSettingsPath,\n mergeHooksSettings,\n readJsonFile,\n writeJsonFile,\n type ClaudeSettings,\n} from \"./plugins/claude-code/settings.js\";\nimport {\n loadCodexConfig,\n writeCodexConfig,\n getCodexConfigPath,\n} from \"./plugins/codex/config.js\";\nimport { installCodexHooksConfig } from \"./plugins/codex/install.js\";\nimport {\n loadCursorConfig,\n writeCursorConfig,\n getCursorConfigPath,\n} from \"./plugins/cursor/config.js\";\nimport { getCursorHooksPath } from \"./plugins/cursor/paths.js\";\nimport {\n mergeCursorHooks,\n type CursorHooksConfig,\n} from \"./plugins/cursor/hooks-config.js\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ToolId } from \"./plugin.js\";\n\n/**\n * The shared on-disk per-tool config shape. All three plugins persist\n * exactly these fields (see each plugin's `config.ts`).\n */\nexport interface DoctorToolConfig {\n agentId: string;\n apiKey: string;\n agentName: string;\n source: string;\n createdAt?: string;\n monitoringEndpoint: string;\n}\n\nexport interface DoctorToolAdapter {\n tool: ToolId;\n displayName: string;\n /** Short label for the config file in human messages. */\n configRelLabel: string;\n /** Short label for the hooks surface in human messages. */\n hooksLabel: string;\n /** Load + parse the per-tool config; null when absent/corrupt. */\n loadConfig(projectRoot: string): DoctorToolConfig | null;\n /** Overwrite the per-tool config with a fresh, valid record. */\n writeConfig(projectRoot: string, config: DoctorToolConfig): void;\n /**\n * Idempotently re-merge this tool's hooks at the correct scope. Reuses\n * the same merge path the installer uses, so a repeat call is a no-op\n * when the hooks are already present.\n */\n repairHooks(projectRoot: string, homeDir?: string): void;\n}\n\nconst claudeCodeAdapter: DoctorToolAdapter = {\n tool: \"claude-code\",\n displayName: \"Claude Code\",\n configRelLabel: \".olakai/monitor-claude-code.json\",\n hooksLabel: \".claude/settings.json\",\n loadConfig: (root) => {\n const c = loadClaudeCodeConfig(root, () => {});\n return c ?? null;\n },\n writeConfig: (root, config) => {\n writeClaudeCodeConfig(root, {\n agentId: config.agentId,\n apiKey: config.apiKey,\n agentName: config.agentName,\n source: config.source,\n createdAt: config.createdAt ?? new Date().toISOString(),\n monitoringEndpoint: config.monitoringEndpoint,\n });\n },\n repairHooks: (root) => {\n // Mirror installClaudeCode's hook-merge: read settings.json, merge in\n // the Olakai hook entries (idempotent), write back. Does NOT touch\n // the agent/config — hooks repair is independent of provisioning.\n const settingsPath = getSettingsPath(root);\n const existing = readJsonFile<ClaudeSettings>(settingsPath) ?? {};\n const merged: ClaudeSettings = {\n ...existing,\n hooks: mergeHooksSettings(existing.hooks),\n };\n writeJsonFile(settingsPath, merged);\n },\n};\n\nconst codexAdapter: DoctorToolAdapter = {\n tool: \"codex\",\n displayName: \"OpenAI Codex CLI\",\n configRelLabel: \".olakai/monitor-codex.json\",\n hooksLabel: \"~/.codex/config.toml\",\n loadConfig: (root) => loadCodexConfig(root) ?? null,\n writeConfig: (root, config) => {\n writeCodexConfig(root, {\n agentId: config.agentId,\n apiKey: config.apiKey,\n agentName: config.agentName,\n source: config.source,\n createdAt: config.createdAt ?? new Date().toISOString(),\n monitoringEndpoint: config.monitoringEndpoint,\n });\n },\n repairHooks: () => {\n // Codex hooks live globally; installCodexHooksConfig() is the same\n // idempotent read-merge-write the installer uses.\n installCodexHooksConfig();\n },\n};\n\nconst cursorAdapter: DoctorToolAdapter = {\n tool: \"cursor\",\n displayName: \"Cursor\",\n configRelLabel: \".olakai/monitor-cursor.json\",\n hooksLabel: \"~/.cursor/hooks.json\",\n loadConfig: (root) => loadCursorConfig(root) ?? null,\n writeConfig: (root, config) => {\n writeCursorConfig(root, {\n agentId: config.agentId,\n apiKey: config.apiKey,\n agentName: config.agentName,\n source: config.source,\n createdAt: config.createdAt ?? new Date().toISOString(),\n monitoringEndpoint: config.monitoringEndpoint,\n });\n },\n repairHooks: (_root, homeDir = os.homedir()) => {\n // Cursor hooks live globally at ~/.cursor/hooks.json. Re-merge using\n // the same pure helper the installer uses (idempotent).\n const hooksPath = getCursorHooksPath(homeDir);\n let existing: CursorHooksConfig | null = null;\n try {\n if (fs.existsSync(hooksPath)) {\n existing = JSON.parse(\n fs.readFileSync(hooksPath, \"utf-8\"),\n ) as CursorHooksConfig;\n }\n } catch {\n existing = null;\n }\n const merged = mergeCursorHooks(existing);\n const dir = path.dirname(hooksPath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(hooksPath, JSON.stringify(merged, null, 2) + \"\\n\", \"utf-8\");\n },\n};\n\nconst ADAPTERS: Record<ToolId, DoctorToolAdapter> = {\n \"claude-code\": claudeCodeAdapter,\n codex: codexAdapter,\n cursor: cursorAdapter,\n};\n\nexport function getDoctorToolAdapter(tool: ToolId): DoctorToolAdapter {\n return ADAPTERS[tool];\n}\n\n// Re-export config-path resolvers so callers needing the absolute path\n// (not just the short label) have them without reaching into plugins.\nexport {\n getClaudeCodeConfigPath,\n getCodexConfigPath,\n getCursorConfigPath,\n};\n"],"mappings":"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","names":["os"]}
@@ -233,13 +233,18 @@ async function updateAgent(id, payload) {
233
233
  if (response.status === 401) {
234
234
  throw new Error("Session expired. Run 'olakai login' to authenticate again.");
235
235
  }
236
+ if (response.status === 403) {
237
+ throw new Error(
238
+ "Only the agent's creator or an ADMIN may update it. Ask an admin, or update an agent you created."
239
+ );
240
+ }
236
241
  if (response.status === 404) {
237
242
  throw new Error("Agent not found");
238
243
  }
239
244
  if (response.status === 409) {
240
245
  throw new Error("An agent with this name already exists");
241
246
  }
242
- const error = await response.json();
247
+ const error = await response.json().catch(() => ({}));
243
248
  throw new Error(error.error || "Failed to update agent");
244
249
  }
245
250
  return await response.json();
@@ -873,4 +878,4 @@ export {
873
878
  updateCustomDataConfig,
874
879
  deleteCustomDataConfig
875
880
  };
876
- //# sourceMappingURL=chunk-GU4HEL24.js.map
881
+ //# sourceMappingURL=chunk-KNGRF4XU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/api.ts"],"sourcesContent":["import { getBaseUrl, CLIENT_ID } from \"./config.js\";\nimport { getValidToken } from \"./auth.js\";\n\nexport interface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n}\n\nexport interface TokenErrorResponse {\n error: \"authorization_pending\" | \"expired_token\" | \"access_denied\";\n error_description?: string;\n}\n\nexport interface UserMeResponse {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n role: string;\n accountId: string;\n}\n\n// Config API Types\nexport interface AgentApiKey {\n id: string;\n key?: string; // Only present on creation\n keyMasked: string;\n isActive: boolean;\n createdAt?: string;\n}\n\nexport type DefaultAggregationMethod =\n | \"SUM\"\n | \"AVERAGE\"\n | \"COUNT\"\n | \"MIN\"\n | \"MAX\"\n | \"LATEST\";\n\nexport type KpiScope = \"PROMPT_REQUEST\" | \"CHAT\" | \"DOCUMENT\";\n\nexport interface KpiDefinition {\n id: string;\n name: string;\n description: string | null;\n type: \"PREDEFINED\" | \"USER_DEFINED\";\n category: string | null;\n agentId: string | null;\n scope: KpiScope;\n calculatorId: string;\n calculatorParams: Record<string, unknown> | null;\n unit: string | null;\n defaultAggregationMethod: DefaultAggregationMethod;\n isActive: boolean;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface CreateKpiPayload {\n name: string;\n description?: string;\n type?: \"PREDEFINED\" | \"USER_DEFINED\";\n category?: string;\n agentId?: string;\n scope: KpiScope;\n calculatorId: string;\n calculatorParams?: Record<string, unknown>;\n unit?: string;\n defaultAggregationMethod?: DefaultAggregationMethod;\n}\n\nexport interface UpdateKpiPayload {\n name?: string;\n description?: string | null;\n type?: \"PREDEFINED\" | \"USER_DEFINED\";\n category?: string | null;\n agentId?: string | null;\n scope?: KpiScope;\n calculatorId?: string;\n calculatorParams?: Record<string, unknown>;\n unit?: string | null;\n defaultAggregationMethod?: DefaultAggregationMethod;\n isActive?: boolean;\n}\n\nexport interface ContextVariable {\n name: string;\n description: string;\n type: \"string\" | \"number\" | \"boolean\";\n sampleValue?: string | number | boolean;\n source: \"shared\" | \"custom\";\n}\n\nexport interface FormulaValidationResult {\n valid: boolean;\n error?: string;\n type?: string | null;\n charIndex?: number;\n parsedFormula?: unknown;\n}\n\nexport interface Agent {\n id: string;\n name: string;\n description: string;\n role: \"WORKER\" | \"COORDINATOR\";\n source: \"SDK\" | \"AUTOMATION_PROVIDER\";\n workflowId: string | null;\n category: string | null;\n apiKey: AgentApiKey | null;\n kpiDefinitions?: KpiDefinition[];\n workflow?: Workflow | null;\n}\n\nexport interface Workflow {\n id: string;\n name: string;\n description: string | null;\n isActive: boolean;\n agentCount: number;\n agents?: Agent[];\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface CreateAgentPayload {\n name: string;\n description?: string;\n role?: \"WORKER\" | \"COORDINATOR\";\n workflowId?: string;\n createApiKey?: boolean;\n category?: string;\n source?: string;\n /**\n * Optional `git config --global user.email` captured at install time.\n * Self-monitor-only — the backend uses it to pre-seed a developer\n * identity row keyed on this email so future VCS PRs authored under\n * this address auto-link to the same Olakai User. Omit when git is\n * unavailable or the user hasn't configured an email.\n */\n vcsEmailHint?: string;\n}\n\nexport interface UpdateAgentPayload {\n name?: string;\n description?: string;\n role?: \"WORKER\" | \"COORDINATOR\";\n workflowId?: string | null;\n category?: string | null;\n /**\n * Soft archive / unarchive flag. Sent by `olakai agents archive <id>`\n * (and `--unarchive`). The backend's PUT route maps this onto the\n * Agent.archived column.\n */\n archived?: boolean;\n}\n\nexport interface CreateWorkflowPayload {\n name: string;\n description?: string;\n}\n\nexport interface UpdateWorkflowPayload {\n name?: string;\n description?: string | null;\n isActive?: boolean;\n}\n\n/**\n * Request a device code to start the login flow\n */\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const response = await fetch(`${getBaseUrl()}/api/auth/device/code`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ client_id: CLIENT_ID }),\n });\n\n if (!response.ok) {\n const error = (await response.json()) as { error_description?: string; error?: string };\n throw new Error(error.error_description || error.error || \"Failed to request device code\");\n }\n\n return (await response.json()) as DeviceCodeResponse;\n}\n\n/**\n * Poll for token exchange\n * Returns the token response if approved, or throws an error\n */\nexport async function pollForToken(\n deviceCode: string,\n): Promise<TokenResponse | null> {\n const response = await fetch(`${getBaseUrl()}/api/auth/device/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code: deviceCode,\n client_id: CLIENT_ID,\n }),\n });\n\n const data = (await response.json()) as TokenResponse | TokenErrorResponse;\n\n if (!response.ok) {\n if (\"error\" in data && data.error === \"authorization_pending\") {\n return null; // Still waiting for user\n }\n const errorMsg = \"error_description\" in data ? data.error_description : (\"error\" in data ? data.error : \"Token exchange failed\");\n throw new Error(errorMsg || \"Token exchange failed\");\n }\n\n return data as TokenResponse;\n}\n\n/**\n * Get current user info\n */\nexport async function getCurrentUser(): Promise<UserMeResponse> {\n const token = getValidToken();\n\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/user/me`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n throw new Error(\"Failed to get user info\");\n }\n\n return (await response.json()) as UserMeResponse;\n}\n\n// ============================================\n// Config API - Agents\n// ============================================\n\n/**\n * List agents for the current account\n */\nexport async function listAgents(options?: {\n includeKpis?: boolean;\n}): Promise<Agent[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (options?.includeKpis) {\n params.set(\"includeKpis\", \"true\");\n }\n\n const url = `${getBaseUrl()}/api/config/agents${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list agents\");\n }\n\n const data = (await response.json()) as { agents: Agent[] };\n return data.agents;\n}\n\n/**\n * Get a single agent by ID\n */\nexport async function getAgent(id: string): Promise<Agent> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Agent not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get agent\");\n }\n\n return (await response.json()) as Agent;\n}\n\n/**\n * Create a new agent\n */\nexport async function createAgent(payload: CreateAgentPayload): Promise<Agent> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/agents`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const errorBody = (await response.json().catch(() => ({}))) as {\n error?: string;\n message?: string;\n forbiddenFields?: string[];\n tier?: string;\n limit?: number;\n };\n // Typed backend errors get role-aware, action-oriented messages.\n // Anything else falls through to the generic string the backend\n // sends, or a status-code summary if the body had no message.\n if (response.status === 403) {\n throw new Error(\n \"Your role can't create monitoring agents on this workspace. \" +\n \"Ask an admin to enable employee self-monitoring (or create the agent for you).\",\n );\n }\n if (\n response.status === 400 &&\n errorBody.error === \"self_monitor_admin_field_forbidden\"\n ) {\n const fields = errorBody.forbiddenFields?.join(\", \") ?? \"admin-only\";\n throw new Error(\n `Self-monitoring agents cannot be attached to admin-curated structures (${fields}). The CLI should be sending only name/description/role/source/createApiKey — this is a CLI bug, please report it.`,\n );\n }\n if (\n (response.status === 402 || response.status === 403) &&\n errorBody.error === \"tier_agent_limit_reached\"\n ) {\n throw new Error(\n errorBody.message ??\n `Your account is at its agent limit${errorBody.limit ? ` (${errorBody.limit}/${errorBody.limit} on ${errorBody.tier ?? \"FREE\"} tier)` : \"\"}. Ask your admin to upgrade or remove unused agents.`,\n );\n }\n if (response.status === 409 && errorBody.error === \"name_conflict\") {\n throw new Error(\n errorBody.message ??\n \"An agent with this name already exists on this account. Pick a different name and retry.\",\n );\n }\n if (response.status === 409) {\n throw new Error(\"An agent with this name already exists\");\n }\n if (response.status === 429) {\n throw new Error(\n errorBody.message ??\n \"Too many agent-creation requests. Try again in a minute.\",\n );\n }\n throw new Error(errorBody.error || errorBody.message || \"Failed to create agent\");\n }\n\n return (await response.json()) as Agent;\n}\n\n/**\n * GET /api/config/agents/mine — list agents the caller created.\n *\n * Used by the monitor-init flow to detect an existing self-monitor\n * agent before attempting create (idempotent re-runs across\n * workspaces, plus collision avoidance on shared-repo setups).\n *\n * The response includes only MASKED API keys; to recover the\n * unmasked key for an existing agent the CLI must call\n * `regenerateAgentApiKey()` (loud rotation, intentional).\n */\nexport interface MineAgent {\n id: string;\n name: string;\n description: string;\n role: \"WORKER\" | \"COORDINATOR\";\n source: string;\n creatorUserId: string | null;\n createdAt: string;\n apiKey: { id: string; keyMasked: string; isActive: boolean } | null;\n}\n\nexport async function listMyAgents(filter?: {\n source?: string;\n name?: string;\n}): Promise<MineAgent[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (filter?.source) params.set(\"source\", filter.source);\n if (filter?.name) params.set(\"name\", filter.name);\n const qs = params.toString();\n const url = `${getBaseUrl()}/api/config/agents/mine${qs ? `?${qs}` : \"\"}`;\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 403) {\n throw new Error(\"Your role can't list agents on this workspace.\");\n }\n const errorBody = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(errorBody.error || \"Failed to list your agents\");\n }\n\n const data = (await response.json()) as { agents: MineAgent[] };\n return data.agents;\n}\n\n/**\n * POST /api/config/agents/:id/regenerate-api-key — mint a fresh key,\n * revoke the old one. Owner-or-admin only (backend enforces).\n *\n * The returned `key` is plaintext, shown ONCE. Other workspaces using\n * the old key will start 401-ing on the next monitor request and need\n * to re-init.\n */\nexport async function regenerateAgentApiKey(\n agentId: string,\n): Promise<{ id: string; key: string; keyMasked: string; isActive: boolean }> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(\n `${getBaseUrl()}/api/config/agents/${agentId}/regenerate-api-key`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}` },\n },\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 403) {\n throw new Error(\n \"Only the agent's creator or an ADMIN may rotate its API key.\",\n );\n }\n if (response.status === 404) {\n throw new Error(\"Agent not found.\");\n }\n if (response.status === 429) {\n throw new Error(\"Too many key-rotation requests. Try again in a minute.\");\n }\n const errorBody = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(errorBody.error || \"Failed to regenerate API key\");\n }\n\n return (await response.json()) as {\n id: string;\n key: string;\n keyMasked: string;\n isActive: boolean;\n };\n}\n\n/**\n * Update an agent\n */\nexport async function updateAgent(\n id: string,\n payload: UpdateAgentPayload,\n): Promise<Agent> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 403) {\n throw new Error(\n \"Only the agent's creator or an ADMIN may update it. Ask an admin, or update an agent you created.\",\n );\n }\n if (response.status === 404) {\n throw new Error(\"Agent not found\");\n }\n if (response.status === 409) {\n throw new Error(\"An agent with this name already exists\");\n }\n const error = (await response.json().catch(() => ({}))) as { error?: string };\n throw new Error(error.error || \"Failed to update agent\");\n }\n\n return (await response.json()) as Agent;\n}\n\n/**\n * Delete an agent\n */\nexport async function deleteAgent(id: string): Promise<void> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/agents/${id}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 403) {\n throw new Error(\n \"Only the agent's creator or an ADMIN may delete it.\",\n );\n }\n if (response.status === 404) {\n throw new Error(\"Agent not found\");\n }\n const error = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(error.error || \"Failed to delete agent\");\n }\n}\n\n// ============================================\n// Config API - Workflows\n// ============================================\n\n/**\n * List workflows for the current account\n */\nexport async function listWorkflows(options?: {\n includeAgents?: boolean;\n includeInactive?: boolean;\n}): Promise<Workflow[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (options?.includeAgents) {\n params.set(\"includeAgents\", \"true\");\n }\n if (options?.includeInactive) {\n params.set(\"includeInactive\", \"true\");\n }\n\n const url = `${getBaseUrl()}/api/config/workflows${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list workflows\");\n }\n\n const data = (await response.json()) as { workflows: Workflow[] };\n return data.workflows;\n}\n\n/**\n * Get a single workflow by ID\n */\nexport async function getWorkflow(id: string): Promise<Workflow> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Workflow not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get workflow\");\n }\n\n return (await response.json()) as Workflow;\n}\n\n/**\n * Create a new workflow\n */\nexport async function createWorkflow(\n payload: CreateWorkflowPayload,\n): Promise<Workflow> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/workflows`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 409) {\n throw new Error(\"A workflow with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to create workflow\");\n }\n\n return (await response.json()) as Workflow;\n}\n\n/**\n * Update a workflow\n */\nexport async function updateWorkflow(\n id: string,\n payload: UpdateWorkflowPayload,\n): Promise<Workflow> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Workflow not found\");\n }\n if (response.status === 409) {\n throw new Error(\"A workflow with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to update workflow\");\n }\n\n return (await response.json()) as Workflow;\n}\n\n/**\n * Delete a workflow\n */\nexport async function deleteWorkflow(id: string): Promise<void> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/workflows/${id}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Workflow not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to delete workflow\");\n }\n}\n\n// ============================================\n// Config API - KPIs\n// ============================================\n\n/**\n * List KPI definitions for the current account\n */\nexport async function listKpis(options?: {\n agentId?: string;\n includeInactive?: boolean;\n}): Promise<KpiDefinition[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (options?.agentId) {\n params.set(\"agentId\", options.agentId);\n }\n if (options?.includeInactive) {\n params.set(\"includeInactive\", \"true\");\n }\n\n const url = `${getBaseUrl()}/api/config/kpis${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list KPIs\");\n }\n\n const data = (await response.json()) as { kpiDefinitions: KpiDefinition[] };\n return data.kpiDefinitions;\n}\n\n/**\n * Get a single KPI definition by ID\n */\nexport async function getKpi(id: string): Promise<KpiDefinition> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"KPI definition not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get KPI\");\n }\n\n return (await response.json()) as KpiDefinition;\n}\n\n/**\n * Create a new KPI definition\n */\nexport async function createKpi(payload: CreateKpiPayload): Promise<KpiDefinition> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/kpis`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 409) {\n throw new Error(\"A KPI definition with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to create KPI\");\n }\n\n return (await response.json()) as KpiDefinition;\n}\n\n/**\n * Update a KPI definition\n */\nexport async function updateKpi(\n id: string,\n payload: UpdateKpiPayload,\n): Promise<KpiDefinition> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"KPI definition not found\");\n }\n if (response.status === 409) {\n throw new Error(\"A KPI definition with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to update KPI\");\n }\n\n return (await response.json()) as KpiDefinition;\n}\n\n/**\n * Delete a KPI definition\n */\nexport async function deleteKpi(id: string): Promise<void> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/kpis/${id}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"KPI definition not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to delete KPI\");\n }\n}\n\n/**\n * Get available context variables for KPI formulas\n */\nexport async function getKpiContextVariables(\n agentId?: string,\n scope?: KpiScope,\n): Promise<ContextVariable[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (agentId) {\n params.set(\"agentId\", agentId);\n }\n if (scope) {\n params.set(\"scope\", scope);\n }\n\n const url = `${getBaseUrl()}/api/config/kpis/context-variables${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get context variables\");\n }\n\n const data = (await response.json()) as { contextVariables: ContextVariable[] };\n return data.contextVariables;\n}\n\n/**\n * Validate a KPI formula expression\n */\nexport async function validateKpiFormula(\n formula: string,\n agentId?: string,\n scope?: KpiScope,\n): Promise<FormulaValidationResult> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const body: Record<string, string | undefined> = { formula, agentId };\n if (scope) {\n body.scope = scope;\n }\n\n const response = await fetch(`${getBaseUrl()}/api/config/kpis/validate`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to validate formula\");\n }\n\n return (await response.json()) as FormulaValidationResult;\n}\n\n// ============================================\n// Monitoring API - API Key Validation\n// ============================================\n\nexport interface ApiKeyValidationResult {\n apiKeyId: string;\n accountId: string;\n}\n\n/**\n * Validates a monitoring API key by calling /api/monitoring/prompt/me.\n * Returns the apiKeyId + accountId the key resolves to, or null if the\n * key is invalid (any non-2xx) or the request fails for any reason.\n *\n * Used by `monitor init` to verify the user pasted a key that belongs\n * to the agent they picked. The endpoint is derived from the monitoring\n * endpoint by appending \"/me\".\n */\nexport async function validateMonitoringApiKey(\n monitoringEndpoint: string,\n apiKey: string,\n): Promise<ApiKeyValidationResult | null> {\n // Strip a single trailing slash before appending \"/me\" so we don't\n // produce a double slash in the resolved URL.\n const normalizedEndpoint = monitoringEndpoint.replace(/\\/+$/, \"\");\n const url = `${normalizedEndpoint}/me`;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5000);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n \"x-api-key\": apiKey,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = (await response.json()) as Partial<ApiKeyValidationResult>;\n if (\n typeof data?.accountId !== \"string\" ||\n typeof data?.apiKeyId !== \"string\"\n ) {\n return null;\n }\n\n return { accountId: data.accountId, apiKeyId: data.apiKeyId };\n } catch {\n // Network failure, abort timeout, JSON parse error — any failure\n // soft-fails to null so callers can decide how to surface it.\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n// ============================================\n// Activity API - Prompt Inspection\n// ============================================\n\nexport interface ActivityPrompt {\n id: string;\n createdAt: string;\n agentId: string | null;\n agentName?: string | null;\n workflowId: string | null;\n workflowName?: string | null;\n taskExecutionId?: string | null;\n app: string;\n modelType: string | null;\n modelId: string | null;\n tokens: number;\n requestTime: number;\n isHighRisk: boolean;\n blocked: boolean;\n decorationStatus: string;\n sensitivity: string[];\n prompt?: string;\n response?: string;\n analytics?: {\n task: string | null;\n subtask: string | null;\n timesaved_minutes: number | null;\n riskassessment_dangerousity: number | null;\n };\n kpiData?: Record<string, unknown>;\n}\n\nexport interface ActivityListResponse {\n prompts: ActivityPrompt[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\nexport interface ActivityListOptions {\n agentId?: string;\n workflowId?: string;\n since?: string;\n until?: string;\n limit?: number;\n offset?: number;\n includeContent?: boolean;\n includeAnalytics?: boolean;\n}\n\nexport interface CoreKpiValue {\n name: string;\n value: number;\n measurement: \"count\" | \"ratio\" | \"sum\";\n unitPrefix?: string;\n unitSuffix?: string;\n}\n\nexport interface AggregatedKpi {\n kpiDefinitionId: string;\n name: string;\n value: number | null;\n aggregationMethod: string;\n unit: string | null;\n dataPointCount: number;\n}\n\nexport interface PeriodKpiData {\n periodStart: string;\n periodEnd: string;\n coreKpis: CoreKpiValue[];\n}\n\nexport interface KpiAtom {\n promptRequestId: string;\n createdAt: string;\n kpis: Record<string, unknown>;\n}\n\nexport interface ActivityKpisResponse {\n coreKpis: CoreKpiValue[];\n kpis: AggregatedKpi[];\n periodData?: PeriodKpiData[];\n atoms?: KpiAtom[];\n}\n\nexport interface ActivityKpisOptions {\n agentId?: string;\n workflowId?: string;\n since?: string;\n until?: string;\n period?: \"hourly\" | \"daily\" | \"weekly\";\n includeAtoms?: boolean;\n}\n\n/**\n * List activity prompts with filters\n */\nexport async function listActivity(\n options: ActivityListOptions = {}\n): Promise<ActivityListResponse> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (options.agentId) params.set(\"agentId\", options.agentId);\n if (options.workflowId) params.set(\"workflowId\", options.workflowId);\n if (options.since) params.set(\"since\", options.since);\n if (options.until) params.set(\"until\", options.until);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options.offset !== undefined) params.set(\"offset\", String(options.offset));\n if (options.includeContent) params.set(\"includeContent\", \"true\");\n if (options.includeAnalytics) params.set(\"includeAnalytics\", \"true\");\n\n const url = `${getBaseUrl()}/api/activity/prompts${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list activity\");\n }\n\n return (await response.json()) as ActivityListResponse;\n}\n\n/**\n * Get a single activity prompt by ID\n */\nexport async function getActivity(\n id: string,\n includeContent?: boolean\n): Promise<ActivityPrompt> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (includeContent) params.set(\"includeContent\", \"true\");\n\n const url = `${getBaseUrl()}/api/activity/prompts/${id}${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Prompt request not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get activity\");\n }\n\n return (await response.json()) as ActivityPrompt;\n}\n\n/**\n * Get aggregated KPI values for an agent or workflow\n */\nexport async function getActivityKpis(\n options: ActivityKpisOptions\n): Promise<ActivityKpisResponse> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n if (!options.agentId && !options.workflowId) {\n throw new Error(\"Either agentId or workflowId is required\");\n }\n\n const params = new URLSearchParams();\n if (options.agentId) params.set(\"agentId\", options.agentId);\n if (options.workflowId) params.set(\"workflowId\", options.workflowId);\n if (options.since) params.set(\"since\", options.since);\n if (options.until) params.set(\"until\", options.until);\n if (options.period) params.set(\"period\", options.period);\n if (options.includeAtoms) params.set(\"includeAtoms\", \"true\");\n\n const url = `${getBaseUrl()}/api/activity/kpis${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get activity KPIs\");\n }\n\n return (await response.json()) as ActivityKpisResponse;\n}\n\n// ============================================\n// Activity API - Session Diagnostics\n// ============================================\n\nexport interface SessionDiagnostic {\n id: string;\n createdAt: string;\n agentId: string | null;\n decorationStatus: string;\n decorationDate: string | null;\n decorationCandidate: boolean;\n decorationError: string | null;\n decorationErrorStreak: number;\n decoratorVersion: string | null;\n hasKpiData: boolean;\n}\n\nexport interface SessionsListResponse {\n sessions: SessionDiagnostic[];\n summary: {\n sessionCount: number;\n byStatus: Record<string, number>;\n };\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\nexport interface SessionsListOptions {\n agentId: string;\n since?: string;\n until?: string;\n limit?: number;\n offset?: number;\n}\n\n/**\n * List session diagnostics for an agent\n */\nexport async function listSessions(\n options: SessionsListOptions,\n): Promise<SessionsListResponse> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n params.set(\"agentId\", options.agentId);\n if (options.since) params.set(\"since\", options.since);\n if (options.until) params.set(\"until\", options.until);\n if (options.limit !== undefined) params.set(\"limit\", String(options.limit));\n if (options.offset !== undefined) params.set(\"offset\", String(options.offset));\n\n const url = `${getBaseUrl()}/api/activity/sessions?${params}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list sessions\");\n }\n\n return (await response.json()) as SessionsListResponse;\n}\n\n// Custom Data Config Types\nexport type CustomDataType = \"STRING\" | \"NUMBER\" | \"BOOLEAN\";\n\nexport interface CustomDataConfig {\n id: string;\n accountId: string;\n agentId: string | null; // NULL means legacy account-level config\n name: string;\n description: string | null;\n type: CustomDataType;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface CreateCustomDataPayload {\n agentId: string; // Required for new configs\n name: string;\n description?: string | null;\n type: CustomDataType;\n}\n\nexport interface UpdateCustomDataPayload {\n name?: string;\n description?: string | null;\n type?: CustomDataType;\n // Note: agentId is not updatable after creation\n}\n\n/**\n * List custom data configurations\n * @param agentId - Optional agent ID to filter by. If provided, returns agent-specific configs\n * merged with legacy account-level configs. If omitted, returns all account configs.\n */\nexport async function listCustomDataConfigs(agentId?: string): Promise<CustomDataConfig[]> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const params = new URLSearchParams();\n if (agentId) {\n params.set(\"agentId\", agentId);\n }\n\n const url = `${getBaseUrl()}/api/config/custom-data${params.toString() ? `?${params}` : \"\"}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to list custom data configurations\");\n }\n\n const data = (await response.json()) as { customDataConfigs: CustomDataConfig[] };\n return data.customDataConfigs;\n}\n\n/**\n * Get a single custom data configuration\n */\nexport async function getCustomDataConfig(id: string): Promise<CustomDataConfig> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Custom data configuration not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to get custom data configuration\");\n }\n\n return (await response.json()) as CustomDataConfig;\n}\n\n/**\n * Create a new custom data configuration\n */\nexport async function createCustomDataConfig(\n payload: CreateCustomDataPayload\n): Promise<CustomDataConfig> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const url = `${getBaseUrl()}/api/config/custom-data`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 409) {\n throw new Error(\"A custom data configuration with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to create custom data configuration\");\n }\n\n return (await response.json()) as CustomDataConfig;\n}\n\n/**\n * Update a custom data configuration\n */\nexport async function updateCustomDataConfig(\n id: string,\n payload: UpdateCustomDataPayload\n): Promise<CustomDataConfig> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Custom data configuration not found\");\n }\n if (response.status === 409) {\n throw new Error(\"A custom data configuration with this name already exists\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to update custom data configuration\");\n }\n\n return (await response.json()) as CustomDataConfig;\n}\n\n/**\n * Delete a custom data configuration\n */\nexport async function deleteCustomDataConfig(id: string): Promise<void> {\n const token = getValidToken();\n if (!token) {\n throw new Error(\"Not logged in. Run 'olakai login' first.\");\n }\n\n const url = `${getBaseUrl()}/api/config/custom-data/${encodeURIComponent(id)}`;\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new Error(\"Session expired. Run 'olakai login' to authenticate again.\");\n }\n if (response.status === 404) {\n throw new Error(\"Custom data configuration not found\");\n }\n const error = (await response.json()) as { error?: string };\n throw new Error(error.error || \"Failed to delete custom data configuration\");\n }\n}\n"],"mappings":";;;;;;;AAqLA,eAAsB,oBAAiD;AACrE,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB;AAAA,IACnE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,EAAE,WAAW,UAAU,CAAC;AAAA,EAC/C,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,qBAAqB,MAAM,SAAS,+BAA+B;AAAA,EAC3F;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAMA,eAAsB,aACpB,YAC+B;AAC/B,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,0BAA0B;AAAA,IACpE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACnB,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,WAAW;AAAA,IACb,CAAC;AAAA,EACH,CAAC;AAED,QAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,WAAW,QAAQ,KAAK,UAAU,yBAAyB;AAC7D,aAAO;AAAA,IACT;AACA,UAAM,WAAW,uBAAuB,OAAO,KAAK,oBAAqB,WAAW,OAAO,KAAK,QAAQ;AACxG,UAAM,IAAI,MAAM,YAAY,uBAAuB;AAAA,EACrD;AAEA,SAAO;AACT;AAKA,eAAsB,iBAA0C;AAC9D,QAAM,QAAQ,cAAc;AAE5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,gBAAgB;AAAA,IAC1D,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AASA,eAAsB,WAAW,SAEZ;AACnB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,SAAS,aAAa;AACxB,WAAO,IAAI,eAAe,MAAM;AAAA,EAClC;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,qBAAqB,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACrF,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,uBAAuB;AAAA,EACxD;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAKA,eAAsB,SAAS,IAA4B;AACzD,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,sBAAsB,EAAE,IAAI;AAAA,IACtE,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,qBAAqB;AAAA,EACtD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,YAAY,SAA6C;AAC7E,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,sBAAsB;AAAA,IAChE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAUzD,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,QACE,SAAS,WAAW,OACpB,UAAU,UAAU,sCACpB;AACA,YAAM,SAAS,UAAU,iBAAiB,KAAK,IAAI,KAAK;AACxD,YAAM,IAAI;AAAA,QACR,0EAA0E,MAAM;AAAA,MAClF;AAAA,IACF;AACA,SACG,SAAS,WAAW,OAAO,SAAS,WAAW,QAChD,UAAU,UAAU,4BACpB;AACA,YAAM,IAAI;AAAA,QACR,UAAU,WACR,qCAAqC,UAAU,QAAQ,KAAK,UAAU,KAAK,IAAI,UAAU,KAAK,OAAO,UAAU,QAAQ,MAAM,WAAW,EAAE;AAAA,MAC9I;AAAA,IACF;AACA,QAAI,SAAS,WAAW,OAAO,UAAU,UAAU,iBAAiB;AAClE,YAAM,IAAI;AAAA,QACR,UAAU,WACR;AAAA,MACJ;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR,UAAU,WACR;AAAA,MACJ;AAAA,IACF;AACA,UAAM,IAAI,MAAM,UAAU,SAAS,UAAU,WAAW,wBAAwB;AAAA,EAClF;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAwBA,eAAsB,aAAa,QAGV;AACvB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,OAAO,MAAM;AACtD,MAAI,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,IAAI;AAChD,QAAM,KAAK,OAAO,SAAS;AAC3B,QAAM,MAAM,GAAG,WAAW,CAAC,0BAA0B,KAAK,IAAI,EAAE,KAAK,EAAE;AAEvE,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,EAC9C,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,UAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAGzD,UAAM,IAAI,MAAM,UAAU,SAAS,4BAA4B;AAAA,EACjE;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAUA,eAAsB,sBACpB,SAC4E;AAC5E,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM;AAAA,IACrB,GAAG,WAAW,CAAC,sBAAsB,OAAO;AAAA,IAC5C;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,kBAAkB;AAAA,IACpC;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AACA,UAAM,YAAa,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAGzD,UAAM,IAAI,MAAM,UAAU,SAAS,8BAA8B;AAAA,EACnE;AAEA,SAAQ,MAAM,SAAS,KAAK;AAM9B;AAKA,eAAsB,YACpB,IACA,SACgB;AAChB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,sBAAsB,EAAE,IAAI;AAAA,IACtE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,UAAM,QAAS,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACrD,UAAM,IAAI,MAAM,MAAM,SAAS,wBAAwB;AAAA,EACzD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,YAAY,IAA2B;AAC3D,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,sBAAsB,EAAE,IAAI;AAAA,IACtE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AACA,UAAM,QAAS,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAGrD,UAAM,IAAI,MAAM,MAAM,SAAS,wBAAwB;AAAA,EACzD;AACF;AASA,eAAsB,cAAc,SAGZ;AACtB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,SAAS,eAAe;AAC1B,WAAO,IAAI,iBAAiB,MAAM;AAAA,EACpC;AACA,MAAI,SAAS,iBAAiB;AAC5B,WAAO,IAAI,mBAAmB,MAAM;AAAA,EACtC;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,wBAAwB,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACxF,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,0BAA0B;AAAA,EAC3D;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAKA,eAAsB,YAAY,IAA+B;AAC/D,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB,EAAE,IAAI;AAAA,IACzE,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,wBAAwB;AAAA,EACzD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,eACpB,SACmB;AACnB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB;AAAA,IACnE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,2BAA2B;AAAA,EAC5D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,eACpB,IACA,SACmB;AACnB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB,EAAE,IAAI;AAAA,IACzE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,2BAA2B;AAAA,EAC5D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,eAAe,IAA2B;AAC9D,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB,EAAE,IAAI;AAAA,IACzE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,2BAA2B;AAAA,EAC5D;AACF;AASA,eAAsB,SAAS,SAGF;AAC3B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,SAAS,SAAS;AACpB,WAAO,IAAI,WAAW,QAAQ,OAAO;AAAA,EACvC;AACA,MAAI,SAAS,iBAAiB;AAC5B,WAAO,IAAI,mBAAmB,MAAM;AAAA,EACtC;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,mBAAmB,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACnF,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,qBAAqB;AAAA,EACtD;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAKA,eAAsB,OAAO,IAAoC;AAC/D,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,oBAAoB,EAAE,IAAI;AAAA,IACpE,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,mBAAmB;AAAA,EACpD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,UAAU,SAAmD;AACjF,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,oBAAoB;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,sBAAsB;AAAA,EACvD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,UACpB,IACA,SACwB;AACxB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,oBAAoB,EAAE,IAAI;AAAA,IACpE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,sBAAsB;AAAA,EACvD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,UAAU,IAA2B;AACzD,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,oBAAoB,EAAE,IAAI;AAAA,IACpE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,sBAAsB;AAAA,EACvD;AACF;AAKA,eAAsB,uBACpB,SACA,OAC4B;AAC5B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,SAAS;AACX,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B;AACA,MAAI,OAAO;AACT,WAAO,IAAI,SAAS,KAAK;AAAA,EAC3B;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,qCAAqC,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACrG,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,iCAAiC;AAAA,EAClE;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAKA,eAAsB,mBACpB,SACA,SACA,OACkC;AAClC,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,OAA2C,EAAE,SAAS,QAAQ;AACpE,MAAI,OAAO;AACT,SAAK,QAAQ;AAAA,EACf;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,CAAC,6BAA6B;AAAA,IACvE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,4BAA4B;AAAA,EAC7D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAoBA,eAAsB,yBACpB,oBACA,QACwC;AAGxC,QAAM,qBAAqB,mBAAmB,QAAQ,QAAQ,EAAE;AAChE,QAAM,MAAM,GAAG,kBAAkB;AAEjC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,GAAI;AAEzD,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa;AAAA,MACf;AAAA,MACA,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,QACE,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,aAAa,UAC1B;AACA,aAAO;AAAA,IACT;AAEA,WAAO,EAAE,WAAW,KAAK,WAAW,UAAU,KAAK,SAAS;AAAA,EAC9D,QAAQ;AAGN,WAAO;AAAA,EACT,UAAE;AACA,iBAAa,OAAO;AAAA,EACtB;AACF;AAqGA,eAAsB,aACpB,UAA+B,CAAC,GACD;AAC/B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,QAAS,QAAO,IAAI,WAAW,QAAQ,OAAO;AAC1D,MAAI,QAAQ,WAAY,QAAO,IAAI,cAAc,QAAQ,UAAU;AACnE,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC1E,MAAI,QAAQ,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,QAAQ,MAAM,CAAC;AAC7E,MAAI,QAAQ,eAAgB,QAAO,IAAI,kBAAkB,MAAM;AAC/D,MAAI,QAAQ,iBAAkB,QAAO,IAAI,oBAAoB,MAAM;AAEnE,QAAM,MAAM,GAAG,WAAW,CAAC,wBAAwB,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACxF,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,yBAAyB;AAAA,EAC1D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,YACpB,IACA,gBACyB;AACzB,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,eAAgB,QAAO,IAAI,kBAAkB,MAAM;AAEvD,QAAM,MAAM,GAAG,WAAW,CAAC,yBAAyB,EAAE,GAAG,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AAC9F,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,wBAAwB;AAAA,EACzD;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,gBACpB,SAC+B;AAC/B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,MAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,YAAY;AAC3C,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,QAAS,QAAO,IAAI,WAAW,QAAQ,OAAO;AAC1D,MAAI,QAAQ,WAAY,QAAO,IAAI,cAAc,QAAQ,UAAU;AACnE,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACvD,MAAI,QAAQ,aAAc,QAAO,IAAI,gBAAgB,MAAM;AAE3D,QAAM,MAAM,GAAG,WAAW,CAAC,qBAAqB,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AACrF,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,6BAA6B;AAAA,EAC9D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AA0CA,eAAsB,aACpB,SAC+B;AAC/B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,SAAO,IAAI,WAAW,QAAQ,OAAO;AACrC,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,QAAQ,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC1E,MAAI,QAAQ,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,QAAQ,MAAM,CAAC;AAE7E,QAAM,MAAM,GAAG,WAAW,CAAC,0BAA0B,MAAM;AAC3D,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,yBAAyB;AAAA,EAC1D;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAmCA,eAAsB,sBAAsB,SAA+C;AACzF,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,SAAS;AACX,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,0BAA0B,OAAO,SAAS,IAAI,IAAI,MAAM,KAAK,EAAE;AAC1F,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,2CAA2C;AAAA,EAC5E;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,SAAO,KAAK;AACd;AAKA,eAAsB,oBAAoB,IAAuC;AAC/E,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,2BAA2B,mBAAmB,EAAE,CAAC;AAC5E,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,yCAAyC;AAAA,EAC1E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,uBACpB,SAC2B;AAC3B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC;AAC3B,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,2DAA2D;AAAA,IAC7E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,4CAA4C;AAAA,EAC7E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,uBACpB,IACA,SAC2B;AAC3B,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,2BAA2B,mBAAmB,EAAE,CAAC;AAC5E,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,2DAA2D;AAAA,IAC7E;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,4CAA4C;AAAA,EAC7E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAKA,eAAsB,uBAAuB,IAA2B;AACtE,QAAM,QAAQ,cAAc;AAC5B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,QAAM,MAAM,GAAG,WAAW,CAAC,2BAA2B,mBAAmB,EAAE,CAAC;AAC5E,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,UAAM,IAAI,MAAM,MAAM,SAAS,4CAA4C;AAAA,EAC7E;AACF;","names":[]}
@@ -306,6 +306,7 @@ export {
306
306
  getSettingsPath,
307
307
  readJsonFile,
308
308
  writeJsonFile,
309
+ migrateLegacyClaudeConfigIfNeeded,
309
310
  getClaudeCodeConfigPath,
310
311
  loadClaudeCodeConfig,
311
312
  writeClaudeCodeConfig,
@@ -313,4 +314,4 @@ export {
313
314
  getClaudeCodeStatus,
314
315
  printClaudeCodeStatus
315
316
  };
316
- //# sourceMappingURL=chunk-2Q7JYGCK.js.map
317
+ //# sourceMappingURL=chunk-KY6OHQZW.js.map
@@ -0,0 +1,29 @@
1
+ import {
2
+ buildReport,
3
+ exitCodeForStatus,
4
+ formatFixResults,
5
+ formatReport,
6
+ printDoctorResult,
7
+ resolveTargets,
8
+ runCheckChain,
9
+ runDoctor,
10
+ runFixes,
11
+ targetKey
12
+ } from "./chunk-GXKHWBGO.js";
13
+ import "./chunk-75YQWZ4Q.js";
14
+ import "./chunk-KNGRF4XU.js";
15
+ import "./chunk-KY6OHQZW.js";
16
+ import "./chunk-AVB4N2UN.js";
17
+ export {
18
+ buildReport,
19
+ exitCodeForStatus,
20
+ formatFixResults,
21
+ formatReport,
22
+ printDoctorResult,
23
+ resolveTargets,
24
+ runCheckChain,
25
+ runDoctor,
26
+ runFixes,
27
+ targetKey
28
+ };
29
+ //# sourceMappingURL=doctor-27VDFNP7.js.map