@delt/claude-alarm 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1064,7 +1064,7 @@ async function checkForUpdates() {
1064
1064
  if (latestVersion !== currentVersion) {
1065
1065
  console.log(`
1066
1066
  \u26A0 New version available: ${currentVersion} \u2192 ${latestVersion}`);
1067
- console.log(` Run: npm update -g @delt/claude-alarm
1067
+ console.log(` Run: npm install -g @delt/claude-alarm
1068
1068
  `);
1069
1069
  }
1070
1070
  } catch {
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/hub/session-manager.ts","../src/hub/notifier.ts","../src/hub/server.ts","../src/channel/hub-client.ts","../src/channel/server.ts","../src/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\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 } };\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\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 type { SessionInfo, SessionStatus } from '../shared/types.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 this.sessions.set(session.id, { ...session });\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 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","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\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\r\n configure(options: { desktop?: boolean; webhooks?: WebhookConfig[]; dashboardUrl?: string }): 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 }\r\n\r\n async notify(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 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 http from 'node:http';\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 { loadConfig, saveConfig } from '../shared/config.js';\r\nimport type { ChannelMessage, AppConfig, SessionInfo, WebhookConfig } from '../shared/types.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\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 host: string;\r\n private port: number;\r\n private token?: string;\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\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 // 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 });\r\n this.wssChannel.on('connection', (ws: WebSocket, req: http.IncomingMessage) => this.handleChannelConnection(ws, req));\r\n\r\n // WebSocket for dashboard\r\n this.wssDashboard = new WebSocketServer({ noServer: true });\r\n this.wssDashboard.on('connection', (ws) => this.handleDashboardConnection(ws));\r\n\r\n // Route WebSocket upgrade requests\r\n this.httpServer.on('upgrade', (req, socket, head) => {\r\n const url = new URL(req.url!, `http://${req.headers.host}`);\r\n const pathname = url.pathname;\r\n\r\n // Token auth for WebSocket connections (skip for local requests)\r\n if (this.token && !this.isLocalRequest(req)) {\r\n const wsToken = url.searchParams.get('token');\r\n if (wsToken !== this.token) {\r\n socket.destroy();\r\n return;\r\n }\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 return new Promise((resolve, reject) => {\r\n this.httpServer.on('error', reject);\r\n this.httpServer.listen(this.port, this.host, () => {\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 resolve();\r\n });\r\n });\r\n }\r\n\r\n stop(): Promise<void> {\r\n return new Promise((resolve) => {\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://${req.headers.host}`);\r\n\r\n // CORS headers - restrict to same origin\r\n const origin = req.headers.origin;\r\n if (origin && (origin.includes('127.0.0.1') || origin.includes('localhost'))) {\r\n res.setHeader('Access-Control-Allow-Origin', origin);\r\n }\r\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\r\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\r\n\r\n if (req.method === 'OPTIONS') {\r\n res.writeHead(204);\r\n res.end();\r\n return;\r\n }\r\n\r\n // Token auth for API endpoints (skip dashboard HTML serving)\r\n if (url.pathname !== '/' && this.token) {\r\n if (!this.isLocalRequest(req)) {\r\n const authHeader = req.headers['authorization'];\r\n const bearerToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;\r\n if (bearerToken !== this.token) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Route\r\n if (url.pathname === '/' && req.method === 'GET') {\r\n this.serveDashboard(res);\r\n } else 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 === '/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 {\r\n this.jsonResponse(res, 404, { error: 'Not found' });\r\n }\r\n }\r\n\r\n private serveDashboard(res: http.ServerResponse): void {\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 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 };\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.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 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 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})`);\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 '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.notify(`[${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.notify(`[${replyLabel}] Reply`, msg.content.slice(0, 200), '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 }\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 // 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\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));\r\n }\r\n } else if (msg.type === 'image_upload') {\r\n this.handleImageUpload(msg);\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 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 } = 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 };\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 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 if (!session) return 'unknown';\r\n return session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\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 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 const hub = new HubServer();\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\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 ) {}\r\n\r\n connect(): void {\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 this.ws = new WebSocket(url);\r\n\r\n this.ws.on('open', () => {\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n // Register this session\r\n const registration: ChannelMessage = {\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 },\r\n };\r\n this.ws!.send(JSON.stringify(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 this.ws.on('message', (data) => {\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 this.ws.on('close', () => {\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n this.scheduleReconnect();\r\n });\r\n\r\n this.ws.on('error', (err) => {\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 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 private scheduleReconnect(): void {\r\n if (this.reconnectTimer) 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 { 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 { 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 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\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: { 'claude/channel': {} },\r\n tools: {},\r\n },\r\n instructions:\r\n 'Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. ' +\r\n 'Read the message and act on it. Reply with the same detail and depth as you normally would — do not shorten your response. ' +\r\n 'IMPORTANT: The dashboard user can ONLY see messages sent via the reply tool. Your terminal output is NOT visible on the dashboard. ' +\r\n 'Therefore, when responding to a dashboard message, you MUST call the reply tool with your response so the dashboard user can see it. ' +\r\n 'Use the notify tool to send desktop notifications for key events: task completion, errors, or when user input is needed. ' +\r\n 'Do NOT notify for intermediate steps or simple acknowledgments. ' +\r\n 'Use the status tool to update your session 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);\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// --- 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 // 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 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}`,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()), imagePath: msg.imagePath, mimeType: msg.mimeType },\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\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 } from './shared/config.js';\r\nimport { PID_FILE, LOG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './shared/constants.js';\r\nimport { logger } from './shared/logger.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\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 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 update -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}) on http://${displayHost}:${port}`);\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: http://${displayHost}:${port}`);\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(`Token: ${config.hub.token}`);\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`);\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`);\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\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\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 === '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"],"mappings":"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","names":["path","config","sessionId","fs","path","randomUUID","config","sessionId","WebSocket","sessionId","sessionName","hubHost","hubPort","server_exports","randomUUID","path","init_server","fs","path","fileURLToPath","__dirname","config","HubServer","main"]}
1
+ {"version":3,"sources":["../src/shared/constants.ts","../src/shared/config.ts","../src/shared/logger.ts","../src/hub/session-manager.ts","../src/hub/notifier.ts","../src/hub/server.ts","../src/channel/hub-client.ts","../src/channel/server.ts","../src/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\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 } };\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\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 type { SessionInfo, SessionStatus } from '../shared/types.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 this.sessions.set(session.id, { ...session });\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 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","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\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\r\n configure(options: { desktop?: boolean; webhooks?: WebhookConfig[]; dashboardUrl?: string }): 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 }\r\n\r\n async notify(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 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 http from 'node:http';\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 { loadConfig, saveConfig } from '../shared/config.js';\r\nimport type { ChannelMessage, AppConfig, SessionInfo, WebhookConfig } from '../shared/types.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\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 host: string;\r\n private port: number;\r\n private token?: string;\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\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 // 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 });\r\n this.wssChannel.on('connection', (ws: WebSocket, req: http.IncomingMessage) => this.handleChannelConnection(ws, req));\r\n\r\n // WebSocket for dashboard\r\n this.wssDashboard = new WebSocketServer({ noServer: true });\r\n this.wssDashboard.on('connection', (ws) => this.handleDashboardConnection(ws));\r\n\r\n // Route WebSocket upgrade requests\r\n this.httpServer.on('upgrade', (req, socket, head) => {\r\n const url = new URL(req.url!, `http://${req.headers.host}`);\r\n const pathname = url.pathname;\r\n\r\n // Token auth for WebSocket connections (skip for local requests)\r\n if (this.token && !this.isLocalRequest(req)) {\r\n const wsToken = url.searchParams.get('token');\r\n if (wsToken !== this.token) {\r\n socket.destroy();\r\n return;\r\n }\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 return new Promise((resolve, reject) => {\r\n this.httpServer.on('error', reject);\r\n this.httpServer.listen(this.port, this.host, () => {\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 resolve();\r\n });\r\n });\r\n }\r\n\r\n stop(): Promise<void> {\r\n return new Promise((resolve) => {\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://${req.headers.host}`);\r\n\r\n // CORS headers - restrict to same origin\r\n const origin = req.headers.origin;\r\n if (origin && (origin.includes('127.0.0.1') || origin.includes('localhost'))) {\r\n res.setHeader('Access-Control-Allow-Origin', origin);\r\n }\r\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\r\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\r\n\r\n if (req.method === 'OPTIONS') {\r\n res.writeHead(204);\r\n res.end();\r\n return;\r\n }\r\n\r\n // Token auth for API endpoints (skip dashboard HTML serving)\r\n if (url.pathname !== '/' && this.token) {\r\n if (!this.isLocalRequest(req)) {\r\n const authHeader = req.headers['authorization'];\r\n const bearerToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;\r\n if (bearerToken !== this.token) {\r\n this.jsonResponse(res, 401, { error: 'Unauthorized' });\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Route\r\n if (url.pathname === '/' && req.method === 'GET') {\r\n this.serveDashboard(res);\r\n } else 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 === '/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 {\r\n this.jsonResponse(res, 404, { error: 'Not found' });\r\n }\r\n }\r\n\r\n private serveDashboard(res: http.ServerResponse): void {\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 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 };\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.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 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 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})`);\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 '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.notify(`[${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.notify(`[${replyLabel}] Reply`, msg.content.slice(0, 200), '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 }\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 // 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\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));\r\n }\r\n } else if (msg.type === 'image_upload') {\r\n this.handleImageUpload(msg);\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 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 } = 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 };\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 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 if (!session) return 'unknown';\r\n return session.cwd?.replace(/^.*[/\\\\]/, '') || session.name;\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 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 const hub = new HubServer();\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\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 ) {}\r\n\r\n connect(): void {\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 this.ws = new WebSocket(url);\r\n\r\n this.ws.on('open', () => {\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n // Register this session\r\n const registration: ChannelMessage = {\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 },\r\n };\r\n this.ws!.send(JSON.stringify(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 this.ws.on('message', (data) => {\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 this.ws.on('close', () => {\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n this.scheduleReconnect();\r\n });\r\n\r\n this.ws.on('error', (err) => {\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 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 private scheduleReconnect(): void {\r\n if (this.reconnectTimer) 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 { 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 { 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 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\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: { 'claude/channel': {} },\r\n tools: {},\r\n },\r\n instructions:\r\n 'Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. ' +\r\n 'Read the message and act on it. Reply with the same detail and depth as you normally would — do not shorten your response. ' +\r\n 'IMPORTANT: The dashboard user can ONLY see messages sent via the reply tool. Your terminal output is NOT visible on the dashboard. ' +\r\n 'Therefore, when responding to a dashboard message, you MUST call the reply tool with your response so the dashboard user can see it. ' +\r\n 'Use the notify tool to send desktop notifications for key events: task completion, errors, or when user input is needed. ' +\r\n 'Do NOT notify for intermediate steps or simple acknowledgments. ' +\r\n 'Use the status tool to update your session 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);\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// --- 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 // 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 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}`,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()), imagePath: msg.imagePath, mimeType: msg.mimeType },\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\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 } from './shared/config.js';\r\nimport { PID_FILE, LOG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './shared/constants.js';\r\nimport { logger } from './shared/logger.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\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 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}) on http://${displayHost}:${port}`);\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: http://${displayHost}:${port}`);\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(`Token: ${config.hub.token}`);\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`);\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`);\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\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\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 === '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"],"mappings":"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","names":["path","config","sessionId","fs","path","randomUUID","config","sessionId","WebSocket","sessionId","sessionName","hubHost","hubPort","server_exports","randomUUID","path","init_server","fs","path","fileURLToPath","__dirname","config","HubServer","main"]}
@@ -227,6 +227,8 @@
227
227
  .messages-panel {
228
228
  display: flex;
229
229
  flex-direction: column;
230
+ overflow: hidden;
231
+ min-height: 0;
230
232
  }
231
233
  .messages-header {
232
234
  padding: 12px 20px;
@@ -237,6 +239,7 @@
237
239
  .messages-list {
238
240
  flex: 1;
239
241
  overflow-y: auto;
242
+ min-height: 0;
240
243
  padding: 16px 20px;
241
244
  }
242
245
  .message {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delt/claude-alarm",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Monitor and get notifications from multiple Claude Code sessions via MCP Channels",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -227,6 +227,8 @@
227
227
  .messages-panel {
228
228
  display: flex;
229
229
  flex-direction: column;
230
+ overflow: hidden;
231
+ min-height: 0;
230
232
  }
231
233
  .messages-header {
232
234
  padding: 12px 20px;
@@ -237,6 +239,7 @@
237
239
  .messages-list {
238
240
  flex: 1;
239
241
  overflow-y: auto;
242
+ min-height: 0;
240
243
  padding: 16px 20px;
241
244
  }
242
245
  .message {