@delt/claude-alarm 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +115 -87
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +7 -3
- package/dist/codex/main.js.map +1 -1
- package/dist/hub/server.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/codex/main.js
CHANGED
|
@@ -259,14 +259,18 @@ import { Duplex } from "stream";
|
|
|
259
259
|
import WebSocket2 from "ws";
|
|
260
260
|
function resolveCommand(command, platform = process.platform, env = process.env) {
|
|
261
261
|
if (platform !== "win32" || /[\\/]/.test(command) || path3.extname(command)) return { file: command, shell: false };
|
|
262
|
+
const file = findOnPath(command, platform, env);
|
|
263
|
+
return file ? { file, shell: file.endsWith(".cmd") } : { file: command, shell: false };
|
|
264
|
+
}
|
|
265
|
+
function findOnPath(command, platform = process.platform, env = process.env) {
|
|
262
266
|
const dirs = (env.PATH ?? env.Path ?? "").split(path3.delimiter).filter(Boolean);
|
|
263
|
-
for (const ext of [".exe", ".cmd"]) {
|
|
267
|
+
for (const ext of platform === "win32" ? [".exe", ".cmd"] : [""]) {
|
|
264
268
|
for (const dir of dirs) {
|
|
265
269
|
const file = path3.join(dir, command + ext);
|
|
266
|
-
if (fs3.existsSync(file)) return
|
|
270
|
+
if (fs3.existsSync(file)) return file;
|
|
267
271
|
}
|
|
268
272
|
}
|
|
269
|
-
return
|
|
273
|
+
return void 0;
|
|
270
274
|
}
|
|
271
275
|
var defaultSpawn = (command, args) => {
|
|
272
276
|
const { file, shell } = resolveCommand(command);
|
package/dist/codex/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codex/main.ts","../../src/shared/config.ts","../../src/shared/constants.ts","../../src/shared/crash-guard.ts","../../src/shared/logger.ts","../../src/channel/hub-client.ts","../../src/codex/transport.ts","../../src/codex/rpc.ts","../../src/codex/mapping.ts","../../src/codex/adapter.ts"],"sourcesContent":["import fs from 'node:fs';\r\nimport { loadConfig } from '../shared/config.js';\r\nimport { CODEX_PID_FILE, DEFAULT_HUB_HOST } from '../shared/constants.js';\r\nimport { installCrashGuard, logStartup } from '../shared/crash-guard.js';\r\nimport { logger } from '../shared/logger.js';\r\nimport { CodexAdapter } from './adapter.js';\r\n\r\nfunction isRunning(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\ninstallCrashGuard('codex adapter');\r\n// Once the hub is gone its pipes break; without listeners every log line would become an uncaught EPIPE.\r\nprocess.stdout.on('error', () => {});\r\nprocess.stderr.on('error', () => {});\r\nconst config = loadConfig();\r\n\r\nconst existing = fs.existsSync(CODEX_PID_FILE) ? parseInt(fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim(), 10) : NaN;\r\nif (!Number.isNaN(existing) && existing !== process.pid && isRunning(existing)) {\r\n logger.info(`Codex adapter already running (PID: ${existing})`);\r\n process.exit(0);\r\n}\r\nfs.writeFileSync(CODEX_PID_FILE, String(process.pid), 'utf-8');\r\nlogStartup('Codex adapter');\r\n\r\nconst host = process.env.CLAUDE_ALARM_HUB_HOST ?? (config.hub.host === '0.0.0.0' ? DEFAULT_HUB_HOST : config.hub.host);\r\nconst port = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\r\nconst token = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\r\n\r\nconst adapter = new CodexAdapter({ command: config.codex?.command ?? 'codex', hub: { host, port, token } });\r\nadapter.start();\r\n\r\nlet exiting = false;\r\nconst shutdown = () => {\r\n if (exiting) return;\r\n exiting = true;\r\n adapter.stop();\r\n try {\r\n if (fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim() === String(process.pid)) fs.unlinkSync(CODEX_PID_FILE);\r\n } catch {}\r\n process.exit(0);\r\n};\r\nprocess.on('SIGINT', shutdown);\r\nprocess.on('SIGTERM', shutdown);\r\n\r\n// On Windows `hub stop` kills the hub without running its exit handlers, so stdin EOF is the only sign the parent is gone.\r\nif (process.argv.includes('--watch-stdin')) {\r\n process.stdin.on('end', shutdown);\r\n process.stdin.on('close', shutdown);\r\n process.stdin.resume();\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\r\nimport type { AppConfig } from './types.js';\r\n\r\nconst DEFAULT_CONFIG: AppConfig = {\r\n hub: {\r\n host: DEFAULT_HUB_HOST,\r\n port: DEFAULT_HUB_PORT,\r\n },\r\n notifications: {\r\n desktop: true,\r\n sound: true,\r\n },\r\n webhooks: [],\r\n};\r\n\r\nexport function ensureConfigDir(): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n}\r\n\r\nexport function loadConfig(): AppConfig {\r\n ensureConfigDir();\r\n let config: AppConfig;\r\n if (!fs.existsSync(CONFIG_FILE)) {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n } else {\r\n try {\r\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\r\n const parsed = JSON.parse(raw);\r\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub }, ...(parsed.telegram ? { telegram: parsed.telegram } : {}) };\r\n } catch {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n }\r\n }\r\n\r\n // Auto-generate token if missing\r\n if (!config.hub.token) {\r\n config.hub.token = randomUUID();\r\n saveConfig(config);\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** Get the current token, generating one if needed */\r\nexport function getOrCreateToken(): string {\r\n const config = loadConfig();\r\n return config.hub.token!;\r\n}\r\n\r\nexport function saveConfig(config: AppConfig): void {\r\n ensureConfigDir();\r\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\r\n}\r\n\r\nexport function setCodexEnabled(enabled: boolean): AppConfig {\r\n const config = loadConfig();\r\n config.codex = { ...config.codex, enabled };\r\n saveConfig(config);\r\n return config;\r\n}\r\n\r\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","import path from 'node:path';\r\nimport os from 'node:os';\r\n\r\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\r\nexport const DEFAULT_HUB_PORT = 7900;\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\r\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\r\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\r\nexport const UPLOADS_DIR = path.join(CONFIG_DIR, 'uploads');\r\nexport const CODEX_PID_FILE = path.join(CONFIG_DIR, 'codex.pid');\r\nexport const CODEX_LOG_FILE = path.join(CONFIG_DIR, 'codex.log');\r\n\r\nexport const WS_PATH_CHANNEL = '/ws/channel';\r\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\r\n\r\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\r\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\r\n","import fs from 'node:fs';\nimport { CONFIG_DIR, LOG_FILE } from './constants.js';\nimport { logger } from './logger.js';\n\nconst BURST_WINDOW_MS = 60_000;\nconst BURST_LIMIT = 10;\n\nfunction appendToLog(text: string): void {\n try {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.appendFileSync(LOG_FILE, text, 'utf-8');\n } catch {}\n}\n\nfunction describe(err: unknown): string {\n if (err instanceof Error) return err.stack ?? `${err.name}: ${err.message}`;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\n/**\n * Last-resort net for exceptions no local handler caught.\n *\n * The hub keeps running afterwards: a notification relay that dies silently is\n * worse than one running on possibly-degraded state, and in foreground mode the\n * stack trace would otherwise vanish with the console window. A repeated burst\n * still exits, so a self-feeding error loop cannot spin forever.\n */\nexport function installCrashGuard(context: string): void {\n let recent: number[] = [];\n\n const handle = (kind: string, err: unknown): void => {\n const stack = describe(err);\n appendToLog(`\\n[${new Date().toISOString()}] ${kind} (${context})\\n${stack}\\n`);\n logger.error(`${kind} caught by crash guard: ${stack}`);\n\n const now = Date.now();\n recent = recent.filter((t) => now - t < BURST_WINDOW_MS);\n recent.push(now);\n\n if (recent.length > BURST_LIMIT) {\n const msg = `${recent.length} unhandled errors within ${BURST_WINDOW_MS / 1000}s — exiting`;\n appendToLog(`[${new Date().toISOString()}] ${msg}\\n`);\n logger.error(msg);\n process.exit(1);\n }\n };\n\n process.on('uncaughtException', (err) => handle('uncaughtException', err));\n process.on('unhandledRejection', (reason) => handle('unhandledRejection', reason));\n}\n\nexport function logStartup(context: string): void {\n appendToLog(`\\n[${new Date().toISOString()}] ${context} started (pid ${process.pid})\\n`);\n}\n","/**\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 WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\r\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\r\n\r\nexport class HubClient {\r\n private ws: WebSocket | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\r\n private queue: ChannelMessage[] = [];\r\n private connected = false;\r\n private closed = false;\r\n\r\n constructor(\r\n private sessionId: string,\r\n private sessionName: string,\r\n private hubHost = DEFAULT_HUB_HOST,\r\n private hubPort = DEFAULT_HUB_PORT,\r\n private token?: string,\r\n private getPeerName: () => string | undefined = () => undefined,\r\n private getRegistration: () => Partial<SessionInfo> = () => ({}),\r\n ) {}\r\n\r\n connect(): void {\r\n this.closed = false;\r\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\r\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\r\n logger.debug(`Connecting to hub at ${url}`);\r\n\r\n try {\r\n const ws = new WebSocket(url);\r\n this.ws = ws;\r\n\r\n ws.on('open', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n this.ws!.send(JSON.stringify(this.registration()));\r\n\r\n // Flush queued messages\r\n for (const msg of this.queue) {\r\n this.ws!.send(JSON.stringify(msg));\r\n }\r\n this.queue = [];\r\n });\r\n\r\n ws.on('message', (data) => {\r\n if (this.ws !== ws) return;\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n for (const handler of this.messageHandlers) {\r\n handler(msg);\r\n }\r\n } catch (err) {\r\n logger.warn('Failed to parse hub message:', err);\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n if (!this.closed) this.scheduleReconnect();\r\n });\r\n\r\n ws.on('error', (err) => {\r\n if (this.ws !== ws) return;\r\n logger.debug(`Hub connection error: ${err.message}`);\r\n this.connected = false;\r\n });\r\n } catch {\r\n logger.debug('Failed to connect to hub, will retry');\r\n this.scheduleReconnect();\r\n }\r\n }\r\n\r\n send(msg: ChannelMessage): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n } else {\r\n if (this.queue.length < 100) {\r\n this.queue.push(msg);\r\n }\r\n logger.debug('Hub not connected, message queued');\r\n }\r\n }\r\n\r\n onMessage(handler: (msg: ChannelMessage) => void): void {\r\n this.messageHandlers.push(handler);\r\n }\r\n\r\n disconnect(): void {\r\n this.closed = true;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n this.connected = false;\r\n }\r\n\r\n reregister(): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(this.registration()));\r\n }\r\n }\r\n\r\n private registration(): ChannelMessage {\r\n return {\r\n type: 'register',\r\n session: {\r\n id: this.sessionId,\r\n name: this.sessionName,\r\n status: 'idle',\r\n connectedAt: Date.now(),\r\n lastActivity: Date.now(),\r\n cwd: process.cwd(),\r\n channelEnabled: true,\r\n peerName: this.getPeerName(),\r\n ...this.getRegistration(),\r\n },\r\n };\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnectTimer || this.closed) return;\r\n this.reconnectTimer = setTimeout(() => {\r\n this.reconnectTimer = null;\r\n this.connect();\r\n }, 5000);\r\n }\r\n}\r\n","import { spawn, type ChildProcess } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { Duplex } from 'node:stream';\r\nimport WebSocket, { type ClientOptions } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SpawnFn = (command: string, args: string[]) => ChildProcess;\r\n\r\nexport interface ProxyConnection {\r\n ws: WebSocket;\r\n close(): void;\r\n}\r\n\r\n// npm installs Codex on Windows as a codex.cmd shim, which only runs through a shell.\r\nexport function resolveCommand(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): { file: string; shell: boolean } {\r\n if (platform !== 'win32' || /[\\\\/]/.test(command) || path.extname(command)) return { file: command, shell: false };\r\n const dirs = (env.PATH ?? env.Path ?? '').split(path.delimiter).filter(Boolean);\r\n for (const ext of ['.exe', '.cmd']) {\r\n for (const dir of dirs) {\r\n const file = path.join(dir, command + ext);\r\n if (fs.existsSync(file)) return { file, shell: ext === '.cmd' };\r\n }\r\n }\r\n return { file: command, shell: false };\r\n}\r\n\r\nexport const defaultSpawn: SpawnFn = (command, args) => {\r\n const { file, shell } = resolveCommand(command);\r\n if (shell) return spawn(`\"${file}\" ${args.join(' ')}`, { stdio: 'pipe', windowsHide: true, shell: true });\r\n return spawn(file, args, { stdio: 'pipe', windowsHide: true });\r\n};\r\n\r\n// The daemon control socket speaks WebSocket, not JSONL: `codex app-server proxy` only relays bytes.\r\nexport function connectProxy(command: string, spawnFn: SpawnFn = defaultSpawn): Promise<ProxyConnection> {\r\n const child = spawnFn(command, ['app-server', 'proxy']);\r\n child.stdin?.on('error', () => {});\r\n child.stderr?.on('data', (d) => logger.debug(`codex proxy: ${String(d).trim()}`));\r\n\r\n const stream = new Duplex({\r\n read() {},\r\n write(chunk, _encoding, callback) {\r\n if (child.stdin?.writable) child.stdin.write(chunk, callback);\r\n else callback();\r\n },\r\n final(callback) {\r\n child.stdin?.end();\r\n callback();\r\n },\r\n });\r\n child.stdout?.on('data', (d) => stream.push(d));\r\n child.stdout?.on('end', () => stream.push(null));\r\n Object.assign(stream, { setNoDelay() {}, setTimeout() {}, setKeepAlive() {}, ref() {}, unref() {} });\r\n\r\n const ws = new WebSocket('ws://localhost/', {\r\n createConnection: (() => stream) as unknown as ClientOptions['createConnection'],\r\n });\r\n const close = () => {\r\n ws.terminate();\r\n child.kill();\r\n };\r\n\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n const fail = (err: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n close();\r\n reject(err);\r\n };\r\n child.on('error', fail);\r\n child.once('exit', (code) => fail(new Error(`codex proxy exited (code ${code})`)));\r\n ws.on('error', fail);\r\n ws.once('open', () => {\r\n if (settled) return;\r\n settled = true;\r\n child.on('error', (err) => logger.warn(`codex proxy error: ${err.message}`));\r\n ws.on('error', (err) => logger.warn(`codex daemon socket error: ${err.message}`));\r\n resolve({ ws, close });\r\n });\r\n });\r\n}\r\n","import { EventEmitter } from 'node:events';\r\nimport type WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type RpcId = number | string;\r\n\r\nexport class RpcError extends Error {\r\n constructor(public readonly code: number, message: string) {\r\n super(message);\r\n }\r\n}\r\n\r\ninterface Pending {\r\n resolve: (value: any) => void;\r\n reject: (err: Error) => void;\r\n timer: ReturnType<typeof setTimeout>;\r\n}\r\n\r\nexport class RpcClient extends EventEmitter {\r\n private nextId = 1;\r\n private pending = new Map<RpcId, Pending>();\r\n\r\n constructor(private ws: WebSocket, private timeoutMs = 30_000) {\r\n super();\r\n ws.on('message', (data) => this.onMessage(String(data)));\r\n ws.on('close', () => {\r\n for (const p of this.pending.values()) {\r\n clearTimeout(p.timer);\r\n p.reject(new Error('connection closed'));\r\n }\r\n this.pending.clear();\r\n this.emit('close');\r\n });\r\n }\r\n\r\n request<T = any>(method: string, params?: unknown): Promise<T> {\r\n if (this.ws.readyState !== this.ws.OPEN) return Promise.reject(new Error('connection closed'));\r\n const id = this.nextId++;\r\n return new Promise<T>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n this.pending.delete(id);\r\n reject(new Error(`${method} timed out`));\r\n }, this.timeoutMs);\r\n this.pending.set(id, { resolve, reject, timer });\r\n this.ws.send(JSON.stringify(params === undefined ? { id, method } : { id, method, params }));\r\n });\r\n }\r\n\r\n notify(method: string, params?: unknown): void {\r\n if (this.ws.readyState !== this.ws.OPEN) return;\r\n this.ws.send(JSON.stringify(params === undefined ? { method } : { method, params }));\r\n }\r\n\r\n // ws stops reading the socket for good if a 'message' listener throws.\r\n private guarded(method: string, fn: () => void): void {\r\n try {\r\n fn();\r\n } catch (err) {\r\n logger.warn(`Codex ${method} handling failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private onMessage(text: string): void {\r\n let msg: any;\r\n try {\r\n msg = JSON.parse(text);\r\n } catch {\r\n return;\r\n }\r\n if (!msg || typeof msg !== 'object') return;\r\n if (msg.method === undefined && msg.id !== undefined) {\r\n const p = this.pending.get(msg.id);\r\n if (!p) return;\r\n this.pending.delete(msg.id);\r\n clearTimeout(p.timer);\r\n if (msg.error) p.reject(new RpcError(msg.error.code, msg.error.message));\r\n else p.resolve(msg.result);\r\n } else if (msg.method !== undefined && msg.id !== undefined) {\r\n this.guarded(msg.method, () => this.emit('request', msg.id, msg.method, msg.params));\r\n } else if (msg.method !== undefined) {\r\n this.guarded(msg.method, () => this.emit('notification', msg.method, msg.params));\r\n }\r\n }\r\n}\r\n","import type { MessageSource, SessionStatus } from '../shared/types.js';\r\n\r\nexport type CodexThreadStatus =\r\n | { type: 'notLoaded' | 'idle' | 'systemError' }\r\n | { type: 'active'; activeFlags: string[] };\r\n\r\nexport interface CodexThread {\r\n id: string;\r\n name?: string | null;\r\n preview?: string;\r\n cwd: string;\r\n status: CodexThreadStatus;\r\n parentThreadId?: string | null;\r\n ephemeral?: boolean;\r\n}\r\n\r\nexport interface AgentMessage {\r\n text: string;\r\n phase?: string | null;\r\n}\r\n\r\nconst TITLE_MAX = 30;\r\nconst SOURCE_LABEL: Record<MessageSource, string> = { dashboard: 'Dashboard', telegram: 'Telegram', api: 'API' };\r\n\r\nexport function codexSessionId(threadId: string): string {\r\n return `codex:${threadId}`;\r\n}\r\n\r\nexport function isTrackable(thread: CodexThread): boolean {\r\n return !thread.parentThreadId && !thread.ephemeral && thread.status.type !== 'notLoaded';\r\n}\r\n\r\nexport function threadTitle(thread: CodexThread): string {\r\n const name = thread.name?.trim();\r\n if (name) return name;\r\n const preview = (thread.preview ?? '').replace(/\\s+/g, ' ').trim();\r\n if (preview) return preview.length > TITLE_MAX ? `${preview.slice(0, TITLE_MAX)}…` : preview;\r\n return thread.cwd.replace(/^.*[/\\\\]/, '') || thread.id.slice(0, 8);\r\n}\r\n\r\nexport function hubStatus(status: CodexThreadStatus): SessionStatus {\r\n if (status.type !== 'active') return 'idle';\r\n return status.activeFlags.some((f) => f === 'waitingOnApproval' || f === 'waitingOnUserInput') ? 'waiting_input' : 'working';\r\n}\r\n\r\nexport function withSourcePrefix(content: string, source?: MessageSource): string {\r\n return source ? `[claude-alarm · ${SOURCE_LABEL[source]}] ${content}` : `[claude-alarm] ${content}`;\r\n}\r\n\r\nexport function finalAnswer(messages: AgentMessage[]): string | null {\r\n const finals = messages.filter((m) => m.phase === 'final_answer').map((m) => m.text);\r\n if (finals.length) return finals.join('\\n\\n');\r\n return messages.at(-1)?.text ?? null;\r\n}\r\n","import { HubClient } from '../channel/hub-client.js';\r\nimport { CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\nimport { logger } from '../shared/logger.js';\r\nimport type { ChannelMessage, MessageSource, NotifyLevel, SessionInfo } from '../shared/types.js';\r\nimport { connectProxy, type ProxyConnection, type SpawnFn } from './transport.js';\r\nimport { RpcClient } from './rpc.js';\r\nimport {\r\n codexSessionId,\r\n finalAnswer,\r\n hubStatus,\r\n isTrackable,\r\n threadTitle,\r\n withSourcePrefix,\r\n type AgentMessage,\r\n type CodexThread,\r\n type CodexThreadStatus,\r\n} from './mapping.js';\r\n\r\nexport interface CodexAdapterOptions {\r\n command: string;\r\n hub: { host: string; port: number; token?: string };\r\n spawnFn?: SpawnFn;\r\n reconnectMinMs?: number;\r\n reconnectMaxMs?: number;\r\n idleReleaseMs?: number;\r\n}\r\n\r\ninterface Tracked {\r\n thread: CodexThread;\r\n hub: HubClient;\r\n subscribed: boolean;\r\n wantSubscribed: boolean;\r\n sync: Promise<void>;\r\n pendingTurn: boolean;\r\n turns: Map<string, AgentMessage[]>;\r\n releaseTimer?: ReturnType<typeof setTimeout>;\r\n unrelayed: boolean;\r\n}\r\n\r\nconst APPROVAL_REQUESTS = new Set([\r\n 'item/commandExecution/requestApproval',\r\n 'item/fileChange/requestApproval',\r\n 'mcpServer/elicitation/request',\r\n]);\r\n\r\nexport class CodexAdapter {\r\n private conn?: ProxyConnection;\r\n private rpc?: RpcClient;\r\n private threads = new Map<string, Tracked>();\r\n private stopped = false;\r\n private retryTimer?: ReturnType<typeof setTimeout>;\r\n private delay: number;\r\n\r\n constructor(private opts: CodexAdapterOptions) {\r\n this.delay = opts.reconnectMinMs ?? 2000;\r\n }\r\n\r\n start(): void {\r\n this.stopped = false;\r\n void this.connect();\r\n }\r\n\r\n stop(): void {\r\n this.stopped = true;\r\n if (this.retryTimer) clearTimeout(this.retryTimer);\r\n this.retryTimer = undefined;\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n this.conn?.close();\r\n }\r\n\r\n private async connect(): Promise<void> {\r\n let rpc: RpcClient | undefined;\r\n try {\r\n const conn = await connectProxy(this.opts.command, this.opts.spawnFn);\r\n if (this.stopped) {\r\n conn.close();\r\n return;\r\n }\r\n const live = new RpcClient(conn.ws);\r\n rpc = live;\r\n live.on('notification', (method: string, params: any) => this.onNotification(method, params));\r\n live.on('request', (_id: unknown, method: string, params: any) => this.onServerRequest(method, params));\r\n live.on('close', () => {\r\n if (this.rpc === live) this.onDaemonLost();\r\n });\r\n this.conn = conn;\r\n this.rpc = live;\r\n const init = await live.request<{ userAgent?: string }>('initialize', {\r\n clientInfo: { name: 'claude-alarm', version: CHANNEL_SERVER_VERSION },\r\n });\r\n live.notify('initialized');\r\n logger.info(`Connected to Codex daemon (${init.userAgent ?? 'unknown version'})`);\r\n await this.discover();\r\n this.delay = this.opts.reconnectMinMs ?? 2000;\r\n } catch (err) {\r\n logger.warn(`Codex daemon connection failed: ${(err as Error).message}`);\r\n if (rpc && this.rpc === rpc) {\r\n this.rpc = undefined;\r\n this.conn?.close();\r\n this.conn = undefined;\r\n }\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n this.scheduleRetry();\r\n }\r\n }\r\n\r\n private onDaemonLost(): void {\r\n this.rpc = undefined;\r\n this.conn?.close();\r\n this.conn = undefined;\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n if (!this.stopped) logger.warn('Codex daemon connection lost');\r\n this.scheduleRetry();\r\n }\r\n\r\n private scheduleRetry(): void {\r\n if (this.stopped || this.retryTimer) return;\r\n this.retryTimer = setTimeout(() => {\r\n this.retryTimer = undefined;\r\n void this.connect();\r\n }, this.delay);\r\n this.delay = Math.min(this.delay * 2, this.opts.reconnectMaxMs ?? 60_000);\r\n }\r\n\r\n private async discover(): Promise<void> {\r\n const ids: string[] = [];\r\n let cursor: string | null | undefined;\r\n do {\r\n const page = await this.rpc!.request<{ data: string[]; nextCursor?: string | null }>(\r\n 'thread/loaded/list',\r\n cursor ? { cursor } : {},\r\n );\r\n ids.push(...page.data);\r\n cursor = page.nextCursor;\r\n } while (cursor);\r\n for (const id of ids) await this.refresh(id);\r\n }\r\n\r\n private async refresh(threadId: string): Promise<void> {\r\n try {\r\n const { thread } = await this.rpc!.request<{ thread: CodexThread }>('thread/read', { threadId, includeTurns: false });\r\n this.upsert(thread);\r\n } catch (err) {\r\n logger.debug(`thread/read ${threadId} failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private upsert(thread: CodexThread): void {\r\n if (!isTrackable(thread)) {\r\n this.drop(thread.id);\r\n return;\r\n }\r\n const existing = this.threads.get(thread.id);\r\n if (existing) {\r\n existing.thread = thread;\r\n existing.hub.reregister();\r\n } else {\r\n const { host, port, token } = this.opts.hub;\r\n const hub = new HubClient(codexSessionId(thread.id), threadTitle(thread), host, port, token, () => undefined, () =>\r\n this.registration(thread.id),\r\n );\r\n this.threads.set(thread.id, {\r\n thread,\r\n hub,\r\n subscribed: false,\r\n wantSubscribed: false,\r\n sync: Promise.resolve(),\r\n pendingTurn: false,\r\n turns: new Map(),\r\n unrelayed: false,\r\n });\r\n hub.onMessage((msg) => this.onHubMessage(thread.id, msg));\r\n hub.connect();\r\n }\r\n void this.want(thread.id, thread.status.type === 'active');\r\n }\r\n\r\n private registration(threadId: string): Partial<SessionInfo> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return {};\r\n const title = threadTitle(t.thread);\r\n return { name: title, title, cwd: t.thread.cwd, agentKind: 'codex', status: hubStatus(t.thread.status) };\r\n }\r\n\r\n private want(threadId: string, subscribed: boolean): Promise<void> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return Promise.resolve();\r\n t.wantSubscribed = subscribed;\r\n t.sync = t.sync.then(() => this.syncSubscription(t));\r\n return t.sync;\r\n }\r\n\r\n private async syncSubscription(t: Tracked): Promise<void> {\r\n const threadId = t.thread.id;\r\n if (!this.rpc || this.threads.get(threadId) !== t || t.subscribed === t.wantSubscribed) return;\r\n try {\r\n if (t.wantSubscribed) {\r\n // Only these two fields: any other resume field overrides the user's own thread settings.\r\n await this.rpc.request('thread/resume', { threadId, excludeTurns: true });\r\n t.subscribed = true;\r\n t.unrelayed = false;\r\n } else {\r\n // The daemon unloads a conversation 60 s after its last subscriber leaves; staying subscribed would keep closed Codex windows alive.\r\n await this.rpc.request('thread/unsubscribe', { threadId });\r\n t.subscribed = false;\r\n }\r\n } catch (err) {\r\n logger.debug(`subscription sync for ${threadId} deferred: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private releaseLater(t: Tracked): void {\r\n clearTimeout(t.releaseTimer);\r\n t.releaseTimer = setTimeout(() => {\r\n t.releaseTimer = undefined;\r\n if (this.threads.get(t.thread.id) === t && t.thread.status.type !== 'active' && !t.pendingTurn) {\r\n void this.want(t.thread.id, false);\r\n }\r\n }, this.opts.idleReleaseMs ?? 1000);\r\n }\r\n\r\n private drop(threadId: string): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n this.threads.delete(threadId);\r\n clearTimeout(t.releaseTimer);\r\n t.releaseTimer = undefined;\r\n t.hub.disconnect();\r\n }\r\n\r\n private onNotification(method: string, params: any): void {\r\n switch (method) {\r\n case 'thread/started':\r\n this.upsert(params.thread);\r\n break;\r\n case 'thread/status/changed':\r\n this.onStatus(params.threadId, params.status);\r\n break;\r\n case 'thread/name/updated': {\r\n const t = this.threads.get(params.threadId);\r\n if (t) {\r\n t.thread.name = params.threadName;\r\n t.hub.reregister();\r\n }\r\n break;\r\n }\r\n case 'thread/closed':\r\n case 'thread/archived':\r\n case 'thread/deleted':\r\n this.drop(params.threadId);\r\n break;\r\n case 'item/completed':\r\n if (params.item?.type === 'agentMessage') this.collect(params.threadId, params.turnId, params.item);\r\n break;\r\n case 'turn/completed':\r\n this.onTurnCompleted(params.threadId, params.turn);\r\n break;\r\n }\r\n }\r\n\r\n private onStatus(threadId: string, status: CodexThreadStatus): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) {\r\n if (status.type !== 'notLoaded') void this.refresh(threadId);\r\n return;\r\n }\r\n if (status.type === 'notLoaded') {\r\n this.drop(threadId);\r\n return;\r\n }\r\n t.thread.status = status;\r\n t.pendingTurn = false;\r\n t.hub.send({ type: 'status', sessionId: codexSessionId(threadId), status: hubStatus(status) });\r\n if (status.type === 'systemError') this.notify(threadId, 'Codex error', 'The Codex conversation hit a system error.', 'error');\r\n if (status.type === 'active') {\r\n void this.want(threadId, true);\r\n return;\r\n }\r\n if (t.unrelayed) {\r\n t.unrelayed = false;\r\n this.notify(threadId, 'Reply not relayed', 'Codex finished, but its reply could not be relayed here. Check the Codex window.', 'warning');\r\n }\r\n // The daemon emits idle just before turn/completed; unsubscribing at once could cut off the reply.\r\n this.releaseLater(t);\r\n }\r\n\r\n private collect(threadId: string, turnId: string, item: AgentMessage): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n const list = t.turns.get(turnId) ?? [];\r\n list.push({ text: item.text, phase: item.phase });\r\n t.turns.set(turnId, list);\r\n }\r\n\r\n private onTurnCompleted(\r\n threadId: string,\r\n turn: { id: string; status: string; error?: { message: string } | null; items?: Array<{ type: string } & AgentMessage> },\r\n ): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n const collected = t.turns.get(turn.id) ?? [];\r\n t.turns.delete(turn.id);\r\n if (t.thread.status.type !== 'active') void this.want(threadId, false);\r\n if (turn.status === 'failed' || turn.error) {\r\n this.notify(threadId, 'Codex task failed', turn.error?.message ?? 'The task ended with an error.', 'error');\r\n return;\r\n }\r\n if (turn.status === 'interrupted') {\r\n this.notify(threadId, 'Codex task stopped', 'The task was interrupted.', 'info');\r\n return;\r\n }\r\n // turn/completed may carry only a summary of the items, so prefer what was collected live.\r\n const fromTurn = (turn.items ?? []).filter((i) => i.type === 'agentMessage');\r\n const text = finalAnswer(collected.length ? collected : fromTurn);\r\n if (text) t.hub.send({ type: 'reply', sessionId: codexSessionId(threadId), content: text });\r\n }\r\n\r\n private onHubMessage(threadId: string, msg: ChannelMessage): void {\r\n if (msg.type === 'message_to_session') {\r\n void this.sendTurn(threadId, msg.content, msg.source);\r\n } else if (msg.type === 'image_to_session') {\r\n this.notify(threadId, 'Not delivered', 'Codex sessions do not accept images yet.', 'warning');\r\n }\r\n }\r\n\r\n private async sendTurn(threadId: string, content: string, source?: MessageSource): Promise<void> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n if (t.thread.status.type !== 'idle' || t.pendingTurn) {\r\n this.notify(threadId, 'Not delivered', 'Codex is busy, so the message was not delivered. Send it again when the task finishes.', 'warning');\r\n return;\r\n }\r\n t.pendingTurn = true;\r\n try {\r\n if (!this.rpc) throw new Error('not connected to the Codex daemon');\r\n await this.want(threadId, true);\r\n // A new conversation cannot be subscribed before its first turn (no rollout found); the active broadcast retries it.\r\n t.unrelayed = !t.subscribed;\r\n await this.rpc.request('turn/start', { threadId, input: [{ type: 'text', text: withSourcePrefix(content, source) }] });\r\n } catch (err) {\r\n t.pendingTurn = false;\r\n t.unrelayed = false;\r\n this.releaseLater(t);\r\n this.notify(threadId, 'Not delivered', `Codex rejected the message: ${(err as Error).message}`, 'warning');\r\n }\r\n }\r\n\r\n private onServerRequest(method: string, params: any): void {\r\n if (!APPROVAL_REQUESTS.has(method) || !params?.threadId) {\r\n logger.debug(`Ignoring Codex server request ${method}`);\r\n return;\r\n }\r\n const detail = params.commandActions?.[0]?.command ?? params.command ?? params.reason ?? params.message ?? method;\r\n this.notify(params.threadId, 'Codex approval needed', `Approve or decline in Codex: ${String(detail).slice(0, 300)}`, 'warning');\r\n }\r\n\r\n private notify(threadId: string, title: string, message: string, level: NotifyLevel): void {\r\n this.threads.get(threadId)?.hub.send({ type: 'notify', sessionId: codexSessionId(threadId), title, message, level });\r\n }\r\n}\r\n"],"mappings":"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","names":["fs","path","config","config","fs","fs","token","fs","path","WebSocket","path","fs","WebSocket","existing","host","port","token","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/codex/main.ts","../../src/shared/config.ts","../../src/shared/constants.ts","../../src/shared/crash-guard.ts","../../src/shared/logger.ts","../../src/channel/hub-client.ts","../../src/codex/transport.ts","../../src/codex/rpc.ts","../../src/codex/mapping.ts","../../src/codex/adapter.ts"],"sourcesContent":["import fs from 'node:fs';\r\nimport { loadConfig } from '../shared/config.js';\r\nimport { CODEX_PID_FILE, DEFAULT_HUB_HOST } from '../shared/constants.js';\r\nimport { installCrashGuard, logStartup } from '../shared/crash-guard.js';\r\nimport { logger } from '../shared/logger.js';\r\nimport { CodexAdapter } from './adapter.js';\r\n\r\nfunction isRunning(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\ninstallCrashGuard('codex adapter');\r\n// Once the hub is gone its pipes break; without listeners every log line would become an uncaught EPIPE.\r\nprocess.stdout.on('error', () => {});\r\nprocess.stderr.on('error', () => {});\r\nconst config = loadConfig();\r\n\r\nconst existing = fs.existsSync(CODEX_PID_FILE) ? parseInt(fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim(), 10) : NaN;\r\nif (!Number.isNaN(existing) && existing !== process.pid && isRunning(existing)) {\r\n logger.info(`Codex adapter already running (PID: ${existing})`);\r\n process.exit(0);\r\n}\r\nfs.writeFileSync(CODEX_PID_FILE, String(process.pid), 'utf-8');\r\nlogStartup('Codex adapter');\r\n\r\nconst host = process.env.CLAUDE_ALARM_HUB_HOST ?? (config.hub.host === '0.0.0.0' ? DEFAULT_HUB_HOST : config.hub.host);\r\nconst port = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\r\nconst token = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\r\n\r\nconst adapter = new CodexAdapter({ command: config.codex?.command ?? 'codex', hub: { host, port, token } });\r\nadapter.start();\r\n\r\nlet exiting = false;\r\nconst shutdown = () => {\r\n if (exiting) return;\r\n exiting = true;\r\n adapter.stop();\r\n try {\r\n if (fs.readFileSync(CODEX_PID_FILE, 'utf-8').trim() === String(process.pid)) fs.unlinkSync(CODEX_PID_FILE);\r\n } catch {}\r\n process.exit(0);\r\n};\r\nprocess.on('SIGINT', shutdown);\r\nprocess.on('SIGTERM', shutdown);\r\n\r\n// On Windows `hub stop` kills the hub without running its exit handlers, so stdin EOF is the only sign the parent is gone.\r\nif (process.argv.includes('--watch-stdin')) {\r\n process.stdin.on('end', shutdown);\r\n process.stdin.on('close', shutdown);\r\n process.stdin.resume();\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\r\nimport type { AppConfig } from './types.js';\r\n\r\nconst DEFAULT_CONFIG: AppConfig = {\r\n hub: {\r\n host: DEFAULT_HUB_HOST,\r\n port: DEFAULT_HUB_PORT,\r\n },\r\n notifications: {\r\n desktop: true,\r\n sound: true,\r\n },\r\n webhooks: [],\r\n};\r\n\r\nexport function ensureConfigDir(): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n}\r\n\r\nexport function loadConfig(): AppConfig {\r\n ensureConfigDir();\r\n let config: AppConfig;\r\n if (!fs.existsSync(CONFIG_FILE)) {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n } else {\r\n try {\r\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\r\n const parsed = JSON.parse(raw);\r\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub }, ...(parsed.telegram ? { telegram: parsed.telegram } : {}) };\r\n } catch {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n }\r\n }\r\n\r\n // Auto-generate token if missing\r\n if (!config.hub.token) {\r\n config.hub.token = randomUUID();\r\n saveConfig(config);\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** Get the current token, generating one if needed */\r\nexport function getOrCreateToken(): string {\r\n const config = loadConfig();\r\n return config.hub.token!;\r\n}\r\n\r\nexport function saveConfig(config: AppConfig): void {\r\n ensureConfigDir();\r\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\r\n}\r\n\r\nexport function setCodexEnabled(enabled: boolean): AppConfig {\r\n const config = loadConfig();\r\n config.codex = { ...config.codex, enabled };\r\n saveConfig(config);\r\n return config;\r\n}\r\n\r\nexport function shouldOfferCodex(config: AppConfig, installed: boolean): boolean {\r\n return installed && config.codex === undefined;\r\n}\r\n\r\n/**\r\n * Add claude-alarm as an MCP channel server to .mcp.json\r\n */\r\nexport function setupMcpConfig(targetDir?: string): string {\r\n const dir = targetDir ?? process.cwd();\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try {\r\n mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\r\n } catch {\r\n mcpConfig = {};\r\n }\r\n }\r\n\r\n if (!mcpConfig.mcpServers) {\r\n mcpConfig.mcpServers = {};\r\n }\r\n\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env: {\r\n CLAUDE_ALARM_SESSION_NAME: path.basename(dir),\r\n },\r\n };\r\n\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n return mcpPath;\r\n}\r\n","import path from 'node:path';\r\nimport os from 'node:os';\r\n\r\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\r\nexport const DEFAULT_HUB_PORT = 7900;\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\r\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\r\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\r\nexport const UPLOADS_DIR = path.join(CONFIG_DIR, 'uploads');\r\nexport const CODEX_PID_FILE = path.join(CONFIG_DIR, 'codex.pid');\r\nexport const CODEX_LOG_FILE = path.join(CONFIG_DIR, 'codex.log');\r\n\r\nexport const WS_PATH_CHANNEL = '/ws/channel';\r\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\r\n\r\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\r\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\r\n","import fs from 'node:fs';\nimport { CONFIG_DIR, LOG_FILE } from './constants.js';\nimport { logger } from './logger.js';\n\nconst BURST_WINDOW_MS = 60_000;\nconst BURST_LIMIT = 10;\n\nfunction appendToLog(text: string): void {\n try {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n fs.appendFileSync(LOG_FILE, text, 'utf-8');\n } catch {}\n}\n\nfunction describe(err: unknown): string {\n if (err instanceof Error) return err.stack ?? `${err.name}: ${err.message}`;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\n/**\n * Last-resort net for exceptions no local handler caught.\n *\n * The hub keeps running afterwards: a notification relay that dies silently is\n * worse than one running on possibly-degraded state, and in foreground mode the\n * stack trace would otherwise vanish with the console window. A repeated burst\n * still exits, so a self-feeding error loop cannot spin forever.\n */\nexport function installCrashGuard(context: string): void {\n let recent: number[] = [];\n\n const handle = (kind: string, err: unknown): void => {\n const stack = describe(err);\n appendToLog(`\\n[${new Date().toISOString()}] ${kind} (${context})\\n${stack}\\n`);\n logger.error(`${kind} caught by crash guard: ${stack}`);\n\n const now = Date.now();\n recent = recent.filter((t) => now - t < BURST_WINDOW_MS);\n recent.push(now);\n\n if (recent.length > BURST_LIMIT) {\n const msg = `${recent.length} unhandled errors within ${BURST_WINDOW_MS / 1000}s — exiting`;\n appendToLog(`[${new Date().toISOString()}] ${msg}\\n`);\n logger.error(msg);\n process.exit(1);\n }\n };\n\n process.on('uncaughtException', (err) => handle('uncaughtException', err));\n process.on('unhandledRejection', (reason) => handle('unhandledRejection', reason));\n}\n\nexport function logStartup(context: string): void {\n appendToLog(`\\n[${new Date().toISOString()}] ${context} started (pid ${process.pid})\\n`);\n}\n","/**\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 WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\r\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\r\n\r\nexport class HubClient {\r\n private ws: WebSocket | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\r\n private queue: ChannelMessage[] = [];\r\n private connected = false;\r\n private closed = false;\r\n\r\n constructor(\r\n private sessionId: string,\r\n private sessionName: string,\r\n private hubHost = DEFAULT_HUB_HOST,\r\n private hubPort = DEFAULT_HUB_PORT,\r\n private token?: string,\r\n private getPeerName: () => string | undefined = () => undefined,\r\n private getRegistration: () => Partial<SessionInfo> = () => ({}),\r\n ) {}\r\n\r\n connect(): void {\r\n this.closed = false;\r\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\r\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\r\n logger.debug(`Connecting to hub at ${url}`);\r\n\r\n try {\r\n const ws = new WebSocket(url);\r\n this.ws = ws;\r\n\r\n ws.on('open', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n this.ws!.send(JSON.stringify(this.registration()));\r\n\r\n // Flush queued messages\r\n for (const msg of this.queue) {\r\n this.ws!.send(JSON.stringify(msg));\r\n }\r\n this.queue = [];\r\n });\r\n\r\n ws.on('message', (data) => {\r\n if (this.ws !== ws) return;\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n for (const handler of this.messageHandlers) {\r\n handler(msg);\r\n }\r\n } catch (err) {\r\n logger.warn('Failed to parse hub message:', err);\r\n }\r\n });\r\n\r\n ws.on('close', () => {\r\n if (this.ws !== ws) return;\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n if (!this.closed) this.scheduleReconnect();\r\n });\r\n\r\n ws.on('error', (err) => {\r\n if (this.ws !== ws) return;\r\n logger.debug(`Hub connection error: ${err.message}`);\r\n this.connected = false;\r\n });\r\n } catch {\r\n logger.debug('Failed to connect to hub, will retry');\r\n this.scheduleReconnect();\r\n }\r\n }\r\n\r\n send(msg: ChannelMessage): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n } else {\r\n if (this.queue.length < 100) {\r\n this.queue.push(msg);\r\n }\r\n logger.debug('Hub not connected, message queued');\r\n }\r\n }\r\n\r\n onMessage(handler: (msg: ChannelMessage) => void): void {\r\n this.messageHandlers.push(handler);\r\n }\r\n\r\n disconnect(): void {\r\n this.closed = true;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n this.connected = false;\r\n }\r\n\r\n reregister(): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(this.registration()));\r\n }\r\n }\r\n\r\n private registration(): ChannelMessage {\r\n return {\r\n type: 'register',\r\n session: {\r\n id: this.sessionId,\r\n name: this.sessionName,\r\n status: 'idle',\r\n connectedAt: Date.now(),\r\n lastActivity: Date.now(),\r\n cwd: process.cwd(),\r\n channelEnabled: true,\r\n peerName: this.getPeerName(),\r\n ...this.getRegistration(),\r\n },\r\n };\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnectTimer || this.closed) return;\r\n this.reconnectTimer = setTimeout(() => {\r\n this.reconnectTimer = null;\r\n this.connect();\r\n }, 5000);\r\n }\r\n}\r\n","import { spawn, type ChildProcess } from 'node:child_process';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { Duplex } from 'node:stream';\r\nimport WebSocket, { type ClientOptions } from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type SpawnFn = (command: string, args: string[]) => ChildProcess;\r\n\r\nexport interface ProxyConnection {\r\n ws: WebSocket;\r\n close(): void;\r\n}\r\n\r\n// npm installs Codex on Windows as a codex.cmd shim, which only runs through a shell.\r\nexport function resolveCommand(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): { file: string; shell: boolean } {\r\n if (platform !== 'win32' || /[\\\\/]/.test(command) || path.extname(command)) return { file: command, shell: false };\r\n const file = findOnPath(command, platform, env);\r\n return file ? { file, shell: file.endsWith('.cmd') } : { file: command, shell: false };\r\n}\r\n\r\nexport function findOnPath(\r\n command: string,\r\n platform: NodeJS.Platform = process.platform,\r\n env: NodeJS.ProcessEnv = process.env,\r\n): string | undefined {\r\n const dirs = (env.PATH ?? env.Path ?? '').split(path.delimiter).filter(Boolean);\r\n for (const ext of platform === 'win32' ? ['.exe', '.cmd'] : ['']) {\r\n for (const dir of dirs) {\r\n const file = path.join(dir, command + ext);\r\n if (fs.existsSync(file)) return file;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nexport const defaultSpawn: SpawnFn = (command, args) => {\r\n const { file, shell } = resolveCommand(command);\r\n if (shell) return spawn(`\"${file}\" ${args.join(' ')}`, { stdio: 'pipe', windowsHide: true, shell: true });\r\n return spawn(file, args, { stdio: 'pipe', windowsHide: true });\r\n};\r\n\r\n// The daemon control socket speaks WebSocket, not JSONL: `codex app-server proxy` only relays bytes.\r\nexport function connectProxy(command: string, spawnFn: SpawnFn = defaultSpawn): Promise<ProxyConnection> {\r\n const child = spawnFn(command, ['app-server', 'proxy']);\r\n child.stdin?.on('error', () => {});\r\n child.stderr?.on('data', (d) => logger.debug(`codex proxy: ${String(d).trim()}`));\r\n\r\n const stream = new Duplex({\r\n read() {},\r\n write(chunk, _encoding, callback) {\r\n if (child.stdin?.writable) child.stdin.write(chunk, callback);\r\n else callback();\r\n },\r\n final(callback) {\r\n child.stdin?.end();\r\n callback();\r\n },\r\n });\r\n child.stdout?.on('data', (d) => stream.push(d));\r\n child.stdout?.on('end', () => stream.push(null));\r\n Object.assign(stream, { setNoDelay() {}, setTimeout() {}, setKeepAlive() {}, ref() {}, unref() {} });\r\n\r\n const ws = new WebSocket('ws://localhost/', {\r\n createConnection: (() => stream) as unknown as ClientOptions['createConnection'],\r\n });\r\n const close = () => {\r\n ws.terminate();\r\n child.kill();\r\n };\r\n\r\n return new Promise((resolve, reject) => {\r\n let settled = false;\r\n const fail = (err: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n close();\r\n reject(err);\r\n };\r\n child.on('error', fail);\r\n child.once('exit', (code) => fail(new Error(`codex proxy exited (code ${code})`)));\r\n ws.on('error', fail);\r\n ws.once('open', () => {\r\n if (settled) return;\r\n settled = true;\r\n child.on('error', (err) => logger.warn(`codex proxy error: ${err.message}`));\r\n ws.on('error', (err) => logger.warn(`codex daemon socket error: ${err.message}`));\r\n resolve({ ws, close });\r\n });\r\n });\r\n}\r\n","import { EventEmitter } from 'node:events';\r\nimport type WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\n\r\nexport type RpcId = number | string;\r\n\r\nexport class RpcError extends Error {\r\n constructor(public readonly code: number, message: string) {\r\n super(message);\r\n }\r\n}\r\n\r\ninterface Pending {\r\n resolve: (value: any) => void;\r\n reject: (err: Error) => void;\r\n timer: ReturnType<typeof setTimeout>;\r\n}\r\n\r\nexport class RpcClient extends EventEmitter {\r\n private nextId = 1;\r\n private pending = new Map<RpcId, Pending>();\r\n\r\n constructor(private ws: WebSocket, private timeoutMs = 30_000) {\r\n super();\r\n ws.on('message', (data) => this.onMessage(String(data)));\r\n ws.on('close', () => {\r\n for (const p of this.pending.values()) {\r\n clearTimeout(p.timer);\r\n p.reject(new Error('connection closed'));\r\n }\r\n this.pending.clear();\r\n this.emit('close');\r\n });\r\n }\r\n\r\n request<T = any>(method: string, params?: unknown): Promise<T> {\r\n if (this.ws.readyState !== this.ws.OPEN) return Promise.reject(new Error('connection closed'));\r\n const id = this.nextId++;\r\n return new Promise<T>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n this.pending.delete(id);\r\n reject(new Error(`${method} timed out`));\r\n }, this.timeoutMs);\r\n this.pending.set(id, { resolve, reject, timer });\r\n this.ws.send(JSON.stringify(params === undefined ? { id, method } : { id, method, params }));\r\n });\r\n }\r\n\r\n notify(method: string, params?: unknown): void {\r\n if (this.ws.readyState !== this.ws.OPEN) return;\r\n this.ws.send(JSON.stringify(params === undefined ? { method } : { method, params }));\r\n }\r\n\r\n // ws stops reading the socket for good if a 'message' listener throws.\r\n private guarded(method: string, fn: () => void): void {\r\n try {\r\n fn();\r\n } catch (err) {\r\n logger.warn(`Codex ${method} handling failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private onMessage(text: string): void {\r\n let msg: any;\r\n try {\r\n msg = JSON.parse(text);\r\n } catch {\r\n return;\r\n }\r\n if (!msg || typeof msg !== 'object') return;\r\n if (msg.method === undefined && msg.id !== undefined) {\r\n const p = this.pending.get(msg.id);\r\n if (!p) return;\r\n this.pending.delete(msg.id);\r\n clearTimeout(p.timer);\r\n if (msg.error) p.reject(new RpcError(msg.error.code, msg.error.message));\r\n else p.resolve(msg.result);\r\n } else if (msg.method !== undefined && msg.id !== undefined) {\r\n this.guarded(msg.method, () => this.emit('request', msg.id, msg.method, msg.params));\r\n } else if (msg.method !== undefined) {\r\n this.guarded(msg.method, () => this.emit('notification', msg.method, msg.params));\r\n }\r\n }\r\n}\r\n","import type { MessageSource, SessionStatus } from '../shared/types.js';\r\n\r\nexport type CodexThreadStatus =\r\n | { type: 'notLoaded' | 'idle' | 'systemError' }\r\n | { type: 'active'; activeFlags: string[] };\r\n\r\nexport interface CodexThread {\r\n id: string;\r\n name?: string | null;\r\n preview?: string;\r\n cwd: string;\r\n status: CodexThreadStatus;\r\n parentThreadId?: string | null;\r\n ephemeral?: boolean;\r\n}\r\n\r\nexport interface AgentMessage {\r\n text: string;\r\n phase?: string | null;\r\n}\r\n\r\nconst TITLE_MAX = 30;\r\nconst SOURCE_LABEL: Record<MessageSource, string> = { dashboard: 'Dashboard', telegram: 'Telegram', api: 'API' };\r\n\r\nexport function codexSessionId(threadId: string): string {\r\n return `codex:${threadId}`;\r\n}\r\n\r\nexport function isTrackable(thread: CodexThread): boolean {\r\n return !thread.parentThreadId && !thread.ephemeral && thread.status.type !== 'notLoaded';\r\n}\r\n\r\nexport function threadTitle(thread: CodexThread): string {\r\n const name = thread.name?.trim();\r\n if (name) return name;\r\n const preview = (thread.preview ?? '').replace(/\\s+/g, ' ').trim();\r\n if (preview) return preview.length > TITLE_MAX ? `${preview.slice(0, TITLE_MAX)}…` : preview;\r\n return thread.cwd.replace(/^.*[/\\\\]/, '') || thread.id.slice(0, 8);\r\n}\r\n\r\nexport function hubStatus(status: CodexThreadStatus): SessionStatus {\r\n if (status.type !== 'active') return 'idle';\r\n return status.activeFlags.some((f) => f === 'waitingOnApproval' || f === 'waitingOnUserInput') ? 'waiting_input' : 'working';\r\n}\r\n\r\nexport function withSourcePrefix(content: string, source?: MessageSource): string {\r\n return source ? `[claude-alarm · ${SOURCE_LABEL[source]}] ${content}` : `[claude-alarm] ${content}`;\r\n}\r\n\r\nexport function finalAnswer(messages: AgentMessage[]): string | null {\r\n const finals = messages.filter((m) => m.phase === 'final_answer').map((m) => m.text);\r\n if (finals.length) return finals.join('\\n\\n');\r\n return messages.at(-1)?.text ?? null;\r\n}\r\n","import { HubClient } from '../channel/hub-client.js';\r\nimport { CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\nimport { logger } from '../shared/logger.js';\r\nimport type { ChannelMessage, MessageSource, NotifyLevel, SessionInfo } from '../shared/types.js';\r\nimport { connectProxy, type ProxyConnection, type SpawnFn } from './transport.js';\r\nimport { RpcClient } from './rpc.js';\r\nimport {\r\n codexSessionId,\r\n finalAnswer,\r\n hubStatus,\r\n isTrackable,\r\n threadTitle,\r\n withSourcePrefix,\r\n type AgentMessage,\r\n type CodexThread,\r\n type CodexThreadStatus,\r\n} from './mapping.js';\r\n\r\nexport interface CodexAdapterOptions {\r\n command: string;\r\n hub: { host: string; port: number; token?: string };\r\n spawnFn?: SpawnFn;\r\n reconnectMinMs?: number;\r\n reconnectMaxMs?: number;\r\n idleReleaseMs?: number;\r\n}\r\n\r\ninterface Tracked {\r\n thread: CodexThread;\r\n hub: HubClient;\r\n subscribed: boolean;\r\n wantSubscribed: boolean;\r\n sync: Promise<void>;\r\n pendingTurn: boolean;\r\n turns: Map<string, AgentMessage[]>;\r\n releaseTimer?: ReturnType<typeof setTimeout>;\r\n unrelayed: boolean;\r\n}\r\n\r\nconst APPROVAL_REQUESTS = new Set([\r\n 'item/commandExecution/requestApproval',\r\n 'item/fileChange/requestApproval',\r\n 'mcpServer/elicitation/request',\r\n]);\r\n\r\nexport class CodexAdapter {\r\n private conn?: ProxyConnection;\r\n private rpc?: RpcClient;\r\n private threads = new Map<string, Tracked>();\r\n private stopped = false;\r\n private retryTimer?: ReturnType<typeof setTimeout>;\r\n private delay: number;\r\n\r\n constructor(private opts: CodexAdapterOptions) {\r\n this.delay = opts.reconnectMinMs ?? 2000;\r\n }\r\n\r\n start(): void {\r\n this.stopped = false;\r\n void this.connect();\r\n }\r\n\r\n stop(): void {\r\n this.stopped = true;\r\n if (this.retryTimer) clearTimeout(this.retryTimer);\r\n this.retryTimer = undefined;\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n this.conn?.close();\r\n }\r\n\r\n private async connect(): Promise<void> {\r\n let rpc: RpcClient | undefined;\r\n try {\r\n const conn = await connectProxy(this.opts.command, this.opts.spawnFn);\r\n if (this.stopped) {\r\n conn.close();\r\n return;\r\n }\r\n const live = new RpcClient(conn.ws);\r\n rpc = live;\r\n live.on('notification', (method: string, params: any) => this.onNotification(method, params));\r\n live.on('request', (_id: unknown, method: string, params: any) => this.onServerRequest(method, params));\r\n live.on('close', () => {\r\n if (this.rpc === live) this.onDaemonLost();\r\n });\r\n this.conn = conn;\r\n this.rpc = live;\r\n const init = await live.request<{ userAgent?: string }>('initialize', {\r\n clientInfo: { name: 'claude-alarm', version: CHANNEL_SERVER_VERSION },\r\n });\r\n live.notify('initialized');\r\n logger.info(`Connected to Codex daemon (${init.userAgent ?? 'unknown version'})`);\r\n await this.discover();\r\n this.delay = this.opts.reconnectMinMs ?? 2000;\r\n } catch (err) {\r\n logger.warn(`Codex daemon connection failed: ${(err as Error).message}`);\r\n if (rpc && this.rpc === rpc) {\r\n this.rpc = undefined;\r\n this.conn?.close();\r\n this.conn = undefined;\r\n }\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n this.scheduleRetry();\r\n }\r\n }\r\n\r\n private onDaemonLost(): void {\r\n this.rpc = undefined;\r\n this.conn?.close();\r\n this.conn = undefined;\r\n for (const id of [...this.threads.keys()]) this.drop(id);\r\n if (!this.stopped) logger.warn('Codex daemon connection lost');\r\n this.scheduleRetry();\r\n }\r\n\r\n private scheduleRetry(): void {\r\n if (this.stopped || this.retryTimer) return;\r\n this.retryTimer = setTimeout(() => {\r\n this.retryTimer = undefined;\r\n void this.connect();\r\n }, this.delay);\r\n this.delay = Math.min(this.delay * 2, this.opts.reconnectMaxMs ?? 60_000);\r\n }\r\n\r\n private async discover(): Promise<void> {\r\n const ids: string[] = [];\r\n let cursor: string | null | undefined;\r\n do {\r\n const page = await this.rpc!.request<{ data: string[]; nextCursor?: string | null }>(\r\n 'thread/loaded/list',\r\n cursor ? { cursor } : {},\r\n );\r\n ids.push(...page.data);\r\n cursor = page.nextCursor;\r\n } while (cursor);\r\n for (const id of ids) await this.refresh(id);\r\n }\r\n\r\n private async refresh(threadId: string): Promise<void> {\r\n try {\r\n const { thread } = await this.rpc!.request<{ thread: CodexThread }>('thread/read', { threadId, includeTurns: false });\r\n this.upsert(thread);\r\n } catch (err) {\r\n logger.debug(`thread/read ${threadId} failed: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private upsert(thread: CodexThread): void {\r\n if (!isTrackable(thread)) {\r\n this.drop(thread.id);\r\n return;\r\n }\r\n const existing = this.threads.get(thread.id);\r\n if (existing) {\r\n existing.thread = thread;\r\n existing.hub.reregister();\r\n } else {\r\n const { host, port, token } = this.opts.hub;\r\n const hub = new HubClient(codexSessionId(thread.id), threadTitle(thread), host, port, token, () => undefined, () =>\r\n this.registration(thread.id),\r\n );\r\n this.threads.set(thread.id, {\r\n thread,\r\n hub,\r\n subscribed: false,\r\n wantSubscribed: false,\r\n sync: Promise.resolve(),\r\n pendingTurn: false,\r\n turns: new Map(),\r\n unrelayed: false,\r\n });\r\n hub.onMessage((msg) => this.onHubMessage(thread.id, msg));\r\n hub.connect();\r\n }\r\n void this.want(thread.id, thread.status.type === 'active');\r\n }\r\n\r\n private registration(threadId: string): Partial<SessionInfo> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return {};\r\n const title = threadTitle(t.thread);\r\n return { name: title, title, cwd: t.thread.cwd, agentKind: 'codex', status: hubStatus(t.thread.status) };\r\n }\r\n\r\n private want(threadId: string, subscribed: boolean): Promise<void> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return Promise.resolve();\r\n t.wantSubscribed = subscribed;\r\n t.sync = t.sync.then(() => this.syncSubscription(t));\r\n return t.sync;\r\n }\r\n\r\n private async syncSubscription(t: Tracked): Promise<void> {\r\n const threadId = t.thread.id;\r\n if (!this.rpc || this.threads.get(threadId) !== t || t.subscribed === t.wantSubscribed) return;\r\n try {\r\n if (t.wantSubscribed) {\r\n // Only these two fields: any other resume field overrides the user's own thread settings.\r\n await this.rpc.request('thread/resume', { threadId, excludeTurns: true });\r\n t.subscribed = true;\r\n t.unrelayed = false;\r\n } else {\r\n // The daemon unloads a conversation 60 s after its last subscriber leaves; staying subscribed would keep closed Codex windows alive.\r\n await this.rpc.request('thread/unsubscribe', { threadId });\r\n t.subscribed = false;\r\n }\r\n } catch (err) {\r\n logger.debug(`subscription sync for ${threadId} deferred: ${(err as Error).message}`);\r\n }\r\n }\r\n\r\n private releaseLater(t: Tracked): void {\r\n clearTimeout(t.releaseTimer);\r\n t.releaseTimer = setTimeout(() => {\r\n t.releaseTimer = undefined;\r\n if (this.threads.get(t.thread.id) === t && t.thread.status.type !== 'active' && !t.pendingTurn) {\r\n void this.want(t.thread.id, false);\r\n }\r\n }, this.opts.idleReleaseMs ?? 1000);\r\n }\r\n\r\n private drop(threadId: string): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n this.threads.delete(threadId);\r\n clearTimeout(t.releaseTimer);\r\n t.releaseTimer = undefined;\r\n t.hub.disconnect();\r\n }\r\n\r\n private onNotification(method: string, params: any): void {\r\n switch (method) {\r\n case 'thread/started':\r\n this.upsert(params.thread);\r\n break;\r\n case 'thread/status/changed':\r\n this.onStatus(params.threadId, params.status);\r\n break;\r\n case 'thread/name/updated': {\r\n const t = this.threads.get(params.threadId);\r\n if (t) {\r\n t.thread.name = params.threadName;\r\n t.hub.reregister();\r\n }\r\n break;\r\n }\r\n case 'thread/closed':\r\n case 'thread/archived':\r\n case 'thread/deleted':\r\n this.drop(params.threadId);\r\n break;\r\n case 'item/completed':\r\n if (params.item?.type === 'agentMessage') this.collect(params.threadId, params.turnId, params.item);\r\n break;\r\n case 'turn/completed':\r\n this.onTurnCompleted(params.threadId, params.turn);\r\n break;\r\n }\r\n }\r\n\r\n private onStatus(threadId: string, status: CodexThreadStatus): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) {\r\n if (status.type !== 'notLoaded') void this.refresh(threadId);\r\n return;\r\n }\r\n if (status.type === 'notLoaded') {\r\n this.drop(threadId);\r\n return;\r\n }\r\n t.thread.status = status;\r\n t.pendingTurn = false;\r\n t.hub.send({ type: 'status', sessionId: codexSessionId(threadId), status: hubStatus(status) });\r\n if (status.type === 'systemError') this.notify(threadId, 'Codex error', 'The Codex conversation hit a system error.', 'error');\r\n if (status.type === 'active') {\r\n void this.want(threadId, true);\r\n return;\r\n }\r\n if (t.unrelayed) {\r\n t.unrelayed = false;\r\n this.notify(threadId, 'Reply not relayed', 'Codex finished, but its reply could not be relayed here. Check the Codex window.', 'warning');\r\n }\r\n // The daemon emits idle just before turn/completed; unsubscribing at once could cut off the reply.\r\n this.releaseLater(t);\r\n }\r\n\r\n private collect(threadId: string, turnId: string, item: AgentMessage): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n const list = t.turns.get(turnId) ?? [];\r\n list.push({ text: item.text, phase: item.phase });\r\n t.turns.set(turnId, list);\r\n }\r\n\r\n private onTurnCompleted(\r\n threadId: string,\r\n turn: { id: string; status: string; error?: { message: string } | null; items?: Array<{ type: string } & AgentMessage> },\r\n ): void {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n const collected = t.turns.get(turn.id) ?? [];\r\n t.turns.delete(turn.id);\r\n if (t.thread.status.type !== 'active') void this.want(threadId, false);\r\n if (turn.status === 'failed' || turn.error) {\r\n this.notify(threadId, 'Codex task failed', turn.error?.message ?? 'The task ended with an error.', 'error');\r\n return;\r\n }\r\n if (turn.status === 'interrupted') {\r\n this.notify(threadId, 'Codex task stopped', 'The task was interrupted.', 'info');\r\n return;\r\n }\r\n // turn/completed may carry only a summary of the items, so prefer what was collected live.\r\n const fromTurn = (turn.items ?? []).filter((i) => i.type === 'agentMessage');\r\n const text = finalAnswer(collected.length ? collected : fromTurn);\r\n if (text) t.hub.send({ type: 'reply', sessionId: codexSessionId(threadId), content: text });\r\n }\r\n\r\n private onHubMessage(threadId: string, msg: ChannelMessage): void {\r\n if (msg.type === 'message_to_session') {\r\n void this.sendTurn(threadId, msg.content, msg.source);\r\n } else if (msg.type === 'image_to_session') {\r\n this.notify(threadId, 'Not delivered', 'Codex sessions do not accept images yet.', 'warning');\r\n }\r\n }\r\n\r\n private async sendTurn(threadId: string, content: string, source?: MessageSource): Promise<void> {\r\n const t = this.threads.get(threadId);\r\n if (!t) return;\r\n if (t.thread.status.type !== 'idle' || t.pendingTurn) {\r\n this.notify(threadId, 'Not delivered', 'Codex is busy, so the message was not delivered. Send it again when the task finishes.', 'warning');\r\n return;\r\n }\r\n t.pendingTurn = true;\r\n try {\r\n if (!this.rpc) throw new Error('not connected to the Codex daemon');\r\n await this.want(threadId, true);\r\n // A new conversation cannot be subscribed before its first turn (no rollout found); the active broadcast retries it.\r\n t.unrelayed = !t.subscribed;\r\n await this.rpc.request('turn/start', { threadId, input: [{ type: 'text', text: withSourcePrefix(content, source) }] });\r\n } catch (err) {\r\n t.pendingTurn = false;\r\n t.unrelayed = false;\r\n this.releaseLater(t);\r\n this.notify(threadId, 'Not delivered', `Codex rejected the message: ${(err as Error).message}`, 'warning');\r\n }\r\n }\r\n\r\n private onServerRequest(method: string, params: any): void {\r\n if (!APPROVAL_REQUESTS.has(method) || !params?.threadId) {\r\n logger.debug(`Ignoring Codex server request ${method}`);\r\n return;\r\n }\r\n const detail = params.commandActions?.[0]?.command ?? params.command ?? params.reason ?? params.message ?? method;\r\n this.notify(params.threadId, 'Codex approval needed', `Approve or decline in Codex: ${String(detail).slice(0, 300)}`, 'warning');\r\n }\r\n\r\n private notify(threadId: string, title: string, message: string, level: NotifyLevel): void {\r\n this.threads.get(threadId)?.hub.send({ type: 'notify', sessionId: codexSessionId(threadId), title, message, level });\r\n }\r\n}\r\n"],"mappings":"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","names":["fs","path","config","config","fs","fs","token","fs","path","WebSocket","path","fs","WebSocket","existing","host","port","token","fs"]}
|