@danainnovations/cortex-mcp 1.0.78 → 1.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +83 -29
- package/dist/cli.js.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../bin/cli.ts","../src/wizard/server.ts","../src/wizard/html.ts","../src/config/clients.ts","../src/config/storage.ts","../src/auth/credentials.ts","../src/wizard/routes.ts","../src/cli/setup.ts","../src/cli/configure.ts","../src/proxy/stdio-server.ts","../src/proxy/http-client.ts","../src/proxy/local-filesystem.ts","../src/cli/serve.ts","../src/cli/status.ts","../src/cli/reset.ts","../src/cli/login.ts","../src/cli/connect.ts","../src/cli/logout.ts","../src/cli/whoami.ts","../src/cli/connections.ts","../src/cli/disconnect.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { runSetup } from \"../src/cli/setup.js\";\nimport { runConfigure } from \"../src/cli/configure.js\";\nimport { runServe } from \"../src/cli/serve.js\";\nimport { runStatus } from \"../src/cli/status.js\";\nimport { runReset } from \"../src/cli/reset.js\";\nimport { runLogin } from \"../src/cli/login.js\";\nimport { runLogout } from \"../src/cli/logout.js\";\nimport { runWhoami } from \"../src/cli/whoami.js\";\nimport { runConnect } from \"../src/cli/connect.js\";\nimport { runConnections } from \"../src/cli/connections.js\";\nimport { runDisconnect } from \"../src/cli/disconnect.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"cortex-mcp\")\n .description(\n \"Connect your AI tools to Asana, GitHub, Microsoft 365, Monday.com, Salesforce, Vercel & Supabase via Cortex — supports Claude, Cursor, Codex & more\"\n )\n .version(\"1.0.0\");\n\nprogram\n .command(\"setup\")\n .description(\"Interactive setup wizard — configure MCPs and AI clients\")\n .action(async () => {\n try {\n await runSetup();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Setup failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"configure\")\n .description(\"Non-interactive configuration for a specific client\")\n .option(\"--key <key>\", \"Cortex API key (uses built-in default if not provided)\")\n .requiredOption(\n \"--client <client>\",\n \"Client to configure (claude-desktop, claude-code, cursor, codex, stdio)\"\n )\n .option(\n \"--mcps <mcps>\",\n \"Comma-separated MCP names (default: all)\",\n )\n .action(async (options) => {\n await runConfigure(options);\n });\n\nprogram\n .command(\"serve\")\n .description(\n \"Start stdio MCP proxy server (for OpenClaw and other stdio clients)\"\n )\n .action(async () => {\n await runServe();\n });\n\nprogram\n .command(\"status\")\n .description(\"Show current Cortex MCP configuration\")\n .action(() => {\n runStatus();\n });\n\nprogram\n .command(\"reset\")\n .description(\"Remove all Cortex MCP entries from AI clients\")\n .action(() => {\n runReset();\n });\n\nprogram\n .command(\"login\")\n .description(\"Sign in with your company Okta SSO account\")\n .option(\n \"--email <email>\",\n \"Company email (use if your Okta email differs from your directory email)\"\n )\n .action(async (options: { email?: string }) => {\n try {\n await runLogin(options.email);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Login failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"logout\")\n .description(\"Sign out and remove stored credentials\")\n .action(() => {\n runLogout();\n });\n\nprogram\n .command(\"whoami\")\n .description(\"Show current authenticated user\")\n .action(() => {\n runWhoami();\n });\n\nprogram\n .command(\"connect <provider>\")\n .description(\"Connect your personal account to an MCP service (e.g., asana)\")\n .action(async (provider: string) => {\n try {\n await runConnect(provider);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Connect failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"connections\")\n .description(\"List your OAuth connections to MCP services\")\n .action(async () => {\n try {\n await runConnections();\n } catch (err) {\n console.error(\"Failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"disconnect <provider>\")\n .description(\"Remove your personal OAuth connection to an MCP service\")\n .action(async (provider: string) => {\n try {\n await runDisconnect(provider);\n } catch (err) {\n console.error(\"Disconnect failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"connect-mobile\")\n .description(\"Connect Cortex to Claude on mobile — opens setup page in browser\")\n .action(async () => {\n const { DEFAULT_SERVER_URL } = await import(\"../src/constants.js\");\n const { openBrowser } = await import(\"../src/utils/browser.js\");\n const url = `${DEFAULT_SERVER_URL}/connect`;\n console.log(\"\\nOpening Cortex connect page...\");\n console.log(` ${url}\\n`);\n console.log(\"Follow the steps to connect Cortex to your Claude account.\");\n console.log(\"Once connected, tools will be available on web, desktop, and mobile.\\n\");\n await openBrowser(url);\n });\n\nprogram.parse();\n","import * as http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { getWizardHtml } from \"./html.js\";\nimport { handleApiRoute } from \"./routes.js\";\n\nexport interface WizardServerOptions {\n serverUrl: string;\n}\n\nexport interface WizardServer {\n port: number;\n close: () => Promise<void>;\n waitForCompletion: () => Promise<void>;\n}\n\n/**\n * Start a local HTTP server that serves the setup wizard UI and API routes.\n * Binds to 127.0.0.1 on a random available port (port 0).\n */\nexport function startWizardServer(\n options: WizardServerOptions,\n): Promise<WizardServer> {\n return new Promise((resolve, reject) => {\n let completionResolve: () => void;\n const completionPromise = new Promise<void>((r) => {\n completionResolve = r;\n });\n\n const onComplete = (): void => {\n completionResolve();\n };\n\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://localhost`);\n const path = url.pathname;\n\n // API routes\n if (path.startsWith(\"/api/\")) {\n try {\n const handled = await handleApiRoute(\n path,\n url.searchParams,\n req,\n res,\n options,\n onComplete,\n );\n if (!handled) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: msg }));\n }\n return;\n }\n\n // Serve wizard HTML for root and any other path\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(getWizardHtml());\n });\n\n server.listen(0, \"127.0.0.1\", () => {\n const addr = server.address() as AddressInfo;\n resolve({\n port: addr.port,\n close: () =>\n new Promise<void>((r) => {\n server.close(() => r());\n }),\n waitForCompletion: () => completionPromise,\n });\n });\n\n server.on(\"error\", reject);\n });\n}\n","/**\n * Wizard HTML — single-page setup wizard served by the local HTTP server.\n * Styled with Sonance brand guidelines: Montserrat font, dark mode,\n * \"The Beam\" cyan accent (#00A3E1), Liquid Glass aesthetic.\n */\nexport function getWizardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Cortex MCP Setup</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: #1a1f24;\n color: #fff;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1.6;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n overflow-x: hidden;\n }\n\n /* ── Blue Orb Background ─────────────── */\n .orb {\n position: fixed;\n border-radius: 50%;\n pointer-events: none;\n z-index: 0;\n will-change: transform;\n transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);\n }\n .orb-primary {\n width: 700px;\n height: 700px;\n background: radial-gradient(circle, rgba(0, 163, 225, 0.07) 0%, transparent 70%);\n filter: blur(100px);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n .orb-secondary {\n width: 500px;\n height: 500px;\n background: radial-gradient(circle, rgba(120, 80, 220, 0.05) 0%, transparent 70%);\n filter: blur(80px);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n #app {\n width: 100%;\n max-width: 900px;\n padding: 24px;\n position: relative;\n z-index: 1;\n }\n\n /* ── Persistent Header ────────────────── */\n .wizard-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px 24px;\n }\n .wizard-header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n .header-logo {\n height: 18px;\n width: auto;\n opacity: 0.85;\n }\n .header-divider {\n width: 1px;\n height: 16px;\n background: rgba(255, 255, 255, 0.15);\n }\n .header-product {\n font-size: 13px;\n font-weight: 500;\n color: #00A3E1;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n .header-steps {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .header-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.1);\n transition: all 0.4s ease;\n }\n .header-dot.done { background: #00A3E1; }\n .header-dot.current { background: #00A3E1; opacity: 0.5; }\n\n /* ── Steps ─────────────────────────────── */\n .step { display: none; }\n .step.active { display: block; }\n .step.active.slide-forward { animation: slideInRight 0.35s cubic-bezier(0.16, 1, 0.3, 1); }\n .step.active.slide-backward { animation: slideInLeft 0.35s cubic-bezier(0.16, 1, 0.3, 1); }\n\n @keyframes slideInRight {\n from { opacity: 0; transform: translateX(40px); }\n to { opacity: 1; transform: translateX(0); }\n }\n @keyframes slideInLeft {\n from { opacity: 0; transform: translateX(-40px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n /* ── Liquid Glass Card ─────────────────── */\n .card {\n background: rgba(255, 255, 255, 0.03);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 20px;\n padding: 40px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n }\n\n /* ── Typography ────────────────────────── */\n h1 {\n font-size: 26px;\n font-weight: 300;\n letter-spacing: -0.02em;\n color: #fff;\n margin-bottom: 8px;\n }\n h2 {\n font-size: 20px;\n font-weight: 300;\n letter-spacing: -0.02em;\n color: #fff;\n margin-bottom: 6px;\n }\n .subtitle {\n color: #8f999f;\n font-size: 14px;\n font-weight: 400;\n margin-bottom: 28px;\n line-height: 1.6;\n }\n\n /* ── Welcome ───────────────────────────── */\n .welcome-tagline {\n font-size: 15px;\n color: #00A3E1;\n font-weight: 400;\n margin-bottom: 8px;\n }\n .welcome-hero { padding: 16px 0 28px; }\n .welcome-footer {\n font-size: 11px;\n color: #6b7780;\n margin-top: 20px;\n letter-spacing: 0.04em;\n }\n\n /* ── Buttons ────────────────────────────── */\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 11px 24px;\n border-radius: 12px;\n font-family: 'Montserrat', sans-serif;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n cursor: pointer;\n border: none;\n transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n text-decoration: none;\n }\n .btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n .btn-primary {\n background: #333F48;\n color: #fff;\n }\n .btn-primary:hover:not(:disabled) {\n background: #3a444c;\n box-shadow: 0 0 20px rgba(0, 163, 225, 0.15);\n }\n .btn-primary:active:not(:disabled) { transform: scale(0.98); }\n\n .btn-accent {\n background: #00A3E1;\n color: #fff;\n }\n .btn-accent:hover:not(:disabled) {\n background: #0093cb;\n box-shadow: 0 0 24px rgba(0, 163, 225, 0.3);\n }\n .btn-accent:active:not(:disabled) { transform: scale(0.98); }\n\n .btn-secondary {\n background: rgba(255, 255, 255, 0.03);\n color: #D9D9D6;\n border: 1px solid rgba(255, 255, 255, 0.08);\n }\n .btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.06);\n border-color: rgba(255, 255, 255, 0.12);\n }\n\n .btn-sm { padding: 7px 16px; font-size: 12px; }\n .btn-block { width: 100%; }\n\n .btn-row {\n display: flex;\n gap: 10px;\n margin-top: 28px;\n }\n .btn-row .btn { flex: 1; }\n\n /* ── Spinner ────────────────────────────── */\n .spinner {\n display: inline-block;\n width: 14px;\n height: 14px;\n border: 2px solid rgba(255, 255, 255, 0.1);\n border-top-color: #00A3E1;\n border-radius: 50%;\n animation: spin 0.7s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* ── Status Messages ───────────────────── */\n .status {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 400;\n margin-top: 16px;\n }\n .status-info {\n background: rgba(0, 163, 225, 0.08);\n color: #7dd3fc;\n border: 1px solid rgba(0, 163, 225, 0.15);\n }\n .status-success {\n background: rgba(76, 175, 80, 0.1);\n color: #4CAF50;\n border: 1px solid rgba(76, 175, 80, 0.2);\n }\n .status-error {\n background: rgba(239, 83, 80, 0.1);\n color: #EF5350;\n border: 1px solid rgba(239, 83, 80, 0.2);\n }\n .status-waiting {\n background: rgba(0, 163, 225, 0.06);\n color: #8f999f;\n border: 1px solid rgba(255, 255, 255, 0.06);\n }\n\n /* ── Auth Phases ──────────────────────── */\n .auth-phases {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 16px;\n }\n .auth-phase {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: #6b7780;\n padding: 10px 14px;\n border-radius: 10px;\n transition: all 0.3s ease;\n }\n .auth-phase.active {\n color: #7dd3fc;\n background: rgba(0, 163, 225, 0.06);\n }\n .auth-phase.done { color: #4CAF50; }\n .auth-phase.pending { opacity: 0.35; }\n .phase-icon { font-size: 12px; flex-shrink: 0; width: 14px; text-align: center; }\n\n /* ── MCP Section ──────────────────────── */\n .mcp-section { margin-bottom: 16px; }\n .mcp-section-title {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n margin-bottom: 8px;\n }\n\n /* ── MCP List ──────────────────────────── */\n .mcp-list {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 6px;\n margin-bottom: 8px;\n }\n\n .mcp-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 11px 14px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n cursor: pointer;\n transition: all 0.15s ease;\n user-select: none;\n position: relative;\n }\n .mcp-item:hover { border-color: rgba(0, 163, 225, 0.3); }\n .mcp-tooltip {\n display: none;\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background: #2a3038;\n color: #d0d5da;\n font-size: 11px;\n font-weight: 400;\n padding: 6px 10px;\n border-radius: 6px;\n white-space: nowrap;\n pointer-events: none;\n z-index: 10;\n border: 1px solid rgba(255,255,255,0.1);\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n }\n .mcp-tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #2a3038;\n }\n .mcp-item:hover .mcp-tooltip { display: block; }\n .mcp-item.checked {\n border-color: rgba(0, 163, 225, 0.4);\n background: rgba(0, 163, 225, 0.06);\n }\n .mcp-item input[type=\"checkbox\"] { display: none; }\n .mcp-item.required { cursor: default; }\n .mcp-item.required .mcp-check { background: #00A3E1; border-color: #00A3E1; }\n .mcp-item.required .mcp-check-icon { display: block; }\n .mcp-item.user-connected { border-color: rgba(76, 175, 80, 0.4); cursor: default; }\n .mcp-item.user-connected .mcp-check { background: #4CAF50; border-color: #4CAF50; }\n .mcp-item.user-connected .mcp-check-icon { display: block; }\n .mcp-item.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }\n .mcp-item.disabled .mcp-check { background: transparent; border-color: rgba(255,255,255,0.1); }\n .mcp-coming-soon-label {\n position: absolute;\n top: 6px;\n right: 8px;\n font-size: 9px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255, 255, 255, 0.35);\n background: rgba(255, 255, 255, 0.06);\n padding: 2px 6px;\n border-radius: 4px;\n }\n .mcp-connected-badge {\n display: block;\n font-size: 11px;\n color: #4CAF50;\n margin-top: 2px;\n }\n .mcp-connected-label {\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 9px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #4CAF50;\n }\n .mcp-required-label {\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 9px;\n font-weight: 500;\n color: #8f999f;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .mcp-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.04);\n flex-shrink: 0;\n }\n .mcp-icon svg { width: 16px; height: 16px; fill: #8f999f; }\n .mcp-item.checked .mcp-icon svg { fill: #00A3E1; }\n\n .mcp-check {\n width: 18px;\n height: 18px;\n border: 1.5px solid rgba(255, 255, 255, 0.2);\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: all 0.15s;\n }\n .mcp-item.checked .mcp-check,\n .client-item.checked .mcp-check {\n background: #00A3E1;\n border-color: #00A3E1;\n }\n .mcp-check-icon { display: none; color: #fff; font-size: 11px; font-weight: 600; }\n .mcp-item.checked .mcp-check-icon,\n .client-item.checked .mcp-check-icon { display: block; }\n\n .mcp-info { flex: 1; min-width: 0; }\n .mcp-name { font-size: 13px; font-weight: 500; color: #fff; }\n .mcp-desc { font-size: 11px; color: #8f999f; display: block; margin-top: 2px; font-weight: 400; }\n .mcp-badge {\n font-size: 10px;\n font-weight: 500;\n padding: 1px 7px;\n border-radius: 6px;\n margin-left: 8px;\n letter-spacing: 0.03em;\n text-transform: uppercase;\n }\n .mcp-badge.company {\n background: rgba(76, 175, 80, 0.12);\n color: #4CAF50;\n border: 1px solid rgba(76, 175, 80, 0.2);\n }\n .mcp-badge.personal {\n background: rgba(255, 183, 77, 0.12);\n color: #FFB74D;\n border: 1px solid rgba(255, 183, 77, 0.2);\n }\n\n /* ── Client List ───────────────────────── */\n .client-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 8px;\n }\n .client-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n cursor: pointer;\n user-select: none;\n transition: all 0.15s ease;\n }\n .client-item:hover { border-color: rgba(0, 163, 225, 0.3); }\n .client-item.checked {\n border-color: rgba(0, 163, 225, 0.4);\n background: rgba(0, 163, 225, 0.06);\n }\n .client-item.disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n .client-item.disabled:hover { border-color: rgba(255, 255, 255, 0.06); }\n .client-item.already-configured { border-color: rgba(76, 175, 80, 0.4); cursor: default; }\n .client-item.already-configured .mcp-check { background: #4CAF50; border-color: #4CAF50; }\n .client-item.already-configured .mcp-check-icon { display: block; }\n .client-item.already-configured .client-status { color: #4CAF50; }\n\n .client-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.04);\n flex-shrink: 0;\n }\n .client-icon svg { width: 16px; height: 16px; fill: #8f999f; }\n .client-item.checked .client-icon svg { fill: #00A3E1; }\n\n .client-name { font-size: 13px; font-weight: 500; color: #fff; }\n .client-status {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-left: auto;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n /* ── Connection List ───────────────────── */\n .conn-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 8px;\n }\n .conn-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n transition: all 0.2s ease;\n }\n .conn-item.connected {\n border-left: 3px solid #4CAF50;\n background: rgba(76, 175, 80, 0.04);\n }\n .conn-name { font-size: 13px; font-weight: 500; color: #fff; flex-shrink: 0; }\n .conn-status { font-size: 12px; color: #8f999f; flex: 1; text-align: right; font-weight: 400; }\n .conn-status.connected { color: #4CAF50; }\n .conn-item.company-default { cursor: default; }\n\n /* ── Done / Success ───────────────────── */\n .success-circle {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: rgba(0, 163, 225, 0.1);\n border: 2px solid #00A3E1;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n animation: successPop 0.5s cubic-bezier(0.16, 1, 0.3, 1);\n }\n .success-check {\n font-size: 28px;\n color: #00A3E1;\n animation: checkDraw 0.3s ease 0.2s both;\n }\n @keyframes successPop {\n from { transform: scale(0.5); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n @keyframes checkDraw {\n from { opacity: 0; transform: scale(0.5); }\n to { opacity: 1; transform: scale(1); }\n }\n\n .next-steps {\n text-align: left;\n margin-top: 24px;\n padding: 20px;\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.02);\n border: 1px solid rgba(255, 255, 255, 0.06);\n }\n .next-steps h3 {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-bottom: 12px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .next-step-item {\n font-size: 13px;\n color: #D9D9D6;\n padding: 4px 0;\n font-weight: 400;\n }\n .next-step-item code {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px;\n background: rgba(0, 163, 225, 0.1);\n color: #7dd3fc;\n padding: 2px 6px;\n border-radius: 4px;\n }\n\n /* ── Summary ───────────────────────────── */\n .summary-section { margin-bottom: 24px; }\n .summary-section h3 {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .summary-item {\n font-size: 13px;\n color: #D9D9D6;\n padding: 3px 0;\n font-weight: 400;\n }\n .check-icon { color: #4CAF50; margin-right: 8px; }\n\n /* ── Utility ───────────────────────────── */\n .text-center { text-align: center; }\n .mt-4 { margin-top: 16px; }\n .mt-2 { margin-top: 8px; }\n .text-sm { font-size: 12px; font-weight: 400; }\n .text-muted { color: #6b7780; }\n .hidden { display: none !important; }\n\n /* ── Stdio Snippet ─────────────────────── */\n .stdio-snippet {\n background: rgba(0, 0, 0, 0.3);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 10px;\n padding: 16px;\n margin-top: 12px;\n position: relative;\n }\n .stdio-snippet pre {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px;\n color: #7dd3fc;\n white-space: pre-wrap;\n word-break: break-all;\n margin: 0;\n line-height: 1.5;\n }\n .stdio-snippet .copy-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n padding: 4px 10px;\n font-size: 11px;\n border-radius: 6px;\n background: rgba(0, 163, 225, 0.15);\n color: #00A3E1;\n border: 1px solid rgba(0, 163, 225, 0.3);\n cursor: pointer;\n font-family: 'Montserrat', sans-serif;\n }\n .stdio-snippet .copy-btn:hover {\n background: rgba(0, 163, 225, 0.25);\n }\n\n /* ── Mobile ────────────────────────────── */\n @media (max-width: 640px) {\n #app { padding: 16px; }\n .card { padding: 24px 20px; border-radius: 16px; }\n .mcp-list { grid-template-columns: 1fr; }\n h1 { font-size: 22px; }\n h2 { font-size: 18px; }\n .wizard-header { flex-direction: column; gap: 12px; align-items: flex-start; }\n .orb-primary { width: 400px; height: 400px; }\n .orb-secondary { width: 300px; height: 300px; }\n }\n </style>\n</head>\n<body>\n <!-- Blue Orb Background -->\n <div class=\"orb orb-primary\" id=\"orb-primary\"></div>\n <div class=\"orb orb-secondary\" id=\"orb-secondary\"></div>\n\n <div id=\"app\">\n\n <!-- Persistent Header -->\n <header class=\"wizard-header\">\n <div class=\"wizard-header-left\">\n <img class=\"header-logo\" src=\"data:image/png;base64,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\" alt=\"Sonance\">\n <span class=\"header-divider\"></span>\n <span class=\"header-product\">Cortex</span>\n </div>\n <div class=\"header-steps\" id=\"header-steps\"></div>\n </header>\n\n <!-- Step 1: Welcome -->\n <div id=\"step-welcome\" class=\"step active slide-forward\">\n <div class=\"card text-center\">\n <div class=\"welcome-hero\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\" style=\"margin-bottom: 20px;\">\n <circle cx=\"24\" cy=\"24\" r=\"23\" stroke=\"#00A3E1\" stroke-width=\"1.5\" opacity=\"0.3\"/>\n <circle cx=\"24\" cy=\"24\" r=\"16\" stroke=\"#00A3E1\" stroke-width=\"1\" opacity=\"0.15\"/>\n <circle cx=\"24\" cy=\"24\" r=\"6\" fill=\"#00A3E1\" opacity=\"0.8\"/>\n </svg>\n <h1>Connect Your AI Tools</h1>\n <p class=\"welcome-tagline\">One setup. 10+ platforms. All your AI clients.</p>\n <p class=\"subtitle\" style=\"margin-bottom: 0;\">\n GitHub, Salesforce, M365, Slack, Asana, and more — configured in under 2 minutes.\n </p>\n </div>\n <button class=\"btn btn-accent btn-block\" onclick=\"goToStep('signin')\">Get Started</button>\n <p class=\"welcome-footer\">Powered by Sonance</p>\n </div>\n </div>\n\n <!-- Step 2: Sign In -->\n <div id=\"step-signin\" class=\"step\">\n <div class=\"card\">\n <h2>Sign In</h2>\n <p class=\"subtitle\">Authenticate with your company SSO</p>\n <div id=\"signin-content\"></div>\n </div>\n </div>\n\n <!-- Step 3: Select MCPs -->\n <div id=\"step-mcps\" class=\"step\">\n <div class=\"card\">\n <h2>Select Integrations</h2>\n <p class=\"subtitle\">Choose which services to connect. You can enable more later.</p>\n <div id=\"mcp-list-container\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('signin')\">Back</button>\n <button class=\"btn btn-primary\" onclick=\"saveMcps()\">Continue</button>\n </div>\n </div>\n </div>\n\n <!-- Step 4: Configure Clients -->\n <div id=\"step-clients\" class=\"step\">\n <div class=\"card\">\n <h2>Configure AI Clients</h2>\n <p class=\"subtitle\">Select which AI tools to set up with Cortex</p>\n <div id=\"client-list\" class=\"client-list\"></div>\n <div id=\"client-result\" class=\"hidden\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('mcps')\">Back</button>\n <button id=\"btn-configure\" class=\"btn btn-primary\" onclick=\"configureClients()\">Configure</button>\n </div>\n </div>\n </div>\n\n <!-- Step 5: Connect Accounts -->\n <div id=\"step-connect\" class=\"step\">\n <div class=\"card\">\n <h2>Connect Accounts</h2>\n <p class=\"subtitle\">Link personal accounts for services that require them (optional)</p>\n <div id=\"conn-list\" class=\"conn-list\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('clients')\">Back</button>\n <button class=\"btn btn-primary\" onclick=\"goToStep('done')\">Continue</button>\n </div>\n </div>\n </div>\n\n <!-- Step 6: Done -->\n <div id=\"step-done\" class=\"step\">\n <div class=\"card text-center\">\n <div class=\"success-circle\"><span class=\"success-check\">✓</span></div>\n <h2>Setup Complete</h2>\n <p class=\"subtitle\">Your AI clients are now connected to Cortex</p>\n <div id=\"summary\" style=\"text-align: left; margin: 24px 0;\"></div>\n <div class=\"next-steps\">\n <h3>What's Next</h3>\n <div class=\"next-step-item\">1. Restart your AI clients to load the new tools</div>\n <div class=\"next-step-item\">2. Try: “List my GitHub repos” or “Show my Asana tasks”</div>\n <div class=\"next-step-item\">3. Run <code>cortex-mcp connect <provider></code> to add more accounts later</div>\n </div>\n <div style=\"margin-top: 24px;\">\n <button class=\"btn btn-accent btn-block\" onclick=\"finishSetup()\">Close</button>\n </div>\n <p class=\"text-sm text-muted mt-4\">Config saved to ~/.cortex-mcp/config.json</p>\n </div>\n </div>\n\n </div>\n\n <script>\n // ── State ────────────────────────────────\n var state = {\n credentials: null,\n availableMcps: [],\n selectedMcps: [],\n detectedClients: [],\n selectedClients: [],\n configuredClients: [],\n connections: [],\n };\n\n // ── Step Index Map ───────────────────────\n var STEP_ORDER = ['welcome', 'signin', 'mcps', 'clients', 'connect', 'done'];\n var currentStepIndex = 0;\n\n // ── MCP Icons (inline SVG) ──────────────\n var MCP_ICONS = {\n github: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z\"/></svg>',\n supabase: '<svg viewBox=\"0 0 16 16\"><path d=\"M9.3 14.7c-.2.3-.7.1-.7-.3V9.3h4.6c.5 0 .8-.6.5-1L8.7 1.3c-.2-.3-.7-.1-.7.3v5.1H3.4c-.5 0-.8.6-.5 1l5 7z\"/></svg>',\n vercel: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 1l7 13H1z\"/></svg>',\n m365: '<svg viewBox=\"0 0 16 16\"><rect x=\"1\" y=\"1\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"9\" y=\"1\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"1\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"9\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/></svg>',\n salesforce: '<svg viewBox=\"0 0 16 16\"><path d=\"M6.7 3.3a3 3 0 014.8.5 2.5 2.5 0 013 2.4 2.5 2.5 0 01-1.5 2.3 2.8 2.8 0 01-3.5 2.2A3 3 0 016 12.5a3 3 0 01-3.3-1.8A2.5 2.5 0 011 8.5a2.5 2.5 0 011.8-2.4 2.8 2.8 0 013.9-2.8z\"/></svg>',\n slack: '<svg viewBox=\"0 0 16 16\"><path d=\"M3.5 10a1.5 1.5 0 11-3 0 1.5 1.5 0 011.5-1.5H3.5V10zm.75 0a1.5 1.5 0 113 0v3.5a1.5 1.5 0 11-3 0V10zM6 3.5a1.5 1.5 0 110-3 1.5 1.5 0 011.5 1.5V3.5H6zm0 .75a1.5 1.5 0 110 3H2.5a1.5 1.5 0 110-3H6zM12.5 6a1.5 1.5 0 113 0 1.5 1.5 0 01-1.5 1.5H12.5V6zm-.75 0a1.5 1.5 0 11-3 0V2.5a1.5 1.5 0 113 0V6zM10 12.5a1.5 1.5 0 110 3 1.5 1.5 0 01-1.5-1.5v-1.5H10zm0-.75a1.5 1.5 0 110-3h3.5a1.5 1.5 0 110 3H10z\"/></svg>',\n asana: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"4\" r=\"2.5\"/><circle cx=\"3.5\" cy=\"11\" r=\"2.5\"/><circle cx=\"12.5\" cy=\"11\" r=\"2.5\"/></svg>',\n monday: '<svg viewBox=\"0 0 16 16\"><circle cx=\"4\" cy=\"10\" r=\"2\" /><circle cx=\"8\" cy=\"6\" r=\"2\" /><circle cx=\"12\" cy=\"10\" r=\"2\" /></svg>',\n powerbi: '<svg viewBox=\"0 0 16 16\"><rect x=\"2\" y=\"8\" width=\"3\" height=\"6\" rx=\"1\"/><rect x=\"6.5\" y=\"5\" width=\"3\" height=\"9\" rx=\"1\"/><rect x=\"11\" y=\"2\" width=\"3\" height=\"12\" rx=\"1\"/></svg>',\n bestbuy: '<svg viewBox=\"0 0 16 16\"><path d=\"M2 3h12a1 1 0 011 1v8a1 1 0 01-1 1H2a1 1 0 01-1-1V4a1 1 0 011-1zm3 3v4h2V6H5zm4 0v4h2V6H9z\"/></svg>',\n mailchimp: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 2a6 6 0 100 12A6 6 0 008 2zM6.5 10.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm3 0a1.5 1.5 0 110-3 1.5 1.5 0 010 3z\"/></svg>',\n };\n\n // ── Client Icons (inline SVG) ───────────\n var CLIENT_ICONS = {\n 'claude-desktop': '<svg viewBox=\"0 0 16 16\"><path d=\"M10.5 2H5.5a3 3 0 00-3 3v6a3 3 0 003 3h5a3 3 0 003-3V5a3 3 0 00-3-3zM8 11.5a.75.75 0 110-1.5.75.75 0 010 1.5zm2-3.5H6a.5.5 0 010-1h4a.5.5 0 010 1z\"/></svg>',\n 'claude-code': '<svg viewBox=\"0 0 16 16\"><path d=\"M5.7 11.3a.5.5 0 01-.7-.7L7.3 8 5 5.7a.5.5 0 01.7-.7l3 3a.5.5 0 010 .7l-3 3zM9 11h3a.5.5 0 010 1H9a.5.5 0 010-1z\"/><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\"/></svg>',\n cursor: '<svg viewBox=\"0 0 16 16\"><path d=\"M3 2l10 6-4 1.5L7.5 14z\"/></svg>',\n vscode: '<svg viewBox=\"0 0 16 16\"><path d=\"M11.5 1L5 7l-3-2.5L1 5l3.5 3L1 11l1 .5L5 9l6.5 6 2.5-1V2z\"/></svg>',\n antigravity: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 1l2 5h5l-4 3 1.5 5L8 11l-4.5 3L5 9 1 6h5z\"/></svg>',\n codex: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"8\" r=\"3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 1v3M8 12v3M1 8h3M12 8h3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>',\n perplexity: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"8\" r=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\"/><path d=\"M8 2v12M2 8h12M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linecap=\"round\"/></svg>',\n stdio: '<svg viewBox=\"0 0 16 16\"><path d=\"M2 3h12a1 1 0 011 1v8a1 1 0 01-1 1H2a1 1 0 01-1-1V4a1 1 0 011-1zm1 2v6h10V5H3z\"/></svg>',\n };\n\n // ── Init ─────────────────────────────────\n async function init() {\n try {\n var resp = await fetch('/api/state');\n var data = await resp.json();\n state.credentials = data.credentials;\n state.availableMcps = data.availableMcps || [];\n state.enabledMcps = data.enabledMcps || ['m365'];\n state.selectedMcps = (data.enabledMcps || ['m365']).slice();\n state.detectedClients = data.detectedClients || [];\n state.configuredClients = (data.config && data.config.configuredClients) || [];\n } catch (err) {\n console.error('Failed to load state:', err);\n }\n updateHeaderDots();\n }\n\n init();\n\n // ── Header Dots ─────────────────────────\n function updateHeaderDots() {\n var el = document.getElementById('header-steps');\n var html = '';\n for (var i = 0; i < STEP_ORDER.length; i++) {\n var cls = 'header-dot';\n if (i < currentStepIndex) cls += ' done';\n else if (i === currentStepIndex) cls += ' current';\n html += '<span class=\"' + cls + '\"></span>';\n }\n el.innerHTML = html;\n }\n\n // ── Navigation ───────────────────────────\n function goToStep(step) {\n var newIndex = STEP_ORDER.indexOf(step);\n var direction = newIndex >= currentStepIndex ? 'slide-forward' : 'slide-backward';\n\n var active = document.querySelector('.step.active');\n if (active) {\n active.classList.remove('active', 'slide-forward', 'slide-backward');\n }\n\n var nextEl = document.getElementById('step-' + step);\n nextEl.classList.add('active', direction);\n currentStepIndex = newIndex;\n updateHeaderDots();\n\n if (step === 'signin') renderSignin();\n if (step === 'mcps') renderMcps();\n if (step === 'clients') renderClients();\n if (step === 'connect') renderConnect();\n if (step === 'done') renderDone();\n }\n\n // ── Blue Orb Mouse Follow ───────────────\n (function() {\n var primary = document.getElementById('orb-primary');\n var secondary = document.getElementById('orb-secondary');\n document.addEventListener('mousemove', function(e) {\n var dx = (e.clientX - window.innerWidth / 2) * 0.15;\n var dy = (e.clientY - window.innerHeight / 2) * 0.15;\n primary.style.transform = 'translate(calc(-50% + ' + dx + 'px), calc(-50% + ' + dy + 'px))';\n secondary.style.transform = 'translate(calc(-50% + ' + (-dx * 0.6) + 'px), calc(-50% + ' + (-dy * 0.6) + 'px))';\n });\n })();\n\n // ── Step 2: Sign In ──────────────────────\n function renderSignin() {\n var el = document.getElementById('signin-content');\n\n if (state.credentials) {\n var name = state.credentials.name || state.credentials.email;\n el.innerHTML =\n '<div class=\"status status-success\">' +\n '<span>✓</span>' +\n '<span>Signed in as <strong>' + escapeHtml(name) + '</strong></span>' +\n '</div>' +\n '<div class=\"btn-row\">' +\n '<button class=\"btn btn-primary btn-block\" onclick=\"goToStep(\\\\'mcps\\\\')\">Continue</button>' +\n '</div>';\n return;\n }\n\n el.innerHTML =\n '<button id=\"btn-signin\" class=\"btn btn-accent btn-block\" onclick=\"startLogin()\">Sign in with SSO</button>' +\n '<div id=\"signin-status\" class=\"hidden\"></div>';\n }\n\n async function startLogin() {\n var btn = document.getElementById('btn-signin');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span> Starting...';\n\n var statusEl = document.getElementById('signin-status');\n\n try {\n var resp = await fetch('/api/auth/login', { method: 'POST' });\n var data = await resp.json();\n\n if (data.error) {\n statusEl.className = 'status status-error';\n statusEl.textContent = data.error;\n btn.disabled = false;\n btn.textContent = 'Sign in with SSO';\n return;\n }\n\n window.open(data.auth_url, '_blank');\n\n // Show auth phase indicators\n statusEl.className = '';\n statusEl.innerHTML =\n '<div class=\"auth-phases\">' +\n '<div class=\"auth-phase done\" id=\"phase-browser\"><span class=\"phase-icon\">✓</span> Browser opened</div>' +\n '<div class=\"auth-phase active\" id=\"phase-sso\"><span class=\"spinner\"></span> Waiting for SSO...</div>' +\n '<div class=\"auth-phase pending\" id=\"phase-verify\"><span class=\"phase-icon\">•</span> Verifying identity</div>' +\n '</div>';\n\n btn.className = 'btn btn-secondary btn-block';\n btn.disabled = false;\n btn.textContent = 'Open SSO page again';\n btn.onclick = function() { window.open(data.auth_url, '_blank'); };\n\n var result = await pollUntilDone(\n '/api/auth/poll?session=' + encodeURIComponent(data.session_id),\n 3000,\n data.expires_in * 1000\n );\n\n // Update phases to show completion\n var ssoPhase = document.getElementById('phase-sso');\n if (ssoPhase) {\n ssoPhase.className = 'auth-phase done';\n ssoPhase.innerHTML = '<span class=\"phase-icon\">✓</span> SSO completed';\n }\n var verifyPhase = document.getElementById('phase-verify');\n if (verifyPhase) {\n verifyPhase.className = 'auth-phase done';\n verifyPhase.innerHTML = '<span class=\"phase-icon\">✓</span> Identity verified';\n }\n\n state.credentials = {\n email: result.employee_email || result.email || '',\n name: result.employee_name || result.name || '',\n };\n\n // Brief pause so user sees all green checks\n await new Promise(function(r) { setTimeout(r, 600); });\n renderSignin();\n } catch (err) {\n statusEl.className = 'status status-error';\n statusEl.textContent = err.message === 'Timeout'\n ? 'Authentication timed out. Please try again.'\n : 'Authentication failed: ' + err.message;\n btn.disabled = false;\n btn.textContent = 'Try Again';\n btn.onclick = startLogin;\n }\n }\n\n // ── Step 3: Select MCPs ──────────────────\n\n var MCP_TOOLTIPS = {\n asana: 'Manage projects, tasks, and team workflows',\n github: 'Cloud storage and version control for any projects with code',\n vercel: 'Deploy and host websites and web apps',\n supabase: 'Your project database \\\\u2014 store and query data',\n m365: 'Access your Outlook email, calendar, OneDrive files, and Teams',\n mailchimp: 'Create and manage email marketing campaigns and audiences',\n salesforce: 'Access your CRM \\\\u2014 contacts, leads, opportunities, and reports',\n monday: 'Track work with boards, timelines, and team updates',\n slack: 'Send messages, search conversations, and manage channels',\n powerbi: 'Build dashboards and run data queries on your reports',\n bestbuy: 'Search products, compare prices, and find store locations',\n databricks: 'Query data warehouses, explore Unity Catalog, and manage jobs',\n };\n\n async function renderMcps() {\n var container = document.getElementById('mcp-list-container');\n\n // Fetch per-user available MCPs (uses API key from login)\n try {\n var userResp = await fetch('/api/user-mcps');\n var userData = await userResp.json();\n if (userData.mcps && userData.mcps.length > 0) {\n state.enabledMcps = userData.mcps.map(function(m) { return m.name; });\n state.selectedMcps = state.enabledMcps.slice();\n }\n } catch (e) {\n // Fall back to global enabledMcps from initial state\n }\n\n // Fetch existing OAuth connections to show connected status\n try {\n var resp = await fetch('/api/connections');\n var data = await resp.json();\n state.connections = data.connections || [];\n } catch (e) {\n state.connections = state.connections || [];\n }\n\n var enabledList = (state.enabledMcps && state.enabledMcps.length > 0)\n ? state.enabledMcps : ['m365'];\n var enabledMcps = state.availableMcps.filter(function(m) { return enabledList.indexOf(m.name) !== -1; });\n var disabledMcps = state.availableMcps.filter(function(m) { return enabledList.indexOf(m.name) === -1; });\n\n var html = '';\n\n // Enabled section\n if (enabledMcps.length > 0) {\n html += '<div class=\"mcp-section\">';\n html += '<div class=\"mcp-section-title\">Available integrations</div>';\n html += '<div class=\"mcp-list\">';\n html += enabledMcps.map(function(mcp) { return renderMcpItem(mcp, false, false); }).join('');\n html += '</div></div>';\n }\n\n // Disabled (coming soon) section\n if (disabledMcps.length > 0) {\n html += '<div class=\"mcp-section\">';\n html += '<div class=\"mcp-section-title\">Coming soon</div>';\n html += '<div class=\"mcp-list\">';\n html += disabledMcps.map(function(mcp) { return renderMcpItem(mcp, false, true); }).join('');\n html += '</div></div>';\n }\n\n container.innerHTML = html;\n\n // Attach click handlers to enabled items only (not disabled, not user-connected)\n container.querySelectorAll('.mcp-item:not(.disabled):not(.user-connected)').forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n var cb = item.querySelector('input[type=\"checkbox\"]');\n cb.checked = !cb.checked;\n item.classList.toggle('checked', cb.checked);\n });\n });\n }\n\n function renderMcpItem(mcp, isRequired, isDisabled) {\n var icon = MCP_ICONS[mcp.name] || '';\n var conn = (state.connections || []).find(function(c) { return c.mcp_name === mcp.name; });\n var isConnected = !isDisabled && conn && !conn.is_company_default && conn.account_email;\n var checked = !isDisabled && (isRequired || isConnected || state.selectedMcps.indexOf(mcp.name) !== -1);\n return (\n '<label class=\"mcp-item' + (checked ? ' checked' : '') + (isRequired ? ' required' : '') +\n (isConnected ? ' user-connected' : '') +\n (isDisabled ? ' disabled' : '') + '\" data-mcp=\"' + mcp.name + '\">' +\n (MCP_TOOLTIPS[mcp.name] ? '<span class=\"mcp-tooltip\">' + MCP_TOOLTIPS[mcp.name] + '</span>' : '') +\n '<input type=\"checkbox\" value=\"' + mcp.name + '\"' + (checked ? ' checked' : '') + ((isConnected || isDisabled) ? ' disabled' : '') + '>' +\n (icon ? '<div class=\"mcp-icon\">' + icon + '</div>' : '') +\n '<div class=\"mcp-check\"><span class=\"mcp-check-icon\">✓</span></div>' +\n '<div class=\"mcp-info\">' +\n '<span class=\"mcp-name\">' + escapeHtml(mcp.displayName) +\n '<span class=\"mcp-badge ' + mcp.authMode + '\">' + mcp.authMode + '</span>' +\n '</span>' +\n '<span class=\"mcp-desc\">' + escapeHtml(mcp.description) + '</span>' +\n (isConnected ? '<span class=\"mcp-connected-badge\">✓ Connected as ' + escapeHtml(conn.account_email) + '</span>' : '') +\n '</div>' +\n (isRequired ? '<span class=\"mcp-required-label\">Required</span>' : '') +\n (isConnected && !isRequired ? '<span class=\"mcp-connected-label\">Connected</span>' : '') +\n (isDisabled ? '<span class=\"mcp-coming-soon-label\">Coming Soon</span>' : '') +\n '</label>'\n );\n }\n\n function saveMcps() {\n var checkboxes = document.querySelectorAll('.mcp-item:not(.disabled) input[type=\"checkbox\"]');\n var selected = Array.from(checkboxes).filter(function(cb) { return cb.checked; }).map(function(cb) { return cb.value; });\n state.selectedMcps = selected;\n goToStep('clients');\n }\n\n // ── Step 4: Configure Clients ────────────\n function renderClients() {\n var el = document.getElementById('client-list');\n document.getElementById('client-result').className = 'hidden';\n document.getElementById('btn-configure').disabled = false;\n document.getElementById('btn-configure').textContent = 'Configure';\n\n // Pre-select already-configured clients\n state.selectedClients = state.configuredClients.slice();\n\n el.innerHTML = state.detectedClients.map(function(client) {\n var detected = client.detected;\n var icon = CLIENT_ICONS[client.type] || '';\n var alreadyConfigured = state.configuredClients.indexOf(client.type) !== -1;\n var statusText = alreadyConfigured ? 'Configured' : (detected ? 'Detected' : 'Not found');\n return (\n '<label class=\"client-item' + (detected ? '' : ' disabled') +\n (alreadyConfigured ? ' checked already-configured' : '') + '\" data-client=\"' + client.type + '\">' +\n (icon ? '<div class=\"client-icon\">' + icon + '</div>' : '') +\n '<div class=\"mcp-check\"><span class=\"mcp-check-icon\">✓</span></div>' +\n '<span class=\"client-name\">' + escapeHtml(client.name) + '</span>' +\n '<span class=\"client-status\">' + statusText + '</span>' +\n '</label>'\n );\n }).join('');\n\n el.querySelectorAll('.client-item:not(.disabled):not(.already-configured)').forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n item.classList.toggle('checked');\n state.selectedClients = Array.from(el.querySelectorAll('.client-item.checked'))\n .map(function(i) { return i.dataset.client; });\n });\n });\n }\n\n async function configureClients() {\n if (state.selectedClients.length === 0) {\n goToStep('connect');\n return;\n }\n\n var btn = document.getElementById('btn-configure');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span> Configuring...';\n\n // Show per-client spinners\n state.selectedClients.forEach(function(clientType) {\n var item = document.querySelector('[data-client=\"' + clientType + '\"]');\n if (item) {\n var statusSpan = item.querySelector('.client-status');\n statusSpan.innerHTML = '<span class=\"spinner\"></span>';\n }\n });\n\n try {\n var resp = await fetch('/api/clients/configure', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ clients: state.selectedClients, mcps: state.selectedMcps }),\n });\n var data = await resp.json();\n\n state.configuredClients = data.results.filter(function(r) { return r.success; }).map(function(r) { return r.client; });\n\n // Update per-client status inline\n data.results.forEach(function(r) {\n var item = document.querySelector('[data-client=\"' + r.client + '\"]');\n if (item) {\n var statusSpan = item.querySelector('.client-status');\n if (r.success) {\n statusSpan.innerHTML = '<span style=\"color: #4CAF50;\">✓ Done</span>';\n } else {\n statusSpan.innerHTML = '<span style=\"color: #EF5350;\">✗ Failed</span>';\n }\n }\n });\n\n var resultEl = document.getElementById('client-result');\n resultEl.className = '';\n resultEl.innerHTML = data.results.map(function(r) {\n var html = '<div class=\"status ' + (r.success ? 'status-success' : 'status-error') + '\">' +\n '<span>' + (r.success ? '✓' : '✗') + '</span>' +\n '<span>' + escapeHtml(r.client) + ': ' + (r.success ? 'Configured' : r.error || 'Failed') + '</span>' +\n '</div>';\n // Show copyable snippet for stdio/OpenClaw and Perplexity instructions\n if ((r.client === 'stdio' || r.client === 'perplexity') && r.success && r.message) {\n var snippet = r.message.replace(/^Add this to your client config:\\\\n\\\\n/, '');\n html += '<div class=\"stdio-snippet\">' +\n '<button class=\"copy-btn\" onclick=\"copySnippet(this)\">Copy</button>' +\n '<pre>' + escapeHtml(snippet) + '</pre>' +\n '</div>';\n }\n return html;\n }).join('');\n\n btn.textContent = 'Continue';\n btn.disabled = false;\n btn.onclick = function() { goToStep('connect'); };\n } catch (err) {\n btn.disabled = false;\n btn.textContent = 'Retry';\n var resultEl = document.getElementById('client-result');\n resultEl.className = '';\n resultEl.innerHTML = '<div class=\"status status-error\"><span>✗</span><span>' + escapeHtml(err.message) + '</span></div>';\n }\n }\n\n // ── Step 5: Connect Accounts ─────────────\n async function renderConnect() {\n var el = document.getElementById('conn-list');\n el.innerHTML = '<div class=\"status status-info\"><span class=\"spinner\"></span> Loading connections...</div>';\n\n var selectedMcpList = state.availableMcps.filter(function(m) {\n return state.selectedMcps.indexOf(m.name) !== -1;\n });\n\n if (selectedMcpList.length === 0) {\n el.innerHTML = '<div class=\"status status-info\">No integrations selected.</div>';\n return;\n }\n\n try {\n var resp = await fetch('/api/connections');\n var data = await resp.json();\n state.connections = data.connections || [];\n } catch (e) {\n state.connections = [];\n }\n\n el.innerHTML = selectedMcpList.map(function(mcp) {\n var conn = state.connections.find(function(c) { return c.mcp_name === mcp.name; });\n var isCompany = mcp.authMode !== 'personal';\n var isConnected = conn && !conn.is_company_default && conn.account_email;\n var isCompanyDefault = isCompany;\n\n if (isCompanyDefault) {\n return (\n '<div class=\"conn-item connected company-default\" id=\"conn-' + mcp.name + '\">' +\n '<span class=\"conn-name\">' + escapeHtml(mcp.displayName) + '</span>' +\n '<span class=\"conn-status connected\">✓ Company Account</span>' +\n '</div>'\n );\n }\n\n return (\n '<div class=\"conn-item' + (isConnected ? ' connected' : '') + '\" id=\"conn-' + mcp.name + '\">' +\n '<span class=\"conn-name\">' + escapeHtml(mcp.displayName) + '</span>' +\n (isConnected\n ? '<span class=\"conn-status connected\">✓ ' + escapeHtml(conn.account_email) + '</span>'\n : '<span class=\"conn-status\">Not connected</span>' +\n '<button class=\"btn btn-sm btn-secondary\" onclick=\"connectProvider(\\\\'' + mcp.name + '\\\\', \\\\'' + escapeHtml(mcp.displayName) + '\\\\')\">Connect</button>'\n ) +\n '</div>'\n );\n }).join('');\n }\n\n async function connectProvider(name, displayName) {\n var item = document.getElementById('conn-' + name);\n var btn = item.querySelector('button');\n var statusSpan = item.querySelector('.conn-status');\n\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>';\n statusSpan.textContent = 'Connecting...';\n\n try {\n var resp = await fetch('/api/oauth/connect', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider: name }),\n });\n var data = await resp.json();\n\n if (data.error) {\n statusSpan.textContent = data.error;\n statusSpan.className = 'conn-status';\n btn.disabled = false;\n btn.textContent = 'Retry';\n btn.onclick = function() { connectProvider(name, displayName); };\n return;\n }\n\n window.open(data.authorization_url, '_blank');\n statusSpan.innerHTML = '<span class=\"spinner\"></span> Waiting \\\\u2014 check the new tab';\n btn.textContent = 'Open again';\n btn.disabled = false;\n btn.onclick = function() { window.open(data.authorization_url, '_blank'); };\n\n var result = await pollUntilDone(\n '/api/oauth/poll?session=' + encodeURIComponent(data.session_id),\n 3000,\n data.expires_in * 1000\n );\n\n var email = result.account_email || 'connected';\n item.classList.add('connected');\n statusSpan.className = 'conn-status connected';\n statusSpan.textContent = '\\\\u2713 ' + email;\n btn.remove();\n } catch (err) {\n statusSpan.textContent = err.message === 'Timeout' ? 'Timed out' : err.message;\n statusSpan.className = 'conn-status';\n btn.disabled = false;\n btn.textContent = 'Retry';\n btn.onclick = function() { connectProvider(name, displayName); };\n }\n }\n\n // ── Step 6: Done ─────────────────────────\n function renderDone() {\n var el = document.getElementById('summary');\n var html = '';\n\n html += '<div class=\"summary-section\">';\n html += '<h3>Integrations (' + state.selectedMcps.length + ')</h3>';\n state.selectedMcps.forEach(function(name) {\n var mcp = state.availableMcps.find(function(m) { return m.name === name; });\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(mcp ? mcp.displayName : name) + '</div>';\n });\n html += '</div>';\n\n if (state.configuredClients.length > 0) {\n html += '<div class=\"summary-section\">';\n html += '<h3>Configured Clients</h3>';\n state.configuredClients.forEach(function(c) {\n var client = state.detectedClients.find(function(d) { return d.type === c; });\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(client ? client.name : c) + '</div>';\n });\n html += '</div>';\n }\n\n if (state.credentials) {\n html += '<div class=\"summary-section\">';\n html += '<h3>Signed In</h3>';\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(state.credentials.name || state.credentials.email) + '</div>';\n html += '</div>';\n }\n\n el.innerHTML = html;\n }\n\n async function finishSetup() {\n try {\n await fetch('/api/complete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n mcps: state.selectedMcps,\n configuredClients: state.configuredClients,\n }),\n });\n } catch (e) {\n // Server may already be closing\n }\n\n document.getElementById('step-done').querySelector('.card').innerHTML =\n '<div class=\"text-center\">' +\n '<div class=\"success-circle\"><span class=\"success-check\">✓</span></div>' +\n '<h2>All Done</h2>' +\n '<p class=\"subtitle mt-2\">You can close this tab. Restart your AI clients to see the new tools.</p>' +\n '</div>';\n }\n\n // ── Helpers ──────────────────────────────\n function pollUntilDone(url, intervalMs, timeoutMs) {\n return new Promise(function(resolve, reject) {\n var deadline = Date.now() + timeoutMs;\n var timer = setInterval(async function() {\n if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout'));\n return;\n }\n try {\n var resp = await fetch(url);\n var data = await resp.json();\n if (data.status === 'completed') {\n clearInterval(timer);\n resolve(data);\n } else if (data.status === 'error' || data.status === 'expired') {\n clearInterval(timer);\n reject(new Error(data.error_message || data.status));\n }\n } catch (e) {\n // Network error -- keep polling\n }\n }, intervalMs);\n });\n }\n\n function copySnippet(btn) {\n var pre = btn.parentElement.querySelector('pre');\n navigator.clipboard.writeText(pre.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 2000);\n });\n }\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str || '';\n return div.innerHTML;\n }\n </script>\n</body>\n</html>`;\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { AVAILABLE_MCPS } from \"../constants.js\";\nimport {\n getClaudeDesktopConfigPath,\n getCursorConfigPath,\n getVSCodeMcpConfigPath,\n getAntigravityConfigPath,\n getCodexConfigPath,\n getPlatform,\n isWSL,\n} from \"../utils/platform.js\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ClientType, DetectedClient, HttpMcpEntry } from \"./types.js\";\n\n/**\n * Detect which AI clients are installed on this machine.\n */\nexport function detectClients(): DetectedClient[] {\n const clients: DetectedClient[] = [];\n\n // Claude Desktop\n const desktopPath = getClaudeDesktopConfigPath();\n const desktopDir = dirname(desktopPath);\n clients.push({\n type: \"claude-desktop\",\n name: \"Claude (Desktop)\",\n configPath: desktopPath,\n detected: existsSync(desktopDir),\n });\n\n // Claude Code\n let claudeCodeDetected = false;\n try {\n execSync(\"which claude\", { stdio: \"pipe\" });\n claudeCodeDetected = true;\n } catch {\n // claude CLI not in PATH\n }\n clients.push({\n type: \"claude-code\",\n name: \"Claude Code\",\n configPath: null,\n detected: claudeCodeDetected,\n });\n\n // Cursor\n const cursorPath = getCursorConfigPath();\n const cursorDir = dirname(cursorPath);\n clients.push({\n type: \"cursor\",\n name: \"Cursor\",\n configPath: cursorPath,\n detected: existsSync(cursorDir),\n });\n\n // VS Code\n const home = homedir();\n const vscodePath = getVSCodeMcpConfigPath();\n clients.push({\n type: \"vscode\",\n name: \"VS Code\",\n configPath: vscodePath,\n detected: existsSync(join(home, \".vscode\")),\n });\n\n // Antigravity\n const antigravityPath = getAntigravityConfigPath();\n clients.push({\n type: \"antigravity\",\n name: \"Antigravity\",\n configPath: antigravityPath,\n detected: existsSync(join(home, \".antigravity\")),\n });\n\n // Codex (OpenAI) — CLI and IDE extension share ~/.codex/config.toml\n const codexPath = getCodexConfigPath();\n let codexDetected = existsSync(join(home, \".codex\"));\n if (!codexDetected) {\n try {\n execSync(\"which codex\", { stdio: \"pipe\" });\n codexDetected = true;\n } catch {\n // codex CLI not in PATH\n }\n }\n clients.push({\n type: \"codex\",\n name: \"Codex (OpenAI)\",\n configPath: codexPath,\n detected: codexDetected,\n });\n\n // Perplexity Computer (macOS only)\n const perplexityDir = join(home, \"Library\", \"Containers\", \"ai.perplexity.mac\");\n clients.push({\n type: \"perplexity\",\n name: \"Perplexity Computer\",\n configPath: null,\n detected: getPlatform() === \"macos\" && existsSync(perplexityDir),\n });\n\n // OpenClaw / Stdio (always available — manual copy-paste)\n clients.push({\n type: \"stdio\",\n name: \"OpenClaw (stdio)\",\n configPath: null,\n detected: true,\n });\n\n return clients;\n}\n\n/**\n * Build per-MCP HTTP entries for a given server URL and API key.\n */\nexport function buildHttpEntries(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): Record<string, HttpMcpEntry> {\n const entries: Record<string, HttpMcpEntry> = {};\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n entries[mcp.serverName] = {\n url: `${serverUrl}/mcp/${mcp.name}`,\n headers: { \"x-api-key\": apiKey },\n };\n }\n\n return entries;\n}\n\n/**\n * Configure Claude Desktop by merging a stdio proxy entry into its config file.\n * Uses command/args/env format (Claude Desktop does not support HTTP url/headers).\n * Preserves existing non-Cortex entries.\n */\nexport function configureClaudeDesktop(\n _serverUrl: string,\n apiKey: string,\n _mcps: string[]\n): string {\n const configPath = getClaudeDesktopConfigPath();\n const dir = dirname(configPath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Use cmd.exe wrapper when the target is Windows (native or WSL → Windows)\n const isWindowsTarget = getPlatform() === \"windows\" || isWSL();\n const cortexEntry = isWindowsTarget\n ? {\n command: \"cmd\",\n args: [\"/c\", \"npx\", \"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n }\n : {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n };\n\n // On Windows, Claude Desktop may watch the config file and overwrite it\n // immediately after we write. Retry up to 3 times with verification.\n const maxAttempts = isWindowsTarget ? 3 : 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n // Read existing config or start fresh\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* and cortex entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n }\n }\n\n // Add stdio proxy entry (Claude Desktop only supports command/args)\n // API key is read from ~/.cortex-mcp/credentials.json at runtime,\n // so we don't bake it into the env (avoids stale key issues on re-login).\n servers[\"cortex\"] = cortexEntry;\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n // On Windows, verify the write persisted (Claude Desktop may overwrite)\n if (isWindowsTarget) {\n // Brief delay to let any file-watcher settle\n const start = Date.now();\n while (Date.now() - start < 500) { /* busy wait */ }\n\n try {\n const verify = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const verifyServers = verify.mcpServers as Record<string, unknown> | undefined;\n if (verifyServers && \"cortex\" in verifyServers) {\n return configPath; // Verified — write persisted\n }\n } catch {\n // Verification read failed — retry\n }\n // Claude Desktop overwrote the file — retry\n continue;\n }\n\n // Non-Windows: single write is sufficient\n return configPath;\n }\n\n // All retries exhausted (Windows only)\n throw new Error(\n \"Claude Desktop is overwriting the config file. \" +\n \"Please close Claude Desktop completely (quit from the system tray), \" +\n \"then re-run setup.\"\n );\n}\n\n/**\n * Configure Claude Code by running `claude mcp add` commands.\n */\nexport function configureClaudeCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n\n const url = `${serverUrl}/mcp/${mcp.name}`;\n\n // Remove existing entry first (ignore errors if it doesn't exist)\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n } catch {\n // Entry didn't exist — fine\n }\n\n execSync(\n `claude mcp add --transport http ${mcp.serverName} ${url} -H \"x-api-key: ${apiKey}\"`,\n { stdio: \"pipe\" }\n );\n }\n}\n\n/**\n * Configure Cursor by writing HTTP MCP entries to its config file.\n * Cursor supports HTTP transport natively.\n */\nexport function configureCursor(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCursorConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n // Add new HTTP entries\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure VS Code by writing HTTP MCP entries to its config file.\n */\nexport function configureVSCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getVSCodeMcpConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Antigravity by writing HTTP MCP entries to its config file.\n */\nexport function configureAntigravity(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getAntigravityConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Codex (OpenAI) by writing streamable HTTP entries to ~/.codex/config.toml.\n * Codex uses TOML format. The config is shared by both CLI and IDE extension.\n */\nexport function configureCodex(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCodexConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Read existing config to preserve non-cortex entries\n let existingContent = \"\";\n if (existsSync(configPath)) {\n try {\n existingContent = readFileSync(configPath, \"utf-8\");\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Remove existing cortex-* MCP server blocks from TOML\n // Matches [mcp_servers.cortex-*] blocks until next section or EOF\n const cleaned = existingContent.replace(\n /\\[mcp_servers\\.cortex-[^\\]]*\\][^[]*(?=\\[|$)/g,\n \"\"\n ).trim();\n\n // Build new cortex MCP server entries in TOML format\n const tomlEntries: string[] = [];\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n tomlEntries.push(\n `[mcp_servers.${mcp.serverName}]\\n` +\n `url = \"${serverUrl}/mcp/${mcp.name}\"\\n` +\n `http_headers = { \"x-api-key\" = \"${apiKey}\" }`\n );\n }\n\n const newContent = (cleaned ? cleaned + \"\\n\\n\" : \"\") + tomlEntries.join(\"\\n\\n\") + \"\\n\";\n writeFileSync(configPath, newContent);\n}\n\n/**\n * Configure Perplexity Computer by generating per-MCP connector instructions.\n * Perplexity uses OAuth + Streamable HTTP — users add connectors manually via UI.\n */\nexport function configurePerplexity(\n serverUrl: string,\n _apiKey: string,\n mcps: string[]\n): string {\n const lines: string[] = [\n \"Add each MCP as a separate connector in Perplexity:\",\n \" Settings → Connectors → Add custom connector\",\n \"\",\n ];\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n lines.push(`── ${mcp.displayName} ──`);\n lines.push(` Name: Cortex ${mcp.displayName}`);\n lines.push(` MCP server URL: ${serverUrl}/mcp/${mcp.name}`);\n lines.push(` Authentication: OAuth`);\n lines.push(` Client ID: perplexity`);\n lines.push(` Client Secret: (leave empty)`);\n lines.push(` Transport: Streamable HTTP`);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Reset Perplexity — no-op since config is in Perplexity's UI.\n */\nexport function resetPerplexity(): boolean {\n return false;\n}\n\n/**\n * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)\n */\nexport function generateStdioSnippet(_apiKey: string): string {\n const isWindowsTarget = getPlatform() === \"windows\" || isWSL();\n const config = {\n mcpServers: {\n cortex: isWindowsTarget\n ? {\n command: \"cmd\",\n args: [\"/c\", \"npx\", \"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n }\n : {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n },\n },\n };\n return JSON.stringify(config, null, 2);\n}\n\n/**\n * Remove all cortex-* MCP entries from Claude Desktop config.\n */\nexport function resetClaudeDesktop(): boolean {\n const configPath = getClaudeDesktopConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Cursor config.\n */\nexport function resetCursor(): boolean {\n const configPath = getCursorConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Claude Code.\n */\nexport function resetClaudeCode(): boolean {\n let removed = false;\n for (const mcp of AVAILABLE_MCPS) {\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n removed = true;\n } catch {\n // Entry didn't exist\n }\n }\n return removed;\n}\n\n/**\n * Remove cortex MCP entries from VS Code config.\n */\nexport function resetVSCode(): boolean {\n const configPath = getVSCodeMcpConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Antigravity config.\n */\nexport function resetAntigravity(): boolean {\n const configPath = getAntigravityConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Codex config.\n */\nexport function resetCodex(): boolean {\n const configPath = getCodexConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const cleaned = content.replace(\n /\\[mcp_servers\\.cortex-[^\\]]*\\][^[]*(?=\\[|$)/g,\n \"\"\n ).trim();\n\n if (cleaned !== content.trim()) {\n writeFileSync(configPath, cleaned ? cleaned + \"\\n\" : \"\");\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Configure a specific client type.\n */\nexport function configureClient(\n clientType: ClientType,\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): string {\n switch (clientType) {\n case \"claude-desktop\": {\n const path = configureClaudeDesktop(serverUrl, apiKey, mcps);\n return `Claude Desktop configured (${path})`;\n }\n case \"claude-code\":\n configureClaudeCode(serverUrl, apiKey, mcps);\n return \"Claude Code configured\";\n case \"cursor\":\n configureCursor(serverUrl, apiKey, mcps);\n return \"Cursor configured\";\n case \"vscode\":\n configureVSCode(serverUrl, apiKey, mcps);\n return \"VS Code configured\";\n case \"antigravity\":\n configureAntigravity(serverUrl, apiKey, mcps);\n return \"Antigravity configured\";\n case \"codex\":\n configureCodex(serverUrl, apiKey, mcps);\n return \"Codex configured\";\n case \"perplexity\":\n return configurePerplexity(serverUrl, apiKey, mcps);\n case \"stdio\":\n return (\n \"Add this to your client config:\\n\\n\" +\n generateStdioSnippet(apiKey)\n );\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\nimport type { CortexMcpConfig } from \"./types.js\";\n\n/** Get the config directory path */\nexport function getConfigDir(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME);\n}\n\n/** Get the config file path */\nexport function getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\n/** Read the current config, or return null if none exists */\nexport function readConfig(): CortexMcpConfig | null {\n const path = getConfigPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexMcpConfig;\n } catch {\n return null;\n }\n}\n\n/** Write config to disk (creates directory with 700 permissions, file with 600) */\nexport function writeConfig(config: CortexMcpConfig): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getConfigPath();\n writeFileSync(path, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Create a default config with the given API key and MCPs */\nexport function createConfig(\n apiKey: string,\n mcps: string[]\n): CortexMcpConfig {\n return {\n version: 1,\n server: DEFAULT_SERVER_URL,\n apiKey,\n mcps,\n configuredClients: [],\n };\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME, DEFAULT_API_KEY } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\n\n/** Stored credentials from a successful login */\nexport interface CortexCredentials {\n apiKey: string;\n email: string;\n name?: string;\n authenticatedAt: string; // ISO 8601\n}\n\n/** Get the credentials file path (~/.cortex-mcp/credentials.json) */\nexport function getCredentialsPath(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME);\n}\n\n/** Read stored credentials, or return null if not logged in */\nexport function readCredentials(): CortexCredentials | null {\n const path = getCredentialsPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexCredentials;\n } catch {\n return null;\n }\n}\n\n/** Write credentials to disk (creates directory with 700, file with 600 permissions) */\nexport function writeCredentials(creds: CortexCredentials): void {\n const dir = join(getHomeDir(), CONFIG_DIR_NAME);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getCredentialsPath();\n writeFileSync(path, JSON.stringify(creds, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Delete stored credentials */\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\n/**\n * Get the effective API key to use.\n * Priority: CORTEX_API_KEY env var > stored credentials > config file > default shared key\n */\nexport function getEffectiveApiKey(): string {\n // 1. Environment variable (highest priority)\n const envKey = process.env.CORTEX_API_KEY;\n if (envKey) return envKey;\n\n // 2. Personal credentials from login\n const creds = readCredentials();\n if (creds?.apiKey) return creds.apiKey;\n\n // 3. Config file (set during setup)\n const config = readConfig();\n if (config?.apiKey) return config.apiKey;\n\n // 4. Shared default key (fallback)\n return DEFAULT_API_KEY;\n}\n","import type * as http from \"node:http\";\nimport { AVAILABLE_MCPS, DEFAULT_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { detectClients, configureClient } from \"../config/clients.js\";\nimport { createConfig, readConfig, writeConfig } from \"../config/storage.js\";\nimport {\n getEffectiveApiKey,\n readCredentials,\n writeCredentials,\n} from \"../auth/credentials.js\";\nimport type { ClientType } from \"../config/types.js\";\n\ninterface WizardOptions {\n serverUrl: string;\n}\n\n/** Mutable wizard state — lives for the lifetime of the local server. */\ninterface WizardState {\n apiKey: string;\n}\n\nlet wizardState: WizardState | null = null;\n\nfunction getState(): WizardState {\n if (!wizardState) {\n wizardState = { apiKey: getEffectiveApiKey() };\n }\n return wizardState;\n}\n\n/** Parse JSON body from an incoming request. */\nfunction parseBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n try {\n resolve(JSON.parse(Buffer.concat(chunks).toString()) as Record<string, unknown>);\n } catch {\n resolve({});\n }\n });\n req.on(\"error\", () => resolve({}));\n });\n}\n\n/** Send a JSON response. */\nfunction json(res: http.ServerResponse, data: unknown, status = 200): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n}\n\n/**\n * Handle an API route. Returns true if the route was handled.\n * `onComplete` is called when the wizard finishes (signals server shutdown).\n */\nexport async function handleApiRoute(\n path: string,\n searchParams: URLSearchParams,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n options: WizardOptions,\n onComplete: () => void,\n): Promise<boolean> {\n const method = req.method || \"GET\";\n\n // ── GET /api/state ──\n if (path === \"/api/state\" && method === \"GET\") {\n const creds = readCredentials();\n const config = readConfig();\n const clients = detectClients();\n\n // Fetch admin-controlled setup-enabled MCPs from Cortex backend\n let enabledMcps: string[] = [\"m365\"]; // fallback\n try {\n const setupResp = await fetch(\n `${options.serverUrl}/api/v1/mcps/setup-enabled`,\n );\n if (setupResp.ok) {\n const setupData = (await setupResp.json()) as { enabled?: string[] };\n if (Array.isArray(setupData.enabled)) {\n enabledMcps = setupData.enabled;\n }\n }\n } catch {\n // Use fallback\n }\n\n json(res, {\n credentials: creds ? { email: creds.email, name: creds.name } : null,\n config,\n apiKey: getState().apiKey,\n defaultMcps: [...DEFAULT_MCPS],\n enabledMcps,\n availableMcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n description: m.description,\n authMode: m.authMode,\n })),\n detectedClients: clients,\n });\n return true;\n }\n\n // ── POST /api/auth/login ──\n if (path === \"/api/auth/login\" && method === \"POST\") {\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/auth/employee/initiate`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n if (!resp.ok) {\n json(res, { error: `Server returned HTTP ${resp.status}` }, 502);\n return true;\n }\n const data = await resp.json();\n json(res, data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);\n }\n return true;\n }\n\n // ── GET /api/auth/poll?session=X ──\n if (path === \"/api/auth/poll\" && method === \"GET\") {\n const sessionId = searchParams.get(\"session\");\n if (!sessionId) {\n json(res, { error: \"Missing session parameter\" }, 400);\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/auth/employee/poll/${sessionId}`,\n );\n const result = (await resp.json()) as Record<string, unknown>;\n\n // If login completed, persist credentials and update API key\n if (result.status === \"completed\" && result.api_key) {\n const email =\n (result.employee_email as string) || (result.user_info as Record<string, string>)?.email || \"\";\n const name =\n (result.employee_name as string) || (result.user_info as Record<string, string>)?.name || \"\";\n\n writeCredentials({\n apiKey: result.api_key as string,\n email,\n name,\n authenticatedAt: new Date().toISOString(),\n });\n\n getState().apiKey = result.api_key as string;\n }\n\n json(res, result);\n } catch {\n json(res, { status: \"pending\" });\n }\n return true;\n }\n\n // ── GET /api/user-mcps ──\n // Fetches per-user available MCPs from the backend after login.\n // Returns the MCPs this specific user is allowed to set up.\n if (path === \"/api/user-mcps\" && method === \"GET\") {\n const apiKey = getState().apiKey;\n if (!apiKey) {\n json(res, { mcps: [] });\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/mcps/user-available`,\n { headers: { \"x-api-key\": apiKey } },\n );\n if (resp.ok) {\n const data = await resp.json();\n json(res, data);\n } else {\n // Fallback: return all MCPs with enabledMcps filtering\n json(res, {\n mcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n authMode: m.authMode,\n connected: false,\n })),\n });\n }\n } catch {\n json(res, {\n mcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n authMode: m.authMode,\n connected: false,\n })),\n });\n }\n return true;\n }\n\n // ── POST /api/clients/configure ──\n if (path === \"/api/clients/configure\" && method === \"POST\") {\n const body = await parseBody(req);\n const clients = (body.clients || []) as string[];\n const mcps = (body.mcps || [...DEFAULT_MCPS]) as string[];\n const apiKey = getState().apiKey;\n\n const results: Array<{ client: string; success: boolean; error?: string; message?: string }> = [];\n\n for (const clientType of clients) {\n try {\n const msg = configureClient(\n clientType as ClientType,\n options.serverUrl,\n apiKey,\n mcps,\n );\n results.push({ client: clientType, success: true, message: msg });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n results.push({ client: clientType, success: false, error: msg });\n }\n }\n\n json(res, { results });\n return true;\n }\n\n // ── GET /api/connections ──\n if (path === \"/api/connections\" && method === \"GET\") {\n const apiKey = getState().apiKey;\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connections`,\n { headers: { \"x-api-key\": apiKey } },\n );\n\n if (resp.ok) {\n const data = await resp.json();\n json(res, data);\n } else {\n json(res, { connections: [] });\n }\n } catch {\n json(res, { connections: [] });\n }\n return true;\n }\n\n // ── POST /api/oauth/connect ──\n if (path === \"/api/oauth/connect\" && method === \"POST\") {\n const body = await parseBody(req);\n const provider = body.provider as string;\n const apiKey = getState().apiKey;\n\n if (!provider) {\n json(res, { error: \"Missing provider\" }, 400);\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connect/${provider}/initiate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n },\n );\n\n if (!resp.ok) {\n if (resp.status === 401) {\n json(res, { error: \"Session expired. Please re-login.\" }, 401);\n return true;\n }\n const err = (await resp.json().catch(() => ({}))) as { detail?: string };\n json(res, { error: err.detail || `HTTP ${resp.status}` }, resp.status);\n return true;\n }\n\n const data = await resp.json();\n json(res, data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);\n }\n return true;\n }\n\n // ── GET /api/oauth/poll?session=X ──\n if (path === \"/api/oauth/poll\" && method === \"GET\") {\n const sessionId = searchParams.get(\"session\");\n if (!sessionId) {\n json(res, { error: \"Missing session parameter\" }, 400);\n return true;\n }\n\n const apiKey = getState().apiKey;\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connect/poll/${sessionId}`,\n { headers: { \"x-api-key\": apiKey } },\n );\n const result = await resp.json();\n json(res, result);\n } catch {\n json(res, { status: \"pending\" });\n }\n return true;\n }\n\n // ── POST /api/complete ──\n if (path === \"/api/complete\" && method === \"POST\") {\n const body = await parseBody(req);\n const mcps = (body.mcps || [...DEFAULT_MCPS]) as string[];\n const configuredClients = (body.configuredClients || []) as ClientType[];\n\n // Notify backend that setup is complete (enables company-default connections)\n const apiKey = getState().apiKey;\n try {\n await fetch(`${options.serverUrl}/api/v1/setup/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n });\n } catch {\n // Non-fatal: local setup still succeeds even if backend notification fails\n }\n\n // Write final config\n const config = createConfig(getState().apiKey, mcps);\n config.configuredClients = configuredClients;\n writeConfig(config);\n\n json(res, { success: true });\n\n // Signal completion after response is sent\n setTimeout(onComplete, 100);\n return true;\n }\n\n // Route not matched\n return false;\n}\n","import { DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { openBrowser } from \"../utils/browser.js\";\nimport { startWizardServer } from \"../wizard/server.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\n/**\n * Interactive setup wizard.\n * Launches a local HTTP server and opens the browser-based wizard UI.\n * All setup steps happen in the browser; the terminal just waits.\n */\nexport async function runSetup(): Promise<void> {\n log(\"\");\n log(\" Cortex MCP Setup\");\n log(\" Starting setup wizard...\");\n log(\"\");\n\n const { port, close, waitForCompletion } = await startWizardServer({\n serverUrl: DEFAULT_SERVER_URL,\n });\n\n const url = `http://localhost:${port}`;\n\n log(` Setup wizard running at ${url}`);\n log(\"\");\n\n await openBrowser(url);\n\n log(\" If the browser didn't open, visit:\");\n log(` ${url}`);\n log(\"\");\n log(\" Waiting for setup to complete (press Ctrl+C to cancel)...\");\n\n // Handle Ctrl+C gracefully\n const cleanup = async (): Promise<void> => {\n log(\"\\n Setup cancelled.\");\n await close();\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n // Wait for the browser wizard to signal completion\n await waitForCompletion();\n\n // Brief delay to let the HTTP response flush\n await new Promise((r) => setTimeout(r, 300));\n await close();\n\n log(\"\");\n log(\" Setup complete! Restart your AI clients to see the new tools.\");\n log(\"\");\n}\n","import { DEFAULT_MCPS, DEFAULT_SERVER_URL, MCP_NAMES } from \"../constants.js\";\nimport { getEffectiveApiKey } from \"../auth/credentials.js\";\nimport { createConfig, writeConfig } from \"../config/storage.js\";\nimport { configureClient } from \"../config/clients.js\";\nimport type { ClientType } from \"../config/types.js\";\n\ninterface ConfigureOptions {\n key?: string;\n mcps?: string;\n client: string;\n}\n\nconst VALID_CLIENTS: Record<string, ClientType> = {\n \"claude-desktop\": \"claude-desktop\",\n \"claude-code\": \"claude-code\",\n cursor: \"cursor\",\n codex: \"codex\",\n perplexity: \"perplexity\",\n stdio: \"stdio\",\n};\n\n/**\n * Non-interactive configure command.\n * Writes config to a specific client.\n */\nexport async function runConfigure(options: ConfigureOptions): Promise<void> {\n const key = options.key || getEffectiveApiKey();\n const { client } = options;\n\n // Validate client\n const clientType = VALID_CLIENTS[client];\n if (!clientType) {\n console.error(\n `Unknown client: ${client}. Valid options: ${Object.keys(VALID_CLIENTS).join(\", \")}`\n );\n process.exit(1);\n }\n\n // Parse MCPs\n let selectedMcps: string[];\n if (options.mcps) {\n selectedMcps = options.mcps\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => MCP_NAMES.includes(s));\n\n if (selectedMcps.length === 0) {\n console.error(\n `No valid MCPs. Available: ${MCP_NAMES.join(\", \")}`\n );\n process.exit(1);\n }\n } else {\n selectedMcps = [...DEFAULT_MCPS];\n }\n\n // Configure\n try {\n const result = configureClient(\n clientType,\n DEFAULT_SERVER_URL,\n key,\n selectedMcps\n );\n console.log(result);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to configure ${client}: ${msg}`);\n process.exit(1);\n }\n\n // Save config\n const config = createConfig(key, selectedMcps);\n config.configuredClients = [clientType];\n writeConfig(config);\n\n console.log(`Config saved to ~/.cortex-mcp/config.json`);\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CortexHttpClient } from \"./http-client.js\";\nimport { FILESYSTEM_TOOLS, handleLocalFilesystemTool } from \"./local-filesystem.js\";\n\ninterface StdioServerOptions {\n serverUrl: string;\n apiKey: string;\n /** Use \"cortex\" for composite endpoint, or a specific MCP name */\n endpoint?: string;\n}\n\n/** Tools that support local file_path → upload interception */\nconst UPLOAD_TOOLS = new Set([\"upload_file\", \"upload_file_to_sharepoint\"]);\n\n/** Max file size for inline base64 upload via JSON-RPC (3MB).\n * 3MB raw → 4MB base64 → safely within Vercel's ~4.5MB body limit. */\nconst INLINE_UPLOAD_MAX = 3 * 1024 * 1024;\n\n/**\n * Override upload tool schemas so the LLM only sees `file_path` (not `content`).\n * This forces the LLM to pass a local file path, which the proxy handles locally\n * — no base64 encoding by the LLM, no truncation, no corruption.\n */\nfunction overrideUploadToolSchema(\n tool: Record<string, unknown>\n): Record<string, unknown> {\n const name = tool.name as string;\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n\n if (baseName === \"upload_file\") {\n return {\n ...tool,\n description:\n \"Upload a local file to the user's OneDrive. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically. Works with any file size.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description:\n \"Absolute path to the local file to upload \" +\n \"(e.g. '/Users/name/Documents/report.xlsx')\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in OneDrive \" +\n \"(e.g. 'Documents/report.xlsx')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n if (baseName === \"upload_file_to_sharepoint\") {\n return {\n ...tool,\n description:\n \"Upload a local file to a SharePoint document library. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"site_id\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description: \"Absolute path to the local file to upload\",\n },\n site_id: {\n type: \"string\",\n description: \"SharePoint site ID\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in the site drive \" +\n \"(e.g. 'Shared Documents/report.pdf')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n return tool;\n}\n\n/**\n * Handle a file upload tool call locally by reading the file from disk.\n *\n * - Small files (≤3MB): base64-encode and forward as `content` param\n * - Large files (>3MB): get an upload session URL from backend, then\n * relay 2.5MB chunks through the backend's `upload_file_chunk` tool\n * (the backend has unrestricted internet; the proxy may be sandboxed)\n */\nasync function handleLocalFileUpload(\n cortex: CortexHttpClient,\n toolName: string,\n args: Record<string, unknown>\n): Promise<{ content: Array<{ type: string; text: string }>; isError: boolean }> {\n const filePath = args.file_path as string;\n\n // Validate file exists\n let fileSize: number;\n try {\n fileSize = statSync(filePath as string).size;\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: `File not found: ${filePath}`,\n }) }],\n isError: true,\n };\n }\n\n if (fileSize <= INLINE_UPLOAD_MAX) {\n // Small file: read, base64-encode, and forward via normal JSON-RPC\n const fileBuffer = readFileSync(filePath);\n const base64Content = fileBuffer.toString(\"base64\");\n\n const forwardArgs: Record<string, unknown> = { ...args, content: base64Content };\n delete forwardArgs.file_path;\n\n console.error(\n `[cortex-mcp] ${toolName}: reading local file (${(fileSize / 1024).toFixed(1)}KB), forwarding as base64`\n );\n\n const response = await cortex.callTool(toolName, forwardArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\", text: response.error.message }],\n isError: true,\n };\n }\n\n return response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n }\n\n // Large file: use upload session to upload directly to Microsoft Graph\n console.error(\n `[cortex-mcp] ${toolName}: large file (${(fileSize / 1024 / 1024).toFixed(1)}MB), using upload session`\n );\n\n // Step 1: Get pre-authenticated upload URL from backend\n // Preserve composite prefix (e.g. \"m365__\") so the backend routes correctly\n const prefix = toolName.includes(\"__\") ? toolName.split(\"__\")[0] + \"__\" : \"\";\n const sessionResponse = await cortex.callTool(`${prefix}create_upload_session`, {\n path: args.path,\n });\n\n if (sessionResponse.error) {\n return {\n content: [{ type: \"text\", text: sessionResponse.error.message }],\n isError: true,\n };\n }\n\n const sessionResult = sessionResponse.result as {\n content: Array<{ type: string; text: string }>;\n };\n\n let sessionData: { uploadUrl?: string };\n try {\n sessionData = JSON.parse(sessionResult.content[0].text);\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Failed to parse upload session response from backend\",\n }) }],\n isError: true,\n };\n }\n\n const uploadUrl = sessionData.uploadUrl;\n if (!uploadUrl) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Backend did not return an uploadUrl\",\n }) }],\n isError: true,\n };\n }\n\n // Step 2: Upload file in chunks through the Cortex backend.\n // We relay via the backend (which has unrestricted internet) instead of\n // uploading directly to Graph — the proxy may be in a sandboxed VM that\n // blocks outbound connections to SharePoint/Microsoft Graph.\n const fileBuffer = readFileSync(filePath);\n const chunkSize = 2.5 * 1024 * 1024; // 2.5MB → ~3.33MB base64 → within Vercel 4.5MB limit\n const total = fileBuffer.length;\n let driveItem: Record<string, unknown> = {};\n\n for (let start = 0; start < total; start += chunkSize) {\n const end = Math.min(start + chunkSize, total);\n const chunk = fileBuffer.subarray(start, end);\n const chunkBase64 = Buffer.from(chunk).toString(\"base64\");\n\n console.error(\n `[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total} via backend relay`\n );\n\n const chunkResponse = await cortex.callTool(`${prefix}upload_file_chunk`, {\n upload_url: uploadUrl,\n chunk: chunkBase64,\n range_start: start,\n range_end: end - 1,\n total_size: total,\n });\n\n if (chunkResponse.error) {\n return {\n content: [{ type: \"text\", text: chunkResponse.error.message }],\n isError: true,\n };\n }\n\n const chunkResult = chunkResponse.result as {\n content: Array<{ type: string; text: string }>;\n };\n\n let chunkData: Record<string, unknown>;\n try {\n chunkData = JSON.parse(chunkResult.content[0].text) as Record<string, unknown>;\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Failed to parse chunk upload response from backend\",\n }) }],\n isError: true,\n };\n }\n\n if (!chunkData.success) {\n return {\n content: [{ type: \"text\", text: JSON.stringify(chunkData) }],\n isError: true,\n };\n }\n\n // Final chunk (200/201) contains the DriveItem\n if (chunkData.status === 200 || chunkData.status === 201) {\n driveItem = (chunkData.data as Record<string, unknown>) ?? {};\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: true,\n file: driveItem,\n message: `Uploaded '${args.path}' (${(fileSize / 1024 / 1024).toFixed(1)}MB) via upload session`,\n }) }],\n isError: false,\n };\n}\n\n/**\n * Start a stdio MCP server that proxies all requests to the hosted Cortex server.\n * This is used by clients that only support stdio transport (e.g., OpenClaw).\n */\nexport async function startStdioServer(\n options: StdioServerOptions\n): Promise<void> {\n const { serverUrl, apiKey, endpoint = \"cortex\" } = options;\n\n const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);\n\n const server = new Server(\n { name: \"cortex-mcp\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: false } } }\n );\n\n // Lazy initialization — runs on first request, not at startup\n let initialized = false;\n\n async function ensureInitialized(): Promise<void> {\n if (initialized) return;\n try {\n console.error(\"[cortex-mcp] Initializing backend session...\");\n await cortex.initialize();\n console.error(\"[cortex-mcp] Backend session established.\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[cortex-mcp] Backend initialization failed: ${msg}`);\n // Continue anyway — tools/list and tools/call may still work without a session\n }\n initialized = true;\n }\n\n // Forward tools/list to Cortex\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n await ensureInitialized();\n const response = await cortex.listTools();\n\n if (response.error) {\n throw new Error(response.error.message);\n }\n\n const result = response.result as { tools: Record<string, unknown>[] };\n const tools = (result.tools || []).map(overrideUploadToolSchema);\n return { tools };\n });\n\n // Forward tools/call to Cortex (with local file upload interception)\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n await ensureInitialized();\n const { name, arguments: args } = request.params;\n const typedArgs = (args ?? {}) as Record<string, unknown>;\n\n // Intercept upload tools with file_path — handle locally\n // Composite endpoint prefixes tool names with \"{mcp}__\", so match on suffix\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {\n return handleLocalFileUpload(cortex, name, typedArgs);\n }\n\n // Intercept filesystem tools — execute locally instead of on remote server\n if (FILESYSTEM_TOOLS.has(baseName)) {\n return handleLocalFilesystemTool(name, baseName, typedArgs);\n }\n\n const response = await cortex.callTool(name, typedArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\" as const, text: response.error.message }],\n isError: true,\n };\n }\n\n const result = response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n\n return result;\n });\n\n // Connect to stdio transport immediately — do NOT block on backend init\n console.error(\"[cortex-mcp] Starting stdio server...\");\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[cortex-mcp] Stdio server connected.\");\n}\n","import { PROTOCOL_VERSION } from \"../constants.js\";\n\n/** JSON-RPC 2.0 request */\ninterface JsonRpcRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n id: string | number;\n}\n\n/** JSON-RPC 2.0 response */\ninterface JsonRpcResponse {\n jsonrpc: \"2.0\";\n result?: unknown;\n error?: { code: number; message: string };\n id: string | number | null;\n}\n\n/**\n * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.\n */\nexport class CortexHttpClient {\n private sessionId: string | null = null;\n private requestId = 0;\n\n constructor(\n private serverUrl: string,\n private apiKey: string,\n private endpoint: string = \"cortex\"\n ) {}\n\n /** Send an initialize request to establish a session */\n async initialize(): Promise<JsonRpcResponse> {\n const response = await this.sendRequest(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-proxy\", version: \"1.0.0\" },\n });\n\n // Send initialized notification (no id = notification, no response expected)\n await this.sendNotification(\"notifications/initialized\", {});\n\n return response;\n }\n\n /** List available tools */\n async listTools(): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/list\", {});\n }\n\n /** Call a tool */\n async callTool(\n name: string,\n args: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/call\", { name, arguments: args });\n }\n\n /** Send a JSON-RPC request and return the response */\n private async sendRequest(\n method: string,\n params: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n const id = String(++this.requestId);\n const body: JsonRpcRequest = { jsonrpc: \"2.0\", method, params, id };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n \"x-cortex-client\": \"cortex-mcp-stdio\",\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(60_000),\n });\n\n // Capture session ID from initialize\n const newSessionId = response.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n this.sessionId = newSessionId;\n }\n\n if (!response.ok) {\n const text = await response.text();\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: this.humanReadableError(response.status, text),\n },\n id,\n };\n }\n\n return (await response.json()) as JsonRpcResponse;\n }\n\n /** Send a JSON-RPC notification (no response expected) */\n private async sendNotification(\n method: string,\n params: Record<string, unknown>\n ): Promise<void> {\n const body = { jsonrpc: \"2.0\", method, params };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n \"x-cortex-client\": \"cortex-mcp-stdio\",\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Notifications are fire-and-forget\n }\n }\n\n /** Convert HTTP status codes to user-friendly messages */\n private humanReadableError(status: number, body: string): string {\n switch (status) {\n case 401:\n return \"Invalid API key. Check your key at https://aidentity.app/settings/keys\";\n case 403:\n return \"API key lacks MCP permissions. Create a new key with MCP access.\";\n case 404:\n return \"MCP endpoint not found. The server may be misconfigured.\";\n case 503:\n return \"MCP protocol is disabled on the server.\";\n default:\n return `Server error (${status}): ${body.slice(0, 200)}`;\n }\n }\n}\n","/**\n * Local filesystem tool handlers for cortex-mcp.\n *\n * Intercepts filesystem MCP tool calls and executes them locally on the\n * user's machine using Node.js fs APIs, instead of forwarding to the\n * remote Cortex server (which cannot access local files).\n *\n * Response shapes match the server-side FileSystem MCP exactly so that\n * consumers see identical results regardless of execution location.\n */\n\nimport {\n copyFileSync,\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n unlinkSync,\n writeFileSync,\n appendFileSync,\n renameSync,\n type Dirent,\n} from \"node:fs\";\nimport { basename, dirname, extname, join, relative, resolve } from \"node:path\";\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\n/** Tools handled locally instead of proxied to the server. */\nexport const FILESYSTEM_TOOLS = new Set([\n \"read_file\",\n \"read_file_lines\",\n \"write_file\",\n \"append_file\",\n \"create_directory\",\n \"delete\",\n \"move\",\n \"copy\",\n \"list_directory\",\n \"exists\",\n \"get_file_info\",\n \"search_files\",\n \"find_in_files\",\n \"get_tree\",\n]);\n\n/** Maximum bytes to read in a single read_file call (50 MB). */\nconst MAX_READ_SIZE = 50 * 1024 * 1024;\n\n/** Bytes to sample for binary detection. */\nconst BINARY_CHECK_SIZE = 8192;\n\n/** Binary file magic byte signatures. */\nconst BINARY_SIGNATURES: Buffer[] = [\n Buffer.from([0x89, 0x50, 0x4e, 0x47]), // PNG\n Buffer.from([0xff, 0xd8, 0xff]), // JPEG\n Buffer.from(\"GIF8\"), // GIF\n Buffer.from([0x50, 0x4b, 0x03, 0x04]), // ZIP / DOCX / XLSX\n Buffer.from(\"%PDF\"), // PDF\n Buffer.from([0x7f, 0x45, 0x4c, 0x46]), // ELF\n Buffer.from([0xfe, 0xed, 0xfa]), // Mach-O\n Buffer.from([0xcf, 0xfa, 0xed, 0xfe]), // Mach-O (reversed)\n Buffer.from(\"MZ\"), // Windows executable\n];\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype ToolResult = {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Resolve a path — absolute paths as-is, relative against cwd. */\nfunction resolvePath(p: string): string {\n return resolve(p);\n}\n\n/** Detect whether a file is binary by checking magic bytes and null bytes. */\nfunction isBinaryFile(filePath: string): boolean {\n try {\n const fd = readFileSync(filePath, { flag: \"r\" });\n const chunk = fd.subarray(0, BINARY_CHECK_SIZE);\n if (chunk.length === 0) return false;\n\n for (const sig of BINARY_SIGNATURES) {\n if (chunk.length >= sig.length && chunk.subarray(0, sig.length).equals(sig)) {\n return true;\n }\n }\n return chunk.includes(0);\n } catch {\n return false;\n }\n}\n\n/** Convert a simple glob pattern to a RegExp (supports *, ?, [...]). */\nfunction globToRegex(pattern: string): RegExp {\n let re = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const c = pattern[i];\n if (c === \"*\") {\n re += \".*\";\n } else if (c === \"?\") {\n re += \".\";\n } else if (c === \"[\") {\n const close = pattern.indexOf(\"]\", i + 1);\n if (close !== -1) {\n re += \"[\" + pattern.slice(i + 1, close) + \"]\";\n i = close;\n } else {\n re += \"\\\\[\";\n }\n } else if (\".+^${}()|\\\\\".includes(c)) {\n re += \"\\\\\" + c;\n } else {\n re += c;\n }\n }\n re += \"$\";\n return new RegExp(re);\n}\n\n/** Get file type string from stat/dirent. */\nfunction fileType(isDir: boolean, isSymlink: boolean): string {\n if (isSymlink) return \"symlink\";\n if (isDir) return \"directory\";\n return \"file\";\n}\n\n/** Count files and total bytes in a directory tree. */\nfunction countDirectoryContents(dirPath: string): { files: number; bytes: number } {\n let files = 0;\n let bytes = 0;\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n for (const entry of entries) {\n const full = join(dirPath, entry.name);\n if (entry.isDirectory()) {\n const sub = countDirectoryContents(full);\n files += sub.files;\n bytes += sub.bytes;\n } else {\n files += 1;\n try {\n bytes += statSync(full).size;\n } catch { /* skip */ }\n }\n }\n } catch { /* skip */ }\n return { files, bytes };\n}\n\n/** Build success result. */\nfunction ok(data: unknown): ToolResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data) }],\n isError: false,\n };\n}\n\n/** Build error result. */\nfunction err(message: string): ToolResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ success: false, error: message }) }],\n isError: true,\n };\n}\n\n// ─── Tool Handlers ───────────────────────────────────────────────────────────\n\nfunction handleReadFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const encoding = (args.encoding as string) || \"utf-8\";\n\n const stat = statSync(filePath);\n if (!stat.isFile()) throw new Error(\"Not a file.\");\n if (stat.size > MAX_READ_SIZE) {\n throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size (${MAX_READ_SIZE} bytes).`);\n }\n\n if (isBinaryFile(filePath)) {\n const raw = readFileSync(filePath);\n return ok({\n content: raw.toString(\"base64\"),\n is_binary: true,\n size: stat.size,\n encoding: \"base64\",\n path: filePath,\n });\n }\n\n const text = readFileSync(filePath, encoding as BufferEncoding);\n return ok({\n content: text,\n is_binary: false,\n size: stat.size,\n encoding,\n path: filePath,\n });\n}\n\nfunction handleReadFileLines(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const encoding = (args.encoding as string) || \"utf-8\";\n const startLine = (args.start_line as number) || 1;\n const endLine = args.end_line as number | undefined;\n\n const stat = statSync(filePath);\n if (!stat.isFile()) throw new Error(\"Not a file.\");\n if (stat.size > MAX_READ_SIZE) {\n throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size.`);\n }\n\n if (startLine < 1) throw new Error(\"start_line must be >= 1.\");\n if (endLine !== undefined && endLine < startLine) throw new Error(\"end_line must be >= start_line.\");\n\n const text = readFileSync(filePath, encoding as BufferEncoding);\n const allLines = text.split(/\\n/);\n const totalLines = allLines.length;\n\n const startIdx = startLine - 1;\n const endIdx = endLine !== undefined ? endLine : totalLines;\n const selected = allLines.slice(startIdx, endIdx);\n const actualEnd = Math.min(endIdx, totalLines);\n\n return ok({\n content: selected.join(\"\\n\"),\n start_line: startLine,\n end_line: actualEnd,\n total_lines: totalLines,\n size: stat.size,\n path: filePath,\n });\n}\n\nfunction handleWriteFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const content = args.content as string;\n const encoding = (args.encoding as string) || \"utf-8\";\n const isBinary = (args.is_binary as boolean) || false;\n const overwrite = args.overwrite !== false; // default true\n const createParents = (args.create_parents as boolean) || false;\n\n const created = !existsSync(filePath);\n\n if (!overwrite && !created) {\n throw new Error(\"File already exists.\");\n }\n\n if (createParents) {\n mkdirSync(dirname(filePath), { recursive: true });\n }\n\n let bytesWritten: number;\n let usedEncoding: string;\n\n if (isBinary) {\n const raw = Buffer.from(content, \"base64\");\n writeFileSync(filePath, raw);\n bytesWritten = raw.length;\n usedEncoding = \"base64\";\n } else {\n const encoded = Buffer.from(content, encoding as BufferEncoding);\n writeFileSync(filePath, content, encoding as BufferEncoding);\n bytesWritten = encoded.length;\n usedEncoding = encoding;\n }\n\n return ok({\n path: filePath,\n bytes_written: bytesWritten,\n created,\n encoding: usedEncoding,\n });\n}\n\nfunction handleAppendFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const content = args.content as string;\n const encoding = (args.encoding as string) || \"utf-8\";\n\n if (!existsSync(filePath)) throw new Error(\"File not found.\");\n\n const encoded = Buffer.from(content, encoding as BufferEncoding);\n appendFileSync(filePath, content, encoding as BufferEncoding);\n const newSize = statSync(filePath).size;\n\n return ok({\n path: filePath,\n bytes_written: encoded.length,\n new_size: newSize,\n });\n}\n\nfunction handleCreateDirectory(args: Record<string, unknown>): ToolResult {\n const dirPath = resolvePath(args.path as string);\n const parents = args.parents !== false; // default true\n\n if (existsSync(dirPath)) {\n return ok({ path: dirPath, created: false });\n }\n\n if (parents) {\n mkdirSync(dirPath, { recursive: true });\n } else {\n mkdirSync(dirPath);\n }\n\n return ok({ path: dirPath, created: true });\n}\n\nfunction handleDelete(args: Record<string, unknown>): ToolResult {\n const targetPath = resolvePath(args.path as string);\n const recursive = (args.recursive as boolean) || false;\n\n if (!existsSync(targetPath)) throw new Error(\"Path not found.\");\n\n const stat = statSync(targetPath, { throwIfNoEntry: false });\n if (!stat) throw new Error(\"Path not found.\");\n\n if (stat.isDirectory()) {\n const { files: filesDeleted, bytes: bytesFreed } = countDirectoryContents(targetPath);\n if (recursive) {\n rmSync(targetPath, { recursive: true });\n } else {\n // rmSync without recursive only removes empty dirs\n rmSync(targetPath);\n }\n return ok({\n path: targetPath,\n type: \"directory\",\n files_deleted: recursive ? filesDeleted : 0,\n bytes_freed: recursive ? bytesFreed : 0,\n });\n }\n\n const size = stat.size;\n unlinkSync(targetPath);\n return ok({\n path: targetPath,\n type: \"file\",\n files_deleted: 1,\n bytes_freed: size,\n });\n}\n\nfunction handleMove(args: Record<string, unknown>): ToolResult {\n const source = resolvePath(args.source as string);\n const destination = resolvePath(args.destination as string);\n const overwrite = (args.overwrite as boolean) || false;\n\n if (!existsSync(source)) throw new Error(\"Source path not found.\");\n if (existsSync(destination) && !overwrite) throw new Error(\"Destination already exists.\");\n\n const stat = statSync(source);\n const type = stat.isDirectory() ? \"directory\" : \"file\";\n const bytesMoved = stat.isDirectory()\n ? countDirectoryContents(source).bytes\n : stat.size;\n\n try {\n renameSync(source, destination);\n } catch (e: unknown) {\n // EXDEV: cross-device rename — fallback to copy + delete\n if ((e as NodeJS.ErrnoException).code === \"EXDEV\") {\n if (stat.isDirectory()) {\n cpSync(source, destination, { recursive: true });\n rmSync(source, { recursive: true });\n } else {\n copyFileSync(source, destination);\n unlinkSync(source);\n }\n } else {\n throw e;\n }\n }\n\n return ok({\n source,\n destination,\n type,\n bytes_moved: bytesMoved,\n });\n}\n\nfunction handleCopy(args: Record<string, unknown>): ToolResult {\n const source = resolvePath(args.source as string);\n const destination = resolvePath(args.destination as string);\n const overwrite = (args.overwrite as boolean) || false;\n\n if (!existsSync(source)) throw new Error(\"Source path not found.\");\n if (existsSync(destination) && !overwrite) throw new Error(\"Destination already exists.\");\n\n const stat = statSync(source);\n\n if (stat.isDirectory()) {\n if (existsSync(destination) && overwrite) {\n rmSync(destination, { recursive: true });\n }\n cpSync(source, destination, { recursive: true });\n const { bytes } = countDirectoryContents(destination);\n return ok({ source, destination, type: \"directory\", bytes_copied: bytes });\n }\n\n copyFileSync(source, destination);\n const bytesCopied = statSync(destination).size;\n return ok({ source, destination, type: \"file\", bytes_copied: bytesCopied });\n}\n\nfunction handleListDirectory(args: Record<string, unknown>): ToolResult {\n const dirPath = resolvePath(args.path as string);\n const recursive = (args.recursive as boolean) || false;\n const maxDepth = (args.max_depth as number) || 1;\n const pattern = args.pattern as string | undefined;\n const includeHidden = (args.include_hidden as boolean) || false;\n\n if (!existsSync(dirPath)) throw new Error(\"Directory not found.\");\n const stat = statSync(dirPath);\n if (!stat.isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const effectiveDepth = recursive ? maxDepth : 1;\n const patternRe = pattern ? globToRegex(pattern) : null;\n\n interface Entry {\n name: string;\n path: string;\n type: string;\n size: number;\n modified_at: string;\n is_hidden: boolean;\n }\n\n const entries: Entry[] = [];\n\n function scan(dir: string, depth: number): void {\n if (depth >= effectiveDepth) return;\n\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n dirents.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const d of dirents) {\n if (!includeHidden && d.name.startsWith(\".\")) continue;\n\n const fullPath = join(dir, d.name);\n const isDir = d.isDirectory();\n\n // Apply pattern filter\n if (patternRe && !patternRe.test(d.name)) {\n // Still recurse into directories even if name doesn't match\n if (isDir && effectiveDepth > 1) {\n scan(fullPath, depth + 1);\n }\n continue;\n }\n\n let size = 0;\n let mtime = new Date().toISOString();\n try {\n const s = statSync(fullPath);\n size = isDir ? 0 : s.size;\n mtime = new Date(s.mtimeMs).toISOString();\n } catch { /* skip */ }\n\n entries.push({\n name: d.name,\n path: relative(dirPath, fullPath),\n type: fileType(isDir, d.isSymbolicLink()),\n size,\n modified_at: mtime,\n is_hidden: d.name.startsWith(\".\"),\n });\n\n if (isDir && effectiveDepth > 1) {\n scan(fullPath, depth + 1);\n }\n }\n }\n\n scan(dirPath, 0);\n\n return ok({\n path: dirPath,\n entries,\n total_count: entries.length,\n });\n}\n\nfunction handleExists(args: Record<string, unknown>): ToolResult {\n const targetPath = resolvePath(args.path as string);\n const pathExists = existsSync(targetPath);\n\n let type: string | null = null;\n if (pathExists) {\n const stat = statSync(targetPath, { throwIfNoEntry: false });\n if (stat) {\n type = fileType(stat.isDirectory(), stat.isSymbolicLink());\n }\n }\n\n return ok({ exists: pathExists, path: targetPath, type });\n}\n\nfunction handleGetFileInfo(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n if (!existsSync(filePath)) throw new Error(\"Path not found.\");\n\n const stat = statSync(filePath, { throwIfNoEntry: false });\n if (!stat) throw new Error(\"Path not found.\");\n\n const isDir = stat.isDirectory();\n const isSymlink = stat.isSymbolicLink();\n const name = basename(filePath);\n const ext = isDir ? null : extname(name) || null;\n const binary = !isDir && !isSymlink ? isBinaryFile(filePath) : false;\n\n return ok({\n name,\n path: filePath,\n type: fileType(isDir, isSymlink),\n size: isDir ? 0 : stat.size,\n modified_at: new Date(stat.mtimeMs).toISOString(),\n created_at: stat.birthtimeMs ? new Date(stat.birthtimeMs).toISOString() : null,\n extension: ext,\n is_hidden: name.startsWith(\".\"),\n is_binary: binary,\n });\n}\n\nfunction handleSearchFiles(args: Record<string, unknown>): ToolResult {\n const searchPath = resolvePath(args.path as string);\n const pattern = (args.pattern as string) || \"*\";\n const recursive = args.recursive !== false; // default true\n const includeHidden = (args.include_hidden as boolean) || false;\n const maxResults = (args.max_results as number) || 1000;\n\n if (!existsSync(searchPath)) throw new Error(\"Directory not found.\");\n if (!statSync(searchPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const patternRe = globToRegex(pattern);\n const matches: string[] = [];\n let totalMatches = 0;\n\n function scan(dir: string): void {\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const d of dirents) {\n if (!includeHidden) {\n if (d.name.startsWith(\".\")) continue;\n }\n\n const fullPath = join(dir, d.name);\n\n if (d.isDirectory()) {\n if (recursive) scan(fullPath);\n continue;\n }\n\n if (patternRe.test(d.name)) {\n totalMatches++;\n if (matches.length < maxResults) {\n matches.push(relative(searchPath, fullPath));\n }\n }\n }\n }\n\n scan(searchPath);\n\n return ok({\n matches,\n total_matches: totalMatches,\n truncated: totalMatches > maxResults,\n search_path: searchPath,\n pattern,\n });\n}\n\nfunction handleFindInFiles(args: Record<string, unknown>): ToolResult {\n const searchPath = resolvePath(args.path as string);\n const query = args.query as string;\n const isRegex = (args.is_regex as boolean) || false;\n const caseSensitive = args.case_sensitive !== false; // default true\n const filePattern = args.pattern as string | undefined;\n const contextLines = (args.context_lines as number) || 0;\n const maxResults = (args.max_results as number) || 500;\n const includeHidden = (args.include_hidden as boolean) || false;\n\n if (!existsSync(searchPath)) throw new Error(\"Directory not found.\");\n if (!statSync(searchPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const flags = caseSensitive ? \"\" : \"i\";\n const re = isRegex\n ? new RegExp(query, flags)\n : new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), flags);\n\n const patternRe = filePattern ? globToRegex(filePattern) : null;\n\n interface Match {\n file: string;\n line: number;\n content: string;\n context_before: string[];\n context_after: string[];\n }\n\n const results: Match[] = [];\n let totalMatches = 0;\n let filesSearched = 0;\n let filesWithMatches = 0;\n\n function scan(dir: string): void {\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const d of dirents) {\n if (!includeHidden && d.name.startsWith(\".\")) continue;\n\n const fullPath = join(dir, d.name);\n\n if (d.isDirectory()) {\n scan(fullPath);\n continue;\n }\n\n if (patternRe && !patternRe.test(d.name)) continue;\n if (isBinaryFile(fullPath)) continue;\n\n filesSearched++;\n let fileHasMatch = false;\n\n let lines: string[];\n try {\n const text = readFileSync(fullPath, \"utf-8\");\n lines = text.split(\"\\n\");\n } catch {\n continue;\n }\n\n for (let i = 0; i < lines.length; i++) {\n if (re.test(lines[i])) {\n totalMatches++;\n fileHasMatch = true;\n\n if (results.length < maxResults) {\n const ctxStart = Math.max(0, i - contextLines);\n const ctxEnd = Math.min(lines.length, i + contextLines + 1);\n\n results.push({\n file: relative(searchPath, fullPath),\n line: i + 1,\n content: lines[i],\n context_before: lines.slice(ctxStart, i),\n context_after: lines.slice(i + 1, ctxEnd),\n });\n }\n }\n }\n\n if (fileHasMatch) filesWithMatches++;\n }\n }\n\n scan(searchPath);\n\n return ok({\n results,\n total_matches: totalMatches,\n files_searched: filesSearched,\n files_with_matches: filesWithMatches,\n truncated: totalMatches > maxResults,\n query,\n });\n}\n\nfunction handleGetTree(args: Record<string, unknown>): ToolResult {\n const rootPath = resolvePath(args.path as string);\n const maxDepth = (args.max_depth as number) || 3;\n const includeHidden = (args.include_hidden as boolean) || false;\n const maxEntries = (args.max_entries as number) || 500;\n\n if (!existsSync(rootPath)) throw new Error(\"Directory not found.\");\n if (!statSync(rootPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const lines: string[] = [];\n let entryCount = 0;\n let truncated = false;\n\n const rootName = basename(rootPath) || rootPath;\n lines.push(rootName + \"/\");\n\n function buildTree(dirPath: string, prefix: string, depth: number): void {\n if (truncated || depth >= maxDepth) return;\n\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dirPath, { withFileTypes: true });\n } catch {\n return;\n }\n\n if (!includeHidden) {\n dirents = dirents.filter((d) => !d.name.startsWith(\".\"));\n }\n dirents.sort((a, b) => a.name.localeCompare(b.name));\n\n for (let i = 0; i < dirents.length; i++) {\n if (truncated) return;\n\n entryCount++;\n if (entryCount > maxEntries) {\n truncated = true;\n lines.push(prefix + \"... (truncated)\");\n return;\n }\n\n const d = dirents[i];\n const isLast = i === dirents.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n const childPrefix = prefix + (isLast ? \" \" : \"│ \");\n const fullPath = join(dirPath, d.name);\n\n if (d.isDirectory()) {\n lines.push(prefix + connector + d.name + \"/\");\n buildTree(fullPath, childPrefix, depth + 1);\n } else {\n lines.push(prefix + connector + d.name);\n }\n }\n }\n\n buildTree(rootPath, \"\", 0);\n\n return ok({\n tree: lines.join(\"\\n\"),\n path: rootPath,\n entry_count: entryCount,\n truncated,\n max_depth: maxDepth,\n });\n}\n\n// ─── Dispatcher ──────────────────────────────────────────────────────────────\n\nconst HANDLERS: Record<string, (args: Record<string, unknown>) => ToolResult> = {\n read_file: handleReadFile,\n read_file_lines: handleReadFileLines,\n write_file: handleWriteFile,\n append_file: handleAppendFile,\n create_directory: handleCreateDirectory,\n delete: handleDelete,\n move: handleMove,\n copy: handleCopy,\n list_directory: handleListDirectory,\n exists: handleExists,\n get_file_info: handleGetFileInfo,\n search_files: handleSearchFiles,\n find_in_files: handleFindInFiles,\n get_tree: handleGetTree,\n};\n\n/**\n * Handle a filesystem tool call locally on the user's machine.\n *\n * @param _toolName Full tool name (may include composite prefix like \"filesystem__\")\n * @param baseName Base tool name without prefix (e.g. \"read_file\")\n * @param args Tool arguments from the MCP client\n */\nexport function handleLocalFilesystemTool(\n _toolName: string,\n baseName: string,\n args: Record<string, unknown>,\n): ToolResult {\n console.error(`[cortex-mcp] filesystem.${baseName}: executing locally`);\n\n const handler = HANDLERS[baseName];\n if (!handler) {\n return err(`Unknown filesystem tool: ${baseName}`);\n }\n\n try {\n return handler(args);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n console.error(`[cortex-mcp] filesystem.${baseName} error: ${message}`);\n return err(message);\n }\n}\n","import { DEFAULT_API_KEY, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { startStdioServer } from \"../proxy/stdio-server.js\";\nimport { getEffectiveApiKey } from \"../auth/credentials.js\";\n\n/**\n * Start the stdio proxy server.\n * Reads API key from CORTEX_API_KEY env var or ~/.cortex-mcp/config.json.\n */\nexport async function runServe(): Promise<void> {\n // Resolve API key: env var > credentials > config file > default\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n const apiKey = getEffectiveApiKey();\n\n if (apiKey === DEFAULT_API_KEY) {\n process.stderr.write(\n \"Warning: Using shared API key. Personal MCPs (M365, Slack, etc.) \" +\n \"will not have access to your account.\\n\" +\n \"Run: npx @danainnovations/cortex-mcp@latest login\\n\\n\"\n );\n }\n\n // Start the stdio server (this blocks until the client disconnects)\n await startStdioServer({ serverUrl, apiKey });\n}\n","import { AVAILABLE_MCPS } from \"../constants.js\";\nimport { getConfigPath, readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\n/**\n * Show current Cortex MCP configuration.\n */\nexport function runStatus(): void {\n const config = readConfig();\n\n if (!config) {\n console.log(\"No configuration found. Run: npx @danainnovations/cortex-mcp setup\");\n return;\n }\n\n const maskedKey =\n config.apiKey.slice(0, 8) + \"****\" + config.apiKey.slice(-4);\n\n const creds = readCredentials();\n\n console.log(\"\");\n console.log(\" Cortex MCP Status\");\n console.log(\" -----------------\");\n\n if (creds) {\n console.log(` User: ${creds.name || creds.email}`);\n console.log(` Email: ${creds.email}`);\n } else {\n console.log(\" User: not logged in (using shared key)\");\n }\n\n console.log(` Config: ${getConfigPath()}`);\n console.log(` Server: ${config.server}`);\n console.log(` API Key: ${maskedKey}`);\n console.log(\"\");\n console.log(\" Enabled MCPs:\");\n\n for (const mcpName of config.mcps) {\n const mcp = AVAILABLE_MCPS.find((m) => m.name === mcpName);\n const display = mcp ? mcp.displayName : mcpName;\n console.log(` ${display.padEnd(15)} ${config.server}/mcp/${mcpName}`);\n }\n\n console.log(\"\");\n console.log(\" Configured Clients:\");\n\n if (config.configuredClients.length === 0) {\n console.log(\" (none)\");\n } else {\n for (const client of config.configuredClients) {\n console.log(` ${client}`);\n }\n }\n\n console.log(\"\");\n}\n","import { existsSync, unlinkSync, rmdirSync } from \"node:fs\";\nimport { resetClaudeDesktop, resetClaudeCode, resetCursor, resetCodex } from \"../config/clients.js\";\nimport { getConfigDir, getConfigPath } from \"../config/storage.js\";\n\n/**\n * Remove all Cortex MCP entries from configured clients and delete local config.\n */\nexport function runReset(): void {\n console.log(\"\");\n console.log(\" Resetting Cortex MCP configuration...\");\n\n // Reset Claude Desktop\n const desktopReset = resetClaudeDesktop();\n console.log(\n ` Claude Desktop: ${desktopReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Claude Code\n const codeReset = resetClaudeCode();\n console.log(\n ` Claude Code: ${codeReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Cursor\n const cursorReset = resetCursor();\n console.log(\n ` Cursor: ${cursorReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Codex\n const codexReset = resetCodex();\n console.log(\n ` Codex: ${codexReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Delete local config\n const configPath = getConfigPath();\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n console.log(` Config file: removed`);\n }\n\n const configDir = getConfigDir();\n try {\n rmdirSync(configDir);\n } catch {\n // Directory not empty or doesn't exist — fine\n }\n\n console.log(\"\");\n console.log(\" Done. Restart your AI clients to apply changes.\");\n console.log(\" Note: Login credentials are preserved. Run 'cortex-mcp logout' to sign out.\");\n console.log(\"\");\n}\n","import * as readline from \"node:readline\";\nimport { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig, writeConfig } from \"../config/storage.js\";\nimport { configureClient } from \"../config/clients.js\";\nimport { readCredentials, writeCredentials } from \"../auth/credentials.js\";\nimport { openBrowser } from \"../utils/browser.js\";\nimport { connectProvider } from \"./connect.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport type LoginResult =\n | { status: \"success\"; apiKey: string; email: string; name?: string }\n | { status: \"needs_hint\"; ssoEmail: string }\n | { status: \"failed\" };\n\n/**\n * Core SSO login flow: initiate -> browser -> poll.\n * Reusable by both the `login` command and the setup wizard.\n *\n * Optionally accepts an emailHint for users whose Okta email\n * differs from their company directory email.\n */\nexport async function loginWithSSO(\n serverUrl: string,\n emailHint?: string,\n): Promise<LoginResult> {\n // Initiate auth flow\n let sessionId: string;\n let authUrl: string;\n let expiresIn: number;\n\n try {\n const body: Record<string, string> = {};\n if (emailHint) {\n body.email_hint = emailHint;\n }\n\n const resp = await fetch(`${serverUrl}/api/v1/auth/employee/initiate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n log(` Error: Failed to start authentication (HTTP ${resp.status})`);\n return { status: \"failed\" };\n }\n\n const data = (await resp.json()) as {\n session_id: string;\n auth_url: string;\n expires_in: number;\n };\n sessionId = data.session_id;\n authUrl = data.auth_url;\n expiresIn = data.expires_in;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(` Error: Could not reach Cortex server at ${serverUrl}`);\n log(` ${msg}`);\n return { status: \"failed\" };\n }\n\n // Open browser\n log(\" Opening browser for Okta SSO login...\");\n await openBrowser(authUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${authUrl}`);\n log(\"\");\n\n // Poll for completion\n const deadline = Date.now() + expiresIn * 1000;\n process.stderr.write(\" Waiting for authentication\");\n\n while (Date.now() < deadline) {\n await sleep(3000);\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/auth/employee/poll/${sessionId}`\n );\n const result = (await resp.json()) as {\n status: string;\n api_key?: string;\n employee_name?: string;\n employee_email?: string;\n sso_email?: string;\n user_info?: { email?: string; name?: string };\n error_message?: string;\n };\n\n if (result.status === \"completed\") {\n process.stderr.write(\"\\n\\n\");\n\n const email =\n result.employee_email || result.user_info?.email || \"unknown\";\n const name = result.employee_name || result.user_info?.name || \"\";\n const apiKey = result.api_key || \"\";\n\n return { status: \"success\", apiKey, email, name: name || undefined };\n }\n\n if (result.status === \"expired\") {\n process.stderr.write(\"\\n\");\n log(\" Authentication session expired. Please try again.\");\n return { status: \"failed\" };\n }\n\n if (result.status === \"error\") {\n process.stderr.write(\"\\n\");\n\n // If AD verification failed and no hint was provided, signal for retry\n if (\n result.error_message === \"not_verified_employee\" &&\n !emailHint\n ) {\n return {\n status: \"needs_hint\",\n ssoEmail: result.sso_email || \"unknown\",\n };\n }\n\n log(\n ` Authentication failed: ${result.error_message || \"unknown error\"}`\n );\n return { status: \"failed\" };\n }\n\n // Still pending\n process.stderr.write(\".\");\n } catch {\n process.stderr.write(\"!\");\n }\n }\n\n process.stderr.write(\"\\n\");\n log(\" Timeout waiting for authentication. Please try again.\");\n return { status: \"failed\" };\n}\n\n/**\n * Show MCP connection status and auto-connect personal MCPs (e.g., Asana).\n */\nexport async function showConnectionsAndAutoConnect(\n apiKey: string,\n serverUrl: string,\n): Promise<void> {\n // Fetch existing connections\n let existingConnections: Array<{\n mcp_name: string;\n account_email: string | null;\n is_company_default: boolean;\n }> = [];\n\n try {\n const resp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": apiKey },\n });\n if (resp.ok) {\n const data = (await resp.json()) as {\n connections: typeof existingConnections;\n };\n existingConnections = data.connections || [];\n } else {\n log(\n ` Warning: Could not fetch current MCP connections (HTTP ${resp.status}).`\n );\n if (resp.status === 401 || resp.status === 403) {\n log(\" Warning: Authentication may be expired. Run 'cortex-mcp login' to refresh.\");\n }\n }\n } catch {\n log(\" Warning: Could not fetch current MCP connections due to a network error.\");\n }\n\n // Show MCP connections table\n log(\"\");\n log(\" MCP Connections\");\n log(\" \" + \"\\u2500\".repeat(45));\n\n const personalMcps: Array<{ name: string; displayName: string }> = [];\n\n for (const mcp of AVAILABLE_MCPS) {\n const conn = existingConnections.find((c) => c.mcp_name === mcp.name);\n\n if (mcp.authMode === \"company\") {\n log(` ${mcp.displayName.padEnd(15)} company default`);\n } else {\n // Personal auth required\n if (conn && !conn.is_company_default && conn.account_email) {\n log(` ${mcp.displayName.padEnd(15)} ${conn.account_email}`);\n } else {\n log(\n ` ${mcp.displayName.padEnd(15)} not connected (personal account required)`\n );\n personalMcps.push({ name: mcp.name, displayName: mcp.displayName });\n }\n }\n }\n\n log(\"\");\n\n // Auto-connect unconnected personal MCPs\n if (personalMcps.length === 0) {\n log(\" All accounts connected!\");\n log(\"\");\n return;\n }\n\n log(\" The following MCPs require a personal account to access your data.\");\n log(\" Skip any you don't have an account for — you can always connect later.\\n\");\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const ask = (question: string): Promise<string> =>\n new Promise((resolve) => {\n rl.question(question, (answer) => resolve(answer.trim().toLowerCase()));\n });\n\n for (const mcp of personalMcps) {\n const answer = await ask(\n ` Connect ${mcp.displayName}? (Y/n/s to skip all): `\n );\n\n if (answer === \"s\") {\n log(\" Skipping remaining account connections.\");\n log(\" You can connect anytime with: cortex-mcp connect <provider>\\n\");\n break;\n }\n\n if (answer === \"n\") {\n log(` Skipped. Connect later with: cortex-mcp connect ${mcp.name}`);\n log(\"\");\n continue;\n }\n\n // Y or Enter — proceed with OAuth\n log(\"\");\n const success = await connectProvider(\n mcp.name,\n mcp.displayName,\n apiKey,\n serverUrl,\n \"installUrl\" in mcp ? (mcp.installUrl as string) : undefined,\n );\n\n if (!success) {\n log(\n ` You can connect later with: cortex-mcp connect ${mcp.name}`\n );\n }\n log(\"\");\n }\n\n rl.close();\n}\n\n/**\n * Prompt the user for their company email when Okta email doesn't match AD.\n */\nasync function promptForEmailHint(ssoEmail: string): Promise<string | null> {\n log(\"\");\n log(` Your SSO email (${ssoEmail}) was not found in the employee directory.`);\n log(\n \" This can happen if your Okta account uses a different email than your company directory.\"\n );\n log(\"\");\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(\" Enter your company email address: \", (a) =>\n resolve(a.trim())\n );\n });\n rl.close();\n\n if (!answer || !answer.includes(\"@\")) {\n log(\" Invalid email. Run 'cortex-mcp login --email you@company.com' to try again.\");\n return null;\n }\n\n return answer;\n}\n\n/**\n * Log in via Okta SSO to get a personal API key.\n * Opens the browser, polls for completion, stores credentials,\n * auto-reconfigures any previously set up AI clients,\n * and prompts to connect personal MCP accounts (e.g., Asana).\n *\n * @param emailHint Optional company email (via --email flag) when Okta email differs from AD\n */\nexport async function runLogin(emailHint?: string): Promise<void> {\n // 1. Check if already logged in\n const existing = readCredentials();\n if (existing) {\n log(\"\");\n log(` Already logged in as ${existing.name || existing.email}`);\n log(\" Run 'cortex-mcp logout' first to switch accounts.\");\n log(\"\");\n return;\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n log(\"\");\n log(\" Cortex MCP Login\");\n log(\" Sign in with your company Okta SSO account\");\n log(\"\");\n\n // 2. Run SSO flow\n let result = await loginWithSSO(serverUrl, emailHint);\n\n // 3. Handle email hint retry if AD verification failed\n if (result.status === \"needs_hint\") {\n let hintEmail: string | null;\n\n if (emailHint) {\n // --email was already provided but SSO email still didn't match\n // (this shouldn't happen since hint is passed on first try, but handle it)\n hintEmail = emailHint;\n } else {\n hintEmail = await promptForEmailHint(result.ssoEmail);\n }\n\n if (!hintEmail) {\n process.exit(1);\n }\n\n log(\"\");\n log(\" Retrying with your company email. You'll need to complete SSO again.\");\n log(\"\");\n\n result = await loginWithSSO(serverUrl, hintEmail);\n\n if (result.status === \"needs_hint\") {\n log(\"\");\n log(\" The provided email was also not found in the employee directory.\");\n log(\" Please verify your email and contact IT if the issue persists.\");\n process.exit(1);\n }\n }\n\n if (result.status !== \"success\") {\n process.exit(1);\n }\n\n const { apiKey, email, name } = result;\n\n // 3. Save credentials\n writeCredentials({\n apiKey,\n email,\n name,\n authenticatedAt: new Date().toISOString(),\n });\n\n log(` Authenticated as ${name || email}${name ? ` (${email})` : \"\"}`);\n log(\" Personal API key saved.\");\n\n // 4. Auto-reconfigure existing clients and update saved config\n const existingConfig = readConfig();\n if (existingConfig) {\n // Always update the stored API key\n existingConfig.apiKey = apiKey;\n\n // Re-configure clients if any were previously set up\n if (existingConfig.configuredClients?.length > 0) {\n log(\"\");\n log(\" Updating configured clients:\");\n\n for (const clientType of existingConfig.configuredClients) {\n try {\n configureClient(\n clientType,\n existingConfig.server || DEFAULT_SERVER_URL,\n apiKey,\n existingConfig.mcps\n );\n log(` ${clientType}: updated`);\n } catch {\n log(` ${clientType}: failed to update`);\n }\n }\n }\n\n writeConfig(existingConfig);\n }\n\n // 5. Show MCP connection status and auto-connect personal MCPs\n await showConnectionsAndAutoConnect(apiKey, serverUrl);\n\n // 6. Notify backend that setup/login is complete (enables company-default connections)\n try {\n await fetch(`${serverUrl}/api/v1/setup/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n });\n } catch {\n // Non-fatal\n }\n\n log(\" Done! Restart your AI clients to apply.\");\n log(\"\");\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\nimport { openBrowser } from \"../utils/browser.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction waitForEnter(): Promise<void> {\n return new Promise((resolve) => {\n process.stdin.setRawMode?.(false);\n process.stdin.resume();\n process.stdin.once(\"data\", () => {\n process.stdin.pause();\n resolve();\n });\n });\n}\n\n/**\n * Core OAuth connect flow: initiate -> browser -> poll.\n * Reusable by both the `connect` command and the post-login auto-connect.\n *\n * Returns true on success, false on failure/timeout.\n */\nexport async function connectProvider(\n providerName: string,\n displayName: string,\n apiKey: string,\n serverUrl: string,\n installUrl?: string,\n): Promise<boolean> {\n // 1. Initiate OAuth flow\n let sessionId: string;\n let authUrl: string;\n let expiresIn: number;\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connect/${providerName}/initiate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n }\n );\n\n if (!resp.ok) {\n if (resp.status === 401) {\n log(\" Error: Your session has expired. Run 'cortex-mcp login' to re-authenticate.\");\n return false;\n }\n\n const err = (await resp.json().catch(() => ({}))) as { detail?: string };\n log(` Error: ${err.detail || `HTTP ${resp.status}`}`);\n return false;\n }\n\n const data = (await resp.json()) as {\n session_id: string;\n authorization_url: string;\n expires_in: number;\n };\n sessionId = data.session_id;\n authUrl = data.authorization_url;\n expiresIn = data.expires_in;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(` Error: Could not reach Cortex server at ${serverUrl}`);\n log(` ${msg}`);\n return false;\n }\n\n // 2. App installation (Monday.com requires install before authorize)\n if (installUrl) {\n log(` First, install the ${displayName} app on your account...`);\n await openBrowser(installUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${installUrl}`);\n log(\"\");\n log(\" Press Enter once the app is installed...\");\n await waitForEnter();\n log(\"\");\n }\n\n // 3. Open browser\n log(` Opening browser for ${displayName} authorization...`);\n await openBrowser(authUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${authUrl}`);\n log(\"\");\n\n // 4. Poll for completion\n const deadline = Date.now() + expiresIn * 1000;\n process.stderr.write(\" Waiting for authorization\");\n\n while (Date.now() < deadline) {\n await sleep(3000);\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connect/poll/${sessionId}`,\n {\n headers: {\n \"x-api-key\": apiKey,\n },\n }\n );\n const result = (await resp.json()) as {\n status: string;\n account_email?: string;\n account_name?: string;\n error_message?: string;\n };\n\n if (result.status === \"completed\") {\n process.stderr.write(\"\\n\\n\");\n const who = result.account_email || \"unknown\";\n log(` Connected as ${who}`);\n log(` ${displayName} tools will now use your personal account.`);\n log(\"\");\n return true;\n }\n\n if (result.status === \"expired\") {\n process.stderr.write(\"\\n\");\n log(\" Authorization session expired. Please try again.\");\n return false;\n }\n\n if (result.status === \"error\") {\n process.stderr.write(\"\\n\");\n log(\n ` Connection failed: ${result.error_message || \"unknown error\"}`\n );\n return false;\n }\n\n // Still pending\n process.stderr.write(\".\");\n } catch {\n process.stderr.write(\"!\");\n }\n }\n\n process.stderr.write(\"\\n\");\n log(\" Timeout waiting for authorization. Please try again.\");\n return false;\n}\n\n/**\n * Connect a personal OAuth account (e.g., Asana) via browser-based flow.\n * CLI command handler — validates credentials and delegates to connectProvider().\n */\nexport async function runConnect(provider: string): Promise<void> {\n // 1. Verify user is logged in\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" You must be logged in first.\");\n log(\" Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n // 2. Validate provider\n const mcp = AVAILABLE_MCPS.find(\n (m) => m.name === provider || m.displayName.toLowerCase() === provider.toLowerCase()\n );\n if (!mcp) {\n log(\"\");\n log(` Unknown provider: ${provider}`);\n log(` Available: ${AVAILABLE_MCPS.map((m) => m.name).join(\", \")}`);\n log(\"\");\n process.exit(1);\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n log(\"\");\n log(` Cortex MCP — Connect ${mcp.displayName}`);\n log(` Link your personal ${mcp.displayName} account`);\n log(\"\");\n\n const success = await connectProvider(\n mcp.name,\n mcp.displayName,\n creds.apiKey,\n serverUrl,\n \"installUrl\" in mcp ? (mcp.installUrl as string) : undefined,\n );\n\n if (!success) {\n process.exit(1);\n }\n}\n","import { deleteCredentials, readCredentials } from \"../auth/credentials.js\";\n\nexport function runLogout(): void {\n const creds = readCredentials();\n\n if (!creds) {\n console.log(\"\");\n console.log(\" Not currently logged in.\");\n console.log(\"\");\n return;\n }\n\n deleteCredentials();\n\n console.log(\"\");\n console.log(` Logged out (was: ${creds.email})`);\n console.log(\" MCP tools will use the shared default key.\");\n console.log(\" Run 'cortex-mcp login' to sign in again.\");\n console.log(\"\");\n}\n","import { readCredentials } from \"../auth/credentials.js\";\n\nexport function runWhoami(): void {\n const creds = readCredentials();\n\n if (!creds) {\n console.log(\"\");\n console.log(\" Not logged in. Using shared default API key.\");\n console.log(\" Run 'cortex-mcp login' to authenticate.\");\n console.log(\"\");\n return;\n }\n\n console.log(\"\");\n console.log(\" Cortex MCP Account\");\n console.log(\" ------------------\");\n if (creds.name) {\n console.log(` Name: ${creds.name}`);\n }\n console.log(` Email: ${creds.email}`);\n console.log(` Authenticated: ${creds.authenticatedAt}`);\n console.log(\n ` API Key: ${creds.apiKey.slice(0, 8)}****${creds.apiKey.slice(-4)}`\n );\n console.log(\"\");\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\ninterface MCPConnection {\n id: string;\n mcp_name: string;\n provider: string;\n account_email: string | null;\n status: string;\n is_company_default: boolean;\n}\n\n/**\n * List OAuth connections for the authenticated user.\n */\nexport async function runConnections(): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" Not logged in. Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n try {\n const resp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": creds.apiKey },\n });\n\n if (!resp.ok) {\n log(\"\");\n log(\" Error fetching connections.\");\n log(\"\");\n process.exit(1);\n }\n\n const data = (await resp.json()) as { connections: MCPConnection[] };\n const connections = data.connections || [];\n\n log(\"\");\n log(\" Cortex MCP Connections\");\n log(\" ----------------------\");\n\n // Show status for each available MCP\n for (const mcp of AVAILABLE_MCPS) {\n const conn = connections.find((c) => c.mcp_name === mcp.name);\n\n let statusText: string;\n if (!conn) {\n statusText = \"not connected\";\n } else if (conn.is_company_default) {\n statusText = \"(company default)\";\n } else {\n statusText = conn.account_email || \"connected\";\n }\n\n log(` ${mcp.displayName.padEnd(15)} ${statusText}`);\n }\n\n log(\"\");\n log(\" Connect an account: cortex-mcp connect <provider>\");\n log(\"\");\n } catch {\n log(\"\");\n log(\" Error: Could not reach Cortex server.\");\n log(\"\");\n process.exit(1);\n }\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\ninterface MCPConnection {\n id: string;\n mcp_name: string;\n provider: string;\n account_email: string | null;\n status: string;\n is_company_default: boolean;\n}\n\n/**\n * Disconnect a personal OAuth connection for a provider.\n */\nexport async function runDisconnect(provider: string): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" Not logged in. Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n // Validate provider\n const mcp = AVAILABLE_MCPS.find(\n (m) => m.name === provider || m.displayName.toLowerCase() === provider.toLowerCase()\n );\n if (!mcp) {\n log(\"\");\n log(` Unknown provider: ${provider}`);\n log(` Available: ${AVAILABLE_MCPS.map((m) => m.name).join(\", \")}`);\n log(\"\");\n process.exit(1);\n }\n\n const providerName = mcp.name;\n const displayName = mcp.displayName;\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n try {\n // List connections to find the user's personal connection\n const listResp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": creds.apiKey },\n });\n\n if (!listResp.ok) {\n log(\"\");\n log(\" Error fetching connections.\");\n log(\"\");\n process.exit(1);\n }\n\n const listData = (await listResp.json()) as {\n connections: MCPConnection[];\n };\n const match = (listData.connections || []).find(\n (c) => c.mcp_name === providerName && !c.is_company_default\n );\n\n if (!match) {\n log(\"\");\n log(` No personal connection found for ${displayName}.`);\n log(\"\");\n process.exit(1);\n }\n\n // Disconnect\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connections/${match.id}/disconnect`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": creds.apiKey },\n }\n );\n\n if (resp.ok) {\n log(\"\");\n log(` Disconnected from ${displayName}.`);\n log(\n ` ${displayName} tools will fall back to the company default token.`\n );\n log(\"\");\n } else {\n log(\"\");\n log(\" Error disconnecting. Please try again.\");\n log(\"\");\n process.exit(1);\n }\n } catch {\n log(\"\");\n log(\" Error: Could not reach Cortex server.\");\n log(\"\");\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,YAAY,UAAU;;;ACKf,SAAS,gBAAwB;AACtC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAq4CT;;;AC34CA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgB;AAWzB,SAAS,eAAe;AACxB,SAAS,YAAY;AAMd,SAAS,gBAAkC;AAChD,QAAM,UAA4B,CAAC;AAGnC,QAAM,cAAc,2BAA2B;AAC/C,QAAM,aAAa,QAAQ,WAAW;AACtC,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,WAAW,UAAU;AAAA,EACjC,CAAC;AAGD,MAAI,qBAAqB;AACzB,MAAI;AACF,aAAS,gBAAgB,EAAE,OAAO,OAAO,CAAC;AAC1C,yBAAqB;AAAA,EACvB,QAAQ;AAAA,EAER;AACA,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AAGD,QAAM,aAAa,oBAAoB;AACvC,QAAM,YAAY,QAAQ,UAAU;AACpC,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,WAAW,SAAS;AAAA,EAChC,CAAC;AAGD,QAAM,OAAO,QAAQ;AACrB,QAAM,aAAa,uBAAuB;AAC1C,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,WAAW,KAAK,MAAM,SAAS,CAAC;AAAA,EAC5C,CAAC;AAGD,QAAM,kBAAkB,yBAAyB;AACjD,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,WAAW,KAAK,MAAM,cAAc,CAAC;AAAA,EACjD,CAAC;AAGD,QAAM,YAAY,mBAAmB;AACrC,MAAI,gBAAgB,WAAW,KAAK,MAAM,QAAQ,CAAC;AACnD,MAAI,CAAC,eAAe;AAClB,QAAI;AACF,eAAS,eAAe,EAAE,OAAO,OAAO,CAAC;AACzC,sBAAgB;AAAA,IAClB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AAGD,QAAM,gBAAgB,KAAK,MAAM,WAAW,cAAc,mBAAmB;AAC7E,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU,YAAY,MAAM,WAAW,WAAW,aAAa;AAAA,EACjE,CAAC;AAGD,UAAQ,KAAK;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AAED,SAAO;AACT;AAKO,SAAS,iBACd,WACA,QACA,MAC8B;AAC9B,QAAM,UAAwC,CAAC;AAE/C,aAAW,OAAO,gBAAgB;AAChC,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,EAAG;AAC9B,YAAQ,IAAI,UAAU,IAAI;AAAA,MACxB,KAAK,GAAG,SAAS,QAAQ,IAAI,IAAI;AAAA,MACjC,SAAS,EAAE,aAAa,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,uBACd,YACA,QACA,OACQ;AACR,QAAM,aAAa,2BAA2B;AAC9C,QAAM,MAAM,QAAQ,UAAU;AAG9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAGA,QAAM,kBAAkB,YAAY,MAAM,aAAa,MAAM;AAC7D,QAAM,cAAc,kBAChB;AAAA,IACE,SAAS;AAAA,IACT,MAAM,CAAC,MAAM,OAAO,MAAM,sCAAsC,OAAO;AAAA,EACzE,IACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM,CAAC,MAAM,sCAAsC,OAAO;AAAA,EAC5D;AAIJ,QAAM,cAAc,kBAAkB,IAAI;AAE1C,WAAS,UAAU,GAAG,UAAU,aAAa,WAAW;AAEtD,QAAI,SAAkC,CAAC;AACvC,QAAI,WAAW,UAAU,GAAG;AAC1B,UAAI;AACF,iBAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAAA,MACvD,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,aAAO,aAAa,CAAC;AAAA,IACvB;AAEA,UAAM,UAAU,OAAO;AAGvB,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,IAAI,WAAW,SAAS,KAAK,QAAQ,UAAU;AACjD,eAAO,QAAQ,GAAG;AAAA,MACpB;AAAA,IACF;AAKA,YAAQ,QAAQ,IAAI;AAEpB,kBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAGhE,QAAI,iBAAiB;AAEnB,YAAM,QAAQ,KAAK,IAAI;AACvB,aAAO,KAAK,IAAI,IAAI,QAAQ,KAAK;AAAA,MAAkB;AAEnD,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,cAAM,gBAAgB,OAAO;AAC7B,YAAI,iBAAiB,YAAY,eAAe;AAC9C,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAAA,MAER;AAEA;AAAA,IACF;AAGA,WAAO;AAAA,EACT;AAGA,QAAM,IAAI;AAAA,IACR;AAAA,EAGF;AACF;AAKO,SAAS,oBACd,WACA,QACA,MACM;AACN,aAAW,OAAO,gBAAgB;AAChC,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,EAAG;AAE9B,UAAM,MAAM,GAAG,SAAS,QAAQ,IAAI,IAAI;AAGxC,QAAI;AACF,eAAS,qBAAqB,IAAI,UAAU,IAAI,EAAE,OAAO,OAAO,CAAC;AAAA,IACnE,QAAQ;AAAA,IAER;AAEA;AAAA,MACE,mCAAmC,IAAI,UAAU,IAAI,GAAG,mBAAmB,MAAM;AAAA,MACjF,EAAE,OAAO,OAAO;AAAA,IAClB;AAAA,EACF;AACF;AAMO,SAAS,gBACd,WACA,QACA,MACM;AACN,QAAM,aAAa,oBAAoB;AACvC,QAAM,MAAM,QAAQ,UAAU;AAE9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,MAAI,SAAkC,CAAC;AACvC,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,eAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,WAAO,aAAa,CAAC;AAAA,EACvB;AAEA,QAAM,UAAU,OAAO;AAGvB,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,aAAO,QAAQ,GAAG;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,UAAU,iBAAiB,WAAW,QAAQ,IAAI;AACxD,SAAO,OAAO,SAAS,OAAO;AAE9B,gBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAClE;AAKO,SAAS,gBACd,WACA,QACA,MACM;AACN,QAAM,aAAa,uBAAuB;AAC1C,QAAM,MAAM,QAAQ,UAAU;AAE9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,MAAI,SAAkC,CAAC;AACvC,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,eAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,WAAO,aAAa,CAAC;AAAA,EACvB;AAEA,QAAM,UAAU,OAAO;AAEvB,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,aAAO,QAAQ,GAAG;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,UAAU,iBAAiB,WAAW,QAAQ,IAAI;AACxD,SAAO,OAAO,SAAS,OAAO;AAE9B,gBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAClE;AAKO,SAAS,qBACd,WACA,QACA,MACM;AACN,QAAM,aAAa,yBAAyB;AAC5C,QAAM,MAAM,QAAQ,UAAU;AAE9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,MAAI,SAAkC,CAAC;AACvC,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,eAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,WAAO,aAAa,CAAC;AAAA,EACvB;AAEA,QAAM,UAAU,OAAO;AAEvB,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,aAAO,QAAQ,GAAG;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,UAAU,iBAAiB,WAAW,QAAQ,IAAI;AACxD,SAAO,OAAO,SAAS,OAAO;AAE9B,gBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAClE;AAMO,SAAS,eACd,WACA,QACA,MACM;AACN,QAAM,aAAa,mBAAmB;AACtC,QAAM,MAAM,QAAQ,UAAU;AAE9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAGA,MAAI,kBAAkB;AACtB,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,wBAAkB,aAAa,YAAY,OAAO;AAAA,IACpD,QAAQ;AAAA,IAER;AAAA,EACF;AAIA,QAAM,UAAU,gBAAgB;AAAA,IAC9B;AAAA,IACA;AAAA,EACF,EAAE,KAAK;AAGP,QAAM,cAAwB,CAAC;AAC/B,aAAW,OAAO,gBAAgB;AAChC,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,EAAG;AAC9B,gBAAY;AAAA,MACV,gBAAgB,IAAI,UAAU;AAAA,SACpB,SAAS,QAAQ,IAAI,IAAI;AAAA,kCACA,MAAM;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,cAAc,UAAU,UAAU,SAAS,MAAM,YAAY,KAAK,MAAM,IAAI;AAClF,gBAAc,YAAY,UAAU;AACtC;AAMO,SAAS,oBACd,WACA,SACA,MACQ;AACR,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,OAAO,gBAAgB;AAChC,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,EAAG;AAC9B,UAAM,KAAK,gBAAM,IAAI,WAAW,eAAK;AACrC,UAAM,KAAK,kBAAkB,IAAI,WAAW,EAAE;AAC9C,UAAM,KAAK,qBAAqB,SAAS,QAAQ,IAAI,IAAI,EAAE;AAC3D,UAAM,KAAK,yBAAyB;AACpC,UAAM,KAAK,yBAAyB;AACpC,UAAM,KAAK,gCAAgC;AAC3C,UAAM,KAAK,8BAA8B;AACzC,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAYO,SAAS,qBAAqB,SAAyB;AAC5D,QAAM,kBAAkB,YAAY,MAAM,aAAa,MAAM;AAC7D,QAAM,SAAS;AAAA,IACb,YAAY;AAAA,MACV,QAAQ,kBACJ;AAAA,QACE,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,OAAO,MAAM,sCAAsC,OAAO;AAAA,MACzE,IACA;AAAA,QACE,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,sCAAsC,OAAO;AAAA,MAC5D;AAAA,IACN;AAAA,EACF;AACA,SAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;AAKO,SAAS,qBAA8B;AAC5C,QAAM,aAAa,2BAA2B;AAC9C,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,QAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,UAAM,UAAU,OAAO;AACvB,QAAI,UAAU;AACd,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,IAAI,WAAW,SAAS,KAAK,QAAQ,UAAU;AACjD,eAAO,QAAQ,GAAG;AAClB,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,QAAI,SAAS;AACX,oBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAAA,IAClE;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,cAAuB;AACrC,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,QAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,UAAM,UAAU,OAAO;AACvB,QAAI,UAAU;AACd,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,IAAI,WAAW,SAAS,KAAK,QAAQ,UAAU;AACjD,eAAO,QAAQ,GAAG;AAClB,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,QAAI,SAAS;AACX,oBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAAA,IAClE;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,kBAA2B;AACzC,MAAI,UAAU;AACd,aAAW,OAAO,gBAAgB;AAChC,QAAI;AACF,eAAS,qBAAqB,IAAI,UAAU,IAAI,EAAE,OAAO,OAAO,CAAC;AACjE,gBAAU;AAAA,IACZ,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AA+DO,SAAS,aAAsB;AACpC,QAAM,aAAa,mBAAmB;AACtC,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACF,UAAM,UAAU,aAAa,YAAY,OAAO;AAChD,UAAM,UAAU,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,IACF,EAAE,KAAK;AAEP,QAAI,YAAY,QAAQ,KAAK,GAAG;AAC9B,oBAAc,YAAY,UAAU,UAAU,OAAO,EAAE;AACvD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,gBACd,YACA,WACA,QACA,MACQ;AACR,UAAQ,YAAY;AAAA,IAClB,KAAK,kBAAkB;AACrB,YAAM,OAAO,uBAAuB,WAAW,QAAQ,IAAI;AAC3D,aAAO,8BAA8B,IAAI;AAAA,IAC3C;AAAA,IACA,KAAK;AACH,0BAAoB,WAAW,QAAQ,IAAI;AAC3C,aAAO;AAAA,IACT,KAAK;AACH,sBAAgB,WAAW,QAAQ,IAAI;AACvC,aAAO;AAAA,IACT,KAAK;AACH,sBAAgB,WAAW,QAAQ,IAAI;AACvC,aAAO;AAAA,IACT,KAAK;AACH,2BAAqB,WAAW,QAAQ,IAAI;AAC5C,aAAO;AAAA,IACT,KAAK;AACH,qBAAe,WAAW,QAAQ,IAAI;AACtC,aAAO;AAAA,IACT,KAAK;AACH,aAAO,oBAAoB,WAAW,QAAQ,IAAI;AAAA,IACpD,KAAK;AACH,aACE,wCACA,qBAAqB,MAAM;AAAA,EAEjC;AACF;;;ACjrBA,SAAS,cAAAA,aAAY,aAAAC,YAAW,gBAAAC,eAAc,iBAAAC,sBAAqB;AACnE,SAAS,QAAAC,aAAY;AAMd,SAAS,eAAuB;AACrC,SAAOC,MAAK,WAAW,GAAG,eAAe;AAC3C;AAGO,SAAS,gBAAwB;AACtC,SAAOA,MAAK,aAAa,GAAG,gBAAgB;AAC9C;AAGO,SAAS,aAAqC;AACnD,QAAM,OAAO,cAAc;AAC3B,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAE9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,OAAO;AACtC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,YAAY,QAA+B;AACzD,QAAM,MAAM,aAAa;AACzB,MAAI,CAACD,YAAW,GAAG,GAAG;AACpB,IAAAE,WAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AAEA,QAAM,OAAO,cAAc;AAC3B,EAAAC,eAAc,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM;AAAA,IAC1D,MAAM;AAAA,EACR,CAAC;AACH;AAGO,SAAS,aACd,QACA,MACiB;AACjB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,mBAAmB,CAAC;AAAA,EACtB;AACF;;;ACtDA,SAAS,cAAAC,aAAY,aAAAC,YAAW,gBAAAC,eAAc,YAAY,iBAAAC,sBAAqB;AAC/E,SAAS,QAAAC,aAAY;AAcd,SAAS,qBAA6B;AAC3C,SAAOC,MAAK,WAAW,GAAG,iBAAiB,qBAAqB;AAClE;AAGO,SAAS,kBAA4C;AAC1D,QAAM,OAAO,mBAAmB;AAChC,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAE9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,OAAO;AACtC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAiB,OAAgC;AAC/D,QAAM,MAAMF,MAAK,WAAW,GAAG,eAAe;AAC9C,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,IAAAE,WAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AAEA,QAAM,OAAO,mBAAmB;AAChC,EAAAC,eAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,MAAM;AAAA,IACzD,MAAM;AAAA,EACR,CAAC;AACH;AAGO,SAAS,oBAA0B;AACxC,QAAM,OAAO,mBAAmB;AAChC,MAAIH,YAAW,IAAI,GAAG;AACpB,eAAW,IAAI;AAAA,EACjB;AACF;AAMO,SAAS,qBAA6B;AAE3C,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,OAAQ,QAAO;AAGnB,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,OAAO,OAAQ,QAAO,MAAM;AAGhC,QAAM,SAAS,WAAW;AAC1B,MAAI,QAAQ,OAAQ,QAAO,OAAO;AAGlC,SAAO;AACT;;;ACpDA,IAAI,cAAkC;AAEtC,SAAS,WAAwB;AAC/B,MAAI,CAAC,aAAa;AAChB,kBAAc,EAAE,QAAQ,mBAAmB,EAAE;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,SAAS,UAAU,KAA6D;AAC9E,SAAO,IAAI,QAAQ,CAACI,aAAY;AAC9B,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM;AAClB,UAAI;AACF,QAAAA,SAAQ,KAAK,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC,CAA4B;AAAA,MACjF,QAAQ;AACN,QAAAA,SAAQ,CAAC,CAAC;AAAA,MACZ;AAAA,IACF,CAAC;AACD,QAAI,GAAG,SAAS,MAAMA,SAAQ,CAAC,CAAC,CAAC;AAAA,EACnC,CAAC;AACH;AAGA,SAAS,KAAK,KAA0B,MAAe,SAAS,KAAW;AACzE,MAAI,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAC5D,MAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAC9B;AAMA,eAAsB,eACpB,MACA,cACA,KACA,KACA,SACA,YACkB;AAClB,QAAM,SAAS,IAAI,UAAU;AAG7B,MAAI,SAAS,gBAAgB,WAAW,OAAO;AAC7C,UAAM,QAAQ,gBAAgB;AAC9B,UAAM,SAAS,WAAW;AAC1B,UAAM,UAAU,cAAc;AAG9B,QAAI,cAAwB,CAAC,MAAM;AACnC,QAAI;AACF,YAAM,YAAY,MAAM;AAAA,QACtB,GAAG,QAAQ,SAAS;AAAA,MACtB;AACA,UAAI,UAAU,IAAI;AAChB,cAAM,YAAa,MAAM,UAAU,KAAK;AACxC,YAAI,MAAM,QAAQ,UAAU,OAAO,GAAG;AACpC,wBAAc,UAAU;AAAA,QAC1B;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,SAAK,KAAK;AAAA,MACR,aAAa,QAAQ,EAAE,OAAO,MAAM,OAAO,MAAM,MAAM,KAAK,IAAI;AAAA,MAChE;AAAA,MACA,QAAQ,SAAS,EAAE;AAAA,MACnB,aAAa,CAAC,GAAG,YAAY;AAAA,MAC7B;AAAA,MACA,eAAe,eAAe,IAAI,CAAC,OAAO;AAAA,QACxC,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,aAAa,EAAE;AAAA,QACf,UAAU,EAAE;AAAA,MACd,EAAE;AAAA,MACF,iBAAiB;AAAA,IACnB,CAAC;AACD,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,qBAAqB,WAAW,QAAQ;AACnD,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS;AAAA,QACpB;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAChD;AAAA,MACF;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,aAAK,KAAK,EAAE,OAAO,wBAAwB,KAAK,MAAM,GAAG,GAAG,GAAG;AAC/D,eAAO;AAAA,MACT;AACA,YAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,WAAK,KAAK,IAAI;AAAA,IAChB,SAASC,MAAK;AACZ,YAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,WAAK,KAAK,EAAE,OAAO,kCAAkC,GAAG,GAAG,GAAG,GAAG;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,oBAAoB,WAAW,OAAO;AACjD,UAAM,YAAY,aAAa,IAAI,SAAS;AAC5C,QAAI,CAAC,WAAW;AACd,WAAK,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS,8BAA8B,SAAS;AAAA,MAC7D;AACA,YAAM,SAAU,MAAM,KAAK,KAAK;AAGhC,UAAI,OAAO,WAAW,eAAe,OAAO,SAAS;AACnD,cAAM,QACH,OAAO,kBAA8B,OAAO,WAAsC,SAAS;AAC9F,cAAM,OACH,OAAO,iBAA6B,OAAO,WAAsC,QAAQ;AAE5F,yBAAiB;AAAA,UACf,QAAQ,OAAO;AAAA,UACf;AAAA,UACA;AAAA,UACA,kBAAiB,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC1C,CAAC;AAED,iBAAS,EAAE,SAAS,OAAO;AAAA,MAC7B;AAEA,WAAK,KAAK,MAAM;AAAA,IAClB,QAAQ;AACN,WAAK,KAAK,EAAE,QAAQ,UAAU,CAAC;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAKA,MAAI,SAAS,oBAAoB,WAAW,OAAO;AACjD,UAAM,SAAS,SAAS,EAAE;AAC1B,QAAI,CAAC,QAAQ;AACX,WAAK,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;AACtB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS;AAAA,QACpB,EAAE,SAAS,EAAE,aAAa,OAAO,EAAE;AAAA,MACrC;AACA,UAAI,KAAK,IAAI;AACX,cAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,aAAK,KAAK,IAAI;AAAA,MAChB,OAAO;AAEL,aAAK,KAAK;AAAA,UACR,MAAM,eAAe,IAAI,CAAC,OAAO;AAAA,YAC/B,MAAM,EAAE;AAAA,YACR,aAAa,EAAE;AAAA,YACf,UAAU,EAAE;AAAA,YACZ,WAAW;AAAA,UACb,EAAE;AAAA,QACJ,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN,WAAK,KAAK;AAAA,QACR,MAAM,eAAe,IAAI,CAAC,OAAO;AAAA,UAC/B,MAAM,EAAE;AAAA,UACR,aAAa,EAAE;AAAA,UACf,UAAU,EAAE;AAAA,UACZ,WAAW;AAAA,QACb,EAAE;AAAA,MACJ,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,4BAA4B,WAAW,QAAQ;AAC1D,UAAM,OAAO,MAAM,UAAU,GAAG;AAChC,UAAM,UAAW,KAAK,WAAW,CAAC;AAClC,UAAM,OAAQ,KAAK,QAAQ,CAAC,GAAG,YAAY;AAC3C,UAAM,SAAS,SAAS,EAAE;AAE1B,UAAM,UAAyF,CAAC;AAEhG,eAAW,cAAc,SAAS;AAChC,UAAI;AACF,cAAM,MAAM;AAAA,UACV;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,QACF;AACA,gBAAQ,KAAK,EAAE,QAAQ,YAAY,SAAS,MAAM,SAAS,IAAI,CAAC;AAAA,MAClE,SAASA,MAAK;AACZ,cAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,gBAAQ,KAAK,EAAE,QAAQ,YAAY,SAAS,OAAO,OAAO,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,SAAK,KAAK,EAAE,QAAQ,CAAC;AACrB,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,sBAAsB,WAAW,OAAO;AACnD,UAAM,SAAS,SAAS,EAAE;AAE1B,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS;AAAA,QACpB,EAAE,SAAS,EAAE,aAAa,OAAO,EAAE;AAAA,MACrC;AAEA,UAAI,KAAK,IAAI;AACX,cAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,aAAK,KAAK,IAAI;AAAA,MAChB,OAAO;AACL,aAAK,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;AAAA,MAC/B;AAAA,IACF,QAAQ;AACN,WAAK,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,wBAAwB,WAAW,QAAQ;AACtD,UAAM,OAAO,MAAM,UAAU,GAAG;AAChC,UAAM,WAAW,KAAK;AACtB,UAAM,SAAS,SAAS,EAAE;AAE1B,QAAI,CAAC,UAAU;AACb,WAAK,KAAK,EAAE,OAAO,mBAAmB,GAAG,GAAG;AAC5C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS,yBAAyB,QAAQ;AAAA,QACrD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,KAAK,IAAI;AACZ,YAAI,KAAK,WAAW,KAAK;AACvB,eAAK,KAAK,EAAE,OAAO,oCAAoC,GAAG,GAAG;AAC7D,iBAAO;AAAA,QACT;AACA,cAAMA,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,aAAK,KAAK,EAAE,OAAOA,KAAI,UAAU,QAAQ,KAAK,MAAM,GAAG,GAAG,KAAK,MAAM;AACrE,eAAO;AAAA,MACT;AAEA,YAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,WAAK,KAAK,IAAI;AAAA,IAChB,SAASA,MAAK;AACZ,YAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,WAAK,KAAK,EAAE,OAAO,kCAAkC,GAAG,GAAG,GAAG,GAAG;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,qBAAqB,WAAW,OAAO;AAClD,UAAM,YAAY,aAAa,IAAI,SAAS;AAC5C,QAAI,CAAC,WAAW;AACd,WAAK,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,SAAS,EAAE;AAE1B,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,QAAQ,SAAS,8BAA8B,SAAS;AAAA,QAC3D,EAAE,SAAS,EAAE,aAAa,OAAO,EAAE;AAAA,MACrC;AACA,YAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,WAAK,KAAK,MAAM;AAAA,IAClB,QAAQ;AACN,WAAK,KAAK,EAAE,QAAQ,UAAU,CAAC;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,mBAAmB,WAAW,QAAQ;AACjD,UAAM,OAAO,MAAM,UAAU,GAAG;AAChC,UAAM,OAAQ,KAAK,QAAQ,CAAC,GAAG,YAAY;AAC3C,UAAM,oBAAqB,KAAK,qBAAqB,CAAC;AAGtD,UAAM,SAAS,SAAS,EAAE;AAC1B,QAAI;AACF,YAAM,MAAM,GAAG,QAAQ,SAAS,0BAA0B;AAAA,QACxD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAGA,UAAM,SAAS,aAAa,SAAS,EAAE,QAAQ,IAAI;AACnD,WAAO,oBAAoB;AAC3B,gBAAY,MAAM;AAElB,SAAK,KAAK,EAAE,SAAS,KAAK,CAAC;AAG3B,eAAW,YAAY,GAAG;AAC1B,WAAO;AAAA,EACT;AAGA,SAAO;AACT;;;ALjVO,SAAS,kBACd,SACuB;AACvB,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,QAAI;AACJ,UAAM,oBAAoB,IAAI,QAAc,CAAC,MAAM;AACjD,0BAAoB;AAAA,IACtB,CAAC;AAED,UAAM,aAAa,MAAY;AAC7B,wBAAkB;AAAA,IACpB;AAEA,UAAM,SAAc,kBAAa,OAAO,KAAK,QAAQ;AACnD,YAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB;AACtD,YAAM,OAAO,IAAI;AAGjB,UAAI,KAAK,WAAW,OAAO,GAAG;AAC5B,YAAI;AACF,gBAAM,UAAU,MAAM;AAAA,YACpB;AAAA,YACA,IAAI;AAAA,YACJ;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,cAAI,CAAC,SAAS;AACZ,gBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,YAAY,CAAC,CAAC;AAAA,UAChD;AAAA,QACF,SAASC,MAAK;AACZ,gBAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,QACxC;AACA;AAAA,MACF;AAGA,UAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,UAAI,IAAI,cAAc,CAAC;AAAA,IACzB,CAAC;AAED,WAAO,OAAO,GAAG,aAAa,MAAM;AAClC,YAAM,OAAO,OAAO,QAAQ;AAC5B,MAAAD,SAAQ;AAAA,QACN,MAAM,KAAK;AAAA,QACX,OAAO,MACL,IAAI,QAAc,CAAC,MAAM;AACvB,iBAAO,MAAM,MAAM,EAAE,CAAC;AAAA,QACxB,CAAC;AAAA,QACH,mBAAmB,MAAM;AAAA,MAC3B,CAAC;AAAA,IACH,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AAAA,EAC3B,CAAC;AACH;;;AM1EA,SAAS,IAAI,KAAmB;AAC9B,UAAQ,OAAO,MAAM,MAAM,IAAI;AACjC;AAOA,eAAsB,WAA0B;AAC9C,MAAI,EAAE;AACN,MAAI,oBAAoB;AACxB,MAAI,4BAA4B;AAChC,MAAI,EAAE;AAEN,QAAM,EAAE,MAAM,OAAO,kBAAkB,IAAI,MAAM,kBAAkB;AAAA,IACjE,WAAW;AAAA,EACb,CAAC;AAED,QAAM,MAAM,oBAAoB,IAAI;AAEpC,MAAI,6BAA6B,GAAG,EAAE;AACtC,MAAI,EAAE;AAEN,QAAM,YAAY,GAAG;AAErB,MAAI,sCAAsC;AAC1C,MAAI,KAAK,GAAG,EAAE;AACd,MAAI,EAAE;AACN,MAAI,6DAA6D;AAGjE,QAAM,UAAU,YAA2B;AACzC,QAAI,sBAAsB;AAC1B,UAAM,MAAM;AACZ,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,WAAW,OAAO;AAG7B,QAAM,kBAAkB;AAGxB,QAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC3C,QAAM,MAAM;AAEZ,MAAI,EAAE;AACN,MAAI,iEAAiE;AACrE,MAAI,EAAE;AACR;;;AC1CA,IAAM,gBAA4C;AAAA,EAChD,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,OAAO;AACT;AAMA,eAAsB,aAAa,SAA0C;AAC3E,QAAM,MAAM,QAAQ,OAAO,mBAAmB;AAC9C,QAAM,EAAE,OAAO,IAAI;AAGnB,QAAM,aAAa,cAAc,MAAM;AACvC,MAAI,CAAC,YAAY;AACf,YAAQ;AAAA,MACN,mBAAmB,MAAM,oBAAoB,OAAO,KAAK,aAAa,EAAE,KAAK,IAAI,CAAC;AAAA,IACpF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AACJ,MAAI,QAAQ,MAAM;AAChB,mBAAe,QAAQ,KACpB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC;AAEtC,QAAI,aAAa,WAAW,GAAG;AAC7B,cAAQ;AAAA,QACN,6BAA6B,UAAU,KAAK,IAAI,CAAC;AAAA,MACnD;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AACL,mBAAe,CAAC,GAAG,YAAY;AAAA,EACjC;AAGA,MAAI;AACF,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,YAAQ,IAAI,MAAM;AAAA,EACpB,SAASE,MAAK;AACZ,UAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,YAAQ,MAAM,uBAAuB,MAAM,KAAK,GAAG,EAAE;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SAAS,aAAa,KAAK,YAAY;AAC7C,SAAO,oBAAoB,CAAC,UAAU;AACtC,cAAY,MAAM;AAElB,UAAQ,IAAI,2CAA2C;AACzD;;;AC7EA,SAAS,gBAAAC,eAAc,YAAAC,iBAAgB;AACvC,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;;;ACeA,IAAM,mBAAN,MAAuB;AAAA,EAI5B,YACU,WACA,QACA,WAAmB,UAC3B;AAHQ;AACA;AACA;AAAA,EACP;AAAA,EAPK,YAA2B;AAAA,EAC3B,YAAY;AAAA;AAAA,EASpB,MAAM,aAAuC;AAC3C,UAAM,WAAW,MAAM,KAAK,YAAY,cAAc;AAAA,MACpD,iBAAiB;AAAA,MACjB,cAAc,CAAC;AAAA,MACf,YAAY,EAAE,MAAM,oBAAoB,SAAS,QAAQ;AAAA,IAC3D,CAAC;AAGD,UAAM,KAAK,iBAAiB,6BAA6B,CAAC,CAAC;AAE3D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,YAAsC;AAC1C,WAAO,KAAK,YAAY,cAAc,CAAC,CAAC;AAAA,EAC1C;AAAA;AAAA,EAGA,MAAM,SACJ,MACA,MAC0B;AAC1B,WAAO,KAAK,YAAY,cAAc,EAAE,MAAM,WAAW,KAAK,CAAC;AAAA,EACjE;AAAA;AAAA,EAGA,MAAc,YACZ,QACA,QAC0B;AAC1B,UAAM,KAAK,OAAO,EAAE,KAAK,SAAS;AAClC,UAAM,OAAuB,EAAE,SAAS,OAAO,QAAQ,QAAQ,GAAG;AAElE,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,wBAAwB;AAAA,MACxB,mBAAmB;AAAA,IACrB;AAEA,QAAI,KAAK,WAAW;AAClB,cAAQ,gBAAgB,IAAI,KAAK;AAAA,IACnC;AAEA,UAAM,MAAM,GAAG,KAAK,SAAS,QAAQ,KAAK,QAAQ;AAClD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,YAAY,QAAQ,GAAM;AAAA,IACpC,CAAC;AAGD,UAAM,eAAe,SAAS,QAAQ,IAAI,gBAAgB;AAC1D,QAAI,cAAc;AAChB,WAAK,YAAY;AAAA,IACnB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS,KAAK,mBAAmB,SAAS,QAAQ,IAAI;AAAA,QACxD;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAc,iBACZ,QACA,QACe;AACf,UAAM,OAAO,EAAE,SAAS,OAAO,QAAQ,OAAO;AAE9C,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,wBAAwB;AAAA,MACxB,mBAAmB;AAAA,IACrB;AAEA,QAAI,KAAK,WAAW;AAClB,cAAQ,gBAAgB,IAAI,KAAK;AAAA,IACnC;AAEA,UAAM,MAAM,GAAG,KAAK,SAAS,QAAQ,KAAK,QAAQ;AAElD,QAAI;AACF,YAAM,MAAM,KAAK;AAAA,QACf,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA,EAGQ,mBAAmB,QAAgB,MAAsB;AAC/D,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,iBAAiB,MAAM,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC;AAAA,IAC1D;AAAA,EACF;AACF;;;AC9IA;AAAA,EACE;AAAA,EACA;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,gBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAAC;AAAA,EACA,iBAAAC;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,UAAU,WAAAC,UAAS,SAAS,QAAAC,OAAM,UAAU,eAAe;AAK7D,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,gBAAgB,KAAK,OAAO;AAGlC,IAAM,oBAAoB;AAG1B,IAAM,oBAA8B;AAAA,EAClC,OAAO,KAAK,CAAC,KAAM,IAAM,IAAM,EAAI,CAAC;AAAA;AAAA,EACpC,OAAO,KAAK,CAAC,KAAM,KAAM,GAAI,CAAC;AAAA;AAAA,EAC9B,OAAO,KAAK,MAAM;AAAA;AAAA,EAClB,OAAO,KAAK,CAAC,IAAM,IAAM,GAAM,CAAI,CAAC;AAAA;AAAA,EACpC,OAAO,KAAK,MAAM;AAAA;AAAA,EAClB,OAAO,KAAK,CAAC,KAAM,IAAM,IAAM,EAAI,CAAC;AAAA;AAAA,EACpC,OAAO,KAAK,CAAC,KAAM,KAAM,GAAI,CAAC;AAAA;AAAA,EAC9B,OAAO,KAAK,CAAC,KAAM,KAAM,KAAM,GAAI,CAAC;AAAA;AAAA,EACpC,OAAO,KAAK,IAAI;AAAA;AAClB;AAYA,SAAS,YAAY,GAAmB;AACtC,SAAO,QAAQ,CAAC;AAClB;AAGA,SAAS,aAAa,UAA2B;AAC/C,MAAI;AACF,UAAM,KAAKJ,cAAa,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,UAAM,QAAQ,GAAG,SAAS,GAAG,iBAAiB;AAC9C,QAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,eAAW,OAAO,mBAAmB;AACnC,UAAI,MAAM,UAAU,IAAI,UAAU,MAAM,SAAS,GAAG,IAAI,MAAM,EAAE,OAAO,GAAG,GAAG;AAC3E,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,MAAM,SAAS,CAAC;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,YAAY,SAAyB;AAC5C,MAAI,KAAK;AACT,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,MAAM,KAAK;AACb,YAAM;AAAA,IACR,WAAW,MAAM,KAAK;AACpB,YAAM;AAAA,IACR,WAAW,MAAM,KAAK;AACpB,YAAM,QAAQ,QAAQ,QAAQ,KAAK,IAAI,CAAC;AACxC,UAAI,UAAU,IAAI;AAChB,cAAM,MAAM,QAAQ,MAAM,IAAI,GAAG,KAAK,IAAI;AAC1C,YAAI;AAAA,MACN,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF,WAAW,cAAc,SAAS,CAAC,GAAG;AACpC,YAAM,OAAO;AAAA,IACf,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACA,QAAM;AACN,SAAO,IAAI,OAAO,EAAE;AACtB;AAGA,SAAS,SAAS,OAAgB,WAA4B;AAC5D,MAAI,UAAW,QAAO;AACtB,MAAI,MAAO,QAAO;AAClB,SAAO;AACT;AAGA,SAAS,uBAAuB,SAAmD;AACjF,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI;AACF,UAAM,UAAU,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAC5D,eAAW,SAAS,SAAS;AAC3B,YAAM,OAAOI,MAAK,SAAS,MAAM,IAAI;AACrC,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,MAAM,uBAAuB,IAAI;AACvC,iBAAS,IAAI;AACb,iBAAS,IAAI;AAAA,MACf,OAAO;AACL,iBAAS;AACT,YAAI;AACF,mBAAS,SAAS,IAAI,EAAE;AAAA,QAC1B,QAAQ;AAAA,QAAa;AAAA,MACvB;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAa;AACrB,SAAO,EAAE,OAAO,MAAM;AACxB;AAGA,SAAS,GAAG,MAA2B;AACrC,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,IAAI,EAAE,CAAC;AAAA,IACtD,SAAS;AAAA,EACX;AACF;AAGA,SAAS,IAAI,SAA6B;AACxC,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,SAAS,OAAO,OAAO,QAAQ,CAAC,EAAE,CAAC;AAAA,IACpF,SAAS;AAAA,EACX;AACF;AAIA,SAAS,eAAe,MAA2C;AACjE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,QAAM,WAAY,KAAK,YAAuB;AAE9C,QAAM,OAAO,SAAS,QAAQ;AAC9B,MAAI,CAAC,KAAK,OAAO,EAAG,OAAM,IAAI,MAAM,aAAa;AACjD,MAAI,KAAK,OAAO,eAAe;AAC7B,UAAM,IAAI,MAAM,cAAc,KAAK,IAAI,yCAAyC,aAAa,UAAU;AAAA,EACzG;AAEA,MAAI,aAAa,QAAQ,GAAG;AAC1B,UAAM,MAAMJ,cAAa,QAAQ;AACjC,WAAO,GAAG;AAAA,MACR,SAAS,IAAI,SAAS,QAAQ;AAAA,MAC9B,WAAW;AAAA,MACX,MAAM,KAAK;AAAA,MACX,UAAU;AAAA,MACV,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,OAAOA,cAAa,UAAU,QAA0B;AAC9D,SAAO,GAAG;AAAA,IACR,SAAS;AAAA,IACT,WAAW;AAAA,IACX,MAAM,KAAK;AAAA,IACX;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACH;AAEA,SAAS,oBAAoB,MAA2C;AACtE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,QAAM,WAAY,KAAK,YAAuB;AAC9C,QAAM,YAAa,KAAK,cAAyB;AACjD,QAAM,UAAU,KAAK;AAErB,QAAM,OAAO,SAAS,QAAQ;AAC9B,MAAI,CAAC,KAAK,OAAO,EAAG,OAAM,IAAI,MAAM,aAAa;AACjD,MAAI,KAAK,OAAO,eAAe;AAC7B,UAAM,IAAI,MAAM,cAAc,KAAK,IAAI,uCAAuC;AAAA,EAChF;AAEA,MAAI,YAAY,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAC7D,MAAI,YAAY,UAAa,UAAU,UAAW,OAAM,IAAI,MAAM,iCAAiC;AAEnG,QAAM,OAAOA,cAAa,UAAU,QAA0B;AAC9D,QAAM,WAAW,KAAK,MAAM,IAAI;AAChC,QAAM,aAAa,SAAS;AAE5B,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,YAAY,SAAY,UAAU;AACjD,QAAM,WAAW,SAAS,MAAM,UAAU,MAAM;AAChD,QAAM,YAAY,KAAK,IAAI,QAAQ,UAAU;AAE7C,SAAO,GAAG;AAAA,IACR,SAAS,SAAS,KAAK,IAAI;AAAA,IAC3B,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,EACR,CAAC;AACH;AAEA,SAAS,gBAAgB,MAA2C;AAClE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,QAAM,UAAU,KAAK;AACrB,QAAM,WAAY,KAAK,YAAuB;AAC9C,QAAM,WAAY,KAAK,aAAyB;AAChD,QAAM,YAAY,KAAK,cAAc;AACrC,QAAM,gBAAiB,KAAK,kBAA8B;AAE1D,QAAM,UAAU,CAACF,YAAW,QAAQ;AAEpC,MAAI,CAAC,aAAa,CAAC,SAAS;AAC1B,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC;AAEA,MAAI,eAAe;AACjB,IAAAC,WAAUI,SAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAClD;AAEA,MAAI;AACJ,MAAI;AAEJ,MAAI,UAAU;AACZ,UAAM,MAAM,OAAO,KAAK,SAAS,QAAQ;AACzC,IAAAD,eAAc,UAAU,GAAG;AAC3B,mBAAe,IAAI;AACnB,mBAAe;AAAA,EACjB,OAAO;AACL,UAAM,UAAU,OAAO,KAAK,SAAS,QAA0B;AAC/D,IAAAA,eAAc,UAAU,SAAS,QAA0B;AAC3D,mBAAe,QAAQ;AACvB,mBAAe;AAAA,EACjB;AAEA,SAAO,GAAG;AAAA,IACR,MAAM;AAAA,IACN,eAAe;AAAA,IACf;AAAA,IACA,UAAU;AAAA,EACZ,CAAC;AACH;AAEA,SAAS,iBAAiB,MAA2C;AACnE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,QAAM,UAAU,KAAK;AACrB,QAAM,WAAY,KAAK,YAAuB;AAE9C,MAAI,CAACJ,YAAW,QAAQ,EAAG,OAAM,IAAI,MAAM,iBAAiB;AAE5D,QAAM,UAAU,OAAO,KAAK,SAAS,QAA0B;AAC/D,iBAAe,UAAU,SAAS,QAA0B;AAC5D,QAAM,UAAU,SAAS,QAAQ,EAAE;AAEnC,SAAO,GAAG;AAAA,IACR,MAAM;AAAA,IACN,eAAe,QAAQ;AAAA,IACvB,UAAU;AAAA,EACZ,CAAC;AACH;AAEA,SAAS,sBAAsB,MAA2C;AACxE,QAAM,UAAU,YAAY,KAAK,IAAc;AAC/C,QAAM,UAAU,KAAK,YAAY;AAEjC,MAAIA,YAAW,OAAO,GAAG;AACvB,WAAO,GAAG,EAAE,MAAM,SAAS,SAAS,MAAM,CAAC;AAAA,EAC7C;AAEA,MAAI,SAAS;AACX,IAAAC,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EACxC,OAAO;AACL,IAAAA,WAAU,OAAO;AAAA,EACnB;AAEA,SAAO,GAAG,EAAE,MAAM,SAAS,SAAS,KAAK,CAAC;AAC5C;AAEA,SAAS,aAAa,MAA2C;AAC/D,QAAM,aAAa,YAAY,KAAK,IAAc;AAClD,QAAM,YAAa,KAAK,aAAyB;AAEjD,MAAI,CAACD,YAAW,UAAU,EAAG,OAAM,IAAI,MAAM,iBAAiB;AAE9D,QAAM,OAAO,SAAS,YAAY,EAAE,gBAAgB,MAAM,CAAC;AAC3D,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,iBAAiB;AAE5C,MAAI,KAAK,YAAY,GAAG;AACtB,UAAM,EAAE,OAAO,cAAc,OAAO,WAAW,IAAI,uBAAuB,UAAU;AACpF,QAAI,WAAW;AACb,aAAO,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,IACxC,OAAO;AAEL,aAAO,UAAU;AAAA,IACnB;AACA,WAAO,GAAG;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,eAAe,YAAY,eAAe;AAAA,MAC1C,aAAa,YAAY,aAAa;AAAA,IACxC,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,KAAK;AAClB,EAAAG,YAAW,UAAU;AACrB,SAAO,GAAG;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,eAAe;AAAA,IACf,aAAa;AAAA,EACf,CAAC;AACH;AAEA,SAAS,WAAW,MAA2C;AAC7D,QAAM,SAAS,YAAY,KAAK,MAAgB;AAChD,QAAM,cAAc,YAAY,KAAK,WAAqB;AAC1D,QAAM,YAAa,KAAK,aAAyB;AAEjD,MAAI,CAACH,YAAW,MAAM,EAAG,OAAM,IAAI,MAAM,wBAAwB;AACjE,MAAIA,YAAW,WAAW,KAAK,CAAC,UAAW,OAAM,IAAI,MAAM,6BAA6B;AAExF,QAAM,OAAO,SAAS,MAAM;AAC5B,QAAM,OAAO,KAAK,YAAY,IAAI,cAAc;AAChD,QAAM,aAAa,KAAK,YAAY,IAChC,uBAAuB,MAAM,EAAE,QAC/B,KAAK;AAET,MAAI;AACF,eAAW,QAAQ,WAAW;AAAA,EAChC,SAAS,GAAY;AAEnB,QAAK,EAA4B,SAAS,SAAS;AACjD,UAAI,KAAK,YAAY,GAAG;AACtB,eAAO,QAAQ,aAAa,EAAE,WAAW,KAAK,CAAC;AAC/C,eAAO,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,MACpC,OAAO;AACL,qBAAa,QAAQ,WAAW;AAChC,QAAAG,YAAW,MAAM;AAAA,MACnB;AAAA,IACF,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO,GAAG;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf,CAAC;AACH;AAEA,SAAS,WAAW,MAA2C;AAC7D,QAAM,SAAS,YAAY,KAAK,MAAgB;AAChD,QAAM,cAAc,YAAY,KAAK,WAAqB;AAC1D,QAAM,YAAa,KAAK,aAAyB;AAEjD,MAAI,CAACH,YAAW,MAAM,EAAG,OAAM,IAAI,MAAM,wBAAwB;AACjE,MAAIA,YAAW,WAAW,KAAK,CAAC,UAAW,OAAM,IAAI,MAAM,6BAA6B;AAExF,QAAM,OAAO,SAAS,MAAM;AAE5B,MAAI,KAAK,YAAY,GAAG;AACtB,QAAIA,YAAW,WAAW,KAAK,WAAW;AACxC,aAAO,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,IACzC;AACA,WAAO,QAAQ,aAAa,EAAE,WAAW,KAAK,CAAC;AAC/C,UAAM,EAAE,MAAM,IAAI,uBAAuB,WAAW;AACpD,WAAO,GAAG,EAAE,QAAQ,aAAa,MAAM,aAAa,cAAc,MAAM,CAAC;AAAA,EAC3E;AAEA,eAAa,QAAQ,WAAW;AAChC,QAAM,cAAc,SAAS,WAAW,EAAE;AAC1C,SAAO,GAAG,EAAE,QAAQ,aAAa,MAAM,QAAQ,cAAc,YAAY,CAAC;AAC5E;AAEA,SAAS,oBAAoB,MAA2C;AACtE,QAAM,UAAU,YAAY,KAAK,IAAc;AAC/C,QAAM,YAAa,KAAK,aAAyB;AACjD,QAAM,WAAY,KAAK,aAAwB;AAC/C,QAAM,UAAU,KAAK;AACrB,QAAM,gBAAiB,KAAK,kBAA8B;AAE1D,MAAI,CAACA,YAAW,OAAO,EAAG,OAAM,IAAI,MAAM,sBAAsB;AAChE,QAAM,OAAO,SAAS,OAAO;AAC7B,MAAI,CAAC,KAAK,YAAY,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAEnE,QAAM,iBAAiB,YAAY,WAAW;AAC9C,QAAM,YAAY,UAAU,YAAY,OAAO,IAAI;AAWnD,QAAM,UAAmB,CAAC;AAE1B,WAAS,KAAK,KAAa,OAAqB;AAC9C,QAAI,SAAS,eAAgB;AAE7B,QAAI;AACJ,QAAI;AACF,gBAAU,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACpD,QAAQ;AACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAEnD,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,iBAAiB,EAAE,KAAK,WAAW,GAAG,EAAG;AAE9C,YAAM,WAAWM,MAAK,KAAK,EAAE,IAAI;AACjC,YAAM,QAAQ,EAAE,YAAY;AAG5B,UAAI,aAAa,CAAC,UAAU,KAAK,EAAE,IAAI,GAAG;AAExC,YAAI,SAAS,iBAAiB,GAAG;AAC/B,eAAK,UAAU,QAAQ,CAAC;AAAA,QAC1B;AACA;AAAA,MACF;AAEA,UAAI,OAAO;AACX,UAAI,SAAQ,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAI;AACF,cAAM,IAAI,SAAS,QAAQ;AAC3B,eAAO,QAAQ,IAAI,EAAE;AACrB,gBAAQ,IAAI,KAAK,EAAE,OAAO,EAAE,YAAY;AAAA,MAC1C,QAAQ;AAAA,MAAa;AAErB,cAAQ,KAAK;AAAA,QACX,MAAM,EAAE;AAAA,QACR,MAAM,SAAS,SAAS,QAAQ;AAAA,QAChC,MAAM,SAAS,OAAO,EAAE,eAAe,CAAC;AAAA,QACxC;AAAA,QACA,aAAa;AAAA,QACb,WAAW,EAAE,KAAK,WAAW,GAAG;AAAA,MAClC,CAAC;AAED,UAAI,SAAS,iBAAiB,GAAG;AAC/B,aAAK,UAAU,QAAQ,CAAC;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,OAAK,SAAS,CAAC;AAEf,SAAO,GAAG;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA,aAAa,QAAQ;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,aAAa,MAA2C;AAC/D,QAAM,aAAa,YAAY,KAAK,IAAc;AAClD,QAAM,aAAaN,YAAW,UAAU;AAExC,MAAI,OAAsB;AAC1B,MAAI,YAAY;AACd,UAAM,OAAO,SAAS,YAAY,EAAE,gBAAgB,MAAM,CAAC;AAC3D,QAAI,MAAM;AACR,aAAO,SAAS,KAAK,YAAY,GAAG,KAAK,eAAe,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,SAAO,GAAG,EAAE,QAAQ,YAAY,MAAM,YAAY,KAAK,CAAC;AAC1D;AAEA,SAAS,kBAAkB,MAA2C;AACpE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,MAAI,CAACA,YAAW,QAAQ,EAAG,OAAM,IAAI,MAAM,iBAAiB;AAE5D,QAAM,OAAO,SAAS,UAAU,EAAE,gBAAgB,MAAM,CAAC;AACzD,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,iBAAiB;AAE5C,QAAM,QAAQ,KAAK,YAAY;AAC/B,QAAM,YAAY,KAAK,eAAe;AACtC,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI,KAAK;AAC5C,QAAM,SAAS,CAAC,SAAS,CAAC,YAAY,aAAa,QAAQ,IAAI;AAE/D,SAAO,GAAG;AAAA,IACR;AAAA,IACA,MAAM;AAAA,IACN,MAAM,SAAS,OAAO,SAAS;AAAA,IAC/B,MAAM,QAAQ,IAAI,KAAK;AAAA,IACvB,aAAa,IAAI,KAAK,KAAK,OAAO,EAAE,YAAY;AAAA,IAChD,YAAY,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,EAAE,YAAY,IAAI;AAAA,IAC1E,WAAW;AAAA,IACX,WAAW,KAAK,WAAW,GAAG;AAAA,IAC9B,WAAW;AAAA,EACb,CAAC;AACH;AAEA,SAAS,kBAAkB,MAA2C;AACpE,QAAM,aAAa,YAAY,KAAK,IAAc;AAClD,QAAM,UAAW,KAAK,WAAsB;AAC5C,QAAM,YAAY,KAAK,cAAc;AACrC,QAAM,gBAAiB,KAAK,kBAA8B;AAC1D,QAAM,aAAc,KAAK,eAA0B;AAEnD,MAAI,CAACA,YAAW,UAAU,EAAG,OAAM,IAAI,MAAM,sBAAsB;AACnE,MAAI,CAAC,SAAS,UAAU,EAAE,YAAY,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAEnF,QAAM,YAAY,YAAY,OAAO;AACrC,QAAM,UAAoB,CAAC;AAC3B,MAAI,eAAe;AAEnB,WAAS,KAAK,KAAmB;AAC/B,QAAI;AACJ,QAAI;AACF,gBAAU,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACpD,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,eAAe;AAClB,YAAI,EAAE,KAAK,WAAW,GAAG,EAAG;AAAA,MAC9B;AAEA,YAAM,WAAWM,MAAK,KAAK,EAAE,IAAI;AAEjC,UAAI,EAAE,YAAY,GAAG;AACnB,YAAI,UAAW,MAAK,QAAQ;AAC5B;AAAA,MACF;AAEA,UAAI,UAAU,KAAK,EAAE,IAAI,GAAG;AAC1B;AACA,YAAI,QAAQ,SAAS,YAAY;AAC/B,kBAAQ,KAAK,SAAS,YAAY,QAAQ,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,OAAK,UAAU;AAEf,SAAO,GAAG;AAAA,IACR;AAAA,IACA,eAAe;AAAA,IACf,WAAW,eAAe;AAAA,IAC1B,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACH;AAEA,SAAS,kBAAkB,MAA2C;AACpE,QAAM,aAAa,YAAY,KAAK,IAAc;AAClD,QAAM,QAAQ,KAAK;AACnB,QAAM,UAAW,KAAK,YAAwB;AAC9C,QAAM,gBAAgB,KAAK,mBAAmB;AAC9C,QAAM,cAAc,KAAK;AACzB,QAAM,eAAgB,KAAK,iBAA4B;AACvD,QAAM,aAAc,KAAK,eAA0B;AACnD,QAAM,gBAAiB,KAAK,kBAA8B;AAE1D,MAAI,CAACN,YAAW,UAAU,EAAG,OAAM,IAAI,MAAM,sBAAsB;AACnE,MAAI,CAAC,SAAS,UAAU,EAAE,YAAY,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAEnF,QAAM,QAAQ,gBAAgB,KAAK;AACnC,QAAM,KAAK,UACP,IAAI,OAAO,OAAO,KAAK,IACvB,IAAI,OAAO,MAAM,QAAQ,uBAAuB,MAAM,GAAG,KAAK;AAElE,QAAM,YAAY,cAAc,YAAY,WAAW,IAAI;AAU3D,QAAM,UAAmB,CAAC;AAC1B,MAAI,eAAe;AACnB,MAAI,gBAAgB;AACpB,MAAI,mBAAmB;AAEvB,WAAS,KAAK,KAAmB;AAC/B,QAAI;AACJ,QAAI;AACF,gBAAU,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACpD,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,iBAAiB,EAAE,KAAK,WAAW,GAAG,EAAG;AAE9C,YAAM,WAAWM,MAAK,KAAK,EAAE,IAAI;AAEjC,UAAI,EAAE,YAAY,GAAG;AACnB,aAAK,QAAQ;AACb;AAAA,MACF;AAEA,UAAI,aAAa,CAAC,UAAU,KAAK,EAAE,IAAI,EAAG;AAC1C,UAAI,aAAa,QAAQ,EAAG;AAE5B;AACA,UAAI,eAAe;AAEnB,UAAI;AACJ,UAAI;AACF,cAAM,OAAOJ,cAAa,UAAU,OAAO;AAC3C,gBAAQ,KAAK,MAAM,IAAI;AAAA,MACzB,QAAQ;AACN;AAAA,MACF;AAEA,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,GAAG,KAAK,MAAM,CAAC,CAAC,GAAG;AACrB;AACA,yBAAe;AAEf,cAAI,QAAQ,SAAS,YAAY;AAC/B,kBAAM,WAAW,KAAK,IAAI,GAAG,IAAI,YAAY;AAC7C,kBAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI,eAAe,CAAC;AAE1D,oBAAQ,KAAK;AAAA,cACX,MAAM,SAAS,YAAY,QAAQ;AAAA,cACnC,MAAM,IAAI;AAAA,cACV,SAAS,MAAM,CAAC;AAAA,cAChB,gBAAgB,MAAM,MAAM,UAAU,CAAC;AAAA,cACvC,eAAe,MAAM,MAAM,IAAI,GAAG,MAAM;AAAA,YAC1C,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,UAAI,aAAc;AAAA,IACpB;AAAA,EACF;AAEA,OAAK,UAAU;AAEf,SAAO,GAAG;AAAA,IACR;AAAA,IACA,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,WAAW,eAAe;AAAA,IAC1B;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAc,MAA2C;AAChE,QAAM,WAAW,YAAY,KAAK,IAAc;AAChD,QAAM,WAAY,KAAK,aAAwB;AAC/C,QAAM,gBAAiB,KAAK,kBAA8B;AAC1D,QAAM,aAAc,KAAK,eAA0B;AAEnD,MAAI,CAACF,YAAW,QAAQ,EAAG,OAAM,IAAI,MAAM,sBAAsB;AACjE,MAAI,CAAC,SAAS,QAAQ,EAAE,YAAY,EAAG,OAAM,IAAI,MAAM,0BAA0B;AAEjF,QAAM,QAAkB,CAAC;AACzB,MAAI,aAAa;AACjB,MAAI,YAAY;AAEhB,QAAM,WAAW,SAAS,QAAQ,KAAK;AACvC,QAAM,KAAK,WAAW,GAAG;AAEzB,WAAS,UAAU,SAAiB,QAAgB,OAAqB;AACvE,QAAI,aAAa,SAAS,SAAU;AAEpC,QAAI;AACJ,QAAI;AACF,gBAAU,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAAA,IACxD,QAAQ;AACN;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,gBAAU,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,WAAW,GAAG,CAAC;AAAA,IACzD;AACA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAEnD,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAI,UAAW;AAEf;AACA,UAAI,aAAa,YAAY;AAC3B,oBAAY;AACZ,cAAM,KAAK,SAAS,iBAAiB;AACrC;AAAA,MACF;AAEA,YAAM,IAAI,QAAQ,CAAC;AACnB,YAAM,SAAS,MAAM,QAAQ,SAAS;AACtC,YAAM,YAAY,SAAS,wBAAS;AACpC,YAAM,cAAc,UAAU,SAAS,SAAS;AAChD,YAAM,WAAWM,MAAK,SAAS,EAAE,IAAI;AAErC,UAAI,EAAE,YAAY,GAAG;AACnB,cAAM,KAAK,SAAS,YAAY,EAAE,OAAO,GAAG;AAC5C,kBAAU,UAAU,aAAa,QAAQ,CAAC;AAAA,MAC5C,OAAO;AACL,cAAM,KAAK,SAAS,YAAY,EAAE,IAAI;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAEA,YAAU,UAAU,IAAI,CAAC;AAEzB,SAAO,GAAG;AAAA,IACR,MAAM,MAAM,KAAK,IAAI;AAAA,IACrB,MAAM;AAAA,IACN,aAAa;AAAA,IACb;AAAA,IACA,WAAW;AAAA,EACb,CAAC;AACH;AAIA,IAAM,WAA0E;AAAA,EAC9E,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,cAAc;AAAA,EACd,eAAe;AAAA,EACf,UAAU;AACZ;AASO,SAAS,0BACd,WACA,UACA,MACY;AACZ,UAAQ,MAAM,2BAA2B,QAAQ,qBAAqB;AAEtE,QAAM,UAAU,SAAS,QAAQ;AACjC,MAAI,CAAC,SAAS;AACZ,WAAO,IAAI,4BAA4B,QAAQ,EAAE;AAAA,EACnD;AAEA,MAAI;AACF,WAAO,QAAQ,IAAI;AAAA,EACrB,SAAS,GAAY;AACnB,UAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACzD,YAAQ,MAAM,2BAA2B,QAAQ,WAAW,OAAO,EAAE;AACrE,WAAO,IAAI,OAAO;AAAA,EACpB;AACF;;;AFlxBA,IAAM,eAAe,oBAAI,IAAI,CAAC,eAAe,2BAA2B,CAAC;AAIzE,IAAM,oBAAoB,IAAI,OAAO;AAOrC,SAAS,yBACP,MACyB;AACzB,QAAM,OAAO,KAAK;AAClB,QAAM,WAAW,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,EAAE,IAAI,IAAK;AAEjE,MAAI,aAAa,eAAe;AAC9B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aACE;AAAA,MAGF,aAAa;AAAA,QACX,MAAM;AAAA,QACN,UAAU,CAAC,aAAa,MAAM;AAAA,QAC9B,YAAY;AAAA,UACV,WAAW;AAAA,YACT,MAAM;AAAA,YACN,aACE;AAAA,UAEJ;AAAA,UACA,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,aACE;AAAA,UAEJ;AAAA,UACA,cAAc;AAAA,YACZ,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,aAAa,6BAA6B;AAC5C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aACE;AAAA,MAGF,aAAa;AAAA,QACX,MAAM;AAAA,QACN,UAAU,CAAC,aAAa,WAAW,MAAM;AAAA,QACzC,YAAY;AAAA,UACV,WAAW;AAAA,YACT,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,SAAS;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,aACE;AAAA,UAEJ;AAAA,UACA,cAAc;AAAA,YACZ,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAUA,eAAe,sBACb,QACA,UACA,MAC+E;AAC/E,QAAM,WAAW,KAAK;AAGtB,MAAI;AACJ,MAAI;AACF,eAAWC,UAAS,QAAkB,EAAE;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,QAC7C,SAAS;AAAA,QACT,OAAO,mBAAmB,QAAQ;AAAA,MACpC,CAAC,EAAE,CAAC;AAAA,MACJ,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,YAAY,mBAAmB;AAEjC,UAAMC,cAAaC,cAAa,QAAQ;AACxC,UAAM,gBAAgBD,YAAW,SAAS,QAAQ;AAElD,UAAM,cAAuC,EAAE,GAAG,MAAM,SAAS,cAAc;AAC/E,WAAO,YAAY;AAEnB,YAAQ;AAAA,MACN,gBAAgB,QAAQ,0BAA0B,WAAW,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC/E;AAEA,UAAM,WAAW,MAAM,OAAO,SAAS,UAAU,WAAW;AAE5D,QAAI,SAAS,OAAO;AAClB,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ,CAAC;AAAA,QACxD,SAAS;AAAA,MACX;AAAA,IACF;AAEA,WAAO,SAAS;AAAA,EAIlB;AAGA,UAAQ;AAAA,IACN,gBAAgB,QAAQ,kBAAkB,WAAW,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,EAC9E;AAIA,QAAM,SAAS,SAAS,SAAS,IAAI,IAAI,SAAS,MAAM,IAAI,EAAE,CAAC,IAAI,OAAO;AAC1E,QAAM,kBAAkB,MAAM,OAAO,SAAS,GAAG,MAAM,yBAAyB;AAAA,IAC9E,MAAM,KAAK;AAAA,EACb,CAAC;AAED,MAAI,gBAAgB,OAAO;AACzB,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,gBAAgB,MAAM,QAAQ,CAAC;AAAA,MAC/D,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,gBAAgB,gBAAgB;AAItC,MAAI;AACJ,MAAI;AACF,kBAAc,KAAK,MAAM,cAAc,QAAQ,CAAC,EAAE,IAAI;AAAA,EACxD,QAAQ;AACN,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,QAC7C,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC,EAAE,CAAC;AAAA,MACJ,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,YAAY,YAAY;AAC9B,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,QAC7C,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC,EAAE,CAAC;AAAA,MACJ,SAAS;AAAA,IACX;AAAA,EACF;AAMA,QAAM,aAAaC,cAAa,QAAQ;AACxC,QAAM,YAAY,MAAM,OAAO;AAC/B,QAAM,QAAQ,WAAW;AACzB,MAAI,YAAqC,CAAC;AAE1C,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS,WAAW;AACrD,UAAM,MAAM,KAAK,IAAI,QAAQ,WAAW,KAAK;AAC7C,UAAM,QAAQ,WAAW,SAAS,OAAO,GAAG;AAC5C,UAAM,cAAc,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;AAExD,YAAQ;AAAA,MACN,gCAAgC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK;AAAA,IAC3D;AAEA,UAAM,gBAAgB,MAAM,OAAO,SAAS,GAAG,MAAM,qBAAqB;AAAA,MACxE,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,WAAW,MAAM;AAAA,MACjB,YAAY;AAAA,IACd,CAAC;AAED,QAAI,cAAc,OAAO;AACvB,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,cAAc,MAAM,QAAQ,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,cAAc,cAAc;AAIlC,QAAI;AACJ,QAAI;AACF,kBAAY,KAAK,MAAM,YAAY,QAAQ,CAAC,EAAE,IAAI;AAAA,IACpD,QAAQ;AACN,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,UAC7C,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC,EAAE,CAAC;AAAA,QACJ,SAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,SAAS;AACtB,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,SAAS,EAAE,CAAC;AAAA,QAC3D,SAAS;AAAA,MACX;AAAA,IACF;AAGA,QAAI,UAAU,WAAW,OAAO,UAAU,WAAW,KAAK;AACxD,kBAAa,UAAU,QAAoC,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU;AAAA,MAC7C,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS,aAAa,KAAK,IAAI,OAAO,WAAW,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC1E,CAAC,EAAE,CAAC;AAAA,IACJ,SAAS;AAAA,EACX;AACF;AAMA,eAAsB,iBACpB,SACe;AACf,QAAM,EAAE,WAAW,QAAQ,WAAW,SAAS,IAAI;AAEnD,QAAM,SAAS,IAAI,iBAAiB,WAAW,QAAQ,QAAQ;AAE/D,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,cAAc,SAAS,QAAQ;AAAA,IACvC,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,MAAM,EAAE,EAAE;AAAA,EACpD;AAGA,MAAI,cAAc;AAElB,iBAAe,oBAAmC;AAChD,QAAI,YAAa;AACjB,QAAI;AACF,cAAQ,MAAM,8CAA8C;AAC5D,YAAM,OAAO,WAAW;AACxB,cAAQ,MAAM,2CAA2C;AAAA,IAC3D,SAASC,MAAK;AACZ,YAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,cAAQ,MAAM,+CAA+C,GAAG,EAAE;AAAA,IAEpE;AACA,kBAAc;AAAA,EAChB;AAGA,SAAO,kBAAkB,wBAAwB,YAAY;AAC3D,UAAM,kBAAkB;AACxB,UAAM,WAAW,MAAM,OAAO,UAAU;AAExC,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI,MAAM,SAAS,MAAM,OAAO;AAAA,IACxC;AAEA,UAAM,SAAS,SAAS;AACxB,UAAM,SAAS,OAAO,SAAS,CAAC,GAAG,IAAI,wBAAwB;AAC/D,WAAO,EAAE,MAAM;AAAA,EACjB,CAAC;AAGD,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,kBAAkB;AACxB,UAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAC1C,UAAM,YAAa,QAAQ,CAAC;AAI5B,UAAM,WAAW,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,EAAE,IAAI,IAAK;AACjE,QAAI,aAAa,IAAI,QAAQ,KAAK,UAAU,WAAW;AACrD,aAAO,sBAAsB,QAAQ,MAAM,SAAS;AAAA,IACtD;AAGA,QAAI,iBAAiB,IAAI,QAAQ,GAAG;AAClC,aAAO,0BAA0B,MAAM,UAAU,SAAS;AAAA,IAC5D;AAEA,UAAM,WAAW,MAAM,OAAO,SAAS,MAAM,SAAS;AAEtD,QAAI,SAAS,OAAO;AAClB,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,SAAS,MAAM,QAAQ,CAAC;AAAA,QACjE,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,SAAS,SAAS;AAKxB,WAAO;AAAA,EACT,CAAC;AAGD,UAAQ,MAAM,uCAAuC;AACrD,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,sCAAsC;AACtD;;;AGtWA,eAAsB,WAA0B;AAE9C,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,QAAQ,UAAU;AACpC,QAAM,SAAS,mBAAmB;AAElC,MAAI,WAAW,iBAAiB;AAC9B,YAAQ,OAAO;AAAA,MACb;AAAA,IAGF;AAAA,EACF;AAGA,QAAM,iBAAiB,EAAE,WAAW,OAAO,CAAC;AAC9C;;;AClBO,SAAS,YAAkB;AAChC,QAAM,SAAS,WAAW;AAE1B,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,oEAAoE;AAChF;AAAA,EACF;AAEA,QAAM,YACJ,OAAO,OAAO,MAAM,GAAG,CAAC,IAAI,SAAS,OAAO,OAAO,MAAM,EAAE;AAE7D,QAAM,QAAQ,gBAAgB;AAE9B,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,qBAAqB;AACjC,UAAQ,IAAI,qBAAqB;AAEjC,MAAI,OAAO;AACT,YAAQ,IAAI,cAAc,MAAM,QAAQ,MAAM,KAAK,EAAE;AACrD,YAAQ,IAAI,cAAc,MAAM,KAAK,EAAE;AAAA,EACzC,OAAO;AACL,YAAQ,IAAI,6CAA6C;AAAA,EAC3D;AAEA,UAAQ,IAAI,cAAc,cAAc,CAAC,EAAE;AAC3C,UAAQ,IAAI,cAAc,OAAO,MAAM,EAAE;AACzC,UAAQ,IAAI,cAAc,SAAS,EAAE;AACrC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,iBAAiB;AAE7B,aAAW,WAAW,OAAO,MAAM;AACjC,UAAM,MAAM,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AACzD,UAAM,UAAU,MAAM,IAAI,cAAc;AACxC,YAAQ,IAAI,OAAO,QAAQ,OAAO,EAAE,CAAC,IAAI,OAAO,MAAM,QAAQ,OAAO,EAAE;AAAA,EACzE;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,uBAAuB;AAEnC,MAAI,OAAO,kBAAkB,WAAW,GAAG;AACzC,YAAQ,IAAI,YAAY;AAAA,EAC1B,OAAO;AACL,eAAW,UAAU,OAAO,mBAAmB;AAC7C,cAAQ,IAAI,OAAO,MAAM,EAAE;AAAA,IAC7B;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AAChB;;;ACvDA,SAAS,cAAAC,aAAY,cAAAC,aAAY,iBAAiB;AAO3C,SAAS,WAAiB;AAC/B,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,yCAAyC;AAGrD,QAAM,eAAe,mBAAmB;AACxC,UAAQ;AAAA,IACN,qBAAqB,eAAe,oBAAoB,kBAAkB;AAAA,EAC5E;AAGA,QAAM,YAAY,gBAAgB;AAClC,UAAQ;AAAA,IACN,qBAAqB,YAAY,oBAAoB,kBAAkB;AAAA,EACzE;AAGA,QAAM,cAAc,YAAY;AAChC,UAAQ;AAAA,IACN,qBAAqB,cAAc,oBAAoB,kBAAkB;AAAA,EAC3E;AAGA,QAAM,aAAa,WAAW;AAC9B,UAAQ;AAAA,IACN,qBAAqB,aAAa,oBAAoB,kBAAkB;AAAA,EAC1E;AAGA,QAAM,aAAa,cAAc;AACjC,MAAIC,YAAW,UAAU,GAAG;AAC1B,IAAAC,YAAW,UAAU;AACrB,YAAQ,IAAI,2BAA2B;AAAA,EACzC;AAEA,QAAM,YAAY,aAAa;AAC/B,MAAI;AACF,cAAU,SAAS;AAAA,EACrB,QAAQ;AAAA,EAER;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,mDAAmD;AAC/D,UAAQ,IAAI,+EAA+E;AAC3F,UAAQ,IAAI,EAAE;AAChB;;;ACrDA,YAAY,cAAc;;;ACK1B,SAASC,KAAI,KAAmB;AAC9B,UAAQ,OAAO,MAAM,MAAM,IAAI;AACjC;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAEA,SAAS,eAA8B;AACrC,SAAO,IAAI,QAAQ,CAACA,aAAY;AAC9B,YAAQ,MAAM,aAAa,KAAK;AAChC,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,KAAK,QAAQ,MAAM;AAC/B,cAAQ,MAAM,MAAM;AACpB,MAAAA,SAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAQA,eAAsB,gBACpB,cACA,aACA,QACA,WACA,YACkB;AAElB,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,UAAM,OAAO,MAAM;AAAA,MACjB,GAAG,SAAS,yBAAyB,YAAY;AAAA,MACjD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,IAAI;AACZ,UAAI,KAAK,WAAW,KAAK;AACvB,QAAAD,KAAI,+EAA+E;AACnF,eAAO;AAAA,MACT;AAEA,YAAME,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,MAAAF,KAAI,YAAYE,KAAI,UAAU,QAAQ,KAAK,MAAM,EAAE,EAAE;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,OAAQ,MAAM,KAAK,KAAK;AAK9B,gBAAY,KAAK;AACjB,cAAU,KAAK;AACf,gBAAY,KAAK;AAAA,EACnB,SAASA,MAAK;AACZ,UAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,IAAAF,KAAI,6CAA6C,SAAS,EAAE;AAC5D,IAAAA,KAAI,KAAK,GAAG,EAAE;AACd,WAAO;AAAA,EACT;AAGA,MAAI,YAAY;AACd,IAAAA,KAAI,wBAAwB,WAAW,yBAAyB;AAChE,UAAM,YAAY,UAAU;AAC5B,IAAAA,KAAI,sCAAsC;AAC1C,IAAAA,KAAI,KAAK,UAAU,EAAE;AACrB,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,4CAA4C;AAChD,UAAM,aAAa;AACnB,IAAAA,KAAI,EAAE;AAAA,EACR;AAGA,EAAAA,KAAI,yBAAyB,WAAW,mBAAmB;AAC3D,QAAM,YAAY,OAAO;AACzB,EAAAA,KAAI,sCAAsC;AAC1C,EAAAA,KAAI,KAAK,OAAO,EAAE;AAClB,EAAAA,KAAI,EAAE;AAGN,QAAM,WAAW,KAAK,IAAI,IAAI,YAAY;AAC1C,UAAQ,OAAO,MAAM,6BAA6B;AAElD,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,GAAI;AAEhB,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,SAAS,8BAA8B,SAAS;AAAA,QACnD;AAAA,UACE,SAAS;AAAA,YACP,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,KAAK,KAAK;AAOhC,UAAI,OAAO,WAAW,aAAa;AACjC,gBAAQ,OAAO,MAAM,MAAM;AAC3B,cAAM,MAAM,OAAO,iBAAiB;AACpC,QAAAA,KAAI,kBAAkB,GAAG,EAAE;AAC3B,QAAAA,KAAI,KAAK,WAAW,4CAA4C;AAChE,QAAAA,KAAI,EAAE;AACN,eAAO;AAAA,MACT;AAEA,UAAI,OAAO,WAAW,WAAW;AAC/B,gBAAQ,OAAO,MAAM,IAAI;AACzB,QAAAA,KAAI,oDAAoD;AACxD,eAAO;AAAA,MACT;AAEA,UAAI,OAAO,WAAW,SAAS;AAC7B,gBAAQ,OAAO,MAAM,IAAI;AACzB,QAAAA;AAAA,UACE,wBAAwB,OAAO,iBAAiB,eAAe;AAAA,QACjE;AACA,eAAO;AAAA,MACT;AAGA,cAAQ,OAAO,MAAM,GAAG;AAAA,IAC1B,QAAQ;AACN,cAAQ,OAAO,MAAM,GAAG;AAAA,IAC1B;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,IAAI;AACzB,EAAAA,KAAI,wDAAwD;AAC5D,SAAO;AACT;AAMA,eAAsB,WAAW,UAAiC;AAEhE,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO;AACV,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,gCAAgC;AACpC,IAAAA,KAAI,yBAAyB;AAC7B,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,MAAM,eAAe;AAAA,IACzB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,YAAY,YAAY,MAAM,SAAS,YAAY;AAAA,EACrF;AACA,MAAI,CAAC,KAAK;AACR,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,uBAAuB,QAAQ,EAAE;AACrC,IAAAA,KAAI,gBAAgB,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAClE,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,QAAQ,UAAU;AAEpC,EAAAA,KAAI,EAAE;AACN,EAAAA,KAAI,+BAA0B,IAAI,WAAW,EAAE;AAC/C,EAAAA,KAAI,wBAAwB,IAAI,WAAW,UAAU;AACrD,EAAAA,KAAI,EAAE;AAEN,QAAM,UAAU,MAAM;AAAA,IACpB,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,gBAAgB,MAAO,IAAI,aAAwB;AAAA,EACrD;AAEA,MAAI,CAAC,SAAS;AACZ,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ADnMA,SAASG,KAAI,KAAmB;AAC9B,UAAQ,OAAO,MAAM,MAAM,IAAI;AACjC;AAEA,SAASC,OAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAcA,eAAsB,aACpB,WACA,WACsB;AAEtB,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,UAAM,OAA+B,CAAC;AACtC,QAAI,WAAW;AACb,WAAK,aAAa;AAAA,IACpB;AAEA,UAAM,OAAO,MAAM,MAAM,GAAG,SAAS,kCAAkC;AAAA,MACrE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,KAAK,IAAI;AACZ,MAAAF,KAAI,iDAAiD,KAAK,MAAM,GAAG;AACnE,aAAO,EAAE,QAAQ,SAAS;AAAA,IAC5B;AAEA,UAAM,OAAQ,MAAM,KAAK,KAAK;AAK9B,gBAAY,KAAK;AACjB,cAAU,KAAK;AACf,gBAAY,KAAK;AAAA,EACnB,SAASG,MAAK;AACZ,UAAM,MAAMA,gBAAe,QAAQA,KAAI,UAAU,OAAOA,IAAG;AAC3D,IAAAH,KAAI,6CAA6C,SAAS,EAAE;AAC5D,IAAAA,KAAI,KAAK,GAAG,EAAE;AACd,WAAO,EAAE,QAAQ,SAAS;AAAA,EAC5B;AAGA,EAAAA,KAAI,yCAAyC;AAC7C,QAAM,YAAY,OAAO;AACzB,EAAAA,KAAI,sCAAsC;AAC1C,EAAAA,KAAI,KAAK,OAAO,EAAE;AAClB,EAAAA,KAAI,EAAE;AAGN,QAAM,WAAW,KAAK,IAAI,IAAI,YAAY;AAC1C,UAAQ,OAAO,MAAM,8BAA8B;AAEnD,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAMC,OAAM,GAAI;AAEhB,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,GAAG,SAAS,8BAA8B,SAAS;AAAA,MACrD;AACA,YAAM,SAAU,MAAM,KAAK,KAAK;AAUhC,UAAI,OAAO,WAAW,aAAa;AACjC,gBAAQ,OAAO,MAAM,MAAM;AAE3B,cAAM,QACJ,OAAO,kBAAkB,OAAO,WAAW,SAAS;AACtD,cAAM,OAAO,OAAO,iBAAiB,OAAO,WAAW,QAAQ;AAC/D,cAAM,SAAS,OAAO,WAAW;AAEjC,eAAO,EAAE,QAAQ,WAAW,QAAQ,OAAO,MAAM,QAAQ,OAAU;AAAA,MACrE;AAEA,UAAI,OAAO,WAAW,WAAW;AAC/B,gBAAQ,OAAO,MAAM,IAAI;AACzB,QAAAD,KAAI,qDAAqD;AACzD,eAAO,EAAE,QAAQ,SAAS;AAAA,MAC5B;AAEA,UAAI,OAAO,WAAW,SAAS;AAC7B,gBAAQ,OAAO,MAAM,IAAI;AAGzB,YACE,OAAO,kBAAkB,2BACzB,CAAC,WACD;AACA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,UAAU,OAAO,aAAa;AAAA,UAChC;AAAA,QACF;AAEA,QAAAA;AAAA,UACE,4BAA4B,OAAO,iBAAiB,eAAe;AAAA,QACrE;AACA,eAAO,EAAE,QAAQ,SAAS;AAAA,MAC5B;AAGA,cAAQ,OAAO,MAAM,GAAG;AAAA,IAC1B,QAAQ;AACN,cAAQ,OAAO,MAAM,GAAG;AAAA,IAC1B;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,IAAI;AACzB,EAAAA,KAAI,yDAAyD;AAC7D,SAAO,EAAE,QAAQ,SAAS;AAC5B;AAKA,eAAsB,8BACpB,QACA,WACe;AAEf,MAAI,sBAIC,CAAC;AAEN,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,GAAG,SAAS,6BAA6B;AAAA,MAChE,SAAS,EAAE,aAAa,OAAO;AAAA,IACjC,CAAC;AACD,QAAI,KAAK,IAAI;AACX,YAAM,OAAQ,MAAM,KAAK,KAAK;AAG9B,4BAAsB,KAAK,eAAe,CAAC;AAAA,IAC7C,OAAO;AACL,MAAAA;AAAA,QACE,4DAA4D,KAAK,MAAM;AAAA,MACzE;AACA,UAAI,KAAK,WAAW,OAAO,KAAK,WAAW,KAAK;AAC9C,QAAAA,KAAI,8EAA8E;AAAA,MACpF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,IAAAA,KAAI,4EAA4E;AAAA,EAClF;AAGA,EAAAA,KAAI,EAAE;AACN,EAAAA,KAAI,mBAAmB;AACvB,EAAAA,KAAI,OAAO,SAAS,OAAO,EAAE,CAAC;AAE9B,QAAM,eAA6D,CAAC;AAEpE,aAAW,OAAO,gBAAgB;AAChC,UAAM,OAAO,oBAAoB,KAAK,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;AAEpE,QAAI,IAAI,aAAa,WAAW;AAC9B,MAAAA,KAAI,OAAO,IAAI,YAAY,OAAO,EAAE,CAAC,kBAAkB;AAAA,IACzD,OAAO;AAEL,UAAI,QAAQ,CAAC,KAAK,sBAAsB,KAAK,eAAe;AAC1D,QAAAA,KAAI,OAAO,IAAI,YAAY,OAAO,EAAE,CAAC,IAAI,KAAK,aAAa,EAAE;AAAA,MAC/D,OAAO;AACL,QAAAA;AAAA,UACE,OAAO,IAAI,YAAY,OAAO,EAAE,CAAC;AAAA,QACnC;AACA,qBAAa,KAAK,EAAE,MAAM,IAAI,MAAM,aAAa,IAAI,YAAY,CAAC;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,KAAI,EAAE;AAGN,MAAI,aAAa,WAAW,GAAG;AAC7B,IAAAA,KAAI,2BAA2B;AAC/B,IAAAA,KAAI,EAAE;AACN;AAAA,EACF;AAEA,EAAAA,KAAI,sEAAsE;AAC1E,EAAAA,KAAI,iFAA4E;AAEhF,QAAM,KAAc,yBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,QAAM,MAAM,CAAC,aACX,IAAI,QAAQ,CAACE,aAAY;AACvB,OAAG,SAAS,UAAU,CAAC,WAAWA,SAAQ,OAAO,KAAK,EAAE,YAAY,CAAC,CAAC;AAAA,EACxE,CAAC;AAEH,aAAW,OAAO,cAAc;AAC9B,UAAM,SAAS,MAAM;AAAA,MACnB,aAAa,IAAI,WAAW;AAAA,IAC9B;AAEA,QAAI,WAAW,KAAK;AAClB,MAAAF,KAAI,2CAA2C;AAC/C,MAAAA,KAAI,iEAAiE;AACrE;AAAA,IACF;AAEA,QAAI,WAAW,KAAK;AAClB,MAAAA,KAAI,uDAAuD,IAAI,IAAI,EAAE;AACrE,MAAAA,KAAI,EAAE;AACN;AAAA,IACF;AAGA,IAAAA,KAAI,EAAE;AACN,UAAM,UAAU,MAAM;AAAA,MACpB,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,gBAAgB,MAAO,IAAI,aAAwB;AAAA,IACrD;AAEA,QAAI,CAAC,SAAS;AACZ,MAAAA;AAAA,QACE,oDAAoD,IAAI,IAAI;AAAA,MAC9D;AAAA,IACF;AACA,IAAAA,KAAI,EAAE;AAAA,EACR;AAEA,KAAG,MAAM;AACX;AAKA,eAAe,mBAAmB,UAA0C;AAC1E,EAAAA,KAAI,EAAE;AACN,EAAAA,KAAI,qBAAqB,QAAQ,4CAA4C;AAC7E,EAAAA;AAAA,IACE;AAAA,EACF;AACA,EAAAA,KAAI,EAAE;AAEN,QAAM,KAAc,yBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,QAAM,SAAS,MAAM,IAAI,QAAgB,CAACE,aAAY;AACpD,OAAG;AAAA,MAAS;AAAA,MAAwC,CAAC,MACnDA,SAAQ,EAAE,KAAK,CAAC;AAAA,IAClB;AAAA,EACF,CAAC;AACD,KAAG,MAAM;AAET,MAAI,CAAC,UAAU,CAAC,OAAO,SAAS,GAAG,GAAG;AACpC,IAAAF,KAAI,+EAA+E;AACnF,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAUA,eAAsB,SAAS,WAAmC;AAEhE,QAAM,WAAW,gBAAgB;AACjC,MAAI,UAAU;AACZ,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,0BAA0B,SAAS,QAAQ,SAAS,KAAK,EAAE;AAC/D,IAAAA,KAAI,qDAAqD;AACzD,IAAAA,KAAI,EAAE;AACN;AAAA,EACF;AAEA,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,QAAQ,UAAU;AAEpC,EAAAA,KAAI,EAAE;AACN,EAAAA,KAAI,oBAAoB;AACxB,EAAAA,KAAI,8CAA8C;AAClD,EAAAA,KAAI,EAAE;AAGN,MAAI,SAAS,MAAM,aAAa,WAAW,SAAS;AAGpD,MAAI,OAAO,WAAW,cAAc;AAClC,QAAI;AAEJ,QAAI,WAAW;AAGb,kBAAY;AAAA,IACd,OAAO;AACL,kBAAY,MAAM,mBAAmB,OAAO,QAAQ;AAAA,IACtD;AAEA,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,wEAAwE;AAC5E,IAAAA,KAAI,EAAE;AAEN,aAAS,MAAM,aAAa,WAAW,SAAS;AAEhD,QAAI,OAAO,WAAW,cAAc;AAClC,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,oEAAoE;AACxE,MAAAA,KAAI,kEAAkE;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,WAAW;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,QAAQ,OAAO,KAAK,IAAI;AAGhC,mBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAiB,oBAAI,KAAK,GAAE,YAAY;AAAA,EAC1C,CAAC;AAED,EAAAA,KAAI,sBAAsB,QAAQ,KAAK,GAAG,OAAO,KAAK,KAAK,MAAM,EAAE,EAAE;AACrE,EAAAA,KAAI,2BAA2B;AAG/B,QAAM,iBAAiB,WAAW;AAClC,MAAI,gBAAgB;AAElB,mBAAe,SAAS;AAGxB,QAAI,eAAe,mBAAmB,SAAS,GAAG;AAChD,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,gCAAgC;AAEpC,iBAAW,cAAc,eAAe,mBAAmB;AACzD,YAAI;AACF;AAAA,YACE;AAAA,YACA,eAAe,UAAU;AAAA,YACzB;AAAA,YACA,eAAe;AAAA,UACjB;AACA,UAAAA,KAAI,OAAO,UAAU,WAAW;AAAA,QAClC,QAAQ;AACN,UAAAA,KAAI,OAAO,UAAU,oBAAoB;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAEA,gBAAY,cAAc;AAAA,EAC5B;AAGA,QAAM,8BAA8B,QAAQ,SAAS;AAGrD,MAAI;AACF,UAAM,MAAM,GAAG,SAAS,0BAA0B;AAAA,MAChD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AAEA,EAAAA,KAAI,2CAA2C;AAC/C,EAAAA,KAAI,EAAE;AACR;;;AEnaO,SAAS,YAAkB;AAChC,QAAM,QAAQ,gBAAgB;AAE9B,MAAI,CAAC,OAAO;AACV,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,4BAA4B;AACxC,YAAQ,IAAI,EAAE;AACd;AAAA,EACF;AAEA,oBAAkB;AAElB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,sBAAsB,MAAM,KAAK,GAAG;AAChD,UAAQ,IAAI,8CAA8C;AAC1D,UAAQ,IAAI,4CAA4C;AACxD,UAAQ,IAAI,EAAE;AAChB;;;ACjBO,SAAS,YAAkB;AAChC,QAAM,QAAQ,gBAAgB;AAE9B,MAAI,CAAC,OAAO;AACV,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,gDAAgD;AAC5D,YAAQ,IAAI,2CAA2C;AACvD,YAAQ,IAAI,EAAE;AACd;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,sBAAsB;AAClC,MAAI,MAAM,MAAM;AACd,YAAQ,IAAI,qBAAqB,MAAM,IAAI,EAAE;AAAA,EAC/C;AACA,UAAQ,IAAI,qBAAqB,MAAM,KAAK,EAAE;AAC9C,UAAQ,IAAI,qBAAqB,MAAM,eAAe,EAAE;AACxD,UAAQ;AAAA,IACN,qBAAqB,MAAM,OAAO,MAAM,GAAG,CAAC,CAAC,OAAO,MAAM,OAAO,MAAM,EAAE,CAAC;AAAA,EAC5E;AACA,UAAQ,IAAI,EAAE;AAChB;;;ACrBA,SAASI,KAAI,KAAmB;AAC9B,UAAQ,OAAO,MAAM,MAAM,IAAI;AACjC;AAcA,eAAsB,iBAAgC;AACpD,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO;AACV,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,wCAAwC;AAC5C,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,QAAQ,UAAU;AAEpC,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,GAAG,SAAS,6BAA6B;AAAA,MAChE,SAAS,EAAE,aAAa,MAAM,OAAO;AAAA,IACvC,CAAC;AAED,QAAI,CAAC,KAAK,IAAI;AACZ,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,+BAA+B;AACnC,MAAAA,KAAI,EAAE;AACN,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAM,cAAc,KAAK,eAAe,CAAC;AAEzC,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,0BAA0B;AAC9B,IAAAA,KAAI,0BAA0B;AAG9B,eAAW,OAAO,gBAAgB;AAChC,YAAM,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;AAE5D,UAAI;AACJ,UAAI,CAAC,MAAM;AACT,qBAAa;AAAA,MACf,WAAW,KAAK,oBAAoB;AAClC,qBAAa;AAAA,MACf,OAAO;AACL,qBAAa,KAAK,iBAAiB;AAAA,MACrC;AAEA,MAAAA,KAAI,KAAK,IAAI,YAAY,OAAO,EAAE,CAAC,IAAI,UAAU,EAAE;AAAA,IACrD;AAEA,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,qDAAqD;AACzD,IAAAA,KAAI,EAAE;AAAA,EACR,QAAQ;AACN,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,yCAAyC;AAC7C,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACxEA,SAASC,KAAI,KAAmB;AAC9B,UAAQ,OAAO,MAAM,MAAM,IAAI;AACjC;AAcA,eAAsB,cAAc,UAAiC;AACnE,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO;AACV,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,wCAAwC;AAC5C,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,MAAM,eAAe;AAAA,IACzB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,YAAY,YAAY,MAAM,SAAS,YAAY;AAAA,EACrF;AACA,MAAI,CAAC,KAAK;AACR,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,uBAAuB,QAAQ,EAAE;AACrC,IAAAA,KAAI,gBAAgB,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAClE,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,eAAe,IAAI;AACzB,QAAM,cAAc,IAAI;AACxB,QAAM,SAAS,WAAW;AAC1B,QAAM,YAAY,QAAQ,UAAU;AAEpC,MAAI;AAEF,UAAM,WAAW,MAAM,MAAM,GAAG,SAAS,6BAA6B;AAAA,MACpE,SAAS,EAAE,aAAa,MAAM,OAAO;AAAA,IACvC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,+BAA+B;AACnC,MAAAA,KAAI,EAAE;AACN,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,WAAY,MAAM,SAAS,KAAK;AAGtC,UAAM,SAAS,SAAS,eAAe,CAAC,GAAG;AAAA,MACzC,CAAC,MAAM,EAAE,aAAa,gBAAgB,CAAC,EAAE;AAAA,IAC3C;AAEA,QAAI,CAAC,OAAO;AACV,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,sCAAsC,WAAW,GAAG;AACxD,MAAAA,KAAI,EAAE;AACN,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,OAAO,MAAM;AAAA,MACjB,GAAG,SAAS,6BAA6B,MAAM,EAAE;AAAA,MACjD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,aAAa,MAAM,OAAO;AAAA,MACvC;AAAA,IACF;AAEA,QAAI,KAAK,IAAI;AACX,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,uBAAuB,WAAW,GAAG;AACzC,MAAAA;AAAA,QACE,KAAK,WAAW;AAAA,MAClB;AACA,MAAAA,KAAI,EAAE;AAAA,IACR,OAAO;AACL,MAAAA,KAAI,EAAE;AACN,MAAAA,KAAI,0CAA0C;AAC9C,MAAAA,KAAI,EAAE;AACN,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,QAAQ;AACN,IAAAA,KAAI,EAAE;AACN,IAAAA,KAAI,yCAAyC;AAC7C,IAAAA,KAAI,EAAE;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ApBxFA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,YAAY,EACjB;AAAA,EACC;AACF,EACC,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,+DAA0D,EACtE,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,SAAS;AAAA,EACjB,SAASC,MAAK;AACZ,QAAKA,KAA8B,SAAS,uBAAuB;AACjE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,MAAM,iBAAiBA,IAAG;AAClC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,WAAW,EACnB,YAAY,qDAAqD,EACjE,OAAO,eAAe,wDAAwD,EAC9E;AAAA,EACC;AAAA,EACA;AACF,EACC;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,OAAO,YAAY;AACzB,QAAM,aAAa,OAAO;AAC5B,CAAC;AAEH,QACG,QAAQ,OAAO,EACf;AAAA,EACC;AACF,EACC,OAAO,YAAY;AAClB,QAAM,SAAS;AACjB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,uCAAuC,EACnD,OAAO,MAAM;AACZ,YAAU;AACZ,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,+CAA+C,EAC3D,OAAO,MAAM;AACZ,WAAS;AACX,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,4CAA4C,EACxD;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,OAAO,YAAgC;AAC7C,MAAI;AACF,UAAM,SAAS,QAAQ,KAAK;AAAA,EAC9B,SAASA,MAAK;AACZ,QAAKA,KAA8B,SAAS,uBAAuB;AACjE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,MAAM,iBAAiBA,IAAG;AAClC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,wCAAwC,EACpD,OAAO,MAAM;AACZ,YAAU;AACZ,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,iCAAiC,EAC7C,OAAO,MAAM;AACZ,YAAU;AACZ,CAAC;AAEH,QACG,QAAQ,oBAAoB,EAC5B,YAAY,+DAA+D,EAC3E,OAAO,OAAO,aAAqB;AAClC,MAAI;AACF,UAAM,WAAW,QAAQ;AAAA,EAC3B,SAASA,MAAK;AACZ,QAAKA,KAA8B,SAAS,uBAAuB;AACjE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,MAAM,mBAAmBA,IAAG;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,6CAA6C,EACzD,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,eAAe;AAAA,EACvB,SAASA,MAAK;AACZ,YAAQ,MAAM,WAAWA,IAAG;AAC5B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,uBAAuB,EAC/B,YAAY,yDAAyD,EACrE,OAAO,OAAO,aAAqB;AAClC,MAAI;AACF,UAAM,cAAc,QAAQ;AAAA,EAC9B,SAASA,MAAK;AACZ,YAAQ,MAAM,sBAAsBA,IAAG;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,uEAAkE,EAC9E,OAAO,YAAY;AAClB,QAAM,EAAE,oBAAAC,oBAAmB,IAAI,MAAM,OAAO,yBAAqB;AACjE,QAAM,EAAE,aAAAC,aAAY,IAAI,MAAM,OAAO,uBAAyB;AAC9D,QAAM,MAAM,GAAGD,mBAAkB;AACjC,UAAQ,IAAI,kCAAkC;AAC9C,UAAQ,IAAI,KAAK,GAAG;AAAA,CAAI;AACxB,UAAQ,IAAI,4DAA4D;AACxE,UAAQ,IAAI,wEAAwE;AACpF,QAAMC,aAAY,GAAG;AACvB,CAAC;AAEH,QAAQ,MAAM;","names":["existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","err","resolve","err","err","readFileSync","statSync","existsSync","mkdirSync","readFileSync","unlinkSync","writeFileSync","dirname","join","statSync","fileBuffer","readFileSync","err","existsSync","unlinkSync","existsSync","unlinkSync","log","resolve","err","log","sleep","resolve","err","log","log","err","DEFAULT_SERVER_URL","openBrowser"]}
|
|
1
|
+
{"version":3,"sources":["../bin/cli.ts","../src/wizard/server.ts","../src/wizard/html.ts","../src/wizard/routes.ts","../src/config/clients.ts","../src/config/storage.ts","../src/auth/credentials.ts","../src/cli/setup.ts","../src/cli/configure.ts","../src/proxy/stdio-server.ts","../src/proxy/http-client.ts","../src/proxy/local-filesystem.ts","../src/cli/serve.ts","../src/cli/status.ts","../src/cli/reset.ts","../src/cli/login.ts","../src/cli/connect.ts","../src/cli/logout.ts","../src/cli/whoami.ts","../src/cli/connections.ts","../src/cli/disconnect.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { runSetup } from \"../src/cli/setup.js\";\nimport { runConfigure } from \"../src/cli/configure.js\";\nimport { runServe } from \"../src/cli/serve.js\";\nimport { runStatus } from \"../src/cli/status.js\";\nimport { runReset } from \"../src/cli/reset.js\";\nimport { runLogin } from \"../src/cli/login.js\";\nimport { runLogout } from \"../src/cli/logout.js\";\nimport { runWhoami } from \"../src/cli/whoami.js\";\nimport { runConnect } from \"../src/cli/connect.js\";\nimport { runConnections } from \"../src/cli/connections.js\";\nimport { runDisconnect } from \"../src/cli/disconnect.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"cortex-mcp\")\n .description(\n \"Connect your AI tools to Asana, GitHub, Microsoft 365, Monday.com, Salesforce, Vercel & Supabase via Cortex — supports Claude, Cursor, Codex & more\"\n )\n .version(\"1.0.0\");\n\nprogram\n .command(\"setup\")\n .description(\"Interactive setup wizard — configure MCPs and AI clients\")\n .action(async () => {\n try {\n await runSetup();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Setup failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"configure\")\n .description(\"Non-interactive configuration for a specific client\")\n .option(\"--key <key>\", \"Cortex API key (uses built-in default if not provided)\")\n .requiredOption(\n \"--client <client>\",\n \"Client to configure (claude-desktop, claude-code, cursor, codex, stdio)\"\n )\n .option(\n \"--mcps <mcps>\",\n \"Comma-separated MCP names (default: all)\",\n )\n .action(async (options) => {\n await runConfigure(options);\n });\n\nprogram\n .command(\"serve\")\n .description(\n \"Start stdio MCP proxy server (for OpenClaw and other stdio clients)\"\n )\n .action(async () => {\n await runServe();\n });\n\nprogram\n .command(\"status\")\n .description(\"Show current Cortex MCP configuration\")\n .action(() => {\n runStatus();\n });\n\nprogram\n .command(\"reset\")\n .description(\"Remove all Cortex MCP entries from AI clients\")\n .action(() => {\n runReset();\n });\n\nprogram\n .command(\"login\")\n .description(\"Sign in with your company Okta SSO account\")\n .option(\n \"--email <email>\",\n \"Company email (use if your Okta email differs from your directory email)\"\n )\n .action(async (options: { email?: string }) => {\n try {\n await runLogin(options.email);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Login failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"logout\")\n .description(\"Sign out and remove stored credentials\")\n .action(() => {\n runLogout();\n });\n\nprogram\n .command(\"whoami\")\n .description(\"Show current authenticated user\")\n .action(() => {\n runWhoami();\n });\n\nprogram\n .command(\"connect <provider>\")\n .description(\"Connect your personal account to an MCP service (e.g., asana)\")\n .action(async (provider: string) => {\n try {\n await runConnect(provider);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n process.exit(0);\n }\n console.error(\"Connect failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"connections\")\n .description(\"List your OAuth connections to MCP services\")\n .action(async () => {\n try {\n await runConnections();\n } catch (err) {\n console.error(\"Failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"disconnect <provider>\")\n .description(\"Remove your personal OAuth connection to an MCP service\")\n .action(async (provider: string) => {\n try {\n await runDisconnect(provider);\n } catch (err) {\n console.error(\"Disconnect failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"connect-mobile\")\n .description(\"Connect Cortex to Claude on mobile — opens setup page in browser\")\n .action(async () => {\n const { DEFAULT_SERVER_URL } = await import(\"../src/constants.js\");\n const { openBrowser } = await import(\"../src/utils/browser.js\");\n const url = `${DEFAULT_SERVER_URL}/connect`;\n console.log(\"\\nOpening Cortex connect page...\");\n console.log(` ${url}\\n`);\n console.log(\"Follow the steps to connect Cortex to your Claude account.\");\n console.log(\"Once connected, tools will be available on web, desktop, and mobile.\\n\");\n await openBrowser(url);\n });\n\nprogram.parse();\n","import * as http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { getWizardHtml } from \"./html.js\";\nimport { handleApiRoute } from \"./routes.js\";\n\nexport interface WizardServerOptions {\n serverUrl: string;\n}\n\nexport interface WizardServer {\n port: number;\n close: () => Promise<void>;\n waitForCompletion: () => Promise<void>;\n}\n\n/**\n * Start a local HTTP server that serves the setup wizard UI and API routes.\n * Binds to 127.0.0.1 on a random available port (port 0).\n */\nexport function startWizardServer(\n options: WizardServerOptions,\n): Promise<WizardServer> {\n return new Promise((resolve, reject) => {\n let completionResolve: () => void;\n const completionPromise = new Promise<void>((r) => {\n completionResolve = r;\n });\n\n const onComplete = (): void => {\n completionResolve();\n };\n\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://localhost`);\n const path = url.pathname;\n\n // API routes\n if (path.startsWith(\"/api/\")) {\n try {\n const handled = await handleApiRoute(\n path,\n url.searchParams,\n req,\n res,\n options,\n onComplete,\n );\n if (!handled) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: msg }));\n }\n return;\n }\n\n // Serve wizard HTML for root and any other path\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(getWizardHtml());\n });\n\n server.listen(0, \"127.0.0.1\", () => {\n const addr = server.address() as AddressInfo;\n resolve({\n port: addr.port,\n close: () =>\n new Promise<void>((r) => {\n server.close(() => r());\n }),\n waitForCompletion: () => completionPromise,\n });\n });\n\n server.on(\"error\", reject);\n });\n}\n","/**\n * Wizard HTML — single-page setup wizard served by the local HTTP server.\n * Styled with Sonance brand guidelines: Montserrat font, dark mode,\n * \"The Beam\" cyan accent (#00A3E1), Liquid Glass aesthetic.\n */\nexport function getWizardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Cortex MCP Setup</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: #1a1f24;\n color: #fff;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1.6;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n overflow-x: hidden;\n }\n\n /* ── Blue Orb Background ─────────────── */\n .orb {\n position: fixed;\n border-radius: 50%;\n pointer-events: none;\n z-index: 0;\n will-change: transform;\n transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);\n }\n .orb-primary {\n width: 700px;\n height: 700px;\n background: radial-gradient(circle, rgba(0, 163, 225, 0.07) 0%, transparent 70%);\n filter: blur(100px);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n .orb-secondary {\n width: 500px;\n height: 500px;\n background: radial-gradient(circle, rgba(120, 80, 220, 0.05) 0%, transparent 70%);\n filter: blur(80px);\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n #app {\n width: 100%;\n max-width: 900px;\n padding: 24px;\n position: relative;\n z-index: 1;\n }\n\n /* ── Persistent Header ────────────────── */\n .wizard-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px 24px;\n }\n .wizard-header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n .header-logo {\n height: 18px;\n width: auto;\n opacity: 0.85;\n }\n .header-divider {\n width: 1px;\n height: 16px;\n background: rgba(255, 255, 255, 0.15);\n }\n .header-product {\n font-size: 13px;\n font-weight: 500;\n color: #00A3E1;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n .header-steps {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .header-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.1);\n transition: all 0.4s ease;\n }\n .header-dot.done { background: #00A3E1; }\n .header-dot.current { background: #00A3E1; opacity: 0.5; }\n\n /* ── Steps ─────────────────────────────── */\n .step { display: none; }\n .step.active { display: block; }\n .step.active.slide-forward { animation: slideInRight 0.35s cubic-bezier(0.16, 1, 0.3, 1); }\n .step.active.slide-backward { animation: slideInLeft 0.35s cubic-bezier(0.16, 1, 0.3, 1); }\n\n @keyframes slideInRight {\n from { opacity: 0; transform: translateX(40px); }\n to { opacity: 1; transform: translateX(0); }\n }\n @keyframes slideInLeft {\n from { opacity: 0; transform: translateX(-40px); }\n to { opacity: 1; transform: translateX(0); }\n }\n\n /* ── Liquid Glass Card ─────────────────── */\n .card {\n background: rgba(255, 255, 255, 0.03);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 20px;\n padding: 40px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n }\n\n /* ── Typography ────────────────────────── */\n h1 {\n font-size: 26px;\n font-weight: 300;\n letter-spacing: -0.02em;\n color: #fff;\n margin-bottom: 8px;\n }\n h2 {\n font-size: 20px;\n font-weight: 300;\n letter-spacing: -0.02em;\n color: #fff;\n margin-bottom: 6px;\n }\n .subtitle {\n color: #8f999f;\n font-size: 14px;\n font-weight: 400;\n margin-bottom: 28px;\n line-height: 1.6;\n }\n\n /* ── Welcome ───────────────────────────── */\n .welcome-tagline {\n font-size: 15px;\n color: #00A3E1;\n font-weight: 400;\n margin-bottom: 8px;\n }\n .welcome-hero { padding: 16px 0 28px; }\n .welcome-footer {\n font-size: 11px;\n color: #6b7780;\n margin-top: 20px;\n letter-spacing: 0.04em;\n }\n\n /* ── Buttons ────────────────────────────── */\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 11px 24px;\n border-radius: 12px;\n font-family: 'Montserrat', sans-serif;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n cursor: pointer;\n border: none;\n transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n text-decoration: none;\n }\n .btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n .btn-primary {\n background: #333F48;\n color: #fff;\n }\n .btn-primary:hover:not(:disabled) {\n background: #3a444c;\n box-shadow: 0 0 20px rgba(0, 163, 225, 0.15);\n }\n .btn-primary:active:not(:disabled) { transform: scale(0.98); }\n\n .btn-accent {\n background: #00A3E1;\n color: #fff;\n }\n .btn-accent:hover:not(:disabled) {\n background: #0093cb;\n box-shadow: 0 0 24px rgba(0, 163, 225, 0.3);\n }\n .btn-accent:active:not(:disabled) { transform: scale(0.98); }\n\n .btn-secondary {\n background: rgba(255, 255, 255, 0.03);\n color: #D9D9D6;\n border: 1px solid rgba(255, 255, 255, 0.08);\n }\n .btn-secondary:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.06);\n border-color: rgba(255, 255, 255, 0.12);\n }\n\n .btn-sm { padding: 7px 16px; font-size: 12px; }\n .btn-block { width: 100%; }\n\n .btn-row {\n display: flex;\n gap: 10px;\n margin-top: 28px;\n }\n .btn-row .btn { flex: 1; }\n\n /* ── Spinner ────────────────────────────── */\n .spinner {\n display: inline-block;\n width: 14px;\n height: 14px;\n border: 2px solid rgba(255, 255, 255, 0.1);\n border-top-color: #00A3E1;\n border-radius: 50%;\n animation: spin 0.7s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n\n /* ── Status Messages ───────────────────── */\n .status {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 400;\n margin-top: 16px;\n }\n .status-info {\n background: rgba(0, 163, 225, 0.08);\n color: #7dd3fc;\n border: 1px solid rgba(0, 163, 225, 0.15);\n }\n .status-success {\n background: rgba(76, 175, 80, 0.1);\n color: #4CAF50;\n border: 1px solid rgba(76, 175, 80, 0.2);\n }\n .status-error {\n background: rgba(239, 83, 80, 0.1);\n color: #EF5350;\n border: 1px solid rgba(239, 83, 80, 0.2);\n }\n .status-waiting {\n background: rgba(0, 163, 225, 0.06);\n color: #8f999f;\n border: 1px solid rgba(255, 255, 255, 0.06);\n }\n\n /* ── Auth Phases ──────────────────────── */\n .auth-phases {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 16px;\n }\n .auth-phase {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: #6b7780;\n padding: 10px 14px;\n border-radius: 10px;\n transition: all 0.3s ease;\n }\n .auth-phase.active {\n color: #7dd3fc;\n background: rgba(0, 163, 225, 0.06);\n }\n .auth-phase.done { color: #4CAF50; }\n .auth-phase.pending { opacity: 0.35; }\n .phase-icon { font-size: 12px; flex-shrink: 0; width: 14px; text-align: center; }\n\n /* ── MCP Section ──────────────────────── */\n .mcp-section { margin-bottom: 16px; }\n .mcp-section-title {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n margin-bottom: 8px;\n }\n\n /* ── MCP List ──────────────────────────── */\n .mcp-list {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 6px;\n margin-bottom: 8px;\n }\n\n .mcp-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 11px 14px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n cursor: pointer;\n transition: all 0.15s ease;\n user-select: none;\n position: relative;\n }\n .mcp-item:hover { border-color: rgba(0, 163, 225, 0.3); }\n .mcp-tooltip {\n display: none;\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background: #2a3038;\n color: #d0d5da;\n font-size: 11px;\n font-weight: 400;\n padding: 6px 10px;\n border-radius: 6px;\n white-space: nowrap;\n pointer-events: none;\n z-index: 10;\n border: 1px solid rgba(255,255,255,0.1);\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n }\n .mcp-tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #2a3038;\n }\n .mcp-item:hover .mcp-tooltip { display: block; }\n .mcp-item.checked {\n border-color: rgba(0, 163, 225, 0.4);\n background: rgba(0, 163, 225, 0.06);\n }\n .mcp-item input[type=\"checkbox\"] { display: none; }\n .mcp-item.required { cursor: default; }\n .mcp-item.required .mcp-check { background: #00A3E1; border-color: #00A3E1; }\n .mcp-item.required .mcp-check-icon { display: block; }\n .mcp-item.user-connected { border-color: rgba(76, 175, 80, 0.4); cursor: default; }\n .mcp-item.user-connected .mcp-check { background: #4CAF50; border-color: #4CAF50; }\n .mcp-item.user-connected .mcp-check-icon { display: block; }\n .mcp-item.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }\n .mcp-item.disabled .mcp-check { background: transparent; border-color: rgba(255,255,255,0.1); }\n .mcp-coming-soon-label {\n position: absolute;\n top: 6px;\n right: 8px;\n font-size: 9px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255, 255, 255, 0.35);\n background: rgba(255, 255, 255, 0.06);\n padding: 2px 6px;\n border-radius: 4px;\n }\n .mcp-connected-badge {\n display: block;\n font-size: 11px;\n color: #4CAF50;\n margin-top: 2px;\n }\n .mcp-connected-label {\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 9px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #4CAF50;\n }\n .mcp-required-label {\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 9px;\n font-weight: 500;\n color: #8f999f;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .mcp-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.04);\n flex-shrink: 0;\n }\n .mcp-icon svg { width: 16px; height: 16px; fill: #8f999f; }\n .mcp-item.checked .mcp-icon svg { fill: #00A3E1; }\n\n .mcp-check {\n width: 18px;\n height: 18px;\n border: 1.5px solid rgba(255, 255, 255, 0.2);\n border-radius: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: all 0.15s;\n }\n .mcp-item.checked .mcp-check,\n .client-item.checked .mcp-check {\n background: #00A3E1;\n border-color: #00A3E1;\n }\n .mcp-check-icon { display: none; color: #fff; font-size: 11px; font-weight: 600; }\n .mcp-item.checked .mcp-check-icon,\n .client-item.checked .mcp-check-icon { display: block; }\n\n .mcp-info { flex: 1; min-width: 0; }\n .mcp-name { font-size: 13px; font-weight: 500; color: #fff; }\n .mcp-desc { font-size: 11px; color: #8f999f; display: block; margin-top: 2px; font-weight: 400; }\n .mcp-badge {\n font-size: 10px;\n font-weight: 500;\n padding: 1px 7px;\n border-radius: 6px;\n margin-left: 8px;\n letter-spacing: 0.03em;\n text-transform: uppercase;\n }\n .mcp-badge.company {\n background: rgba(76, 175, 80, 0.12);\n color: #4CAF50;\n border: 1px solid rgba(76, 175, 80, 0.2);\n }\n .mcp-badge.personal {\n background: rgba(255, 183, 77, 0.12);\n color: #FFB74D;\n border: 1px solid rgba(255, 183, 77, 0.2);\n }\n\n /* ── Client List ───────────────────────── */\n .client-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 8px;\n }\n .client-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n cursor: pointer;\n user-select: none;\n transition: all 0.15s ease;\n }\n .client-item:hover { border-color: rgba(0, 163, 225, 0.3); }\n .client-item.checked {\n border-color: rgba(0, 163, 225, 0.4);\n background: rgba(0, 163, 225, 0.06);\n }\n .client-item.disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n .client-item.disabled:hover { border-color: rgba(255, 255, 255, 0.06); }\n .client-item.already-configured { border-color: rgba(76, 175, 80, 0.4); cursor: default; }\n .client-item.already-configured .mcp-check { background: #4CAF50; border-color: #4CAF50; }\n .client-item.already-configured .mcp-check-icon { display: block; }\n .client-item.already-configured .client-status { color: #4CAF50; }\n\n .client-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.04);\n flex-shrink: 0;\n }\n .client-icon svg { width: 16px; height: 16px; fill: #8f999f; }\n .client-item.checked .client-icon svg { fill: #00A3E1; }\n\n .client-name { font-size: 13px; font-weight: 500; color: #fff; }\n .client-status {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-left: auto;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n /* ── Connection List ───────────────────── */\n .conn-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 8px;\n }\n .conn-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.06);\n transition: all 0.2s ease;\n }\n .conn-item.connected {\n border-left: 3px solid #4CAF50;\n background: rgba(76, 175, 80, 0.04);\n }\n .conn-name { font-size: 13px; font-weight: 500; color: #fff; flex-shrink: 0; }\n .conn-status { font-size: 12px; color: #8f999f; flex: 1; text-align: right; font-weight: 400; }\n .conn-status.connected { color: #4CAF50; }\n .conn-item.company-default { cursor: default; }\n\n /* ── Done / Success ───────────────────── */\n .success-circle {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: rgba(0, 163, 225, 0.1);\n border: 2px solid #00A3E1;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n animation: successPop 0.5s cubic-bezier(0.16, 1, 0.3, 1);\n }\n .success-check {\n font-size: 28px;\n color: #00A3E1;\n animation: checkDraw 0.3s ease 0.2s both;\n }\n @keyframes successPop {\n from { transform: scale(0.5); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n @keyframes checkDraw {\n from { opacity: 0; transform: scale(0.5); }\n to { opacity: 1; transform: scale(1); }\n }\n\n .next-steps {\n text-align: left;\n margin-top: 24px;\n padding: 20px;\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.02);\n border: 1px solid rgba(255, 255, 255, 0.06);\n }\n .next-steps h3 {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-bottom: 12px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .next-step-item {\n font-size: 13px;\n color: #D9D9D6;\n padding: 4px 0;\n font-weight: 400;\n }\n .next-step-item code {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px;\n background: rgba(0, 163, 225, 0.1);\n color: #7dd3fc;\n padding: 2px 6px;\n border-radius: 4px;\n }\n\n /* ── Summary ───────────────────────────── */\n .summary-section { margin-bottom: 24px; }\n .summary-section h3 {\n font-size: 11px;\n font-weight: 500;\n color: #8f999f;\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n .summary-item {\n font-size: 13px;\n color: #D9D9D6;\n padding: 3px 0;\n font-weight: 400;\n }\n .check-icon { color: #4CAF50; margin-right: 8px; }\n\n /* ── Utility ───────────────────────────── */\n .text-center { text-align: center; }\n .mt-4 { margin-top: 16px; }\n .mt-2 { margin-top: 8px; }\n .text-sm { font-size: 12px; font-weight: 400; }\n .text-muted { color: #6b7780; }\n .hidden { display: none !important; }\n\n /* ── Stdio Snippet ─────────────────────── */\n .stdio-snippet {\n background: rgba(0, 0, 0, 0.3);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 10px;\n padding: 16px;\n margin-top: 12px;\n position: relative;\n }\n .stdio-snippet pre {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px;\n color: #7dd3fc;\n white-space: pre-wrap;\n word-break: break-all;\n margin: 0;\n line-height: 1.5;\n }\n .stdio-snippet .copy-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n padding: 4px 10px;\n font-size: 11px;\n border-radius: 6px;\n background: rgba(0, 163, 225, 0.15);\n color: #00A3E1;\n border: 1px solid rgba(0, 163, 225, 0.3);\n cursor: pointer;\n font-family: 'Montserrat', sans-serif;\n }\n .stdio-snippet .copy-btn:hover {\n background: rgba(0, 163, 225, 0.25);\n }\n\n /* ── Mobile ────────────────────────────── */\n @media (max-width: 640px) {\n #app { padding: 16px; }\n .card { padding: 24px 20px; border-radius: 16px; }\n .mcp-list { grid-template-columns: 1fr; }\n h1 { font-size: 22px; }\n h2 { font-size: 18px; }\n .wizard-header { flex-direction: column; gap: 12px; align-items: flex-start; }\n .orb-primary { width: 400px; height: 400px; }\n .orb-secondary { width: 300px; height: 300px; }\n }\n </style>\n</head>\n<body>\n <!-- Blue Orb Background -->\n <div class=\"orb orb-primary\" id=\"orb-primary\"></div>\n <div class=\"orb orb-secondary\" id=\"orb-secondary\"></div>\n\n <div id=\"app\">\n\n <!-- Persistent Header -->\n <header class=\"wizard-header\">\n <div class=\"wizard-header-left\">\n <img class=\"header-logo\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAA/CAMAAAD5XG5xAAAANlBMVEVMaXHa2tfZ2dfa2tfQ1tbZ2dbZ2dbZ2dba2tfZ2dYAo+IApeQApeQApuYApuXn5+Tf39wArO3XFnpPAAAAD3RSTlMAv3SNEzKm+9xV/Gg11aD4UYCxAAAACXBIWXMAAC4jAAAuIwF4pT92AAAIgElEQVR42u2c6ZLbIAyAw2nYTRby/i9bLjs+0GHXyXam1o9OJ2ts0IcQCMHtdskll1xyySWXXHLJJf+pGO+llN4bvauYNl6qJHJvwTPknuTjH20tlu9ssfbWDTFkiYMT0nDLyalcCKyCRiTBn/LpCctq6vfz6+uLRSS/U+DvlPmzdLdVYmpwURX8UisIUbBaXX77KOl7g/Acm7LDvByroMktcahqZIghcoDoRwLy/OEAye8MAn3EpkccyTVVbdViC3UvF0ZyXYlgfYyrWo1F6pdS7Skk2saxXHl/LRki3v9NeUjgykuPaK6B8EzE59oFhT2iwhBxIL4pahpL8Ba7GbmeRAHVNZZvDM4J4fII1NSMDxt+CLVuMRWzVkwFQ1QUEFw1CUhkAXkUICwT8aWywaMWggNJHbAqKo9UdtHiQUJAUBtxMI8Y0/BfdKC1Vy7W6juPGnjtHXL0bNpMBZHR2rTe4f8eSDOQr4fmAhkGcxhIGkcKDme9njxvanEEW5yARCdhUR4eQlaqN2NngNSmRahGZDZjbOHkDAUEUQ0XyKMBeX6zgUTEe+FA/FAbtnbiXgSoxQlIsLe9kjXb4WuA3+c8Qm/s9LUjgfo2FQemGiaQe/bo34nKk2EiGUjuK4j3QoG0cURpuMX+FCDZ2cWu3mWEJ0O2VEEBQ23AuqJpBoKohgnkJ6Mo4xbDRKqFRMx7YUBM4QHYfW3xtj2HgJR3GWBOYeD5SKqevGF/hvRdgEiHqYYH5F4HK11MhAMkujLsBLkfiC71RWw69v56BEj+EthVNTaFhHmMRBQIJBoTEcfOA5IMpLhznokUC9Hl32h2A8kjNOZ+kko6fz0CJKsnyP0QiSm9hRtuKom81oAcDQvIffTmOv/nwQJiSleBNAsD8QGfheRHzO0cIAGfgULrNmLRW5kJBAimGhaQnylqkv/3vPOAtDnMPiClNXu1dBCI3w+kVG8gYlbwkDQCQVTDAXJ/rQjvnMXhCAQxbxBI6YH756/HLCTuHrJK9RRnstCtzQQEVg0HyM8srPjDCDGOQNL3oSZDQFg98DQgw274ef1Ju9w6u9UIENiKGED03Co4JjIBaVMSwwfiDxrIsWnvbvrFpixvPt3riC8goGNnAFnG3Rkm8gICei8ISG5JNJ8CUmon9M4Cnrfi7LmIGRBINTQQ/VjYxJ2Ows+AQN4LAJJHrChunwJSxo0g+B1ARHxGvmhH58E5EEA1NJDv1cTqQZrIHAjgvQAgPnJ85mlA2qp7UEwj0QP3K0AMYAGkrxoayHp1/k2ayBxIC6hKHhAZDs15DwMpvTSH9F9R5XOmZWU25nEgfcdOAtkuzh9PIgq/ANJ37AAQJLj0HiA1cls2mqwkd+19ILZ5lqYuCSB1d3DVXhLINnxFxk+WQLreCwDCHaNPA1LiutMGsRMKtRTJnnKY/ti7AtJTDQXkvtV+9vKoiayA9LwXAKT4wttBIEPahgVEEMkKcdq1jwOSPKL4QAYWkI5qKCCP53OzBfKdfsNMZA2k470QIOI4kAgIkU9Rdl+nPXkkeUSxR1RgzbkBslUNBeQ7yXpKpXs/IkA6jv09QMAMB8dMOCJSKc63kK1jZyc57AzaLTvSxrG/BUgEc4AcK10u572FMXlE8udOfwNks2L/DJCN93qHD4nCAyLZM2kjRfMoveWQPHmW1VPNh4Csvde/M8vqZjICiWX8dSuwoOoBWanmU0BW3utfWYdgWS9bb2HIvMNFQzQPyFI1nwKyCsUDQBR7THgrEHDTr0wduLEsIri4Dql5Goj2uJg9QJaOHYtl2d8HUrvPtg2W2WGgsa0PZLFix4CkpGlczB4gC+8FRXuHo07kZCDVf8vupq/gjVg97QBA5qpBgGgqgRnSAABk7r2g/RBxdMw6G0h/3lp3/A0nKtxtHgRkphoEiAzI4jci2bsQkJn3goBIZhd8OxDdf6EKHLeugA1DGMhLNTAQjYeHkLx6CMhsxY7uqR8JwJ8NBHihoZLXJ7V3B14QyMuxw0B8xawhceBwDwJ5OXYw60RRaXK/ayHVOxD1E5CBIEAmxw4DoRZpEvwsDGTSN5yXNRzbNDy2p+53ZzVqPFX61cbbTiBjMRCIp3bH4BkRAmT0XnDmYvVcHj+geE4qaVKB37nUbvXDPiWRFFoMSFWNBYEIcsUIui4MSPNeCs/txc8Yxc7RjQNAis7lviyFcREewBNvMiIZwyiQ6j/d0Nc7I2xjoMA2BqQuucpnHfYA3Hf7XmY/EBOQnBMLZygi53Wm0xL2dgBIC8UDQDhRJWjdigJpjh3ZpKgPAH3XNHX8PZDxnFYn56SoFQ5gtENDEjpPBFcEB9Ice/fLrCxLE/uLBhxI3XfDdo1kOzxpOpHYqkR7yvmQepIxnTZRpqdWj2dGxM2Ru/GMob0dBNJU0wPCyyAUfc9HAGm5N8jGh6wNW5+qzIHxogl10ixLq/FQdMpuMGUyb9LZ0kAd69b1FG45lNqO75rx+G7EzkVTQJoD3QJhxpmBjEkKSF1yYjtR7Rj47NxxarFtmuqeiy5O2GKisdPwZU+9rHfr7nqMePqVHM+prw+4o6epaSDVsW+BcHNYRTe0QwEZHTueLzUpKjU4HVNvm6sR8KbkOfUAVChfrDFuvY8RoYirtRrJmKsyiyTFQCRBkkCAxHntmBGlEv20Xa9syOOvzJscli2GNEUFQuEKaS+G8LrDI+akOckIFdRclfklKdFROanlrhNPTMbD9q4TRZebJhydvieRDjn7gqOSDsSwuhUmwtfPuECJQa8DEg18GriU56b6lluEYrv5QzDKmTx2Eq5ZdoZXlX7jJSD7fO2F732WKJ4+oTgpB9x7k5SlRFP3QJmy6bYzjJavj5LyV67L+h3RB28Wu+SSSy655JJLLrnkkv9a/gAogKu+LRoCQQAAAABJRU5ErkJggg==\" alt=\"Sonance\">\n <span class=\"header-divider\"></span>\n <span class=\"header-product\">Cortex</span>\n </div>\n <div class=\"header-steps\" id=\"header-steps\"></div>\n </header>\n\n <!-- Step 1: Welcome -->\n <div id=\"step-welcome\" class=\"step active slide-forward\">\n <div class=\"card text-center\">\n <div class=\"welcome-hero\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\" style=\"margin-bottom: 20px;\">\n <circle cx=\"24\" cy=\"24\" r=\"23\" stroke=\"#00A3E1\" stroke-width=\"1.5\" opacity=\"0.3\"/>\n <circle cx=\"24\" cy=\"24\" r=\"16\" stroke=\"#00A3E1\" stroke-width=\"1\" opacity=\"0.15\"/>\n <circle cx=\"24\" cy=\"24\" r=\"6\" fill=\"#00A3E1\" opacity=\"0.8\"/>\n </svg>\n <h1>Connect Your AI Tools</h1>\n <p class=\"welcome-tagline\">One setup. 10+ platforms. All your AI clients.</p>\n <p class=\"subtitle\" style=\"margin-bottom: 0;\">\n GitHub, Salesforce, M365, Slack, Asana, and more — configured in under 2 minutes.\n </p>\n </div>\n <button class=\"btn btn-accent btn-block\" onclick=\"goToStep('signin')\">Get Started</button>\n <p class=\"welcome-footer\">Powered by Sonance</p>\n </div>\n </div>\n\n <!-- Step 2: Sign In -->\n <div id=\"step-signin\" class=\"step\">\n <div class=\"card\">\n <h2>Sign In</h2>\n <p class=\"subtitle\">Authenticate with your company SSO</p>\n <div id=\"signin-content\"></div>\n </div>\n </div>\n\n <!-- Step 3: Select MCPs -->\n <div id=\"step-mcps\" class=\"step\">\n <div class=\"card\">\n <h2>Select Integrations</h2>\n <p class=\"subtitle\">Choose which services to connect. You can enable more later.</p>\n <div id=\"mcp-list-container\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('signin')\">Back</button>\n <button class=\"btn btn-primary\" onclick=\"saveMcps()\">Continue</button>\n </div>\n </div>\n </div>\n\n <!-- Step 4: Configure Clients -->\n <div id=\"step-clients\" class=\"step\">\n <div class=\"card\">\n <h2>Configure AI Clients</h2>\n <p class=\"subtitle\">Select which AI tools to set up with Cortex</p>\n <div id=\"client-list\" class=\"client-list\"></div>\n <div id=\"client-result\" class=\"hidden\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('mcps')\">Back</button>\n <button id=\"btn-configure\" class=\"btn btn-primary\" onclick=\"configureClients()\">Configure</button>\n </div>\n </div>\n </div>\n\n <!-- Step 5: Connect Accounts -->\n <div id=\"step-connect\" class=\"step\">\n <div class=\"card\">\n <h2>Connect Accounts</h2>\n <p class=\"subtitle\">Link personal accounts for services that require them (optional)</p>\n <div id=\"conn-list\" class=\"conn-list\"></div>\n <div class=\"btn-row\">\n <button class=\"btn btn-secondary\" onclick=\"goToStep('clients')\">Back</button>\n <button class=\"btn btn-primary\" onclick=\"goToStep('done')\">Continue</button>\n </div>\n </div>\n </div>\n\n <!-- Step 6: Done -->\n <div id=\"step-done\" class=\"step\">\n <div class=\"card text-center\">\n <div class=\"success-circle\"><span class=\"success-check\">✓</span></div>\n <h2>Setup Complete</h2>\n <p class=\"subtitle\">Your AI clients are now connected to Cortex</p>\n <div id=\"summary\" style=\"text-align: left; margin: 24px 0;\"></div>\n <div class=\"next-steps\">\n <h3>What's Next</h3>\n <div class=\"next-step-item\">1. Restart your AI clients to load the new tools</div>\n <div class=\"next-step-item\">2. Try: “List my GitHub repos” or “Show my Asana tasks”</div>\n <div class=\"next-step-item\">3. Run <code>cortex-mcp connect <provider></code> to add more accounts later</div>\n </div>\n <div style=\"margin-top: 24px;\">\n <button class=\"btn btn-accent btn-block\" onclick=\"finishSetup()\">Close</button>\n </div>\n <p class=\"text-sm text-muted mt-4\">Config saved to ~/.cortex-mcp/config.json</p>\n </div>\n </div>\n\n </div>\n\n <script>\n // ── State ────────────────────────────────\n var state = {\n credentials: null,\n availableMcps: [],\n selectedMcps: [],\n detectedClients: [],\n selectedClients: [],\n configuredClients: [],\n connections: [],\n };\n\n // ── Step Index Map ───────────────────────\n var STEP_ORDER = ['welcome', 'signin', 'mcps', 'clients', 'connect', 'done'];\n var currentStepIndex = 0;\n\n // ── MCP Icons (inline SVG) ──────────────\n var MCP_ICONS = {\n github: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z\"/></svg>',\n supabase: '<svg viewBox=\"0 0 16 16\"><path d=\"M9.3 14.7c-.2.3-.7.1-.7-.3V9.3h4.6c.5 0 .8-.6.5-1L8.7 1.3c-.2-.3-.7-.1-.7.3v5.1H3.4c-.5 0-.8.6-.5 1l5 7z\"/></svg>',\n vercel: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 1l7 13H1z\"/></svg>',\n m365: '<svg viewBox=\"0 0 16 16\"><rect x=\"1\" y=\"1\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"9\" y=\"1\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"1\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/><rect x=\"9\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/></svg>',\n salesforce: '<svg viewBox=\"0 0 16 16\"><path d=\"M6.7 3.3a3 3 0 014.8.5 2.5 2.5 0 013 2.4 2.5 2.5 0 01-1.5 2.3 2.8 2.8 0 01-3.5 2.2A3 3 0 016 12.5a3 3 0 01-3.3-1.8A2.5 2.5 0 011 8.5a2.5 2.5 0 011.8-2.4 2.8 2.8 0 013.9-2.8z\"/></svg>',\n slack: '<svg viewBox=\"0 0 16 16\"><path d=\"M3.5 10a1.5 1.5 0 11-3 0 1.5 1.5 0 011.5-1.5H3.5V10zm.75 0a1.5 1.5 0 113 0v3.5a1.5 1.5 0 11-3 0V10zM6 3.5a1.5 1.5 0 110-3 1.5 1.5 0 011.5 1.5V3.5H6zm0 .75a1.5 1.5 0 110 3H2.5a1.5 1.5 0 110-3H6zM12.5 6a1.5 1.5 0 113 0 1.5 1.5 0 01-1.5 1.5H12.5V6zm-.75 0a1.5 1.5 0 11-3 0V2.5a1.5 1.5 0 113 0V6zM10 12.5a1.5 1.5 0 110 3 1.5 1.5 0 01-1.5-1.5v-1.5H10zm0-.75a1.5 1.5 0 110-3h3.5a1.5 1.5 0 110 3H10z\"/></svg>',\n asana: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"4\" r=\"2.5\"/><circle cx=\"3.5\" cy=\"11\" r=\"2.5\"/><circle cx=\"12.5\" cy=\"11\" r=\"2.5\"/></svg>',\n monday: '<svg viewBox=\"0 0 16 16\"><circle cx=\"4\" cy=\"10\" r=\"2\" /><circle cx=\"8\" cy=\"6\" r=\"2\" /><circle cx=\"12\" cy=\"10\" r=\"2\" /></svg>',\n powerbi: '<svg viewBox=\"0 0 16 16\"><rect x=\"2\" y=\"8\" width=\"3\" height=\"6\" rx=\"1\"/><rect x=\"6.5\" y=\"5\" width=\"3\" height=\"9\" rx=\"1\"/><rect x=\"11\" y=\"2\" width=\"3\" height=\"12\" rx=\"1\"/></svg>',\n bestbuy: '<svg viewBox=\"0 0 16 16\"><path d=\"M2 3h12a1 1 0 011 1v8a1 1 0 01-1 1H2a1 1 0 01-1-1V4a1 1 0 011-1zm3 3v4h2V6H5zm4 0v4h2V6H9z\"/></svg>',\n mailchimp: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 2a6 6 0 100 12A6 6 0 008 2zM6.5 10.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm3 0a1.5 1.5 0 110-3 1.5 1.5 0 010 3z\"/></svg>',\n };\n\n // ── Client Icons (inline SVG) ───────────\n var CLIENT_ICONS = {\n 'claude-desktop': '<svg viewBox=\"0 0 16 16\"><path d=\"M10.5 2H5.5a3 3 0 00-3 3v6a3 3 0 003 3h5a3 3 0 003-3V5a3 3 0 00-3-3zM8 11.5a.75.75 0 110-1.5.75.75 0 010 1.5zm2-3.5H6a.5.5 0 010-1h4a.5.5 0 010 1z\"/></svg>',\n 'claude-code': '<svg viewBox=\"0 0 16 16\"><path d=\"M5.7 11.3a.5.5 0 01-.7-.7L7.3 8 5 5.7a.5.5 0 01.7-.7l3 3a.5.5 0 010 .7l-3 3zM9 11h3a.5.5 0 010 1H9a.5.5 0 010-1z\"/><rect x=\"1\" y=\"2\" width=\"14\" height=\"12\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1\"/></svg>',\n cursor: '<svg viewBox=\"0 0 16 16\"><path d=\"M3 2l10 6-4 1.5L7.5 14z\"/></svg>',\n vscode: '<svg viewBox=\"0 0 16 16\"><path d=\"M11.5 1L5 7l-3-2.5L1 5l3.5 3L1 11l1 .5L5 9l6.5 6 2.5-1V2z\"/></svg>',\n antigravity: '<svg viewBox=\"0 0 16 16\"><path d=\"M8 1l2 5h5l-4 3 1.5 5L8 11l-4.5 3L5 9 1 6h5z\"/></svg>',\n codex: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"8\" r=\"3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 1v3M8 12v3M1 8h3M12 8h3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>',\n perplexity: '<svg viewBox=\"0 0 16 16\"><circle cx=\"8\" cy=\"8\" r=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\"/><path d=\"M8 2v12M2 8h12M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linecap=\"round\"/></svg>',\n stdio: '<svg viewBox=\"0 0 16 16\"><path d=\"M2 3h12a1 1 0 011 1v8a1 1 0 01-1 1H2a1 1 0 01-1-1V4a1 1 0 011-1zm1 2v6h10V5H3z\"/></svg>',\n };\n\n // ── Init ─────────────────────────────────\n async function init() {\n try {\n var resp = await fetch('/api/state');\n var data = await resp.json();\n state.credentials = data.credentials;\n state.availableMcps = data.availableMcps || [];\n state.enabledMcps = data.enabledMcps || ['m365'];\n state.selectedMcps = (data.enabledMcps || ['m365']).slice();\n state.detectedClients = data.detectedClients || [];\n state.configuredClients = (data.config && data.config.configuredClients) || [];\n } catch (err) {\n console.error('Failed to load state:', err);\n }\n updateHeaderDots();\n }\n\n init();\n\n // ── Header Dots ─────────────────────────\n function updateHeaderDots() {\n var el = document.getElementById('header-steps');\n var html = '';\n for (var i = 0; i < STEP_ORDER.length; i++) {\n var cls = 'header-dot';\n if (i < currentStepIndex) cls += ' done';\n else if (i === currentStepIndex) cls += ' current';\n html += '<span class=\"' + cls + '\"></span>';\n }\n el.innerHTML = html;\n }\n\n // ── Navigation ───────────────────────────\n function goToStep(step) {\n var newIndex = STEP_ORDER.indexOf(step);\n var direction = newIndex >= currentStepIndex ? 'slide-forward' : 'slide-backward';\n\n var active = document.querySelector('.step.active');\n if (active) {\n active.classList.remove('active', 'slide-forward', 'slide-backward');\n }\n\n var nextEl = document.getElementById('step-' + step);\n nextEl.classList.add('active', direction);\n currentStepIndex = newIndex;\n updateHeaderDots();\n\n if (step === 'signin') renderSignin();\n if (step === 'mcps') renderMcps();\n if (step === 'clients') renderClients();\n if (step === 'connect') renderConnect();\n if (step === 'done') renderDone();\n }\n\n // ── Blue Orb Mouse Follow ───────────────\n (function() {\n var primary = document.getElementById('orb-primary');\n var secondary = document.getElementById('orb-secondary');\n document.addEventListener('mousemove', function(e) {\n var dx = (e.clientX - window.innerWidth / 2) * 0.15;\n var dy = (e.clientY - window.innerHeight / 2) * 0.15;\n primary.style.transform = 'translate(calc(-50% + ' + dx + 'px), calc(-50% + ' + dy + 'px))';\n secondary.style.transform = 'translate(calc(-50% + ' + (-dx * 0.6) + 'px), calc(-50% + ' + (-dy * 0.6) + 'px))';\n });\n })();\n\n // ── Step 2: Sign In ──────────────────────\n function renderSignin() {\n var el = document.getElementById('signin-content');\n\n if (state.credentials) {\n var name = state.credentials.name || state.credentials.email;\n el.innerHTML =\n '<div class=\"status status-success\">' +\n '<span>✓</span>' +\n '<span>Signed in as <strong>' + escapeHtml(name) + '</strong></span>' +\n '</div>' +\n '<div class=\"btn-row\">' +\n '<button class=\"btn btn-primary btn-block\" onclick=\"goToStep(\\\\'mcps\\\\')\">Continue</button>' +\n '</div>';\n return;\n }\n\n el.innerHTML =\n '<button id=\"btn-signin\" class=\"btn btn-accent btn-block\" onclick=\"startLogin()\">Sign in with SSO</button>' +\n '<div id=\"signin-status\" class=\"hidden\"></div>';\n }\n\n async function startLogin() {\n var btn = document.getElementById('btn-signin');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span> Starting...';\n\n var statusEl = document.getElementById('signin-status');\n\n try {\n var resp = await fetch('/api/auth/login', { method: 'POST' });\n var data = await resp.json();\n\n if (data.error) {\n statusEl.className = 'status status-error';\n statusEl.textContent = data.error;\n btn.disabled = false;\n btn.textContent = 'Sign in with SSO';\n return;\n }\n\n window.open(data.auth_url, '_blank');\n\n // Show auth phase indicators\n statusEl.className = '';\n statusEl.innerHTML =\n '<div class=\"auth-phases\">' +\n '<div class=\"auth-phase done\" id=\"phase-browser\"><span class=\"phase-icon\">✓</span> Browser opened</div>' +\n '<div class=\"auth-phase active\" id=\"phase-sso\"><span class=\"spinner\"></span> Waiting for SSO...</div>' +\n '<div class=\"auth-phase pending\" id=\"phase-verify\"><span class=\"phase-icon\">•</span> Verifying identity</div>' +\n '</div>';\n\n btn.className = 'btn btn-secondary btn-block';\n btn.disabled = false;\n btn.textContent = 'Open SSO page again';\n btn.onclick = function() { window.open(data.auth_url, '_blank'); };\n\n var result = await pollUntilDone(\n '/api/auth/poll?session=' + encodeURIComponent(data.session_id),\n 3000,\n data.expires_in * 1000\n );\n\n // Update phases to show completion\n var ssoPhase = document.getElementById('phase-sso');\n if (ssoPhase) {\n ssoPhase.className = 'auth-phase done';\n ssoPhase.innerHTML = '<span class=\"phase-icon\">✓</span> SSO completed';\n }\n var verifyPhase = document.getElementById('phase-verify');\n if (verifyPhase) {\n verifyPhase.className = 'auth-phase done';\n verifyPhase.innerHTML = '<span class=\"phase-icon\">✓</span> Identity verified';\n }\n\n state.credentials = {\n email: result.employee_email || result.email || '',\n name: result.employee_name || result.name || '',\n };\n\n // Brief pause so user sees all green checks\n await new Promise(function(r) { setTimeout(r, 600); });\n renderSignin();\n } catch (err) {\n statusEl.className = 'status status-error';\n statusEl.textContent = err.message === 'Timeout'\n ? 'Authentication timed out. Please try again.'\n : 'Authentication failed: ' + err.message;\n btn.disabled = false;\n btn.textContent = 'Try Again';\n btn.onclick = startLogin;\n }\n }\n\n // ── Step 3: Select MCPs ──────────────────\n\n var MCP_TOOLTIPS = {\n asana: 'Manage projects, tasks, and team workflows',\n github: 'Cloud storage and version control for any projects with code',\n vercel: 'Deploy and host websites and web apps',\n supabase: 'Your project database \\\\u2014 store and query data',\n m365: 'Access your Outlook email, calendar, OneDrive files, and Teams',\n mailchimp: 'Create and manage email marketing campaigns and audiences',\n salesforce: 'Access your CRM \\\\u2014 contacts, leads, opportunities, and reports',\n monday: 'Track work with boards, timelines, and team updates',\n slack: 'Send messages, search conversations, and manage channels',\n powerbi: 'Build dashboards and run data queries on your reports',\n bestbuy: 'Search products, compare prices, and find store locations',\n databricks: 'Query data warehouses, explore Unity Catalog, and manage jobs',\n };\n\n async function renderMcps() {\n var container = document.getElementById('mcp-list-container');\n\n // Fetch per-user available MCPs (uses API key from login)\n try {\n var userResp = await fetch('/api/user-mcps');\n var userData = await userResp.json();\n if (userData.mcps && userData.mcps.length > 0) {\n state.enabledMcps = userData.mcps.map(function(m) { return m.name; });\n state.selectedMcps = state.enabledMcps.slice();\n }\n } catch (e) {\n // Fall back to global enabledMcps from initial state\n }\n\n // Fetch existing OAuth connections to show connected status\n try {\n var resp = await fetch('/api/connections');\n var data = await resp.json();\n state.connections = data.connections || [];\n } catch (e) {\n state.connections = state.connections || [];\n }\n\n var enabledList = (state.enabledMcps && state.enabledMcps.length > 0)\n ? state.enabledMcps : ['m365'];\n var enabledMcps = state.availableMcps.filter(function(m) { return enabledList.indexOf(m.name) !== -1; });\n var disabledMcps = state.availableMcps.filter(function(m) { return enabledList.indexOf(m.name) === -1; });\n\n var html = '';\n\n // Enabled section\n if (enabledMcps.length > 0) {\n html += '<div class=\"mcp-section\">';\n html += '<div class=\"mcp-section-title\">Available integrations</div>';\n html += '<div class=\"mcp-list\">';\n html += enabledMcps.map(function(mcp) { return renderMcpItem(mcp, false, false); }).join('');\n html += '</div></div>';\n }\n\n // Disabled (coming soon) section\n if (disabledMcps.length > 0) {\n html += '<div class=\"mcp-section\">';\n html += '<div class=\"mcp-section-title\">Coming soon</div>';\n html += '<div class=\"mcp-list\">';\n html += disabledMcps.map(function(mcp) { return renderMcpItem(mcp, false, true); }).join('');\n html += '</div></div>';\n }\n\n container.innerHTML = html;\n\n // Attach click handlers to enabled items only (not disabled, not user-connected)\n container.querySelectorAll('.mcp-item:not(.disabled):not(.user-connected)').forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n var cb = item.querySelector('input[type=\"checkbox\"]');\n cb.checked = !cb.checked;\n item.classList.toggle('checked', cb.checked);\n });\n });\n }\n\n function renderMcpItem(mcp, isRequired, isDisabled) {\n var icon = MCP_ICONS[mcp.name] || '';\n var conn = (state.connections || []).find(function(c) { return c.mcp_name === mcp.name; });\n var isConnected = !isDisabled && conn && !conn.is_company_default && conn.account_email;\n var checked = !isDisabled && (isRequired || isConnected || state.selectedMcps.indexOf(mcp.name) !== -1);\n return (\n '<label class=\"mcp-item' + (checked ? ' checked' : '') + (isRequired ? ' required' : '') +\n (isConnected ? ' user-connected' : '') +\n (isDisabled ? ' disabled' : '') + '\" data-mcp=\"' + mcp.name + '\">' +\n (MCP_TOOLTIPS[mcp.name] ? '<span class=\"mcp-tooltip\">' + MCP_TOOLTIPS[mcp.name] + '</span>' : '') +\n '<input type=\"checkbox\" value=\"' + mcp.name + '\"' + (checked ? ' checked' : '') + ((isConnected || isDisabled) ? ' disabled' : '') + '>' +\n (icon ? '<div class=\"mcp-icon\">' + icon + '</div>' : '') +\n '<div class=\"mcp-check\"><span class=\"mcp-check-icon\">✓</span></div>' +\n '<div class=\"mcp-info\">' +\n '<span class=\"mcp-name\">' + escapeHtml(mcp.displayName) +\n '<span class=\"mcp-badge ' + mcp.authMode + '\">' + mcp.authMode + '</span>' +\n '</span>' +\n '<span class=\"mcp-desc\">' + escapeHtml(mcp.description) + '</span>' +\n (isConnected ? '<span class=\"mcp-connected-badge\">✓ Connected as ' + escapeHtml(conn.account_email) + '</span>' : '') +\n '</div>' +\n (isRequired ? '<span class=\"mcp-required-label\">Required</span>' : '') +\n (isConnected && !isRequired ? '<span class=\"mcp-connected-label\">Connected</span>' : '') +\n (isDisabled ? '<span class=\"mcp-coming-soon-label\">Coming Soon</span>' : '') +\n '</label>'\n );\n }\n\n function saveMcps() {\n var checkboxes = document.querySelectorAll('.mcp-item:not(.disabled) input[type=\"checkbox\"]');\n var selected = Array.from(checkboxes).filter(function(cb) { return cb.checked; }).map(function(cb) { return cb.value; });\n state.selectedMcps = selected;\n goToStep('clients');\n }\n\n // ── Step 4: Configure Clients ────────────\n function renderClients() {\n var el = document.getElementById('client-list');\n document.getElementById('client-result').className = 'hidden';\n document.getElementById('btn-configure').disabled = false;\n document.getElementById('btn-configure').textContent = 'Configure';\n\n // Pre-select already-configured clients\n state.selectedClients = state.configuredClients.slice();\n\n el.innerHTML = state.detectedClients.map(function(client) {\n var detected = client.detected;\n var icon = CLIENT_ICONS[client.type] || '';\n var alreadyConfigured = state.configuredClients.indexOf(client.type) !== -1;\n var statusText = alreadyConfigured ? 'Configured' : (detected ? 'Detected' : 'Not found');\n return (\n '<label class=\"client-item' + (detected ? '' : ' disabled') +\n (alreadyConfigured ? ' checked already-configured' : '') + '\" data-client=\"' + client.type + '\">' +\n (icon ? '<div class=\"client-icon\">' + icon + '</div>' : '') +\n '<div class=\"mcp-check\"><span class=\"mcp-check-icon\">✓</span></div>' +\n '<span class=\"client-name\">' + escapeHtml(client.name) + '</span>' +\n '<span class=\"client-status\">' + statusText + '</span>' +\n '</label>'\n );\n }).join('');\n\n el.querySelectorAll('.client-item:not(.disabled):not(.already-configured)').forEach(function(item) {\n item.addEventListener('click', function(e) {\n e.preventDefault();\n item.classList.toggle('checked');\n state.selectedClients = Array.from(el.querySelectorAll('.client-item.checked'))\n .map(function(i) { return i.dataset.client; });\n });\n });\n }\n\n async function configureClients() {\n if (state.selectedClients.length === 0) {\n goToStep('connect');\n return;\n }\n\n var btn = document.getElementById('btn-configure');\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span> Configuring...';\n\n // Show per-client spinners\n state.selectedClients.forEach(function(clientType) {\n var item = document.querySelector('[data-client=\"' + clientType + '\"]');\n if (item) {\n var statusSpan = item.querySelector('.client-status');\n statusSpan.innerHTML = '<span class=\"spinner\"></span>';\n }\n });\n\n try {\n var resp = await fetch('/api/clients/configure', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ clients: state.selectedClients, mcps: state.selectedMcps }),\n });\n var data = await resp.json();\n\n state.configuredClients = data.results.filter(function(r) { return r.success; }).map(function(r) { return r.client; });\n\n // Update per-client status inline\n data.results.forEach(function(r) {\n var item = document.querySelector('[data-client=\"' + r.client + '\"]');\n if (item) {\n var statusSpan = item.querySelector('.client-status');\n if (r.success) {\n statusSpan.innerHTML = '<span style=\"color: #4CAF50;\">✓ Done</span>';\n } else {\n statusSpan.innerHTML = '<span style=\"color: #EF5350;\">✗ Failed</span>';\n }\n }\n });\n\n var resultEl = document.getElementById('client-result');\n resultEl.className = '';\n resultEl.innerHTML = data.results.map(function(r) {\n var html = '<div class=\"status ' + (r.success ? 'status-success' : 'status-error') + '\">' +\n '<span>' + (r.success ? '✓' : '✗') + '</span>' +\n '<span>' + (r.success ? escapeHtml(r.message || 'Configured') : escapeHtml(r.client) + ': ' + (r.error || 'Failed')) + '</span>' +\n '</div>';\n // Show copyable snippet for stdio/OpenClaw and Perplexity instructions\n if ((r.client === 'stdio' || r.client === 'perplexity') && r.success && r.message) {\n var snippet = r.message.replace(/^Add this to your client config:\\\\n\\\\n/, '');\n html += '<div class=\"stdio-snippet\">' +\n '<button class=\"copy-btn\" onclick=\"copySnippet(this)\">Copy</button>' +\n '<pre>' + escapeHtml(snippet) + '</pre>' +\n '</div>';\n }\n return html;\n }).join('');\n\n // Add verify button for debugging config issues\n var verifyHtml = '<div style=\"margin-top: 12px; text-align: center;\">' +\n '<button onclick=\"verifyConfig()\" style=\"background: transparent; border: 1px solid #555; color: #aaa; padding: 6px 16px; border-radius: 6px; cursor: pointer; font-size: 12px;\">Verify Config File</button>' +\n '<pre id=\"verify-result\" style=\"display:none; margin-top: 8px; padding: 10px; background: #1a1a2e; border-radius: 6px; font-size: 11px; text-align: left; max-height: 200px; overflow: auto; color: #ccc;\"></pre>' +\n '</div>';\n resultEl.innerHTML += verifyHtml;\n\n btn.textContent = 'Continue';\n btn.disabled = false;\n btn.onclick = function() { goToStep('connect'); };\n } catch (err) {\n btn.disabled = false;\n btn.textContent = 'Retry';\n var resultEl = document.getElementById('client-result');\n resultEl.className = '';\n resultEl.innerHTML = '<div class=\"status status-error\"><span>✗</span><span>' + escapeHtml(err.message) + '</span></div>';\n }\n }\n\n // ── Step 5: Connect Accounts ─────────────\n async function renderConnect() {\n var el = document.getElementById('conn-list');\n el.innerHTML = '<div class=\"status status-info\"><span class=\"spinner\"></span> Loading connections...</div>';\n\n var selectedMcpList = state.availableMcps.filter(function(m) {\n return state.selectedMcps.indexOf(m.name) !== -1;\n });\n\n if (selectedMcpList.length === 0) {\n el.innerHTML = '<div class=\"status status-info\">No integrations selected.</div>';\n return;\n }\n\n try {\n var resp = await fetch('/api/connections');\n var data = await resp.json();\n state.connections = data.connections || [];\n } catch (e) {\n state.connections = [];\n }\n\n el.innerHTML = selectedMcpList.map(function(mcp) {\n var conn = state.connections.find(function(c) { return c.mcp_name === mcp.name; });\n var isCompany = mcp.authMode !== 'personal';\n var isConnected = conn && !conn.is_company_default && conn.account_email;\n var isCompanyDefault = isCompany;\n\n if (isCompanyDefault) {\n return (\n '<div class=\"conn-item connected company-default\" id=\"conn-' + mcp.name + '\">' +\n '<span class=\"conn-name\">' + escapeHtml(mcp.displayName) + '</span>' +\n '<span class=\"conn-status connected\">✓ Company Account</span>' +\n '</div>'\n );\n }\n\n return (\n '<div class=\"conn-item' + (isConnected ? ' connected' : '') + '\" id=\"conn-' + mcp.name + '\">' +\n '<span class=\"conn-name\">' + escapeHtml(mcp.displayName) + '</span>' +\n (isConnected\n ? '<span class=\"conn-status connected\">✓ ' + escapeHtml(conn.account_email) + '</span>'\n : '<span class=\"conn-status\">Not connected</span>' +\n '<button class=\"btn btn-sm btn-secondary\" onclick=\"connectProvider(\\\\'' + mcp.name + '\\\\', \\\\'' + escapeHtml(mcp.displayName) + '\\\\')\">Connect</button>'\n ) +\n '</div>'\n );\n }).join('');\n }\n\n async function connectProvider(name, displayName) {\n var item = document.getElementById('conn-' + name);\n var btn = item.querySelector('button');\n var statusSpan = item.querySelector('.conn-status');\n\n btn.disabled = true;\n btn.innerHTML = '<span class=\"spinner\"></span>';\n statusSpan.textContent = 'Connecting...';\n\n try {\n var resp = await fetch('/api/oauth/connect', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider: name }),\n });\n var data = await resp.json();\n\n if (data.error) {\n statusSpan.textContent = data.error;\n statusSpan.className = 'conn-status';\n btn.disabled = false;\n btn.textContent = 'Retry';\n btn.onclick = function() { connectProvider(name, displayName); };\n return;\n }\n\n window.open(data.authorization_url, '_blank');\n statusSpan.innerHTML = '<span class=\"spinner\"></span> Waiting \\\\u2014 check the new tab';\n btn.textContent = 'Open again';\n btn.disabled = false;\n btn.onclick = function() { window.open(data.authorization_url, '_blank'); };\n\n var result = await pollUntilDone(\n '/api/oauth/poll?session=' + encodeURIComponent(data.session_id),\n 3000,\n data.expires_in * 1000\n );\n\n var email = result.account_email || 'connected';\n item.classList.add('connected');\n statusSpan.className = 'conn-status connected';\n statusSpan.textContent = '\\\\u2713 ' + email;\n btn.remove();\n } catch (err) {\n statusSpan.textContent = err.message === 'Timeout' ? 'Timed out' : err.message;\n statusSpan.className = 'conn-status';\n btn.disabled = false;\n btn.textContent = 'Retry';\n btn.onclick = function() { connectProvider(name, displayName); };\n }\n }\n\n // ── Step 6: Done ─────────────────────────\n function renderDone() {\n var el = document.getElementById('summary');\n var html = '';\n\n html += '<div class=\"summary-section\">';\n html += '<h3>Integrations (' + state.selectedMcps.length + ')</h3>';\n state.selectedMcps.forEach(function(name) {\n var mcp = state.availableMcps.find(function(m) { return m.name === name; });\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(mcp ? mcp.displayName : name) + '</div>';\n });\n html += '</div>';\n\n if (state.configuredClients.length > 0) {\n html += '<div class=\"summary-section\">';\n html += '<h3>Configured Clients</h3>';\n state.configuredClients.forEach(function(c) {\n var client = state.detectedClients.find(function(d) { return d.type === c; });\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(client ? client.name : c) + '</div>';\n });\n html += '</div>';\n }\n\n if (state.credentials) {\n html += '<div class=\"summary-section\">';\n html += '<h3>Signed In</h3>';\n html += '<div class=\"summary-item\"><span class=\"check-icon\">✓</span>' + escapeHtml(state.credentials.name || state.credentials.email) + '</div>';\n html += '</div>';\n }\n\n el.innerHTML = html;\n }\n\n async function finishSetup() {\n try {\n await fetch('/api/complete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n mcps: state.selectedMcps,\n configuredClients: state.configuredClients,\n }),\n });\n } catch (e) {\n // Server may already be closing\n }\n\n document.getElementById('step-done').querySelector('.card').innerHTML =\n '<div class=\"text-center\">' +\n '<div class=\"success-circle\"><span class=\"success-check\">✓</span></div>' +\n '<h2>All Done</h2>' +\n '<p class=\"subtitle mt-2\">You can close this tab. Restart your AI clients to see the new tools.</p>' +\n '</div>';\n }\n\n // ── Helpers ──────────────────────────────\n function pollUntilDone(url, intervalMs, timeoutMs) {\n return new Promise(function(resolve, reject) {\n var deadline = Date.now() + timeoutMs;\n var timer = setInterval(async function() {\n if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout'));\n return;\n }\n try {\n var resp = await fetch(url);\n var data = await resp.json();\n if (data.status === 'completed') {\n clearInterval(timer);\n resolve(data);\n } else if (data.status === 'error' || data.status === 'expired') {\n clearInterval(timer);\n reject(new Error(data.error_message || data.status));\n }\n } catch (e) {\n // Network error -- keep polling\n }\n }, intervalMs);\n });\n }\n\n async function verifyConfig() {\n var el = document.getElementById('verify-result');\n el.style.display = 'block';\n el.textContent = 'Reading config file...';\n try {\n var resp = await fetch('/api/debug/config');\n var data = await resp.json();\n el.textContent = JSON.stringify(data, null, 2);\n } catch (err) {\n el.textContent = 'Error: ' + err.message;\n }\n }\n\n function copySnippet(btn) {\n var pre = btn.parentElement.querySelector('pre');\n navigator.clipboard.writeText(pre.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 2000);\n });\n }\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str || '';\n return div.innerHTML;\n }\n </script>\n</body>\n</html>`;\n}\n","import type * as http from \"node:http\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { AVAILABLE_MCPS, DEFAULT_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { detectClients, configureClient } from \"../config/clients.js\";\nimport { createConfig, readConfig, writeConfig } from \"../config/storage.js\";\nimport {\n getEffectiveApiKey,\n readCredentials,\n writeCredentials,\n} from \"../auth/credentials.js\";\nimport { getClaudeDesktopConfigPath, getPlatform } from \"../utils/platform.js\";\nimport type { ClientType } from \"../config/types.js\";\n\ninterface WizardOptions {\n serverUrl: string;\n}\n\n/** Mutable wizard state — lives for the lifetime of the local server. */\ninterface WizardState {\n apiKey: string;\n}\n\nlet wizardState: WizardState | null = null;\n\nfunction getState(): WizardState {\n if (!wizardState) {\n wizardState = { apiKey: getEffectiveApiKey() };\n }\n return wizardState;\n}\n\n/** Parse JSON body from an incoming request. */\nfunction parseBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n try {\n resolve(JSON.parse(Buffer.concat(chunks).toString()) as Record<string, unknown>);\n } catch {\n resolve({});\n }\n });\n req.on(\"error\", () => resolve({}));\n });\n}\n\n/** Send a JSON response. */\nfunction json(res: http.ServerResponse, data: unknown, status = 200): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n}\n\n/**\n * Handle an API route. Returns true if the route was handled.\n * `onComplete` is called when the wizard finishes (signals server shutdown).\n */\nexport async function handleApiRoute(\n path: string,\n searchParams: URLSearchParams,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n options: WizardOptions,\n onComplete: () => void,\n): Promise<boolean> {\n const method = req.method || \"GET\";\n\n // ── GET /api/state ──\n if (path === \"/api/state\" && method === \"GET\") {\n const creds = readCredentials();\n const config = readConfig();\n const clients = detectClients();\n\n // Fetch admin-controlled setup-enabled MCPs from Cortex backend\n let enabledMcps: string[] = [\"m365\"]; // fallback\n try {\n const setupResp = await fetch(\n `${options.serverUrl}/api/v1/mcps/setup-enabled`,\n );\n if (setupResp.ok) {\n const setupData = (await setupResp.json()) as { enabled?: string[] };\n if (Array.isArray(setupData.enabled)) {\n enabledMcps = setupData.enabled;\n }\n }\n } catch {\n // Use fallback\n }\n\n json(res, {\n credentials: creds ? { email: creds.email, name: creds.name } : null,\n config,\n apiKey: getState().apiKey,\n defaultMcps: [...DEFAULT_MCPS],\n enabledMcps,\n availableMcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n description: m.description,\n authMode: m.authMode,\n })),\n detectedClients: clients,\n });\n return true;\n }\n\n // ── POST /api/auth/login ──\n if (path === \"/api/auth/login\" && method === \"POST\") {\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/auth/employee/initiate`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n if (!resp.ok) {\n json(res, { error: `Server returned HTTP ${resp.status}` }, 502);\n return true;\n }\n const data = await resp.json();\n json(res, data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);\n }\n return true;\n }\n\n // ── GET /api/auth/poll?session=X ──\n if (path === \"/api/auth/poll\" && method === \"GET\") {\n const sessionId = searchParams.get(\"session\");\n if (!sessionId) {\n json(res, { error: \"Missing session parameter\" }, 400);\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/auth/employee/poll/${sessionId}`,\n );\n const result = (await resp.json()) as Record<string, unknown>;\n\n // If login completed, persist credentials and update API key\n if (result.status === \"completed\" && result.api_key) {\n const email =\n (result.employee_email as string) || (result.user_info as Record<string, string>)?.email || \"\";\n const name =\n (result.employee_name as string) || (result.user_info as Record<string, string>)?.name || \"\";\n\n writeCredentials({\n apiKey: result.api_key as string,\n email,\n name,\n authenticatedAt: new Date().toISOString(),\n });\n\n getState().apiKey = result.api_key as string;\n }\n\n json(res, result);\n } catch {\n json(res, { status: \"pending\" });\n }\n return true;\n }\n\n // ── GET /api/user-mcps ──\n // Fetches per-user available MCPs from the backend after login.\n // Returns the MCPs this specific user is allowed to set up.\n if (path === \"/api/user-mcps\" && method === \"GET\") {\n const apiKey = getState().apiKey;\n if (!apiKey) {\n json(res, { mcps: [] });\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/mcps/user-available`,\n { headers: { \"x-api-key\": apiKey } },\n );\n if (resp.ok) {\n const data = await resp.json();\n json(res, data);\n } else {\n // Fallback: return all MCPs with enabledMcps filtering\n json(res, {\n mcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n authMode: m.authMode,\n connected: false,\n })),\n });\n }\n } catch {\n json(res, {\n mcps: AVAILABLE_MCPS.map((m) => ({\n name: m.name,\n displayName: m.displayName,\n authMode: m.authMode,\n connected: false,\n })),\n });\n }\n return true;\n }\n\n // ── POST /api/clients/configure ──\n if (path === \"/api/clients/configure\" && method === \"POST\") {\n const body = await parseBody(req);\n const clients = (body.clients || []) as string[];\n const mcps = (body.mcps || [...DEFAULT_MCPS]) as string[];\n const apiKey = getState().apiKey;\n\n const results: Array<{ client: string; success: boolean; error?: string; message?: string }> = [];\n\n for (const clientType of clients) {\n try {\n const msg = configureClient(\n clientType as ClientType,\n options.serverUrl,\n apiKey,\n mcps,\n );\n results.push({ client: clientType, success: true, message: msg });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n results.push({ client: clientType, success: false, error: msg });\n }\n }\n\n json(res, { results });\n return true;\n }\n\n // ── GET /api/debug/config ──\n if (path === \"/api/debug/config\" && method === \"GET\") {\n const configPath = getClaudeDesktopConfigPath();\n const result: Record<string, unknown> = {\n path: configPath,\n exists: existsSync(configPath),\n platform: getPlatform(),\n };\n\n if (existsSync(configPath)) {\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n result.contents = parsed;\n result.hasMcpServers = !!(parsed?.mcpServers);\n result.hasCortex = !!(parsed?.mcpServers?.cortex);\n } catch (e) {\n result.error = String(e);\n result.rawContents = readFileSync(configPath, \"utf-8\");\n }\n }\n\n json(res, result);\n return true;\n }\n\n // ── GET /api/connections ──\n if (path === \"/api/connections\" && method === \"GET\") {\n const apiKey = getState().apiKey;\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connections`,\n { headers: { \"x-api-key\": apiKey } },\n );\n\n if (resp.ok) {\n const data = await resp.json();\n json(res, data);\n } else {\n json(res, { connections: [] });\n }\n } catch {\n json(res, { connections: [] });\n }\n return true;\n }\n\n // ── POST /api/oauth/connect ──\n if (path === \"/api/oauth/connect\" && method === \"POST\") {\n const body = await parseBody(req);\n const provider = body.provider as string;\n const apiKey = getState().apiKey;\n\n if (!provider) {\n json(res, { error: \"Missing provider\" }, 400);\n return true;\n }\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connect/${provider}/initiate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n },\n );\n\n if (!resp.ok) {\n if (resp.status === 401) {\n json(res, { error: \"Session expired. Please re-login.\" }, 401);\n return true;\n }\n const err = (await resp.json().catch(() => ({}))) as { detail?: string };\n json(res, { error: err.detail || `HTTP ${resp.status}` }, resp.status);\n return true;\n }\n\n const data = await resp.json();\n json(res, data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);\n }\n return true;\n }\n\n // ── GET /api/oauth/poll?session=X ──\n if (path === \"/api/oauth/poll\" && method === \"GET\") {\n const sessionId = searchParams.get(\"session\");\n if (!sessionId) {\n json(res, { error: \"Missing session parameter\" }, 400);\n return true;\n }\n\n const apiKey = getState().apiKey;\n\n try {\n const resp = await fetch(\n `${options.serverUrl}/api/v1/oauth/connect/poll/${sessionId}`,\n { headers: { \"x-api-key\": apiKey } },\n );\n const result = await resp.json();\n json(res, result);\n } catch {\n json(res, { status: \"pending\" });\n }\n return true;\n }\n\n // ── POST /api/complete ──\n if (path === \"/api/complete\" && method === \"POST\") {\n const body = await parseBody(req);\n const mcps = (body.mcps || [...DEFAULT_MCPS]) as string[];\n const configuredClients = (body.configuredClients || []) as ClientType[];\n\n // Notify backend that setup is complete (enables company-default connections)\n const apiKey = getState().apiKey;\n try {\n await fetch(`${options.serverUrl}/api/v1/setup/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n });\n } catch {\n // Non-fatal: local setup still succeeds even if backend notification fails\n }\n\n // Write final config\n const config = createConfig(getState().apiKey, mcps);\n config.configuredClients = configuredClients;\n writeConfig(config);\n\n json(res, { success: true });\n\n // Signal completion after response is sent\n setTimeout(onComplete, 100);\n return true;\n }\n\n // Route not matched\n return false;\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { AVAILABLE_MCPS } from \"../constants.js\";\nimport {\n getClaudeDesktopConfigPath,\n getCursorConfigPath,\n getVSCodeMcpConfigPath,\n getAntigravityConfigPath,\n getCodexConfigPath,\n getPlatform,\n isWSL,\n} from \"../utils/platform.js\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ClientType, DetectedClient, HttpMcpEntry } from \"./types.js\";\n\n/**\n * Detect which AI clients are installed on this machine.\n */\nexport function detectClients(): DetectedClient[] {\n const clients: DetectedClient[] = [];\n\n // Claude Desktop\n const desktopPath = getClaudeDesktopConfigPath();\n const desktopDir = dirname(desktopPath);\n clients.push({\n type: \"claude-desktop\",\n name: \"Claude (Desktop)\",\n configPath: desktopPath,\n detected: existsSync(desktopDir),\n });\n\n // Claude Code\n let claudeCodeDetected = false;\n try {\n execSync(\"which claude\", { stdio: \"pipe\" });\n claudeCodeDetected = true;\n } catch {\n // claude CLI not in PATH\n }\n clients.push({\n type: \"claude-code\",\n name: \"Claude Code\",\n configPath: null,\n detected: claudeCodeDetected,\n });\n\n // Cursor\n const cursorPath = getCursorConfigPath();\n const cursorDir = dirname(cursorPath);\n clients.push({\n type: \"cursor\",\n name: \"Cursor\",\n configPath: cursorPath,\n detected: existsSync(cursorDir),\n });\n\n // VS Code\n const home = homedir();\n const vscodePath = getVSCodeMcpConfigPath();\n clients.push({\n type: \"vscode\",\n name: \"VS Code\",\n configPath: vscodePath,\n detected: existsSync(join(home, \".vscode\")),\n });\n\n // Antigravity\n const antigravityPath = getAntigravityConfigPath();\n clients.push({\n type: \"antigravity\",\n name: \"Antigravity\",\n configPath: antigravityPath,\n detected: existsSync(join(home, \".antigravity\")),\n });\n\n // Codex (OpenAI) — CLI and IDE extension share ~/.codex/config.toml\n const codexPath = getCodexConfigPath();\n let codexDetected = existsSync(join(home, \".codex\"));\n if (!codexDetected) {\n try {\n execSync(\"which codex\", { stdio: \"pipe\" });\n codexDetected = true;\n } catch {\n // codex CLI not in PATH\n }\n }\n clients.push({\n type: \"codex\",\n name: \"Codex (OpenAI)\",\n configPath: codexPath,\n detected: codexDetected,\n });\n\n // Perplexity Computer (macOS only)\n const perplexityDir = join(home, \"Library\", \"Containers\", \"ai.perplexity.mac\");\n clients.push({\n type: \"perplexity\",\n name: \"Perplexity Computer\",\n configPath: null,\n detected: getPlatform() === \"macos\" && existsSync(perplexityDir),\n });\n\n // OpenClaw / Stdio (always available — manual copy-paste)\n clients.push({\n type: \"stdio\",\n name: \"OpenClaw (stdio)\",\n configPath: null,\n detected: true,\n });\n\n return clients;\n}\n\n/**\n * Build per-MCP HTTP entries for a given server URL and API key.\n */\nexport function buildHttpEntries(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): Record<string, HttpMcpEntry> {\n const entries: Record<string, HttpMcpEntry> = {};\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n entries[mcp.serverName] = {\n url: `${serverUrl}/mcp/${mcp.name}`,\n headers: { \"x-api-key\": apiKey },\n };\n }\n\n return entries;\n}\n\n/**\n * Configure Claude Desktop by merging a stdio proxy entry into its config file.\n * Uses command/args/env format (Claude Desktop does not support HTTP url/headers).\n * Preserves existing non-Cortex entries.\n */\nexport function configureClaudeDesktop(\n _serverUrl: string,\n apiKey: string,\n _mcps: string[]\n): string {\n const configPath = getClaudeDesktopConfigPath();\n const dir = dirname(configPath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Use cmd.exe wrapper when the target is Windows (native or WSL → Windows)\n const isWindowsTarget = getPlatform() === \"windows\" || isWSL();\n const cortexEntry = isWindowsTarget\n ? {\n command: \"cmd\",\n args: [\"/c\", \"npx\", \"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n }\n : {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n };\n\n // On Windows, Claude Desktop may watch the config file and overwrite it\n // immediately after we write. Retry up to 3 times with verification.\n const maxAttempts = isWindowsTarget ? 3 : 1;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n // Read existing config or start fresh\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* and cortex entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n }\n }\n\n // Add stdio proxy entry (Claude Desktop only supports command/args)\n // API key is read from ~/.cortex-mcp/credentials.json at runtime,\n // so we don't bake it into the env (avoids stale key issues on re-login).\n servers[\"cortex\"] = cortexEntry;\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n // On Windows, verify the write persisted (Claude Desktop may overwrite)\n if (isWindowsTarget) {\n // Brief delay to let any file-watcher settle\n const start = Date.now();\n while (Date.now() - start < 500) { /* busy wait */ }\n\n try {\n const verify = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const verifyServers = verify.mcpServers as Record<string, unknown> | undefined;\n if (verifyServers && \"cortex\" in verifyServers) {\n return configPath; // Verified — write persisted\n }\n } catch {\n // Verification read failed — retry\n }\n // Claude Desktop overwrote the file — retry\n continue;\n }\n\n // Non-Windows: verify the write\n try {\n const verify = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!verify.mcpServers || !(\"cortex\" in verify.mcpServers)) {\n throw new Error(`Wrote to ${configPath} but mcpServers.cortex not found after write`);\n }\n } catch (e) {\n if (e instanceof Error && e.message.includes(\"mcpServers.cortex not found\")) throw e;\n throw new Error(`Failed to verify config at ${configPath}: ${e}`);\n }\n return configPath;\n }\n\n // All retries exhausted (Windows only)\n throw new Error(\n `Failed to write config to ${configPath}. ` +\n \"Claude Desktop may be overwriting the file. \" +\n \"Please close Claude Desktop completely (quit from the system tray), \" +\n \"then re-run setup.\"\n );\n}\n\n/**\n * Configure Claude Code by running `claude mcp add` commands.\n */\nexport function configureClaudeCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n\n const url = `${serverUrl}/mcp/${mcp.name}`;\n\n // Remove existing entry first (ignore errors if it doesn't exist)\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n } catch {\n // Entry didn't exist — fine\n }\n\n execSync(\n `claude mcp add --transport http ${mcp.serverName} ${url} -H \"x-api-key: ${apiKey}\"`,\n { stdio: \"pipe\" }\n );\n }\n}\n\n/**\n * Configure Cursor by writing HTTP MCP entries to its config file.\n * Cursor supports HTTP transport natively.\n */\nexport function configureCursor(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCursorConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n // Add new HTTP entries\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure VS Code by writing HTTP MCP entries to its config file.\n */\nexport function configureVSCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getVSCodeMcpConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Antigravity by writing HTTP MCP entries to its config file.\n */\nexport function configureAntigravity(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getAntigravityConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Codex (OpenAI) by writing streamable HTTP entries to ~/.codex/config.toml.\n * Codex uses TOML format. The config is shared by both CLI and IDE extension.\n */\nexport function configureCodex(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCodexConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Read existing config to preserve non-cortex entries\n let existingContent = \"\";\n if (existsSync(configPath)) {\n try {\n existingContent = readFileSync(configPath, \"utf-8\");\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Remove existing cortex-* MCP server blocks from TOML\n // Matches [mcp_servers.cortex-*] blocks until next section or EOF\n const cleaned = existingContent.replace(\n /\\[mcp_servers\\.cortex-[^\\]]*\\][^[]*(?=\\[|$)/g,\n \"\"\n ).trim();\n\n // Build new cortex MCP server entries in TOML format\n const tomlEntries: string[] = [];\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n tomlEntries.push(\n `[mcp_servers.${mcp.serverName}]\\n` +\n `url = \"${serverUrl}/mcp/${mcp.name}\"\\n` +\n `http_headers = { \"x-api-key\" = \"${apiKey}\" }`\n );\n }\n\n const newContent = (cleaned ? cleaned + \"\\n\\n\" : \"\") + tomlEntries.join(\"\\n\\n\") + \"\\n\";\n writeFileSync(configPath, newContent);\n}\n\n/**\n * Configure Perplexity Computer by generating per-MCP connector instructions.\n * Perplexity uses OAuth + Streamable HTTP — users add connectors manually via UI.\n */\nexport function configurePerplexity(\n serverUrl: string,\n _apiKey: string,\n mcps: string[]\n): string {\n const lines: string[] = [\n \"Add each MCP as a separate connector in Perplexity:\",\n \" Settings → Connectors → Add custom connector\",\n \"\",\n ];\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n lines.push(`── ${mcp.displayName} ──`);\n lines.push(` Name: Cortex ${mcp.displayName}`);\n lines.push(` MCP server URL: ${serverUrl}/mcp/${mcp.name}`);\n lines.push(` Authentication: OAuth`);\n lines.push(` Client ID: perplexity`);\n lines.push(` Client Secret: (leave empty)`);\n lines.push(` Transport: Streamable HTTP`);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Reset Perplexity — no-op since config is in Perplexity's UI.\n */\nexport function resetPerplexity(): boolean {\n return false;\n}\n\n/**\n * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)\n */\nexport function generateStdioSnippet(_apiKey: string): string {\n const isWindowsTarget = getPlatform() === \"windows\" || isWSL();\n const config = {\n mcpServers: {\n cortex: isWindowsTarget\n ? {\n command: \"cmd\",\n args: [\"/c\", \"npx\", \"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n }\n : {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n },\n },\n };\n return JSON.stringify(config, null, 2);\n}\n\n/**\n * Remove all cortex-* MCP entries from Claude Desktop config.\n */\nexport function resetClaudeDesktop(): boolean {\n const configPath = getClaudeDesktopConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Cursor config.\n */\nexport function resetCursor(): boolean {\n const configPath = getCursorConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Claude Code.\n */\nexport function resetClaudeCode(): boolean {\n let removed = false;\n for (const mcp of AVAILABLE_MCPS) {\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n removed = true;\n } catch {\n // Entry didn't exist\n }\n }\n return removed;\n}\n\n/**\n * Remove cortex MCP entries from VS Code config.\n */\nexport function resetVSCode(): boolean {\n const configPath = getVSCodeMcpConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Antigravity config.\n */\nexport function resetAntigravity(): boolean {\n const configPath = getAntigravityConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Codex config.\n */\nexport function resetCodex(): boolean {\n const configPath = getCodexConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const cleaned = content.replace(\n /\\[mcp_servers\\.cortex-[^\\]]*\\][^[]*(?=\\[|$)/g,\n \"\"\n ).trim();\n\n if (cleaned !== content.trim()) {\n writeFileSync(configPath, cleaned ? cleaned + \"\\n\" : \"\");\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Configure a specific client type.\n */\nexport function configureClient(\n clientType: ClientType,\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): string {\n switch (clientType) {\n case \"claude-desktop\": {\n const path = configureClaudeDesktop(serverUrl, apiKey, mcps);\n return `Claude Desktop configured (${path})`;\n }\n case \"claude-code\":\n configureClaudeCode(serverUrl, apiKey, mcps);\n return \"Claude Code configured\";\n case \"cursor\":\n configureCursor(serverUrl, apiKey, mcps);\n return \"Cursor configured\";\n case \"vscode\":\n configureVSCode(serverUrl, apiKey, mcps);\n return \"VS Code configured\";\n case \"antigravity\":\n configureAntigravity(serverUrl, apiKey, mcps);\n return \"Antigravity configured\";\n case \"codex\":\n configureCodex(serverUrl, apiKey, mcps);\n return \"Codex configured\";\n case \"perplexity\":\n return configurePerplexity(serverUrl, apiKey, mcps);\n case \"stdio\":\n return (\n \"Add this to your client config:\\n\\n\" +\n generateStdioSnippet(apiKey)\n );\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\nimport type { CortexMcpConfig } from \"./types.js\";\n\n/** Get the config directory path */\nexport function getConfigDir(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME);\n}\n\n/** Get the config file path */\nexport function getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\n/** Read the current config, or return null if none exists */\nexport function readConfig(): CortexMcpConfig | null {\n const path = getConfigPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexMcpConfig;\n } catch {\n return null;\n }\n}\n\n/** Write config to disk (creates directory with 700 permissions, file with 600) */\nexport function writeConfig(config: CortexMcpConfig): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getConfigPath();\n writeFileSync(path, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Create a default config with the given API key and MCPs */\nexport function createConfig(\n apiKey: string,\n mcps: string[]\n): CortexMcpConfig {\n return {\n version: 1,\n server: DEFAULT_SERVER_URL,\n apiKey,\n mcps,\n configuredClients: [],\n };\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME, DEFAULT_API_KEY } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\n\n/** Stored credentials from a successful login */\nexport interface CortexCredentials {\n apiKey: string;\n email: string;\n name?: string;\n authenticatedAt: string; // ISO 8601\n}\n\n/** Get the credentials file path (~/.cortex-mcp/credentials.json) */\nexport function getCredentialsPath(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME);\n}\n\n/** Read stored credentials, or return null if not logged in */\nexport function readCredentials(): CortexCredentials | null {\n const path = getCredentialsPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexCredentials;\n } catch {\n return null;\n }\n}\n\n/** Write credentials to disk (creates directory with 700, file with 600 permissions) */\nexport function writeCredentials(creds: CortexCredentials): void {\n const dir = join(getHomeDir(), CONFIG_DIR_NAME);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getCredentialsPath();\n writeFileSync(path, JSON.stringify(creds, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Delete stored credentials */\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\n/**\n * Get the effective API key to use.\n * Priority: CORTEX_API_KEY env var > stored credentials > config file > default shared key\n */\nexport function getEffectiveApiKey(): string {\n // 1. Environment variable (highest priority)\n const envKey = process.env.CORTEX_API_KEY;\n if (envKey) return envKey;\n\n // 2. Personal credentials from login\n const creds = readCredentials();\n if (creds?.apiKey) return creds.apiKey;\n\n // 3. Config file (set during setup)\n const config = readConfig();\n if (config?.apiKey) return config.apiKey;\n\n // 4. Shared default key (fallback)\n return DEFAULT_API_KEY;\n}\n","import { DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { openBrowser } from \"../utils/browser.js\";\nimport { startWizardServer } from \"../wizard/server.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\n/**\n * Interactive setup wizard.\n * Launches a local HTTP server and opens the browser-based wizard UI.\n * All setup steps happen in the browser; the terminal just waits.\n */\nexport async function runSetup(): Promise<void> {\n log(\"\");\n log(\" Cortex MCP Setup\");\n log(\" Starting setup wizard...\");\n log(\"\");\n\n const { port, close, waitForCompletion } = await startWizardServer({\n serverUrl: DEFAULT_SERVER_URL,\n });\n\n const url = `http://localhost:${port}`;\n\n log(` Setup wizard running at ${url}`);\n log(\"\");\n\n await openBrowser(url);\n\n log(\" If the browser didn't open, visit:\");\n log(` ${url}`);\n log(\"\");\n log(\" Waiting for setup to complete (press Ctrl+C to cancel)...\");\n\n // Handle Ctrl+C gracefully\n const cleanup = async (): Promise<void> => {\n log(\"\\n Setup cancelled.\");\n await close();\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n // Wait for the browser wizard to signal completion\n await waitForCompletion();\n\n // Brief delay to let the HTTP response flush\n await new Promise((r) => setTimeout(r, 300));\n await close();\n\n log(\"\");\n log(\" Setup complete! Restart your AI clients to see the new tools.\");\n log(\"\");\n}\n","import { DEFAULT_MCPS, DEFAULT_SERVER_URL, MCP_NAMES } from \"../constants.js\";\nimport { getEffectiveApiKey } from \"../auth/credentials.js\";\nimport { createConfig, writeConfig } from \"../config/storage.js\";\nimport { configureClient } from \"../config/clients.js\";\nimport type { ClientType } from \"../config/types.js\";\n\ninterface ConfigureOptions {\n key?: string;\n mcps?: string;\n client: string;\n}\n\nconst VALID_CLIENTS: Record<string, ClientType> = {\n \"claude-desktop\": \"claude-desktop\",\n \"claude-code\": \"claude-code\",\n cursor: \"cursor\",\n codex: \"codex\",\n perplexity: \"perplexity\",\n stdio: \"stdio\",\n};\n\n/**\n * Non-interactive configure command.\n * Writes config to a specific client.\n */\nexport async function runConfigure(options: ConfigureOptions): Promise<void> {\n const key = options.key || getEffectiveApiKey();\n const { client } = options;\n\n // Validate client\n const clientType = VALID_CLIENTS[client];\n if (!clientType) {\n console.error(\n `Unknown client: ${client}. Valid options: ${Object.keys(VALID_CLIENTS).join(\", \")}`\n );\n process.exit(1);\n }\n\n // Parse MCPs\n let selectedMcps: string[];\n if (options.mcps) {\n selectedMcps = options.mcps\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => MCP_NAMES.includes(s));\n\n if (selectedMcps.length === 0) {\n console.error(\n `No valid MCPs. Available: ${MCP_NAMES.join(\", \")}`\n );\n process.exit(1);\n }\n } else {\n selectedMcps = [...DEFAULT_MCPS];\n }\n\n // Configure\n try {\n const result = configureClient(\n clientType,\n DEFAULT_SERVER_URL,\n key,\n selectedMcps\n );\n console.log(result);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to configure ${client}: ${msg}`);\n process.exit(1);\n }\n\n // Save config\n const config = createConfig(key, selectedMcps);\n config.configuredClients = [clientType];\n writeConfig(config);\n\n console.log(`Config saved to ~/.cortex-mcp/config.json`);\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CortexHttpClient } from \"./http-client.js\";\nimport { FILESYSTEM_TOOLS, handleLocalFilesystemTool } from \"./local-filesystem.js\";\n\ninterface StdioServerOptions {\n serverUrl: string;\n apiKey: string;\n /** Use \"cortex\" for composite endpoint, or a specific MCP name */\n endpoint?: string;\n}\n\n/** Tools that support local file_path → upload interception */\nconst UPLOAD_TOOLS = new Set([\"upload_file\", \"upload_file_to_sharepoint\"]);\n\n/** Max file size for inline base64 upload via JSON-RPC (3MB).\n * 3MB raw → 4MB base64 → safely within Vercel's ~4.5MB body limit. */\nconst INLINE_UPLOAD_MAX = 3 * 1024 * 1024;\n\n/**\n * Override upload tool schemas so the LLM only sees `file_path` (not `content`).\n * This forces the LLM to pass a local file path, which the proxy handles locally\n * — no base64 encoding by the LLM, no truncation, no corruption.\n */\nfunction overrideUploadToolSchema(\n tool: Record<string, unknown>\n): Record<string, unknown> {\n const name = tool.name as string;\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n\n if (baseName === \"upload_file\") {\n return {\n ...tool,\n description:\n \"Upload a local file to the user's OneDrive. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically. Works with any file size.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description:\n \"Absolute path to the local file to upload \" +\n \"(e.g. '/Users/name/Documents/report.xlsx')\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in OneDrive \" +\n \"(e.g. 'Documents/report.xlsx')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n if (baseName === \"upload_file_to_sharepoint\") {\n return {\n ...tool,\n description:\n \"Upload a local file to a SharePoint document library. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"site_id\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description: \"Absolute path to the local file to upload\",\n },\n site_id: {\n type: \"string\",\n description: \"SharePoint site ID\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in the site drive \" +\n \"(e.g. 'Shared Documents/report.pdf')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n return tool;\n}\n\n/**\n * Handle a file upload tool call locally by reading the file from disk.\n *\n * - Small files (≤3MB): base64-encode and forward as `content` param\n * - Large files (>3MB): get an upload session URL from backend, then\n * relay 2.5MB chunks through the backend's `upload_file_chunk` tool\n * (the backend has unrestricted internet; the proxy may be sandboxed)\n */\nasync function handleLocalFileUpload(\n cortex: CortexHttpClient,\n toolName: string,\n args: Record<string, unknown>\n): Promise<{ content: Array<{ type: string; text: string }>; isError: boolean }> {\n const filePath = args.file_path as string;\n\n // Validate file exists\n let fileSize: number;\n try {\n fileSize = statSync(filePath as string).size;\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: `File not found: ${filePath}`,\n }) }],\n isError: true,\n };\n }\n\n if (fileSize <= INLINE_UPLOAD_MAX) {\n // Small file: read, base64-encode, and forward via normal JSON-RPC\n const fileBuffer = readFileSync(filePath);\n const base64Content = fileBuffer.toString(\"base64\");\n\n const forwardArgs: Record<string, unknown> = { ...args, content: base64Content };\n delete forwardArgs.file_path;\n\n console.error(\n `[cortex-mcp] ${toolName}: reading local file (${(fileSize / 1024).toFixed(1)}KB), forwarding as base64`\n );\n\n const response = await cortex.callTool(toolName, forwardArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\", text: response.error.message }],\n isError: true,\n };\n }\n\n return response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n }\n\n // Large file: use upload session to upload directly to Microsoft Graph\n console.error(\n `[cortex-mcp] ${toolName}: large file (${(fileSize / 1024 / 1024).toFixed(1)}MB), using upload session`\n );\n\n // Step 1: Get pre-authenticated upload URL from backend\n // Preserve composite prefix (e.g. \"m365__\") so the backend routes correctly\n const prefix = toolName.includes(\"__\") ? toolName.split(\"__\")[0] + \"__\" : \"\";\n const sessionResponse = await cortex.callTool(`${prefix}create_upload_session`, {\n path: args.path,\n });\n\n if (sessionResponse.error) {\n return {\n content: [{ type: \"text\", text: sessionResponse.error.message }],\n isError: true,\n };\n }\n\n const sessionResult = sessionResponse.result as {\n content: Array<{ type: string; text: string }>;\n };\n\n let sessionData: { uploadUrl?: string };\n try {\n sessionData = JSON.parse(sessionResult.content[0].text);\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Failed to parse upload session response from backend\",\n }) }],\n isError: true,\n };\n }\n\n const uploadUrl = sessionData.uploadUrl;\n if (!uploadUrl) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Backend did not return an uploadUrl\",\n }) }],\n isError: true,\n };\n }\n\n // Step 2: Upload file in chunks through the Cortex backend.\n // We relay via the backend (which has unrestricted internet) instead of\n // uploading directly to Graph — the proxy may be in a sandboxed VM that\n // blocks outbound connections to SharePoint/Microsoft Graph.\n const fileBuffer = readFileSync(filePath);\n const chunkSize = 2.5 * 1024 * 1024; // 2.5MB → ~3.33MB base64 → within Vercel 4.5MB limit\n const total = fileBuffer.length;\n let driveItem: Record<string, unknown> = {};\n\n for (let start = 0; start < total; start += chunkSize) {\n const end = Math.min(start + chunkSize, total);\n const chunk = fileBuffer.subarray(start, end);\n const chunkBase64 = Buffer.from(chunk).toString(\"base64\");\n\n console.error(\n `[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total} via backend relay`\n );\n\n const chunkResponse = await cortex.callTool(`${prefix}upload_file_chunk`, {\n upload_url: uploadUrl,\n chunk: chunkBase64,\n range_start: start,\n range_end: end - 1,\n total_size: total,\n });\n\n if (chunkResponse.error) {\n return {\n content: [{ type: \"text\", text: chunkResponse.error.message }],\n isError: true,\n };\n }\n\n const chunkResult = chunkResponse.result as {\n content: Array<{ type: string; text: string }>;\n };\n\n let chunkData: Record<string, unknown>;\n try {\n chunkData = JSON.parse(chunkResult.content[0].text) as Record<string, unknown>;\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Failed to parse chunk upload response from backend\",\n }) }],\n isError: true,\n };\n }\n\n if (!chunkData.success) {\n return {\n content: [{ type: \"text\", text: JSON.stringify(chunkData) }],\n isError: true,\n };\n }\n\n // Final chunk (200/201) contains the DriveItem\n if (chunkData.status === 200 || chunkData.status === 201) {\n driveItem = (chunkData.data as Record<string, unknown>) ?? {};\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: true,\n file: driveItem,\n message: `Uploaded '${args.path}' (${(fileSize / 1024 / 1024).toFixed(1)}MB) via upload session`,\n }) }],\n isError: false,\n };\n}\n\n/**\n * Start a stdio MCP server that proxies all requests to the hosted Cortex server.\n * This is used by clients that only support stdio transport (e.g., OpenClaw).\n */\nexport async function startStdioServer(\n options: StdioServerOptions\n): Promise<void> {\n const { serverUrl, apiKey, endpoint = \"cortex\" } = options;\n\n const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);\n\n const server = new Server(\n { name: \"cortex-mcp\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: false } } }\n );\n\n // Lazy initialization — runs on first request, not at startup\n let initialized = false;\n\n async function ensureInitialized(): Promise<void> {\n if (initialized) return;\n try {\n console.error(\"[cortex-mcp] Initializing backend session...\");\n await cortex.initialize();\n console.error(\"[cortex-mcp] Backend session established.\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[cortex-mcp] Backend initialization failed: ${msg}`);\n // Continue anyway — tools/list and tools/call may still work without a session\n }\n initialized = true;\n }\n\n // Forward tools/list to Cortex\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n await ensureInitialized();\n const response = await cortex.listTools();\n\n if (response.error) {\n throw new Error(response.error.message);\n }\n\n const result = response.result as { tools: Record<string, unknown>[] };\n const tools = (result.tools || []).map(overrideUploadToolSchema);\n return { tools };\n });\n\n // Forward tools/call to Cortex (with local file upload interception)\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n await ensureInitialized();\n const { name, arguments: args } = request.params;\n const typedArgs = (args ?? {}) as Record<string, unknown>;\n\n // Intercept upload tools with file_path — handle locally\n // Composite endpoint prefixes tool names with \"{mcp}__\", so match on suffix\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {\n return handleLocalFileUpload(cortex, name, typedArgs);\n }\n\n // Intercept filesystem tools — execute locally instead of on remote server\n if (FILESYSTEM_TOOLS.has(baseName)) {\n return handleLocalFilesystemTool(name, baseName, typedArgs);\n }\n\n const response = await cortex.callTool(name, typedArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\" as const, text: response.error.message }],\n isError: true,\n };\n }\n\n const result = response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n\n return result;\n });\n\n // Connect to stdio transport immediately — do NOT block on backend init\n console.error(\"[cortex-mcp] Starting stdio server...\");\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[cortex-mcp] Stdio server connected.\");\n}\n","import { PROTOCOL_VERSION } from \"../constants.js\";\n\n/** JSON-RPC 2.0 request */\ninterface JsonRpcRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n id: string | number;\n}\n\n/** JSON-RPC 2.0 response */\ninterface JsonRpcResponse {\n jsonrpc: \"2.0\";\n result?: unknown;\n error?: { code: number; message: string };\n id: string | number | null;\n}\n\n/**\n * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.\n */\nexport class CortexHttpClient {\n private sessionId: string | null = null;\n private requestId = 0;\n\n constructor(\n private serverUrl: string,\n private apiKey: string,\n private endpoint: string = \"cortex\"\n ) {}\n\n /** Send an initialize request to establish a session */\n async initialize(): Promise<JsonRpcResponse> {\n const response = await this.sendRequest(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-proxy\", version: \"1.0.0\" },\n });\n\n // Send initialized notification (no id = notification, no response expected)\n await this.sendNotification(\"notifications/initialized\", {});\n\n return response;\n }\n\n /** List available tools */\n async listTools(): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/list\", {});\n }\n\n /** Call a tool */\n async callTool(\n name: string,\n args: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/call\", { name, arguments: args });\n }\n\n /** Send a JSON-RPC request and return the response */\n private async sendRequest(\n method: string,\n params: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n const id = String(++this.requestId);\n const body: JsonRpcRequest = { jsonrpc: \"2.0\", method, params, id };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n \"x-cortex-client\": \"cortex-mcp-stdio\",\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(60_000),\n });\n\n // Capture session ID from initialize\n const newSessionId = response.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n this.sessionId = newSessionId;\n }\n\n if (!response.ok) {\n const text = await response.text();\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: this.humanReadableError(response.status, text),\n },\n id,\n };\n }\n\n return (await response.json()) as JsonRpcResponse;\n }\n\n /** Send a JSON-RPC notification (no response expected) */\n private async sendNotification(\n method: string,\n params: Record<string, unknown>\n ): Promise<void> {\n const body = { jsonrpc: \"2.0\", method, params };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n \"x-cortex-client\": \"cortex-mcp-stdio\",\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Notifications are fire-and-forget\n }\n }\n\n /** Convert HTTP status codes to user-friendly messages */\n private humanReadableError(status: number, body: string): string {\n switch (status) {\n case 401:\n return \"Invalid API key. Check your key at https://aidentity.app/settings/keys\";\n case 403:\n return \"API key lacks MCP permissions. Create a new key with MCP access.\";\n case 404:\n return \"MCP endpoint not found. The server may be misconfigured.\";\n case 503:\n return \"MCP protocol is disabled on the server.\";\n default:\n return `Server error (${status}): ${body.slice(0, 200)}`;\n }\n }\n}\n","/**\n * Local filesystem tool handlers for cortex-mcp.\n *\n * Intercepts filesystem MCP tool calls and executes them locally on the\n * user's machine using Node.js fs APIs, instead of forwarding to the\n * remote Cortex server (which cannot access local files).\n *\n * Response shapes match the server-side FileSystem MCP exactly so that\n * consumers see identical results regardless of execution location.\n */\n\nimport {\n copyFileSync,\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n unlinkSync,\n writeFileSync,\n appendFileSync,\n renameSync,\n type Dirent,\n} from \"node:fs\";\nimport { basename, dirname, extname, join, relative, resolve } from \"node:path\";\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\n/** Tools handled locally instead of proxied to the server. */\nexport const FILESYSTEM_TOOLS = new Set([\n \"read_file\",\n \"read_file_lines\",\n \"write_file\",\n \"append_file\",\n \"create_directory\",\n \"delete\",\n \"move\",\n \"copy\",\n \"list_directory\",\n \"exists\",\n \"get_file_info\",\n \"search_files\",\n \"find_in_files\",\n \"get_tree\",\n]);\n\n/** Maximum bytes to read in a single read_file call (50 MB). */\nconst MAX_READ_SIZE = 50 * 1024 * 1024;\n\n/** Bytes to sample for binary detection. */\nconst BINARY_CHECK_SIZE = 8192;\n\n/** Binary file magic byte signatures. */\nconst BINARY_SIGNATURES: Buffer[] = [\n Buffer.from([0x89, 0x50, 0x4e, 0x47]), // PNG\n Buffer.from([0xff, 0xd8, 0xff]), // JPEG\n Buffer.from(\"GIF8\"), // GIF\n Buffer.from([0x50, 0x4b, 0x03, 0x04]), // ZIP / DOCX / XLSX\n Buffer.from(\"%PDF\"), // PDF\n Buffer.from([0x7f, 0x45, 0x4c, 0x46]), // ELF\n Buffer.from([0xfe, 0xed, 0xfa]), // Mach-O\n Buffer.from([0xcf, 0xfa, 0xed, 0xfe]), // Mach-O (reversed)\n Buffer.from(\"MZ\"), // Windows executable\n];\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype ToolResult = {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Resolve a path — absolute paths as-is, relative against cwd. */\nfunction resolvePath(p: string): string {\n return resolve(p);\n}\n\n/** Detect whether a file is binary by checking magic bytes and null bytes. */\nfunction isBinaryFile(filePath: string): boolean {\n try {\n const fd = readFileSync(filePath, { flag: \"r\" });\n const chunk = fd.subarray(0, BINARY_CHECK_SIZE);\n if (chunk.length === 0) return false;\n\n for (const sig of BINARY_SIGNATURES) {\n if (chunk.length >= sig.length && chunk.subarray(0, sig.length).equals(sig)) {\n return true;\n }\n }\n return chunk.includes(0);\n } catch {\n return false;\n }\n}\n\n/** Convert a simple glob pattern to a RegExp (supports *, ?, [...]). */\nfunction globToRegex(pattern: string): RegExp {\n let re = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const c = pattern[i];\n if (c === \"*\") {\n re += \".*\";\n } else if (c === \"?\") {\n re += \".\";\n } else if (c === \"[\") {\n const close = pattern.indexOf(\"]\", i + 1);\n if (close !== -1) {\n re += \"[\" + pattern.slice(i + 1, close) + \"]\";\n i = close;\n } else {\n re += \"\\\\[\";\n }\n } else if (\".+^${}()|\\\\\".includes(c)) {\n re += \"\\\\\" + c;\n } else {\n re += c;\n }\n }\n re += \"$\";\n return new RegExp(re);\n}\n\n/** Get file type string from stat/dirent. */\nfunction fileType(isDir: boolean, isSymlink: boolean): string {\n if (isSymlink) return \"symlink\";\n if (isDir) return \"directory\";\n return \"file\";\n}\n\n/** Count files and total bytes in a directory tree. */\nfunction countDirectoryContents(dirPath: string): { files: number; bytes: number } {\n let files = 0;\n let bytes = 0;\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n for (const entry of entries) {\n const full = join(dirPath, entry.name);\n if (entry.isDirectory()) {\n const sub = countDirectoryContents(full);\n files += sub.files;\n bytes += sub.bytes;\n } else {\n files += 1;\n try {\n bytes += statSync(full).size;\n } catch { /* skip */ }\n }\n }\n } catch { /* skip */ }\n return { files, bytes };\n}\n\n/** Build success result. */\nfunction ok(data: unknown): ToolResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data) }],\n isError: false,\n };\n}\n\n/** Build error result. */\nfunction err(message: string): ToolResult {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ success: false, error: message }) }],\n isError: true,\n };\n}\n\n// ─── Tool Handlers ───────────────────────────────────────────────────────────\n\nfunction handleReadFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const encoding = (args.encoding as string) || \"utf-8\";\n\n const stat = statSync(filePath);\n if (!stat.isFile()) throw new Error(\"Not a file.\");\n if (stat.size > MAX_READ_SIZE) {\n throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size (${MAX_READ_SIZE} bytes).`);\n }\n\n if (isBinaryFile(filePath)) {\n const raw = readFileSync(filePath);\n return ok({\n content: raw.toString(\"base64\"),\n is_binary: true,\n size: stat.size,\n encoding: \"base64\",\n path: filePath,\n });\n }\n\n const text = readFileSync(filePath, encoding as BufferEncoding);\n return ok({\n content: text,\n is_binary: false,\n size: stat.size,\n encoding,\n path: filePath,\n });\n}\n\nfunction handleReadFileLines(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const encoding = (args.encoding as string) || \"utf-8\";\n const startLine = (args.start_line as number) || 1;\n const endLine = args.end_line as number | undefined;\n\n const stat = statSync(filePath);\n if (!stat.isFile()) throw new Error(\"Not a file.\");\n if (stat.size > MAX_READ_SIZE) {\n throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size.`);\n }\n\n if (startLine < 1) throw new Error(\"start_line must be >= 1.\");\n if (endLine !== undefined && endLine < startLine) throw new Error(\"end_line must be >= start_line.\");\n\n const text = readFileSync(filePath, encoding as BufferEncoding);\n const allLines = text.split(/\\n/);\n const totalLines = allLines.length;\n\n const startIdx = startLine - 1;\n const endIdx = endLine !== undefined ? endLine : totalLines;\n const selected = allLines.slice(startIdx, endIdx);\n const actualEnd = Math.min(endIdx, totalLines);\n\n return ok({\n content: selected.join(\"\\n\"),\n start_line: startLine,\n end_line: actualEnd,\n total_lines: totalLines,\n size: stat.size,\n path: filePath,\n });\n}\n\nfunction handleWriteFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const content = args.content as string;\n const encoding = (args.encoding as string) || \"utf-8\";\n const isBinary = (args.is_binary as boolean) || false;\n const overwrite = args.overwrite !== false; // default true\n const createParents = (args.create_parents as boolean) || false;\n\n const created = !existsSync(filePath);\n\n if (!overwrite && !created) {\n throw new Error(\"File already exists.\");\n }\n\n if (createParents) {\n mkdirSync(dirname(filePath), { recursive: true });\n }\n\n let bytesWritten: number;\n let usedEncoding: string;\n\n if (isBinary) {\n const raw = Buffer.from(content, \"base64\");\n writeFileSync(filePath, raw);\n bytesWritten = raw.length;\n usedEncoding = \"base64\";\n } else {\n const encoded = Buffer.from(content, encoding as BufferEncoding);\n writeFileSync(filePath, content, encoding as BufferEncoding);\n bytesWritten = encoded.length;\n usedEncoding = encoding;\n }\n\n return ok({\n path: filePath,\n bytes_written: bytesWritten,\n created,\n encoding: usedEncoding,\n });\n}\n\nfunction handleAppendFile(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n const content = args.content as string;\n const encoding = (args.encoding as string) || \"utf-8\";\n\n if (!existsSync(filePath)) throw new Error(\"File not found.\");\n\n const encoded = Buffer.from(content, encoding as BufferEncoding);\n appendFileSync(filePath, content, encoding as BufferEncoding);\n const newSize = statSync(filePath).size;\n\n return ok({\n path: filePath,\n bytes_written: encoded.length,\n new_size: newSize,\n });\n}\n\nfunction handleCreateDirectory(args: Record<string, unknown>): ToolResult {\n const dirPath = resolvePath(args.path as string);\n const parents = args.parents !== false; // default true\n\n if (existsSync(dirPath)) {\n return ok({ path: dirPath, created: false });\n }\n\n if (parents) {\n mkdirSync(dirPath, { recursive: true });\n } else {\n mkdirSync(dirPath);\n }\n\n return ok({ path: dirPath, created: true });\n}\n\nfunction handleDelete(args: Record<string, unknown>): ToolResult {\n const targetPath = resolvePath(args.path as string);\n const recursive = (args.recursive as boolean) || false;\n\n if (!existsSync(targetPath)) throw new Error(\"Path not found.\");\n\n const stat = statSync(targetPath, { throwIfNoEntry: false });\n if (!stat) throw new Error(\"Path not found.\");\n\n if (stat.isDirectory()) {\n const { files: filesDeleted, bytes: bytesFreed } = countDirectoryContents(targetPath);\n if (recursive) {\n rmSync(targetPath, { recursive: true });\n } else {\n // rmSync without recursive only removes empty dirs\n rmSync(targetPath);\n }\n return ok({\n path: targetPath,\n type: \"directory\",\n files_deleted: recursive ? filesDeleted : 0,\n bytes_freed: recursive ? bytesFreed : 0,\n });\n }\n\n const size = stat.size;\n unlinkSync(targetPath);\n return ok({\n path: targetPath,\n type: \"file\",\n files_deleted: 1,\n bytes_freed: size,\n });\n}\n\nfunction handleMove(args: Record<string, unknown>): ToolResult {\n const source = resolvePath(args.source as string);\n const destination = resolvePath(args.destination as string);\n const overwrite = (args.overwrite as boolean) || false;\n\n if (!existsSync(source)) throw new Error(\"Source path not found.\");\n if (existsSync(destination) && !overwrite) throw new Error(\"Destination already exists.\");\n\n const stat = statSync(source);\n const type = stat.isDirectory() ? \"directory\" : \"file\";\n const bytesMoved = stat.isDirectory()\n ? countDirectoryContents(source).bytes\n : stat.size;\n\n try {\n renameSync(source, destination);\n } catch (e: unknown) {\n // EXDEV: cross-device rename — fallback to copy + delete\n if ((e as NodeJS.ErrnoException).code === \"EXDEV\") {\n if (stat.isDirectory()) {\n cpSync(source, destination, { recursive: true });\n rmSync(source, { recursive: true });\n } else {\n copyFileSync(source, destination);\n unlinkSync(source);\n }\n } else {\n throw e;\n }\n }\n\n return ok({\n source,\n destination,\n type,\n bytes_moved: bytesMoved,\n });\n}\n\nfunction handleCopy(args: Record<string, unknown>): ToolResult {\n const source = resolvePath(args.source as string);\n const destination = resolvePath(args.destination as string);\n const overwrite = (args.overwrite as boolean) || false;\n\n if (!existsSync(source)) throw new Error(\"Source path not found.\");\n if (existsSync(destination) && !overwrite) throw new Error(\"Destination already exists.\");\n\n const stat = statSync(source);\n\n if (stat.isDirectory()) {\n if (existsSync(destination) && overwrite) {\n rmSync(destination, { recursive: true });\n }\n cpSync(source, destination, { recursive: true });\n const { bytes } = countDirectoryContents(destination);\n return ok({ source, destination, type: \"directory\", bytes_copied: bytes });\n }\n\n copyFileSync(source, destination);\n const bytesCopied = statSync(destination).size;\n return ok({ source, destination, type: \"file\", bytes_copied: bytesCopied });\n}\n\nfunction handleListDirectory(args: Record<string, unknown>): ToolResult {\n const dirPath = resolvePath(args.path as string);\n const recursive = (args.recursive as boolean) || false;\n const maxDepth = (args.max_depth as number) || 1;\n const pattern = args.pattern as string | undefined;\n const includeHidden = (args.include_hidden as boolean) || false;\n\n if (!existsSync(dirPath)) throw new Error(\"Directory not found.\");\n const stat = statSync(dirPath);\n if (!stat.isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const effectiveDepth = recursive ? maxDepth : 1;\n const patternRe = pattern ? globToRegex(pattern) : null;\n\n interface Entry {\n name: string;\n path: string;\n type: string;\n size: number;\n modified_at: string;\n is_hidden: boolean;\n }\n\n const entries: Entry[] = [];\n\n function scan(dir: string, depth: number): void {\n if (depth >= effectiveDepth) return;\n\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n dirents.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const d of dirents) {\n if (!includeHidden && d.name.startsWith(\".\")) continue;\n\n const fullPath = join(dir, d.name);\n const isDir = d.isDirectory();\n\n // Apply pattern filter\n if (patternRe && !patternRe.test(d.name)) {\n // Still recurse into directories even if name doesn't match\n if (isDir && effectiveDepth > 1) {\n scan(fullPath, depth + 1);\n }\n continue;\n }\n\n let size = 0;\n let mtime = new Date().toISOString();\n try {\n const s = statSync(fullPath);\n size = isDir ? 0 : s.size;\n mtime = new Date(s.mtimeMs).toISOString();\n } catch { /* skip */ }\n\n entries.push({\n name: d.name,\n path: relative(dirPath, fullPath),\n type: fileType(isDir, d.isSymbolicLink()),\n size,\n modified_at: mtime,\n is_hidden: d.name.startsWith(\".\"),\n });\n\n if (isDir && effectiveDepth > 1) {\n scan(fullPath, depth + 1);\n }\n }\n }\n\n scan(dirPath, 0);\n\n return ok({\n path: dirPath,\n entries,\n total_count: entries.length,\n });\n}\n\nfunction handleExists(args: Record<string, unknown>): ToolResult {\n const targetPath = resolvePath(args.path as string);\n const pathExists = existsSync(targetPath);\n\n let type: string | null = null;\n if (pathExists) {\n const stat = statSync(targetPath, { throwIfNoEntry: false });\n if (stat) {\n type = fileType(stat.isDirectory(), stat.isSymbolicLink());\n }\n }\n\n return ok({ exists: pathExists, path: targetPath, type });\n}\n\nfunction handleGetFileInfo(args: Record<string, unknown>): ToolResult {\n const filePath = resolvePath(args.path as string);\n if (!existsSync(filePath)) throw new Error(\"Path not found.\");\n\n const stat = statSync(filePath, { throwIfNoEntry: false });\n if (!stat) throw new Error(\"Path not found.\");\n\n const isDir = stat.isDirectory();\n const isSymlink = stat.isSymbolicLink();\n const name = basename(filePath);\n const ext = isDir ? null : extname(name) || null;\n const binary = !isDir && !isSymlink ? isBinaryFile(filePath) : false;\n\n return ok({\n name,\n path: filePath,\n type: fileType(isDir, isSymlink),\n size: isDir ? 0 : stat.size,\n modified_at: new Date(stat.mtimeMs).toISOString(),\n created_at: stat.birthtimeMs ? new Date(stat.birthtimeMs).toISOString() : null,\n extension: ext,\n is_hidden: name.startsWith(\".\"),\n is_binary: binary,\n });\n}\n\nfunction handleSearchFiles(args: Record<string, unknown>): ToolResult {\n const searchPath = resolvePath(args.path as string);\n const pattern = (args.pattern as string) || \"*\";\n const recursive = args.recursive !== false; // default true\n const includeHidden = (args.include_hidden as boolean) || false;\n const maxResults = (args.max_results as number) || 1000;\n\n if (!existsSync(searchPath)) throw new Error(\"Directory not found.\");\n if (!statSync(searchPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const patternRe = globToRegex(pattern);\n const matches: string[] = [];\n let totalMatches = 0;\n\n function scan(dir: string): void {\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const d of dirents) {\n if (!includeHidden) {\n if (d.name.startsWith(\".\")) continue;\n }\n\n const fullPath = join(dir, d.name);\n\n if (d.isDirectory()) {\n if (recursive) scan(fullPath);\n continue;\n }\n\n if (patternRe.test(d.name)) {\n totalMatches++;\n if (matches.length < maxResults) {\n matches.push(relative(searchPath, fullPath));\n }\n }\n }\n }\n\n scan(searchPath);\n\n return ok({\n matches,\n total_matches: totalMatches,\n truncated: totalMatches > maxResults,\n search_path: searchPath,\n pattern,\n });\n}\n\nfunction handleFindInFiles(args: Record<string, unknown>): ToolResult {\n const searchPath = resolvePath(args.path as string);\n const query = args.query as string;\n const isRegex = (args.is_regex as boolean) || false;\n const caseSensitive = args.case_sensitive !== false; // default true\n const filePattern = args.pattern as string | undefined;\n const contextLines = (args.context_lines as number) || 0;\n const maxResults = (args.max_results as number) || 500;\n const includeHidden = (args.include_hidden as boolean) || false;\n\n if (!existsSync(searchPath)) throw new Error(\"Directory not found.\");\n if (!statSync(searchPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const flags = caseSensitive ? \"\" : \"i\";\n const re = isRegex\n ? new RegExp(query, flags)\n : new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), flags);\n\n const patternRe = filePattern ? globToRegex(filePattern) : null;\n\n interface Match {\n file: string;\n line: number;\n content: string;\n context_before: string[];\n context_after: string[];\n }\n\n const results: Match[] = [];\n let totalMatches = 0;\n let filesSearched = 0;\n let filesWithMatches = 0;\n\n function scan(dir: string): void {\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const d of dirents) {\n if (!includeHidden && d.name.startsWith(\".\")) continue;\n\n const fullPath = join(dir, d.name);\n\n if (d.isDirectory()) {\n scan(fullPath);\n continue;\n }\n\n if (patternRe && !patternRe.test(d.name)) continue;\n if (isBinaryFile(fullPath)) continue;\n\n filesSearched++;\n let fileHasMatch = false;\n\n let lines: string[];\n try {\n const text = readFileSync(fullPath, \"utf-8\");\n lines = text.split(\"\\n\");\n } catch {\n continue;\n }\n\n for (let i = 0; i < lines.length; i++) {\n if (re.test(lines[i])) {\n totalMatches++;\n fileHasMatch = true;\n\n if (results.length < maxResults) {\n const ctxStart = Math.max(0, i - contextLines);\n const ctxEnd = Math.min(lines.length, i + contextLines + 1);\n\n results.push({\n file: relative(searchPath, fullPath),\n line: i + 1,\n content: lines[i],\n context_before: lines.slice(ctxStart, i),\n context_after: lines.slice(i + 1, ctxEnd),\n });\n }\n }\n }\n\n if (fileHasMatch) filesWithMatches++;\n }\n }\n\n scan(searchPath);\n\n return ok({\n results,\n total_matches: totalMatches,\n files_searched: filesSearched,\n files_with_matches: filesWithMatches,\n truncated: totalMatches > maxResults,\n query,\n });\n}\n\nfunction handleGetTree(args: Record<string, unknown>): ToolResult {\n const rootPath = resolvePath(args.path as string);\n const maxDepth = (args.max_depth as number) || 3;\n const includeHidden = (args.include_hidden as boolean) || false;\n const maxEntries = (args.max_entries as number) || 500;\n\n if (!existsSync(rootPath)) throw new Error(\"Directory not found.\");\n if (!statSync(rootPath).isDirectory()) throw new Error(\"Path is not a directory.\");\n\n const lines: string[] = [];\n let entryCount = 0;\n let truncated = false;\n\n const rootName = basename(rootPath) || rootPath;\n lines.push(rootName + \"/\");\n\n function buildTree(dirPath: string, prefix: string, depth: number): void {\n if (truncated || depth >= maxDepth) return;\n\n let dirents: Dirent[];\n try {\n dirents = readdirSync(dirPath, { withFileTypes: true });\n } catch {\n return;\n }\n\n if (!includeHidden) {\n dirents = dirents.filter((d) => !d.name.startsWith(\".\"));\n }\n dirents.sort((a, b) => a.name.localeCompare(b.name));\n\n for (let i = 0; i < dirents.length; i++) {\n if (truncated) return;\n\n entryCount++;\n if (entryCount > maxEntries) {\n truncated = true;\n lines.push(prefix + \"... (truncated)\");\n return;\n }\n\n const d = dirents[i];\n const isLast = i === dirents.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n const childPrefix = prefix + (isLast ? \" \" : \"│ \");\n const fullPath = join(dirPath, d.name);\n\n if (d.isDirectory()) {\n lines.push(prefix + connector + d.name + \"/\");\n buildTree(fullPath, childPrefix, depth + 1);\n } else {\n lines.push(prefix + connector + d.name);\n }\n }\n }\n\n buildTree(rootPath, \"\", 0);\n\n return ok({\n tree: lines.join(\"\\n\"),\n path: rootPath,\n entry_count: entryCount,\n truncated,\n max_depth: maxDepth,\n });\n}\n\n// ─── Dispatcher ──────────────────────────────────────────────────────────────\n\nconst HANDLERS: Record<string, (args: Record<string, unknown>) => ToolResult> = {\n read_file: handleReadFile,\n read_file_lines: handleReadFileLines,\n write_file: handleWriteFile,\n append_file: handleAppendFile,\n create_directory: handleCreateDirectory,\n delete: handleDelete,\n move: handleMove,\n copy: handleCopy,\n list_directory: handleListDirectory,\n exists: handleExists,\n get_file_info: handleGetFileInfo,\n search_files: handleSearchFiles,\n find_in_files: handleFindInFiles,\n get_tree: handleGetTree,\n};\n\n/**\n * Handle a filesystem tool call locally on the user's machine.\n *\n * @param _toolName Full tool name (may include composite prefix like \"filesystem__\")\n * @param baseName Base tool name without prefix (e.g. \"read_file\")\n * @param args Tool arguments from the MCP client\n */\nexport function handleLocalFilesystemTool(\n _toolName: string,\n baseName: string,\n args: Record<string, unknown>,\n): ToolResult {\n console.error(`[cortex-mcp] filesystem.${baseName}: executing locally`);\n\n const handler = HANDLERS[baseName];\n if (!handler) {\n return err(`Unknown filesystem tool: ${baseName}`);\n }\n\n try {\n return handler(args);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n console.error(`[cortex-mcp] filesystem.${baseName} error: ${message}`);\n return err(message);\n }\n}\n","import { DEFAULT_API_KEY, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { startStdioServer } from \"../proxy/stdio-server.js\";\nimport { getEffectiveApiKey } from \"../auth/credentials.js\";\n\n/**\n * Start the stdio proxy server.\n * Reads API key from CORTEX_API_KEY env var or ~/.cortex-mcp/config.json.\n */\nexport async function runServe(): Promise<void> {\n // Resolve API key: env var > credentials > config file > default\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n const apiKey = getEffectiveApiKey();\n\n if (apiKey === DEFAULT_API_KEY) {\n process.stderr.write(\n \"Warning: Using shared API key. Personal MCPs (M365, Slack, etc.) \" +\n \"will not have access to your account.\\n\" +\n \"Run: npx @danainnovations/cortex-mcp@latest login\\n\\n\"\n );\n }\n\n // Start the stdio server (this blocks until the client disconnects)\n await startStdioServer({ serverUrl, apiKey });\n}\n","import { AVAILABLE_MCPS } from \"../constants.js\";\nimport { getConfigPath, readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\n/**\n * Show current Cortex MCP configuration.\n */\nexport function runStatus(): void {\n const config = readConfig();\n\n if (!config) {\n console.log(\"No configuration found. Run: npx @danainnovations/cortex-mcp setup\");\n return;\n }\n\n const maskedKey =\n config.apiKey.slice(0, 8) + \"****\" + config.apiKey.slice(-4);\n\n const creds = readCredentials();\n\n console.log(\"\");\n console.log(\" Cortex MCP Status\");\n console.log(\" -----------------\");\n\n if (creds) {\n console.log(` User: ${creds.name || creds.email}`);\n console.log(` Email: ${creds.email}`);\n } else {\n console.log(\" User: not logged in (using shared key)\");\n }\n\n console.log(` Config: ${getConfigPath()}`);\n console.log(` Server: ${config.server}`);\n console.log(` API Key: ${maskedKey}`);\n console.log(\"\");\n console.log(\" Enabled MCPs:\");\n\n for (const mcpName of config.mcps) {\n const mcp = AVAILABLE_MCPS.find((m) => m.name === mcpName);\n const display = mcp ? mcp.displayName : mcpName;\n console.log(` ${display.padEnd(15)} ${config.server}/mcp/${mcpName}`);\n }\n\n console.log(\"\");\n console.log(\" Configured Clients:\");\n\n if (config.configuredClients.length === 0) {\n console.log(\" (none)\");\n } else {\n for (const client of config.configuredClients) {\n console.log(` ${client}`);\n }\n }\n\n console.log(\"\");\n}\n","import { existsSync, unlinkSync, rmdirSync } from \"node:fs\";\nimport { resetClaudeDesktop, resetClaudeCode, resetCursor, resetCodex } from \"../config/clients.js\";\nimport { getConfigDir, getConfigPath } from \"../config/storage.js\";\n\n/**\n * Remove all Cortex MCP entries from configured clients and delete local config.\n */\nexport function runReset(): void {\n console.log(\"\");\n console.log(\" Resetting Cortex MCP configuration...\");\n\n // Reset Claude Desktop\n const desktopReset = resetClaudeDesktop();\n console.log(\n ` Claude Desktop: ${desktopReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Claude Code\n const codeReset = resetClaudeCode();\n console.log(\n ` Claude Code: ${codeReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Cursor\n const cursorReset = resetCursor();\n console.log(\n ` Cursor: ${cursorReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Codex\n const codexReset = resetCodex();\n console.log(\n ` Codex: ${codexReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Delete local config\n const configPath = getConfigPath();\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n console.log(` Config file: removed`);\n }\n\n const configDir = getConfigDir();\n try {\n rmdirSync(configDir);\n } catch {\n // Directory not empty or doesn't exist — fine\n }\n\n console.log(\"\");\n console.log(\" Done. Restart your AI clients to apply changes.\");\n console.log(\" Note: Login credentials are preserved. Run 'cortex-mcp logout' to sign out.\");\n console.log(\"\");\n}\n","import * as readline from \"node:readline\";\nimport { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig, writeConfig } from \"../config/storage.js\";\nimport { configureClient } from \"../config/clients.js\";\nimport { readCredentials, writeCredentials } from \"../auth/credentials.js\";\nimport { openBrowser } from \"../utils/browser.js\";\nimport { connectProvider } from \"./connect.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport type LoginResult =\n | { status: \"success\"; apiKey: string; email: string; name?: string }\n | { status: \"needs_hint\"; ssoEmail: string }\n | { status: \"failed\" };\n\n/**\n * Core SSO login flow: initiate -> browser -> poll.\n * Reusable by both the `login` command and the setup wizard.\n *\n * Optionally accepts an emailHint for users whose Okta email\n * differs from their company directory email.\n */\nexport async function loginWithSSO(\n serverUrl: string,\n emailHint?: string,\n): Promise<LoginResult> {\n // Initiate auth flow\n let sessionId: string;\n let authUrl: string;\n let expiresIn: number;\n\n try {\n const body: Record<string, string> = {};\n if (emailHint) {\n body.email_hint = emailHint;\n }\n\n const resp = await fetch(`${serverUrl}/api/v1/auth/employee/initiate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!resp.ok) {\n log(` Error: Failed to start authentication (HTTP ${resp.status})`);\n return { status: \"failed\" };\n }\n\n const data = (await resp.json()) as {\n session_id: string;\n auth_url: string;\n expires_in: number;\n };\n sessionId = data.session_id;\n authUrl = data.auth_url;\n expiresIn = data.expires_in;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(` Error: Could not reach Cortex server at ${serverUrl}`);\n log(` ${msg}`);\n return { status: \"failed\" };\n }\n\n // Open browser\n log(\" Opening browser for Okta SSO login...\");\n await openBrowser(authUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${authUrl}`);\n log(\"\");\n\n // Poll for completion\n const deadline = Date.now() + expiresIn * 1000;\n process.stderr.write(\" Waiting for authentication\");\n\n while (Date.now() < deadline) {\n await sleep(3000);\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/auth/employee/poll/${sessionId}`\n );\n const result = (await resp.json()) as {\n status: string;\n api_key?: string;\n employee_name?: string;\n employee_email?: string;\n sso_email?: string;\n user_info?: { email?: string; name?: string };\n error_message?: string;\n };\n\n if (result.status === \"completed\") {\n process.stderr.write(\"\\n\\n\");\n\n const email =\n result.employee_email || result.user_info?.email || \"unknown\";\n const name = result.employee_name || result.user_info?.name || \"\";\n const apiKey = result.api_key || \"\";\n\n return { status: \"success\", apiKey, email, name: name || undefined };\n }\n\n if (result.status === \"expired\") {\n process.stderr.write(\"\\n\");\n log(\" Authentication session expired. Please try again.\");\n return { status: \"failed\" };\n }\n\n if (result.status === \"error\") {\n process.stderr.write(\"\\n\");\n\n // If AD verification failed and no hint was provided, signal for retry\n if (\n result.error_message === \"not_verified_employee\" &&\n !emailHint\n ) {\n return {\n status: \"needs_hint\",\n ssoEmail: result.sso_email || \"unknown\",\n };\n }\n\n log(\n ` Authentication failed: ${result.error_message || \"unknown error\"}`\n );\n return { status: \"failed\" };\n }\n\n // Still pending\n process.stderr.write(\".\");\n } catch {\n process.stderr.write(\"!\");\n }\n }\n\n process.stderr.write(\"\\n\");\n log(\" Timeout waiting for authentication. Please try again.\");\n return { status: \"failed\" };\n}\n\n/**\n * Show MCP connection status and auto-connect personal MCPs (e.g., Asana).\n */\nexport async function showConnectionsAndAutoConnect(\n apiKey: string,\n serverUrl: string,\n): Promise<void> {\n // Fetch existing connections\n let existingConnections: Array<{\n mcp_name: string;\n account_email: string | null;\n is_company_default: boolean;\n }> = [];\n\n try {\n const resp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": apiKey },\n });\n if (resp.ok) {\n const data = (await resp.json()) as {\n connections: typeof existingConnections;\n };\n existingConnections = data.connections || [];\n } else {\n log(\n ` Warning: Could not fetch current MCP connections (HTTP ${resp.status}).`\n );\n if (resp.status === 401 || resp.status === 403) {\n log(\" Warning: Authentication may be expired. Run 'cortex-mcp login' to refresh.\");\n }\n }\n } catch {\n log(\" Warning: Could not fetch current MCP connections due to a network error.\");\n }\n\n // Show MCP connections table\n log(\"\");\n log(\" MCP Connections\");\n log(\" \" + \"\\u2500\".repeat(45));\n\n const personalMcps: Array<{ name: string; displayName: string }> = [];\n\n for (const mcp of AVAILABLE_MCPS) {\n const conn = existingConnections.find((c) => c.mcp_name === mcp.name);\n\n if (mcp.authMode === \"company\") {\n log(` ${mcp.displayName.padEnd(15)} company default`);\n } else {\n // Personal auth required\n if (conn && !conn.is_company_default && conn.account_email) {\n log(` ${mcp.displayName.padEnd(15)} ${conn.account_email}`);\n } else {\n log(\n ` ${mcp.displayName.padEnd(15)} not connected (personal account required)`\n );\n personalMcps.push({ name: mcp.name, displayName: mcp.displayName });\n }\n }\n }\n\n log(\"\");\n\n // Auto-connect unconnected personal MCPs\n if (personalMcps.length === 0) {\n log(\" All accounts connected!\");\n log(\"\");\n return;\n }\n\n log(\" The following MCPs require a personal account to access your data.\");\n log(\" Skip any you don't have an account for — you can always connect later.\\n\");\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const ask = (question: string): Promise<string> =>\n new Promise((resolve) => {\n rl.question(question, (answer) => resolve(answer.trim().toLowerCase()));\n });\n\n for (const mcp of personalMcps) {\n const answer = await ask(\n ` Connect ${mcp.displayName}? (Y/n/s to skip all): `\n );\n\n if (answer === \"s\") {\n log(\" Skipping remaining account connections.\");\n log(\" You can connect anytime with: cortex-mcp connect <provider>\\n\");\n break;\n }\n\n if (answer === \"n\") {\n log(` Skipped. Connect later with: cortex-mcp connect ${mcp.name}`);\n log(\"\");\n continue;\n }\n\n // Y or Enter — proceed with OAuth\n log(\"\");\n const success = await connectProvider(\n mcp.name,\n mcp.displayName,\n apiKey,\n serverUrl,\n \"installUrl\" in mcp ? (mcp.installUrl as string) : undefined,\n );\n\n if (!success) {\n log(\n ` You can connect later with: cortex-mcp connect ${mcp.name}`\n );\n }\n log(\"\");\n }\n\n rl.close();\n}\n\n/**\n * Prompt the user for their company email when Okta email doesn't match AD.\n */\nasync function promptForEmailHint(ssoEmail: string): Promise<string | null> {\n log(\"\");\n log(` Your SSO email (${ssoEmail}) was not found in the employee directory.`);\n log(\n \" This can happen if your Okta account uses a different email than your company directory.\"\n );\n log(\"\");\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(\" Enter your company email address: \", (a) =>\n resolve(a.trim())\n );\n });\n rl.close();\n\n if (!answer || !answer.includes(\"@\")) {\n log(\" Invalid email. Run 'cortex-mcp login --email you@company.com' to try again.\");\n return null;\n }\n\n return answer;\n}\n\n/**\n * Log in via Okta SSO to get a personal API key.\n * Opens the browser, polls for completion, stores credentials,\n * auto-reconfigures any previously set up AI clients,\n * and prompts to connect personal MCP accounts (e.g., Asana).\n *\n * @param emailHint Optional company email (via --email flag) when Okta email differs from AD\n */\nexport async function runLogin(emailHint?: string): Promise<void> {\n // 1. Check if already logged in\n const existing = readCredentials();\n if (existing) {\n log(\"\");\n log(` Already logged in as ${existing.name || existing.email}`);\n log(\" Run 'cortex-mcp logout' first to switch accounts.\");\n log(\"\");\n return;\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n log(\"\");\n log(\" Cortex MCP Login\");\n log(\" Sign in with your company Okta SSO account\");\n log(\"\");\n\n // 2. Run SSO flow\n let result = await loginWithSSO(serverUrl, emailHint);\n\n // 3. Handle email hint retry if AD verification failed\n if (result.status === \"needs_hint\") {\n let hintEmail: string | null;\n\n if (emailHint) {\n // --email was already provided but SSO email still didn't match\n // (this shouldn't happen since hint is passed on first try, but handle it)\n hintEmail = emailHint;\n } else {\n hintEmail = await promptForEmailHint(result.ssoEmail);\n }\n\n if (!hintEmail) {\n process.exit(1);\n }\n\n log(\"\");\n log(\" Retrying with your company email. You'll need to complete SSO again.\");\n log(\"\");\n\n result = await loginWithSSO(serverUrl, hintEmail);\n\n if (result.status === \"needs_hint\") {\n log(\"\");\n log(\" The provided email was also not found in the employee directory.\");\n log(\" Please verify your email and contact IT if the issue persists.\");\n process.exit(1);\n }\n }\n\n if (result.status !== \"success\") {\n process.exit(1);\n }\n\n const { apiKey, email, name } = result;\n\n // 3. Save credentials\n writeCredentials({\n apiKey,\n email,\n name,\n authenticatedAt: new Date().toISOString(),\n });\n\n log(` Authenticated as ${name || email}${name ? ` (${email})` : \"\"}`);\n log(\" Personal API key saved.\");\n\n // 4. Auto-reconfigure existing clients and update saved config\n const existingConfig = readConfig();\n if (existingConfig) {\n // Always update the stored API key\n existingConfig.apiKey = apiKey;\n\n // Re-configure clients if any were previously set up\n if (existingConfig.configuredClients?.length > 0) {\n log(\"\");\n log(\" Updating configured clients:\");\n\n for (const clientType of existingConfig.configuredClients) {\n try {\n configureClient(\n clientType,\n existingConfig.server || DEFAULT_SERVER_URL,\n apiKey,\n existingConfig.mcps\n );\n log(` ${clientType}: updated`);\n } catch {\n log(` ${clientType}: failed to update`);\n }\n }\n }\n\n writeConfig(existingConfig);\n }\n\n // 5. Show MCP connection status and auto-connect personal MCPs\n await showConnectionsAndAutoConnect(apiKey, serverUrl);\n\n // 6. Notify backend that setup/login is complete (enables company-default connections)\n try {\n await fetch(`${serverUrl}/api/v1/setup/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n });\n } catch {\n // Non-fatal\n }\n\n log(\" Done! Restart your AI clients to apply.\");\n log(\"\");\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\nimport { openBrowser } from \"../utils/browser.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction waitForEnter(): Promise<void> {\n return new Promise((resolve) => {\n process.stdin.setRawMode?.(false);\n process.stdin.resume();\n process.stdin.once(\"data\", () => {\n process.stdin.pause();\n resolve();\n });\n });\n}\n\n/**\n * Core OAuth connect flow: initiate -> browser -> poll.\n * Reusable by both the `connect` command and the post-login auto-connect.\n *\n * Returns true on success, false on failure/timeout.\n */\nexport async function connectProvider(\n providerName: string,\n displayName: string,\n apiKey: string,\n serverUrl: string,\n installUrl?: string,\n): Promise<boolean> {\n // 1. Initiate OAuth flow\n let sessionId: string;\n let authUrl: string;\n let expiresIn: number;\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connect/${providerName}/initiate`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n },\n }\n );\n\n if (!resp.ok) {\n if (resp.status === 401) {\n log(\" Error: Your session has expired. Run 'cortex-mcp login' to re-authenticate.\");\n return false;\n }\n\n const err = (await resp.json().catch(() => ({}))) as { detail?: string };\n log(` Error: ${err.detail || `HTTP ${resp.status}`}`);\n return false;\n }\n\n const data = (await resp.json()) as {\n session_id: string;\n authorization_url: string;\n expires_in: number;\n };\n sessionId = data.session_id;\n authUrl = data.authorization_url;\n expiresIn = data.expires_in;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(` Error: Could not reach Cortex server at ${serverUrl}`);\n log(` ${msg}`);\n return false;\n }\n\n // 2. App installation (Monday.com requires install before authorize)\n if (installUrl) {\n log(` First, install the ${displayName} app on your account...`);\n await openBrowser(installUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${installUrl}`);\n log(\"\");\n log(\" Press Enter once the app is installed...\");\n await waitForEnter();\n log(\"\");\n }\n\n // 3. Open browser\n log(` Opening browser for ${displayName} authorization...`);\n await openBrowser(authUrl);\n log(\" If the browser didn't open, visit:\");\n log(` ${authUrl}`);\n log(\"\");\n\n // 4. Poll for completion\n const deadline = Date.now() + expiresIn * 1000;\n process.stderr.write(\" Waiting for authorization\");\n\n while (Date.now() < deadline) {\n await sleep(3000);\n\n try {\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connect/poll/${sessionId}`,\n {\n headers: {\n \"x-api-key\": apiKey,\n },\n }\n );\n const result = (await resp.json()) as {\n status: string;\n account_email?: string;\n account_name?: string;\n error_message?: string;\n };\n\n if (result.status === \"completed\") {\n process.stderr.write(\"\\n\\n\");\n const who = result.account_email || \"unknown\";\n log(` Connected as ${who}`);\n log(` ${displayName} tools will now use your personal account.`);\n log(\"\");\n return true;\n }\n\n if (result.status === \"expired\") {\n process.stderr.write(\"\\n\");\n log(\" Authorization session expired. Please try again.\");\n return false;\n }\n\n if (result.status === \"error\") {\n process.stderr.write(\"\\n\");\n log(\n ` Connection failed: ${result.error_message || \"unknown error\"}`\n );\n return false;\n }\n\n // Still pending\n process.stderr.write(\".\");\n } catch {\n process.stderr.write(\"!\");\n }\n }\n\n process.stderr.write(\"\\n\");\n log(\" Timeout waiting for authorization. Please try again.\");\n return false;\n}\n\n/**\n * Connect a personal OAuth account (e.g., Asana) via browser-based flow.\n * CLI command handler — validates credentials and delegates to connectProvider().\n */\nexport async function runConnect(provider: string): Promise<void> {\n // 1. Verify user is logged in\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" You must be logged in first.\");\n log(\" Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n // 2. Validate provider\n const mcp = AVAILABLE_MCPS.find(\n (m) => m.name === provider || m.displayName.toLowerCase() === provider.toLowerCase()\n );\n if (!mcp) {\n log(\"\");\n log(` Unknown provider: ${provider}`);\n log(` Available: ${AVAILABLE_MCPS.map((m) => m.name).join(\", \")}`);\n log(\"\");\n process.exit(1);\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n log(\"\");\n log(` Cortex MCP — Connect ${mcp.displayName}`);\n log(` Link your personal ${mcp.displayName} account`);\n log(\"\");\n\n const success = await connectProvider(\n mcp.name,\n mcp.displayName,\n creds.apiKey,\n serverUrl,\n \"installUrl\" in mcp ? (mcp.installUrl as string) : undefined,\n );\n\n if (!success) {\n process.exit(1);\n }\n}\n","import { deleteCredentials, readCredentials } from \"../auth/credentials.js\";\n\nexport function runLogout(): void {\n const creds = readCredentials();\n\n if (!creds) {\n console.log(\"\");\n console.log(\" Not currently logged in.\");\n console.log(\"\");\n return;\n }\n\n deleteCredentials();\n\n console.log(\"\");\n console.log(` Logged out (was: ${creds.email})`);\n console.log(\" MCP tools will use the shared default key.\");\n console.log(\" Run 'cortex-mcp login' to sign in again.\");\n console.log(\"\");\n}\n","import { readCredentials } from \"../auth/credentials.js\";\n\nexport function runWhoami(): void {\n const creds = readCredentials();\n\n if (!creds) {\n console.log(\"\");\n console.log(\" Not logged in. Using shared default API key.\");\n console.log(\" Run 'cortex-mcp login' to authenticate.\");\n console.log(\"\");\n return;\n }\n\n console.log(\"\");\n console.log(\" Cortex MCP Account\");\n console.log(\" ------------------\");\n if (creds.name) {\n console.log(` Name: ${creds.name}`);\n }\n console.log(` Email: ${creds.email}`);\n console.log(` Authenticated: ${creds.authenticatedAt}`);\n console.log(\n ` API Key: ${creds.apiKey.slice(0, 8)}****${creds.apiKey.slice(-4)}`\n );\n console.log(\"\");\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\ninterface MCPConnection {\n id: string;\n mcp_name: string;\n provider: string;\n account_email: string | null;\n status: string;\n is_company_default: boolean;\n}\n\n/**\n * List OAuth connections for the authenticated user.\n */\nexport async function runConnections(): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" Not logged in. Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n try {\n const resp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": creds.apiKey },\n });\n\n if (!resp.ok) {\n log(\"\");\n log(\" Error fetching connections.\");\n log(\"\");\n process.exit(1);\n }\n\n const data = (await resp.json()) as { connections: MCPConnection[] };\n const connections = data.connections || [];\n\n log(\"\");\n log(\" Cortex MCP Connections\");\n log(\" ----------------------\");\n\n // Show status for each available MCP\n for (const mcp of AVAILABLE_MCPS) {\n const conn = connections.find((c) => c.mcp_name === mcp.name);\n\n let statusText: string;\n if (!conn) {\n statusText = \"not connected\";\n } else if (conn.is_company_default) {\n statusText = \"(company default)\";\n } else {\n statusText = conn.account_email || \"connected\";\n }\n\n log(` ${mcp.displayName.padEnd(15)} ${statusText}`);\n }\n\n log(\"\");\n log(\" Connect an account: cortex-mcp connect <provider>\");\n log(\"\");\n } catch {\n log(\"\");\n log(\" Error: Could not reach Cortex server.\");\n log(\"\");\n process.exit(1);\n }\n}\n","import { AVAILABLE_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { readCredentials } from \"../auth/credentials.js\";\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\ninterface MCPConnection {\n id: string;\n mcp_name: string;\n provider: string;\n account_email: string | null;\n status: string;\n is_company_default: boolean;\n}\n\n/**\n * Disconnect a personal OAuth connection for a provider.\n */\nexport async function runDisconnect(provider: string): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n log(\"\");\n log(\" Not logged in. Run: cortex-mcp login\");\n log(\"\");\n process.exit(1);\n }\n\n // Validate provider\n const mcp = AVAILABLE_MCPS.find(\n (m) => m.name === provider || m.displayName.toLowerCase() === provider.toLowerCase()\n );\n if (!mcp) {\n log(\"\");\n log(` Unknown provider: ${provider}`);\n log(` Available: ${AVAILABLE_MCPS.map((m) => m.name).join(\", \")}`);\n log(\"\");\n process.exit(1);\n }\n\n const providerName = mcp.name;\n const displayName = mcp.displayName;\n const config = readConfig();\n const serverUrl = config?.server || DEFAULT_SERVER_URL;\n\n try {\n // List connections to find the user's personal connection\n const listResp = await fetch(`${serverUrl}/api/v1/oauth/connections`, {\n headers: { \"x-api-key\": creds.apiKey },\n });\n\n if (!listResp.ok) {\n log(\"\");\n log(\" Error fetching connections.\");\n log(\"\");\n process.exit(1);\n }\n\n const listData = (await listResp.json()) as {\n connections: MCPConnection[];\n };\n const match = (listData.connections || []).find(\n (c) => c.mcp_name === providerName && !c.is_company_default\n );\n\n if (!match) {\n log(\"\");\n log(` No personal connection found for ${displayName}.`);\n log(\"\");\n process.exit(1);\n }\n\n // Disconnect\n const resp = await fetch(\n `${serverUrl}/api/v1/oauth/connections/${match.id}/disconnect`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": creds.apiKey },\n }\n );\n\n if (resp.ok) {\n log(\"\");\n log(` Disconnected from ${displayName}.`);\n log(\n ` ${displayName} tools will fall back to the company default token.`\n );\n log(\"\");\n } else {\n log(\"\");\n log(\" Error disconnecting. Please try again.\");\n log(\"\");\n process.exit(1);\n }\n } catch {\n log(\"\");\n log(\" Error: Could not reach Cortex server.\");\n log(\"\");\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,YAAY,UAAU;;;ACKf,SAAS,gBAAwB;AACtC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["existsSync","readFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","err","existsSync","readFileSync","resolve","err","err","readFileSync","statSync","existsSync","mkdirSync","readFileSync","unlinkSync","writeFileSync","dirname","join","statSync","fileBuffer","readFileSync","err","existsSync","unlinkSync","existsSync","unlinkSync","log","resolve","err","log","sleep","resolve","err","log","log","err","DEFAULT_SERVER_URL","openBrowser"]}
|