clawfire 0.6.12 → 0.6.13
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 +1 -1
- package/dist/{dev-server-BX2DLQQB.js → dev-server-O6MKAB6R.js} +11 -2
- package/dist/dev.cjs +11 -2
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +11 -2
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
package/dist/dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dev.ts","../src/dev/dev-server.ts","../src/core/schema.ts","../src/core/errors.ts","../src/core/logger.ts","../src/firebase/auth.ts","../src/routing/router.ts","../src/routing/discover.ts","../src/playground/html.ts","../src/dev/watcher.ts","../src/dev/page-compiler.ts","../src/dev/env-manager.ts","../src/dev/firebase-status.ts","../src/dev/dashboard-html.ts","../src/dev/firebase-setup.ts"],"sourcesContent":["/**\n * clawfire/dev — Development Server with Hot Reload\n *\n * @example\n * ```ts\n * import { startDevServer } from \"clawfire/dev\";\n *\n * startDevServer({ port: 3456 });\n * ```\n */\nexport { DevServer, startDevServer, type DevServerOptions } from \"./dev/index.js\";\nexport { FileWatcher, type WatchEvent, type WatchEventType } from \"./dev/index.js\";\n","/**\n * Clawfire Development Server\n *\n * Two-port architecture:\n * - Frontend Server (port 3000): pages, public/ serving, HMR, API proxy, SPA fallback\n * - API Server (port 3456): API routes, Playground, SSE\n */\nimport http from \"node:http\";\nimport { resolve, join, relative, extname } from \"node:path\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport type { APIContract } from \"../core/schema.js\";\nimport { ClawfireRouter, createRouter, type RouterOptions } from \"../routing/router.js\";\nimport { discoverRoutes } from \"../routing/discover.js\";\nimport { generatePlaygroundHtml } from \"../playground/html.js\";\nimport { FileWatcher, type WatchEvent } from \"./watcher.js\";\nimport { PageCompiler } from \"./page-compiler.js\";\nimport { logger } from \"../core/logger.js\";\nimport { EnvManager } from \"./env-manager.js\";\nimport { checkFirebaseStatus, clearFirebaseStatusCache, fetchFirebaseSdkConfig } from \"./firebase-status.js\";\nimport { generateDashboardHtml } from \"./dashboard-html.js\";\nimport { FirebaseSetup } from \"./firebase-setup.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface DevServerOptions {\n /** Project root path */\n projectDir?: string;\n /** Frontend server port (default: 3000) */\n port?: number;\n /** API server port (default: 3456) */\n apiPort?: number;\n /** Router options */\n routerOptions?: Partial<RouterOptions>;\n /** Hot reload enabled (default: true) */\n hotReload?: boolean;\n /** Change detection debounce ms (default: 150) */\n debounceMs?: number;\n /** Callback to manually register routes (instead of file-based) */\n onSetupRoutes?: (router: ClawfireRouter) => void | Promise<void>;\n}\n\ninterface SSEClient {\n id: number;\n res: http.ServerResponse;\n}\n\n// ─── MIME Types ──────────────────────────────────────────────────────\n\nconst MIME_TYPES: Record<string, string> = {\n html: \"text/html; charset=utf-8\",\n css: \"text/css; charset=utf-8\",\n js: \"application/javascript; charset=utf-8\",\n mjs: \"application/javascript; charset=utf-8\",\n json: \"application/json; charset=utf-8\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n webp: \"image/webp\",\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n eot: \"application/vnd.ms-fontobject\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n pdf: \"application/pdf\",\n txt: \"text/plain; charset=utf-8\",\n xml: \"application/xml; charset=utf-8\",\n};\n\n/** File extensions treated as non-HTML static assets */\nconst STATIC_EXTENSIONS = new Set([\n \".css\", \".js\", \".mjs\", \".json\", \".png\", \".jpg\", \".jpeg\", \".gif\", \".svg\",\n \".ico\", \".webp\", \".woff\", \".woff2\", \".ttf\", \".eot\", \".mp4\", \".webm\",\n \".mp3\", \".wav\", \".pdf\", \".txt\", \".xml\", \".map\",\n]);\n\n// ─── HMR Script ─────────────────────────────────────────────────────\n\nfunction generateHmrScript(port: number): string {\n return `\n<script data-clawfire-hmr>\n(function() {\n var dot, status;\n function createBanner() {\n var banner = document.createElement('div');\n banner.id = 'clawfire-dev-banner';\n banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;padding:6px 16px;background:#1a1a2e;color:#f97316;font-size:12px;font-family:monospace;z-index:99999;display:flex;align-items:center;gap:8px;border-top:1px solid #2a2a2a;';\n banner.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;\" id=\"clawfire-dot\"></span><span>Clawfire Dev</span><span style=\"color:#666;margin-left:auto;\" id=\"clawfire-status\">Connected</span>';\n document.body.appendChild(banner);\n dot = document.getElementById('clawfire-dot');\n status = document.getElementById('clawfire-status');\n }\n\n function refreshCss() {\n var links = document.querySelectorAll('link[rel=\"stylesheet\"]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (href) {\n var url = new URL(href, location.href);\n url.searchParams.set('_hmr', Date.now().toString());\n links[i].setAttribute('href', url.toString());\n }\n }\n var styles = document.querySelectorAll('style[data-href]');\n for (var j = 0; j < styles.length; j++) {\n var dataHref = styles[j].getAttribute('data-href');\n if (dataHref) {\n fetch(dataHref + '?_hmr=' + Date.now())\n .then(function(r) { return r.text(); })\n .then(function(css) { styles[j].textContent = css; })\n .catch(function() {});\n }\n }\n if (status) status.textContent = 'CSS updated';\n setTimeout(function() { if (status) status.textContent = 'Connected'; }, 1500);\n }\n\n var reconnectTimer;\n function connect() {\n var es = new EventSource('http://localhost:${port}/__dev/events');\n es.onopen = function() {\n if (!dot) createBanner();\n dot.style.background = '#22c55e';\n status.textContent = 'Connected';\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n };\n es.onmessage = function(e) {\n try {\n var data = JSON.parse(e.data);\n if (data.type === 'connected') return;\n if (data.type === 'css-change') {\n refreshCss();\n return;\n }\n if (data.type === 'error') {\n if (dot) dot.style.background = '#ef4444';\n if (status) status.textContent = 'Error: ' + (data.message || 'reload failed');\n return;\n }\n // frontend-change, route-change, page-change, component-change → full reload\n if (dot) dot.style.background = '#eab308';\n if (status) status.textContent = 'Reloading...';\n setTimeout(function() { location.reload(); }, 300);\n } catch(err) {}\n };\n es.onerror = function() {\n es.close();\n if (dot) dot.style.background = '#ef4444';\n if (status) status.textContent = 'Disconnected';\n reconnectTimer = setTimeout(connect, 2000);\n };\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', function() { connect(); });\n } else {\n connect();\n }\n})();\n</script>`;\n}\n\n// ─── Client-Side Router Script ──────────────────────────────────────\n\nfunction generateRouterScript(): string {\n return `\n<script data-clawfire-router>\n(function() {\n function updateActiveNav() {\n var path = location.pathname;\n var links = document.querySelectorAll('#nav-links a[href]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (!href || href.startsWith('http')) continue;\n var isActive = (path === '/' && href === '/') || (href !== '/' && path.startsWith(href));\n links[i].setAttribute('data-active', isActive ? 'true' : 'false');\n }\n }\n\n function navigate(url) {\n var target = new URL(url, location.href);\n // Only handle same-origin, non-hash navigation\n if (target.origin !== location.origin) return false;\n if (target.pathname === location.pathname && target.hash) return false;\n\n fetch(target.pathname, {\n headers: { 'X-Clawfire-Partial': 'true' }\n })\n .then(function(res) {\n if (!res.ok) throw new Error('Page not found');\n return res.json();\n })\n .then(function(data) {\n // Update page content\n var container = document.getElementById('clawfire-page');\n if (container) {\n container.innerHTML = data.html;\n // Execute scripts in new content\n var scripts = container.querySelectorAll('script');\n for (var i = 0; i < scripts.length; i++) {\n var newScript = document.createElement('script');\n if (scripts[i].src) {\n newScript.src = scripts[i].src;\n } else {\n newScript.textContent = scripts[i].textContent;\n }\n scripts[i].parentNode.replaceChild(newScript, scripts[i]);\n }\n }\n // Update title\n if (data.meta && data.meta.title) {\n document.title = data.meta.title;\n }\n // Update URL\n history.pushState(null, '', target.pathname);\n // Update nav active state\n updateActiveNav();\n // Dispatch event for page scripts\n document.dispatchEvent(new CustomEvent('clawfire:navigate', { detail: { path: data.path } }));\n // Scroll to top\n window.scrollTo(0, 0);\n })\n .catch(function(err) {\n // Fallback: full page navigation\n location.href = url;\n });\n\n return true;\n }\n\n // Intercept link clicks\n document.addEventListener('click', function(e) {\n var anchor = e.target.closest ? e.target.closest('a[href]') : null;\n if (!anchor) return;\n var href = anchor.getAttribute('href');\n if (!href) return;\n // Skip external links, new-tab links, modified clicks\n if (href.startsWith('http') || href.startsWith('//')) return;\n if (anchor.target === '_blank') return;\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;\n if (anchor.hasAttribute('download')) return;\n\n e.preventDefault();\n navigate(href);\n });\n\n // Handle back/forward\n window.addEventListener('popstate', function() {\n navigate(location.pathname);\n });\n\n // Set initial active nav state\n updateActiveNav();\n})();\n</script>`;\n}\n\n// ─── Production Router Script (for static hosting) ─────────────────\n\nfunction generateProductionRouterScript(): string {\n return `\n<script data-clawfire-router>\n(function() {\n function updateActiveNav() {\n var path = location.pathname;\n var links = document.querySelectorAll('#nav-links a[href]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (!href || href.startsWith('http')) continue;\n var isActive = (path === '/' && href === '/') || (href !== '/' && path.startsWith(href));\n links[i].setAttribute('data-active', isActive ? 'true' : 'false');\n }\n }\n\n function navigate(url) {\n var target = new URL(url, location.href);\n if (target.origin !== location.origin) return false;\n if (target.pathname === location.pathname && target.hash) return false;\n\n fetch(target.pathname)\n .then(function(res) {\n if (!res.ok) throw new Error('Page not found');\n return res.text();\n })\n .then(function(html) {\n // Parse the fetched full HTML and extract #clawfire-page content\n var parser = new DOMParser();\n var doc = parser.parseFromString(html, 'text/html');\n var newPage = doc.getElementById('clawfire-page');\n if (!newPage) throw new Error('No #clawfire-page found');\n\n var container = document.getElementById('clawfire-page');\n if (container) {\n container.innerHTML = newPage.innerHTML;\n // Execute scripts in new content\n var scripts = container.querySelectorAll('script');\n for (var i = 0; i < scripts.length; i++) {\n var newScript = document.createElement('script');\n if (scripts[i].src) {\n newScript.src = scripts[i].src;\n } else {\n newScript.textContent = scripts[i].textContent;\n }\n scripts[i].parentNode.replaceChild(newScript, scripts[i]);\n }\n }\n // Update title from fetched document\n var newTitle = doc.querySelector('title');\n if (newTitle) document.title = newTitle.textContent || '';\n // Update URL\n history.pushState(null, '', target.pathname);\n updateActiveNav();\n document.dispatchEvent(new CustomEvent('clawfire:navigate', { detail: { path: target.pathname } }));\n window.scrollTo(0, 0);\n })\n .catch(function() {\n // Fallback: full page navigation\n location.href = url;\n });\n\n return true;\n }\n\n document.addEventListener('click', function(e) {\n var anchor = e.target.closest ? e.target.closest('a[href]') : null;\n if (!anchor) return;\n var href = anchor.getAttribute('href');\n if (!href) return;\n if (href.startsWith('http') || href.startsWith('//')) return;\n if (anchor.target === '_blank') return;\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;\n if (anchor.hasAttribute('download')) return;\n\n e.preventDefault();\n navigate(href);\n });\n\n window.addEventListener('popstate', function() {\n navigate(location.pathname);\n });\n\n updateActiveNav();\n})();\n</script>`;\n}\n\n// ─── Dev Server ──────────────────────────────────────────────────────\n\nexport class DevServer {\n private frontendServer: http.Server | null = null;\n private apiServer: http.Server | null = null;\n private router: ClawfireRouter;\n private watcher: FileWatcher | null = null;\n private frontendSseClients: SSEClient[] = [];\n private apiSseClients: SSEClient[] = [];\n private sseIdCounter = 0;\n private options: Required<DevServerOptions>;\n private routesDir: string;\n private schemasDir: string;\n private publicDir: string;\n private pagesDir: string;\n private componentsDir: string;\n private playgroundHtml = \"\";\n private importCounter = 0;\n private isReloading = false;\n private pageCompiler: PageCompiler;\n private envManager: EnvManager;\n private firebaseSetup: FirebaseSetup;\n\n constructor(options: DevServerOptions = {}) {\n this.options = {\n projectDir: options.projectDir || process.cwd(),\n port: options.port || 3000,\n apiPort: options.apiPort || 3456,\n routerOptions: options.routerOptions || {},\n hotReload: options.hotReload !== false,\n debounceMs: options.debounceMs || 150,\n onSetupRoutes: options.onSetupRoutes || (() => {}),\n };\n\n this.routesDir = resolve(this.options.projectDir, \"functions/routes\");\n this.schemasDir = resolve(this.options.projectDir, \"functions/schemas\");\n this.publicDir = resolve(this.options.projectDir, \"public\");\n this.pagesDir = resolve(this.options.projectDir, \"app/pages\");\n this.componentsDir = resolve(this.options.projectDir, \"app/components\");\n this.pageCompiler = new PageCompiler(this.options.projectDir);\n this.envManager = new EnvManager(this.options.projectDir);\n this.firebaseSetup = new FirebaseSetup(this.options.projectDir);\n\n this.router = createRouter({\n cors: [\"*\"],\n rateLimit: 0,\n ...this.options.routerOptions,\n });\n }\n\n // ─── Lifecycle ──────────────────────────────────────────────────────\n\n async start(): Promise<void> {\n await this.loadRoutes();\n this.regeneratePlayground();\n\n // Create API server (port 3456)\n this.apiServer = http.createServer((req, res) => this.handleApiRequest(req, res));\n\n // Create Frontend server (port 3000)\n this.frontendServer = http.createServer((req, res) => this.handleFrontendRequest(req, res));\n\n if (this.options.hotReload) {\n this.startWatcher();\n }\n\n // Start API server first\n await new Promise<void>((resolve, reject) => {\n this.apiServer!.listen(this.options.apiPort, () => resolve());\n this.apiServer!.on(\"error\", reject);\n });\n\n // Then start frontend server\n await new Promise<void>((resolve, reject) => {\n this.frontendServer!.listen(this.options.port, () => resolve());\n this.frontendServer!.on(\"error\", reject);\n });\n\n this.printStartupBanner();\n }\n\n async stop(): Promise<void> {\n this.watcher?.close();\n this.firebaseSetup.destroy();\n\n for (const client of [...this.frontendSseClients, ...this.apiSseClients]) {\n client.res.end();\n }\n this.frontendSseClients = [];\n this.apiSseClients = [];\n this.router.destroy();\n\n if (this.apiServer) {\n await new Promise<void>((resolve) => {\n this.apiServer!.close(() => resolve());\n });\n }\n if (this.frontendServer) {\n await new Promise<void>((resolve) => {\n this.frontendServer!.close(() => resolve());\n });\n }\n }\n\n // ─── Route Loading ─────────────────────────────────────────────────\n\n private async loadRoutes(): Promise<void> {\n // Set Firebase config env var for store module (dual-mode support)\n const setupState = this.firebaseSetup.loadState();\n if (setupState.dbMode === \"firestore\") {\n const config = this.readProjectConfig();\n const firebaseConfig: Record<string, string> = {};\n for (const field of config.fields) {\n if (!field.isPlaceholder) firebaseConfig[field.key] = field.value;\n }\n if (firebaseConfig.apiKey && firebaseConfig.projectId) {\n process.env.CLAWFIRE_FIREBASE_CONFIG = JSON.stringify(firebaseConfig);\n }\n } else {\n delete process.env.CLAWFIRE_FIREBASE_CONFIG;\n }\n\n this.router.destroy();\n this.router = createRouter({\n cors: [\"*\"],\n rateLimit: 0,\n ...this.options.routerOptions,\n });\n\n if (this.options.onSetupRoutes) {\n await this.options.onSetupRoutes(this.router);\n if (this.router.getRoutes().length > 0) return;\n }\n\n if (!existsSync(this.routesDir)) return;\n\n const discovered = discoverRoutes(this.routesDir);\n for (const route of discovered) {\n try {\n const fullPath = resolve(this.routesDir, route.filePath);\n const fileUrl = pathToFileURL(fullPath).href;\n const mod = await import(`${fileUrl}?v=${++this.importCounter}`);\n const contract = mod.default as APIContract<any, any>;\n if (contract && typeof contract.handler === \"function\" && contract.input && contract.output && contract.meta) {\n this.router.register(route.apiPath, contract);\n }\n } catch (err) {\n logger.warn(`Failed to load route: ${route.filePath}`, err);\n }\n }\n }\n\n private async reloadRoutes(event: WatchEvent): Promise<void> {\n if (this.isReloading) return;\n this.isReloading = true;\n\n const relPath = relative(this.options.projectDir, event.filePath);\n const timestamp = new Date().toLocaleTimeString();\n\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[36m${relPath}\\x1b[0m changed`);\n console.log(\" Reloading routes...\");\n\n try {\n await this.loadRoutes();\n this.regeneratePlayground();\n clearFirebaseStatusCache();\n\n const routeCount = this.router.getRoutes().length;\n console.log(` \\x1b[32m✓\\x1b[0m ${routeCount} routes loaded`);\n\n // Notify API SSE clients (playground)\n this.broadcastSSE(this.apiSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n routes: routeCount,\n });\n\n // Also notify frontend SSE clients for route changes\n this.broadcastSSE(this.frontendSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n routes: routeCount,\n });\n } catch (err) {\n console.log(` \\x1b[31m✗\\x1b[0m Reload failed:`, err);\n const errorData = {\n type: \"error\",\n file: relPath,\n message: err instanceof Error ? err.message : \"Unknown error\",\n };\n this.broadcastSSE(this.apiSseClients, errorData);\n this.broadcastSSE(this.frontendSseClients, errorData);\n } finally {\n this.isReloading = false;\n }\n }\n\n // ─── Frontend Change Handler ───────────────────────────────────────\n\n private handleFrontendChange(event: WatchEvent): void {\n const relPath = relative(this.options.projectDir, event.filePath);\n const timestamp = new Date().toLocaleTimeString();\n\n if (event.type === \"css-change\") {\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[35m${relPath}\\x1b[0m CSS updated`);\n this.broadcastSSE(this.frontendSseClients, {\n type: \"css-change\",\n file: relPath,\n timestamp: event.timestamp,\n });\n } else {\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[35m${relPath}\\x1b[0m changed → reload`);\n this.broadcastSSE(this.frontendSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n });\n }\n }\n\n // ─── File Watcher ──────────────────────────────────────────────────\n\n private startWatcher(): void {\n this.watcher = new FileWatcher(this.options.debounceMs);\n\n // routes\n if (existsSync(this.routesDir)) {\n this.watcher.watchDir(this.routesDir, \"route-change\");\n }\n\n // schemas\n if (existsSync(this.schemasDir)) {\n this.watcher.watchDir(this.schemasDir, \"schema-change\");\n }\n\n // config file\n const configFile = resolve(this.options.projectDir, \"clawfire.config.ts\");\n if (existsSync(configFile)) {\n this.watcher.watchFile(configFile, \"config-change\");\n }\n\n // public/ directory (frontend hot reload)\n if (existsSync(this.publicDir)) {\n this.watcher.watchDirFrontend(this.publicDir);\n }\n\n // pages/ directory\n if (existsSync(this.pagesDir)) {\n this.watcher.watchDir(this.pagesDir, \"page-change\");\n }\n\n // components/ directory\n if (existsSync(this.componentsDir)) {\n this.watcher.watchDir(this.componentsDir, \"component-change\");\n }\n\n // API/schema/config changes → route reload\n this.watcher.on(\"route-change\", (event: WatchEvent) => this.reloadRoutes(event));\n this.watcher.on(\"schema-change\", (event: WatchEvent) => this.reloadRoutes(event));\n this.watcher.on(\"config-change\", (event: WatchEvent) => this.reloadRoutes(event));\n\n // Frontend changes → browser notification (CSS hot / full reload)\n this.watcher.on(\"frontend-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n this.watcher.on(\"css-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n\n // Page/component changes → full reload broadcast\n this.watcher.on(\"page-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n this.watcher.on(\"component-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n }\n\n // ─── SSE (Server-Sent Events) ──────────────────────────────────────\n\n private handleSSE(req: http.IncomingMessage, res: http.ServerResponse, clients: SSEClient[]): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n const clientId = ++this.sseIdCounter;\n const client: SSEClient = { id: clientId, res };\n clients.push(client);\n\n res.write(`data: ${JSON.stringify({ type: \"connected\", id: clientId })}\\n\\n`);\n\n req.on(\"close\", () => {\n const idx = clients.indexOf(client);\n if (idx !== -1) clients.splice(idx, 1);\n });\n }\n\n private broadcastSSE(clients: SSEClient[], data: Record<string, unknown>): void {\n const message = `data: ${JSON.stringify(data)}\\n\\n`;\n for (const client of clients) {\n try {\n client.res.write(message);\n } catch {\n // disconnected client\n }\n }\n }\n\n // ─── Playground ────────────────────────────────────────────────────\n\n private regeneratePlayground(): void {\n const baseHtml = generatePlaygroundHtml({\n title: \"Clawfire Dev Playground\",\n apiBaseUrl: `http://localhost:${this.options.apiPort}`,\n });\n\n const dashboardHtml = generateDashboardHtml({\n apiPort: this.options.apiPort,\n });\n\n // Tab bar + tab switching script\n const tabBar = `\n<div id=\"clawfire-tab-bar\" style=\"position:sticky;top:0;z-index:9999;background:#0a0a0a;border-bottom:1px solid #2a2a2a;padding:0 16px;display:flex;gap:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\">\n <button onclick=\"switchTab('apis')\" id=\"tab-btn-apis\" style=\"padding:10px 20px;background:transparent;border:none;border-bottom:2px solid #f97316;color:#f97316;font-size:14px;font-weight:600;cursor:pointer;\">APIs</button>\n <button onclick=\"switchTab('dashboard')\" id=\"tab-btn-dashboard\" style=\"padding:10px 20px;background:transparent;border:none;border-bottom:2px solid transparent;color:#a3a3a3;font-size:14px;font-weight:600;cursor:pointer;\">Dashboard</button>\n</div>`;\n\n const tabScript = `\n<script>\nfunction switchTab(tab) {\n var apis = document.getElementById('tab-apis');\n var dashboard = document.getElementById('tab-dashboard');\n var btnApis = document.getElementById('tab-btn-apis');\n var btnDash = document.getElementById('tab-btn-dashboard');\n\n if (tab === 'apis') {\n apis.style.display = 'block';\n dashboard.style.display = 'none';\n btnApis.style.borderBottomColor = '#f97316';\n btnApis.style.color = '#f97316';\n btnDash.style.borderBottomColor = 'transparent';\n btnDash.style.color = '#a3a3a3';\n } else {\n apis.style.display = 'none';\n dashboard.style.display = 'block';\n btnApis.style.borderBottomColor = 'transparent';\n btnApis.style.color = '#a3a3a3';\n btnDash.style.borderBottomColor = '#f97316';\n btnDash.style.color = '#f97316';\n // Lazy-load dashboard data on first click\n if (window._loadDashboard) window._loadDashboard();\n }\n // Persist active tab across reloads (auto-fill writes config → watcher reloads page)\n try { localStorage.setItem('clawfire-active-tab', tab); } catch(e) {}\n}\n// Restore saved tab on page load\n(function() {\n try {\n var saved = localStorage.getItem('clawfire-active-tab');\n if (saved === 'dashboard') {\n var fn = function() { switchTab('dashboard'); };\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', fn);\n } else {\n fn();\n }\n }\n } catch(e) {}\n})();\n</script>`;\n\n const liveReloadScript = `\n<script>\n(function() {\n var banner = document.createElement('div');\n banner.id = 'dev-banner';\n banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;padding:6px 16px;background:#1a1a2e;color:#f97316;font-size:12px;font-family:monospace;z-index:9999;display:flex;align-items:center;gap:8px;border-top:1px solid #2a2a2a;';\n banner.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;\" id=\"dev-dot\"></span><span>Clawfire Dev Server</span><span style=\"color:#666;margin-left:auto;\" id=\"dev-status\">Connected</span>';\n document.body.appendChild(banner);\n\n var reconnectTimer;\n function connect() {\n var es = new EventSource('http://localhost:${this.options.apiPort}/__dev/events');\n es.onopen = function() {\n document.getElementById('dev-dot').style.background = '#22c55e';\n document.getElementById('dev-status').textContent = 'Connected';\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n };\n es.onmessage = function(e) {\n try {\n var data = JSON.parse(e.data);\n if (data.type === 'connected') return;\n if (data.type === 'error') {\n document.getElementById('dev-dot').style.background = '#ef4444';\n document.getElementById('dev-status').textContent = 'Error: ' + (data.message || 'reload failed');\n return;\n }\n document.getElementById('dev-dot').style.background = '#eab308';\n document.getElementById('dev-status').textContent = 'Reloading...';\n setTimeout(function() { window.location.reload(); }, 300);\n } catch(err) {}\n };\n es.onerror = function() {\n es.close();\n document.getElementById('dev-dot').style.background = '#ef4444';\n document.getElementById('dev-status').textContent = 'Disconnected — reconnecting...';\n reconnectTimer = setTimeout(connect, 2000);\n };\n }\n connect();\n})();\n</script>`;\n\n // Inject: tab bar after <body>, wrap playground in tab-apis, add dashboard tab\n let html = baseHtml;\n // Insert tab bar right after <body...>\n html = html.replace(/<body[^>]*>/, (match) => `${match}\\n${tabBar}`);\n // Wrap existing content: find the first element after body and wrap everything until </body>\n // Strategy: wrap all body content in tab-apis div, then append dashboard\n const bodyOpenMatch = html.match(/<body[^>]*>/);\n if (bodyOpenMatch) {\n const bodyOpenEnd = html.indexOf(bodyOpenMatch[0]) + bodyOpenMatch[0].length;\n const tabBarEnd = html.indexOf(\"</div>\", bodyOpenEnd) + \"</div>\".length; // end of tab bar\n const bodyClose = html.lastIndexOf(\"</body>\");\n\n const beforeTabBar = html.slice(0, tabBarEnd);\n const playgroundContent = html.slice(tabBarEnd, bodyClose);\n const afterBody = html.slice(bodyClose);\n\n html = beforeTabBar +\n `\\n<div id=\"tab-apis\">${playgroundContent}</div>` +\n `\\n<div id=\"tab-dashboard\" style=\"display:none;\">${dashboardHtml}</div>` +\n `\\n${tabScript}` +\n `\\n${liveReloadScript}\\n` +\n afterBody;\n } else {\n // Fallback: just append scripts\n html = html.replace(\"</body>\", `${liveReloadScript}\\n</body>`);\n }\n\n this.playgroundHtml = html;\n }\n\n // ─── Script Injection ──────────────────────────────────────────────\n\n /**\n * Inject HMR and (optionally) client-side router scripts before </body>.\n */\n private injectScripts(html: string, includeRouter: boolean): string {\n const hmr = generateHmrScript(this.options.port);\n const router = includeRouter ? generateRouterScript() : \"\";\n const scripts = router + hmr;\n\n if (html.includes(\"</body>\")) {\n return html.replace(\"</body>\", scripts + \"\\n</body>\");\n }\n return html + scripts;\n }\n\n // ─── Static File Serving ──────────────────────────────────────────\n\n private serveStaticFile(filePath: string, res: http.ServerResponse): boolean {\n if (!existsSync(filePath)) return false;\n\n try {\n const content = readFileSync(filePath);\n const ext = extname(filePath).slice(1).toLowerCase();\n const mime = MIME_TYPES[ext] || \"application/octet-stream\";\n\n // HTML files get HMR script injected\n if (ext === \"html\") {\n const html = content.toString(\"utf-8\");\n const injected = this.injectScripts(html, false);\n res.writeHead(200, { \"Content-Type\": mime });\n res.end(injected);\n return true;\n }\n\n res.writeHead(200, { \"Content-Type\": mime });\n res.end(content);\n return true;\n } catch {\n return false;\n }\n }\n\n // ─── API Proxy ────────────────────────────────────────────────────\n\n private proxyToApiServer(req: http.IncomingMessage, res: http.ServerResponse): void {\n const proxyReq = http.request(\n {\n hostname: \"127.0.0.1\",\n port: this.options.apiPort,\n path: req.url,\n method: req.method,\n headers: {\n ...req.headers,\n host: `localhost:${this.options.apiPort}`,\n },\n },\n (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);\n proxyRes.pipe(res, { end: true });\n },\n );\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: { code: \"PROXY_ERROR\", message: \"API server unavailable\" } }));\n });\n\n req.pipe(proxyReq, { end: true });\n }\n\n // ─── Frontend Request Handler ─────────────────────────────────────\n\n private handleFrontendRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || \"/\", `http://localhost:${this.options.port}`);\n\n // 1. SSE endpoint for frontend HMR\n if (url.pathname === \"/__dev/events\") {\n this.handleSSE(req, res, this.frontendSseClients);\n return;\n }\n\n // 2. Proxy /api/* requests to API server\n if (url.pathname.startsWith(\"/api\")) {\n this.proxyToApiServer(req, res);\n return;\n }\n\n // 3. Proxy /__dev/* (except /events handled above) and /__playground to API server\n if (url.pathname.startsWith(\"/__\")) {\n this.proxyToApiServer(req, res);\n return;\n }\n\n // 4. Non-HTML file extensions → serve from public/\n const ext = extname(url.pathname);\n if (ext && STATIC_EXTENSIONS.has(ext)) {\n const filePath = resolve(this.publicDir, url.pathname.slice(1));\n if (filePath.startsWith(this.publicDir) && this.serveStaticFile(filePath, res)) {\n return;\n }\n // Static file not found\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n // 5. Page system\n if (this.pageCompiler.isActive()) {\n const isPartial = req.headers[\"x-clawfire-partial\"] === \"true\";\n const pagePath = this.pageCompiler.resolve(url.pathname);\n\n if (pagePath) {\n try {\n if (isPartial) {\n // SPA navigation — return JSON with page content only\n const partial = this.pageCompiler.compilePartial(pagePath, url.pathname);\n res.writeHead(200, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(partial));\n } else {\n // Full page render with layouts + components + script injection\n const compiled = this.pageCompiler.compile(pagePath);\n const html = this.injectScripts(compiled.html, true);\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n }\n } catch (err) {\n logger.warn(`Page compilation error: ${url.pathname}`, err);\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(`<h1>500 — Page Compilation Error</h1><pre>${err instanceof Error ? err.message : \"Unknown error\"}</pre>`);\n }\n return;\n }\n\n // 404 page\n const page404 = this.pageCompiler.resolve404();\n if (page404) {\n try {\n if (isPartial) {\n const partial = this.pageCompiler.compilePartial(page404, url.pathname);\n res.writeHead(404, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(partial));\n } else {\n const compiled = this.pageCompiler.compile(page404);\n const html = this.injectScripts(compiled.html, true);\n res.writeHead(404, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n }\n } catch {\n res.writeHead(404);\n res.end(\"Not found\");\n }\n return;\n }\n }\n\n // 6. Serve static files from public/ (for paths without extension, e.g. directory index)\n const requestedPath = url.pathname === \"/\" ? \"index.html\" : url.pathname.slice(1);\n const filePath = resolve(this.publicDir, requestedPath);\n\n // Security: prevent directory traversal\n if (!filePath.startsWith(this.publicDir)) {\n res.writeHead(403);\n res.end(\"Forbidden\");\n return;\n }\n\n if (this.serveStaticFile(filePath, res)) {\n return;\n }\n\n // 7. SPA fallback: serve index.html for unmatched routes\n const indexPath = resolve(this.publicDir, \"index.html\");\n if (existsSync(indexPath)) {\n this.serveStaticFile(indexPath, res);\n return;\n }\n\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── API Request Handler ──────────────────────────────────────────\n\n private handleApiRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || \"/\", `http://localhost:${this.options.apiPort}`);\n\n // SSE endpoint for playground\n if (url.pathname === \"/__dev/events\") {\n this.handleSSE(req, res, this.apiSseClients);\n return;\n }\n\n // Playground\n if (url.pathname === \"/\" || url.pathname === \"/__playground\") {\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(this.playgroundHtml);\n return;\n }\n\n // API requests\n if (url.pathname.startsWith(\"/api\")) {\n // CORS\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", async () => {\n let parsed = {};\n try { parsed = body ? JSON.parse(body) : {}; } catch {}\n\n await this.router.handleRequest(\n {\n method: req.method,\n path: url.pathname,\n body: parsed,\n headers: req.headers as Record<string, string>,\n ip: req.socket.remoteAddress || \"127.0.0.1\",\n },\n {\n set(h: Record<string, string>) {\n for (const [k, v] of Object.entries(h)) {\n try { res.setHeader(k, v); } catch {}\n }\n },\n status(code: number) {\n return {\n json(data: unknown) {\n res.writeHead(code, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n },\n send(data: string) { res.writeHead(code); res.end(data); },\n end() { res.writeHead(code); res.end(); },\n };\n },\n },\n );\n });\n return;\n }\n\n // Dev dashboard endpoints\n if (url.pathname.startsWith(\"/__dev/\")) {\n this.handleDevEndpoint(req, res, url);\n return;\n }\n\n // 404\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── Startup Banner ────────────────────────────────────────────────\n\n private printStartupBanner(): void {\n const routes = this.router.getRoutes();\n const watching = this.options.hotReload;\n const pagesActive = this.pageCompiler.isActive();\n\n console.log(\"\");\n console.log(\" \\x1b[1m\\x1b[33m⚡ Clawfire Dev Server\\x1b[0m\");\n console.log(\" \\x1b[2m─────────────────────────────────────\\x1b[0m\");\n console.log(` \\x1b[36mApp\\x1b[0m : http://localhost:${this.options.port}`);\n console.log(` \\x1b[36mAPI\\x1b[0m : http://localhost:${this.options.apiPort}/api/...`);\n console.log(` \\x1b[36mPlayground\\x1b[0m : http://localhost:${this.options.apiPort}`);\n console.log(` \\x1b[36mPages\\x1b[0m : ${pagesActive ? \"\\x1b[32mON\\x1b[0m (app/pages/)\" : \"\\x1b[2mOFF\\x1b[0m\"}`);\n console.log(\"\");\n console.log(` \\x1b[32mRoutes (${routes.length})\\x1b[0m:`);\n for (const route of routes) {\n const auth = route.contract.meta.auth || \"public\";\n const authColor = auth === \"public\" ? \"32\" : auth === \"authenticated\" ? \"34\" : auth === \"role\" ? \"33\" : \"31\";\n console.log(` POST /api\\x1b[1m${route.path}\\x1b[0m \\x1b[${authColor}m[${auth}]\\x1b[0m \\x1b[2m${route.contract.meta.description}\\x1b[0m`);\n }\n console.log(\"\");\n if (watching) {\n const watchDirs = [\"functions/routes/\", \"functions/schemas/\", \"public/\"];\n if (pagesActive) watchDirs.push(\"app/pages/\", \"app/components/\");\n console.log(` \\x1b[35mHot Reload\\x1b[0m : \\x1b[32mON\\x1b[0m`);\n console.log(` \\x1b[2mWatching: ${watchDirs.join(\", \")}\\x1b[0m`);\n } else {\n console.log(` \\x1b[35mHot Reload\\x1b[0m : OFF`);\n }\n console.log(`\\n \\x1b[2mPress Ctrl+C to stop\\x1b[0m\\n`);\n }\n\n // ─── Dev Dashboard Endpoints ──────────────────────────────────────\n\n private handleDevEndpoint(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n url: URL,\n ): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const sendJson = (data: unknown, status = 200) => {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n };\n\n // GET /__dev/firebase-status\n if (url.pathname === \"/__dev/firebase-status\" && req.method === \"GET\") {\n checkFirebaseStatus(this.options.projectDir)\n .then((status) => sendJson(status))\n .catch((err) => sendJson({ error: err.message }, 500));\n return;\n }\n\n // GET /__dev/config\n if (url.pathname === \"/__dev/config\" && req.method === \"GET\") {\n sendJson(this.readProjectConfig());\n return;\n }\n\n // POST /__dev/config — update config field values\n if (url.pathname === \"/__dev/config\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (data.action === \"set\" && data.key && data.value !== undefined) {\n this.updateProjectConfig(data.key, String(data.value));\n clearFirebaseStatusCache();\n sendJson({ ok: true });\n } else if (data.action === \"set-multiple\" && data.fields) {\n for (const [key, value] of Object.entries(data.fields)) {\n this.updateProjectConfig(key, String(value));\n }\n clearFirebaseStatusCache();\n sendJson({ ok: true });\n } else {\n sendJson({ error: \"Invalid action\" }, 400);\n }\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/firebase-sdk-config — auto-fill from Firebase CLI (uses saved web app ID if available)\n if (url.pathname === \"/__dev/firebase-sdk-config\" && req.method === \"GET\") {\n const savedState = this.firebaseSetup.loadState();\n fetchFirebaseSdkConfig(this.options.projectDir, savedState.webAppId)\n .then((config) => sendJson(config))\n .catch((err) => sendJson({ error: err instanceof Error ? err.message : \"Failed to fetch SDK config\" }, 500));\n return;\n }\n\n // GET /__dev/env\n if (url.pathname === \"/__dev/env\" && req.method === \"GET\") {\n try {\n sendJson(this.envManager.read());\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Read failed\" }, 500);\n }\n return;\n }\n\n // POST /__dev/env\n if (url.pathname === \"/__dev/env\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (data.action === \"set\") {\n this.envManager.set(data.key, data.value, data.description);\n sendJson({ ok: true });\n } else if (data.action === \"delete\") {\n this.envManager.delete(data.key);\n sendJson({ ok: true });\n } else {\n sendJson({ error: \"Invalid action\" }, 400);\n }\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // ─── Setup Wizard Endpoints ─────────────────────────────────────\n\n // GET /__dev/setup/status\n if (url.pathname === \"/__dev/setup/status\" && req.method === \"GET\") {\n this.firebaseSetup.getStatus()\n .then((status) => sendJson(status))\n .catch((err) => sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/install-cli\n if (url.pathname === \"/__dev/setup/install-cli\" && req.method === \"POST\") {\n this.firebaseSetup.installCli()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/login — opens a real terminal window\n if (url.pathname === \"/__dev/setup/login\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n let reauth = false;\n try {\n const data = JSON.parse(body);\n reauth = !!data.reauth;\n } catch {\n // default: not reauth\n }\n const result = this.firebaseSetup.openLoginTerminal(reauth);\n sendJson(result);\n });\n return;\n }\n\n // GET /__dev/setup/projects\n if (url.pathname === \"/__dev/setup/projects\" && req.method === \"GET\") {\n this.firebaseSetup.listProjects()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ projects: [], error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/select-project\n // After project selection, automatically: list web apps → create if needed → fetch SDK config → write to clawfire.config.ts\n if (url.pathname === \"/__dev/setup/select-project\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.projectId) {\n sendJson({ success: false, message: \"projectId is required\" }, 400);\n return;\n }\n (async () => {\n // Step 1: Select project\n const selectResult = await this.firebaseSetup.selectProject(data.projectId);\n if (!selectResult.success) {\n sendJson(selectResult);\n return;\n }\n\n const steps: string[] = [selectResult.message];\n\n try {\n // Step 2: List web apps → create one if none exist\n const { apps } = await this.firebaseSetup.listWebApps();\n let webAppId = \"\";\n if (apps.length > 0) {\n webAppId = apps[0].appId;\n this.firebaseSetup.selectWebApp(apps[0].appId, apps[0].displayName);\n steps.push(`Web app \"${apps[0].displayName}\" selected.`);\n } else {\n // Auto-create a web app named after the project\n const createResult = await this.firebaseSetup.createWebApp(data.projectId);\n if (createResult.success && createResult.appId) {\n webAppId = createResult.appId;\n steps.push(`Web app \"${data.projectId}\" created.`);\n } else {\n steps.push(`Web app creation skipped: ${createResult.message}`);\n }\n }\n\n // Step 3: Fetch SDK config and write to clawfire.config.ts\n if (webAppId) {\n try {\n const sdkConfig = await fetchFirebaseSdkConfig(this.options.projectDir, webAppId);\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(sdkConfig)) {\n if (value && typeof value === \"string\") {\n fields[key] = value;\n }\n }\n if (Object.keys(fields).length > 0) {\n for (const [key, value] of Object.entries(fields)) {\n try {\n this.updateProjectConfig(key, value);\n } catch {\n // Key might not exist in config template — skip\n }\n }\n steps.push(\"Config auto-filled in clawfire.config.ts.\");\n }\n } catch (configErr) {\n steps.push(`Config auto-fill skipped: ${configErr instanceof Error ? configErr.message : \"unknown error\"}`);\n }\n }\n } catch (autoFillErr) {\n // Auto-fill failed but project selection succeeded — still report success\n steps.push(`Auto-setup partial: ${autoFillErr instanceof Error ? autoFillErr.message : \"unknown error\"}`);\n }\n\n clearFirebaseStatusCache();\n sendJson({ success: true, message: steps.join(\" \"), steps });\n })().catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/setup/web-apps\n if (url.pathname === \"/__dev/setup/web-apps\" && req.method === \"GET\") {\n this.firebaseSetup.listWebApps()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ apps: [], error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/create-web-app\n if (url.pathname === \"/__dev/setup/create-web-app\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.displayName) {\n sendJson({ success: false, message: \"displayName is required\" }, 400);\n return;\n }\n this.firebaseSetup.createWebApp(data.displayName)\n .then((result) => {\n clearFirebaseStatusCache();\n sendJson(result);\n })\n .catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // POST /__dev/deploy/hosting\n if (url.pathname === \"/__dev/deploy/hosting\" && req.method === \"POST\") {\n (async () => {\n try {\n const steps: string[] = [];\n const functionsDir = resolve(this.options.projectDir, \"functions\");\n const hasFunctions = existsSync(functionsDir);\n\n // ── Step 1: Read Firebase config ──\n const projectConfig = this.readProjectConfig();\n const firebaseConfig: Record<string, string> = {};\n for (const field of projectConfig.fields) {\n if (!field.isPlaceholder) {\n firebaseConfig[field.key] = field.value;\n }\n }\n\n // ── Step 2: Pre-render pages into public/ ──\n if (this.pageCompiler.isActive()) {\n const configScript = Object.keys(firebaseConfig).length > 0\n ? `\\n<script>window.__CLAWFIRE_CONFIG__=${JSON.stringify(firebaseConfig)};</script>`\n : \"\";\n const routerScript = generateProductionRouterScript();\n const scriptToInject = configScript + routerScript;\n const buildResult = this.pageCompiler.buildForProduction(this.publicDir, scriptToInject);\n console.log(` \\x1b[32m✓\\x1b[0m Built ${buildResult.pages.length} pages for production`);\n steps.push(`${buildResult.pages.length} pages built`);\n if (configScript) {\n console.log(` \\x1b[32m✓\\x1b[0m Firebase config injected (projectId: ${firebaseConfig.projectId || \"?\"})`);\n steps.push(\"Firebase config injected\");\n }\n if (buildResult.errors.length > 0) {\n for (const err of buildResult.errors) {\n console.log(` \\x1b[31m✗\\x1b[0m ${err}`);\n }\n }\n }\n\n // ── Step 3: Ensure firebase.json has API rewrite + functions config ──\n const deployConfig = this.firebaseSetup.ensureDeployConfig();\n if (deployConfig.success && deployConfig.changes.length > 0) {\n for (const change of deployConfig.changes) {\n console.log(` \\x1b[32m✓\\x1b[0m firebase.json: ${change}`);\n }\n steps.push(`firebase.json updated (${deployConfig.changes.length} fixes)`);\n }\n\n // ── Step 4: Sync env to functions/.env ──\n let deployFunctions = false;\n if (hasFunctions) {\n const syncResult = this.firebaseSetup.syncEnvToFunctions(firebaseConfig);\n if (syncResult.success) {\n console.log(` \\x1b[32m✓\\x1b[0m ${syncResult.message}`);\n steps.push(`${syncResult.count} env vars synced`);\n } else {\n console.log(` \\x1b[33m⚠\\x1b[0m Env sync: ${syncResult.message}`);\n }\n\n // ── Step 5: Build functions ──\n console.log(\" Building functions...\");\n const buildResult = await this.firebaseSetup.buildFunctions();\n if (buildResult.success) {\n console.log(` \\x1b[32m✓\\x1b[0m ${buildResult.message}`);\n steps.push(\"Functions built\");\n deployFunctions = true;\n } else {\n console.log(` \\x1b[31m✗\\x1b[0m ${buildResult.message}`);\n console.log(\" \\x1b[33m⚠\\x1b[0m Deploying hosting only (functions build failed)\");\n steps.push(\"Functions build failed — hosting only\");\n }\n }\n\n // ── Step 6: Deploy ──\n const targets = deployFunctions ? \"hosting,functions\" : \"hosting\";\n console.log(` Deploying ${targets}...`);\n const result = await this.firebaseSetup.deployHosting(targets);\n clearFirebaseStatusCache();\n\n sendJson({\n ...result,\n message: result.message + (steps.length > 0 ? ` (${steps.join(\", \")})` : \"\"),\n });\n } catch (err) {\n sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500);\n }\n })();\n return;\n }\n\n // POST /__dev/enable-service\n if (url.pathname === \"/__dev/enable-service\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n const service = data.service;\n if (!service || ![\"hosting\", \"firestore\", \"storage\"].includes(service)) {\n sendJson({ success: false, message: \"Invalid service. Use: hosting, firestore, storage\" }, 400);\n return;\n }\n const result = this.firebaseSetup.enableService(service);\n clearFirebaseStatusCache();\n sendJson(result);\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/db-mode — get current database mode\n if (url.pathname === \"/__dev/db-mode\" && req.method === \"GET\") {\n const state = this.firebaseSetup.loadState();\n sendJson({ mode: state.dbMode || \"memory\" });\n return;\n }\n\n // POST /__dev/db-mode — switch database mode\n if (url.pathname === \"/__dev/db-mode\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", async () => {\n try {\n const data = JSON.parse(body);\n const mode = data.mode;\n if (mode !== \"memory\" && mode !== \"firestore\") {\n sendJson({ success: false, message: \"Invalid mode. Use: memory, firestore\" }, 400);\n return;\n }\n this.firebaseSetup.saveState({ dbMode: mode });\n // Reload routes to apply new mode (sets/clears env var)\n await this.loadRoutes();\n this.regeneratePlayground();\n sendJson({ success: true, mode, message: `Database mode switched to \"${mode}\".` });\n } catch (err) {\n sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // POST /__dev/setup/create-firestore — one-click Firestore setup\n if (url.pathname === \"/__dev/setup/create-firestore\" && req.method === \"POST\") {\n (async () => {\n try {\n // Step 1: Create Firestore database\n const createResult = await this.firebaseSetup.createFirestoreDatabase();\n if (!createResult.success) {\n sendJson(createResult);\n return;\n }\n\n // Step 2: Enable Firestore service (writes firebase.json + firestore.rules)\n const enableResult = this.firebaseSetup.enableService(\"firestore\");\n if (!enableResult.success) {\n sendJson(enableResult);\n return;\n }\n\n // Step 3: Deploy open rules for dev\n const rulesResult = await this.firebaseSetup.deployFirestoreRules(true);\n if (!rulesResult.success) {\n sendJson(rulesResult);\n return;\n }\n\n // Step 4: Set mode to firestore\n this.firebaseSetup.saveState({ dbMode: \"firestore\" });\n\n // Step 5: Reload routes (applies env var)\n await this.loadRoutes();\n this.regeneratePlayground();\n clearFirebaseStatusCache();\n\n sendJson({\n success: true,\n message: \"Firestore connected! Database created, rules deployed, mode switched to Firestore.\",\n });\n } catch (err) {\n sendJson({\n success: false,\n message: err instanceof Error ? err.message : \"Failed to setup Firestore\",\n }, 500);\n }\n })();\n return;\n }\n\n // POST /__dev/setup/select-web-app\n if (url.pathname === \"/__dev/setup/select-web-app\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.appId) {\n sendJson({ success: false, message: \"appId is required\" }, 400);\n return;\n }\n const result = this.firebaseSetup.selectWebApp(data.appId, data.displayName || \"\");\n clearFirebaseStatusCache();\n sendJson(result);\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // Unknown /__dev/ endpoint\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── Config Reader ────────────────────────────────────────────────\n\n private readProjectConfig(): { fields: Array<{ key: string; value: string; isPlaceholder: boolean }> } {\n const configPath = resolve(this.options.projectDir, \"clawfire.config.ts\");\n const fields: Array<{ key: string; value: string; isPlaceholder: boolean }> = [];\n\n if (!existsSync(configPath)) {\n return { fields };\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n\n // Parse key-value pairs from the config file\n // Match patterns like: apiKey: \"value\", authDomain: \"value\", etc.\n const kvPattern = /(\\w+)\\s*:\\s*[\"'`]([^\"'`]*)[\"'`]/g;\n let match: RegExpExecArray | null;\n\n while ((match = kvPattern.exec(content)) !== null) {\n const key = match[1];\n const value = match[2];\n const isPlaceholder = /^YOUR_/i.test(value) || /^CHANGE_ME$/i.test(value) || /^TODO$/i.test(value);\n fields.push({ key, value, isPlaceholder });\n }\n } catch {\n // ignore read errors\n }\n\n return { fields };\n }\n\n /** Update a single key's value in clawfire.config.ts */\n private updateProjectConfig(key: string, value: string): void {\n const configPath = resolve(this.options.projectDir, \"clawfire.config.ts\");\n if (!existsSync(configPath)) {\n throw new Error(\"clawfire.config.ts not found\");\n }\n\n let content = readFileSync(configPath, \"utf-8\");\n\n // Match key: \"value\" or key: 'value' patterns\n const pattern = new RegExp(\n `(${this.escapeRegex(key)}\\\\s*:\\\\s*)[\"'\\`][^\"'\\`]*[\"'\\`]`,\n );\n\n if (!pattern.test(content)) {\n throw new Error(`Key \"${key}\" not found in config`);\n }\n\n content = content.replace(pattern, `$1\"${value.replace(/\"/g, '\\\\\"')}\"`);\n writeFileSync(configPath, content, \"utf-8\");\n }\n\n private escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n}\n\n/**\n * Create and start dev server (one-line helper)\n */\nexport async function startDevServer(options?: DevServerOptions): Promise<DevServer> {\n const server = new DevServer(options);\n await server.start();\n\n const shutdown = async () => {\n console.log(\"\\n Shutting down...\");\n await server.stop();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return server;\n}\n","/**\n * Clawfire Schema & Contract System\n *\n * 모든 API는 input/output schema + meta + handler로 구성된 \"계약(Contract)\"으로 정의됩니다.\n * Zod를 사용하여 타입 안전성과 런타임 검증을 동시에 보장합니다.\n */\nimport { z, type ZodType, type ZodObject, type ZodRawShape } from \"zod\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\n/** 인증 컨텍스트 */\nexport interface AuthContext {\n uid: string;\n email?: string;\n emailVerified?: boolean;\n role?: string;\n customClaims?: Record<string, unknown>;\n token?: string;\n}\n\n/** API 핸들러에 전달되는 컨텍스트 */\nexport interface HandlerContext {\n auth: AuthContext | null;\n /** 재인증 여부 (민감 작업용) */\n reauthenticated?: boolean;\n /** 원본 요청 헤더 */\n headers?: Record<string, string>;\n /** 요청 IP */\n ip?: string;\n}\n\n/** 권한 수준 */\nexport type AuthLevel = \"public\" | \"authenticated\" | \"role\" | \"reauth\";\n\n/** API 메타데이터 */\nexport interface APIMeta {\n /** API 설명 (AI/Playground용) */\n description: string;\n /** 태그 (그룹화용) */\n tags?: string[];\n /** 인증 요구 수준 */\n auth?: AuthLevel;\n /** 필요 역할 (auth가 'role'일 때) */\n roles?: string[];\n /** 재인증 필요 여부 */\n reauth?: boolean;\n /** Rate limit (초당 요청 수) */\n rateLimit?: number;\n /** 비활성화 여부 */\n deprecated?: boolean;\n /** 예시 입력값 */\n exampleInput?: unknown;\n /** 예시 출력값 */\n exampleOutput?: unknown;\n}\n\n/** API 계약 정의 */\nexport interface APIContract<\n TInput extends ZodType = ZodType,\n TOutput extends ZodType = ZodType,\n> {\n /** 입력 스키마 */\n input: TInput;\n /** 출력 스키마 */\n output: TOutput;\n /** 메타데이터 */\n meta: APIMeta;\n /** 핸들러 함수 */\n handler: (\n input: z.infer<TInput>,\n ctx: HandlerContext,\n ) => Promise<z.infer<TOutput>>;\n}\n\n/** 모델 필드 정의 */\nexport interface ModelField {\n type: \"string\" | \"number\" | \"boolean\" | \"timestamp\" | \"array\" | \"map\" | \"reference\" | \"geopoint\";\n required?: boolean;\n description?: string;\n default?: unknown;\n /** 배열 아이템 타입 */\n items?: ModelField;\n /** 맵 값 타입 */\n values?: ModelField;\n /** 참조 대상 컬렉션 */\n ref?: string;\n /** enum 값 리스트 */\n enum?: string[];\n}\n\n/** 모델 정의 */\nexport interface ModelDefinition {\n /** 컬렉션 이름 */\n collection: string;\n /** 필드 정의 */\n fields: Record<string, ModelField>;\n /** 서브컬렉션 */\n subcollections?: Record<string, ModelDefinition>;\n /** 인덱스 */\n indexes?: ModelIndex[];\n /** 보안 규칙 */\n rules?: ModelRules;\n /** 타임스탬프 자동 생성 */\n timestamps?: boolean;\n /** 소프트 삭제 */\n softDelete?: boolean;\n}\n\n/** Firestore 인덱스 */\nexport interface ModelIndex {\n fields: Array<{ field: string; order?: \"asc\" | \"desc\" }>;\n}\n\n/** 모델 보안 규칙 */\nexport interface ModelRules {\n read?: AuthLevel;\n create?: AuthLevel;\n update?: AuthLevel;\n delete?: AuthLevel;\n readRoles?: string[];\n createRoles?: string[];\n updateRoles?: string[];\n deleteRoles?: string[];\n /** 소유자만 읽기/쓰기 가능 필드 */\n ownerField?: string;\n}\n\n// ─── Builders ────────────────────────────────────────────────────────\n\n/**\n * API 계약 정의\n *\n * @example\n * ```ts\n * export default defineAPI({\n * input: z.object({ name: z.string() }),\n * output: z.object({ id: z.string(), name: z.string() }),\n * meta: { description: \"상품 생성\", auth: \"authenticated\" },\n * handler: async (input, ctx) => {\n * const id = await db.create(\"products\", input);\n * return { id, ...input };\n * }\n * });\n * ```\n */\nexport function defineAPI<\n TInput extends ZodType,\n TOutput extends ZodType,\n>(contract: APIContract<TInput, TOutput>): APIContract<TInput, TOutput> {\n return contract;\n}\n\n/**\n * 모델(Firestore 컬렉션) 정의\n *\n * @example\n * ```ts\n * export const Product = defineModel({\n * collection: \"products\",\n * fields: {\n * name: { type: \"string\", required: true },\n * price: { type: \"number\", required: true },\n * tags: { type: \"array\", items: { type: \"string\" } },\n * },\n * timestamps: true,\n * rules: { read: \"public\", create: \"authenticated\" }\n * });\n * ```\n */\nexport function defineModel(definition: ModelDefinition): ModelDefinition {\n return {\n timestamps: true,\n ...definition,\n };\n}\n\n// ─── Schema Utilities ────────────────────────────────────────────────\n\n/** Zod 스키마에서 JSON Schema 생성 (Playground/문서용) */\nexport function zodToJsonSchema(schema: ZodType): Record<string, unknown> {\n return extractZodShape(schema);\n}\n\nfunction extractZodShape(schema: ZodType): Record<string, unknown> {\n const def = (schema as any)._def;\n\n if (!def) return { type: \"unknown\" };\n\n switch (def.typeName) {\n case \"ZodObject\": {\n const shape = (schema as ZodObject<ZodRawShape>).shape;\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = extractZodShape(value as ZodType);\n if (!(value as any).isOptional?.()) {\n const innerDef = (value as any)._def;\n if (innerDef?.typeName !== \"ZodOptional\" && innerDef?.typeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n\n return { type: \"object\", properties, ...(required.length > 0 ? { required } : {}) };\n }\n case \"ZodString\":\n return { type: \"string\", ...(def.checks?.length ? extractStringChecks(def.checks) : {}) };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodArray\":\n return { type: \"array\", items: extractZodShape(def.type) };\n case \"ZodEnum\":\n return { type: \"string\", enum: def.values };\n case \"ZodOptional\":\n return { ...extractZodShape(def.innerType), optional: true };\n case \"ZodDefault\":\n return { ...extractZodShape(def.innerType), default: def.defaultValue() };\n case \"ZodNullable\":\n return { ...extractZodShape(def.innerType), nullable: true };\n case \"ZodLiteral\":\n return { type: typeof def.value, const: def.value };\n case \"ZodUnion\":\n return { oneOf: def.options.map((o: ZodType) => extractZodShape(o)) };\n case \"ZodRecord\":\n return { type: \"object\", additionalProperties: extractZodShape(def.valueType) };\n case \"ZodDate\":\n return { type: \"string\", format: \"date-time\" };\n default:\n return { type: \"unknown\" };\n }\n}\n\nfunction extractStringChecks(checks: Array<{ kind: string; value?: unknown }>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const check of checks) {\n switch (check.kind) {\n case \"min\": result.minLength = check.value; break;\n case \"max\": result.maxLength = check.value; break;\n case \"email\": result.format = \"email\"; break;\n case \"url\": result.format = \"uri\"; break;\n case \"uuid\": result.format = \"uuid\"; break;\n }\n }\n return result;\n}\n\n/** 모델 정의에서 Zod 스키마 자동 생성 */\nexport function modelToZodSchema(model: ModelDefinition): ZodObject<ZodRawShape> {\n const shape: ZodRawShape = {};\n\n for (const [key, field] of Object.entries(model.fields)) {\n let fieldSchema: ZodType = fieldToZod(field);\n if (!field.required) {\n fieldSchema = fieldSchema.optional();\n }\n shape[key] = fieldSchema;\n }\n\n if (model.timestamps) {\n shape.createdAt = z.string().datetime().optional();\n shape.updatedAt = z.string().datetime().optional();\n }\n\n if (model.softDelete) {\n shape.deletedAt = z.string().datetime().nullable().optional();\n }\n\n return z.object(shape);\n}\n\nfunction fieldToZod(field: ModelField): ZodType {\n switch (field.type) {\n case \"string\":\n if (field.enum) return z.enum(field.enum as [string, ...string[]]);\n return z.string();\n case \"number\":\n return z.number();\n case \"boolean\":\n return z.boolean();\n case \"timestamp\":\n return z.string().datetime();\n case \"array\":\n if (field.items) return z.array(fieldToZod(field.items));\n return z.array(z.unknown());\n case \"map\":\n if (field.values) return z.record(z.string(), fieldToZod(field.values));\n return z.record(z.string(), z.unknown());\n case \"reference\":\n return z.string(); // 참조는 문서 경로 문자열\n case \"geopoint\":\n return z.object({ latitude: z.number(), longitude: z.number() });\n default:\n return z.unknown();\n }\n}\n\n// ─── Manifest ────────────────────────────────────────────────────────\n\n/** API 매니페스트 항목 */\nexport interface ManifestEntry {\n path: string;\n method: \"POST\"; // Clawfire는 모두 POST\n meta: APIMeta;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n}\n\n/** 전체 매니페스트 */\nexport interface Manifest {\n version: string;\n generatedAt: string;\n apis: ManifestEntry[];\n models: Record<string, ModelDefinition>;\n}\n\n/** 계약에서 매니페스트 항목 생성 */\nexport function contractToManifest(\n path: string,\n contract: APIContract<any, any>,\n): ManifestEntry {\n return {\n path,\n method: \"POST\",\n meta: contract.meta,\n inputSchema: zodToJsonSchema(contract.input),\n outputSchema: zodToJsonSchema(contract.output),\n };\n}\n","/**\n * Clawfire Error System\n *\n * 표준화된 에러 코드와 구조로 일관된 에러 응답을 제공합니다.\n */\n\nexport type ClawfireErrorCode =\n | \"VALIDATION_ERROR\"\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"RATE_LIMITED\"\n | \"REAUTH_REQUIRED\"\n | \"INTERNAL_ERROR\"\n | \"SERVICE_UNAVAILABLE\";\n\nconst HTTP_STATUS_MAP: Record<ClawfireErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n REAUTH_REQUIRED: 401,\n INTERNAL_ERROR: 500,\n SERVICE_UNAVAILABLE: 503,\n};\n\nexport class ClawfireError extends Error {\n readonly code: ClawfireErrorCode;\n readonly statusCode: number;\n readonly details?: unknown;\n\n constructor(code: ClawfireErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = \"ClawfireError\";\n this.code = code;\n this.statusCode = HTTP_STATUS_MAP[code];\n this.details = details;\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.details ? { details: this.details } : {}),\n },\n };\n }\n}\n\n/** 편의 팩토리 함수 */\nexport const Errors = {\n validation: (message: string, details?: unknown) =>\n new ClawfireError(\"VALIDATION_ERROR\", message, details),\n\n unauthorized: (message = \"Authentication required\") =>\n new ClawfireError(\"UNAUTHORIZED\", message),\n\n forbidden: (message = \"Insufficient permissions\") =>\n new ClawfireError(\"FORBIDDEN\", message),\n\n notFound: (message = \"Resource not found\") =>\n new ClawfireError(\"NOT_FOUND\", message),\n\n conflict: (message: string) =>\n new ClawfireError(\"CONFLICT\", message),\n\n rateLimited: (message = \"Too many requests\") =>\n new ClawfireError(\"RATE_LIMITED\", message),\n\n reauthRequired: (message = \"Re-authentication required for this action\") =>\n new ClawfireError(\"REAUTH_REQUIRED\", message),\n\n internal: (message = \"Internal server error\") =>\n new ClawfireError(\"INTERNAL_ERROR\", message),\n\n unavailable: (message = \"Service temporarily unavailable\") =>\n new ClawfireError(\"SERVICE_UNAVAILABLE\", message),\n};\n","/**\n * Clawfire Logger\n *\n * 보안 로깅 (민감 정보 마스킹 포함)\n */\n\n/** 마스킹할 필드명 패턴 */\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"api_key\",\n \"authorization\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"cardNumber\",\n \"card_number\",\n \"cvv\",\n \"pin\",\n];\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet currentLevel: LogLevel = \"info\";\n\nexport function setLogLevel(level: LogLevel) {\n currentLevel = level;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\n/** 민감 필드 마스킹 */\nexport function maskSensitive(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === \"string\") return obj;\n if (typeof obj !== \"object\") return obj;\n\n if (Array.isArray(obj)) {\n return obj.map(maskSensitive);\n }\n\n const masked: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_FIELDS.some((f) => key.toLowerCase().includes(f.toLowerCase()))) {\n masked[key] = \"***MASKED***\";\n } else if (typeof value === \"object\" && value !== null) {\n masked[key] = maskSensitive(value);\n } else {\n masked[key] = value;\n }\n }\n return masked;\n}\n\nfunction formatLog(level: LogLevel, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [CLAWFIRE] [${level.toUpperCase()}]`;\n if (data !== undefined) {\n return `${prefix} ${message} ${JSON.stringify(maskSensitive(data))}`;\n }\n return `${prefix} ${message}`;\n}\n\nexport const logger = {\n debug(message: string, data?: unknown) {\n if (shouldLog(\"debug\")) console.debug(formatLog(\"debug\", message, data));\n },\n info(message: string, data?: unknown) {\n if (shouldLog(\"info\")) console.info(formatLog(\"info\", message, data));\n },\n warn(message: string, data?: unknown) {\n if (shouldLog(\"warn\")) console.warn(formatLog(\"warn\", message, data));\n },\n error(message: string, data?: unknown) {\n if (shouldLog(\"error\")) console.error(formatLog(\"error\", message, data));\n },\n};\n","/**\n * Clawfire Auth Helpers\n *\n * Firebase Auth 통합: 토큰 검증, 역할 관리, 재인증\n */\nimport type { AuthContext, AuthLevel } from \"../core/schema.js\";\nimport { Errors } from \"../core/errors.js\";\n\n// ─── Server-side Auth (Admin SDK) ────────────────────────────────────\n\n/**\n * Firebase ID 토큰에서 AuthContext 추출 (서버사이드)\n */\nexport async function verifyToken(\n auth: any, // firebase-admin Auth instance\n idToken: string,\n): Promise<AuthContext> {\n const decoded = await auth.verifyIdToken(idToken);\n return {\n uid: decoded.uid,\n email: decoded.email,\n emailVerified: decoded.email_verified,\n role: decoded.role || decoded.customClaims?.role,\n customClaims: decoded,\n token: idToken,\n };\n}\n\n/**\n * 재인증 토큰 검증 (최근 5분 이내 인증)\n */\nexport async function verifyReauth(\n auth: any,\n idToken: string,\n maxAgeSeconds = 300,\n): Promise<AuthContext> {\n const decoded = await auth.verifyIdToken(idToken, true);\n const authTime = decoded.auth_time * 1000;\n const now = Date.now();\n\n if (now - authTime > maxAgeSeconds * 1000) {\n throw Errors.reauthRequired(\n `Re-authentication required. Last auth was ${Math.floor((now - authTime) / 1000)}s ago.`,\n );\n }\n\n return {\n uid: decoded.uid,\n email: decoded.email,\n emailVerified: decoded.email_verified,\n role: decoded.role || decoded.customClaims?.role,\n customClaims: decoded,\n token: idToken,\n };\n}\n\n/**\n * 요청에서 Authorization 헤더의 Bearer 토큰 추출\n */\nexport function extractBearerToken(authHeader?: string): string | null {\n if (!authHeader) return null;\n const parts = authHeader.split(\" \");\n if (parts.length !== 2 || parts[0] !== \"Bearer\") return null;\n return parts[1];\n}\n\n/**\n * 인증 수준 체크\n */\nexport function checkAuthLevel(\n authCtx: AuthContext | null,\n level: AuthLevel,\n roles?: string[],\n reauthenticated?: boolean,\n): void {\n switch (level) {\n case \"public\":\n return; // 누구나 접근 가능\n\n case \"authenticated\":\n if (!authCtx) throw Errors.unauthorized();\n return;\n\n case \"role\":\n if (!authCtx) throw Errors.unauthorized();\n if (!roles || roles.length === 0) return;\n if (!authCtx.role || !roles.includes(authCtx.role)) {\n throw Errors.forbidden(`Required role: ${roles.join(\" or \")}`);\n }\n return;\n\n case \"reauth\":\n if (!authCtx) throw Errors.unauthorized();\n if (!reauthenticated) {\n throw Errors.reauthRequired();\n }\n return;\n }\n}\n\n// ─── Custom Claims / Role Management ─────────────────────────────────\n\n/**\n * 사용자에게 역할(Role) 설정\n */\nexport async function setUserRole(\n auth: any,\n uid: string,\n role: string,\n): Promise<void> {\n const user = await auth.getUser(uid);\n const currentClaims = user.customClaims || {};\n await auth.setCustomUserClaims(uid, { ...currentClaims, role });\n}\n\n/**\n * 사용자의 역할 조회\n */\nexport async function getUserRole(auth: any, uid: string): Promise<string | undefined> {\n const user = await auth.getUser(uid);\n return user.customClaims?.role;\n}\n\n/**\n * 사용자에게 커스텀 클레임 설정\n */\nexport async function setCustomClaims(\n auth: any,\n uid: string,\n claims: Record<string, unknown>,\n): Promise<void> {\n const user = await auth.getUser(uid);\n const currentClaims = user.customClaims || {};\n await auth.setCustomUserClaims(uid, { ...currentClaims, ...claims });\n}\n\n// ─── Client-side Auth Helpers ────────────────────────────────────────\n\n/**\n * 클라이언트 사이드 Auth 헬퍼 (firebase/auth 래핑)\n */\nexport function createClientAuth(firebaseAuth: any) {\n return {\n /** 현재 사용자 조회 */\n getCurrentUser(): any | null {\n return firebaseAuth.currentUser;\n },\n\n /** ID 토큰 취득 */\n async getIdToken(forceRefresh = false): Promise<string | null> {\n const user = firebaseAuth.currentUser;\n if (!user) return null;\n return user.getIdToken(forceRefresh);\n },\n\n /** 인증 상태 변경 리스너 */\n onAuthStateChanged(callback: (user: any | null) => void): () => void {\n return firebaseAuth.onAuthStateChanged(callback);\n },\n\n /** 로그아웃 */\n async signOut(): Promise<void> {\n await firebaseAuth.signOut();\n },\n\n /** 원시 auth 접근 */\n raw: firebaseAuth,\n };\n}\n\nexport type ClientAuth = ReturnType<typeof createClientAuth>;\n","/**\n * Clawfire File-based Router\n *\n * functions/routes/ 디렉터리 구조를 자동으로 API 경로로 매핑합니다.\n *\n * 파일 구조 → API 경로:\n * functions/routes/products/list.ts → /api/products/list\n * functions/routes/products/create.ts → /api/products/create\n * functions/routes/auth/login.ts → /api/auth/login\n * functions/routes/users/[id]/get.ts → /api/users/:id/get\n */\nimport { z } from \"zod\";\nimport type { APIContract, HandlerContext, AuthContext, Manifest, ManifestEntry } from \"../core/schema.js\";\nimport { contractToManifest, zodToJsonSchema } from \"../core/schema.js\";\nimport { ClawfireError, Errors } from \"../core/errors.js\";\nimport { logger } from \"../core/logger.js\";\nimport { checkAuthLevel, extractBearerToken, verifyToken, verifyReauth } from \"../firebase/auth.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface Route {\n path: string;\n contract: APIContract<any, any>;\n}\n\nexport interface RouterOptions {\n /** Firebase Admin Auth 인스턴스 */\n auth?: any;\n /** Firebase Admin Firestore 인스턴스 */\n firestore?: any;\n /** CORS 허용 도메인 */\n cors?: string[];\n /** 전역 rate limit */\n rateLimit?: number;\n /** 전역 미들웨어 */\n middleware?: Middleware[];\n /** 플레이그라운드 활성화 */\n playground?: boolean;\n}\n\nexport type Middleware = (\n input: unknown,\n ctx: HandlerContext,\n next: () => Promise<unknown>,\n) => Promise<unknown>;\n\n// ─── Rate Limiter (in-memory) ────────────────────────────────────────\n\nclass RateLimiter {\n private store = new Map<string, { count: number; resetAt: number }>();\n private defaultLimit: number;\n\n constructor(defaultLimit: number) {\n this.defaultLimit = defaultLimit;\n }\n\n check(key: string, limit?: number): boolean {\n const max = limit || this.defaultLimit;\n const now = Date.now();\n const entry = this.store.get(key);\n\n if (!entry || now > entry.resetAt) {\n this.store.set(key, { count: 1, resetAt: now + 60000 }); // 1분 윈도우\n return true;\n }\n\n if (entry.count >= max) {\n return false;\n }\n\n entry.count++;\n return true;\n }\n\n // 오래된 엔트리 정리\n cleanup() {\n const now = Date.now();\n for (const [key, entry] of this.store) {\n if (now > entry.resetAt) this.store.delete(key);\n }\n }\n}\n\n// ─── Router ──────────────────────────────────────────────────────────\n\nexport class ClawfireRouter {\n private routes = new Map<string, Route>();\n private options: RouterOptions;\n private rateLimiter: RateLimiter;\n private cleanupInterval?: ReturnType<typeof setInterval>;\n\n constructor(options: RouterOptions = {}) {\n this.options = options;\n this.rateLimiter = new RateLimiter(options.rateLimit || 100);\n this.cleanupInterval = setInterval(() => this.rateLimiter.cleanup(), 60000);\n }\n\n /** 라우트 등록 */\n register(path: string, contract: APIContract<any, any>): this {\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n this.routes.set(normalizedPath, { path: normalizedPath, contract });\n logger.debug(`Route registered: ${normalizedPath}`);\n return this;\n }\n\n /** 여러 라우트 한 번에 등록 */\n registerAll(routes: Record<string, APIContract<any, any>>): this {\n for (const [path, contract] of Object.entries(routes)) {\n this.register(path, contract);\n }\n return this;\n }\n\n /** 매니페스트 생성 */\n getManifest(): Manifest {\n const apis: ManifestEntry[] = [];\n for (const [path, route] of this.routes) {\n apis.push(contractToManifest(path, route.contract));\n }\n\n return {\n version: \"1.0.0\",\n generatedAt: new Date().toISOString(),\n apis,\n models: {},\n };\n }\n\n /** HTTP 요청 핸들러 (Firebase Functions에서 사용) */\n async handleRequest(\n req: { path?: string; url?: string; body?: unknown; headers?: Record<string, string>; ip?: string; method?: string },\n res: { status: (code: number) => { json: (data: unknown) => void; send: (data: string) => void; end: () => void }; set: (headers: Record<string, string>) => void },\n ): Promise<void> {\n // CORS 처리\n const corsOrigins = this.options.cors || [];\n const origin = req.headers?.origin || req.headers?.Origin || \"\";\n\n if (corsOrigins.length > 0 && corsOrigins.includes(origin)) {\n res.set({\n \"Access-Control-Allow-Origin\": origin,\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Max-Age\": \"86400\",\n });\n } else if (corsOrigins.length === 0) {\n // CORS 기본 deny — 설정 없으면 허용하지 않음\n res.set({\n \"Access-Control-Allow-Origin\": \"\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n });\n }\n\n // OPTIONS (preflight)\n if (req.method === \"OPTIONS\") {\n res.status(204).end();\n return;\n }\n\n // 안전 헤더\n res.set({\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-Frame-Options\": \"DENY\",\n \"X-XSS-Protection\": \"1; mode=block\",\n \"Content-Type\": \"application/json\",\n });\n\n // 경로 추출 — /api/xxx/yyy → /xxx/yyy\n let routePath = req.path || req.url || \"\";\n if (routePath.startsWith(\"/api\")) {\n routePath = routePath.slice(4);\n }\n\n // 시스템 엔드포인트 (GET 허용)\n if (routePath === \"/__manifest\") {\n res.status(200).json(this.getManifest());\n return;\n }\n\n // POST만 허용 (시스템 엔드포인트 제외)\n if (req.method && req.method !== \"POST\") {\n res.status(405).json({ error: { code: \"METHOD_NOT_ALLOWED\", message: \"Only POST is allowed\" } });\n return;\n }\n\n // 라우트 매칭\n const route = this.matchRoute(routePath);\n if (!route) {\n res.status(404).json({ error: { code: \"NOT_FOUND\", message: `API not found: ${routePath}` } });\n return;\n }\n\n try {\n // Rate limit\n const clientKey = req.ip || \"unknown\";\n const rateLimit = route.contract.meta.rateLimit || this.options.rateLimit;\n if (rateLimit && !this.rateLimiter.check(clientKey, rateLimit)) {\n throw Errors.rateLimited();\n }\n\n // Auth 처리\n let authCtx: AuthContext | null = null;\n let reauthenticated = false;\n const authHeader = req.headers?.authorization || req.headers?.Authorization;\n\n if (authHeader && this.options.auth) {\n const token = extractBearerToken(authHeader as string);\n if (token) {\n if (route.contract.meta.reauth || route.contract.meta.auth === \"reauth\") {\n authCtx = await verifyReauth(this.options.auth, token);\n reauthenticated = true;\n } else {\n authCtx = await verifyToken(this.options.auth, token);\n }\n }\n }\n\n // 권한 체크\n if (route.contract.meta.auth) {\n checkAuthLevel(authCtx, route.contract.meta.auth, route.contract.meta.roles, reauthenticated);\n }\n\n // 입력 검증\n const rawInput = req.body || {};\n const parsed = route.contract.input.safeParse(rawInput);\n if (!parsed.success) {\n throw Errors.validation(\"Invalid input\", parsed.error.flatten());\n }\n\n // 핸들러 컨텍스트\n const ctx: HandlerContext = {\n auth: authCtx,\n reauthenticated,\n headers: req.headers as Record<string, string>,\n ip: req.ip,\n };\n\n // 미들웨어 + 핸들러 실행\n let result: unknown;\n if (this.options.middleware && this.options.middleware.length > 0) {\n result = await this.runMiddleware(\n this.options.middleware,\n parsed.data,\n ctx,\n () => route.contract.handler(parsed.data, ctx),\n );\n } else {\n result = await route.contract.handler(parsed.data, ctx);\n }\n\n // 성공 응답\n res.status(200).json({ data: result });\n } catch (err) {\n if (err instanceof ClawfireError) {\n logger.warn(`API error [${err.code}]: ${err.message}`);\n res.status(err.statusCode).json(err.toJSON());\n } else {\n logger.error(\"Unhandled error\", err);\n res.status(500).json({\n error: {\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n });\n }\n }\n }\n\n /** 라우트 매칭 (동적 파라미터 지원) */\n private matchRoute(path: string): Route | undefined {\n // 정확한 매칭\n const exact = this.routes.get(path);\n if (exact) return exact;\n\n // 동적 파라미터 매칭 (/users/:id/get → /users/abc123/get)\n for (const [routePath, route] of this.routes) {\n if (this.matchDynamicRoute(routePath, path)) {\n return route;\n }\n }\n\n return undefined;\n }\n\n private matchDynamicRoute(pattern: string, actual: string): boolean {\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const actualParts = actual.split(\"/\").filter(Boolean);\n if (patternParts.length !== actualParts.length) return false;\n\n return patternParts.every(\n (part, i) => part.startsWith(\":\") || part.startsWith(\"[\") || part === actualParts[i],\n );\n }\n\n /** 미들웨어 체인 실행 */\n private async runMiddleware(\n middlewares: Middleware[],\n input: unknown,\n ctx: HandlerContext,\n handler: () => Promise<unknown>,\n ): Promise<unknown> {\n let index = 0;\n const next = async (): Promise<unknown> => {\n if (index >= middlewares.length) {\n return handler();\n }\n const mw = middlewares[index++];\n return mw(input, ctx, next);\n };\n return next();\n }\n\n /** 등록된 라우트 목록 */\n getRoutes(): Route[] {\n return Array.from(this.routes.values());\n }\n\n /** 리소스 정리 */\n destroy() {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n }\n}\n\n/**\n * 라우터 생성 헬퍼\n */\nexport function createRouter(options?: RouterOptions): ClawfireRouter {\n return new ClawfireRouter(options);\n}\n","/**\n * Clawfire Route Discovery\n *\n * 파일 시스템에서 라우트 자동 발견 (빌드 타임 & 런타임)\n */\nimport { resolve, relative, join } from \"path\";\nimport { existsSync, readdirSync, statSync } from \"fs\";\n\nexport interface DiscoveredRoute {\n /** 파일 경로 (상대) */\n filePath: string;\n /** API 경로 (/products/list) */\n apiPath: string;\n /** 동적 파라미터 이름 */\n params: string[];\n}\n\n/**\n * routes 디렉터리에서 라우트 파일 자동 발견\n *\n * @param routesDir - routes 디렉터리 절대 경로\n * @returns 발견된 라우트 목록\n *\n * @example\n * ```\n * functions/routes/products/list.ts → { apiPath: \"/products/list\", params: [] }\n * functions/routes/products/[id]/get.ts → { apiPath: \"/products/:id/get\", params: [\"id\"] }\n * functions/routes/health.ts → { apiPath: \"/health\", params: [] }\n * ```\n */\nexport function discoverRoutes(routesDir: string): DiscoveredRoute[] {\n if (!existsSync(routesDir)) {\n return [];\n }\n\n const routes: DiscoveredRoute[] = [];\n scanDirectory(routesDir, routesDir, routes);\n return routes.sort((a, b) => a.apiPath.localeCompare(b.apiPath));\n}\n\nfunction scanDirectory(baseDir: string, currentDir: string, routes: DiscoveredRoute[]): void {\n const entries = readdirSync(currentDir);\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n // 숨김 디렉터리, node_modules 무시\n if (entry.startsWith(\".\") || entry === \"node_modules\") continue;\n scanDirectory(baseDir, fullPath, routes);\n } else if (stat.isFile()) {\n // .ts, .js 파일만\n if (!entry.endsWith(\".ts\") && !entry.endsWith(\".js\")) continue;\n // index, _로 시작하는 파일 무시\n if (entry.startsWith(\"_\")) continue;\n // .d.ts 무시\n if (entry.endsWith(\".d.ts\")) continue;\n\n const relativePath = relative(baseDir, fullPath);\n const route = filePathToRoute(relativePath);\n routes.push(route);\n }\n }\n}\n\nfunction filePathToRoute(filePath: string): DiscoveredRoute {\n const params: string[] = [];\n\n // 확장자 제거\n let routePath = filePath.replace(/\\.(ts|js)$/, \"\");\n\n // Windows 경로 → POSIX\n routePath = routePath.replace(/\\\\/g, \"/\");\n\n // index 파일은 디렉터리 자체\n if (routePath.endsWith(\"/index\") || routePath === \"index\") {\n routePath = routePath.replace(/\\/?index$/, \"\");\n }\n\n // [param] → :param 변환\n routePath = routePath.replace(/\\[([^\\]]+)\\]/g, (_, param) => {\n params.push(param);\n return `:${param}`;\n });\n\n // 앞에 / 추가\n const apiPath = `/${routePath}`;\n\n return {\n filePath,\n apiPath,\n params,\n };\n}\n\n/**\n * 라우트 파일에서 import하여 라우터에 등록하는 코드 생성 (빌드 타임)\n */\nexport function generateRouteImports(routes: DiscoveredRoute[], routesDir: string): string {\n const lines: string[] = [\n '// AUTO-GENERATED by Clawfire — DO NOT EDIT',\n '// This file is regenerated whenever routes change.',\n '',\n 'import { createRouter } from \"clawfire/functions\";',\n '',\n ];\n\n routes.forEach((route, i) => {\n const importPath = `./${route.filePath.replace(/\\.(ts|js)$/, \".js\")}`;\n lines.push(`import route_${i} from \"${importPath}\";`);\n });\n\n lines.push('');\n lines.push('export function registerAllRoutes(router: ReturnType<typeof createRouter>) {');\n\n routes.forEach((route, i) => {\n lines.push(` router.register(\"${route.apiPath}\", route_${i});`);\n });\n\n lines.push(' return router;');\n lines.push('}');\n\n return lines.join('\\n');\n}\n","/**\n * Clawfire Playground\n *\n * 웹 기반 API 탐색기: API 목록, 인증 테스트, 요청/응답 뷰어\n * 단일 HTML 파일로 생성되어 Firebase Hosting에 배포됩니다.\n */\n\nexport function generatePlaygroundHtml(options?: {\n title?: string;\n apiBaseUrl?: string;\n}): string {\n const title = options?.title || \"Clawfire Playground\";\n const apiBaseUrl = options?.apiBaseUrl || \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n <style>\n :root {\n --bg: #0a0a0a;\n --surface: #141414;\n --surface2: #1e1e1e;\n --border: #2a2a2a;\n --text: #e5e5e5;\n --text2: #a3a3a3;\n --accent: #f97316;\n --accent2: #fb923c;\n --green: #22c55e;\n --red: #ef4444;\n --blue: #3b82f6;\n --yellow: #eab308;\n --radius: 8px;\n --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --mono: 'JetBrains Mono', 'Fira Code', monospace;\n }\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: var(--font); background: var(--bg); color: var(--text); min-height: 100vh; }\n\n .layout { display: grid; grid-template-columns: 320px 1fr; min-height: 100vh; }\n .sidebar { background: var(--surface); border-right: 1px solid var(--border); overflow-y: auto; }\n .main { padding: 24px; overflow-y: auto; }\n\n .logo { padding: 20px; border-bottom: 1px solid var(--border); }\n .logo h1 { font-size: 20px; font-weight: 700; color: var(--accent); }\n .logo p { font-size: 12px; color: var(--text2); margin-top: 4px; }\n\n .auth-section { padding: 16px; border-bottom: 1px solid var(--border); }\n .auth-section label { font-size: 12px; color: var(--text2); display: block; margin-bottom: 6px; }\n .auth-input { width: 100%; padding: 8px 12px; background: var(--surface2); border: 1px solid var(--border);\n border-radius: var(--radius); color: var(--text); font-family: var(--mono); font-size: 12px; }\n .auth-status { font-size: 11px; margin-top: 6px; }\n .auth-status.ok { color: var(--green); }\n .auth-status.no { color: var(--text2); }\n\n .search { padding: 12px 16px; border-bottom: 1px solid var(--border); }\n .search input { width: 100%; padding: 8px 12px; background: var(--surface2); border: 1px solid var(--border);\n border-radius: var(--radius); color: var(--text); font-size: 13px; }\n\n .api-list { padding: 8px 0; }\n .api-group { padding: 4px 0; }\n .api-group-title { padding: 8px 16px; font-size: 11px; color: var(--text2); text-transform: uppercase;\n letter-spacing: 0.5px; font-weight: 600; }\n .api-item { padding: 8px 16px; cursor: pointer; transition: background 0.15s; display: flex; align-items: center;\n gap: 8px; font-size: 13px; }\n .api-item:hover { background: var(--surface2); }\n .api-item.active { background: var(--surface2); border-left: 2px solid var(--accent); }\n .api-badge { font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 600; }\n .badge-public { background: #22c55e20; color: var(--green); }\n .badge-auth { background: #3b82f620; color: var(--blue); }\n .badge-role { background: #eab30820; color: var(--yellow); }\n .badge-reauth { background: #ef444420; color: var(--red); }\n\n .panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 16px; }\n .panel-header { padding: 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center;\n justify-content: space-between; }\n .panel-header h2 { font-size: 16px; font-weight: 600; }\n .panel-body { padding: 16px; }\n\n .field { margin-bottom: 12px; }\n .field label { font-size: 12px; color: var(--text2); display: block; margin-bottom: 4px; }\n .field-type { font-size: 11px; color: var(--text2); font-family: var(--mono); }\n .field-required { color: var(--red); font-size: 11px; }\n\n textarea, input[type=\"text\"] { width: 100%; padding: 10px 14px; background: var(--surface2);\n border: 1px solid var(--border); border-radius: var(--radius); color: var(--text);\n font-family: var(--mono); font-size: 13px; resize: vertical; }\n textarea { min-height: 200px; }\n\n .btn { padding: 10px 20px; border: none; border-radius: var(--radius); font-size: 14px;\n font-weight: 600; cursor: pointer; transition: all 0.15s; }\n .btn-primary { background: var(--accent); color: white; }\n .btn-primary:hover { background: var(--accent2); }\n .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }\n\n .response-section { margin-top: 16px; }\n .status-badge { font-size: 12px; padding: 4px 8px; border-radius: 4px; font-weight: 600; }\n .status-ok { background: #22c55e20; color: var(--green); }\n .status-err { background: #ef444420; color: var(--red); }\n pre { background: var(--surface2); padding: 16px; border-radius: var(--radius); overflow-x: auto;\n font-family: var(--mono); font-size: 13px; line-height: 1.5; white-space: pre-wrap; }\n\n .schema-info { font-size: 12px; color: var(--text2); line-height: 1.6; }\n .schema-info code { background: var(--surface2); padding: 2px 6px; border-radius: 4px; font-family: var(--mono);\n font-size: 11px; }\n\n .empty-state { text-align: center; padding: 80px 40px; color: var(--text2); }\n .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }\n\n .timer { font-size: 12px; color: var(--text2); font-family: var(--mono); }\n\n @media (max-width: 768px) {\n .layout { grid-template-columns: 1fr; }\n .sidebar { max-height: 40vh; }\n }\n </style>\n</head>\n<body>\n <div class=\"layout\">\n <div class=\"sidebar\">\n <div class=\"logo\">\n <h1>Clawfire</h1>\n <p>API Playground</p>\n </div>\n <div class=\"auth-section\">\n <label>Bearer Token</label>\n <input type=\"text\" class=\"auth-input\" id=\"token-input\" placeholder=\"Paste your ID token...\">\n <div class=\"auth-status no\" id=\"auth-status\">Not authenticated</div>\n </div>\n <div class=\"search\">\n <input type=\"text\" id=\"search-input\" placeholder=\"Search APIs...\">\n </div>\n <div class=\"api-list\" id=\"api-list\"></div>\n </div>\n <div class=\"main\" id=\"main-content\">\n <div class=\"empty-state\">\n <h2>Select an API</h2>\n <p>Choose an API from the sidebar to test it.</p>\n </div>\n </div>\n </div>\n\n <script>\n const BASE_URL = ${JSON.stringify(apiBaseUrl)} || window.location.origin;\n let manifest = null;\n let selectedApi = null;\n\n async function loadManifest() {\n try {\n const res = await fetch(BASE_URL + '/api/__manifest', { method: 'POST' });\n manifest = await res.json();\n renderApiList(manifest.apis);\n } catch (e) {\n document.getElementById('api-list').innerHTML =\n '<div style=\"padding:16px;color:var(--red);font-size:13px;\">Failed to load API manifest. Make sure your server is running.</div>';\n }\n }\n\n function renderApiList(apis) {\n const groups = {};\n apis.forEach(api => {\n const parts = api.path.split('/').filter(Boolean);\n const group = parts.length > 1 ? parts[0] : 'root';\n if (!groups[group]) groups[group] = [];\n groups[group].push(api);\n });\n\n const el = document.getElementById('api-list');\n el.innerHTML = Object.entries(groups).map(([group, items]) =>\n '<div class=\"api-group\">' +\n '<div class=\"api-group-title\">' + group + '</div>' +\n items.map(api => {\n const auth = api.meta.auth || 'public';\n const badgeClass = 'badge-' + auth;\n return '<div class=\"api-item\" onclick=\"selectApi(\\\\'' + api.path + '\\\\')\">' +\n '<span class=\"api-badge ' + badgeClass + '\">' + auth.toUpperCase() + '</span>' +\n '<span>' + api.path + '</span>' +\n '</div>';\n }).join('') +\n '</div>'\n ).join('');\n }\n\n function selectApi(path) {\n selectedApi = manifest.apis.find(a => a.path === path);\n if (!selectedApi) return;\n\n document.querySelectorAll('.api-item').forEach(el => el.classList.remove('active'));\n event.currentTarget?.classList.add('active');\n\n const main = document.getElementById('main-content');\n const exampleInput = selectedApi.meta.exampleInput\n ? JSON.stringify(selectedApi.meta.exampleInput, null, 2)\n : generateExampleFromSchema(selectedApi.inputSchema);\n\n main.innerHTML =\n '<div class=\"panel\">' +\n '<div class=\"panel-header\">' +\n '<h2>POST ' + selectedApi.path + '</h2>' +\n '<span class=\"api-badge badge-' + (selectedApi.meta.auth || 'public') + '\">' +\n (selectedApi.meta.auth || 'public').toUpperCase() + '</span>' +\n '</div>' +\n '<div class=\"panel-body\">' +\n '<p style=\"color:var(--text2);margin-bottom:16px;\">' + (selectedApi.meta.description || '') + '</p>' +\n (selectedApi.meta.tags ? '<div style=\"margin-bottom:12px;\">' + selectedApi.meta.tags.map(t =>\n '<span style=\"background:var(--surface2);padding:2px 8px;border-radius:4px;font-size:11px;margin-right:4px;\">' + t + '</span>'\n ).join('') + '</div>' : '') +\n '<div class=\"schema-info\" style=\"margin-bottom:16px;\">' +\n '<strong>Input Schema:</strong><br>' + renderSchemaInfo(selectedApi.inputSchema) +\n '</div>' +\n '<div class=\"schema-info\" style=\"margin-bottom:16px;\">' +\n '<strong>Output Schema:</strong><br>' + renderSchemaInfo(selectedApi.outputSchema) +\n '</div>' +\n '</div>' +\n '</div>' +\n '<div class=\"panel\">' +\n '<div class=\"panel-header\"><h2>Request</h2></div>' +\n '<div class=\"panel-body\">' +\n '<textarea id=\"req-body\" placeholder=\"Request JSON body\">' + exampleInput + '</textarea>' +\n '<div style=\"margin-top:12px;display:flex;align-items:center;gap:12px;\">' +\n '<button class=\"btn btn-primary\" onclick=\"sendRequest()\">Send Request</button>' +\n '<span class=\"timer\" id=\"timer\"></span>' +\n '</div>' +\n '</div>' +\n '</div>' +\n '<div class=\"response-section\" id=\"response-section\"></div>';\n }\n\n function renderSchemaInfo(schema) {\n if (!schema || !schema.properties) return '<code>void</code>';\n return Object.entries(schema.properties).map(([key, prop]) => {\n const required = schema.required?.includes(key);\n const type = prop.type || 'unknown';\n const enumVals = prop.enum ? ' (' + prop.enum.join(', ') + ')' : '';\n return '<code>' + key + '</code>: <span class=\"field-type\">' + type + enumVals + '</span>' +\n (required ? ' <span class=\"field-required\">required</span>' : ' <span style=\"color:var(--text2);font-size:11px;\">optional</span>');\n }).join('<br>');\n }\n\n function generateExampleFromSchema(schema) {\n if (!schema || !schema.properties) return '{}';\n const obj = {};\n for (const [key, prop] of Object.entries(schema.properties)) {\n if (prop.enum) { obj[key] = prop.enum[0]; continue; }\n switch (prop.type) {\n case 'string': obj[key] = prop.format === 'email' ? 'user@example.com' : 'string'; break;\n case 'number': obj[key] = 0; break;\n case 'boolean': obj[key] = false; break;\n case 'array': obj[key] = []; break;\n case 'object': obj[key] = {}; break;\n default: obj[key] = null;\n }\n }\n return JSON.stringify(obj, null, 2);\n }\n\n async function sendRequest() {\n if (!selectedApi) return;\n const body = document.getElementById('req-body').value;\n const token = document.getElementById('token-input').value;\n const timer = document.getElementById('timer');\n const section = document.getElementById('response-section');\n\n let parsed;\n try { parsed = JSON.parse(body); } catch {\n section.innerHTML = '<div class=\"panel\"><div class=\"panel-body\"><pre style=\"color:var(--red)\">Invalid JSON</pre></div></div>';\n return;\n }\n\n const start = performance.now();\n timer.textContent = 'Sending...';\n\n try {\n const headers = { 'Content-Type': 'application/json' };\n if (token) headers['Authorization'] = 'Bearer ' + token;\n\n const res = await fetch(BASE_URL + '/api' + selectedApi.path, {\n method: 'POST', headers, body: JSON.stringify(parsed)\n });\n const elapsed = Math.round(performance.now() - start);\n timer.textContent = elapsed + 'ms';\n\n const json = await res.json();\n const isOk = res.ok;\n\n section.innerHTML =\n '<div class=\"panel\">' +\n '<div class=\"panel-header\">' +\n '<h2>Response</h2>' +\n '<span class=\"status-badge ' + (isOk ? 'status-ok' : 'status-err') + '\">' +\n res.status + ' ' + res.statusText + '</span>' +\n '</div>' +\n '<div class=\"panel-body\"><pre>' + syntaxHighlight(JSON.stringify(json, null, 2)) + '</pre></div>' +\n '</div>';\n } catch (e) {\n const elapsed = Math.round(performance.now() - start);\n timer.textContent = elapsed + 'ms';\n section.innerHTML =\n '<div class=\"panel\"><div class=\"panel-body\"><pre style=\"color:var(--red)\">Network error: ' + e.message + '</pre></div></div>';\n }\n }\n\n function syntaxHighlight(json) {\n return json.replace(/(\"(\\\\\\\\u[a-fA-F0-9]{4}|\\\\\\\\[^u]|[^\\\\\\\\\"])*\"(\\\\s*:)?)|\\\\b(true|false|null)\\\\b|-?\\\\d+(\\\\.\\\\d+)?([eE][+-]?\\\\d+)?/g,\n function(match) {\n let cls = 'color:#eab308';\n if (/^\"/.test(match)) {\n if (/:$/.test(match)) cls = 'color:#3b82f6';\n else cls = 'color:#22c55e';\n } else if (/true|false/.test(match)) cls = 'color:#f97316';\n else if (/null/.test(match)) cls = 'color:#ef4444';\n return '<span style=\"' + cls + '\">' + match + '</span>';\n }\n );\n }\n\n // Search\n document.getElementById('search-input')?.addEventListener('input', (e) => {\n if (!manifest) return;\n const q = e.target.value.toLowerCase();\n const filtered = manifest.apis.filter(a => a.path.toLowerCase().includes(q) || a.meta.description?.toLowerCase().includes(q));\n renderApiList(filtered);\n });\n\n // Token status\n document.getElementById('token-input')?.addEventListener('input', (e) => {\n const el = document.getElementById('auth-status');\n if (e.target.value) {\n el.textContent = 'Token set';\n el.className = 'auth-status ok';\n } else {\n el.textContent = 'Not authenticated';\n el.className = 'auth-status no';\n }\n });\n\n loadManifest();\n </script>\n</body>\n</html>`;\n}\n","/**\n * Clawfire File Watcher\n *\n * Node.js fs.watch 기반 파일 변경 감지 (외부 의존성 없음)\n * macOS/Windows에서 recursive 지원, Linux에서는 디렉터리별 개별 감시\n */\nimport { watch, existsSync, statSync, readdirSync } from \"fs\";\nimport { join, extname } from \"path\";\nimport { EventEmitter } from \"events\";\n\nexport type WatchEventType = \"route-change\" | \"schema-change\" | \"config-change\" | \"frontend-change\" | \"css-change\" | \"page-change\" | \"component-change\" | \"any-change\";\n\nexport interface WatchEvent {\n type: WatchEventType;\n filePath: string;\n timestamp: number;\n}\n\nexport class FileWatcher extends EventEmitter {\n private watchers: ReturnType<typeof watch>[] = [];\n private debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private debounceMs: number;\n\n constructor(debounceMs = 150) {\n super();\n this.debounceMs = debounceMs;\n }\n\n /**\n * 디렉터리 감시 시작\n */\n watchDir(dir: string, eventType: WatchEventType): this {\n if (!existsSync(dir)) return this;\n\n try {\n // macOS/Windows: recursive 옵션 지원\n const watcher = watch(dir, { recursive: true }, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n this.emitDebounced(filePath, eventType);\n });\n\n watcher.on(\"error\", (err) => {\n // Linux에서 recursive 미지원 시 fallback\n if ((err as any).code === \"ERR_FEATURE_UNAVAILABLE_ON_PLATFORM\") {\n this.watchDirRecursiveManual(dir, eventType);\n }\n });\n\n this.watchers.push(watcher);\n } catch {\n // fallback: 수동 재귀 감시\n this.watchDirRecursiveManual(dir, eventType);\n }\n\n return this;\n }\n\n /**\n * 단일 파일 감시\n */\n watchFile(filePath: string, eventType: WatchEventType): this {\n if (!existsSync(filePath)) return this;\n\n const watcher = watch(filePath, (event) => {\n this.emitDebounced(filePath, eventType);\n });\n\n this.watchers.push(watcher);\n return this;\n }\n\n /**\n * 프론트엔드 디렉터리 감시 (확장자별 이벤트 타입 자동 결정)\n */\n watchDirFrontend(dir: string): this {\n if (!existsSync(dir)) return this;\n\n try {\n const watcher = watch(dir, { recursive: true }, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n const ext = extname(filePath);\n const eventType: WatchEventType = ext === \".css\" ? \"css-change\" : \"frontend-change\";\n this.emitDebounced(filePath, eventType);\n });\n\n watcher.on(\"error\", (err) => {\n if ((err as any).code === \"ERR_FEATURE_UNAVAILABLE_ON_PLATFORM\") {\n this.watchDirFrontendRecursiveManual(dir);\n }\n });\n\n this.watchers.push(watcher);\n } catch {\n this.watchDirFrontendRecursiveManual(dir);\n }\n\n return this;\n }\n\n /**\n * Linux fallback: 프론트엔드 디렉터리 수동 재귀 감시\n */\n private watchDirFrontendRecursiveManual(dir: string): void {\n if (!existsSync(dir)) return;\n\n const watcher = watch(dir, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n const ext = extname(filePath);\n const eventType: WatchEventType = ext === \".css\" ? \"css-change\" : \"frontend-change\";\n this.emitDebounced(filePath, eventType);\n });\n this.watchers.push(watcher);\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith(\".\") && entry.name !== \"node_modules\") {\n this.watchDirFrontendRecursiveManual(join(dir, entry.name));\n }\n }\n } catch {}\n }\n\n /**\n * Linux fallback: 디렉터리 수동 재귀 감시\n */\n private watchDirRecursiveManual(dir: string, eventType: WatchEventType): void {\n if (!existsSync(dir)) return;\n\n const watcher = watch(dir, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n this.emitDebounced(filePath, eventType);\n });\n this.watchers.push(watcher);\n\n // 서브디렉터리도 감시\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith(\".\") && entry.name !== \"node_modules\") {\n this.watchDirRecursiveManual(join(dir, entry.name), eventType);\n }\n }\n } catch {}\n }\n\n /**\n * 감시 대상 파일인지 확인\n */\n private isWatchedFile(filePath: string): boolean {\n const ext = extname(filePath);\n return [\".ts\", \".tsx\", \".js\", \".jsx\", \".json\", \".html\", \".css\", \".svg\", \".png\", \".jpg\", \".jpeg\", \".gif\", \".ico\", \".webp\", \".woff\", \".woff2\"].includes(ext);\n }\n\n /**\n * 디바운스 이벤트 발생\n */\n private emitDebounced(filePath: string, type: WatchEventType): void {\n const existing = this.debounceTimers.get(filePath);\n if (existing) clearTimeout(existing);\n\n this.debounceTimers.set(\n filePath,\n setTimeout(() => {\n this.debounceTimers.delete(filePath);\n const event: WatchEvent = { type, filePath, timestamp: Date.now() };\n this.emit(\"change\", event);\n this.emit(type, event);\n }, this.debounceMs),\n );\n }\n\n /**\n * 모든 감시 중지\n */\n close(): void {\n for (const watcher of this.watchers) {\n watcher.close();\n }\n this.watchers = [];\n for (const timer of this.debounceTimers.values()) {\n clearTimeout(timer);\n }\n this.debounceTimers.clear();\n this.removeAllListeners();\n }\n}\n","/**\n * Clawfire Page Compiler\n *\n * File-based page routing with layouts and components.\n * Resolves URLs to page files, processes components (<c-name />),\n * applies nested layouts (<slot />), and extracts metadata.\n */\nimport { resolve, join, relative, dirname, basename, extname } from \"node:path\";\nimport { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync } from \"node:fs\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface PageMeta {\n title?: string;\n description?: string;\n [key: string]: string | undefined;\n}\n\nexport interface CompiledPage {\n html: string;\n meta: PageMeta;\n filePath: string;\n layouts: string[];\n}\n\nexport interface PartialPage {\n html: string;\n meta: PageMeta;\n path: string;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────\n\nconst MAX_COMPONENT_DEPTH = 10;\nconst META_REGEX = /<!--\\s*@(\\w+):\\s*(.+?)\\s*-->/g;\nconst COMPONENT_REGEX = /<c-([a-z][a-z0-9-]*)\\s*\\/>/g;\nconst SLOT_MARKER = \"<slot />\";\n\n// ─── Page Compiler ───────────────────────────────────────────────────\n\nexport class PageCompiler {\n private pagesDir: string;\n private componentsDir: string;\n\n constructor(private projectDir: string) {\n this.pagesDir = resolve(projectDir, \"app/pages\");\n this.componentsDir = resolve(projectDir, \"app/components\");\n }\n\n /**\n * Check if the page system is active (app/pages/ exists)\n */\n isActive(): boolean {\n return existsSync(this.pagesDir);\n }\n\n /**\n * Resolve a URL pathname to a page file path.\n * Returns null if no matching page exists.\n *\n * / → app/pages/index.html\n * /about → app/pages/about.html OR app/pages/about/index.html\n * /todos → app/pages/todos/index.html OR app/pages/todos.html\n */\n resolve(pathname: string): string | null {\n if (!this.isActive()) return null;\n\n // Normalize: strip trailing slash, default to /\n const clean = pathname === \"/\" ? \"\" : pathname.replace(/\\/+$/, \"\");\n const segments = clean ? clean.split(\"/\").filter(Boolean) : [];\n\n // Build candidate paths\n const candidates: string[] = [];\n\n if (segments.length === 0) {\n candidates.push(join(this.pagesDir, \"index.html\"));\n } else {\n const pathPart = segments.join(\"/\");\n // Try direct file first, then directory/index\n candidates.push(join(this.pagesDir, `${pathPart}.html`));\n candidates.push(join(this.pagesDir, pathPart, \"index.html\"));\n }\n\n for (const candidate of candidates) {\n // Security: must be within pagesDir\n if (!candidate.startsWith(this.pagesDir)) continue;\n // Skip layout and special files\n const name = basename(candidate);\n if (name.startsWith(\"_\")) continue;\n if (existsSync(candidate)) return candidate;\n }\n\n return null;\n }\n\n /**\n * Resolve the 404 page file path.\n */\n resolve404(): string | null {\n const path404 = join(this.pagesDir, \"_404.html\");\n return existsSync(path404) ? path404 : null;\n }\n\n /**\n * Compile a full page with layouts and components.\n */\n compile(pagePath: string): CompiledPage {\n // 1. Read page file\n let pageHtml = readFileSync(pagePath, \"utf-8\");\n\n // 2. Extract metadata\n const meta = this.extractMeta(pageHtml);\n pageHtml = this.stripMeta(pageHtml);\n\n // 3. Process components in page\n pageHtml = this.processComponents(pageHtml);\n\n // 4. Wrap in page container div\n pageHtml = this.wrapPageContent(pageHtml);\n\n // 5. Collect layout chain\n const layouts = this.collectLayouts(pagePath);\n\n // 6. Apply layouts (innermost first)\n let html = pageHtml;\n for (const layoutPath of layouts) {\n let layoutHtml = readFileSync(layoutPath, \"utf-8\");\n layoutHtml = this.processComponents(layoutHtml);\n html = layoutHtml.replace(SLOT_MARKER, html);\n }\n\n // 7. Set <title> from metadata\n if (meta.title) {\n html = this.setTitle(html, meta.title);\n }\n\n return {\n html,\n meta,\n filePath: pagePath,\n layouts,\n };\n }\n\n /**\n * Compile a partial page for SPA navigation.\n * Returns only the page content (no layout) plus metadata.\n */\n compilePartial(pagePath: string, pathname: string): PartialPage {\n let pageHtml = readFileSync(pagePath, \"utf-8\");\n const meta = this.extractMeta(pageHtml);\n pageHtml = this.stripMeta(pageHtml);\n pageHtml = this.processComponents(pageHtml);\n\n return {\n html: pageHtml,\n meta,\n path: pathname,\n };\n }\n\n /**\n * Build all pages into static HTML for production deployment.\n * Walks app/pages/ recursively, compiles each page, and writes to outputDir.\n *\n * - Skips _layout.html (wrapper-only, not standalone)\n * - _404.html → 404.html (Firebase auto-serves for 404s)\n * - Injects optional script (e.g. production router) before </body>\n */\n buildForProduction(outputDir: string, scriptToInject?: string): { pages: string[]; errors: string[] } {\n const pages: string[] = [];\n const errors: string[] = [];\n\n if (!this.isActive()) {\n return { pages, errors };\n }\n\n const walk = (dir: string) => {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n walk(fullPath);\n continue;\n }\n\n if (!entry.endsWith(\".html\")) continue;\n\n // Skip layout files — they are wrappers, not standalone pages\n if (entry === \"_layout.html\") continue;\n\n try {\n // Compile the page (applies layouts + components + metadata)\n const compiled = this.compile(fullPath);\n let html = compiled.html;\n\n // Inject production script before </body>\n if (scriptToInject) {\n if (html.includes(\"</body>\")) {\n html = html.replace(\"</body>\", scriptToInject + \"\\n</body>\");\n } else {\n html += scriptToInject;\n }\n }\n\n // Determine output path\n const relPath = relative(this.pagesDir, fullPath);\n let outputPath: string;\n\n if (entry === \"_404.html\") {\n // _404.html → 404.html at the same directory level\n const parentRel = relative(this.pagesDir, dir);\n outputPath = parentRel ? join(outputDir, parentRel, \"404.html\") : join(outputDir, \"404.html\");\n } else {\n outputPath = join(outputDir, relPath);\n }\n\n // Ensure output directory exists\n const outputDirPath = dirname(outputPath);\n mkdirSync(outputDirPath, { recursive: true });\n\n writeFileSync(outputPath, html, \"utf-8\");\n pages.push(relPath);\n } catch (err) {\n const relPath = relative(this.pagesDir, fullPath);\n errors.push(`${relPath}: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n }\n };\n\n walk(this.pagesDir);\n return { pages, errors };\n }\n\n // ─── Internal Methods ────────────────────────────────────────────\n\n /**\n * Extract <!-- @key: value --> metadata from HTML.\n */\n private extractMeta(html: string): PageMeta {\n const meta: PageMeta = {};\n let match: RegExpExecArray | null;\n const regex = new RegExp(META_REGEX.source, META_REGEX.flags);\n while ((match = regex.exec(html)) !== null) {\n meta[match[1]] = match[2];\n }\n return meta;\n }\n\n /**\n * Strip metadata comments from HTML.\n */\n private stripMeta(html: string): string {\n return html.replace(META_REGEX, \"\").trim();\n }\n\n /**\n * Process <c-name /> component tags by replacing them with component file contents.\n * Supports nested components up to MAX_COMPONENT_DEPTH.\n */\n private processComponents(html: string, depth = 0): string {\n if (depth >= MAX_COMPONENT_DEPTH) return html;\n if (!COMPONENT_REGEX.test(html)) return html;\n\n // Reset regex state\n const regex = new RegExp(COMPONENT_REGEX.source, COMPONENT_REGEX.flags);\n\n const result = html.replace(regex, (_match, name: string) => {\n const componentPath = join(this.componentsDir, `${name}.html`);\n if (!existsSync(componentPath)) {\n return `<!-- component \"${name}\" not found -->`;\n }\n const content = readFileSync(componentPath, \"utf-8\").trim();\n // Recursively process nested components\n return this.processComponents(content, depth + 1);\n });\n\n return result;\n }\n\n /**\n * Wrap page content in the router target div.\n */\n private wrapPageContent(html: string): string {\n return `<div id=\"clawfire-page\">\\n${html}\\n</div>`;\n }\n\n /**\n * Collect layout files from page directory up to pages root.\n * Returns innermost layout first (closest to page).\n */\n private collectLayouts(pagePath: string): string[] {\n const layouts: string[] = [];\n let dir = dirname(pagePath);\n\n while (dir.startsWith(this.pagesDir)) {\n const layoutPath = join(dir, \"_layout.html\");\n if (existsSync(layoutPath)) {\n layouts.push(layoutPath);\n }\n // Stop at pages root\n if (dir === this.pagesDir) break;\n dir = dirname(dir);\n }\n\n // Reverse: outermost layout applied last (wraps everything)\n // We want to reduce from innermost out, so reverse:\n // page → innermost layout's <slot /> → outer layout's <slot />\n // The array is already [innermost, ..., outermost] from the walk.\n // We apply innermost first, which means page goes into innermost <slot />,\n // then that result goes into the next outer layout's <slot />, etc.\n return layouts;\n }\n\n /**\n * Set or update <title> tag in HTML.\n */\n private setTitle(html: string, title: string): string {\n if (html.includes(\"<title>\")) {\n return html.replace(/<title>[^<]*<\\/title>/, `<title>${title}</title>`);\n }\n if (html.includes(\"</head>\")) {\n return html.replace(\"</head>\", ` <title>${title}</title>\\n</head>`);\n }\n return html;\n }\n}\n","/**\n * Clawfire Environment Variable Manager\n *\n * Manages .env file and .env.descriptions.json for the dev dashboard.\n * Preserves comments and ordering in .env files.\n */\nimport { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface EnvVariable {\n key: string;\n value: string;\n description: string;\n isPlaceholder: boolean;\n line: number;\n}\n\nexport interface EnvData {\n variables: EnvVariable[];\n descriptions: Record<string, string>;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────\n\nconst KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/i;\nconst PLACEHOLDER_PATTERNS = [\n /^YOUR_/i,\n /^CHANGE_ME$/i,\n /^TODO$/i,\n /^REPLACE_/i,\n /^XXX/i,\n /^$/,\n];\n\n// ─── EnvManager ──────────────────────────────────────────────────────\n\nexport class EnvManager {\n private envPath: string;\n private descriptionsPath: string;\n\n constructor(projectDir: string) {\n this.envPath = resolve(projectDir, \".env\");\n this.descriptionsPath = resolve(projectDir, \".env.descriptions.json\");\n }\n\n /** Read all env variables with descriptions and placeholder detection */\n read(): EnvData {\n const descriptions = this.readDescriptions();\n const variables: EnvVariable[] = [];\n\n if (!existsSync(this.envPath)) {\n return { variables, descriptions };\n }\n\n const content = readFileSync(this.envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n // Skip empty lines and comments\n if (!line || line.startsWith(\"#\")) continue;\n\n const eqIdx = line.indexOf(\"=\");\n if (eqIdx === -1) continue;\n\n const key = line.slice(0, eqIdx).trim();\n const value = line.slice(eqIdx + 1).trim();\n\n if (!KEY_PATTERN.test(key)) continue;\n\n variables.push({\n key,\n value,\n description: descriptions[key] || \"\",\n isPlaceholder: isPlaceholder(value),\n line: i + 1,\n });\n }\n\n return { variables, descriptions };\n }\n\n /** Set or update an env variable */\n set(key: string, value: string, description?: string): void {\n if (!KEY_PATTERN.test(key)) {\n throw new Error(`Invalid key: \"${key}\" — must match ${KEY_PATTERN}`);\n }\n\n // Update .env file\n let lines: string[] = [];\n if (existsSync(this.envPath)) {\n lines = readFileSync(this.envPath, \"utf-8\").split(\"\\n\");\n }\n\n let found = false;\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (trimmed.startsWith(\"#\") || !trimmed) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const existingKey = trimmed.slice(0, eqIdx).trim();\n if (existingKey === key) {\n lines[i] = `${key}=${value}`;\n found = true;\n break;\n }\n }\n\n if (!found) {\n // Add to end, ensure trailing newline separation\n if (lines.length > 0 && lines[lines.length - 1].trim() !== \"\") {\n lines.push(`${key}=${value}`);\n } else {\n // Replace trailing empty line\n lines.push(`${key}=${value}`);\n }\n }\n\n writeFileSync(this.envPath, lines.join(\"\\n\"), \"utf-8\");\n\n // Update description if provided\n if (description !== undefined) {\n const descriptions = this.readDescriptions();\n descriptions[key] = description;\n this.writeDescriptions(descriptions);\n }\n }\n\n /** Delete an env variable */\n delete(key: string): void {\n if (!existsSync(this.envPath)) return;\n\n const lines = readFileSync(this.envPath, \"utf-8\").split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\") || !trimmed) return true;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) return true;\n return trimmed.slice(0, eqIdx).trim() !== key;\n });\n\n writeFileSync(this.envPath, filtered.join(\"\\n\"), \"utf-8\");\n\n // Remove description\n const descriptions = this.readDescriptions();\n if (key in descriptions) {\n delete descriptions[key];\n this.writeDescriptions(descriptions);\n }\n }\n\n // ─── Private ─────────────────────────────────────────────────────\n\n private readDescriptions(): Record<string, string> {\n if (!existsSync(this.descriptionsPath)) return {};\n try {\n return JSON.parse(readFileSync(this.descriptionsPath, \"utf-8\"));\n } catch {\n return {};\n }\n }\n\n private writeDescriptions(descriptions: Record<string, string>): void {\n writeFileSync(\n this.descriptionsPath,\n JSON.stringify(descriptions, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction isPlaceholder(value: string): boolean {\n return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(value));\n}\n","/**\n * Clawfire Firebase Status Checker\n *\n * Checks Firebase project health via file inspection and CLI commands.\n * Results are cached for 30 seconds to avoid repeated CLI calls.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execFile } from \"node:child_process\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type ServiceStatus = \"configured\" | \"placeholder\" | \"missing\";\n\nexport interface ServiceInfo {\n name: string;\n status: ServiceStatus;\n detail?: string;\n}\n\nexport interface FirebaseEnvironmentStatus {\n /** CLI info */\n cli: {\n installed: boolean;\n version: string;\n authenticated: boolean;\n user: string;\n };\n /** Active Firebase project */\n project: {\n id: string;\n hasFirebaseJson: boolean;\n };\n /** Per-service status */\n services: ServiceInfo[];\n /** Config placeholder warnings */\n configWarnings: string[];\n /** Timestamp of this check */\n timestamp: number;\n}\n\n// ─── Cache ───────────────────────────────────────────────────────────\n\nlet cachedStatus: FirebaseEnvironmentStatus | null = null;\nlet cacheTime = 0;\nconst CACHE_TTL = 30_000; // 30 seconds\n\n// ─── Main ────────────────────────────────────────────────────────────\n\nexport async function checkFirebaseStatus(\n projectDir: string,\n): Promise<FirebaseEnvironmentStatus> {\n const now = Date.now();\n if (cachedStatus && now - cacheTime < CACHE_TTL) {\n return cachedStatus;\n }\n\n const [fileStatus, cliStatus] = await Promise.all([\n checkFiles(projectDir),\n checkCli(projectDir),\n ]);\n\n const status: FirebaseEnvironmentStatus = {\n cli: cliStatus,\n project: fileStatus.project,\n services: fileStatus.services,\n configWarnings: fileStatus.configWarnings,\n timestamp: now,\n };\n\n cachedStatus = status;\n cacheTime = now;\n return status;\n}\n\n/** Clear the status cache (useful after config changes) */\nexport function clearFirebaseStatusCache(): void {\n cachedStatus = null;\n cacheTime = 0;\n}\n\n// ─── File Checks (sync, fast) ────────────────────────────────────────\n\ninterface FileCheckResult {\n project: { id: string; hasFirebaseJson: boolean };\n services: ServiceInfo[];\n configWarnings: string[];\n}\n\nfunction checkFiles(projectDir: string): FileCheckResult {\n const services: ServiceInfo[] = [];\n const configWarnings: string[] = [];\n\n // firebase.json\n const firebaseJsonPath = resolve(projectDir, \"firebase.json\");\n let hasFirebaseJson = false;\n let firebaseConfig: Record<string, unknown> = {};\n\n if (existsSync(firebaseJsonPath)) {\n hasFirebaseJson = true;\n try {\n firebaseConfig = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n } catch {\n // invalid JSON\n }\n }\n\n // Hosting\n if (firebaseConfig.hosting) {\n services.push({ name: \"Hosting\", status: \"configured\", detail: \"firebase.json\" });\n } else {\n services.push({ name: \"Hosting\", status: \"missing\" });\n }\n\n // Firestore\n const firestoreRulesPath = resolve(projectDir, \"firestore.rules\");\n if (firebaseConfig.firestore) {\n if (existsSync(firestoreRulesPath)) {\n services.push({ name: \"Firestore\", status: \"configured\", detail: \"Rules file found\" });\n } else {\n services.push({ name: \"Firestore\", status: \"placeholder\", detail: \"Configured but no rules file\" });\n }\n } else {\n services.push({ name: \"Firestore\", status: \"missing\" });\n }\n\n // Functions\n const functionsIndexPath = resolve(projectDir, \"functions/index.ts\");\n if (firebaseConfig.functions) {\n if (existsSync(functionsIndexPath)) {\n services.push({ name: \"Functions\", status: \"configured\", detail: \"functions/index.ts found\" });\n } else {\n services.push({ name: \"Functions\", status: \"placeholder\", detail: \"Configured but no entry file\" });\n }\n } else {\n services.push({ name: \"Functions\", status: \"missing\" });\n }\n\n // Storage\n const storageRulesPath = resolve(projectDir, \"storage.rules\");\n if (firebaseConfig.storage) {\n if (existsSync(storageRulesPath)) {\n services.push({ name: \"Storage\", status: \"configured\", detail: \"Rules file found\" });\n } else {\n services.push({ name: \"Storage\", status: \"placeholder\", detail: \"Configured but no rules file\" });\n }\n } else {\n services.push({ name: \"Storage\", status: \"missing\" });\n }\n\n // Firestore indexes\n const indexesPath = resolve(projectDir, \"firestore.indexes.json\");\n if (existsSync(indexesPath)) {\n services.push({ name: \"Indexes\", status: \"configured\", detail: \"firestore.indexes.json\" });\n } else if (firebaseConfig.firestore) {\n services.push({ name: \"Indexes\", status: \"placeholder\", detail: \"No indexes file\" });\n }\n\n // clawfire.config.ts — check for placeholder values\n const configPath = resolve(projectDir, \"clawfire.config.ts\");\n if (existsSync(configPath)) {\n try {\n const configContent = readFileSync(configPath, \"utf-8\");\n const placeholderMatches = configContent.match(/YOUR_[A-Z_]+/g);\n if (placeholderMatches) {\n for (const match of new Set(placeholderMatches)) {\n configWarnings.push(`Placeholder found: ${match}`);\n }\n }\n } catch {\n // ignore read errors\n }\n }\n\n // Project ID from .firebaserc\n let projectId = \"\";\n const firebasercPath = resolve(projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n projectId = rc?.projects?.default || \"\";\n } catch {\n // invalid JSON\n }\n }\n\n return {\n project: { id: projectId, hasFirebaseJson },\n services,\n configWarnings,\n };\n}\n\n// ─── CLI Checks (async, with timeouts) ───────────────────────────────\n\ninterface CliCheckResult {\n installed: boolean;\n version: string;\n authenticated: boolean;\n user: string;\n}\n\nasync function checkCli(projectDir: string): Promise<CliCheckResult> {\n const result: CliCheckResult = {\n installed: false,\n version: \"\",\n authenticated: false,\n user: \"\",\n };\n\n // Check firebase --version\n try {\n const version = await execWithTimeout(\"firebase\", [\"--version\"], projectDir, 5000);\n result.installed = true;\n result.version = version.trim();\n } catch {\n return result; // If CLI not installed, skip auth check\n }\n\n // Check firebase login status\n try {\n const loginOutput = await execWithTimeout(\n \"firebase\",\n [\"login:list\", \"--json\"],\n projectDir,\n 5000,\n );\n const loginData = JSON.parse(loginOutput);\n if (loginData?.result && Array.isArray(loginData.result) && loginData.result.length > 0) {\n result.authenticated = true;\n result.user = loginData.result[0]?.user?.email || loginData.result[0]?.email || \"\";\n }\n } catch {\n // Not authenticated or command failed\n }\n\n return result;\n}\n\n// ─── Firebase SDK Config (auto-fill) ─────────────────────────────────\n\nexport interface FirebaseSdkConfig {\n apiKey?: string;\n authDomain?: string;\n projectId?: string;\n storageBucket?: string;\n messagingSenderId?: string;\n appId?: string;\n}\n\n/**\n * Fetch Firebase web SDK config via CLI.\n * Requires: firebase CLI installed, logged in, active project set.\n *\n * If appId is provided (from saved state), uses it directly.\n * Otherwise:\n * 1. Try `firebase apps:sdkconfig web --json` (works if exactly 1 web app)\n * 2. If that fails, list all apps to find web app IDs\n * 3. If web apps found, retry with specific app ID\n * 4. If no web apps, return clear error\n */\nexport async function fetchFirebaseSdkConfig(\n projectDir: string,\n appId?: string,\n): Promise<FirebaseSdkConfig> {\n // If we have a saved app ID, use it directly\n if (appId) {\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", appId, \"--json\"],\n projectDir,\n 15000,\n );\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);\n }\n throw new Error(\"Could not parse Firebase SDK config from CLI output\");\n }\n\n // Step 1: Try direct sdkconfig (works if project has exactly 1 web app)\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", \"--json\"],\n projectDir,\n 15000,\n );\n\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch {\n // Fall through to Step 2\n }\n\n // Step 2: List all apps to find web app IDs\n let webApps: Array<{ appId: string; displayName: string }> = [];\n try {\n const appsOutput = await execWithTimeout(\n \"firebase\",\n [\"apps:list\", \"--json\"],\n projectDir,\n 15000,\n );\n const appsData = JSON.parse(appsOutput);\n const allApps = appsData?.result || [];\n webApps = allApps\n .filter((a: Record<string, unknown>) => a.platform === \"WEB\")\n .map((a: Record<string, unknown>) => ({\n appId: String(a.appId || \"\"),\n displayName: String(a.displayName || \"\"),\n }))\n .filter((a: { appId: string }) => a.appId);\n } catch {\n // Can't list apps — give generic error\n throw new Error(\n \"Failed to fetch Firebase SDK config. \" +\n \"Make sure you are logged in and have an active project selected.\"\n );\n }\n\n // Step 3: No web apps found\n if (webApps.length === 0) {\n throw new Error(\n \"No web app registered in this Firebase project. \" +\n \"Go to Firebase Console > Project Settings > General > Your apps > Add app (Web) to create one, \" +\n \"then click Auto-fill again.\"\n );\n }\n\n // Step 4: Retry with specific app ID\n const firstAppId = webApps[0].appId;\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", firstAppId, \"--json\"],\n projectDir,\n 15000,\n );\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n throw new Error(`Failed to fetch config for web app ${firstAppId}: ${msg}`);\n }\n\n throw new Error(\"Could not parse Firebase SDK config from CLI output\");\n}\n\n/** Parse the output of `firebase apps:sdkconfig` */\nfunction parseSdkConfigOutput(output: string): FirebaseSdkConfig | null {\n try {\n const data = JSON.parse(output);\n\n // Firebase CLI returns { status: \"success\", result: { sdkConfig: { ... } } }\n if (data?.result?.sdkConfig) {\n return data.result.sdkConfig;\n }\n\n // Some CLI versions return fileContents with the config object as JS\n if (data?.result?.fileContents) {\n const contents = data.result.fileContents as string;\n const config: FirebaseSdkConfig = {};\n const extract = (key: string) => {\n const match = contents.match(new RegExp(`\"${key}\"\\\\s*:\\\\s*\"([^\"]+)\"`));\n return match ? match[1] : undefined;\n };\n config.apiKey = extract(\"apiKey\");\n config.authDomain = extract(\"authDomain\");\n config.projectId = extract(\"projectId\");\n config.storageBucket = extract(\"storageBucket\");\n config.messagingSenderId = extract(\"messagingSenderId\");\n config.appId = extract(\"appId\");\n if (config.apiKey || config.projectId) return config;\n }\n } catch {\n // JSON parse failed\n }\n return null;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction execWithTimeout(\n command: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = execFile(command, args, { cwd, timeout: timeoutMs }, (err, stdout, stderr) => {\n if (err) {\n // Include stderr in error message for better debugging\n const detail = stderr?.trim() || stdout?.trim() || \"\";\n const enriched = new Error(`${err.message}${detail ? \"\\n\" + detail : \"\"}`);\n reject(enriched);\n } else {\n resolve(stdout);\n }\n });\n // Extra safety: kill if timeout is reached\n const timer = setTimeout(() => {\n proc.kill(\"SIGTERM\");\n reject(new Error(\"Timeout\"));\n }, timeoutMs + 500);\n proc.on(\"exit\", () => clearTimeout(timer));\n });\n}\n","/**\n * Clawfire Dev Dashboard HTML Generator\n *\n * Generates the Dashboard tab content for the dev server.\n * Sections: Firebase Setup Wizard, Firebase Status, Config Overview (editable), Environment Variables.\n * All CSS/JS is inline — no external dependencies.\n * Data is lazy-loaded from /__dev/* endpoints.\n */\n\nexport interface DashboardHtmlOptions {\n apiPort: number;\n}\n\nexport function generateDashboardHtml(options: DashboardHtmlOptions): string {\n const { apiPort } = options;\n\n return `\n<div id=\"dashboard-content\" style=\"padding:24px;max-width:1200px;margin:0 auto;font-family:var(--font, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\">\n\n <!-- Loading State -->\n <div id=\"dash-loading\" style=\"text-align:center;padding:60px 0;color:#a3a3a3;\">\n <div style=\"font-size:24px;margin-bottom:8px;\">Loading dashboard...</div>\n </div>\n\n <!-- Dashboard Sections (hidden until loaded) -->\n <div id=\"dash-loaded\" style=\"display:none;\">\n\n <!-- Section 0: Firebase Setup Wizard -->\n <div id=\"setup-wizard\" style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Firebase Setup</h2>\n <div id=\"setup-card\" style=\"padding:20px;border-radius:12px;border:1px solid #2a2a2a;background:#141414;\">\n\n <!-- Step indicators -->\n <div id=\"setup-steps\" style=\"display:flex;gap:0;margin-bottom:20px;overflow:hidden;border-radius:8px;border:1px solid #2a2a2a;\">\n <div id=\"step-ind-1\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#1a1a2e;color:#f97316;border-right:1px solid #2a2a2a;\">\n 1. CLI\n </div>\n <div id=\"step-ind-2\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 2. Login\n </div>\n <div id=\"step-ind-3\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 3. Project\n </div>\n <div id=\"step-ind-4\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 4. Web App\n </div>\n <div id=\"step-ind-5\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;\">\n 5. Auto-fill\n </div>\n </div>\n\n <!-- Step 1: CLI Install -->\n <div id=\"step-1\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step1-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase CLI</div>\n <div id=\"step1-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step1-action\" style=\"display:none;\">\n <button id=\"install-cli-btn\" onclick=\"installFirebaseCli()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Install Firebase CLI\n </button>\n <div id=\"install-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 2: Login -->\n <div id=\"step-2\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step2-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase Authentication</div>\n <div id=\"step2-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step2-action\" style=\"display:none;\">\n <button id=\"login-btn\" onclick=\"startFirebaseLogin(false)\" style=\"padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Login to Firebase\n </button>\n <button id=\"reauth-btn\" onclick=\"startFirebaseLogin(true)\" style=\"display:none;padding:8px 20px;background:#eab308;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Re-authenticate\n </button>\n </div>\n <!-- Login waiting message -->\n <div id=\"login-waiting\" style=\"display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;\">\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px;\">\n <span style=\"display:inline-block;width:14px;height:14px;border:2px solid #3b82f6;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;\"></span>\n <span style=\"color:#e5e5e5;font-size:13px;font-weight:600;\">Waiting for login...</span>\n </div>\n <div style=\"font-size:13px;color:#a3a3a3;\">A terminal window has been opened. Please complete the Firebase login there.</div>\n <div style=\"font-size:12px;color:#666;margin-top:6px;\">This page will update automatically when login is detected.</div>\n </div>\n <div id=\"login-result\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n\n <!-- Step 3: Select Project -->\n <div id=\"step-3\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step3-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase Project</div>\n <div id=\"step3-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step3-action\" style=\"display:none;\">\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <select id=\"project-select\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;font-family:monospace;min-width:280px;outline:none;\">\n <option value=\"\">Loading projects...</option>\n </select>\n <button id=\"select-project-btn\" onclick=\"selectFirebaseProject()\" style=\"padding:8px 20px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Use This Project\n </button>\n <button onclick=\"refreshProjectList()\" style=\"padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;\" title=\"Refresh\">\n Refresh\n </button>\n </div>\n <div id=\"project-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 4: Web App -->\n <div id=\"step-4\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step4-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Web App</div>\n <div id=\"step4-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking web apps...</div>\n </div>\n </div>\n <div id=\"step4-action\" style=\"display:none;\">\n <!-- Existing web apps list -->\n <div id=\"webapp-list\" style=\"display:none;\">\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <select id=\"webapp-select\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;font-family:monospace;min-width:280px;outline:none;\">\n <option value=\"\">Loading web apps...</option>\n </select>\n <button id=\"select-webapp-btn\" onclick=\"selectWebApp()\" style=\"padding:8px 20px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Use This App\n </button>\n <button onclick=\"showCreateWebApp()\" style=\"padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;\">\n + Create New\n </button>\n </div>\n </div>\n <!-- Create new web app form -->\n <div id=\"webapp-create\" style=\"display:none;\">\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:8px;\">No web app found. Create one to get Firebase SDK config:</div>\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <input id=\"webapp-name\" type=\"text\" placeholder=\"My Web App\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;min-width:220px;outline:none;\" />\n <button id=\"create-webapp-btn\" onclick=\"createWebApp()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Create Web App\n </button>\n </div>\n </div>\n <div id=\"webapp-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 5: Done / Auto-fill -->\n <div id=\"step-5\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step5-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Auto-fill Config</div>\n <div id=\"step5-detail\" style=\"font-size:13px;color:#a3a3a3;\">Ready to auto-fill your clawfire.config.ts</div>\n </div>\n </div>\n <div id=\"step5-action\">\n <button id=\"autofill-wizard-btn\" onclick=\"autoFillConfig()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Auto-fill Config Now\n </button>\n <div id=\"autofill-wizard-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- All done banner -->\n <div id=\"setup-done\" style=\"display:none;padding:16px;border-radius:8px;background:#0a1a0a;border:1px solid #22c55e;text-align:center;\">\n <div style=\"font-size:16px;color:#22c55e;font-weight:700;margin-bottom:4px;\">Setup Complete</div>\n <div id=\"setup-done-detail\" style=\"font-size:13px;color:#a3a3a3;\"></div>\n <button onclick=\"startFirebaseLogin(true)\" style=\"margin-top:8px;padding:6px 14px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:12px;cursor:pointer;\">Re-authenticate</button>\n </div>\n </div>\n </div>\n\n <!-- Section 1: Firebase Status -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Firebase Status</h2>\n\n <!-- CLI Banner -->\n <div id=\"cli-banner\" style=\"padding:12px 16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;margin-bottom:16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;\">\n <span id=\"cli-dot\" style=\"width:10px;height:10px;border-radius:50%;background:#666;display:inline-block;flex-shrink:0;\"></span>\n <span id=\"cli-text\" style=\"color:#e5e5e5;font-size:14px;\">Checking CLI...</span>\n <span id=\"cli-project\" style=\"color:#a3a3a3;font-size:12px;margin-left:auto;font-family:monospace;\"></span>\n </div>\n\n <!-- Service Cards Grid -->\n <div id=\"service-grid\" style=\"display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;\"></div>\n </div>\n\n <!-- Section 2: Deploy -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Deploy</h2>\n <div style=\"display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;\">\n <div style=\"padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;\">\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px;\">\n <span style=\"font-size:16px;\">🌐</span>\n <span style=\"font-weight:600;color:#e5e5e5;\">Hosting</span>\n </div>\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:12px;\">Deploy your app to Firebase Hosting</div>\n <button id=\"deploy-hosting-btn\" onclick=\"deployHosting()\" style=\"padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Deploy Hosting\n </button>\n <div id=\"deploy-hosting-status\" style=\"display:none;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;\"></div>\n <div id=\"deploy-hosting-url\" style=\"display:none;margin-top:8px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;\">\n <div style=\"font-size:11px;color:#a3a3a3;margin-bottom:4px;\">Live URL</div>\n <a id=\"deploy-hosting-link\" href=\"#\" target=\"_blank\" style=\"color:#22c55e;font-family:monospace;font-size:14px;text-decoration:none;word-break:break-all;\"></a>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Section 3: Database -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Database</h2>\n <div id=\"db-card\" style=\"padding:20px;border-radius:12px;border:1px solid #2a2a2a;background:#141414;\">\n <!-- Mode Toggle -->\n <div style=\"display:flex;align-items:center;gap:16px;margin-bottom:16px;\">\n <span style=\"font-size:13px;color:#a3a3a3;font-weight:600;\">Mode:</span>\n <label id=\"db-mode-memory\" style=\"display:flex;align-items:center;gap:6px;cursor:pointer;\" onclick=\"switchDbMode('memory')\">\n <span id=\"db-radio-memory\" style=\"width:16px;height:16px;border-radius:50%;border:2px solid #f97316;display:inline-flex;align-items:center;justify-content:center;\">\n <span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>\n </span>\n <span style=\"color:#e5e5e5;font-size:13px;\">In-Memory</span>\n </label>\n <label id=\"db-mode-firestore\" style=\"display:flex;align-items:center;gap:6px;cursor:pointer;\" onclick=\"switchDbMode('firestore')\">\n <span id=\"db-radio-firestore\" style=\"width:16px;height:16px;border-radius:50%;border:2px solid #666;display:inline-flex;align-items:center;justify-content:center;\">\n </span>\n <span style=\"color:#a3a3a3;font-size:13px;\">Firestore</span>\n </label>\n </div>\n\n <!-- Connect to Firestore panel -->\n <div id=\"db-connect-panel\" style=\"padding:16px;border-radius:8px;background:#0a0a0a;border:1px solid #2a2a2a;\">\n <div style=\"font-weight:600;color:#e5e5e5;font-size:14px;margin-bottom:6px;\">Connect to Firestore</div>\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:12px;\">Creates database, deploys open security rules, and enables Firestore mode.</div>\n <button id=\"db-connect-btn\" onclick=\"connectFirestore()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Connect to Firestore\n </button>\n <div style=\"margin-top:10px;font-size:12px;color:#eab308;\">\n ⚠ Open rules (allow all) for dev only. Use /clawfire-model for production rules.\n </div>\n <div id=\"db-connect-status\" style=\"display:none;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;\"></div>\n </div>\n\n <!-- Firestore Connected banner -->\n <div id=\"db-connected-banner\" style=\"display:none;padding:16px;border-radius:8px;background:#0a1a0a;border:1px solid #22c55e;text-align:center;\">\n <div style=\"font-size:14px;color:#22c55e;font-weight:700;\">Firestore Connected</div>\n <div style=\"font-size:12px;color:#a3a3a3;margin-top:4px;\">Data persists across server restarts.</div>\n </div>\n </div>\n </div>\n\n <!-- Section 4: Config Overview -->\n <div style=\"margin-bottom:32px;\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;\">Config Overview</h2>\n <button id=\"autofill-btn\" onclick=\"autoFillConfig()\" style=\"padding:6px 14px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">Auto-fill from Firebase</button>\n </div>\n <div id=\"autofill-status\" style=\"display:none;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;\"></div>\n <div id=\"config-section\" style=\"border-radius:8px;border:1px solid #2a2a2a;background:#141414;overflow:hidden;\">\n <table id=\"config-table\" style=\"width:100%;border-collapse:collapse;font-size:13px;\">\n <thead>\n <tr style=\"border-bottom:1px solid #2a2a2a;\">\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;width:180px;\">Key</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Value</th>\n <th style=\"padding:10px 16px;text-align:right;color:#a3a3a3;font-weight:500;width:80px;\">Action</th>\n </tr>\n </thead>\n <tbody id=\"config-tbody\"></tbody>\n </table>\n <div id=\"config-empty\" style=\"display:none;padding:32px;text-align:center;color:#666;\">\n No clawfire.config.ts found.\n </div>\n </div>\n </div>\n\n <!-- Section 4: Environment Variables -->\n <div style=\"margin-bottom:32px;\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;\">Environment Variables</h2>\n <button id=\"env-add-btn\" onclick=\"showEnvModal()\" style=\"padding:6px 14px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">+ Add Variable</button>\n </div>\n <div id=\"env-table-wrap\" style=\"border-radius:8px;border:1px solid #2a2a2a;background:#141414;overflow:hidden;\">\n <table id=\"env-table\" style=\"width:100%;border-collapse:collapse;font-size:13px;\">\n <thead>\n <tr style=\"border-bottom:1px solid #2a2a2a;\">\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Key</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Value</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Description</th>\n <th style=\"padding:10px 16px;text-align:right;color:#a3a3a3;font-weight:500;width:120px;\">Actions</th>\n </tr>\n </thead>\n <tbody id=\"env-tbody\"></tbody>\n </table>\n <div id=\"env-empty\" style=\"display:none;padding:32px;text-align:center;color:#666;\">\n No environment variables found. Click \"+ Add Variable\" to create one.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Env Modal -->\n <div id=\"env-modal\" style=\"display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;align-items:center;justify-content:center;\">\n <div style=\"background:#1e1e1e;border:1px solid #2a2a2a;border-radius:12px;padding:24px;width:440px;max-width:90vw;\">\n <h3 id=\"modal-title\" style=\"font-size:16px;font-weight:700;color:#e5e5e5;margin-bottom:16px;\">Add Variable</h3>\n <div style=\"margin-bottom:12px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Key</label>\n <input id=\"modal-key\" type=\"text\" placeholder=\"API_KEY\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-family:monospace;font-size:14px;outline:none;\" />\n </div>\n <div style=\"margin-bottom:12px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Value</label>\n <input id=\"modal-value\" type=\"text\" placeholder=\"your-value-here\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-family:monospace;font-size:14px;outline:none;\" />\n </div>\n <div style=\"margin-bottom:20px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Description (optional)</label>\n <input id=\"modal-desc\" type=\"text\" placeholder=\"What this variable is for\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:14px;outline:none;\" />\n </div>\n <div id=\"modal-error\" style=\"display:none;padding:8px 12px;background:#1c0808;border:1px solid #ef4444;border-radius:6px;color:#ef4444;font-size:12px;margin-bottom:12px;\"></div>\n <div style=\"display:flex;gap:8px;justify-content:flex-end;\">\n <button onclick=\"hideEnvModal()\" style=\"padding:8px 16px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;cursor:pointer;font-size:13px;\">Cancel</button>\n <button id=\"modal-save-btn\" onclick=\"saveEnvVar()\" style=\"padding:8px 16px;background:#f97316;border:none;border-radius:6px;color:#000;font-weight:600;cursor:pointer;font-size:13px;\">Save</button>\n </div>\n </div>\n </div>\n</div>\n\n<style>\n@keyframes spin { to { transform: rotate(360deg); } }\n#login-spinner { animation: spin 1s linear infinite; }\n</style>\n\n<script>\n(function() {\n var API = 'http://localhost:${apiPort}';\n var envData = [];\n var configData = [];\n var editingKey = null;\n var loginPollTimer = null;\n\n // ─── Load Dashboard Data ─────────────────────────────────────────\n window._loadDashboard = function() {\n if (window._dashboardLoaded) return;\n window._dashboardLoaded = true;\n Promise.all([\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/config').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/env').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/setup/status').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/db-mode').then(function(r) { return r.json(); }),\n ]).then(function(results) {\n renderFirebaseStatus(results[0]);\n renderConfig(results[1]);\n renderEnvVars(results[2]);\n renderSetupWizard(results[3]);\n renderDbMode(results[4].mode || 'memory');\n document.getElementById('dash-loading').style.display = 'none';\n document.getElementById('dash-loaded').style.display = 'block';\n }).catch(function(err) {\n document.getElementById('dash-loading').innerHTML =\n '<div style=\"color:#ef4444;\">Failed to load dashboard data</div>' +\n '<div style=\"color:#666;font-size:13px;margin-top:8px;\">' + err.message + '</div>';\n });\n };\n\n // ─── Setup Wizard ─────────────────────────────────────────────────\n\n function setStepIndicator(activeStep) {\n for (var i = 1; i <= 5; i++) {\n var el = document.getElementById('step-ind-' + i);\n if (i < activeStep) {\n el.style.background = '#0a1a0a';\n el.style.color = '#22c55e';\n } else if (i === activeStep) {\n el.style.background = '#1a1a2e';\n el.style.color = '#f97316';\n } else {\n el.style.background = '#0a0a0a';\n el.style.color = '#666';\n }\n }\n }\n\n function renderSetupWizard(status) {\n var GREEN = '\\\\u2705';\n var YELLOW = '\\\\u26A0\\\\uFE0F';\n var RED = '\\\\u274C';\n var CIRCLE = '\\\\u26AA';\n\n // Hide all steps first\n for (var i = 1; i <= 5; i++) {\n document.getElementById('step-' + i).style.display = 'none';\n }\n document.getElementById('setup-done').style.display = 'none';\n // Reset login UI state from previous interactions\n document.getElementById('login-waiting').style.display = 'none';\n document.getElementById('login-result').style.display = 'none';\n\n if (status.nextStep === 'done') {\n // All done!\n setStepIndicator(6);\n document.getElementById('setup-done').style.display = 'block';\n var detail = 'Logged in as ' + status.auth.user + ' | Project: ' + status.project.id;\n if (status.webApp && status.webApp.displayName) {\n detail += ' | App: ' + status.webApp.displayName;\n }\n document.getElementById('setup-done-detail').textContent = detail;\n return;\n }\n\n // Step 1: CLI\n var step1 = document.getElementById('step-1');\n step1.style.display = 'block';\n if (status.cli.installed) {\n document.getElementById('step1-icon').textContent = GREEN;\n document.getElementById('step1-detail').textContent = 'Firebase CLI v' + status.cli.version + ' installed';\n document.getElementById('step1-action').style.display = 'none';\n } else {\n document.getElementById('step1-icon').textContent = RED;\n document.getElementById('step1-detail').textContent = 'Firebase CLI is not installed';\n document.getElementById('step1-action').style.display = 'block';\n }\n\n if (status.nextStep === 'install-cli') {\n setStepIndicator(1);\n return;\n }\n\n // Step 2: Auth\n var step2 = document.getElementById('step-2');\n step2.style.display = 'block';\n if (status.auth.authenticated) {\n document.getElementById('step2-icon').textContent = GREEN;\n document.getElementById('step2-detail').textContent = 'Logged in as ' + status.auth.user;\n document.getElementById('step2-action').style.display = 'block';\n document.getElementById('login-btn').style.display = 'none';\n document.getElementById('reauth-btn').style.display = 'inline-block';\n document.getElementById('login-waiting').style.display = 'none';\n } else {\n document.getElementById('step2-icon').textContent = RED;\n document.getElementById('step2-detail').textContent = 'Not logged in to Firebase';\n document.getElementById('step2-action').style.display = 'block';\n document.getElementById('login-btn').style.display = 'inline-block';\n document.getElementById('reauth-btn').style.display = 'none';\n document.getElementById('login-waiting').style.display = 'none';\n }\n\n if (status.nextStep === 'login') {\n setStepIndicator(2);\n return;\n }\n\n // Step 3: Project Selection\n var step3 = document.getElementById('step-3');\n step3.style.display = 'block';\n if (status.project.id) {\n document.getElementById('step3-icon').textContent = GREEN;\n document.getElementById('step3-detail').textContent = 'Active project: ' + status.project.id;\n document.getElementById('step3-action').style.display = 'block';\n } else {\n document.getElementById('step3-icon').textContent = YELLOW;\n document.getElementById('step3-detail').textContent = 'No project selected';\n document.getElementById('step3-action').style.display = 'block';\n }\n\n // Load project list\n loadProjectList(status.project.id);\n\n if (status.nextStep === 'select-project') {\n setStepIndicator(3);\n return;\n }\n\n // Step 4: Web App\n var step4 = document.getElementById('step-4');\n step4.style.display = 'block';\n if (status.webApp && status.webApp.appId) {\n document.getElementById('step4-icon').textContent = GREEN;\n document.getElementById('step4-detail').textContent = 'Web app: ' + (status.webApp.displayName || status.webApp.appId);\n document.getElementById('step4-action').style.display = 'block';\n // Show list with current selection\n loadWebAppList(status.webApp.appId);\n } else {\n document.getElementById('step4-icon').textContent = YELLOW;\n document.getElementById('step4-detail').textContent = 'No web app selected';\n document.getElementById('step4-action').style.display = 'block';\n loadWebAppList('');\n }\n\n if (status.nextStep === 'create-web-app') {\n setStepIndicator(4);\n return;\n }\n\n // Step 5: Auto-fill\n var step5 = document.getElementById('step-5');\n step5.style.display = 'block';\n document.getElementById('step5-icon').textContent = CIRCLE;\n setStepIndicator(5);\n }\n\n // ─── Step 1: Install CLI ──────────────────────────────────────────\n window.installFirebaseCli = function() {\n var btn = document.getElementById('install-cli-btn');\n var status = document.getElementById('install-status');\n btn.disabled = true;\n btn.textContent = 'Installing...';\n status.style.display = 'block';\n status.textContent = 'Running npm install -g firebase-tools... This may take a minute.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/install-cli', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Refresh wizard\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Install Firebase CLI';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Install Firebase CLI';\n });\n };\n\n // ─── Step 2: Login (opens terminal window) ──────────────────────\n window.startFirebaseLogin = function(reauth) {\n var loginBtn = document.getElementById('login-btn');\n var reauthBtn = document.getElementById('reauth-btn');\n var waiting = document.getElementById('login-waiting');\n var result = document.getElementById('login-result');\n\n loginBtn.style.display = 'none';\n reauthBtn.style.display = 'none';\n waiting.style.display = 'block';\n result.style.display = 'none';\n\n fetch(API + '/__dev/setup/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ reauth: !!reauth })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (!data.success) {\n waiting.style.display = 'none';\n result.textContent = data.message;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n loginBtn.style.display = 'inline-block';\n return;\n }\n // Poll setup status until login is detected\n startLoginPoll();\n })\n .catch(function(err) {\n waiting.style.display = 'none';\n result.textContent = 'Failed: ' + err.message;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n loginBtn.style.display = 'inline-block';\n });\n };\n\n function startLoginPoll() {\n if (loginPollTimer) clearInterval(loginPollTimer);\n loginPollTimer = setInterval(function() {\n fetch(API + '/__dev/setup/status')\n .then(function(r) { return r.json(); })\n .then(function(status) {\n if (status.auth.authenticated) {\n clearInterval(loginPollTimer);\n loginPollTimer = null;\n document.getElementById('login-waiting').style.display = 'none';\n var result = document.getElementById('login-result');\n result.textContent = 'Login successful! Logged in as ' + status.auth.user;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Wait for token to fully settle, then refresh wizard + force-load projects\n setTimeout(function() {\n refreshSetupStatus();\n // Extra delay for project list — Firebase CLI needs time after fresh login\n setTimeout(function() { loadProjectList(''); }, 2500);\n }, 2000);\n }\n })\n .catch(function() {});\n }, 3000);\n }\n\n // ─── Step 3: Project Selection ────────────────────────────────────\n function loadProjectList(currentProjectId, retryCount) {\n retryCount = retryCount || 0;\n var select = document.getElementById('project-select');\n select.innerHTML = '<option value=\"\">Loading projects...</option>';\n select.disabled = true;\n\n fetch(API + '/__dev/setup/projects')\n .then(function(r) { return r.json(); })\n .then(function(data) {\n select.innerHTML = '';\n if (data.error) {\n // Auto-retry up to 2 times on error (token might not be ready yet after fresh login)\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">Error: ' + escHtml(data.error) + '</option>';\n return;\n }\n if (!data.projects || data.projects.length === 0) {\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">No projects found</option>';\n return;\n }\n\n select.innerHTML = '<option value=\"\">-- Select a project --</option>';\n data.projects.forEach(function(p) {\n var opt = document.createElement('option');\n opt.value = p.projectId;\n opt.textContent = p.displayName + ' (' + p.projectId + ')';\n if (p.projectId === currentProjectId) opt.selected = true;\n select.appendChild(opt);\n });\n select.disabled = false;\n })\n .catch(function(err) {\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">Failed to load</option>';\n });\n }\n\n window.refreshProjectList = function() {\n loadProjectList('');\n };\n\n window.selectFirebaseProject = function() {\n var select = document.getElementById('project-select');\n var btn = document.getElementById('select-project-btn');\n var status = document.getElementById('project-status');\n var projectId = select.value;\n\n if (!projectId) {\n status.textContent = 'Please select a project first.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n\n btn.disabled = true;\n btn.textContent = 'Setting up...';\n status.textContent = 'Selecting project, detecting web app, auto-filling config...';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1c;border:1px solid #3b82f6;color:#93c5fd;';\n\n fetch(API + '/__dev/setup/select-project', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ projectId: projectId })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n var msg = data.steps ? data.steps.join('\\\\n') : data.message;\n status.textContent = msg;\n status.style.whiteSpace = 'pre-line';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;white-space:pre-line;';\n btn.textContent = 'Done';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This Project';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This Project';\n });\n };\n\n // ─── Step 4: Web App ──────────────────────────────────────────────\n function loadWebAppList(currentAppId, retryCount) {\n retryCount = retryCount || 0;\n var listDiv = document.getElementById('webapp-list');\n var createDiv = document.getElementById('webapp-create');\n var select = document.getElementById('webapp-select');\n\n fetch(API + '/__dev/setup/web-apps')\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) {\n if (retryCount < 2) {\n setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);\n return;\n }\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n document.getElementById('webapp-create').querySelector('div').textContent = 'Error loading apps: ' + data.error;\n return;\n }\n if (!data.apps || data.apps.length === 0) {\n // No web apps — show create form\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n return;\n }\n\n // Show list\n listDiv.style.display = 'block';\n createDiv.style.display = 'none';\n select.innerHTML = '<option value=\"\">-- Select a web app --</option>';\n data.apps.forEach(function(app) {\n var opt = document.createElement('option');\n opt.value = app.appId;\n opt.setAttribute('data-name', app.displayName || '');\n opt.textContent = (app.displayName || 'Unnamed') + ' (' + app.appId.slice(-12) + ')';\n if (app.appId === currentAppId) opt.selected = true;\n select.appendChild(opt);\n });\n select.disabled = false;\n })\n .catch(function() {\n if (retryCount < 2) {\n setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);\n return;\n }\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n });\n }\n\n window.showCreateWebApp = function() {\n document.getElementById('webapp-list').style.display = 'none';\n document.getElementById('webapp-create').style.display = 'block';\n document.getElementById('webapp-name').focus();\n };\n\n window.selectWebApp = function() {\n var select = document.getElementById('webapp-select');\n var status = document.getElementById('webapp-status');\n var appId = select.value;\n if (!appId) {\n status.textContent = 'Please select a web app first.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n var selectedOption = select.options[select.selectedIndex];\n var displayName = selectedOption.getAttribute('data-name') || '';\n\n var btn = document.getElementById('select-webapp-btn');\n btn.disabled = true;\n btn.textContent = 'Saving...';\n\n fetch(API + '/__dev/setup/select-web-app', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ appId: appId, displayName: displayName })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This App';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This App';\n });\n };\n\n window.createWebApp = function() {\n var nameInput = document.getElementById('webapp-name');\n var btn = document.getElementById('create-webapp-btn');\n var status = document.getElementById('webapp-status');\n var displayName = nameInput.value.trim();\n\n if (!displayName) {\n status.textContent = 'Please enter a name for the web app.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n\n btn.disabled = true;\n btn.textContent = 'Creating...';\n status.textContent = 'Creating web app... This may take a moment.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/create-web-app', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ displayName: displayName })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Create Web App';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Create Web App';\n });\n };\n\n // ─── Refresh Setup Status ─────────────────────────────────────────\n function refreshSetupStatus() {\n fetch(API + '/__dev/setup/status')\n .then(function(r) { return r.json(); })\n .then(function(status) { renderSetupWizard(status); })\n .catch(function() {});\n\n // Also refresh firebase status section\n fetch(API + '/__dev/firebase-status')\n .then(function(r) { return r.json(); })\n .then(function(data) { renderFirebaseStatus(data); })\n .catch(function() {});\n }\n\n // ─── Firebase Status ─────────────────────────────────────────────\n function renderFirebaseStatus(data) {\n var dot = document.getElementById('cli-dot');\n var text = document.getElementById('cli-text');\n var proj = document.getElementById('cli-project');\n\n if (!data.cli.installed) {\n dot.style.background = '#ef4444';\n text.textContent = 'Firebase CLI not installed';\n } else if (!data.cli.authenticated) {\n dot.style.background = '#eab308';\n text.textContent = 'Firebase CLI v' + data.cli.version + ' \\\\u2014 Not logged in';\n } else {\n dot.style.background = '#22c55e';\n text.textContent = 'Firebase CLI v' + data.cli.version + ' \\\\u2014 ' + data.cli.user;\n }\n\n if (data.project.id) {\n proj.textContent = 'Project: ' + data.project.id;\n } else {\n proj.textContent = 'No active project';\n proj.style.color = '#eab308';\n }\n\n // Service Cards\n var grid = document.getElementById('service-grid');\n grid.innerHTML = '';\n var statusColors = { configured: '#22c55e', placeholder: '#eab308', missing: '#666' };\n var statusLabels = { configured: 'Ready', placeholder: 'Needs Setup', missing: 'Not Configured' };\n\n var enableableServices = { 'Hosting': 'hosting', 'Firestore': 'firestore', 'Storage': 'storage' };\n data.services.forEach(function(svc) {\n var card = document.createElement('div');\n card.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;';\n var enableBtn = '';\n if (svc.status === 'missing' && enableableServices[svc.name]) {\n enableBtn = '<button onclick=\"enableService(\\\\'' + enableableServices[svc.name] + '\\\\')\" style=\"margin-top:8px;padding:4px 12px;background:#f97316;color:#000;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;\">Enable</button>';\n }\n card.innerHTML =\n '<div style=\"display:flex;align-items:center;gap:8px;margin-bottom:6px;\">' +\n '<span style=\"width:8px;height:8px;border-radius:50%;background:' + statusColors[svc.status] + ';display:inline-block;\"></span>' +\n '<span style=\"font-weight:600;color:#e5e5e5;\">' + svc.name + '</span>' +\n '</div>' +\n '<div style=\"font-size:12px;color:' + statusColors[svc.status] + ';\">' + statusLabels[svc.status] + '</div>' +\n (svc.detail ? '<div style=\"font-size:11px;color:#666;margin-top:4px;\">' + svc.detail + '</div>' : '') +\n enableBtn;\n grid.appendChild(card);\n });\n\n if (data.configWarnings && data.configWarnings.length > 0) {\n var warningCard = document.createElement('div');\n warningCard.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #eab308;background:#1a1a0a;grid-column:1/-1;';\n warningCard.innerHTML =\n '<div style=\"font-weight:600;color:#eab308;margin-bottom:6px;\">Config Warnings</div>' +\n data.configWarnings.map(function(w) {\n return '<div style=\"font-size:12px;color:#eab308;font-family:monospace;\">' + escHtml(w) + '</div>';\n }).join('');\n grid.appendChild(warningCard);\n }\n }\n\n // ─── Config Overview (editable) ──────────────────────────────────\n function renderConfig(data) {\n var tbody = document.getElementById('config-tbody');\n var empty = document.getElementById('config-empty');\n var table = document.getElementById('config-table');\n\n configData = (data && data.fields) ? data.fields : [];\n\n if (configData.length === 0) {\n table.style.display = 'none';\n empty.style.display = 'block';\n return;\n }\n\n table.style.display = 'table';\n empty.style.display = 'none';\n tbody.innerHTML = '';\n\n configData.forEach(function(field) {\n var tr = document.createElement('tr');\n tr.style.borderBottom = '1px solid #2a2a2a';\n tr.id = 'cfg-row-' + field.key;\n var color = field.isPlaceholder ? '#eab308' : '#a3a3a3';\n var badge = field.isPlaceholder ? ' <span style=\"background:#eab30822;color:#eab308;padding:1px 6px;border-radius:4px;font-size:10px;\">PLACEHOLDER</span>' : '';\n tr.innerHTML =\n '<td style=\"padding:10px 16px;color:#e5e5e5;font-family:monospace;white-space:nowrap;\">' + escHtml(field.key) + '</td>' +\n '<td style=\"padding:10px 16px;font-family:monospace;\">' +\n '<span id=\"cfg-val-' + field.key + '\" style=\"color:' + color + ';\">' + escHtml(field.value) + '</span>' +\n badge +\n '<input id=\"cfg-input-' + field.key + '\" type=\"text\" value=\"' + escHtml(field.value) + '\" style=\"display:none;width:100%;padding:4px 8px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:4px;color:#e5e5e5;font-family:monospace;font-size:13px;outline:none;\" />' +\n '</td>' +\n '<td style=\"padding:10px 16px;text-align:right;white-space:nowrap;\">' +\n '<button id=\"cfg-edit-' + field.key + '\" onclick=\"editConfigField(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"background:none;border:none;color:#3b82f6;cursor:pointer;font-size:12px;padding:4px 8px;\">Edit</button>' +\n '<button id=\"cfg-save-' + field.key + '\" onclick=\"saveConfigField(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"display:none;background:none;border:none;color:#22c55e;cursor:pointer;font-size:12px;padding:4px 8px;\">Save</button>' +\n '<button id=\"cfg-cancel-' + field.key + '\" onclick=\"cancelConfigEdit(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"display:none;background:none;border:none;color:#a3a3a3;cursor:pointer;font-size:12px;padding:4px 8px;\">Cancel</button>' +\n '</td>';\n tbody.appendChild(tr);\n });\n }\n\n window.editConfigField = function(key) {\n var valSpan = document.getElementById('cfg-val-' + key);\n var input = document.getElementById('cfg-input-' + key);\n var editBtn = document.getElementById('cfg-edit-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n var cancelBtn = document.getElementById('cfg-cancel-' + key);\n if (!valSpan || !input) return;\n var badges = valSpan.parentNode.querySelectorAll('span[style*=\"PLACEHOLDER\"]');\n for (var i = 0; i < badges.length; i++) badges[i].style.display = 'none';\n valSpan.style.display = 'none';\n input.style.display = 'block';\n input.focus();\n input.select();\n editBtn.style.display = 'none';\n saveBtn.style.display = 'inline';\n cancelBtn.style.display = 'inline';\n };\n\n window.cancelConfigEdit = function(key) {\n var valSpan = document.getElementById('cfg-val-' + key);\n var input = document.getElementById('cfg-input-' + key);\n var editBtn = document.getElementById('cfg-edit-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n var cancelBtn = document.getElementById('cfg-cancel-' + key);\n if (!valSpan || !input) return;\n var badges = valSpan.parentNode.querySelectorAll('span[style*=\"border-radius\"]');\n for (var i = 0; i < badges.length; i++) badges[i].style.display = '';\n valSpan.style.display = '';\n input.style.display = 'none';\n input.value = valSpan.textContent;\n editBtn.style.display = 'inline';\n saveBtn.style.display = 'none';\n cancelBtn.style.display = 'none';\n };\n\n window.saveConfigField = function(key) {\n var input = document.getElementById('cfg-input-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n if (!input) return;\n var value = input.value;\n saveBtn.textContent = '...';\n fetch(API + '/__dev/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set', key: key, value: value })\n }).then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) { alert('Error: ' + data.error); saveBtn.textContent = 'Save'; return; }\n return fetch(API + '/__dev/config').then(function(r) { return r.json(); });\n })\n .then(function(data) { if (data) renderConfig(data); })\n .catch(function(err) { alert('Failed: ' + err.message); saveBtn.textContent = 'Save'; });\n };\n\n // ─── Auto-fill from Firebase ─────────────────────────────────────\n window.autoFillConfig = function() {\n var btn = document.getElementById('autofill-btn');\n var wizBtn = document.getElementById('autofill-wizard-btn');\n var status = document.getElementById('autofill-status');\n var wizStatus = document.getElementById('autofill-wizard-status');\n if (btn) { btn.disabled = true; btn.textContent = 'Fetching...'; }\n if (wizBtn) { wizBtn.disabled = true; wizBtn.textContent = 'Fetching...'; }\n if (status) status.style.display = 'none';\n if (wizStatus) wizStatus.style.display = 'none';\n\n fetch(API + '/__dev/firebase-sdk-config')\n .then(function(r) {\n if (!r.ok) {\n return r.json().catch(function() { return { error: 'Server error (' + r.status + ')' }; });\n }\n return r.json();\n })\n .then(function(data) {\n if (data.error) {\n showAutoFillResult(false, data.error);\n return;\n }\n\n var fields = {};\n if (data.apiKey) fields.apiKey = data.apiKey;\n if (data.authDomain) fields.authDomain = data.authDomain;\n if (data.projectId) fields.projectId = data.projectId;\n if (data.storageBucket) fields.storageBucket = data.storageBucket;\n if (data.appId) fields.appId = data.appId;\n\n var count = Object.keys(fields).length;\n if (count === 0) {\n showAutoFillResult(false, 'No web app config found. Create a Web app in Firebase Console first.');\n return;\n }\n\n return fetch(API + '/__dev/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set-multiple', fields: fields })\n }).then(function(r) { return r.json(); })\n .then(function(result) {\n if (result.error) {\n showAutoFillResult(false, 'Error saving: ' + result.error);\n } else {\n showAutoFillResult(true, count + ' fields updated from Firebase project!');\n return fetch(API + '/__dev/config').then(function(r) { return r.json(); })\n .then(function(cfg) { renderConfig(cfg); });\n }\n });\n })\n .catch(function(err) {\n showAutoFillResult(false, 'Failed: ' + err.message);\n })\n .finally(function() {\n if (btn) { btn.disabled = false; btn.textContent = 'Auto-fill from Firebase'; }\n if (wizBtn) { wizBtn.disabled = false; wizBtn.textContent = 'Auto-fill Config Now'; }\n });\n };\n\n function showAutoFillResult(success, message) {\n var targets = ['autofill-status', 'autofill-wizard-status'];\n targets.forEach(function(id) {\n var el = document.getElementById(id);\n if (!el) return;\n el.textContent = message;\n if (success) {\n el.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n } else {\n el.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n });\n }\n\n // ─── Deploy Hosting ──────────────────────────────────────────────\n window.deployHosting = function() {\n var btn = document.getElementById('deploy-hosting-btn');\n var status = document.getElementById('deploy-hosting-status');\n var urlBox = document.getElementById('deploy-hosting-url');\n btn.disabled = true;\n btn.textContent = 'Deploying...';\n status.textContent = 'Deploying to Firebase Hosting... This may take up to 2 minutes.';\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n urlBox.style.display = 'none';\n\n fetch(API + '/__dev/deploy/hosting', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n if (data.url) {\n var link = document.getElementById('deploy-hosting-link');\n link.href = data.url;\n link.textContent = data.url;\n urlBox.style.display = 'block';\n }\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n btn.disabled = false;\n btn.textContent = 'Deploy Hosting';\n // Refresh firebase status\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Deploy Hosting';\n });\n };\n\n // ─── Enable Service ────────────────────────────────────────────────\n window.enableService = function(service) {\n fetch(API + '/__dev/enable-service', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service: service })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n // Refresh firebase status to show updated service cards\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n } else {\n alert('Failed to enable ' + service + ': ' + data.message);\n }\n })\n .catch(function(err) {\n alert('Error: ' + err.message);\n });\n };\n\n // ─── Database Mode ──────────────────────────────────────────────\n function loadDbMode() {\n fetch(API + '/__dev/db-mode')\n .then(function(r) { return r.json(); })\n .then(function(data) { renderDbMode(data.mode || 'memory'); })\n .catch(function() { renderDbMode('memory'); });\n }\n\n function renderDbMode(mode) {\n var radioMem = document.getElementById('db-radio-memory');\n var radioFs = document.getElementById('db-radio-firestore');\n var connectPanel = document.getElementById('db-connect-panel');\n var connectedBanner = document.getElementById('db-connected-banner');\n var memLabel = document.getElementById('db-mode-memory');\n var fsLabel = document.getElementById('db-mode-firestore');\n\n if (mode === 'firestore') {\n radioMem.innerHTML = '';\n radioMem.style.borderColor = '#666';\n radioFs.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>';\n radioFs.style.borderColor = '#f97316';\n memLabel.querySelector('span:last-child').style.color = '#a3a3a3';\n fsLabel.querySelector('span:last-child').style.color = '#e5e5e5';\n connectPanel.style.display = 'none';\n connectedBanner.style.display = 'block';\n } else {\n radioMem.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>';\n radioMem.style.borderColor = '#f97316';\n radioFs.innerHTML = '';\n radioFs.style.borderColor = '#666';\n memLabel.querySelector('span:last-child').style.color = '#e5e5e5';\n fsLabel.querySelector('span:last-child').style.color = '#a3a3a3';\n connectPanel.style.display = 'block';\n connectedBanner.style.display = 'none';\n }\n }\n\n window.switchDbMode = function(mode) {\n var status = document.getElementById('db-connect-status');\n status.style.display = 'none';\n\n fetch(API + '/__dev/db-mode', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ mode: mode })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n renderDbMode(data.mode);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n });\n };\n\n window.connectFirestore = function() {\n var btn = document.getElementById('db-connect-btn');\n var status = document.getElementById('db-connect-status');\n btn.disabled = true;\n btn.textContent = 'Connecting...';\n status.textContent = 'Creating Firestore database... This may take a minute.';\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/create-firestore', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Switch UI to firestore mode\n setTimeout(function() { renderDbMode('firestore'); }, 1500);\n // Refresh firebase status\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n btn.disabled = false;\n btn.textContent = 'Connect to Firestore';\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Connect to Firestore';\n });\n };\n\n // ─── Environment Variables ───────────────────────────────────────\n function renderEnvVars(data) {\n envData = data.variables || [];\n var tbody = document.getElementById('env-tbody');\n var empty = document.getElementById('env-empty');\n var table = document.getElementById('env-table');\n\n if (envData.length === 0) {\n table.style.display = 'none';\n empty.style.display = 'block';\n return;\n }\n\n table.style.display = 'table';\n empty.style.display = 'none';\n tbody.innerHTML = '';\n\n envData.forEach(function(v) {\n var tr = document.createElement('tr');\n tr.style.borderBottom = '1px solid #2a2a2a';\n var maskedVal = v.value ? v.value.slice(0, 3) + '...' + v.value.slice(-2) : '';\n if (v.value.length <= 5) maskedVal = '***';\n var placeholderBadge = v.isPlaceholder ? ' <span style=\"background:#eab30822;color:#eab308;padding:1px 6px;border-radius:4px;font-size:10px;\">PLACEHOLDER</span>' : '';\n tr.innerHTML =\n '<td style=\"padding:10px 16px;color:#e5e5e5;font-family:monospace;white-space:nowrap;\">' + escHtml(v.key) + placeholderBadge + '</td>' +\n '<td style=\"padding:10px 16px;color:#a3a3a3;font-family:monospace;\">' +\n '<span class=\"env-val\" data-key=\"' + escAttr(v.key) + '\" data-masked=\"' + escAttr(maskedVal) + '\" data-full=\"' + escAttr(v.value) + '\">' + escHtml(maskedVal) + '</span>' +\n ' <button onclick=\"toggleReveal(this)\" style=\"background:none;border:none;color:#666;cursor:pointer;font-size:11px;padding:2px 4px;\">reveal</button>' +\n '</td>' +\n '<td style=\"padding:10px 16px;color:#666;font-size:12px;\">' + escHtml(v.description || '') + '</td>' +\n '<td style=\"padding:10px 16px;text-align:right;white-space:nowrap;\">' +\n '<button onclick=\"editEnvVar(\\\\'' + escAttr(v.key) + '\\\\')\" style=\"background:none;border:none;color:#3b82f6;cursor:pointer;font-size:12px;padding:4px 8px;\">Edit</button>' +\n '<button onclick=\"deleteEnvVar(\\\\'' + escAttr(v.key) + '\\\\')\" style=\"background:none;border:none;color:#ef4444;cursor:pointer;font-size:12px;padding:4px 8px;\">Delete</button>' +\n '</td>';\n tbody.appendChild(tr);\n });\n }\n\n // ─── Env Modal ───────────────────────────────────────────────────\n window.showEnvModal = function(key) {\n editingKey = key || null;\n var modal = document.getElementById('env-modal');\n var title = document.getElementById('modal-title');\n var keyInput = document.getElementById('modal-key');\n var valInput = document.getElementById('modal-value');\n var descInput = document.getElementById('modal-desc');\n var errorEl = document.getElementById('modal-error');\n errorEl.style.display = 'none';\n\n if (editingKey) {\n title.textContent = 'Edit Variable';\n keyInput.value = editingKey;\n keyInput.readOnly = true;\n keyInput.style.opacity = '0.5';\n var existing = envData.find(function(v) { return v.key === editingKey; });\n valInput.value = existing ? existing.value : '';\n descInput.value = existing ? existing.description : '';\n } else {\n title.textContent = 'Add Variable';\n keyInput.value = '';\n keyInput.readOnly = false;\n keyInput.style.opacity = '1';\n valInput.value = '';\n descInput.value = '';\n }\n\n modal.style.display = 'flex';\n (editingKey ? valInput : keyInput).focus();\n };\n\n window.hideEnvModal = function() {\n document.getElementById('env-modal').style.display = 'none';\n editingKey = null;\n };\n\n window.editEnvVar = function(key) {\n showEnvModal(key);\n };\n\n window.saveEnvVar = function() {\n var key = document.getElementById('modal-key').value.trim();\n var value = document.getElementById('modal-value').value;\n var desc = document.getElementById('modal-desc').value.trim();\n var errorEl = document.getElementById('modal-error');\n\n if (!key) {\n errorEl.textContent = 'Key is required';\n errorEl.style.display = 'block';\n return;\n }\n if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) {\n errorEl.textContent = 'Invalid key format. Use UPPER_SNAKE_CASE.';\n errorEl.style.display = 'block';\n return;\n }\n\n fetch(API + '/__dev/env', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set', key: key, value: value, description: desc || undefined })\n }).then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) {\n errorEl.textContent = data.error;\n errorEl.style.display = 'block';\n return;\n }\n hideEnvModal();\n return fetch(API + '/__dev/env').then(function(r) { return r.json(); });\n })\n .then(function(data) { if (data) renderEnvVars(data); })\n .catch(function(err) {\n errorEl.textContent = err.message;\n errorEl.style.display = 'block';\n });\n };\n\n window.deleteEnvVar = function(key) {\n if (!confirm('Delete ' + key + '?')) return;\n fetch(API + '/__dev/env', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'delete', key: key })\n }).then(function() {\n return fetch(API + '/__dev/env').then(function(r) { return r.json(); });\n }).then(function(data) { renderEnvVars(data); })\n .catch(function() {});\n };\n\n window.toggleReveal = function(btn) {\n var span = btn.previousElementSibling;\n var masked = span.getAttribute('data-masked');\n var full = span.getAttribute('data-full');\n if (span.textContent === masked) {\n span.textContent = full;\n btn.textContent = 'hide';\n } else {\n span.textContent = masked;\n btn.textContent = 'reveal';\n }\n };\n\n // ─── Helpers ─────────────────────────────────────────────────────\n function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"'); }\n function escAttr(s) { return String(s).replace(/&/g,'&').replace(/'/g,\"\\\\\\\\'\").replace(/\"/g,'"').replace(/</g,'<'); }\n})();\n</script>`;\n}\n","/**\n * Clawfire Firebase Setup Orchestration\n *\n * Manages the full Firebase setup flow:\n * 1. Check/install Firebase CLI\n * 2. Open a real terminal for `firebase login` (TTY required)\n * 3. List available projects\n * 4. Select project → auto-fill config\n *\n * All operations are dev-only (localhost).\n */\nimport { execFile, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport { tmpdir, platform, homedir } from \"node:os\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface SetupStatus {\n /** Firebase CLI */\n cli: {\n installed: boolean;\n version: string;\n };\n /** Auth state */\n auth: {\n authenticated: boolean;\n user: string;\n };\n /** Active project */\n project: {\n id: string;\n hasFirebaserc: boolean;\n };\n /** Web App */\n webApp: {\n appId: string;\n displayName: string;\n };\n /** Overall readiness */\n ready: boolean;\n /** Current step the user should do next */\n nextStep: \"install-cli\" | \"login\" | \"select-project\" | \"create-web-app\" | \"done\";\n}\n\nexport interface FirebaseProject {\n projectId: string;\n displayName: string;\n projectNumber: string;\n state: string;\n}\n\nexport interface WebApp {\n appId: string;\n displayName: string;\n}\n\n/** Persisted setup state — saved to .clawfire-setup.json */\nexport interface SetupState {\n projectId?: string;\n webAppId?: string;\n webAppDisplayName?: string;\n dbMode?: \"memory\" | \"firestore\";\n lastUpdated?: string;\n}\n\n// ─── Firebase Setup Manager ──────────────────────────────────────────\n\nexport class FirebaseSetup {\n private projectDir: string;\n private stateFilePath: string;\n\n constructor(projectDir: string) {\n this.projectDir = projectDir;\n this.stateFilePath = resolve(projectDir, \".clawfire-setup.json\");\n }\n\n // ─── State Persistence ────────────────────────────────────────────\n\n loadState(): SetupState {\n try {\n if (existsSync(this.stateFilePath)) {\n return JSON.parse(readFileSync(this.stateFilePath, \"utf-8\"));\n }\n } catch {\n // corrupt file — start fresh\n }\n return {};\n }\n\n saveState(partial: Partial<SetupState>): void {\n const current = this.loadState();\n const merged = { ...current, ...partial, lastUpdated: new Date().toISOString() };\n writeFileSync(this.stateFilePath, JSON.stringify(merged, null, 2) + \"\\n\", \"utf-8\");\n }\n\n // ─── Status Check ──────────────────────────────────────────────────\n\n async getStatus(): Promise<SetupStatus> {\n const savedState = this.loadState();\n\n const status: SetupStatus = {\n cli: { installed: false, version: \"\" },\n auth: { authenticated: false, user: \"\" },\n project: { id: \"\", hasFirebaserc: false },\n webApp: { appId: savedState.webAppId || \"\", displayName: savedState.webAppDisplayName || \"\" },\n ready: false,\n nextStep: \"install-cli\",\n };\n\n // 1. Check CLI\n try {\n const version = await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n status.cli.installed = true;\n status.cli.version = version.trim();\n } catch {\n status.nextStep = \"install-cli\";\n return status;\n }\n\n // 2. Check auth — use login:list and also verify token works\n try {\n const loginOutput = await this.execTimeout(\n \"firebase\",\n [\"login:list\", \"--json\"],\n 5000,\n );\n const loginData = JSON.parse(loginOutput);\n if (loginData?.result && Array.isArray(loginData.result) && loginData.result.length > 0) {\n status.auth.authenticated = true;\n status.auth.user = loginData.result[0]?.user?.email || loginData.result[0]?.email || \"\";\n }\n } catch {\n // Not authenticated\n }\n\n if (!status.auth.authenticated) {\n status.nextStep = \"login\";\n return status;\n }\n\n // 3. Check active project\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n status.project.hasFirebaserc = true;\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n status.project.id = rc?.projects?.default || \"\";\n } catch {\n // invalid JSON\n }\n }\n\n if (!status.project.id) {\n status.nextStep = \"select-project\";\n return status;\n }\n\n // 4. Check web app (from saved state)\n if (!status.webApp.appId) {\n status.nextStep = \"create-web-app\";\n return status;\n }\n\n status.ready = true;\n status.nextStep = \"done\";\n return status;\n }\n\n // ─── CLI Install ───────────────────────────────────────────────────\n\n async installCli(): Promise<{ success: boolean; message: string }> {\n try {\n // Check if already installed\n try {\n await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n return { success: true, message: \"Firebase CLI is already installed.\" };\n } catch {\n // Not installed, proceed\n }\n\n // Install via npm\n await this.execTimeout(\n \"npm\",\n [\"install\", \"-g\", \"firebase-tools\"],\n 120000, // 2 min timeout for install\n );\n\n // Verify installation\n try {\n const version = await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n return { success: true, message: `Firebase CLI v${version.trim()} installed successfully.` };\n } catch {\n return { success: false, message: \"Installation completed but CLI not found in PATH. Try restarting your terminal.\" };\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to install Firebase CLI: ${msg}` };\n }\n }\n\n // ─── Login via Terminal ───────────────────────────────────────────\n\n /**\n * Opens a new terminal window and runs `firebase login`.\n *\n * Firebase CLI requires a real TTY for interactive login.\n * Instead of trying to fake a TTY, we open an actual terminal.\n * The dashboard polls getStatus() to detect when login completes.\n *\n * macOS: Creates a .command file and opens it (Terminal.app)\n * Linux: Tries common terminal emulators\n * Windows: Opens a new cmd window\n */\n openLoginTerminal(reauth: boolean = false): { success: boolean; message: string } {\n const cmd = reauth ? \"firebase login --reauth\" : \"firebase login\";\n const os = platform();\n\n try {\n if (os === \"darwin\") {\n // macOS: .command files auto-open in Terminal.app — no special permissions needed\n // After login completes, osascript closes the terminal window matching our filename\n const scriptPath = join(tmpdir(), \"clawfire-firebase-login.command\");\n writeFileSync(scriptPath, [\n \"#!/bin/bash\",\n `cd \"${this.projectDir}\"`,\n cmd,\n 'echo \"\"',\n 'echo \"Login complete! Closing in 3 seconds...\"',\n \"sleep 3\",\n // Spawn osascript in background to close this specific terminal window, then exit\n '(sleep 1 && osascript -e \\'tell application \"Terminal\" to close (every window whose name contains \"clawfire-firebase-login\")\\' 2>/dev/null) &',\n \"exit 0\",\n ].join(\"\\n\"), { mode: 0o755 });\n\n const child = spawn(\"open\", [scriptPath], { detached: true, stdio: \"ignore\" });\n child.unref();\n } else if (os === \"win32\") {\n // Windows: open a new cmd window that auto-closes after login\n const child = spawn(\"cmd\", [\"/c\", \"start\", \"cmd\", \"/c\", `${cmd} && timeout /t 3 >nul`], {\n cwd: this.projectDir,\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n } else {\n // Linux: create script, try common terminal emulators\n const scriptPath = join(tmpdir(), \"clawfire-firebase-login.sh\");\n writeFileSync(scriptPath, [\n \"#!/bin/bash\",\n `cd \"${this.projectDir}\"`,\n cmd,\n 'echo \"\"',\n 'echo \"Login complete! Closing in 3 seconds...\"',\n \"sleep 3\",\n \"exit 0\",\n ].join(\"\\n\"), { mode: 0o755 });\n\n const terminals = [\n { cmd: \"x-terminal-emulator\", args: [\"-e\", scriptPath] },\n { cmd: \"gnome-terminal\", args: [\"--\", \"bash\", scriptPath] },\n { cmd: \"konsole\", args: [\"-e\", \"bash\", scriptPath] },\n { cmd: \"xfce4-terminal\", args: [\"-e\", scriptPath] },\n { cmd: \"xterm\", args: [\"-e\", scriptPath] },\n ];\n\n let opened = false;\n for (const t of terminals) {\n try {\n const child = spawn(t.cmd, t.args, { detached: true, stdio: \"ignore\" });\n child.unref();\n // If spawn didn't throw, it at least started\n child.on(\"error\", () => {});\n opened = true;\n break;\n } catch {\n continue;\n }\n }\n\n if (!opened) {\n return {\n success: false,\n message: `Could not find a terminal emulator. Please run \"${cmd}\" manually in your terminal.`,\n };\n }\n }\n\n return {\n success: true,\n message: \"Terminal window opened. Please complete the login in the new terminal.\",\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return {\n success: false,\n message: `Failed to open terminal: ${msg}. Please run \"${cmd}\" manually in your terminal.`,\n };\n }\n }\n\n // ─── Project Listing ───────────────────────────────────────────────\n\n async listProjects(): Promise<{ projects: FirebaseProject[]; error?: string }> {\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"projects:list\", \"--json\"],\n 30000, // 30s — can be slow on first call\n );\n\n const data = JSON.parse(output);\n\n if (data?.result && Array.isArray(data.result)) {\n const projects: FirebaseProject[] = data.result.map((p: Record<string, unknown>) => ({\n projectId: p.projectId || \"\",\n displayName: p.displayName || p.projectId || \"\",\n projectNumber: p.projectNumber || \"\",\n state: p.lifecycleState || p.state || \"ACTIVE\",\n }));\n return { projects };\n }\n\n return { projects: [], error: \"Unexpected response format\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n // Common cases\n if (msg.includes(\"authenticate\") || msg.includes(\"login\") || msg.includes(\"credential\")) {\n return { projects: [], error: \"Session expired. Please re-authenticate first.\" };\n }\n if (msg.includes(\"Timeout\")) {\n return { projects: [], error: \"Request timed out. Please try again.\" };\n }\n return { projects: [], error: msg };\n }\n }\n\n // ─── Project Selection ─────────────────────────────────────────────\n\n async selectProject(projectId: string): Promise<{ success: boolean; message: string }> {\n if (!projectId || !/^[a-z0-9-]+$/.test(projectId)) {\n return { success: false, message: \"Invalid project ID format.\" };\n }\n\n // Clear web app if project changed (different project may have different web apps)\n const savedState = this.loadState();\n if (savedState.projectId && savedState.projectId !== projectId) {\n this.saveState({ projectId, webAppId: undefined, webAppDisplayName: undefined });\n } else {\n this.saveState({ projectId });\n }\n\n try {\n // Set project via firebase use\n await this.execTimeout(\n \"firebase\",\n [\"use\", projectId],\n 10000,\n );\n\n // Also update .firebaserc directly for reliability\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n let rc: Record<string, unknown> = {};\n if (existsSync(firebasercPath)) {\n try {\n rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n } catch {\n rc = {};\n }\n }\n if (!rc.projects || typeof rc.projects !== \"object\") {\n rc.projects = {};\n }\n (rc.projects as Record<string, string>).default = projectId;\n writeFileSync(firebasercPath, JSON.stringify(rc, null, 2) + \"\\n\", \"utf-8\");\n\n return { success: true, message: `Active project set to \"${projectId}\".` };\n } catch {\n // If firebase use fails, still try writing .firebaserc directly\n try {\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n const rc = {\n projects: { default: projectId },\n };\n writeFileSync(firebasercPath, JSON.stringify(rc, null, 2) + \"\\n\", \"utf-8\");\n return { success: true, message: `Active project set to \"${projectId}\" (via .firebaserc).` };\n } catch (writeErr) {\n const msg = writeErr instanceof Error ? writeErr.message : \"Unknown error\";\n return { success: false, message: `Failed to set project: ${msg}` };\n }\n }\n }\n\n // ─── Web App Management ───────────────────────────────────────────\n\n async listWebApps(): Promise<{ apps: WebApp[]; error?: string }> {\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"apps:list\", \"--json\"],\n 15000,\n );\n const data = JSON.parse(output);\n const allApps = data?.result || [];\n const webApps: WebApp[] = allApps\n .filter((a: Record<string, unknown>) => a.platform === \"WEB\")\n .map((a: Record<string, unknown>) => ({\n appId: String(a.appId || \"\"),\n displayName: String(a.displayName || \"\"),\n }))\n .filter((a: { appId: string }) => a.appId);\n return { apps: webApps };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { apps: [], error: msg };\n }\n }\n\n async createWebApp(displayName: string): Promise<{ success: boolean; appId?: string; message: string }> {\n if (!displayName || displayName.length < 1) {\n return { success: false, message: \"Display name is required.\" };\n }\n\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"apps:create\", \"web\", displayName, \"--json\"],\n 30000,\n );\n const data = JSON.parse(output);\n const appId = data?.result?.appId || \"\";\n if (appId) {\n // Save to persistent state\n this.saveState({ webAppId: appId, webAppDisplayName: displayName });\n return { success: true, appId, message: `Web app \"${displayName}\" created successfully.` };\n }\n return { success: false, message: \"App created but could not retrieve app ID.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to create web app: ${msg}` };\n }\n }\n\n selectWebApp(appId: string, displayName: string): { success: boolean; message: string } {\n this.saveState({ webAppId: appId, webAppDisplayName: displayName });\n return { success: true, message: `Web app \"${displayName}\" selected.` };\n }\n\n // ─── Deploy ─────────────────────────────────────────────────────────\n\n async deployHosting(targets = \"hosting\"): Promise<{ success: boolean; url?: string; message: string }> {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n if (!existsSync(firebaseJsonPath)) {\n return { success: false, message: \"firebase.json not found. Enable hosting first.\" };\n }\n\n try {\n const config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n if (!config.hosting) {\n return { success: false, message: \"Hosting not configured in firebase.json. Click Enable first.\" };\n }\n } catch {\n return { success: false, message: \"Invalid firebase.json.\" };\n }\n\n const timeoutMs = targets.includes(\"functions\") ? 300000 : 120000;\n\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"deploy\", \"--only\", targets, \"--json\"],\n timeoutMs,\n );\n\n // Parse URL from deploy output\n let url = \"\";\n try {\n const data = JSON.parse(output);\n if (data?.result) {\n for (const key of Object.keys(data.result)) {\n if (key.startsWith(\"hosting\") && typeof data.result[key] === \"object\") {\n url = data.result[key]?.url || data.result[key]?.site?.url || \"\";\n if (url) break;\n }\n }\n }\n } catch {\n // Try regex fallback\n const urlMatch = output.match(/https:\\/\\/[a-z0-9-]+\\.web\\.app/i);\n if (urlMatch) url = urlMatch[0];\n }\n\n // Construct URL from project ID as last resort\n if (!url) {\n const state = this.loadState();\n if (state.projectId) {\n url = `https://${state.projectId}.web.app`;\n }\n }\n\n const label = targets.includes(\"functions\") ? \"Hosting + Functions\" : \"Hosting\";\n return { success: true, url: url || undefined, message: `${label} deployed successfully!` };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Deploy failed: ${msg}` };\n }\n }\n\n // ─── Full Deploy Pipeline ────────────────────────────────────────────\n\n /**\n * Sync environment variables to functions/.env for production.\n * Copies project root .env vars + injects CLAWFIRE_FIREBASE_CONFIG.\n */\n syncEnvToFunctions(firebaseConfig?: Record<string, string>): { success: boolean; message: string; count: number } {\n const functionsDir = resolve(this.projectDir, \"functions\");\n if (!existsSync(functionsDir)) {\n return { success: false, message: \"functions/ directory not found.\", count: 0 };\n }\n\n const lines: string[] = [];\n\n // 1. Copy project root .env vars (strip 'export ' prefix, skip build-only vars)\n const rootEnvPath = resolve(this.projectDir, \".env\");\n if (existsSync(rootEnvPath)) {\n const content = readFileSync(rootEnvPath, \"utf-8\");\n for (const rawLine of content.split(\"\\n\")) {\n let trimmed = rawLine.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n // Strip bash 'export ' prefix\n if (trimmed.startsWith(\"export \")) {\n trimmed = trimmed.slice(7);\n }\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n // Skip build-only / dev-only vars\n if (key === \"NPM_TOKEN\" || key === \"NODE_ENV\") continue;\n lines.push(trimmed);\n }\n }\n\n // 2. Inject CLAWFIRE_FIREBASE_CONFIG from clawfire.config.ts\n if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {\n // Remove existing entry if present\n const filtered = lines.filter((l) => !l.startsWith(\"CLAWFIRE_FIREBASE_CONFIG=\"));\n filtered.push(`CLAWFIRE_FIREBASE_CONFIG=${JSON.stringify(firebaseConfig)}`);\n lines.length = 0;\n lines.push(...filtered);\n }\n\n const functionsEnvPath = resolve(functionsDir, \".env\");\n writeFileSync(functionsEnvPath, lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n return { success: true, message: `Synced ${lines.length} env vars to functions/.env`, count: lines.length };\n }\n\n /**\n * Ensure firebase.json is fully configured for deployment:\n * - functions config exists\n * - hosting has cleanUrls\n * - hosting has API rewrite (/api/** → api function) before catch-all\n *\n * Fixes existing configs created by older versions.\n */\n ensureDeployConfig(): { success: boolean; message: string; changes: string[] } {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n if (!existsSync(firebaseJsonPath)) {\n return { success: false, message: \"firebase.json not found.\", changes: [] };\n }\n\n try {\n const config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n const changes: string[] = [];\n\n // 1. Ensure functions config\n if (!config.functions) {\n config.functions = {\n source: \"functions\",\n runtime: \"nodejs20\",\n codebase: \"clawfire\",\n };\n changes.push(\"Added functions config\");\n }\n\n // 2. Ensure hosting has cleanUrls\n if (config.hosting && !config.hosting.cleanUrls) {\n config.hosting.cleanUrls = true;\n changes.push(\"Added cleanUrls\");\n }\n\n // 3. Ensure hosting has API rewrite before the catch-all\n if (config.hosting) {\n if (!config.hosting.rewrites) {\n config.hosting.rewrites = [];\n }\n const rewrites = config.hosting.rewrites as Array<{ source: string; function?: string; destination?: string }>;\n const hasApiRewrite = rewrites.some(\n (r) => r.source === \"/api/**\" && r.function === \"api\",\n );\n if (!hasApiRewrite) {\n const apiRewrite = { source: \"/api/**\", function: \"api\" };\n // Insert before the catch-all \"**\" rewrite\n const catchAllIndex = rewrites.findIndex((r) => r.source === \"**\");\n if (catchAllIndex >= 0) {\n rewrites.splice(catchAllIndex, 0, apiRewrite);\n } else {\n // No catch-all — add API rewrite + catch-all\n rewrites.push(apiRewrite);\n rewrites.push({ source: \"**\", destination: \"/index.html\" });\n }\n changes.push(\"Added API rewrite (/api/** → Cloud Function)\");\n }\n }\n\n if (changes.length > 0) {\n writeFileSync(firebaseJsonPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n }\n\n return {\n success: true,\n message: changes.length > 0 ? changes.join(\", \") : \"Deploy config up to date.\",\n changes,\n };\n } catch {\n return { success: false, message: \"Invalid firebase.json.\", changes: [] };\n }\n }\n\n /**\n * Install dependencies and build functions for deployment.\n */\n async buildFunctions(): Promise<{ success: boolean; message: string }> {\n const functionsDir = resolve(this.projectDir, \"functions\");\n if (!existsSync(functionsDir)) {\n return { success: false, message: \"functions/ directory not found.\" };\n }\n\n const pkgPath = resolve(functionsDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return { success: false, message: \"functions/package.json not found.\" };\n }\n\n // 1. Install/update dependencies (always run to ensure latest versions)\n try {\n await this.execTimeout(\"npm\", [\"install\"], 120000, functionsDir);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `npm install in functions/ failed: ${msg}` };\n }\n\n // 2. Build: try 'npm run build', fallback to direct tsc\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.scripts?.build) {\n await this.execTimeout(\"npm\", [\"run\", \"build\"], 60000, functionsDir);\n } else {\n // Fallback: run tsc directly from functions/\n const tsconfigPath = resolve(functionsDir, \"tsconfig.json\");\n if (existsSync(tsconfigPath)) {\n await this.execTimeout(\"npx\", [\"tsc\"], 60000, functionsDir);\n }\n }\n return { success: true, message: \"Functions built successfully.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Functions build failed: ${msg}` };\n }\n }\n\n // ─── Service Enable ────────────────────────────────────────────────\n\n enableService(service: \"hosting\" | \"firestore\" | \"storage\"): { success: boolean; message: string } {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n let config: Record<string, unknown> = {};\n\n if (existsSync(firebaseJsonPath)) {\n try {\n config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n } catch {\n config = {};\n }\n }\n\n switch (service) {\n case \"hosting\": {\n if (!config.hosting) {\n config.hosting = {\n public: \"public\",\n ignore: [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n cleanUrls: true,\n rewrites: [\n { source: \"/api/**\", function: \"api\" },\n { source: \"**\", destination: \"/index.html\" },\n ],\n };\n }\n break;\n }\n case \"firestore\": {\n if (!config.firestore) {\n config.firestore = {\n rules: \"firestore.rules\",\n indexes: \"firestore.indexes.json\",\n };\n }\n const rulesPath = resolve(this.projectDir, \"firestore.rules\");\n if (!existsSync(rulesPath)) {\n writeFileSync(\n rulesPath,\n \"rules_version = '2';\\nservice cloud.firestore {\\n match /databases/{database}/documents {\\n match /{document=**} {\\n allow read, write: if request.auth != null;\\n }\\n }\\n}\\n\",\n );\n }\n const indexesPath = resolve(this.projectDir, \"firestore.indexes.json\");\n if (!existsSync(indexesPath)) {\n writeFileSync(indexesPath, JSON.stringify({ indexes: [], fieldOverrides: [] }, null, 2) + \"\\n\");\n }\n break;\n }\n case \"storage\": {\n if (!config.storage) {\n config.storage = { rules: \"storage.rules\" };\n }\n const storageRulesPath = resolve(this.projectDir, \"storage.rules\");\n if (!existsSync(storageRulesPath)) {\n writeFileSync(\n storageRulesPath,\n \"rules_version = '2';\\nservice firebase.storage {\\n match /b/{bucket}/o {\\n match /{allPaths=**} {\\n allow read, write: if request.auth != null;\\n }\\n }\\n}\\n\",\n );\n }\n break;\n }\n }\n\n writeFileSync(firebaseJsonPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return { success: true, message: `${service} enabled in firebase.json.` };\n }\n\n // ─── Firestore Database Automation ──────────────────────────────────\n\n /**\n * Read Firebase CLI's stored OAuth token and exchange for access token.\n * No gcloud CLI needed — uses the token from `firebase login`.\n */\n private async getFirebaseAccessToken(): Promise<string | null> {\n const home = homedir();\n const configPath = join(home, \".config\", \"configstore\", \"firebase-tools.json\");\n\n if (!existsSync(configPath)) return null;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n let refreshToken: string | null = null;\n\n // Multi-account format (Firebase CLI v11+)\n if (config?.activeAccounts) {\n const activeEmail = config?.user?.email;\n if (activeEmail && config.activeAccounts[activeEmail]?.tokens?.refresh_token) {\n refreshToken = config.activeAccounts[activeEmail].tokens.refresh_token;\n }\n if (!refreshToken) {\n for (const account of Object.values(config.activeAccounts) as any[]) {\n if (account?.tokens?.refresh_token) {\n refreshToken = account.tokens.refresh_token;\n break;\n }\n }\n }\n }\n\n // Legacy single-account format\n if (!refreshToken && config?.tokens?.refresh_token) {\n refreshToken = config.tokens.refresh_token;\n }\n\n if (!refreshToken) return null;\n\n // Exchange refresh token for access token using Firebase CLI's public OAuth client\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: \"563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com\",\n client_secret: \"j9iVZfS8kkCEFUPaAeJV0sAi\",\n });\n\n const res = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (res.ok) {\n const data = (await res.json()) as { access_token?: string };\n return data.access_token || null;\n }\n } catch {\n // Token read/exchange failed — will fallback to other methods\n }\n\n return null;\n }\n\n /**\n * Enable Firestore API using Google Service Usage REST API.\n * Uses Firebase CLI's stored auth token — no gcloud CLI needed.\n * Falls back to gcloud CLI, then manual URL.\n */\n private async enableFirestoreApi(): Promise<{ success: boolean; message: string }> {\n const state = this.loadState();\n let projectId = state.projectId || \"\";\n if (!projectId) {\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n projectId = rc?.projects?.default || \"\";\n } catch {}\n }\n }\n\n if (!projectId) {\n return { success: false, message: \"No project ID found. Select a project first.\" };\n }\n\n // Method 1: REST API with Firebase CLI's stored OAuth token (no gcloud needed)\n try {\n const accessToken = await this.getFirebaseAccessToken();\n if (accessToken) {\n const res = await fetch(\n `https://serviceusage.googleapis.com/v1/projects/${projectId}/services/firestore.googleapis.com:enable`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n if (res.ok) {\n return { success: true, message: \"Firestore API enabled via REST API.\" };\n }\n // 409 = already enabled, also fine\n if (res.status === 409) {\n return { success: true, message: \"Firestore API already enabled.\" };\n }\n }\n } catch {\n // REST API call failed — try fallback methods\n }\n\n // Method 2: Fallback to gcloud CLI\n try {\n await this.execTimeout(\n \"gcloud\",\n [\"services\", \"enable\", \"firestore.googleapis.com\", \"--project\", projectId],\n 60000,\n );\n return { success: true, message: \"Firestore API enabled via gcloud.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n if (!(msg.includes(\"ENOENT\") || msg.includes(\"not found\") || msg.includes(\"command not found\"))) {\n return { success: false, message: `Failed to enable Firestore API: ${msg}` };\n }\n }\n\n // Method 3: Manual fallback\n return {\n success: false,\n message: `Failed to enable Firestore API automatically. Please enable it manually:\\nhttps://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=${projectId}`,\n };\n }\n\n /**\n * Create Firestore database via CLI.\n * Automatically enables Firestore API if needed.\n * Handles \"ALREADY_EXISTS\" gracefully — returns success.\n */\n async createFirestoreDatabase(location = \"nam5\"): Promise<{ success: boolean; message: string }> {\n const dbArgs = [\"firestore:databases:create\", \"(default)\", \"--location\", location, \"--json\"];\n\n // Helper: attempt database creation, return categorized result\n const tryCreate = async (): Promise<{ ok: boolean; alreadyExists: boolean; needsApi: boolean; msg: string }> => {\n try {\n await this.execTimeout(\"firebase\", dbArgs, 60000);\n return { ok: true, alreadyExists: false, needsApi: false, msg: \"\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n if (msg.includes(\"ALREADY_EXISTS\") || msg.includes(\"already exists\")) {\n return { ok: true, alreadyExists: true, needsApi: false, msg };\n }\n if (msg.includes(\"403\") || msg.includes(\"has not been used\") || msg.includes(\"is disabled\")) {\n return { ok: false, alreadyExists: false, needsApi: true, msg };\n }\n return { ok: false, alreadyExists: false, needsApi: false, msg };\n }\n };\n\n // First attempt\n const first = await tryCreate();\n if (first.ok) {\n return { success: true, message: first.alreadyExists ? \"Firestore database already exists.\" : `Firestore database created (location: ${location}).` };\n }\n if (!first.needsApi) {\n return { success: false, message: `Failed to create Firestore database: ${first.msg}` };\n }\n\n // API not enabled — enable it\n const enableResult = await this.enableFirestoreApi();\n if (!enableResult.success) {\n return enableResult;\n }\n\n // Retry with increasing waits: 5s, 5s, 10s, 10s, 15s (total 45s)\n const waits = [5000, 5000, 10000, 10000, 15000];\n for (let i = 0; i < waits.length; i++) {\n await new Promise((r) => setTimeout(r, waits[i]));\n const retry = await tryCreate();\n if (retry.ok) {\n return { success: true, message: `Firestore API enabled and database created (location: ${location}).` };\n }\n if (!retry.needsApi) {\n // Different error (not API-related) — stop retrying\n return { success: false, message: `Failed to create Firestore database: ${retry.msg}` };\n }\n }\n\n return {\n success: false,\n message: \"Firestore API was enabled but database creation timed out waiting for propagation. Please wait a minute and try again.\",\n };\n }\n\n /**\n * Deploy open Firestore security rules for dev testing.\n * Writes `allow read, write: if true` rules and deploys them.\n */\n async deployFirestoreRules(openForDev = true): Promise<{ success: boolean; message: string }> {\n const rulesPath = resolve(this.projectDir, \"firestore.rules\");\n\n try {\n // Write open rules for dev\n if (openForDev) {\n writeFileSync(\n rulesPath,\n \"rules_version = '2';\\nservice cloud.firestore {\\n match /databases/{database}/documents {\\n match /{document=**} {\\n allow read, write: if true;\\n }\\n }\\n}\\n\",\n );\n }\n\n // Deploy rules\n await this.execTimeout(\n \"firebase\",\n [\"deploy\", \"--only\", \"firestore:rules\", \"--json\"],\n 60000,\n );\n return { success: true, message: \"Firestore rules deployed.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to deploy Firestore rules: ${msg}` };\n }\n }\n\n // ─── Helpers ───────────────────────────────────────────────────────\n\n private execTimeout(command: string, args: string[], timeoutMs: number, cwd?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = execFile(command, args, { cwd: cwd || this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {\n if (err) {\n // Include stderr in error message for better debugging\n const detail = stderr?.trim() || stdout?.trim() || \"\";\n const enriched = new Error(`${err.message}${detail ? \"\\n\" + detail : \"\"}`);\n reject(enriched);\n } else {\n resolve(stdout);\n }\n });\n const timer = setTimeout(() => {\n proc.kill(\"SIGTERM\");\n reject(new Error(\"Timeout\"));\n }, timeoutMs + 500);\n proc.on(\"exit\", () => clearTimeout(timer));\n });\n }\n\n /** Cleanup resources */\n destroy(): void {\n // No persistent processes to clean up\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBA8HO,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAyUuB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["import_node_path","import_node_fs","import_fs","import_path","import_node_fs","import_node_path","import_node_fs","import_node_path","resolve","import_node_child_process","import_node_fs","import_node_path","resolve","http","resolve","filePath","isPlaceholder"]}
|
|
1
|
+
{"version":3,"sources":["../src/dev.ts","../src/dev/dev-server.ts","../src/core/schema.ts","../src/core/errors.ts","../src/core/logger.ts","../src/firebase/auth.ts","../src/routing/router.ts","../src/routing/discover.ts","../src/playground/html.ts","../src/dev/watcher.ts","../src/dev/page-compiler.ts","../src/dev/env-manager.ts","../src/dev/firebase-status.ts","../src/dev/dashboard-html.ts","../src/dev/firebase-setup.ts"],"sourcesContent":["/**\n * clawfire/dev — Development Server with Hot Reload\n *\n * @example\n * ```ts\n * import { startDevServer } from \"clawfire/dev\";\n *\n * startDevServer({ port: 3456 });\n * ```\n */\nexport { DevServer, startDevServer, type DevServerOptions } from \"./dev/index.js\";\nexport { FileWatcher, type WatchEvent, type WatchEventType } from \"./dev/index.js\";\n","/**\n * Clawfire Development Server\n *\n * Two-port architecture:\n * - Frontend Server (port 3000): pages, public/ serving, HMR, API proxy, SPA fallback\n * - API Server (port 3456): API routes, Playground, SSE\n */\nimport http from \"node:http\";\nimport { resolve, join, relative, extname } from \"node:path\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport type { APIContract } from \"../core/schema.js\";\nimport { ClawfireRouter, createRouter, type RouterOptions } from \"../routing/router.js\";\nimport { discoverRoutes } from \"../routing/discover.js\";\nimport { generatePlaygroundHtml } from \"../playground/html.js\";\nimport { FileWatcher, type WatchEvent } from \"./watcher.js\";\nimport { PageCompiler } from \"./page-compiler.js\";\nimport { logger } from \"../core/logger.js\";\nimport { EnvManager } from \"./env-manager.js\";\nimport { checkFirebaseStatus, clearFirebaseStatusCache, fetchFirebaseSdkConfig } from \"./firebase-status.js\";\nimport { generateDashboardHtml } from \"./dashboard-html.js\";\nimport { FirebaseSetup } from \"./firebase-setup.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface DevServerOptions {\n /** Project root path */\n projectDir?: string;\n /** Frontend server port (default: 3000) */\n port?: number;\n /** API server port (default: 3456) */\n apiPort?: number;\n /** Router options */\n routerOptions?: Partial<RouterOptions>;\n /** Hot reload enabled (default: true) */\n hotReload?: boolean;\n /** Change detection debounce ms (default: 150) */\n debounceMs?: number;\n /** Callback to manually register routes (instead of file-based) */\n onSetupRoutes?: (router: ClawfireRouter) => void | Promise<void>;\n}\n\ninterface SSEClient {\n id: number;\n res: http.ServerResponse;\n}\n\n// ─── MIME Types ──────────────────────────────────────────────────────\n\nconst MIME_TYPES: Record<string, string> = {\n html: \"text/html; charset=utf-8\",\n css: \"text/css; charset=utf-8\",\n js: \"application/javascript; charset=utf-8\",\n mjs: \"application/javascript; charset=utf-8\",\n json: \"application/json; charset=utf-8\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n webp: \"image/webp\",\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n eot: \"application/vnd.ms-fontobject\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n pdf: \"application/pdf\",\n txt: \"text/plain; charset=utf-8\",\n xml: \"application/xml; charset=utf-8\",\n};\n\n/** File extensions treated as non-HTML static assets */\nconst STATIC_EXTENSIONS = new Set([\n \".css\", \".js\", \".mjs\", \".json\", \".png\", \".jpg\", \".jpeg\", \".gif\", \".svg\",\n \".ico\", \".webp\", \".woff\", \".woff2\", \".ttf\", \".eot\", \".mp4\", \".webm\",\n \".mp3\", \".wav\", \".pdf\", \".txt\", \".xml\", \".map\",\n]);\n\n// ─── HMR Script ─────────────────────────────────────────────────────\n\nfunction generateHmrScript(port: number): string {\n return `\n<script data-clawfire-hmr>\n(function() {\n var dot, status;\n function createBanner() {\n var banner = document.createElement('div');\n banner.id = 'clawfire-dev-banner';\n banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;padding:6px 16px;background:#1a1a2e;color:#f97316;font-size:12px;font-family:monospace;z-index:99999;display:flex;align-items:center;gap:8px;border-top:1px solid #2a2a2a;';\n banner.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;\" id=\"clawfire-dot\"></span><span>Clawfire Dev</span><span style=\"color:#666;margin-left:auto;\" id=\"clawfire-status\">Connected</span>';\n document.body.appendChild(banner);\n dot = document.getElementById('clawfire-dot');\n status = document.getElementById('clawfire-status');\n }\n\n function refreshCss() {\n var links = document.querySelectorAll('link[rel=\"stylesheet\"]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (href) {\n var url = new URL(href, location.href);\n url.searchParams.set('_hmr', Date.now().toString());\n links[i].setAttribute('href', url.toString());\n }\n }\n var styles = document.querySelectorAll('style[data-href]');\n for (var j = 0; j < styles.length; j++) {\n var dataHref = styles[j].getAttribute('data-href');\n if (dataHref) {\n fetch(dataHref + '?_hmr=' + Date.now())\n .then(function(r) { return r.text(); })\n .then(function(css) { styles[j].textContent = css; })\n .catch(function() {});\n }\n }\n if (status) status.textContent = 'CSS updated';\n setTimeout(function() { if (status) status.textContent = 'Connected'; }, 1500);\n }\n\n var reconnectTimer;\n function connect() {\n var es = new EventSource('http://localhost:${port}/__dev/events');\n es.onopen = function() {\n if (!dot) createBanner();\n dot.style.background = '#22c55e';\n status.textContent = 'Connected';\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n };\n es.onmessage = function(e) {\n try {\n var data = JSON.parse(e.data);\n if (data.type === 'connected') return;\n if (data.type === 'css-change') {\n refreshCss();\n return;\n }\n if (data.type === 'error') {\n if (dot) dot.style.background = '#ef4444';\n if (status) status.textContent = 'Error: ' + (data.message || 'reload failed');\n return;\n }\n // frontend-change, route-change, page-change, component-change → full reload\n if (dot) dot.style.background = '#eab308';\n if (status) status.textContent = 'Reloading...';\n setTimeout(function() { location.reload(); }, 300);\n } catch(err) {}\n };\n es.onerror = function() {\n es.close();\n if (dot) dot.style.background = '#ef4444';\n if (status) status.textContent = 'Disconnected';\n reconnectTimer = setTimeout(connect, 2000);\n };\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', function() { connect(); });\n } else {\n connect();\n }\n})();\n</script>`;\n}\n\n// ─── Client-Side Router Script ──────────────────────────────────────\n\nfunction generateRouterScript(): string {\n return `\n<script data-clawfire-router>\n(function() {\n function updateActiveNav() {\n var path = location.pathname;\n var links = document.querySelectorAll('#nav-links a[href]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (!href || href.startsWith('http')) continue;\n var isActive = (path === '/' && href === '/') || (href !== '/' && path.startsWith(href));\n links[i].setAttribute('data-active', isActive ? 'true' : 'false');\n }\n }\n\n function navigate(url) {\n var target = new URL(url, location.href);\n // Only handle same-origin, non-hash navigation\n if (target.origin !== location.origin) return false;\n if (target.pathname === location.pathname && target.hash) return false;\n\n fetch(target.pathname, {\n headers: { 'X-Clawfire-Partial': 'true' }\n })\n .then(function(res) {\n if (!res.ok) throw new Error('Page not found');\n return res.json();\n })\n .then(function(data) {\n // Update page content\n var container = document.getElementById('clawfire-page');\n if (container) {\n container.innerHTML = data.html;\n // Execute scripts in new content\n var scripts = container.querySelectorAll('script');\n for (var i = 0; i < scripts.length; i++) {\n var newScript = document.createElement('script');\n if (scripts[i].src) {\n newScript.src = scripts[i].src;\n } else {\n newScript.textContent = scripts[i].textContent;\n }\n scripts[i].parentNode.replaceChild(newScript, scripts[i]);\n }\n }\n // Update title\n if (data.meta && data.meta.title) {\n document.title = data.meta.title;\n }\n // Update URL\n history.pushState(null, '', target.pathname);\n // Update nav active state\n updateActiveNav();\n // Dispatch event for page scripts\n document.dispatchEvent(new CustomEvent('clawfire:navigate', { detail: { path: data.path } }));\n // Scroll to top\n window.scrollTo(0, 0);\n })\n .catch(function(err) {\n // Fallback: full page navigation\n location.href = url;\n });\n\n return true;\n }\n\n // Intercept link clicks\n document.addEventListener('click', function(e) {\n var anchor = e.target.closest ? e.target.closest('a[href]') : null;\n if (!anchor) return;\n var href = anchor.getAttribute('href');\n if (!href) return;\n // Skip external links, new-tab links, modified clicks\n if (href.startsWith('http') || href.startsWith('//')) return;\n if (anchor.target === '_blank') return;\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;\n if (anchor.hasAttribute('download')) return;\n\n e.preventDefault();\n navigate(href);\n });\n\n // Handle back/forward\n window.addEventListener('popstate', function() {\n navigate(location.pathname);\n });\n\n // Set initial active nav state\n updateActiveNav();\n})();\n</script>`;\n}\n\n// ─── Production Router Script (for static hosting) ─────────────────\n\nfunction generateProductionRouterScript(): string {\n return `\n<script data-clawfire-router>\n(function() {\n function updateActiveNav() {\n var path = location.pathname;\n var links = document.querySelectorAll('#nav-links a[href]');\n for (var i = 0; i < links.length; i++) {\n var href = links[i].getAttribute('href');\n if (!href || href.startsWith('http')) continue;\n var isActive = (path === '/' && href === '/') || (href !== '/' && path.startsWith(href));\n links[i].setAttribute('data-active', isActive ? 'true' : 'false');\n }\n }\n\n function navigate(url) {\n var target = new URL(url, location.href);\n if (target.origin !== location.origin) return false;\n if (target.pathname === location.pathname && target.hash) return false;\n\n fetch(target.pathname)\n .then(function(res) {\n if (!res.ok) throw new Error('Page not found');\n return res.text();\n })\n .then(function(html) {\n // Parse the fetched full HTML and extract #clawfire-page content\n var parser = new DOMParser();\n var doc = parser.parseFromString(html, 'text/html');\n var newPage = doc.getElementById('clawfire-page');\n if (!newPage) throw new Error('No #clawfire-page found');\n\n var container = document.getElementById('clawfire-page');\n if (container) {\n container.innerHTML = newPage.innerHTML;\n // Execute scripts in new content\n var scripts = container.querySelectorAll('script');\n for (var i = 0; i < scripts.length; i++) {\n var newScript = document.createElement('script');\n if (scripts[i].src) {\n newScript.src = scripts[i].src;\n } else {\n newScript.textContent = scripts[i].textContent;\n }\n scripts[i].parentNode.replaceChild(newScript, scripts[i]);\n }\n }\n // Update title from fetched document\n var newTitle = doc.querySelector('title');\n if (newTitle) document.title = newTitle.textContent || '';\n // Update URL\n history.pushState(null, '', target.pathname);\n updateActiveNav();\n document.dispatchEvent(new CustomEvent('clawfire:navigate', { detail: { path: target.pathname } }));\n window.scrollTo(0, 0);\n })\n .catch(function() {\n // Fallback: full page navigation\n location.href = url;\n });\n\n return true;\n }\n\n document.addEventListener('click', function(e) {\n var anchor = e.target.closest ? e.target.closest('a[href]') : null;\n if (!anchor) return;\n var href = anchor.getAttribute('href');\n if (!href) return;\n if (href.startsWith('http') || href.startsWith('//')) return;\n if (anchor.target === '_blank') return;\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;\n if (anchor.hasAttribute('download')) return;\n\n e.preventDefault();\n navigate(href);\n });\n\n window.addEventListener('popstate', function() {\n navigate(location.pathname);\n });\n\n updateActiveNav();\n})();\n</script>`;\n}\n\n// ─── Dev Server ──────────────────────────────────────────────────────\n\nexport class DevServer {\n private frontendServer: http.Server | null = null;\n private apiServer: http.Server | null = null;\n private router: ClawfireRouter;\n private watcher: FileWatcher | null = null;\n private frontendSseClients: SSEClient[] = [];\n private apiSseClients: SSEClient[] = [];\n private sseIdCounter = 0;\n private options: Required<DevServerOptions>;\n private routesDir: string;\n private schemasDir: string;\n private publicDir: string;\n private pagesDir: string;\n private componentsDir: string;\n private playgroundHtml = \"\";\n private importCounter = 0;\n private isReloading = false;\n private pageCompiler: PageCompiler;\n private envManager: EnvManager;\n private firebaseSetup: FirebaseSetup;\n\n constructor(options: DevServerOptions = {}) {\n this.options = {\n projectDir: options.projectDir || process.cwd(),\n port: options.port || 3000,\n apiPort: options.apiPort || 3456,\n routerOptions: options.routerOptions || {},\n hotReload: options.hotReload !== false,\n debounceMs: options.debounceMs || 150,\n onSetupRoutes: options.onSetupRoutes || (() => {}),\n };\n\n this.routesDir = resolve(this.options.projectDir, \"functions/routes\");\n this.schemasDir = resolve(this.options.projectDir, \"functions/schemas\");\n this.publicDir = resolve(this.options.projectDir, \"public\");\n this.pagesDir = resolve(this.options.projectDir, \"app/pages\");\n this.componentsDir = resolve(this.options.projectDir, \"app/components\");\n this.pageCompiler = new PageCompiler(this.options.projectDir);\n this.envManager = new EnvManager(this.options.projectDir);\n this.firebaseSetup = new FirebaseSetup(this.options.projectDir);\n\n this.router = createRouter({\n cors: [\"*\"],\n rateLimit: 0,\n ...this.options.routerOptions,\n });\n }\n\n // ─── Lifecycle ──────────────────────────────────────────────────────\n\n async start(): Promise<void> {\n await this.loadRoutes();\n this.regeneratePlayground();\n\n // Create API server (port 3456)\n this.apiServer = http.createServer((req, res) => this.handleApiRequest(req, res));\n\n // Create Frontend server (port 3000)\n this.frontendServer = http.createServer((req, res) => this.handleFrontendRequest(req, res));\n\n if (this.options.hotReload) {\n this.startWatcher();\n }\n\n // Start API server first\n await new Promise<void>((resolve, reject) => {\n this.apiServer!.listen(this.options.apiPort, () => resolve());\n this.apiServer!.on(\"error\", reject);\n });\n\n // Then start frontend server\n await new Promise<void>((resolve, reject) => {\n this.frontendServer!.listen(this.options.port, () => resolve());\n this.frontendServer!.on(\"error\", reject);\n });\n\n this.printStartupBanner();\n }\n\n async stop(): Promise<void> {\n this.watcher?.close();\n this.firebaseSetup.destroy();\n\n for (const client of [...this.frontendSseClients, ...this.apiSseClients]) {\n client.res.end();\n }\n this.frontendSseClients = [];\n this.apiSseClients = [];\n this.router.destroy();\n\n if (this.apiServer) {\n await new Promise<void>((resolve) => {\n this.apiServer!.close(() => resolve());\n });\n }\n if (this.frontendServer) {\n await new Promise<void>((resolve) => {\n this.frontendServer!.close(() => resolve());\n });\n }\n }\n\n // ─── Route Loading ─────────────────────────────────────────────────\n\n private async loadRoutes(): Promise<void> {\n // Set Firebase config env var for store module (dual-mode support)\n const setupState = this.firebaseSetup.loadState();\n if (setupState.dbMode === \"firestore\") {\n const config = this.readProjectConfig();\n const firebaseConfig: Record<string, string> = {};\n for (const field of config.fields) {\n if (!field.isPlaceholder) firebaseConfig[field.key] = field.value;\n }\n if (firebaseConfig.apiKey && firebaseConfig.projectId) {\n process.env.CLAWFIRE_FIREBASE_CONFIG = JSON.stringify(firebaseConfig);\n }\n } else {\n delete process.env.CLAWFIRE_FIREBASE_CONFIG;\n }\n\n this.router.destroy();\n this.router = createRouter({\n cors: [\"*\"],\n rateLimit: 0,\n ...this.options.routerOptions,\n });\n\n if (this.options.onSetupRoutes) {\n await this.options.onSetupRoutes(this.router);\n if (this.router.getRoutes().length > 0) return;\n }\n\n if (!existsSync(this.routesDir)) return;\n\n const discovered = discoverRoutes(this.routesDir);\n for (const route of discovered) {\n try {\n const fullPath = resolve(this.routesDir, route.filePath);\n const fileUrl = pathToFileURL(fullPath).href;\n const mod = await import(`${fileUrl}?v=${++this.importCounter}`);\n const contract = mod.default as APIContract<any, any>;\n if (contract && typeof contract.handler === \"function\" && contract.input && contract.output && contract.meta) {\n this.router.register(route.apiPath, contract);\n }\n } catch (err) {\n logger.warn(`Failed to load route: ${route.filePath}`, err);\n }\n }\n }\n\n private async reloadRoutes(event: WatchEvent): Promise<void> {\n if (this.isReloading) return;\n this.isReloading = true;\n\n const relPath = relative(this.options.projectDir, event.filePath);\n const timestamp = new Date().toLocaleTimeString();\n\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[36m${relPath}\\x1b[0m changed`);\n console.log(\" Reloading routes...\");\n\n try {\n await this.loadRoutes();\n this.regeneratePlayground();\n clearFirebaseStatusCache();\n\n const routeCount = this.router.getRoutes().length;\n console.log(` \\x1b[32m✓\\x1b[0m ${routeCount} routes loaded`);\n\n // Notify API SSE clients (playground)\n this.broadcastSSE(this.apiSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n routes: routeCount,\n });\n\n // Also notify frontend SSE clients for route changes\n this.broadcastSSE(this.frontendSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n routes: routeCount,\n });\n } catch (err) {\n console.log(` \\x1b[31m✗\\x1b[0m Reload failed:`, err);\n const errorData = {\n type: \"error\",\n file: relPath,\n message: err instanceof Error ? err.message : \"Unknown error\",\n };\n this.broadcastSSE(this.apiSseClients, errorData);\n this.broadcastSSE(this.frontendSseClients, errorData);\n } finally {\n this.isReloading = false;\n }\n }\n\n // ─── Frontend Change Handler ───────────────────────────────────────\n\n private handleFrontendChange(event: WatchEvent): void {\n const relPath = relative(this.options.projectDir, event.filePath);\n const timestamp = new Date().toLocaleTimeString();\n\n if (event.type === \"css-change\") {\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[35m${relPath}\\x1b[0m CSS updated`);\n this.broadcastSSE(this.frontendSseClients, {\n type: \"css-change\",\n file: relPath,\n timestamp: event.timestamp,\n });\n } else {\n console.log(`\\n \\x1b[33m[${timestamp}]\\x1b[0m \\x1b[35m${relPath}\\x1b[0m changed → reload`);\n this.broadcastSSE(this.frontendSseClients, {\n type: event.type,\n file: relPath,\n timestamp: event.timestamp,\n });\n }\n }\n\n // ─── File Watcher ──────────────────────────────────────────────────\n\n private startWatcher(): void {\n this.watcher = new FileWatcher(this.options.debounceMs);\n\n // routes\n if (existsSync(this.routesDir)) {\n this.watcher.watchDir(this.routesDir, \"route-change\");\n }\n\n // schemas\n if (existsSync(this.schemasDir)) {\n this.watcher.watchDir(this.schemasDir, \"schema-change\");\n }\n\n // config file\n const configFile = resolve(this.options.projectDir, \"clawfire.config.ts\");\n if (existsSync(configFile)) {\n this.watcher.watchFile(configFile, \"config-change\");\n }\n\n // public/ directory (frontend hot reload)\n if (existsSync(this.publicDir)) {\n this.watcher.watchDirFrontend(this.publicDir);\n }\n\n // pages/ directory\n if (existsSync(this.pagesDir)) {\n this.watcher.watchDir(this.pagesDir, \"page-change\");\n }\n\n // components/ directory\n if (existsSync(this.componentsDir)) {\n this.watcher.watchDir(this.componentsDir, \"component-change\");\n }\n\n // API/schema/config changes → route reload\n this.watcher.on(\"route-change\", (event: WatchEvent) => this.reloadRoutes(event));\n this.watcher.on(\"schema-change\", (event: WatchEvent) => this.reloadRoutes(event));\n this.watcher.on(\"config-change\", (event: WatchEvent) => this.reloadRoutes(event));\n\n // Frontend changes → browser notification (CSS hot / full reload)\n this.watcher.on(\"frontend-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n this.watcher.on(\"css-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n\n // Page/component changes → full reload broadcast\n this.watcher.on(\"page-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n this.watcher.on(\"component-change\", (event: WatchEvent) => this.handleFrontendChange(event));\n }\n\n // ─── SSE (Server-Sent Events) ──────────────────────────────────────\n\n private handleSSE(req: http.IncomingMessage, res: http.ServerResponse, clients: SSEClient[]): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n const clientId = ++this.sseIdCounter;\n const client: SSEClient = { id: clientId, res };\n clients.push(client);\n\n res.write(`data: ${JSON.stringify({ type: \"connected\", id: clientId })}\\n\\n`);\n\n req.on(\"close\", () => {\n const idx = clients.indexOf(client);\n if (idx !== -1) clients.splice(idx, 1);\n });\n }\n\n private broadcastSSE(clients: SSEClient[], data: Record<string, unknown>): void {\n const message = `data: ${JSON.stringify(data)}\\n\\n`;\n for (const client of clients) {\n try {\n client.res.write(message);\n } catch {\n // disconnected client\n }\n }\n }\n\n // ─── Playground ────────────────────────────────────────────────────\n\n private regeneratePlayground(): void {\n const baseHtml = generatePlaygroundHtml({\n title: \"Clawfire Dev Playground\",\n apiBaseUrl: `http://localhost:${this.options.apiPort}`,\n });\n\n const dashboardHtml = generateDashboardHtml({\n apiPort: this.options.apiPort,\n });\n\n // Tab bar + tab switching script\n const tabBar = `\n<div id=\"clawfire-tab-bar\" style=\"position:sticky;top:0;z-index:9999;background:#0a0a0a;border-bottom:1px solid #2a2a2a;padding:0 16px;display:flex;gap:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\">\n <button onclick=\"switchTab('apis')\" id=\"tab-btn-apis\" style=\"padding:10px 20px;background:transparent;border:none;border-bottom:2px solid #f97316;color:#f97316;font-size:14px;font-weight:600;cursor:pointer;\">APIs</button>\n <button onclick=\"switchTab('dashboard')\" id=\"tab-btn-dashboard\" style=\"padding:10px 20px;background:transparent;border:none;border-bottom:2px solid transparent;color:#a3a3a3;font-size:14px;font-weight:600;cursor:pointer;\">Dashboard</button>\n</div>`;\n\n const tabScript = `\n<script>\nfunction switchTab(tab) {\n var apis = document.getElementById('tab-apis');\n var dashboard = document.getElementById('tab-dashboard');\n var btnApis = document.getElementById('tab-btn-apis');\n var btnDash = document.getElementById('tab-btn-dashboard');\n\n if (tab === 'apis') {\n apis.style.display = 'block';\n dashboard.style.display = 'none';\n btnApis.style.borderBottomColor = '#f97316';\n btnApis.style.color = '#f97316';\n btnDash.style.borderBottomColor = 'transparent';\n btnDash.style.color = '#a3a3a3';\n } else {\n apis.style.display = 'none';\n dashboard.style.display = 'block';\n btnApis.style.borderBottomColor = 'transparent';\n btnApis.style.color = '#a3a3a3';\n btnDash.style.borderBottomColor = '#f97316';\n btnDash.style.color = '#f97316';\n // Lazy-load dashboard data on first click\n if (window._loadDashboard) window._loadDashboard();\n }\n // Persist active tab across reloads (auto-fill writes config → watcher reloads page)\n try { localStorage.setItem('clawfire-active-tab', tab); } catch(e) {}\n}\n// Restore saved tab on page load\n(function() {\n try {\n var saved = localStorage.getItem('clawfire-active-tab');\n if (saved === 'dashboard') {\n var fn = function() { switchTab('dashboard'); };\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', fn);\n } else {\n fn();\n }\n }\n } catch(e) {}\n})();\n</script>`;\n\n const liveReloadScript = `\n<script>\n(function() {\n var banner = document.createElement('div');\n banner.id = 'dev-banner';\n banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;padding:6px 16px;background:#1a1a2e;color:#f97316;font-size:12px;font-family:monospace;z-index:9999;display:flex;align-items:center;gap:8px;border-top:1px solid #2a2a2a;';\n banner.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;\" id=\"dev-dot\"></span><span>Clawfire Dev Server</span><span style=\"color:#666;margin-left:auto;\" id=\"dev-status\">Connected</span>';\n document.body.appendChild(banner);\n\n var reconnectTimer;\n function connect() {\n var es = new EventSource('http://localhost:${this.options.apiPort}/__dev/events');\n es.onopen = function() {\n document.getElementById('dev-dot').style.background = '#22c55e';\n document.getElementById('dev-status').textContent = 'Connected';\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n };\n es.onmessage = function(e) {\n try {\n var data = JSON.parse(e.data);\n if (data.type === 'connected') return;\n if (data.type === 'error') {\n document.getElementById('dev-dot').style.background = '#ef4444';\n document.getElementById('dev-status').textContent = 'Error: ' + (data.message || 'reload failed');\n return;\n }\n document.getElementById('dev-dot').style.background = '#eab308';\n document.getElementById('dev-status').textContent = 'Reloading...';\n setTimeout(function() { window.location.reload(); }, 300);\n } catch(err) {}\n };\n es.onerror = function() {\n es.close();\n document.getElementById('dev-dot').style.background = '#ef4444';\n document.getElementById('dev-status').textContent = 'Disconnected — reconnecting...';\n reconnectTimer = setTimeout(connect, 2000);\n };\n }\n connect();\n})();\n</script>`;\n\n // Inject: tab bar after <body>, wrap playground in tab-apis, add dashboard tab\n let html = baseHtml;\n // Insert tab bar right after <body...>\n html = html.replace(/<body[^>]*>/, (match) => `${match}\\n${tabBar}`);\n // Wrap existing content: find the first element after body and wrap everything until </body>\n // Strategy: wrap all body content in tab-apis div, then append dashboard\n const bodyOpenMatch = html.match(/<body[^>]*>/);\n if (bodyOpenMatch) {\n const bodyOpenEnd = html.indexOf(bodyOpenMatch[0]) + bodyOpenMatch[0].length;\n const tabBarEnd = html.indexOf(\"</div>\", bodyOpenEnd) + \"</div>\".length; // end of tab bar\n const bodyClose = html.lastIndexOf(\"</body>\");\n\n const beforeTabBar = html.slice(0, tabBarEnd);\n const playgroundContent = html.slice(tabBarEnd, bodyClose);\n const afterBody = html.slice(bodyClose);\n\n html = beforeTabBar +\n `\\n<div id=\"tab-apis\">${playgroundContent}</div>` +\n `\\n<div id=\"tab-dashboard\" style=\"display:none;\">${dashboardHtml}</div>` +\n `\\n${tabScript}` +\n `\\n${liveReloadScript}\\n` +\n afterBody;\n } else {\n // Fallback: just append scripts\n html = html.replace(\"</body>\", `${liveReloadScript}\\n</body>`);\n }\n\n this.playgroundHtml = html;\n }\n\n // ─── Script Injection ──────────────────────────────────────────────\n\n /**\n * Inject HMR and (optionally) client-side router scripts before </body>.\n */\n private injectScripts(html: string, includeRouter: boolean): string {\n const hmr = generateHmrScript(this.options.port);\n const router = includeRouter ? generateRouterScript() : \"\";\n const scripts = router + hmr;\n\n if (html.includes(\"</body>\")) {\n return html.replace(\"</body>\", scripts + \"\\n</body>\");\n }\n return html + scripts;\n }\n\n // ─── Static File Serving ──────────────────────────────────────────\n\n private serveStaticFile(filePath: string, res: http.ServerResponse): boolean {\n if (!existsSync(filePath)) return false;\n\n try {\n const content = readFileSync(filePath);\n const ext = extname(filePath).slice(1).toLowerCase();\n const mime = MIME_TYPES[ext] || \"application/octet-stream\";\n\n // HTML files get HMR script injected\n if (ext === \"html\") {\n const html = content.toString(\"utf-8\");\n const injected = this.injectScripts(html, false);\n res.writeHead(200, { \"Content-Type\": mime });\n res.end(injected);\n return true;\n }\n\n res.writeHead(200, { \"Content-Type\": mime });\n res.end(content);\n return true;\n } catch {\n return false;\n }\n }\n\n // ─── API Proxy ────────────────────────────────────────────────────\n\n private proxyToApiServer(req: http.IncomingMessage, res: http.ServerResponse): void {\n const proxyReq = http.request(\n {\n hostname: \"127.0.0.1\",\n port: this.options.apiPort,\n path: req.url,\n method: req.method,\n headers: {\n ...req.headers,\n host: `localhost:${this.options.apiPort}`,\n },\n },\n (proxyRes) => {\n res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);\n proxyRes.pipe(res, { end: true });\n },\n );\n\n proxyReq.on(\"error\", (err) => {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: { code: \"PROXY_ERROR\", message: \"API server unavailable\" } }));\n });\n\n req.pipe(proxyReq, { end: true });\n }\n\n // ─── Frontend Request Handler ─────────────────────────────────────\n\n private handleFrontendRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || \"/\", `http://localhost:${this.options.port}`);\n\n // 1. SSE endpoint for frontend HMR\n if (url.pathname === \"/__dev/events\") {\n this.handleSSE(req, res, this.frontendSseClients);\n return;\n }\n\n // 2. Proxy /api/* requests to API server\n if (url.pathname.startsWith(\"/api\")) {\n this.proxyToApiServer(req, res);\n return;\n }\n\n // 3. Proxy /__dev/* (except /events handled above) and /__playground to API server\n if (url.pathname.startsWith(\"/__\")) {\n this.proxyToApiServer(req, res);\n return;\n }\n\n // 4. Non-HTML file extensions → serve from public/\n const ext = extname(url.pathname);\n if (ext && STATIC_EXTENSIONS.has(ext)) {\n const filePath = resolve(this.publicDir, url.pathname.slice(1));\n if (filePath.startsWith(this.publicDir) && this.serveStaticFile(filePath, res)) {\n return;\n }\n // Static file not found\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n // 5. Page system\n if (this.pageCompiler.isActive()) {\n const isPartial = req.headers[\"x-clawfire-partial\"] === \"true\";\n const pagePath = this.pageCompiler.resolve(url.pathname);\n\n if (pagePath) {\n try {\n if (isPartial) {\n // SPA navigation — return JSON with page content only\n const partial = this.pageCompiler.compilePartial(pagePath, url.pathname);\n res.writeHead(200, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(partial));\n } else {\n // Full page render with layouts + components + script injection\n const compiled = this.pageCompiler.compile(pagePath);\n const html = this.injectScripts(compiled.html, true);\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n }\n } catch (err) {\n logger.warn(`Page compilation error: ${url.pathname}`, err);\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(`<h1>500 — Page Compilation Error</h1><pre>${err instanceof Error ? err.message : \"Unknown error\"}</pre>`);\n }\n return;\n }\n\n // 404 page\n const page404 = this.pageCompiler.resolve404();\n if (page404) {\n try {\n if (isPartial) {\n const partial = this.pageCompiler.compilePartial(page404, url.pathname);\n res.writeHead(404, { \"Content-Type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(partial));\n } else {\n const compiled = this.pageCompiler.compile(page404);\n const html = this.injectScripts(compiled.html, true);\n res.writeHead(404, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n }\n } catch {\n res.writeHead(404);\n res.end(\"Not found\");\n }\n return;\n }\n }\n\n // 6. Serve static files from public/ (for paths without extension, e.g. directory index)\n const requestedPath = url.pathname === \"/\" ? \"index.html\" : url.pathname.slice(1);\n const filePath = resolve(this.publicDir, requestedPath);\n\n // Security: prevent directory traversal\n if (!filePath.startsWith(this.publicDir)) {\n res.writeHead(403);\n res.end(\"Forbidden\");\n return;\n }\n\n if (this.serveStaticFile(filePath, res)) {\n return;\n }\n\n // 7. SPA fallback: serve index.html for unmatched routes\n const indexPath = resolve(this.publicDir, \"index.html\");\n if (existsSync(indexPath)) {\n this.serveStaticFile(indexPath, res);\n return;\n }\n\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── API Request Handler ──────────────────────────────────────────\n\n private handleApiRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || \"/\", `http://localhost:${this.options.apiPort}`);\n\n // SSE endpoint for playground\n if (url.pathname === \"/__dev/events\") {\n this.handleSSE(req, res, this.apiSseClients);\n return;\n }\n\n // Playground\n if (url.pathname === \"/\" || url.pathname === \"/__playground\") {\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(this.playgroundHtml);\n return;\n }\n\n // API requests\n if (url.pathname.startsWith(\"/api\")) {\n // CORS\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", async () => {\n let parsed = {};\n try { parsed = body ? JSON.parse(body) : {}; } catch {}\n\n await this.router.handleRequest(\n {\n method: req.method,\n path: url.pathname,\n body: parsed,\n headers: req.headers as Record<string, string>,\n ip: req.socket.remoteAddress || \"127.0.0.1\",\n },\n {\n set(h: Record<string, string>) {\n for (const [k, v] of Object.entries(h)) {\n try { res.setHeader(k, v); } catch {}\n }\n },\n status(code: number) {\n return {\n json(data: unknown) {\n res.writeHead(code, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n },\n send(data: string) { res.writeHead(code); res.end(data); },\n end() { res.writeHead(code); res.end(); },\n };\n },\n },\n );\n });\n return;\n }\n\n // Dev dashboard endpoints\n if (url.pathname.startsWith(\"/__dev/\")) {\n this.handleDevEndpoint(req, res, url);\n return;\n }\n\n // 404\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── Startup Banner ────────────────────────────────────────────────\n\n private printStartupBanner(): void {\n const routes = this.router.getRoutes();\n const watching = this.options.hotReload;\n const pagesActive = this.pageCompiler.isActive();\n\n console.log(\"\");\n console.log(\" \\x1b[1m\\x1b[33m⚡ Clawfire Dev Server\\x1b[0m\");\n console.log(\" \\x1b[2m─────────────────────────────────────\\x1b[0m\");\n console.log(` \\x1b[36mApp\\x1b[0m : http://localhost:${this.options.port}`);\n console.log(` \\x1b[36mAPI\\x1b[0m : http://localhost:${this.options.apiPort}/api/...`);\n console.log(` \\x1b[36mPlayground\\x1b[0m : http://localhost:${this.options.apiPort}`);\n console.log(` \\x1b[36mPages\\x1b[0m : ${pagesActive ? \"\\x1b[32mON\\x1b[0m (app/pages/)\" : \"\\x1b[2mOFF\\x1b[0m\"}`);\n console.log(\"\");\n console.log(` \\x1b[32mRoutes (${routes.length})\\x1b[0m:`);\n for (const route of routes) {\n const auth = route.contract.meta.auth || \"public\";\n const authColor = auth === \"public\" ? \"32\" : auth === \"authenticated\" ? \"34\" : auth === \"role\" ? \"33\" : \"31\";\n console.log(` POST /api\\x1b[1m${route.path}\\x1b[0m \\x1b[${authColor}m[${auth}]\\x1b[0m \\x1b[2m${route.contract.meta.description}\\x1b[0m`);\n }\n console.log(\"\");\n if (watching) {\n const watchDirs = [\"functions/routes/\", \"functions/schemas/\", \"public/\"];\n if (pagesActive) watchDirs.push(\"app/pages/\", \"app/components/\");\n console.log(` \\x1b[35mHot Reload\\x1b[0m : \\x1b[32mON\\x1b[0m`);\n console.log(` \\x1b[2mWatching: ${watchDirs.join(\", \")}\\x1b[0m`);\n } else {\n console.log(` \\x1b[35mHot Reload\\x1b[0m : OFF`);\n }\n console.log(`\\n \\x1b[2mPress Ctrl+C to stop\\x1b[0m\\n`);\n }\n\n // ─── Dev Dashboard Endpoints ──────────────────────────────────────\n\n private handleDevEndpoint(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n url: URL,\n ): void {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const sendJson = (data: unknown, status = 200) => {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n };\n\n // GET /__dev/firebase-status\n if (url.pathname === \"/__dev/firebase-status\" && req.method === \"GET\") {\n checkFirebaseStatus(this.options.projectDir)\n .then((status) => sendJson(status))\n .catch((err) => sendJson({ error: err.message }, 500));\n return;\n }\n\n // GET /__dev/config\n if (url.pathname === \"/__dev/config\" && req.method === \"GET\") {\n sendJson(this.readProjectConfig());\n return;\n }\n\n // POST /__dev/config — update config field values\n if (url.pathname === \"/__dev/config\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (data.action === \"set\" && data.key && data.value !== undefined) {\n this.updateProjectConfig(data.key, String(data.value));\n clearFirebaseStatusCache();\n sendJson({ ok: true });\n } else if (data.action === \"set-multiple\" && data.fields) {\n for (const [key, value] of Object.entries(data.fields)) {\n this.updateProjectConfig(key, String(value));\n }\n clearFirebaseStatusCache();\n sendJson({ ok: true });\n } else {\n sendJson({ error: \"Invalid action\" }, 400);\n }\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/firebase-sdk-config — auto-fill from Firebase CLI (uses saved web app ID if available)\n if (url.pathname === \"/__dev/firebase-sdk-config\" && req.method === \"GET\") {\n const savedState = this.firebaseSetup.loadState();\n fetchFirebaseSdkConfig(this.options.projectDir, savedState.webAppId)\n .then((config) => sendJson(config))\n .catch((err) => sendJson({ error: err instanceof Error ? err.message : \"Failed to fetch SDK config\" }, 500));\n return;\n }\n\n // GET /__dev/env\n if (url.pathname === \"/__dev/env\" && req.method === \"GET\") {\n try {\n sendJson(this.envManager.read());\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Read failed\" }, 500);\n }\n return;\n }\n\n // POST /__dev/env\n if (url.pathname === \"/__dev/env\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (data.action === \"set\") {\n this.envManager.set(data.key, data.value, data.description);\n sendJson({ ok: true });\n } else if (data.action === \"delete\") {\n this.envManager.delete(data.key);\n sendJson({ ok: true });\n } else {\n sendJson({ error: \"Invalid action\" }, 400);\n }\n } catch (err) {\n sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // ─── Setup Wizard Endpoints ─────────────────────────────────────\n\n // GET /__dev/setup/status\n if (url.pathname === \"/__dev/setup/status\" && req.method === \"GET\") {\n this.firebaseSetup.getStatus()\n .then((status) => sendJson(status))\n .catch((err) => sendJson({ error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/install-cli\n if (url.pathname === \"/__dev/setup/install-cli\" && req.method === \"POST\") {\n this.firebaseSetup.installCli()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/login — opens a real terminal window\n if (url.pathname === \"/__dev/setup/login\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n let reauth = false;\n try {\n const data = JSON.parse(body);\n reauth = !!data.reauth;\n } catch {\n // default: not reauth\n }\n const result = this.firebaseSetup.openLoginTerminal(reauth);\n sendJson(result);\n });\n return;\n }\n\n // GET /__dev/setup/projects\n if (url.pathname === \"/__dev/setup/projects\" && req.method === \"GET\") {\n this.firebaseSetup.listProjects()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ projects: [], error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/select-project\n // After project selection, automatically: list web apps → create if needed → fetch SDK config → write to clawfire.config.ts\n if (url.pathname === \"/__dev/setup/select-project\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.projectId) {\n sendJson({ success: false, message: \"projectId is required\" }, 400);\n return;\n }\n (async () => {\n // Step 1: Select project\n const selectResult = await this.firebaseSetup.selectProject(data.projectId);\n if (!selectResult.success) {\n sendJson(selectResult);\n return;\n }\n\n const steps: string[] = [selectResult.message];\n\n try {\n // Step 2: List web apps → create one if none exist\n const { apps } = await this.firebaseSetup.listWebApps();\n let webAppId = \"\";\n if (apps.length > 0) {\n webAppId = apps[0].appId;\n this.firebaseSetup.selectWebApp(apps[0].appId, apps[0].displayName);\n steps.push(`Web app \"${apps[0].displayName}\" selected.`);\n } else {\n // Auto-create a web app named after the project\n const createResult = await this.firebaseSetup.createWebApp(data.projectId);\n if (createResult.success && createResult.appId) {\n webAppId = createResult.appId;\n steps.push(`Web app \"${data.projectId}\" created.`);\n } else {\n steps.push(`Web app creation skipped: ${createResult.message}`);\n }\n }\n\n // Step 3: Fetch SDK config and write to clawfire.config.ts\n if (webAppId) {\n try {\n const sdkConfig = await fetchFirebaseSdkConfig(this.options.projectDir, webAppId);\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(sdkConfig)) {\n if (value && typeof value === \"string\") {\n fields[key] = value;\n }\n }\n if (Object.keys(fields).length > 0) {\n for (const [key, value] of Object.entries(fields)) {\n try {\n this.updateProjectConfig(key, value);\n } catch {\n // Key might not exist in config template — skip\n }\n }\n steps.push(\"Config auto-filled in clawfire.config.ts.\");\n }\n } catch (configErr) {\n steps.push(`Config auto-fill skipped: ${configErr instanceof Error ? configErr.message : \"unknown error\"}`);\n }\n }\n } catch (autoFillErr) {\n // Auto-fill failed but project selection succeeded — still report success\n steps.push(`Auto-setup partial: ${autoFillErr instanceof Error ? autoFillErr.message : \"unknown error\"}`);\n }\n\n clearFirebaseStatusCache();\n sendJson({ success: true, message: steps.join(\" \"), steps });\n })().catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/setup/web-apps\n if (url.pathname === \"/__dev/setup/web-apps\" && req.method === \"GET\") {\n this.firebaseSetup.listWebApps()\n .then((result) => sendJson(result))\n .catch((err) => sendJson({ apps: [], error: err instanceof Error ? err.message : \"Failed\" }, 500));\n return;\n }\n\n // POST /__dev/setup/create-web-app\n if (url.pathname === \"/__dev/setup/create-web-app\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.displayName) {\n sendJson({ success: false, message: \"displayName is required\" }, 400);\n return;\n }\n this.firebaseSetup.createWebApp(data.displayName)\n .then((result) => {\n clearFirebaseStatusCache();\n sendJson(result);\n })\n .catch((err) => sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500));\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // POST /__dev/deploy/hosting\n if (url.pathname === \"/__dev/deploy/hosting\" && req.method === \"POST\") {\n (async () => {\n try {\n const steps: string[] = [];\n const functionsDir = resolve(this.options.projectDir, \"functions\");\n const hasFunctions = existsSync(functionsDir);\n\n // ── Step 1: Read Firebase config ──\n const projectConfig = this.readProjectConfig();\n const firebaseConfig: Record<string, string> = {};\n for (const field of projectConfig.fields) {\n if (!field.isPlaceholder) {\n firebaseConfig[field.key] = field.value;\n }\n }\n\n // ── Step 2: Pre-render pages into public/ ──\n if (this.pageCompiler.isActive()) {\n const configScript = Object.keys(firebaseConfig).length > 0\n ? `\\n<script>window.__CLAWFIRE_CONFIG__=${JSON.stringify(firebaseConfig)};</script>`\n : \"\";\n const routerScript = generateProductionRouterScript();\n const scriptToInject = configScript + routerScript;\n const buildResult = this.pageCompiler.buildForProduction(this.publicDir, scriptToInject);\n console.log(` \\x1b[32m✓\\x1b[0m Built ${buildResult.pages.length} pages for production`);\n steps.push(`${buildResult.pages.length} pages built`);\n if (configScript) {\n console.log(` \\x1b[32m✓\\x1b[0m Firebase config injected (projectId: ${firebaseConfig.projectId || \"?\"})`);\n steps.push(\"Firebase config injected\");\n }\n if (buildResult.errors.length > 0) {\n for (const err of buildResult.errors) {\n console.log(` \\x1b[31m✗\\x1b[0m ${err}`);\n }\n }\n }\n\n // ── Step 3: Ensure firebase.json has API rewrite + functions config ──\n const deployConfig = this.firebaseSetup.ensureDeployConfig();\n if (deployConfig.success && deployConfig.changes.length > 0) {\n for (const change of deployConfig.changes) {\n console.log(` \\x1b[32m✓\\x1b[0m firebase.json: ${change}`);\n }\n steps.push(`firebase.json updated (${deployConfig.changes.length} fixes)`);\n }\n\n // ── Step 4: Sync env to functions/.env ──\n let deployFunctions = false;\n if (hasFunctions) {\n const syncResult = this.firebaseSetup.syncEnvToFunctions(firebaseConfig);\n if (syncResult.success) {\n console.log(` \\x1b[32m✓\\x1b[0m ${syncResult.message}`);\n steps.push(`${syncResult.count} env vars synced`);\n } else {\n console.log(` \\x1b[33m⚠\\x1b[0m Env sync: ${syncResult.message}`);\n }\n\n // ── Step 5: Build functions ──\n console.log(\" Building functions...\");\n const buildResult = await this.firebaseSetup.buildFunctions();\n if (buildResult.success) {\n console.log(` \\x1b[32m✓\\x1b[0m ${buildResult.message}`);\n steps.push(\"Functions built\");\n deployFunctions = true;\n } else {\n console.log(` \\x1b[31m✗\\x1b[0m ${buildResult.message}`);\n console.log(\" \\x1b[33m⚠\\x1b[0m Deploying hosting only (functions build failed)\");\n steps.push(\"Functions build failed — hosting only\");\n }\n }\n\n // ── Step 6: Deploy ──\n let targets = deployFunctions ? \"hosting,functions\" : \"hosting\";\n console.log(` Deploying ${targets}...`);\n let result = await this.firebaseSetup.deployHosting(targets);\n\n // Fallback: if hosting+functions failed (e.g. Blaze plan required), retry hosting only\n if (!result.success && deployFunctions) {\n const msg = result.message.toLowerCase();\n if (msg.includes(\"blaze\") || msg.includes(\"pay-as-you-go\") || msg.includes(\"upgrade\") || msg.includes(\"billing\") || msg.includes(\"artifactregistry\")) {\n console.log(\" \\x1b[33m⚠\\x1b[0m Functions require Blaze plan. Deploying hosting only...\");\n steps.push(\"Functions skipped (requires Blaze plan)\");\n targets = \"hosting\";\n result = await this.firebaseSetup.deployHosting(targets);\n }\n }\n\n clearFirebaseStatusCache();\n\n sendJson({\n ...result,\n message: result.message + (steps.length > 0 ? ` (${steps.join(\", \")})` : \"\"),\n });\n } catch (err) {\n sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 500);\n }\n })();\n return;\n }\n\n // POST /__dev/enable-service\n if (url.pathname === \"/__dev/enable-service\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n const service = data.service;\n if (!service || ![\"hosting\", \"firestore\", \"storage\"].includes(service)) {\n sendJson({ success: false, message: \"Invalid service. Use: hosting, firestore, storage\" }, 400);\n return;\n }\n const result = this.firebaseSetup.enableService(service);\n clearFirebaseStatusCache();\n sendJson(result);\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // GET /__dev/db-mode — get current database mode\n if (url.pathname === \"/__dev/db-mode\" && req.method === \"GET\") {\n const state = this.firebaseSetup.loadState();\n sendJson({ mode: state.dbMode || \"memory\" });\n return;\n }\n\n // POST /__dev/db-mode — switch database mode\n if (url.pathname === \"/__dev/db-mode\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", async () => {\n try {\n const data = JSON.parse(body);\n const mode = data.mode;\n if (mode !== \"memory\" && mode !== \"firestore\") {\n sendJson({ success: false, message: \"Invalid mode. Use: memory, firestore\" }, 400);\n return;\n }\n this.firebaseSetup.saveState({ dbMode: mode });\n // Reload routes to apply new mode (sets/clears env var)\n await this.loadRoutes();\n this.regeneratePlayground();\n sendJson({ success: true, mode, message: `Database mode switched to \"${mode}\".` });\n } catch (err) {\n sendJson({ success: false, message: err instanceof Error ? err.message : \"Failed\" }, 400);\n }\n });\n return;\n }\n\n // POST /__dev/setup/create-firestore — one-click Firestore setup\n if (url.pathname === \"/__dev/setup/create-firestore\" && req.method === \"POST\") {\n (async () => {\n try {\n // Step 1: Create Firestore database\n const createResult = await this.firebaseSetup.createFirestoreDatabase();\n if (!createResult.success) {\n sendJson(createResult);\n return;\n }\n\n // Step 2: Enable Firestore service (writes firebase.json + firestore.rules)\n const enableResult = this.firebaseSetup.enableService(\"firestore\");\n if (!enableResult.success) {\n sendJson(enableResult);\n return;\n }\n\n // Step 3: Deploy open rules for dev\n const rulesResult = await this.firebaseSetup.deployFirestoreRules(true);\n if (!rulesResult.success) {\n sendJson(rulesResult);\n return;\n }\n\n // Step 4: Set mode to firestore\n this.firebaseSetup.saveState({ dbMode: \"firestore\" });\n\n // Step 5: Reload routes (applies env var)\n await this.loadRoutes();\n this.regeneratePlayground();\n clearFirebaseStatusCache();\n\n sendJson({\n success: true,\n message: \"Firestore connected! Database created, rules deployed, mode switched to Firestore.\",\n });\n } catch (err) {\n sendJson({\n success: false,\n message: err instanceof Error ? err.message : \"Failed to setup Firestore\",\n }, 500);\n }\n })();\n return;\n }\n\n // POST /__dev/setup/select-web-app\n if (url.pathname === \"/__dev/setup/select-web-app\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => { body += chunk; });\n req.on(\"end\", () => {\n try {\n const data = JSON.parse(body);\n if (!data.appId) {\n sendJson({ success: false, message: \"appId is required\" }, 400);\n return;\n }\n const result = this.firebaseSetup.selectWebApp(data.appId, data.displayName || \"\");\n clearFirebaseStatusCache();\n sendJson(result);\n } catch {\n sendJson({ success: false, message: \"Invalid JSON body\" }, 400);\n }\n });\n return;\n }\n\n // Unknown /__dev/ endpoint\n res.writeHead(404);\n res.end(\"Not found\");\n }\n\n // ─── Config Reader ────────────────────────────────────────────────\n\n private readProjectConfig(): { fields: Array<{ key: string; value: string; isPlaceholder: boolean }> } {\n const configPath = resolve(this.options.projectDir, \"clawfire.config.ts\");\n const fields: Array<{ key: string; value: string; isPlaceholder: boolean }> = [];\n\n if (!existsSync(configPath)) {\n return { fields };\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n\n // Parse key-value pairs from the config file\n // Match patterns like: apiKey: \"value\", authDomain: \"value\", etc.\n const kvPattern = /(\\w+)\\s*:\\s*[\"'`]([^\"'`]*)[\"'`]/g;\n let match: RegExpExecArray | null;\n\n while ((match = kvPattern.exec(content)) !== null) {\n const key = match[1];\n const value = match[2];\n const isPlaceholder = /^YOUR_/i.test(value) || /^CHANGE_ME$/i.test(value) || /^TODO$/i.test(value);\n fields.push({ key, value, isPlaceholder });\n }\n } catch {\n // ignore read errors\n }\n\n return { fields };\n }\n\n /** Update a single key's value in clawfire.config.ts */\n private updateProjectConfig(key: string, value: string): void {\n const configPath = resolve(this.options.projectDir, \"clawfire.config.ts\");\n if (!existsSync(configPath)) {\n throw new Error(\"clawfire.config.ts not found\");\n }\n\n let content = readFileSync(configPath, \"utf-8\");\n\n // Match key: \"value\" or key: 'value' patterns\n const pattern = new RegExp(\n `(${this.escapeRegex(key)}\\\\s*:\\\\s*)[\"'\\`][^\"'\\`]*[\"'\\`]`,\n );\n\n if (!pattern.test(content)) {\n throw new Error(`Key \"${key}\" not found in config`);\n }\n\n content = content.replace(pattern, `$1\"${value.replace(/\"/g, '\\\\\"')}\"`);\n writeFileSync(configPath, content, \"utf-8\");\n }\n\n private escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n}\n\n/**\n * Create and start dev server (one-line helper)\n */\nexport async function startDevServer(options?: DevServerOptions): Promise<DevServer> {\n const server = new DevServer(options);\n await server.start();\n\n const shutdown = async () => {\n console.log(\"\\n Shutting down...\");\n await server.stop();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return server;\n}\n","/**\n * Clawfire Schema & Contract System\n *\n * 모든 API는 input/output schema + meta + handler로 구성된 \"계약(Contract)\"으로 정의됩니다.\n * Zod를 사용하여 타입 안전성과 런타임 검증을 동시에 보장합니다.\n */\nimport { z, type ZodType, type ZodObject, type ZodRawShape } from \"zod\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\n/** 인증 컨텍스트 */\nexport interface AuthContext {\n uid: string;\n email?: string;\n emailVerified?: boolean;\n role?: string;\n customClaims?: Record<string, unknown>;\n token?: string;\n}\n\n/** API 핸들러에 전달되는 컨텍스트 */\nexport interface HandlerContext {\n auth: AuthContext | null;\n /** 재인증 여부 (민감 작업용) */\n reauthenticated?: boolean;\n /** 원본 요청 헤더 */\n headers?: Record<string, string>;\n /** 요청 IP */\n ip?: string;\n}\n\n/** 권한 수준 */\nexport type AuthLevel = \"public\" | \"authenticated\" | \"role\" | \"reauth\";\n\n/** API 메타데이터 */\nexport interface APIMeta {\n /** API 설명 (AI/Playground용) */\n description: string;\n /** 태그 (그룹화용) */\n tags?: string[];\n /** 인증 요구 수준 */\n auth?: AuthLevel;\n /** 필요 역할 (auth가 'role'일 때) */\n roles?: string[];\n /** 재인증 필요 여부 */\n reauth?: boolean;\n /** Rate limit (초당 요청 수) */\n rateLimit?: number;\n /** 비활성화 여부 */\n deprecated?: boolean;\n /** 예시 입력값 */\n exampleInput?: unknown;\n /** 예시 출력값 */\n exampleOutput?: unknown;\n}\n\n/** API 계약 정의 */\nexport interface APIContract<\n TInput extends ZodType = ZodType,\n TOutput extends ZodType = ZodType,\n> {\n /** 입력 스키마 */\n input: TInput;\n /** 출력 스키마 */\n output: TOutput;\n /** 메타데이터 */\n meta: APIMeta;\n /** 핸들러 함수 */\n handler: (\n input: z.infer<TInput>,\n ctx: HandlerContext,\n ) => Promise<z.infer<TOutput>>;\n}\n\n/** 모델 필드 정의 */\nexport interface ModelField {\n type: \"string\" | \"number\" | \"boolean\" | \"timestamp\" | \"array\" | \"map\" | \"reference\" | \"geopoint\";\n required?: boolean;\n description?: string;\n default?: unknown;\n /** 배열 아이템 타입 */\n items?: ModelField;\n /** 맵 값 타입 */\n values?: ModelField;\n /** 참조 대상 컬렉션 */\n ref?: string;\n /** enum 값 리스트 */\n enum?: string[];\n}\n\n/** 모델 정의 */\nexport interface ModelDefinition {\n /** 컬렉션 이름 */\n collection: string;\n /** 필드 정의 */\n fields: Record<string, ModelField>;\n /** 서브컬렉션 */\n subcollections?: Record<string, ModelDefinition>;\n /** 인덱스 */\n indexes?: ModelIndex[];\n /** 보안 규칙 */\n rules?: ModelRules;\n /** 타임스탬프 자동 생성 */\n timestamps?: boolean;\n /** 소프트 삭제 */\n softDelete?: boolean;\n}\n\n/** Firestore 인덱스 */\nexport interface ModelIndex {\n fields: Array<{ field: string; order?: \"asc\" | \"desc\" }>;\n}\n\n/** 모델 보안 규칙 */\nexport interface ModelRules {\n read?: AuthLevel;\n create?: AuthLevel;\n update?: AuthLevel;\n delete?: AuthLevel;\n readRoles?: string[];\n createRoles?: string[];\n updateRoles?: string[];\n deleteRoles?: string[];\n /** 소유자만 읽기/쓰기 가능 필드 */\n ownerField?: string;\n}\n\n// ─── Builders ────────────────────────────────────────────────────────\n\n/**\n * API 계약 정의\n *\n * @example\n * ```ts\n * export default defineAPI({\n * input: z.object({ name: z.string() }),\n * output: z.object({ id: z.string(), name: z.string() }),\n * meta: { description: \"상품 생성\", auth: \"authenticated\" },\n * handler: async (input, ctx) => {\n * const id = await db.create(\"products\", input);\n * return { id, ...input };\n * }\n * });\n * ```\n */\nexport function defineAPI<\n TInput extends ZodType,\n TOutput extends ZodType,\n>(contract: APIContract<TInput, TOutput>): APIContract<TInput, TOutput> {\n return contract;\n}\n\n/**\n * 모델(Firestore 컬렉션) 정의\n *\n * @example\n * ```ts\n * export const Product = defineModel({\n * collection: \"products\",\n * fields: {\n * name: { type: \"string\", required: true },\n * price: { type: \"number\", required: true },\n * tags: { type: \"array\", items: { type: \"string\" } },\n * },\n * timestamps: true,\n * rules: { read: \"public\", create: \"authenticated\" }\n * });\n * ```\n */\nexport function defineModel(definition: ModelDefinition): ModelDefinition {\n return {\n timestamps: true,\n ...definition,\n };\n}\n\n// ─── Schema Utilities ────────────────────────────────────────────────\n\n/** Zod 스키마에서 JSON Schema 생성 (Playground/문서용) */\nexport function zodToJsonSchema(schema: ZodType): Record<string, unknown> {\n return extractZodShape(schema);\n}\n\nfunction extractZodShape(schema: ZodType): Record<string, unknown> {\n const def = (schema as any)._def;\n\n if (!def) return { type: \"unknown\" };\n\n switch (def.typeName) {\n case \"ZodObject\": {\n const shape = (schema as ZodObject<ZodRawShape>).shape;\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = extractZodShape(value as ZodType);\n if (!(value as any).isOptional?.()) {\n const innerDef = (value as any)._def;\n if (innerDef?.typeName !== \"ZodOptional\" && innerDef?.typeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n\n return { type: \"object\", properties, ...(required.length > 0 ? { required } : {}) };\n }\n case \"ZodString\":\n return { type: \"string\", ...(def.checks?.length ? extractStringChecks(def.checks) : {}) };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodArray\":\n return { type: \"array\", items: extractZodShape(def.type) };\n case \"ZodEnum\":\n return { type: \"string\", enum: def.values };\n case \"ZodOptional\":\n return { ...extractZodShape(def.innerType), optional: true };\n case \"ZodDefault\":\n return { ...extractZodShape(def.innerType), default: def.defaultValue() };\n case \"ZodNullable\":\n return { ...extractZodShape(def.innerType), nullable: true };\n case \"ZodLiteral\":\n return { type: typeof def.value, const: def.value };\n case \"ZodUnion\":\n return { oneOf: def.options.map((o: ZodType) => extractZodShape(o)) };\n case \"ZodRecord\":\n return { type: \"object\", additionalProperties: extractZodShape(def.valueType) };\n case \"ZodDate\":\n return { type: \"string\", format: \"date-time\" };\n default:\n return { type: \"unknown\" };\n }\n}\n\nfunction extractStringChecks(checks: Array<{ kind: string; value?: unknown }>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const check of checks) {\n switch (check.kind) {\n case \"min\": result.minLength = check.value; break;\n case \"max\": result.maxLength = check.value; break;\n case \"email\": result.format = \"email\"; break;\n case \"url\": result.format = \"uri\"; break;\n case \"uuid\": result.format = \"uuid\"; break;\n }\n }\n return result;\n}\n\n/** 모델 정의에서 Zod 스키마 자동 생성 */\nexport function modelToZodSchema(model: ModelDefinition): ZodObject<ZodRawShape> {\n const shape: ZodRawShape = {};\n\n for (const [key, field] of Object.entries(model.fields)) {\n let fieldSchema: ZodType = fieldToZod(field);\n if (!field.required) {\n fieldSchema = fieldSchema.optional();\n }\n shape[key] = fieldSchema;\n }\n\n if (model.timestamps) {\n shape.createdAt = z.string().datetime().optional();\n shape.updatedAt = z.string().datetime().optional();\n }\n\n if (model.softDelete) {\n shape.deletedAt = z.string().datetime().nullable().optional();\n }\n\n return z.object(shape);\n}\n\nfunction fieldToZod(field: ModelField): ZodType {\n switch (field.type) {\n case \"string\":\n if (field.enum) return z.enum(field.enum as [string, ...string[]]);\n return z.string();\n case \"number\":\n return z.number();\n case \"boolean\":\n return z.boolean();\n case \"timestamp\":\n return z.string().datetime();\n case \"array\":\n if (field.items) return z.array(fieldToZod(field.items));\n return z.array(z.unknown());\n case \"map\":\n if (field.values) return z.record(z.string(), fieldToZod(field.values));\n return z.record(z.string(), z.unknown());\n case \"reference\":\n return z.string(); // 참조는 문서 경로 문자열\n case \"geopoint\":\n return z.object({ latitude: z.number(), longitude: z.number() });\n default:\n return z.unknown();\n }\n}\n\n// ─── Manifest ────────────────────────────────────────────────────────\n\n/** API 매니페스트 항목 */\nexport interface ManifestEntry {\n path: string;\n method: \"POST\"; // Clawfire는 모두 POST\n meta: APIMeta;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n}\n\n/** 전체 매니페스트 */\nexport interface Manifest {\n version: string;\n generatedAt: string;\n apis: ManifestEntry[];\n models: Record<string, ModelDefinition>;\n}\n\n/** 계약에서 매니페스트 항목 생성 */\nexport function contractToManifest(\n path: string,\n contract: APIContract<any, any>,\n): ManifestEntry {\n return {\n path,\n method: \"POST\",\n meta: contract.meta,\n inputSchema: zodToJsonSchema(contract.input),\n outputSchema: zodToJsonSchema(contract.output),\n };\n}\n","/**\n * Clawfire Error System\n *\n * 표준화된 에러 코드와 구조로 일관된 에러 응답을 제공합니다.\n */\n\nexport type ClawfireErrorCode =\n | \"VALIDATION_ERROR\"\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"RATE_LIMITED\"\n | \"REAUTH_REQUIRED\"\n | \"INTERNAL_ERROR\"\n | \"SERVICE_UNAVAILABLE\";\n\nconst HTTP_STATUS_MAP: Record<ClawfireErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n REAUTH_REQUIRED: 401,\n INTERNAL_ERROR: 500,\n SERVICE_UNAVAILABLE: 503,\n};\n\nexport class ClawfireError extends Error {\n readonly code: ClawfireErrorCode;\n readonly statusCode: number;\n readonly details?: unknown;\n\n constructor(code: ClawfireErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = \"ClawfireError\";\n this.code = code;\n this.statusCode = HTTP_STATUS_MAP[code];\n this.details = details;\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n ...(this.details ? { details: this.details } : {}),\n },\n };\n }\n}\n\n/** 편의 팩토리 함수 */\nexport const Errors = {\n validation: (message: string, details?: unknown) =>\n new ClawfireError(\"VALIDATION_ERROR\", message, details),\n\n unauthorized: (message = \"Authentication required\") =>\n new ClawfireError(\"UNAUTHORIZED\", message),\n\n forbidden: (message = \"Insufficient permissions\") =>\n new ClawfireError(\"FORBIDDEN\", message),\n\n notFound: (message = \"Resource not found\") =>\n new ClawfireError(\"NOT_FOUND\", message),\n\n conflict: (message: string) =>\n new ClawfireError(\"CONFLICT\", message),\n\n rateLimited: (message = \"Too many requests\") =>\n new ClawfireError(\"RATE_LIMITED\", message),\n\n reauthRequired: (message = \"Re-authentication required for this action\") =>\n new ClawfireError(\"REAUTH_REQUIRED\", message),\n\n internal: (message = \"Internal server error\") =>\n new ClawfireError(\"INTERNAL_ERROR\", message),\n\n unavailable: (message = \"Service temporarily unavailable\") =>\n new ClawfireError(\"SERVICE_UNAVAILABLE\", message),\n};\n","/**\n * Clawfire Logger\n *\n * 보안 로깅 (민감 정보 마스킹 포함)\n */\n\n/** 마스킹할 필드명 패턴 */\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"secret\",\n \"apiKey\",\n \"api_key\",\n \"authorization\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"cardNumber\",\n \"card_number\",\n \"cvv\",\n \"pin\",\n];\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet currentLevel: LogLevel = \"info\";\n\nexport function setLogLevel(level: LogLevel) {\n currentLevel = level;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\n/** 민감 필드 마스킹 */\nexport function maskSensitive(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === \"string\") return obj;\n if (typeof obj !== \"object\") return obj;\n\n if (Array.isArray(obj)) {\n return obj.map(maskSensitive);\n }\n\n const masked: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_FIELDS.some((f) => key.toLowerCase().includes(f.toLowerCase()))) {\n masked[key] = \"***MASKED***\";\n } else if (typeof value === \"object\" && value !== null) {\n masked[key] = maskSensitive(value);\n } else {\n masked[key] = value;\n }\n }\n return masked;\n}\n\nfunction formatLog(level: LogLevel, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [CLAWFIRE] [${level.toUpperCase()}]`;\n if (data !== undefined) {\n return `${prefix} ${message} ${JSON.stringify(maskSensitive(data))}`;\n }\n return `${prefix} ${message}`;\n}\n\nexport const logger = {\n debug(message: string, data?: unknown) {\n if (shouldLog(\"debug\")) console.debug(formatLog(\"debug\", message, data));\n },\n info(message: string, data?: unknown) {\n if (shouldLog(\"info\")) console.info(formatLog(\"info\", message, data));\n },\n warn(message: string, data?: unknown) {\n if (shouldLog(\"warn\")) console.warn(formatLog(\"warn\", message, data));\n },\n error(message: string, data?: unknown) {\n if (shouldLog(\"error\")) console.error(formatLog(\"error\", message, data));\n },\n};\n","/**\n * Clawfire Auth Helpers\n *\n * Firebase Auth 통합: 토큰 검증, 역할 관리, 재인증\n */\nimport type { AuthContext, AuthLevel } from \"../core/schema.js\";\nimport { Errors } from \"../core/errors.js\";\n\n// ─── Server-side Auth (Admin SDK) ────────────────────────────────────\n\n/**\n * Firebase ID 토큰에서 AuthContext 추출 (서버사이드)\n */\nexport async function verifyToken(\n auth: any, // firebase-admin Auth instance\n idToken: string,\n): Promise<AuthContext> {\n const decoded = await auth.verifyIdToken(idToken);\n return {\n uid: decoded.uid,\n email: decoded.email,\n emailVerified: decoded.email_verified,\n role: decoded.role || decoded.customClaims?.role,\n customClaims: decoded,\n token: idToken,\n };\n}\n\n/**\n * 재인증 토큰 검증 (최근 5분 이내 인증)\n */\nexport async function verifyReauth(\n auth: any,\n idToken: string,\n maxAgeSeconds = 300,\n): Promise<AuthContext> {\n const decoded = await auth.verifyIdToken(idToken, true);\n const authTime = decoded.auth_time * 1000;\n const now = Date.now();\n\n if (now - authTime > maxAgeSeconds * 1000) {\n throw Errors.reauthRequired(\n `Re-authentication required. Last auth was ${Math.floor((now - authTime) / 1000)}s ago.`,\n );\n }\n\n return {\n uid: decoded.uid,\n email: decoded.email,\n emailVerified: decoded.email_verified,\n role: decoded.role || decoded.customClaims?.role,\n customClaims: decoded,\n token: idToken,\n };\n}\n\n/**\n * 요청에서 Authorization 헤더의 Bearer 토큰 추출\n */\nexport function extractBearerToken(authHeader?: string): string | null {\n if (!authHeader) return null;\n const parts = authHeader.split(\" \");\n if (parts.length !== 2 || parts[0] !== \"Bearer\") return null;\n return parts[1];\n}\n\n/**\n * 인증 수준 체크\n */\nexport function checkAuthLevel(\n authCtx: AuthContext | null,\n level: AuthLevel,\n roles?: string[],\n reauthenticated?: boolean,\n): void {\n switch (level) {\n case \"public\":\n return; // 누구나 접근 가능\n\n case \"authenticated\":\n if (!authCtx) throw Errors.unauthorized();\n return;\n\n case \"role\":\n if (!authCtx) throw Errors.unauthorized();\n if (!roles || roles.length === 0) return;\n if (!authCtx.role || !roles.includes(authCtx.role)) {\n throw Errors.forbidden(`Required role: ${roles.join(\" or \")}`);\n }\n return;\n\n case \"reauth\":\n if (!authCtx) throw Errors.unauthorized();\n if (!reauthenticated) {\n throw Errors.reauthRequired();\n }\n return;\n }\n}\n\n// ─── Custom Claims / Role Management ─────────────────────────────────\n\n/**\n * 사용자에게 역할(Role) 설정\n */\nexport async function setUserRole(\n auth: any,\n uid: string,\n role: string,\n): Promise<void> {\n const user = await auth.getUser(uid);\n const currentClaims = user.customClaims || {};\n await auth.setCustomUserClaims(uid, { ...currentClaims, role });\n}\n\n/**\n * 사용자의 역할 조회\n */\nexport async function getUserRole(auth: any, uid: string): Promise<string | undefined> {\n const user = await auth.getUser(uid);\n return user.customClaims?.role;\n}\n\n/**\n * 사용자에게 커스텀 클레임 설정\n */\nexport async function setCustomClaims(\n auth: any,\n uid: string,\n claims: Record<string, unknown>,\n): Promise<void> {\n const user = await auth.getUser(uid);\n const currentClaims = user.customClaims || {};\n await auth.setCustomUserClaims(uid, { ...currentClaims, ...claims });\n}\n\n// ─── Client-side Auth Helpers ────────────────────────────────────────\n\n/**\n * 클라이언트 사이드 Auth 헬퍼 (firebase/auth 래핑)\n */\nexport function createClientAuth(firebaseAuth: any) {\n return {\n /** 현재 사용자 조회 */\n getCurrentUser(): any | null {\n return firebaseAuth.currentUser;\n },\n\n /** ID 토큰 취득 */\n async getIdToken(forceRefresh = false): Promise<string | null> {\n const user = firebaseAuth.currentUser;\n if (!user) return null;\n return user.getIdToken(forceRefresh);\n },\n\n /** 인증 상태 변경 리스너 */\n onAuthStateChanged(callback: (user: any | null) => void): () => void {\n return firebaseAuth.onAuthStateChanged(callback);\n },\n\n /** 로그아웃 */\n async signOut(): Promise<void> {\n await firebaseAuth.signOut();\n },\n\n /** 원시 auth 접근 */\n raw: firebaseAuth,\n };\n}\n\nexport type ClientAuth = ReturnType<typeof createClientAuth>;\n","/**\n * Clawfire File-based Router\n *\n * functions/routes/ 디렉터리 구조를 자동으로 API 경로로 매핑합니다.\n *\n * 파일 구조 → API 경로:\n * functions/routes/products/list.ts → /api/products/list\n * functions/routes/products/create.ts → /api/products/create\n * functions/routes/auth/login.ts → /api/auth/login\n * functions/routes/users/[id]/get.ts → /api/users/:id/get\n */\nimport { z } from \"zod\";\nimport type { APIContract, HandlerContext, AuthContext, Manifest, ManifestEntry } from \"../core/schema.js\";\nimport { contractToManifest, zodToJsonSchema } from \"../core/schema.js\";\nimport { ClawfireError, Errors } from \"../core/errors.js\";\nimport { logger } from \"../core/logger.js\";\nimport { checkAuthLevel, extractBearerToken, verifyToken, verifyReauth } from \"../firebase/auth.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface Route {\n path: string;\n contract: APIContract<any, any>;\n}\n\nexport interface RouterOptions {\n /** Firebase Admin Auth 인스턴스 */\n auth?: any;\n /** Firebase Admin Firestore 인스턴스 */\n firestore?: any;\n /** CORS 허용 도메인 */\n cors?: string[];\n /** 전역 rate limit */\n rateLimit?: number;\n /** 전역 미들웨어 */\n middleware?: Middleware[];\n /** 플레이그라운드 활성화 */\n playground?: boolean;\n}\n\nexport type Middleware = (\n input: unknown,\n ctx: HandlerContext,\n next: () => Promise<unknown>,\n) => Promise<unknown>;\n\n// ─── Rate Limiter (in-memory) ────────────────────────────────────────\n\nclass RateLimiter {\n private store = new Map<string, { count: number; resetAt: number }>();\n private defaultLimit: number;\n\n constructor(defaultLimit: number) {\n this.defaultLimit = defaultLimit;\n }\n\n check(key: string, limit?: number): boolean {\n const max = limit || this.defaultLimit;\n const now = Date.now();\n const entry = this.store.get(key);\n\n if (!entry || now > entry.resetAt) {\n this.store.set(key, { count: 1, resetAt: now + 60000 }); // 1분 윈도우\n return true;\n }\n\n if (entry.count >= max) {\n return false;\n }\n\n entry.count++;\n return true;\n }\n\n // 오래된 엔트리 정리\n cleanup() {\n const now = Date.now();\n for (const [key, entry] of this.store) {\n if (now > entry.resetAt) this.store.delete(key);\n }\n }\n}\n\n// ─── Router ──────────────────────────────────────────────────────────\n\nexport class ClawfireRouter {\n private routes = new Map<string, Route>();\n private options: RouterOptions;\n private rateLimiter: RateLimiter;\n private cleanupInterval?: ReturnType<typeof setInterval>;\n\n constructor(options: RouterOptions = {}) {\n this.options = options;\n this.rateLimiter = new RateLimiter(options.rateLimit || 100);\n this.cleanupInterval = setInterval(() => this.rateLimiter.cleanup(), 60000);\n }\n\n /** 라우트 등록 */\n register(path: string, contract: APIContract<any, any>): this {\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n this.routes.set(normalizedPath, { path: normalizedPath, contract });\n logger.debug(`Route registered: ${normalizedPath}`);\n return this;\n }\n\n /** 여러 라우트 한 번에 등록 */\n registerAll(routes: Record<string, APIContract<any, any>>): this {\n for (const [path, contract] of Object.entries(routes)) {\n this.register(path, contract);\n }\n return this;\n }\n\n /** 매니페스트 생성 */\n getManifest(): Manifest {\n const apis: ManifestEntry[] = [];\n for (const [path, route] of this.routes) {\n apis.push(contractToManifest(path, route.contract));\n }\n\n return {\n version: \"1.0.0\",\n generatedAt: new Date().toISOString(),\n apis,\n models: {},\n };\n }\n\n /** HTTP 요청 핸들러 (Firebase Functions에서 사용) */\n async handleRequest(\n req: { path?: string; url?: string; body?: unknown; headers?: Record<string, string>; ip?: string; method?: string },\n res: { status: (code: number) => { json: (data: unknown) => void; send: (data: string) => void; end: () => void }; set: (headers: Record<string, string>) => void },\n ): Promise<void> {\n // CORS 처리\n const corsOrigins = this.options.cors || [];\n const origin = req.headers?.origin || req.headers?.Origin || \"\";\n\n if (corsOrigins.length > 0 && corsOrigins.includes(origin)) {\n res.set({\n \"Access-Control-Allow-Origin\": origin,\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Max-Age\": \"86400\",\n });\n } else if (corsOrigins.length === 0) {\n // CORS 기본 deny — 설정 없으면 허용하지 않음\n res.set({\n \"Access-Control-Allow-Origin\": \"\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n });\n }\n\n // OPTIONS (preflight)\n if (req.method === \"OPTIONS\") {\n res.status(204).end();\n return;\n }\n\n // 안전 헤더\n res.set({\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-Frame-Options\": \"DENY\",\n \"X-XSS-Protection\": \"1; mode=block\",\n \"Content-Type\": \"application/json\",\n });\n\n // 경로 추출 — /api/xxx/yyy → /xxx/yyy\n let routePath = req.path || req.url || \"\";\n if (routePath.startsWith(\"/api\")) {\n routePath = routePath.slice(4);\n }\n\n // 시스템 엔드포인트 (GET 허용)\n if (routePath === \"/__manifest\") {\n res.status(200).json(this.getManifest());\n return;\n }\n\n // POST만 허용 (시스템 엔드포인트 제외)\n if (req.method && req.method !== \"POST\") {\n res.status(405).json({ error: { code: \"METHOD_NOT_ALLOWED\", message: \"Only POST is allowed\" } });\n return;\n }\n\n // 라우트 매칭\n const route = this.matchRoute(routePath);\n if (!route) {\n res.status(404).json({ error: { code: \"NOT_FOUND\", message: `API not found: ${routePath}` } });\n return;\n }\n\n try {\n // Rate limit\n const clientKey = req.ip || \"unknown\";\n const rateLimit = route.contract.meta.rateLimit || this.options.rateLimit;\n if (rateLimit && !this.rateLimiter.check(clientKey, rateLimit)) {\n throw Errors.rateLimited();\n }\n\n // Auth 처리\n let authCtx: AuthContext | null = null;\n let reauthenticated = false;\n const authHeader = req.headers?.authorization || req.headers?.Authorization;\n\n if (authHeader && this.options.auth) {\n const token = extractBearerToken(authHeader as string);\n if (token) {\n if (route.contract.meta.reauth || route.contract.meta.auth === \"reauth\") {\n authCtx = await verifyReauth(this.options.auth, token);\n reauthenticated = true;\n } else {\n authCtx = await verifyToken(this.options.auth, token);\n }\n }\n }\n\n // 권한 체크\n if (route.contract.meta.auth) {\n checkAuthLevel(authCtx, route.contract.meta.auth, route.contract.meta.roles, reauthenticated);\n }\n\n // 입력 검증\n const rawInput = req.body || {};\n const parsed = route.contract.input.safeParse(rawInput);\n if (!parsed.success) {\n throw Errors.validation(\"Invalid input\", parsed.error.flatten());\n }\n\n // 핸들러 컨텍스트\n const ctx: HandlerContext = {\n auth: authCtx,\n reauthenticated,\n headers: req.headers as Record<string, string>,\n ip: req.ip,\n };\n\n // 미들웨어 + 핸들러 실행\n let result: unknown;\n if (this.options.middleware && this.options.middleware.length > 0) {\n result = await this.runMiddleware(\n this.options.middleware,\n parsed.data,\n ctx,\n () => route.contract.handler(parsed.data, ctx),\n );\n } else {\n result = await route.contract.handler(parsed.data, ctx);\n }\n\n // 성공 응답\n res.status(200).json({ data: result });\n } catch (err) {\n if (err instanceof ClawfireError) {\n logger.warn(`API error [${err.code}]: ${err.message}`);\n res.status(err.statusCode).json(err.toJSON());\n } else {\n logger.error(\"Unhandled error\", err);\n res.status(500).json({\n error: {\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred\",\n },\n });\n }\n }\n }\n\n /** 라우트 매칭 (동적 파라미터 지원) */\n private matchRoute(path: string): Route | undefined {\n // 정확한 매칭\n const exact = this.routes.get(path);\n if (exact) return exact;\n\n // 동적 파라미터 매칭 (/users/:id/get → /users/abc123/get)\n for (const [routePath, route] of this.routes) {\n if (this.matchDynamicRoute(routePath, path)) {\n return route;\n }\n }\n\n return undefined;\n }\n\n private matchDynamicRoute(pattern: string, actual: string): boolean {\n const patternParts = pattern.split(\"/\").filter(Boolean);\n const actualParts = actual.split(\"/\").filter(Boolean);\n if (patternParts.length !== actualParts.length) return false;\n\n return patternParts.every(\n (part, i) => part.startsWith(\":\") || part.startsWith(\"[\") || part === actualParts[i],\n );\n }\n\n /** 미들웨어 체인 실행 */\n private async runMiddleware(\n middlewares: Middleware[],\n input: unknown,\n ctx: HandlerContext,\n handler: () => Promise<unknown>,\n ): Promise<unknown> {\n let index = 0;\n const next = async (): Promise<unknown> => {\n if (index >= middlewares.length) {\n return handler();\n }\n const mw = middlewares[index++];\n return mw(input, ctx, next);\n };\n return next();\n }\n\n /** 등록된 라우트 목록 */\n getRoutes(): Route[] {\n return Array.from(this.routes.values());\n }\n\n /** 리소스 정리 */\n destroy() {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n }\n }\n}\n\n/**\n * 라우터 생성 헬퍼\n */\nexport function createRouter(options?: RouterOptions): ClawfireRouter {\n return new ClawfireRouter(options);\n}\n","/**\n * Clawfire Route Discovery\n *\n * 파일 시스템에서 라우트 자동 발견 (빌드 타임 & 런타임)\n */\nimport { resolve, relative, join } from \"path\";\nimport { existsSync, readdirSync, statSync } from \"fs\";\n\nexport interface DiscoveredRoute {\n /** 파일 경로 (상대) */\n filePath: string;\n /** API 경로 (/products/list) */\n apiPath: string;\n /** 동적 파라미터 이름 */\n params: string[];\n}\n\n/**\n * routes 디렉터리에서 라우트 파일 자동 발견\n *\n * @param routesDir - routes 디렉터리 절대 경로\n * @returns 발견된 라우트 목록\n *\n * @example\n * ```\n * functions/routes/products/list.ts → { apiPath: \"/products/list\", params: [] }\n * functions/routes/products/[id]/get.ts → { apiPath: \"/products/:id/get\", params: [\"id\"] }\n * functions/routes/health.ts → { apiPath: \"/health\", params: [] }\n * ```\n */\nexport function discoverRoutes(routesDir: string): DiscoveredRoute[] {\n if (!existsSync(routesDir)) {\n return [];\n }\n\n const routes: DiscoveredRoute[] = [];\n scanDirectory(routesDir, routesDir, routes);\n return routes.sort((a, b) => a.apiPath.localeCompare(b.apiPath));\n}\n\nfunction scanDirectory(baseDir: string, currentDir: string, routes: DiscoveredRoute[]): void {\n const entries = readdirSync(currentDir);\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n // 숨김 디렉터리, node_modules 무시\n if (entry.startsWith(\".\") || entry === \"node_modules\") continue;\n scanDirectory(baseDir, fullPath, routes);\n } else if (stat.isFile()) {\n // .ts, .js 파일만\n if (!entry.endsWith(\".ts\") && !entry.endsWith(\".js\")) continue;\n // index, _로 시작하는 파일 무시\n if (entry.startsWith(\"_\")) continue;\n // .d.ts 무시\n if (entry.endsWith(\".d.ts\")) continue;\n\n const relativePath = relative(baseDir, fullPath);\n const route = filePathToRoute(relativePath);\n routes.push(route);\n }\n }\n}\n\nfunction filePathToRoute(filePath: string): DiscoveredRoute {\n const params: string[] = [];\n\n // 확장자 제거\n let routePath = filePath.replace(/\\.(ts|js)$/, \"\");\n\n // Windows 경로 → POSIX\n routePath = routePath.replace(/\\\\/g, \"/\");\n\n // index 파일은 디렉터리 자체\n if (routePath.endsWith(\"/index\") || routePath === \"index\") {\n routePath = routePath.replace(/\\/?index$/, \"\");\n }\n\n // [param] → :param 변환\n routePath = routePath.replace(/\\[([^\\]]+)\\]/g, (_, param) => {\n params.push(param);\n return `:${param}`;\n });\n\n // 앞에 / 추가\n const apiPath = `/${routePath}`;\n\n return {\n filePath,\n apiPath,\n params,\n };\n}\n\n/**\n * 라우트 파일에서 import하여 라우터에 등록하는 코드 생성 (빌드 타임)\n */\nexport function generateRouteImports(routes: DiscoveredRoute[], routesDir: string): string {\n const lines: string[] = [\n '// AUTO-GENERATED by Clawfire — DO NOT EDIT',\n '// This file is regenerated whenever routes change.',\n '',\n 'import { createRouter } from \"clawfire/functions\";',\n '',\n ];\n\n routes.forEach((route, i) => {\n const importPath = `./${route.filePath.replace(/\\.(ts|js)$/, \".js\")}`;\n lines.push(`import route_${i} from \"${importPath}\";`);\n });\n\n lines.push('');\n lines.push('export function registerAllRoutes(router: ReturnType<typeof createRouter>) {');\n\n routes.forEach((route, i) => {\n lines.push(` router.register(\"${route.apiPath}\", route_${i});`);\n });\n\n lines.push(' return router;');\n lines.push('}');\n\n return lines.join('\\n');\n}\n","/**\n * Clawfire Playground\n *\n * 웹 기반 API 탐색기: API 목록, 인증 테스트, 요청/응답 뷰어\n * 단일 HTML 파일로 생성되어 Firebase Hosting에 배포됩니다.\n */\n\nexport function generatePlaygroundHtml(options?: {\n title?: string;\n apiBaseUrl?: string;\n}): string {\n const title = options?.title || \"Clawfire Playground\";\n const apiBaseUrl = options?.apiBaseUrl || \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n <style>\n :root {\n --bg: #0a0a0a;\n --surface: #141414;\n --surface2: #1e1e1e;\n --border: #2a2a2a;\n --text: #e5e5e5;\n --text2: #a3a3a3;\n --accent: #f97316;\n --accent2: #fb923c;\n --green: #22c55e;\n --red: #ef4444;\n --blue: #3b82f6;\n --yellow: #eab308;\n --radius: 8px;\n --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --mono: 'JetBrains Mono', 'Fira Code', monospace;\n }\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: var(--font); background: var(--bg); color: var(--text); min-height: 100vh; }\n\n .layout { display: grid; grid-template-columns: 320px 1fr; min-height: 100vh; }\n .sidebar { background: var(--surface); border-right: 1px solid var(--border); overflow-y: auto; }\n .main { padding: 24px; overflow-y: auto; }\n\n .logo { padding: 20px; border-bottom: 1px solid var(--border); }\n .logo h1 { font-size: 20px; font-weight: 700; color: var(--accent); }\n .logo p { font-size: 12px; color: var(--text2); margin-top: 4px; }\n\n .auth-section { padding: 16px; border-bottom: 1px solid var(--border); }\n .auth-section label { font-size: 12px; color: var(--text2); display: block; margin-bottom: 6px; }\n .auth-input { width: 100%; padding: 8px 12px; background: var(--surface2); border: 1px solid var(--border);\n border-radius: var(--radius); color: var(--text); font-family: var(--mono); font-size: 12px; }\n .auth-status { font-size: 11px; margin-top: 6px; }\n .auth-status.ok { color: var(--green); }\n .auth-status.no { color: var(--text2); }\n\n .search { padding: 12px 16px; border-bottom: 1px solid var(--border); }\n .search input { width: 100%; padding: 8px 12px; background: var(--surface2); border: 1px solid var(--border);\n border-radius: var(--radius); color: var(--text); font-size: 13px; }\n\n .api-list { padding: 8px 0; }\n .api-group { padding: 4px 0; }\n .api-group-title { padding: 8px 16px; font-size: 11px; color: var(--text2); text-transform: uppercase;\n letter-spacing: 0.5px; font-weight: 600; }\n .api-item { padding: 8px 16px; cursor: pointer; transition: background 0.15s; display: flex; align-items: center;\n gap: 8px; font-size: 13px; }\n .api-item:hover { background: var(--surface2); }\n .api-item.active { background: var(--surface2); border-left: 2px solid var(--accent); }\n .api-badge { font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 600; }\n .badge-public { background: #22c55e20; color: var(--green); }\n .badge-auth { background: #3b82f620; color: var(--blue); }\n .badge-role { background: #eab30820; color: var(--yellow); }\n .badge-reauth { background: #ef444420; color: var(--red); }\n\n .panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 16px; }\n .panel-header { padding: 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center;\n justify-content: space-between; }\n .panel-header h2 { font-size: 16px; font-weight: 600; }\n .panel-body { padding: 16px; }\n\n .field { margin-bottom: 12px; }\n .field label { font-size: 12px; color: var(--text2); display: block; margin-bottom: 4px; }\n .field-type { font-size: 11px; color: var(--text2); font-family: var(--mono); }\n .field-required { color: var(--red); font-size: 11px; }\n\n textarea, input[type=\"text\"] { width: 100%; padding: 10px 14px; background: var(--surface2);\n border: 1px solid var(--border); border-radius: var(--radius); color: var(--text);\n font-family: var(--mono); font-size: 13px; resize: vertical; }\n textarea { min-height: 200px; }\n\n .btn { padding: 10px 20px; border: none; border-radius: var(--radius); font-size: 14px;\n font-weight: 600; cursor: pointer; transition: all 0.15s; }\n .btn-primary { background: var(--accent); color: white; }\n .btn-primary:hover { background: var(--accent2); }\n .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }\n\n .response-section { margin-top: 16px; }\n .status-badge { font-size: 12px; padding: 4px 8px; border-radius: 4px; font-weight: 600; }\n .status-ok { background: #22c55e20; color: var(--green); }\n .status-err { background: #ef444420; color: var(--red); }\n pre { background: var(--surface2); padding: 16px; border-radius: var(--radius); overflow-x: auto;\n font-family: var(--mono); font-size: 13px; line-height: 1.5; white-space: pre-wrap; }\n\n .schema-info { font-size: 12px; color: var(--text2); line-height: 1.6; }\n .schema-info code { background: var(--surface2); padding: 2px 6px; border-radius: 4px; font-family: var(--mono);\n font-size: 11px; }\n\n .empty-state { text-align: center; padding: 80px 40px; color: var(--text2); }\n .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }\n\n .timer { font-size: 12px; color: var(--text2); font-family: var(--mono); }\n\n @media (max-width: 768px) {\n .layout { grid-template-columns: 1fr; }\n .sidebar { max-height: 40vh; }\n }\n </style>\n</head>\n<body>\n <div class=\"layout\">\n <div class=\"sidebar\">\n <div class=\"logo\">\n <h1>Clawfire</h1>\n <p>API Playground</p>\n </div>\n <div class=\"auth-section\">\n <label>Bearer Token</label>\n <input type=\"text\" class=\"auth-input\" id=\"token-input\" placeholder=\"Paste your ID token...\">\n <div class=\"auth-status no\" id=\"auth-status\">Not authenticated</div>\n </div>\n <div class=\"search\">\n <input type=\"text\" id=\"search-input\" placeholder=\"Search APIs...\">\n </div>\n <div class=\"api-list\" id=\"api-list\"></div>\n </div>\n <div class=\"main\" id=\"main-content\">\n <div class=\"empty-state\">\n <h2>Select an API</h2>\n <p>Choose an API from the sidebar to test it.</p>\n </div>\n </div>\n </div>\n\n <script>\n const BASE_URL = ${JSON.stringify(apiBaseUrl)} || window.location.origin;\n let manifest = null;\n let selectedApi = null;\n\n async function loadManifest() {\n try {\n const res = await fetch(BASE_URL + '/api/__manifest', { method: 'POST' });\n manifest = await res.json();\n renderApiList(manifest.apis);\n } catch (e) {\n document.getElementById('api-list').innerHTML =\n '<div style=\"padding:16px;color:var(--red);font-size:13px;\">Failed to load API manifest. Make sure your server is running.</div>';\n }\n }\n\n function renderApiList(apis) {\n const groups = {};\n apis.forEach(api => {\n const parts = api.path.split('/').filter(Boolean);\n const group = parts.length > 1 ? parts[0] : 'root';\n if (!groups[group]) groups[group] = [];\n groups[group].push(api);\n });\n\n const el = document.getElementById('api-list');\n el.innerHTML = Object.entries(groups).map(([group, items]) =>\n '<div class=\"api-group\">' +\n '<div class=\"api-group-title\">' + group + '</div>' +\n items.map(api => {\n const auth = api.meta.auth || 'public';\n const badgeClass = 'badge-' + auth;\n return '<div class=\"api-item\" onclick=\"selectApi(\\\\'' + api.path + '\\\\')\">' +\n '<span class=\"api-badge ' + badgeClass + '\">' + auth.toUpperCase() + '</span>' +\n '<span>' + api.path + '</span>' +\n '</div>';\n }).join('') +\n '</div>'\n ).join('');\n }\n\n function selectApi(path) {\n selectedApi = manifest.apis.find(a => a.path === path);\n if (!selectedApi) return;\n\n document.querySelectorAll('.api-item').forEach(el => el.classList.remove('active'));\n event.currentTarget?.classList.add('active');\n\n const main = document.getElementById('main-content');\n const exampleInput = selectedApi.meta.exampleInput\n ? JSON.stringify(selectedApi.meta.exampleInput, null, 2)\n : generateExampleFromSchema(selectedApi.inputSchema);\n\n main.innerHTML =\n '<div class=\"panel\">' +\n '<div class=\"panel-header\">' +\n '<h2>POST ' + selectedApi.path + '</h2>' +\n '<span class=\"api-badge badge-' + (selectedApi.meta.auth || 'public') + '\">' +\n (selectedApi.meta.auth || 'public').toUpperCase() + '</span>' +\n '</div>' +\n '<div class=\"panel-body\">' +\n '<p style=\"color:var(--text2);margin-bottom:16px;\">' + (selectedApi.meta.description || '') + '</p>' +\n (selectedApi.meta.tags ? '<div style=\"margin-bottom:12px;\">' + selectedApi.meta.tags.map(t =>\n '<span style=\"background:var(--surface2);padding:2px 8px;border-radius:4px;font-size:11px;margin-right:4px;\">' + t + '</span>'\n ).join('') + '</div>' : '') +\n '<div class=\"schema-info\" style=\"margin-bottom:16px;\">' +\n '<strong>Input Schema:</strong><br>' + renderSchemaInfo(selectedApi.inputSchema) +\n '</div>' +\n '<div class=\"schema-info\" style=\"margin-bottom:16px;\">' +\n '<strong>Output Schema:</strong><br>' + renderSchemaInfo(selectedApi.outputSchema) +\n '</div>' +\n '</div>' +\n '</div>' +\n '<div class=\"panel\">' +\n '<div class=\"panel-header\"><h2>Request</h2></div>' +\n '<div class=\"panel-body\">' +\n '<textarea id=\"req-body\" placeholder=\"Request JSON body\">' + exampleInput + '</textarea>' +\n '<div style=\"margin-top:12px;display:flex;align-items:center;gap:12px;\">' +\n '<button class=\"btn btn-primary\" onclick=\"sendRequest()\">Send Request</button>' +\n '<span class=\"timer\" id=\"timer\"></span>' +\n '</div>' +\n '</div>' +\n '</div>' +\n '<div class=\"response-section\" id=\"response-section\"></div>';\n }\n\n function renderSchemaInfo(schema) {\n if (!schema || !schema.properties) return '<code>void</code>';\n return Object.entries(schema.properties).map(([key, prop]) => {\n const required = schema.required?.includes(key);\n const type = prop.type || 'unknown';\n const enumVals = prop.enum ? ' (' + prop.enum.join(', ') + ')' : '';\n return '<code>' + key + '</code>: <span class=\"field-type\">' + type + enumVals + '</span>' +\n (required ? ' <span class=\"field-required\">required</span>' : ' <span style=\"color:var(--text2);font-size:11px;\">optional</span>');\n }).join('<br>');\n }\n\n function generateExampleFromSchema(schema) {\n if (!schema || !schema.properties) return '{}';\n const obj = {};\n for (const [key, prop] of Object.entries(schema.properties)) {\n if (prop.enum) { obj[key] = prop.enum[0]; continue; }\n switch (prop.type) {\n case 'string': obj[key] = prop.format === 'email' ? 'user@example.com' : 'string'; break;\n case 'number': obj[key] = 0; break;\n case 'boolean': obj[key] = false; break;\n case 'array': obj[key] = []; break;\n case 'object': obj[key] = {}; break;\n default: obj[key] = null;\n }\n }\n return JSON.stringify(obj, null, 2);\n }\n\n async function sendRequest() {\n if (!selectedApi) return;\n const body = document.getElementById('req-body').value;\n const token = document.getElementById('token-input').value;\n const timer = document.getElementById('timer');\n const section = document.getElementById('response-section');\n\n let parsed;\n try { parsed = JSON.parse(body); } catch {\n section.innerHTML = '<div class=\"panel\"><div class=\"panel-body\"><pre style=\"color:var(--red)\">Invalid JSON</pre></div></div>';\n return;\n }\n\n const start = performance.now();\n timer.textContent = 'Sending...';\n\n try {\n const headers = { 'Content-Type': 'application/json' };\n if (token) headers['Authorization'] = 'Bearer ' + token;\n\n const res = await fetch(BASE_URL + '/api' + selectedApi.path, {\n method: 'POST', headers, body: JSON.stringify(parsed)\n });\n const elapsed = Math.round(performance.now() - start);\n timer.textContent = elapsed + 'ms';\n\n const json = await res.json();\n const isOk = res.ok;\n\n section.innerHTML =\n '<div class=\"panel\">' +\n '<div class=\"panel-header\">' +\n '<h2>Response</h2>' +\n '<span class=\"status-badge ' + (isOk ? 'status-ok' : 'status-err') + '\">' +\n res.status + ' ' + res.statusText + '</span>' +\n '</div>' +\n '<div class=\"panel-body\"><pre>' + syntaxHighlight(JSON.stringify(json, null, 2)) + '</pre></div>' +\n '</div>';\n } catch (e) {\n const elapsed = Math.round(performance.now() - start);\n timer.textContent = elapsed + 'ms';\n section.innerHTML =\n '<div class=\"panel\"><div class=\"panel-body\"><pre style=\"color:var(--red)\">Network error: ' + e.message + '</pre></div></div>';\n }\n }\n\n function syntaxHighlight(json) {\n return json.replace(/(\"(\\\\\\\\u[a-fA-F0-9]{4}|\\\\\\\\[^u]|[^\\\\\\\\\"])*\"(\\\\s*:)?)|\\\\b(true|false|null)\\\\b|-?\\\\d+(\\\\.\\\\d+)?([eE][+-]?\\\\d+)?/g,\n function(match) {\n let cls = 'color:#eab308';\n if (/^\"/.test(match)) {\n if (/:$/.test(match)) cls = 'color:#3b82f6';\n else cls = 'color:#22c55e';\n } else if (/true|false/.test(match)) cls = 'color:#f97316';\n else if (/null/.test(match)) cls = 'color:#ef4444';\n return '<span style=\"' + cls + '\">' + match + '</span>';\n }\n );\n }\n\n // Search\n document.getElementById('search-input')?.addEventListener('input', (e) => {\n if (!manifest) return;\n const q = e.target.value.toLowerCase();\n const filtered = manifest.apis.filter(a => a.path.toLowerCase().includes(q) || a.meta.description?.toLowerCase().includes(q));\n renderApiList(filtered);\n });\n\n // Token status\n document.getElementById('token-input')?.addEventListener('input', (e) => {\n const el = document.getElementById('auth-status');\n if (e.target.value) {\n el.textContent = 'Token set';\n el.className = 'auth-status ok';\n } else {\n el.textContent = 'Not authenticated';\n el.className = 'auth-status no';\n }\n });\n\n loadManifest();\n </script>\n</body>\n</html>`;\n}\n","/**\n * Clawfire File Watcher\n *\n * Node.js fs.watch 기반 파일 변경 감지 (외부 의존성 없음)\n * macOS/Windows에서 recursive 지원, Linux에서는 디렉터리별 개별 감시\n */\nimport { watch, existsSync, statSync, readdirSync } from \"fs\";\nimport { join, extname } from \"path\";\nimport { EventEmitter } from \"events\";\n\nexport type WatchEventType = \"route-change\" | \"schema-change\" | \"config-change\" | \"frontend-change\" | \"css-change\" | \"page-change\" | \"component-change\" | \"any-change\";\n\nexport interface WatchEvent {\n type: WatchEventType;\n filePath: string;\n timestamp: number;\n}\n\nexport class FileWatcher extends EventEmitter {\n private watchers: ReturnType<typeof watch>[] = [];\n private debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private debounceMs: number;\n\n constructor(debounceMs = 150) {\n super();\n this.debounceMs = debounceMs;\n }\n\n /**\n * 디렉터리 감시 시작\n */\n watchDir(dir: string, eventType: WatchEventType): this {\n if (!existsSync(dir)) return this;\n\n try {\n // macOS/Windows: recursive 옵션 지원\n const watcher = watch(dir, { recursive: true }, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n this.emitDebounced(filePath, eventType);\n });\n\n watcher.on(\"error\", (err) => {\n // Linux에서 recursive 미지원 시 fallback\n if ((err as any).code === \"ERR_FEATURE_UNAVAILABLE_ON_PLATFORM\") {\n this.watchDirRecursiveManual(dir, eventType);\n }\n });\n\n this.watchers.push(watcher);\n } catch {\n // fallback: 수동 재귀 감시\n this.watchDirRecursiveManual(dir, eventType);\n }\n\n return this;\n }\n\n /**\n * 단일 파일 감시\n */\n watchFile(filePath: string, eventType: WatchEventType): this {\n if (!existsSync(filePath)) return this;\n\n const watcher = watch(filePath, (event) => {\n this.emitDebounced(filePath, eventType);\n });\n\n this.watchers.push(watcher);\n return this;\n }\n\n /**\n * 프론트엔드 디렉터리 감시 (확장자별 이벤트 타입 자동 결정)\n */\n watchDirFrontend(dir: string): this {\n if (!existsSync(dir)) return this;\n\n try {\n const watcher = watch(dir, { recursive: true }, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n const ext = extname(filePath);\n const eventType: WatchEventType = ext === \".css\" ? \"css-change\" : \"frontend-change\";\n this.emitDebounced(filePath, eventType);\n });\n\n watcher.on(\"error\", (err) => {\n if ((err as any).code === \"ERR_FEATURE_UNAVAILABLE_ON_PLATFORM\") {\n this.watchDirFrontendRecursiveManual(dir);\n }\n });\n\n this.watchers.push(watcher);\n } catch {\n this.watchDirFrontendRecursiveManual(dir);\n }\n\n return this;\n }\n\n /**\n * Linux fallback: 프론트엔드 디렉터리 수동 재귀 감시\n */\n private watchDirFrontendRecursiveManual(dir: string): void {\n if (!existsSync(dir)) return;\n\n const watcher = watch(dir, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n const ext = extname(filePath);\n const eventType: WatchEventType = ext === \".css\" ? \"css-change\" : \"frontend-change\";\n this.emitDebounced(filePath, eventType);\n });\n this.watchers.push(watcher);\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith(\".\") && entry.name !== \"node_modules\") {\n this.watchDirFrontendRecursiveManual(join(dir, entry.name));\n }\n }\n } catch {}\n }\n\n /**\n * Linux fallback: 디렉터리 수동 재귀 감시\n */\n private watchDirRecursiveManual(dir: string, eventType: WatchEventType): void {\n if (!existsSync(dir)) return;\n\n const watcher = watch(dir, (event, filename) => {\n if (!filename) return;\n const filePath = join(dir, filename);\n if (!this.isWatchedFile(filePath)) return;\n this.emitDebounced(filePath, eventType);\n });\n this.watchers.push(watcher);\n\n // 서브디렉터리도 감시\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith(\".\") && entry.name !== \"node_modules\") {\n this.watchDirRecursiveManual(join(dir, entry.name), eventType);\n }\n }\n } catch {}\n }\n\n /**\n * 감시 대상 파일인지 확인\n */\n private isWatchedFile(filePath: string): boolean {\n const ext = extname(filePath);\n return [\".ts\", \".tsx\", \".js\", \".jsx\", \".json\", \".html\", \".css\", \".svg\", \".png\", \".jpg\", \".jpeg\", \".gif\", \".ico\", \".webp\", \".woff\", \".woff2\"].includes(ext);\n }\n\n /**\n * 디바운스 이벤트 발생\n */\n private emitDebounced(filePath: string, type: WatchEventType): void {\n const existing = this.debounceTimers.get(filePath);\n if (existing) clearTimeout(existing);\n\n this.debounceTimers.set(\n filePath,\n setTimeout(() => {\n this.debounceTimers.delete(filePath);\n const event: WatchEvent = { type, filePath, timestamp: Date.now() };\n this.emit(\"change\", event);\n this.emit(type, event);\n }, this.debounceMs),\n );\n }\n\n /**\n * 모든 감시 중지\n */\n close(): void {\n for (const watcher of this.watchers) {\n watcher.close();\n }\n this.watchers = [];\n for (const timer of this.debounceTimers.values()) {\n clearTimeout(timer);\n }\n this.debounceTimers.clear();\n this.removeAllListeners();\n }\n}\n","/**\n * Clawfire Page Compiler\n *\n * File-based page routing with layouts and components.\n * Resolves URLs to page files, processes components (<c-name />),\n * applies nested layouts (<slot />), and extracts metadata.\n */\nimport { resolve, join, relative, dirname, basename, extname } from \"node:path\";\nimport { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync } from \"node:fs\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface PageMeta {\n title?: string;\n description?: string;\n [key: string]: string | undefined;\n}\n\nexport interface CompiledPage {\n html: string;\n meta: PageMeta;\n filePath: string;\n layouts: string[];\n}\n\nexport interface PartialPage {\n html: string;\n meta: PageMeta;\n path: string;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────\n\nconst MAX_COMPONENT_DEPTH = 10;\nconst META_REGEX = /<!--\\s*@(\\w+):\\s*(.+?)\\s*-->/g;\nconst COMPONENT_REGEX = /<c-([a-z][a-z0-9-]*)\\s*\\/>/g;\nconst SLOT_MARKER = \"<slot />\";\n\n// ─── Page Compiler ───────────────────────────────────────────────────\n\nexport class PageCompiler {\n private pagesDir: string;\n private componentsDir: string;\n\n constructor(private projectDir: string) {\n this.pagesDir = resolve(projectDir, \"app/pages\");\n this.componentsDir = resolve(projectDir, \"app/components\");\n }\n\n /**\n * Check if the page system is active (app/pages/ exists)\n */\n isActive(): boolean {\n return existsSync(this.pagesDir);\n }\n\n /**\n * Resolve a URL pathname to a page file path.\n * Returns null if no matching page exists.\n *\n * / → app/pages/index.html\n * /about → app/pages/about.html OR app/pages/about/index.html\n * /todos → app/pages/todos/index.html OR app/pages/todos.html\n */\n resolve(pathname: string): string | null {\n if (!this.isActive()) return null;\n\n // Normalize: strip trailing slash, default to /\n const clean = pathname === \"/\" ? \"\" : pathname.replace(/\\/+$/, \"\");\n const segments = clean ? clean.split(\"/\").filter(Boolean) : [];\n\n // Build candidate paths\n const candidates: string[] = [];\n\n if (segments.length === 0) {\n candidates.push(join(this.pagesDir, \"index.html\"));\n } else {\n const pathPart = segments.join(\"/\");\n // Try direct file first, then directory/index\n candidates.push(join(this.pagesDir, `${pathPart}.html`));\n candidates.push(join(this.pagesDir, pathPart, \"index.html\"));\n }\n\n for (const candidate of candidates) {\n // Security: must be within pagesDir\n if (!candidate.startsWith(this.pagesDir)) continue;\n // Skip layout and special files\n const name = basename(candidate);\n if (name.startsWith(\"_\")) continue;\n if (existsSync(candidate)) return candidate;\n }\n\n return null;\n }\n\n /**\n * Resolve the 404 page file path.\n */\n resolve404(): string | null {\n const path404 = join(this.pagesDir, \"_404.html\");\n return existsSync(path404) ? path404 : null;\n }\n\n /**\n * Compile a full page with layouts and components.\n */\n compile(pagePath: string): CompiledPage {\n // 1. Read page file\n let pageHtml = readFileSync(pagePath, \"utf-8\");\n\n // 2. Extract metadata\n const meta = this.extractMeta(pageHtml);\n pageHtml = this.stripMeta(pageHtml);\n\n // 3. Process components in page\n pageHtml = this.processComponents(pageHtml);\n\n // 4. Wrap in page container div\n pageHtml = this.wrapPageContent(pageHtml);\n\n // 5. Collect layout chain\n const layouts = this.collectLayouts(pagePath);\n\n // 6. Apply layouts (innermost first)\n let html = pageHtml;\n for (const layoutPath of layouts) {\n let layoutHtml = readFileSync(layoutPath, \"utf-8\");\n layoutHtml = this.processComponents(layoutHtml);\n html = layoutHtml.replace(SLOT_MARKER, html);\n }\n\n // 7. Set <title> from metadata\n if (meta.title) {\n html = this.setTitle(html, meta.title);\n }\n\n return {\n html,\n meta,\n filePath: pagePath,\n layouts,\n };\n }\n\n /**\n * Compile a partial page for SPA navigation.\n * Returns only the page content (no layout) plus metadata.\n */\n compilePartial(pagePath: string, pathname: string): PartialPage {\n let pageHtml = readFileSync(pagePath, \"utf-8\");\n const meta = this.extractMeta(pageHtml);\n pageHtml = this.stripMeta(pageHtml);\n pageHtml = this.processComponents(pageHtml);\n\n return {\n html: pageHtml,\n meta,\n path: pathname,\n };\n }\n\n /**\n * Build all pages into static HTML for production deployment.\n * Walks app/pages/ recursively, compiles each page, and writes to outputDir.\n *\n * - Skips _layout.html (wrapper-only, not standalone)\n * - _404.html → 404.html (Firebase auto-serves for 404s)\n * - Injects optional script (e.g. production router) before </body>\n */\n buildForProduction(outputDir: string, scriptToInject?: string): { pages: string[]; errors: string[] } {\n const pages: string[] = [];\n const errors: string[] = [];\n\n if (!this.isActive()) {\n return { pages, errors };\n }\n\n const walk = (dir: string) => {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n walk(fullPath);\n continue;\n }\n\n if (!entry.endsWith(\".html\")) continue;\n\n // Skip layout files — they are wrappers, not standalone pages\n if (entry === \"_layout.html\") continue;\n\n try {\n // Compile the page (applies layouts + components + metadata)\n const compiled = this.compile(fullPath);\n let html = compiled.html;\n\n // Inject production script before </body>\n if (scriptToInject) {\n if (html.includes(\"</body>\")) {\n html = html.replace(\"</body>\", scriptToInject + \"\\n</body>\");\n } else {\n html += scriptToInject;\n }\n }\n\n // Determine output path\n const relPath = relative(this.pagesDir, fullPath);\n let outputPath: string;\n\n if (entry === \"_404.html\") {\n // _404.html → 404.html at the same directory level\n const parentRel = relative(this.pagesDir, dir);\n outputPath = parentRel ? join(outputDir, parentRel, \"404.html\") : join(outputDir, \"404.html\");\n } else {\n outputPath = join(outputDir, relPath);\n }\n\n // Ensure output directory exists\n const outputDirPath = dirname(outputPath);\n mkdirSync(outputDirPath, { recursive: true });\n\n writeFileSync(outputPath, html, \"utf-8\");\n pages.push(relPath);\n } catch (err) {\n const relPath = relative(this.pagesDir, fullPath);\n errors.push(`${relPath}: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n }\n };\n\n walk(this.pagesDir);\n return { pages, errors };\n }\n\n // ─── Internal Methods ────────────────────────────────────────────\n\n /**\n * Extract <!-- @key: value --> metadata from HTML.\n */\n private extractMeta(html: string): PageMeta {\n const meta: PageMeta = {};\n let match: RegExpExecArray | null;\n const regex = new RegExp(META_REGEX.source, META_REGEX.flags);\n while ((match = regex.exec(html)) !== null) {\n meta[match[1]] = match[2];\n }\n return meta;\n }\n\n /**\n * Strip metadata comments from HTML.\n */\n private stripMeta(html: string): string {\n return html.replace(META_REGEX, \"\").trim();\n }\n\n /**\n * Process <c-name /> component tags by replacing them with component file contents.\n * Supports nested components up to MAX_COMPONENT_DEPTH.\n */\n private processComponents(html: string, depth = 0): string {\n if (depth >= MAX_COMPONENT_DEPTH) return html;\n if (!COMPONENT_REGEX.test(html)) return html;\n\n // Reset regex state\n const regex = new RegExp(COMPONENT_REGEX.source, COMPONENT_REGEX.flags);\n\n const result = html.replace(regex, (_match, name: string) => {\n const componentPath = join(this.componentsDir, `${name}.html`);\n if (!existsSync(componentPath)) {\n return `<!-- component \"${name}\" not found -->`;\n }\n const content = readFileSync(componentPath, \"utf-8\").trim();\n // Recursively process nested components\n return this.processComponents(content, depth + 1);\n });\n\n return result;\n }\n\n /**\n * Wrap page content in the router target div.\n */\n private wrapPageContent(html: string): string {\n return `<div id=\"clawfire-page\">\\n${html}\\n</div>`;\n }\n\n /**\n * Collect layout files from page directory up to pages root.\n * Returns innermost layout first (closest to page).\n */\n private collectLayouts(pagePath: string): string[] {\n const layouts: string[] = [];\n let dir = dirname(pagePath);\n\n while (dir.startsWith(this.pagesDir)) {\n const layoutPath = join(dir, \"_layout.html\");\n if (existsSync(layoutPath)) {\n layouts.push(layoutPath);\n }\n // Stop at pages root\n if (dir === this.pagesDir) break;\n dir = dirname(dir);\n }\n\n // Reverse: outermost layout applied last (wraps everything)\n // We want to reduce from innermost out, so reverse:\n // page → innermost layout's <slot /> → outer layout's <slot />\n // The array is already [innermost, ..., outermost] from the walk.\n // We apply innermost first, which means page goes into innermost <slot />,\n // then that result goes into the next outer layout's <slot />, etc.\n return layouts;\n }\n\n /**\n * Set or update <title> tag in HTML.\n */\n private setTitle(html: string, title: string): string {\n if (html.includes(\"<title>\")) {\n return html.replace(/<title>[^<]*<\\/title>/, `<title>${title}</title>`);\n }\n if (html.includes(\"</head>\")) {\n return html.replace(\"</head>\", ` <title>${title}</title>\\n</head>`);\n }\n return html;\n }\n}\n","/**\n * Clawfire Environment Variable Manager\n *\n * Manages .env file and .env.descriptions.json for the dev dashboard.\n * Preserves comments and ordering in .env files.\n */\nimport { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface EnvVariable {\n key: string;\n value: string;\n description: string;\n isPlaceholder: boolean;\n line: number;\n}\n\nexport interface EnvData {\n variables: EnvVariable[];\n descriptions: Record<string, string>;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────\n\nconst KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/i;\nconst PLACEHOLDER_PATTERNS = [\n /^YOUR_/i,\n /^CHANGE_ME$/i,\n /^TODO$/i,\n /^REPLACE_/i,\n /^XXX/i,\n /^$/,\n];\n\n// ─── EnvManager ──────────────────────────────────────────────────────\n\nexport class EnvManager {\n private envPath: string;\n private descriptionsPath: string;\n\n constructor(projectDir: string) {\n this.envPath = resolve(projectDir, \".env\");\n this.descriptionsPath = resolve(projectDir, \".env.descriptions.json\");\n }\n\n /** Read all env variables with descriptions and placeholder detection */\n read(): EnvData {\n const descriptions = this.readDescriptions();\n const variables: EnvVariable[] = [];\n\n if (!existsSync(this.envPath)) {\n return { variables, descriptions };\n }\n\n const content = readFileSync(this.envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n // Skip empty lines and comments\n if (!line || line.startsWith(\"#\")) continue;\n\n const eqIdx = line.indexOf(\"=\");\n if (eqIdx === -1) continue;\n\n const key = line.slice(0, eqIdx).trim();\n const value = line.slice(eqIdx + 1).trim();\n\n if (!KEY_PATTERN.test(key)) continue;\n\n variables.push({\n key,\n value,\n description: descriptions[key] || \"\",\n isPlaceholder: isPlaceholder(value),\n line: i + 1,\n });\n }\n\n return { variables, descriptions };\n }\n\n /** Set or update an env variable */\n set(key: string, value: string, description?: string): void {\n if (!KEY_PATTERN.test(key)) {\n throw new Error(`Invalid key: \"${key}\" — must match ${KEY_PATTERN}`);\n }\n\n // Update .env file\n let lines: string[] = [];\n if (existsSync(this.envPath)) {\n lines = readFileSync(this.envPath, \"utf-8\").split(\"\\n\");\n }\n\n let found = false;\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (trimmed.startsWith(\"#\") || !trimmed) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const existingKey = trimmed.slice(0, eqIdx).trim();\n if (existingKey === key) {\n lines[i] = `${key}=${value}`;\n found = true;\n break;\n }\n }\n\n if (!found) {\n // Add to end, ensure trailing newline separation\n if (lines.length > 0 && lines[lines.length - 1].trim() !== \"\") {\n lines.push(`${key}=${value}`);\n } else {\n // Replace trailing empty line\n lines.push(`${key}=${value}`);\n }\n }\n\n writeFileSync(this.envPath, lines.join(\"\\n\"), \"utf-8\");\n\n // Update description if provided\n if (description !== undefined) {\n const descriptions = this.readDescriptions();\n descriptions[key] = description;\n this.writeDescriptions(descriptions);\n }\n }\n\n /** Delete an env variable */\n delete(key: string): void {\n if (!existsSync(this.envPath)) return;\n\n const lines = readFileSync(this.envPath, \"utf-8\").split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\") || !trimmed) return true;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) return true;\n return trimmed.slice(0, eqIdx).trim() !== key;\n });\n\n writeFileSync(this.envPath, filtered.join(\"\\n\"), \"utf-8\");\n\n // Remove description\n const descriptions = this.readDescriptions();\n if (key in descriptions) {\n delete descriptions[key];\n this.writeDescriptions(descriptions);\n }\n }\n\n // ─── Private ─────────────────────────────────────────────────────\n\n private readDescriptions(): Record<string, string> {\n if (!existsSync(this.descriptionsPath)) return {};\n try {\n return JSON.parse(readFileSync(this.descriptionsPath, \"utf-8\"));\n } catch {\n return {};\n }\n }\n\n private writeDescriptions(descriptions: Record<string, string>): void {\n writeFileSync(\n this.descriptionsPath,\n JSON.stringify(descriptions, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction isPlaceholder(value: string): boolean {\n return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(value));\n}\n","/**\n * Clawfire Firebase Status Checker\n *\n * Checks Firebase project health via file inspection and CLI commands.\n * Results are cached for 30 seconds to avoid repeated CLI calls.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execFile } from \"node:child_process\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type ServiceStatus = \"configured\" | \"placeholder\" | \"missing\";\n\nexport interface ServiceInfo {\n name: string;\n status: ServiceStatus;\n detail?: string;\n}\n\nexport interface FirebaseEnvironmentStatus {\n /** CLI info */\n cli: {\n installed: boolean;\n version: string;\n authenticated: boolean;\n user: string;\n };\n /** Active Firebase project */\n project: {\n id: string;\n hasFirebaseJson: boolean;\n };\n /** Per-service status */\n services: ServiceInfo[];\n /** Config placeholder warnings */\n configWarnings: string[];\n /** Timestamp of this check */\n timestamp: number;\n}\n\n// ─── Cache ───────────────────────────────────────────────────────────\n\nlet cachedStatus: FirebaseEnvironmentStatus | null = null;\nlet cacheTime = 0;\nconst CACHE_TTL = 30_000; // 30 seconds\n\n// ─── Main ────────────────────────────────────────────────────────────\n\nexport async function checkFirebaseStatus(\n projectDir: string,\n): Promise<FirebaseEnvironmentStatus> {\n const now = Date.now();\n if (cachedStatus && now - cacheTime < CACHE_TTL) {\n return cachedStatus;\n }\n\n const [fileStatus, cliStatus] = await Promise.all([\n checkFiles(projectDir),\n checkCli(projectDir),\n ]);\n\n const status: FirebaseEnvironmentStatus = {\n cli: cliStatus,\n project: fileStatus.project,\n services: fileStatus.services,\n configWarnings: fileStatus.configWarnings,\n timestamp: now,\n };\n\n cachedStatus = status;\n cacheTime = now;\n return status;\n}\n\n/** Clear the status cache (useful after config changes) */\nexport function clearFirebaseStatusCache(): void {\n cachedStatus = null;\n cacheTime = 0;\n}\n\n// ─── File Checks (sync, fast) ────────────────────────────────────────\n\ninterface FileCheckResult {\n project: { id: string; hasFirebaseJson: boolean };\n services: ServiceInfo[];\n configWarnings: string[];\n}\n\nfunction checkFiles(projectDir: string): FileCheckResult {\n const services: ServiceInfo[] = [];\n const configWarnings: string[] = [];\n\n // firebase.json\n const firebaseJsonPath = resolve(projectDir, \"firebase.json\");\n let hasFirebaseJson = false;\n let firebaseConfig: Record<string, unknown> = {};\n\n if (existsSync(firebaseJsonPath)) {\n hasFirebaseJson = true;\n try {\n firebaseConfig = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n } catch {\n // invalid JSON\n }\n }\n\n // Hosting\n if (firebaseConfig.hosting) {\n services.push({ name: \"Hosting\", status: \"configured\", detail: \"firebase.json\" });\n } else {\n services.push({ name: \"Hosting\", status: \"missing\" });\n }\n\n // Firestore\n const firestoreRulesPath = resolve(projectDir, \"firestore.rules\");\n if (firebaseConfig.firestore) {\n if (existsSync(firestoreRulesPath)) {\n services.push({ name: \"Firestore\", status: \"configured\", detail: \"Rules file found\" });\n } else {\n services.push({ name: \"Firestore\", status: \"placeholder\", detail: \"Configured but no rules file\" });\n }\n } else {\n services.push({ name: \"Firestore\", status: \"missing\" });\n }\n\n // Functions\n const functionsIndexPath = resolve(projectDir, \"functions/index.ts\");\n if (firebaseConfig.functions) {\n if (existsSync(functionsIndexPath)) {\n services.push({ name: \"Functions\", status: \"configured\", detail: \"functions/index.ts found\" });\n } else {\n services.push({ name: \"Functions\", status: \"placeholder\", detail: \"Configured but no entry file\" });\n }\n } else {\n services.push({ name: \"Functions\", status: \"missing\" });\n }\n\n // Storage\n const storageRulesPath = resolve(projectDir, \"storage.rules\");\n if (firebaseConfig.storage) {\n if (existsSync(storageRulesPath)) {\n services.push({ name: \"Storage\", status: \"configured\", detail: \"Rules file found\" });\n } else {\n services.push({ name: \"Storage\", status: \"placeholder\", detail: \"Configured but no rules file\" });\n }\n } else {\n services.push({ name: \"Storage\", status: \"missing\" });\n }\n\n // Firestore indexes\n const indexesPath = resolve(projectDir, \"firestore.indexes.json\");\n if (existsSync(indexesPath)) {\n services.push({ name: \"Indexes\", status: \"configured\", detail: \"firestore.indexes.json\" });\n } else if (firebaseConfig.firestore) {\n services.push({ name: \"Indexes\", status: \"placeholder\", detail: \"No indexes file\" });\n }\n\n // clawfire.config.ts — check for placeholder values\n const configPath = resolve(projectDir, \"clawfire.config.ts\");\n if (existsSync(configPath)) {\n try {\n const configContent = readFileSync(configPath, \"utf-8\");\n const placeholderMatches = configContent.match(/YOUR_[A-Z_]+/g);\n if (placeholderMatches) {\n for (const match of new Set(placeholderMatches)) {\n configWarnings.push(`Placeholder found: ${match}`);\n }\n }\n } catch {\n // ignore read errors\n }\n }\n\n // Project ID from .firebaserc\n let projectId = \"\";\n const firebasercPath = resolve(projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n projectId = rc?.projects?.default || \"\";\n } catch {\n // invalid JSON\n }\n }\n\n return {\n project: { id: projectId, hasFirebaseJson },\n services,\n configWarnings,\n };\n}\n\n// ─── CLI Checks (async, with timeouts) ───────────────────────────────\n\ninterface CliCheckResult {\n installed: boolean;\n version: string;\n authenticated: boolean;\n user: string;\n}\n\nasync function checkCli(projectDir: string): Promise<CliCheckResult> {\n const result: CliCheckResult = {\n installed: false,\n version: \"\",\n authenticated: false,\n user: \"\",\n };\n\n // Check firebase --version\n try {\n const version = await execWithTimeout(\"firebase\", [\"--version\"], projectDir, 5000);\n result.installed = true;\n result.version = version.trim();\n } catch {\n return result; // If CLI not installed, skip auth check\n }\n\n // Check firebase login status\n try {\n const loginOutput = await execWithTimeout(\n \"firebase\",\n [\"login:list\", \"--json\"],\n projectDir,\n 5000,\n );\n const loginData = JSON.parse(loginOutput);\n if (loginData?.result && Array.isArray(loginData.result) && loginData.result.length > 0) {\n result.authenticated = true;\n result.user = loginData.result[0]?.user?.email || loginData.result[0]?.email || \"\";\n }\n } catch {\n // Not authenticated or command failed\n }\n\n return result;\n}\n\n// ─── Firebase SDK Config (auto-fill) ─────────────────────────────────\n\nexport interface FirebaseSdkConfig {\n apiKey?: string;\n authDomain?: string;\n projectId?: string;\n storageBucket?: string;\n messagingSenderId?: string;\n appId?: string;\n}\n\n/**\n * Fetch Firebase web SDK config via CLI.\n * Requires: firebase CLI installed, logged in, active project set.\n *\n * If appId is provided (from saved state), uses it directly.\n * Otherwise:\n * 1. Try `firebase apps:sdkconfig web --json` (works if exactly 1 web app)\n * 2. If that fails, list all apps to find web app IDs\n * 3. If web apps found, retry with specific app ID\n * 4. If no web apps, return clear error\n */\nexport async function fetchFirebaseSdkConfig(\n projectDir: string,\n appId?: string,\n): Promise<FirebaseSdkConfig> {\n // If we have a saved app ID, use it directly\n if (appId) {\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", appId, \"--json\"],\n projectDir,\n 15000,\n );\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n throw new Error(`Failed to fetch config for web app ${appId}: ${msg}`);\n }\n throw new Error(\"Could not parse Firebase SDK config from CLI output\");\n }\n\n // Step 1: Try direct sdkconfig (works if project has exactly 1 web app)\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", \"--json\"],\n projectDir,\n 15000,\n );\n\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch {\n // Fall through to Step 2\n }\n\n // Step 2: List all apps to find web app IDs\n let webApps: Array<{ appId: string; displayName: string }> = [];\n try {\n const appsOutput = await execWithTimeout(\n \"firebase\",\n [\"apps:list\", \"--json\"],\n projectDir,\n 15000,\n );\n const appsData = JSON.parse(appsOutput);\n const allApps = appsData?.result || [];\n webApps = allApps\n .filter((a: Record<string, unknown>) => a.platform === \"WEB\")\n .map((a: Record<string, unknown>) => ({\n appId: String(a.appId || \"\"),\n displayName: String(a.displayName || \"\"),\n }))\n .filter((a: { appId: string }) => a.appId);\n } catch {\n // Can't list apps — give generic error\n throw new Error(\n \"Failed to fetch Firebase SDK config. \" +\n \"Make sure you are logged in and have an active project selected.\"\n );\n }\n\n // Step 3: No web apps found\n if (webApps.length === 0) {\n throw new Error(\n \"No web app registered in this Firebase project. \" +\n \"Go to Firebase Console > Project Settings > General > Your apps > Add app (Web) to create one, \" +\n \"then click Auto-fill again.\"\n );\n }\n\n // Step 4: Retry with specific app ID\n const firstAppId = webApps[0].appId;\n try {\n const output = await execWithTimeout(\n \"firebase\",\n [\"apps:sdkconfig\", \"web\", firstAppId, \"--json\"],\n projectDir,\n 15000,\n );\n const config = parseSdkConfigOutput(output);\n if (config) return config;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n throw new Error(`Failed to fetch config for web app ${firstAppId}: ${msg}`);\n }\n\n throw new Error(\"Could not parse Firebase SDK config from CLI output\");\n}\n\n/** Parse the output of `firebase apps:sdkconfig` */\nfunction parseSdkConfigOutput(output: string): FirebaseSdkConfig | null {\n try {\n const data = JSON.parse(output);\n\n // Firebase CLI returns { status: \"success\", result: { sdkConfig: { ... } } }\n if (data?.result?.sdkConfig) {\n return data.result.sdkConfig;\n }\n\n // Some CLI versions return fileContents with the config object as JS\n if (data?.result?.fileContents) {\n const contents = data.result.fileContents as string;\n const config: FirebaseSdkConfig = {};\n const extract = (key: string) => {\n const match = contents.match(new RegExp(`\"${key}\"\\\\s*:\\\\s*\"([^\"]+)\"`));\n return match ? match[1] : undefined;\n };\n config.apiKey = extract(\"apiKey\");\n config.authDomain = extract(\"authDomain\");\n config.projectId = extract(\"projectId\");\n config.storageBucket = extract(\"storageBucket\");\n config.messagingSenderId = extract(\"messagingSenderId\");\n config.appId = extract(\"appId\");\n if (config.apiKey || config.projectId) return config;\n }\n } catch {\n // JSON parse failed\n }\n return null;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction execWithTimeout(\n command: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = execFile(command, args, { cwd, timeout: timeoutMs }, (err, stdout, stderr) => {\n if (err) {\n // Include stderr in error message for better debugging\n const detail = stderr?.trim() || stdout?.trim() || \"\";\n const enriched = new Error(`${err.message}${detail ? \"\\n\" + detail : \"\"}`);\n reject(enriched);\n } else {\n resolve(stdout);\n }\n });\n // Extra safety: kill if timeout is reached\n const timer = setTimeout(() => {\n proc.kill(\"SIGTERM\");\n reject(new Error(\"Timeout\"));\n }, timeoutMs + 500);\n proc.on(\"exit\", () => clearTimeout(timer));\n });\n}\n","/**\n * Clawfire Dev Dashboard HTML Generator\n *\n * Generates the Dashboard tab content for the dev server.\n * Sections: Firebase Setup Wizard, Firebase Status, Config Overview (editable), Environment Variables.\n * All CSS/JS is inline — no external dependencies.\n * Data is lazy-loaded from /__dev/* endpoints.\n */\n\nexport interface DashboardHtmlOptions {\n apiPort: number;\n}\n\nexport function generateDashboardHtml(options: DashboardHtmlOptions): string {\n const { apiPort } = options;\n\n return `\n<div id=\"dashboard-content\" style=\"padding:24px;max-width:1200px;margin:0 auto;font-family:var(--font, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\">\n\n <!-- Loading State -->\n <div id=\"dash-loading\" style=\"text-align:center;padding:60px 0;color:#a3a3a3;\">\n <div style=\"font-size:24px;margin-bottom:8px;\">Loading dashboard...</div>\n </div>\n\n <!-- Dashboard Sections (hidden until loaded) -->\n <div id=\"dash-loaded\" style=\"display:none;\">\n\n <!-- Section 0: Firebase Setup Wizard -->\n <div id=\"setup-wizard\" style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Firebase Setup</h2>\n <div id=\"setup-card\" style=\"padding:20px;border-radius:12px;border:1px solid #2a2a2a;background:#141414;\">\n\n <!-- Step indicators -->\n <div id=\"setup-steps\" style=\"display:flex;gap:0;margin-bottom:20px;overflow:hidden;border-radius:8px;border:1px solid #2a2a2a;\">\n <div id=\"step-ind-1\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#1a1a2e;color:#f97316;border-right:1px solid #2a2a2a;\">\n 1. CLI\n </div>\n <div id=\"step-ind-2\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 2. Login\n </div>\n <div id=\"step-ind-3\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 3. Project\n </div>\n <div id=\"step-ind-4\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;border-right:1px solid #2a2a2a;\">\n 4. Web App\n </div>\n <div id=\"step-ind-5\" class=\"step-ind\" style=\"flex:1;padding:8px 12px;text-align:center;font-size:11px;font-weight:600;background:#0a0a0a;color:#666;\">\n 5. Auto-fill\n </div>\n </div>\n\n <!-- Step 1: CLI Install -->\n <div id=\"step-1\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step1-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase CLI</div>\n <div id=\"step1-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step1-action\" style=\"display:none;\">\n <button id=\"install-cli-btn\" onclick=\"installFirebaseCli()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Install Firebase CLI\n </button>\n <div id=\"install-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 2: Login -->\n <div id=\"step-2\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step2-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase Authentication</div>\n <div id=\"step2-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step2-action\" style=\"display:none;\">\n <button id=\"login-btn\" onclick=\"startFirebaseLogin(false)\" style=\"padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Login to Firebase\n </button>\n <button id=\"reauth-btn\" onclick=\"startFirebaseLogin(true)\" style=\"display:none;padding:8px 20px;background:#eab308;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Re-authenticate\n </button>\n </div>\n <!-- Login waiting message -->\n <div id=\"login-waiting\" style=\"display:none;margin-top:12px;padding:16px;border-radius:8px;background:#0a0a1a;border:1px solid #3b82f6;\">\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px;\">\n <span style=\"display:inline-block;width:14px;height:14px;border:2px solid #3b82f6;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;\"></span>\n <span style=\"color:#e5e5e5;font-size:13px;font-weight:600;\">Waiting for login...</span>\n </div>\n <div style=\"font-size:13px;color:#a3a3a3;\">A terminal window has been opened. Please complete the Firebase login there.</div>\n <div style=\"font-size:12px;color:#666;margin-top:6px;\">This page will update automatically when login is detected.</div>\n </div>\n <div id=\"login-result\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n\n <!-- Step 3: Select Project -->\n <div id=\"step-3\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step3-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Firebase Project</div>\n <div id=\"step3-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking...</div>\n </div>\n </div>\n <div id=\"step3-action\" style=\"display:none;\">\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <select id=\"project-select\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;font-family:monospace;min-width:280px;outline:none;\">\n <option value=\"\">Loading projects...</option>\n </select>\n <button id=\"select-project-btn\" onclick=\"selectFirebaseProject()\" style=\"padding:8px 20px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Use This Project\n </button>\n <button onclick=\"refreshProjectList()\" style=\"padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;\" title=\"Refresh\">\n Refresh\n </button>\n </div>\n <div id=\"project-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 4: Web App -->\n <div id=\"step-4\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step4-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Web App</div>\n <div id=\"step4-detail\" style=\"font-size:13px;color:#a3a3a3;\">Checking web apps...</div>\n </div>\n </div>\n <div id=\"step4-action\" style=\"display:none;\">\n <!-- Existing web apps list -->\n <div id=\"webapp-list\" style=\"display:none;\">\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <select id=\"webapp-select\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;font-family:monospace;min-width:280px;outline:none;\">\n <option value=\"\">Loading web apps...</option>\n </select>\n <button id=\"select-webapp-btn\" onclick=\"selectWebApp()\" style=\"padding:8px 20px;background:#22c55e;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Use This App\n </button>\n <button onclick=\"showCreateWebApp()\" style=\"padding:8px 12px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:13px;cursor:pointer;\">\n + Create New\n </button>\n </div>\n </div>\n <!-- Create new web app form -->\n <div id=\"webapp-create\" style=\"display:none;\">\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:8px;\">No web app found. Create one to get Firebase SDK config:</div>\n <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\n <input id=\"webapp-name\" type=\"text\" placeholder=\"My Web App\" style=\"padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:13px;min-width:220px;outline:none;\" />\n <button id=\"create-webapp-btn\" onclick=\"createWebApp()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Create Web App\n </button>\n </div>\n </div>\n <div id=\"webapp-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- Step 5: Done / Auto-fill -->\n <div id=\"step-5\" style=\"display:none;\">\n <div style=\"display:flex;align-items:center;gap:12px;margin-bottom:12px;\">\n <span id=\"step5-icon\" style=\"font-size:20px;\">⚪</span>\n <div>\n <div style=\"font-weight:600;color:#e5e5e5;font-size:15px;\">Auto-fill Config</div>\n <div id=\"step5-detail\" style=\"font-size:13px;color:#a3a3a3;\">Ready to auto-fill your clawfire.config.ts</div>\n </div>\n </div>\n <div id=\"step5-action\">\n <button id=\"autofill-wizard-btn\" onclick=\"autoFillConfig()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Auto-fill Config Now\n </button>\n <div id=\"autofill-wizard-status\" style=\"display:none;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;\"></div>\n </div>\n </div>\n\n <!-- All done banner -->\n <div id=\"setup-done\" style=\"display:none;padding:16px;border-radius:8px;background:#0a1a0a;border:1px solid #22c55e;text-align:center;\">\n <div style=\"font-size:16px;color:#22c55e;font-weight:700;margin-bottom:4px;\">Setup Complete</div>\n <div id=\"setup-done-detail\" style=\"font-size:13px;color:#a3a3a3;\"></div>\n <button onclick=\"startFirebaseLogin(true)\" style=\"margin-top:8px;padding:6px 14px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;font-size:12px;cursor:pointer;\">Re-authenticate</button>\n </div>\n </div>\n </div>\n\n <!-- Section 1: Firebase Status -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Firebase Status</h2>\n\n <!-- CLI Banner -->\n <div id=\"cli-banner\" style=\"padding:12px 16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;margin-bottom:16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;\">\n <span id=\"cli-dot\" style=\"width:10px;height:10px;border-radius:50%;background:#666;display:inline-block;flex-shrink:0;\"></span>\n <span id=\"cli-text\" style=\"color:#e5e5e5;font-size:14px;\">Checking CLI...</span>\n <span id=\"cli-project\" style=\"color:#a3a3a3;font-size:12px;margin-left:auto;font-family:monospace;\"></span>\n </div>\n\n <!-- Service Cards Grid -->\n <div id=\"service-grid\" style=\"display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;\"></div>\n </div>\n\n <!-- Section 2: Deploy -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Deploy</h2>\n <div style=\"display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;\">\n <div style=\"padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;\">\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px;\">\n <span style=\"font-size:16px;\">🌐</span>\n <span style=\"font-weight:600;color:#e5e5e5;\">Hosting</span>\n </div>\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:12px;\">Deploy your app to Firebase Hosting</div>\n <button id=\"deploy-hosting-btn\" onclick=\"deployHosting()\" style=\"padding:8px 20px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Deploy Hosting\n </button>\n <div id=\"deploy-hosting-status\" style=\"display:none;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;\"></div>\n <div id=\"deploy-hosting-url\" style=\"display:none;margin-top:8px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;\">\n <div style=\"font-size:11px;color:#a3a3a3;margin-bottom:4px;\">Live URL</div>\n <a id=\"deploy-hosting-link\" href=\"#\" target=\"_blank\" style=\"color:#22c55e;font-family:monospace;font-size:14px;text-decoration:none;word-break:break-all;\"></a>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Section 3: Database -->\n <div style=\"margin-bottom:32px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;margin-bottom:16px;\">Database</h2>\n <div id=\"db-card\" style=\"padding:20px;border-radius:12px;border:1px solid #2a2a2a;background:#141414;\">\n <!-- Mode Toggle -->\n <div style=\"display:flex;align-items:center;gap:16px;margin-bottom:16px;\">\n <span style=\"font-size:13px;color:#a3a3a3;font-weight:600;\">Mode:</span>\n <label id=\"db-mode-memory\" style=\"display:flex;align-items:center;gap:6px;cursor:pointer;\" onclick=\"switchDbMode('memory')\">\n <span id=\"db-radio-memory\" style=\"width:16px;height:16px;border-radius:50%;border:2px solid #f97316;display:inline-flex;align-items:center;justify-content:center;\">\n <span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>\n </span>\n <span style=\"color:#e5e5e5;font-size:13px;\">In-Memory</span>\n </label>\n <label id=\"db-mode-firestore\" style=\"display:flex;align-items:center;gap:6px;cursor:pointer;\" onclick=\"switchDbMode('firestore')\">\n <span id=\"db-radio-firestore\" style=\"width:16px;height:16px;border-radius:50%;border:2px solid #666;display:inline-flex;align-items:center;justify-content:center;\">\n </span>\n <span style=\"color:#a3a3a3;font-size:13px;\">Firestore</span>\n </label>\n </div>\n\n <!-- Connect to Firestore panel -->\n <div id=\"db-connect-panel\" style=\"padding:16px;border-radius:8px;background:#0a0a0a;border:1px solid #2a2a2a;\">\n <div style=\"font-weight:600;color:#e5e5e5;font-size:14px;margin-bottom:6px;\">Connect to Firestore</div>\n <div style=\"font-size:13px;color:#a3a3a3;margin-bottom:12px;\">Creates database, deploys open security rules, and enables Firestore mode.</div>\n <button id=\"db-connect-btn\" onclick=\"connectFirestore()\" style=\"padding:8px 20px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">\n Connect to Firestore\n </button>\n <div style=\"margin-top:10px;font-size:12px;color:#eab308;\">\n ⚠ Open rules (allow all) for dev only. Use /clawfire-model for production rules.\n </div>\n <div id=\"db-connect-status\" style=\"display:none;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;\"></div>\n </div>\n\n <!-- Firestore Connected banner -->\n <div id=\"db-connected-banner\" style=\"display:none;padding:16px;border-radius:8px;background:#0a1a0a;border:1px solid #22c55e;text-align:center;\">\n <div style=\"font-size:14px;color:#22c55e;font-weight:700;\">Firestore Connected</div>\n <div style=\"font-size:12px;color:#a3a3a3;margin-top:4px;\">Data persists across server restarts.</div>\n </div>\n </div>\n </div>\n\n <!-- Section 4: Config Overview -->\n <div style=\"margin-bottom:32px;\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;\">Config Overview</h2>\n <button id=\"autofill-btn\" onclick=\"autoFillConfig()\" style=\"padding:6px 14px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">Auto-fill from Firebase</button>\n </div>\n <div id=\"autofill-status\" style=\"display:none;padding:8px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;\"></div>\n <div id=\"config-section\" style=\"border-radius:8px;border:1px solid #2a2a2a;background:#141414;overflow:hidden;\">\n <table id=\"config-table\" style=\"width:100%;border-collapse:collapse;font-size:13px;\">\n <thead>\n <tr style=\"border-bottom:1px solid #2a2a2a;\">\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;width:180px;\">Key</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Value</th>\n <th style=\"padding:10px 16px;text-align:right;color:#a3a3a3;font-weight:500;width:80px;\">Action</th>\n </tr>\n </thead>\n <tbody id=\"config-tbody\"></tbody>\n </table>\n <div id=\"config-empty\" style=\"display:none;padding:32px;text-align:center;color:#666;\">\n No clawfire.config.ts found.\n </div>\n </div>\n </div>\n\n <!-- Section 4: Environment Variables -->\n <div style=\"margin-bottom:32px;\">\n <div style=\"display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;\">\n <h2 style=\"font-size:18px;font-weight:700;color:#f97316;\">Environment Variables</h2>\n <button id=\"env-add-btn\" onclick=\"showEnvModal()\" style=\"padding:6px 14px;background:#f97316;color:#000;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">+ Add Variable</button>\n </div>\n <div id=\"env-table-wrap\" style=\"border-radius:8px;border:1px solid #2a2a2a;background:#141414;overflow:hidden;\">\n <table id=\"env-table\" style=\"width:100%;border-collapse:collapse;font-size:13px;\">\n <thead>\n <tr style=\"border-bottom:1px solid #2a2a2a;\">\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Key</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Value</th>\n <th style=\"padding:10px 16px;text-align:left;color:#a3a3a3;font-weight:500;\">Description</th>\n <th style=\"padding:10px 16px;text-align:right;color:#a3a3a3;font-weight:500;width:120px;\">Actions</th>\n </tr>\n </thead>\n <tbody id=\"env-tbody\"></tbody>\n </table>\n <div id=\"env-empty\" style=\"display:none;padding:32px;text-align:center;color:#666;\">\n No environment variables found. Click \"+ Add Variable\" to create one.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Env Modal -->\n <div id=\"env-modal\" style=\"display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;align-items:center;justify-content:center;\">\n <div style=\"background:#1e1e1e;border:1px solid #2a2a2a;border-radius:12px;padding:24px;width:440px;max-width:90vw;\">\n <h3 id=\"modal-title\" style=\"font-size:16px;font-weight:700;color:#e5e5e5;margin-bottom:16px;\">Add Variable</h3>\n <div style=\"margin-bottom:12px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Key</label>\n <input id=\"modal-key\" type=\"text\" placeholder=\"API_KEY\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-family:monospace;font-size:14px;outline:none;\" />\n </div>\n <div style=\"margin-bottom:12px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Value</label>\n <input id=\"modal-value\" type=\"text\" placeholder=\"your-value-here\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-family:monospace;font-size:14px;outline:none;\" />\n </div>\n <div style=\"margin-bottom:20px;\">\n <label style=\"display:block;font-size:12px;color:#a3a3a3;margin-bottom:4px;\">Description (optional)</label>\n <input id=\"modal-desc\" type=\"text\" placeholder=\"What this variable is for\" style=\"width:100%;padding:8px 12px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:6px;color:#e5e5e5;font-size:14px;outline:none;\" />\n </div>\n <div id=\"modal-error\" style=\"display:none;padding:8px 12px;background:#1c0808;border:1px solid #ef4444;border-radius:6px;color:#ef4444;font-size:12px;margin-bottom:12px;\"></div>\n <div style=\"display:flex;gap:8px;justify-content:flex-end;\">\n <button onclick=\"hideEnvModal()\" style=\"padding:8px 16px;background:transparent;border:1px solid #2a2a2a;border-radius:6px;color:#a3a3a3;cursor:pointer;font-size:13px;\">Cancel</button>\n <button id=\"modal-save-btn\" onclick=\"saveEnvVar()\" style=\"padding:8px 16px;background:#f97316;border:none;border-radius:6px;color:#000;font-weight:600;cursor:pointer;font-size:13px;\">Save</button>\n </div>\n </div>\n </div>\n</div>\n\n<style>\n@keyframes spin { to { transform: rotate(360deg); } }\n#login-spinner { animation: spin 1s linear infinite; }\n</style>\n\n<script>\n(function() {\n var API = 'http://localhost:${apiPort}';\n var envData = [];\n var configData = [];\n var editingKey = null;\n var loginPollTimer = null;\n\n // ─── Load Dashboard Data ─────────────────────────────────────────\n window._loadDashboard = function() {\n if (window._dashboardLoaded) return;\n window._dashboardLoaded = true;\n Promise.all([\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/config').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/env').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/setup/status').then(function(r) { return r.json(); }),\n fetch(API + '/__dev/db-mode').then(function(r) { return r.json(); }),\n ]).then(function(results) {\n renderFirebaseStatus(results[0]);\n renderConfig(results[1]);\n renderEnvVars(results[2]);\n renderSetupWizard(results[3]);\n renderDbMode(results[4].mode || 'memory');\n document.getElementById('dash-loading').style.display = 'none';\n document.getElementById('dash-loaded').style.display = 'block';\n }).catch(function(err) {\n document.getElementById('dash-loading').innerHTML =\n '<div style=\"color:#ef4444;\">Failed to load dashboard data</div>' +\n '<div style=\"color:#666;font-size:13px;margin-top:8px;\">' + err.message + '</div>';\n });\n };\n\n // ─── Setup Wizard ─────────────────────────────────────────────────\n\n function setStepIndicator(activeStep) {\n for (var i = 1; i <= 5; i++) {\n var el = document.getElementById('step-ind-' + i);\n if (i < activeStep) {\n el.style.background = '#0a1a0a';\n el.style.color = '#22c55e';\n } else if (i === activeStep) {\n el.style.background = '#1a1a2e';\n el.style.color = '#f97316';\n } else {\n el.style.background = '#0a0a0a';\n el.style.color = '#666';\n }\n }\n }\n\n function renderSetupWizard(status) {\n var GREEN = '\\\\u2705';\n var YELLOW = '\\\\u26A0\\\\uFE0F';\n var RED = '\\\\u274C';\n var CIRCLE = '\\\\u26AA';\n\n // Hide all steps first\n for (var i = 1; i <= 5; i++) {\n document.getElementById('step-' + i).style.display = 'none';\n }\n document.getElementById('setup-done').style.display = 'none';\n // Reset login UI state from previous interactions\n document.getElementById('login-waiting').style.display = 'none';\n document.getElementById('login-result').style.display = 'none';\n\n if (status.nextStep === 'done') {\n // All done!\n setStepIndicator(6);\n document.getElementById('setup-done').style.display = 'block';\n var detail = 'Logged in as ' + status.auth.user + ' | Project: ' + status.project.id;\n if (status.webApp && status.webApp.displayName) {\n detail += ' | App: ' + status.webApp.displayName;\n }\n document.getElementById('setup-done-detail').textContent = detail;\n return;\n }\n\n // Step 1: CLI\n var step1 = document.getElementById('step-1');\n step1.style.display = 'block';\n if (status.cli.installed) {\n document.getElementById('step1-icon').textContent = GREEN;\n document.getElementById('step1-detail').textContent = 'Firebase CLI v' + status.cli.version + ' installed';\n document.getElementById('step1-action').style.display = 'none';\n } else {\n document.getElementById('step1-icon').textContent = RED;\n document.getElementById('step1-detail').textContent = 'Firebase CLI is not installed';\n document.getElementById('step1-action').style.display = 'block';\n }\n\n if (status.nextStep === 'install-cli') {\n setStepIndicator(1);\n return;\n }\n\n // Step 2: Auth\n var step2 = document.getElementById('step-2');\n step2.style.display = 'block';\n if (status.auth.authenticated) {\n document.getElementById('step2-icon').textContent = GREEN;\n document.getElementById('step2-detail').textContent = 'Logged in as ' + status.auth.user;\n document.getElementById('step2-action').style.display = 'block';\n document.getElementById('login-btn').style.display = 'none';\n document.getElementById('reauth-btn').style.display = 'inline-block';\n document.getElementById('login-waiting').style.display = 'none';\n } else {\n document.getElementById('step2-icon').textContent = RED;\n document.getElementById('step2-detail').textContent = 'Not logged in to Firebase';\n document.getElementById('step2-action').style.display = 'block';\n document.getElementById('login-btn').style.display = 'inline-block';\n document.getElementById('reauth-btn').style.display = 'none';\n document.getElementById('login-waiting').style.display = 'none';\n }\n\n if (status.nextStep === 'login') {\n setStepIndicator(2);\n return;\n }\n\n // Step 3: Project Selection\n var step3 = document.getElementById('step-3');\n step3.style.display = 'block';\n if (status.project.id) {\n document.getElementById('step3-icon').textContent = GREEN;\n document.getElementById('step3-detail').textContent = 'Active project: ' + status.project.id;\n document.getElementById('step3-action').style.display = 'block';\n } else {\n document.getElementById('step3-icon').textContent = YELLOW;\n document.getElementById('step3-detail').textContent = 'No project selected';\n document.getElementById('step3-action').style.display = 'block';\n }\n\n // Load project list\n loadProjectList(status.project.id);\n\n if (status.nextStep === 'select-project') {\n setStepIndicator(3);\n return;\n }\n\n // Step 4: Web App\n var step4 = document.getElementById('step-4');\n step4.style.display = 'block';\n if (status.webApp && status.webApp.appId) {\n document.getElementById('step4-icon').textContent = GREEN;\n document.getElementById('step4-detail').textContent = 'Web app: ' + (status.webApp.displayName || status.webApp.appId);\n document.getElementById('step4-action').style.display = 'block';\n // Show list with current selection\n loadWebAppList(status.webApp.appId);\n } else {\n document.getElementById('step4-icon').textContent = YELLOW;\n document.getElementById('step4-detail').textContent = 'No web app selected';\n document.getElementById('step4-action').style.display = 'block';\n loadWebAppList('');\n }\n\n if (status.nextStep === 'create-web-app') {\n setStepIndicator(4);\n return;\n }\n\n // Step 5: Auto-fill\n var step5 = document.getElementById('step-5');\n step5.style.display = 'block';\n document.getElementById('step5-icon').textContent = CIRCLE;\n setStepIndicator(5);\n }\n\n // ─── Step 1: Install CLI ──────────────────────────────────────────\n window.installFirebaseCli = function() {\n var btn = document.getElementById('install-cli-btn');\n var status = document.getElementById('install-status');\n btn.disabled = true;\n btn.textContent = 'Installing...';\n status.style.display = 'block';\n status.textContent = 'Running npm install -g firebase-tools... This may take a minute.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/install-cli', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Refresh wizard\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Install Firebase CLI';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Install Firebase CLI';\n });\n };\n\n // ─── Step 2: Login (opens terminal window) ──────────────────────\n window.startFirebaseLogin = function(reauth) {\n var loginBtn = document.getElementById('login-btn');\n var reauthBtn = document.getElementById('reauth-btn');\n var waiting = document.getElementById('login-waiting');\n var result = document.getElementById('login-result');\n\n loginBtn.style.display = 'none';\n reauthBtn.style.display = 'none';\n waiting.style.display = 'block';\n result.style.display = 'none';\n\n fetch(API + '/__dev/setup/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ reauth: !!reauth })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (!data.success) {\n waiting.style.display = 'none';\n result.textContent = data.message;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n loginBtn.style.display = 'inline-block';\n return;\n }\n // Poll setup status until login is detected\n startLoginPoll();\n })\n .catch(function(err) {\n waiting.style.display = 'none';\n result.textContent = 'Failed: ' + err.message;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n loginBtn.style.display = 'inline-block';\n });\n };\n\n function startLoginPoll() {\n if (loginPollTimer) clearInterval(loginPollTimer);\n loginPollTimer = setInterval(function() {\n fetch(API + '/__dev/setup/status')\n .then(function(r) { return r.json(); })\n .then(function(status) {\n if (status.auth.authenticated) {\n clearInterval(loginPollTimer);\n loginPollTimer = null;\n document.getElementById('login-waiting').style.display = 'none';\n var result = document.getElementById('login-result');\n result.textContent = 'Login successful! Logged in as ' + status.auth.user;\n result.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Wait for token to fully settle, then refresh wizard + force-load projects\n setTimeout(function() {\n refreshSetupStatus();\n // Extra delay for project list — Firebase CLI needs time after fresh login\n setTimeout(function() { loadProjectList(''); }, 2500);\n }, 2000);\n }\n })\n .catch(function() {});\n }, 3000);\n }\n\n // ─── Step 3: Project Selection ────────────────────────────────────\n function loadProjectList(currentProjectId, retryCount) {\n retryCount = retryCount || 0;\n var select = document.getElementById('project-select');\n select.innerHTML = '<option value=\"\">Loading projects...</option>';\n select.disabled = true;\n\n fetch(API + '/__dev/setup/projects')\n .then(function(r) { return r.json(); })\n .then(function(data) {\n select.innerHTML = '';\n if (data.error) {\n // Auto-retry up to 2 times on error (token might not be ready yet after fresh login)\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">Error: ' + escHtml(data.error) + '</option>';\n return;\n }\n if (!data.projects || data.projects.length === 0) {\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">No projects found</option>';\n return;\n }\n\n select.innerHTML = '<option value=\"\">-- Select a project --</option>';\n data.projects.forEach(function(p) {\n var opt = document.createElement('option');\n opt.value = p.projectId;\n opt.textContent = p.displayName + ' (' + p.projectId + ')';\n if (p.projectId === currentProjectId) opt.selected = true;\n select.appendChild(opt);\n });\n select.disabled = false;\n })\n .catch(function(err) {\n if (retryCount < 2) {\n select.innerHTML = '<option value=\"\">Loading projects... (retry)</option>';\n setTimeout(function() { loadProjectList(currentProjectId, retryCount + 1); }, 3000);\n return;\n }\n select.innerHTML = '<option value=\"\">Failed to load</option>';\n });\n }\n\n window.refreshProjectList = function() {\n loadProjectList('');\n };\n\n window.selectFirebaseProject = function() {\n var select = document.getElementById('project-select');\n var btn = document.getElementById('select-project-btn');\n var status = document.getElementById('project-status');\n var projectId = select.value;\n\n if (!projectId) {\n status.textContent = 'Please select a project first.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n\n btn.disabled = true;\n btn.textContent = 'Setting up...';\n status.textContent = 'Selecting project, detecting web app, auto-filling config...';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1c;border:1px solid #3b82f6;color:#93c5fd;';\n\n fetch(API + '/__dev/setup/select-project', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ projectId: projectId })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n var msg = data.steps ? data.steps.join('\\\\n') : data.message;\n status.textContent = msg;\n status.style.whiteSpace = 'pre-line';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;white-space:pre-line;';\n btn.textContent = 'Done';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This Project';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This Project';\n });\n };\n\n // ─── Step 4: Web App ──────────────────────────────────────────────\n function loadWebAppList(currentAppId, retryCount) {\n retryCount = retryCount || 0;\n var listDiv = document.getElementById('webapp-list');\n var createDiv = document.getElementById('webapp-create');\n var select = document.getElementById('webapp-select');\n\n fetch(API + '/__dev/setup/web-apps')\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) {\n if (retryCount < 2) {\n setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);\n return;\n }\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n document.getElementById('webapp-create').querySelector('div').textContent = 'Error loading apps: ' + data.error;\n return;\n }\n if (!data.apps || data.apps.length === 0) {\n // No web apps — show create form\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n return;\n }\n\n // Show list\n listDiv.style.display = 'block';\n createDiv.style.display = 'none';\n select.innerHTML = '<option value=\"\">-- Select a web app --</option>';\n data.apps.forEach(function(app) {\n var opt = document.createElement('option');\n opt.value = app.appId;\n opt.setAttribute('data-name', app.displayName || '');\n opt.textContent = (app.displayName || 'Unnamed') + ' (' + app.appId.slice(-12) + ')';\n if (app.appId === currentAppId) opt.selected = true;\n select.appendChild(opt);\n });\n select.disabled = false;\n })\n .catch(function() {\n if (retryCount < 2) {\n setTimeout(function() { loadWebAppList(currentAppId, retryCount + 1); }, 3000);\n return;\n }\n createDiv.style.display = 'block';\n listDiv.style.display = 'none';\n });\n }\n\n window.showCreateWebApp = function() {\n document.getElementById('webapp-list').style.display = 'none';\n document.getElementById('webapp-create').style.display = 'block';\n document.getElementById('webapp-name').focus();\n };\n\n window.selectWebApp = function() {\n var select = document.getElementById('webapp-select');\n var status = document.getElementById('webapp-status');\n var appId = select.value;\n if (!appId) {\n status.textContent = 'Please select a web app first.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n var selectedOption = select.options[select.selectedIndex];\n var displayName = selectedOption.getAttribute('data-name') || '';\n\n var btn = document.getElementById('select-webapp-btn');\n btn.disabled = true;\n btn.textContent = 'Saving...';\n\n fetch(API + '/__dev/setup/select-web-app', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ appId: appId, displayName: displayName })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This App';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Use This App';\n });\n };\n\n window.createWebApp = function() {\n var nameInput = document.getElementById('webapp-name');\n var btn = document.getElementById('create-webapp-btn');\n var status = document.getElementById('webapp-status');\n var displayName = nameInput.value.trim();\n\n if (!displayName) {\n status.textContent = 'Please enter a name for the web app.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1a1a0a;border:1px solid #eab308;color:#eab308;';\n return;\n }\n\n btn.disabled = true;\n btn.textContent = 'Creating...';\n status.textContent = 'Creating web app... This may take a moment.';\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/create-web-app', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ displayName: displayName })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n setTimeout(refreshSetupStatus, 1000);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Create Web App';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Create Web App';\n });\n };\n\n // ─── Refresh Setup Status ─────────────────────────────────────────\n function refreshSetupStatus() {\n fetch(API + '/__dev/setup/status')\n .then(function(r) { return r.json(); })\n .then(function(status) { renderSetupWizard(status); })\n .catch(function() {});\n\n // Also refresh firebase status section\n fetch(API + '/__dev/firebase-status')\n .then(function(r) { return r.json(); })\n .then(function(data) { renderFirebaseStatus(data); })\n .catch(function() {});\n }\n\n // ─── Firebase Status ─────────────────────────────────────────────\n function renderFirebaseStatus(data) {\n var dot = document.getElementById('cli-dot');\n var text = document.getElementById('cli-text');\n var proj = document.getElementById('cli-project');\n\n if (!data.cli.installed) {\n dot.style.background = '#ef4444';\n text.textContent = 'Firebase CLI not installed';\n } else if (!data.cli.authenticated) {\n dot.style.background = '#eab308';\n text.textContent = 'Firebase CLI v' + data.cli.version + ' \\\\u2014 Not logged in';\n } else {\n dot.style.background = '#22c55e';\n text.textContent = 'Firebase CLI v' + data.cli.version + ' \\\\u2014 ' + data.cli.user;\n }\n\n if (data.project.id) {\n proj.textContent = 'Project: ' + data.project.id;\n } else {\n proj.textContent = 'No active project';\n proj.style.color = '#eab308';\n }\n\n // Service Cards\n var grid = document.getElementById('service-grid');\n grid.innerHTML = '';\n var statusColors = { configured: '#22c55e', placeholder: '#eab308', missing: '#666' };\n var statusLabels = { configured: 'Ready', placeholder: 'Needs Setup', missing: 'Not Configured' };\n\n var enableableServices = { 'Hosting': 'hosting', 'Firestore': 'firestore', 'Storage': 'storage' };\n data.services.forEach(function(svc) {\n var card = document.createElement('div');\n card.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #2a2a2a;background:#141414;';\n var enableBtn = '';\n if (svc.status === 'missing' && enableableServices[svc.name]) {\n enableBtn = '<button onclick=\"enableService(\\\\'' + enableableServices[svc.name] + '\\\\')\" style=\"margin-top:8px;padding:4px 12px;background:#f97316;color:#000;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;\">Enable</button>';\n }\n card.innerHTML =\n '<div style=\"display:flex;align-items:center;gap:8px;margin-bottom:6px;\">' +\n '<span style=\"width:8px;height:8px;border-radius:50%;background:' + statusColors[svc.status] + ';display:inline-block;\"></span>' +\n '<span style=\"font-weight:600;color:#e5e5e5;\">' + svc.name + '</span>' +\n '</div>' +\n '<div style=\"font-size:12px;color:' + statusColors[svc.status] + ';\">' + statusLabels[svc.status] + '</div>' +\n (svc.detail ? '<div style=\"font-size:11px;color:#666;margin-top:4px;\">' + svc.detail + '</div>' : '') +\n enableBtn;\n grid.appendChild(card);\n });\n\n if (data.configWarnings && data.configWarnings.length > 0) {\n var warningCard = document.createElement('div');\n warningCard.style.cssText = 'padding:16px;border-radius:8px;border:1px solid #eab308;background:#1a1a0a;grid-column:1/-1;';\n warningCard.innerHTML =\n '<div style=\"font-weight:600;color:#eab308;margin-bottom:6px;\">Config Warnings</div>' +\n data.configWarnings.map(function(w) {\n return '<div style=\"font-size:12px;color:#eab308;font-family:monospace;\">' + escHtml(w) + '</div>';\n }).join('');\n grid.appendChild(warningCard);\n }\n }\n\n // ─── Config Overview (editable) ──────────────────────────────────\n function renderConfig(data) {\n var tbody = document.getElementById('config-tbody');\n var empty = document.getElementById('config-empty');\n var table = document.getElementById('config-table');\n\n configData = (data && data.fields) ? data.fields : [];\n\n if (configData.length === 0) {\n table.style.display = 'none';\n empty.style.display = 'block';\n return;\n }\n\n table.style.display = 'table';\n empty.style.display = 'none';\n tbody.innerHTML = '';\n\n configData.forEach(function(field) {\n var tr = document.createElement('tr');\n tr.style.borderBottom = '1px solid #2a2a2a';\n tr.id = 'cfg-row-' + field.key;\n var color = field.isPlaceholder ? '#eab308' : '#a3a3a3';\n var badge = field.isPlaceholder ? ' <span style=\"background:#eab30822;color:#eab308;padding:1px 6px;border-radius:4px;font-size:10px;\">PLACEHOLDER</span>' : '';\n tr.innerHTML =\n '<td style=\"padding:10px 16px;color:#e5e5e5;font-family:monospace;white-space:nowrap;\">' + escHtml(field.key) + '</td>' +\n '<td style=\"padding:10px 16px;font-family:monospace;\">' +\n '<span id=\"cfg-val-' + field.key + '\" style=\"color:' + color + ';\">' + escHtml(field.value) + '</span>' +\n badge +\n '<input id=\"cfg-input-' + field.key + '\" type=\"text\" value=\"' + escHtml(field.value) + '\" style=\"display:none;width:100%;padding:4px 8px;background:#0a0a0a;border:1px solid #2a2a2a;border-radius:4px;color:#e5e5e5;font-family:monospace;font-size:13px;outline:none;\" />' +\n '</td>' +\n '<td style=\"padding:10px 16px;text-align:right;white-space:nowrap;\">' +\n '<button id=\"cfg-edit-' + field.key + '\" onclick=\"editConfigField(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"background:none;border:none;color:#3b82f6;cursor:pointer;font-size:12px;padding:4px 8px;\">Edit</button>' +\n '<button id=\"cfg-save-' + field.key + '\" onclick=\"saveConfigField(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"display:none;background:none;border:none;color:#22c55e;cursor:pointer;font-size:12px;padding:4px 8px;\">Save</button>' +\n '<button id=\"cfg-cancel-' + field.key + '\" onclick=\"cancelConfigEdit(\\\\'' + escHtml(field.key) + '\\\\')\" style=\"display:none;background:none;border:none;color:#a3a3a3;cursor:pointer;font-size:12px;padding:4px 8px;\">Cancel</button>' +\n '</td>';\n tbody.appendChild(tr);\n });\n }\n\n window.editConfigField = function(key) {\n var valSpan = document.getElementById('cfg-val-' + key);\n var input = document.getElementById('cfg-input-' + key);\n var editBtn = document.getElementById('cfg-edit-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n var cancelBtn = document.getElementById('cfg-cancel-' + key);\n if (!valSpan || !input) return;\n var badges = valSpan.parentNode.querySelectorAll('span[style*=\"PLACEHOLDER\"]');\n for (var i = 0; i < badges.length; i++) badges[i].style.display = 'none';\n valSpan.style.display = 'none';\n input.style.display = 'block';\n input.focus();\n input.select();\n editBtn.style.display = 'none';\n saveBtn.style.display = 'inline';\n cancelBtn.style.display = 'inline';\n };\n\n window.cancelConfigEdit = function(key) {\n var valSpan = document.getElementById('cfg-val-' + key);\n var input = document.getElementById('cfg-input-' + key);\n var editBtn = document.getElementById('cfg-edit-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n var cancelBtn = document.getElementById('cfg-cancel-' + key);\n if (!valSpan || !input) return;\n var badges = valSpan.parentNode.querySelectorAll('span[style*=\"border-radius\"]');\n for (var i = 0; i < badges.length; i++) badges[i].style.display = '';\n valSpan.style.display = '';\n input.style.display = 'none';\n input.value = valSpan.textContent;\n editBtn.style.display = 'inline';\n saveBtn.style.display = 'none';\n cancelBtn.style.display = 'none';\n };\n\n window.saveConfigField = function(key) {\n var input = document.getElementById('cfg-input-' + key);\n var saveBtn = document.getElementById('cfg-save-' + key);\n if (!input) return;\n var value = input.value;\n saveBtn.textContent = '...';\n fetch(API + '/__dev/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set', key: key, value: value })\n }).then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) { alert('Error: ' + data.error); saveBtn.textContent = 'Save'; return; }\n return fetch(API + '/__dev/config').then(function(r) { return r.json(); });\n })\n .then(function(data) { if (data) renderConfig(data); })\n .catch(function(err) { alert('Failed: ' + err.message); saveBtn.textContent = 'Save'; });\n };\n\n // ─── Auto-fill from Firebase ─────────────────────────────────────\n window.autoFillConfig = function() {\n var btn = document.getElementById('autofill-btn');\n var wizBtn = document.getElementById('autofill-wizard-btn');\n var status = document.getElementById('autofill-status');\n var wizStatus = document.getElementById('autofill-wizard-status');\n if (btn) { btn.disabled = true; btn.textContent = 'Fetching...'; }\n if (wizBtn) { wizBtn.disabled = true; wizBtn.textContent = 'Fetching...'; }\n if (status) status.style.display = 'none';\n if (wizStatus) wizStatus.style.display = 'none';\n\n fetch(API + '/__dev/firebase-sdk-config')\n .then(function(r) {\n if (!r.ok) {\n return r.json().catch(function() { return { error: 'Server error (' + r.status + ')' }; });\n }\n return r.json();\n })\n .then(function(data) {\n if (data.error) {\n showAutoFillResult(false, data.error);\n return;\n }\n\n var fields = {};\n if (data.apiKey) fields.apiKey = data.apiKey;\n if (data.authDomain) fields.authDomain = data.authDomain;\n if (data.projectId) fields.projectId = data.projectId;\n if (data.storageBucket) fields.storageBucket = data.storageBucket;\n if (data.appId) fields.appId = data.appId;\n\n var count = Object.keys(fields).length;\n if (count === 0) {\n showAutoFillResult(false, 'No web app config found. Create a Web app in Firebase Console first.');\n return;\n }\n\n return fetch(API + '/__dev/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set-multiple', fields: fields })\n }).then(function(r) { return r.json(); })\n .then(function(result) {\n if (result.error) {\n showAutoFillResult(false, 'Error saving: ' + result.error);\n } else {\n showAutoFillResult(true, count + ' fields updated from Firebase project!');\n return fetch(API + '/__dev/config').then(function(r) { return r.json(); })\n .then(function(cfg) { renderConfig(cfg); });\n }\n });\n })\n .catch(function(err) {\n showAutoFillResult(false, 'Failed: ' + err.message);\n })\n .finally(function() {\n if (btn) { btn.disabled = false; btn.textContent = 'Auto-fill from Firebase'; }\n if (wizBtn) { wizBtn.disabled = false; wizBtn.textContent = 'Auto-fill Config Now'; }\n });\n };\n\n function showAutoFillResult(success, message) {\n var targets = ['autofill-status', 'autofill-wizard-status'];\n targets.forEach(function(id) {\n var el = document.getElementById(id);\n if (!el) return;\n el.textContent = message;\n if (success) {\n el.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n } else {\n el.style.cssText = 'display:block;margin-top:8px;font-size:13px;padding:8px 12px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n });\n }\n\n // ─── Deploy Hosting ──────────────────────────────────────────────\n window.deployHosting = function() {\n var btn = document.getElementById('deploy-hosting-btn');\n var status = document.getElementById('deploy-hosting-status');\n var urlBox = document.getElementById('deploy-hosting-url');\n btn.disabled = true;\n btn.textContent = 'Deploying...';\n status.textContent = 'Deploying to Firebase Hosting... This may take up to 2 minutes.';\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n urlBox.style.display = 'none';\n\n fetch(API + '/__dev/deploy/hosting', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n if (data.url) {\n var link = document.getElementById('deploy-hosting-link');\n link.href = data.url;\n link.textContent = data.url;\n urlBox.style.display = 'block';\n }\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n btn.disabled = false;\n btn.textContent = 'Deploy Hosting';\n // Refresh firebase status\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Deploy Hosting';\n });\n };\n\n // ─── Enable Service ────────────────────────────────────────────────\n window.enableService = function(service) {\n fetch(API + '/__dev/enable-service', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service: service })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n // Refresh firebase status to show updated service cards\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n } else {\n alert('Failed to enable ' + service + ': ' + data.message);\n }\n })\n .catch(function(err) {\n alert('Error: ' + err.message);\n });\n };\n\n // ─── Database Mode ──────────────────────────────────────────────\n function loadDbMode() {\n fetch(API + '/__dev/db-mode')\n .then(function(r) { return r.json(); })\n .then(function(data) { renderDbMode(data.mode || 'memory'); })\n .catch(function() { renderDbMode('memory'); });\n }\n\n function renderDbMode(mode) {\n var radioMem = document.getElementById('db-radio-memory');\n var radioFs = document.getElementById('db-radio-firestore');\n var connectPanel = document.getElementById('db-connect-panel');\n var connectedBanner = document.getElementById('db-connected-banner');\n var memLabel = document.getElementById('db-mode-memory');\n var fsLabel = document.getElementById('db-mode-firestore');\n\n if (mode === 'firestore') {\n radioMem.innerHTML = '';\n radioMem.style.borderColor = '#666';\n radioFs.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>';\n radioFs.style.borderColor = '#f97316';\n memLabel.querySelector('span:last-child').style.color = '#a3a3a3';\n fsLabel.querySelector('span:last-child').style.color = '#e5e5e5';\n connectPanel.style.display = 'none';\n connectedBanner.style.display = 'block';\n } else {\n radioMem.innerHTML = '<span style=\"width:8px;height:8px;border-radius:50%;background:#f97316;\"></span>';\n radioMem.style.borderColor = '#f97316';\n radioFs.innerHTML = '';\n radioFs.style.borderColor = '#666';\n memLabel.querySelector('span:last-child').style.color = '#e5e5e5';\n fsLabel.querySelector('span:last-child').style.color = '#a3a3a3';\n connectPanel.style.display = 'block';\n connectedBanner.style.display = 'none';\n }\n }\n\n window.switchDbMode = function(mode) {\n var status = document.getElementById('db-connect-status');\n status.style.display = 'none';\n\n fetch(API + '/__dev/db-mode', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ mode: mode })\n })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n renderDbMode(data.mode);\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n });\n };\n\n window.connectFirestore = function() {\n var btn = document.getElementById('db-connect-btn');\n var status = document.getElementById('db-connect-status');\n btn.disabled = true;\n btn.textContent = 'Connecting...';\n status.textContent = 'Creating Firestore database... This may take a minute.';\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a0a1a;border:1px solid #3b82f6;color:#3b82f6;';\n\n fetch(API + '/__dev/setup/create-firestore', { method: 'POST' })\n .then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.success) {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#0a1a0a;border:1px solid #22c55e;color:#22c55e;';\n // Switch UI to firestore mode\n setTimeout(function() { renderDbMode('firestore'); }, 1500);\n // Refresh firebase status\n fetch(API + '/__dev/firebase-status').then(function(r) { return r.json(); }).then(function(d) { renderFirebaseStatus(d); }).catch(function(){});\n } else {\n status.textContent = data.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n }\n btn.disabled = false;\n btn.textContent = 'Connect to Firestore';\n })\n .catch(function(err) {\n status.textContent = 'Error: ' + err.message;\n status.style.cssText = 'display:block;margin-top:10px;font-size:13px;padding:10px 14px;border-radius:6px;background:#1c0808;border:1px solid #ef4444;color:#ef4444;';\n btn.disabled = false;\n btn.textContent = 'Connect to Firestore';\n });\n };\n\n // ─── Environment Variables ───────────────────────────────────────\n function renderEnvVars(data) {\n envData = data.variables || [];\n var tbody = document.getElementById('env-tbody');\n var empty = document.getElementById('env-empty');\n var table = document.getElementById('env-table');\n\n if (envData.length === 0) {\n table.style.display = 'none';\n empty.style.display = 'block';\n return;\n }\n\n table.style.display = 'table';\n empty.style.display = 'none';\n tbody.innerHTML = '';\n\n envData.forEach(function(v) {\n var tr = document.createElement('tr');\n tr.style.borderBottom = '1px solid #2a2a2a';\n var maskedVal = v.value ? v.value.slice(0, 3) + '...' + v.value.slice(-2) : '';\n if (v.value.length <= 5) maskedVal = '***';\n var placeholderBadge = v.isPlaceholder ? ' <span style=\"background:#eab30822;color:#eab308;padding:1px 6px;border-radius:4px;font-size:10px;\">PLACEHOLDER</span>' : '';\n tr.innerHTML =\n '<td style=\"padding:10px 16px;color:#e5e5e5;font-family:monospace;white-space:nowrap;\">' + escHtml(v.key) + placeholderBadge + '</td>' +\n '<td style=\"padding:10px 16px;color:#a3a3a3;font-family:monospace;\">' +\n '<span class=\"env-val\" data-key=\"' + escAttr(v.key) + '\" data-masked=\"' + escAttr(maskedVal) + '\" data-full=\"' + escAttr(v.value) + '\">' + escHtml(maskedVal) + '</span>' +\n ' <button onclick=\"toggleReveal(this)\" style=\"background:none;border:none;color:#666;cursor:pointer;font-size:11px;padding:2px 4px;\">reveal</button>' +\n '</td>' +\n '<td style=\"padding:10px 16px;color:#666;font-size:12px;\">' + escHtml(v.description || '') + '</td>' +\n '<td style=\"padding:10px 16px;text-align:right;white-space:nowrap;\">' +\n '<button onclick=\"editEnvVar(\\\\'' + escAttr(v.key) + '\\\\')\" style=\"background:none;border:none;color:#3b82f6;cursor:pointer;font-size:12px;padding:4px 8px;\">Edit</button>' +\n '<button onclick=\"deleteEnvVar(\\\\'' + escAttr(v.key) + '\\\\')\" style=\"background:none;border:none;color:#ef4444;cursor:pointer;font-size:12px;padding:4px 8px;\">Delete</button>' +\n '</td>';\n tbody.appendChild(tr);\n });\n }\n\n // ─── Env Modal ───────────────────────────────────────────────────\n window.showEnvModal = function(key) {\n editingKey = key || null;\n var modal = document.getElementById('env-modal');\n var title = document.getElementById('modal-title');\n var keyInput = document.getElementById('modal-key');\n var valInput = document.getElementById('modal-value');\n var descInput = document.getElementById('modal-desc');\n var errorEl = document.getElementById('modal-error');\n errorEl.style.display = 'none';\n\n if (editingKey) {\n title.textContent = 'Edit Variable';\n keyInput.value = editingKey;\n keyInput.readOnly = true;\n keyInput.style.opacity = '0.5';\n var existing = envData.find(function(v) { return v.key === editingKey; });\n valInput.value = existing ? existing.value : '';\n descInput.value = existing ? existing.description : '';\n } else {\n title.textContent = 'Add Variable';\n keyInput.value = '';\n keyInput.readOnly = false;\n keyInput.style.opacity = '1';\n valInput.value = '';\n descInput.value = '';\n }\n\n modal.style.display = 'flex';\n (editingKey ? valInput : keyInput).focus();\n };\n\n window.hideEnvModal = function() {\n document.getElementById('env-modal').style.display = 'none';\n editingKey = null;\n };\n\n window.editEnvVar = function(key) {\n showEnvModal(key);\n };\n\n window.saveEnvVar = function() {\n var key = document.getElementById('modal-key').value.trim();\n var value = document.getElementById('modal-value').value;\n var desc = document.getElementById('modal-desc').value.trim();\n var errorEl = document.getElementById('modal-error');\n\n if (!key) {\n errorEl.textContent = 'Key is required';\n errorEl.style.display = 'block';\n return;\n }\n if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) {\n errorEl.textContent = 'Invalid key format. Use UPPER_SNAKE_CASE.';\n errorEl.style.display = 'block';\n return;\n }\n\n fetch(API + '/__dev/env', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'set', key: key, value: value, description: desc || undefined })\n }).then(function(r) { return r.json(); })\n .then(function(data) {\n if (data.error) {\n errorEl.textContent = data.error;\n errorEl.style.display = 'block';\n return;\n }\n hideEnvModal();\n return fetch(API + '/__dev/env').then(function(r) { return r.json(); });\n })\n .then(function(data) { if (data) renderEnvVars(data); })\n .catch(function(err) {\n errorEl.textContent = err.message;\n errorEl.style.display = 'block';\n });\n };\n\n window.deleteEnvVar = function(key) {\n if (!confirm('Delete ' + key + '?')) return;\n fetch(API + '/__dev/env', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'delete', key: key })\n }).then(function() {\n return fetch(API + '/__dev/env').then(function(r) { return r.json(); });\n }).then(function(data) { renderEnvVars(data); })\n .catch(function() {});\n };\n\n window.toggleReveal = function(btn) {\n var span = btn.previousElementSibling;\n var masked = span.getAttribute('data-masked');\n var full = span.getAttribute('data-full');\n if (span.textContent === masked) {\n span.textContent = full;\n btn.textContent = 'hide';\n } else {\n span.textContent = masked;\n btn.textContent = 'reveal';\n }\n };\n\n // ─── Helpers ─────────────────────────────────────────────────────\n function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"'); }\n function escAttr(s) { return String(s).replace(/&/g,'&').replace(/'/g,\"\\\\\\\\'\").replace(/\"/g,'"').replace(/</g,'<'); }\n})();\n</script>`;\n}\n","/**\n * Clawfire Firebase Setup Orchestration\n *\n * Manages the full Firebase setup flow:\n * 1. Check/install Firebase CLI\n * 2. Open a real terminal for `firebase login` (TTY required)\n * 3. List available projects\n * 4. Select project → auto-fill config\n *\n * All operations are dev-only (localhost).\n */\nimport { execFile, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport { tmpdir, platform, homedir } from \"node:os\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface SetupStatus {\n /** Firebase CLI */\n cli: {\n installed: boolean;\n version: string;\n };\n /** Auth state */\n auth: {\n authenticated: boolean;\n user: string;\n };\n /** Active project */\n project: {\n id: string;\n hasFirebaserc: boolean;\n };\n /** Web App */\n webApp: {\n appId: string;\n displayName: string;\n };\n /** Overall readiness */\n ready: boolean;\n /** Current step the user should do next */\n nextStep: \"install-cli\" | \"login\" | \"select-project\" | \"create-web-app\" | \"done\";\n}\n\nexport interface FirebaseProject {\n projectId: string;\n displayName: string;\n projectNumber: string;\n state: string;\n}\n\nexport interface WebApp {\n appId: string;\n displayName: string;\n}\n\n/** Persisted setup state — saved to .clawfire-setup.json */\nexport interface SetupState {\n projectId?: string;\n webAppId?: string;\n webAppDisplayName?: string;\n dbMode?: \"memory\" | \"firestore\";\n lastUpdated?: string;\n}\n\n// ─── Firebase Setup Manager ──────────────────────────────────────────\n\nexport class FirebaseSetup {\n private projectDir: string;\n private stateFilePath: string;\n\n constructor(projectDir: string) {\n this.projectDir = projectDir;\n this.stateFilePath = resolve(projectDir, \".clawfire-setup.json\");\n }\n\n // ─── State Persistence ────────────────────────────────────────────\n\n loadState(): SetupState {\n try {\n if (existsSync(this.stateFilePath)) {\n return JSON.parse(readFileSync(this.stateFilePath, \"utf-8\"));\n }\n } catch {\n // corrupt file — start fresh\n }\n return {};\n }\n\n saveState(partial: Partial<SetupState>): void {\n const current = this.loadState();\n const merged = { ...current, ...partial, lastUpdated: new Date().toISOString() };\n writeFileSync(this.stateFilePath, JSON.stringify(merged, null, 2) + \"\\n\", \"utf-8\");\n }\n\n // ─── Status Check ──────────────────────────────────────────────────\n\n async getStatus(): Promise<SetupStatus> {\n const savedState = this.loadState();\n\n const status: SetupStatus = {\n cli: { installed: false, version: \"\" },\n auth: { authenticated: false, user: \"\" },\n project: { id: \"\", hasFirebaserc: false },\n webApp: { appId: savedState.webAppId || \"\", displayName: savedState.webAppDisplayName || \"\" },\n ready: false,\n nextStep: \"install-cli\",\n };\n\n // 1. Check CLI\n try {\n const version = await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n status.cli.installed = true;\n status.cli.version = version.trim();\n } catch {\n status.nextStep = \"install-cli\";\n return status;\n }\n\n // 2. Check auth — use login:list and also verify token works\n try {\n const loginOutput = await this.execTimeout(\n \"firebase\",\n [\"login:list\", \"--json\"],\n 5000,\n );\n const loginData = JSON.parse(loginOutput);\n if (loginData?.result && Array.isArray(loginData.result) && loginData.result.length > 0) {\n status.auth.authenticated = true;\n status.auth.user = loginData.result[0]?.user?.email || loginData.result[0]?.email || \"\";\n }\n } catch {\n // Not authenticated\n }\n\n if (!status.auth.authenticated) {\n status.nextStep = \"login\";\n return status;\n }\n\n // 3. Check active project\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n status.project.hasFirebaserc = true;\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n status.project.id = rc?.projects?.default || \"\";\n } catch {\n // invalid JSON\n }\n }\n\n if (!status.project.id) {\n status.nextStep = \"select-project\";\n return status;\n }\n\n // 4. Check web app (from saved state)\n if (!status.webApp.appId) {\n status.nextStep = \"create-web-app\";\n return status;\n }\n\n status.ready = true;\n status.nextStep = \"done\";\n return status;\n }\n\n // ─── CLI Install ───────────────────────────────────────────────────\n\n async installCli(): Promise<{ success: boolean; message: string }> {\n try {\n // Check if already installed\n try {\n await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n return { success: true, message: \"Firebase CLI is already installed.\" };\n } catch {\n // Not installed, proceed\n }\n\n // Install via npm\n await this.execTimeout(\n \"npm\",\n [\"install\", \"-g\", \"firebase-tools\"],\n 120000, // 2 min timeout for install\n );\n\n // Verify installation\n try {\n const version = await this.execTimeout(\"firebase\", [\"--version\"], 5000);\n return { success: true, message: `Firebase CLI v${version.trim()} installed successfully.` };\n } catch {\n return { success: false, message: \"Installation completed but CLI not found in PATH. Try restarting your terminal.\" };\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to install Firebase CLI: ${msg}` };\n }\n }\n\n // ─── Login via Terminal ───────────────────────────────────────────\n\n /**\n * Opens a new terminal window and runs `firebase login`.\n *\n * Firebase CLI requires a real TTY for interactive login.\n * Instead of trying to fake a TTY, we open an actual terminal.\n * The dashboard polls getStatus() to detect when login completes.\n *\n * macOS: Creates a .command file and opens it (Terminal.app)\n * Linux: Tries common terminal emulators\n * Windows: Opens a new cmd window\n */\n openLoginTerminal(reauth: boolean = false): { success: boolean; message: string } {\n const cmd = reauth ? \"firebase login --reauth\" : \"firebase login\";\n const os = platform();\n\n try {\n if (os === \"darwin\") {\n // macOS: .command files auto-open in Terminal.app — no special permissions needed\n // After login completes, osascript closes the terminal window matching our filename\n const scriptPath = join(tmpdir(), \"clawfire-firebase-login.command\");\n writeFileSync(scriptPath, [\n \"#!/bin/bash\",\n `cd \"${this.projectDir}\"`,\n cmd,\n 'echo \"\"',\n 'echo \"Login complete! Closing in 3 seconds...\"',\n \"sleep 3\",\n // Spawn osascript in background to close this specific terminal window, then exit\n '(sleep 1 && osascript -e \\'tell application \"Terminal\" to close (every window whose name contains \"clawfire-firebase-login\")\\' 2>/dev/null) &',\n \"exit 0\",\n ].join(\"\\n\"), { mode: 0o755 });\n\n const child = spawn(\"open\", [scriptPath], { detached: true, stdio: \"ignore\" });\n child.unref();\n } else if (os === \"win32\") {\n // Windows: open a new cmd window that auto-closes after login\n const child = spawn(\"cmd\", [\"/c\", \"start\", \"cmd\", \"/c\", `${cmd} && timeout /t 3 >nul`], {\n cwd: this.projectDir,\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n } else {\n // Linux: create script, try common terminal emulators\n const scriptPath = join(tmpdir(), \"clawfire-firebase-login.sh\");\n writeFileSync(scriptPath, [\n \"#!/bin/bash\",\n `cd \"${this.projectDir}\"`,\n cmd,\n 'echo \"\"',\n 'echo \"Login complete! Closing in 3 seconds...\"',\n \"sleep 3\",\n \"exit 0\",\n ].join(\"\\n\"), { mode: 0o755 });\n\n const terminals = [\n { cmd: \"x-terminal-emulator\", args: [\"-e\", scriptPath] },\n { cmd: \"gnome-terminal\", args: [\"--\", \"bash\", scriptPath] },\n { cmd: \"konsole\", args: [\"-e\", \"bash\", scriptPath] },\n { cmd: \"xfce4-terminal\", args: [\"-e\", scriptPath] },\n { cmd: \"xterm\", args: [\"-e\", scriptPath] },\n ];\n\n let opened = false;\n for (const t of terminals) {\n try {\n const child = spawn(t.cmd, t.args, { detached: true, stdio: \"ignore\" });\n child.unref();\n // If spawn didn't throw, it at least started\n child.on(\"error\", () => {});\n opened = true;\n break;\n } catch {\n continue;\n }\n }\n\n if (!opened) {\n return {\n success: false,\n message: `Could not find a terminal emulator. Please run \"${cmd}\" manually in your terminal.`,\n };\n }\n }\n\n return {\n success: true,\n message: \"Terminal window opened. Please complete the login in the new terminal.\",\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return {\n success: false,\n message: `Failed to open terminal: ${msg}. Please run \"${cmd}\" manually in your terminal.`,\n };\n }\n }\n\n // ─── Project Listing ───────────────────────────────────────────────\n\n async listProjects(): Promise<{ projects: FirebaseProject[]; error?: string }> {\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"projects:list\", \"--json\"],\n 30000, // 30s — can be slow on first call\n );\n\n const data = JSON.parse(output);\n\n if (data?.result && Array.isArray(data.result)) {\n const projects: FirebaseProject[] = data.result.map((p: Record<string, unknown>) => ({\n projectId: p.projectId || \"\",\n displayName: p.displayName || p.projectId || \"\",\n projectNumber: p.projectNumber || \"\",\n state: p.lifecycleState || p.state || \"ACTIVE\",\n }));\n return { projects };\n }\n\n return { projects: [], error: \"Unexpected response format\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n // Common cases\n if (msg.includes(\"authenticate\") || msg.includes(\"login\") || msg.includes(\"credential\")) {\n return { projects: [], error: \"Session expired. Please re-authenticate first.\" };\n }\n if (msg.includes(\"Timeout\")) {\n return { projects: [], error: \"Request timed out. Please try again.\" };\n }\n return { projects: [], error: msg };\n }\n }\n\n // ─── Project Selection ─────────────────────────────────────────────\n\n async selectProject(projectId: string): Promise<{ success: boolean; message: string }> {\n if (!projectId || !/^[a-z0-9-]+$/.test(projectId)) {\n return { success: false, message: \"Invalid project ID format.\" };\n }\n\n // Clear web app if project changed (different project may have different web apps)\n const savedState = this.loadState();\n if (savedState.projectId && savedState.projectId !== projectId) {\n this.saveState({ projectId, webAppId: undefined, webAppDisplayName: undefined });\n } else {\n this.saveState({ projectId });\n }\n\n try {\n // Set project via firebase use\n await this.execTimeout(\n \"firebase\",\n [\"use\", projectId],\n 10000,\n );\n\n // Also update .firebaserc directly for reliability\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n let rc: Record<string, unknown> = {};\n if (existsSync(firebasercPath)) {\n try {\n rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n } catch {\n rc = {};\n }\n }\n if (!rc.projects || typeof rc.projects !== \"object\") {\n rc.projects = {};\n }\n (rc.projects as Record<string, string>).default = projectId;\n writeFileSync(firebasercPath, JSON.stringify(rc, null, 2) + \"\\n\", \"utf-8\");\n\n return { success: true, message: `Active project set to \"${projectId}\".` };\n } catch {\n // If firebase use fails, still try writing .firebaserc directly\n try {\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n const rc = {\n projects: { default: projectId },\n };\n writeFileSync(firebasercPath, JSON.stringify(rc, null, 2) + \"\\n\", \"utf-8\");\n return { success: true, message: `Active project set to \"${projectId}\" (via .firebaserc).` };\n } catch (writeErr) {\n const msg = writeErr instanceof Error ? writeErr.message : \"Unknown error\";\n return { success: false, message: `Failed to set project: ${msg}` };\n }\n }\n }\n\n // ─── Web App Management ───────────────────────────────────────────\n\n async listWebApps(): Promise<{ apps: WebApp[]; error?: string }> {\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"apps:list\", \"--json\"],\n 15000,\n );\n const data = JSON.parse(output);\n const allApps = data?.result || [];\n const webApps: WebApp[] = allApps\n .filter((a: Record<string, unknown>) => a.platform === \"WEB\")\n .map((a: Record<string, unknown>) => ({\n appId: String(a.appId || \"\"),\n displayName: String(a.displayName || \"\"),\n }))\n .filter((a: { appId: string }) => a.appId);\n return { apps: webApps };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { apps: [], error: msg };\n }\n }\n\n async createWebApp(displayName: string): Promise<{ success: boolean; appId?: string; message: string }> {\n if (!displayName || displayName.length < 1) {\n return { success: false, message: \"Display name is required.\" };\n }\n\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"apps:create\", \"web\", displayName, \"--json\"],\n 30000,\n );\n const data = JSON.parse(output);\n const appId = data?.result?.appId || \"\";\n if (appId) {\n // Save to persistent state\n this.saveState({ webAppId: appId, webAppDisplayName: displayName });\n return { success: true, appId, message: `Web app \"${displayName}\" created successfully.` };\n }\n return { success: false, message: \"App created but could not retrieve app ID.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to create web app: ${msg}` };\n }\n }\n\n selectWebApp(appId: string, displayName: string): { success: boolean; message: string } {\n this.saveState({ webAppId: appId, webAppDisplayName: displayName });\n return { success: true, message: `Web app \"${displayName}\" selected.` };\n }\n\n // ─── Deploy ─────────────────────────────────────────────────────────\n\n async deployHosting(targets = \"hosting\"): Promise<{ success: boolean; url?: string; message: string }> {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n if (!existsSync(firebaseJsonPath)) {\n return { success: false, message: \"firebase.json not found. Enable hosting first.\" };\n }\n\n try {\n const config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n if (!config.hosting) {\n return { success: false, message: \"Hosting not configured in firebase.json. Click Enable first.\" };\n }\n } catch {\n return { success: false, message: \"Invalid firebase.json.\" };\n }\n\n const timeoutMs = targets.includes(\"functions\") ? 300000 : 120000;\n\n try {\n const output = await this.execTimeout(\n \"firebase\",\n [\"deploy\", \"--only\", targets, \"--json\"],\n timeoutMs,\n );\n\n // Parse URL from deploy output\n let url = \"\";\n try {\n const data = JSON.parse(output);\n if (data?.result) {\n for (const key of Object.keys(data.result)) {\n if (key.startsWith(\"hosting\") && typeof data.result[key] === \"object\") {\n url = data.result[key]?.url || data.result[key]?.site?.url || \"\";\n if (url) break;\n }\n }\n }\n } catch {\n // Try regex fallback\n const urlMatch = output.match(/https:\\/\\/[a-z0-9-]+\\.web\\.app/i);\n if (urlMatch) url = urlMatch[0];\n }\n\n // Construct URL from project ID as last resort\n if (!url) {\n const state = this.loadState();\n if (state.projectId) {\n url = `https://${state.projectId}.web.app`;\n }\n }\n\n const label = targets.includes(\"functions\") ? \"Hosting + Functions\" : \"Hosting\";\n return { success: true, url: url || undefined, message: `${label} deployed successfully!` };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Deploy failed: ${msg}` };\n }\n }\n\n // ─── Full Deploy Pipeline ────────────────────────────────────────────\n\n /**\n * Sync environment variables to functions/.env for production.\n * Copies project root .env vars + injects CLAWFIRE_FIREBASE_CONFIG.\n */\n syncEnvToFunctions(firebaseConfig?: Record<string, string>): { success: boolean; message: string; count: number } {\n const functionsDir = resolve(this.projectDir, \"functions\");\n if (!existsSync(functionsDir)) {\n return { success: false, message: \"functions/ directory not found.\", count: 0 };\n }\n\n const lines: string[] = [];\n\n // 1. Copy project root .env vars (strip 'export ' prefix, skip build-only vars)\n const rootEnvPath = resolve(this.projectDir, \".env\");\n if (existsSync(rootEnvPath)) {\n const content = readFileSync(rootEnvPath, \"utf-8\");\n for (const rawLine of content.split(\"\\n\")) {\n let trimmed = rawLine.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n // Strip bash 'export ' prefix\n if (trimmed.startsWith(\"export \")) {\n trimmed = trimmed.slice(7);\n }\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n // Skip build-only / dev-only vars\n if (key === \"NPM_TOKEN\" || key === \"NODE_ENV\") continue;\n lines.push(trimmed);\n }\n }\n\n // 2. Inject CLAWFIRE_FIREBASE_CONFIG from clawfire.config.ts\n if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {\n // Remove existing entry if present\n const filtered = lines.filter((l) => !l.startsWith(\"CLAWFIRE_FIREBASE_CONFIG=\"));\n filtered.push(`CLAWFIRE_FIREBASE_CONFIG=${JSON.stringify(firebaseConfig)}`);\n lines.length = 0;\n lines.push(...filtered);\n }\n\n const functionsEnvPath = resolve(functionsDir, \".env\");\n writeFileSync(functionsEnvPath, lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n return { success: true, message: `Synced ${lines.length} env vars to functions/.env`, count: lines.length };\n }\n\n /**\n * Ensure firebase.json is fully configured for deployment:\n * - functions config exists\n * - hosting has cleanUrls\n * - hosting has API rewrite (/api/** → api function) before catch-all\n *\n * Fixes existing configs created by older versions.\n */\n ensureDeployConfig(): { success: boolean; message: string; changes: string[] } {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n if (!existsSync(firebaseJsonPath)) {\n return { success: false, message: \"firebase.json not found.\", changes: [] };\n }\n\n try {\n const config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n const changes: string[] = [];\n\n // 1. Ensure functions config\n if (!config.functions) {\n config.functions = {\n source: \"functions\",\n runtime: \"nodejs20\",\n codebase: \"clawfire\",\n };\n changes.push(\"Added functions config\");\n }\n\n // 2. Ensure hosting has cleanUrls\n if (config.hosting && !config.hosting.cleanUrls) {\n config.hosting.cleanUrls = true;\n changes.push(\"Added cleanUrls\");\n }\n\n // 3. Ensure hosting has API rewrite before the catch-all\n if (config.hosting) {\n if (!config.hosting.rewrites) {\n config.hosting.rewrites = [];\n }\n const rewrites = config.hosting.rewrites as Array<{ source: string; function?: string; destination?: string }>;\n const hasApiRewrite = rewrites.some(\n (r) => r.source === \"/api/**\" && r.function === \"api\",\n );\n if (!hasApiRewrite) {\n const apiRewrite = { source: \"/api/**\", function: \"api\" };\n // Insert before the catch-all \"**\" rewrite\n const catchAllIndex = rewrites.findIndex((r) => r.source === \"**\");\n if (catchAllIndex >= 0) {\n rewrites.splice(catchAllIndex, 0, apiRewrite);\n } else {\n // No catch-all — add API rewrite + catch-all\n rewrites.push(apiRewrite);\n rewrites.push({ source: \"**\", destination: \"/index.html\" });\n }\n changes.push(\"Added API rewrite (/api/** → Cloud Function)\");\n }\n }\n\n if (changes.length > 0) {\n writeFileSync(firebaseJsonPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n }\n\n return {\n success: true,\n message: changes.length > 0 ? changes.join(\", \") : \"Deploy config up to date.\",\n changes,\n };\n } catch {\n return { success: false, message: \"Invalid firebase.json.\", changes: [] };\n }\n }\n\n /**\n * Install dependencies and build functions for deployment.\n */\n async buildFunctions(): Promise<{ success: boolean; message: string }> {\n const functionsDir = resolve(this.projectDir, \"functions\");\n if (!existsSync(functionsDir)) {\n return { success: false, message: \"functions/ directory not found.\" };\n }\n\n const pkgPath = resolve(functionsDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return { success: false, message: \"functions/package.json not found.\" };\n }\n\n // 1. Install/update dependencies (always run to ensure latest versions)\n try {\n await this.execTimeout(\"npm\", [\"install\"], 120000, functionsDir);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `npm install in functions/ failed: ${msg}` };\n }\n\n // 2. Build: try 'npm run build', fallback to direct tsc\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.scripts?.build) {\n await this.execTimeout(\"npm\", [\"run\", \"build\"], 60000, functionsDir);\n } else {\n // Fallback: run tsc directly from functions/\n const tsconfigPath = resolve(functionsDir, \"tsconfig.json\");\n if (existsSync(tsconfigPath)) {\n await this.execTimeout(\"npx\", [\"tsc\"], 60000, functionsDir);\n }\n }\n return { success: true, message: \"Functions built successfully.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Functions build failed: ${msg}` };\n }\n }\n\n // ─── Service Enable ────────────────────────────────────────────────\n\n enableService(service: \"hosting\" | \"firestore\" | \"storage\"): { success: boolean; message: string } {\n const firebaseJsonPath = resolve(this.projectDir, \"firebase.json\");\n let config: Record<string, unknown> = {};\n\n if (existsSync(firebaseJsonPath)) {\n try {\n config = JSON.parse(readFileSync(firebaseJsonPath, \"utf-8\"));\n } catch {\n config = {};\n }\n }\n\n switch (service) {\n case \"hosting\": {\n if (!config.hosting) {\n config.hosting = {\n public: \"public\",\n ignore: [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n cleanUrls: true,\n rewrites: [\n { source: \"/api/**\", function: \"api\" },\n { source: \"**\", destination: \"/index.html\" },\n ],\n };\n }\n break;\n }\n case \"firestore\": {\n if (!config.firestore) {\n config.firestore = {\n rules: \"firestore.rules\",\n indexes: \"firestore.indexes.json\",\n };\n }\n const rulesPath = resolve(this.projectDir, \"firestore.rules\");\n if (!existsSync(rulesPath)) {\n writeFileSync(\n rulesPath,\n \"rules_version = '2';\\nservice cloud.firestore {\\n match /databases/{database}/documents {\\n match /{document=**} {\\n allow read, write: if request.auth != null;\\n }\\n }\\n}\\n\",\n );\n }\n const indexesPath = resolve(this.projectDir, \"firestore.indexes.json\");\n if (!existsSync(indexesPath)) {\n writeFileSync(indexesPath, JSON.stringify({ indexes: [], fieldOverrides: [] }, null, 2) + \"\\n\");\n }\n break;\n }\n case \"storage\": {\n if (!config.storage) {\n config.storage = { rules: \"storage.rules\" };\n }\n const storageRulesPath = resolve(this.projectDir, \"storage.rules\");\n if (!existsSync(storageRulesPath)) {\n writeFileSync(\n storageRulesPath,\n \"rules_version = '2';\\nservice firebase.storage {\\n match /b/{bucket}/o {\\n match /{allPaths=**} {\\n allow read, write: if request.auth != null;\\n }\\n }\\n}\\n\",\n );\n }\n break;\n }\n }\n\n writeFileSync(firebaseJsonPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n return { success: true, message: `${service} enabled in firebase.json.` };\n }\n\n // ─── Firestore Database Automation ──────────────────────────────────\n\n /**\n * Read Firebase CLI's stored OAuth token and exchange for access token.\n * No gcloud CLI needed — uses the token from `firebase login`.\n */\n private async getFirebaseAccessToken(): Promise<string | null> {\n const home = homedir();\n const configPath = join(home, \".config\", \"configstore\", \"firebase-tools.json\");\n\n if (!existsSync(configPath)) return null;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n let refreshToken: string | null = null;\n\n // Multi-account format (Firebase CLI v11+)\n if (config?.activeAccounts) {\n const activeEmail = config?.user?.email;\n if (activeEmail && config.activeAccounts[activeEmail]?.tokens?.refresh_token) {\n refreshToken = config.activeAccounts[activeEmail].tokens.refresh_token;\n }\n if (!refreshToken) {\n for (const account of Object.values(config.activeAccounts) as any[]) {\n if (account?.tokens?.refresh_token) {\n refreshToken = account.tokens.refresh_token;\n break;\n }\n }\n }\n }\n\n // Legacy single-account format\n if (!refreshToken && config?.tokens?.refresh_token) {\n refreshToken = config.tokens.refresh_token;\n }\n\n if (!refreshToken) return null;\n\n // Exchange refresh token for access token using Firebase CLI's public OAuth client\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: \"563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com\",\n client_secret: \"j9iVZfS8kkCEFUPaAeJV0sAi\",\n });\n\n const res = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (res.ok) {\n const data = (await res.json()) as { access_token?: string };\n return data.access_token || null;\n }\n } catch {\n // Token read/exchange failed — will fallback to other methods\n }\n\n return null;\n }\n\n /**\n * Enable Firestore API using Google Service Usage REST API.\n * Uses Firebase CLI's stored auth token — no gcloud CLI needed.\n * Falls back to gcloud CLI, then manual URL.\n */\n private async enableFirestoreApi(): Promise<{ success: boolean; message: string }> {\n const state = this.loadState();\n let projectId = state.projectId || \"\";\n if (!projectId) {\n const firebasercPath = resolve(this.projectDir, \".firebaserc\");\n if (existsSync(firebasercPath)) {\n try {\n const rc = JSON.parse(readFileSync(firebasercPath, \"utf-8\"));\n projectId = rc?.projects?.default || \"\";\n } catch {}\n }\n }\n\n if (!projectId) {\n return { success: false, message: \"No project ID found. Select a project first.\" };\n }\n\n // Method 1: REST API with Firebase CLI's stored OAuth token (no gcloud needed)\n try {\n const accessToken = await this.getFirebaseAccessToken();\n if (accessToken) {\n const res = await fetch(\n `https://serviceusage.googleapis.com/v1/projects/${projectId}/services/firestore.googleapis.com:enable`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n if (res.ok) {\n return { success: true, message: \"Firestore API enabled via REST API.\" };\n }\n // 409 = already enabled, also fine\n if (res.status === 409) {\n return { success: true, message: \"Firestore API already enabled.\" };\n }\n }\n } catch {\n // REST API call failed — try fallback methods\n }\n\n // Method 2: Fallback to gcloud CLI\n try {\n await this.execTimeout(\n \"gcloud\",\n [\"services\", \"enable\", \"firestore.googleapis.com\", \"--project\", projectId],\n 60000,\n );\n return { success: true, message: \"Firestore API enabled via gcloud.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n if (!(msg.includes(\"ENOENT\") || msg.includes(\"not found\") || msg.includes(\"command not found\"))) {\n return { success: false, message: `Failed to enable Firestore API: ${msg}` };\n }\n }\n\n // Method 3: Manual fallback\n return {\n success: false,\n message: `Failed to enable Firestore API automatically. Please enable it manually:\\nhttps://console.developers.google.com/apis/api/firestore.googleapis.com/overview?project=${projectId}`,\n };\n }\n\n /**\n * Create Firestore database via CLI.\n * Automatically enables Firestore API if needed.\n * Handles \"ALREADY_EXISTS\" gracefully — returns success.\n */\n async createFirestoreDatabase(location = \"nam5\"): Promise<{ success: boolean; message: string }> {\n const dbArgs = [\"firestore:databases:create\", \"(default)\", \"--location\", location, \"--json\"];\n\n // Helper: attempt database creation, return categorized result\n const tryCreate = async (): Promise<{ ok: boolean; alreadyExists: boolean; needsApi: boolean; msg: string }> => {\n try {\n await this.execTimeout(\"firebase\", dbArgs, 60000);\n return { ok: true, alreadyExists: false, needsApi: false, msg: \"\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n if (msg.includes(\"ALREADY_EXISTS\") || msg.includes(\"already exists\")) {\n return { ok: true, alreadyExists: true, needsApi: false, msg };\n }\n if (msg.includes(\"403\") || msg.includes(\"has not been used\") || msg.includes(\"is disabled\")) {\n return { ok: false, alreadyExists: false, needsApi: true, msg };\n }\n return { ok: false, alreadyExists: false, needsApi: false, msg };\n }\n };\n\n // First attempt\n const first = await tryCreate();\n if (first.ok) {\n return { success: true, message: first.alreadyExists ? \"Firestore database already exists.\" : `Firestore database created (location: ${location}).` };\n }\n if (!first.needsApi) {\n return { success: false, message: `Failed to create Firestore database: ${first.msg}` };\n }\n\n // API not enabled — enable it\n const enableResult = await this.enableFirestoreApi();\n if (!enableResult.success) {\n return enableResult;\n }\n\n // Retry with increasing waits: 5s, 5s, 10s, 10s, 15s (total 45s)\n const waits = [5000, 5000, 10000, 10000, 15000];\n for (let i = 0; i < waits.length; i++) {\n await new Promise((r) => setTimeout(r, waits[i]));\n const retry = await tryCreate();\n if (retry.ok) {\n return { success: true, message: `Firestore API enabled and database created (location: ${location}).` };\n }\n if (!retry.needsApi) {\n // Different error (not API-related) — stop retrying\n return { success: false, message: `Failed to create Firestore database: ${retry.msg}` };\n }\n }\n\n return {\n success: false,\n message: \"Firestore API was enabled but database creation timed out waiting for propagation. Please wait a minute and try again.\",\n };\n }\n\n /**\n * Deploy open Firestore security rules for dev testing.\n * Writes `allow read, write: if true` rules and deploys them.\n */\n async deployFirestoreRules(openForDev = true): Promise<{ success: boolean; message: string }> {\n const rulesPath = resolve(this.projectDir, \"firestore.rules\");\n\n try {\n // Write open rules for dev\n if (openForDev) {\n writeFileSync(\n rulesPath,\n \"rules_version = '2';\\nservice cloud.firestore {\\n match /databases/{database}/documents {\\n match /{document=**} {\\n allow read, write: if true;\\n }\\n }\\n}\\n\",\n );\n }\n\n // Deploy rules\n await this.execTimeout(\n \"firebase\",\n [\"deploy\", \"--only\", \"firestore:rules\", \"--json\"],\n 60000,\n );\n return { success: true, message: \"Firestore rules deployed.\" };\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown error\";\n return { success: false, message: `Failed to deploy Firestore rules: ${msg}` };\n }\n }\n\n // ─── Helpers ───────────────────────────────────────────────────────\n\n private execTimeout(command: string, args: string[], timeoutMs: number, cwd?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = execFile(command, args, { cwd: cwd || this.projectDir, timeout: timeoutMs }, (err, stdout, stderr) => {\n if (err) {\n // Include stderr in error message for better debugging\n const detail = stderr?.trim() || stdout?.trim() || \"\";\n const enriched = new Error(`${err.message}${detail ? \"\\n\" + detail : \"\"}`);\n reject(enriched);\n } else {\n resolve(stdout);\n }\n });\n const timer = setTimeout(() => {\n proc.kill(\"SIGTERM\");\n reject(new Error(\"Timeout\"));\n }, timeoutMs + 500);\n proc.on(\"exit\", () => clearTimeout(timer));\n });\n }\n\n /** Cleanup resources */\n destroy(): void {\n // No persistent processes to clean up\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBA8HO,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAyUuB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["import_node_path","import_node_fs","import_fs","import_path","import_node_fs","import_node_path","import_node_fs","import_node_path","resolve","import_node_child_process","import_node_fs","import_node_path","resolve","http","resolve","filePath","isPlaceholder"]}
|