jh-web-gateway 2.0.0 → 2.0.1
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
CHANGED
|
@@ -516,16 +516,21 @@ Options:
|
|
|
516
516
|
serve --port <n> Override the configured port
|
|
517
517
|
serve --pages <n> Max concurrent browser pages (default: 3)
|
|
518
518
|
logs --limit <n> Number of log entries to show (default: 50)
|
|
519
|
+
--version, -v Print version and exit
|
|
519
520
|
--help Show this help message
|
|
520
521
|
`);
|
|
521
522
|
}
|
|
522
523
|
async function main() {
|
|
524
|
+
if (command === "--version" || command === "-v") {
|
|
525
|
+
console.log("2.0.1");
|
|
526
|
+
process.exit(0);
|
|
527
|
+
}
|
|
523
528
|
if (command === "--help" || command === "-h") {
|
|
524
529
|
printHelp();
|
|
525
530
|
process.exit(0);
|
|
526
531
|
}
|
|
527
532
|
if (!command || command === "tui") {
|
|
528
|
-
const { launchTui } = await import("./tui-
|
|
533
|
+
const { launchTui } = await import("./tui-2J5JN3FF.js");
|
|
529
534
|
await launchTui();
|
|
530
535
|
return;
|
|
531
536
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/setup.ts","../src/cli/serve.ts","../src/cli/auth.ts","../src/cli/config.ts","../src/cli/status.ts","../src/cli/logs.ts","../src/cli/start.ts","../src/core/reauth-lock.ts","../src/cli.ts"],"sourcesContent":["/**\n * Interactive TUI setup wizard.\n * Steps: Chrome detection → JH auth → port selection → verify\n */\nimport * as p from \"@clack/prompts\";\nimport { getChromeWebSocketUrl } from \"../infra/chrome-cdp.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { generateApiKey } from \"../infra/gateway-auth.js\";\nimport { MODEL_ENDPOINT_MAP } from \"../infra/types.js\";\n\nconst COMMON_CDP_PORTS = [9222, 9223];\n\n/** Format a UNIX timestamp as a human-readable date string. */\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\n/** Step 1: Detect Chrome on common ports or prompt for custom URL. */\nasync function detectChrome(): Promise<string> {\n p.log.step(\"Step 1: Chrome detection\");\n\n for (const port of COMMON_CDP_PORTS) {\n const url = `http://127.0.0.1:${port}`;\n const spinner = p.spinner();\n spinner.start(`Checking ${url}…`);\n try {\n await getChromeWebSocketUrl(url, 2000);\n spinner.stop(`Chrome found at ${url}`);\n return url;\n } catch {\n spinner.stop(`Not found at ${url}`);\n }\n }\n\n // Prompt for custom CDP URL\n const custom = await p.text({\n message: \"Enter your Chrome CDP URL (e.g. http://127.0.0.1:9224):\",\n placeholder: \"http://127.0.0.1:9222\",\n validate(value) {\n if (!value.startsWith(\"http://\") && !value.startsWith(\"https://\")) {\n return \"Must be a valid URL starting with http:// or https://\";\n }\n },\n });\n\n if (p.isCancel(custom)) {\n p.cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n\n // Verify the custom URL\n const spinner = p.spinner();\n spinner.start(`Checking ${custom}…`);\n try {\n await getChromeWebSocketUrl(custom, 5000);\n spinner.stop(`Chrome found at ${custom}`);\n return custom;\n } catch (err) {\n spinner.stop(`Failed to connect to ${custom}`);\n throw err;\n }\n}\n\n/** Step 2: Capture JH credentials via CDP. */\nasync function captureAuth(cdpUrl: string): Promise<number> {\n p.log.step(\"Step 2: JH authentication\");\n p.log.info(\n \"Opening chat.ai.jh.edu in your browser. Send any message to trigger auth capture…\"\n );\n\n const spinner = p.spinner();\n spinner.start(\"Waiting for credentials (up to 120s)…\");\n\n const creds = await captureCredentials(cdpUrl, 120_000);\n const expiry = getTokenExpiry(creds.bearerToken);\n spinner.stop(`Credentials captured! Token expires: ${formatExpiry(expiry)}`);\n\n return expiry;\n}\n\n/** Step 3: Port selection. */\nasync function selectPort(): Promise<number> {\n p.log.step(\"Step 3: Port selection\");\n\n const input = await p.text({\n message: \"Gateway port:\",\n placeholder: \"8741\",\n defaultValue: \"8741\",\n validate(value) {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n return \"Must be a valid port number (1–65535)\";\n }\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n\n return Number(input);\n}\n\n/** Step 4: Verify by listing available models. */\nasync function verifyConnection(): Promise<void> {\n p.log.step(\"Step 4: Verification\");\n const models = Object.keys(MODEL_ENDPOINT_MAP);\n p.log.success(`Available models: ${models.join(\", \")}`);\n}\n\n/** Run the full setup wizard. */\nexport async function runSetup(): Promise<void> {\n p.intro(\"JH Web Gateway — Setup Wizard\");\n\n let cdpUrl: string | undefined;\n let port = 8741;\n\n // Step 1: Chrome detection (with retry)\n while (true) {\n try {\n cdpUrl = await detectChrome();\n break;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Chrome detection failed: ${msg}`);\n const retry = await p.confirm({ message: \"Retry Chrome detection?\" });\n if (p.isCancel(retry) || !retry) {\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n }\n }\n\n // Save CDP URL\n await updateConfig({ cdpUrl });\n\n // Step 2: Auth capture (with retry)\n while (true) {\n try {\n await captureAuth(cdpUrl);\n break;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Auth capture failed: ${msg}`);\n const retry = await p.confirm({ message: \"Retry authentication?\" });\n if (p.isCancel(retry) || !retry) {\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n }\n }\n\n // Step 3: Port selection\n try {\n port = await selectPort();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Port selection failed: ${msg}`);\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n\n // Generate API key and persist final config\n const apiKey = generateApiKey();\n await updateConfig({\n port,\n auth: { mode: \"bearer\", token: apiKey },\n });\n\n // Step 4: Verify\n await verifyConnection();\n\n // Load final config to confirm\n const config = await loadConfig();\n const baseUrl = `http://127.0.0.1:${config.port}`;\n\n p.outro(\"Setup complete!\");\n\n console.log(\"\\n Base URL: \" + baseUrl);\n console.log(\" API Key: \" + apiKey);\n console.log(\"\\n Test with curl:\");\n console.log(\n ` curl ${baseUrl}/v1/models -H \"Authorization: Bearer ${apiKey}\"\\n`\n );\n}\n","/**\n * `serve` command — start the HTTP server.\n */\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { connectToChrome, findOrOpenJhPage } from \"../infra/chrome-cdp.js\";\nimport { startServer } from \"../server.js\";\nimport { PagePool } from \"../core/page-pool.js\";\nimport type { Page } from \"playwright-core\";\n\nexport async function runServe(options: { port?: number; pages?: number }): Promise<void> {\n const config = await loadConfig();\n\n if (options.port !== undefined) {\n await updateConfig({ port: options.port });\n config.port = options.port;\n }\n\n const maxPages = options.pages ?? 3;\n\n let pool: PagePool | null = null;\n let browser: { close(): Promise<void> } | undefined;\n try {\n const conn = await connectToChrome(config.cdpUrl);\n const seedPage = await findOrOpenJhPage(conn.browser);\n browser = conn.browser;\n console.log(`Connected to Chrome at ${config.cdpUrl}`);\n\n const currentUrl = seedPage.url();\n if (!currentUrl.includes(\"chat.ai.jh.edu\")) {\n console.log(\"Navigating to chat.ai.jh.edu...\");\n await seedPage.goto(\"https://chat.ai.jh.edu\", { waitUntil: \"networkidle\" });\n }\n console.log(`Browser page: ${seedPage.url()}`);\n\n // Initialize the page pool\n pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(conn.browser, seedPage);\n console.log(`Page pool initialized (max ${maxPages} concurrent pages)`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`Warning: Could not connect to Chrome: ${msg}`);\n console.warn(\"Chat completions will fail until Chrome is available.\");\n }\n\n await startServer(config, {\n getPool: () => pool,\n getCredentials: () => config.credentials,\n browser,\n });\n}\n","/**\n * `auth` command — re-capture JH credentials without full setup wizard.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\n\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\nexport async function runAuth(): Promise<void> {\n const config = await loadConfig();\n\n console.log(`Connecting to Chrome at ${config.cdpUrl}…`);\n console.log(\n \"Opening chat.ai.jh.edu. Send any message to trigger auth capture (timeout: 120s)…\"\n );\n\n const creds = await captureCredentials(config.cdpUrl, 120_000);\n const expiry = getTokenExpiry(creds.bearerToken);\n\n console.log(\"Credentials captured successfully.\");\n console.log(`Token expires: ${formatExpiry(expiry)}`);\n}\n","/**\n * `config` command — print current configuration with credentials redacted.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport type { GatewayConfig } from \"../infra/types.js\";\n\nfunction redactConfig(config: GatewayConfig): Record<string, unknown> {\n return {\n cdpUrl: config.cdpUrl,\n port: config.port,\n defaultModel: config.defaultModel,\n defaultEndpoint: config.defaultEndpoint,\n credentials:\n config.credentials !== null\n ? {\n bearerToken: \"[REDACTED]\",\n cookie: \"[REDACTED]\",\n userAgent: config.credentials.userAgent,\n }\n : null,\n auth: {\n mode: config.auth.mode,\n token: config.auth.token !== null ? \"[REDACTED]\" : null,\n },\n maxQueueWaitMs: config.maxQueueWaitMs,\n };\n}\n\nexport async function runConfig(): Promise<void> {\n const config = await loadConfig();\n const redacted = redactConfig(config);\n console.log(JSON.stringify(redacted, null, 2));\n}\n","/**\n * `status` command — display Chrome connection status, token expiry, gateway state.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport { getChromeWebSocketUrl } from \"../infra/chrome-cdp.js\";\nimport { getTokenExpiry } from \"../core/auth-capture.js\";\nimport { isTokenExpired } from \"../core/client.js\";\n\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\nexport async function runStatus(): Promise<void> {\n const config = await loadConfig();\n\n // Chrome connection status\n let chromeStatus: string;\n try {\n await getChromeWebSocketUrl(config.cdpUrl, 3000);\n chromeStatus = `connected (${config.cdpUrl})`;\n } catch {\n chromeStatus = `disconnected (${config.cdpUrl})`;\n }\n\n // Token expiry\n let tokenStatus: string;\n if (!config.credentials) {\n tokenStatus = \"no credentials stored\";\n } else {\n const exp = getTokenExpiry(config.credentials.bearerToken);\n const expired = isTokenExpired(config.credentials.bearerToken);\n tokenStatus = expired\n ? `expired at ${formatExpiry(exp)}`\n : `valid until ${formatExpiry(exp)}`;\n }\n\n // Gateway running state — check if something is listening on the configured port\n let gatewayStatus: string;\n try {\n const res = await fetch(`http://127.0.0.1:${config.port}/health`, {\n signal: AbortSignal.timeout(1000),\n });\n if (res.ok) {\n gatewayStatus = `running on port ${config.port}`;\n } else {\n gatewayStatus = `port ${config.port} responded with ${res.status}`;\n }\n } catch {\n gatewayStatus = `not running (port ${config.port})`;\n }\n\n console.log(`Chrome: ${chromeStatus}`);\n console.log(`Token: ${tokenStatus}`);\n console.log(`Gateway: ${gatewayStatus}`);\n}\n","/**\n * `logs` command — query and display recent request logs.\n */\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { RequestLogEntry } from \"../infra/types.js\";\n\nconst LOG_DIR = join(homedir(), \".jh-gateway\", \"logs\");\n\nasync function readLogFile(filePath: string): Promise<RequestLogEntry[]> {\n const raw = await readFile(filePath, \"utf8\");\n const entries: RequestLogEntry[] = [];\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) {continue;}\n try {\n entries.push(JSON.parse(trimmed) as RequestLogEntry);\n } catch {\n // Skip malformed lines\n }\n }\n return entries;\n}\n\nexport async function runLogs(options: { limit?: number }): Promise<void> {\n const limit = options.limit ?? 50;\n\n let files: string[];\n try {\n const names = await readdir(LOG_DIR);\n // Sort descending (newest first) by filename (YYYY-MM-DD.jsonl)\n files = names\n .filter((n) => n.endsWith(\".jsonl\"))\n .toSorted()\n .toReversed()\n .map((n) => join(LOG_DIR, n));\n } catch {\n console.log(\"No log files found. Logs are written once the server handles requests.\");\n return;\n }\n\n const entries: RequestLogEntry[] = [];\n for (const file of files) {\n if (entries.length >= limit) {break;}\n try {\n const fileEntries = await readLogFile(file);\n entries.push(...fileEntries);\n } catch {\n // Skip unreadable files\n }\n }\n\n // Take the most recent `limit` entries (files are sorted newest-first,\n // but entries within a file are oldest-first — reverse within each file)\n const recent = entries.slice(-limit).toReversed();\n\n if (recent.length === 0) {\n console.log(\"No log entries found.\");\n return;\n }\n\n for (const entry of recent) {\n const tokens = `${entry.estimatedTokens.prompt}p/${entry.estimatedTokens.completion}c`;\n console.log(\n `${entry.timestamp} ${entry.method.padEnd(4)} ${entry.path.padEnd(30)} ` +\n `${String(entry.statusCode).padEnd(4)} ${String(entry.latencyMs).padStart(6)}ms ` +\n `${(entry.model ?? \"-\").padEnd(20)} tokens:${tokens}`\n );\n }\n}\n","/**\n * `start` command — unified single-command startup.\n * Orchestrates Chrome connection, authentication, server startup,\n * and background token refresh in one terminal.\n */\nimport * as p from \"@clack/prompts\";\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { ChromeManager } from \"../infra/chrome-manager.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport { findOrOpenJhPage, checkBrowserLoginState } from \"../infra/chrome-cdp.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\nimport { shouldRefresh, CredentialHolder, TokenRefresher } from \"../core/token-refresher.js\";\nimport { ReauthLock } from \"../core/reauth-lock.js\";\nimport { PagePool } from \"../core/page-pool.js\";\nimport { startServer } from \"../server.js\";\n\nexport interface StartOptions {\n port?: number;\n pages?: number;\n headless?: boolean;\n}\n\nexport async function runStart(options: StartOptions): Promise<void> {\n // ── Load config and apply overrides ─────────────────────────────────\n const config = await loadConfig();\n\n if (options.port !== undefined) {\n await updateConfig({ port: options.port });\n config.port = options.port;\n }\n\n const maxPages = options.pages ?? 3;\n const cdpPort = parseInt(new URL(config.cdpUrl).port, 10) || 9222;\n\n const chromeManager = new ChromeManager({\n cdpPort,\n headless: options.headless,\n });\n\n // ── Phase 1: Connect to Chrome ──────────────────────────────────────\n const s = p.spinner();\n s.start(\"Connecting to Chrome...\");\n\n let state: ChromeManagerState;\n try {\n state = await chromeManager.connect();\n s.stop(\n state.selfLaunched\n ? \"Chrome launched and connected.\"\n : \"Connected to existing Chrome instance.\",\n );\n await chromeManager.showWindow(state);\n } catch (err) {\n s.stop(\"Failed to connect to Chrome.\");\n throw err;\n }\n\n // ── Phase 2: Authenticate if needed ─────────────────────────────────\n const needsAuth =\n !config.credentials ||\n !config.credentials.expiresAt ||\n shouldRefresh(Date.now(), config.credentials.expiresAt, 0);\n\n if (needsAuth && options.headless) {\n await chromeManager.shutdown(state);\n throw new Error(\n \"Cannot authenticate in headless mode — no browser window to log in.\\n\" +\n \"Run `jh-gateway start` (without --headless) first to log in, then use --headless on subsequent runs.\",\n );\n }\n\n if (needsAuth) {\n s.start(\"Waiting for login (timeout: 300s)...\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n\n const expiryStr = creds.expiresAt\n ? new Date(creds.expiresAt * 1000).toLocaleString()\n : \"unknown\";\n s.stop(`Credentials captured. Token expires: ${expiryStr}`);\n } catch (err) {\n s.stop(\"Authentication failed.\");\n // Clean up Chrome if we launched it\n await chromeManager.shutdown(state);\n throw err;\n }\n } else {\n p.log.info(\"Valid credentials found. Verifying browser session…\");\n const browserLoggedIn = await checkBrowserLoginState(state.browser);\n if (!browserLoggedIn) {\n p.log.warn(\n \"Browser session has expired — please log in to chat.ai.jh.edu.\",\n );\n s.start(\"Waiting for login (timeout: 300s)…\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n const expiryStr = creds.expiresAt\n ? new Date(creds.expiresAt * 1000).toLocaleString()\n : \"unknown\";\n s.stop(`Credentials re-captured. Token expires: ${expiryStr}`);\n } catch (err) {\n s.stop(\"Re-authentication failed.\");\n await chromeManager.shutdown(state);\n throw err;\n }\n }\n }\n\n // ── Phase 3: Initialize PagePool, CredentialHolder, ReauthLock, Server\n s.start(`Starting server on port ${config.port}...`);\n\n // Find the seed page for the pool\n const seedPage = await findOrOpenJhPage(state.browser);\n\n const pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(state.browser, seedPage);\n\n const credentialHolder = new CredentialHolder();\n if (config.credentials) {\n credentialHolder.set(config.credentials);\n }\n\n const _reauthLock = new ReauthLock();\n\n const serverHandle = await startServer(config, {\n getPool: () => pool,\n getCredentials: () => credentialHolder.get(),\n browser: state.browser,\n });\n\n s.stop(`Server running on port ${config.port}.`);\n\n // ── Phase 4: Start TokenRefresher background loop ───────────────────\n const tokenRefresher = new TokenRefresher(credentialHolder, config.cdpUrl);\n tokenRefresher.start();\n\n // ── Display success info ────────────────────────────────────────────\n const baseUrl = `http://127.0.0.1:${config.port}`;\n const apiKey = config.auth.token ?? \"(no auth required)\";\n\n p.log.success(`Gateway ready!`);\n p.log.info(`Base URL: ${baseUrl}`);\n p.log.info(`API Key: ${apiKey}`);\n p.log.info(\"Press Ctrl+C to stop.\");\n\n // ── Shutdown handler ────────────────────────────────────────────────\n // disconnect (not shutdown): hides Chrome and detaches CDP, but keeps\n // the Chrome process alive so the next `start` reconnects instantly.\n const shutdown = async () => {\n tokenRefresher.stop();\n await serverHandle.close();\n await chromeManager.disconnect(state);\n };\n\n process.on(\"SIGINT\", async () => {\n await shutdown();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await shutdown();\n process.exit(0);\n });\n}\n","import type { GatewayCredentials } from \"../infra/types.js\";\n\n/**\n * Deduplication lock ensuring only one credential re-capture runs at a time.\n * Concurrent callers share the same in-flight promise; after it settles the\n * lock resets so the next `acquire()` starts a fresh re-capture.\n */\nexport class ReauthLock {\n private inflight: Promise<GatewayCredentials> | null = null;\n\n async acquire(\n recaptureFn: () => Promise<GatewayCredentials>,\n ): Promise<GatewayCredentials> {\n if (this.inflight) {\n return this.inflight;\n }\n\n this.inflight = recaptureFn().finally(() => {\n this.inflight = null;\n });\n\n return this.inflight;\n }\n}\n","#!/usr/bin/env node\n/**\n * jh-gateway CLI entry point.\n * Registered as the `jh-gateway` bin in package.json.\n */\nimport { runSetup } from \"./cli/setup.js\";\nimport { runServe } from \"./cli/serve.js\";\nimport { runAuth } from \"./cli/auth.js\";\nimport { runConfig } from \"./cli/config.js\";\nimport { runStatus } from \"./cli/status.js\";\nimport { runLogs } from \"./cli/logs.js\";\nimport { runStart } from \"./cli/start.js\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction parseFlags(argv: string[]): Record<string, string | boolean> {\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith(\"--\")) {\n const key = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n }\n return flags;\n}\n\nfunction printHelp(): void {\n console.log(`\njh-gateway — JH Web Gateway CLI\n\nUsage:\n jh-gateway <command> [options]\n\nCommands:\n start Launch Chrome, authenticate, and start the gateway server\n setup Interactive setup wizard (Chrome detection, auth, port)\n serve Start the HTTP server\n auth Re-capture JH credentials\n config Print current configuration (credentials redacted)\n status Show Chrome connection, token expiry, and gateway state\n logs Display recent request logs\n\nOptions:\n start --headless Launch Chrome in headless mode\n start --port <n> Override the configured port\n start --pages <n> Max concurrent browser pages (default: 3)\n serve --port <n> Override the configured port\n serve --pages <n> Max concurrent browser pages (default: 3)\n logs --limit <n> Number of log entries to show (default: 50)\n --help Show this help message\n`);\n}\n\nasync function main(): Promise<void> {\n if (command === \"--help\" || command === \"-h\") {\n printHelp();\n process.exit(0);\n }\n\n if (!command || command === \"tui\") {\n const { launchTui } = await import(\"./tui/index.js\");\n await launchTui();\n return;\n }\n\n const flags = parseFlags(args.slice(1));\n\n try {\n switch (command) {\n case \"start\": {\n const headless = flags[\"headless\"] === true;\n const startPortFlag = flags[\"port\"];\n const startPort =\n startPortFlag !== undefined && startPortFlag !== true\n ? Number(startPortFlag)\n : undefined;\n if (startPort !== undefined && (isNaN(startPort) || startPort < 1 || startPort > 65535)) {\n console.error(\"Error: --port must be a valid port number (1–65535)\");\n process.exit(1);\n }\n const startPagesFlag = flags[\"pages\"];\n const startPages =\n startPagesFlag !== undefined && startPagesFlag !== true\n ? Number(startPagesFlag)\n : undefined;\n if (startPages !== undefined && (isNaN(startPages) || startPages < 1 || startPages > 10)) {\n console.error(\"Error: --pages must be between 1 and 10\");\n process.exit(1);\n }\n await runStart({ headless, port: startPort, pages: startPages });\n break;\n }\n\n case \"setup\":\n await runSetup();\n break;\n\n case \"serve\": {\n const portFlag = flags[\"port\"];\n const port =\n portFlag !== undefined && portFlag !== true\n ? Number(portFlag)\n : undefined;\n if (port !== undefined && (isNaN(port) || port < 1 || port > 65535)) {\n console.error(\"Error: --port must be a valid port number (1–65535)\");\n process.exit(1);\n }\n const pagesFlag = flags[\"pages\"];\n const pages =\n pagesFlag !== undefined && pagesFlag !== true\n ? Number(pagesFlag)\n : undefined;\n if (pages !== undefined && (isNaN(pages) || pages < 1 || pages > 10)) {\n console.error(\"Error: --pages must be between 1 and 10\");\n process.exit(1);\n }\n await runServe({ port, pages });\n break;\n }\n\n case \"auth\":\n await runAuth();\n break;\n\n case \"config\":\n await runConfig();\n break;\n\n case \"status\":\n await runStatus();\n break;\n\n case \"logs\": {\n const limitFlag = flags[\"limit\"];\n const limit =\n limitFlag !== undefined && limitFlag !== true\n ? Number(limitFlag)\n : undefined;\n if (limit !== undefined && (isNaN(limit) || limit < 1)) {\n console.error(\"Error: --limit must be a positive number\");\n process.exit(1);\n }\n await runLogs({ limit });\n break;\n }\n\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["spinner","formatExpiry","formatExpiry","p"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/setup.ts","../src/cli/serve.ts","../src/cli/auth.ts","../src/cli/config.ts","../src/cli/status.ts","../src/cli/logs.ts","../src/cli/start.ts","../src/core/reauth-lock.ts","../src/cli.ts"],"sourcesContent":["/**\n * Interactive TUI setup wizard.\n * Steps: Chrome detection → JH auth → port selection → verify\n */\nimport * as p from \"@clack/prompts\";\nimport { getChromeWebSocketUrl } from \"../infra/chrome-cdp.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { generateApiKey } from \"../infra/gateway-auth.js\";\nimport { MODEL_ENDPOINT_MAP } from \"../infra/types.js\";\n\nconst COMMON_CDP_PORTS = [9222, 9223];\n\n/** Format a UNIX timestamp as a human-readable date string. */\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\n/** Step 1: Detect Chrome on common ports or prompt for custom URL. */\nasync function detectChrome(): Promise<string> {\n p.log.step(\"Step 1: Chrome detection\");\n\n for (const port of COMMON_CDP_PORTS) {\n const url = `http://127.0.0.1:${port}`;\n const spinner = p.spinner();\n spinner.start(`Checking ${url}…`);\n try {\n await getChromeWebSocketUrl(url, 2000);\n spinner.stop(`Chrome found at ${url}`);\n return url;\n } catch {\n spinner.stop(`Not found at ${url}`);\n }\n }\n\n // Prompt for custom CDP URL\n const custom = await p.text({\n message: \"Enter your Chrome CDP URL (e.g. http://127.0.0.1:9224):\",\n placeholder: \"http://127.0.0.1:9222\",\n validate(value) {\n if (!value.startsWith(\"http://\") && !value.startsWith(\"https://\")) {\n return \"Must be a valid URL starting with http:// or https://\";\n }\n },\n });\n\n if (p.isCancel(custom)) {\n p.cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n\n // Verify the custom URL\n const spinner = p.spinner();\n spinner.start(`Checking ${custom}…`);\n try {\n await getChromeWebSocketUrl(custom, 5000);\n spinner.stop(`Chrome found at ${custom}`);\n return custom;\n } catch (err) {\n spinner.stop(`Failed to connect to ${custom}`);\n throw err;\n }\n}\n\n/** Step 2: Capture JH credentials via CDP. */\nasync function captureAuth(cdpUrl: string): Promise<number> {\n p.log.step(\"Step 2: JH authentication\");\n p.log.info(\n \"Opening chat.ai.jh.edu in your browser. Send any message to trigger auth capture…\"\n );\n\n const spinner = p.spinner();\n spinner.start(\"Waiting for credentials (up to 120s)…\");\n\n const creds = await captureCredentials(cdpUrl, 120_000);\n const expiry = getTokenExpiry(creds.bearerToken);\n spinner.stop(`Credentials captured! Token expires: ${formatExpiry(expiry)}`);\n\n return expiry;\n}\n\n/** Step 3: Port selection. */\nasync function selectPort(): Promise<number> {\n p.log.step(\"Step 3: Port selection\");\n\n const input = await p.text({\n message: \"Gateway port:\",\n placeholder: \"8741\",\n defaultValue: \"8741\",\n validate(value) {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n return \"Must be a valid port number (1–65535)\";\n }\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel(\"Setup cancelled.\");\n process.exit(0);\n }\n\n return Number(input);\n}\n\n/** Step 4: Verify by listing available models. */\nasync function verifyConnection(): Promise<void> {\n p.log.step(\"Step 4: Verification\");\n const models = Object.keys(MODEL_ENDPOINT_MAP);\n p.log.success(`Available models: ${models.join(\", \")}`);\n}\n\n/** Run the full setup wizard. */\nexport async function runSetup(): Promise<void> {\n p.intro(\"JH Web Gateway — Setup Wizard\");\n\n let cdpUrl: string | undefined;\n let port = 8741;\n\n // Step 1: Chrome detection (with retry)\n while (true) {\n try {\n cdpUrl = await detectChrome();\n break;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Chrome detection failed: ${msg}`);\n const retry = await p.confirm({ message: \"Retry Chrome detection?\" });\n if (p.isCancel(retry) || !retry) {\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n }\n }\n\n // Save CDP URL\n await updateConfig({ cdpUrl });\n\n // Step 2: Auth capture (with retry)\n while (true) {\n try {\n await captureAuth(cdpUrl);\n break;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Auth capture failed: ${msg}`);\n const retry = await p.confirm({ message: \"Retry authentication?\" });\n if (p.isCancel(retry) || !retry) {\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n }\n }\n\n // Step 3: Port selection\n try {\n port = await selectPort();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n p.log.error(`Port selection failed: ${msg}`);\n p.cancel(\"Setup cancelled.\");\n process.exit(1);\n }\n\n // Generate API key and persist final config\n const apiKey = generateApiKey();\n await updateConfig({\n port,\n auth: { mode: \"bearer\", token: apiKey },\n });\n\n // Step 4: Verify\n await verifyConnection();\n\n // Load final config to confirm\n const config = await loadConfig();\n const baseUrl = `http://127.0.0.1:${config.port}`;\n\n p.outro(\"Setup complete!\");\n\n console.log(\"\\n Base URL: \" + baseUrl);\n console.log(\" API Key: \" + apiKey);\n console.log(\"\\n Test with curl:\");\n console.log(\n ` curl ${baseUrl}/v1/models -H \"Authorization: Bearer ${apiKey}\"\\n`\n );\n}\n","/**\n * `serve` command — start the HTTP server.\n */\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { connectToChrome, findOrOpenJhPage } from \"../infra/chrome-cdp.js\";\nimport { startServer } from \"../server.js\";\nimport { PagePool } from \"../core/page-pool.js\";\nimport type { Page } from \"playwright-core\";\n\nexport async function runServe(options: { port?: number; pages?: number }): Promise<void> {\n const config = await loadConfig();\n\n if (options.port !== undefined) {\n await updateConfig({ port: options.port });\n config.port = options.port;\n }\n\n const maxPages = options.pages ?? 3;\n\n let pool: PagePool | null = null;\n let browser: { close(): Promise<void> } | undefined;\n try {\n const conn = await connectToChrome(config.cdpUrl);\n const seedPage = await findOrOpenJhPage(conn.browser);\n browser = conn.browser;\n console.log(`Connected to Chrome at ${config.cdpUrl}`);\n\n const currentUrl = seedPage.url();\n if (!currentUrl.includes(\"chat.ai.jh.edu\")) {\n console.log(\"Navigating to chat.ai.jh.edu...\");\n await seedPage.goto(\"https://chat.ai.jh.edu\", { waitUntil: \"networkidle\" });\n }\n console.log(`Browser page: ${seedPage.url()}`);\n\n // Initialize the page pool\n pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(conn.browser, seedPage);\n console.log(`Page pool initialized (max ${maxPages} concurrent pages)`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`Warning: Could not connect to Chrome: ${msg}`);\n console.warn(\"Chat completions will fail until Chrome is available.\");\n }\n\n await startServer(config, {\n getPool: () => pool,\n getCredentials: () => config.credentials,\n browser,\n });\n}\n","/**\n * `auth` command — re-capture JH credentials without full setup wizard.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\n\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\nexport async function runAuth(): Promise<void> {\n const config = await loadConfig();\n\n console.log(`Connecting to Chrome at ${config.cdpUrl}…`);\n console.log(\n \"Opening chat.ai.jh.edu. Send any message to trigger auth capture (timeout: 120s)…\"\n );\n\n const creds = await captureCredentials(config.cdpUrl, 120_000);\n const expiry = getTokenExpiry(creds.bearerToken);\n\n console.log(\"Credentials captured successfully.\");\n console.log(`Token expires: ${formatExpiry(expiry)}`);\n}\n","/**\n * `config` command — print current configuration with credentials redacted.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport type { GatewayConfig } from \"../infra/types.js\";\n\nfunction redactConfig(config: GatewayConfig): Record<string, unknown> {\n return {\n cdpUrl: config.cdpUrl,\n port: config.port,\n defaultModel: config.defaultModel,\n defaultEndpoint: config.defaultEndpoint,\n credentials:\n config.credentials !== null\n ? {\n bearerToken: \"[REDACTED]\",\n cookie: \"[REDACTED]\",\n userAgent: config.credentials.userAgent,\n }\n : null,\n auth: {\n mode: config.auth.mode,\n token: config.auth.token !== null ? \"[REDACTED]\" : null,\n },\n maxQueueWaitMs: config.maxQueueWaitMs,\n };\n}\n\nexport async function runConfig(): Promise<void> {\n const config = await loadConfig();\n const redacted = redactConfig(config);\n console.log(JSON.stringify(redacted, null, 2));\n}\n","/**\n * `status` command — display Chrome connection status, token expiry, gateway state.\n */\nimport { loadConfig } from \"../infra/config.js\";\nimport { getChromeWebSocketUrl } from \"../infra/chrome-cdp.js\";\nimport { getTokenExpiry } from \"../core/auth-capture.js\";\nimport { isTokenExpired } from \"../core/client.js\";\n\nfunction formatExpiry(exp: number): string {\n if (exp === 0) {return \"unknown\";}\n return new Date(exp * 1000).toLocaleString();\n}\n\nexport async function runStatus(): Promise<void> {\n const config = await loadConfig();\n\n // Chrome connection status\n let chromeStatus: string;\n try {\n await getChromeWebSocketUrl(config.cdpUrl, 3000);\n chromeStatus = `connected (${config.cdpUrl})`;\n } catch {\n chromeStatus = `disconnected (${config.cdpUrl})`;\n }\n\n // Token expiry\n let tokenStatus: string;\n if (!config.credentials) {\n tokenStatus = \"no credentials stored\";\n } else {\n const exp = getTokenExpiry(config.credentials.bearerToken);\n const expired = isTokenExpired(config.credentials.bearerToken);\n tokenStatus = expired\n ? `expired at ${formatExpiry(exp)}`\n : `valid until ${formatExpiry(exp)}`;\n }\n\n // Gateway running state — check if something is listening on the configured port\n let gatewayStatus: string;\n try {\n const res = await fetch(`http://127.0.0.1:${config.port}/health`, {\n signal: AbortSignal.timeout(1000),\n });\n if (res.ok) {\n gatewayStatus = `running on port ${config.port}`;\n } else {\n gatewayStatus = `port ${config.port} responded with ${res.status}`;\n }\n } catch {\n gatewayStatus = `not running (port ${config.port})`;\n }\n\n console.log(`Chrome: ${chromeStatus}`);\n console.log(`Token: ${tokenStatus}`);\n console.log(`Gateway: ${gatewayStatus}`);\n}\n","/**\n * `logs` command — query and display recent request logs.\n */\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { RequestLogEntry } from \"../infra/types.js\";\n\nconst LOG_DIR = join(homedir(), \".jh-gateway\", \"logs\");\n\nasync function readLogFile(filePath: string): Promise<RequestLogEntry[]> {\n const raw = await readFile(filePath, \"utf8\");\n const entries: RequestLogEntry[] = [];\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) {continue;}\n try {\n entries.push(JSON.parse(trimmed) as RequestLogEntry);\n } catch {\n // Skip malformed lines\n }\n }\n return entries;\n}\n\nexport async function runLogs(options: { limit?: number }): Promise<void> {\n const limit = options.limit ?? 50;\n\n let files: string[];\n try {\n const names = await readdir(LOG_DIR);\n // Sort descending (newest first) by filename (YYYY-MM-DD.jsonl)\n files = names\n .filter((n) => n.endsWith(\".jsonl\"))\n .toSorted()\n .toReversed()\n .map((n) => join(LOG_DIR, n));\n } catch {\n console.log(\"No log files found. Logs are written once the server handles requests.\");\n return;\n }\n\n const entries: RequestLogEntry[] = [];\n for (const file of files) {\n if (entries.length >= limit) {break;}\n try {\n const fileEntries = await readLogFile(file);\n entries.push(...fileEntries);\n } catch {\n // Skip unreadable files\n }\n }\n\n // Take the most recent `limit` entries (files are sorted newest-first,\n // but entries within a file are oldest-first — reverse within each file)\n const recent = entries.slice(-limit).toReversed();\n\n if (recent.length === 0) {\n console.log(\"No log entries found.\");\n return;\n }\n\n for (const entry of recent) {\n const tokens = `${entry.estimatedTokens.prompt}p/${entry.estimatedTokens.completion}c`;\n console.log(\n `${entry.timestamp} ${entry.method.padEnd(4)} ${entry.path.padEnd(30)} ` +\n `${String(entry.statusCode).padEnd(4)} ${String(entry.latencyMs).padStart(6)}ms ` +\n `${(entry.model ?? \"-\").padEnd(20)} tokens:${tokens}`\n );\n }\n}\n","/**\n * `start` command — unified single-command startup.\n * Orchestrates Chrome connection, authentication, server startup,\n * and background token refresh in one terminal.\n */\nimport * as p from \"@clack/prompts\";\nimport { loadConfig, updateConfig } from \"../infra/config.js\";\nimport { ChromeManager } from \"../infra/chrome-manager.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport { findOrOpenJhPage, checkBrowserLoginState } from \"../infra/chrome-cdp.js\";\nimport { captureCredentials, getTokenExpiry } from \"../core/auth-capture.js\";\nimport { shouldRefresh, CredentialHolder, TokenRefresher } from \"../core/token-refresher.js\";\nimport { ReauthLock } from \"../core/reauth-lock.js\";\nimport { PagePool } from \"../core/page-pool.js\";\nimport { startServer } from \"../server.js\";\n\nexport interface StartOptions {\n port?: number;\n pages?: number;\n headless?: boolean;\n}\n\nexport async function runStart(options: StartOptions): Promise<void> {\n // ── Load config and apply overrides ─────────────────────────────────\n const config = await loadConfig();\n\n if (options.port !== undefined) {\n await updateConfig({ port: options.port });\n config.port = options.port;\n }\n\n const maxPages = options.pages ?? 3;\n const cdpPort = parseInt(new URL(config.cdpUrl).port, 10) || 9222;\n\n const chromeManager = new ChromeManager({\n cdpPort,\n headless: options.headless,\n });\n\n // ── Phase 1: Connect to Chrome ──────────────────────────────────────\n const s = p.spinner();\n s.start(\"Connecting to Chrome...\");\n\n let state: ChromeManagerState;\n try {\n state = await chromeManager.connect();\n s.stop(\n state.selfLaunched\n ? \"Chrome launched and connected.\"\n : \"Connected to existing Chrome instance.\",\n );\n await chromeManager.showWindow(state);\n } catch (err) {\n s.stop(\"Failed to connect to Chrome.\");\n throw err;\n }\n\n // ── Phase 2: Authenticate if needed ─────────────────────────────────\n const needsAuth =\n !config.credentials ||\n !config.credentials.expiresAt ||\n shouldRefresh(Date.now(), config.credentials.expiresAt, 0);\n\n if (needsAuth && options.headless) {\n await chromeManager.shutdown(state);\n throw new Error(\n \"Cannot authenticate in headless mode — no browser window to log in.\\n\" +\n \"Run `jh-gateway start` (without --headless) first to log in, then use --headless on subsequent runs.\",\n );\n }\n\n if (needsAuth) {\n s.start(\"Waiting for login (timeout: 300s)...\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n\n const expiryStr = creds.expiresAt\n ? new Date(creds.expiresAt * 1000).toLocaleString()\n : \"unknown\";\n s.stop(`Credentials captured. Token expires: ${expiryStr}`);\n } catch (err) {\n s.stop(\"Authentication failed.\");\n // Clean up Chrome if we launched it\n await chromeManager.shutdown(state);\n throw err;\n }\n } else {\n p.log.info(\"Valid credentials found. Verifying browser session…\");\n const browserLoggedIn = await checkBrowserLoginState(state.browser);\n if (!browserLoggedIn) {\n p.log.warn(\n \"Browser session has expired — please log in to chat.ai.jh.edu.\",\n );\n s.start(\"Waiting for login (timeout: 300s)…\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n const expiryStr = creds.expiresAt\n ? new Date(creds.expiresAt * 1000).toLocaleString()\n : \"unknown\";\n s.stop(`Credentials re-captured. Token expires: ${expiryStr}`);\n } catch (err) {\n s.stop(\"Re-authentication failed.\");\n await chromeManager.shutdown(state);\n throw err;\n }\n }\n }\n\n // ── Phase 3: Initialize PagePool, CredentialHolder, ReauthLock, Server\n s.start(`Starting server on port ${config.port}...`);\n\n // Find the seed page for the pool\n const seedPage = await findOrOpenJhPage(state.browser);\n\n const pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(state.browser, seedPage);\n\n const credentialHolder = new CredentialHolder();\n if (config.credentials) {\n credentialHolder.set(config.credentials);\n }\n\n const _reauthLock = new ReauthLock();\n\n const serverHandle = await startServer(config, {\n getPool: () => pool,\n getCredentials: () => credentialHolder.get(),\n browser: state.browser,\n });\n\n s.stop(`Server running on port ${config.port}.`);\n\n // ── Phase 4: Start TokenRefresher background loop ───────────────────\n const tokenRefresher = new TokenRefresher(credentialHolder, config.cdpUrl);\n tokenRefresher.start();\n\n // ── Display success info ────────────────────────────────────────────\n const baseUrl = `http://127.0.0.1:${config.port}`;\n const apiKey = config.auth.token ?? \"(no auth required)\";\n\n p.log.success(`Gateway ready!`);\n p.log.info(`Base URL: ${baseUrl}`);\n p.log.info(`API Key: ${apiKey}`);\n p.log.info(\"Press Ctrl+C to stop.\");\n\n // ── Shutdown handler ────────────────────────────────────────────────\n // disconnect (not shutdown): hides Chrome and detaches CDP, but keeps\n // the Chrome process alive so the next `start` reconnects instantly.\n const shutdown = async () => {\n tokenRefresher.stop();\n await serverHandle.close();\n await chromeManager.disconnect(state);\n };\n\n process.on(\"SIGINT\", async () => {\n await shutdown();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await shutdown();\n process.exit(0);\n });\n}\n","import type { GatewayCredentials } from \"../infra/types.js\";\n\n/**\n * Deduplication lock ensuring only one credential re-capture runs at a time.\n * Concurrent callers share the same in-flight promise; after it settles the\n * lock resets so the next `acquire()` starts a fresh re-capture.\n */\nexport class ReauthLock {\n private inflight: Promise<GatewayCredentials> | null = null;\n\n async acquire(\n recaptureFn: () => Promise<GatewayCredentials>,\n ): Promise<GatewayCredentials> {\n if (this.inflight) {\n return this.inflight;\n }\n\n this.inflight = recaptureFn().finally(() => {\n this.inflight = null;\n });\n\n return this.inflight;\n }\n}\n","#!/usr/bin/env node\n/**\n * jh-gateway CLI entry point.\n * Registered as the `jh-gateway` bin in package.json.\n */\nimport { runSetup } from \"./cli/setup.js\";\nimport { runServe } from \"./cli/serve.js\";\nimport { runAuth } from \"./cli/auth.js\";\nimport { runConfig } from \"./cli/config.js\";\nimport { runStatus } from \"./cli/status.js\";\nimport { runLogs } from \"./cli/logs.js\";\nimport { runStart } from \"./cli/start.js\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction parseFlags(argv: string[]): Record<string, string | boolean> {\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith(\"--\")) {\n const key = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n }\n return flags;\n}\n\nfunction printHelp(): void {\n console.log(`\njh-gateway — JH Web Gateway CLI\n\nUsage:\n jh-gateway <command> [options]\n\nCommands:\n start Launch Chrome, authenticate, and start the gateway server\n setup Interactive setup wizard (Chrome detection, auth, port)\n serve Start the HTTP server\n auth Re-capture JH credentials\n config Print current configuration (credentials redacted)\n status Show Chrome connection, token expiry, and gateway state\n logs Display recent request logs\n\nOptions:\n start --headless Launch Chrome in headless mode\n start --port <n> Override the configured port\n start --pages <n> Max concurrent browser pages (default: 3)\n serve --port <n> Override the configured port\n serve --pages <n> Max concurrent browser pages (default: 3)\n logs --limit <n> Number of log entries to show (default: 50)\n --version, -v Print version and exit\n --help Show this help message\n`);\n}\n\nasync function main(): Promise<void> {\n if (command === \"--version\" || command === \"-v\") {\n console.log(__APP_VERSION__);\n process.exit(0);\n }\n\n if (command === \"--help\" || command === \"-h\") {\n printHelp();\n process.exit(0);\n }\n\n if (!command || command === \"tui\") {\n const { launchTui } = await import(\"./tui/index.js\");\n await launchTui();\n return;\n }\n\n const flags = parseFlags(args.slice(1));\n\n try {\n switch (command) {\n case \"start\": {\n const headless = flags[\"headless\"] === true;\n const startPortFlag = flags[\"port\"];\n const startPort =\n startPortFlag !== undefined && startPortFlag !== true\n ? Number(startPortFlag)\n : undefined;\n if (startPort !== undefined && (isNaN(startPort) || startPort < 1 || startPort > 65535)) {\n console.error(\"Error: --port must be a valid port number (1–65535)\");\n process.exit(1);\n }\n const startPagesFlag = flags[\"pages\"];\n const startPages =\n startPagesFlag !== undefined && startPagesFlag !== true\n ? Number(startPagesFlag)\n : undefined;\n if (startPages !== undefined && (isNaN(startPages) || startPages < 1 || startPages > 10)) {\n console.error(\"Error: --pages must be between 1 and 10\");\n process.exit(1);\n }\n await runStart({ headless, port: startPort, pages: startPages });\n break;\n }\n\n case \"setup\":\n await runSetup();\n break;\n\n case \"serve\": {\n const portFlag = flags[\"port\"];\n const port =\n portFlag !== undefined && portFlag !== true\n ? Number(portFlag)\n : undefined;\n if (port !== undefined && (isNaN(port) || port < 1 || port > 65535)) {\n console.error(\"Error: --port must be a valid port number (1–65535)\");\n process.exit(1);\n }\n const pagesFlag = flags[\"pages\"];\n const pages =\n pagesFlag !== undefined && pagesFlag !== true\n ? Number(pagesFlag)\n : undefined;\n if (pages !== undefined && (isNaN(pages) || pages < 1 || pages > 10)) {\n console.error(\"Error: --pages must be between 1 and 10\");\n process.exit(1);\n }\n await runServe({ port, pages });\n break;\n }\n\n case \"auth\":\n await runAuth();\n break;\n\n case \"config\":\n await runConfig();\n break;\n\n case \"status\":\n await runStatus();\n break;\n\n case \"logs\": {\n const limitFlag = flags[\"limit\"];\n const limit =\n limitFlag !== undefined && limitFlag !== true\n ? Number(limitFlag)\n : undefined;\n if (limit !== undefined && (isNaN(limit) || limit < 1)) {\n console.error(\"Error: --limit must be a positive number\");\n process.exit(1);\n }\n await runLogs({ limit });\n break;\n }\n\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["spinner","formatExpiry","formatExpiry","p"]}
|
|
@@ -446,9 +446,13 @@ function SplashScreen({ onComplete }) {
|
|
|
446
446
|
const id = setInterval(() => setShowCursor((p) => !p), 530);
|
|
447
447
|
return () => clearInterval(id);
|
|
448
448
|
}, [phase]);
|
|
449
|
-
const logoStartRow = Math.max(0, Math.floor((termRows - JHU_LOGO_LINES.length - 6) / 2));
|
|
449
|
+
const logoStartRow = Math.max(0, Math.floor((termRows - JHU_LOGO_LINES.length - 6) / 2) - 1);
|
|
450
450
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
451
451
|
/* @__PURE__ */ jsx5(Starfield, { rows: termRows, cols: termCols, tick }),
|
|
452
|
+
/* @__PURE__ */ jsx5(Box4, { width: "100%", justifyContent: "flex-end", paddingRight: 2, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, color: "cyan", children: [
|
|
453
|
+
"v",
|
|
454
|
+
"2.0.1"
|
|
455
|
+
] }) }),
|
|
452
456
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: logoStartRow, alignItems: "flex-start", paddingLeft: 2, children: [
|
|
453
457
|
JHU_LOGO_LINES.map((line, i) => {
|
|
454
458
|
let displayLine;
|
|
@@ -1285,4 +1289,4 @@ async function launchTui() {
|
|
|
1285
1289
|
export {
|
|
1286
1290
|
launchTui
|
|
1287
1291
|
};
|
|
1288
|
-
//# sourceMappingURL=tui-
|
|
1292
|
+
//# sourceMappingURL=tui-2J5JN3FF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/index.ts","../src/tui/App.tsx","../src/tui/AppContext.tsx","../src/tui/components/HeaderBar.tsx","../src/tui/components/FooterBar.tsx","../src/tui/components/QuitDialog.tsx","../src/tui/services/gateway-lifecycle.ts","../src/tui/utils/shortcuts.ts","../src/tui/panels/SplashScreen.tsx","../src/tui/panels/MainMenu.tsx","../src/tui/types.ts","../src/tui/utils/navigation.ts","../src/tui/panels/GatewayPanel.tsx","../src/tui/panels/ModelSelector.tsx","../src/tui/panels/ChatPanel.tsx","../src/tui/panels/InfoPanel.tsx","../src/tui/utils/clipboard.ts","../src/tui/panels/SettingsPanel.tsx"],"sourcesContent":["import React from \"react\";\nimport { render } from \"ink\";\nimport { App } from \"./App.js\";\n\nexport async function launchTui(): Promise<void> {\n // ── Terminal size check ────────────────────────────────────────────────────\n const checkSize = (): boolean => {\n const cols = process.stdout.columns ?? 0;\n const rows = process.stdout.rows ?? 0;\n return cols >= 80 && rows >= 24;\n };\n\n if (!checkSize()) {\n process.stderr.write(\n `Terminal too small (${process.stdout.columns ?? 0}×${process.stdout.rows ?? 0}). ` +\n `Please resize to at least 80×24.\\n`,\n );\n await new Promise<void>((resolve) => {\n const onResize = () => {\n if (checkSize()) {\n process.stdout.removeListener(\"resize\", onResize);\n resolve();\n }\n };\n process.stdout.on(\"resize\", onResize);\n });\n }\n\n // ── Shutdown callback registered by AppContent ─────────────────────────────\n let shutdown: (() => Promise<void>) | null = null;\n\n const { waitUntilExit, unmount } = render(\n React.createElement(App, {\n onRegisterShutdown: (fn) => {\n shutdown = fn;\n },\n }),\n { exitOnCtrlC: false },\n );\n\n // ── Signal handlers ────────────────────────────────────────────────────────\n const handleSignal = (): void => {\n void (async () => {\n try {\n if (shutdown) await shutdown();\n } catch {\n // ignore errors during graceful shutdown\n }\n unmount();\n process.exit(0);\n })();\n };\n\n process.on(\"SIGINT\", handleSignal);\n process.on(\"SIGTERM\", handleSignal);\n\n // ── Unhandled error safety net ─────────────────────────────────────────────\n process.on(\"uncaughtException\", (err) => {\n unmount();\n process.stderr.write(`Unhandled error: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n\n try {\n await waitUntilExit();\n } finally {\n process.removeListener(\"SIGINT\", handleSignal);\n process.removeListener(\"SIGTERM\", handleSignal);\n }\n}\n","import React, { useState, useRef, useEffect } from \"react\";\nimport { Box, useInput } from \"ink\";\nimport { AppProvider, useAppContext } from \"./AppContext.js\";\nimport { HeaderBar } from \"./components/HeaderBar.js\";\nimport { FooterBar } from \"./components/FooterBar.js\";\nimport { QuitDialog } from \"./components/QuitDialog.js\";\nimport { PANEL_SHORTCUTS } from \"./utils/shortcuts.js\";\nimport { SplashScreen } from \"./panels/SplashScreen.js\";\nimport { MainMenu } from \"./panels/MainMenu.js\";\nimport { GatewayPanel } from \"./panels/GatewayPanel.js\";\nimport { ModelSelector } from \"./panels/ModelSelector.js\";\nimport { ChatPanel } from \"./panels/ChatPanel.js\";\nimport { InfoPanel } from \"./panels/InfoPanel.js\";\nimport { SettingsPanel } from \"./panels/SettingsPanel.js\";\nimport { stopGateway } from \"./services/gateway-lifecycle.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\ninterface AppProps {\n onRegisterShutdown?: (fn: () => Promise<void>) => void;\n}\n\n// ── Inner component (inside AppProvider) ──────────────────────────────────────\n\nfunction AppContent({ onRegisterShutdown }: AppProps): React.ReactElement {\n const { state, navigate, setActiveModel } = useAppContext();\n const { currentPanel, gatewayStatus, activeModel } = state;\n\n const [showQuit, setShowQuit] = useState(false);\n\n // Keep a ref to the latest state so the shutdown closure always sees fresh values\n const latestStateRef = useRef(state);\n useEffect(() => {\n latestStateRef.current = state;\n });\n\n // Register the shutdown function once on mount\n useEffect(() => {\n if (onRegisterShutdown) {\n onRegisterShutdown(async () => {\n const { gatewayStatus: status, serverHandle, chromeState, tokenRefresher } =\n latestStateRef.current;\n if (status === \"running\" && serverHandle && chromeState && tokenRefresher) {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n }\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Global q handler — active only when the quit dialog is not already showing\n // and not on the splash screen\n useInput(\n (input) => {\n if (input === \"q\" && currentPanel !== \"splash\") {\n setShowQuit(true);\n }\n },\n { isActive: !showQuit },\n );\n\n // ── Panel router ─────────────────────────────────────────────────────────────\n\n const renderPanel = (): React.ReactElement => {\n switch (currentPanel) {\n case \"splash\":\n return <SplashScreen onComplete={() => navigate(\"menu\")} />;\n case \"menu\":\n return <MainMenu onQuit={() => setShowQuit(true)} />;\n case \"gateway\":\n return <GatewayPanel />;\n case \"model\":\n return (\n <ModelSelector\n activeModel={activeModel}\n onSelect={(model) => setActiveModel(model)}\n onBack={() => navigate(\"menu\")}\n />\n );\n case \"chat\":\n return <ChatPanel />;\n case \"info\":\n return <InfoPanel />;\n case \"settings\":\n return <SettingsPanel />;\n }\n };\n\n // ── Layout ───────────────────────────────────────────────────────────────────\n\n return (\n <Box flexDirection=\"column\" height=\"100%\">\n <HeaderBar gatewayStatus={gatewayStatus} />\n <Box flexGrow={1} flexDirection=\"column\">\n {showQuit ? (\n <QuitDialog onCancel={() => setShowQuit(false)} />\n ) : (\n renderPanel()\n )}\n </Box>\n <FooterBar shortcuts={PANEL_SHORTCUTS[currentPanel]} />\n </Box>\n );\n}\n\n// ── Root component ─────────────────────────────────────────────────────────────\n\nexport function App({ onRegisterShutdown }: AppProps): React.ReactElement {\n return (\n <AppProvider>\n <AppContent onRegisterShutdown={onRegisterShutdown} />\n </AppProvider>\n );\n}\n","import React, { createContext, useContext, useState, useEffect } from \"react\";\nimport { loadConfig } from \"../infra/config.js\";\nimport type { GatewayConfig } from \"../infra/types.js\";\nimport type { TuiAppState, PanelId } from \"./types.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport type { TokenRefresher } from \"../core/token-refresher.js\";\nimport type { ServerHandle } from \"../server.js\";\n\n// ── Context value type ─────────────────────────────────────────────────────────\n\ninterface AppContextValue {\n state: TuiAppState;\n navigate: (panelId: PanelId) => void;\n setGatewayStatus: (status: TuiAppState[\"gatewayStatus\"]) => void;\n setGatewayError: (error: string | null) => void;\n setActiveModel: (model: string) => void;\n setServerHandle: (handle: ServerHandle | null) => void;\n setChromeState: (state: ChromeManagerState | null) => void;\n setTokenRefresher: (refresher: TokenRefresher | null) => void;\n setConfig: (config: GatewayConfig) => void;\n}\n\n// ── Default/initial state ──────────────────────────────────────────────────────\n\nconst defaultConfig: GatewayConfig = {\n cdpUrl: \"http://127.0.0.1:9222\",\n port: 8741,\n defaultModel: \"claude-opus-4.5\",\n defaultEndpoint: \"AnthropicClaude\",\n credentials: null,\n auth: { mode: \"none\", token: null },\n maxQueueWaitMs: 120000,\n};\n\nconst initialState: TuiAppState = {\n currentPanel: \"splash\",\n gatewayStatus: \"stopped\",\n gatewayError: null,\n activeModel: defaultConfig.defaultModel,\n config: defaultConfig,\n serverHandle: null,\n chromeState: null,\n tokenRefresher: null,\n};\n\n// ── Context ────────────────────────────────────────────────────────────────────\n\nconst AppContext = createContext<AppContextValue | null>(null);\n\n// ── Provider ──────────────────────────────────────────────────────────────────\n\ninterface AppProviderProps {\n children: React.ReactNode;\n}\n\nexport function AppProvider({ children }: AppProviderProps): React.ReactElement {\n const [state, setState] = useState<TuiAppState>(initialState);\n\n useEffect(() => {\n loadConfig()\n .then((config) => {\n setState((prev) => ({\n ...prev,\n config,\n activeModel: config.defaultModel,\n }));\n })\n .catch(() => {\n // Keep defaults on error\n });\n }, []);\n\n const navigate = (panelId: PanelId): void => {\n setState((prev) => ({ ...prev, currentPanel: panelId }));\n };\n\n const setGatewayStatus = (status: TuiAppState[\"gatewayStatus\"]): void => {\n setState((prev) => ({ ...prev, gatewayStatus: status }));\n };\n\n const setGatewayError = (error: string | null): void => {\n setState((prev) => ({ ...prev, gatewayError: error }));\n };\n\n const setActiveModel = (model: string): void => {\n setState((prev) => ({ ...prev, activeModel: model }));\n };\n\n const setServerHandle = (handle: ServerHandle | null): void => {\n setState((prev) => ({ ...prev, serverHandle: handle }));\n };\n\n const setChromeState = (chromeState: ChromeManagerState | null): void => {\n setState((prev) => ({ ...prev, chromeState }));\n };\n\n const setTokenRefresher = (refresher: TokenRefresher | null): void => {\n setState((prev) => ({ ...prev, tokenRefresher: refresher }));\n };\n\n const setConfig = (config: GatewayConfig): void => {\n setState((prev) => ({ ...prev, config }));\n };\n\n const value: AppContextValue = {\n state,\n navigate,\n setGatewayStatus,\n setGatewayError,\n setActiveModel,\n setServerHandle,\n setChromeState,\n setTokenRefresher,\n setConfig,\n };\n\n return <AppContext.Provider value={value}>{children}</AppContext.Provider>;\n}\n\n// ── Hook ──────────────────────────────────────────────────────────────────────\n\nexport function useAppContext(): AppContextValue {\n const ctx = useContext(AppContext);\n if (!ctx) {\n throw new Error(\"useAppContext must be used within AppProvider\");\n }\n return ctx;\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\n\ninterface HeaderBarProps {\n gatewayStatus: \"stopped\" | \"starting\" | \"running\" | \"error\";\n}\n\nexport function HeaderBar({ gatewayStatus }: HeaderBarProps): React.ReactElement {\n const dotColor =\n gatewayStatus === \"running\"\n ? \"green\"\n : gatewayStatus === \"starting\"\n ? \"yellow\"\n : \"red\";\n\n const statusLabel =\n gatewayStatus === \"running\"\n ? \"running\"\n : gatewayStatus === \"starting\"\n ? \"starting\"\n : gatewayStatus === \"error\"\n ? \"error\"\n : \"stopped\";\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Text bold>jh-gateway</Text>\n <Text>\n <Text color={dotColor}>●</Text>\n {\" \"}\n <Text>Gateway: {statusLabel}</Text>\n </Text>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { FooterShortcut } from \"../types.js\";\n\ninterface FooterBarProps {\n shortcuts: FooterShortcut[];\n}\n\nexport function FooterBar({ shortcuts }: FooterBarProps): React.ReactElement {\n return (\n <Box width=\"100%\" gap={2}>\n {shortcuts.map((s) => (\n <Text key={s.key}>\n <Text dimColor>{\"[\"}</Text>\n <Text bold>{s.key}</Text>\n <Text dimColor>{\"]\"}</Text>\n {\" \"}\n <Text>{s.label}</Text>\n </Text>\n ))}\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { stopGateway } from \"../services/gateway-lifecycle.js\";\n\ninterface QuitDialogProps {\n onCancel: () => void;\n}\n\nexport function QuitDialog({ onCancel }: QuitDialogProps): React.ReactElement {\n const { state } = useAppContext();\n const { gatewayStatus, serverHandle, chromeState, tokenRefresher } = state;\n\n useInput((input, key) => {\n if (input === \"y\" || input === \"Y\") {\n void (async () => {\n if (\n gatewayStatus === \"running\" &&\n serverHandle &&\n chromeState &&\n tokenRefresher\n ) {\n try {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n } catch {\n // proceed with exit even if stop fails\n }\n }\n process.exit(0);\n })();\n } else if (input === \"n\" || input === \"N\" || key.escape) {\n onCancel();\n }\n });\n\n return (\n <Box borderStyle=\"round\" borderColor=\"yellow\" padding={1} flexDirection=\"column\">\n <Text bold color=\"yellow\">\n Quit jh-gateway?\n </Text>\n <Box marginTop={1}>\n <Text>Running gateway will be stopped.</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>[y] Yes [N/Esc] Cancel</Text>\n </Box>\n </Box>\n );\n}\n","import { ChromeManager } from \"../../infra/chrome-manager.js\";\nimport type { ChromeManagerState } from \"../../infra/chrome-manager.js\";\nimport { findOrOpenJhPage, checkBrowserLoginState } from \"../../infra/chrome-cdp.js\";\nimport { captureCredentials } from \"../../core/auth-capture.js\";\nimport { shouldRefresh, CredentialHolder, TokenRefresher } from \"../../core/token-refresher.js\";\nimport { PagePool } from \"../../core/page-pool.js\";\nimport { startServer } from \"../../server.js\";\nimport type { ServerHandle } from \"../../server.js\";\nimport type { GatewayConfig } from \"../../infra/types.js\";\n\nexport interface GatewayLifecycleCallbacks {\n onPhase: (phase: string) => void;\n onSuccess: (info: { baseUrl: string; apiKey: string | null }) => void;\n onError: (error: Error) => void;\n}\n\nexport interface StartGatewayResult {\n serverHandle: ServerHandle;\n chromeState: ChromeManagerState;\n tokenRefresher: TokenRefresher;\n}\n\nexport async function startGatewayForTui(\n config: GatewayConfig,\n options: { headless?: boolean; pages?: number },\n callbacks: GatewayLifecycleCallbacks,\n): Promise<StartGatewayResult> {\n const maxPages = options.pages ?? 3;\n const cdpPort = parseInt(new URL(config.cdpUrl).port, 10) || 9222;\n\n const chromeManager = new ChromeManager({\n cdpPort,\n headless: options.headless,\n });\n\n // ── Phase 1: Connect to Chrome ────────────────────────────────────────────\n callbacks.onPhase(\"Connecting to Chrome\");\n let state: ChromeManagerState;\n try {\n state = await chromeManager.connect();\n await chromeManager.showWindow(state);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n throw error;\n }\n\n // ── Phase 2: Authenticate if needed ──────────────────────────────────────\n const needsAuth =\n !config.credentials ||\n !config.credentials.expiresAt ||\n shouldRefresh(Date.now(), config.credentials.expiresAt, 0);\n\n if (needsAuth) {\n callbacks.onPhase(\"Waiting for login\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n } else {\n callbacks.onPhase(\"Verifying browser session\");\n const browserLoggedIn = await checkBrowserLoginState(state.browser);\n if (!browserLoggedIn) {\n callbacks.onPhase(\"Browser session expired — please log in\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n }\n }\n\n // ── Phase 3: PagePool, CredentialHolder, Server ───────────────────────────\n callbacks.onPhase(\"Starting server\");\n try {\n const seedPage = await findOrOpenJhPage(state.browser);\n\n const pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(state.browser, seedPage);\n\n const credentialHolder = new CredentialHolder();\n if (config.credentials) {\n credentialHolder.set(config.credentials);\n }\n\n const serverHandle = await startServer(config, {\n getPool: () => pool,\n getCredentials: () => credentialHolder.get(),\n });\n\n const tokenRefresher = new TokenRefresher(credentialHolder, config.cdpUrl);\n tokenRefresher.start();\n\n const baseUrl = `http://127.0.0.1:${config.port}`;\n const apiKey = config.auth.token ?? null;\n callbacks.onSuccess({ baseUrl, apiKey });\n\n return { serverHandle, chromeState: state, tokenRefresher };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n}\n\nexport async function stopGateway(\n serverHandle: ServerHandle,\n chromeState: ChromeManagerState,\n tokenRefresher: TokenRefresher,\n): Promise<void> {\n tokenRefresher.stop();\n await serverHandle.close();\n\n // disconnect (not shutdown): hides Chrome and detaches CDP, but keeps\n // the Chrome process alive so the next start reconnects instantly.\n const chromeManager = new ChromeManager();\n await chromeManager.disconnect(chromeState);\n}\n","import type { PanelId, FooterShortcut } from \"../types.js\";\n\nexport const PANEL_SHORTCUTS: Record<PanelId, FooterShortcut[]> = {\n splash: [\n { key: \"any\", label: \"Continue\" },\n ],\n menu: [\n { key: \"↑↓\", label: \"Navigate\" },\n { key: \"Enter\", label: \"Select\" },\n { key: \"q\", label: \"Quit\" },\n ],\n gateway: [\n { key: \"Enter\", label: \"Start/Stop\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n model: [\n { key: \"↑↓\", label: \"Navigate\" },\n { key: \"Enter\", label: \"Select\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n chat: [\n { key: \"Enter\", label: \"Send\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n info: [\n { key: \"c\", label: \"Copy URL\" },\n { key: \"k\", label: \"Copy Key\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n settings: [\n { key: \"Enter\", label: \"Edit\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n};\n","import React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport { Box, Text, useInput, useStdout, useAnimation } from \"ink\";\n\nconst JHU_LOGO_LINES = [\n \" ██╗██╗ ██╗ ██╗ ██╗███████╗██████╗ \",\n \" ██║██║ ██║ ██║ ██║██╔════╝██╔══██╗\",\n \" ██║███████║ ██║ █╗ ██║█████╗ ██████╔╝\",\n \"██ ██║██╔══██║ ██║███╗██║██╔══╝ ██╔══██╗\",\n \"╚█████╔╝██║ ██║ ╚███╔███╔╝███████╗██████╔╝\",\n \" ╚════╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═════╝ \",\n \"\",\n \" ██████╗ █████╗ ████████╗███████╗██╗ ██╗ █████╗ ██╗ ██╗\",\n \" ██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔══██╗╚██╗ ██╔╝\",\n \" ██║ ███╗███████║ ██║ █████╗ ██║ █╗ ██║███████║ ╚████╔╝ \",\n \" ██║ ██║██╔══██║ ██║ ██╔══╝ ██║███╗██║██╔══██║ ╚██╔╝ \",\n \" ╚██████╔╝██║ ██║ ██║ ███████╗╚███╔███╔╝██║ ██║ ██║ \",\n \" ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ \",\n];\n\nconst SUBTITLES = [\n \"// Infinite tokens, for school work of course :)))\",\n \"// powered by caffeine, desperation, and vibes\",\n \"// your professor will never know™\",\n \"// if it works, don't ask why\",\n \"// technically not cheating if the AI is also confused\",\n \"// [ERROR 418: I'm a teapot, but make it AI]\",\n \"// running on 3 hours of sleep and pure audacity\",\n \"// the tokens are infinite but my GPA is not\",\n \"// SYSTEM OVERRIDE: homework.exe has been defeated\",\n \"// now with 40% more hallucinations!\",\n \"// null pointer? more like null problems 😈\",\n \"// WARNING: may cause uncontrollable productivity\",\n \"// all citations are made up but they sound real\",\n \"// NOT affiliated with Skynet (probably)\",\n \"// lorem ipsum but it writes your thesis\",\n \"// sudo make my-homework --grade=A\",\n \"// ship it. always ship it.\",\n \"// this is fine 🔥 everything is fine 🔥\",\n \"// bridging the gap between laziness and genius\",\n \"// if AI wrote this disclaimer, does it count?\",\n];\n\nconst TAGLINE = SUBTITLES[Math.floor(Math.random() * SUBTITLES.length)]!;\nconst GLITCH_CHARS = \"█▓▒░╬╪╫▲▼◆◇○●□■#@$%&?!~^*\";\nconst STAR_CHARS = [\"·\", \".\", \"*\", \"✦\", \"✧\", \"⋆\", \"+\", \"×\"];\nconst LOGO_COLORS = [\"cyan\", \"cyan\", \"blueBright\", \"blueBright\", \"blue\", \"blue\", \"white\", \"greenBright\", \"green\", \"greenBright\", \"green\", \"greenBright\", \"green\"] as const;\n\nconst GLITCH_PHASE_MS = 600;\nconst REVEAL_PHASE_MS = 900;\nconst TYPEWRITER_INTERVAL_MS = 28;\n\nfunction seededRand(seed: number): () => number {\n let s = seed;\n return () => {\n s = (s * 1664525 + 1013904223) & 0xffffffff;\n return (s >>> 0) / 0xffffffff;\n };\n}\n\nfunction glitchLine(line: string, progress: number, rand: () => number): string {\n return line\n .split(\"\")\n .map((ch) => {\n if (ch === \" \" || ch === \"\\n\") return ch;\n if (rand() > progress) {\n return GLITCH_CHARS[Math.floor(rand() * GLITCH_CHARS.length)];\n }\n return ch;\n })\n .join(\"\");\n}\n\ninterface StarfieldProps {\n rows: number;\n cols: number;\n tick: number;\n}\n\nfunction Starfield({ rows, cols, tick }: StarfieldProps): React.ReactElement {\n const rand = seededRand(42);\n const lines: string[] = [];\n for (let r = 0; r < rows; r++) {\n let row = \"\";\n for (let c = 0; c < cols; c++) {\n const base = rand();\n const twinkleOffset = Math.sin((tick * 0.3 + r * 7.3 + c * 3.7)) * 0.5 + 0.5;\n row += base < 0.025 * twinkleOffset ? STAR_CHARS[Math.floor(rand() * STAR_CHARS.length)] : \" \";\n }\n lines.push(row);\n }\n return (\n <Box flexDirection=\"column\" position=\"absolute\" marginTop={0}>\n {lines.map((l, i) => (\n <Text key={i} dimColor color=\"cyan\">{l}</Text>\n ))}\n </Box>\n );\n}\n\ninterface SplashScreenProps {\n onComplete: () => void;\n}\n\nexport function SplashScreen({ onComplete }: SplashScreenProps): React.ReactElement {\n const { stdout } = useStdout();\n const termCols = stdout?.columns ?? 120;\n const termRows = stdout?.rows ?? 30;\n\n const [phase, setPhase] = useState<\"glitch\" | \"reveal\" | \"done\">(\"glitch\");\n const [glitchProgress, setGlitchProgress] = useState(0);\n const [revealedLines, setRevealedLines] = useState(0);\n const [typewriterCount, setTypewriterCount] = useState(0);\n const [showCursor, setShowCursor] = useState(true);\n const skippedRef = useRef(false);\n\n const { frame: tick } = useAnimation({ interval: 80 });\n\n const skip = useCallback(() => {\n if (skippedRef.current) return;\n skippedRef.current = true;\n setPhase(\"done\");\n setRevealedLines(JHU_LOGO_LINES.length);\n setTypewriterCount(TAGLINE.length);\n setShowCursor(false);\n }, []);\n\n useInput((_, key) => {\n if (phase !== \"done\") {\n skip();\n } else {\n onComplete();\n }\n });\n\n useEffect(() => {\n if (skippedRef.current) return;\n const start = Date.now();\n const id = setInterval(() => {\n const elapsed = Date.now() - start;\n const p = Math.min(elapsed / GLITCH_PHASE_MS, 1);\n setGlitchProgress(p);\n if (p >= 1) {\n clearInterval(id);\n setPhase(\"reveal\");\n }\n }, 40);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n if (phase !== \"reveal\" || skippedRef.current) return;\n const total = JHU_LOGO_LINES.length;\n let count = 0;\n const id = setInterval(() => {\n count++;\n setRevealedLines(count);\n if (count >= total) {\n clearInterval(id);\n setPhase(\"done\");\n }\n }, Math.round(REVEAL_PHASE_MS / total));\n return () => clearInterval(id);\n }, [phase]);\n\n useEffect(() => {\n if (phase !== \"done\") return;\n if (typewriterCount >= TAGLINE.length) return;\n const id = setInterval(() => {\n setTypewriterCount((p) => {\n if (p >= TAGLINE.length) {\n clearInterval(id);\n return p;\n }\n return p + 1;\n });\n }, TYPEWRITER_INTERVAL_MS);\n return () => clearInterval(id);\n }, [phase, typewriterCount]);\n\n useEffect(() => {\n if (phase !== \"done\") return;\n const id = setInterval(() => setShowCursor((p) => !p), 530);\n return () => clearInterval(id);\n }, [phase]);\n\n const logoStartRow = Math.max(0, Math.floor((termRows - JHU_LOGO_LINES.length - 6) / 2) - 1);\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" height=\"100%\">\n <Starfield rows={termRows} cols={termCols} tick={tick} />\n\n <Box width=\"100%\" justifyContent=\"flex-end\" paddingRight={2}>\n <Text dimColor color=\"cyan\">v{__APP_VERSION__}</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={logoStartRow} alignItems=\"flex-start\" paddingLeft={2}>\n {JHU_LOGO_LINES.map((line, i) => {\n let displayLine: string;\n const color = LOGO_COLORS[i] ?? \"cyan\";\n\n if (phase === \"glitch\") {\n const lineRand = seededRand(i * 137 + 11);\n displayLine = glitchLine(line, glitchProgress, lineRand);\n } else if (phase === \"reveal\") {\n if (i < revealedLines) {\n displayLine = line;\n } else {\n const lineRand = seededRand(i * 137 + 11);\n displayLine = glitchLine(line, 0.0, lineRand);\n }\n } else {\n displayLine = line;\n }\n\n return (\n <Text key={i} color={color} bold={i < 6}>\n {displayLine}\n </Text>\n );\n })}\n\n {phase === \"done\" && (\n <>\n <Box marginTop={1}>\n <Text color=\"greenBright\">\n {\" \"}\n {TAGLINE.slice(0, typewriterCount)}\n {showCursor ? \"█\" : \" \"}\n </Text>\n </Box>\n {typewriterCount >= TAGLINE.length && (\n <Box marginTop={1}>\n <Text dimColor> Press any key to continue_</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n </Box>\n );\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { MENU_ITEMS } from \"../types.js\";\nimport { wrapIndex } from \"../utils/navigation.js\";\nimport { useAppContext } from \"../AppContext.js\";\nimport type { PanelId } from \"../types.js\";\n\ninterface MainMenuProps {\n onQuit: () => void;\n}\n\nexport function MainMenu({ onQuit }: MainMenuProps): React.ReactElement {\n const { navigate } = useAppContext();\n const [focusedIndex, setFocusedIndex] = useState(0);\n\n useInput((input, key) => {\n if (key.downArrow) {\n setFocusedIndex((i) => wrapIndex(i, 1, MENU_ITEMS.length));\n } else if (key.upArrow) {\n setFocusedIndex((i) => wrapIndex(i, -1, MENU_ITEMS.length));\n } else if (key.return) {\n const item = MENU_ITEMS[focusedIndex];\n if (item.id === \"quit\") {\n onQuit();\n } else {\n navigate(item.id as PanelId);\n }\n } else if (input === \"q\" || key.escape) {\n onQuit();\n }\n });\n\n const focusedItem = MENU_ITEMS[focusedIndex];\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Main Menu</Text>\n </Box>\n <Box flexDirection=\"column\">\n {MENU_ITEMS.map((item, index) => {\n const isFocused = index === focusedIndex;\n return (\n <Box key={item.id}>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n {item.label}\n </Text>\n </Box>\n );\n })}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>{focusedItem.description}</Text>\n </Box>\n </Box>\n );\n}\n","import type { GatewayConfig } from \"../infra/types.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport type { TokenRefresher } from \"../core/token-refresher.js\";\nimport type { ServerHandle } from \"../server.js\";\n\n// ── Panel IDs ─────────────────────────────────────────────────────────────────\n\nexport type PanelId = \"splash\" | \"menu\" | \"gateway\" | \"model\" | \"chat\" | \"info\" | \"settings\";\n\n// ── App State ─────────────────────────────────────────────────────────────────\n\nexport interface TuiAppState {\n currentPanel: PanelId;\n gatewayStatus: \"stopped\" | \"starting\" | \"running\" | \"error\";\n gatewayError: string | null;\n activeModel: string;\n config: GatewayConfig;\n serverHandle: ServerHandle | null;\n chromeState: ChromeManagerState | null;\n tokenRefresher: TokenRefresher | null;\n}\n\n// ── Menu ──────────────────────────────────────────────────────────────────────\n\nexport interface MenuItem {\n id: PanelId | \"quit\";\n label: string;\n description: string;\n}\n\nexport const MENU_ITEMS: MenuItem[] = [\n { id: \"gateway\", label: \"Start Gateway\", description: \"Launch Chrome, authenticate, and start the HTTP server\" },\n { id: \"model\", label: \"Model\", description: \"Select the active AI model\" },\n { id: \"chat\", label: \"Chat\", description: \"Send a test message to the running gateway\" },\n { id: \"info\", label: \"Server Info\", description: \"View and copy the server URL and API key\" },\n { id: \"settings\",label: \"Settings\", description: \"View and edit gateway configuration\" },\n { id: \"quit\", label: \"Quit\", description: \"Exit jh-gateway\" },\n];\n\n// ── Gateway Phase ─────────────────────────────────────────────────────────────\n\nexport interface GatewayPhase {\n label: string;\n status: \"pending\" | \"active\" | \"done\" | \"error\";\n}\n\n// ── Footer Shortcut ───────────────────────────────────────────────────────────\n\nexport type FooterShortcut = { key: string; label: string };\n","/**\n * Computes a wrapped index after moving `delta` steps through a list of `listSize` items.\n * Handles both positive (down) and negative (up) deltas, wrapping around boundaries.\n */\nexport function wrapIndex(current: number, delta: number, listSize: number): number {\n return ((current + delta) % listSize + listSize) % listSize;\n}\n","import React, { useState, useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { startGatewayForTui, stopGateway } from \"../services/gateway-lifecycle.js\";\nimport type { GatewayPhase } from \"../types.js\";\n\nconst PHASE_LABELS = [\"Connecting to Chrome\", \"Waiting for login\", \"Starting server\"];\n\nfunction phaseIcon(status: GatewayPhase[\"status\"]): string {\n switch (status) {\n case \"pending\": return \"○\";\n case \"active\": return \"◌\";\n case \"done\": return \"●\";\n case \"error\": return \"✗\";\n }\n}\n\nfunction phaseColor(status: GatewayPhase[\"status\"]): string | undefined {\n switch (status) {\n case \"done\": return \"green\";\n case \"active\": return \"cyan\";\n case \"error\": return \"red\";\n default: return \"gray\";\n }\n}\n\nexport function GatewayPanel(): React.ReactElement {\n const { state, navigate, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher } = useAppContext();\n const { gatewayStatus, gatewayError, config, serverHandle, chromeState, tokenRefresher } = state;\n\n const [phases, setPhases] = useState<GatewayPhase[]>([]);\n const [starting, setStarting] = useState(false);\n const [stopping, setStopping] = useState(false);\n const [authPrompt, setAuthPrompt] = useState(false);\n\n const handleStart = useCallback(async () => {\n if (starting || stopping) return;\n setStarting(true);\n setGatewayError(null);\n setGatewayStatus(\"starting\");\n setAuthPrompt(false);\n\n const initial: GatewayPhase[] = PHASE_LABELS.map((label) => ({ label, status: \"pending\" }));\n setPhases(initial);\n\n let phaseIndex = 0;\n\n try {\n const result = await startGatewayForTui(\n config,\n { headless: false },\n {\n onPhase: (phase) => {\n const idx = PHASE_LABELS.indexOf(phase);\n if (idx >= 0) {\n if (phase === \"Waiting for login\") setAuthPrompt(true);\n else setAuthPrompt(false);\n phaseIndex = idx;\n setPhases((prev) =>\n prev.map((p, i) => {\n if (i < idx) return { ...p, status: \"done\" };\n if (i === idx) return { ...p, status: \"active\" };\n return p;\n }),\n );\n }\n },\n onSuccess: ({ baseUrl: _baseUrl, apiKey: _apiKey }) => {\n setAuthPrompt(false);\n setPhases((prev) => prev.map((p) => ({ ...p, status: \"done\" })));\n },\n onError: (_err) => {\n setPhases((prev) =>\n prev.map((p, i) => {\n if (i === phaseIndex) return { ...p, status: \"error\" };\n if (i < phaseIndex) return { ...p, status: \"done\" };\n return p;\n }),\n );\n },\n },\n );\n setServerHandle(result.serverHandle);\n setChromeState(result.chromeState);\n setTokenRefresher(result.tokenRefresher);\n setGatewayStatus(\"running\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setGatewayError(message);\n setGatewayStatus(\"error\");\n } finally {\n setStarting(false);\n }\n }, [starting, stopping, config, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher]);\n\n const handleStop = useCallback(async () => {\n if (stopping || starting || !serverHandle || !chromeState || !tokenRefresher) return;\n setStopping(true);\n try {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n setServerHandle(null);\n setChromeState(null);\n setTokenRefresher(null);\n setGatewayStatus(\"stopped\");\n setGatewayError(null);\n setPhases([]);\n } finally {\n setStopping(false);\n }\n }, [stopping, starting, serverHandle, chromeState, tokenRefresher, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher]);\n\n useInput((_input, key) => {\n if (key.return) {\n if (gatewayStatus === \"running\") {\n void handleStop();\n } else if (gatewayStatus !== \"starting\") {\n void handleStart();\n }\n } else if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n const isRunning = gatewayStatus === \"running\";\n const isStopped = gatewayStatus === \"stopped\" || gatewayStatus === \"error\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Gateway Control</Text>\n </Box>\n\n {/* Action button */}\n <Box marginBottom={1}>\n {starting ? (\n <Text color=\"cyan\">Starting gateway…</Text>\n ) : stopping ? (\n <Text color=\"yellow\">Stopping gateway…</Text>\n ) : isRunning ? (\n <Text>\n <Text color=\"green\">● Running</Text>\n {\" \"}\n <Text dimColor>[Enter] Stop</Text>\n </Text>\n ) : (\n <Text>\n <Text color={gatewayStatus === \"error\" ? \"red\" : \"gray\"}>\n ● {gatewayStatus === \"error\" ? \"Error\" : \"Stopped\"}\n </Text>\n {\" \"}\n <Text dimColor>[Enter] {gatewayStatus === \"error\" ? \"Retry\" : \"Start\"}</Text>\n </Text>\n )}\n </Box>\n\n {/* Phase indicators */}\n {phases.length > 0 && (\n <Box flexDirection=\"column\" marginBottom={1}>\n {phases.map((phase) => (\n <Box key={phase.label}>\n <Text color={phaseColor(phase.status)}>\n {phaseIcon(phase.status)} {phase.label}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n\n {/* Auth prompt */}\n {authPrompt && (\n <Box marginBottom={1} borderStyle=\"round\" borderColor=\"yellow\" padding={1}>\n <Text color=\"yellow\">Please log in via the Chrome window</Text>\n </Box>\n )}\n\n {/* Error message */}\n {gatewayStatus === \"error\" && gatewayError && (\n <Box marginBottom={1}>\n <Text color=\"red\">Error: {gatewayError}</Text>\n </Box>\n )}\n\n {/* Connected info */}\n {isRunning && (\n <Box>\n <Text color=\"green\">Gateway running on http://127.0.0.1:{config.port}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n {isStopped && <Text dimColor>[b/Esc] Back to menu</Text>}\n {isRunning && <Text dimColor>[b/Esc] Back (gateway keeps running)</Text>}\n </Box>\n </Box>\n );\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { MODEL_ENDPOINT_MAP } from \"../../infra/types.js\";\nimport { updateConfig } from \"../../infra/config.js\";\nimport { wrapIndex } from \"../utils/navigation.js\";\n\nconst MODELS = Object.keys(MODEL_ENDPOINT_MAP);\n\ninterface ModelSelectorProps {\n activeModel: string;\n onSelect: (model: string) => void;\n onBack: () => void;\n}\n\nexport function ModelSelector({ activeModel, onSelect, onBack }: ModelSelectorProps): React.ReactElement {\n const [focusedIndex, setFocusedIndex] = useState(() => {\n const idx = MODELS.indexOf(activeModel);\n return idx >= 0 ? idx : 0;\n });\n const [confirmationModel, setConfirmationModel] = useState<string | null>(null);\n\n useEffect(() => {\n if (confirmationModel !== null) {\n const timer = setTimeout(() => setConfirmationModel(null), 1500);\n return () => clearTimeout(timer);\n }\n }, [confirmationModel]);\n\n useInput((input, key) => {\n if (key.downArrow) {\n setFocusedIndex((i) => wrapIndex(i, 1, MODELS.length));\n } else if (key.upArrow) {\n setFocusedIndex((i) => wrapIndex(i, -1, MODELS.length));\n } else if (key.return) {\n const selected = MODELS[focusedIndex];\n updateConfig({ defaultModel: selected }).catch(() => {});\n onSelect(selected);\n setConfirmationModel(selected);\n } else if (input === \"b\" || key.escape) {\n onBack();\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Select Model</Text>\n </Box>\n <Box flexDirection=\"column\">\n {MODELS.map((model, index) => {\n const isFocused = index === focusedIndex;\n const isActive = model === activeModel;\n return (\n <Box key={model}>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n <Text color={isActive ? \"green\" : \"gray\"}>{isActive ? \"●\" : \"○\"}</Text>\n {\" \"}\n {model}\n </Text>\n </Box>\n );\n })}\n </Box>\n {confirmationModel !== null && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ Selected: {confirmationModel}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\n\ninterface ChatMessage {\n role: \"user\" | \"assistant\" | \"error\";\n content: string;\n}\n\nexport function ChatPanel(): React.ReactElement {\n const { state, navigate } = useAppContext();\n const { gatewayStatus, config, activeModel } = state;\n const gatewayRunning = gatewayStatus === \"running\";\n\n const [input, setInput] = useState(\"\");\n const [loading, setLoading] = useState(false);\n const [lastMessage, setLastMessage] = useState<ChatMessage | null>(null);\n const [lastUserInput, setLastUserInput] = useState<string | null>(null);\n\n const handleSubmit = async (value: string) => {\n if (!gatewayRunning) {\n navigate(\"gateway\");\n return;\n }\n const trimmed = value.trim();\n if (!trimmed || loading) return;\n\n setLastUserInput(trimmed);\n setLastMessage(null);\n setLoading(true);\n setInput(\"\");\n\n const port = config.port;\n const apiKey = config.auth.token ?? null;\n\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n\n let response: Response;\n try {\n response = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n },\n body: JSON.stringify({\n model: activeModel,\n messages: [{ role: \"user\", content: trimmed }],\n }),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n if (!response.ok) {\n let errMsg = `Error ${response.status}`;\n try {\n const errBody = await response.json() as { error?: { message?: string } };\n if (errBody?.error?.message) errMsg += `: ${errBody.error.message}`;\n } catch { /* ignore */ }\n setLastMessage({ role: \"error\", content: errMsg });\n return;\n }\n\n const data = await response.json() as { choices?: Array<{ message?: { content?: string | null } }> };\n const content = data?.choices?.[0]?.message?.content ?? \"\";\n setLastMessage({ role: \"assistant\", content });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n setLastMessage({ role: \"error\", content: \"Request timed out\" });\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n setLastMessage({ role: \"error\", content: `Connection failed: ${msg}` });\n }\n } finally {\n setLoading(false);\n }\n };\n\n useInput((_input, key) => {\n if (!gatewayRunning) {\n if (key.return) navigate(\"gateway\");\n else if (_input === \"b\" || key.escape) navigate(\"menu\");\n return;\n }\n\n if (key.escape) {\n if (!loading) navigate(\"menu\");\n return;\n }\n\n if (key.return) {\n void handleSubmit(input);\n return;\n }\n\n if (loading) return;\n\n if (key.backspace || key.delete) {\n setInput((prev) => prev.slice(0, -1));\n return;\n }\n\n if (_input && !key.ctrl && !key.meta) {\n setInput((prev) => prev + _input);\n }\n });\n\n if (!gatewayRunning) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Chat</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"yellow\">Gateway is not running. Press Enter to start it.</Text>\n </Box>\n <Box>\n <Text dimColor>[b/Esc] Back</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Chat</Text>\n <Text dimColor> Model: {activeModel}</Text>\n </Box>\n\n {/* Response area */}\n <Box flexDirection=\"column\" marginBottom={1} minHeight={6}>\n {lastUserInput && (\n <Box marginBottom={1}>\n <Text color=\"cyan\">You: {lastUserInput}</Text>\n </Box>\n )}\n {loading && (\n <Box>\n <Text color=\"yellow\">Thinking…</Text>\n </Box>\n )}\n {!loading && lastMessage?.role === \"assistant\" && (\n <Box>\n <Text color=\"green\">Assistant: {lastMessage.content}</Text>\n </Box>\n )}\n {!loading && lastMessage?.role === \"error\" && (\n <Box>\n <Text color=\"red\">{lastMessage.content}</Text>\n </Box>\n )}\n </Box>\n\n {/* Input area */}\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1}>\n <Text>\n {input.length > 0 ? input : <Text dimColor>Type a message and press Enter…</Text>}\n <Text color=\"cyan\">█</Text>\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>[Enter] Send [b/Esc] Back</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { copyToClipboard } from \"../utils/clipboard.js\";\n\nexport function InfoPanel(): React.ReactElement {\n const { state, navigate } = useAppContext();\n const { gatewayStatus, config } = state;\n const gatewayRunning = gatewayStatus === \"running\";\n\n const port = config.port;\n const apiKey = config.auth.token ?? null;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n const [flash, setFlash] = useState<string | null>(null);\n const [clipboardFailed, setClipboardFailed] = useState<{ url?: boolean; key?: boolean }>({});\n\n useEffect(() => {\n if (flash !== null) {\n const timer = setTimeout(() => setFlash(null), 1500);\n return () => clearTimeout(timer);\n }\n }, [flash]);\n\n useInput((_input, key) => {\n if (_input === \"c\") {\n copyToClipboard(baseUrl).then((ok) => {\n if (ok) {\n setFlash(\"Copied URL!\");\n setClipboardFailed((prev) => ({ ...prev, url: false }));\n } else {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, url: true }));\n }\n }).catch(() => {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, url: true }));\n });\n return;\n }\n\n if (_input === \"k\") {\n if (!apiKey) return;\n copyToClipboard(apiKey).then((ok) => {\n if (ok) {\n setFlash(\"Copied API key!\");\n setClipboardFailed((prev) => ({ ...prev, key: false }));\n } else {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, key: true }));\n }\n }).catch(() => {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, key: true }));\n });\n return;\n }\n\n if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Server Info</Text>\n </Box>\n\n {!gatewayRunning && (\n <Box marginBottom={1}>\n <Text color=\"red\">● Gateway not running</Text>\n </Box>\n )}\n\n <Box borderStyle=\"round\" borderColor={gatewayRunning ? \"green\" : \"gray\"} padding={1} flexDirection=\"column\" marginBottom={1}>\n <Box marginBottom={1}>\n <Text bold>Base URL: </Text>\n <Text color={gatewayRunning ? \"green\" : \"gray\"}>{baseUrl}</Text>\n </Box>\n {clipboardFailed.url && (\n <Box marginBottom={1}>\n <Box borderStyle=\"single\" borderColor=\"yellow\" paddingX={1}>\n <Text color=\"yellow\">{baseUrl}</Text>\n </Box>\n </Box>\n )}\n <Box>\n <Text bold>API Key: </Text>\n {apiKey ? (\n <Text color={gatewayRunning ? \"cyan\" : \"gray\"}>{apiKey}</Text>\n ) : (\n <Text dimColor>no auth</Text>\n )}\n </Box>\n {clipboardFailed.key && apiKey && (\n <Box marginTop={1}>\n <Box borderStyle=\"single\" borderColor=\"yellow\" paddingX={1}>\n <Text color=\"yellow\">{apiKey}</Text>\n </Box>\n </Box>\n )}\n </Box>\n\n {flash && (\n <Box marginBottom={1}>\n <Text color={flash.startsWith(\"Copied\") ? \"green\" : \"yellow\"}>{flash}</Text>\n </Box>\n )}\n\n <Box>\n <Text dimColor>[c] Copy URL {apiKey ? \"[k] Copy Key \" : \"\"}[b/Esc] Back</Text>\n </Box>\n </Box>\n );\n}\n","import { spawn } from \"node:child_process\";\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n let command: string;\n let args: string[];\n\n switch (process.platform) {\n case \"darwin\":\n command = \"pbcopy\";\n args = [];\n break;\n case \"linux\":\n command = \"xclip\";\n args = [\"-selection\", \"clipboard\"];\n break;\n case \"win32\":\n command = \"clip\";\n args = [];\n break;\n default:\n return false;\n }\n\n return new Promise((resolve) => {\n let proc: ReturnType<typeof spawn>;\n try {\n proc = spawn(command, args, { stdio: [\"pipe\", \"ignore\", \"ignore\"] });\n } catch {\n resolve(false);\n return;\n }\n\n proc.on(\"error\", () => resolve(false));\n proc.on(\"close\", (code) => resolve(code === 0));\n\n try {\n proc.stdin?.write(text);\n proc.stdin?.end();\n } catch {\n resolve(false);\n }\n });\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { updateConfig, validateConfig } from \"../../infra/config.js\";\n\ninterface SettingField {\n key: string;\n label: string;\n getValue: (config: ReturnType<typeof getConfigSnapshot>) => string;\n applyValue: (config: ReturnType<typeof getConfigSnapshot>, raw: string) => Record<string, unknown>;\n validate: (raw: string) => string | null;\n}\n\ninterface ConfigSnapshot {\n port: number;\n cdpUrl: string;\n defaultModel: string;\n authMode: string;\n}\n\nfunction getConfigSnapshot(config: { port: number; cdpUrl: string; defaultModel: string; auth: { mode: string } }): ConfigSnapshot {\n return {\n port: config.port,\n cdpUrl: config.cdpUrl,\n defaultModel: config.defaultModel,\n authMode: config.auth.mode,\n };\n}\n\nconst FIELDS: SettingField[] = [\n {\n key: \"port\",\n label: \"Port\",\n getValue: (s) => String(s.port),\n applyValue: (_s, raw) => ({ port: parseInt(raw, 10) }),\n validate: (raw) => {\n const n = parseInt(raw, 10);\n if (isNaN(n) || n < 1 || n > 65535) return \"Port must be between 1 and 65535\";\n return null;\n },\n },\n {\n key: \"cdpUrl\",\n label: \"CDP URL\",\n getValue: (s) => s.cdpUrl,\n applyValue: (_s, raw) => ({ cdpUrl: raw }),\n validate: (raw) => {\n if (!/^https?:\\/\\//.test(raw)) return \"CDP URL must start with http:// or https://\";\n return null;\n },\n },\n {\n key: \"defaultModel\",\n label: \"Default Model\",\n getValue: (s) => s.defaultModel,\n applyValue: (_s, raw) => ({ defaultModel: raw }),\n validate: (raw) => {\n if (!raw.trim()) return \"Model must be a non-empty string\";\n return null;\n },\n },\n {\n key: \"authMode\",\n label: \"Auth Mode\",\n getValue: (s) => s.authMode,\n applyValue: (_s, raw) => ({ auth: { mode: raw } }),\n validate: (raw) => {\n if (raw !== \"none\" && raw !== \"bearer\" && raw !== \"basic\")\n return 'Auth mode must be \"none\", \"bearer\", or \"basic\"';\n return null;\n },\n },\n];\n\nexport function SettingsPanel(): React.ReactElement {\n const { state, navigate, setConfig } = useAppContext();\n const { config } = state;\n\n const snapshot = getConfigSnapshot(config);\n\n const [focusedField, setFocusedField] = useState(0);\n const [editingField, setEditingField] = useState<number | null>(null);\n const [editValue, setEditValue] = useState(\"\");\n const [fieldError, setFieldError] = useState<string | null>(null);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [savedField, setSavedField] = useState<string | null>(null);\n\n const confirmEdit = async () => {\n if (editingField === null) return;\n const field = FIELDS[editingField];\n\n const validErr = field.validate(editValue);\n if (validErr) {\n setFieldError(validErr);\n return;\n }\n\n const partial = field.applyValue(snapshot, editValue);\n\n try {\n const merged = { ...config, ...partial } as Record<string, unknown>;\n if (\"auth\" in partial && typeof partial.auth === \"object\") {\n merged.auth = { ...config.auth, ...(partial.auth as Record<string, unknown>) };\n }\n validateConfig(merged);\n await updateConfig(partial as Parameters<typeof updateConfig>[0]);\n\n const updatedConfig = { ...config, ...partial } as typeof config;\n if (\"auth\" in partial && typeof partial.auth === \"object\") {\n updatedConfig.auth = { ...config.auth, ...(partial.auth as { mode?: \"none\" | \"bearer\" | \"basic\"; token?: string | null }) };\n }\n setConfig(updatedConfig);\n setSavedField(field.key);\n setTimeout(() => setSavedField(null), 1500);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n setSaveError(msg);\n setTimeout(() => setSaveError(null), 3000);\n return;\n }\n\n setEditingField(null);\n setEditValue(\"\");\n setFieldError(null);\n };\n\n useInput((_input, key) => {\n if (editingField !== null) {\n if (key.escape) {\n setEditingField(null);\n setEditValue(\"\");\n setFieldError(null);\n return;\n }\n if (key.return) {\n void confirmEdit();\n return;\n }\n if (key.backspace || key.delete) {\n setEditValue((prev) => prev.slice(0, -1));\n setFieldError(null);\n return;\n }\n if (_input && !key.ctrl && !key.meta) {\n setEditValue((prev) => prev + _input);\n setFieldError(null);\n return;\n }\n return;\n }\n\n if (key.downArrow) {\n setFocusedField((i) => (i + 1) % FIELDS.length);\n return;\n }\n if (key.upArrow) {\n setFocusedField((i) => (i - 1 + FIELDS.length) % FIELDS.length);\n return;\n }\n if (key.return) {\n const field = FIELDS[focusedField];\n setEditValue(field.getValue(snapshot));\n setEditingField(focusedField);\n setFieldError(null);\n return;\n }\n if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Settings</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginBottom={1}>\n {FIELDS.map((field, index) => {\n const isFocused = index === focusedField;\n const isEditing = editingField === index;\n const currentValue = field.getValue(snapshot);\n const isSaved = savedField === field.key;\n\n return (\n <Box key={field.key} flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n <Text bold>{field.label}: </Text>\n {isEditing ? (\n <Text color=\"cyan\">\n {editValue}\n <Text color=\"cyan\">█</Text>\n </Text>\n ) : (\n <Text color={isSaved ? \"green\" : undefined}>\n {currentValue}\n {isSaved ? \" ✓\" : \"\"}\n </Text>\n )}\n </Text>\n </Box>\n {isEditing && fieldError && (\n <Box marginLeft={4}>\n <Text color=\"red\">{fieldError}</Text>\n </Box>\n )}\n </Box>\n );\n })}\n </Box>\n\n {saveError && (\n <Box marginBottom={1}>\n <Text color=\"red\">{saveError}</Text>\n </Box>\n )}\n\n <Box>\n {editingField !== null ? (\n <Text dimColor>[Enter] Confirm [Esc] Cancel</Text>\n ) : (\n <Text dimColor>[↑↓] Navigate [Enter] Edit [b/Esc] Back</Text>\n )}\n </Box>\n </Box>\n );\n}\n"],"mappings":"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","names":["React","useState","useRef","useEffect","Box","useInput","jsx","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","useCallback","Box","Text","useInput","jsx","jsxs","useState","useCallback","useInput","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","useEffect","useInput","Box","Text","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","useEffect","useInput","Box","Text","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","jsx","jsxs","useState","useRef","useEffect","useInput","Box","React"]}
|
package/package.json
CHANGED
package/dist/tui-YQKGK5XA.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tui/index.ts","../src/tui/App.tsx","../src/tui/AppContext.tsx","../src/tui/components/HeaderBar.tsx","../src/tui/components/FooterBar.tsx","../src/tui/components/QuitDialog.tsx","../src/tui/services/gateway-lifecycle.ts","../src/tui/utils/shortcuts.ts","../src/tui/panels/SplashScreen.tsx","../src/tui/panels/MainMenu.tsx","../src/tui/types.ts","../src/tui/utils/navigation.ts","../src/tui/panels/GatewayPanel.tsx","../src/tui/panels/ModelSelector.tsx","../src/tui/panels/ChatPanel.tsx","../src/tui/panels/InfoPanel.tsx","../src/tui/utils/clipboard.ts","../src/tui/panels/SettingsPanel.tsx"],"sourcesContent":["import React from \"react\";\nimport { render } from \"ink\";\nimport { App } from \"./App.js\";\n\nexport async function launchTui(): Promise<void> {\n // ── Terminal size check ────────────────────────────────────────────────────\n const checkSize = (): boolean => {\n const cols = process.stdout.columns ?? 0;\n const rows = process.stdout.rows ?? 0;\n return cols >= 80 && rows >= 24;\n };\n\n if (!checkSize()) {\n process.stderr.write(\n `Terminal too small (${process.stdout.columns ?? 0}×${process.stdout.rows ?? 0}). ` +\n `Please resize to at least 80×24.\\n`,\n );\n await new Promise<void>((resolve) => {\n const onResize = () => {\n if (checkSize()) {\n process.stdout.removeListener(\"resize\", onResize);\n resolve();\n }\n };\n process.stdout.on(\"resize\", onResize);\n });\n }\n\n // ── Shutdown callback registered by AppContent ─────────────────────────────\n let shutdown: (() => Promise<void>) | null = null;\n\n const { waitUntilExit, unmount } = render(\n React.createElement(App, {\n onRegisterShutdown: (fn) => {\n shutdown = fn;\n },\n }),\n { exitOnCtrlC: false },\n );\n\n // ── Signal handlers ────────────────────────────────────────────────────────\n const handleSignal = (): void => {\n void (async () => {\n try {\n if (shutdown) await shutdown();\n } catch {\n // ignore errors during graceful shutdown\n }\n unmount();\n process.exit(0);\n })();\n };\n\n process.on(\"SIGINT\", handleSignal);\n process.on(\"SIGTERM\", handleSignal);\n\n // ── Unhandled error safety net ─────────────────────────────────────────────\n process.on(\"uncaughtException\", (err) => {\n unmount();\n process.stderr.write(`Unhandled error: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n\n try {\n await waitUntilExit();\n } finally {\n process.removeListener(\"SIGINT\", handleSignal);\n process.removeListener(\"SIGTERM\", handleSignal);\n }\n}\n","import React, { useState, useRef, useEffect } from \"react\";\nimport { Box, useInput } from \"ink\";\nimport { AppProvider, useAppContext } from \"./AppContext.js\";\nimport { HeaderBar } from \"./components/HeaderBar.js\";\nimport { FooterBar } from \"./components/FooterBar.js\";\nimport { QuitDialog } from \"./components/QuitDialog.js\";\nimport { PANEL_SHORTCUTS } from \"./utils/shortcuts.js\";\nimport { SplashScreen } from \"./panels/SplashScreen.js\";\nimport { MainMenu } from \"./panels/MainMenu.js\";\nimport { GatewayPanel } from \"./panels/GatewayPanel.js\";\nimport { ModelSelector } from \"./panels/ModelSelector.js\";\nimport { ChatPanel } from \"./panels/ChatPanel.js\";\nimport { InfoPanel } from \"./panels/InfoPanel.js\";\nimport { SettingsPanel } from \"./panels/SettingsPanel.js\";\nimport { stopGateway } from \"./services/gateway-lifecycle.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\ninterface AppProps {\n onRegisterShutdown?: (fn: () => Promise<void>) => void;\n}\n\n// ── Inner component (inside AppProvider) ──────────────────────────────────────\n\nfunction AppContent({ onRegisterShutdown }: AppProps): React.ReactElement {\n const { state, navigate, setActiveModel } = useAppContext();\n const { currentPanel, gatewayStatus, activeModel } = state;\n\n const [showQuit, setShowQuit] = useState(false);\n\n // Keep a ref to the latest state so the shutdown closure always sees fresh values\n const latestStateRef = useRef(state);\n useEffect(() => {\n latestStateRef.current = state;\n });\n\n // Register the shutdown function once on mount\n useEffect(() => {\n if (onRegisterShutdown) {\n onRegisterShutdown(async () => {\n const { gatewayStatus: status, serverHandle, chromeState, tokenRefresher } =\n latestStateRef.current;\n if (status === \"running\" && serverHandle && chromeState && tokenRefresher) {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n }\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Global q handler — active only when the quit dialog is not already showing\n // and not on the splash screen\n useInput(\n (input) => {\n if (input === \"q\" && currentPanel !== \"splash\") {\n setShowQuit(true);\n }\n },\n { isActive: !showQuit },\n );\n\n // ── Panel router ─────────────────────────────────────────────────────────────\n\n const renderPanel = (): React.ReactElement => {\n switch (currentPanel) {\n case \"splash\":\n return <SplashScreen onComplete={() => navigate(\"menu\")} />;\n case \"menu\":\n return <MainMenu onQuit={() => setShowQuit(true)} />;\n case \"gateway\":\n return <GatewayPanel />;\n case \"model\":\n return (\n <ModelSelector\n activeModel={activeModel}\n onSelect={(model) => setActiveModel(model)}\n onBack={() => navigate(\"menu\")}\n />\n );\n case \"chat\":\n return <ChatPanel />;\n case \"info\":\n return <InfoPanel />;\n case \"settings\":\n return <SettingsPanel />;\n }\n };\n\n // ── Layout ───────────────────────────────────────────────────────────────────\n\n return (\n <Box flexDirection=\"column\" height=\"100%\">\n <HeaderBar gatewayStatus={gatewayStatus} />\n <Box flexGrow={1} flexDirection=\"column\">\n {showQuit ? (\n <QuitDialog onCancel={() => setShowQuit(false)} />\n ) : (\n renderPanel()\n )}\n </Box>\n <FooterBar shortcuts={PANEL_SHORTCUTS[currentPanel]} />\n </Box>\n );\n}\n\n// ── Root component ─────────────────────────────────────────────────────────────\n\nexport function App({ onRegisterShutdown }: AppProps): React.ReactElement {\n return (\n <AppProvider>\n <AppContent onRegisterShutdown={onRegisterShutdown} />\n </AppProvider>\n );\n}\n","import React, { createContext, useContext, useState, useEffect } from \"react\";\nimport { loadConfig } from \"../infra/config.js\";\nimport type { GatewayConfig } from \"../infra/types.js\";\nimport type { TuiAppState, PanelId } from \"./types.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport type { TokenRefresher } from \"../core/token-refresher.js\";\nimport type { ServerHandle } from \"../server.js\";\n\n// ── Context value type ─────────────────────────────────────────────────────────\n\ninterface AppContextValue {\n state: TuiAppState;\n navigate: (panelId: PanelId) => void;\n setGatewayStatus: (status: TuiAppState[\"gatewayStatus\"]) => void;\n setGatewayError: (error: string | null) => void;\n setActiveModel: (model: string) => void;\n setServerHandle: (handle: ServerHandle | null) => void;\n setChromeState: (state: ChromeManagerState | null) => void;\n setTokenRefresher: (refresher: TokenRefresher | null) => void;\n setConfig: (config: GatewayConfig) => void;\n}\n\n// ── Default/initial state ──────────────────────────────────────────────────────\n\nconst defaultConfig: GatewayConfig = {\n cdpUrl: \"http://127.0.0.1:9222\",\n port: 8741,\n defaultModel: \"claude-opus-4.5\",\n defaultEndpoint: \"AnthropicClaude\",\n credentials: null,\n auth: { mode: \"none\", token: null },\n maxQueueWaitMs: 120000,\n};\n\nconst initialState: TuiAppState = {\n currentPanel: \"splash\",\n gatewayStatus: \"stopped\",\n gatewayError: null,\n activeModel: defaultConfig.defaultModel,\n config: defaultConfig,\n serverHandle: null,\n chromeState: null,\n tokenRefresher: null,\n};\n\n// ── Context ────────────────────────────────────────────────────────────────────\n\nconst AppContext = createContext<AppContextValue | null>(null);\n\n// ── Provider ──────────────────────────────────────────────────────────────────\n\ninterface AppProviderProps {\n children: React.ReactNode;\n}\n\nexport function AppProvider({ children }: AppProviderProps): React.ReactElement {\n const [state, setState] = useState<TuiAppState>(initialState);\n\n useEffect(() => {\n loadConfig()\n .then((config) => {\n setState((prev) => ({\n ...prev,\n config,\n activeModel: config.defaultModel,\n }));\n })\n .catch(() => {\n // Keep defaults on error\n });\n }, []);\n\n const navigate = (panelId: PanelId): void => {\n setState((prev) => ({ ...prev, currentPanel: panelId }));\n };\n\n const setGatewayStatus = (status: TuiAppState[\"gatewayStatus\"]): void => {\n setState((prev) => ({ ...prev, gatewayStatus: status }));\n };\n\n const setGatewayError = (error: string | null): void => {\n setState((prev) => ({ ...prev, gatewayError: error }));\n };\n\n const setActiveModel = (model: string): void => {\n setState((prev) => ({ ...prev, activeModel: model }));\n };\n\n const setServerHandle = (handle: ServerHandle | null): void => {\n setState((prev) => ({ ...prev, serverHandle: handle }));\n };\n\n const setChromeState = (chromeState: ChromeManagerState | null): void => {\n setState((prev) => ({ ...prev, chromeState }));\n };\n\n const setTokenRefresher = (refresher: TokenRefresher | null): void => {\n setState((prev) => ({ ...prev, tokenRefresher: refresher }));\n };\n\n const setConfig = (config: GatewayConfig): void => {\n setState((prev) => ({ ...prev, config }));\n };\n\n const value: AppContextValue = {\n state,\n navigate,\n setGatewayStatus,\n setGatewayError,\n setActiveModel,\n setServerHandle,\n setChromeState,\n setTokenRefresher,\n setConfig,\n };\n\n return <AppContext.Provider value={value}>{children}</AppContext.Provider>;\n}\n\n// ── Hook ──────────────────────────────────────────────────────────────────────\n\nexport function useAppContext(): AppContextValue {\n const ctx = useContext(AppContext);\n if (!ctx) {\n throw new Error(\"useAppContext must be used within AppProvider\");\n }\n return ctx;\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\n\ninterface HeaderBarProps {\n gatewayStatus: \"stopped\" | \"starting\" | \"running\" | \"error\";\n}\n\nexport function HeaderBar({ gatewayStatus }: HeaderBarProps): React.ReactElement {\n const dotColor =\n gatewayStatus === \"running\"\n ? \"green\"\n : gatewayStatus === \"starting\"\n ? \"yellow\"\n : \"red\";\n\n const statusLabel =\n gatewayStatus === \"running\"\n ? \"running\"\n : gatewayStatus === \"starting\"\n ? \"starting\"\n : gatewayStatus === \"error\"\n ? \"error\"\n : \"stopped\";\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Text bold>jh-gateway</Text>\n <Text>\n <Text color={dotColor}>●</Text>\n {\" \"}\n <Text>Gateway: {statusLabel}</Text>\n </Text>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { FooterShortcut } from \"../types.js\";\n\ninterface FooterBarProps {\n shortcuts: FooterShortcut[];\n}\n\nexport function FooterBar({ shortcuts }: FooterBarProps): React.ReactElement {\n return (\n <Box width=\"100%\" gap={2}>\n {shortcuts.map((s) => (\n <Text key={s.key}>\n <Text dimColor>{\"[\"}</Text>\n <Text bold>{s.key}</Text>\n <Text dimColor>{\"]\"}</Text>\n {\" \"}\n <Text>{s.label}</Text>\n </Text>\n ))}\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { stopGateway } from \"../services/gateway-lifecycle.js\";\n\ninterface QuitDialogProps {\n onCancel: () => void;\n}\n\nexport function QuitDialog({ onCancel }: QuitDialogProps): React.ReactElement {\n const { state } = useAppContext();\n const { gatewayStatus, serverHandle, chromeState, tokenRefresher } = state;\n\n useInput((input, key) => {\n if (input === \"y\" || input === \"Y\") {\n void (async () => {\n if (\n gatewayStatus === \"running\" &&\n serverHandle &&\n chromeState &&\n tokenRefresher\n ) {\n try {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n } catch {\n // proceed with exit even if stop fails\n }\n }\n process.exit(0);\n })();\n } else if (input === \"n\" || input === \"N\" || key.escape) {\n onCancel();\n }\n });\n\n return (\n <Box borderStyle=\"round\" borderColor=\"yellow\" padding={1} flexDirection=\"column\">\n <Text bold color=\"yellow\">\n Quit jh-gateway?\n </Text>\n <Box marginTop={1}>\n <Text>Running gateway will be stopped.</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>[y] Yes [N/Esc] Cancel</Text>\n </Box>\n </Box>\n );\n}\n","import { ChromeManager } from \"../../infra/chrome-manager.js\";\nimport type { ChromeManagerState } from \"../../infra/chrome-manager.js\";\nimport { findOrOpenJhPage, checkBrowserLoginState } from \"../../infra/chrome-cdp.js\";\nimport { captureCredentials } from \"../../core/auth-capture.js\";\nimport { shouldRefresh, CredentialHolder, TokenRefresher } from \"../../core/token-refresher.js\";\nimport { PagePool } from \"../../core/page-pool.js\";\nimport { startServer } from \"../../server.js\";\nimport type { ServerHandle } from \"../../server.js\";\nimport type { GatewayConfig } from \"../../infra/types.js\";\n\nexport interface GatewayLifecycleCallbacks {\n onPhase: (phase: string) => void;\n onSuccess: (info: { baseUrl: string; apiKey: string | null }) => void;\n onError: (error: Error) => void;\n}\n\nexport interface StartGatewayResult {\n serverHandle: ServerHandle;\n chromeState: ChromeManagerState;\n tokenRefresher: TokenRefresher;\n}\n\nexport async function startGatewayForTui(\n config: GatewayConfig,\n options: { headless?: boolean; pages?: number },\n callbacks: GatewayLifecycleCallbacks,\n): Promise<StartGatewayResult> {\n const maxPages = options.pages ?? 3;\n const cdpPort = parseInt(new URL(config.cdpUrl).port, 10) || 9222;\n\n const chromeManager = new ChromeManager({\n cdpPort,\n headless: options.headless,\n });\n\n // ── Phase 1: Connect to Chrome ────────────────────────────────────────────\n callbacks.onPhase(\"Connecting to Chrome\");\n let state: ChromeManagerState;\n try {\n state = await chromeManager.connect();\n await chromeManager.showWindow(state);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n throw error;\n }\n\n // ── Phase 2: Authenticate if needed ──────────────────────────────────────\n const needsAuth =\n !config.credentials ||\n !config.credentials.expiresAt ||\n shouldRefresh(Date.now(), config.credentials.expiresAt, 0);\n\n if (needsAuth) {\n callbacks.onPhase(\"Waiting for login\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n } else {\n callbacks.onPhase(\"Verifying browser session\");\n const browserLoggedIn = await checkBrowserLoginState(state.browser);\n if (!browserLoggedIn) {\n callbacks.onPhase(\"Browser session expired — please log in\");\n try {\n const creds = await captureCredentials(config.cdpUrl, 300_000);\n config.credentials = {\n bearerToken: creds.bearerToken,\n cookie: creds.cookie,\n userAgent: creds.userAgent,\n expiresAt: creds.expiresAt,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n }\n }\n\n // ── Phase 3: PagePool, CredentialHolder, Server ───────────────────────────\n callbacks.onPhase(\"Starting server\");\n try {\n const seedPage = await findOrOpenJhPage(state.browser);\n\n const pool = new PagePool({\n maxPages,\n maxWaitMs: config.maxQueueWaitMs,\n });\n await pool.init(state.browser, seedPage);\n\n const credentialHolder = new CredentialHolder();\n if (config.credentials) {\n credentialHolder.set(config.credentials);\n }\n\n const serverHandle = await startServer(config, {\n getPool: () => pool,\n getCredentials: () => credentialHolder.get(),\n });\n\n const tokenRefresher = new TokenRefresher(credentialHolder, config.cdpUrl);\n tokenRefresher.start();\n\n const baseUrl = `http://127.0.0.1:${config.port}`;\n const apiKey = config.auth.token ?? null;\n callbacks.onSuccess({ baseUrl, apiKey });\n\n return { serverHandle, chromeState: state, tokenRefresher };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n callbacks.onError(error);\n await chromeManager.shutdown(state);\n throw error;\n }\n}\n\nexport async function stopGateway(\n serverHandle: ServerHandle,\n chromeState: ChromeManagerState,\n tokenRefresher: TokenRefresher,\n): Promise<void> {\n tokenRefresher.stop();\n await serverHandle.close();\n\n // disconnect (not shutdown): hides Chrome and detaches CDP, but keeps\n // the Chrome process alive so the next start reconnects instantly.\n const chromeManager = new ChromeManager();\n await chromeManager.disconnect(chromeState);\n}\n","import type { PanelId, FooterShortcut } from \"../types.js\";\n\nexport const PANEL_SHORTCUTS: Record<PanelId, FooterShortcut[]> = {\n splash: [\n { key: \"any\", label: \"Continue\" },\n ],\n menu: [\n { key: \"↑↓\", label: \"Navigate\" },\n { key: \"Enter\", label: \"Select\" },\n { key: \"q\", label: \"Quit\" },\n ],\n gateway: [\n { key: \"Enter\", label: \"Start/Stop\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n model: [\n { key: \"↑↓\", label: \"Navigate\" },\n { key: \"Enter\", label: \"Select\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n chat: [\n { key: \"Enter\", label: \"Send\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n info: [\n { key: \"c\", label: \"Copy URL\" },\n { key: \"k\", label: \"Copy Key\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n settings: [\n { key: \"Enter\", label: \"Edit\" },\n { key: \"b\", label: \"Back\" },\n { key: \"q\", label: \"Quit\" },\n ],\n};\n","import React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport { Box, Text, useInput, useStdout, useAnimation } from \"ink\";\n\nconst JHU_LOGO_LINES = [\n \" ██╗██╗ ██╗ ██╗ ██╗███████╗██████╗ \",\n \" ██║██║ ██║ ██║ ██║██╔════╝██╔══██╗\",\n \" ██║███████║ ██║ █╗ ██║█████╗ ██████╔╝\",\n \"██ ██║██╔══██║ ██║███╗██║██╔══╝ ██╔══██╗\",\n \"╚█████╔╝██║ ██║ ╚███╔███╔╝███████╗██████╔╝\",\n \" ╚════╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═════╝ \",\n \"\",\n \" ██████╗ █████╗ ████████╗███████╗██╗ ██╗ █████╗ ██╗ ██╗\",\n \" ██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔══██╗╚██╗ ██╔╝\",\n \" ██║ ███╗███████║ ██║ █████╗ ██║ █╗ ██║███████║ ╚████╔╝ \",\n \" ██║ ██║██╔══██║ ██║ ██╔══╝ ██║███╗██║██╔══██║ ╚██╔╝ \",\n \" ╚██████╔╝██║ ██║ ██║ ███████╗╚███╔███╔╝██║ ██║ ██║ \",\n \" ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ \",\n];\n\nconst SUBTITLES = [\n \"// Infinite tokens, for school work of course :)))\",\n \"// powered by caffeine, desperation, and vibes\",\n \"// your professor will never know™\",\n \"// if it works, don't ask why\",\n \"// technically not cheating if the AI is also confused\",\n \"// [ERROR 418: I'm a teapot, but make it AI]\",\n \"// running on 3 hours of sleep and pure audacity\",\n \"// the tokens are infinite but my GPA is not\",\n \"// SYSTEM OVERRIDE: homework.exe has been defeated\",\n \"// now with 40% more hallucinations!\",\n \"// null pointer? more like null problems 😈\",\n \"// WARNING: may cause uncontrollable productivity\",\n \"// all citations are made up but they sound real\",\n \"// NOT affiliated with Skynet (probably)\",\n \"// lorem ipsum but it writes your thesis\",\n \"// sudo make my-homework --grade=A\",\n \"// ship it. always ship it.\",\n \"// this is fine 🔥 everything is fine 🔥\",\n \"// bridging the gap between laziness and genius\",\n \"// if AI wrote this disclaimer, does it count?\",\n];\n\nconst TAGLINE = SUBTITLES[Math.floor(Math.random() * SUBTITLES.length)]!;\nconst GLITCH_CHARS = \"█▓▒░╬╪╫▲▼◆◇○●□■#@$%&?!~^*\";\nconst STAR_CHARS = [\"·\", \".\", \"*\", \"✦\", \"✧\", \"⋆\", \"+\", \"×\"];\nconst LOGO_COLORS = [\"cyan\", \"cyan\", \"blueBright\", \"blueBright\", \"blue\", \"blue\", \"white\", \"greenBright\", \"green\", \"greenBright\", \"green\", \"greenBright\", \"green\"] as const;\n\nconst GLITCH_PHASE_MS = 600;\nconst REVEAL_PHASE_MS = 900;\nconst TYPEWRITER_INTERVAL_MS = 28;\n\nfunction seededRand(seed: number): () => number {\n let s = seed;\n return () => {\n s = (s * 1664525 + 1013904223) & 0xffffffff;\n return (s >>> 0) / 0xffffffff;\n };\n}\n\nfunction glitchLine(line: string, progress: number, rand: () => number): string {\n return line\n .split(\"\")\n .map((ch) => {\n if (ch === \" \" || ch === \"\\n\") return ch;\n if (rand() > progress) {\n return GLITCH_CHARS[Math.floor(rand() * GLITCH_CHARS.length)];\n }\n return ch;\n })\n .join(\"\");\n}\n\ninterface StarfieldProps {\n rows: number;\n cols: number;\n tick: number;\n}\n\nfunction Starfield({ rows, cols, tick }: StarfieldProps): React.ReactElement {\n const rand = seededRand(42);\n const lines: string[] = [];\n for (let r = 0; r < rows; r++) {\n let row = \"\";\n for (let c = 0; c < cols; c++) {\n const base = rand();\n const twinkleOffset = Math.sin((tick * 0.3 + r * 7.3 + c * 3.7)) * 0.5 + 0.5;\n row += base < 0.025 * twinkleOffset ? STAR_CHARS[Math.floor(rand() * STAR_CHARS.length)] : \" \";\n }\n lines.push(row);\n }\n return (\n <Box flexDirection=\"column\" position=\"absolute\" marginTop={0}>\n {lines.map((l, i) => (\n <Text key={i} dimColor color=\"cyan\">{l}</Text>\n ))}\n </Box>\n );\n}\n\ninterface SplashScreenProps {\n onComplete: () => void;\n}\n\nexport function SplashScreen({ onComplete }: SplashScreenProps): React.ReactElement {\n const { stdout } = useStdout();\n const termCols = stdout?.columns ?? 120;\n const termRows = stdout?.rows ?? 30;\n\n const [phase, setPhase] = useState<\"glitch\" | \"reveal\" | \"done\">(\"glitch\");\n const [glitchProgress, setGlitchProgress] = useState(0);\n const [revealedLines, setRevealedLines] = useState(0);\n const [typewriterCount, setTypewriterCount] = useState(0);\n const [showCursor, setShowCursor] = useState(true);\n const skippedRef = useRef(false);\n\n const { frame: tick } = useAnimation({ interval: 80 });\n\n const skip = useCallback(() => {\n if (skippedRef.current) return;\n skippedRef.current = true;\n setPhase(\"done\");\n setRevealedLines(JHU_LOGO_LINES.length);\n setTypewriterCount(TAGLINE.length);\n setShowCursor(false);\n }, []);\n\n useInput((_, key) => {\n if (phase !== \"done\") {\n skip();\n } else {\n onComplete();\n }\n });\n\n useEffect(() => {\n if (skippedRef.current) return;\n const start = Date.now();\n const id = setInterval(() => {\n const elapsed = Date.now() - start;\n const p = Math.min(elapsed / GLITCH_PHASE_MS, 1);\n setGlitchProgress(p);\n if (p >= 1) {\n clearInterval(id);\n setPhase(\"reveal\");\n }\n }, 40);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n if (phase !== \"reveal\" || skippedRef.current) return;\n const total = JHU_LOGO_LINES.length;\n let count = 0;\n const id = setInterval(() => {\n count++;\n setRevealedLines(count);\n if (count >= total) {\n clearInterval(id);\n setPhase(\"done\");\n }\n }, Math.round(REVEAL_PHASE_MS / total));\n return () => clearInterval(id);\n }, [phase]);\n\n useEffect(() => {\n if (phase !== \"done\") return;\n if (typewriterCount >= TAGLINE.length) return;\n const id = setInterval(() => {\n setTypewriterCount((p) => {\n if (p >= TAGLINE.length) {\n clearInterval(id);\n return p;\n }\n return p + 1;\n });\n }, TYPEWRITER_INTERVAL_MS);\n return () => clearInterval(id);\n }, [phase, typewriterCount]);\n\n useEffect(() => {\n if (phase !== \"done\") return;\n const id = setInterval(() => setShowCursor((p) => !p), 530);\n return () => clearInterval(id);\n }, [phase]);\n\n const logoStartRow = Math.max(0, Math.floor((termRows - JHU_LOGO_LINES.length - 6) / 2));\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" height=\"100%\">\n <Starfield rows={termRows} cols={termCols} tick={tick} />\n\n <Box flexDirection=\"column\" marginTop={logoStartRow} alignItems=\"flex-start\" paddingLeft={2}>\n {JHU_LOGO_LINES.map((line, i) => {\n let displayLine: string;\n const color = LOGO_COLORS[i] ?? \"cyan\";\n\n if (phase === \"glitch\") {\n const lineRand = seededRand(i * 137 + 11);\n displayLine = glitchLine(line, glitchProgress, lineRand);\n } else if (phase === \"reveal\") {\n if (i < revealedLines) {\n displayLine = line;\n } else {\n const lineRand = seededRand(i * 137 + 11);\n displayLine = glitchLine(line, 0.0, lineRand);\n }\n } else {\n displayLine = line;\n }\n\n return (\n <Text key={i} color={color} bold={i < 6}>\n {displayLine}\n </Text>\n );\n })}\n\n {phase === \"done\" && (\n <>\n <Box marginTop={1}>\n <Text color=\"greenBright\">\n {\" \"}\n {TAGLINE.slice(0, typewriterCount)}\n {showCursor ? \"█\" : \" \"}\n </Text>\n </Box>\n {typewriterCount >= TAGLINE.length && (\n <Box marginTop={1}>\n <Text dimColor> Press any key to continue_</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n </Box>\n );\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { MENU_ITEMS } from \"../types.js\";\nimport { wrapIndex } from \"../utils/navigation.js\";\nimport { useAppContext } from \"../AppContext.js\";\nimport type { PanelId } from \"../types.js\";\n\ninterface MainMenuProps {\n onQuit: () => void;\n}\n\nexport function MainMenu({ onQuit }: MainMenuProps): React.ReactElement {\n const { navigate } = useAppContext();\n const [focusedIndex, setFocusedIndex] = useState(0);\n\n useInput((input, key) => {\n if (key.downArrow) {\n setFocusedIndex((i) => wrapIndex(i, 1, MENU_ITEMS.length));\n } else if (key.upArrow) {\n setFocusedIndex((i) => wrapIndex(i, -1, MENU_ITEMS.length));\n } else if (key.return) {\n const item = MENU_ITEMS[focusedIndex];\n if (item.id === \"quit\") {\n onQuit();\n } else {\n navigate(item.id as PanelId);\n }\n } else if (input === \"q\" || key.escape) {\n onQuit();\n }\n });\n\n const focusedItem = MENU_ITEMS[focusedIndex];\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Main Menu</Text>\n </Box>\n <Box flexDirection=\"column\">\n {MENU_ITEMS.map((item, index) => {\n const isFocused = index === focusedIndex;\n return (\n <Box key={item.id}>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n {item.label}\n </Text>\n </Box>\n );\n })}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>{focusedItem.description}</Text>\n </Box>\n </Box>\n );\n}\n","import type { GatewayConfig } from \"../infra/types.js\";\nimport type { ChromeManagerState } from \"../infra/chrome-manager.js\";\nimport type { TokenRefresher } from \"../core/token-refresher.js\";\nimport type { ServerHandle } from \"../server.js\";\n\n// ── Panel IDs ─────────────────────────────────────────────────────────────────\n\nexport type PanelId = \"splash\" | \"menu\" | \"gateway\" | \"model\" | \"chat\" | \"info\" | \"settings\";\n\n// ── App State ─────────────────────────────────────────────────────────────────\n\nexport interface TuiAppState {\n currentPanel: PanelId;\n gatewayStatus: \"stopped\" | \"starting\" | \"running\" | \"error\";\n gatewayError: string | null;\n activeModel: string;\n config: GatewayConfig;\n serverHandle: ServerHandle | null;\n chromeState: ChromeManagerState | null;\n tokenRefresher: TokenRefresher | null;\n}\n\n// ── Menu ──────────────────────────────────────────────────────────────────────\n\nexport interface MenuItem {\n id: PanelId | \"quit\";\n label: string;\n description: string;\n}\n\nexport const MENU_ITEMS: MenuItem[] = [\n { id: \"gateway\", label: \"Start Gateway\", description: \"Launch Chrome, authenticate, and start the HTTP server\" },\n { id: \"model\", label: \"Model\", description: \"Select the active AI model\" },\n { id: \"chat\", label: \"Chat\", description: \"Send a test message to the running gateway\" },\n { id: \"info\", label: \"Server Info\", description: \"View and copy the server URL and API key\" },\n { id: \"settings\",label: \"Settings\", description: \"View and edit gateway configuration\" },\n { id: \"quit\", label: \"Quit\", description: \"Exit jh-gateway\" },\n];\n\n// ── Gateway Phase ─────────────────────────────────────────────────────────────\n\nexport interface GatewayPhase {\n label: string;\n status: \"pending\" | \"active\" | \"done\" | \"error\";\n}\n\n// ── Footer Shortcut ───────────────────────────────────────────────────────────\n\nexport type FooterShortcut = { key: string; label: string };\n","/**\n * Computes a wrapped index after moving `delta` steps through a list of `listSize` items.\n * Handles both positive (down) and negative (up) deltas, wrapping around boundaries.\n */\nexport function wrapIndex(current: number, delta: number, listSize: number): number {\n return ((current + delta) % listSize + listSize) % listSize;\n}\n","import React, { useState, useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { startGatewayForTui, stopGateway } from \"../services/gateway-lifecycle.js\";\nimport type { GatewayPhase } from \"../types.js\";\n\nconst PHASE_LABELS = [\"Connecting to Chrome\", \"Waiting for login\", \"Starting server\"];\n\nfunction phaseIcon(status: GatewayPhase[\"status\"]): string {\n switch (status) {\n case \"pending\": return \"○\";\n case \"active\": return \"◌\";\n case \"done\": return \"●\";\n case \"error\": return \"✗\";\n }\n}\n\nfunction phaseColor(status: GatewayPhase[\"status\"]): string | undefined {\n switch (status) {\n case \"done\": return \"green\";\n case \"active\": return \"cyan\";\n case \"error\": return \"red\";\n default: return \"gray\";\n }\n}\n\nexport function GatewayPanel(): React.ReactElement {\n const { state, navigate, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher } = useAppContext();\n const { gatewayStatus, gatewayError, config, serverHandle, chromeState, tokenRefresher } = state;\n\n const [phases, setPhases] = useState<GatewayPhase[]>([]);\n const [starting, setStarting] = useState(false);\n const [stopping, setStopping] = useState(false);\n const [authPrompt, setAuthPrompt] = useState(false);\n\n const handleStart = useCallback(async () => {\n if (starting || stopping) return;\n setStarting(true);\n setGatewayError(null);\n setGatewayStatus(\"starting\");\n setAuthPrompt(false);\n\n const initial: GatewayPhase[] = PHASE_LABELS.map((label) => ({ label, status: \"pending\" }));\n setPhases(initial);\n\n let phaseIndex = 0;\n\n try {\n const result = await startGatewayForTui(\n config,\n { headless: false },\n {\n onPhase: (phase) => {\n const idx = PHASE_LABELS.indexOf(phase);\n if (idx >= 0) {\n if (phase === \"Waiting for login\") setAuthPrompt(true);\n else setAuthPrompt(false);\n phaseIndex = idx;\n setPhases((prev) =>\n prev.map((p, i) => {\n if (i < idx) return { ...p, status: \"done\" };\n if (i === idx) return { ...p, status: \"active\" };\n return p;\n }),\n );\n }\n },\n onSuccess: ({ baseUrl: _baseUrl, apiKey: _apiKey }) => {\n setAuthPrompt(false);\n setPhases((prev) => prev.map((p) => ({ ...p, status: \"done\" })));\n },\n onError: (_err) => {\n setPhases((prev) =>\n prev.map((p, i) => {\n if (i === phaseIndex) return { ...p, status: \"error\" };\n if (i < phaseIndex) return { ...p, status: \"done\" };\n return p;\n }),\n );\n },\n },\n );\n setServerHandle(result.serverHandle);\n setChromeState(result.chromeState);\n setTokenRefresher(result.tokenRefresher);\n setGatewayStatus(\"running\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setGatewayError(message);\n setGatewayStatus(\"error\");\n } finally {\n setStarting(false);\n }\n }, [starting, stopping, config, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher]);\n\n const handleStop = useCallback(async () => {\n if (stopping || starting || !serverHandle || !chromeState || !tokenRefresher) return;\n setStopping(true);\n try {\n await stopGateway(serverHandle, chromeState, tokenRefresher);\n setServerHandle(null);\n setChromeState(null);\n setTokenRefresher(null);\n setGatewayStatus(\"stopped\");\n setGatewayError(null);\n setPhases([]);\n } finally {\n setStopping(false);\n }\n }, [stopping, starting, serverHandle, chromeState, tokenRefresher, setGatewayStatus, setGatewayError, setServerHandle, setChromeState, setTokenRefresher]);\n\n useInput((_input, key) => {\n if (key.return) {\n if (gatewayStatus === \"running\") {\n void handleStop();\n } else if (gatewayStatus !== \"starting\") {\n void handleStart();\n }\n } else if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n const isRunning = gatewayStatus === \"running\";\n const isStopped = gatewayStatus === \"stopped\" || gatewayStatus === \"error\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Gateway Control</Text>\n </Box>\n\n {/* Action button */}\n <Box marginBottom={1}>\n {starting ? (\n <Text color=\"cyan\">Starting gateway…</Text>\n ) : stopping ? (\n <Text color=\"yellow\">Stopping gateway…</Text>\n ) : isRunning ? (\n <Text>\n <Text color=\"green\">● Running</Text>\n {\" \"}\n <Text dimColor>[Enter] Stop</Text>\n </Text>\n ) : (\n <Text>\n <Text color={gatewayStatus === \"error\" ? \"red\" : \"gray\"}>\n ● {gatewayStatus === \"error\" ? \"Error\" : \"Stopped\"}\n </Text>\n {\" \"}\n <Text dimColor>[Enter] {gatewayStatus === \"error\" ? \"Retry\" : \"Start\"}</Text>\n </Text>\n )}\n </Box>\n\n {/* Phase indicators */}\n {phases.length > 0 && (\n <Box flexDirection=\"column\" marginBottom={1}>\n {phases.map((phase) => (\n <Box key={phase.label}>\n <Text color={phaseColor(phase.status)}>\n {phaseIcon(phase.status)} {phase.label}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n\n {/* Auth prompt */}\n {authPrompt && (\n <Box marginBottom={1} borderStyle=\"round\" borderColor=\"yellow\" padding={1}>\n <Text color=\"yellow\">Please log in via the Chrome window</Text>\n </Box>\n )}\n\n {/* Error message */}\n {gatewayStatus === \"error\" && gatewayError && (\n <Box marginBottom={1}>\n <Text color=\"red\">Error: {gatewayError}</Text>\n </Box>\n )}\n\n {/* Connected info */}\n {isRunning && (\n <Box>\n <Text color=\"green\">Gateway running on http://127.0.0.1:{config.port}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n {isStopped && <Text dimColor>[b/Esc] Back to menu</Text>}\n {isRunning && <Text dimColor>[b/Esc] Back (gateway keeps running)</Text>}\n </Box>\n </Box>\n );\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { MODEL_ENDPOINT_MAP } from \"../../infra/types.js\";\nimport { updateConfig } from \"../../infra/config.js\";\nimport { wrapIndex } from \"../utils/navigation.js\";\n\nconst MODELS = Object.keys(MODEL_ENDPOINT_MAP);\n\ninterface ModelSelectorProps {\n activeModel: string;\n onSelect: (model: string) => void;\n onBack: () => void;\n}\n\nexport function ModelSelector({ activeModel, onSelect, onBack }: ModelSelectorProps): React.ReactElement {\n const [focusedIndex, setFocusedIndex] = useState(() => {\n const idx = MODELS.indexOf(activeModel);\n return idx >= 0 ? idx : 0;\n });\n const [confirmationModel, setConfirmationModel] = useState<string | null>(null);\n\n useEffect(() => {\n if (confirmationModel !== null) {\n const timer = setTimeout(() => setConfirmationModel(null), 1500);\n return () => clearTimeout(timer);\n }\n }, [confirmationModel]);\n\n useInput((input, key) => {\n if (key.downArrow) {\n setFocusedIndex((i) => wrapIndex(i, 1, MODELS.length));\n } else if (key.upArrow) {\n setFocusedIndex((i) => wrapIndex(i, -1, MODELS.length));\n } else if (key.return) {\n const selected = MODELS[focusedIndex];\n updateConfig({ defaultModel: selected }).catch(() => {});\n onSelect(selected);\n setConfirmationModel(selected);\n } else if (input === \"b\" || key.escape) {\n onBack();\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Select Model</Text>\n </Box>\n <Box flexDirection=\"column\">\n {MODELS.map((model, index) => {\n const isFocused = index === focusedIndex;\n const isActive = model === activeModel;\n return (\n <Box key={model}>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n <Text color={isActive ? \"green\" : \"gray\"}>{isActive ? \"●\" : \"○\"}</Text>\n {\" \"}\n {model}\n </Text>\n </Box>\n );\n })}\n </Box>\n {confirmationModel !== null && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ Selected: {confirmationModel}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\n\ninterface ChatMessage {\n role: \"user\" | \"assistant\" | \"error\";\n content: string;\n}\n\nexport function ChatPanel(): React.ReactElement {\n const { state, navigate } = useAppContext();\n const { gatewayStatus, config, activeModel } = state;\n const gatewayRunning = gatewayStatus === \"running\";\n\n const [input, setInput] = useState(\"\");\n const [loading, setLoading] = useState(false);\n const [lastMessage, setLastMessage] = useState<ChatMessage | null>(null);\n const [lastUserInput, setLastUserInput] = useState<string | null>(null);\n\n const handleSubmit = async (value: string) => {\n if (!gatewayRunning) {\n navigate(\"gateway\");\n return;\n }\n const trimmed = value.trim();\n if (!trimmed || loading) return;\n\n setLastUserInput(trimmed);\n setLastMessage(null);\n setLoading(true);\n setInput(\"\");\n\n const port = config.port;\n const apiKey = config.auth.token ?? null;\n\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n\n let response: Response;\n try {\n response = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n },\n body: JSON.stringify({\n model: activeModel,\n messages: [{ role: \"user\", content: trimmed }],\n }),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n if (!response.ok) {\n let errMsg = `Error ${response.status}`;\n try {\n const errBody = await response.json() as { error?: { message?: string } };\n if (errBody?.error?.message) errMsg += `: ${errBody.error.message}`;\n } catch { /* ignore */ }\n setLastMessage({ role: \"error\", content: errMsg });\n return;\n }\n\n const data = await response.json() as { choices?: Array<{ message?: { content?: string | null } }> };\n const content = data?.choices?.[0]?.message?.content ?? \"\";\n setLastMessage({ role: \"assistant\", content });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n setLastMessage({ role: \"error\", content: \"Request timed out\" });\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n setLastMessage({ role: \"error\", content: `Connection failed: ${msg}` });\n }\n } finally {\n setLoading(false);\n }\n };\n\n useInput((_input, key) => {\n if (!gatewayRunning) {\n if (key.return) navigate(\"gateway\");\n else if (_input === \"b\" || key.escape) navigate(\"menu\");\n return;\n }\n\n if (key.escape) {\n if (!loading) navigate(\"menu\");\n return;\n }\n\n if (key.return) {\n void handleSubmit(input);\n return;\n }\n\n if (loading) return;\n\n if (key.backspace || key.delete) {\n setInput((prev) => prev.slice(0, -1));\n return;\n }\n\n if (_input && !key.ctrl && !key.meta) {\n setInput((prev) => prev + _input);\n }\n });\n\n if (!gatewayRunning) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Chat</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"yellow\">Gateway is not running. Press Enter to start it.</Text>\n </Box>\n <Box>\n <Text dimColor>[b/Esc] Back</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Chat</Text>\n <Text dimColor> Model: {activeModel}</Text>\n </Box>\n\n {/* Response area */}\n <Box flexDirection=\"column\" marginBottom={1} minHeight={6}>\n {lastUserInput && (\n <Box marginBottom={1}>\n <Text color=\"cyan\">You: {lastUserInput}</Text>\n </Box>\n )}\n {loading && (\n <Box>\n <Text color=\"yellow\">Thinking…</Text>\n </Box>\n )}\n {!loading && lastMessage?.role === \"assistant\" && (\n <Box>\n <Text color=\"green\">Assistant: {lastMessage.content}</Text>\n </Box>\n )}\n {!loading && lastMessage?.role === \"error\" && (\n <Box>\n <Text color=\"red\">{lastMessage.content}</Text>\n </Box>\n )}\n </Box>\n\n {/* Input area */}\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1}>\n <Text>\n {input.length > 0 ? input : <Text dimColor>Type a message and press Enter…</Text>}\n <Text color=\"cyan\">█</Text>\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>[Enter] Send [b/Esc] Back</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { copyToClipboard } from \"../utils/clipboard.js\";\n\nexport function InfoPanel(): React.ReactElement {\n const { state, navigate } = useAppContext();\n const { gatewayStatus, config } = state;\n const gatewayRunning = gatewayStatus === \"running\";\n\n const port = config.port;\n const apiKey = config.auth.token ?? null;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n const [flash, setFlash] = useState<string | null>(null);\n const [clipboardFailed, setClipboardFailed] = useState<{ url?: boolean; key?: boolean }>({});\n\n useEffect(() => {\n if (flash !== null) {\n const timer = setTimeout(() => setFlash(null), 1500);\n return () => clearTimeout(timer);\n }\n }, [flash]);\n\n useInput((_input, key) => {\n if (_input === \"c\") {\n copyToClipboard(baseUrl).then((ok) => {\n if (ok) {\n setFlash(\"Copied URL!\");\n setClipboardFailed((prev) => ({ ...prev, url: false }));\n } else {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, url: true }));\n }\n }).catch(() => {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, url: true }));\n });\n return;\n }\n\n if (_input === \"k\") {\n if (!apiKey) return;\n copyToClipboard(apiKey).then((ok) => {\n if (ok) {\n setFlash(\"Copied API key!\");\n setClipboardFailed((prev) => ({ ...prev, key: false }));\n } else {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, key: true }));\n }\n }).catch(() => {\n setFlash(\"Clipboard unavailable\");\n setClipboardFailed((prev) => ({ ...prev, key: true }));\n });\n return;\n }\n\n if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Server Info</Text>\n </Box>\n\n {!gatewayRunning && (\n <Box marginBottom={1}>\n <Text color=\"red\">● Gateway not running</Text>\n </Box>\n )}\n\n <Box borderStyle=\"round\" borderColor={gatewayRunning ? \"green\" : \"gray\"} padding={1} flexDirection=\"column\" marginBottom={1}>\n <Box marginBottom={1}>\n <Text bold>Base URL: </Text>\n <Text color={gatewayRunning ? \"green\" : \"gray\"}>{baseUrl}</Text>\n </Box>\n {clipboardFailed.url && (\n <Box marginBottom={1}>\n <Box borderStyle=\"single\" borderColor=\"yellow\" paddingX={1}>\n <Text color=\"yellow\">{baseUrl}</Text>\n </Box>\n </Box>\n )}\n <Box>\n <Text bold>API Key: </Text>\n {apiKey ? (\n <Text color={gatewayRunning ? \"cyan\" : \"gray\"}>{apiKey}</Text>\n ) : (\n <Text dimColor>no auth</Text>\n )}\n </Box>\n {clipboardFailed.key && apiKey && (\n <Box marginTop={1}>\n <Box borderStyle=\"single\" borderColor=\"yellow\" paddingX={1}>\n <Text color=\"yellow\">{apiKey}</Text>\n </Box>\n </Box>\n )}\n </Box>\n\n {flash && (\n <Box marginBottom={1}>\n <Text color={flash.startsWith(\"Copied\") ? \"green\" : \"yellow\"}>{flash}</Text>\n </Box>\n )}\n\n <Box>\n <Text dimColor>[c] Copy URL {apiKey ? \"[k] Copy Key \" : \"\"}[b/Esc] Back</Text>\n </Box>\n </Box>\n );\n}\n","import { spawn } from \"node:child_process\";\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n let command: string;\n let args: string[];\n\n switch (process.platform) {\n case \"darwin\":\n command = \"pbcopy\";\n args = [];\n break;\n case \"linux\":\n command = \"xclip\";\n args = [\"-selection\", \"clipboard\"];\n break;\n case \"win32\":\n command = \"clip\";\n args = [];\n break;\n default:\n return false;\n }\n\n return new Promise((resolve) => {\n let proc: ReturnType<typeof spawn>;\n try {\n proc = spawn(command, args, { stdio: [\"pipe\", \"ignore\", \"ignore\"] });\n } catch {\n resolve(false);\n return;\n }\n\n proc.on(\"error\", () => resolve(false));\n proc.on(\"close\", (code) => resolve(code === 0));\n\n try {\n proc.stdin?.write(text);\n proc.stdin?.end();\n } catch {\n resolve(false);\n }\n });\n}\n","import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useAppContext } from \"../AppContext.js\";\nimport { updateConfig, validateConfig } from \"../../infra/config.js\";\n\ninterface SettingField {\n key: string;\n label: string;\n getValue: (config: ReturnType<typeof getConfigSnapshot>) => string;\n applyValue: (config: ReturnType<typeof getConfigSnapshot>, raw: string) => Record<string, unknown>;\n validate: (raw: string) => string | null;\n}\n\ninterface ConfigSnapshot {\n port: number;\n cdpUrl: string;\n defaultModel: string;\n authMode: string;\n}\n\nfunction getConfigSnapshot(config: { port: number; cdpUrl: string; defaultModel: string; auth: { mode: string } }): ConfigSnapshot {\n return {\n port: config.port,\n cdpUrl: config.cdpUrl,\n defaultModel: config.defaultModel,\n authMode: config.auth.mode,\n };\n}\n\nconst FIELDS: SettingField[] = [\n {\n key: \"port\",\n label: \"Port\",\n getValue: (s) => String(s.port),\n applyValue: (_s, raw) => ({ port: parseInt(raw, 10) }),\n validate: (raw) => {\n const n = parseInt(raw, 10);\n if (isNaN(n) || n < 1 || n > 65535) return \"Port must be between 1 and 65535\";\n return null;\n },\n },\n {\n key: \"cdpUrl\",\n label: \"CDP URL\",\n getValue: (s) => s.cdpUrl,\n applyValue: (_s, raw) => ({ cdpUrl: raw }),\n validate: (raw) => {\n if (!/^https?:\\/\\//.test(raw)) return \"CDP URL must start with http:// or https://\";\n return null;\n },\n },\n {\n key: \"defaultModel\",\n label: \"Default Model\",\n getValue: (s) => s.defaultModel,\n applyValue: (_s, raw) => ({ defaultModel: raw }),\n validate: (raw) => {\n if (!raw.trim()) return \"Model must be a non-empty string\";\n return null;\n },\n },\n {\n key: \"authMode\",\n label: \"Auth Mode\",\n getValue: (s) => s.authMode,\n applyValue: (_s, raw) => ({ auth: { mode: raw } }),\n validate: (raw) => {\n if (raw !== \"none\" && raw !== \"bearer\" && raw !== \"basic\")\n return 'Auth mode must be \"none\", \"bearer\", or \"basic\"';\n return null;\n },\n },\n];\n\nexport function SettingsPanel(): React.ReactElement {\n const { state, navigate, setConfig } = useAppContext();\n const { config } = state;\n\n const snapshot = getConfigSnapshot(config);\n\n const [focusedField, setFocusedField] = useState(0);\n const [editingField, setEditingField] = useState<number | null>(null);\n const [editValue, setEditValue] = useState(\"\");\n const [fieldError, setFieldError] = useState<string | null>(null);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [savedField, setSavedField] = useState<string | null>(null);\n\n const confirmEdit = async () => {\n if (editingField === null) return;\n const field = FIELDS[editingField];\n\n const validErr = field.validate(editValue);\n if (validErr) {\n setFieldError(validErr);\n return;\n }\n\n const partial = field.applyValue(snapshot, editValue);\n\n try {\n const merged = { ...config, ...partial } as Record<string, unknown>;\n if (\"auth\" in partial && typeof partial.auth === \"object\") {\n merged.auth = { ...config.auth, ...(partial.auth as Record<string, unknown>) };\n }\n validateConfig(merged);\n await updateConfig(partial as Parameters<typeof updateConfig>[0]);\n\n const updatedConfig = { ...config, ...partial } as typeof config;\n if (\"auth\" in partial && typeof partial.auth === \"object\") {\n updatedConfig.auth = { ...config.auth, ...(partial.auth as { mode?: \"none\" | \"bearer\" | \"basic\"; token?: string | null }) };\n }\n setConfig(updatedConfig);\n setSavedField(field.key);\n setTimeout(() => setSavedField(null), 1500);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n setSaveError(msg);\n setTimeout(() => setSaveError(null), 3000);\n return;\n }\n\n setEditingField(null);\n setEditValue(\"\");\n setFieldError(null);\n };\n\n useInput((_input, key) => {\n if (editingField !== null) {\n if (key.escape) {\n setEditingField(null);\n setEditValue(\"\");\n setFieldError(null);\n return;\n }\n if (key.return) {\n void confirmEdit();\n return;\n }\n if (key.backspace || key.delete) {\n setEditValue((prev) => prev.slice(0, -1));\n setFieldError(null);\n return;\n }\n if (_input && !key.ctrl && !key.meta) {\n setEditValue((prev) => prev + _input);\n setFieldError(null);\n return;\n }\n return;\n }\n\n if (key.downArrow) {\n setFocusedField((i) => (i + 1) % FIELDS.length);\n return;\n }\n if (key.upArrow) {\n setFocusedField((i) => (i - 1 + FIELDS.length) % FIELDS.length);\n return;\n }\n if (key.return) {\n const field = FIELDS[focusedField];\n setEditValue(field.getValue(snapshot));\n setEditingField(focusedField);\n setFieldError(null);\n return;\n }\n if (_input === \"b\" || key.escape) {\n navigate(\"menu\");\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>Settings</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginBottom={1}>\n {FIELDS.map((field, index) => {\n const isFocused = index === focusedField;\n const isEditing = editingField === index;\n const currentValue = field.getValue(snapshot);\n const isSaved = savedField === field.key;\n\n return (\n <Box key={field.key} flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color={isFocused ? \"cyan\" : undefined} bold={isFocused}>\n {isFocused ? \"> \" : \" \"}\n <Text bold>{field.label}: </Text>\n {isEditing ? (\n <Text color=\"cyan\">\n {editValue}\n <Text color=\"cyan\">█</Text>\n </Text>\n ) : (\n <Text color={isSaved ? \"green\" : undefined}>\n {currentValue}\n {isSaved ? \" ✓\" : \"\"}\n </Text>\n )}\n </Text>\n </Box>\n {isEditing && fieldError && (\n <Box marginLeft={4}>\n <Text color=\"red\">{fieldError}</Text>\n </Box>\n )}\n </Box>\n );\n })}\n </Box>\n\n {saveError && (\n <Box marginBottom={1}>\n <Text color=\"red\">{saveError}</Text>\n </Box>\n )}\n\n <Box>\n {editingField !== null ? (\n <Text dimColor>[Enter] Confirm [Esc] Cancel</Text>\n ) : (\n <Text dimColor>[↑↓] Navigate [Enter] Edit [b/Esc] Back</Text>\n )}\n </Box>\n </Box>\n );\n}\n"],"mappings":"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","names":["React","useState","useRef","useEffect","Box","useInput","jsx","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","useCallback","Box","Text","useInput","jsx","jsxs","useState","useCallback","useInput","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","useEffect","useInput","Box","Text","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","useEffect","Box","Text","useInput","jsx","jsxs","useState","useEffect","useInput","Box","Text","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","jsx","jsxs","useState","useRef","useEffect","useInput","Box","React"]}
|