import { spawn } from "node:child_process"; import path from "node:path"; function parseBoolean(value) { return String(value || "").trim().toLowerCase() === "true"; } function parseArgs(value) { return String(value || "") .trim() .split(/\s+/) .filter(Boolean); } function parseTimeoutMs(value) { const parsed = Number.parseInt(String(value || ""), 10); return Number.isFinite(parsed) && parsed > 0 ? parsed : 3000; } function parseNonNegativeInteger(value, fallback) { const parsed = Number.parseInt(String(value ?? ""), 10); return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback; } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function trimToDefined(value) { const trimmed = String(value ?? "").trim(); return trimmed ? trimmed : undefined; } function pickConfigValue(config, key, fallback) { if (config && config[key] !== undefined && config[key] !== null && `${config[key]}`.trim() !== "") { return config[key]; } return fallback; } function resolveCommandArgs(command, args, cwd) { const runtimeName = path.basename(command || "").toLowerCase(); const scriptRuntimes = new Set([ "node", "node.exe", "tsx", "tsx.cmd", "bun", "bun.exe", "deno", "deno.exe", ]); if (!scriptRuntimes.has(runtimeName) || args.length === 0) { return args; } const [first, ...rest] = args; if (!first || first.startsWith("-")) { return args; } return [path.isAbsolute(first) ? first : path.resolve(cwd || process.cwd(), first), ...rest]; } function parseJsonLine(rawOutput) { const lines = String(rawOutput || "") .trim() .split(/\r?\n/) .map((line) => line.trim()) .filter(Boolean); return JSON.parse(lines.at(-1) || ""); } export function getCodexDesktopRefreshBridgeConfig(env = process.env, config = {}) { const enabled = parseBoolean( pickConfigValue(config, "codexDesktopRefreshEnabled", env.BOSS_CODEX_DESKTOP_REFRESH_ENABLED), ); const command = trimToDefined( pickConfigValue(config, "codexDesktopRefreshCommand", env.BOSS_CODEX_DESKTOP_REFRESH_COMMAND), ) || undefined; const endpoint = trimToDefined( pickConfigValue(config, "codexDesktopRefreshEndpoint", env.BOSS_CODEX_DESKTOP_REFRESH_ENDPOINT), ) || undefined; const args = Array.isArray(config?.codexDesktopRefreshArgs) ? config.codexDesktopRefreshArgs.map((item) => String(item)).filter(Boolean) : parseArgs(pickConfigValue(config, "codexDesktopRefreshArgs", env.BOSS_CODEX_DESKTOP_REFRESH_ARGS)); const cwd = trimToDefined( pickConfigValue(config, "codexDesktopRefreshWorkdir", env.BOSS_CODEX_DESKTOP_REFRESH_WORKDIR), ) || undefined; const timeoutMs = parseTimeoutMs( pickConfigValue(config, "codexDesktopRefreshTimeoutMs", env.BOSS_CODEX_DESKTOP_REFRESH_TIMEOUT_MS), ); const appName = trimToDefined(pickConfigValue(config, "codexDesktopRefreshAppName", env.BOSS_CODEX_DESKTOP_APP_NAME)) || "Codex"; const refreshMode = trimToDefined( pickConfigValue(config, "codexDesktopRefreshMode", env.BOSS_CODEX_DESKTOP_REFRESH_MODE), ) || "deeplink-reload"; const retryCount = parseNonNegativeInteger( pickConfigValue(config, "codexDesktopRefreshRetryCount", env.BOSS_CODEX_DESKTOP_REFRESH_RETRY_COUNT), 2, ); const retryDelayMs = parseNonNegativeInteger( pickConfigValue(config, "codexDesktopRefreshRetryDelayMs", env.BOSS_CODEX_DESKTOP_REFRESH_RETRY_DELAY_MS), 120, ); return { enabled, endpoint, command, args, cwd, timeoutMs, appName, refreshMode, retryCount, retryDelayMs, }; } function buildCodexDesktopRefreshPayload(config, mirrorHint) { if (!config?.enabled) { throw new Error("CODEX_DESKTOP_REFRESH_DISABLED"); } const targetThreadRef = trimToDefined(mirrorHint?.targetThreadRef); const sourceMessageId = trimToDefined(mirrorHint?.sourceMessageId); if (!targetThreadRef || !sourceMessageId) { throw new Error("CODEX_DESKTOP_REFRESH_HINT_REQUIRED"); } return { kind: "codex_desktop_refresh_hint", targetThreadRef, sourceMessageId, rolloutPath: trimToDefined(mirrorHint?.rolloutPath), threadTouchStatus: trimToDefined(mirrorHint?.threadTouchStatus), appName: trimToDefined(config.appName) || "Codex", refreshMode: trimToDefined(config.refreshMode) || "deeplink-reload", requestedAt: new Date().toISOString(), }; } export function buildCodexDesktopRefreshExecution(config, mirrorHint) { if (!config?.enabled) { throw new Error("CODEX_DESKTOP_REFRESH_DISABLED"); } if (!config?.command) { throw new Error("CODEX_DESKTOP_REFRESH_COMMAND_REQUIRED"); } const cwd = config.cwd || process.cwd(); return { command: config.command, args: resolveCommandArgs(config.command, config.args || [], cwd), cwd, timeoutMs: config.timeoutMs || 3000, stdinPayload: buildCodexDesktopRefreshPayload(config, mirrorHint), }; } export function parseCodexDesktopRefreshResult(rawOutput) { const parsed = parseJsonLine(rawOutput); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error("INVALID_CODEX_DESKTOP_REFRESH_PAYLOAD"); } const attemptCount = parseNonNegativeInteger(parsed.attemptCount, undefined); const baseResult = { targetThreadRef: trimToDefined(parsed.targetThreadRef), appName: trimToDefined(parsed.appName), deepLink: trimToDefined(parsed.deepLink), attemptCount, }; if (parsed.status === "failed") { return { status: "failed", ...baseResult, detail: trimToDefined(parsed.error) || "CODEX_DESKTOP_REFRESH_FAILED", }; } if (parsed.status !== "completed" && parsed.status !== "skipped") { throw new Error("INVALID_CODEX_DESKTOP_REFRESH_PAYLOAD"); } return { status: parsed.status, ...baseResult, detail: trimToDefined(parsed.detail), }; } function compactUndefinedFields(result) { return Object.fromEntries(Object.entries(result).filter(([, value]) => value !== undefined)); } function attachBridgeAttemptCount(result, attemptIndex) { if (result.attemptCount !== undefined || attemptIndex > 1) { return compactUndefinedFields({ ...result, attemptCount: result.attemptCount ?? attemptIndex, }); } return compactUndefinedFields(result); } function runCodexDesktopRefreshExecution(execution) { return new Promise((resolve, reject) => { const child = spawn(execution.command, execution.args, { cwd: execution.cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"], }); let stdout = ""; let stderr = ""; let timedOut = false; const timer = setTimeout(() => { timedOut = true; child.kill("SIGKILL"); }, execution.timeoutMs); child.stdout.setEncoding("utf8"); child.stderr.setEncoding("utf8"); child.stdout.on("data", (chunk) => { stdout += chunk; }); child.stderr.on("data", (chunk) => { stderr += chunk; }); child.on("error", (error) => { clearTimeout(timer); reject(error); }); child.on("close", (code) => { clearTimeout(timer); if (timedOut) { reject(new Error("CODEX_DESKTOP_REFRESH_TIMEOUT")); return; } if (code !== 0) { reject(new Error(stderr.trim() || `codex desktop refresh exit code ${code}`)); return; } try { resolve(parseCodexDesktopRefreshResult(stdout)); } catch (error) { reject(error); } }); child.stdin.write(JSON.stringify(execution.stdinPayload)); child.stdin.end(); }); } async function runCodexDesktopRefreshEndpoint(config, payload) { const controller = new AbortController(); const timer = setTimeout(() => { controller.abort(); }, config.timeoutMs || 3000); try { const response = await fetch(config.endpoint, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), signal: controller.signal, }); const body = await response.text(); if (!response.ok) { throw new Error(body.trim() || `codex desktop refresh endpoint status ${response.status}`); } return parseCodexDesktopRefreshResult(body); } finally { clearTimeout(timer); } } async function executeWithRetries(operation, runnerConfig) { const maxAttempts = Math.max(1, parseNonNegativeInteger(runnerConfig.retryCount, 2) + 1); const retryDelayMs = parseNonNegativeInteger(runnerConfig.retryDelayMs, 120); let lastError; let lastFailedResult; for (let attemptIndex = 1; attemptIndex <= maxAttempts; attemptIndex += 1) { try { const result = attachBridgeAttemptCount(await operation(), attemptIndex); if (result.status !== "failed") { return result; } lastFailedResult = result; } catch (error) { lastError = error; } if (attemptIndex < maxAttempts) { await sleep(retryDelayMs); } } if (lastFailedResult) { return attachBridgeAttemptCount(lastFailedResult, maxAttempts); } const message = lastError instanceof Error ? lastError.message : String(lastError || "CODEX_DESKTOP_REFRESH_FAILED"); throw new Error(`${message}; attempts=${maxAttempts}`); } export async function executeCodexDesktopRefreshBridge(mirrorHint, config = {}) { const runnerConfig = config && Object.prototype.hasOwnProperty.call(config, "enabled") ? config : getCodexDesktopRefreshBridgeConfig(process.env, config); if (!runnerConfig.enabled) { return { status: "skipped", reason: "disabled", }; } if (runnerConfig.endpoint) { const endpointPayload = buildCodexDesktopRefreshPayload(runnerConfig, mirrorHint); try { return await executeWithRetries(() => runCodexDesktopRefreshEndpoint(runnerConfig, endpointPayload), runnerConfig); } catch (error) { if (!runnerConfig.command) { throw error; } } } const execution = buildCodexDesktopRefreshExecution(runnerConfig, mirrorHint); return executeWithRetries(() => runCodexDesktopRefreshExecution(execution), runnerConfig); }