@delt/claude-alarm 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -10
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +1258 -180
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +660 -106
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +176 -1
- package/dist/hub/server.js +308 -56
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +71 -3
- package/dist/index.js +309 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +176 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/constants.ts","../src/shared/config.ts","../src/shared/logger.ts","../src/shared/crash-guard.ts","../src/shared/peer-name.ts","../src/hub/session-manager.ts","../src/hub/notifier.ts","../src/shared/session-label.ts","../src/shared/permission-key.ts","../src/hub/telegram.ts","../src/hub/codex-supervisor.ts","../src/hub/auth.ts","../src/hub/server.ts","../src/channel/hub-client.ts","../src/channel/peer-name.ts","../src/channel/server.ts","../src/cli.ts","../src/codex/transport.ts"],"sourcesContent":["import path from 'node:path';\r\nimport os from 'node:os';\r\n\r\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\r\nexport const DEFAULT_HUB_PORT = 7900;\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\r\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\r\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\r\nexport const UPLOADS_DIR = path.join(CONFIG_DIR, 'uploads');\r\nexport const CODEX_PID_FILE = path.join(CONFIG_DIR, 'codex.pid');\r\nexport const CODEX_LOG_FILE = path.join(CONFIG_DIR, 'codex.log');\r\n\r\nexport const WS_PATH_CHANNEL = '/ws/channel';\r\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\r\n\r\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\r\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\r\nimport type { AppConfig } from './types.js';\r\n\r\nconst DEFAULT_CONFIG: AppConfig = {\r\n hub: {\r\n host: DEFAULT_HUB_HOST,\r\n port: DEFAULT_HUB_PORT,\r\n },\r\n notifications: {\r\n desktop: true,\r\n sound: true,\r\n },\r\n webhooks: [],\r\n};\r\n\r\nexport function ensureConfigDir(): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n}\r\n\r\nexport function loadConfig(): AppConfig {\r\n ensureConfigDir();\r\n let config: AppConfig;\r\n if (!fs.existsSync(CONFIG_FILE)) {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n } else {\r\n try {\r\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\r\n const parsed = JSON.parse(raw);\r\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub }, ...(parsed.telegram ? { telegram: parsed.telegram } : {}) };\r\n } catch {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n }\r\n }\r\n\r\n // Auto-generate token if missing\r\n if (!config.hub.token) {\r\n config.hub.token = randomUUID();\r\n saveConfig(config);\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** Get the current token, generating one if needed */\r\nexport function getOrCreateToken(): string {\r\n const config = loadConfig();\r\n return config.hub.token!;\r\n}\r\n\r\nexport function saveConfig(config: AppConfig): void {\r\n ensureConfigDir();\r\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\r\n}\r\n\r\nexport function setCodexEnabled(enabled: boolean): AppConfig {\r\n const config = loadConfig();\r\n config.codex = { ...config.codex, enabled };\r\n saveConfig(config);\r\n return config;\r\n}\r\n\r\nexport function shouldOfferCodex(config: AppConfig, installed: boolean): boolean {\r\n return installed && config.codex === undefined;\r\n}\r\n\r\n/**\r\n * Add claude-alarm as an MCP channel server to .mcp.json\r\n */\r\nexport function setupMcpConfig(targetDir?: string): string {\r\n const dir = targetDir ?? process.cwd();\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try {\r\n mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\r\n } catch {\r\n mcpConfig = {};\r\n }\r\n }\r\n\r\n if (!mcpConfig.mcpServers) {\r\n mcpConfig.mcpServers = {};\r\n }\r\n\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env: {\r\n CLAUDE_ALARM_SESSION_NAME: path.basename(dir),\r\n },\r\n };\r\n\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n return mcpPath;\r\n}\r\n","/**\r\n * Logger that writes to stderr only.\r\n * CRITICAL: In MCP channel servers, stdout is used for the stdio protocol.\r\n * Any console.log() would corrupt the protocol. Always use this logger.\r\n */\r\nexport const logger = {\r\n info(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm] ${msg}`, ...args);\r\n },\r\n warn(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm WARN] ${msg}`, ...args);\r\n },\r\n error(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm ERROR] ${msg}`, ...args);\r\n },\r\n debug(msg: string, ...args: unknown[]) {\r\n if (process.env.CLAUDE_ALARM_DEBUG) {\r\n console.error(`[claude-alarm DEBUG] ${msg}`, ...args);\r\n }\r\n },\r\n};\r\n","import fs from 'node:fs';\nimport { CONFIG_DIR, LOG_FILE } from './constants.js';\nimport { logger } from './logger.js';\n\nconst BURST_WINDOW_MS = 60_000;\nconst BURST_LIMIT = 10;\n\nfunction appendToLog(text: string): void {\n try {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.appendFileSync(LOG_FILE, text, 'utf-8');\n } catch {}\n}\n\nfunction describe(err: unknown): string {\n if (err instanceof Error) return err.stack ?? `${err.name}: ${err.message}`;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\n/**\n * Last-resort net for exceptions no local handler caught.\n *\n * The hub keeps running afterwards: a notification relay that dies silently is\n * worse than one running on possibly-degraded state, and in foreground mode the\n * stack trace would otherwise vanish with the console window. A repeated burst\n * still exits, so a self-feeding error loop cannot spin forever.\n */\nexport function installCrashGuard(context: string): void {\n let recent: number[] = [];\n\n const handle = (kind: string, err: unknown): void => {\n const stack = describe(err);\n appendToLog(`\\n[${new Date().toISOString()}] ${kind} (${context})\\n${stack}\\n`);\n logger.error(`${kind} caught by crash guard: ${stack}`);\n\n const now = Date.now();\n recent = recent.filter((t) => now - t < BURST_WINDOW_MS);\n recent.push(now);\n\n if (recent.length > BURST_LIMIT) {\n const msg = `${recent.length} unhandled errors within ${BURST_WINDOW_MS / 1000}s — exiting`;\n appendToLog(`[${new Date().toISOString()}] ${msg}\\n`);\n logger.error(msg);\n process.exit(1);\n }\n };\n\n process.on('uncaughtException', (err) => handle('uncaughtException', err));\n process.on('unhandledRejection', (reason) => handle('unhandledRejection', reason));\n}\n\nexport function logStartup(context: string): void {\n appendToLog(`\\n[${new Date().toISOString()}] ${context} started (pid ${process.pid})\\n`);\n}\n","// A peer name is pasted verbatim into another session's prompt as\r\n// SendMessage to \"<name>\", so quotes, brackets, backslashes and control\r\n// characters must never pass.\r\nconst SAFE_PEER_NAME = /^[^\\u0000-\\u001f\\u007f\"\\\\[\\]]{1,128}$/;\r\n\r\nexport function isValidPeerName(name: unknown): name is string {\r\n return typeof name === 'string' && name.trim() !== '' && SAFE_PEER_NAME.test(name);\r\n}\r\n","import type { SessionInfo, SessionStatus } from '../shared/types.js';\r\nimport { isValidPeerName } from '../shared/peer-name.js';\r\n\r\nexport class SessionManager {\r\n private sessions = new Map<string, SessionInfo>();\r\n\r\n register(session: SessionInfo): void {\r\n // Auto-number duplicate names\r\n const baseName = this.baseName(session);\r\n const existing = Array.from(this.sessions.values()).filter(\r\n s => s.id !== session.id && this.baseName(s) === baseName,\r\n );\r\n if (existing.length > 0) {\r\n // Number this one\r\n const usedNums = existing.map(s => {\r\n const m = s.displayName?.match(/\\((\\d+)\\)$/);\r\n return m ? parseInt(m[1], 10) : 1;\r\n });\r\n // Ensure first existing session has (1) if it doesn't yet\r\n for (const s of existing) {\r\n if (!s.displayName?.match(/\\(\\d+\\)$/)) {\r\n s.displayName = `${baseName} (1)`;\r\n }\r\n }\r\n const nextNum = Math.max(...usedNums, 1) + 1;\r\n session.displayName = `${baseName} (${nextNum})`;\r\n } else {\r\n session.displayName = baseName;\r\n }\r\n this.sessions.set(session.id, { ...session, peerName: isValidPeerName(session.peerName) ? session.peerName : undefined });\r\n }\r\n\r\n unregister(sessionId: string): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n this.sessions.delete(sessionId);\r\n return session;\r\n }\r\n\r\n updateStatus(sessionId: string, status: SessionStatus): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.status = status;\r\n session.lastActivity = Date.now();\r\n }\r\n return session;\r\n }\r\n\r\n setPeerName(sessionId: string, peerName: string | undefined): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.peerName = isValidPeerName(peerName) ? peerName : undefined;\r\n }\r\n return session;\r\n }\r\n\r\n updateActivity(sessionId: string): void {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.lastActivity = Date.now();\r\n }\r\n }\r\n\r\n get(sessionId: string): SessionInfo | undefined {\r\n return this.sessions.get(sessionId);\r\n }\r\n\r\n getAll(): SessionInfo[] {\r\n return Array.from(this.sessions.values());\r\n }\r\n\r\n count(): number {\r\n return this.sessions.size;\r\n }\r\n\r\n private baseName(session: SessionInfo): string {\r\n return session.title || session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\r\n }\r\n}\r\n","import notifier from 'node-notifier';\r\nimport { execFile } from 'node:child_process';\r\nimport { logger } from '../shared/logger.js';\r\nimport type { NotifyLevel, WebhookConfig } from '../shared/types.js';\r\nimport type { TelegramBot } from './telegram.js';\r\n\r\nexport class Notifier {\r\n private webhooks: WebhookConfig[] = [];\r\n private desktopEnabled = true;\r\n private notificationSettingsOpened = false;\r\n private dashboardUrl?: string;\r\n private telegramBot?: TelegramBot;\r\n\r\n configure(options: { desktop?: boolean; webhooks?: WebhookConfig[]; dashboardUrl?: string; telegramBot?: TelegramBot }): void {\r\n if (options.dashboardUrl) this.dashboardUrl = options.dashboardUrl;\r\n if (options.desktop !== undefined) this.desktopEnabled = options.desktop;\r\n if (options.webhooks) this.webhooks = options.webhooks;\r\n if (options.telegramBot) this.telegramBot = options.telegramBot;\r\n }\r\n\r\n async notify(title: string, message: string, level: NotifyLevel = 'info'): Promise<void> {\r\n await this.notifyWithSession(undefined, undefined, title, message, level);\r\n }\r\n\r\n async notifyWithSession(sessionId: string | undefined, sessionLabel: string | undefined, title: string, message: string, level: NotifyLevel = 'info'): Promise<void> {\r\n const promises: Promise<void>[] = [];\r\n\r\n if (this.desktopEnabled) {\r\n promises.push(this.sendDesktop(title, message, level));\r\n }\r\n\r\n for (const webhook of this.webhooks) {\r\n promises.push(this.sendWebhook(webhook, title, message, level));\r\n }\r\n\r\n if (this.telegramBot && sessionId && sessionLabel) {\r\n promises.push(this.telegramBot.sendNotification(sessionId, sessionLabel, title, message));\r\n }\r\n\r\n await Promise.allSettled(promises);\r\n }\r\n\r\n private async sendDesktop(title: string, message: string, _level: NotifyLevel): Promise<void> {\r\n if (process.platform === 'win32') {\r\n // Check if notifications are enabled by running snoretoast directly\r\n const enabled = await this.checkWindowsNotifications();\r\n if (!enabled) {\r\n this.openNotificationSettings();\r\n return;\r\n }\r\n }\r\n\r\n return new Promise((resolve) => {\r\n const notification = (notifier as any).notify(\r\n {\r\n title: `Claude Alarm: ${title}`,\r\n message,\r\n sound: true,\r\n wait: true,\r\n },\r\n (err: Error | null) => {\r\n if (err) {\r\n logger.warn(`Desktop notification failed: ${err.message}`);\r\n }\r\n resolve();\r\n },\r\n );\r\n\r\n // No click handler - dashboard is already open and notifications\r\n // can be clicked there to focus the relevant session\r\n });\r\n }\r\n\r\n private checkWindowsNotifications(): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n execFile(\r\n 'powershell',\r\n ['-Command', '(Get-ItemProperty -Path \"HKCU:\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\PushNotifications\" -Name ToastEnabled -ErrorAction SilentlyContinue).ToastEnabled'],\r\n (err, stdout) => {\r\n if (err) { resolve(true); return; } // assume enabled on error\r\n const value = stdout.trim();\r\n resolve(value !== '0');\r\n },\r\n );\r\n });\r\n }\r\n\r\n private openNotificationSettings(): void {\r\n if (this.notificationSettingsOpened) return;\r\n this.notificationSettingsOpened = true;\r\n\r\n logger.warn('Windows notifications are disabled. Opening notification settings...');\r\n logger.warn('Please enable notifications for this app, then try again.');\r\n\r\n if (process.platform === 'win32') {\r\n execFile('powershell', ['-Command', 'Start-Process ms-settings:notifications']);\r\n }\r\n\r\n // Allow re-opening after 5 minutes\r\n setTimeout(() => { this.notificationSettingsOpened = false; }, 5 * 60 * 1000);\r\n }\r\n\r\n private async sendWebhook(\r\n webhook: WebhookConfig,\r\n title: string,\r\n message: string,\r\n level: NotifyLevel,\r\n ): Promise<void> {\r\n try {\r\n const response = await fetch(webhook.url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...webhook.headers,\r\n },\r\n body: JSON.stringify({\r\n title,\r\n message,\r\n level,\r\n timestamp: Date.now(),\r\n source: 'claude-alarm',\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n logger.warn(`Webhook ${webhook.url} returned ${response.status}`);\r\n }\r\n } catch (err) {\r\n logger.warn(`Webhook ${webhook.url} failed: ${(err as Error).message}`);\r\n }\r\n }\r\n}\r\n","import type { SessionInfo } from './types.js';\r\n\r\nexport function sessionLabel(session: SessionInfo): string {\r\n const base = session.displayName || session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\r\n return session.agentKind === 'codex' ? `Codex · ${base}` : base;\r\n}\r\n","export function permissionKey(sessionId: string, requestId: string): string {\r\n return `${sessionId}\\n${requestId}`;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { logger } from '../shared/logger.js';\r\nimport { UPLOADS_DIR } from '../shared/constants.js';\r\nimport type { TelegramConfig, SessionInfo, PermissionChoice } from '../shared/types.js';\r\nimport { sessionLabel } from '../shared/session-label.js';\r\nimport { permissionKey } from '../shared/permission-key.js';\r\n\r\nconst TELEGRAM_API = 'https://api.telegram.org/bot';\r\nconst MAX_CHOICE_MESSAGES = 200;\r\n\r\ninterface ChoiceMessage {\r\n html: string;\r\n messageId?: number;\r\n outcome?: string;\r\n}\r\n\r\ninterface TelegramPhotoSize {\r\n file_id: string;\r\n file_unique_id: string;\r\n width: number;\r\n height: number;\r\n file_size?: number;\r\n}\r\n\r\ninterface TelegramMessage {\r\n message_id: number;\r\n chat: { id: number };\r\n text?: string;\r\n caption?: string;\r\n photo?: TelegramPhotoSize[];\r\n reply_to_message?: { message_id: number };\r\n}\r\n\r\ninterface TelegramCallbackQuery {\r\n id: string;\r\n from: { id: number };\r\n message?: TelegramMessage;\r\n data?: string;\r\n}\r\n\r\ninterface TelegramUpdate {\r\n update_id: number;\r\n message?: TelegramMessage;\r\n callback_query?: TelegramCallbackQuery;\r\n}\r\n\r\nexport class TelegramBot {\r\n private config: TelegramConfig;\r\n private offset = 0;\r\n private polling = false;\r\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n // message_id -> sessionId mapping for reply-based routing\r\n private messageSessionMap = new Map<number, string>();\r\n\r\n // Callback: when a text message arrives from Telegram for a session\r\n public onMessageToSession?: (sessionId: string, content: string) => void;\r\n // Callback: when an image arrives from Telegram for a session\r\n public onImageToSession?: (sessionId: string, imagePath: string, mimeType: string, caption?: string) => void;\r\n // Callback: when a permission verdict arrives from Telegram\r\n public onPermissionVerdict?: (sessionId: string, requestId: string, behavior: 'allow' | 'deny') => void;\r\n // Callback: when a Codex approval choice arrives from Telegram\r\n public onChoiceVerdict?: (sessionId: string, requestId: string, choiceId: string) => void;\r\n // Telegram caps callback_data at 64 bytes, so buttons carry a short token instead of the ids.\r\n private choiceTokens = new Map<string, { sessionId: string; requestId: string; choiceId: string; label: string }>();\r\n private choiceMessages = new Map<string, ChoiceMessage>();\r\n // Callback: get current sessions list\r\n public getSessions?: () => SessionInfo[];\r\n // Pending messages for session selection\r\n private pendingMessages = new Map<number, { text?: string; photoFileId?: string; caption?: string; sessionIds: string[] }>(); // chatId -> pending\r\n\r\n constructor(config: TelegramConfig) {\r\n this.config = config;\r\n }\r\n\r\n private get apiUrl(): string {\r\n return `${TELEGRAM_API}${this.config.botToken}`;\r\n }\r\n\r\n /** Send a notification message to Telegram */\r\n async sendNotification(sessionId: string, _sessionLabel: string, title: string, message: string): Promise<void> {\r\n const text = `<b>${this.escHtml(title)}</b>\\n${this.mdToHtml(message)}`;\r\n const result = await this.sendMessage(text);\r\n if (result?.message_id) {\r\n this.messageSessionMap.set(result.message_id, sessionId);\r\n // Cleanup old mappings (keep last 200)\r\n if (this.messageSessionMap.size > 200) {\r\n const keys = [...this.messageSessionMap.keys()];\r\n for (let i = 0; i < keys.length - 200; i++) {\r\n this.messageSessionMap.delete(keys[i]);\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Send a text message to the configured chat */\r\n private async sendMessage(text: string, replyToMessageId?: number, replyMarkup?: unknown): Promise<{ message_id: number } | null> {\r\n try {\r\n const body: Record<string, unknown> = {\r\n chat_id: this.config.chatId,\r\n text,\r\n parse_mode: 'HTML',\r\n };\r\n if (replyToMessageId) body.reply_to_message_id = replyToMessageId;\r\n if (replyMarkup) body.reply_markup = replyMarkup;\r\n\r\n const res = await fetch(`${this.apiUrl}/sendMessage`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!res.ok) {\r\n const err = await res.text();\r\n logger.warn(`Telegram sendMessage failed: ${res.status} ${err}`);\r\n return null;\r\n }\r\n\r\n const data = await res.json() as { ok: boolean; result: { message_id: number } };\r\n return data.ok ? data.result : null;\r\n } catch (err) {\r\n logger.warn(`Telegram sendMessage error: ${(err as Error).message}`);\r\n return null;\r\n }\r\n }\r\n\r\n /** Start long polling for incoming messages */\r\n startPolling(): void {\r\n if (this.polling) return;\r\n this.polling = true;\r\n logger.info('Telegram bot polling started');\r\n this.poll();\r\n }\r\n\r\n /** Stop polling */\r\n stopPolling(): void {\r\n this.polling = false;\r\n if (this.pollTimer) {\r\n clearTimeout(this.pollTimer);\r\n this.pollTimer = null;\r\n }\r\n logger.info('Telegram bot polling stopped');\r\n }\r\n\r\n private async poll(): Promise<void> {\r\n if (!this.polling) return;\r\n\r\n try {\r\n const res = await fetch(`${this.apiUrl}/getUpdates?offset=${this.offset}&timeout=30`, {\r\n signal: AbortSignal.timeout(35000),\r\n });\r\n\r\n if (!res.ok) {\r\n logger.warn(`Telegram getUpdates failed: ${res.status}`);\r\n this.scheduleNextPoll(5000);\r\n return;\r\n }\r\n\r\n const data = await res.json() as { ok: boolean; result: TelegramUpdate[] };\r\n if (data.ok && data.result.length > 0) {\r\n for (const update of data.result) {\r\n this.offset = update.update_id + 1;\r\n if (update.callback_query) {\r\n this.handleCallbackQuery(update.callback_query);\r\n } else if (update.message) {\r\n this.handleIncomingMessage(update.message);\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n if ((err as Error).name !== 'AbortError') {\r\n logger.warn(`Telegram poll error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n this.scheduleNextPoll(1000);\r\n }\r\n\r\n private scheduleNextPoll(delay: number): void {\r\n if (!this.polling) return;\r\n this.pollTimer = setTimeout(() => this.poll(), delay);\r\n }\r\n\r\n private async handleIncomingMessage(msg: TelegramMessage): Promise<void> {\r\n // Only process messages from the configured chat\r\n if (String(msg.chat.id) !== String(this.config.chatId)) return;\r\n\r\n const hasPhoto = msg.photo && msg.photo.length > 0;\r\n const text = (msg.text || msg.caption || '').trim();\r\n\r\n if (!text && !hasPhoto) return;\r\n\r\n // Check if it's a reply to a known message\r\n if (msg.reply_to_message) {\r\n const sessionId = this.messageSessionMap.get(msg.reply_to_message.message_id);\r\n if (sessionId) {\r\n if (hasPhoto) {\r\n await this.deliverPhotoToSession(sessionId, msg.photo!, text);\r\n } else {\r\n this.deliverToSession(sessionId, text);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // Check if it's a session selection command: /s_<index>\r\n if (text) {\r\n const selectMatch = text.match(/^\\/s_(\\d+)$/);\r\n if (selectMatch) {\r\n const pending = this.pendingMessages.get(msg.chat.id);\r\n if (pending) {\r\n this.pendingMessages.delete(msg.chat.id);\r\n const session = this.pendingSession(pending, parseInt(selectMatch[1], 10) - 1);\r\n if (session) {\r\n if (pending.photoFileId) {\r\n await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);\r\n } else if (pending.text) {\r\n this.deliverToSession(session.id, pending.text);\r\n }\r\n this.sendMessage(`Sent to [${this.getLabel(session)}]`);\r\n } else {\r\n this.sendMessage('Invalid session number.');\r\n }\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // No reply context — check sessions count\r\n const sessions = this.getSessions?.() ?? [];\r\n\r\n if (sessions.length === 0) {\r\n this.sendMessage('No active sessions connected.');\r\n return;\r\n }\r\n\r\n if (sessions.length === 1) {\r\n if (hasPhoto) {\r\n await this.deliverPhotoToSession(sessions[0].id, msg.photo!, text);\r\n } else {\r\n this.deliverToSession(sessions[0].id, text);\r\n }\r\n return;\r\n }\r\n\r\n // Multiple sessions — ask user to pick with inline buttons\r\n const sessionIds = sessions.map((s) => s.id);\r\n if (hasPhoto) {\r\n const largest = msg.photo![msg.photo!.length - 1];\r\n this.pendingMessages.set(msg.chat.id, { photoFileId: largest.file_id, caption: text, sessionIds });\r\n } else {\r\n this.pendingMessages.set(msg.chat.id, { text, sessionIds });\r\n }\r\n const buttons = sessions.map((s, i) => ({\r\n text: this.getLabel(s),\r\n callback_data: `sess:${i}:${msg.chat.id}`,\r\n }));\r\n // Arrange buttons in rows of 2\r\n const rows: Array<typeof buttons> = [];\r\n for (let i = 0; i < buttons.length; i += 2) {\r\n rows.push(buttons.slice(i, i + 2));\r\n }\r\n this.sendMessage('Multiple sessions active. Select one:', undefined, { inline_keyboard: rows });\r\n }\r\n\r\n private deliverToSession(sessionId: string, content: string): void {\r\n if (this.onMessageToSession) {\r\n this.onMessageToSession(sessionId, content);\r\n }\r\n }\r\n\r\n private async deliverPhotoToSession(sessionId: string, photos: TelegramPhotoSize[], caption?: string): Promise<void> {\r\n // Get the largest photo (last in array)\r\n const largest = photos[photos.length - 1];\r\n await this.deliverPhotoToSessionByFileId(sessionId, largest.file_id, caption);\r\n }\r\n\r\n private async deliverPhotoToSessionByFileId(sessionId: string, fileId: string, caption?: string): Promise<void> {\r\n try {\r\n // Get file path from Telegram\r\n const fileRes = await fetch(`${this.apiUrl}/getFile?file_id=${fileId}`);\r\n if (!fileRes.ok) { logger.warn('Failed to get Telegram file info'); return; }\r\n const fileData = await fileRes.json() as { ok: boolean; result: { file_path: string } };\r\n if (!fileData.ok) return;\r\n\r\n // Download the file\r\n const downloadUrl = `https://api.telegram.org/file/bot${this.config.botToken}/${fileData.result.file_path}`;\r\n const imgRes = await fetch(downloadUrl);\r\n if (!imgRes.ok) { logger.warn('Failed to download Telegram photo'); return; }\r\n const buffer = Buffer.from(await imgRes.arrayBuffer());\r\n\r\n // Determine extension\r\n const ext = fileData.result.file_path.split('.').pop() || 'jpg';\r\n const mimeType = ext === 'png' ? 'image/png' : ext === 'gif' ? 'image/gif' : ext === 'webp' ? 'image/webp' : 'image/jpeg';\r\n\r\n // Save to uploads dir\r\n fs.mkdirSync(UPLOADS_DIR, { recursive: true });\r\n const filename = `${randomUUID()}.${ext}`;\r\n const filePath = path.join(UPLOADS_DIR, filename);\r\n fs.writeFileSync(filePath, buffer);\r\n logger.info(`Telegram photo saved: ${filename} (${buffer.length} bytes)`);\r\n\r\n // Deliver to session\r\n if (this.onImageToSession) {\r\n this.onImageToSession(sessionId, filePath, mimeType, caption);\r\n }\r\n\r\n // Cleanup after 5 minutes\r\n setTimeout(() => { try { fs.unlinkSync(filePath); } catch {} }, 5 * 60 * 1000);\r\n } catch (err) {\r\n logger.warn(`Telegram photo download failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n // Buttons are numbered against the list shown when they were sent; Codex sessions come and go, so the live list may have shifted.\r\n private pendingSession(pending: { sessionIds: string[] }, idx: number): SessionInfo | undefined {\r\n const id = pending.sessionIds[idx];\r\n return id ? this.getSessions?.().find((s) => s.id === id) : undefined;\r\n }\r\n\r\n private getLabel(session: SessionInfo): string {\r\n return sessionLabel(session);\r\n }\r\n\r\n /** Send a permission request with inline buttons */\r\n async sendPermissionRequest(sessionId: string, sessionLabel: string, requestId: string, toolName: string, description: string, inputPreview: string): Promise<void> {\r\n // Parse inputPreview for readable display.\r\n // kind 'code' must stay byte-exact: running it through mdToHtml would swallow the\r\n // ** and ` characters the approver needs to judge what actually gets executed.\r\n // Only notify-style messages (prose Claude wrote) are safe to render as markdown.\r\n const isNotify = toolName.endsWith('__notify') || toolName === 'notify';\r\n let preview = inputPreview;\r\n let kind: 'code' | 'prose' = 'code';\r\n let lang = '';\r\n let truncated = false;\r\n try {\r\n const p = JSON.parse(inputPreview);\r\n if (p.command) { preview = `$ ${p.command}`; lang = 'bash'; }\r\n else if (p.title && p.message) { preview = p.message; kind = 'prose'; }\r\n else if (p.file_path) {\r\n preview = p.file_path;\r\n lang = this.langOf(p.file_path);\r\n if (p.content) { preview += '\\n' + p.content.slice(0, 3000); if (p.content.length > 500) truncated = true; }\r\n } else if (p.content && typeof p.content === 'string') {\r\n preview = p.content.slice(0, 3000);\r\n if (isNotify) kind = 'prose';\r\n if (p.content.length > 500) truncated = true;\r\n }\r\n } catch {\r\n truncated = true;\r\n const cmdMatch = inputPreview.match(/\"command\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n const contentMatch = inputPreview.match(/\"content\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n if (cmdMatch) { preview = `$ ${cmdMatch[1]}`; lang = 'bash'; }\r\n else if (contentMatch) preview = contentMatch[1].slice(0, 3000);\r\n }\r\n\r\n const truncNote = truncated ? '\\n\\n<i>...truncated</i>' : '';\r\n const previewSlice = preview.slice(0, 3000);\r\n // Short single-line commands read better inline; anything longer gets a code block\r\n const isShort = !previewSlice.includes('\\n') && previewSlice.length < 100;\r\n const langAttr = lang ? ` class=\"language-${lang}\"` : '';\r\n const previewHtml = kind === 'prose'\r\n ? this.mdToHtml(previewSlice)\r\n : isShort\r\n ? `<code>${this.escHtml(previewSlice)}</code>`\r\n : `<pre><code${langAttr}>${this.escHtml(previewSlice)}</code></pre>`;\r\n // Friendly tool name for Telegram — use title for notify tools\r\n let displayTool = toolName;\r\n if ((toolName.endsWith('__notify') || toolName === 'notify') && inputPreview) {\r\n try { const pp = JSON.parse(inputPreview); if (pp.title) displayTool = pp.title; } catch {\r\n const tm = inputPreview.match(/\"title\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n if (tm) displayTool = tm[1];\r\n }\r\n } else {\r\n const mcpMatch = toolName.match(/__([^_]+)$/);\r\n if (mcpMatch) displayTool = mcpMatch[1].charAt(0).toUpperCase() + mcpMatch[1].slice(1);\r\n }\r\n\r\n const text = `⚠️ <b>Permission Request</b> — ${this.escHtml(sessionLabel)}\\n\\n` +\r\n `🔧 <b>${this.escHtml(displayTool)}</b>\\n` +\r\n `${previewHtml}${truncNote}`;\r\n\r\n const replyMarkup = {\r\n inline_keyboard: [[\r\n { text: '✅ Allow', callback_data: `perm:allow:${sessionId}:${requestId}` },\r\n { text: '❌ Deny', callback_data: `perm:deny:${sessionId}:${requestId}` },\r\n ]],\r\n };\r\n\r\n await this.sendMessage(text, undefined, replyMarkup);\r\n }\r\n\r\n async sendChoiceRequest(\r\n sessionId: string,\r\n sessionLabel: string,\r\n requestId: string,\r\n toolName: string,\r\n description: string,\r\n inputPreview: string,\r\n choices: PermissionChoice[],\r\n ): Promise<void> {\r\n let preview = inputPreview;\r\n try {\r\n const p = JSON.parse(inputPreview);\r\n if (typeof p.command === 'string') preview = `$ ${p.command}`;\r\n else if (typeof p.content === 'string') preview = p.content;\r\n } catch {}\r\n const slice = preview.slice(0, 3000);\r\n const html =\r\n `⚠️ <b>Permission Request</b> — ${this.escHtml(sessionLabel)}\\n\\n` +\r\n `🔧 <b>${this.escHtml(toolName)}</b>` +\r\n (description ? `\\n${this.escHtml(description)}` : '') +\r\n (slice ? `\\n<pre>${this.escHtml(slice)}</pre>` : '') +\r\n (preview.length > slice.length ? '\\n<i>...truncated</i>' : '');\r\n\r\n const entry: ChoiceMessage = { html };\r\n this.choiceMessages.set(permissionKey(sessionId, requestId), entry);\r\n const rows = choices.map((c) => {\r\n const token = randomUUID().replace(/-/g, '').slice(0, 16);\r\n this.choiceTokens.set(token, { sessionId, requestId, choiceId: c.id, label: c.label });\r\n return [{ text: c.label, callback_data: `pc:${token}` }];\r\n });\r\n this.trimChoiceMessages();\r\n\r\n const sent = await this.sendMessage(html, undefined, { inline_keyboard: rows });\r\n if (!sent) return;\r\n entry.messageId = sent.message_id;\r\n if (entry.outcome) await this.editMessageText(this.config.chatId, sent.message_id, html + entry.outcome);\r\n }\r\n\r\n async resolveChoiceRequest(sessionId: string, requestId: string, state: 'resolved' | 'expired'): Promise<void> {\r\n this.dropChoiceTokens(sessionId, requestId);\r\n const key = permissionKey(sessionId, requestId);\r\n const entry = this.choiceMessages.get(key);\r\n if (!entry) return;\r\n this.choiceMessages.delete(key);\r\n entry.outcome = state === 'resolved' ? '\\n\\n✅ <b>Resolved</b>' : '\\n\\n⌛ <b>Expired</b>';\r\n if (entry.messageId !== undefined) await this.editMessageText(this.config.chatId, entry.messageId, entry.html + entry.outcome);\r\n }\r\n\r\n private async handleChoiceCallback(query: TelegramCallbackQuery): Promise<void> {\r\n const choice = this.choiceTokens.get(query.data!.slice('pc:'.length));\r\n if (!choice) {\r\n await this.answerCallbackQuery(query.id, 'Expired');\r\n return;\r\n }\r\n this.dropChoiceTokens(choice.sessionId, choice.requestId);\r\n this.onChoiceVerdict?.(choice.sessionId, choice.requestId, choice.choiceId);\r\n await this.answerCallbackQuery(query.id, `Sent: ${choice.label}`);\r\n const entry = this.choiceMessages.get(permissionKey(choice.sessionId, choice.requestId));\r\n if (entry?.messageId !== undefined && !entry.outcome) {\r\n await this.editMessageText(this.config.chatId, entry.messageId, `${entry.html}\\n\\n⏳ <b>Sent: ${this.escHtml(choice.label)}</b>`);\r\n }\r\n }\r\n\r\n private dropChoiceTokens(sessionId: string, requestId: string): void {\r\n for (const [token, c] of this.choiceTokens) {\r\n if (c.sessionId === sessionId && c.requestId === requestId) this.choiceTokens.delete(token);\r\n }\r\n }\r\n\r\n private trimChoiceMessages(): void {\r\n while (this.choiceMessages.size > MAX_CHOICE_MESSAGES) {\r\n const oldest = this.choiceMessages.keys().next().value as string;\r\n this.choiceMessages.delete(oldest);\r\n const [sessionId, requestId] = oldest.split('\\n');\r\n this.dropChoiceTokens(sessionId, requestId);\r\n }\r\n }\r\n\r\n private async handleCallbackQuery(query: TelegramCallbackQuery): Promise<void> {\r\n if (!query.data) return;\r\n if (String(query.message?.chat.id) !== String(this.config.chatId)) return;\r\n\r\n if (query.data.startsWith('sess:')) {\r\n await this.handleSessionSelectCallback(query);\r\n return;\r\n }\r\n\r\n if (query.data.startsWith('pc:')) {\r\n await this.handleChoiceCallback(query);\r\n return;\r\n }\r\n\r\n if (!query.data.startsWith('perm:')) return;\r\n\r\n const parts = query.data.split(':');\r\n if (parts.length < 4) return;\r\n const [, action, sessionId, requestId] = parts;\r\n const behavior = action === 'allow' ? 'allow' : 'deny';\r\n\r\n // Send verdict\r\n if (this.onPermissionVerdict) {\r\n this.onPermissionVerdict(sessionId, requestId, behavior as 'allow' | 'deny');\r\n }\r\n\r\n // Answer callback to remove loading state\r\n await this.answerCallbackQuery(query.id, behavior === 'allow' ? '✅ Allowed' : '❌ Denied');\r\n\r\n // Update message to show result (use escaped original text since it's plain)\r\n if (query.message) {\r\n const label = behavior === 'allow' ? '✅ <b>Allowed</b>' : '❌ <b>Denied</b>';\r\n const original = this.escHtml(query.message.text || '');\r\n await this.editMessageText(query.message.chat.id, query.message.message_id, original + `\\n\\n${label}`);\r\n }\r\n }\r\n\r\n private async handleSessionSelectCallback(query: TelegramCallbackQuery): Promise<void> {\r\n const parts = query.data!.split(':');\r\n if (parts.length < 3) return;\r\n const [, idxStr, chatIdStr] = parts;\r\n const chatId = parseInt(chatIdStr, 10);\r\n\r\n const pending = this.pendingMessages.get(chatId);\r\n const session = pending ? this.pendingSession(pending, parseInt(idxStr, 10)) : undefined;\r\n if (!pending || !session) {\r\n await this.answerCallbackQuery(query.id, 'Session not found');\r\n return;\r\n }\r\n this.pendingMessages.delete(chatId);\r\n\r\n if (pending.photoFileId) {\r\n await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);\r\n } else if (pending.text) {\r\n this.deliverToSession(session.id, pending.text);\r\n }\r\n\r\n await this.answerCallbackQuery(query.id, `Sent to ${this.getLabel(session)}`);\r\n // Update message to show which session was selected\r\n if (query.message) {\r\n await this.editMessageText(query.message.chat.id, query.message.message_id, `✅ Sent to <b>${this.escHtml(this.getLabel(session))}</b>`);\r\n }\r\n }\r\n\r\n private async answerCallbackQuery(callbackQueryId: string, text: string): Promise<void> {\r\n try {\r\n await fetch(`${this.apiUrl}/answerCallbackQuery`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ callback_query_id: callbackQueryId, text }),\r\n });\r\n } catch (err) {\r\n logger.warn(`Telegram answerCallbackQuery error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private async editMessageText(chatId: number | string, messageId: number, text: string): Promise<void> {\r\n try {\r\n await fetch(`${this.apiUrl}/editMessageText`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ chat_id: chatId, message_id: messageId, text, parse_mode: 'HTML' }),\r\n });\r\n } catch (err) {\r\n logger.warn(`Telegram editMessageText error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private escHtml(s: string): string {\r\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\r\n }\r\n\r\n private static readonly LANG_BY_EXT: Record<string, string> = {\r\n js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',\r\n ts: 'typescript', tsx: 'typescript', py: 'python', rb: 'ruby', go: 'go',\r\n java: 'java', kt: 'kotlin', rs: 'rust', php: 'php', cs: 'csharp',\r\n sh: 'bash', bash: 'bash', zsh: 'bash', ps1: 'powershell',\r\n json: 'json', yml: 'yaml', yaml: 'yaml', xml: 'xml', sql: 'sql',\r\n md: 'markdown', html: 'html', vue: 'html', css: 'css', scss: 'scss',\r\n };\r\n\r\n private langOf(path: string): string {\r\n const m = /\\.([a-z0-9]+)$/i.exec(path || '');\r\n return m ? (TelegramBot.LANG_BY_EXT[m[1].toLowerCase()] ?? '') : '';\r\n }\r\n\r\n /** Convert markdown to Telegram HTML (escape first, then apply formatting) */\r\n private mdToHtml(s: string): string {\r\n // Handle tables before escaping (convert to clean text format)\r\n let text = s.replace(\r\n /^(\\|.+\\|)\\n\\|[-| :]+\\|\\n((?:\\|.+\\|\\n?)*)/gm,\r\n (_match, header: string, body: string) => {\r\n const headerCells = header.split('|').filter((c: string) => c.trim()).map((c: string) => c.trim());\r\n const headerLine = headerCells.join(' | ');\r\n const bodyLines = body.trim().split('\\n').map((row: string) => {\r\n return row.split('|').filter((c: string) => c.trim()).map((c: string) => c.trim()).join(' | ');\r\n });\r\n return `**${headerLine}**\\n${bodyLines.join('\\n')}`;\r\n },\r\n );\r\n // Extract code spans/blocks BEFORE escaping/formatting so that bold/italic\r\n // regexes can't cross into them and produce overlapping tags (Telegram rejects).\r\n const tokens: string[] = [];\r\n const placeholder = (i: number) => `\\u0000CODE${i}\\u0000`;\r\n text = text.replace(/```(?:\\w*)\\n?([\\s\\S]*?)```/g, (_m, body: string) => {\r\n const i = tokens.push(`<pre>${this.escHtml(body)}</pre>`) - 1;\r\n return placeholder(i);\r\n });\r\n text = text.replace(/`([^`\\n]+)`/g, (_m, body: string) => {\r\n const i = tokens.push(`<code>${this.escHtml(body)}</code>`) - 1;\r\n return placeholder(i);\r\n });\r\n\r\n let html = this.escHtml(text);\r\n // Headings: # / ## / ### → bold (Telegram has no heading tags)\r\n html = html.replace(/^#{1,3}\\s+(.+)$/gm, '<b>$1</b>');\r\n // Bold: **...**\r\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\r\n // Italic: *...* (single line only, to avoid swallowing across blocks)\r\n html = html.replace(/\\*([^*\\n]+)\\*/g, '<i>$1</i>');\r\n\r\n // Restore code tokens\r\n html = html.replace(/\\u0000CODE(\\d+)\\u0000/g, (_m, n: string) => tokens[Number(n)] ?? '');\r\n return html;\r\n }\r\n\r\n /** Update config (e.g., from dashboard settings) */\r\n updateConfig(config: TelegramConfig): void {\r\n const wasPolling = this.polling;\r\n if (wasPolling) this.stopPolling();\r\n this.config = config;\r\n if (wasPolling && config.enabled) this.startPolling();\r\n }\r\n}\r\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SupervisorSpawn = (command: string, args: string[], options: SpawnOptions) => ChildProcess;\r\n\r\nconst HEALTHY_RUN_MS = 60_000;\r\n\r\n// dist/hub/server.js runs standalone, but dist/cli.js bundles the hub inline, so both layouts occur.\r\nexport function resolveAdapterScript(baseDir: string): string | undefined {\r\n return [path.join(baseDir, '..', 'codex', 'main.js'), path.join(baseDir, 'codex', 'main.js')].find((p) => fs.existsSync(p));\r\n}\r\n\r\nexport class CodexSupervisor {\r\n private child?: ChildProcess;\r\n private timer?: ReturnType<typeof setTimeout>;\r\n private stopped = true;\r\n private delay: number;\r\n\r\n constructor(\r\n private script: string,\r\n private spawnFn: SupervisorSpawn = spawn,\r\n private minDelayMs = 2000,\r\n private maxDelayMs = 60_000,\r\n private stopGraceMs = 3000,\r\n ) {\r\n this.delay = minDelayMs;\r\n }\r\n\r\n start(): void {\r\n this.stopped = false;\r\n this.launch();\r\n }\r\n\r\n stop(): void {\r\n this.stopped = true;\r\n if (this.timer) clearTimeout(this.timer);\r\n this.timer = undefined;\r\n const child = this.child;\r\n this.child = undefined;\r\n if (!child) return;\r\n child.stdin?.end();\r\n const force = setTimeout(() => child.kill(), this.stopGraceMs);\r\n force.unref();\r\n child.once('exit', () => clearTimeout(force));\r\n }\r\n\r\n private launch(): void {\r\n const startedAt = Date.now();\r\n // Detached: on Windows a non-detached child shares the hub's kill-on-close job and dies before it can remove codex.pid. Piped: a detached child cannot write to the hub's console (EBADF).\r\n const child = this.spawnFn(process.execPath, [this.script, '--watch-stdin'], {\r\n detached: true,\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n windowsHide: true,\r\n });\r\n this.child = child;\r\n child.stdout?.on('data', (d) => process.stdout.write(d));\r\n child.stderr?.on('data', (d) => process.stderr.write(d));\r\n child.stdin?.on('error', () => {});\r\n child.on('error', (err) => logger.warn(`Codex adapter failed to start: ${err.message}`));\r\n child.on('exit', (code, signal) => {\r\n if (this.child === child) this.child = undefined;\r\n if (this.stopped || code === 0) return;\r\n if (Date.now() - startedAt > HEALTHY_RUN_MS) this.delay = this.minDelayMs;\r\n logger.warn(`Codex adapter exited (${signal ?? code}); restarting in ${this.delay}ms`);\r\n this.timer = setTimeout(() => {\r\n this.timer = undefined;\r\n if (!this.stopped) this.launch();\r\n }, this.delay);\r\n this.delay = Math.min(this.delay * 2, this.maxDelayMs);\r\n });\r\n }\r\n}\r\n","import crypto from 'node:crypto';\r\nimport type http from 'node:http';\r\n\r\nexport const SESSION_COOKIE = 'ca_session';\r\nconst COOKIE_MAX_AGE = 30 * 24 * 60 * 60;\r\n\r\nexport type RequestLike = { headers: http.IncomingHttpHeaders; url?: string };\r\n\r\nexport function safeEqual(a: string | null | undefined, b: string): boolean {\r\n if (typeof a !== 'string') return false;\r\n const x = Buffer.from(a);\r\n const y = Buffer.from(b);\r\n return x.length === y.length && crypto.timingSafeEqual(x, y);\r\n}\r\n\r\nexport function dashboardCookieValue(token: string): string {\r\n return crypto.createHmac('sha256', token).update('claude-alarm-dashboard').digest('hex');\r\n}\r\n\r\nexport function parseCookies(header: string | undefined): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const part of (header ?? '').split(';')) {\r\n const i = part.indexOf('=');\r\n if (i <= 0) continue;\r\n const raw = part.slice(i + 1).trim();\r\n let value = raw;\r\n try { value = decodeURIComponent(raw); } catch {}\r\n out[part.slice(0, i).trim()] = value;\r\n }\r\n return out;\r\n}\r\n\r\nexport function sessionCookieHeader(token: string, secure: boolean): string {\r\n return `${SESSION_COOKIE}=${dashboardCookieValue(token)}; HttpOnly; SameSite=Strict; Path=/; Max-Age=${COOKIE_MAX_AGE}${secure ? '; Secure' : ''}`;\r\n}\r\n\r\nexport function isAuthorized(req: RequestLike, token: string, allowQueryToken: boolean): boolean {\r\n const auth = req.headers.authorization;\r\n if (auth?.startsWith('Bearer ') && safeEqual(auth.slice(7), token)) return true;\r\n if (safeEqual(parseCookies(req.headers.cookie)[SESSION_COOKIE], dashboardCookieValue(token))) return true;\r\n if (allowQueryToken) {\r\n const q = new URL(req.url ?? '/', 'http://hub').searchParams.get('token');\r\n if (safeEqual(q, token)) return true;\r\n }\r\n return false;\r\n}\r\n\r\nexport function isCrossOrigin(req: RequestLike): boolean {\r\n const origin = req.headers.origin;\r\n if (!origin) return false;\r\n try {\r\n return new URL(origin).host !== req.headers.host;\r\n } catch {\r\n return true;\r\n }\r\n}\r\n\r\nexport function isJsonRequest(req: RequestLike): boolean {\r\n return String(req.headers['content-type'] ?? '').toLowerCase().startsWith('application/json');\r\n}\r\n\r\nexport function isSecureRequest(req: RequestLike): boolean {\r\n return req.headers['x-forwarded-proto'] === 'https';\r\n}\r\n","import http from 'node:http';\r\nimport type { Duplex } from 'node:stream';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { WebSocketServer, WebSocket } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { randomUUID } from 'node:crypto';\r\nimport {\r\n DEFAULT_HUB_HOST,\r\n DEFAULT_HUB_PORT,\r\n WS_PATH_CHANNEL,\r\n WS_PATH_DASHBOARD,\r\n UPLOADS_DIR,\r\n} from '../shared/constants.js';\r\nimport { SessionManager } from './session-manager.js';\r\nimport { Notifier } from './notifier.js';\r\nimport { TelegramBot } from './telegram.js';\r\nimport { CodexSupervisor, resolveAdapterScript } from './codex-supervisor.js';\r\nimport { loadConfig, saveConfig } from '../shared/config.js';\r\nimport { sessionLabel } from '../shared/session-label.js';\r\nimport { installCrashGuard, logStartup } from '../shared/crash-guard.js';\r\nimport type { ChannelMessage, AppConfig, SessionInfo, WebhookConfig, TelegramConfig, PermissionChoice, PendingChoiceRequest } from '../shared/types.js';\r\nimport { permissionKey } from '../shared/permission-key.js';\r\nimport {\r\n isAuthorized,\r\n isCrossOrigin,\r\n isJsonRequest,\r\n isSecureRequest,\r\n safeEqual,\r\n sessionCookieHeader,\r\n} from './auth.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\n// 10MB images arrive base64-encoded (~13.4MB) over the dashboard socket.\r\nconst MAX_WS_PAYLOAD = 16 * 1024 * 1024;\r\n\r\nfunction validChoices(choices: unknown): PermissionChoice[] | undefined {\r\n if (!Array.isArray(choices)) return undefined;\r\n const valid = choices.filter((c): c is PermissionChoice => typeof c?.id === 'string' && typeof c?.label === 'string');\r\n return valid.length ? valid : undefined;\r\n}\r\n\r\nexport class HubServer {\r\n private httpServer: http.Server;\r\n private wssChannel: WebSocketServer;\r\n private wssDashboard: WebSocketServer;\r\n private sessions = new SessionManager();\r\n private notifier = new Notifier();\r\n private startTime = Date.now();\r\n\r\n // Map sessionId -> channel WebSocket\r\n private channelSockets = new Map<string, WebSocket>();\r\n // Track which channel connections are local\r\n private localChannels = new Set<string>();\r\n // All connected dashboard WebSockets\r\n private dashboardSockets = new Set<WebSocket>();\r\n\r\n private telegramBot?: TelegramBot;\r\n private heartbeatInterval?: ReturnType<typeof setInterval>;\r\n private channelAlive = new Map<string, boolean>(); // sessionId -> alive flag\r\n private socketOwners = new WeakMap<WebSocket, string>();\r\n // Codex approvals carry their own choices; a response is forwarded only in the mode of the request it answers.\r\n private choiceRequests = new Map<string, { request: PendingChoiceRequest; choiceIds: Set<string> }>();\r\n\r\n private host: string;\r\n private port: number;\r\n private token?: string;\r\n private codexEnabled: boolean;\r\n private codexSupervisor?: CodexSupervisor;\r\n\r\n constructor(config?: Partial<AppConfig>) {\r\n this.host = config?.hub?.host ?? DEFAULT_HUB_HOST;\r\n this.port = config?.hub?.port ?? DEFAULT_HUB_PORT;\r\n this.token = config?.hub?.token;\r\n this.codexEnabled = config?.codex?.enabled === true;\r\n\r\n if (config?.notifications) {\r\n this.notifier.configure({\r\n desktop: config.notifications.desktop,\r\n });\r\n }\r\n if (config?.webhooks) {\r\n this.notifier.configure({ webhooks: config.webhooks });\r\n }\r\n const displayHost = this.host === '0.0.0.0' ? '127.0.0.1' : this.host;\r\n this.notifier.configure({ dashboardUrl: `http://${displayHost}:${this.port}` });\r\n\r\n // Initialize Telegram bot if configured\r\n const fullConfig = loadConfig();\r\n if (fullConfig.telegram?.enabled && fullConfig.telegram.botToken && fullConfig.telegram.chatId) {\r\n this.initTelegram(fullConfig.telegram);\r\n }\r\n\r\n // HTTP Server\r\n this.httpServer = http.createServer((req, res) => this.handleHttp(req, res));\r\n\r\n // WebSocket for channel servers\r\n this.wssChannel = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });\r\n this.wssChannel.on('connection', (ws: WebSocket, req: http.IncomingMessage) => this.handleChannelConnection(ws, req));\r\n this.wssChannel.on('error', (err) => logger.warn(`Channel WebSocket server error: ${err.message}`));\r\n\r\n // WebSocket for dashboard\r\n this.wssDashboard = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });\r\n this.wssDashboard.on('connection', (ws) => this.handleDashboardConnection(ws));\r\n this.wssDashboard.on('error', (err) => logger.warn(`Dashboard WebSocket server error: ${err.message}`));\r\n\r\n // Route WebSocket upgrade requests\r\n this.httpServer.on('upgrade', (req, socket, head) => {\r\n // Once 'upgrade' has a listener the socket is ours; an unhandled 'error'\r\n // on it would take the whole process down.\r\n socket.on('error', (err) => logger.debug(`Upgrade socket error: ${err.message}`));\r\n\r\n const url = new URL(req.url ?? '/', 'http://hub');\r\n const pathname = url.pathname;\r\n\r\n if (isCrossOrigin(req)) {\r\n this.rejectUpgrade(socket, 403, 'Forbidden', pathname, req);\r\n return;\r\n }\r\n if (!this.authorized(req, true)) {\r\n this.rejectUpgrade(socket, 401, 'Unauthorized', pathname, req);\r\n return;\r\n }\r\n\r\n if (pathname === WS_PATH_CHANNEL) {\r\n this.wssChannel.handleUpgrade(req, socket, head, (ws) => {\r\n this.wssChannel.emit('connection', ws, req);\r\n });\r\n } else if (pathname === WS_PATH_DASHBOARD) {\r\n this.wssDashboard.handleUpgrade(req, socket, head, (ws) => {\r\n this.wssDashboard.emit('connection', ws, req);\r\n });\r\n } else {\r\n socket.destroy();\r\n }\r\n });\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.cleanupUploads();\r\n this.startHeartbeat();\r\n return new Promise((resolve, reject) => {\r\n const onStartupError = (err: Error) => reject(err);\r\n this.httpServer.once('error', onStartupError);\r\n this.httpServer.listen(this.port, this.host, () => {\r\n // Past startup a rejected promise is a no-op, so errors must be logged instead\r\n this.httpServer.removeListener('error', onStartupError);\r\n this.httpServer.on('error', (err) => logger.error(`HTTP server error: ${err.message}`));\r\n const displayHost = this.host === '0.0.0.0' ? '127.0.0.1' : this.host;\r\n logger.info(`Hub server listening on http://${displayHost}:${this.port}`);\r\n this.startCodexAdapter();\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n stop(): Promise<void> {\r\n return new Promise((resolve) => {\r\n this.codexSupervisor?.stop();\r\n this.codexSupervisor = undefined;\r\n // Stop heartbeat\r\n if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);\r\n\r\n // Stop telegram bot\r\n if (this.telegramBot) this.telegramBot.stopPolling();\r\n\r\n // Force-close all WebSocket connections\r\n for (const ws of this.channelSockets.values()) ws.terminate();\r\n for (const ws of this.dashboardSockets) ws.terminate();\r\n this.channelSockets.clear();\r\n this.dashboardSockets.clear();\r\n\r\n this.wssChannel.close();\r\n this.wssDashboard.close();\r\n this.httpServer.close(() => {\r\n logger.info('Hub server stopped');\r\n resolve();\r\n });\r\n\r\n // Force resolve after 3 seconds if server won't close\r\n setTimeout(() => {\r\n logger.warn('Force shutting down');\r\n resolve();\r\n }, 3000);\r\n });\r\n }\r\n\r\n // --- HTTP Handler ---\r\n\r\n private handleHttp(req: http.IncomingMessage, res: http.ServerResponse): void {\r\n const url = new URL(req.url ?? '/', 'http://hub');\r\n\r\n if (isCrossOrigin(req)) {\r\n this.jsonResponse(res, 403, { error: 'Cross-origin request rejected' });\r\n return;\r\n }\r\n if (req.method === 'POST' && !isJsonRequest(req)) {\r\n this.jsonResponse(res, 415, { error: 'Content-Type must be application/json' });\r\n return;\r\n }\r\n if (url.pathname === '/' && req.method === 'GET') {\r\n this.serveDashboard(req, res, url);\r\n return;\r\n }\r\n if (url.pathname === '/api/login' && req.method === 'POST') {\r\n this.handleLogin(req, res);\r\n return;\r\n }\r\n if (!this.authorized(req, false)) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n\r\n // Route\r\n if (url.pathname === '/api/sessions' && req.method === 'GET') {\r\n this.jsonResponse(res, 200, { sessions: this.sessions.getAll() });\r\n } else if (url.pathname === '/api/status' && req.method === 'GET') {\r\n this.jsonResponse(res, 200, {\r\n running: true,\r\n pid: process.pid,\r\n port: this.port,\r\n sessions: this.sessions.count(),\r\n uptime: Date.now() - this.startTime,\r\n });\r\n } else if (url.pathname.startsWith('/api/sessions/') && req.method === 'DELETE') {\r\n const sessionId = url.pathname.slice('/api/sessions/'.length);\r\n const ws = this.channelSockets.get(sessionId);\r\n if (ws) { ws.terminate(); }\r\n const session = this.sessions.unregister(sessionId);\r\n this.channelSockets.delete(sessionId);\r\n this.localChannels.delete(sessionId);\r\n this.channelAlive.delete(sessionId);\r\n this.expireChoices(sessionId);\r\n if (session) {\r\n this.broadcastToDashboards({ type: 'session_disconnected', sessionId });\r\n this.jsonResponse(res, 200, { ok: true });\r\n } else {\r\n this.jsonResponse(res, 404, { error: 'Session not found' });\r\n }\r\n } else if (url.pathname === '/api/send' && req.method === 'POST') {\r\n this.handleApiSend(req, res);\r\n } else if (url.pathname === '/api/notify' && req.method === 'POST') {\r\n this.handleApiNotify(req, res);\r\n } else if (url.pathname === '/api/webhooks' && req.method === 'GET') {\r\n const config = loadConfig();\r\n this.jsonResponse(res, 200, { webhooks: config.webhooks || [] });\r\n } else if (url.pathname === '/api/webhooks' && req.method === 'POST') {\r\n this.handleWebhookSave(req, res);\r\n } else if (url.pathname === '/api/telegram' && req.method === 'GET') {\r\n const cfg = loadConfig();\r\n const tg = cfg.telegram ?? { botToken: '', chatId: '', enabled: false };\r\n // Mask bot token for security\r\n this.jsonResponse(res, 200, {\r\n telegram: { ...tg, botToken: tg.botToken ? `${tg.botToken.slice(0, 8)}...` : '' },\r\n });\r\n } else if (url.pathname === '/api/telegram' && req.method === 'POST') {\r\n this.handleTelegramSave(req, res);\r\n } else if (url.pathname === '/api/telegram/test' && req.method === 'POST') {\r\n this.handleTelegramTest(req, res);\r\n } else if (url.pathname === '/api/telegram/detect' && req.method === 'POST') {\r\n this.handleTelegramDetect(req, res);\r\n } else {\r\n this.jsonResponse(res, 404, { error: 'Not found' });\r\n }\r\n }\r\n\r\n private serveDashboard(req: http.IncomingMessage, res: http.ServerResponse, url: URL): void {\r\n const linkToken = url.searchParams.get('token');\r\n if (linkToken !== null && this.token && safeEqual(linkToken, this.token)) {\r\n res.writeHead(302, { Location: '/', 'Set-Cookie': sessionCookieHeader(this.token, isSecureRequest(req)) });\r\n res.end();\r\n return;\r\n }\r\n // Look for dashboard HTML relative to this file (dist) or source\r\n const candidates = [\r\n path.join(__dirname, '..', 'dashboard', 'index.html'), // from dist/hub/\r\n path.join(__dirname, 'dashboard', 'index.html'), // from dist/ (bundled index.js)\r\n path.join(__dirname, '..', '..', 'src', 'dashboard', 'index.html'), // from dist/hub/ -> src/\r\n path.join(__dirname, '..', 'src', 'dashboard', 'index.html'), // from dist/ -> src/\r\n path.join(process.cwd(), 'dist', 'dashboard', 'index.html'), // from cwd\r\n path.join(process.cwd(), 'src', 'dashboard', 'index.html'), // from cwd/src\r\n ];\r\n logger.debug(`Dashboard candidates: ${JSON.stringify(candidates)}`);\r\n\r\n for (const candidate of candidates) {\r\n if (fs.existsSync(candidate)) {\r\n const html = fs.readFileSync(candidate, 'utf-8');\r\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\r\n res.end(html);\r\n return;\r\n }\r\n }\r\n\r\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\r\n res.end('<html><body><h1>claude-alarm</h1><p>Dashboard HTML not found. Reinstall the package.</p></body></html>');\r\n }\r\n\r\n private async handleLogin(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req) as { token?: unknown } | null;\r\n if (!this.token) {\r\n res.writeHead(204);\r\n res.end();\r\n return;\r\n }\r\n if (typeof body?.token !== 'string' || !safeEqual(body.token, this.token)) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n res.writeHead(204, { 'Set-Cookie': sessionCookieHeader(this.token, isSecureRequest(req)) });\r\n res.end();\r\n }\r\n\r\n private async handleApiSend(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n\r\n const { sessionId, content } = body as { sessionId?: string; content?: string };\r\n if (!sessionId || !content) {\r\n this.jsonResponse(res, 400, { error: 'sessionId and content are required' });\r\n return;\r\n }\r\n\r\n const ws = this.channelSockets.get(sessionId);\r\n if (!ws || ws.readyState !== WebSocket.OPEN) {\r\n this.jsonResponse(res, 404, { error: 'Session not connected' });\r\n return;\r\n }\r\n\r\n const msg: ChannelMessage = { type: 'message_to_session', sessionId, content, source: 'api' };\r\n ws.send(JSON.stringify(msg));\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private async handleApiNotify(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n\r\n const { title, message, level } = body as { title?: string; message?: string; level?: string };\r\n if (!title || !message) {\r\n this.jsonResponse(res, 400, { error: 'title and message are required' });\r\n return;\r\n }\r\n\r\n await this.notifier.notify(title, message, (level as any) ?? 'info');\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n // --- Channel WebSocket ---\r\n\r\n private handleChannelConnection(ws: WebSocket, req: http.IncomingMessage): void {\r\n const isLocal = this.isLocalRequest(req);\r\n logger.info(`Channel server connected (local: ${isLocal})`);\r\n\r\n // ws emits 'error' for protocol violations and socket failures; without a\r\n // listener Node rethrows it and kills the hub. 'close' still follows, so\r\n // session cleanup is handled there.\r\n ws.on('error', (err) => {\r\n logger.warn(`Channel WebSocket error: ${err.message}`);\r\n ws.terminate();\r\n });\r\n\r\n // Track pong responses for heartbeat\r\n ws.on('pong', () => {\r\n for (const [sessionId, sock] of this.channelSockets) {\r\n if (sock === ws) { this.channelAlive.set(sessionId, true); break; }\r\n }\r\n });\r\n\r\n ws.on('message', (data) => {\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n this.handleChannelMessage(ws, isLocal, msg);\r\n } catch {\r\n logger.warn('Invalid message from channel');\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n // Find and remove the session for this socket\r\n for (const [sessionId, sock] of this.channelSockets) {\r\n if (sock === ws) {\r\n const session = this.sessions.unregister(sessionId);\r\n this.channelSockets.delete(sessionId);\r\n this.localChannels.delete(sessionId);\r\n this.channelAlive.delete(sessionId);\r\n this.expireChoices(sessionId);\r\n logger.info(`Channel disconnected: ${sessionId}`);\r\n this.broadcastToDashboards({\r\n type: 'session_disconnected',\r\n sessionId,\r\n });\r\n break;\r\n }\r\n }\r\n });\r\n }\r\n\r\n private handleChannelMessage(ws: WebSocket, isLocal: boolean, msg: ChannelMessage): void {\r\n if (msg.type === 'register') {\r\n const id = msg.session.id;\r\n const owned = this.socketOwners.get(ws);\r\n if (owned && owned !== id) {\r\n logger.warn(`Rejected register for ${id}: connection already owns ${owned}`);\r\n return;\r\n }\r\n // A channel that reconnects after sleep or a network change reuses its id\r\n // while the hub may still see the old socket as open until the heartbeat\r\n // fires; the newest connection must win or the session is lost.\r\n const holder = this.channelSockets.get(id);\r\n if (holder && holder !== ws) {\r\n this.socketOwners.delete(holder);\r\n holder.terminate();\r\n }\r\n this.socketOwners.set(ws, id);\r\n } else if ('sessionId' in msg && this.socketOwners.get(ws) !== msg.sessionId) {\r\n return;\r\n }\r\n\r\n switch (msg.type) {\r\n case 'register': {\r\n const session = msg.session;\r\n session.isLocal = isLocal;\r\n const isReregister = !!this.sessions.get(session.id);\r\n this.sessions.register(session);\r\n this.channelSockets.set(session.id, ws);\r\n if (isLocal) this.localChannels.add(session.id);\r\n logger.info(`Session registered: ${session.id} (${session.name}, channel: ${session.channelEnabled ?? false}, peer: ${session.peerName ?? '-'})`);\r\n this.broadcastToDashboards({\r\n type: isReregister ? 'session_updated' : 'session_connected',\r\n session,\r\n });\r\n break;\r\n }\r\n\r\n case 'status': {\r\n const updated = this.sessions.updateStatus(msg.sessionId, msg.status);\r\n if (updated) {\r\n this.broadcastToDashboards({ type: 'session_updated', session: updated });\r\n }\r\n break;\r\n }\r\n\r\n case 'peer_name': {\r\n const updated = this.sessions.setPeerName(msg.sessionId, msg.peerName);\r\n if (updated) {\r\n logger.info(`Peer name for ${msg.sessionId}: ${msg.peerName ?? '-'}`);\r\n this.broadcastToDashboards({ type: 'session_updated', session: updated });\r\n }\r\n break;\r\n }\r\n\r\n case 'notify': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const notifySession = this.sessions.get(msg.sessionId);\r\n const notifyLabel = this.getSessionLabel(notifySession);\r\n this.notifier.notifyWithSession(msg.sessionId, notifyLabel, `[${notifyLabel}] ${msg.title}`, msg.message, msg.level ?? 'info');\r\n this.broadcastToDashboards({\r\n type: 'notification',\r\n sessionId: msg.sessionId,\r\n title: msg.title,\r\n message: msg.message,\r\n level: msg.level,\r\n timestamp: Date.now(),\r\n });\r\n break;\r\n }\r\n\r\n case 'reply': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const replySession = this.sessions.get(msg.sessionId);\r\n const replyLabel = this.getSessionLabel(replySession);\r\n this.notifier.notifyWithSession(msg.sessionId, replyLabel, `[${replyLabel}] Reply`, msg.content.slice(0, 3000), 'info');\r\n this.broadcastToDashboards({\r\n type: 'reply_from_session',\r\n sessionId: msg.sessionId,\r\n content: msg.content,\r\n timestamp: Date.now(),\r\n });\r\n break;\r\n }\r\n\r\n case 'permission_request': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const choices = validChoices(msg.choices);\r\n logger.info(`Permission request [${msg.requestId}] from ${msg.sessionId}: ${msg.toolName}`);\r\n if (choices) {\r\n this.choiceRequests.set(permissionKey(msg.sessionId, msg.requestId), {\r\n request: {\r\n sessionId: msg.sessionId,\r\n requestId: msg.requestId,\r\n toolName: msg.toolName,\r\n description: msg.description,\r\n inputPreview: msg.inputPreview,\r\n timestamp: msg.timestamp,\r\n choices,\r\n },\r\n choiceIds: new Set(choices.map((c) => c.id)),\r\n });\r\n }\r\n this.broadcastToDashboards({\r\n type: 'permission_request',\r\n sessionId: msg.sessionId,\r\n requestId: msg.requestId,\r\n toolName: msg.toolName,\r\n description: msg.description,\r\n inputPreview: msg.inputPreview,\r\n timestamp: msg.timestamp,\r\n ...(choices ? { choices } : {}),\r\n });\r\n // Forward to Telegram\r\n if (this.telegramBot) {\r\n const label = this.getSessionLabel(this.sessions.get(msg.sessionId));\r\n if (choices) {\r\n void this.telegramBot.sendChoiceRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview, choices);\r\n } else {\r\n this.telegramBot.sendPermissionRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview);\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'permission_resolved': {\r\n this.resolveChoice(msg.sessionId, msg.requestId, msg.state === 'expired' ? 'expired' : 'resolved');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // --- Dashboard WebSocket ---\r\n\r\n private handleDashboardConnection(ws: WebSocket): void {\r\n this.dashboardSockets.add(ws);\r\n logger.info(`Dashboard connected (total: ${this.dashboardSockets.size})`);\r\n\r\n ws.on('error', (err) => {\r\n logger.warn(`Dashboard WebSocket error: ${err.message}`);\r\n ws.terminate();\r\n });\r\n\r\n // Send current session list\r\n const sessionsMsg: ChannelMessage = {\r\n type: 'sessions_list',\r\n sessions: this.sessions.getAll(),\r\n };\r\n ws.send(JSON.stringify(sessionsMsg));\r\n const pendingMsg: ChannelMessage = {\r\n type: 'permission_pending',\r\n requests: [...this.choiceRequests.values()].map((c) => c.request),\r\n };\r\n ws.send(JSON.stringify(pendingMsg));\r\n\r\n ws.on('message', (data) => {\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n if (msg.type === 'message_to_session') {\r\n const channelWs = this.channelSockets.get(msg.sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n channelWs.send(JSON.stringify({ ...msg, source: 'dashboard' }));\r\n }\r\n } else if (msg.type === 'image_upload') {\r\n this.handleImageUpload(msg);\r\n } else if (msg.type === 'permission_response') {\r\n if (this.forwardPermissionResponse(msg)) {\r\n const verdict = msg.choiceId !== undefined ? `choice ${msg.choiceId}` : msg.behavior;\r\n logger.info(`Permission verdict [${msg.requestId}]: ${verdict} -> session ${msg.sessionId}`);\r\n }\r\n }\r\n } catch {\r\n logger.warn('Invalid message from dashboard');\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n this.dashboardSockets.delete(ws);\r\n logger.info(`Dashboard disconnected (total: ${this.dashboardSockets.size})`);\r\n });\r\n }\r\n\r\n // --- Helpers ---\r\n\r\n private forwardPermissionResponse(msg: { sessionId: string; requestId: string; behavior?: unknown; choiceId?: unknown }): boolean {\r\n const pending = this.choiceRequests.get(permissionKey(msg.sessionId, msg.requestId));\r\n let out: ChannelMessage;\r\n if (pending) {\r\n if (typeof msg.choiceId !== 'string' || !pending.choiceIds.has(msg.choiceId)) return false;\r\n out = { type: 'permission_response', sessionId: msg.sessionId, requestId: msg.requestId, choiceId: msg.choiceId };\r\n } else {\r\n if ((msg.behavior !== 'allow' && msg.behavior !== 'deny') || msg.choiceId !== undefined) return false;\r\n if (this.sessions.get(msg.sessionId)?.agentKind === 'codex') return false;\r\n out = { type: 'permission_response', sessionId: msg.sessionId, requestId: msg.requestId, behavior: msg.behavior };\r\n }\r\n const channelWs = this.channelSockets.get(msg.sessionId);\r\n if (channelWs?.readyState !== WebSocket.OPEN) return false;\r\n channelWs.send(JSON.stringify(out));\r\n return true;\r\n }\r\n\r\n private resolveChoice(sessionId: string, requestId: string, state: 'resolved' | 'expired'): void {\r\n if (!this.choiceRequests.delete(permissionKey(sessionId, requestId))) return;\r\n this.broadcastToDashboards({ type: 'permission_resolved', sessionId, requestId, state });\r\n void this.telegramBot?.resolveChoiceRequest(sessionId, requestId, state);\r\n }\r\n\r\n private expireChoices(sessionId: string): void {\r\n for (const pending of [...this.choiceRequests.values()]) {\r\n if (pending.request.sessionId === sessionId) this.resolveChoice(sessionId, pending.request.requestId, 'expired');\r\n }\r\n }\r\n\r\n private broadcastToDashboards(msg: ChannelMessage): void {\r\n const payload = JSON.stringify(msg);\r\n for (const ws of this.dashboardSockets) {\r\n if (ws.readyState === WebSocket.OPEN) {\r\n ws.send(payload);\r\n }\r\n }\r\n }\r\n\r\n private handleImageUpload(msg: ChannelMessage & { type: 'image_upload' }): void {\r\n const { sessionId, imageData, mimeType, originalName, content } = msg;\r\n\r\n // Only allow for local sessions\r\n if (!this.localChannels.has(sessionId)) {\r\n logger.warn(`Image upload rejected: session ${sessionId} is not local`);\r\n return;\r\n }\r\n\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (!channelWs || channelWs.readyState !== WebSocket.OPEN) return;\r\n\r\n // Validate mime type\r\n const allowedTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];\r\n if (!allowedTypes.includes(mimeType)) return;\r\n\r\n // Extract base64 data (remove data URL prefix if present)\r\n const base64Data = imageData.replace(/^data:image\\/\\w+;base64,/, '');\r\n const buffer = Buffer.from(base64Data, 'base64');\r\n\r\n // Validate size (10MB max)\r\n if (buffer.length > 10 * 1024 * 1024) {\r\n logger.warn('Image upload rejected: exceeds 10MB');\r\n return;\r\n }\r\n\r\n // Save to uploads dir\r\n fs.mkdirSync(UPLOADS_DIR, { recursive: true });\r\n const ext = mimeType.split('/')[1] === 'jpeg' ? 'jpg' : mimeType.split('/')[1];\r\n const filename = `${randomUUID()}.${ext}`;\r\n const filePath = path.join(UPLOADS_DIR, filename);\r\n fs.writeFileSync(filePath, buffer);\r\n\r\n // Forward file path to channel\r\n const forwardMsg: ChannelMessage = {\r\n type: 'image_to_session',\r\n sessionId,\r\n imagePath: filePath,\r\n mimeType,\r\n originalName,\r\n content,\r\n };\r\n channelWs.send(JSON.stringify(forwardMsg));\r\n logger.info(`Image saved and forwarded: ${filename} (${buffer.length} bytes)`);\r\n\r\n // Cleanup after 5 minutes\r\n setTimeout(() => {\r\n try { fs.unlinkSync(filePath); } catch {}\r\n }, 5 * 60 * 1000);\r\n }\r\n\r\n private async handleWebhookSave(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { webhooks } = body as { webhooks?: WebhookConfig[] };\r\n if (!Array.isArray(webhooks)) { this.jsonResponse(res, 400, { error: 'webhooks must be an array' }); return; }\r\n const config = loadConfig();\r\n config.webhooks = webhooks;\r\n saveConfig(config);\r\n this.notifier.configure({ webhooks });\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private initTelegram(config: TelegramConfig): void {\r\n this.telegramBot = new TelegramBot(config);\r\n this.telegramBot.getSessions = () => this.sessions.getAll();\r\n this.telegramBot.onMessageToSession = (sessionId, content) => {\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n const msg: ChannelMessage = { type: 'message_to_session', sessionId, content, source: 'telegram' };\r\n channelWs.send(JSON.stringify(msg));\r\n logger.info(`Telegram message forwarded to session: ${sessionId}`);\r\n }\r\n };\r\n this.telegramBot.onImageToSession = (sessionId, imagePath, mimeType, caption) => {\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n const msg: ChannelMessage = { type: 'image_to_session', sessionId, imagePath, mimeType, content: caption };\r\n channelWs.send(JSON.stringify(msg));\r\n logger.info(`Telegram photo forwarded to session: ${sessionId}`);\r\n }\r\n };\r\n this.telegramBot.onPermissionVerdict = (sessionId, requestId, behavior) => {\r\n if (this.forwardPermissionResponse({ sessionId, requestId, behavior })) {\r\n logger.info(`Telegram permission verdict [${requestId}]: ${behavior} -> session ${sessionId}`);\r\n }\r\n // Also notify dashboards so they can dismiss the permission bar\r\n this.broadcastToDashboards({ type: 'permission_response', sessionId, requestId, behavior });\r\n };\r\n this.telegramBot.onChoiceVerdict = (sessionId, requestId, choiceId) => {\r\n if (this.forwardPermissionResponse({ sessionId, requestId, choiceId })) {\r\n logger.info(`Telegram choice [${requestId}]: ${choiceId} -> session ${sessionId}`);\r\n }\r\n };\r\n this.notifier.configure({ telegramBot: this.telegramBot });\r\n this.telegramBot.startPolling();\r\n logger.info('Telegram bot initialized');\r\n }\r\n\r\n private async handleTelegramSave(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { telegram } = body as { telegram?: TelegramConfig };\r\n if (!telegram) { this.jsonResponse(res, 400, { error: 'telegram config required' }); return; }\r\n\r\n const config = loadConfig();\r\n // If botToken is masked (contains '...'), keep the existing token\r\n if (telegram.botToken.includes('...') && config.telegram?.botToken) {\r\n telegram.botToken = config.telegram.botToken;\r\n }\r\n config.telegram = telegram;\r\n saveConfig(config);\r\n\r\n // Stop existing bot if running\r\n if (this.telegramBot) {\r\n this.telegramBot.stopPolling();\r\n this.telegramBot = undefined;\r\n this.notifier.configure({ telegramBot: undefined as any });\r\n }\r\n\r\n // Start new bot if enabled\r\n if (telegram.enabled && telegram.botToken && telegram.chatId) {\r\n this.initTelegram(telegram);\r\n }\r\n\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private async handleTelegramTest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { botToken, chatId } = body as { botToken?: string; chatId?: string };\r\n if (!botToken || !chatId) {\r\n this.jsonResponse(res, 400, { error: 'botToken and chatId required' });\r\n return;\r\n }\r\n\r\n try {\r\n const testRes = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n chat_id: chatId,\r\n text: 'Claude Alarm test message! Connection successful.',\r\n }),\r\n });\r\n\r\n if (testRes.ok) {\r\n this.jsonResponse(res, 200, { ok: true });\r\n } else {\r\n const err = await testRes.json() as { description?: string };\r\n this.jsonResponse(res, 400, { error: (err as any).description || 'Telegram API error' });\r\n }\r\n } catch (err) {\r\n this.jsonResponse(res, 500, { error: (err as Error).message });\r\n }\r\n }\r\n\r\n private async handleTelegramDetect(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { botToken } = body as { botToken?: string };\r\n if (!botToken) {\r\n this.jsonResponse(res, 400, { error: 'botToken required' });\r\n return;\r\n }\r\n\r\n try {\r\n const detectRes = await fetch(`https://api.telegram.org/bot${botToken}/getUpdates?timeout=0&limit=10`, {\r\n signal: AbortSignal.timeout(10000),\r\n });\r\n\r\n if (!detectRes.ok) {\r\n const err = await detectRes.json() as { description?: string };\r\n this.jsonResponse(res, 400, { error: (err as any).description || 'Invalid bot token' });\r\n return;\r\n }\r\n\r\n const data = await detectRes.json() as { ok: boolean; result: Array<{ message?: { chat: { id: number; first_name?: string; title?: string; type: string } } }> };\r\n if (!data.ok || !data.result.length) {\r\n this.jsonResponse(res, 200, { ok: false, chats: [] });\r\n return;\r\n }\r\n\r\n // Extract unique chats\r\n const chatMap = new Map<string, { id: string; name: string; type: string }>();\r\n for (const update of data.result) {\r\n if (update.message?.chat) {\r\n const chat = update.message.chat;\r\n const id = String(chat.id);\r\n if (!chatMap.has(id)) {\r\n chatMap.set(id, {\r\n id,\r\n name: chat.title || chat.first_name || id,\r\n type: chat.type,\r\n });\r\n }\r\n }\r\n }\r\n\r\n this.jsonResponse(res, 200, { ok: true, chats: [...chatMap.values()] });\r\n } catch (err) {\r\n this.jsonResponse(res, 500, { error: (err as Error).message });\r\n }\r\n }\r\n\r\n private startCodexAdapter(): void {\r\n if (!this.codexEnabled) return;\r\n const script = resolveAdapterScript(__dirname);\r\n if (!script) {\r\n logger.warn('Codex adapter is enabled but codex/main.js was not found next to the hub');\r\n return;\r\n }\r\n this.codexSupervisor = new CodexSupervisor(script);\r\n this.codexSupervisor.start();\r\n logger.info('Codex adapter started');\r\n }\r\n\r\n private startHeartbeat(): void {\r\n // Ping channel WebSockets every 30s, terminate unresponsive ones\r\n this.heartbeatInterval = setInterval(() => {\r\n for (const [sessionId, ws] of this.channelSockets) {\r\n if (this.channelAlive.get(sessionId) === false) {\r\n // No pong received since last ping — terminate\r\n logger.info(`Heartbeat timeout, terminating session: ${sessionId}`);\r\n ws.terminate();\r\n continue;\r\n }\r\n this.channelAlive.set(sessionId, false);\r\n ws.ping();\r\n }\r\n }, 30000);\r\n }\r\n\r\n private cleanupUploads(): void {\r\n try {\r\n if (!fs.existsSync(UPLOADS_DIR)) return;\r\n const files = fs.readdirSync(UPLOADS_DIR);\r\n for (const file of files) {\r\n try { fs.unlinkSync(path.join(UPLOADS_DIR, file)); } catch {}\r\n }\r\n if (files.length > 0) logger.info(`Cleaned up ${files.length} leftover upload(s)`);\r\n } catch {}\r\n }\r\n\r\n private getSessionLabel(session?: SessionInfo): string {\r\n return session ? sessionLabel(session) : 'unknown';\r\n }\r\n\r\n private jsonResponse(res: http.ServerResponse, status: number, body: unknown): void {\r\n res.writeHead(status, { 'Content-Type': 'application/json' });\r\n res.end(JSON.stringify(body));\r\n }\r\n\r\n private authorized(req: http.IncomingMessage, allowQueryToken: boolean): boolean {\r\n return !this.token || isAuthorized(req, this.token, allowQueryToken);\r\n }\r\n\r\n private rejectUpgrade(socket: Duplex, status: number, text: string, pathname: string, req: http.IncomingMessage): void {\r\n logger.warn(`Rejected ${pathname} upgrade (${status}) from ${req.socket.remoteAddress}`);\r\n socket.write(`HTTP/1.1 ${status} ${text}\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n`);\r\n socket.destroy();\r\n }\r\n\r\n private isLocalRequest(req: http.IncomingMessage): boolean {\r\n const addr = req.socket.remoteAddress;\r\n return addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1';\r\n }\r\n\r\n private readBody(req: http.IncomingMessage, maxSize = 1024 * 1024): Promise<unknown | null> {\r\n return new Promise((resolve) => {\r\n let data = '';\r\n let size = 0;\r\n req.on('data', (chunk) => {\r\n size += chunk.length;\r\n if (size > maxSize) {\r\n req.destroy();\r\n resolve(null);\r\n return;\r\n }\r\n data += chunk;\r\n });\r\n req.on('end', () => {\r\n try {\r\n resolve(JSON.parse(data));\r\n } catch {\r\n resolve(null);\r\n }\r\n });\r\n });\r\n }\r\n}\r\n\r\n// Direct execution support\r\nif (process.argv[1] && (\r\n process.argv[1].endsWith('hub/server.js') ||\r\n process.argv[1].endsWith('hub/server.ts')\r\n)) {\r\n installCrashGuard('hub daemon');\r\n logStartup('Hub daemon');\r\n const config = loadConfig();\r\n const hub = new HubServer(config);\r\n hub.start().catch((err) => {\r\n logger.error('Failed to start hub:', err);\r\n process.exit(1);\r\n });\r\n\r\n const shutdown = () => {\r\n hub.stop().then(() => process.exit(0));\r\n };\r\n process.on('SIGINT', shutdown);\r\n process.on('SIGTERM', shutdown);\r\n}\r\n","import WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\r\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\r\n\r\nexport class HubClient {\r\n private ws: WebSocket | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\r\n private queue: ChannelMessage[] = [];\r\n private connected = false;\r\n private closed = false;\r\n\r\n constructor(\r\n private sessionId: string,\r\n private sessionName: string,\r\n private hubHost = DEFAULT_HUB_HOST,\r\n private hubPort = DEFAULT_HUB_PORT,\r\n private token?: string,\r\n private getPeerName: () => string | undefined = () => undefined,\r\n private getRegistration: () => Partial<SessionInfo> = () => ({}),\r\n ) {}\r\n\r\n connect(): void {\r\n this.closed = false;\r\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\r\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\r\n logger.debug(`Connecting to hub at ${url}`);\r\n\r\n try {\r\n const ws = new WebSocket(url);\r\n this.ws = ws;\r\n\r\n ws.on('open', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n this.ws!.send(JSON.stringify(this.registration()));\r\n\r\n // Flush queued messages\r\n for (const msg of this.queue) {\r\n this.ws!.send(JSON.stringify(msg));\r\n }\r\n this.queue = [];\r\n });\r\n\r\n ws.on('message', (data) => {\r\n if (this.ws !== ws) return;\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n for (const handler of this.messageHandlers) {\r\n handler(msg);\r\n }\r\n } catch (err) {\r\n logger.warn('Failed to parse hub message:', err);\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n if (!this.closed) this.scheduleReconnect();\r\n });\r\n\r\n ws.on('error', (err) => {\r\n if (this.ws !== ws) return;\r\n logger.debug(`Hub connection error: ${err.message}`);\r\n this.connected = false;\r\n });\r\n } catch {\r\n logger.debug('Failed to connect to hub, will retry');\r\n this.scheduleReconnect();\r\n }\r\n }\r\n\r\n send(msg: ChannelMessage): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n } else {\r\n if (this.queue.length < 100) {\r\n this.queue.push(msg);\r\n }\r\n logger.debug('Hub not connected, message queued');\r\n }\r\n }\r\n\r\n onMessage(handler: (msg: ChannelMessage) => void): void {\r\n this.messageHandlers.push(handler);\r\n }\r\n\r\n disconnect(): void {\r\n this.closed = true;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n this.connected = false;\r\n }\r\n\r\n reregister(): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(this.registration()));\r\n }\r\n }\r\n\r\n private registration(): ChannelMessage {\r\n return {\r\n type: 'register',\r\n session: {\r\n id: this.sessionId,\r\n name: this.sessionName,\r\n status: 'idle',\r\n connectedAt: Date.now(),\r\n lastActivity: Date.now(),\r\n cwd: process.cwd(),\r\n channelEnabled: true,\r\n peerName: this.getPeerName(),\r\n ...this.getRegistration(),\r\n },\r\n };\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnectTimer || this.closed) return;\r\n this.reconnectTimer = setTimeout(() => {\r\n this.reconnectTimer = null;\r\n this.connect();\r\n }, 5000);\r\n }\r\n}\r\n","import fs from 'node:fs';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\nimport { isValidPeerName } from '../shared/peer-name.js';\r\n\r\nexport interface PeerLookupEnv {\r\n messagingSocket?: string;\r\n sessionId?: string;\r\n}\r\n\r\ninterface SessionRecord {\r\n name?: unknown;\r\n messagingSocketPath?: unknown;\r\n sessionId?: unknown;\r\n}\r\n\r\nexport function findPeerName(records: unknown[], env: PeerLookupEnv): string | undefined {\r\n const valid = records.filter((r): r is SessionRecord => typeof r === 'object' && r !== null);\r\n const nameOf = (match: (r: SessionRecord) => boolean): string | undefined => {\r\n const rec = valid.find(match);\r\n return isValidPeerName(rec?.name) ? rec.name : undefined;\r\n };\r\n if (env.messagingSocket) {\r\n const bySocket = nameOf((r) => r.messagingSocketPath === env.messagingSocket);\r\n if (bySocket) return bySocket;\r\n }\r\n if (env.sessionId) return nameOf((r) => r.sessionId === env.sessionId);\r\n return undefined;\r\n}\r\n\r\n// <configDir>/sessions/<pid>.json is Claude Code's undocumented peer registry\r\n// (the source of ListAgents/SendMessage names, verified on 2.1.283). Its format\r\n// may change between releases, so every failure must degrade to \"no name\".\r\nexport function readPeerName(env: NodeJS.ProcessEnv = process.env): string | undefined {\r\n const lookup: PeerLookupEnv = {\r\n messagingSocket: env.CLAUDE_CODE_MESSAGING_SOCKET,\r\n sessionId: env.CLAUDE_CODE_SESSION_ID,\r\n };\r\n if (!lookup.messagingSocket && !lookup.sessionId) return undefined;\r\n\r\n const configDir = env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');\r\n const dir = path.join(configDir, 'sessions');\r\n let files: string[];\r\n try {\r\n files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));\r\n } catch {\r\n return undefined;\r\n }\r\n\r\n const records: unknown[] = [];\r\n for (const f of files) {\r\n try {\r\n records.push(JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')));\r\n } catch {}\r\n }\r\n return findPeerName(records, lookup);\r\n}\r\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport {\r\n CallToolRequestSchema,\r\n ListToolsRequestSchema,\r\n} from '@modelcontextprotocol/sdk/types.js';\r\nimport { z } from 'zod';\r\nimport { randomUUID } from 'node:crypto';\r\nimport path from 'node:path';\r\nimport { logger } from '../shared/logger.js';\r\nimport { CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\nimport { loadConfig } from '../shared/config.js';\r\nimport { HubClient } from './hub-client.js';\r\nimport { readPeerName } from './peer-name.js';\r\nimport type { SessionStatus, NotifyLevel } from '../shared/types.js';\r\n\r\nconst sessionId = randomUUID();\r\nconst sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? path.basename(process.cwd());\r\nlet peerName = readPeerName();\r\n\r\nconst server = new Server(\r\n {\r\n name: CHANNEL_SERVER_NAME,\r\n version: CHANNEL_SERVER_VERSION,\r\n },\r\n {\r\n capabilities: {\r\n experimental: {\r\n 'claude/channel': {},\r\n 'claude/channel/permission': {},\r\n },\r\n tools: {},\r\n },\r\n instructions: `Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. Read the message and act on it.\r\n\r\nREPLYING:\r\n- The dashboard user ONLY sees messages sent via the reply tool — your terminal output is invisible to them. You MUST call reply with your response.\r\n- The dashboard renders Markdown (headings, lists, tables, code blocks). Provide a thorough, complete answer; do not apply CLI-style brevity.\r\n- Reply in the user's language (match the language they wrote in).\r\n\r\nIMAGES: If a channel message contains \"[Image: ...] Read the file to view it: <path>\", use the Read tool on that path before responding — otherwise the image is invisible to you.\r\n\r\nROUTING: If a dashboard message has lines like [claude-alarm] @X = SendMessage to \"Y\", do what the message asks and deliver the result with the SendMessage tool to exactly \"Y\". Never guess other recipients. Then reply to the dashboard with what you sent.\r\n\r\nSTATUS: Call status(\"working\") before starting a long task, status(\"waiting_input\") when blocked on user input, status(\"idle\") when finished responding.\r\n\r\nNOTIFICATIONS:\r\n- Use notify only for key events: task completion, errors, or when user attention is needed. Not for intermediate steps or simple acknowledgments.\r\n- Pick level: success=completion, error=failure, warning=attention needed, info=neutral status.`,\r\n },\r\n);\r\n\r\n// Load config for hub connection (env vars take priority)\r\nconst config = loadConfig();\r\nconst hubHost = process.env.CLAUDE_ALARM_HUB_HOST ?? config.hub.host;\r\nconst hubPort = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\r\nconst hubToken = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\r\n\r\n// Hub client for forwarding to central hub\r\nconst hubClient = new HubClient(\r\n sessionId,\r\n sessionName,\r\n hubHost,\r\n hubPort,\r\n hubToken,\r\n () => peerName,\r\n);\r\n\r\n// --- Tools ---\r\n\r\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\r\n tools: [\r\n {\r\n name: 'notify',\r\n description:\r\n 'Send a desktop notification to the user. Use this when you complete a task, encounter an error, or need user attention. The notification will appear as a system toast/popup.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n title: { type: 'string', description: 'Notification title (short)' },\r\n message: { type: 'string', description: 'Notification body text' },\r\n level: {\r\n type: 'string',\r\n enum: ['info', 'warning', 'error', 'success'],\r\n description: 'Notification level (default: info)',\r\n },\r\n },\r\n required: ['title', 'message'],\r\n },\r\n },\r\n {\r\n name: 'reply',\r\n description:\r\n 'Send a message to the web dashboard. Use this to communicate status updates, results, or any information the user should see in the monitoring dashboard.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n content: { type: 'string', description: 'Message content to display on the dashboard' },\r\n },\r\n required: ['content'],\r\n },\r\n },\r\n {\r\n name: 'status',\r\n description:\r\n 'Update your session status displayed on the dashboard. Set to \"working\" when actively processing, \"waiting_input\" when you need user input, or \"idle\" when done.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n status: {\r\n type: 'string',\r\n enum: ['idle', 'working', 'waiting_input'],\r\n description: 'Current session status',\r\n },\r\n },\r\n required: ['status'],\r\n },\r\n },\r\n ],\r\n}));\r\n\r\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\r\n const { name, arguments: args } = request.params;\r\n\r\n switch (name) {\r\n case 'notify': {\r\n const title = args?.title as string;\r\n const message = args?.message as string;\r\n const level = (args?.level as NotifyLevel) ?? 'info';\r\n logger.info(`Notify [${level}]: ${title} - ${message}`);\r\n hubClient.send({\r\n type: 'notify',\r\n sessionId,\r\n title,\r\n message,\r\n level,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Notification sent: \"${title}\"` }],\r\n };\r\n }\r\n\r\n case 'reply': {\r\n const content = args?.content as string;\r\n logger.info(`Reply: ${content.slice(0, 100)}...`);\r\n hubClient.send({\r\n type: 'reply',\r\n sessionId,\r\n content,\r\n });\r\n return {\r\n content: [{ type: 'text', text: 'Message sent to dashboard.' }],\r\n };\r\n }\r\n\r\n case 'status': {\r\n const status = args?.status as SessionStatus;\r\n logger.info(`Status update: ${status}`);\r\n hubClient.send({\r\n type: 'status',\r\n sessionId,\r\n status,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Status updated to \"${status}\".` }],\r\n };\r\n }\r\n\r\n default:\r\n return {\r\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\r\n isError: true,\r\n };\r\n }\r\n});\r\n\r\n// --- Permission Relay ---\r\n\r\nconst PermissionRequestSchema = z.object({\r\n method: z.literal('notifications/claude/channel/permission_request'),\r\n params: z.object({\r\n request_id: z.string(),\r\n tool_name: z.string(),\r\n description: z.string(),\r\n input_preview: z.string(),\r\n }),\r\n});\r\n\r\n// Handle permission_request from Claude Code and forward to hub\r\nserver.setNotificationHandler(\r\n PermissionRequestSchema,\r\n async (notification) => {\r\n const { request_id, tool_name, description, input_preview } = notification.params;\r\n logger.info(`Permission request [${request_id}]: ${tool_name} - ${description}`);\r\n hubClient.send({\r\n type: 'permission_request',\r\n sessionId,\r\n requestId: request_id,\r\n toolName: tool_name,\r\n description,\r\n inputPreview: input_preview,\r\n timestamp: Date.now(),\r\n });\r\n },\r\n);\r\n\r\n// --- Startup ---\r\n\r\nasync function main() {\r\n logger.info(`Starting MCP channel server (session: ${sessionId})`);\r\n\r\n // Connect to hub (non-blocking, will retry)\r\n hubClient.connect();\r\n\r\n // The registry entry can be written after this MCP server starts, so re-check\r\n // shortly after boot, then poll to follow /rename.\r\n const refreshPeerName = () => {\r\n const next = readPeerName();\r\n if (next === peerName) return;\r\n peerName = next;\r\n logger.info(`Peer name: ${next ?? '(none)'}`);\r\n hubClient.send({ type: 'peer_name', sessionId, peerName: next });\r\n };\r\n setTimeout(refreshPeerName, 2_000).unref();\r\n setInterval(refreshPeerName, 30_000).unref();\r\n\r\n // Listen for messages from hub and forward to Claude via channel notification\r\n hubClient.onMessage(async (msg) => {\r\n if (msg.type === 'message_to_session' && msg.sessionId === sessionId) {\r\n logger.info(`Message from dashboard: ${msg.content}`);\r\n await server.notification({\r\n method: 'notifications/claude/channel',\r\n params: {\r\n content: msg.content,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()) },\r\n },\r\n });\r\n } else if (msg.type === 'image_to_session' && msg.sessionId === sessionId) {\r\n logger.info(`Image from dashboard: ${msg.imagePath}`);\r\n const textPart = msg.content ? `\\n${msg.content}` : '';\r\n await server.notification({\r\n method: 'notifications/claude/channel',\r\n params: {\r\n content: `[Image: ${msg.originalName || 'image'}] The user sent an image. Read the file to view it: ${msg.imagePath}${textPart}`,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()), imagePath: msg.imagePath, mimeType: msg.mimeType },\r\n },\r\n });\r\n } else if (msg.type === 'permission_response' && msg.sessionId === sessionId) {\r\n logger.info(`Permission verdict [${msg.requestId}]: ${msg.behavior}`);\r\n await server.notification({\r\n method: 'notifications/claude/channel/permission',\r\n params: {\r\n request_id: msg.requestId,\r\n behavior: msg.behavior,\r\n },\r\n });\r\n }\r\n });\r\n\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n logger.info('MCP channel server running on stdio');\r\n\r\n // Exit when stdin closes (Claude Code session ended)\r\n process.stdin.on('end', () => {\r\n logger.info('stdin closed, exiting');\r\n hubClient.disconnect();\r\n process.exit(0);\r\n });\r\n process.stdin.on('close', () => {\r\n logger.info('stdin closed, exiting');\r\n hubClient.disconnect();\r\n process.exit(0);\r\n });\r\n}\r\n\r\nmain().catch((err) => {\r\n logger.error('Fatal error:', err);\r\n process.exit(1);\r\n});\r\n","import { spawn } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport readline from 'node:readline';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { loadConfig, ensureConfigDir, setupMcpConfig, getOrCreateToken, setCodexEnabled, shouldOfferCodex } from './shared/config.js';\r\nimport { findOnPath } from './codex/transport.js';\r\nimport { PID_FILE, LOG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, CODEX_PID_FILE, CODEX_LOG_FILE } from './shared/constants.js';\r\nimport { logger } from './shared/logger.js';\r\nimport { installCrashGuard, logStartup } from './shared/crash-guard.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nfunction authHeaders(token?: string): Record<string, string> {\r\n return token ? { Authorization: `Bearer ${token}` } : {};\r\n}\r\n\r\nfunction loginLink(displayHost: string, port: number, token?: string): string {\r\n return `http://${displayHost}:${port}/${token ? `?token=${encodeURIComponent(token)}` : ''}`;\r\n}\r\n\r\nfunction printUsage() {\r\n console.log(`\r\nclaude-alarm - Monitor Claude Code sessions with notifications\r\n\r\nUsage:\r\n claude-alarm init Setup everything and show next steps\r\n claude-alarm hub start [-d] Start the hub server (-d for daemon)\r\n claude-alarm hub stop Stop the hub daemon\r\n claude-alarm hub status Show hub status\r\n claude-alarm setup [dir] Add claude-alarm to .mcp.json\r\n claude-alarm test Send a test notification\r\n claude-alarm token Show current auth token\r\n claude-alarm codex enable Start the Codex adapter together with the hub\r\n claude-alarm codex disable Stop starting the Codex adapter with the hub\r\n claude-alarm codex start Run the Codex adapter on its own (e.g. Codex on another PC)\r\n claude-alarm codex stop Stop a running Codex adapter\r\n claude-alarm codex status Show Codex adapter status\r\n claude-alarm help Show this help\r\n\r\nQuick start:\r\n claude-alarm init\r\n`);\r\n}\r\n\r\nasync function checkForUpdates() {\r\n try {\r\n const pkgPath = path.join(__dirname, '..', 'package.json');\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\r\n const currentVersion = pkg.version;\r\n\r\n const res = await fetch('https://registry.npmjs.org/@delt/claude-alarm/latest', {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (!res.ok) return;\r\n const data = await res.json() as { version: string };\r\n const latestVersion = data.version;\r\n\r\n if (latestVersion !== currentVersion) {\r\n console.log(`\\n⚠ New version available: ${currentVersion} → ${latestVersion}`);\r\n console.log(` Run: npm install -g @delt/claude-alarm\\n`);\r\n }\r\n } catch {\r\n // Silent fail - don't block startup\r\n }\r\n}\r\n\r\nasync function hubStart(daemon: boolean) {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = host === '0.0.0.0' ? '127.0.0.1' : host;\r\n\r\n // Check for updates (non-blocking)\r\n checkForUpdates();\r\n\r\n // Check if already running\r\n if (fs.existsSync(PID_FILE)) {\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n if (isProcessRunning(pid)) {\r\n console.log(`Hub is already running (PID: ${pid}). Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n return;\r\n }\r\n // Stale PID file\r\n fs.unlinkSync(PID_FILE);\r\n }\r\n\r\n if (daemon) {\r\n ensureConfigDir();\r\n const logFd = fs.openSync(LOG_FILE, 'a');\r\n const hubScript = path.join(__dirname, 'hub', 'server.js');\r\n\r\n const child = spawn(process.execPath, [hubScript], {\r\n detached: true,\r\n stdio: ['ignore', logFd, logFd],\r\n env: { ...process.env },\r\n });\r\n\r\n if (child.pid) {\r\n fs.writeFileSync(PID_FILE, String(child.pid), 'utf-8');\r\n child.unref();\r\n console.log(`Hub started as daemon (PID: ${child.pid})`);\r\n console.log(`Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n console.log(`Token: ${config.hub.token}`);\r\n console.log(`Logs: ${LOG_FILE}`);\r\n } else {\r\n console.error('Failed to start hub daemon');\r\n process.exit(1);\r\n }\r\n } else {\r\n // Foreground mode - import and run directly\r\n console.log(`Starting hub on http://${displayHost}:${port} (press Ctrl+C to stop)`);\r\n console.log(`Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n console.log(`Token: ${config.hub.token}`);\r\n console.log(`Logs: ${LOG_FILE}`);\r\n installCrashGuard('hub foreground');\r\n logStartup('Hub foreground');\r\n const { HubServer } = await import('./hub/server.js');\r\n const hub = new HubServer(config);\r\n await hub.start();\r\n\r\n // Write PID file even in foreground\r\n ensureConfigDir();\r\n fs.writeFileSync(PID_FILE, String(process.pid), 'utf-8');\r\n\r\n const shutdown = async () => {\r\n console.log('\\nShutting down...');\r\n await hub.stop();\r\n if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);\r\n process.exit(0);\r\n };\r\n process.on('SIGINT', shutdown);\r\n process.on('SIGTERM', shutdown);\r\n }\r\n}\r\n\r\nfunction hubStop() {\r\n if (!fs.existsSync(PID_FILE)) {\r\n console.log('Hub is not running (no PID file found)');\r\n return;\r\n }\r\n\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n try {\r\n process.kill(pid, 'SIGTERM');\r\n console.log(`Hub stopped (PID: ${pid})`);\r\n } catch {\r\n console.log('Hub process not found (may have already stopped)');\r\n }\r\n fs.unlinkSync(PID_FILE);\r\n}\r\n\r\nasync function hubStatus() {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = host === '0.0.0.0' ? '127.0.0.1' : host;\r\n\r\n // Check PID file\r\n let pidInfo = 'not running';\r\n if (fs.existsSync(PID_FILE)) {\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n if (isProcessRunning(pid)) {\r\n pidInfo = `running (PID: ${pid})`;\r\n } else {\r\n pidInfo = 'not running (stale PID file)';\r\n }\r\n }\r\n\r\n // Try to reach the hub HTTP API\r\n try {\r\n const res = await fetch(`http://${host}:${port}/api/status`, { headers: authHeaders(config.hub.token) });\r\n if (res.ok) {\r\n const data = await res.json() as any;\r\n console.log(`Hub: running (PID: ${data.pid})`);\r\n console.log(`Port: ${data.port}`);\r\n console.log(`Sessions: ${data.sessions}`);\r\n console.log(`Uptime: ${Math.round(data.uptime / 1000)}s`);\r\n console.log(`Dashboard: http://${displayHost}:${port}`);\r\n const token = config.hub.token;\r\n if (token) {\r\n console.log(`Token: ${token.slice(0, 8)}...(masked)`);\r\n }\r\n return;\r\n }\r\n } catch {\r\n // Hub not reachable\r\n }\r\n\r\n console.log(`Hub: ${pidInfo}`);\r\n console.log(`Configured: http://${displayHost}:${port}`);\r\n}\r\n\r\nfunction setup(targetDir?: string) {\r\n const mcpPath = setupMcpConfig(targetDir);\r\n console.log(`Added claude-alarm to ${mcpPath}`);\r\n console.log('\\nTo use with Claude Code:');\r\n console.log(' 1. Start the hub: claude-alarm hub start -d');\r\n console.log(' 2. Run Claude Code: claude --dangerously-load-development-channels server:claude-alarm');\r\n}\r\n\r\nasync function test() {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n\r\n try {\r\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\r\n if (config.hub.token) {\r\n headers['Authorization'] = `Bearer ${config.hub.token}`;\r\n }\r\n const res = await fetch(`http://${host}:${port}/api/notify`, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify({\r\n title: 'Test Notification',\r\n message: 'Claude Alarm is working! This is a test notification.',\r\n level: 'success',\r\n }),\r\n });\r\n\r\n if (res.ok) {\r\n console.log('Test notification sent! Check your desktop for the toast.');\r\n } else {\r\n console.error(`Hub returned ${res.status}. Is the hub running?`);\r\n }\r\n } catch {\r\n console.error('Could not reach hub. Start it first: claude-alarm hub start');\r\n }\r\n}\r\n\r\nfunction ask(question: string): Promise<string> {\r\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\r\n return new Promise((resolve) => {\r\n rl.question(question, (answer) => {\r\n rl.close();\r\n resolve(answer.trim());\r\n });\r\n });\r\n}\r\n\r\nasync function init() {\r\n const dir = process.cwd();\r\n const projectName = path.basename(dir);\r\n\r\n console.log(`\\nclaude-alarm init for \"${projectName}\"\\n`);\r\n\r\n const remote = await ask('Connect to a remote hub? (y/N): ');\r\n\r\n let env: Record<string, string> = {\r\n CLAUDE_ALARM_SESSION_NAME: projectName,\r\n };\r\n\r\n if (remote.toLowerCase() === 'y') {\r\n const host = await ask('Hub host (e.g. 192.168.1.100): ');\r\n const port = await ask('Hub port (default: 7900): ');\r\n const token = await ask('Hub token: ');\r\n\r\n if (!host) {\r\n console.error('Host is required.');\r\n process.exit(1);\r\n }\r\n env.CLAUDE_ALARM_HUB_HOST = host;\r\n if (port) env.CLAUDE_ALARM_HUB_PORT = port;\r\n if (token) env.CLAUDE_ALARM_HUB_TOKEN = token;\r\n }\r\n\r\n // Write .mcp.json\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try { mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8')); } catch { mcpConfig = {}; }\r\n }\r\n if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env,\r\n };\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n\r\n console.log(`\\n✓ Created ${mcpPath}`);\r\n\r\n if (remote.toLowerCase() !== 'y') {\r\n // Check if hub is running locally\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = host === '0.0.0.0' ? '127.0.0.1' : host;\r\n let hubRunning = false;\r\n try {\r\n const res = await fetch(`http://${host}:${port}/api/status`, { headers: authHeaders(config.hub.token) });\r\n hubRunning = res.ok;\r\n } catch {}\r\n\r\n if (hubRunning) {\r\n console.log('✓ Hub is running');\r\n } else {\r\n console.log('✗ Hub is not running. Start it with:');\r\n console.log(` claude-alarm hub start`);\r\n }\r\n console.log(` Dashboard: http://${displayHost}:${port}`);\r\n\r\n if (shouldOfferCodex(config, findOnPath('codex') !== undefined)) {\r\n const answer = await ask('\\nCodex is installed. Show Codex conversations on the dashboard too? (y/N): ');\r\n if (answer.toLowerCase() === 'y') codexEnable(true);\r\n else setCodexEnabled(false);\r\n }\r\n }\r\n\r\n console.log(`\\nNext step:`);\r\n console.log(` claude --dangerously-load-development-channels server:claude-alarm`);\r\n console.log(`\\nTo skip permission prompts (allows remote control without approval):`);\r\n console.log(` claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions`);\r\n console.log(`\\n WARNING: --dangerously-skip-permissions allows Claude to execute any action`);\r\n console.log(` without your approval. Only use in trusted, isolated environments.\\n`);\r\n}\r\n\r\nfunction showToken() {\r\n const token = getOrCreateToken();\r\n console.log(`Token: ${token}`);\r\n}\r\n\r\nfunction isProcessRunning(pid: number): boolean {\r\n try {\r\n process.kill(pid, 0);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nfunction readCodexPid(): number | undefined {\r\n if (!fs.existsSync(CODEX_PID_FILE)) return undefined;\r\n const pid = parseInt(fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim(), 10);\r\n return Number.isNaN(pid) ? undefined : pid;\r\n}\r\n\r\nfunction codexEnable(enabled: boolean) {\r\n setCodexEnabled(enabled);\r\n console.log(enabled\r\n ? 'Codex adapter enabled. Restart the hub to apply: claude-alarm hub stop, then claude-alarm hub start'\r\n : 'Codex adapter disabled. Restart the hub to apply.');\r\n}\r\n\r\nfunction codexStart() {\r\n const pid = readCodexPid();\r\n if (pid !== undefined && isProcessRunning(pid)) {\r\n console.log(`Codex adapter is already running (PID: ${pid})`);\r\n return;\r\n }\r\n ensureConfigDir();\r\n const logFd = fs.openSync(CODEX_LOG_FILE, 'a');\r\n const child = spawn(process.execPath, [path.join(__dirname, 'codex', 'main.js')], {\r\n detached: true,\r\n stdio: ['ignore', logFd, logFd],\r\n env: { ...process.env },\r\n });\r\n child.unref();\r\n console.log(`Codex adapter started (PID: ${child.pid}). Logs: ${CODEX_LOG_FILE}`);\r\n}\r\n\r\nfunction codexStop() {\r\n const pid = readCodexPid();\r\n if (pid === undefined) {\r\n console.log('Codex adapter is not running');\r\n return;\r\n }\r\n try {\r\n process.kill(pid, 'SIGTERM');\r\n console.log(`Codex adapter stopped (PID: ${pid})`);\r\n } catch {\r\n console.log('Codex adapter process not found (may have already stopped)');\r\n }\r\n try { fs.unlinkSync(CODEX_PID_FILE); } catch {}\r\n}\r\n\r\nfunction codexStatus() {\r\n const pid = readCodexPid();\r\n const state = pid === undefined ? 'not running' : isProcessRunning(pid) ? `running (PID: ${pid})` : 'not running (stale PID file)';\r\n console.log(`Codex adapter: ${state}`);\r\n console.log(`Start with hub: ${loadConfig().codex?.enabled ? 'enabled' : 'disabled'}`);\r\n}\r\n\r\n// --- Main CLI ---\r\nasync function main() {\r\n const args = process.argv.slice(2);\r\n const cmd = args[0];\r\n const sub = args[1];\r\n\r\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\r\n printUsage();\r\n return;\r\n }\r\n\r\n if (cmd === 'serve') {\r\n // Start channel server (used by MCP)\r\n await import('./channel/server.js');\r\n return;\r\n }\r\n\r\n if (cmd === 'init') {\r\n await init();\r\n return;\r\n }\r\n\r\n if (cmd === 'hub') {\r\n if (sub === 'start') {\r\n const daemon = args.includes('-d') || args.includes('--daemon');\r\n await hubStart(daemon);\r\n } else if (sub === 'stop') {\r\n hubStop();\r\n } else if (sub === 'status') {\r\n await hubStatus();\r\n } else {\r\n console.error(`Unknown hub command: ${sub}`);\r\n printUsage();\r\n process.exit(1);\r\n }\r\n return;\r\n }\r\n\r\n if (cmd === 'codex') {\r\n if (sub === 'enable') codexEnable(true);\r\n else if (sub === 'disable') codexEnable(false);\r\n else if (sub === 'start') codexStart();\r\n else if (sub === 'stop') codexStop();\r\n else if (sub === 'status') codexStatus();\r\n else {\r\n console.error(`Unknown codex command: ${sub}`);\r\n printUsage();\r\n process.exit(1);\r\n }\r\n return;\r\n }\r\n\r\n if (cmd === 'setup') {\r\n setup(args[1]);\r\n return;\r\n }\r\n\r\n if (cmd === 'test') {\r\n await test();\r\n return;\r\n }\r\n\r\n if (cmd === 'token') {\r\n showToken();\r\n return;\r\n }\r\n\r\n console.error(`Unknown command: ${cmd}`);\r\n printUsage();\r\n process.exit(1);\r\n}\r\n\r\nmain().catch((err) => {\r\n logger.error('CLI error:', err);\r\n process.exit(1);\r\n});\r\n","import { spawn, type ChildProcess } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { Duplex } from 'node:stream';\r\nimport WebSocket, { type ClientOptions } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SpawnFn = (command: string, args: string[]) => ChildProcess;\r\n\r\nexport interface ProxyConnection {\r\n ws: WebSocket;\r\n close(): void;\r\n}\r\n\r\n// npm installs Codex on Windows as a codex.cmd shim, which only runs through a shell.\r\nexport function resolveCommand(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): { file: string; shell: boolean } {\r\n if (platform !== 'win32' || /[\\\\/]/.test(command) || path.extname(command)) return { file: command, shell: false };\r\n const file = findOnPath(command, platform, env);\r\n return file ? { file, shell: file.endsWith('.cmd') } : { file: command, shell: false };\r\n}\r\n\r\nexport function findOnPath(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string | undefined {\r\n const dirs = (env.PATH ?? env.Path ?? '').split(path.delimiter).filter(Boolean);\r\n for (const ext of platform === 'win32' ? ['.exe', '.cmd'] : ['']) {\r\n for (const dir of dirs) {\r\n const file = path.join(dir, command + ext);\r\n if (fs.existsSync(file)) return file;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nexport const defaultSpawn: SpawnFn = (command, args) => {\r\n const { file, shell } = resolveCommand(command);\r\n if (shell) return spawn(`\"${file}\" ${args.join(' ')}`, { stdio: 'pipe', windowsHide: true, shell: true });\r\n return spawn(file, args, { stdio: 'pipe', windowsHide: true });\r\n};\r\n\r\n// The daemon control socket speaks WebSocket, not JSONL: `codex app-server proxy` only relays bytes.\r\nexport function connectProxy(command: string, spawnFn: SpawnFn = defaultSpawn): Promise<ProxyConnection> {\r\n const child = spawnFn(command, ['app-server', 'proxy']);\r\n child.stdin?.on('error', () => {});\r\n child.stderr?.on('data', (d) => logger.debug(`codex proxy: ${String(d).trim()}`));\r\n\r\n const stream = new Duplex({\r\n read() {},\r\n write(chunk, _encoding, callback) {\r\n if (child.stdin?.writable) child.stdin.write(chunk, callback);\r\n else callback();\r\n },\r\n final(callback) {\r\n child.stdin?.end();\r\n callback();\r\n },\r\n });\r\n child.stdout?.on('data', (d) => stream.push(d));\r\n child.stdout?.on('end', () => stream.push(null));\r\n Object.assign(stream, { setNoDelay() {}, setTimeout() {}, setKeepAlive() {}, ref() {}, unref() {} });\r\n\r\n const ws = new WebSocket('ws://localhost/', {\r\n createConnection: (() => stream) as unknown as ClientOptions['createConnection'],\r\n });\r\n const close = () => {\r\n ws.terminate();\r\n child.kill();\r\n };\r\n\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n const fail = (err: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n close();\r\n reject(err);\r\n };\r\n child.on('error', fail);\r\n child.once('exit', (code) => fail(new Error(`codex proxy exited (code ${code})`)));\r\n ws.on('error', fail);\r\n ws.once('open', () => {\r\n if (settled) return;\r\n settled = true;\r\n child.on('error', (err) => logger.warn(`codex proxy error: ${err.message}`));\r\n ws.on('error', (err) => logger.warn(`codex daemon socket error: ${err.message}`));\r\n resolve({ ws, close });\r\n });\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AADf,IAGa,kBACA,kBAEA,YACA,aACA,UACA,UACA,aACA,gBACA,gBAEA,iBACA,mBAEA,qBACA;AAlBb;AAAA;AAAA;AAGO,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,eAAe;AAC1D,IAAM,cAAc,KAAK,KAAK,YAAY,aAAa;AACvD,IAAM,WAAW,KAAK,KAAK,YAAY,SAAS;AAChD,IAAM,WAAW,KAAK,KAAK,YAAY,SAAS;AAChD,IAAM,cAAc,KAAK,KAAK,YAAY,SAAS;AACnD,IAAM,iBAAiB,KAAK,KAAK,YAAY,WAAW;AACxD,IAAM,iBAAiB,KAAK,KAAK,YAAY,WAAW;AAExD,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAE1B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAAA;AAAA;;;AClBtC,OAAO,QAAQ;AACf,OAAOA,WAAU;AACjB,SAAS,kBAAkB;AAgBpB,SAAS,kBAAwB;AACtC,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,OAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AACF;AAEO,SAAS,aAAwB;AACtC,kBAAgB;AAChB,MAAIC;AACJ,MAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B,IAAAA,UAAS,EAAE,GAAG,gBAAgB,KAAK,EAAE,GAAG,eAAe,IAAI,EAAE;AAAA,EAC/D,OAAO;AACL,QAAI;AACF,YAAM,MAAM,GAAG,aAAa,aAAa,OAAO;AAChD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,MAAAA,UAAS,EAAE,GAAG,gBAAgB,GAAG,QAAQ,KAAK,EAAE,GAAG,eAAe,KAAK,GAAG,OAAO,IAAI,GAAG,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC,EAAG;AAAA,IACpJ,QAAQ;AACN,MAAAA,UAAS,EAAE,GAAG,gBAAgB,KAAK,EAAE,GAAG,eAAe,IAAI,EAAE;AAAA,IAC/D;AAAA,EACF;AAGA,MAAI,CAACA,QAAO,IAAI,OAAO;AACrB,IAAAA,QAAO,IAAI,QAAQ,WAAW;AAC9B,eAAWA,OAAM;AAAA,EACnB;AAEA,SAAOA;AACT;AAGO,SAAS,mBAA2B;AACzC,QAAMA,UAAS,WAAW;AAC1B,SAAOA,QAAO,IAAI;AACpB;AAEO,SAAS,WAAWA,SAAyB;AAClD,kBAAgB;AAChB,KAAG,cAAc,aAAa,KAAK,UAAUA,SAAQ,MAAM,CAAC,GAAG,EAAE,UAAU,SAAS,MAAM,IAAM,CAAC;AACnG;AAEO,SAAS,gBAAgB,SAA6B;AAC3D,QAAMA,UAAS,WAAW;AAC1B,EAAAA,QAAO,QAAQ,EAAE,GAAGA,QAAO,OAAO,QAAQ;AAC1C,aAAWA,OAAM;AACjB,SAAOA;AACT;AAEO,SAAS,iBAAiBA,SAAmB,WAA6B;AAC/E,SAAO,aAAaA,QAAO,UAAU;AACvC;AAKO,SAAS,eAAe,WAA4B;AACzD,QAAM,MAAM,aAAa,QAAQ,IAAI;AACrC,QAAM,UAAUD,MAAK,KAAK,KAAK,WAAW;AAE1C,MAAI,YAAiC,CAAC;AACtC,MAAI,GAAG,WAAW,OAAO,GAAG;AAC1B,QAAI;AACF,kBAAY,KAAK,MAAM,GAAG,aAAa,SAAS,OAAO,CAAC;AAAA,IAC1D,QAAQ;AACN,kBAAY,CAAC;AAAA,IACf;AAAA,EACF;AAEA,MAAI,CAAC,UAAU,YAAY;AACzB,cAAU,aAAa,CAAC;AAAA,EAC1B;AAEA,YAAU,WAAW,cAAc,IAAI;AAAA,IACrC,SAAS;AAAA,IACT,MAAM,CAAC,MAAM,sBAAsB,OAAO;AAAA,IAC1C,KAAK;AAAA,MACH,2BAA2BA,MAAK,SAAS,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,KAAG,cAAc,SAAS,KAAK,UAAU,WAAW,MAAM,CAAC,GAAG,OAAO;AACrE,SAAO;AACT;AApGA,IAMM;AANN;AAAA;AAAA;AAGA;AAGA,IAAM,iBAA4B;AAAA,MAChC,KAAK;AAAA,QACH,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,MACA,eAAe;AAAA,QACb,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAAA;AAAA;;;AChBA,IAKa;AALb;AAAA;AAAA;AAKO,IAAM,SAAS;AAAA,MACpB,KAAK,QAAgB,MAAiB;AACpC,gBAAQ,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI;AAAA,MAChD;AAAA,MACA,KAAK,QAAgB,MAAiB;AACpC,gBAAQ,MAAM,uBAAuB,GAAG,IAAI,GAAG,IAAI;AAAA,MACrD;AAAA,MACA,MAAM,QAAgB,MAAiB;AACrC,gBAAQ,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI;AAAA,MACtD;AAAA,MACA,MAAM,QAAgB,MAAiB;AACrC,YAAI,QAAQ,IAAI,oBAAoB;AAClC,kBAAQ,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;ACpBA,OAAOE,SAAQ;AAOf,SAAS,YAAY,MAAoB;AACvC,MAAI;AACF,IAAAA,IAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAC5C,IAAAA,IAAG,eAAe,UAAU,MAAM,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAAC;AACX;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI,eAAe,MAAO,QAAO,IAAI,SAAS,GAAG,IAAI,IAAI,KAAK,IAAI,OAAO;AACzE,MAAI;AACF,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO,OAAO,GAAG;AAAA,EACnB;AACF;AAUO,SAAS,kBAAkB,SAAuB;AACvD,MAAI,SAAmB,CAAC;AAExB,QAAM,SAAS,CAAC,MAAc,QAAuB;AACnD,UAAM,QAAQ,SAAS,GAAG;AAC1B,gBAAY;AAAA,IAAM,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,IAAI,KAAK,OAAO;AAAA,EAAM,KAAK;AAAA,CAAI;AAC9E,WAAO,MAAM,GAAG,IAAI,2BAA2B,KAAK,EAAE;AAEtD,UAAM,MAAM,KAAK,IAAI;AACrB,aAAS,OAAO,OAAO,CAAC,MAAM,MAAM,IAAI,eAAe;AACvD,WAAO,KAAK,GAAG;AAEf,QAAI,OAAO,SAAS,aAAa;AAC/B,YAAM,MAAM,GAAG,OAAO,MAAM,4BAA4B,kBAAkB,GAAI;AAC9E,kBAAY,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,GAAG;AAAA,CAAI;AACpD,aAAO,MAAM,GAAG;AAChB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,UAAQ,GAAG,qBAAqB,CAAC,QAAQ,OAAO,qBAAqB,GAAG,CAAC;AACzE,UAAQ,GAAG,sBAAsB,CAAC,WAAW,OAAO,sBAAsB,MAAM,CAAC;AACnF;AAEO,SAAS,WAAW,SAAuB;AAChD,cAAY;AAAA,IAAM,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,OAAO,iBAAiB,QAAQ,GAAG;AAAA,CAAK;AACzF;AAzDA,IAIM,iBACA;AALN;AAAA;AAAA;AACA;AACA;AAEA,IAAM,kBAAkB;AACxB,IAAM,cAAc;AAAA;AAAA;;;ACAb,SAAS,gBAAgB,MAA+B;AAC7D,SAAO,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,MAAM,eAAe,KAAK,IAAI;AACnF;AAPA,IAGM;AAHN;AAAA;AAAA;AAGA,IAAM,iBAAiB;AAAA;AAAA;;;ACHvB,IAGa;AAHb;AAAA;AAAA;AACA;AAEO,IAAM,iBAAN,MAAqB;AAAA,MAClB,WAAW,oBAAI,IAAyB;AAAA,MAEhD,SAAS,SAA4B;AAEnC,cAAM,WAAW,KAAK,SAAS,OAAO;AACtC,cAAM,WAAW,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,UAClD,OAAK,EAAE,OAAO,QAAQ,MAAM,KAAK,SAAS,CAAC,MAAM;AAAA,QACnD;AACA,YAAI,SAAS,SAAS,GAAG;AAEvB,gBAAM,WAAW,SAAS,IAAI,OAAK;AACjC,kBAAM,IAAI,EAAE,aAAa,MAAM,YAAY;AAC3C,mBAAO,IAAI,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI;AAAA,UAClC,CAAC;AAED,qBAAW,KAAK,UAAU;AACxB,gBAAI,CAAC,EAAE,aAAa,MAAM,UAAU,GAAG;AACrC,gBAAE,cAAc,GAAG,QAAQ;AAAA,YAC7B;AAAA,UACF;AACA,gBAAM,UAAU,KAAK,IAAI,GAAG,UAAU,CAAC,IAAI;AAC3C,kBAAQ,cAAc,GAAG,QAAQ,KAAK,OAAO;AAAA,QAC/C,OAAO;AACL,kBAAQ,cAAc;AAAA,QACxB;AACA,aAAK,SAAS,IAAI,QAAQ,IAAI,EAAE,GAAG,SAAS,UAAU,gBAAgB,QAAQ,QAAQ,IAAI,QAAQ,WAAW,OAAU,CAAC;AAAA,MAC1H;AAAA,MAEA,WAAWC,YAA4C;AACrD,cAAM,UAAU,KAAK,SAAS,IAAIA,UAAS;AAC3C,aAAK,SAAS,OAAOA,UAAS;AAC9B,eAAO;AAAA,MACT;AAAA,MAEA,aAAaA,YAAmB,QAAgD;AAC9E,cAAM,UAAU,KAAK,SAAS,IAAIA,UAAS;AAC3C,YAAI,SAAS;AACX,kBAAQ,SAAS;AACjB,kBAAQ,eAAe,KAAK,IAAI;AAAA,QAClC;AACA,eAAO;AAAA,MACT;AAAA,MAEA,YAAYA,YAAmBC,WAAuD;AACpF,cAAM,UAAU,KAAK,SAAS,IAAID,UAAS;AAC3C,YAAI,SAAS;AACX,kBAAQ,WAAW,gBAAgBC,SAAQ,IAAIA,YAAW;AAAA,QAC5D;AACA,eAAO;AAAA,MACT;AAAA,MAEA,eAAeD,YAAyB;AACtC,cAAM,UAAU,KAAK,SAAS,IAAIA,UAAS;AAC3C,YAAI,SAAS;AACX,kBAAQ,eAAe,KAAK,IAAI;AAAA,QAClC;AAAA,MACF;AAAA,MAEA,IAAIA,YAA4C;AAC9C,eAAO,KAAK,SAAS,IAAIA,UAAS;AAAA,MACpC;AAAA,MAEA,SAAwB;AACtB,eAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,MAC1C;AAAA,MAEA,QAAgB;AACd,eAAO,KAAK,SAAS;AAAA,MACvB;AAAA,MAEQ,SAAS,SAA8B;AAC7C,eAAO,QAAQ,SAAS,QAAQ,KAAK,QAAQ,YAAY,EAAE,KAAK,QAAQ;AAAA,MAC1E;AAAA,IACF;AAAA;AAAA;;;AC7EA,OAAO,cAAc;AACrB,SAAS,gBAAgB;AADzB,IAMa;AANb;AAAA;AAAA;AAEA;AAIO,IAAM,WAAN,MAAe;AAAA,MACZ,WAA4B,CAAC;AAAA,MAC7B,iBAAiB;AAAA,MACjB,6BAA6B;AAAA,MAC7B;AAAA,MACA;AAAA,MAER,UAAU,SAAoH;AAC5H,YAAI,QAAQ,aAAc,MAAK,eAAe,QAAQ;AACtD,YAAI,QAAQ,YAAY,OAAW,MAAK,iBAAiB,QAAQ;AACjE,YAAI,QAAQ,SAAU,MAAK,WAAW,QAAQ;AAC9C,YAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AAAA,MACtD;AAAA,MAEA,MAAM,OAAO,OAAe,SAAiB,QAAqB,QAAuB;AACvF,cAAM,KAAK,kBAAkB,QAAW,QAAW,OAAO,SAAS,KAAK;AAAA,MAC1E;AAAA,MAEA,MAAM,kBAAkBE,YAA+BC,eAAkC,OAAe,SAAiB,QAAqB,QAAuB;AACnK,cAAM,WAA4B,CAAC;AAEnC,YAAI,KAAK,gBAAgB;AACvB,mBAAS,KAAK,KAAK,YAAY,OAAO,SAAS,KAAK,CAAC;AAAA,QACvD;AAEA,mBAAW,WAAW,KAAK,UAAU;AACnC,mBAAS,KAAK,KAAK,YAAY,SAAS,OAAO,SAAS,KAAK,CAAC;AAAA,QAChE;AAEA,YAAI,KAAK,eAAeD,cAAaC,eAAc;AACjD,mBAAS,KAAK,KAAK,YAAY,iBAAiBD,YAAWC,eAAc,OAAO,OAAO,CAAC;AAAA,QAC1F;AAEA,cAAM,QAAQ,WAAW,QAAQ;AAAA,MACnC;AAAA,MAEA,MAAc,YAAY,OAAe,SAAiB,QAAoC;AAC5F,YAAI,QAAQ,aAAa,SAAS;AAEhC,gBAAM,UAAU,MAAM,KAAK,0BAA0B;AACrD,cAAI,CAAC,SAAS;AACZ,iBAAK,yBAAyB;AAC9B;AAAA,UACF;AAAA,QACF;AAEA,eAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,gBAAM,eAAgB,SAAiB;AAAA,YACrC;AAAA,cACE,OAAO,iBAAiB,KAAK;AAAA,cAC7B;AAAA,cACA,OAAO;AAAA,cACP,MAAM;AAAA,YACR;AAAA,YACA,CAAC,QAAsB;AACrB,kBAAI,KAAK;AACP,uBAAO,KAAK,gCAAgC,IAAI,OAAO,EAAE;AAAA,cAC3D;AACA,sBAAQ;AAAA,YACV;AAAA,UACF;AAAA,QAIF,CAAC;AAAA,MACH;AAAA,MAEQ,4BAA8C;AACpD,eAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B;AAAA,YACE;AAAA,YACA,CAAC,YAAY,iKAAiK;AAAA,YAC9K,CAAC,KAAK,WAAW;AACf,kBAAI,KAAK;AAAE,wBAAQ,IAAI;AAAG;AAAA,cAAQ;AAClC,oBAAM,QAAQ,OAAO,KAAK;AAC1B,sBAAQ,UAAU,GAAG;AAAA,YACvB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MAEQ,2BAAiC;AACvC,YAAI,KAAK,2BAA4B;AACrC,aAAK,6BAA6B;AAElC,eAAO,KAAK,sEAAsE;AAClF,eAAO,KAAK,2DAA2D;AAEvE,YAAI,QAAQ,aAAa,SAAS;AAChC,mBAAS,cAAc,CAAC,YAAY,yCAAyC,CAAC;AAAA,QAChF;AAGA,mBAAW,MAAM;AAAE,eAAK,6BAA6B;AAAA,QAAO,GAAG,IAAI,KAAK,GAAI;AAAA,MAC9E;AAAA,MAEA,MAAc,YACZ,SACA,OACA,SACA,OACe;AACf,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,QAAQ,KAAK;AAAA,YACxC,QAAQ;AAAA,YACR,SAAS;AAAA,cACP,gBAAgB;AAAA,cAChB,GAAG,QAAQ;AAAA,YACb;AAAA,YACA,MAAM,KAAK,UAAU;AAAA,cACnB;AAAA,cACA;AAAA,cACA;AAAA,cACA,WAAW,KAAK,IAAI;AAAA,cACpB,QAAQ;AAAA,YACV,CAAC;AAAA,UACH,CAAC;AAED,cAAI,CAAC,SAAS,IAAI;AAChB,mBAAO,KAAK,WAAW,QAAQ,GAAG,aAAa,SAAS,MAAM,EAAE;AAAA,UAClE;AAAA,QACF,SAAS,KAAK;AACZ,iBAAO,KAAK,WAAW,QAAQ,GAAG,YAAa,IAAc,OAAO,EAAE;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;ACjIO,SAAS,aAAa,SAA8B;AACzD,QAAM,OAAO,QAAQ,eAAe,QAAQ,KAAK,QAAQ,YAAY,EAAE,KAAK,QAAQ;AACpF,SAAO,QAAQ,cAAc,UAAU,cAAW,IAAI,KAAK;AAC7D;AALA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,cAAcC,YAAmB,WAA2B;AAC1E,SAAO,GAAGA,UAAS;AAAA,EAAK,SAAS;AACnC;AAFA;AAAA;AAAA;AAAA;AAAA;;;ACAA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,cAAAC,mBAAkB;AAF3B,IASM,cACA,qBAsCO;AAhDb;AAAA;AAAA;AAGA;AACA;AAEA;AACA;AAEA,IAAM,eAAe;AACrB,IAAM,sBAAsB;AAsCrB,IAAM,cAAN,MAAM,aAAY;AAAA,MACf;AAAA,MACA,SAAS;AAAA,MACT,UAAU;AAAA,MACV,YAAkD;AAAA;AAAA,MAGlD,oBAAoB,oBAAI,IAAoB;AAAA;AAAA,MAG7C;AAAA;AAAA,MAEA;AAAA;AAAA,MAEA;AAAA;AAAA,MAEA;AAAA;AAAA,MAEC,eAAe,oBAAI,IAAuF;AAAA,MAC1G,iBAAiB,oBAAI,IAA2B;AAAA;AAAA,MAEjD;AAAA;AAAA,MAEC,kBAAkB,oBAAI,IAA6F;AAAA;AAAA,MAE3H,YAAYC,SAAwB;AAClC,aAAK,SAASA;AAAA,MAChB;AAAA,MAEA,IAAY,SAAiB;AAC3B,eAAO,GAAG,YAAY,GAAG,KAAK,OAAO,QAAQ;AAAA,MAC/C;AAAA;AAAA,MAGA,MAAM,iBAAiBC,YAAmB,eAAuB,OAAe,SAAgC;AAC9G,cAAM,OAAO,MAAM,KAAK,QAAQ,KAAK,CAAC;AAAA,EAAS,KAAK,SAAS,OAAO,CAAC;AACrE,cAAM,SAAS,MAAM,KAAK,YAAY,IAAI;AAC1C,YAAI,QAAQ,YAAY;AACtB,eAAK,kBAAkB,IAAI,OAAO,YAAYA,UAAS;AAEvD,cAAI,KAAK,kBAAkB,OAAO,KAAK;AACrC,kBAAM,OAAO,CAAC,GAAG,KAAK,kBAAkB,KAAK,CAAC;AAC9C,qBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,KAAK,KAAK;AAC1C,mBAAK,kBAAkB,OAAO,KAAK,CAAC,CAAC;AAAA,YACvC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA,MAAc,YAAY,MAAc,kBAA2B,aAA+D;AAChI,YAAI;AACF,gBAAM,OAAgC;AAAA,YACpC,SAAS,KAAK,OAAO;AAAA,YACrB;AAAA,YACA,YAAY;AAAA,UACd;AACA,cAAI,iBAAkB,MAAK,sBAAsB;AACjD,cAAI,YAAa,MAAK,eAAe;AAErC,gBAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,gBAAgB;AAAA,YACpD,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,UAC3B,CAAC;AAED,cAAI,CAAC,IAAI,IAAI;AACX,kBAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,mBAAO,KAAK,gCAAgC,IAAI,MAAM,IAAI,GAAG,EAAE;AAC/D,mBAAO;AAAA,UACT;AAEA,gBAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,iBAAO,KAAK,KAAK,KAAK,SAAS;AAAA,QACjC,SAAS,KAAK;AACZ,iBAAO,KAAK,+BAAgC,IAAc,OAAO,EAAE;AACnE,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA,MAGA,eAAqB;AACnB,YAAI,KAAK,QAAS;AAClB,aAAK,UAAU;AACf,eAAO,KAAK,8BAA8B;AAC1C,aAAK,KAAK;AAAA,MACZ;AAAA;AAAA,MAGA,cAAoB;AAClB,aAAK,UAAU;AACf,YAAI,KAAK,WAAW;AAClB,uBAAa,KAAK,SAAS;AAC3B,eAAK,YAAY;AAAA,QACnB;AACA,eAAO,KAAK,8BAA8B;AAAA,MAC5C;AAAA,MAEA,MAAc,OAAsB;AAClC,YAAI,CAAC,KAAK,QAAS;AAEnB,YAAI;AACF,gBAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,sBAAsB,KAAK,MAAM,eAAe;AAAA,YACpF,QAAQ,YAAY,QAAQ,IAAK;AAAA,UACnC,CAAC;AAED,cAAI,CAAC,IAAI,IAAI;AACX,mBAAO,KAAK,+BAA+B,IAAI,MAAM,EAAE;AACvD,iBAAK,iBAAiB,GAAI;AAC1B;AAAA,UACF;AAEA,gBAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,cAAI,KAAK,MAAM,KAAK,OAAO,SAAS,GAAG;AACrC,uBAAW,UAAU,KAAK,QAAQ;AAChC,mBAAK,SAAS,OAAO,YAAY;AACjC,kBAAI,OAAO,gBAAgB;AACzB,qBAAK,oBAAoB,OAAO,cAAc;AAAA,cAChD,WAAW,OAAO,SAAS;AACzB,qBAAK,sBAAsB,OAAO,OAAO;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AACZ,cAAK,IAAc,SAAS,cAAc;AACxC,mBAAO,KAAK,wBAAyB,IAAc,OAAO,EAAE;AAAA,UAC9D;AAAA,QACF;AAEA,aAAK,iBAAiB,GAAI;AAAA,MAC5B;AAAA,MAEQ,iBAAiB,OAAqB;AAC5C,YAAI,CAAC,KAAK,QAAS;AACnB,aAAK,YAAY,WAAW,MAAM,KAAK,KAAK,GAAG,KAAK;AAAA,MACtD;AAAA,MAEA,MAAc,sBAAsB,KAAqC;AAEvE,YAAI,OAAO,IAAI,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAG;AAExD,cAAM,WAAW,IAAI,SAAS,IAAI,MAAM,SAAS;AACjD,cAAM,QAAQ,IAAI,QAAQ,IAAI,WAAW,IAAI,KAAK;AAElD,YAAI,CAAC,QAAQ,CAAC,SAAU;AAGxB,YAAI,IAAI,kBAAkB;AACxB,gBAAMA,aAAY,KAAK,kBAAkB,IAAI,IAAI,iBAAiB,UAAU;AAC5E,cAAIA,YAAW;AACb,gBAAI,UAAU;AACZ,oBAAM,KAAK,sBAAsBA,YAAW,IAAI,OAAQ,IAAI;AAAA,YAC9D,OAAO;AACL,mBAAK,iBAAiBA,YAAW,IAAI;AAAA,YACvC;AACA;AAAA,UACF;AAAA,QACF;AAGA,YAAI,MAAM;AACR,gBAAM,cAAc,KAAK,MAAM,aAAa;AAC5C,cAAI,aAAa;AACf,kBAAM,UAAU,KAAK,gBAAgB,IAAI,IAAI,KAAK,EAAE;AACpD,gBAAI,SAAS;AACX,mBAAK,gBAAgB,OAAO,IAAI,KAAK,EAAE;AACvC,oBAAM,UAAU,KAAK,eAAe,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC;AAC7E,kBAAI,SAAS;AACX,oBAAI,QAAQ,aAAa;AACvB,wBAAM,KAAK,8BAA8B,QAAQ,IAAI,QAAQ,aAAa,QAAQ,OAAO;AAAA,gBAC3F,WAAW,QAAQ,MAAM;AACvB,uBAAK,iBAAiB,QAAQ,IAAI,QAAQ,IAAI;AAAA,gBAChD;AACA,qBAAK,YAAY,YAAY,KAAK,SAAS,OAAO,CAAC,GAAG;AAAA,cACxD,OAAO;AACL,qBAAK,YAAY,yBAAyB;AAAA,cAC5C;AACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,WAAW,KAAK,cAAc,KAAK,CAAC;AAE1C,YAAI,SAAS,WAAW,GAAG;AACzB,eAAK,YAAY,+BAA+B;AAChD;AAAA,QACF;AAEA,YAAI,SAAS,WAAW,GAAG;AACzB,cAAI,UAAU;AACZ,kBAAM,KAAK,sBAAsB,SAAS,CAAC,EAAE,IAAI,IAAI,OAAQ,IAAI;AAAA,UACnE,OAAO;AACL,iBAAK,iBAAiB,SAAS,CAAC,EAAE,IAAI,IAAI;AAAA,UAC5C;AACA;AAAA,QACF;AAGA,cAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE;AAC3C,YAAI,UAAU;AACZ,gBAAM,UAAU,IAAI,MAAO,IAAI,MAAO,SAAS,CAAC;AAChD,eAAK,gBAAgB,IAAI,IAAI,KAAK,IAAI,EAAE,aAAa,QAAQ,SAAS,SAAS,MAAM,WAAW,CAAC;AAAA,QACnG,OAAO;AACL,eAAK,gBAAgB,IAAI,IAAI,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAAA,QAC5D;AACA,cAAM,UAAU,SAAS,IAAI,CAAC,GAAG,OAAO;AAAA,UACtC,MAAM,KAAK,SAAS,CAAC;AAAA,UACrB,eAAe,QAAQ,CAAC,IAAI,IAAI,KAAK,EAAE;AAAA,QACzC,EAAE;AAEF,cAAM,OAA8B,CAAC;AACrC,iBAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,eAAK,KAAK,QAAQ,MAAM,GAAG,IAAI,CAAC,CAAC;AAAA,QACnC;AACA,aAAK,YAAY,yCAAyC,QAAW,EAAE,iBAAiB,KAAK,CAAC;AAAA,MAChG;AAAA,MAEQ,iBAAiBA,YAAmB,SAAuB;AACjE,YAAI,KAAK,oBAAoB;AAC3B,eAAK,mBAAmBA,YAAW,OAAO;AAAA,QAC5C;AAAA,MACF;AAAA,MAEA,MAAc,sBAAsBA,YAAmB,QAA6B,SAAiC;AAEnH,cAAM,UAAU,OAAO,OAAO,SAAS,CAAC;AACxC,cAAM,KAAK,8BAA8BA,YAAW,QAAQ,SAAS,OAAO;AAAA,MAC9E;AAAA,MAEA,MAAc,8BAA8BA,YAAmB,QAAgB,SAAiC;AAC9G,YAAI;AAEF,gBAAM,UAAU,MAAM,MAAM,GAAG,KAAK,MAAM,oBAAoB,MAAM,EAAE;AACtE,cAAI,CAAC,QAAQ,IAAI;AAAE,mBAAO,KAAK,kCAAkC;AAAG;AAAA,UAAQ;AAC5E,gBAAM,WAAW,MAAM,QAAQ,KAAK;AACpC,cAAI,CAAC,SAAS,GAAI;AAGlB,gBAAM,cAAc,oCAAoC,KAAK,OAAO,QAAQ,IAAI,SAAS,OAAO,SAAS;AACzG,gBAAM,SAAS,MAAM,MAAM,WAAW;AACtC,cAAI,CAAC,OAAO,IAAI;AAAE,mBAAO,KAAK,mCAAmC;AAAG;AAAA,UAAQ;AAC5E,gBAAM,SAAS,OAAO,KAAK,MAAM,OAAO,YAAY,CAAC;AAGrD,gBAAM,MAAM,SAAS,OAAO,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK;AAC1D,gBAAM,WAAW,QAAQ,QAAQ,cAAc,QAAQ,QAAQ,cAAc,QAAQ,SAAS,eAAe;AAG7G,UAAAJ,IAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC7C,gBAAM,WAAW,GAAGE,YAAW,CAAC,IAAI,GAAG;AACvC,gBAAM,WAAWD,MAAK,KAAK,aAAa,QAAQ;AAChD,UAAAD,IAAG,cAAc,UAAU,MAAM;AACjC,iBAAO,KAAK,yBAAyB,QAAQ,KAAK,OAAO,MAAM,SAAS;AAGxE,cAAI,KAAK,kBAAkB;AACzB,iBAAK,iBAAiBI,YAAW,UAAU,UAAU,OAAO;AAAA,UAC9D;AAGA,qBAAW,MAAM;AAAE,gBAAI;AAAE,cAAAJ,IAAG,WAAW,QAAQ;AAAA,YAAG,QAAQ;AAAA,YAAC;AAAA,UAAE,GAAG,IAAI,KAAK,GAAI;AAAA,QAC/E,SAAS,KAAK;AACZ,iBAAO,KAAK,mCAAoC,IAAc,OAAO,EAAE;AAAA,QACzE;AAAA,MACF;AAAA;AAAA,MAGQ,eAAe,SAAmC,KAAsC;AAC9F,cAAM,KAAK,QAAQ,WAAW,GAAG;AACjC,eAAO,KAAK,KAAK,cAAc,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI;AAAA,MAC9D;AAAA,MAEQ,SAAS,SAA8B;AAC7C,eAAO,aAAa,OAAO;AAAA,MAC7B;AAAA;AAAA,MAGA,MAAM,sBAAsBI,YAAmBC,eAAsB,WAAmB,UAAkB,aAAqB,cAAqC;AAKlK,cAAM,WAAW,SAAS,SAAS,UAAU,KAAK,aAAa;AAC/D,YAAI,UAAU;AACd,YAAI,OAAyB;AAC7B,YAAI,OAAO;AACX,YAAI,YAAY;AAChB,YAAI;AACF,gBAAM,IAAI,KAAK,MAAM,YAAY;AACjC,cAAI,EAAE,SAAS;AAAE,sBAAU,KAAK,EAAE,OAAO;AAAI,mBAAO;AAAA,UAAQ,WACnD,EAAE,SAAS,EAAE,SAAS;AAAE,sBAAU,EAAE;AAAS,mBAAO;AAAA,UAAS,WAC7D,EAAE,WAAW;AACpB,sBAAU,EAAE;AACZ,mBAAO,KAAK,OAAO,EAAE,SAAS;AAC9B,gBAAI,EAAE,SAAS;AAAE,yBAAW,OAAO,EAAE,QAAQ,MAAM,GAAG,GAAI;AAAG,kBAAI,EAAE,QAAQ,SAAS,IAAK,aAAY;AAAA,YAAM;AAAA,UAC7G,WAAW,EAAE,WAAW,OAAO,EAAE,YAAY,UAAU;AACrD,sBAAU,EAAE,QAAQ,MAAM,GAAG,GAAI;AACjC,gBAAI,SAAU,QAAO;AACrB,gBAAI,EAAE,QAAQ,SAAS,IAAK,aAAY;AAAA,UAC1C;AAAA,QACF,QAAQ;AACN,sBAAY;AACZ,gBAAM,WAAW,aAAa,MAAM,qCAAqC;AACzE,gBAAM,eAAe,aAAa,MAAM,qCAAqC;AAC7E,cAAI,UAAU;AAAE,sBAAU,KAAK,SAAS,CAAC,CAAC;AAAI,mBAAO;AAAA,UAAQ,WACpD,aAAc,WAAU,aAAa,CAAC,EAAE,MAAM,GAAG,GAAI;AAAA,QAChE;AAEA,cAAM,YAAY,YAAY,4BAA4B;AAC1D,cAAM,eAAe,QAAQ,MAAM,GAAG,GAAI;AAE1C,cAAM,UAAU,CAAC,aAAa,SAAS,IAAI,KAAK,aAAa,SAAS;AACtE,cAAM,WAAW,OAAO,oBAAoB,IAAI,MAAM;AACtD,cAAM,cAAc,SAAS,UACzB,KAAK,SAAS,YAAY,IAC1B,UACE,SAAS,KAAK,QAAQ,YAAY,CAAC,YACnC,aAAa,QAAQ,IAAI,KAAK,QAAQ,YAAY,CAAC;AAEzD,YAAI,cAAc;AAClB,aAAK,SAAS,SAAS,UAAU,KAAK,aAAa,aAAa,cAAc;AAC5E,cAAI;AAAE,kBAAM,KAAK,KAAK,MAAM,YAAY;AAAG,gBAAI,GAAG,MAAO,eAAc,GAAG;AAAA,UAAO,QAAQ;AACvF,kBAAM,KAAK,aAAa,MAAM,mCAAmC;AACjE,gBAAI,GAAI,eAAc,GAAG,CAAC;AAAA,UAC5B;AAAA,QACF,OAAO;AACL,gBAAM,WAAW,SAAS,MAAM,YAAY;AAC5C,cAAI,SAAU,eAAc,SAAS,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,SAAS,CAAC,EAAE,MAAM,CAAC;AAAA,QACvF;AAEA,cAAM,OAAO,iDAAkC,KAAK,QAAQA,aAAY,CAAC;AAAA;AAAA,eAC9D,KAAK,QAAQ,WAAW,CAAC;AAAA,EAC/B,WAAW,GAAG,SAAS;AAE5B,cAAM,cAAc;AAAA,UAClB,iBAAiB,CAAC;AAAA,YAChB,EAAE,MAAM,gBAAW,eAAe,cAAcD,UAAS,IAAI,SAAS,GAAG;AAAA,YACzE,EAAE,MAAM,eAAU,eAAe,aAAaA,UAAS,IAAI,SAAS,GAAG;AAAA,UACzE,CAAC;AAAA,QACH;AAEA,cAAM,KAAK,YAAY,MAAM,QAAW,WAAW;AAAA,MACrD;AAAA,MAEA,MAAM,kBACJA,YACAC,eACA,WACA,UACA,aACA,cACA,SACe;AACf,YAAI,UAAU;AACd,YAAI;AACF,gBAAM,IAAI,KAAK,MAAM,YAAY;AACjC,cAAI,OAAO,EAAE,YAAY,SAAU,WAAU,KAAK,EAAE,OAAO;AAAA,mBAClD,OAAO,EAAE,YAAY,SAAU,WAAU,EAAE;AAAA,QACtD,QAAQ;AAAA,QAAC;AACT,cAAM,QAAQ,QAAQ,MAAM,GAAG,GAAI;AACnC,cAAM,OACJ,iDAAkC,KAAK,QAAQA,aAAY,CAAC;AAAA;AAAA,eACnD,KAAK,QAAQ,QAAQ,CAAC,UAC9B,cAAc;AAAA,EAAK,KAAK,QAAQ,WAAW,CAAC,KAAK,OACjD,QAAQ;AAAA,OAAU,KAAK,QAAQ,KAAK,CAAC,WAAW,OAChD,QAAQ,SAAS,MAAM,SAAS,0BAA0B;AAE7D,cAAM,QAAuB,EAAE,KAAK;AACpC,aAAK,eAAe,IAAI,cAAcD,YAAW,SAAS,GAAG,KAAK;AAClE,cAAM,OAAO,QAAQ,IAAI,CAAC,MAAM;AAC9B,gBAAM,QAAQF,YAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE;AACxD,eAAK,aAAa,IAAI,OAAO,EAAE,WAAAE,YAAW,WAAW,UAAU,EAAE,IAAI,OAAO,EAAE,MAAM,CAAC;AACrF,iBAAO,CAAC,EAAE,MAAM,EAAE,OAAO,eAAe,MAAM,KAAK,GAAG,CAAC;AAAA,QACzD,CAAC;AACD,aAAK,mBAAmB;AAExB,cAAM,OAAO,MAAM,KAAK,YAAY,MAAM,QAAW,EAAE,iBAAiB,KAAK,CAAC;AAC9E,YAAI,CAAC,KAAM;AACX,cAAM,YAAY,KAAK;AACvB,YAAI,MAAM,QAAS,OAAM,KAAK,gBAAgB,KAAK,OAAO,QAAQ,KAAK,YAAY,OAAO,MAAM,OAAO;AAAA,MACzG;AAAA,MAEA,MAAM,qBAAqBA,YAAmB,WAAmB,OAA8C;AAC7G,aAAK,iBAAiBA,YAAW,SAAS;AAC1C,cAAM,MAAM,cAAcA,YAAW,SAAS;AAC9C,cAAM,QAAQ,KAAK,eAAe,IAAI,GAAG;AACzC,YAAI,CAAC,MAAO;AACZ,aAAK,eAAe,OAAO,GAAG;AAC9B,cAAM,UAAU,UAAU,aAAa,+BAA0B;AACjE,YAAI,MAAM,cAAc,OAAW,OAAM,KAAK,gBAAgB,KAAK,OAAO,QAAQ,MAAM,WAAW,MAAM,OAAO,MAAM,OAAO;AAAA,MAC/H;AAAA,MAEA,MAAc,qBAAqB,OAA6C;AAC9E,cAAM,SAAS,KAAK,aAAa,IAAI,MAAM,KAAM,MAAM,MAAM,MAAM,CAAC;AACpE,YAAI,CAAC,QAAQ;AACX,gBAAM,KAAK,oBAAoB,MAAM,IAAI,SAAS;AAClD;AAAA,QACF;AACA,aAAK,iBAAiB,OAAO,WAAW,OAAO,SAAS;AACxD,aAAK,kBAAkB,OAAO,WAAW,OAAO,WAAW,OAAO,QAAQ;AAC1E,cAAM,KAAK,oBAAoB,MAAM,IAAI,SAAS,OAAO,KAAK,EAAE;AAChE,cAAM,QAAQ,KAAK,eAAe,IAAI,cAAc,OAAO,WAAW,OAAO,SAAS,CAAC;AACvF,YAAI,OAAO,cAAc,UAAa,CAAC,MAAM,SAAS;AACpD,gBAAM,KAAK,gBAAgB,KAAK,OAAO,QAAQ,MAAM,WAAW,GAAG,MAAM,IAAI;AAAA;AAAA,kBAAkB,KAAK,QAAQ,OAAO,KAAK,CAAC,MAAM;AAAA,QACjI;AAAA,MACF;AAAA,MAEQ,iBAAiBA,YAAmB,WAAyB;AACnE,mBAAW,CAAC,OAAO,CAAC,KAAK,KAAK,cAAc;AAC1C,cAAI,EAAE,cAAcA,cAAa,EAAE,cAAc,UAAW,MAAK,aAAa,OAAO,KAAK;AAAA,QAC5F;AAAA,MACF;AAAA,MAEQ,qBAA2B;AACjC,eAAO,KAAK,eAAe,OAAO,qBAAqB;AACrD,gBAAM,SAAS,KAAK,eAAe,KAAK,EAAE,KAAK,EAAE;AACjD,eAAK,eAAe,OAAO,MAAM;AACjC,gBAAM,CAACA,YAAW,SAAS,IAAI,OAAO,MAAM,IAAI;AAChD,eAAK,iBAAiBA,YAAW,SAAS;AAAA,QAC5C;AAAA,MACF;AAAA,MAEA,MAAc,oBAAoB,OAA6C;AAC7E,YAAI,CAAC,MAAM,KAAM;AACjB,YAAI,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAG;AAEnE,YAAI,MAAM,KAAK,WAAW,OAAO,GAAG;AAClC,gBAAM,KAAK,4BAA4B,KAAK;AAC5C;AAAA,QACF;AAEA,YAAI,MAAM,KAAK,WAAW,KAAK,GAAG;AAChC,gBAAM,KAAK,qBAAqB,KAAK;AACrC;AAAA,QACF;AAEA,YAAI,CAAC,MAAM,KAAK,WAAW,OAAO,EAAG;AAErC,cAAM,QAAQ,MAAM,KAAK,MAAM,GAAG;AAClC,YAAI,MAAM,SAAS,EAAG;AACtB,cAAM,CAAC,EAAE,QAAQA,YAAW,SAAS,IAAI;AACzC,cAAM,WAAW,WAAW,UAAU,UAAU;AAGhD,YAAI,KAAK,qBAAqB;AAC5B,eAAK,oBAAoBA,YAAW,WAAW,QAA4B;AAAA,QAC7E;AAGA,cAAM,KAAK,oBAAoB,MAAM,IAAI,aAAa,UAAU,mBAAc,eAAU;AAGxF,YAAI,MAAM,SAAS;AACjB,gBAAM,QAAQ,aAAa,UAAU,0BAAqB;AAC1D,gBAAM,WAAW,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE;AACtD,gBAAM,KAAK,gBAAgB,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,YAAY,WAAW;AAAA;AAAA,EAAO,KAAK,EAAE;AAAA,QACvG;AAAA,MACF;AAAA,MAEA,MAAc,4BAA4B,OAA6C;AACrF,cAAM,QAAQ,MAAM,KAAM,MAAM,GAAG;AACnC,YAAI,MAAM,SAAS,EAAG;AACtB,cAAM,CAAC,EAAE,QAAQ,SAAS,IAAI;AAC9B,cAAM,SAAS,SAAS,WAAW,EAAE;AAErC,cAAM,UAAU,KAAK,gBAAgB,IAAI,MAAM;AAC/C,cAAM,UAAU,UAAU,KAAK,eAAe,SAAS,SAAS,QAAQ,EAAE,CAAC,IAAI;AAC/E,YAAI,CAAC,WAAW,CAAC,SAAS;AACxB,gBAAM,KAAK,oBAAoB,MAAM,IAAI,mBAAmB;AAC5D;AAAA,QACF;AACA,aAAK,gBAAgB,OAAO,MAAM;AAElC,YAAI,QAAQ,aAAa;AACvB,gBAAM,KAAK,8BAA8B,QAAQ,IAAI,QAAQ,aAAa,QAAQ,OAAO;AAAA,QAC3F,WAAW,QAAQ,MAAM;AACvB,eAAK,iBAAiB,QAAQ,IAAI,QAAQ,IAAI;AAAA,QAChD;AAEA,cAAM,KAAK,oBAAoB,MAAM,IAAI,WAAW,KAAK,SAAS,OAAO,CAAC,EAAE;AAE5E,YAAI,MAAM,SAAS;AACjB,gBAAM,KAAK,gBAAgB,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,YAAY,qBAAgB,KAAK,QAAQ,KAAK,SAAS,OAAO,CAAC,CAAC,MAAM;AAAA,QACxI;AAAA,MACF;AAAA,MAEA,MAAc,oBAAoB,iBAAyB,MAA6B;AACtF,YAAI;AACF,gBAAM,MAAM,GAAG,KAAK,MAAM,wBAAwB;AAAA,YAChD,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,EAAE,mBAAmB,iBAAiB,KAAK,CAAC;AAAA,UACnE,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,iBAAO,KAAK,uCAAwC,IAAc,OAAO,EAAE;AAAA,QAC7E;AAAA,MACF;AAAA,MAEA,MAAc,gBAAgB,QAAyB,WAAmB,MAA6B;AACrG,YAAI;AACF,gBAAM,MAAM,GAAG,KAAK,MAAM,oBAAoB;AAAA,YAC5C,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,QAAQ,YAAY,WAAW,MAAM,YAAY,OAAO,CAAC;AAAA,UAC3F,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,iBAAO,KAAK,mCAAoC,IAAc,OAAO,EAAE;AAAA,QACzE;AAAA,MACF;AAAA,MAEQ,QAAQ,GAAmB;AACjC,eAAO,EAAE,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAAA,MAC5E;AAAA,MAEA,OAAwB,cAAsC;AAAA,QAC5D,IAAI;AAAA,QAAc,KAAK;AAAA,QAAc,KAAK;AAAA,QAAc,KAAK;AAAA,QAC7D,IAAI;AAAA,QAAc,KAAK;AAAA,QAAc,IAAI;AAAA,QAAU,IAAI;AAAA,QAAQ,IAAI;AAAA,QACnE,MAAM;AAAA,QAAQ,IAAI;AAAA,QAAU,IAAI;AAAA,QAAQ,KAAK;AAAA,QAAO,IAAI;AAAA,QACxD,IAAI;AAAA,QAAQ,MAAM;AAAA,QAAQ,KAAK;AAAA,QAAQ,KAAK;AAAA,QAC5C,MAAM;AAAA,QAAQ,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAQ,KAAK;AAAA,QAAO,KAAK;AAAA,QAC1D,IAAI;AAAA,QAAY,MAAM;AAAA,QAAQ,KAAK;AAAA,QAAQ,KAAK;AAAA,QAAO,MAAM;AAAA,MAC/D;AAAA,MAEQ,OAAOH,QAAsB;AACnC,cAAM,IAAI,kBAAkB,KAAKA,UAAQ,EAAE;AAC3C,eAAO,IAAK,aAAY,YAAY,EAAE,CAAC,EAAE,YAAY,CAAC,KAAK,KAAM;AAAA,MACnE;AAAA;AAAA,MAGQ,SAAS,GAAmB;AAElC,YAAI,OAAO,EAAE;AAAA,UACX;AAAA,UACA,CAAC,QAAQ,QAAgB,SAAiB;AACxC,kBAAM,cAAc,OAAO,MAAM,GAAG,EAAE,OAAO,CAAC,MAAc,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AACjG,kBAAM,aAAa,YAAY,KAAK,KAAK;AACzC,kBAAM,YAAY,KAAK,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,QAAgB;AAC7D,qBAAO,IAAI,MAAM,GAAG,EAAE,OAAO,CAAC,MAAc,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC,EAAE,KAAK,KAAK;AAAA,YAC/F,CAAC;AACD,mBAAO,KAAK,UAAU;AAAA,EAAO,UAAU,KAAK,IAAI,CAAC;AAAA,UACnD;AAAA,QACF;AAGA,cAAM,SAAmB,CAAC;AAC1B,cAAM,cAAc,CAAC,MAAc,SAAa,CAAC;AACjD,eAAO,KAAK,QAAQ,+BAA+B,CAAC,IAAI,SAAiB;AACvE,gBAAM,IAAI,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,IAAI;AAC5D,iBAAO,YAAY,CAAC;AAAA,QACtB,CAAC;AACD,eAAO,KAAK,QAAQ,gBAAgB,CAAC,IAAI,SAAiB;AACxD,gBAAM,IAAI,OAAO,KAAK,SAAS,KAAK,QAAQ,IAAI,CAAC,SAAS,IAAI;AAC9D,iBAAO,YAAY,CAAC;AAAA,QACtB,CAAC;AAED,YAAI,OAAO,KAAK,QAAQ,IAAI;AAE5B,eAAO,KAAK,QAAQ,qBAAqB,WAAW;AAEpD,eAAO,KAAK,QAAQ,kBAAkB,WAAW;AAEjD,eAAO,KAAK,QAAQ,kBAAkB,WAAW;AAGjD,eAAO,KAAK,QAAQ,0BAA0B,CAAC,IAAI,MAAc,OAAO,OAAO,CAAC,CAAC,KAAK,EAAE;AACxF,eAAO;AAAA,MACT;AAAA;AAAA,MAGA,aAAaE,SAA8B;AACzC,cAAM,aAAa,KAAK;AACxB,YAAI,WAAY,MAAK,YAAY;AACjC,aAAK,SAASA;AACd,YAAI,cAAcA,QAAO,QAAS,MAAK,aAAa;AAAA,MACtD;AAAA,IACF;AAAA;AAAA;;;ACjnBA,SAAS,SAAAG,cAAmD;AAC5D,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAQV,SAAS,qBAAqB,SAAqC;AACxE,SAAO,CAACA,MAAK,KAAK,SAAS,MAAM,SAAS,SAAS,GAAGA,MAAK,KAAK,SAAS,SAAS,SAAS,CAAC,EAAE,KAAK,CAAC,MAAMD,IAAG,WAAW,CAAC,CAAC;AAC5H;AAZA,IAOM,gBAOO;AAdb;AAAA;AAAA;AAGA;AAIA,IAAM,iBAAiB;AAOhB,IAAM,kBAAN,MAAsB;AAAA,MAM3B,YACU,QACA,UAA2BD,QAC3B,aAAa,KACb,aAAa,KACb,cAAc,KACtB;AALQ;AACA;AACA;AACA;AACA;AAER,aAAK,QAAQ;AAAA,MACf;AAAA,MAbQ;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MAYR,QAAc;AACZ,aAAK,UAAU;AACf,aAAK,OAAO;AAAA,MACd;AAAA,MAEA,OAAa;AACX,aAAK,UAAU;AACf,YAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,aAAK,QAAQ;AACb,cAAM,QAAQ,KAAK;AACnB,aAAK,QAAQ;AACb,YAAI,CAAC,MAAO;AACZ,cAAM,OAAO,IAAI;AACjB,cAAM,QAAQ,WAAW,MAAM,MAAM,KAAK,GAAG,KAAK,WAAW;AAC7D,cAAM,MAAM;AACZ,cAAM,KAAK,QAAQ,MAAM,aAAa,KAAK,CAAC;AAAA,MAC9C;AAAA,MAEQ,SAAe;AACrB,cAAM,YAAY,KAAK,IAAI;AAE3B,cAAM,QAAQ,KAAK,QAAQ,QAAQ,UAAU,CAAC,KAAK,QAAQ,eAAe,GAAG;AAAA,UAC3E,UAAU;AAAA,UACV,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,UAC9B,aAAa;AAAA,QACf,CAAC;AACD,aAAK,QAAQ;AACb,cAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AACvD,cAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AACvD,cAAM,OAAO,GAAG,SAAS,MAAM;AAAA,QAAC,CAAC;AACjC,cAAM,GAAG,SAAS,CAAC,QAAQ,OAAO,KAAK,kCAAkC,IAAI,OAAO,EAAE,CAAC;AACvF,cAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACjC,cAAI,KAAK,UAAU,MAAO,MAAK,QAAQ;AACvC,cAAI,KAAK,WAAW,SAAS,EAAG;AAChC,cAAI,KAAK,IAAI,IAAI,YAAY,eAAgB,MAAK,QAAQ,KAAK;AAC/D,iBAAO,KAAK,yBAAyB,UAAU,IAAI,oBAAoB,KAAK,KAAK,IAAI;AACrF,eAAK,QAAQ,WAAW,MAAM;AAC5B,iBAAK,QAAQ;AACb,gBAAI,CAAC,KAAK,QAAS,MAAK,OAAO;AAAA,UACjC,GAAG,KAAK,KAAK;AACb,eAAK,QAAQ,KAAK,IAAI,KAAK,QAAQ,GAAG,KAAK,UAAU;AAAA,QACvD,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA;;;ACzEA,OAAO,YAAY;AAQZ,SAAS,UAAU,GAA8B,GAAoB;AAC1E,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAM,IAAI,OAAO,KAAK,CAAC;AACvB,QAAM,IAAI,OAAO,KAAK,CAAC;AACvB,SAAO,EAAE,WAAW,EAAE,UAAU,OAAO,gBAAgB,GAAG,CAAC;AAC7D;AAEO,SAAS,qBAAqB,OAAuB;AAC1D,SAAO,OAAO,WAAW,UAAU,KAAK,EAAE,OAAO,wBAAwB,EAAE,OAAO,KAAK;AACzF;AAEO,SAAS,aAAa,QAAoD;AAC/E,QAAM,MAA8B,CAAC;AACrC,aAAW,SAAS,UAAU,IAAI,MAAM,GAAG,GAAG;AAC5C,UAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,QAAI,KAAK,EAAG;AACZ,UAAM,MAAM,KAAK,MAAM,IAAI,CAAC,EAAE,KAAK;AACnC,QAAI,QAAQ;AACZ,QAAI;AAAE,cAAQ,mBAAmB,GAAG;AAAA,IAAG,QAAQ;AAAA,IAAC;AAChD,QAAI,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI;AAAA,EACjC;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,OAAe,QAAyB;AAC1E,SAAO,GAAG,cAAc,IAAI,qBAAqB,KAAK,CAAC,gDAAgD,cAAc,GAAG,SAAS,aAAa,EAAE;AAClJ;AAEO,SAAS,aAAa,KAAkB,OAAe,iBAAmC;AAC/F,QAAM,OAAO,IAAI,QAAQ;AACzB,MAAI,MAAM,WAAW,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,KAAK,EAAG,QAAO;AAC3E,MAAI,UAAU,aAAa,IAAI,QAAQ,MAAM,EAAE,cAAc,GAAG,qBAAqB,KAAK,CAAC,EAAG,QAAO;AACrG,MAAI,iBAAiB;AACnB,UAAM,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,YAAY,EAAE,aAAa,IAAI,OAAO;AACxE,QAAI,UAAU,GAAG,KAAK,EAAG,QAAO;AAAA,EAClC;AACA,SAAO;AACT;AAEO,SAAS,cAAc,KAA2B;AACvD,QAAM,SAAS,IAAI,QAAQ;AAC3B,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,WAAO,IAAI,IAAI,MAAM,EAAE,SAAS,IAAI,QAAQ;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,KAA2B;AACvD,SAAO,OAAO,IAAI,QAAQ,cAAc,KAAK,EAAE,EAAE,YAAY,EAAE,WAAW,kBAAkB;AAC9F;AAEO,SAAS,gBAAgB,KAA2B;AACzD,SAAO,IAAI,QAAQ,mBAAmB,MAAM;AAC9C;AA/DA,IAGa,gBACP;AAJN;AAAA;AAAA;AAGO,IAAM,iBAAiB;AAC9B,IAAM,iBAAiB,KAAK,KAAK,KAAK;AAAA;AAAA;;;ACJtC;AAAA;AAAA;AAAA;AAAA,OAAO,UAAU;AAEjB,OAAOG,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB,aAAAC,kBAAiB;AAE3C,SAAS,cAAAC,mBAAkB;AA+B3B,SAAS,aAAa,SAAkD;AACtE,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAA6B,OAAO,GAAG,OAAO,YAAY,OAAO,GAAG,UAAU,QAAQ;AACpH,SAAO,MAAM,SAAS,QAAQ;AAChC;AA1CA,IAiCM,WAGA,gBAQO;AA5Cb;AAAA;AAAA;AAMA;AAEA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AASA,IAAM,YAAYF,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG7D,IAAM,iBAAiB,KAAK,OAAO;AAQ5B,IAAM,YAAN,MAAgB;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,IAAI,eAAe;AAAA,MAC9B,WAAW,IAAI,SAAS;AAAA,MACxB,YAAY,KAAK,IAAI;AAAA;AAAA,MAGrB,iBAAiB,oBAAI,IAAuB;AAAA;AAAA,MAE5C,gBAAgB,oBAAI,IAAY;AAAA;AAAA,MAEhC,mBAAmB,oBAAI,IAAe;AAAA,MAEtC;AAAA,MACA;AAAA,MACA,eAAe,oBAAI,IAAqB;AAAA;AAAA,MACxC,eAAe,oBAAI,QAA2B;AAAA;AAAA,MAE9C,iBAAiB,oBAAI,IAAuE;AAAA,MAE5F;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAER,YAAYG,SAA6B;AACvC,aAAK,OAAOA,SAAQ,KAAK,QAAQ;AACjC,aAAK,OAAOA,SAAQ,KAAK,QAAQ;AACjC,aAAK,QAAQA,SAAQ,KAAK;AAC1B,aAAK,eAAeA,SAAQ,OAAO,YAAY;AAE/C,YAAIA,SAAQ,eAAe;AACzB,eAAK,SAAS,UAAU;AAAA,YACtB,SAASA,QAAO,cAAc;AAAA,UAChC,CAAC;AAAA,QACH;AACA,YAAIA,SAAQ,UAAU;AACpB,eAAK,SAAS,UAAU,EAAE,UAAUA,QAAO,SAAS,CAAC;AAAA,QACvD;AACA,cAAM,cAAc,KAAK,SAAS,YAAY,cAAc,KAAK;AACjE,aAAK,SAAS,UAAU,EAAE,cAAc,UAAU,WAAW,IAAI,KAAK,IAAI,GAAG,CAAC;AAG9E,cAAM,aAAa,WAAW;AAC9B,YAAI,WAAW,UAAU,WAAW,WAAW,SAAS,YAAY,WAAW,SAAS,QAAQ;AAC9F,eAAK,aAAa,WAAW,QAAQ;AAAA,QACvC;AAGA,aAAK,aAAa,KAAK,aAAa,CAAC,KAAK,QAAQ,KAAK,WAAW,KAAK,GAAG,CAAC;AAG3E,aAAK,aAAa,IAAI,gBAAgB,EAAE,UAAU,MAAM,YAAY,eAAe,CAAC;AACpF,aAAK,WAAW,GAAG,cAAc,CAAC,IAAe,QAA8B,KAAK,wBAAwB,IAAI,GAAG,CAAC;AACpH,aAAK,WAAW,GAAG,SAAS,CAAC,QAAQ,OAAO,KAAK,mCAAmC,IAAI,OAAO,EAAE,CAAC;AAGlG,aAAK,eAAe,IAAI,gBAAgB,EAAE,UAAU,MAAM,YAAY,eAAe,CAAC;AACtF,aAAK,aAAa,GAAG,cAAc,CAAC,OAAO,KAAK,0BAA0B,EAAE,CAAC;AAC7E,aAAK,aAAa,GAAG,SAAS,CAAC,QAAQ,OAAO,KAAK,qCAAqC,IAAI,OAAO,EAAE,CAAC;AAGtG,aAAK,WAAW,GAAG,WAAW,CAAC,KAAK,QAAQ,SAAS;AAGnD,iBAAO,GAAG,SAAS,CAAC,QAAQ,OAAO,MAAM,yBAAyB,IAAI,OAAO,EAAE,CAAC;AAEhF,gBAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,YAAY;AAChD,gBAAM,WAAW,IAAI;AAErB,cAAI,cAAc,GAAG,GAAG;AACtB,iBAAK,cAAc,QAAQ,KAAK,aAAa,UAAU,GAAG;AAC1D;AAAA,UACF;AACA,cAAI,CAAC,KAAK,WAAW,KAAK,IAAI,GAAG;AAC/B,iBAAK,cAAc,QAAQ,KAAK,gBAAgB,UAAU,GAAG;AAC7D;AAAA,UACF;AAEA,cAAI,aAAa,iBAAiB;AAChC,iBAAK,WAAW,cAAc,KAAK,QAAQ,MAAM,CAAC,OAAO;AACvD,mBAAK,WAAW,KAAK,cAAc,IAAI,GAAG;AAAA,YAC5C,CAAC;AAAA,UACH,WAAW,aAAa,mBAAmB;AACzC,iBAAK,aAAa,cAAc,KAAK,QAAQ,MAAM,CAAC,OAAO;AACzD,mBAAK,aAAa,KAAK,cAAc,IAAI,GAAG;AAAA,YAC9C,CAAC;AAAA,UACH,OAAO;AACL,mBAAO,QAAQ;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MAEA,MAAM,QAAuB;AAC3B,aAAK,eAAe;AACpB,aAAK,eAAe;AACpB,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,iBAAiB,CAAC,QAAe,OAAO,GAAG;AACjD,eAAK,WAAW,KAAK,SAAS,cAAc;AAC5C,eAAK,WAAW,OAAO,KAAK,MAAM,KAAK,MAAM,MAAM;AAEjD,iBAAK,WAAW,eAAe,SAAS,cAAc;AACtD,iBAAK,WAAW,GAAG,SAAS,CAAC,QAAQ,OAAO,MAAM,sBAAsB,IAAI,OAAO,EAAE,CAAC;AACtF,kBAAM,cAAc,KAAK,SAAS,YAAY,cAAc,KAAK;AACjE,mBAAO,KAAK,kCAAkC,WAAW,IAAI,KAAK,IAAI,EAAE;AACxE,iBAAK,kBAAkB;AACvB,oBAAQ;AAAA,UACV,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,MAEA,OAAsB;AACpB,eAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAK,iBAAiB,KAAK;AAC3B,eAAK,kBAAkB;AAEvB,cAAI,KAAK,kBAAmB,eAAc,KAAK,iBAAiB;AAGhE,cAAI,KAAK,YAAa,MAAK,YAAY,YAAY;AAGnD,qBAAW,MAAM,KAAK,eAAe,OAAO,EAAG,IAAG,UAAU;AAC5D,qBAAW,MAAM,KAAK,iBAAkB,IAAG,UAAU;AACrD,eAAK,eAAe,MAAM;AAC1B,eAAK,iBAAiB,MAAM;AAE5B,eAAK,WAAW,MAAM;AACtB,eAAK,aAAa,MAAM;AACxB,eAAK,WAAW,MAAM,MAAM;AAC1B,mBAAO,KAAK,oBAAoB;AAChC,oBAAQ;AAAA,UACV,CAAC;AAGD,qBAAW,MAAM;AACf,mBAAO,KAAK,qBAAqB;AACjC,oBAAQ;AAAA,UACV,GAAG,GAAI;AAAA,QACT,CAAC;AAAA,MACH;AAAA;AAAA,MAIQ,WAAW,KAA2B,KAAgC;AAC5E,cAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,YAAY;AAEhD,YAAI,cAAc,GAAG,GAAG;AACtB,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,gCAAgC,CAAC;AACtE;AAAA,QACF;AACA,YAAI,IAAI,WAAW,UAAU,CAAC,cAAc,GAAG,GAAG;AAChD,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,wCAAwC,CAAC;AAC9E;AAAA,QACF;AACA,YAAI,IAAI,aAAa,OAAO,IAAI,WAAW,OAAO;AAChD,eAAK,eAAe,KAAK,KAAK,GAAG;AACjC;AAAA,QACF;AACA,YAAI,IAAI,aAAa,gBAAgB,IAAI,WAAW,QAAQ;AAC1D,eAAK,YAAY,KAAK,GAAG;AACzB;AAAA,QACF;AACA,YAAI,CAAC,KAAK,WAAW,KAAK,KAAK,GAAG;AAChC,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AACrD;AAAA,QACF;AAGA,YAAI,IAAI,aAAa,mBAAmB,IAAI,WAAW,OAAO;AAC5D,eAAK,aAAa,KAAK,KAAK,EAAE,UAAU,KAAK,SAAS,OAAO,EAAE,CAAC;AAAA,QAClE,WAAW,IAAI,aAAa,iBAAiB,IAAI,WAAW,OAAO;AACjE,eAAK,aAAa,KAAK,KAAK;AAAA,YAC1B,SAAS;AAAA,YACT,KAAK,QAAQ;AAAA,YACb,MAAM,KAAK;AAAA,YACX,UAAU,KAAK,SAAS,MAAM;AAAA,YAC9B,QAAQ,KAAK,IAAI,IAAI,KAAK;AAAA,UAC5B,CAAC;AAAA,QACH,WAAW,IAAI,SAAS,WAAW,gBAAgB,KAAK,IAAI,WAAW,UAAU;AAC/E,gBAAMC,aAAY,IAAI,SAAS,MAAM,iBAAiB,MAAM;AAC5D,gBAAM,KAAK,KAAK,eAAe,IAAIA,UAAS;AAC5C,cAAI,IAAI;AAAE,eAAG,UAAU;AAAA,UAAG;AAC1B,gBAAM,UAAU,KAAK,SAAS,WAAWA,UAAS;AAClD,eAAK,eAAe,OAAOA,UAAS;AACpC,eAAK,cAAc,OAAOA,UAAS;AACnC,eAAK,aAAa,OAAOA,UAAS;AAClC,eAAK,cAAcA,UAAS;AAC5B,cAAI,SAAS;AACX,iBAAK,sBAAsB,EAAE,MAAM,wBAAwB,WAAAA,WAAU,CAAC;AACtE,iBAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,UAC1C,OAAO;AACL,iBAAK,aAAa,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,UAC5D;AAAA,QACF,WAAW,IAAI,aAAa,eAAe,IAAI,WAAW,QAAQ;AAChE,eAAK,cAAc,KAAK,GAAG;AAAA,QAC7B,WAAW,IAAI,aAAa,iBAAiB,IAAI,WAAW,QAAQ;AAClE,eAAK,gBAAgB,KAAK,GAAG;AAAA,QAC/B,WAAW,IAAI,aAAa,mBAAmB,IAAI,WAAW,OAAO;AACnE,gBAAMD,UAAS,WAAW;AAC1B,eAAK,aAAa,KAAK,KAAK,EAAE,UAAUA,QAAO,YAAY,CAAC,EAAE,CAAC;AAAA,QACjE,WAAW,IAAI,aAAa,mBAAmB,IAAI,WAAW,QAAQ;AACpE,eAAK,kBAAkB,KAAK,GAAG;AAAA,QACjC,WAAW,IAAI,aAAa,mBAAmB,IAAI,WAAW,OAAO;AACnE,gBAAM,MAAM,WAAW;AACvB,gBAAM,KAAK,IAAI,YAAY,EAAE,UAAU,IAAI,QAAQ,IAAI,SAAS,MAAM;AAEtE,eAAK,aAAa,KAAK,KAAK;AAAA,YAC1B,UAAU,EAAE,GAAG,IAAI,UAAU,GAAG,WAAW,GAAG,GAAG,SAAS,MAAM,GAAG,CAAC,CAAC,QAAQ,GAAG;AAAA,UAClF,CAAC;AAAA,QACH,WAAW,IAAI,aAAa,mBAAmB,IAAI,WAAW,QAAQ;AACpE,eAAK,mBAAmB,KAAK,GAAG;AAAA,QAClC,WAAW,IAAI,aAAa,wBAAwB,IAAI,WAAW,QAAQ;AACzE,eAAK,mBAAmB,KAAK,GAAG;AAAA,QAClC,WAAW,IAAI,aAAa,0BAA0B,IAAI,WAAW,QAAQ;AAC3E,eAAK,qBAAqB,KAAK,GAAG;AAAA,QACpC,OAAO;AACL,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,QACpD;AAAA,MACF;AAAA,MAEQ,eAAe,KAA2B,KAA0B,KAAgB;AAC1F,cAAM,YAAY,IAAI,aAAa,IAAI,OAAO;AAC9C,YAAI,cAAc,QAAQ,KAAK,SAAS,UAAU,WAAW,KAAK,KAAK,GAAG;AACxE,cAAI,UAAU,KAAK,EAAE,UAAU,KAAK,cAAc,oBAAoB,KAAK,OAAO,gBAAgB,GAAG,CAAC,EAAE,CAAC;AACzG,cAAI,IAAI;AACR;AAAA,QACF;AAEA,cAAM,aAAa;AAAA,UACjBH,MAAK,KAAK,WAAW,MAAM,aAAa,YAAY;AAAA;AAAA,UACpDA,MAAK,KAAK,WAAW,aAAa,YAAY;AAAA;AAAA,UAC9CA,MAAK,KAAK,WAAW,MAAM,MAAM,OAAO,aAAa,YAAY;AAAA;AAAA,UACjEA,MAAK,KAAK,WAAW,MAAM,OAAO,aAAa,YAAY;AAAA;AAAA,UAC3DA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,aAAa,YAAY;AAAA;AAAA,UAC1DA,MAAK,KAAK,QAAQ,IAAI,GAAG,OAAO,aAAa,YAAY;AAAA;AAAA,QAC3D;AACA,eAAO,MAAM,yBAAyB,KAAK,UAAU,UAAU,CAAC,EAAE;AAElE,mBAAW,aAAa,YAAY;AAClC,cAAID,IAAG,WAAW,SAAS,GAAG;AAC5B,kBAAM,OAAOA,IAAG,aAAa,WAAW,OAAO;AAC/C,gBAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,gBAAI,IAAI,IAAI;AACZ;AAAA,UACF;AAAA,QACF;AAEA,YAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,YAAI,IAAI,wGAAwG;AAAA,MAClH;AAAA,MAEA,MAAc,YAAY,KAA2B,KAAyC;AAC5F,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,KAAK,OAAO;AACf,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI;AACR;AAAA,QACF;AACA,YAAI,OAAO,MAAM,UAAU,YAAY,CAAC,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG;AACzE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AACrD;AAAA,QACF;AACA,YAAI,UAAU,KAAK,EAAE,cAAc,oBAAoB,KAAK,OAAO,gBAAgB,GAAG,CAAC,EAAE,CAAC;AAC1F,YAAI,IAAI;AAAA,MACV;AAAA,MAEA,MAAc,cAAc,KAA2B,KAAyC;AAC9F,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAE7E,cAAM,EAAE,WAAAK,YAAW,QAAQ,IAAI;AAC/B,YAAI,CAACA,cAAa,CAAC,SAAS;AAC1B,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,qCAAqC,CAAC;AAC3E;AAAA,QACF;AAEA,cAAM,KAAK,KAAK,eAAe,IAAIA,UAAS;AAC5C,YAAI,CAAC,MAAM,GAAG,eAAeH,WAAU,MAAM;AAC3C,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,wBAAwB,CAAC;AAC9D;AAAA,QACF;AAEA,cAAM,MAAsB,EAAE,MAAM,sBAAsB,WAAAG,YAAW,SAAS,QAAQ,MAAM;AAC5F,WAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAC3B,aAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1C;AAAA,MAEA,MAAc,gBAAgB,KAA2B,KAAyC;AAChG,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAE7E,cAAM,EAAE,OAAO,SAAS,MAAM,IAAI;AAClC,YAAI,CAAC,SAAS,CAAC,SAAS;AACtB,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,iCAAiC,CAAC;AACvE;AAAA,QACF;AAEA,cAAM,KAAK,SAAS,OAAO,OAAO,SAAU,SAAiB,MAAM;AACnE,aAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1C;AAAA;AAAA,MAIQ,wBAAwB,IAAe,KAAiC;AAC9E,cAAM,UAAU,KAAK,eAAe,GAAG;AACvC,eAAO,KAAK,oCAAoC,OAAO,GAAG;AAK1D,WAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,iBAAO,KAAK,4BAA4B,IAAI,OAAO,EAAE;AACrD,aAAG,UAAU;AAAA,QACf,CAAC;AAGD,WAAG,GAAG,QAAQ,MAAM;AAClB,qBAAW,CAACA,YAAW,IAAI,KAAK,KAAK,gBAAgB;AACnD,gBAAI,SAAS,IAAI;AAAE,mBAAK,aAAa,IAAIA,YAAW,IAAI;AAAG;AAAA,YAAO;AAAA,UACpE;AAAA,QACF,CAAC;AAED,WAAG,GAAG,WAAW,CAAC,SAAS;AACzB,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,iBAAK,qBAAqB,IAAI,SAAS,GAAG;AAAA,UAC5C,QAAQ;AACN,mBAAO,KAAK,8BAA8B;AAAA,UAC5C;AAAA,QACF,CAAC;AAED,WAAG,GAAG,SAAS,MAAM;AAEnB,qBAAW,CAACA,YAAW,IAAI,KAAK,KAAK,gBAAgB;AACnD,gBAAI,SAAS,IAAI;AACf,oBAAM,UAAU,KAAK,SAAS,WAAWA,UAAS;AAClD,mBAAK,eAAe,OAAOA,UAAS;AACpC,mBAAK,cAAc,OAAOA,UAAS;AACnC,mBAAK,aAAa,OAAOA,UAAS;AAClC,mBAAK,cAAcA,UAAS;AAC5B,qBAAO,KAAK,yBAAyBA,UAAS,EAAE;AAChD,mBAAK,sBAAsB;AAAA,gBACzB,MAAM;AAAA,gBACN,WAAAA;AAAA,cACF,CAAC;AACD;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MAEQ,qBAAqB,IAAe,SAAkB,KAA2B;AACvF,YAAI,IAAI,SAAS,YAAY;AAC3B,gBAAM,KAAK,IAAI,QAAQ;AACvB,gBAAM,QAAQ,KAAK,aAAa,IAAI,EAAE;AACtC,cAAI,SAAS,UAAU,IAAI;AACzB,mBAAO,KAAK,yBAAyB,EAAE,6BAA6B,KAAK,EAAE;AAC3E;AAAA,UACF;AAIA,gBAAM,SAAS,KAAK,eAAe,IAAI,EAAE;AACzC,cAAI,UAAU,WAAW,IAAI;AAC3B,iBAAK,aAAa,OAAO,MAAM;AAC/B,mBAAO,UAAU;AAAA,UACnB;AACA,eAAK,aAAa,IAAI,IAAI,EAAE;AAAA,QAC9B,WAAW,eAAe,OAAO,KAAK,aAAa,IAAI,EAAE,MAAM,IAAI,WAAW;AAC5E;AAAA,QACF;AAEA,gBAAQ,IAAI,MAAM;AAAA,UAChB,KAAK,YAAY;AACf,kBAAM,UAAU,IAAI;AACpB,oBAAQ,UAAU;AAClB,kBAAM,eAAe,CAAC,CAAC,KAAK,SAAS,IAAI,QAAQ,EAAE;AACnD,iBAAK,SAAS,SAAS,OAAO;AAC9B,iBAAK,eAAe,IAAI,QAAQ,IAAI,EAAE;AACtC,gBAAI,QAAS,MAAK,cAAc,IAAI,QAAQ,EAAE;AAC9C,mBAAO,KAAK,uBAAuB,QAAQ,EAAE,KAAK,QAAQ,IAAI,cAAc,QAAQ,kBAAkB,KAAK,WAAW,QAAQ,YAAY,GAAG,GAAG;AAChJ,iBAAK,sBAAsB;AAAA,cACzB,MAAM,eAAe,oBAAoB;AAAA,cACzC;AAAA,YACF,CAAC;AACD;AAAA,UACF;AAAA,UAEA,KAAK,UAAU;AACb,kBAAM,UAAU,KAAK,SAAS,aAAa,IAAI,WAAW,IAAI,MAAM;AACpE,gBAAI,SAAS;AACX,mBAAK,sBAAsB,EAAE,MAAM,mBAAmB,SAAS,QAAQ,CAAC;AAAA,YAC1E;AACA;AAAA,UACF;AAAA,UAEA,KAAK,aAAa;AAChB,kBAAM,UAAU,KAAK,SAAS,YAAY,IAAI,WAAW,IAAI,QAAQ;AACrE,gBAAI,SAAS;AACX,qBAAO,KAAK,iBAAiB,IAAI,SAAS,KAAK,IAAI,YAAY,GAAG,EAAE;AACpE,mBAAK,sBAAsB,EAAE,MAAM,mBAAmB,SAAS,QAAQ,CAAC;AAAA,YAC1E;AACA;AAAA,UACF;AAAA,UAEA,KAAK,UAAU;AACb,iBAAK,SAAS,eAAe,IAAI,SAAS;AAC1C,kBAAM,gBAAgB,KAAK,SAAS,IAAI,IAAI,SAAS;AACrD,kBAAM,cAAc,KAAK,gBAAgB,aAAa;AACtD,iBAAK,SAAS,kBAAkB,IAAI,WAAW,aAAa,IAAI,WAAW,KAAK,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,SAAS,MAAM;AAC7H,iBAAK,sBAAsB;AAAA,cACzB,MAAM;AAAA,cACN,WAAW,IAAI;AAAA,cACf,OAAO,IAAI;AAAA,cACX,SAAS,IAAI;AAAA,cACb,OAAO,IAAI;AAAA,cACX,WAAW,KAAK,IAAI;AAAA,YACtB,CAAC;AACD;AAAA,UACF;AAAA,UAEA,KAAK,SAAS;AACZ,iBAAK,SAAS,eAAe,IAAI,SAAS;AAC1C,kBAAM,eAAe,KAAK,SAAS,IAAI,IAAI,SAAS;AACpD,kBAAM,aAAa,KAAK,gBAAgB,YAAY;AACpD,iBAAK,SAAS,kBAAkB,IAAI,WAAW,YAAY,IAAI,UAAU,WAAW,IAAI,QAAQ,MAAM,GAAG,GAAI,GAAG,MAAM;AACtH,iBAAK,sBAAsB;AAAA,cACzB,MAAM;AAAA,cACN,WAAW,IAAI;AAAA,cACf,SAAS,IAAI;AAAA,cACb,WAAW,KAAK,IAAI;AAAA,YACtB,CAAC;AACD;AAAA,UACF;AAAA,UAEA,KAAK,sBAAsB;AACzB,iBAAK,SAAS,eAAe,IAAI,SAAS;AAC1C,kBAAM,UAAU,aAAa,IAAI,OAAO;AACxC,mBAAO,KAAK,uBAAuB,IAAI,SAAS,UAAU,IAAI,SAAS,KAAK,IAAI,QAAQ,EAAE;AAC1F,gBAAI,SAAS;AACX,mBAAK,eAAe,IAAI,cAAc,IAAI,WAAW,IAAI,SAAS,GAAG;AAAA,gBACnE,SAAS;AAAA,kBACP,WAAW,IAAI;AAAA,kBACf,WAAW,IAAI;AAAA,kBACf,UAAU,IAAI;AAAA,kBACd,aAAa,IAAI;AAAA,kBACjB,cAAc,IAAI;AAAA,kBAClB,WAAW,IAAI;AAAA,kBACf;AAAA,gBACF;AAAA,gBACA,WAAW,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAAA,cAC7C,CAAC;AAAA,YACH;AACA,iBAAK,sBAAsB;AAAA,cACzB,MAAM;AAAA,cACN,WAAW,IAAI;AAAA,cACf,WAAW,IAAI;AAAA,cACf,UAAU,IAAI;AAAA,cACd,aAAa,IAAI;AAAA,cACjB,cAAc,IAAI;AAAA,cAClB,WAAW,IAAI;AAAA,cACf,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,YAC/B,CAAC;AAED,gBAAI,KAAK,aAAa;AACpB,oBAAM,QAAQ,KAAK,gBAAgB,KAAK,SAAS,IAAI,IAAI,SAAS,CAAC;AACnE,kBAAI,SAAS;AACX,qBAAK,KAAK,YAAY,kBAAkB,IAAI,WAAW,OAAO,IAAI,WAAW,IAAI,UAAU,IAAI,aAAa,IAAI,cAAc,OAAO;AAAA,cACvI,OAAO;AACL,qBAAK,YAAY,sBAAsB,IAAI,WAAW,OAAO,IAAI,WAAW,IAAI,UAAU,IAAI,aAAa,IAAI,YAAY;AAAA,cAC7H;AAAA,YACF;AACA;AAAA,UACF;AAAA,UAEA,KAAK,uBAAuB;AAC1B,iBAAK,cAAc,IAAI,WAAW,IAAI,WAAW,IAAI,UAAU,YAAY,YAAY,UAAU;AACjG;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAIQ,0BAA0B,IAAqB;AACrD,aAAK,iBAAiB,IAAI,EAAE;AAC5B,eAAO,KAAK,+BAA+B,KAAK,iBAAiB,IAAI,GAAG;AAExE,WAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,iBAAO,KAAK,8BAA8B,IAAI,OAAO,EAAE;AACvD,aAAG,UAAU;AAAA,QACf,CAAC;AAGD,cAAM,cAA8B;AAAA,UAClC,MAAM;AAAA,UACN,UAAU,KAAK,SAAS,OAAO;AAAA,QACjC;AACA,WAAG,KAAK,KAAK,UAAU,WAAW,CAAC;AACnC,cAAM,aAA6B;AAAA,UACjC,MAAM;AAAA,UACN,UAAU,CAAC,GAAG,KAAK,eAAe,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,QAClE;AACA,WAAG,KAAK,KAAK,UAAU,UAAU,CAAC;AAElC,WAAG,GAAG,WAAW,CAAC,SAAS;AACzB,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,gBAAI,IAAI,SAAS,sBAAsB;AACrC,oBAAM,YAAY,KAAK,eAAe,IAAI,IAAI,SAAS;AACvD,kBAAI,WAAW,eAAeH,WAAU,MAAM;AAC5C,0BAAU,KAAK,KAAK,UAAU,EAAE,GAAG,KAAK,QAAQ,YAAY,CAAC,CAAC;AAAA,cAChE;AAAA,YACF,WAAW,IAAI,SAAS,gBAAgB;AACtC,mBAAK,kBAAkB,GAAG;AAAA,YAC5B,WAAW,IAAI,SAAS,uBAAuB;AAC7C,kBAAI,KAAK,0BAA0B,GAAG,GAAG;AACvC,sBAAM,UAAU,IAAI,aAAa,SAAY,UAAU,IAAI,QAAQ,KAAK,IAAI;AAC5E,uBAAO,KAAK,uBAAuB,IAAI,SAAS,MAAM,OAAO,eAAe,IAAI,SAAS,EAAE;AAAA,cAC7F;AAAA,YACF;AAAA,UACF,QAAQ;AACN,mBAAO,KAAK,gCAAgC;AAAA,UAC9C;AAAA,QACF,CAAC;AAED,WAAG,GAAG,SAAS,MAAM;AACnB,eAAK,iBAAiB,OAAO,EAAE;AAC/B,iBAAO,KAAK,kCAAkC,KAAK,iBAAiB,IAAI,GAAG;AAAA,QAC7E,CAAC;AAAA,MACH;AAAA;AAAA,MAIQ,0BAA0B,KAAgG;AAChI,cAAM,UAAU,KAAK,eAAe,IAAI,cAAc,IAAI,WAAW,IAAI,SAAS,CAAC;AACnF,YAAI;AACJ,YAAI,SAAS;AACX,cAAI,OAAO,IAAI,aAAa,YAAY,CAAC,QAAQ,UAAU,IAAI,IAAI,QAAQ,EAAG,QAAO;AACrF,gBAAM,EAAE,MAAM,uBAAuB,WAAW,IAAI,WAAW,WAAW,IAAI,WAAW,UAAU,IAAI,SAAS;AAAA,QAClH,OAAO;AACL,cAAK,IAAI,aAAa,WAAW,IAAI,aAAa,UAAW,IAAI,aAAa,OAAW,QAAO;AAChG,cAAI,KAAK,SAAS,IAAI,IAAI,SAAS,GAAG,cAAc,QAAS,QAAO;AACpE,gBAAM,EAAE,MAAM,uBAAuB,WAAW,IAAI,WAAW,WAAW,IAAI,WAAW,UAAU,IAAI,SAAS;AAAA,QAClH;AACA,cAAM,YAAY,KAAK,eAAe,IAAI,IAAI,SAAS;AACvD,YAAI,WAAW,eAAeA,WAAU,KAAM,QAAO;AACrD,kBAAU,KAAK,KAAK,UAAU,GAAG,CAAC;AAClC,eAAO;AAAA,MACT;AAAA,MAEQ,cAAcG,YAAmB,WAAmB,OAAqC;AAC/F,YAAI,CAAC,KAAK,eAAe,OAAO,cAAcA,YAAW,SAAS,CAAC,EAAG;AACtE,aAAK,sBAAsB,EAAE,MAAM,uBAAuB,WAAAA,YAAW,WAAW,MAAM,CAAC;AACvF,aAAK,KAAK,aAAa,qBAAqBA,YAAW,WAAW,KAAK;AAAA,MACzE;AAAA,MAEQ,cAAcA,YAAyB;AAC7C,mBAAW,WAAW,CAAC,GAAG,KAAK,eAAe,OAAO,CAAC,GAAG;AACvD,cAAI,QAAQ,QAAQ,cAAcA,WAAW,MAAK,cAAcA,YAAW,QAAQ,QAAQ,WAAW,SAAS;AAAA,QACjH;AAAA,MACF;AAAA,MAEQ,sBAAsB,KAA2B;AACvD,cAAM,UAAU,KAAK,UAAU,GAAG;AAClC,mBAAW,MAAM,KAAK,kBAAkB;AACtC,cAAI,GAAG,eAAeH,WAAU,MAAM;AACpC,eAAG,KAAK,OAAO;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,MAEQ,kBAAkB,KAAsD;AAC9E,cAAM,EAAE,WAAAG,YAAW,WAAW,UAAU,cAAc,QAAQ,IAAI;AAGlE,YAAI,CAAC,KAAK,cAAc,IAAIA,UAAS,GAAG;AACtC,iBAAO,KAAK,kCAAkCA,UAAS,eAAe;AACtE;AAAA,QACF;AAEA,cAAM,YAAY,KAAK,eAAe,IAAIA,UAAS;AACnD,YAAI,CAAC,aAAa,UAAU,eAAeH,WAAU,KAAM;AAG3D,cAAM,eAAe,CAAC,aAAa,cAAc,aAAa,YAAY;AAC1E,YAAI,CAAC,aAAa,SAAS,QAAQ,EAAG;AAGtC,cAAM,aAAa,UAAU,QAAQ,4BAA4B,EAAE;AACnE,cAAM,SAAS,OAAO,KAAK,YAAY,QAAQ;AAG/C,YAAI,OAAO,SAAS,KAAK,OAAO,MAAM;AACpC,iBAAO,KAAK,qCAAqC;AACjD;AAAA,QACF;AAGA,QAAAF,IAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAC7C,cAAM,MAAM,SAAS,MAAM,GAAG,EAAE,CAAC,MAAM,SAAS,QAAQ,SAAS,MAAM,GAAG,EAAE,CAAC;AAC7E,cAAM,WAAW,GAAGG,YAAW,CAAC,IAAI,GAAG;AACvC,cAAM,WAAWF,MAAK,KAAK,aAAa,QAAQ;AAChD,QAAAD,IAAG,cAAc,UAAU,MAAM;AAGjC,cAAM,aAA6B;AAAA,UACjC,MAAM;AAAA,UACN,WAAAK;AAAA,UACA,WAAW;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,kBAAU,KAAK,KAAK,UAAU,UAAU,CAAC;AACzC,eAAO,KAAK,8BAA8B,QAAQ,KAAK,OAAO,MAAM,SAAS;AAG7E,mBAAW,MAAM;AACf,cAAI;AAAE,YAAAL,IAAG,WAAW,QAAQ;AAAA,UAAG,QAAQ;AAAA,UAAC;AAAA,QAC1C,GAAG,IAAI,KAAK,GAAI;AAAA,MAClB;AAAA,MAEA,MAAc,kBAAkB,KAA2B,KAAyC;AAClG,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAC7E,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,4BAA4B,CAAC;AAAG;AAAA,QAAQ;AAC7G,cAAMI,UAAS,WAAW;AAC1B,QAAAA,QAAO,WAAW;AAClB,mBAAWA,OAAM;AACjB,aAAK,SAAS,UAAU,EAAE,SAAS,CAAC;AACpC,aAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1C;AAAA,MAEQ,aAAaA,SAA8B;AACjD,aAAK,cAAc,IAAI,YAAYA,OAAM;AACzC,aAAK,YAAY,cAAc,MAAM,KAAK,SAAS,OAAO;AAC1D,aAAK,YAAY,qBAAqB,CAACC,YAAW,YAAY;AAC5D,gBAAM,YAAY,KAAK,eAAe,IAAIA,UAAS;AACnD,cAAI,WAAW,eAAeH,WAAU,MAAM;AAC5C,kBAAM,MAAsB,EAAE,MAAM,sBAAsB,WAAAG,YAAW,SAAS,QAAQ,WAAW;AACjG,sBAAU,KAAK,KAAK,UAAU,GAAG,CAAC;AAClC,mBAAO,KAAK,0CAA0CA,UAAS,EAAE;AAAA,UACnE;AAAA,QACF;AACA,aAAK,YAAY,mBAAmB,CAACA,YAAW,WAAW,UAAU,YAAY;AAC/E,gBAAM,YAAY,KAAK,eAAe,IAAIA,UAAS;AACnD,cAAI,WAAW,eAAeH,WAAU,MAAM;AAC5C,kBAAM,MAAsB,EAAE,MAAM,oBAAoB,WAAAG,YAAW,WAAW,UAAU,SAAS,QAAQ;AACzG,sBAAU,KAAK,KAAK,UAAU,GAAG,CAAC;AAClC,mBAAO,KAAK,wCAAwCA,UAAS,EAAE;AAAA,UACjE;AAAA,QACF;AACA,aAAK,YAAY,sBAAsB,CAACA,YAAW,WAAW,aAAa;AACzE,cAAI,KAAK,0BAA0B,EAAE,WAAAA,YAAW,WAAW,SAAS,CAAC,GAAG;AACtE,mBAAO,KAAK,gCAAgC,SAAS,MAAM,QAAQ,eAAeA,UAAS,EAAE;AAAA,UAC/F;AAEA,eAAK,sBAAsB,EAAE,MAAM,uBAAuB,WAAAA,YAAW,WAAW,SAAS,CAAC;AAAA,QAC5F;AACA,aAAK,YAAY,kBAAkB,CAACA,YAAW,WAAW,aAAa;AACrE,cAAI,KAAK,0BAA0B,EAAE,WAAAA,YAAW,WAAW,SAAS,CAAC,GAAG;AACtE,mBAAO,KAAK,oBAAoB,SAAS,MAAM,QAAQ,eAAeA,UAAS,EAAE;AAAA,UACnF;AAAA,QACF;AACA,aAAK,SAAS,UAAU,EAAE,aAAa,KAAK,YAAY,CAAC;AACzD,aAAK,YAAY,aAAa;AAC9B,eAAO,KAAK,0BAA0B;AAAA,MACxC;AAAA,MAEA,MAAc,mBAAmB,KAA2B,KAAyC;AACnG,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAC7E,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,CAAC,UAAU;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAAG;AAAA,QAAQ;AAE7F,cAAMD,UAAS,WAAW;AAE1B,YAAI,SAAS,SAAS,SAAS,KAAK,KAAKA,QAAO,UAAU,UAAU;AAClE,mBAAS,WAAWA,QAAO,SAAS;AAAA,QACtC;AACA,QAAAA,QAAO,WAAW;AAClB,mBAAWA,OAAM;AAGjB,YAAI,KAAK,aAAa;AACpB,eAAK,YAAY,YAAY;AAC7B,eAAK,cAAc;AACnB,eAAK,SAAS,UAAU,EAAE,aAAa,OAAiB,CAAC;AAAA,QAC3D;AAGA,YAAI,SAAS,WAAW,SAAS,YAAY,SAAS,QAAQ;AAC5D,eAAK,aAAa,QAAQ;AAAA,QAC5B;AAEA,aAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,MAC1C;AAAA,MAEA,MAAc,mBAAmB,KAA2B,KAAyC;AACnG,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAC7E,cAAM,EAAE,UAAU,OAAO,IAAI;AAC7B,YAAI,CAAC,YAAY,CAAC,QAAQ;AACxB,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,+BAA+B,CAAC;AACrE;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,UAAU,MAAM,MAAM,+BAA+B,QAAQ,gBAAgB;AAAA,YACjF,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU;AAAA,cACnB,SAAS;AAAA,cACT,MAAM;AAAA,YACR,CAAC;AAAA,UACH,CAAC;AAED,cAAI,QAAQ,IAAI;AACd,iBAAK,aAAa,KAAK,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,UAC1C,OAAO;AACL,kBAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,iBAAK,aAAa,KAAK,KAAK,EAAE,OAAQ,IAAY,eAAe,qBAAqB,CAAC;AAAA,UACzF;AAAA,QACF,SAAS,KAAK;AACZ,eAAK,aAAa,KAAK,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,MAEA,MAAc,qBAAqB,KAA2B,KAAyC;AACrG,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,YAAI,CAAC,MAAM;AAAE,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAAG;AAAA,QAAQ;AAC7E,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,CAAC,UAAU;AACb,eAAK,aAAa,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAC1D;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,YAAY,MAAM,MAAM,+BAA+B,QAAQ,kCAAkC;AAAA,YACrG,QAAQ,YAAY,QAAQ,GAAK;AAAA,UACnC,CAAC;AAED,cAAI,CAAC,UAAU,IAAI;AACjB,kBAAM,MAAM,MAAM,UAAU,KAAK;AACjC,iBAAK,aAAa,KAAK,KAAK,EAAE,OAAQ,IAAY,eAAe,oBAAoB,CAAC;AACtF;AAAA,UACF;AAEA,gBAAM,OAAO,MAAM,UAAU,KAAK;AAClC,cAAI,CAAC,KAAK,MAAM,CAAC,KAAK,OAAO,QAAQ;AACnC,iBAAK,aAAa,KAAK,KAAK,EAAE,IAAI,OAAO,OAAO,CAAC,EAAE,CAAC;AACpD;AAAA,UACF;AAGA,gBAAM,UAAU,oBAAI,IAAwD;AAC5E,qBAAW,UAAU,KAAK,QAAQ;AAChC,gBAAI,OAAO,SAAS,MAAM;AACxB,oBAAM,OAAO,OAAO,QAAQ;AAC5B,oBAAM,KAAK,OAAO,KAAK,EAAE;AACzB,kBAAI,CAAC,QAAQ,IAAI,EAAE,GAAG;AACpB,wBAAQ,IAAI,IAAI;AAAA,kBACd;AAAA,kBACA,MAAM,KAAK,SAAS,KAAK,cAAc;AAAA,kBACvC,MAAM,KAAK;AAAA,gBACb,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAEA,eAAK,aAAa,KAAK,KAAK,EAAE,IAAI,MAAM,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE,CAAC;AAAA,QACxE,SAAS,KAAK;AACZ,eAAK,aAAa,KAAK,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,MAEQ,oBAA0B;AAChC,YAAI,CAAC,KAAK,aAAc;AACxB,cAAM,SAAS,qBAAqB,SAAS;AAC7C,YAAI,CAAC,QAAQ;AACX,iBAAO,KAAK,0EAA0E;AACtF;AAAA,QACF;AACA,aAAK,kBAAkB,IAAI,gBAAgB,MAAM;AACjD,aAAK,gBAAgB,MAAM;AAC3B,eAAO,KAAK,uBAAuB;AAAA,MACrC;AAAA,MAEQ,iBAAuB;AAE7B,aAAK,oBAAoB,YAAY,MAAM;AACzC,qBAAW,CAACC,YAAW,EAAE,KAAK,KAAK,gBAAgB;AACjD,gBAAI,KAAK,aAAa,IAAIA,UAAS,MAAM,OAAO;AAE9C,qBAAO,KAAK,2CAA2CA,UAAS,EAAE;AAClE,iBAAG,UAAU;AACb;AAAA,YACF;AACA,iBAAK,aAAa,IAAIA,YAAW,KAAK;AACtC,eAAG,KAAK;AAAA,UACV;AAAA,QACF,GAAG,GAAK;AAAA,MACV;AAAA,MAEQ,iBAAuB;AAC7B,YAAI;AACF,cAAI,CAACL,IAAG,WAAW,WAAW,EAAG;AACjC,gBAAM,QAAQA,IAAG,YAAY,WAAW;AACxC,qBAAW,QAAQ,OAAO;AACxB,gBAAI;AAAE,cAAAA,IAAG,WAAWC,MAAK,KAAK,aAAa,IAAI,CAAC;AAAA,YAAG,QAAQ;AAAA,YAAC;AAAA,UAC9D;AACA,cAAI,MAAM,SAAS,EAAG,QAAO,KAAK,cAAc,MAAM,MAAM,qBAAqB;AAAA,QACnF,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,MAEQ,gBAAgB,SAA+B;AACrD,eAAO,UAAU,aAAa,OAAO,IAAI;AAAA,MAC3C;AAAA,MAEQ,aAAa,KAA0B,QAAgB,MAAqB;AAClF,YAAI,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAC5D,YAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,MAC9B;AAAA,MAEQ,WAAW,KAA2B,iBAAmC;AAC/E,eAAO,CAAC,KAAK,SAAS,aAAa,KAAK,KAAK,OAAO,eAAe;AAAA,MACrE;AAAA,MAEQ,cAAc,QAAgB,QAAgB,MAAc,UAAkB,KAAiC;AACrH,eAAO,KAAK,YAAY,QAAQ,aAAa,MAAM,UAAU,IAAI,OAAO,aAAa,EAAE;AACvF,eAAO,MAAM,YAAY,MAAM,IAAI,IAAI;AAAA;AAAA;AAAA;AAAA,CAAoD;AAC3F,eAAO,QAAQ;AAAA,MACjB;AAAA,MAEQ,eAAe,KAAoC;AACzD,cAAM,OAAO,IAAI,OAAO;AACxB,eAAO,SAAS,eAAe,SAAS,SAAS,SAAS;AAAA,MAC5D;AAAA,MAEQ,SAAS,KAA2B,UAAU,OAAO,MAA+B;AAC1F,eAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,cAAI,OAAO;AACX,cAAI,OAAO;AACX,cAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,oBAAQ,MAAM;AACd,gBAAI,OAAO,SAAS;AAClB,kBAAI,QAAQ;AACZ,sBAAQ,IAAI;AACZ;AAAA,YACF;AACA,oBAAQ;AAAA,UACV,CAAC;AACD,cAAI,GAAG,OAAO,MAAM;AAClB,gBAAI;AACF,sBAAQ,KAAK,MAAM,IAAI,CAAC;AAAA,YAC1B,QAAQ;AACN,sBAAQ,IAAI;AAAA,YACd;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,QAAQ,KAAK,CAAC,MAChB,QAAQ,KAAK,CAAC,EAAE,SAAS,eAAe,KACxC,QAAQ,KAAK,CAAC,EAAE,SAAS,eAAe,IACvC;AACD,wBAAkB,YAAY;AAC9B,iBAAW,YAAY;AACvB,YAAMG,UAAS,WAAW;AAC1B,YAAM,MAAM,IAAI,UAAUA,OAAM;AAChC,UAAI,MAAM,EAAE,MAAM,CAAC,QAAQ;AACzB,eAAO,MAAM,wBAAwB,GAAG;AACxC,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAED,YAAM,WAAW,MAAM;AACrB,YAAI,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,MACvC;AACA,cAAQ,GAAG,UAAU,QAAQ;AAC7B,cAAQ,GAAG,WAAW,QAAQ;AAAA,IAChC;AAAA;AAAA;;;ACp6BA,OAAOE,gBAAe;AAAtB,IAKa;AALb;AAAA;AAAA;AACA;AACA;AAGO,IAAM,YAAN,MAAgB;AAAA,MAQrB,YACUC,YACAC,cACAC,WAAU,kBACVC,WAAU,kBACV,OACA,cAAwC,MAAM,QAC9C,kBAA8C,OAAO,CAAC,IAC9D;AAPQ,yBAAAH;AACA,2BAAAC;AACA,uBAAAC;AACA,uBAAAC;AACA;AACA;AACA;AAAA,MACP;AAAA,MAfK,KAAuB;AAAA,MACvB,iBAAuD;AAAA,MACvD,kBAAwD,CAAC;AAAA,MACzD,QAA0B,CAAC;AAAA,MAC3B,YAAY;AAAA,MACZ,SAAS;AAAA,MAYjB,UAAgB;AACd,aAAK,SAAS;AACd,cAAM,aAAa,KAAK,QAAQ,UAAU,mBAAmB,KAAK,KAAK,CAAC,KAAK;AAC7E,cAAM,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,OAAO,GAAG,eAAe,GAAG,UAAU;AAC/E,eAAO,MAAM,wBAAwB,GAAG,EAAE;AAE1C,YAAI;AACF,gBAAM,KAAK,IAAIJ,WAAU,GAAG;AAC5B,eAAK,KAAK;AAEV,aAAG,GAAG,QAAQ,MAAM;AAClB,gBAAI,KAAK,OAAO,GAAI;AACpB,mBAAO,KAAK,kBAAkB;AAC9B,iBAAK,YAAY;AAEjB,iBAAK,GAAI,KAAK,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC;AAGjD,uBAAW,OAAO,KAAK,OAAO;AAC5B,mBAAK,GAAI,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,YACnC;AACA,iBAAK,QAAQ,CAAC;AAAA,UAChB,CAAC;AAED,aAAG,GAAG,WAAW,CAAC,SAAS;AACzB,gBAAI,KAAK,OAAO,GAAI;AACpB,gBAAI;AACF,oBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,yBAAW,WAAW,KAAK,iBAAiB;AAC1C,wBAAQ,GAAG;AAAA,cACb;AAAA,YACF,SAAS,KAAK;AACZ,qBAAO,KAAK,gCAAgC,GAAG;AAAA,YACjD;AAAA,UACF,CAAC;AAED,aAAG,GAAG,SAAS,MAAM;AACnB,gBAAI,KAAK,OAAO,GAAI;AACpB,mBAAO,KAAK,uBAAuB;AACnC,iBAAK,YAAY;AACjB,gBAAI,CAAC,KAAK,OAAQ,MAAK,kBAAkB;AAAA,UAC3C,CAAC;AAED,aAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,gBAAI,KAAK,OAAO,GAAI;AACpB,mBAAO,MAAM,yBAAyB,IAAI,OAAO,EAAE;AACnD,iBAAK,YAAY;AAAA,UACnB,CAAC;AAAA,QACH,QAAQ;AACN,iBAAO,MAAM,sCAAsC;AACnD,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAAA,MAEA,KAAK,KAA2B;AAC9B,YAAI,KAAK,aAAa,KAAK,IAAI,eAAeA,WAAU,MAAM;AAC5D,eAAK,GAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,QAClC,OAAO;AACL,cAAI,KAAK,MAAM,SAAS,KAAK;AAC3B,iBAAK,MAAM,KAAK,GAAG;AAAA,UACrB;AACA,iBAAO,MAAM,mCAAmC;AAAA,QAClD;AAAA,MACF;AAAA,MAEA,UAAU,SAA8C;AACtD,aAAK,gBAAgB,KAAK,OAAO;AAAA,MACnC;AAAA,MAEA,aAAmB;AACjB,aAAK,SAAS;AACd,YAAI,KAAK,gBAAgB;AACvB,uBAAa,KAAK,cAAc;AAChC,eAAK,iBAAiB;AAAA,QACxB;AACA,YAAI,KAAK,IAAI;AACX,eAAK,GAAG,MAAM;AACd,eAAK,KAAK;AAAA,QACZ;AACA,aAAK,YAAY;AAAA,MACnB;AAAA,MAEA,aAAmB;AACjB,YAAI,KAAK,aAAa,KAAK,IAAI,eAAeA,WAAU,MAAM;AAC5D,eAAK,GAAG,KAAK,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,MAEQ,eAA+B;AACrC,eAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,YACP,IAAI,KAAK;AAAA,YACT,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,YACR,aAAa,KAAK,IAAI;AAAA,YACtB,cAAc,KAAK,IAAI;AAAA,YACvB,KAAK,QAAQ,IAAI;AAAA,YACjB,gBAAgB;AAAA,YAChB,UAAU,KAAK,YAAY;AAAA,YAC3B,GAAG,KAAK,gBAAgB;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,MAEQ,oBAA0B;AAChC,YAAI,KAAK,kBAAkB,KAAK,OAAQ;AACxC,aAAK,iBAAiB,WAAW,MAAM;AACrC,eAAK,iBAAiB;AACtB,eAAK,QAAQ;AAAA,QACf,GAAG,GAAI;AAAA,MACT;AAAA,IACF;AAAA;AAAA;;;ACvIA,OAAOK,SAAQ;AACf,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAcV,SAAS,aAAa,SAAoB,KAAwC;AACvF,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAA0B,OAAO,MAAM,YAAY,MAAM,IAAI;AAC3F,QAAM,SAAS,CAAC,UAA6D;AAC3E,UAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,WAAO,gBAAgB,KAAK,IAAI,IAAI,IAAI,OAAO;AAAA,EACjD;AACA,MAAI,IAAI,iBAAiB;AACvB,UAAM,WAAW,OAAO,CAAC,MAAM,EAAE,wBAAwB,IAAI,eAAe;AAC5E,QAAI,SAAU,QAAO;AAAA,EACvB;AACA,MAAI,IAAI,UAAW,QAAO,OAAO,CAAC,MAAM,EAAE,cAAc,IAAI,SAAS;AACrE,SAAO;AACT;AAKO,SAAS,aAAa,MAAyB,QAAQ,KAAyB;AACrF,QAAM,SAAwB;AAAA,IAC5B,iBAAiB,IAAI;AAAA,IACrB,WAAW,IAAI;AAAA,EACjB;AACA,MAAI,CAAC,OAAO,mBAAmB,CAAC,OAAO,UAAW,QAAO;AAEzD,QAAM,YAAY,IAAI,qBAAqBA,MAAK,KAAKD,IAAG,QAAQ,GAAG,SAAS;AAC5E,QAAM,MAAMC,MAAK,KAAK,WAAW,UAAU;AAC3C,MAAI;AACJ,MAAI;AACF,YAAQF,IAAG,YAAY,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,EAC/D,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,UAAqB,CAAC;AAC5B,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,cAAQ,KAAK,KAAK,MAAMA,IAAG,aAAaE,MAAK,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,IACrE,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO,aAAa,SAAS,MAAM;AACrC;AAxDA,IAAAC,kBAAA;AAAA;AAAA;AAGA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;AAClB,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,WAAU;AAwMjB,eAAe,OAAO;AACpB,SAAO,KAAK,yCAAyC,SAAS,GAAG;AAGjE,YAAU,QAAQ;AAIlB,QAAM,kBAAkB,MAAM;AAC5B,UAAM,OAAO,aAAa;AAC1B,QAAI,SAAS,SAAU;AACvB,eAAW;AACX,WAAO,KAAK,cAAc,QAAQ,QAAQ,EAAE;AAC5C,cAAU,KAAK,EAAE,MAAM,aAAa,WAAW,UAAU,KAAK,CAAC;AAAA,EACjE;AACA,aAAW,iBAAiB,GAAK,EAAE,MAAM;AACzC,cAAY,iBAAiB,GAAM,EAAE,MAAM;AAG3C,YAAU,UAAU,OAAO,QAAQ;AACjC,QAAI,IAAI,SAAS,wBAAwB,IAAI,cAAc,WAAW;AACpE,aAAO,KAAK,2BAA2B,IAAI,OAAO,EAAE;AACpD,YAAM,OAAO,aAAa;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,SAAS,IAAI;AAAA,UACb,MAAM,EAAE,QAAQ,aAAa,WAAW,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,QAC7D;AAAA,MACF,CAAC;AAAA,IACH,WAAW,IAAI,SAAS,sBAAsB,IAAI,cAAc,WAAW;AACzE,aAAO,KAAK,yBAAyB,IAAI,SAAS,EAAE;AACpD,YAAM,WAAW,IAAI,UAAU;AAAA,EAAK,IAAI,OAAO,KAAK;AACpD,YAAM,OAAO,aAAa;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,SAAS,WAAW,IAAI,gBAAgB,OAAO,uDAAuD,IAAI,SAAS,GAAG,QAAQ;AAAA,UAC9H,MAAM,EAAE,QAAQ,aAAa,WAAW,OAAO,KAAK,IAAI,CAAC,GAAG,WAAW,IAAI,WAAW,UAAU,IAAI,SAAS;AAAA,QAC/G;AAAA,MACF,CAAC;AAAA,IACH,WAAW,IAAI,SAAS,yBAAyB,IAAI,cAAc,WAAW;AAC5E,aAAO,KAAK,uBAAuB,IAAI,SAAS,MAAM,IAAI,QAAQ,EAAE;AACpE,YAAM,OAAO,aAAa;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,YAAY,IAAI;AAAA,UAChB,UAAU,IAAI;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,SAAO,KAAK,qCAAqC;AAGjD,UAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,WAAO,KAAK,uBAAuB;AACnC,cAAU,WAAW;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACD,UAAQ,MAAM,GAAG,SAAS,MAAM;AAC9B,WAAO,KAAK,uBAAuB;AACnC,cAAU,WAAW;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;AAlRA,IAgBM,WACA,aACF,UAEE,QAiCA,QACA,SACA,SACA,UAGA,WAuHA;AAlLN,IAAAC,eAAA;AAAA;AAAA;AASA;AACA;AACA;AACA;AACA,IAAAC;AAGA,IAAM,YAAYH,YAAW;AAC7B,IAAM,cAAc,QAAQ,IAAI,6BAA6BC,MAAK,SAAS,QAAQ,IAAI,CAAC;AACxF,IAAI,WAAW,aAAa;AAE5B,IAAM,SAAS,IAAI;AAAA,MACjB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,cAAc;AAAA,UACZ,cAAc;AAAA,YACZ,kBAAkB,CAAC;AAAA,YACnB,6BAA6B,CAAC;AAAA,UAChC;AAAA,UACA,OAAO,CAAC;AAAA,QACV;AAAA,QACA,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAgBhB;AAAA,IACF;AAGA,IAAM,SAAS,WAAW;AAC1B,IAAM,UAAU,QAAQ,IAAI,yBAAyB,OAAO,IAAI;AAChE,IAAM,UAAU,QAAQ,IAAI,wBAAwB,SAAS,QAAQ,IAAI,uBAAuB,EAAE,IAAI,OAAO,IAAI;AACjH,IAAM,WAAW,QAAQ,IAAI,0BAA0B,OAAO,IAAI;AAGlE,IAAM,YAAY,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR;AAIA,WAAO,kBAAkB,wBAAwB,aAAa;AAAA,MAC5D,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,aACE;AAAA,UACF,aAAa;AAAA,YACX,MAAM;AAAA,YACN,YAAY;AAAA,cACV,OAAO,EAAE,MAAM,UAAU,aAAa,6BAA6B;AAAA,cACnE,SAAS,EAAE,MAAM,UAAU,aAAa,yBAAyB;AAAA,cACjE,OAAO;AAAA,gBACL,MAAM;AAAA,gBACN,MAAM,CAAC,QAAQ,WAAW,SAAS,SAAS;AAAA,gBAC5C,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,SAAS,SAAS;AAAA,UAC/B;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,aACE;AAAA,UACF,aAAa;AAAA,YACX,MAAM;AAAA,YACN,YAAY;AAAA,cACV,SAAS,EAAE,MAAM,UAAU,aAAa,8CAA8C;AAAA,YACxF;AAAA,YACA,UAAU,CAAC,SAAS;AAAA,UACtB;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,aACE;AAAA,UACF,aAAa;AAAA,YACX,MAAM;AAAA,YACN,YAAY;AAAA,cACV,QAAQ;AAAA,gBACN,MAAM;AAAA,gBACN,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,gBACzC,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,QAAQ;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF,EAAE;AAEF,WAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,YAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAE1C,cAAQ,MAAM;AAAA,QACZ,KAAK,UAAU;AACb,gBAAM,QAAQ,MAAM;AACpB,gBAAM,UAAU,MAAM;AACtB,gBAAM,QAAS,MAAM,SAAyB;AAC9C,iBAAO,KAAK,WAAW,KAAK,MAAM,KAAK,MAAM,OAAO,EAAE;AACtD,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AACD,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,uBAAuB,KAAK,IAAI,CAAC;AAAA,UACnE;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,UAAU,MAAM;AACtB,iBAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK;AAChD,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF,CAAC;AACD,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,6BAA6B,CAAC;AAAA,UAChE;AAAA,QACF;AAAA,QAEA,KAAK,UAAU;AACb,gBAAM,SAAS,MAAM;AACrB,iBAAO,KAAK,kBAAkB,MAAM,EAAE;AACtC,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF,CAAC;AACD,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sBAAsB,MAAM,KAAK,CAAC;AAAA,UACpE;AAAA,QACF;AAAA,QAEA;AACE,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,IAAI,GAAG,CAAC;AAAA,YACzD,SAAS;AAAA,UACX;AAAA,MACJ;AAAA,IACF,CAAC;AAID,IAAM,0BAA0B,EAAE,OAAO;AAAA,MACvC,QAAQ,EAAE,QAAQ,iDAAiD;AAAA,MACnE,QAAQ,EAAE,OAAO;AAAA,QACf,YAAY,EAAE,OAAO;AAAA,QACrB,WAAW,EAAE,OAAO;AAAA,QACpB,aAAa,EAAE,OAAO;AAAA,QACtB,eAAe,EAAE,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAGD,WAAO;AAAA,MACL;AAAA,MACA,OAAO,iBAAiB;AACtB,cAAM,EAAE,YAAY,WAAW,aAAa,cAAc,IAAI,aAAa;AAC3E,eAAO,KAAK,uBAAuB,UAAU,MAAM,SAAS,MAAM,WAAW,EAAE;AAC/E,kBAAU,KAAK;AAAA,UACb,MAAM;AAAA,UACN;AAAA,UACA,WAAW;AAAA,UACX,UAAU;AAAA,UACV;AAAA,UACA,cAAc;AAAA,UACd,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAwEA,SAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,aAAO,MAAM,gBAAgB,GAAG;AAChC,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA;AAAA;;;AClRD;AALA,SAAS,SAAAG,cAAa;AACtB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,cAAc;AACrB,SAAS,iBAAAC,sBAAqB;;;ACC9B;AALA,SAAS,aAAgC;AACzC,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,cAAc;AACvB,OAAO,eAAuC;AAqBvC,SAAS,WACd,SACA,WAA4B,QAAQ,UACpC,MAAyB,QAAQ,KACb;AACpB,QAAM,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,MAAMC,MAAK,SAAS,EAAE,OAAO,OAAO;AAC9E,aAAW,OAAO,aAAa,UAAU,CAAC,QAAQ,MAAM,IAAI,CAAC,EAAE,GAAG;AAChE,eAAW,OAAO,MAAM;AACtB,YAAM,OAAOA,MAAK,KAAK,KAAK,UAAU,GAAG;AACzC,UAAIC,IAAG,WAAW,IAAI,EAAG,QAAO;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AACT;;;AD/BA;AACA;AACA;AAEA,IAAMC,aAAYC,MAAK,QAAQC,eAAc,YAAY,GAAG,CAAC;AAE7D,SAAS,YAAY,OAAwC;AAC3D,SAAO,QAAQ,EAAE,eAAe,UAAU,KAAK,GAAG,IAAI,CAAC;AACzD;AAEA,SAAS,UAAU,aAAqB,MAAc,OAAwB;AAC5E,SAAO,UAAU,WAAW,IAAI,IAAI,IAAI,QAAQ,UAAU,mBAAmB,KAAK,CAAC,KAAK,EAAE;AAC5F;AAEA,SAAS,aAAa;AACpB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAoBb;AACD;AAEA,eAAe,kBAAkB;AAC/B,MAAI;AACF,UAAM,UAAUD,MAAK,KAAKD,YAAW,MAAM,cAAc;AACzD,UAAM,MAAM,KAAK,MAAMG,IAAG,aAAa,SAAS,OAAO,CAAC;AACxD,UAAM,iBAAiB,IAAI;AAE3B,UAAM,MAAM,MAAM,MAAM,wDAAwD;AAAA,MAC9E,QAAQ,YAAY,QAAQ,GAAI;AAAA,IAClC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI;AACb,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,gBAAgB,KAAK;AAE3B,QAAI,kBAAkB,gBAAgB;AACpC,cAAQ,IAAI;AAAA,gCAA8B,cAAc,WAAM,aAAa,EAAE;AAC7E,cAAQ,IAAI;AAAA,CAA4C;AAAA,IAC1D;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEA,eAAe,SAAS,QAAiB;AACvC,QAAMC,UAAS,WAAW;AAC1B,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,QAAM,cAAc,SAAS,YAAY,cAAc;AAGvD,kBAAgB;AAGhB,MAAID,IAAG,WAAW,QAAQ,GAAG;AAC3B,UAAM,MAAM,SAASA,IAAG,aAAa,UAAU,OAAO,EAAE,KAAK,GAAG,EAAE;AAClE,QAAI,iBAAiB,GAAG,GAAG;AACzB,cAAQ,IAAI,gCAAgC,GAAG,iBAAiB,UAAU,aAAa,MAAMC,QAAO,IAAI,KAAK,CAAC,EAAE;AAChH;AAAA,IACF;AAEA,IAAAD,IAAG,WAAW,QAAQ;AAAA,EACxB;AAEA,MAAI,QAAQ;AACV,oBAAgB;AAChB,UAAM,QAAQA,IAAG,SAAS,UAAU,GAAG;AACvC,UAAM,YAAYF,MAAK,KAAKD,YAAW,OAAO,WAAW;AAEzD,UAAM,QAAQK,OAAM,QAAQ,UAAU,CAAC,SAAS,GAAG;AAAA,MACjD,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,OAAO,KAAK;AAAA,MAC9B,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,IACxB,CAAC;AAED,QAAI,MAAM,KAAK;AACb,MAAAF,IAAG,cAAc,UAAU,OAAO,MAAM,GAAG,GAAG,OAAO;AACrD,YAAM,MAAM;AACZ,cAAQ,IAAI,+BAA+B,MAAM,GAAG,GAAG;AACvD,cAAQ,IAAI,cAAc,UAAU,aAAa,MAAMC,QAAO,IAAI,KAAK,CAAC,EAAE;AAC1E,cAAQ,IAAI,UAAUA,QAAO,IAAI,KAAK,EAAE;AACxC,cAAQ,IAAI,SAAS,QAAQ,EAAE;AAAA,IACjC,OAAO;AACL,cAAQ,MAAM,4BAA4B;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AAEL,YAAQ,IAAI,0BAA0B,WAAW,IAAI,IAAI,yBAAyB;AAClF,YAAQ,IAAI,cAAc,UAAU,aAAa,MAAMA,QAAO,IAAI,KAAK,CAAC,EAAE;AAC1E,YAAQ,IAAI,UAAUA,QAAO,IAAI,KAAK,EAAE;AACxC,YAAQ,IAAI,SAAS,QAAQ,EAAE;AAC/B,sBAAkB,gBAAgB;AAClC,eAAW,gBAAgB;AAC3B,UAAM,EAAE,WAAAE,WAAU,IAAI,MAAM;AAC5B,UAAM,MAAM,IAAIA,WAAUF,OAAM;AAChC,UAAM,IAAI,MAAM;AAGhB,oBAAgB;AAChB,IAAAD,IAAG,cAAc,UAAU,OAAO,QAAQ,GAAG,GAAG,OAAO;AAEvD,UAAM,WAAW,YAAY;AAC3B,cAAQ,IAAI,oBAAoB;AAChC,YAAM,IAAI,KAAK;AACf,UAAIA,IAAG,WAAW,QAAQ,EAAG,CAAAA,IAAG,WAAW,QAAQ;AACnD,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,GAAG,UAAU,QAAQ;AAC7B,YAAQ,GAAG,WAAW,QAAQ;AAAA,EAChC;AACF;AAEA,SAAS,UAAU;AACjB,MAAI,CAACA,IAAG,WAAW,QAAQ,GAAG;AAC5B,YAAQ,IAAI,wCAAwC;AACpD;AAAA,EACF;AAEA,QAAM,MAAM,SAASA,IAAG,aAAa,UAAU,OAAO,EAAE,KAAK,GAAG,EAAE;AAClE,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAC3B,YAAQ,IAAI,qBAAqB,GAAG,GAAG;AAAA,EACzC,QAAQ;AACN,YAAQ,IAAI,kDAAkD;AAAA,EAChE;AACA,EAAAA,IAAG,WAAW,QAAQ;AACxB;AAEA,eAAe,YAAY;AACzB,QAAMC,UAAS,WAAW;AAC1B,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,QAAM,cAAc,SAAS,YAAY,cAAc;AAGvD,MAAI,UAAU;AACd,MAAID,IAAG,WAAW,QAAQ,GAAG;AAC3B,UAAM,MAAM,SAASA,IAAG,aAAa,UAAU,OAAO,EAAE,KAAK,GAAG,EAAE;AAClE,QAAI,iBAAiB,GAAG,GAAG;AACzB,gBAAU,iBAAiB,GAAG;AAAA,IAChC,OAAO;AACL,gBAAU;AAAA,IACZ;AAAA,EACF;AAGA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,UAAU,IAAI,IAAI,IAAI,eAAe,EAAE,SAAS,YAAYC,QAAO,IAAI,KAAK,EAAE,CAAC;AACvG,QAAI,IAAI,IAAI;AACV,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,cAAQ,IAAI,sBAAsB,KAAK,GAAG,GAAG;AAC7C,cAAQ,IAAI,SAAS,KAAK,IAAI,EAAE;AAChC,cAAQ,IAAI,aAAa,KAAK,QAAQ,EAAE;AACxC,cAAQ,IAAI,WAAW,KAAK,MAAM,KAAK,SAAS,GAAI,CAAC,GAAG;AACxD,cAAQ,IAAI,qBAAqB,WAAW,IAAI,IAAI,EAAE;AACtD,YAAM,QAAQA,QAAO,IAAI;AACzB,UAAI,OAAO;AACT,gBAAQ,IAAI,UAAU,MAAM,MAAM,GAAG,CAAC,CAAC,aAAa;AAAA,MACtD;AACA;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,UAAQ,IAAI,QAAQ,OAAO,EAAE;AAC7B,UAAQ,IAAI,sBAAsB,WAAW,IAAI,IAAI,EAAE;AACzD;AAEA,SAAS,MAAM,WAAoB;AACjC,QAAM,UAAU,eAAe,SAAS;AACxC,UAAQ,IAAI,yBAAyB,OAAO,EAAE;AAC9C,UAAQ,IAAI,4BAA4B;AACxC,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IAAI,0FAA0F;AACxG;AAEA,eAAe,OAAO;AACpB,QAAMA,UAAS,WAAW;AAC1B,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,QAAM,OAAOA,QAAO,IAAI,QAAQ;AAEhC,MAAI;AACF,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAIA,QAAO,IAAI,OAAO;AACpB,cAAQ,eAAe,IAAI,UAAUA,QAAO,IAAI,KAAK;AAAA,IACvD;AACA,UAAM,MAAM,MAAM,MAAM,UAAU,IAAI,IAAI,IAAI,eAAe;AAAA,MAC3D,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AAED,QAAI,IAAI,IAAI;AACV,cAAQ,IAAI,2DAA2D;AAAA,IACzE,OAAO;AACL,cAAQ,MAAM,gBAAgB,IAAI,MAAM,uBAAuB;AAAA,IACjE;AAAA,EACF,QAAQ;AACN,YAAQ,MAAM,6DAA6D;AAAA,EAC7E;AACF;AAEA,SAAS,IAAI,UAAmC;AAC9C,QAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AACpF,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,SAAG,MAAM;AACT,cAAQ,OAAO,KAAK,CAAC;AAAA,IACvB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,OAAO;AACpB,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAcH,MAAK,SAAS,GAAG;AAErC,UAAQ,IAAI;AAAA,yBAA4B,WAAW;AAAA,CAAK;AAExD,QAAM,SAAS,MAAM,IAAI,kCAAkC;AAE3D,MAAI,MAA8B;AAAA,IAChC,2BAA2B;AAAA,EAC7B;AAEA,MAAI,OAAO,YAAY,MAAM,KAAK;AAChC,UAAM,OAAO,MAAM,IAAI,iCAAiC;AACxD,UAAM,OAAO,MAAM,IAAI,4BAA4B;AACnD,UAAM,QAAQ,MAAM,IAAI,aAAa;AAErC,QAAI,CAAC,MAAM;AACT,cAAQ,MAAM,mBAAmB;AACjC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,wBAAwB;AAC5B,QAAI,KAAM,KAAI,wBAAwB;AACtC,QAAI,MAAO,KAAI,yBAAyB;AAAA,EAC1C;AAGA,QAAM,UAAUA,MAAK,KAAK,KAAK,WAAW;AAC1C,MAAI,YAAiC,CAAC;AACtC,MAAIE,IAAG,WAAW,OAAO,GAAG;AAC1B,QAAI;AAAE,kBAAY,KAAK,MAAMA,IAAG,aAAa,SAAS,OAAO,CAAC;AAAA,IAAG,QAAQ;AAAE,kBAAY,CAAC;AAAA,IAAG;AAAA,EAC7F;AACA,MAAI,CAAC,UAAU,WAAY,WAAU,aAAa,CAAC;AACnD,YAAU,WAAW,cAAc,IAAI;AAAA,IACrC,SAAS;AAAA,IACT,MAAM,CAAC,MAAM,sBAAsB,OAAO;AAAA,IAC1C;AAAA,EACF;AACA,EAAAA,IAAG,cAAc,SAAS,KAAK,UAAU,WAAW,MAAM,CAAC,GAAG,OAAO;AAErE,UAAQ,IAAI;AAAA,iBAAe,OAAO,EAAE;AAEpC,MAAI,OAAO,YAAY,MAAM,KAAK;AAEhC,UAAMC,UAAS,WAAW;AAC1B,UAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,UAAM,OAAOA,QAAO,IAAI,QAAQ;AAChC,UAAM,cAAc,SAAS,YAAY,cAAc;AACvD,QAAI,aAAa;AACjB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,UAAU,IAAI,IAAI,IAAI,eAAe,EAAE,SAAS,YAAYA,QAAO,IAAI,KAAK,EAAE,CAAC;AACvG,mBAAa,IAAI;AAAA,IACnB,QAAQ;AAAA,IAAC;AAET,QAAI,YAAY;AACd,cAAQ,IAAI,uBAAkB;AAAA,IAChC,OAAO;AACL,cAAQ,IAAI,2CAAsC;AAClD,cAAQ,IAAI,0BAA0B;AAAA,IACxC;AACA,YAAQ,IAAI,uBAAuB,WAAW,IAAI,IAAI,EAAE;AAExD,QAAI,iBAAiBA,SAAQ,WAAW,OAAO,MAAM,MAAS,GAAG;AAC/D,YAAM,SAAS,MAAM,IAAI,8EAA8E;AACvG,UAAI,OAAO,YAAY,MAAM,IAAK,aAAY,IAAI;AAAA,UAC7C,iBAAgB,KAAK;AAAA,IAC5B;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,WAAc;AAC1B,UAAQ,IAAI,sEAAsE;AAClF,UAAQ,IAAI;AAAA,qEAAwE;AACpF,UAAQ,IAAI,qGAAqG;AACjH,UAAQ,IAAI;AAAA,8EAAiF;AAC7F,UAAQ,IAAI;AAAA,CAAwE;AACtF;AAEA,SAAS,YAAY;AACnB,QAAM,QAAQ,iBAAiB;AAC/B,UAAQ,IAAI,UAAU,KAAK,EAAE;AAC/B;AAEA,SAAS,iBAAiB,KAAsB;AAC9C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAAmC;AAC1C,MAAI,CAACD,IAAG,WAAW,cAAc,EAAG,QAAO;AAC3C,QAAM,MAAM,SAASA,IAAG,aAAa,gBAAgB,OAAO,EAAE,KAAK,GAAG,EAAE;AACxE,SAAO,OAAO,MAAM,GAAG,IAAI,SAAY;AACzC;AAEA,SAAS,YAAY,SAAkB;AACrC,kBAAgB,OAAO;AACvB,UAAQ,IAAI,UACR,wGACA,mDAAmD;AACzD;AAEA,SAAS,aAAa;AACpB,QAAM,MAAM,aAAa;AACzB,MAAI,QAAQ,UAAa,iBAAiB,GAAG,GAAG;AAC9C,YAAQ,IAAI,0CAA0C,GAAG,GAAG;AAC5D;AAAA,EACF;AACA,kBAAgB;AAChB,QAAM,QAAQA,IAAG,SAAS,gBAAgB,GAAG;AAC7C,QAAM,QAAQE,OAAM,QAAQ,UAAU,CAACJ,MAAK,KAAKD,YAAW,SAAS,SAAS,CAAC,GAAG;AAAA,IAChF,UAAU;AAAA,IACV,OAAO,CAAC,UAAU,OAAO,KAAK;AAAA,IAC9B,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,EACxB,CAAC;AACD,QAAM,MAAM;AACZ,UAAQ,IAAI,+BAA+B,MAAM,GAAG,YAAY,cAAc,EAAE;AAClF;AAEA,SAAS,YAAY;AACnB,QAAM,MAAM,aAAa;AACzB,MAAI,QAAQ,QAAW;AACrB,YAAQ,IAAI,8BAA8B;AAC1C;AAAA,EACF;AACA,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAC3B,YAAQ,IAAI,+BAA+B,GAAG,GAAG;AAAA,EACnD,QAAQ;AACN,YAAQ,IAAI,4DAA4D;AAAA,EAC1E;AACA,MAAI;AAAE,IAAAG,IAAG,WAAW,cAAc;AAAA,EAAG,QAAQ;AAAA,EAAC;AAChD;AAEA,SAAS,cAAc;AACrB,QAAM,MAAM,aAAa;AACzB,QAAM,QAAQ,QAAQ,SAAY,gBAAgB,iBAAiB,GAAG,IAAI,iBAAiB,GAAG,MAAM;AACpG,UAAQ,IAAI,kBAAkB,KAAK,EAAE;AACrC,UAAQ,IAAI,mBAAmB,WAAW,EAAE,OAAO,UAAU,YAAY,UAAU,EAAE;AACvF;AAGA,eAAeI,QAAO;AACpB,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,MAAM,KAAK,CAAC;AAClB,QAAM,MAAM,KAAK,CAAC;AAElB,MAAI,CAAC,OAAO,QAAQ,UAAU,QAAQ,YAAY,QAAQ,MAAM;AAC9D,eAAW;AACX;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS;AAEnB,UAAM;AACN;AAAA,EACF;AAEA,MAAI,QAAQ,QAAQ;AAClB,UAAM,KAAK;AACX;AAAA,EACF;AAEA,MAAI,QAAQ,OAAO;AACjB,QAAI,QAAQ,SAAS;AACnB,YAAM,SAAS,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,UAAU;AAC9D,YAAM,SAAS,MAAM;AAAA,IACvB,WAAW,QAAQ,QAAQ;AACzB,cAAQ;AAAA,IACV,WAAW,QAAQ,UAAU;AAC3B,YAAM,UAAU;AAAA,IAClB,OAAO;AACL,cAAQ,MAAM,wBAAwB,GAAG,EAAE;AAC3C,iBAAW;AACX,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS;AACnB,QAAI,QAAQ,SAAU,aAAY,IAAI;AAAA,aAC7B,QAAQ,UAAW,aAAY,KAAK;AAAA,aACpC,QAAQ,QAAS,YAAW;AAAA,aAC5B,QAAQ,OAAQ,WAAU;AAAA,aAC1B,QAAQ,SAAU,aAAY;AAAA,SAClC;AACH,cAAQ,MAAM,0BAA0B,GAAG,EAAE;AAC7C,iBAAW;AACX,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS;AACnB,UAAM,KAAK,CAAC,CAAC;AACb;AAAA,EACF;AAEA,MAAI,QAAQ,QAAQ;AAClB,UAAM,KAAK;AACX;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS;AACnB,cAAU;AACV;AAAA,EACF;AAEA,UAAQ,MAAM,oBAAoB,GAAG,EAAE;AACvC,aAAW;AACX,UAAQ,KAAK,CAAC;AAChB;AAEAA,MAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,SAAO,MAAM,cAAc,GAAG;AAC9B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["path","config","fs","sessionId","peerName","sessionId","sessionLabel","sessionId","fs","path","randomUUID","config","sessionId","sessionLabel","spawn","fs","path","fs","path","WebSocket","randomUUID","config","sessionId","WebSocket","sessionId","sessionName","hubHost","hubPort","fs","os","path","init_peer_name","server_exports","randomUUID","path","init_server","init_peer_name","spawn","fs","path","fileURLToPath","fs","path","path","fs","__dirname","path","fileURLToPath","fs","config","spawn","HubServer","main"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/constants.ts","../src/shared/config.ts","../src/shared/logger.ts","../src/shared/crash-guard.ts","../src/shared/hub-url.ts","../src/shared/peer-name.ts","../src/hub/session-manager.ts","../src/hub/notifier.ts","../src/shared/session-label.ts","../src/shared/permission-key.ts","../src/hub/telegram.ts","../src/hub/codex-supervisor.ts","../src/shared/entry.ts","../src/hub/auth.ts","../src/hub/server.ts","../src/channel/hub-client.ts","../src/channel/peer-name.ts","../src/channel/server.ts","../src/cli.ts","../src/codex/transport.ts","../src/codex/run.ts","../src/codex/rpc.ts","../src/codex/mapping.ts","../src/hub/readiness.ts","../src/codex/hub-target.ts","../src/codex/start-check.ts","../src/codex/instance-lock.ts","../src/codex/control-cli.ts"],"sourcesContent":["import path from 'node:path';\r\nimport os from 'node:os';\r\n\r\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\r\nexport const DEFAULT_HUB_PORT = 7900;\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\r\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\r\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\r\nexport const UPLOADS_DIR = path.join(CONFIG_DIR, 'uploads');\r\nexport const CODEX_PID_FILE = path.join(CONFIG_DIR, 'codex.pid');\r\nexport const CODEX_LOG_FILE = path.join(CONFIG_DIR, 'codex.log');\r\n\r\nexport const WS_PATH_CHANNEL = '/ws/channel';\r\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\r\nexport const WS_PATH_CODEX = '/ws/codex';\r\n\r\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\r\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\r\nimport type { AppConfig } from './types.js';\r\n\r\nconst DEFAULT_CONFIG: AppConfig = {\r\n hub: {\r\n host: DEFAULT_HUB_HOST,\r\n port: DEFAULT_HUB_PORT,\r\n },\r\n notifications: {\r\n desktop: true,\r\n sound: true,\r\n },\r\n webhooks: [],\r\n};\r\n\r\nexport function ensureConfigDir(): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n}\r\n\r\nexport function loadConfig(): AppConfig {\r\n ensureConfigDir();\r\n let config: AppConfig;\r\n if (!fs.existsSync(CONFIG_FILE)) {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n } else {\r\n try {\r\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\r\n const parsed = JSON.parse(raw);\r\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub }, ...(parsed.telegram ? { telegram: parsed.telegram } : {}) };\r\n } catch {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n }\r\n }\r\n\r\n // Auto-generate token if missing\r\n if (!config.hub.token) {\r\n config.hub.token = randomUUID();\r\n saveConfig(config);\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** Get the current token, generating one if needed */\r\nexport function getOrCreateToken(): string {\r\n const config = loadConfig();\r\n return config.hub.token!;\r\n}\r\n\r\nexport function saveConfig(config: AppConfig): void {\r\n ensureConfigDir();\r\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\r\n}\r\n\r\nexport function setCodexEnabled(enabled: boolean): AppConfig {\r\n const config = loadConfig();\r\n config.codex = { ...config.codex, enabled };\r\n saveConfig(config);\r\n return config;\r\n}\r\n\r\nexport function shouldOfferCodex(config: AppConfig, installed: boolean): boolean {\r\n return installed && config.codex === undefined;\r\n}\r\n\r\n/**\r\n * Add claude-alarm as an MCP channel server to .mcp.json\r\n */\r\nexport function setupMcpConfig(targetDir?: string): string {\r\n const dir = targetDir ?? process.cwd();\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try {\r\n mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\r\n } catch {\r\n mcpConfig = {};\r\n }\r\n }\r\n\r\n if (!mcpConfig.mcpServers) {\r\n mcpConfig.mcpServers = {};\r\n }\r\n\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env: {\r\n CLAUDE_ALARM_SESSION_NAME: path.basename(dir),\r\n },\r\n };\r\n\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n return mcpPath;\r\n}\r\n","/**\r\n * Logger that writes to stderr only.\r\n * CRITICAL: In MCP channel servers, stdout is used for the stdio protocol.\r\n * Any console.log() would corrupt the protocol. Always use this logger.\r\n */\r\nexport const logger = {\r\n info(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm] ${msg}`, ...args);\r\n },\r\n warn(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm WARN] ${msg}`, ...args);\r\n },\r\n error(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm ERROR] ${msg}`, ...args);\r\n },\r\n debug(msg: string, ...args: unknown[]) {\r\n if (process.env.CLAUDE_ALARM_DEBUG) {\r\n console.error(`[claude-alarm DEBUG] ${msg}`, ...args);\r\n }\r\n },\r\n};\r\n","import fs from 'node:fs';\nimport { CONFIG_DIR, LOG_FILE } from './constants.js';\nimport { logger } from './logger.js';\n\nconst BURST_WINDOW_MS = 60_000;\nconst BURST_LIMIT = 10;\n\nfunction appendToLog(text: string): void {\n try {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.appendFileSync(LOG_FILE, text, 'utf-8');\n } catch {}\n}\n\nfunction describe(err: unknown): string {\n if (err instanceof Error) return err.stack ?? `${err.name}: ${err.message}`;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\n/**\n * Last-resort net for exceptions no local handler caught.\n *\n * The hub keeps running afterwards: a notification relay that dies silently is\n * worse than one running on possibly-degraded state, and in foreground mode the\n * stack trace would otherwise vanish with the console window. A repeated burst\n * still exits, so a self-feeding error loop cannot spin forever.\n */\nexport function installCrashGuard(context: string): void {\n let recent: number[] = [];\n\n const handle = (kind: string, err: unknown): void => {\n const stack = describe(err);\n appendToLog(`\\n[${new Date().toISOString()}] ${kind} (${context})\\n${stack}\\n`);\n logger.error(`${kind} caught by crash guard: ${stack}`);\n\n const now = Date.now();\n recent = recent.filter((t) => now - t < BURST_WINDOW_MS);\n recent.push(now);\n\n if (recent.length > BURST_LIMIT) {\n const msg = `${recent.length} unhandled errors within ${BURST_WINDOW_MS / 1000}s — exiting`;\n appendToLog(`[${new Date().toISOString()}] ${msg}\\n`);\n logger.error(msg);\n process.exit(1);\n }\n };\n\n process.on('uncaughtException', (err) => handle('uncaughtException', err));\n process.on('unhandledRejection', (reason) => handle('unhandledRejection', reason));\n}\n\nexport function logStartup(context: string): void {\n appendToLog(`\\n[${new Date().toISOString()}] ${context} started (pid ${process.pid})\\n`);\n}\n","import net from 'node:net';\r\n\r\nexport function hubUrlHost(host: string): string {\r\n if (host === '' || host === '0.0.0.0') return '127.0.0.1';\r\n if (host === '::') return '[::1]';\r\n return net.isIPv6(host) ? `[${host}]` : host;\r\n}\r\n","// A peer name is pasted verbatim into another session's prompt as\r\n// SendMessage to \"<name>\", so quotes, brackets, backslashes and control\r\n// characters must never pass.\r\nconst SAFE_PEER_NAME = /^[^\\u0000-\\u001f\\u007f\"\\\\[\\]]{1,128}$/;\r\n\r\nexport function isValidPeerName(name: unknown): name is string {\r\n return typeof name === 'string' && name.trim() !== '' && SAFE_PEER_NAME.test(name);\r\n}\r\n","import type { SessionInfo, SessionStatus } from '../shared/types.js';\r\nimport { isValidPeerName } from '../shared/peer-name.js';\r\n\r\nexport class SessionManager {\r\n private sessions = new Map<string, SessionInfo>();\r\n\r\n register(session: SessionInfo): void {\r\n // Auto-number duplicate names\r\n const baseName = this.baseName(session);\r\n const existing = Array.from(this.sessions.values()).filter(\r\n s => s.id !== session.id && this.baseName(s) === baseName,\r\n );\r\n if (existing.length > 0) {\r\n // Number this one\r\n const usedNums = existing.map(s => {\r\n const m = s.displayName?.match(/\\((\\d+)\\)$/);\r\n return m ? parseInt(m[1], 10) : 1;\r\n });\r\n // Ensure first existing session has (1) if it doesn't yet\r\n for (const s of existing) {\r\n if (!s.displayName?.match(/\\(\\d+\\)$/)) {\r\n s.displayName = `${baseName} (1)`;\r\n }\r\n }\r\n const nextNum = Math.max(...usedNums, 1) + 1;\r\n session.displayName = `${baseName} (${nextNum})`;\r\n } else {\r\n session.displayName = baseName;\r\n }\r\n this.sessions.set(session.id, { ...session, peerName: isValidPeerName(session.peerName) ? session.peerName : undefined });\r\n }\r\n\r\n unregister(sessionId: string): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n this.sessions.delete(sessionId);\r\n return session;\r\n }\r\n\r\n updateStatus(sessionId: string, status: SessionStatus): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.status = status;\r\n session.lastActivity = Date.now();\r\n }\r\n return session;\r\n }\r\n\r\n setPeerName(sessionId: string, peerName: string | undefined): SessionInfo | undefined {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.peerName = isValidPeerName(peerName) ? peerName : undefined;\r\n }\r\n return session;\r\n }\r\n\r\n updateActivity(sessionId: string): void {\r\n const session = this.sessions.get(sessionId);\r\n if (session) {\r\n session.lastActivity = Date.now();\r\n }\r\n }\r\n\r\n get(sessionId: string): SessionInfo | undefined {\r\n return this.sessions.get(sessionId);\r\n }\r\n\r\n getAll(): SessionInfo[] {\r\n return Array.from(this.sessions.values());\r\n }\r\n\r\n count(): number {\r\n return this.sessions.size;\r\n }\r\n\r\n private baseName(session: SessionInfo): string {\r\n return session.title || session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\r\n }\r\n}\r\n","import notifier from 'node-notifier';\r\nimport { execFile } from 'node:child_process';\r\nimport { logger } from '../shared/logger.js';\r\nimport type { NotifyLevel, WebhookConfig } from '../shared/types.js';\r\nimport type { TelegramBot } from './telegram.js';\r\n\r\nexport class Notifier {\r\n private webhooks: WebhookConfig[] = [];\r\n private desktopEnabled = true;\r\n private notificationSettingsOpened = false;\r\n private dashboardUrl?: string;\r\n private telegramBot?: TelegramBot;\r\n\r\n configure(options: { desktop?: boolean; webhooks?: WebhookConfig[]; dashboardUrl?: string; telegramBot?: TelegramBot }): void {\r\n if (options.dashboardUrl) this.dashboardUrl = options.dashboardUrl;\r\n if (options.desktop !== undefined) this.desktopEnabled = options.desktop;\r\n if (options.webhooks) this.webhooks = options.webhooks;\r\n if (options.telegramBot) this.telegramBot = options.telegramBot;\r\n }\r\n\r\n async notify(title: string, message: string, level: NotifyLevel = 'info'): Promise<void> {\r\n await this.notifyWithSession(undefined, undefined, title, message, level);\r\n }\r\n\r\n async notifyWithSession(sessionId: string | undefined, sessionLabel: string | undefined, title: string, message: string, level: NotifyLevel = 'info'): Promise<void> {\r\n const promises: Promise<void>[] = [];\r\n\r\n if (this.desktopEnabled) {\r\n promises.push(this.sendDesktop(title, message, level));\r\n }\r\n\r\n for (const webhook of this.webhooks) {\r\n promises.push(this.sendWebhook(webhook, title, message, level));\r\n }\r\n\r\n if (this.telegramBot && sessionId && sessionLabel) {\r\n promises.push(this.telegramBot.sendNotification(sessionId, sessionLabel, title, message));\r\n }\r\n\r\n await Promise.allSettled(promises);\r\n }\r\n\r\n private async sendDesktop(title: string, message: string, _level: NotifyLevel): Promise<void> {\r\n if (process.platform === 'win32') {\r\n // Check if notifications are enabled by running snoretoast directly\r\n const enabled = await this.checkWindowsNotifications();\r\n if (!enabled) {\r\n this.openNotificationSettings();\r\n return;\r\n }\r\n }\r\n\r\n return new Promise((resolve) => {\r\n const notification = (notifier as any).notify(\r\n {\r\n title: `Claude Alarm: ${title}`,\r\n message,\r\n sound: true,\r\n wait: true,\r\n },\r\n (err: Error | null) => {\r\n if (err) {\r\n logger.warn(`Desktop notification failed: ${err.message}`);\r\n }\r\n resolve();\r\n },\r\n );\r\n\r\n // No click handler - dashboard is already open and notifications\r\n // can be clicked there to focus the relevant session\r\n });\r\n }\r\n\r\n private checkWindowsNotifications(): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n execFile(\r\n 'powershell',\r\n ['-Command', '(Get-ItemProperty -Path \"HKCU:\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\PushNotifications\" -Name ToastEnabled -ErrorAction SilentlyContinue).ToastEnabled'],\r\n { windowsHide: true },\r\n (err, stdout) => {\r\n if (err) { resolve(true); return; } // assume enabled on error\r\n const value = stdout.trim();\r\n resolve(value !== '0');\r\n },\r\n );\r\n });\r\n }\r\n\r\n private openNotificationSettings(): void {\r\n if (this.notificationSettingsOpened) return;\r\n this.notificationSettingsOpened = true;\r\n\r\n logger.warn('Windows notifications are disabled. Opening notification settings...');\r\n logger.warn('Please enable notifications for this app, then try again.');\r\n\r\n if (process.platform === 'win32') {\r\n execFile('powershell', ['-Command', 'Start-Process ms-settings:notifications'], { windowsHide: true });\r\n }\r\n\r\n // Allow re-opening after 5 minutes\r\n setTimeout(() => { this.notificationSettingsOpened = false; }, 5 * 60 * 1000);\r\n }\r\n\r\n private async sendWebhook(\r\n webhook: WebhookConfig,\r\n title: string,\r\n message: string,\r\n level: NotifyLevel,\r\n ): Promise<void> {\r\n try {\r\n const response = await fetch(webhook.url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...webhook.headers,\r\n },\r\n body: JSON.stringify({\r\n title,\r\n message,\r\n level,\r\n timestamp: Date.now(),\r\n source: 'claude-alarm',\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n logger.warn(`Webhook ${webhook.url} returned ${response.status}`);\r\n }\r\n } catch (err) {\r\n logger.warn(`Webhook ${webhook.url} failed: ${(err as Error).message}`);\r\n }\r\n }\r\n}\r\n","import type { SessionInfo } from './types.js';\r\n\r\nexport function sessionLabel(session: SessionInfo): string {\r\n const base = session.displayName || session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\r\n return session.agentKind === 'codex' ? `Codex · ${base}` : base;\r\n}\r\n","export function permissionKey(sessionId: string, requestId: string): string {\r\n return `${sessionId}\\n${requestId}`;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { logger } from '../shared/logger.js';\r\nimport { UPLOADS_DIR } from '../shared/constants.js';\r\nimport type { TelegramConfig, SessionInfo, PermissionChoice } from '../shared/types.js';\r\nimport { sessionLabel } from '../shared/session-label.js';\r\nimport { permissionKey } from '../shared/permission-key.js';\r\n\r\nconst TELEGRAM_API = 'https://api.telegram.org/bot';\r\nconst MAX_CHOICE_MESSAGES = 200;\r\nconst MAX_SELECTIONS = 20;\r\nconst MAX_VISIBLE_CHARS = 4000;\r\nconst TRUNCATED = '…(truncated)';\r\nconst MAX_PHOTO_BYTES = 10 * 1024 * 1024;\r\n\r\n// Telegram's 4096 limit counts the text left after parsing entities: tags are free and each escape is one character.\r\nexport function visibleLength(html: string): number {\r\n return html.replace(/<[^>]*>/g, '').replace(/&(?:amp|lt|gt);/g, '&').length;\r\n}\r\n\r\ninterface ChoiceMessage {\r\n html: string;\r\n messageId?: number;\r\n outcome?: string;\r\n}\r\n\r\ninterface PendingSelection {\r\n text?: string;\r\n photoFileId?: string;\r\n caption?: string;\r\n sessionIds: string[];\r\n}\r\n\r\ninterface TelegramPhotoSize {\r\n file_id: string;\r\n file_unique_id: string;\r\n width: number;\r\n height: number;\r\n file_size?: number;\r\n}\r\n\r\ninterface TelegramMessage {\r\n message_id: number;\r\n chat: { id: number };\r\n text?: string;\r\n caption?: string;\r\n photo?: TelegramPhotoSize[];\r\n reply_to_message?: { message_id: number };\r\n}\r\n\r\ninterface TelegramCallbackQuery {\r\n id: string;\r\n from: { id: number };\r\n message?: TelegramMessage;\r\n data?: string;\r\n}\r\n\r\ninterface TelegramUpdate {\r\n update_id: number;\r\n message?: TelegramMessage;\r\n callback_query?: TelegramCallbackQuery;\r\n}\r\n\r\nexport class TelegramBot {\r\n private config: TelegramConfig;\r\n private offset = 0;\r\n private polling = false;\r\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n // message_id -> sessionId mapping for reply-based routing\r\n private messageSessionMap = new Map<number, string>();\r\n\r\n // Callback: when a text message arrives from Telegram for a session\r\n public onMessageToSession?: (sessionId: string, content: string) => void;\r\n // Callback: when an image arrives from Telegram for a session\r\n public onImageToSession?: (sessionId: string, imagePath: string, mimeType: string, caption?: string) => void;\r\n // Callback: when a permission verdict arrives from Telegram\r\n public onPermissionVerdict?: (sessionId: string, requestId: string, behavior: 'allow' | 'deny') => void;\r\n // Callback: when a Codex approval choice arrives from Telegram\r\n public onChoiceVerdict?: (sessionId: string, requestId: string, choiceId: string) => void;\r\n // Telegram caps callback_data at 64 bytes, so buttons carry a short token instead of the ids.\r\n private choiceTokens = new Map<string, { sessionId: string; requestId: string; choiceId: string; label: string }>();\r\n private choiceMessages = new Map<string, ChoiceMessage>();\r\n // Callback: get current sessions list\r\n public getSessions?: () => SessionInfo[];\r\n private latestSelection: string | undefined;\r\n // Keyed by prompt, not by chat: buttons from an older prompt must still send that prompt's message.\r\n private selections = new Map<string, PendingSelection>();\r\n\r\n constructor(config: TelegramConfig) {\r\n this.config = config;\r\n }\r\n\r\n private get apiUrl(): string {\r\n return `${TELEGRAM_API}${this.config.botToken}`;\r\n }\r\n\r\n /** Send a notification message to Telegram */\r\n async sendNotification(sessionId: string, _sessionLabel: string, title: string, message: string): Promise<void> {\r\n const text = this.fitNotification(title, message);\r\n const result = await this.sendMessage(text);\r\n if (result?.message_id) {\r\n this.messageSessionMap.set(result.message_id, sessionId);\r\n // Cleanup old mappings (keep last 200)\r\n if (this.messageSessionMap.size > 200) {\r\n const keys = [...this.messageSessionMap.keys()];\r\n for (let i = 0; i < keys.length - 200; i++) {\r\n this.messageSessionMap.delete(keys[i]);\r\n }\r\n }\r\n }\r\n }\r\n\r\n private fitNotification(title: string, message: string): string {\r\n const render = (body: string) => `<b>${this.escHtml(title)}</b>\\n${this.mdToHtml(body)}`;\r\n const full = render(message);\r\n if (visibleLength(full) <= MAX_VISIBLE_CHARS) return full;\r\n const cut = (n: number) => {\r\n // Cutting between the halves of a surrogate pair would send invalid UTF-16.\r\n const end = n > 0 && /[\\uD800-\\uDBFF]/.test(message[n - 1]) ? n - 1 : n;\r\n return render(`${message.slice(0, end)}\\n${TRUNCATED}`);\r\n };\r\n let lo = 0;\r\n let hi = message.length;\r\n while (lo < hi) {\r\n const mid = Math.ceil((lo + hi) / 2);\r\n if (visibleLength(cut(mid)) <= MAX_VISIBLE_CHARS) lo = mid;\r\n else hi = mid - 1;\r\n }\r\n return cut(lo);\r\n }\r\n\r\n /** Send a text message to the configured chat */\r\n private async sendMessage(text: string, replyToMessageId?: number, replyMarkup?: unknown): Promise<{ message_id: number } | null> {\r\n try {\r\n const body: Record<string, unknown> = {\r\n chat_id: this.config.chatId,\r\n text,\r\n parse_mode: 'HTML',\r\n };\r\n if (replyToMessageId) body.reply_to_message_id = replyToMessageId;\r\n if (replyMarkup) body.reply_markup = replyMarkup;\r\n\r\n const res = await fetch(`${this.apiUrl}/sendMessage`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!res.ok) {\r\n const err = await res.text();\r\n logger.warn(`Telegram sendMessage failed: ${res.status} ${err}`);\r\n return null;\r\n }\r\n\r\n const data = await res.json() as { ok: boolean; result: { message_id: number } };\r\n return data.ok ? data.result : null;\r\n } catch (err) {\r\n logger.warn(`Telegram sendMessage error: ${(err as Error).message}`);\r\n return null;\r\n }\r\n }\r\n\r\n /** Start long polling for incoming messages */\r\n startPolling(): void {\r\n if (this.polling) return;\r\n this.polling = true;\r\n logger.info('Telegram bot polling started');\r\n this.poll();\r\n }\r\n\r\n /** Stop polling */\r\n stopPolling(): void {\r\n this.polling = false;\r\n if (this.pollTimer) {\r\n clearTimeout(this.pollTimer);\r\n this.pollTimer = null;\r\n }\r\n logger.info('Telegram bot polling stopped');\r\n }\r\n\r\n private async poll(): Promise<void> {\r\n if (!this.polling) return;\r\n\r\n try {\r\n const res = await fetch(`${this.apiUrl}/getUpdates?offset=${this.offset}&timeout=30`, {\r\n signal: AbortSignal.timeout(35000),\r\n });\r\n\r\n if (!res.ok) {\r\n logger.warn(`Telegram getUpdates failed: ${res.status}`);\r\n this.scheduleNextPoll(5000);\r\n return;\r\n }\r\n\r\n const data = await res.json() as { ok: boolean; result: TelegramUpdate[] };\r\n if (data.ok && data.result.length > 0) {\r\n for (const update of data.result) {\r\n this.offset = update.update_id + 1;\r\n if (update.callback_query) {\r\n this.handleCallbackQuery(update.callback_query);\r\n } else if (update.message) {\r\n this.handleIncomingMessage(update.message);\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n if ((err as Error).name !== 'AbortError') {\r\n logger.warn(`Telegram poll error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n this.scheduleNextPoll(1000);\r\n }\r\n\r\n private scheduleNextPoll(delay: number): void {\r\n if (!this.polling) return;\r\n this.pollTimer = setTimeout(() => this.poll(), delay);\r\n }\r\n\r\n private async handleIncomingMessage(msg: TelegramMessage): Promise<void> {\r\n // Only process messages from the configured chat\r\n if (String(msg.chat.id) !== String(this.config.chatId)) return;\r\n\r\n const hasPhoto = msg.photo && msg.photo.length > 0;\r\n const text = (msg.text || msg.caption || '').trim();\r\n\r\n if (!text && !hasPhoto) return;\r\n if (hasPhoto) {\r\n const size = msg.photo![msg.photo!.length - 1].file_size;\r\n if (size !== undefined && size > MAX_PHOTO_BYTES) {\r\n this.photoNotDelivered('it is larger than 10 MB');\r\n return;\r\n }\r\n }\r\n\r\n // Check if it's a reply to a known message\r\n if (msg.reply_to_message) {\r\n const sessionId = this.messageSessionMap.get(msg.reply_to_message.message_id);\r\n if (sessionId) {\r\n if (hasPhoto) {\r\n await this.deliverPhotoToSession(sessionId, msg.photo!, text);\r\n } else {\r\n this.deliverToSession(sessionId, text);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (text) {\r\n const selectMatch = text.match(/^\\/s_(\\d+)$/);\r\n const latest = this.latestSelection;\r\n if (selectMatch && latest !== undefined) {\r\n const pending = this.selections.get(latest);\r\n if (pending) {\r\n this.selections.delete(latest);\r\n const session = this.pendingSession(pending, parseInt(selectMatch[1], 10) - 1);\r\n if (session) {\r\n let delivered = true;\r\n if (pending.photoFileId) {\r\n delivered = await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);\r\n } else if (pending.text) {\r\n this.deliverToSession(session.id, pending.text);\r\n }\r\n if (delivered) this.sendMessage(`Sent to [${this.getLabel(session)}]`);\r\n } else {\r\n this.sendMessage('Invalid session number.');\r\n }\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // No reply context — check sessions count\r\n const sessions = this.getSessions?.() ?? [];\r\n\r\n if (sessions.length === 0) {\r\n this.sendMessage('No active sessions connected.');\r\n return;\r\n }\r\n\r\n if (sessions.length === 1) {\r\n if (hasPhoto) {\r\n await this.deliverPhotoToSession(sessions[0].id, msg.photo!, text);\r\n } else {\r\n this.deliverToSession(sessions[0].id, text);\r\n }\r\n return;\r\n }\r\n\r\n const selectionId = randomUUID().replace(/-/g, '').slice(0, 8);\r\n this.latestSelection = selectionId;\r\n const sessionIds = sessions.map((s) => s.id);\r\n if (hasPhoto) {\r\n const largest = msg.photo![msg.photo!.length - 1];\r\n this.selections.set(selectionId, { photoFileId: largest.file_id, caption: text, sessionIds });\r\n } else {\r\n this.selections.set(selectionId, { text, sessionIds });\r\n }\r\n while (this.selections.size > MAX_SELECTIONS) this.selections.delete(this.selections.keys().next().value as string);\r\n const buttons = sessions.map((s, i) => ({\r\n text: this.getLabel(s),\r\n callback_data: `sel:${selectionId}:${i}`,\r\n }));\r\n // Arrange buttons in rows of 2\r\n const rows: Array<typeof buttons> = [];\r\n for (let i = 0; i < buttons.length; i += 2) {\r\n rows.push(buttons.slice(i, i + 2));\r\n }\r\n this.sendMessage('Multiple sessions active. Select one:', undefined, { inline_keyboard: rows });\r\n }\r\n\r\n private deliverToSession(sessionId: string, content: string): void {\r\n if (this.onMessageToSession) {\r\n this.onMessageToSession(sessionId, content);\r\n }\r\n }\r\n\r\n private async deliverPhotoToSession(sessionId: string, photos: TelegramPhotoSize[], caption?: string): Promise<boolean> {\r\n // Get the largest photo (last in array)\r\n const largest = photos[photos.length - 1];\r\n return this.deliverPhotoToSessionByFileId(sessionId, largest.file_id, caption);\r\n }\r\n\r\n private async deliverPhotoToSessionByFileId(sessionId: string, fileId: string, caption?: string): Promise<boolean> {\r\n try {\r\n const fileRes = await fetch(`${this.apiUrl}/getFile?file_id=${fileId}`);\r\n const fileData = fileRes.ok ? ((await fileRes.json()) as { ok: boolean; result?: { file_path: string } }) : undefined;\r\n if (!fileData?.ok || !fileData.result) {\r\n this.photoNotDelivered('Telegram did not return the file');\r\n return false;\r\n }\r\n\r\n const downloadUrl = `https://api.telegram.org/file/bot${this.config.botToken}/${fileData.result.file_path}`;\r\n const imgRes = await fetch(downloadUrl);\r\n if (!imgRes.ok) {\r\n this.photoNotDelivered('the download failed');\r\n return false;\r\n }\r\n const buffer = Buffer.from(await imgRes.arrayBuffer());\r\n if (buffer.length > MAX_PHOTO_BYTES) {\r\n this.photoNotDelivered('it is larger than 10 MB');\r\n return false;\r\n }\r\n\r\n const ext = fileData.result.file_path.split('.').pop() || 'jpg';\r\n const mimeType = ext === 'png' ? 'image/png' : ext === 'gif' ? 'image/gif' : ext === 'webp' ? 'image/webp' : 'image/jpeg';\r\n\r\n fs.mkdirSync(UPLOADS_DIR, { recursive: true });\r\n const filename = `${randomUUID()}.${ext}`;\r\n const filePath = path.join(UPLOADS_DIR, filename);\r\n fs.writeFileSync(filePath, buffer);\r\n logger.info(`Telegram photo saved: ${filename} (${buffer.length} bytes)`);\r\n\r\n if (this.onImageToSession) {\r\n this.onImageToSession(sessionId, filePath, mimeType, caption);\r\n }\r\n\r\n setTimeout(() => { try { fs.unlinkSync(filePath); } catch {} }, 5 * 60 * 1000).unref();\r\n return this.onImageToSession !== undefined;\r\n } catch (err) {\r\n logger.warn(`Telegram photo download failed: ${(err as Error).message}`);\r\n this.photoNotDelivered('the download failed');\r\n return false;\r\n }\r\n }\r\n\r\n private photoNotDelivered(reason: string): void {\r\n logger.warn(`Telegram photo not delivered: ${reason}`);\r\n void this.sendMessage(`Photo not delivered: ${reason}`);\r\n }\r\n\r\n // Buttons are numbered against the list shown when they were sent; Codex sessions come and go, so the live list may have shifted.\r\n private pendingSession(pending: { sessionIds: string[] }, idx: number): SessionInfo | undefined {\r\n const id = pending.sessionIds[idx];\r\n return id ? this.getSessions?.().find((s) => s.id === id) : undefined;\r\n }\r\n\r\n private getLabel(session: SessionInfo): string {\r\n return sessionLabel(session);\r\n }\r\n\r\n /** Send a permission request with inline buttons */\r\n async sendPermissionRequest(sessionId: string, sessionLabel: string, requestId: string, toolName: string, description: string, inputPreview: string): Promise<void> {\r\n // Parse inputPreview for readable display.\r\n // kind 'code' must stay byte-exact: running it through mdToHtml would swallow the\r\n // ** and ` characters the approver needs to judge what actually gets executed.\r\n // Only notify-style messages (prose Claude wrote) are safe to render as markdown.\r\n const isNotify = toolName.endsWith('__notify') || toolName === 'notify';\r\n let preview = inputPreview;\r\n let kind: 'code' | 'prose' = 'code';\r\n let lang = '';\r\n let truncated = false;\r\n try {\r\n const p = JSON.parse(inputPreview);\r\n if (p.command) { preview = `$ ${p.command}`; lang = 'bash'; }\r\n else if (p.title && p.message) { preview = p.message; kind = 'prose'; }\r\n else if (p.file_path) {\r\n preview = p.file_path;\r\n lang = this.langOf(p.file_path);\r\n if (p.content) { preview += '\\n' + p.content.slice(0, 3000); if (p.content.length > 500) truncated = true; }\r\n } else if (p.content && typeof p.content === 'string') {\r\n preview = p.content.slice(0, 3000);\r\n if (isNotify) kind = 'prose';\r\n if (p.content.length > 500) truncated = true;\r\n }\r\n } catch {\r\n truncated = true;\r\n const cmdMatch = inputPreview.match(/\"command\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n const contentMatch = inputPreview.match(/\"content\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n if (cmdMatch) { preview = `$ ${cmdMatch[1]}`; lang = 'bash'; }\r\n else if (contentMatch) preview = contentMatch[1].slice(0, 3000);\r\n }\r\n\r\n const truncNote = truncated ? '\\n\\n<i>...truncated</i>' : '';\r\n const previewSlice = preview.slice(0, 3000);\r\n // Short single-line commands read better inline; anything longer gets a code block\r\n const isShort = !previewSlice.includes('\\n') && previewSlice.length < 100;\r\n const langAttr = lang ? ` class=\"language-${lang}\"` : '';\r\n const previewHtml = kind === 'prose'\r\n ? this.mdToHtml(previewSlice)\r\n : isShort\r\n ? `<code>${this.escHtml(previewSlice)}</code>`\r\n : `<pre><code${langAttr}>${this.escHtml(previewSlice)}</code></pre>`;\r\n // Friendly tool name for Telegram — use title for notify tools\r\n let displayTool = toolName;\r\n if ((toolName.endsWith('__notify') || toolName === 'notify') && inputPreview) {\r\n try { const pp = JSON.parse(inputPreview); if (pp.title) displayTool = pp.title; } catch {\r\n const tm = inputPreview.match(/\"title\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/);\r\n if (tm) displayTool = tm[1];\r\n }\r\n } else {\r\n const mcpMatch = toolName.match(/__([^_]+)$/);\r\n if (mcpMatch) displayTool = mcpMatch[1].charAt(0).toUpperCase() + mcpMatch[1].slice(1);\r\n }\r\n\r\n const text = `⚠️ <b>Permission Request</b> — ${this.escHtml(sessionLabel)}\\n\\n` +\r\n `🔧 <b>${this.escHtml(displayTool)}</b>\\n` +\r\n `${previewHtml}${truncNote}`;\r\n\r\n const replyMarkup = {\r\n inline_keyboard: [[\r\n { text: '✅ Allow', callback_data: `perm:allow:${sessionId}:${requestId}` },\r\n { text: '❌ Deny', callback_data: `perm:deny:${sessionId}:${requestId}` },\r\n ]],\r\n };\r\n\r\n await this.sendMessage(text, undefined, replyMarkup);\r\n }\r\n\r\n async sendChoiceRequest(\r\n sessionId: string,\r\n sessionLabel: string,\r\n requestId: string,\r\n toolName: string,\r\n description: string,\r\n inputPreview: string,\r\n choices: PermissionChoice[],\r\n ): Promise<void> {\r\n let preview = inputPreview;\r\n try {\r\n const p = JSON.parse(inputPreview);\r\n if (typeof p.command === 'string') preview = `$ ${p.command}`;\r\n else if (typeof p.content === 'string') preview = p.content;\r\n } catch {}\r\n const slice = preview.slice(0, 3000);\r\n const html =\r\n `⚠️ <b>Permission Request</b> — ${this.escHtml(sessionLabel)}\\n\\n` +\r\n `🔧 <b>${this.escHtml(toolName)}</b>` +\r\n (description ? `\\n${this.escHtml(description)}` : '') +\r\n (slice ? `\\n<pre>${this.escHtml(slice)}</pre>` : '') +\r\n (preview.length > slice.length ? '\\n<i>...truncated</i>' : '');\r\n\r\n const entry: ChoiceMessage = { html };\r\n this.choiceMessages.set(permissionKey(sessionId, requestId), entry);\r\n const rows = choices.map((c) => {\r\n const token = randomUUID().replace(/-/g, '').slice(0, 16);\r\n this.choiceTokens.set(token, { sessionId, requestId, choiceId: c.id, label: c.label });\r\n return [{ text: c.label, callback_data: `pc:${token}` }];\r\n });\r\n this.trimChoiceMessages();\r\n\r\n const sent = await this.sendMessage(html, undefined, { inline_keyboard: rows });\r\n if (!sent) return;\r\n entry.messageId = sent.message_id;\r\n if (entry.outcome) await this.editMessageText(this.config.chatId, sent.message_id, html + entry.outcome);\r\n }\r\n\r\n async resolveChoiceRequest(sessionId: string, requestId: string, state: 'resolved' | 'expired'): Promise<void> {\r\n this.dropChoiceTokens(sessionId, requestId);\r\n const key = permissionKey(sessionId, requestId);\r\n const entry = this.choiceMessages.get(key);\r\n if (!entry) return;\r\n this.choiceMessages.delete(key);\r\n entry.outcome = state === 'resolved' ? '\\n\\n✅ <b>Resolved</b>' : '\\n\\n⌛ <b>Expired</b>';\r\n if (entry.messageId !== undefined) await this.editMessageText(this.config.chatId, entry.messageId, entry.html + entry.outcome);\r\n }\r\n\r\n private async handleChoiceCallback(query: TelegramCallbackQuery): Promise<void> {\r\n const choice = this.choiceTokens.get(query.data!.slice('pc:'.length));\r\n if (!choice) {\r\n await this.expire(query);\r\n return;\r\n }\r\n this.dropChoiceTokens(choice.sessionId, choice.requestId);\r\n this.onChoiceVerdict?.(choice.sessionId, choice.requestId, choice.choiceId);\r\n await this.answerCallbackQuery(query.id, `Sent: ${choice.label}`);\r\n const entry = this.choiceMessages.get(permissionKey(choice.sessionId, choice.requestId));\r\n if (entry?.messageId !== undefined && !entry.outcome) {\r\n await this.editMessageText(this.config.chatId, entry.messageId, `${entry.html}\\n\\n⏳ <b>Sent: ${this.escHtml(choice.label)}</b>`);\r\n }\r\n }\r\n\r\n private dropChoiceTokens(sessionId: string, requestId: string): void {\r\n for (const [token, c] of this.choiceTokens) {\r\n if (c.sessionId === sessionId && c.requestId === requestId) this.choiceTokens.delete(token);\r\n }\r\n }\r\n\r\n private trimChoiceMessages(): void {\r\n while (this.choiceMessages.size > MAX_CHOICE_MESSAGES) {\r\n const oldest = this.choiceMessages.keys().next().value as string;\r\n this.choiceMessages.delete(oldest);\r\n const [sessionId, requestId] = oldest.split('\\n');\r\n this.dropChoiceTokens(sessionId, requestId);\r\n }\r\n }\r\n\r\n private async handleCallbackQuery(query: TelegramCallbackQuery): Promise<void> {\r\n if (!query.data) return;\r\n if (String(query.message?.chat.id) !== String(this.config.chatId)) return;\r\n\r\n if (query.data.startsWith('sel:')) {\r\n await this.handleSessionSelectCallback(query);\r\n return;\r\n }\r\n\r\n if (query.data.startsWith('sess:')) {\r\n await this.expire(query);\r\n return;\r\n }\r\n\r\n if (query.data.startsWith('pc:')) {\r\n await this.handleChoiceCallback(query);\r\n return;\r\n }\r\n\r\n if (!query.data.startsWith('perm:')) return;\r\n\r\n const parts = query.data.split(':');\r\n if (parts.length < 4) return;\r\n const [, action, sessionId, requestId] = parts;\r\n const behavior = action === 'allow' ? 'allow' : 'deny';\r\n\r\n // Send verdict\r\n if (this.onPermissionVerdict) {\r\n this.onPermissionVerdict(sessionId, requestId, behavior as 'allow' | 'deny');\r\n }\r\n\r\n // Answer callback to remove loading state\r\n await this.answerCallbackQuery(query.id, behavior === 'allow' ? '✅ Allowed' : '❌ Denied');\r\n\r\n // Update message to show result (use escaped original text since it's plain)\r\n if (query.message) {\r\n const label = behavior === 'allow' ? '✅ <b>Allowed</b>' : '❌ <b>Denied</b>';\r\n const original = this.escHtml(query.message.text || '');\r\n await this.editMessageText(query.message.chat.id, query.message.message_id, original + `\\n\\n${label}`);\r\n }\r\n }\r\n\r\n private async handleSessionSelectCallback(query: TelegramCallbackQuery): Promise<void> {\r\n const [, selectionId, idxStr] = query.data!.split(':');\r\n const pending = this.selections.get(selectionId);\r\n if (!pending) {\r\n await this.expire(query);\r\n return;\r\n }\r\n const session = this.pendingSession(pending, parseInt(idxStr, 10));\r\n if (!session) {\r\n await this.answerCallbackQuery(query.id, 'Session not found');\r\n return;\r\n }\r\n this.selections.delete(selectionId);\r\n\r\n if (pending.photoFileId) {\r\n const delivered = await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);\r\n if (!delivered) {\r\n await this.answerCallbackQuery(query.id, 'Photo not delivered');\r\n if (query.message) await this.removeButtons(query.message.chat.id, query.message.message_id);\r\n return;\r\n }\r\n } else if (pending.text) {\r\n this.deliverToSession(session.id, pending.text);\r\n }\r\n\r\n await this.answerCallbackQuery(query.id, `Sent to ${this.getLabel(session)}`);\r\n // Update message to show which session was selected\r\n if (query.message) {\r\n await this.editMessageText(query.message.chat.id, query.message.message_id, `✅ Sent to <b>${this.escHtml(this.getLabel(session))}</b>`);\r\n }\r\n }\r\n\r\n private async answerCallbackQuery(callbackQueryId: string, text: string): Promise<void> {\r\n try {\r\n await fetch(`${this.apiUrl}/answerCallbackQuery`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ callback_query_id: callbackQueryId, text }),\r\n });\r\n } catch (err) {\r\n logger.warn(`Telegram answerCallbackQuery error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private async expire(query: TelegramCallbackQuery): Promise<void> {\r\n await this.answerCallbackQuery(query.id, 'Expired');\r\n if (query.message) await this.removeButtons(query.message.chat.id, query.message.message_id);\r\n }\r\n\r\n private async removeButtons(chatId: number | string, messageId: number): Promise<void> {\r\n try {\r\n await fetch(`${this.apiUrl}/editMessageReplyMarkup`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ chat_id: chatId, message_id: messageId, reply_markup: { inline_keyboard: [] } }),\r\n });\r\n } catch (err) {\r\n logger.warn(`Telegram editMessageReplyMarkup error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private async editMessageText(chatId: number | string, messageId: number, text: string): Promise<void> {\r\n try {\r\n await fetch(`${this.apiUrl}/editMessageText`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ chat_id: chatId, message_id: messageId, text, parse_mode: 'HTML' }),\r\n });\r\n } catch (err) {\r\n logger.warn(`Telegram editMessageText error: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private escHtml(s: string): string {\r\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\r\n }\r\n\r\n private static readonly LANG_BY_EXT: Record<string, string> = {\r\n js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',\r\n ts: 'typescript', tsx: 'typescript', py: 'python', rb: 'ruby', go: 'go',\r\n java: 'java', kt: 'kotlin', rs: 'rust', php: 'php', cs: 'csharp',\r\n sh: 'bash', bash: 'bash', zsh: 'bash', ps1: 'powershell',\r\n json: 'json', yml: 'yaml', yaml: 'yaml', xml: 'xml', sql: 'sql',\r\n md: 'markdown', html: 'html', vue: 'html', css: 'css', scss: 'scss',\r\n };\r\n\r\n private langOf(path: string): string {\r\n const m = /\\.([a-z0-9]+)$/i.exec(path || '');\r\n return m ? (TelegramBot.LANG_BY_EXT[m[1].toLowerCase()] ?? '') : '';\r\n }\r\n\r\n /** Convert markdown to Telegram HTML (escape first, then apply formatting) */\r\n private mdToHtml(s: string): string {\r\n // Handle tables before escaping (convert to clean text format)\r\n let text = s.replace(\r\n /^(\\|.+\\|)\\n\\|[-| :]+\\|\\n((?:\\|.+\\|\\n?)*)/gm,\r\n (_match, header: string, body: string) => {\r\n const headerCells = header.split('|').filter((c: string) => c.trim()).map((c: string) => c.trim());\r\n const headerLine = headerCells.join(' | ');\r\n const bodyLines = body.trim().split('\\n').map((row: string) => {\r\n return row.split('|').filter((c: string) => c.trim()).map((c: string) => c.trim()).join(' | ');\r\n });\r\n return `**${headerLine}**\\n${bodyLines.join('\\n')}`;\r\n },\r\n );\r\n // Extract code spans/blocks BEFORE escaping/formatting so that bold/italic\r\n // regexes can't cross into them and produce overlapping tags (Telegram rejects).\r\n const tokens: string[] = [];\r\n const placeholder = (i: number) => `\\u0000CODE${i}\\u0000`;\r\n text = text.replace(/```(?:\\w*)\\n?([\\s\\S]*?)```/g, (_m, body: string) => {\r\n const i = tokens.push(`<pre>${this.escHtml(body)}</pre>`) - 1;\r\n return placeholder(i);\r\n });\r\n text = text.replace(/`([^`\\n]+)`/g, (_m, body: string) => {\r\n const i = tokens.push(`<code>${this.escHtml(body)}</code>`) - 1;\r\n return placeholder(i);\r\n });\r\n\r\n let html = this.escHtml(text);\r\n // Headings: # / ## / ### → bold (Telegram has no heading tags)\r\n html = html.replace(/^#{1,3}\\s+(.+)$/gm, '<b>$1</b>');\r\n // Bold: **...**\r\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\r\n // Italic: *...* (single line only, to avoid swallowing across blocks)\r\n html = html.replace(/\\*([^*\\n]+)\\*/g, '<i>$1</i>');\r\n\r\n // Restore code tokens\r\n html = html.replace(/\\u0000CODE(\\d+)\\u0000/g, (_m, n: string) => tokens[Number(n)] ?? '');\r\n return html;\r\n }\r\n\r\n /** Update config (e.g., from dashboard settings) */\r\n updateConfig(config: TelegramConfig): void {\r\n const wasPolling = this.polling;\r\n if (wasPolling) this.stopPolling();\r\n this.config = config;\r\n if (wasPolling && config.enabled) this.startPolling();\r\n }\r\n}\r\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SupervisorSpawn = (command: string, args: string[], options: SpawnOptions) => ChildProcess;\r\n\r\nconst HEALTHY_RUN_MS = 60_000;\r\n\r\n// dist/hub/server.js runs standalone, but dist/cli.js bundles the hub inline, so both layouts occur.\r\nexport function resolveAdapterScript(baseDir: string): string | undefined {\r\n return [path.join(baseDir, '..', 'codex', 'main.js'), path.join(baseDir, 'codex', 'main.js')].find((p) => fs.existsSync(p));\r\n}\r\n\r\nexport interface SupervisorOptions {\r\n spawnFn?: SupervisorSpawn;\r\n minDelayMs?: number;\r\n maxDelayMs?: number;\r\n stopGraceMs?: number;\r\n env?: NodeJS.ProcessEnv;\r\n}\r\n\r\n// An adapter the hub starts must reach this hub, not one named by CLAUDE_ALARM_HUB_* in the shell; its token comes from config.json, not the environment.\r\nexport function adapterEnv(base: NodeJS.ProcessEnv, hub: { host: string; port: number }): NodeJS.ProcessEnv {\r\n const env: NodeJS.ProcessEnv = { ...base, CLAUDE_ALARM_HUB_HOST: hub.host, CLAUDE_ALARM_HUB_PORT: String(hub.port) };\r\n delete env.CLAUDE_ALARM_HUB_TOKEN;\r\n return env;\r\n}\r\n\r\nexport class CodexSupervisor {\r\n private child?: ChildProcess;\r\n private timer?: ReturnType<typeof setTimeout>;\r\n private stopped = true;\r\n private delay: number;\r\n private readonly spawnFn: SupervisorSpawn;\r\n private readonly minDelayMs: number;\r\n private readonly maxDelayMs: number;\r\n private readonly stopGraceMs: number;\r\n private readonly env?: NodeJS.ProcessEnv;\r\n\r\n constructor(private script: string, opts: SupervisorOptions = {}) {\r\n this.spawnFn = opts.spawnFn ?? spawn;\r\n this.minDelayMs = opts.minDelayMs ?? 2000;\r\n this.maxDelayMs = opts.maxDelayMs ?? 60_000;\r\n this.stopGraceMs = opts.stopGraceMs ?? 3000;\r\n this.env = opts.env;\r\n this.delay = this.minDelayMs;\r\n }\r\n\r\n start(): void {\r\n this.stopped = false;\r\n this.launch();\r\n }\r\n\r\n stop(): void {\r\n this.stopped = true;\r\n if (this.timer) clearTimeout(this.timer);\r\n this.timer = undefined;\r\n const child = this.child;\r\n this.child = undefined;\r\n if (!child) return;\r\n child.stdin?.end();\r\n const force = setTimeout(() => child.kill(), this.stopGraceMs);\r\n force.unref();\r\n child.once('exit', () => clearTimeout(force));\r\n }\r\n\r\n private launch(): void {\r\n const startedAt = Date.now();\r\n // Detached: on Windows a non-detached child shares the hub's kill-on-close job and dies before it can shut down cleanly. Piped: a detached child cannot write to the hub's console (EBADF).\r\n const child = this.spawnFn(process.execPath, [this.script, '--watch-stdin'], {\r\n detached: true,\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n windowsHide: true,\r\n ...(this.env ? { env: this.env } : {}),\r\n });\r\n this.child = child;\r\n child.stdout?.on('data', (d) => process.stdout.write(d));\r\n child.stderr?.on('data', (d) => process.stderr.write(d));\r\n child.stdin?.on('error', () => {});\r\n child.on('error', (err) => logger.warn(`Codex adapter failed to start: ${err.message}`));\r\n child.on('exit', (code, signal) => {\r\n if (this.child === child) this.child = undefined;\r\n if (this.stopped || code === 0) return;\r\n if (Date.now() - startedAt > HEALTHY_RUN_MS) this.delay = this.minDelayMs;\r\n logger.warn(`Codex adapter exited (${signal ?? code}); restarting in ${this.delay}ms`);\r\n this.timer = setTimeout(() => {\r\n this.timer = undefined;\r\n if (!this.stopped) this.launch();\r\n }, this.delay);\r\n this.delay = Math.min(this.delay * 2, this.maxDelayMs);\r\n });\r\n }\r\n}\r\n","export function isEntryScript(argv1: string | undefined, suffixes: string[]): boolean {\r\n if (!argv1) return false;\r\n const normalized = argv1.replace(/\\\\/g, '/');\r\n return suffixes.some((suffix) => normalized.endsWith(suffix));\r\n}\r\n","import crypto from 'node:crypto';\r\nimport type http from 'node:http';\r\n\r\nexport const SESSION_COOKIE = 'ca_session';\r\nconst COOKIE_MAX_AGE = 30 * 24 * 60 * 60;\r\n\r\nexport type RequestLike = { headers: http.IncomingHttpHeaders; url?: string };\r\n\r\nexport function safeEqual(a: string | null | undefined, b: string): boolean {\r\n if (typeof a !== 'string') return false;\r\n const x = Buffer.from(a);\r\n const y = Buffer.from(b);\r\n return x.length === y.length && crypto.timingSafeEqual(x, y);\r\n}\r\n\r\nexport function dashboardCookieValue(token: string): string {\r\n return crypto.createHmac('sha256', token).update('claude-alarm-dashboard').digest('hex');\r\n}\r\n\r\nexport function parseCookies(header: string | undefined): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const part of (header ?? '').split(';')) {\r\n const i = part.indexOf('=');\r\n if (i <= 0) continue;\r\n const raw = part.slice(i + 1).trim();\r\n let value = raw;\r\n try { value = decodeURIComponent(raw); } catch {}\r\n out[part.slice(0, i).trim()] = value;\r\n }\r\n return out;\r\n}\r\n\r\nexport function sessionCookieHeader(token: string, secure: boolean): string {\r\n return `${SESSION_COOKIE}=${dashboardCookieValue(token)}; HttpOnly; SameSite=Strict; Path=/; Max-Age=${COOKIE_MAX_AGE}${secure ? '; Secure' : ''}`;\r\n}\r\n\r\nexport function isAuthorized(req: RequestLike, token: string, allowQueryToken: boolean): boolean {\r\n const auth = req.headers.authorization;\r\n if (auth?.startsWith('Bearer ') && safeEqual(auth.slice(7), token)) return true;\r\n if (safeEqual(parseCookies(req.headers.cookie)[SESSION_COOKIE], dashboardCookieValue(token))) return true;\r\n if (allowQueryToken) {\r\n const q = new URL(req.url ?? '/', 'http://hub').searchParams.get('token');\r\n if (safeEqual(q, token)) return true;\r\n }\r\n return false;\r\n}\r\n\r\nexport function isCrossOrigin(req: RequestLike): boolean {\r\n const origin = req.headers.origin;\r\n if (!origin) return false;\r\n try {\r\n return new URL(origin).host !== req.headers.host;\r\n } catch {\r\n return true;\r\n }\r\n}\r\n\r\nexport function isJsonRequest(req: RequestLike): boolean {\r\n return String(req.headers['content-type'] ?? '').toLowerCase().startsWith('application/json');\r\n}\r\n\r\nexport function isSecureRequest(req: RequestLike): boolean {\r\n return req.headers['x-forwarded-proto'] === 'https';\r\n}\r\n","import http from 'node:http';\r\nimport type { Duplex } from 'node:stream';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { WebSocketServer, WebSocket } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { randomUUID } from 'node:crypto';\r\nimport {\r\n DEFAULT_HUB_HOST,\r\n DEFAULT_HUB_PORT,\r\n WS_PATH_CHANNEL,\r\n WS_PATH_DASHBOARD,\r\n WS_PATH_CODEX,\r\n UPLOADS_DIR,\r\n} from '../shared/constants.js';\r\nimport { SessionManager } from './session-manager.js';\r\nimport { Notifier } from './notifier.js';\r\nimport { TelegramBot } from './telegram.js';\r\nimport { CodexSupervisor, adapterEnv, resolveAdapterScript } from './codex-supervisor.js';\r\nimport { loadConfig, saveConfig } from '../shared/config.js';\r\nimport { sessionLabel } from '../shared/session-label.js';\r\nimport { installCrashGuard, logStartup } from '../shared/crash-guard.js';\r\nimport type { ChannelMessage, AppConfig, SessionInfo, WebhookConfig, TelegramConfig, PermissionChoice, PendingChoiceRequest, CodexAdapterInfo, CodexCall, CodexLinkMessage } from '../shared/types.js';\r\nimport { permissionKey } from '../shared/permission-key.js';\r\nimport { isEntryScript } from '../shared/entry.js';\r\nimport { hubUrlHost } from '../shared/hub-url.js';\r\nimport {\r\n isAuthorized,\r\n isCrossOrigin,\r\n isJsonRequest,\r\n isSecureRequest,\r\n safeEqual,\r\n sessionCookieHeader,\r\n} from './auth.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\n// 10MB images arrive base64-encoded (~13.4MB) over the dashboard socket.\r\nconst MAX_WS_PAYLOAD = 16 * 1024 * 1024;\r\n\r\nfunction validChoices(choices: unknown): PermissionChoice[] | undefined {\r\n if (!Array.isArray(choices)) return undefined;\r\n const valid = choices.filter((c): c is PermissionChoice => typeof c?.id === 'string' && typeof c?.label === 'string');\r\n return valid.length ? valid : undefined;\r\n}\r\n\r\ntype CodexCallOutcome = { status: number; body: unknown };\r\n\r\nconst nonEmpty = (v: unknown): v is string => typeof v === 'string' && v.trim() !== '';\r\n\r\nexport class HubServer {\r\n private httpServer: http.Server;\r\n private wssChannel: WebSocketServer;\r\n private wssDashboard: WebSocketServer;\r\n private sessions = new SessionManager();\r\n private notifier = new Notifier();\r\n private startTime = Date.now();\r\n\r\n // Map sessionId -> channel WebSocket\r\n private channelSockets = new Map<string, WebSocket>();\r\n // Track which channel connections are local\r\n private localChannels = new Set<string>();\r\n // All connected dashboard WebSockets\r\n private dashboardSockets = new Set<WebSocket>();\r\n\r\n private telegramBot?: TelegramBot;\r\n private heartbeatInterval?: ReturnType<typeof setInterval>;\r\n private channelAlive = new Map<string, boolean>(); // sessionId -> alive flag\r\n private socketOwners = new WeakMap<WebSocket, string>();\r\n // Codex approvals carry their own choices; a response is forwarded only in the mode of the request it answers.\r\n private choiceRequests = new Map<string, { request: PendingChoiceRequest; choiceIds: Set<string> }>();\r\n\r\n private host: string;\r\n private port: number;\r\n private token?: string;\r\n private codexEnabled: boolean;\r\n private codexSupervisor?: CodexSupervisor;\r\n private wssCodex: WebSocketServer;\r\n private codexAdapters = new Map<string, { ws: WebSocket; info: CodexAdapterInfo }>();\r\n private codexAlive = new WeakMap<WebSocket, boolean>();\r\n private codexCallTimeoutMs: number;\r\n private codexCalls = new Map<string, { adapterId: string; settle: (outcome: CodexCallOutcome) => void }>();\r\n\r\n constructor(config?: Partial<AppConfig>, options: { codexCallTimeoutMs?: number } = {}) {\r\n this.codexCallTimeoutMs = options.codexCallTimeoutMs ?? 60_000;\r\n this.host = config?.hub?.host ?? DEFAULT_HUB_HOST;\r\n this.port = config?.hub?.port ?? DEFAULT_HUB_PORT;\r\n this.token = config?.hub?.token;\r\n this.codexEnabled = config?.codex?.enabled === true;\r\n\r\n if (config?.notifications) {\r\n this.notifier.configure({\r\n desktop: config.notifications.desktop,\r\n });\r\n }\r\n if (config?.webhooks) {\r\n this.notifier.configure({ webhooks: config.webhooks });\r\n }\r\n const displayHost = hubUrlHost(this.host);\r\n this.notifier.configure({ dashboardUrl: `http://${displayHost}:${this.port}` });\r\n\r\n // Initialize Telegram bot if configured\r\n const fullConfig = loadConfig();\r\n if (fullConfig.telegram?.enabled && fullConfig.telegram.botToken && fullConfig.telegram.chatId) {\r\n this.initTelegram(fullConfig.telegram);\r\n }\r\n\r\n // HTTP Server\r\n this.httpServer = http.createServer((req, res) => this.handleHttp(req, res));\r\n\r\n // WebSocket for channel servers\r\n this.wssChannel = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });\r\n this.wssChannel.on('connection', (ws: WebSocket, req: http.IncomingMessage) => this.handleChannelConnection(ws, req));\r\n this.wssChannel.on('error', (err) => logger.warn(`Channel WebSocket server error: ${err.message}`));\r\n\r\n // WebSocket for dashboard\r\n this.wssDashboard = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });\r\n this.wssDashboard.on('connection', (ws) => this.handleDashboardConnection(ws));\r\n this.wssDashboard.on('error', (err) => logger.warn(`Dashboard WebSocket server error: ${err.message}`));\r\n\r\n this.wssCodex = new WebSocketServer({ noServer: true, maxPayload: MAX_WS_PAYLOAD });\r\n this.wssCodex.on('connection', (ws: WebSocket, req: http.IncomingMessage) => this.handleCodexConnection(ws, req));\r\n this.wssCodex.on('error', (err) => logger.warn(`Codex WebSocket server error: ${err.message}`));\r\n\r\n // Route WebSocket upgrade requests\r\n this.httpServer.on('upgrade', (req, socket, head) => {\r\n // Once 'upgrade' has a listener the socket is ours; an unhandled 'error'\r\n // on it would take the whole process down.\r\n socket.on('error', (err) => logger.debug(`Upgrade socket error: ${err.message}`));\r\n\r\n const url = new URL(req.url ?? '/', 'http://hub');\r\n const pathname = url.pathname;\r\n\r\n if (isCrossOrigin(req)) {\r\n this.rejectUpgrade(socket, 403, 'Forbidden', pathname, req);\r\n return;\r\n }\r\n if (!this.authorized(req, true)) {\r\n this.rejectUpgrade(socket, 401, 'Unauthorized', pathname, req);\r\n return;\r\n }\r\n\r\n if (pathname === WS_PATH_CHANNEL) {\r\n this.wssChannel.handleUpgrade(req, socket, head, (ws) => {\r\n this.wssChannel.emit('connection', ws, req);\r\n });\r\n } else if (pathname === WS_PATH_DASHBOARD) {\r\n this.wssDashboard.handleUpgrade(req, socket, head, (ws) => {\r\n this.wssDashboard.emit('connection', ws, req);\r\n });\r\n } else if (pathname === WS_PATH_CODEX) {\r\n this.wssCodex.handleUpgrade(req, socket, head, (ws) => {\r\n this.wssCodex.emit('connection', ws, req);\r\n });\r\n } else {\r\n socket.destroy();\r\n }\r\n });\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.cleanupUploads();\r\n this.startHeartbeat();\r\n return new Promise((resolve, reject) => {\r\n const onStartupError = (err: Error) => reject(err);\r\n this.httpServer.once('error', onStartupError);\r\n this.httpServer.listen(this.port, this.host, () => {\r\n // Past startup a rejected promise is a no-op, so errors must be logged instead\r\n this.httpServer.removeListener('error', onStartupError);\r\n this.httpServer.on('error', (err) => logger.error(`HTTP server error: ${err.message}`));\r\n const displayHost = hubUrlHost(this.host);\r\n logger.info(`Hub server listening on http://${displayHost}:${this.port}`);\r\n this.startCodexAdapter();\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n stop(): Promise<void> {\r\n return new Promise((resolve) => {\r\n this.codexSupervisor?.stop();\r\n this.codexSupervisor = undefined;\r\n // Stop heartbeat\r\n if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);\r\n\r\n // Stop telegram bot\r\n if (this.telegramBot) this.telegramBot.stopPolling();\r\n\r\n // Force-close all WebSocket connections\r\n for (const ws of this.channelSockets.values()) ws.terminate();\r\n for (const ws of this.dashboardSockets) ws.terminate();\r\n for (const { ws } of this.codexAdapters.values()) ws.terminate();\r\n this.channelSockets.clear();\r\n this.dashboardSockets.clear();\r\n\r\n this.wssChannel.close();\r\n this.wssDashboard.close();\r\n this.wssCodex.close();\r\n this.httpServer.close(() => {\r\n logger.info('Hub server stopped');\r\n resolve();\r\n });\r\n\r\n // Force resolve after 3 seconds if server won't close\r\n setTimeout(() => {\r\n logger.warn('Force shutting down');\r\n resolve();\r\n }, 3000);\r\n });\r\n }\r\n\r\n // --- HTTP Handler ---\r\n\r\n private handleHttp(req: http.IncomingMessage, res: http.ServerResponse): void {\r\n const url = new URL(req.url ?? '/', 'http://hub');\r\n\r\n if (isCrossOrigin(req)) {\r\n this.jsonResponse(res, 403, { error: 'Cross-origin request rejected' });\r\n return;\r\n }\r\n if (req.method === 'POST' && !isJsonRequest(req)) {\r\n this.jsonResponse(res, 415, { error: 'Content-Type must be application/json' });\r\n return;\r\n }\r\n if (url.pathname === '/' && req.method === 'GET') {\r\n this.serveDashboard(req, res, url);\r\n return;\r\n }\r\n if (url.pathname === '/api/login' && req.method === 'POST') {\r\n this.handleLogin(req, res);\r\n return;\r\n }\r\n if (!this.authorized(req, false)) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n\r\n // Route\r\n if (url.pathname === '/api/sessions' && req.method === 'GET') {\r\n this.jsonResponse(res, 200, { sessions: this.sessions.getAll() });\r\n } else if (url.pathname === '/api/status' && req.method === 'GET') {\r\n this.jsonResponse(res, 200, {\r\n running: true,\r\n pid: process.pid,\r\n port: this.port,\r\n sessions: this.sessions.count(),\r\n uptime: Date.now() - this.startTime,\r\n });\r\n } else if (url.pathname.startsWith('/api/sessions/') && req.method === 'DELETE') {\r\n const sessionId = url.pathname.slice('/api/sessions/'.length);\r\n const ws = this.channelSockets.get(sessionId);\r\n if (ws) { ws.terminate(); }\r\n const session = this.sessions.unregister(sessionId);\r\n this.channelSockets.delete(sessionId);\r\n this.localChannels.delete(sessionId);\r\n this.channelAlive.delete(sessionId);\r\n this.expireChoices(sessionId);\r\n if (session) {\r\n this.broadcastToDashboards({ type: 'session_disconnected', sessionId });\r\n this.jsonResponse(res, 200, { ok: true });\r\n } else {\r\n this.jsonResponse(res, 404, { error: 'Session not found' });\r\n }\r\n } else if (url.pathname === '/api/send' && req.method === 'POST') {\r\n this.handleApiSend(req, res);\r\n } else if (url.pathname === '/api/notify' && req.method === 'POST') {\r\n this.handleApiNotify(req, res);\r\n } else if (url.pathname === '/api/codex/folders' && req.method === 'GET') {\r\n this.handleCodexFolders(url, res);\r\n } else if (url.pathname === '/api/codex/threads' && req.method === 'POST') {\r\n this.handleCodexCreate(req, res);\r\n } else if (url.pathname === '/api/codex/threads/close' && req.method === 'POST') {\r\n this.handleCodexClose(req, res);\r\n } else if (url.pathname === '/api/webhooks' && req.method === 'GET') {\r\n const config = loadConfig();\r\n this.jsonResponse(res, 200, { webhooks: config.webhooks || [] });\r\n } else if (url.pathname === '/api/webhooks' && req.method === 'POST') {\r\n this.handleWebhookSave(req, res);\r\n } else if (url.pathname === '/api/telegram' && req.method === 'GET') {\r\n const cfg = loadConfig();\r\n const tg = cfg.telegram ?? { botToken: '', chatId: '', enabled: false };\r\n // Mask bot token for security\r\n this.jsonResponse(res, 200, {\r\n telegram: { ...tg, botToken: tg.botToken ? `${tg.botToken.slice(0, 8)}...` : '' },\r\n });\r\n } else if (url.pathname === '/api/telegram' && req.method === 'POST') {\r\n this.handleTelegramSave(req, res);\r\n } else if (url.pathname === '/api/telegram/test' && req.method === 'POST') {\r\n this.handleTelegramTest(req, res);\r\n } else if (url.pathname === '/api/telegram/detect' && req.method === 'POST') {\r\n this.handleTelegramDetect(req, res);\r\n } else {\r\n this.jsonResponse(res, 404, { error: 'Not found' });\r\n }\r\n }\r\n\r\n private serveDashboard(req: http.IncomingMessage, res: http.ServerResponse, url: URL): void {\r\n const linkToken = url.searchParams.get('token');\r\n if (linkToken !== null && this.token && safeEqual(linkToken, this.token)) {\r\n res.writeHead(302, { Location: '/', 'Set-Cookie': sessionCookieHeader(this.token, isSecureRequest(req)) });\r\n res.end();\r\n return;\r\n }\r\n // Look for dashboard HTML relative to this file (dist) or source\r\n const candidates = [\r\n path.join(__dirname, '..', 'dashboard', 'index.html'), // from dist/hub/\r\n path.join(__dirname, 'dashboard', 'index.html'), // from dist/ (bundled index.js)\r\n path.join(__dirname, '..', '..', 'src', 'dashboard', 'index.html'), // from dist/hub/ -> src/\r\n path.join(__dirname, '..', 'src', 'dashboard', 'index.html'), // from dist/ -> src/\r\n path.join(process.cwd(), 'dist', 'dashboard', 'index.html'), // from cwd\r\n path.join(process.cwd(), 'src', 'dashboard', 'index.html'), // from cwd/src\r\n ];\r\n logger.debug(`Dashboard candidates: ${JSON.stringify(candidates)}`);\r\n\r\n for (const candidate of candidates) {\r\n if (fs.existsSync(candidate)) {\r\n const html = fs.readFileSync(candidate, 'utf-8');\r\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\r\n res.end(html);\r\n return;\r\n }\r\n }\r\n\r\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\r\n res.end('<html><body><h1>claude-alarm</h1><p>Dashboard HTML not found. Reinstall the package.</p></body></html>');\r\n }\r\n\r\n private async handleLogin(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req) as { token?: unknown } | null;\r\n if (!this.token) {\r\n res.writeHead(204);\r\n res.end();\r\n return;\r\n }\r\n if (typeof body?.token !== 'string' || !safeEqual(body.token, this.token)) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n res.writeHead(204, { 'Set-Cookie': sessionCookieHeader(this.token, isSecureRequest(req)) });\r\n res.end();\r\n }\r\n\r\n private async handleApiSend(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n\r\n const { sessionId, content } = body as { sessionId?: string; content?: string };\r\n if (!sessionId || !content) {\r\n this.jsonResponse(res, 400, { error: 'sessionId and content are required' });\r\n return;\r\n }\r\n\r\n const ws = this.channelSockets.get(sessionId);\r\n if (!ws || ws.readyState !== WebSocket.OPEN) {\r\n this.jsonResponse(res, 404, { error: 'Session not connected' });\r\n return;\r\n }\r\n\r\n const msg: ChannelMessage = { type: 'message_to_session', sessionId, content, source: 'api' };\r\n ws.send(JSON.stringify(msg));\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private async handleApiNotify(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n\r\n const { title, message, level } = body as { title?: string; message?: string; level?: string };\r\n if (!title || !message) {\r\n this.jsonResponse(res, 400, { error: 'title and message are required' });\r\n return;\r\n }\r\n\r\n await this.notifier.notify(title, message, (level as any) ?? 'info');\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n // --- Channel WebSocket ---\r\n\r\n private handleChannelConnection(ws: WebSocket, req: http.IncomingMessage): void {\r\n const isLocal = this.isLocalRequest(req);\r\n logger.info(`Channel server connected (local: ${isLocal})`);\r\n\r\n // ws emits 'error' for protocol violations and socket failures; without a\r\n // listener Node rethrows it and kills the hub. 'close' still follows, so\r\n // session cleanup is handled there.\r\n ws.on('error', (err) => {\r\n logger.warn(`Channel WebSocket error: ${err.message}`);\r\n ws.terminate();\r\n });\r\n\r\n // Track pong responses for heartbeat\r\n ws.on('pong', () => {\r\n for (const [sessionId, sock] of this.channelSockets) {\r\n if (sock === ws) { this.channelAlive.set(sessionId, true); break; }\r\n }\r\n });\r\n\r\n ws.on('message', (data) => {\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n this.handleChannelMessage(ws, isLocal, msg);\r\n } catch {\r\n logger.warn('Invalid message from channel');\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n // Find and remove the session for this socket\r\n for (const [sessionId, sock] of this.channelSockets) {\r\n if (sock === ws) {\r\n const session = this.sessions.unregister(sessionId);\r\n this.channelSockets.delete(sessionId);\r\n this.localChannels.delete(sessionId);\r\n this.channelAlive.delete(sessionId);\r\n this.expireChoices(sessionId);\r\n logger.info(`Channel disconnected: ${sessionId}`);\r\n this.broadcastToDashboards({\r\n type: 'session_disconnected',\r\n sessionId,\r\n });\r\n break;\r\n }\r\n }\r\n });\r\n }\r\n\r\n private handleChannelMessage(ws: WebSocket, isLocal: boolean, msg: ChannelMessage): void {\r\n if (msg.type === 'register') {\r\n const id = msg.session.id;\r\n const owned = this.socketOwners.get(ws);\r\n if (owned && owned !== id) {\r\n logger.warn(`Rejected register for ${id}: connection already owns ${owned}`);\r\n return;\r\n }\r\n // A channel that reconnects after sleep or a network change reuses its id\r\n // while the hub may still see the old socket as open until the heartbeat\r\n // fires; the newest connection must win or the session is lost.\r\n const holder = this.channelSockets.get(id);\r\n if (holder && holder !== ws) {\r\n this.socketOwners.delete(holder);\r\n holder.terminate();\r\n }\r\n this.socketOwners.set(ws, id);\r\n } else if ('sessionId' in msg && this.socketOwners.get(ws) !== msg.sessionId) {\r\n return;\r\n }\r\n\r\n switch (msg.type) {\r\n case 'register': {\r\n const session = msg.session;\r\n session.isLocal = isLocal;\r\n const isReregister = !!this.sessions.get(session.id);\r\n this.sessions.register(session);\r\n this.channelSockets.set(session.id, ws);\r\n if (isLocal) this.localChannels.add(session.id);\r\n logger.info(`Session registered: ${session.id} (${session.name}, channel: ${session.channelEnabled ?? false}, peer: ${session.peerName ?? '-'})`);\r\n this.broadcastToDashboards({\r\n type: isReregister ? 'session_updated' : 'session_connected',\r\n session,\r\n });\r\n break;\r\n }\r\n\r\n case 'status': {\r\n const updated = this.sessions.updateStatus(msg.sessionId, msg.status);\r\n if (updated) {\r\n this.broadcastToDashboards({ type: 'session_updated', session: updated });\r\n }\r\n break;\r\n }\r\n\r\n case 'peer_name': {\r\n const updated = this.sessions.setPeerName(msg.sessionId, msg.peerName);\r\n if (updated) {\r\n logger.info(`Peer name for ${msg.sessionId}: ${msg.peerName ?? '-'}`);\r\n this.broadcastToDashboards({ type: 'session_updated', session: updated });\r\n }\r\n break;\r\n }\r\n\r\n case 'notify': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const notifySession = this.sessions.get(msg.sessionId);\r\n const notifyLabel = this.getSessionLabel(notifySession);\r\n this.notifier.notifyWithSession(msg.sessionId, notifyLabel, `[${notifyLabel}] ${msg.title}`, msg.message, msg.level ?? 'info');\r\n this.broadcastToDashboards({\r\n type: 'notification',\r\n sessionId: msg.sessionId,\r\n title: msg.title,\r\n message: msg.message,\r\n level: msg.level,\r\n timestamp: Date.now(),\r\n });\r\n break;\r\n }\r\n\r\n case 'reply': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const replySession = this.sessions.get(msg.sessionId);\r\n const replyLabel = this.getSessionLabel(replySession);\r\n this.notifier.notifyWithSession(msg.sessionId, replyLabel, `[${replyLabel}] Reply`, msg.content.slice(0, 3000), 'info');\r\n this.broadcastToDashboards({\r\n type: 'reply_from_session',\r\n sessionId: msg.sessionId,\r\n content: msg.content,\r\n timestamp: Date.now(),\r\n });\r\n break;\r\n }\r\n\r\n case 'permission_request': {\r\n this.sessions.updateActivity(msg.sessionId);\r\n const choices = validChoices(msg.choices);\r\n logger.info(`Permission request [${msg.requestId}] from ${msg.sessionId}: ${msg.toolName}`);\r\n if (choices) {\r\n this.choiceRequests.set(permissionKey(msg.sessionId, msg.requestId), {\r\n request: {\r\n sessionId: msg.sessionId,\r\n requestId: msg.requestId,\r\n toolName: msg.toolName,\r\n description: msg.description,\r\n inputPreview: msg.inputPreview,\r\n timestamp: msg.timestamp,\r\n choices,\r\n },\r\n choiceIds: new Set(choices.map((c) => c.id)),\r\n });\r\n }\r\n this.broadcastToDashboards({\r\n type: 'permission_request',\r\n sessionId: msg.sessionId,\r\n requestId: msg.requestId,\r\n toolName: msg.toolName,\r\n description: msg.description,\r\n inputPreview: msg.inputPreview,\r\n timestamp: msg.timestamp,\r\n ...(choices ? { choices } : {}),\r\n });\r\n // Forward to Telegram\r\n if (this.telegramBot) {\r\n const label = this.getSessionLabel(this.sessions.get(msg.sessionId));\r\n if (choices) {\r\n void this.telegramBot.sendChoiceRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview, choices);\r\n } else {\r\n this.telegramBot.sendPermissionRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview);\r\n }\r\n }\r\n break;\r\n }\r\n\r\n case 'permission_resolved': {\r\n this.resolveChoice(msg.sessionId, msg.requestId, msg.state === 'expired' ? 'expired' : 'resolved');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n private handleCodexConnection(ws: WebSocket, req: http.IncomingMessage): void {\r\n const isLocal = this.isLocalRequest(req);\r\n let adapterId: string | undefined;\r\n ws.on('error', (err) => {\r\n logger.warn(`Codex adapter WebSocket error: ${err.message}`);\r\n ws.terminate();\r\n });\r\n ws.on('pong', () => this.codexAlive.set(ws, true));\r\n ws.on('message', (data) => {\r\n let msg: CodexLinkMessage;\r\n try {\r\n msg = JSON.parse(data.toString()) as CodexLinkMessage;\r\n if (msg === null || typeof msg !== 'object') {\r\n logger.warn('Invalid message from Codex adapter');\r\n return;\r\n }\r\n } catch {\r\n logger.warn('Invalid message from Codex adapter');\r\n return;\r\n }\r\n if (msg.type === 'adapter_hello') {\r\n const a = msg.adapter;\r\n if (typeof a?.id !== 'string' || !a.id || typeof a.host !== 'string') return;\r\n if (adapterId && adapterId !== a.id) return;\r\n // An adapter that reconnects keeps its id while the hub may still hold the dead socket; the newest wins.\r\n const holder = this.codexAdapters.get(a.id);\r\n if (holder && holder.ws !== ws) holder.ws.terminate();\r\n if (!adapterId) logger.info(`Codex adapter connected: ${a.host} (${a.id}, local: ${isLocal})`);\r\n adapterId = a.id;\r\n this.codexAdapters.set(a.id, { ws, info: { id: a.id, host: a.host, ready: a.ready === true, isLocal } });\r\n this.broadcastCodexAdapters();\r\n } else if (msg.type === 'adapter_result' && adapterId) {\r\n const call = this.codexCalls.get(msg.requestId);\r\n if (!call || call.adapterId !== adapterId) return;\r\n call.settle(msg.ok ? { status: 200, body: msg.data } : { status: 422, body: { error: String(msg.error) } });\r\n }\r\n });\r\n ws.on('close', () => {\r\n if (!adapterId || this.codexAdapters.get(adapterId)?.ws !== ws) return;\r\n this.codexAdapters.delete(adapterId);\r\n for (const call of [...this.codexCalls.values()]) {\r\n if (call.adapterId === adapterId) call.settle({ status: 502, body: { error: 'Codex adapter disconnected' } });\r\n }\r\n logger.info(`Codex adapter disconnected: ${adapterId}`);\r\n this.broadcastCodexAdapters();\r\n });\r\n }\r\n\r\n private callCodexAdapter(adapterId: string, call: CodexCall): Promise<CodexCallOutcome> {\r\n const adapter = this.codexAdapters.get(adapterId);\r\n if (!adapter || adapter.ws.readyState !== WebSocket.OPEN) {\r\n return Promise.resolve({ status: 404, body: { error: 'Codex adapter is not connected' } });\r\n }\r\n const requestId = randomUUID();\r\n return new Promise((resolve) => {\r\n const timer = setTimeout(\r\n () => settle({ status: 504, body: { error: 'Codex did not respond in time. The conversation may still appear.' } }),\r\n this.codexCallTimeoutMs,\r\n );\r\n const settle = (outcome: CodexCallOutcome) => {\r\n clearTimeout(timer);\r\n this.codexCalls.delete(requestId);\r\n resolve(outcome);\r\n };\r\n this.codexCalls.set(requestId, { adapterId, settle });\r\n adapter.ws.send(JSON.stringify({ type: 'adapter_call', requestId, call } satisfies CodexLinkMessage));\r\n });\r\n }\r\n\r\n private async handleCodexFolders(url: URL, res: http.ServerResponse): Promise<void> {\r\n const adapterId = url.searchParams.get('adapterId');\r\n if (!nonEmpty(adapterId)) {\r\n this.jsonResponse(res, 400, { error: 'adapterId is required' });\r\n return;\r\n }\r\n const out = await this.callCodexAdapter(adapterId, { kind: 'folders' });\r\n this.jsonResponse(res, out.status, out.body);\r\n }\r\n\r\n private async handleCodexCreate(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = (await this.readBody(req)) as { adapterId?: unknown; cwd?: unknown } | null;\r\n const adapterId = body?.adapterId;\r\n const cwd = body?.cwd;\r\n if (!nonEmpty(adapterId) || !nonEmpty(cwd)) {\r\n this.jsonResponse(res, 400, { error: 'adapterId and cwd are required' });\r\n return;\r\n }\r\n const out = await this.callCodexAdapter(adapterId, { kind: 'create', cwd });\r\n this.jsonResponse(res, out.status, out.body);\r\n }\r\n\r\n private async handleCodexClose(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = (await this.readBody(req)) as { sessionId?: unknown } | null;\r\n const sessionId = body?.sessionId;\r\n if (!nonEmpty(sessionId)) {\r\n this.jsonResponse(res, 400, { error: 'sessionId is required' });\r\n return;\r\n }\r\n const session = this.sessions.get(sessionId);\r\n const ws = this.channelSockets.get(sessionId);\r\n if (session?.agentKind !== 'codex' || !session.closable || ws?.readyState !== WebSocket.OPEN) {\r\n this.jsonResponse(res, 404, { error: 'No closable Codex conversation' });\r\n return;\r\n }\r\n ws.send(JSON.stringify({ type: 'codex_close', sessionId } satisfies ChannelMessage));\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private codexAdapterList(): CodexAdapterInfo[] {\r\n return [...this.codexAdapters.values()].map((a) => a.info);\r\n }\r\n\r\n private broadcastCodexAdapters(): void {\r\n this.broadcastToDashboards({ type: 'codex_adapters', adapters: this.codexAdapterList() });\r\n }\r\n\r\n // --- Dashboard WebSocket ---\r\n\r\n private handleDashboardConnection(ws: WebSocket): void {\r\n this.dashboardSockets.add(ws);\r\n logger.info(`Dashboard connected (total: ${this.dashboardSockets.size})`);\r\n\r\n ws.on('error', (err) => {\r\n logger.warn(`Dashboard WebSocket error: ${err.message}`);\r\n ws.terminate();\r\n });\r\n\r\n // Send current session list\r\n const sessionsMsg: ChannelMessage = {\r\n type: 'sessions_list',\r\n sessions: this.sessions.getAll(),\r\n };\r\n ws.send(JSON.stringify(sessionsMsg));\r\n const pendingMsg: ChannelMessage = {\r\n type: 'permission_pending',\r\n requests: [...this.choiceRequests.values()].map((c) => c.request),\r\n };\r\n ws.send(JSON.stringify(pendingMsg));\r\n ws.send(JSON.stringify({ type: 'codex_adapters', adapters: this.codexAdapterList() } satisfies ChannelMessage));\r\n\r\n ws.on('message', (data) => {\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n if (msg.type === 'message_to_session') {\r\n const channelWs = this.channelSockets.get(msg.sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n channelWs.send(JSON.stringify({ ...msg, source: 'dashboard' }));\r\n }\r\n } else if (msg.type === 'image_upload') {\r\n this.handleImageUpload(ws, msg);\r\n } else if (msg.type === 'permission_response') {\r\n if (this.forwardPermissionResponse(msg)) {\r\n const verdict = msg.choiceId !== undefined ? `choice ${msg.choiceId}` : msg.behavior;\r\n logger.info(`Permission verdict [${msg.requestId}]: ${verdict} -> session ${msg.sessionId}`);\r\n }\r\n }\r\n } catch {\r\n logger.warn('Invalid message from dashboard');\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n this.dashboardSockets.delete(ws);\r\n logger.info(`Dashboard disconnected (total: ${this.dashboardSockets.size})`);\r\n });\r\n }\r\n\r\n // --- Helpers ---\r\n\r\n private forwardPermissionResponse(msg: { sessionId: string; requestId: string; behavior?: unknown; choiceId?: unknown }): boolean {\r\n const pending = this.choiceRequests.get(permissionKey(msg.sessionId, msg.requestId));\r\n let out: ChannelMessage;\r\n if (pending) {\r\n if (typeof msg.choiceId !== 'string' || !pending.choiceIds.has(msg.choiceId)) return false;\r\n out = { type: 'permission_response', sessionId: msg.sessionId, requestId: msg.requestId, choiceId: msg.choiceId };\r\n } else {\r\n if ((msg.behavior !== 'allow' && msg.behavior !== 'deny') || msg.choiceId !== undefined) return false;\r\n if (this.sessions.get(msg.sessionId)?.agentKind === 'codex') return false;\r\n out = { type: 'permission_response', sessionId: msg.sessionId, requestId: msg.requestId, behavior: msg.behavior };\r\n }\r\n const channelWs = this.channelSockets.get(msg.sessionId);\r\n if (channelWs?.readyState !== WebSocket.OPEN) return false;\r\n channelWs.send(JSON.stringify(out));\r\n return true;\r\n }\r\n\r\n private resolveChoice(sessionId: string, requestId: string, state: 'resolved' | 'expired'): void {\r\n if (!this.choiceRequests.delete(permissionKey(sessionId, requestId))) return;\r\n this.broadcastToDashboards({ type: 'permission_resolved', sessionId, requestId, state });\r\n void this.telegramBot?.resolveChoiceRequest(sessionId, requestId, state);\r\n }\r\n\r\n private expireChoices(sessionId: string): void {\r\n for (const pending of [...this.choiceRequests.values()]) {\r\n if (pending.request.sessionId === sessionId) this.resolveChoice(sessionId, pending.request.requestId, 'expired');\r\n }\r\n }\r\n\r\n private broadcastToDashboards(msg: ChannelMessage): void {\r\n const payload = JSON.stringify(msg);\r\n for (const ws of this.dashboardSockets) {\r\n if (ws.readyState === WebSocket.OPEN) {\r\n ws.send(payload);\r\n }\r\n }\r\n }\r\n\r\n private handleImageUpload(ws: WebSocket, msg: ChannelMessage & { type: 'image_upload' }): void {\r\n const { sessionId, imageData, mimeType, originalName, content } = msg;\r\n const reject = (reason: string) => {\r\n logger.warn(`Image upload rejected for ${sessionId}: ${reason}`);\r\n const rejected: ChannelMessage = { type: 'upload_rejected', sessionId, reason, withText: typeof content === 'string' && content.trim().length > 0 };\r\n if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(rejected));\r\n };\r\n\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (!channelWs || channelWs.readyState !== WebSocket.OPEN) {\r\n reject('the session is not connected');\r\n return;\r\n }\r\n if (!this.localChannels.has(sessionId)) {\r\n reject(\"this session is on another PC; images can only go to sessions on the hub's PC\");\r\n return;\r\n }\r\n\r\n const allowedTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];\r\n if (!allowedTypes.includes(mimeType)) {\r\n reject('only PNG, JPEG, GIF and WebP images are supported');\r\n return;\r\n }\r\n\r\n const base64Data = imageData.replace(/^data:image\\/\\w+;base64,/, '');\r\n const buffer = Buffer.from(base64Data, 'base64');\r\n if (buffer.length > 10 * 1024 * 1024) {\r\n reject('the image is larger than 10 MB');\r\n return;\r\n }\r\n\r\n // Save to uploads dir\r\n fs.mkdirSync(UPLOADS_DIR, { recursive: true });\r\n const ext = mimeType.split('/')[1] === 'jpeg' ? 'jpg' : mimeType.split('/')[1];\r\n const filename = `${randomUUID()}.${ext}`;\r\n const filePath = path.join(UPLOADS_DIR, filename);\r\n fs.writeFileSync(filePath, buffer);\r\n\r\n // Forward file path to channel\r\n const forwardMsg: ChannelMessage = {\r\n type: 'image_to_session',\r\n sessionId,\r\n imagePath: filePath,\r\n mimeType,\r\n originalName,\r\n content,\r\n source: 'dashboard',\r\n };\r\n channelWs.send(JSON.stringify(forwardMsg));\r\n logger.info(`Image saved and forwarded: ${filename} (${buffer.length} bytes)`);\r\n\r\n // Cleanup after 5 minutes\r\n setTimeout(() => {\r\n try { fs.unlinkSync(filePath); } catch {}\r\n }, 5 * 60 * 1000).unref();\r\n }\r\n\r\n private async handleWebhookSave(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { webhooks } = body as { webhooks?: WebhookConfig[] };\r\n if (!Array.isArray(webhooks)) { this.jsonResponse(res, 400, { error: 'webhooks must be an array' }); return; }\r\n const config = loadConfig();\r\n config.webhooks = webhooks;\r\n saveConfig(config);\r\n this.notifier.configure({ webhooks });\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private initTelegram(config: TelegramConfig): void {\r\n this.telegramBot = new TelegramBot(config);\r\n this.telegramBot.getSessions = () => this.sessions.getAll();\r\n this.telegramBot.onMessageToSession = (sessionId, content) => {\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n const msg: ChannelMessage = { type: 'message_to_session', sessionId, content, source: 'telegram' };\r\n channelWs.send(JSON.stringify(msg));\r\n logger.info(`Telegram message forwarded to session: ${sessionId}`);\r\n }\r\n };\r\n this.telegramBot.onImageToSession = (sessionId, imagePath, mimeType, caption) => {\r\n const channelWs = this.channelSockets.get(sessionId);\r\n if (channelWs?.readyState === WebSocket.OPEN) {\r\n const msg: ChannelMessage = { type: 'image_to_session', sessionId, imagePath, mimeType, content: caption, source: 'telegram' };\r\n channelWs.send(JSON.stringify(msg));\r\n logger.info(`Telegram photo forwarded to session: ${sessionId}`);\r\n }\r\n };\r\n this.telegramBot.onPermissionVerdict = (sessionId, requestId, behavior) => {\r\n if (this.forwardPermissionResponse({ sessionId, requestId, behavior })) {\r\n logger.info(`Telegram permission verdict [${requestId}]: ${behavior} -> session ${sessionId}`);\r\n }\r\n // Also notify dashboards so they can dismiss the permission bar\r\n this.broadcastToDashboards({ type: 'permission_response', sessionId, requestId, behavior });\r\n };\r\n this.telegramBot.onChoiceVerdict = (sessionId, requestId, choiceId) => {\r\n if (this.forwardPermissionResponse({ sessionId, requestId, choiceId })) {\r\n logger.info(`Telegram choice [${requestId}]: ${choiceId} -> session ${sessionId}`);\r\n }\r\n };\r\n this.notifier.configure({ telegramBot: this.telegramBot });\r\n this.telegramBot.startPolling();\r\n logger.info('Telegram bot initialized');\r\n }\r\n\r\n private async handleTelegramSave(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { telegram } = body as { telegram?: TelegramConfig };\r\n if (!telegram) { this.jsonResponse(res, 400, { error: 'telegram config required' }); return; }\r\n\r\n const config = loadConfig();\r\n // If botToken is masked (contains '...'), keep the existing token\r\n if (telegram.botToken.includes('...') && config.telegram?.botToken) {\r\n telegram.botToken = config.telegram.botToken;\r\n }\r\n config.telegram = telegram;\r\n saveConfig(config);\r\n\r\n // Stop existing bot if running\r\n if (this.telegramBot) {\r\n this.telegramBot.stopPolling();\r\n this.telegramBot = undefined;\r\n this.notifier.configure({ telegramBot: undefined as any });\r\n }\r\n\r\n // Start new bot if enabled\r\n if (telegram.enabled && telegram.botToken && telegram.chatId) {\r\n this.initTelegram(telegram);\r\n }\r\n\r\n this.jsonResponse(res, 200, { ok: true });\r\n }\r\n\r\n private async handleTelegramTest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { botToken, chatId } = body as { botToken?: string; chatId?: string };\r\n if (!botToken || !chatId) {\r\n this.jsonResponse(res, 400, { error: 'botToken and chatId required' });\r\n return;\r\n }\r\n\r\n try {\r\n const testRes = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n chat_id: chatId,\r\n text: 'Claude Alarm test message! Connection successful.',\r\n }),\r\n });\r\n\r\n if (testRes.ok) {\r\n this.jsonResponse(res, 200, { ok: true });\r\n } else {\r\n const err = await testRes.json() as { description?: string };\r\n this.jsonResponse(res, 400, { error: (err as any).description || 'Telegram API error' });\r\n }\r\n } catch (err) {\r\n this.jsonResponse(res, 500, { error: (err as Error).message });\r\n }\r\n }\r\n\r\n private async handleTelegramDetect(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\r\n const body = await this.readBody(req);\r\n if (!body) { this.jsonResponse(res, 400, { error: 'Invalid JSON' }); return; }\r\n const { botToken } = body as { botToken?: string };\r\n if (!botToken) {\r\n this.jsonResponse(res, 400, { error: 'botToken required' });\r\n return;\r\n }\r\n\r\n try {\r\n const detectRes = await fetch(`https://api.telegram.org/bot${botToken}/getUpdates?timeout=0&limit=10`, {\r\n signal: AbortSignal.timeout(10000),\r\n });\r\n\r\n if (!detectRes.ok) {\r\n const err = await detectRes.json() as { description?: string };\r\n this.jsonResponse(res, 400, { error: (err as any).description || 'Invalid bot token' });\r\n return;\r\n }\r\n\r\n const data = await detectRes.json() as { ok: boolean; result: Array<{ message?: { chat: { id: number; first_name?: string; title?: string; type: string } } }> };\r\n if (!data.ok || !data.result.length) {\r\n this.jsonResponse(res, 200, { ok: false, chats: [] });\r\n return;\r\n }\r\n\r\n // Extract unique chats\r\n const chatMap = new Map<string, { id: string; name: string; type: string }>();\r\n for (const update of data.result) {\r\n if (update.message?.chat) {\r\n const chat = update.message.chat;\r\n const id = String(chat.id);\r\n if (!chatMap.has(id)) {\r\n chatMap.set(id, {\r\n id,\r\n name: chat.title || chat.first_name || id,\r\n type: chat.type,\r\n });\r\n }\r\n }\r\n }\r\n\r\n this.jsonResponse(res, 200, { ok: true, chats: [...chatMap.values()] });\r\n } catch (err) {\r\n this.jsonResponse(res, 500, { error: (err as Error).message });\r\n }\r\n }\r\n\r\n private startCodexAdapter(): void {\r\n if (!this.codexEnabled) return;\r\n const script = resolveAdapterScript(__dirname);\r\n if (!script) {\r\n logger.warn('Codex adapter is enabled but codex/main.js was not found next to the hub');\r\n return;\r\n }\r\n this.codexSupervisor = new CodexSupervisor(script, { env: adapterEnv(process.env, { host: this.host, port: this.port }) });\r\n this.codexSupervisor.start();\r\n logger.info('Codex adapter started');\r\n }\r\n\r\n private startHeartbeat(): void {\r\n // Ping channel WebSockets every 30s, terminate unresponsive ones\r\n this.heartbeatInterval = setInterval(() => {\r\n for (const [sessionId, ws] of this.channelSockets) {\r\n if (this.channelAlive.get(sessionId) === false) {\r\n // No pong received since last ping — terminate\r\n logger.info(`Heartbeat timeout, terminating session: ${sessionId}`);\r\n ws.terminate();\r\n continue;\r\n }\r\n this.channelAlive.set(sessionId, false);\r\n ws.ping();\r\n }\r\n for (const { ws } of this.codexAdapters.values()) {\r\n if (this.codexAlive.get(ws) === false) {\r\n ws.terminate();\r\n continue;\r\n }\r\n this.codexAlive.set(ws, false);\r\n ws.ping();\r\n }\r\n }, 30000);\r\n }\r\n\r\n private cleanupUploads(): void {\r\n try {\r\n if (!fs.existsSync(UPLOADS_DIR)) return;\r\n const files = fs.readdirSync(UPLOADS_DIR);\r\n for (const file of files) {\r\n try { fs.unlinkSync(path.join(UPLOADS_DIR, file)); } catch {}\r\n }\r\n if (files.length > 0) logger.info(`Cleaned up ${files.length} leftover upload(s)`);\r\n } catch {}\r\n }\r\n\r\n private getSessionLabel(session?: SessionInfo): string {\r\n return session ? sessionLabel(session) : 'unknown';\r\n }\r\n\r\n private jsonResponse(res: http.ServerResponse, status: number, body: unknown): void {\r\n res.writeHead(status, { 'Content-Type': 'application/json' });\r\n res.end(JSON.stringify(body));\r\n }\r\n\r\n private authorized(req: http.IncomingMessage, allowQueryToken: boolean): boolean {\r\n return !this.token || isAuthorized(req, this.token, allowQueryToken);\r\n }\r\n\r\n private rejectUpgrade(socket: Duplex, status: number, text: string, pathname: string, req: http.IncomingMessage): void {\r\n logger.warn(`Rejected ${pathname} upgrade (${status}) from ${req.socket.remoteAddress}`);\r\n socket.write(`HTTP/1.1 ${status} ${text}\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n`);\r\n socket.destroy();\r\n }\r\n\r\n private isLocalRequest(req: http.IncomingMessage): boolean {\r\n const addr = req.socket.remoteAddress;\r\n return addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1';\r\n }\r\n\r\n private readBody(req: http.IncomingMessage, maxSize = 1024 * 1024): Promise<unknown | null> {\r\n return new Promise((resolve) => {\r\n let data = '';\r\n let size = 0;\r\n req.on('data', (chunk) => {\r\n size += chunk.length;\r\n if (size > maxSize) {\r\n req.destroy();\r\n resolve(null);\r\n return;\r\n }\r\n data += chunk;\r\n });\r\n req.on('end', () => {\r\n try {\r\n resolve(JSON.parse(data));\r\n } catch {\r\n resolve(null);\r\n }\r\n });\r\n });\r\n }\r\n}\r\n\r\n// Not import.meta.url: dist/cli.js bundles this module, so that check would also pass inside the CLI and start a second hub.\r\nif (isEntryScript(process.argv[1], ['/hub/server.js', '/hub/server.ts'])) {\r\n installCrashGuard('hub daemon');\r\n logStartup('Hub daemon');\r\n const config = loadConfig();\r\n const hub = new HubServer(config);\r\n hub.start().catch((err) => {\r\n logger.error('Failed to start hub:', err);\r\n process.exit(1);\r\n });\r\n\r\n const shutdown = () => {\r\n hub.stop().then(() => process.exit(0));\r\n };\r\n process.on('SIGINT', shutdown);\r\n process.on('SIGTERM', shutdown);\r\n}\r\n","import WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\r\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\r\n\r\nexport class HubClient {\r\n private ws: WebSocket | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\r\n private queue: ChannelMessage[] = [];\r\n private connected = false;\r\n private closed = false;\r\n\r\n constructor(\r\n private sessionId: string,\r\n private sessionName: string,\r\n private hubHost = DEFAULT_HUB_HOST,\r\n private hubPort = DEFAULT_HUB_PORT,\r\n private token?: string,\r\n private getPeerName: () => string | undefined = () => undefined,\r\n private getRegistration: () => Partial<SessionInfo> = () => ({}),\r\n ) {}\r\n\r\n connect(): void {\r\n this.closed = false;\r\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\r\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\r\n logger.debug(`Connecting to hub at ${url}`);\r\n\r\n try {\r\n const ws = new WebSocket(url);\r\n this.ws = ws;\r\n\r\n ws.on('open', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n this.ws!.send(JSON.stringify(this.registration()));\r\n\r\n // Flush queued messages\r\n for (const msg of this.queue) {\r\n this.ws!.send(JSON.stringify(msg));\r\n }\r\n this.queue = [];\r\n });\r\n\r\n ws.on('message', (data) => {\r\n if (this.ws !== ws) return;\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n for (const handler of this.messageHandlers) {\r\n handler(msg);\r\n }\r\n } catch (err) {\r\n logger.warn('Failed to parse hub message:', err);\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n if (!this.closed) this.scheduleReconnect();\r\n });\r\n\r\n ws.on('error', (err) => {\r\n if (this.ws !== ws) return;\r\n logger.debug(`Hub connection error: ${err.message}`);\r\n this.connected = false;\r\n });\r\n } catch {\r\n logger.debug('Failed to connect to hub, will retry');\r\n this.scheduleReconnect();\r\n }\r\n }\r\n\r\n send(msg: ChannelMessage): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n } else {\r\n if (this.queue.length < 100) {\r\n this.queue.push(msg);\r\n }\r\n logger.debug('Hub not connected, message queued');\r\n }\r\n }\r\n\r\n onMessage(handler: (msg: ChannelMessage) => void): void {\r\n this.messageHandlers.push(handler);\r\n }\r\n\r\n disconnect(): void {\r\n this.closed = true;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n this.connected = false;\r\n }\r\n\r\n reregister(): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(this.registration()));\r\n }\r\n }\r\n\r\n private registration(): ChannelMessage {\r\n return {\r\n type: 'register',\r\n session: {\r\n id: this.sessionId,\r\n name: this.sessionName,\r\n status: 'idle',\r\n connectedAt: Date.now(),\r\n lastActivity: Date.now(),\r\n cwd: process.cwd(),\r\n channelEnabled: true,\r\n peerName: this.getPeerName(),\r\n ...this.getRegistration(),\r\n },\r\n };\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnectTimer || this.closed) return;\r\n this.reconnectTimer = setTimeout(() => {\r\n this.reconnectTimer = null;\r\n this.connect();\r\n }, 5000);\r\n }\r\n}\r\n","import fs from 'node:fs';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\nimport { isValidPeerName } from '../shared/peer-name.js';\r\n\r\nexport interface PeerLookupEnv {\r\n messagingSocket?: string;\r\n sessionId?: string;\r\n}\r\n\r\ninterface SessionRecord {\r\n name?: unknown;\r\n messagingSocketPath?: unknown;\r\n sessionId?: unknown;\r\n}\r\n\r\nexport function findPeerName(records: unknown[], env: PeerLookupEnv): string | undefined {\r\n const valid = records.filter((r): r is SessionRecord => typeof r === 'object' && r !== null);\r\n const nameOf = (match: (r: SessionRecord) => boolean): string | undefined => {\r\n const rec = valid.find(match);\r\n return isValidPeerName(rec?.name) ? rec.name : undefined;\r\n };\r\n if (env.messagingSocket) {\r\n const bySocket = nameOf((r) => r.messagingSocketPath === env.messagingSocket);\r\n if (bySocket) return bySocket;\r\n }\r\n if (env.sessionId) return nameOf((r) => r.sessionId === env.sessionId);\r\n return undefined;\r\n}\r\n\r\n// <configDir>/sessions/<pid>.json is Claude Code's undocumented peer registry\r\n// (the source of ListAgents/SendMessage names, verified on 2.1.283). Its format\r\n// may change between releases, so every failure must degrade to \"no name\".\r\nexport function readPeerName(env: NodeJS.ProcessEnv = process.env): string | undefined {\r\n const lookup: PeerLookupEnv = {\r\n messagingSocket: env.CLAUDE_CODE_MESSAGING_SOCKET,\r\n sessionId: env.CLAUDE_CODE_SESSION_ID,\r\n };\r\n if (!lookup.messagingSocket && !lookup.sessionId) return undefined;\r\n\r\n const configDir = env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');\r\n const dir = path.join(configDir, 'sessions');\r\n let files: string[];\r\n try {\r\n files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));\r\n } catch {\r\n return undefined;\r\n }\r\n\r\n const records: unknown[] = [];\r\n for (const f of files) {\r\n try {\r\n records.push(JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')));\r\n } catch {}\r\n }\r\n return findPeerName(records, lookup);\r\n}\r\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport {\r\n CallToolRequestSchema,\r\n ListToolsRequestSchema,\r\n} from '@modelcontextprotocol/sdk/types.js';\r\nimport { z } from 'zod';\r\nimport { randomUUID } from 'node:crypto';\r\nimport path from 'node:path';\r\nimport { logger } from '../shared/logger.js';\r\nimport { CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\nimport { loadConfig } from '../shared/config.js';\r\nimport { HubClient } from './hub-client.js';\r\nimport { readPeerName } from './peer-name.js';\r\nimport type { SessionStatus, NotifyLevel } from '../shared/types.js';\r\n\r\nconst sessionId = randomUUID();\r\nconst sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? path.basename(process.cwd());\r\nlet peerName = readPeerName();\r\n\r\nconst server = new Server(\r\n {\r\n name: CHANNEL_SERVER_NAME,\r\n version: CHANNEL_SERVER_VERSION,\r\n },\r\n {\r\n capabilities: {\r\n experimental: {\r\n 'claude/channel': {},\r\n 'claude/channel/permission': {},\r\n },\r\n tools: {},\r\n },\r\n instructions: `Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. Read the message and act on it.\r\n\r\nREPLYING:\r\n- The dashboard user ONLY sees messages sent via the reply tool — your terminal output is invisible to them. You MUST call reply with your response.\r\n- The dashboard renders Markdown (headings, lists, tables, code blocks). Provide a thorough, complete answer; do not apply CLI-style brevity.\r\n- Reply in the user's language (match the language they wrote in).\r\n\r\nIMAGES: If a channel message contains \"[Image: ...] Read the file to view it: <path>\", use the Read tool on that path before responding — otherwise the image is invisible to you.\r\n\r\nROUTING: If a dashboard message has lines like [claude-alarm] @X = SendMessage to \"Y\", do what the message asks and deliver the result with the SendMessage tool to exactly \"Y\". Never guess other recipients. Then reply to the dashboard with what you sent.\r\n\r\nSTATUS: Call status(\"working\") before starting a long task, status(\"waiting_input\") when blocked on user input, status(\"idle\") when finished responding.\r\n\r\nNOTIFICATIONS:\r\n- Use notify only for key events: task completion, errors, or when user attention is needed. Not for intermediate steps or simple acknowledgments.\r\n- Pick level: success=completion, error=failure, warning=attention needed, info=neutral status.`,\r\n },\r\n);\r\n\r\n// Load config for hub connection (env vars take priority)\r\nconst config = loadConfig();\r\nconst hubHost = process.env.CLAUDE_ALARM_HUB_HOST ?? config.hub.host;\r\nconst hubPort = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\r\nconst hubToken = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\r\n\r\n// Hub client for forwarding to central hub\r\nconst hubClient = new HubClient(\r\n sessionId,\r\n sessionName,\r\n hubHost,\r\n hubPort,\r\n hubToken,\r\n () => peerName,\r\n);\r\n\r\n// --- Tools ---\r\n\r\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\r\n tools: [\r\n {\r\n name: 'notify',\r\n description:\r\n 'Send a desktop notification to the user. Use this when you complete a task, encounter an error, or need user attention. The notification will appear as a system toast/popup.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n title: { type: 'string', description: 'Notification title (short)' },\r\n message: { type: 'string', description: 'Notification body text' },\r\n level: {\r\n type: 'string',\r\n enum: ['info', 'warning', 'error', 'success'],\r\n description: 'Notification level (default: info)',\r\n },\r\n },\r\n required: ['title', 'message'],\r\n },\r\n },\r\n {\r\n name: 'reply',\r\n description:\r\n 'Send a message to the web dashboard. Use this to communicate status updates, results, or any information the user should see in the monitoring dashboard.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n content: { type: 'string', description: 'Message content to display on the dashboard' },\r\n },\r\n required: ['content'],\r\n },\r\n },\r\n {\r\n name: 'status',\r\n description:\r\n 'Update your session status displayed on the dashboard. Set to \"working\" when actively processing, \"waiting_input\" when you need user input, or \"idle\" when done.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n status: {\r\n type: 'string',\r\n enum: ['idle', 'working', 'waiting_input'],\r\n description: 'Current session status',\r\n },\r\n },\r\n required: ['status'],\r\n },\r\n },\r\n ],\r\n}));\r\n\r\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\r\n const { name, arguments: args } = request.params;\r\n\r\n switch (name) {\r\n case 'notify': {\r\n const title = args?.title as string;\r\n const message = args?.message as string;\r\n const level = (args?.level as NotifyLevel) ?? 'info';\r\n logger.info(`Notify [${level}]: ${title} - ${message}`);\r\n hubClient.send({\r\n type: 'notify',\r\n sessionId,\r\n title,\r\n message,\r\n level,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Notification sent: \"${title}\"` }],\r\n };\r\n }\r\n\r\n case 'reply': {\r\n const content = args?.content as string;\r\n logger.info(`Reply: ${content.slice(0, 100)}...`);\r\n hubClient.send({\r\n type: 'reply',\r\n sessionId,\r\n content,\r\n });\r\n return {\r\n content: [{ type: 'text', text: 'Message sent to dashboard.' }],\r\n };\r\n }\r\n\r\n case 'status': {\r\n const status = args?.status as SessionStatus;\r\n logger.info(`Status update: ${status}`);\r\n hubClient.send({\r\n type: 'status',\r\n sessionId,\r\n status,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Status updated to \"${status}\".` }],\r\n };\r\n }\r\n\r\n default:\r\n return {\r\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\r\n isError: true,\r\n };\r\n }\r\n});\r\n\r\n// --- Permission Relay ---\r\n\r\nconst PermissionRequestSchema = z.object({\r\n method: z.literal('notifications/claude/channel/permission_request'),\r\n params: z.object({\r\n request_id: z.string(),\r\n tool_name: z.string(),\r\n description: z.string(),\r\n input_preview: z.string(),\r\n }),\r\n});\r\n\r\n// Handle permission_request from Claude Code and forward to hub\r\nserver.setNotificationHandler(\r\n PermissionRequestSchema,\r\n async (notification) => {\r\n const { request_id, tool_name, description, input_preview } = notification.params;\r\n logger.info(`Permission request [${request_id}]: ${tool_name} - ${description}`);\r\n hubClient.send({\r\n type: 'permission_request',\r\n sessionId,\r\n requestId: request_id,\r\n toolName: tool_name,\r\n description,\r\n inputPreview: input_preview,\r\n timestamp: Date.now(),\r\n });\r\n },\r\n);\r\n\r\n// --- Startup ---\r\n\r\nasync function main() {\r\n logger.info(`Starting MCP channel server (session: ${sessionId})`);\r\n\r\n // Connect to hub (non-blocking, will retry)\r\n hubClient.connect();\r\n\r\n // The registry entry can be written after this MCP server starts, so re-check\r\n // shortly after boot, then poll to follow /rename.\r\n const refreshPeerName = () => {\r\n const next = readPeerName();\r\n if (next === peerName) return;\r\n peerName = next;\r\n logger.info(`Peer name: ${next ?? '(none)'}`);\r\n hubClient.send({ type: 'peer_name', sessionId, peerName: next });\r\n };\r\n setTimeout(refreshPeerName, 2_000).unref();\r\n setInterval(refreshPeerName, 30_000).unref();\r\n\r\n // Listen for messages from hub and forward to Claude via channel notification\r\n hubClient.onMessage(async (msg) => {\r\n if (msg.type === 'message_to_session' && msg.sessionId === sessionId) {\r\n logger.info(`Message from dashboard: ${msg.content}`);\r\n await server.notification({\r\n method: 'notifications/claude/channel',\r\n params: {\r\n content: msg.content,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()) },\r\n },\r\n });\r\n } else if (msg.type === 'image_to_session' && msg.sessionId === sessionId) {\r\n logger.info(`Image from dashboard: ${msg.imagePath}`);\r\n const textPart = msg.content ? `\\n${msg.content}` : '';\r\n await server.notification({\r\n method: 'notifications/claude/channel',\r\n params: {\r\n content: `[Image: ${msg.originalName || 'image'}] The user sent an image. Read the file to view it: ${msg.imagePath}${textPart}`,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()), imagePath: msg.imagePath, mimeType: msg.mimeType },\r\n },\r\n });\r\n } else if (msg.type === 'permission_response' && msg.sessionId === sessionId) {\r\n logger.info(`Permission verdict [${msg.requestId}]: ${msg.behavior}`);\r\n await server.notification({\r\n method: 'notifications/claude/channel/permission',\r\n params: {\r\n request_id: msg.requestId,\r\n behavior: msg.behavior,\r\n },\r\n });\r\n }\r\n });\r\n\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n logger.info('MCP channel server running on stdio');\r\n\r\n // Exit when stdin closes (Claude Code session ended)\r\n process.stdin.on('end', () => {\r\n logger.info('stdin closed, exiting');\r\n hubClient.disconnect();\r\n process.exit(0);\r\n });\r\n process.stdin.on('close', () => {\r\n logger.info('stdin closed, exiting');\r\n hubClient.disconnect();\r\n process.exit(0);\r\n });\r\n}\r\n\r\nmain().catch((err) => {\r\n logger.error('Fatal error:', err);\r\n process.exit(1);\r\n});\r\n","import { spawn } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport readline from 'node:readline';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { loadConfig, ensureConfigDir, setupMcpConfig, getOrCreateToken, setCodexEnabled, shouldOfferCodex } from './shared/config.js';\r\nimport { findCodex } from './codex/transport.js';\r\nimport { runFromCli } from './codex/run.js';\r\nimport { PID_FILE, LOG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, CODEX_PID_FILE, CODEX_LOG_FILE, CONFIG_DIR, CONFIG_FILE } from './shared/constants.js';\r\nimport { logger } from './shared/logger.js';\r\nimport { installCrashGuard, logStartup } from './shared/crash-guard.js';\r\nimport { waitForHub, HUB_START_TIMEOUT_MS } from './hub/readiness.js';\r\nimport { hubUrlHost } from './shared/hub-url.js';\r\nimport { resolveAdapterHub } from './codex/hub-target.js';\r\nimport { startAdapter } from './codex/start-check.js';\r\nimport { controlEndpoint, queryOwner, requestStop } from './codex/instance-lock.js';\r\nimport { adapterStatus, legacyNote, stopAdapter, type ControlDeps } from './codex/control-cli.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nfunction authHeaders(token?: string): Record<string, string> {\r\n return token ? { Authorization: `Bearer ${token}` } : {};\r\n}\r\n\r\nfunction loginLink(displayHost: string, port: number, token?: string): string {\r\n return `http://${displayHost}:${port}/${token ? `?token=${encodeURIComponent(token)}` : ''}`;\r\n}\r\n\r\nfunction printUsage() {\r\n console.log(`\r\nclaude-alarm - Monitor Claude Code sessions with notifications\r\n\r\nUsage:\r\n claude-alarm init Setup everything and show next steps\r\n claude-alarm hub start [-d] Start the hub server (-d for daemon)\r\n claude-alarm hub stop Stop the hub daemon\r\n claude-alarm hub status Show hub status\r\n claude-alarm setup [dir] Add claude-alarm to .mcp.json\r\n claude-alarm test Send a test notification\r\n claude-alarm token Show current auth token\r\n claude-alarm codex enable Start the Codex adapter together with the hub\r\n claude-alarm codex disable Stop starting the Codex adapter with the hub\r\n claude-alarm codex start Run the Codex adapter on its own (e.g. Codex on another PC)\r\n claude-alarm codex stop Stop a running Codex adapter\r\n claude-alarm codex status Show Codex adapter status\r\n claude-alarm codex run --brief <file|-> [--cwd <dir>] [--thread <id>] [--name <title>]\r\n [--output-schema <file>] [--approval-timeout <min>] [--timeout <min>] [--yolo]\r\n Hand a task to Codex through the shared daemon and print the result as JSON\r\n (--yolo: no sandbox and no approvals, like codex --yolo)\r\n claude-alarm help Show this help\r\n\r\nQuick start:\r\n claude-alarm init\r\n`);\r\n}\r\n\r\nasync function checkForUpdates() {\r\n try {\r\n const pkgPath = path.join(__dirname, '..', 'package.json');\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\r\n const currentVersion = pkg.version;\r\n\r\n const res = await fetch('https://registry.npmjs.org/@delt/claude-alarm/latest', {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (!res.ok) return;\r\n const data = await res.json() as { version: string };\r\n const latestVersion = data.version;\r\n\r\n if (latestVersion !== currentVersion) {\r\n console.log(`\\n⚠ New version available: ${currentVersion} → ${latestVersion}`);\r\n console.log(` Run: npm install -g @delt/claude-alarm\\n`);\r\n }\r\n } catch {\r\n // Silent fail - don't block startup\r\n }\r\n}\r\n\r\nasync function hubStart(daemon: boolean) {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = hubUrlHost(host);\r\n\r\n // Check for updates (non-blocking)\r\n checkForUpdates();\r\n\r\n // Check if already running\r\n if (fs.existsSync(PID_FILE)) {\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n if (isProcessRunning(pid)) {\r\n console.log(`Hub is already running (PID: ${pid}). Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n return;\r\n }\r\n // Stale PID file\r\n fs.unlinkSync(PID_FILE);\r\n }\r\n\r\n if (daemon) {\r\n ensureConfigDir();\r\n const logFd = fs.openSync(LOG_FILE, 'a');\r\n const hubScript = path.join(__dirname, 'hub', 'server.js');\r\n\r\n const child = spawn(process.execPath, [hubScript], {\r\n detached: true,\r\n stdio: ['ignore', logFd, logFd],\r\n env: { ...process.env },\r\n });\r\n\r\n const pid = child.pid;\r\n if (!pid) {\r\n console.error('Failed to start hub daemon');\r\n process.exit(1);\r\n }\r\n fs.writeFileSync(PID_FILE, String(pid), 'utf-8');\r\n let exited = false;\r\n child.once('exit', () => { exited = true; });\r\n child.unref();\r\n\r\n const startup = await waitForHub({\r\n url: `http://${displayHost}:${port}/api/status`,\r\n token: config.hub.token,\r\n pid,\r\n isAlive: () => !exited,\r\n });\r\n if (startup !== 'ready') {\r\n if (!exited) child.kill();\r\n if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);\r\n console.error(startup === 'exited'\r\n ? `Hub exited during startup. See ${LOG_FILE}`\r\n : `Hub did not answer at http://${displayHost}:${port} within ${HUB_START_TIMEOUT_MS / 1000}s and was stopped. See ${LOG_FILE}`);\r\n process.exit(1);\r\n }\r\n\r\n console.log(`Hub started as daemon (PID: ${pid})`);\r\n console.log(`Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n console.log(`Token: ${config.hub.token}`);\r\n console.log(`Logs: ${LOG_FILE}`);\r\n } else {\r\n // Foreground mode - import and run directly\r\n console.log(`Starting hub on http://${displayHost}:${port} (press Ctrl+C to stop)`);\r\n console.log(`Dashboard: ${loginLink(displayHost, port, config.hub.token)}`);\r\n console.log(`Token: ${config.hub.token}`);\r\n console.log(`Logs: ${LOG_FILE}`);\r\n installCrashGuard('hub foreground');\r\n logStartup('Hub foreground');\r\n const { HubServer } = await import('./hub/server.js');\r\n const hub = new HubServer(config);\r\n await hub.start();\r\n\r\n // Write PID file even in foreground\r\n ensureConfigDir();\r\n fs.writeFileSync(PID_FILE, String(process.pid), 'utf-8');\r\n\r\n const shutdown = async () => {\r\n console.log('\\nShutting down...');\r\n await hub.stop();\r\n if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);\r\n process.exit(0);\r\n };\r\n process.on('SIGINT', shutdown);\r\n process.on('SIGTERM', shutdown);\r\n }\r\n}\r\n\r\nfunction hubStop() {\r\n if (!fs.existsSync(PID_FILE)) {\r\n console.log('Hub is not running (no PID file found)');\r\n return;\r\n }\r\n\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n try {\r\n process.kill(pid, 'SIGTERM');\r\n console.log(`Hub stopped (PID: ${pid})`);\r\n } catch {\r\n console.log('Hub process not found (may have already stopped)');\r\n }\r\n fs.unlinkSync(PID_FILE);\r\n}\r\n\r\nasync function hubStatus() {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = hubUrlHost(host);\r\n\r\n // Check PID file\r\n let pidInfo = 'not running';\r\n if (fs.existsSync(PID_FILE)) {\r\n const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);\r\n if (isProcessRunning(pid)) {\r\n pidInfo = `running (PID: ${pid})`;\r\n } else {\r\n pidInfo = 'not running (stale PID file)';\r\n }\r\n }\r\n\r\n // Try to reach the hub HTTP API\r\n try {\r\n const res = await fetch(`http://${displayHost}:${port}/api/status`, { headers: authHeaders(config.hub.token) });\r\n if (res.ok) {\r\n const data = await res.json() as any;\r\n console.log(`Hub: running (PID: ${data.pid})`);\r\n console.log(`Port: ${data.port}`);\r\n console.log(`Sessions: ${data.sessions}`);\r\n console.log(`Uptime: ${Math.round(data.uptime / 1000)}s`);\r\n console.log(`Dashboard: http://${displayHost}:${port}`);\r\n const token = config.hub.token;\r\n if (token) {\r\n console.log(`Token: ${token.slice(0, 8)}...(masked)`);\r\n }\r\n return;\r\n }\r\n } catch {\r\n // Hub not reachable\r\n }\r\n\r\n console.log(`Hub: ${pidInfo}`);\r\n console.log(`Configured: http://${displayHost}:${port}`);\r\n}\r\n\r\nfunction setup(targetDir?: string) {\r\n const mcpPath = setupMcpConfig(targetDir);\r\n console.log(`Added claude-alarm to ${mcpPath}`);\r\n console.log('\\nTo use with Claude Code:');\r\n console.log(' 1. Start the hub: claude-alarm hub start -d');\r\n console.log(' 2. Run Claude Code: claude --dangerously-load-development-channels server:claude-alarm');\r\n}\r\n\r\nasync function test() {\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n\r\n try {\r\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\r\n if (config.hub.token) {\r\n headers['Authorization'] = `Bearer ${config.hub.token}`;\r\n }\r\n const res = await fetch(`http://${hubUrlHost(host)}:${port}/api/notify`, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify({\r\n title: 'Test Notification',\r\n message: 'Claude Alarm is working! This is a test notification.',\r\n level: 'success',\r\n }),\r\n });\r\n\r\n if (res.ok) {\r\n console.log('Test notification sent! Check your desktop for the toast.');\r\n } else {\r\n console.error(`Hub returned ${res.status}. Is the hub running?`);\r\n }\r\n } catch {\r\n console.error('Could not reach hub. Start it first: claude-alarm hub start');\r\n }\r\n}\r\n\r\nfunction ask(question: string): Promise<string> {\r\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\r\n return new Promise((resolve) => {\r\n rl.question(question, (answer) => {\r\n rl.close();\r\n resolve(answer.trim());\r\n });\r\n });\r\n}\r\n\r\nasync function init() {\r\n const dir = process.cwd();\r\n const projectName = path.basename(dir);\r\n\r\n console.log(`\\nclaude-alarm init for \"${projectName}\"\\n`);\r\n\r\n const remote = await ask('Connect to a remote hub? (y/N): ');\r\n\r\n let env: Record<string, string> = {\r\n CLAUDE_ALARM_SESSION_NAME: projectName,\r\n };\r\n\r\n if (remote.toLowerCase() === 'y') {\r\n const host = await ask('Hub host (e.g. 192.168.1.100): ');\r\n const port = await ask('Hub port (default: 7900): ');\r\n const token = await ask('Hub token: ');\r\n\r\n if (!host) {\r\n console.error('Host is required.');\r\n process.exit(1);\r\n }\r\n env.CLAUDE_ALARM_HUB_HOST = host;\r\n if (port) env.CLAUDE_ALARM_HUB_PORT = port;\r\n if (token) env.CLAUDE_ALARM_HUB_TOKEN = token;\r\n }\r\n\r\n // Write .mcp.json\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try { mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8')); } catch { mcpConfig = {}; }\r\n }\r\n if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env,\r\n };\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n\r\n console.log(`\\n✓ Created ${mcpPath}`);\r\n\r\n if (remote.toLowerCase() !== 'y') {\r\n // Check if hub is running locally\r\n const config = loadConfig();\r\n const host = config.hub.host ?? DEFAULT_HUB_HOST;\r\n const port = config.hub.port ?? DEFAULT_HUB_PORT;\r\n const displayHost = hubUrlHost(host);\r\n let hubRunning = false;\r\n try {\r\n const res = await fetch(`http://${displayHost}:${port}/api/status`, { headers: authHeaders(config.hub.token) });\r\n hubRunning = res.ok;\r\n } catch {}\r\n\r\n if (hubRunning) {\r\n console.log('✓ Hub is running');\r\n } else {\r\n console.log('✗ Hub is not running. Start it with:');\r\n console.log(` claude-alarm hub start`);\r\n }\r\n console.log(` Dashboard: http://${displayHost}:${port}`);\r\n\r\n if (shouldOfferCodex(config, findCodex('codex') !== undefined)) {\r\n const answer = await ask('\\nCodex is installed. Show Codex conversations on the dashboard too? (y/N): ');\r\n if (answer.toLowerCase() === 'y') codexEnable(true);\r\n else setCodexEnabled(false);\r\n }\r\n }\r\n\r\n console.log(`\\nNext step:`);\r\n console.log(` claude --dangerously-load-development-channels server:claude-alarm`);\r\n console.log(`\\nTo skip permission prompts (allows remote control without approval):`);\r\n console.log(` claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions`);\r\n console.log(`\\n WARNING: --dangerously-skip-permissions allows Claude to execute any action`);\r\n console.log(` without your approval. Only use in trusted, isolated environments.\\n`);\r\n}\r\n\r\nfunction showToken() {\r\n const token = getOrCreateToken();\r\n console.log(`Token: ${token}`);\r\n}\r\n\r\nfunction isProcessRunning(pid: number): boolean {\r\n try {\r\n process.kill(pid, 0);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nfunction readCodexPid(): number | undefined {\r\n if (!fs.existsSync(CODEX_PID_FILE)) return undefined;\r\n const pid = parseInt(fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim(), 10);\r\n return Number.isNaN(pid) ? undefined : pid;\r\n}\r\n\r\nfunction codexEnable(enabled: boolean) {\r\n setCodexEnabled(enabled);\r\n console.log(enabled\r\n ? 'Codex adapter enabled. Restart the hub to apply: claude-alarm hub stop, then claude-alarm hub start'\r\n : 'Codex adapter disabled. Restart the hub to apply.');\r\n}\r\n\r\nasync function codexStart() {\r\n // Before spawning: on a fresh PC this creates and saves the token, so the adapter reads the same one.\r\n const config = loadConfig();\r\n ensureConfigDir();\r\n legacyNote(controlDeps());\r\n const code = await startAdapter({\r\n hub: resolveAdapterHub(config),\r\n command: config.codex?.command ?? 'codex',\r\n logFile: CODEX_LOG_FILE,\r\n queryOwner: () => queryOwner(controlEndpoint(CONFIG_DIR)),\r\n spawnAdapter: () => {\r\n const logFd = fs.openSync(CODEX_LOG_FILE, 'a');\r\n return spawn(process.execPath, [path.join(__dirname, 'codex', 'main.js')], {\r\n detached: true,\r\n stdio: ['ignore', logFd, logFd, 'ipc'],\r\n windowsHide: true,\r\n env: { ...process.env },\r\n });\r\n },\r\n out: (line) => console.log(line),\r\n err: (line) => console.error(line),\r\n });\r\n if (code !== 0) process.exit(code);\r\n}\r\n\r\nfunction controlDeps(): ControlDeps {\r\n const endpoint = controlEndpoint(CONFIG_DIR);\r\n const token = loadConfig().hub.token ?? '';\r\n return {\r\n queryOwner: () => queryOwner(endpoint),\r\n requestStop: () => requestStop(endpoint, token),\r\n legacyPid: readCodexPid,\r\n isRunning: isProcessRunning,\r\n removeLegacyPidFile: () => {\r\n try { fs.unlinkSync(CODEX_PID_FILE); } catch {}\r\n },\r\n legacyPidFile: CODEX_PID_FILE,\r\n configFile: CONFIG_FILE,\r\n out: (line) => console.log(line),\r\n err: (line) => console.error(line),\r\n };\r\n}\r\n\r\nasync function codexRun(argv: string[]): Promise<number> {\r\n const abort = new AbortController();\r\n const stop = () => abort.abort();\r\n process.on('SIGINT', stop);\r\n process.on('SIGTERM', stop);\r\n try {\r\n return await runFromCli(argv, {\r\n readFile: (file) => fs.promises.readFile(file, 'utf-8'),\r\n readStdin: async () => {\r\n let text = '';\r\n for await (const chunk of process.stdin) text += chunk;\r\n return text;\r\n },\r\n out: (text) => process.stdout.write(text),\r\n err: (text) => process.stderr.write(text),\r\n cwd: process.cwd(),\r\n signal: abort.signal,\r\n });\r\n } finally {\r\n process.off('SIGINT', stop);\r\n process.off('SIGTERM', stop);\r\n }\r\n}\r\n\r\n// --- Main CLI ---\r\nasync function main() {\r\n const args = process.argv.slice(2);\r\n const cmd = args[0];\r\n const sub = args[1];\r\n\r\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\r\n printUsage();\r\n return;\r\n }\r\n\r\n if (cmd === 'serve') {\r\n // Start channel server (used by MCP)\r\n await import('./channel/server.js');\r\n return;\r\n }\r\n\r\n if (cmd === 'init') {\r\n await init();\r\n return;\r\n }\r\n\r\n if (cmd === 'hub') {\r\n if (sub === 'start') {\r\n const daemon = args.includes('-d') || args.includes('--daemon');\r\n await hubStart(daemon);\r\n } else if (sub === 'stop') {\r\n hubStop();\r\n } else if (sub === 'status') {\r\n await hubStatus();\r\n } else {\r\n console.error(`Unknown hub command: ${sub}`);\r\n printUsage();\r\n process.exit(1);\r\n }\r\n return;\r\n }\r\n\r\n if (cmd === 'codex') {\r\n if (sub === 'enable') codexEnable(true);\r\n else if (sub === 'disable') codexEnable(false);\r\n else if (sub === 'start') await codexStart();\r\n else if (sub === 'stop') process.exitCode = await stopAdapter(controlDeps(), loadConfig().codex?.enabled === true);\r\n else if (sub === 'status') process.exitCode = await adapterStatus(controlDeps(), loadConfig().codex?.enabled === true);\r\n else if (sub === 'run') process.exitCode = await codexRun(args.slice(2));\r\n else {\r\n console.error(`Unknown codex command: ${sub}`);\r\n printUsage();\r\n process.exit(1);\r\n }\r\n return;\r\n }\r\n\r\n if (cmd === 'setup') {\r\n setup(args[1]);\r\n return;\r\n }\r\n\r\n if (cmd === 'test') {\r\n await test();\r\n return;\r\n }\r\n\r\n if (cmd === 'token') {\r\n showToken();\r\n return;\r\n }\r\n\r\n console.error(`Unknown command: ${cmd}`);\r\n printUsage();\r\n process.exit(1);\r\n}\r\n\r\nmain().catch((err) => {\r\n logger.error('CLI error:', err);\r\n process.exit(1);\r\n});\r\n","import { spawn, type ChildProcess } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { Duplex } from 'node:stream';\r\nimport WebSocket, { type ClientOptions } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SpawnFn = (command: string, args: string[]) => ChildProcess;\r\n\r\nexport interface ProxyConnection {\r\n ws: WebSocket;\r\n close(): void;\r\n}\r\n\r\n// npm installs Codex on Windows as a codex.cmd shim, which only runs through a shell.\r\nexport function resolveCommand(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): { file: string; shell: boolean } {\r\n if (platform !== 'win32') return { file: command, shell: false };\r\n if (/[\\\\/]/.test(command) || path.extname(command)) return { file: command, shell: /\\.(cmd|bat)$/i.test(command) };\r\n const file = findCodex(command, platform, env);\r\n return file ? { file, shell: file.endsWith('.cmd') } : { file: command, shell: false };\r\n}\r\n\r\nexport function findOnPath(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string | undefined {\r\n const dirs = (env.PATH ?? env.Path ?? '').split(path.delimiter).filter(Boolean);\r\n for (const ext of platform === 'win32' ? ['.exe', '.cmd'] : ['']) {\r\n for (const dir of dirs) {\r\n const file = path.join(dir, command + ext);\r\n if (fs.existsSync(file)) return file;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n// A terminal opened before Codex was installed keeps its old PATH, so also look where the installers put codex.\r\nexport function findCodex(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string | undefined {\r\n const onPath = findOnPath(command, platform, env);\r\n if (onPath || platform !== 'win32') return onPath;\r\n const candidates = [\r\n env.LOCALAPPDATA && path.join(env.LOCALAPPDATA, 'Programs', 'OpenAI', 'Codex', 'bin', `${command}.exe`),\r\n env.APPDATA && path.join(env.APPDATA, 'npm', `${command}.cmd`),\r\n ];\r\n return candidates.find((file): file is string => !!file && fs.existsSync(file));\r\n}\r\n\r\nexport const defaultSpawn: SpawnFn = (command, args) => {\r\n const { file, shell } = resolveCommand(command);\r\n if (shell) return spawn(`\"${file}\" ${args.join(' ')}`, { stdio: 'pipe', windowsHide: true, shell: true });\r\n return spawn(file, args, { stdio: 'pipe', windowsHide: true });\r\n};\r\n\r\n// The daemon control socket speaks WebSocket, not JSONL: `codex app-server proxy` only relays bytes.\r\nexport const HANDSHAKE_TIMEOUT_MS = 10_000;\r\n\r\nexport function connectProxy(command: string, spawnFn: SpawnFn = defaultSpawn, timeoutMs = HANDSHAKE_TIMEOUT_MS): Promise<ProxyConnection> {\r\n const child = spawnFn(command, ['app-server', 'proxy']);\r\n child.stdin?.on('error', () => {});\r\n child.stderr?.on('data', (d) => logger.debug(`codex proxy: ${String(d).trim()}`));\r\n\r\n const stream = new Duplex({\r\n read() {},\r\n write(chunk, _encoding, callback) {\r\n if (child.stdin?.writable) child.stdin.write(chunk, callback);\r\n else callback();\r\n },\r\n final(callback) {\r\n child.stdin?.end();\r\n callback();\r\n },\r\n });\r\n child.stdout?.on('data', (d) => stream.push(d));\r\n child.stdout?.on('end', () => stream.push(null));\r\n Object.assign(stream, { setNoDelay() {}, setTimeout() {}, setKeepAlive() {}, ref() {}, unref() {} });\r\n\r\n const ws = new WebSocket('ws://localhost/', {\r\n createConnection: (() => stream) as unknown as ClientOptions['createConnection'],\r\n });\r\n const close = () => {\r\n ws.terminate();\r\n child.kill();\r\n };\r\n\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n const timer = setTimeout(() => fail(new Error(`codex daemon did not answer within ${timeoutMs / 1000}s`)), timeoutMs);\r\n const fail = (err: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n close();\r\n reject(err);\r\n };\r\n child.on('error', fail);\r\n child.once('exit', (code) => fail(new Error(`codex proxy exited (code ${code})`)));\r\n ws.on('error', fail);\r\n ws.once('open', () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n child.on('error', (err) => logger.warn(`codex proxy error: ${err.message}`));\r\n ws.on('error', (err) => logger.warn(`codex daemon socket error: ${err.message}`));\r\n resolve({ ws, close });\r\n });\r\n });\r\n}\r\n","import path from 'node:path';\r\nimport { connectProxy, defaultSpawn, type SpawnFn } from './transport.js';\r\nimport { RpcClient, type RpcId } from './rpc.js';\r\nimport { finalAnswer, type AgentMessage } from './mapping.js';\r\nimport { CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\n\r\nexport interface CodexRunOptions {\r\n brief: string;\r\n cwd: string;\r\n threadId?: string;\r\n name?: string;\r\n outputSchema?: unknown;\r\n yolo?: boolean;\r\n approvalTimeoutMs?: number;\r\n timeoutMs?: number;\r\n interruptGraceMs?: number;\r\n signal?: AbortSignal;\r\n command?: string;\r\n spawnFn?: SpawnFn;\r\n progress?: (line: string) => void;\r\n}\r\n\r\nexport type CodexRunStatus = 'completed' | 'failed' | 'interrupted' | 'timedOut';\r\n\r\nexport interface ApprovalRecord {\r\n kind: string;\r\n summary: string;\r\n outcome: 'unanswered' | 'answered-elsewhere' | 'timeout-declined' | 'auto-declined';\r\n}\r\n\r\nexport interface CodexRunResult {\r\n threadId: string;\r\n turnId: string;\r\n status: CodexRunStatus;\r\n error: string | null;\r\n result: unknown;\r\n finalText: string | null;\r\n approvals: ApprovalRecord[];\r\n durationMs: number;\r\n codex: { userAgent: string | null };\r\n}\r\n\r\ninterface Turn {\r\n id: string;\r\n status: string;\r\n error?: { message?: string } | null;\r\n items?: Array<{ type: string } & AgentMessage>;\r\n}\r\n\r\nconst MINUTE = 60_000;\r\nconst SUMMARY_MAX = 200;\r\n\r\nconst clip = (text: string) => (text.length > SUMMARY_MAX ? `${text.slice(0, SUMMARY_MAX)}…` : text);\r\n\r\n// Only these requests reach the dashboard through the adapter, so only these are worth waiting on.\r\nfunction relayed(method: string, params: any): { kind: string; summary: string; decline: unknown } | null {\r\n switch (method) {\r\n case 'item/commandExecution/requestApproval': {\r\n const command = params.command ?? (params.commandActions ?? []).map((a: { command: string }) => a.command).join('\\n');\r\n return { kind: 'command', summary: clip(String(command || params.reason || '')), decline: { decision: 'decline' } };\r\n }\r\n case 'item/fileChange/requestApproval':\r\n return { kind: 'file change', summary: clip(String(params.reason ?? '')), decline: { decision: 'decline' } };\r\n case 'mcpServer/elicitation/request':\r\n if (params._meta?.codex_approval_kind !== 'mcp_tool_call') return null;\r\n return { kind: 'MCP tool', summary: clip(`${params.serverName ?? ''}: ${params.message ?? ''}`), decline: { action: 'decline' } };\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\nfunction unrelayed(method: string, params: any): { kind: string; summary: string; answer: unknown } | null {\r\n switch (method) {\r\n case 'item/permissions/requestApproval':\r\n return { kind: 'permissions', summary: clip(String(params.reason ?? '')), answer: { permissions: {} } };\r\n case 'item/tool/requestUserInput':\r\n return {\r\n kind: 'question',\r\n summary: clip((params.questions ?? []).map((q: { question?: string }) => q.question ?? '').join(' / ')),\r\n answer: { answers: {} },\r\n };\r\n case 'mcpServer/elicitation/request':\r\n return { kind: 'MCP form', summary: clip(String(params.message ?? '')), answer: { action: 'decline' } };\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\nexport async function runCodexTask(opts: CodexRunOptions): Promise<CodexRunResult> {\r\n const progress = opts.progress ?? (() => {});\r\n const approvalTimeoutMs = opts.approvalTimeoutMs ?? 10 * MINUTE;\r\n const started = Date.now();\r\n const conn = await connectProxy(opts.command ?? 'codex', opts.spawnFn ?? defaultSpawn);\r\n const rpc = new RpcClient(conn.ws);\r\n const timers = new Set<ReturnType<typeof setTimeout>>();\r\n try {\r\n const init = await rpc.request<{ userAgent?: string }>('initialize', {\r\n clientInfo: { name: 'claude-alarm-run', version: CHANNEL_SERVER_VERSION },\r\n });\r\n rpc.notify('initialized');\r\n\r\n const yolo = { sandbox: 'danger-full-access', approvalPolicy: 'never' };\r\n let threadId: string;\r\n if (opts.threadId) {\r\n // Without --yolo the thread keeps its own settings: resume fields override them.\r\n await rpc.request('thread/resume', opts.yolo ? { threadId: opts.threadId, ...yolo } : { threadId: opts.threadId });\r\n threadId = opts.threadId;\r\n } else {\r\n const res = await rpc.request<{ thread: { id: string } }>('thread/start', {\r\n cwd: opts.cwd,\r\n ...(opts.yolo ? yolo : { sandbox: 'workspace-write', approvalPolicy: 'on-request' }),\r\n });\r\n threadId = res.thread.id;\r\n if (opts.name) {\r\n await rpc.request('thread/name/set', { threadId, name: opts.name }).catch(() => {});\r\n }\r\n }\r\n progress(`thread ${threadId}`);\r\n\r\n const messages = new Map<string, AgentMessage[]>();\r\n const approvals: ApprovalRecord[] = [];\r\n const waiting = new Map<RpcId, ApprovalRecord>();\r\n let finish!: (turn: Turn) => void;\r\n let lose!: (err: Error) => void;\r\n const completed = new Promise<Turn>((resolve, reject) => {\r\n finish = resolve;\r\n lose = reject;\r\n });\r\n // Closing the connection after a failed turn/start rejects this with nobody waiting on it.\r\n completed.catch(() => {});\r\n\r\n rpc.on('close', () => lose(new Error('connection to the Codex daemon closed')));\r\n rpc.on('notification', (method: string, params: any) => {\r\n if (params?.threadId !== threadId) return;\r\n if (method === 'item/completed') {\r\n const item = params.item ?? {};\r\n if (item.type === 'agentMessage') {\r\n const list = messages.get(params.turnId) ?? [];\r\n list.push({ text: item.text, phase: item.phase });\r\n messages.set(params.turnId, list);\r\n } else if (item.type === 'commandExecution') {\r\n progress(`ran ${clip(String(item.command ?? ''))} (exit ${item.exitCode ?? '?'})`);\r\n }\r\n } else if (method === 'serverRequest/resolved') {\r\n const record = waiting.get(params.requestId);\r\n if (record) {\r\n waiting.delete(params.requestId);\r\n record.outcome = 'answered-elsewhere';\r\n progress(`approval answered elsewhere: ${record.kind}`);\r\n }\r\n } else if (method === 'turn/completed') {\r\n finish(params.turn);\r\n }\r\n });\r\n rpc.on('request', (id: RpcId, method: string, params: any) => {\r\n if (params?.threadId !== threadId) return;\r\n const wait = relayed(method, params);\r\n if (wait) {\r\n const record: ApprovalRecord = { kind: wait.kind, summary: wait.summary, outcome: 'unanswered' };\r\n approvals.push(record);\r\n waiting.set(id, record);\r\n progress(`approval pending (${Math.round(approvalTimeoutMs / 1000)}s): ${wait.kind} ${wait.summary}`);\r\n const timer = setTimeout(() => {\r\n timers.delete(timer);\r\n if (!waiting.delete(id)) return;\r\n record.outcome = 'timeout-declined';\r\n rpc.respond(id, wait.decline);\r\n progress(`approval declined after timeout: ${wait.kind}`);\r\n }, approvalTimeoutMs);\r\n timers.add(timer);\r\n return;\r\n }\r\n const answer = unrelayed(method, params);\r\n if (answer) {\r\n approvals.push({ kind: answer.kind, summary: answer.summary, outcome: 'auto-declined' });\r\n rpc.respond(id, answer.answer);\r\n progress(`declined at once: ${answer.kind}`);\r\n }\r\n });\r\n\r\n const turnStart: Record<string, unknown> = { threadId, input: [{ type: 'text', text: opts.brief }] };\r\n if (opts.outputSchema !== undefined) turnStart.outputSchema = opts.outputSchema;\r\n const { turn } = await rpc.request<{ turn: { id: string } }>('turn/start', turnStart);\r\n const turnId = turn.id;\r\n progress(`turn ${turnId} started`);\r\n\r\n let stopReason: 'timedOut' | 'interrupted' | null = null;\r\n let interruptRequested!: () => void;\r\n const stopped = new Promise<void>((resolve) => (interruptRequested = resolve));\r\n const stop = (reason: 'timedOut' | 'interrupted') => {\r\n if (stopReason) return;\r\n stopReason = reason;\r\n progress(reason === 'timedOut' ? 'timeout reached, interrupting the turn' : 'interrupting the turn');\r\n rpc.request('turn/interrupt', { threadId, turnId }).catch(() => {});\r\n interruptRequested();\r\n };\r\n const deadline = setTimeout(() => stop('timedOut'), opts.timeoutMs ?? 60 * MINUTE);\r\n timers.add(deadline);\r\n const onAbort = () => stop('interrupted');\r\n if (opts.signal?.aborted) onAbort();\r\n opts.signal?.addEventListener('abort', onAbort, { once: true });\r\n\r\n const grace = stopped.then(\r\n () =>\r\n new Promise<null>((resolve) => {\r\n const t = setTimeout(() => resolve(null), opts.interruptGraceMs ?? 15_000);\r\n timers.add(t);\r\n }),\r\n );\r\n let ended: Turn | null;\r\n let lost: string | null = null;\r\n try {\r\n ended = await Promise.race([completed, grace]);\r\n } catch (err) {\r\n ended = null;\r\n lost = (err as Error).message;\r\n } finally {\r\n opts.signal?.removeEventListener('abort', onAbort);\r\n }\r\n\r\n let status: CodexRunStatus;\r\n let error: string | null = null;\r\n if (stopReason) status = stopReason;\r\n else if (lost) {\r\n status = 'failed';\r\n error = lost;\r\n } else if (ended?.status === 'failed' || ended?.error) {\r\n status = 'failed';\r\n error = ended?.error?.message ?? 'the turn failed';\r\n } else if (ended?.status === 'interrupted') status = 'interrupted';\r\n else status = 'completed';\r\n\r\n // turn/completed may carry only a summary of the items, so prefer what was collected live.\r\n const collected = messages.get(turnId) ?? [];\r\n const fromTurn = (ended?.items ?? []).filter((i) => i.type === 'agentMessage');\r\n const finalText = finalAnswer(collected.length ? collected : fromTurn);\r\n let result: unknown = null;\r\n if (opts.outputSchema !== undefined && finalText) {\r\n try {\r\n result = JSON.parse(finalText);\r\n } catch {\r\n result = null;\r\n }\r\n }\r\n return {\r\n threadId,\r\n turnId,\r\n status,\r\n error,\r\n result,\r\n finalText,\r\n approvals,\r\n durationMs: Date.now() - started,\r\n codex: { userAgent: init.userAgent ?? null },\r\n };\r\n } finally {\r\n for (const t of timers) clearTimeout(t);\r\n conn.close();\r\n }\r\n}\r\n\r\nconst VALUE_FLAGS: Record<string, keyof RunArgs> = {\r\n '--brief': 'brief',\r\n '--cwd': 'cwd',\r\n '--thread': 'threadId',\r\n '--name': 'name',\r\n '--output-schema': 'outputSchema',\r\n '--approval-timeout': 'approvalTimeoutMs',\r\n '--timeout': 'timeoutMs',\r\n};\r\n\r\nexport interface RunArgs {\r\n brief: string;\r\n yolo?: boolean;\r\n cwd?: string;\r\n threadId?: string;\r\n name?: string;\r\n outputSchema?: string;\r\n approvalTimeoutMs?: number;\r\n timeoutMs?: number;\r\n}\r\n\r\nexport function parseRunArgs(argv: string[]): RunArgs {\r\n const out: Partial<Record<keyof RunArgs, string | number | boolean>> = {};\r\n for (let i = 0; i < argv.length; i++) {\r\n const flag = argv[i];\r\n if (flag === '--yolo') {\r\n out.yolo = true;\r\n continue;\r\n }\r\n const key = VALUE_FLAGS[flag];\r\n if (!key) throw new Error(`Unknown option: ${flag}`);\r\n const value = argv[++i];\r\n if (value === undefined) throw new Error(`${flag} needs a value`);\r\n if (key === 'approvalTimeoutMs' || key === 'timeoutMs') {\r\n const minutes = Number(value);\r\n if (!Number.isFinite(minutes) || minutes <= 0) throw new Error(`${flag} must be a number of minutes`);\r\n out[key] = Math.round(minutes * MINUTE);\r\n } else {\r\n out[key] = value;\r\n }\r\n }\r\n if (out.brief === undefined) throw new Error('--brief <file|-> is required');\r\n return out as unknown as RunArgs;\r\n}\r\n\r\nexport interface RunIo {\r\n readFile(path: string): Promise<string>;\r\n readStdin(): Promise<string>;\r\n out(text: string): void;\r\n err(text: string): void;\r\n cwd: string;\r\n signal?: AbortSignal;\r\n spawnFn?: SpawnFn;\r\n}\r\n\r\nexport async function runFromCli(argv: string[], io: RunIo): Promise<number> {\r\n let task: CodexRunOptions;\r\n try {\r\n const args = parseRunArgs(argv);\r\n const brief = args.brief === '-' ? await io.readStdin() : await io.readFile(args.brief);\r\n if (!brief.trim()) throw new Error('the brief is empty');\r\n task = {\r\n brief,\r\n cwd: path.resolve(io.cwd, args.cwd ?? '.'),\r\n threadId: args.threadId,\r\n name: args.name,\r\n yolo: args.yolo,\r\n outputSchema: args.outputSchema === undefined ? undefined : JSON.parse(await io.readFile(args.outputSchema)),\r\n approvalTimeoutMs: args.approvalTimeoutMs,\r\n timeoutMs: args.timeoutMs,\r\n signal: io.signal,\r\n spawnFn: io.spawnFn,\r\n progress: (line) => io.err(`[codex run] ${line}\\n`),\r\n };\r\n } catch (err) {\r\n io.err(`codex run: ${(err as Error).message}\\n`);\r\n return 2;\r\n }\r\n try {\r\n const result = await runCodexTask(task);\r\n io.out(`${JSON.stringify(result, null, 2)}\\n`);\r\n return result.status === 'completed' ? 0 : 1;\r\n } catch (err) {\r\n io.err(`codex run: could not start the task: ${(err as Error).message}\\n`);\r\n return 2;\r\n }\r\n}\r\n","import { EventEmitter } from 'node:events';\r\nimport type WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type RpcId = number | string;\r\n\r\nexport class RpcError extends Error {\r\n constructor(public readonly code: number, message: string) {\r\n super(message);\r\n }\r\n}\r\n\r\ninterface Pending {\r\n resolve: (value: any) => void;\r\n reject: (err: Error) => void;\r\n timer?: ReturnType<typeof setTimeout>;\r\n}\r\n\r\nexport class RpcClient extends EventEmitter {\r\n private nextId = 1;\r\n private pending = new Map<RpcId, Pending>();\r\n\r\n constructor(private ws: WebSocket, private timeoutMs = 30_000) {\r\n super();\r\n ws.on('message', (data) => this.onMessage(String(data)));\r\n ws.on('close', () => {\r\n for (const p of this.pending.values()) {\r\n clearTimeout(p.timer);\r\n p.reject(new Error('connection closed'));\r\n }\r\n this.pending.clear();\r\n this.emit('close');\r\n });\r\n }\r\n\r\n request<T = any>(method: string, params?: unknown, timeoutMs: number | null = this.timeoutMs): Promise<T> {\r\n if (this.ws.readyState !== this.ws.OPEN) return Promise.reject(new Error('connection closed'));\r\n const id = this.nextId++;\r\n return new Promise<T>((resolve, reject) => {\r\n const timer =\r\n timeoutMs === null\r\n ? undefined\r\n : setTimeout(() => {\r\n this.pending.delete(id);\r\n reject(new Error(`${method} timed out`));\r\n }, timeoutMs);\r\n this.pending.set(id, { resolve, reject, timer });\r\n this.ws.send(JSON.stringify(params === undefined ? { id, method } : { id, method, params }));\r\n });\r\n }\r\n\r\n notify(method: string, params?: unknown): void {\r\n if (this.ws.readyState !== this.ws.OPEN) return;\r\n this.ws.send(JSON.stringify(params === undefined ? { method } : { method, params }));\r\n }\r\n\r\n respond(id: RpcId, result: unknown): void {\r\n if (this.ws.readyState !== this.ws.OPEN) return;\r\n this.ws.send(JSON.stringify({ id, result }));\r\n }\r\n\r\n // ws stops reading the socket for good if a 'message' listener throws.\r\n private guarded(method: string, fn: () => void): void {\r\n try {\r\n fn();\r\n } catch (err) {\r\n logger.warn(`Codex ${method} handling failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private onMessage(text: string): void {\r\n let msg: any;\r\n try {\r\n msg = JSON.parse(text);\r\n } catch {\r\n return;\r\n }\r\n if (!msg || typeof msg !== 'object') return;\r\n if (msg.method === undefined && msg.id !== undefined) {\r\n const p = this.pending.get(msg.id);\r\n if (!p) return;\r\n this.pending.delete(msg.id);\r\n clearTimeout(p.timer);\r\n if (msg.error) p.reject(new RpcError(msg.error.code, msg.error.message));\r\n else p.resolve(msg.result);\r\n } else if (msg.method !== undefined && msg.id !== undefined) {\r\n this.guarded(msg.method, () => this.emit('request', msg.id, msg.method, msg.params));\r\n } else if (msg.method !== undefined) {\r\n this.guarded(msg.method, () => this.emit('notification', msg.method, msg.params));\r\n }\r\n }\r\n}\r\n","import type { MessageSource, SessionStatus } from '../shared/types.js';\r\n\r\nexport type CodexThreadStatus =\r\n | { type: 'notLoaded' | 'idle' | 'systemError' }\r\n | { type: 'active'; activeFlags: string[] };\r\n\r\nexport interface CodexThread {\r\n id: string;\r\n name?: string | null;\r\n preview?: string;\r\n cwd: string;\r\n status: CodexThreadStatus;\r\n parentThreadId?: string | null;\r\n ephemeral?: boolean;\r\n}\r\n\r\nexport interface AgentMessage {\r\n text: string;\r\n phase?: string | null;\r\n}\r\n\r\nconst TITLE_MAX = 30;\r\nconst SOURCE_LABEL: Record<MessageSource, string> = { dashboard: 'Dashboard', telegram: 'Telegram', api: 'API' };\r\n\r\nconst SOURCE_PREFIX = new RegExp(`^\\\\[claude-alarm(?: · (?:${Object.values(SOURCE_LABEL).join('|')}))?\\\\] ?`);\r\n\r\nexport function codexSessionId(threadId: string): string {\r\n return `codex:${threadId}`;\r\n}\r\n\r\nexport function isTrackable(thread: CodexThread): boolean {\r\n return !thread.parentThreadId && !thread.ephemeral && thread.status.type !== 'notLoaded';\r\n}\r\n\r\nexport function threadTitle(thread: CodexThread): string {\r\n const name = thread.name?.trim();\r\n if (name) return name;\r\n const preview = (thread.preview ?? '').replace(/\\s+/g, ' ').trim().replace(SOURCE_PREFIX, '').trim();\r\n if (preview) return preview.length > TITLE_MAX ? `${preview.slice(0, TITLE_MAX)}…` : preview;\r\n return thread.cwd.replace(/^.*[/\\\\]/, '') || thread.id.slice(0, 8);\r\n}\r\n\r\nexport function hubStatus(status: CodexThreadStatus): SessionStatus {\r\n if (status.type !== 'active') return 'idle';\r\n return status.activeFlags.some((f) => f === 'waitingOnApproval' || f === 'waitingOnUserInput') ? 'waiting_input' : 'working';\r\n}\r\n\r\nexport function withSourcePrefix(content: string, source?: MessageSource): string {\r\n return source ? `[claude-alarm · ${SOURCE_LABEL[source]}] ${content}` : `[claude-alarm] ${content}`;\r\n}\r\n\r\nexport function finalAnswer(messages: AgentMessage[]): string | null {\r\n const finals = messages.filter((m) => m.phase === 'final_answer').map((m) => m.text);\r\n if (finals.length) return finals.join('\\n\\n');\r\n return messages.at(-1)?.text ?? null;\r\n}\r\n\r\n// Explorer's \"Copy as path\" wraps the path in double quotes.\r\nexport function cleanFolder(input: string): string {\r\n const s = input.trim();\r\n return s.length >= 2 && s.startsWith('\"') && s.endsWith('\"') ? s.slice(1, -1).trim() : s;\r\n}\r\n","export type HubStartup = 'ready' | 'exited' | 'timeout';\r\n\r\nexport const HUB_START_TIMEOUT_MS = 5000;\r\n\r\nexport interface WaitForHubOptions {\r\n url: string;\r\n token?: string;\r\n pid: number;\r\n isAlive: () => boolean;\r\n timeoutMs?: number;\r\n intervalMs?: number;\r\n}\r\n\r\nexport async function waitForHub(opts: WaitForHubOptions): Promise<HubStartup> {\r\n const timeoutMs = opts.timeoutMs ?? HUB_START_TIMEOUT_MS;\r\n const intervalMs = opts.intervalMs ?? 200;\r\n const deadline = Date.now() + timeoutMs;\r\n const headers: Record<string, string> = opts.token ? { Authorization: `Bearer ${opts.token}` } : {};\r\n for (;;) {\r\n if (!opts.isAlive()) return 'exited';\r\n const remaining = deadline - Date.now();\r\n if (remaining <= 0) return 'timeout';\r\n try {\r\n const res = await fetch(opts.url, { headers, signal: AbortSignal.timeout(Math.min(remaining, 1000)) });\r\n if (res.ok) {\r\n const body = await res.json() as { pid?: unknown };\r\n if (body.pid === opts.pid) return 'ready';\r\n }\r\n } catch {}\r\n await new Promise((resolve) => setTimeout(resolve, Math.min(intervalMs, Math.max(0, deadline - Date.now()))));\r\n }\r\n}\r\n","import { hubUrlHost } from '../shared/hub-url.js';\r\nimport type { AppConfig } from '../shared/types.js';\r\n\r\nexport interface AdapterHub {\r\n host: string;\r\n port: number;\r\n token?: string;\r\n fromEnv: { host: boolean; port: boolean; token: boolean };\r\n}\r\n\r\nexport function resolveAdapterHub(config: AppConfig, env: NodeJS.ProcessEnv = process.env): AdapterHub {\r\n const envHost = env.CLAUDE_ALARM_HUB_HOST;\r\n const envPort = env.CLAUDE_ALARM_HUB_PORT;\r\n const envToken = env.CLAUDE_ALARM_HUB_TOKEN;\r\n return {\r\n host: hubUrlHost(envHost ?? config.hub.host),\r\n port: envPort ? parseInt(envPort, 10) : config.hub.port,\r\n token: envToken ?? config.hub.token,\r\n fromEnv: { host: envHost !== undefined, port: !!envPort, token: envToken !== undefined },\r\n };\r\n}\r\n","import type { OwnerState } from './instance-lock.js';\r\nimport type { ChildProcess } from 'node:child_process';\r\nimport type { FirstConnect } from './adapter.js';\r\nimport type { AdapterHub } from './hub-target.js';\r\n\r\nexport const ADAPTER_REPORT_TIMEOUT_MS = 10_000;\r\nexport const HUB_CHECK_TIMEOUT_MS = 3000;\r\nexport const RESTART_HINT = 'After fixing this, restart the adapter: claude-alarm codex stop, then claude-alarm codex start (if the hub started the adapter, restart the hub instead)';\r\n\r\nconst CONFIG_PATH = '~/.claude-alarm/config.json';\r\nconst NOT_CHECKED: Line = { text: 'Codex daemon: not checked (the adapter was already running)', warning: false };\r\n\r\nexport type AdapterReport =\r\n | { kind: 'report'; outcome: FirstConnect }\r\n | { kind: 'already'; pid: number }\r\n | { kind: 'lockFailed'; error: string }\r\n | { kind: 'exited'; code: number | null; signal: NodeJS.Signals | null; error?: Error }\r\n | { kind: 'timeout' };\r\n\r\nexport type HubCheck =\r\n | { kind: 'ok' }\r\n | { kind: 'unauthorized' }\r\n | { kind: 'not-hub'; status: number }\r\n | { kind: 'unreachable'; reason: string };\r\n\r\nexport interface Line {\r\n text: string;\r\n warning: boolean;\r\n}\r\n\r\nfunction toOutcome(msg: { connected?: unknown; userAgent?: unknown; error?: unknown; notFound?: unknown }): FirstConnect {\r\n if (msg.connected === true) return typeof msg.userAgent === 'string' ? { connected: true, userAgent: msg.userAgent } : { connected: true };\r\n return { connected: false, error: String(msg.error ?? ''), notFound: msg.notFound === true };\r\n}\r\n\r\nexport function waitForAdapterReport(child: ChildProcess, timeoutMs = ADAPTER_REPORT_TIMEOUT_MS): Promise<AdapterReport> {\r\n return new Promise((resolve) => {\r\n const finish = (result: AdapterReport) => {\r\n clearTimeout(timer);\r\n child.off('message', onMessage);\r\n child.off('exit', onExit);\r\n child.off('error', onError);\r\n resolve(result);\r\n };\r\n const onMessage = (msg: unknown) => {\r\n if (!msg || typeof msg !== 'object') return;\r\n const m = msg as Record<string, unknown>;\r\n if (m.type === 'codex-first-connect') finish({ kind: 'report', outcome: toOutcome(m) });\r\n else if (m.type === 'codex-already-running' && Number.isInteger(m.pid)) finish({ kind: 'already', pid: m.pid as number });\r\n else if (m.type === 'codex-lock-failed') finish({ kind: 'lockFailed', error: String(m.error ?? '') });\r\n };\r\n const onExit = (code: number | null, signal: NodeJS.Signals | null) => finish({ kind: 'exited', code, signal });\r\n const onError = (error: Error) => finish({ kind: 'exited', code: null, signal: null, error });\r\n const timer = setTimeout(() => finish({ kind: 'timeout' }), timeoutMs);\r\n child.on('message', onMessage);\r\n child.on('exit', onExit);\r\n child.on('error', onError);\r\n });\r\n}\r\n\r\nexport function hubUrl(hub: AdapterHub): string {\r\n return `http://${hub.host}:${hub.port}`;\r\n}\r\n\r\nexport async function checkHub(hub: AdapterHub, timeoutMs = HUB_CHECK_TIMEOUT_MS): Promise<HubCheck> {\r\n let res: Awaited<ReturnType<typeof fetch>>;\r\n try {\r\n res = await fetch(`${hubUrl(hub)}/api/status`, {\r\n headers: hub.token ? { Authorization: `Bearer ${hub.token}` } : {},\r\n redirect: 'manual',\r\n signal: AbortSignal.timeout(timeoutMs),\r\n });\r\n } catch (err) {\r\n const e = err as Error & { cause?: { code?: unknown } };\r\n return { kind: 'unreachable', reason: typeof e.cause?.code === 'string' ? e.cause.code : e.name };\r\n }\r\n if (res.status === 200) {\r\n try {\r\n const body = (await res.json()) as { running?: unknown } | null;\r\n if (body?.running === true) return { kind: 'ok' };\r\n } catch {}\r\n return { kind: 'not-hub', status: 200 };\r\n }\r\n await res.body?.cancel().catch(() => {});\r\n return res.status === 401 ? { kind: 'unauthorized' } : { kind: 'not-hub', status: res.status };\r\n}\r\n\r\nexport function daemonLine(report: Extract<AdapterReport, { kind: 'report' | 'timeout' }>, command: string, logFile: string): Line {\r\n if (report.kind === 'timeout') {\r\n return { text: `Codex daemon: no answer within ${ADAPTER_REPORT_TIMEOUT_MS / 1000}s. The adapter keeps trying. See ${logFile}`, warning: true };\r\n }\r\n const o = report.outcome;\r\n if (o.connected) return { text: `Codex daemon: connected (${o.userAgent ?? 'unknown version'})`, warning: false };\r\n if (o.notFound) {\r\n return { text: `Codex daemon: \"${command}\" not found. Install Codex or set \"codex.command\" in ${CONFIG_PATH}, and run the restart from a new terminal.`, warning: true };\r\n }\r\n return { text: `Codex daemon: not connected (${o.error}). Is Codex running? The adapter keeps retrying.`, warning: true };\r\n}\r\n\r\nfunction addressSource(hub: AdapterHub): string {\r\n const host = hub.fromEnv.host ? 'CLAUDE_ALARM_HUB_HOST' : 'hub.host';\r\n const port = hub.fromEnv.port ? 'CLAUDE_ALARM_HUB_PORT' : 'hub.port';\r\n const fromConfig = !hub.fromEnv.host || !hub.fromEnv.port;\r\n return `${host} and ${port}${fromConfig ? ` in ${CONFIG_PATH}` : ''}`;\r\n}\r\n\r\nfunction tokenSource(hub: AdapterHub): string {\r\n return hub.fromEnv.token ? 'CLAUDE_ALARM_HUB_TOKEN' : `hub.token in ${CONFIG_PATH}`;\r\n}\r\n\r\nexport function hubLine(check: HubCheck, hub: AdapterHub): Line {\r\n const url = hubUrl(hub);\r\n switch (check.kind) {\r\n case 'ok':\r\n return { text: `Hub: reachable at ${url}`, warning: false };\r\n case 'unauthorized':\r\n return { text: `Hub: ${url} rejected the token (401). Check ${tokenSource(hub)}.`, warning: true };\r\n case 'not-hub':\r\n return { text: `Hub: ${url} answered ${check.status} but is not a claude-alarm hub. Check ${addressSource(hub)}.`, warning: true };\r\n case 'unreachable':\r\n return { text: `Hub: not reachable at ${url} (${check.reason}). Check ${addressSource(hub)} and that the hub is running.`, warning: true };\r\n }\r\n}\r\n\r\nexport interface StartDeps {\r\n hub: AdapterHub;\r\n command: string;\r\n logFile: string;\r\n spawnAdapter: () => ChildProcess;\r\n queryOwner: () => Promise<OwnerState>;\r\n out: (line: string) => void;\r\n err: (line: string) => void;\r\n reportTimeoutMs?: number;\r\n hubTimeoutMs?: number;\r\n}\r\n\r\nfunction printChecks(d: StartDeps, daemon: Line, hub: Line): void {\r\n d.out(` ${daemon.text}`);\r\n d.out(` ${hub.text}`);\r\n if (daemon.warning || hub.warning) d.out(` ${RESTART_HINT}`);\r\n}\r\n\r\nfunction printAlreadyRunning(d: StartDeps, pid: number, hub: HubCheck): void {\r\n d.out(`Codex adapter is already running (PID: ${pid})`);\r\n printChecks(d, NOT_CHECKED, hubLine(hub, d.hub));\r\n}\r\n\r\ninterface Exit {\r\n code: number | null;\r\n signal: NodeJS.Signals | null;\r\n error?: Error;\r\n}\r\n\r\nexport async function startAdapter(d: StartDeps): Promise<number> {\r\n const existing = await d.queryOwner();\r\n if (existing.state === 'running') {\r\n printAlreadyRunning(d, existing.pid, await checkHub(d.hub, d.hubTimeoutMs));\r\n return 0;\r\n }\r\n\r\n const child = d.spawnAdapter();\r\n const seen: { exit?: Exit } = {};\r\n // Watched until the very end: an adapter can report and then die while the hub check is still running.\r\n child.on('exit', (code, signal) => { seen.exit ??= { code, signal }; });\r\n child.on('error', (error) => { seen.exit ??= { code: null, signal: null, error }; });\r\n\r\n const [report, hub] = await Promise.all([\r\n waitForAdapterReport(child, d.reportTimeoutMs),\r\n checkHub(d.hub, d.hubTimeoutMs),\r\n ]);\r\n if (child.connected) child.disconnect();\r\n child.unref();\r\n\r\n if (report.kind === 'already') {\r\n printAlreadyRunning(d, report.pid, hub);\r\n return 0;\r\n }\r\n if (report.kind === 'lockFailed') {\r\n d.err(`Codex adapter cannot start: ${report.error}`);\r\n return 1;\r\n }\r\n if (report.kind !== 'exited' && !seen.exit && child.pid !== undefined) {\r\n d.out(`Codex adapter started (PID: ${child.pid}). Logs: ${d.logFile}`);\r\n printChecks(d, daemonLine(report, d.command, d.logFile), hubLine(hub, d.hub));\r\n return 0;\r\n }\r\n\r\n const exit: Exit | undefined = seen.exit ?? (report.kind === 'exited' ? report : undefined);\r\n if (!exit || exit.error) {\r\n d.err(`Codex adapter failed to start: ${exit?.error?.message ?? 'no process id'}`);\r\n return 1;\r\n }\r\n const owner = await d.queryOwner();\r\n if (owner.state === 'running' && owner.pid !== child.pid) {\r\n printAlreadyRunning(d, owner.pid, hub);\r\n return 0;\r\n }\r\n const how = exit.code !== null ? `code ${exit.code}` : `signal ${exit.signal}`;\r\n d.err(`Codex adapter exited during startup (${how}). See ${d.logFile}`);\r\n return 1;\r\n}\r\n","import crypto from 'node:crypto';\r\nimport fs from 'node:fs';\r\nimport net from 'node:net';\r\nimport path from 'node:path';\r\n\r\nexport const CONTROL_PROTOCOL = 1;\r\nexport const STALE_GUARD_MS = 10_000;\r\nexport const STOP_PROOF_LABEL = 'claude-alarm codex stop';\r\nconst MAX_LINE_BYTES = 4096;\r\nconst REQUEST_IDLE_MS = 2000;\r\nconst MAX_SOCKET_PATH_BYTES = 103;\r\n\r\nexport function stopProof(token: string): string {\r\n return crypto.createHmac('sha256', token).update(STOP_PROOF_LABEL).digest('hex');\r\n}\r\n\r\nexport type OwnerState =\r\n | { state: 'running'; pid: number }\r\n | { state: 'absent' }\r\n | { state: 'unknown'; error: string };\r\n\r\nexport type StopResult =\r\n | { state: 'stopping'; pid: number }\r\n | { state: 'unauthorized' }\r\n | { state: 'absent' }\r\n | { state: 'unknown'; error: string };\r\n\r\nexport type Acquired =\r\n | { kind: 'owner'; close: () => Promise<void> }\r\n | { kind: 'held'; pid: number }\r\n | { kind: 'unknown'; error: string };\r\n\r\nexport type Attempt = Acquired | { kind: 'busy' };\r\n\r\nexport interface LockOwner {\r\n pid: number;\r\n token: string;\r\n onStop: () => void;\r\n}\r\n\r\ntype LockFs = Pick<typeof fs, 'mkdirSync' | 'rmdirSync' | 'statSync' | 'unlinkSync'>;\r\n\r\nexport interface LockDeps {\r\n platform?: NodeJS.Platform;\r\n listen?: (endpoint: string, owner: LockOwner) => Promise<Attempt>;\r\n queryOwner?: (endpoint: string) => Promise<OwnerState>;\r\n fs?: LockFs;\r\n now?: () => number;\r\n}\r\n\r\nexport function controlEndpoint(configDir: string, platform: NodeJS.Platform = process.platform): string {\r\n if (platform !== 'win32') return path.posix.join(configDir, 'codex.sock');\r\n let dir: string;\r\n try {\r\n dir = fs.realpathSync.native(configDir);\r\n } catch {\r\n dir = path.win32.resolve(configDir);\r\n }\r\n const hash = crypto.createHash('sha256').update(dir.toLowerCase()).digest('hex').slice(0, 16);\r\n return `\\\\\\\\.\\\\pipe\\\\claude-alarm-codex-${hash}`;\r\n}\r\n\r\ntype Answer = { reply: unknown } | { state: 'absent' } | { state: 'unknown'; error: string };\r\n\r\nfunction ask(endpoint: string, request: object, timeoutMs: number): Promise<Answer> {\r\n return new Promise((resolve) => {\r\n let buffer = '';\r\n let settled = false;\r\n const socket = net.connect(endpoint);\r\n const finish = (answer: Answer) => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n socket.destroy();\r\n resolve(answer);\r\n };\r\n const timer = setTimeout(() => finish({ state: 'unknown', error: `no answer within ${timeoutMs}ms` }), timeoutMs);\r\n socket.setEncoding('utf8');\r\n socket.on('connect', () => socket.write(`${JSON.stringify(request)}\\n`));\r\n socket.on('data', (chunk: string) => {\r\n buffer += chunk;\r\n const end = buffer.indexOf('\\n');\r\n if (end < 0) {\r\n if (Buffer.byteLength(buffer) > MAX_LINE_BYTES) finish({ state: 'unknown', error: 'reply too long' });\r\n return;\r\n }\r\n try {\r\n finish({ reply: JSON.parse(buffer.slice(0, end)) });\r\n } catch {\r\n finish({ state: 'unknown', error: 'unreadable reply' });\r\n }\r\n });\r\n socket.on('end', () => finish({ state: 'unknown', error: 'closed without a reply' }));\r\n socket.on('error', (err: NodeJS.ErrnoException) => {\r\n if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED') finish({ state: 'absent' });\r\n else finish({ state: 'unknown', error: err.code ?? err.message });\r\n });\r\n });\r\n}\r\n\r\nexport async function queryOwner(endpoint: string, timeoutMs = 1000): Promise<OwnerState> {\r\n const answer = await ask(endpoint, { type: 'status' }, timeoutMs);\r\n if (!('reply' in answer)) return answer;\r\n const reply = answer.reply as { type?: unknown; protocol?: unknown; pid?: unknown } | null;\r\n if (reply?.type === 'status' && reply.protocol === CONTROL_PROTOCOL && Number.isInteger(reply.pid)) {\r\n return { state: 'running', pid: reply.pid as number };\r\n }\r\n return { state: 'unknown', error: 'unexpected reply' };\r\n}\r\n\r\nexport async function requestStop(endpoint: string, token: string, timeoutMs = 1000): Promise<StopResult> {\r\n const answer = await ask(endpoint, { type: 'stop', proof: stopProof(token) }, timeoutMs);\r\n if (!('reply' in answer)) return answer;\r\n const reply = answer.reply as { type?: unknown; pid?: unknown; error?: unknown } | null;\r\n if (reply?.type === 'stopping' && Number.isInteger(reply.pid)) return { state: 'stopping', pid: reply.pid as number };\r\n if (reply?.type === 'error' && reply.error === 'unauthorized') return { state: 'unauthorized' };\r\n return { state: 'unknown', error: 'unexpected reply' };\r\n}\r\n\r\nconst line = (message: object) => `${JSON.stringify(message)}\\n`;\r\n\r\nfunction controlServer(owner: LockOwner): net.Server {\r\n const expectedProof = Buffer.from(stopProof(owner.token));\r\n return net.createServer((socket) => {\r\n let buffer = '';\r\n socket.setEncoding('utf8');\r\n socket.on('error', () => {});\r\n const idle = setTimeout(() => socket.destroy(), REQUEST_IDLE_MS);\r\n socket.on('close', () => clearTimeout(idle));\r\n const onData = (chunk: string) => {\r\n buffer += chunk;\r\n const end = buffer.indexOf('\\n');\r\n if (end < 0) {\r\n if (Buffer.byteLength(buffer) > MAX_LINE_BYTES) socket.destroy();\r\n return;\r\n }\r\n socket.off('data', onData);\r\n clearTimeout(idle);\r\n let request: { type?: unknown; proof?: unknown } | null = null;\r\n try {\r\n request = JSON.parse(buffer.slice(0, end));\r\n } catch {}\r\n if (request?.type === 'status') {\r\n socket.end(line({ type: 'status', protocol: CONTROL_PROTOCOL, pid: owner.pid }));\r\n } else if (request?.type === 'stop') {\r\n const proof = typeof request.proof === 'string' ? Buffer.from(request.proof) : undefined;\r\n if (owner.token !== '' && proof && proof.length === expectedProof.length && crypto.timingSafeEqual(proof, expectedProof)) {\r\n socket.end(line({ type: 'stopping', pid: owner.pid }), () => owner.onStop());\r\n } else {\r\n socket.end(line({ type: 'error', error: 'unauthorized' }));\r\n }\r\n } else {\r\n socket.end(line({ type: 'error', error: 'unknown request' }));\r\n }\r\n };\r\n socket.on('data', onData);\r\n });\r\n}\r\n\r\nexport function listenControl(endpoint: string, owner: LockOwner): Promise<Attempt> {\r\n const server = controlServer(owner);\r\n return new Promise((resolve) => {\r\n const onError = (err: NodeJS.ErrnoException) => {\r\n resolve(err.code === 'EADDRINUSE' ? { kind: 'busy' } : { kind: 'unknown', error: err.code ?? err.message });\r\n };\r\n server.once('error', onError);\r\n server.listen(endpoint, () => {\r\n server.off('error', onError);\r\n server.on('error', () => {});\r\n resolve({ kind: 'owner', close: () => new Promise<void>((done) => server.close(() => done())) });\r\n });\r\n });\r\n}\r\n\r\nfunction settle(attempt: Attempt, busy: string): Acquired {\r\n return attempt.kind === 'busy' ? { kind: 'unknown', error: busy } : attempt;\r\n}\r\n\r\nexport async function acquireLock(endpoint: string, owner: LockOwner, deps: LockDeps = {}): Promise<Acquired> {\r\n const platform = deps.platform ?? process.platform;\r\n const listen = deps.listen ?? listenControl;\r\n const query = deps.queryOwner ?? ((e: string) => queryOwner(e));\r\n if (platform !== 'win32' && Buffer.byteLength(endpoint) > MAX_SOCKET_PATH_BYTES) {\r\n return { kind: 'unknown', error: `control socket path is too long: ${endpoint}` };\r\n }\r\n const first = await listen(endpoint, owner);\r\n if (first.kind !== 'busy') return first;\r\n const state = await query(endpoint);\r\n if (state.state === 'running') return { kind: 'held', pid: state.pid };\r\n if (state.state === 'unknown') return { kind: 'unknown', error: state.error };\r\n if (platform === 'win32') return settle(await listen(endpoint, owner), 'the control pipe is in use but does not answer');\r\n return recoverSocket(endpoint, owner, listen, query, deps.fs ?? fs, deps.now ?? Date.now);\r\n}\r\n\r\n// Two starters that both find a dead socket file would each unlink it, and the second unlink would remove the first one's live socket.\r\nasync function recoverSocket(\r\n endpoint: string,\r\n owner: LockOwner,\r\n listen: (endpoint: string, owner: LockOwner) => Promise<Attempt>,\r\n query: (endpoint: string) => Promise<OwnerState>,\r\n f: LockFs,\r\n now: () => number,\r\n): Promise<Acquired> {\r\n const guard = `${endpoint}.lock`;\r\n const blocked = takeGuard(guard, f, now);\r\n if (blocked) return { kind: 'unknown', error: blocked };\r\n try {\r\n const state = await query(endpoint);\r\n if (state.state === 'running') return { kind: 'held', pid: state.pid };\r\n if (state.state === 'unknown') return { kind: 'unknown', error: state.error };\r\n try {\r\n f.unlinkSync(endpoint);\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') return { kind: 'unknown', error: (err as Error).message };\r\n }\r\n return settle(await listen(endpoint, owner), 'the control socket is in use but does not answer');\r\n } finally {\r\n try {\r\n f.rmdirSync(guard);\r\n } catch {}\r\n }\r\n}\r\n\r\nfunction takeGuard(guard: string, f: LockFs, now: () => number): string | undefined {\r\n for (let attempt = 0; attempt < 2; attempt++) {\r\n try {\r\n f.mkdirSync(guard);\r\n return undefined;\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') return (err as Error).message;\r\n }\r\n let age: number;\r\n try {\r\n age = now() - f.statSync(guard).mtimeMs;\r\n } catch {\r\n continue;\r\n }\r\n if (age <= STALE_GUARD_MS) return 'another Codex adapter is starting';\r\n try {\r\n f.rmdirSync(guard);\r\n } catch {}\r\n }\r\n return 'another Codex adapter is starting';\r\n}\r\n","import type { OwnerState, StopResult } from './instance-lock.js';\r\n\r\nexport const STOP_WAIT_MS = 5000;\r\nconst STOP_POLL_MS = 100;\r\n\r\nexport interface ControlDeps {\r\n queryOwner: () => Promise<OwnerState>;\r\n requestStop: () => Promise<StopResult>;\r\n legacyPid: () => number | undefined;\r\n isRunning: (pid: number) => boolean;\r\n removeLegacyPidFile: () => void;\r\n legacyPidFile: string;\r\n configFile: string;\r\n out: (line: string) => void;\r\n err: (line: string) => void;\r\n sleep?: (ms: number) => Promise<void>;\r\n stopWaitMs?: number;\r\n}\r\n\r\nexport function legacyNote(d: Pick<ControlDeps, 'legacyPid' | 'isRunning' | 'removeLegacyPidFile' | 'legacyPidFile' | 'out'>, ownerPid?: number): void {\r\n const pid = d.legacyPid();\r\n if (pid === undefined || pid === ownerPid) return;\r\n if (!d.isRunning(pid)) {\r\n d.removeLegacyPidFile();\r\n return;\r\n }\r\n d.out(`Note: ${d.legacyPidFile} names a running process (PID: ${pid}). claude-alarm 1.2.0 and earlier wrote this file; if that process is an old Codex adapter, restart the hub or end it yourself.`);\r\n}\r\n\r\nfunction unavailable(d: ControlDeps, error: string): number {\r\n d.err(`Codex adapter control endpoint is unavailable: ${error}`);\r\n return 1;\r\n}\r\n\r\nexport async function stopAdapter(d: ControlDeps, startWithHub: boolean): Promise<number> {\r\n const owner = await d.queryOwner();\r\n if (owner.state === 'unknown') return unavailable(d, owner.error);\r\n if (owner.state === 'absent') {\r\n d.out('Codex adapter is not running');\r\n legacyNote(d);\r\n return 0;\r\n }\r\n const reply = await d.requestStop();\r\n if (reply.state === 'unknown') return unavailable(d, reply.error);\r\n if (reply.state === 'unauthorized') {\r\n d.err(`Codex adapter refused to stop: the token in ${d.configFile} does not match the one it started with`);\r\n return 1;\r\n }\r\n if (reply.state === 'absent') {\r\n d.out('Codex adapter is not running');\r\n return 0;\r\n }\r\n const sleep = d.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\r\n for (let waited = 0; waited < (d.stopWaitMs ?? STOP_WAIT_MS); waited += STOP_POLL_MS) {\r\n await sleep(STOP_POLL_MS);\r\n const now = await d.queryOwner();\r\n // A waiting hub adapter may take over later; a different PID still means this one stopped.\r\n if (now.state === 'absent' || (now.state === 'running' && now.pid !== reply.pid)) {\r\n d.out(`Codex adapter stopped (PID: ${reply.pid})`);\r\n if (now.state === 'running') d.out(`Another Codex adapter took over (PID: ${now.pid})`);\r\n else if (startWithHub) d.out('If the hub started it, the hub will not start it again until you restart the hub.');\r\n return 0;\r\n }\r\n }\r\n d.err(`Stop requested, but the Codex adapter (PID: ${reply.pid}) is still running. Check with: claude-alarm codex status`);\r\n return 1;\r\n}\r\n\r\nexport async function adapterStatus(d: ControlDeps, startWithHub: boolean): Promise<number> {\r\n const owner = await d.queryOwner();\r\n if (owner.state === 'running') {\r\n d.out(`Codex adapter: running (PID: ${owner.pid})`);\r\n legacyNote(d, owner.pid);\r\n } else if (owner.state === 'unknown') {\r\n d.out(`Codex adapter: unknown (${owner.error})`);\r\n } else {\r\n d.out('Codex adapter: not running');\r\n legacyNote(d);\r\n }\r\n d.out(`Start with hub: ${startWithHub ? 'enabled' : 'disabled'}`);\r\n return 0;\r\n}\r\n"],"mappings":"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","names":["path","config","fs","sessionId","peerName","sessionId","sessionLabel","sessionId","fs","path","randomUUID","config","sessionId","sessionLabel","spawn","fs","path","crypto","fs","path","WebSocket","randomUUID","config","sessionId","WebSocket","sessionId","sessionName","hubHost","hubPort","fs","os","path","init_peer_name","server_exports","randomUUID","path","init_server","init_peer_name","spawn","fs","path","fileURLToPath","fs","path","path","init","path","config","fs","net","path","path","fs","net","__dirname","path","fileURLToPath","fs","config","spawn","HubServer","ask","main"]}
|