#!/usr/bin/env node import { fileURLToPath } from "node:url"; import path from "node:path"; import { executeCodexAppServerTask, getCodexAppServerRunnerConfig, } from "../local-agent/codex-app-server-runner.mjs"; const DEFAULT_TIMEOUT_MS = 120_000; function normalizeText(value) { return String(value || "").trim(); } function parseArgs(value) { const args = String(value || "") .trim() .split(/\s+/) .filter(Boolean); return args.length > 0 ? args : undefined; } function parseArgsJson(value) { const raw = normalizeText(value); if (!raw) return undefined; try { const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed.map((item) => String(item)).filter(Boolean) : undefined; } catch { return undefined; } } function parseTimeoutMs(value) { const parsed = Number.parseInt(String(value || ""), 10); return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS; } function writeJson(payload) { process.stdout.write(`${JSON.stringify(payload)}\n`); } async function readStdin() { const chunks = []; for await (const chunk of process.stdin) { chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8")); } return chunks.join("").trim(); } function parseJsonPayload(raw) { try { const parsed = JSON.parse(String(raw || "{}")); return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}; } catch { return {}; } } function detectTargetApp(objective) { const text = normalizeText(objective).toLowerCase(); const candidates = [ ["Codex", ["codex"]], ["Google Chrome", ["chrome", "google chrome", "谷歌"]], ["Safari", ["safari"]], ["QQ", ["qq"]], ["微信", ["微信", "wechat"]], ["飞书", ["飞书", "lark", "feishu"]], ["Telegram", ["telegram", "tg"]], ["Finder", ["finder", "访达"]], ["系统设置", ["系统设置", "system settings", "settings"]], ]; const matched = candidates.find(([, aliases]) => aliases.some((alias) => text.includes(alias))); return matched?.[0]; } function buildComputerUsePrompt(payload) { const objective = normalizeText(payload.objective); const targetApp = detectTargetApp(objective); return [ "你是 Boss 的 Codex Computer Use 执行器,正在被要求控制当前这台 macOS 电脑。", "请优先使用 Codex 自带的 Computer Use / Browser / Desktop 能力完成用户目标。", "只执行当前目标直接需要的动作;不要扩展需求,不要改动无关文件。", "遇到发送、提交、删除、支付、授权等高风险动作时,必须停下来要求用户确认,不要静默点击。", targetApp ? `目标应用:${targetApp}` : "目标应用:如果用户没有明确说明,请先根据目标判断最小必要应用。", `用户目标:${objective}`, "完成后用中文返回简短小结,说明已做的动作、结果和是否需要用户下一步确认。", ] .filter(Boolean) .join("\n"); } function buildRunnerConfig(env, payload) { const cwd = normalizeText(env.BOSS_CODEX_COMPUTER_USE_WORKDIR) || normalizeText(payload?.context?.projectCwd) || process.cwd(); return getCodexAppServerRunnerConfig(env, { codexAppServerEnabled: true, codexAppServerCommand: normalizeText(env.BOSS_CODEX_COMPUTER_USE_CODEX_COMMAND) || "codex", codexAppServerArgs: parseArgsJson(env.BOSS_CODEX_COMPUTER_USE_CODEX_ARGS_JSON) ?? parseArgs(env.BOSS_CODEX_COMPUTER_USE_CODEX_ARGS) ?? ["app-server"], codexAppServerWorkdir: cwd, codexAppServerTimeoutMs: parseTimeoutMs(env.BOSS_CODEX_COMPUTER_USE_TIMEOUT_MS), codexAppServerClientName: "boss_codex_computer_use", codexAppServerClientTitle: "Boss Codex Computer Use", codexAppServerClientVersion: "0.1.0", masterAgentWorkdir: cwd, masterAgentModel: normalizeText(env.BOSS_CODEX_COMPUTER_USE_MODEL), }); } export async function runCodexComputerUseTask(payload, options = {}) { const env = options.env || process.env; const requestId = normalizeText(payload?.requestId); const objective = normalizeText(payload?.objective); if (!objective) { return { status: "failed", requestId: requestId || undefined, error: "CODEX_COMPUTER_USE_OBJECTIVE_REQUIRED", computerUseProvider: "codex-computer-use", }; } const runnerConfig = buildRunnerConfig(env, payload); const result = await executeCodexAppServerTask(runnerConfig, { taskId: requestId || "codex-computer-use", taskType: "conversation_reply", targetCodexThreadRef: normalizeText(payload?.context?.codexComputerUseThreadId), targetCodexFolderRef: runnerConfig.cwd, executionPrompt: buildComputerUsePrompt(payload), }); if (result.status !== "completed") { return { status: "failed", requestId: requestId || undefined, error: result.errorMessage || "CODEX_COMPUTER_USE_FAILED", detail: result.stderr, computerUseProvider: "codex-computer-use", }; } return { status: "completed", requestId: requestId || undefined, replyBody: result.replyBody || "Codex Computer Use 已完成本轮桌面控制任务。", targetApp: detectTargetApp(objective), executionSummary: "codex app-server computer use", computerUseProvider: "codex-computer-use", }; } async function main() { const raw = await readStdin(); const payload = parseJsonPayload(raw); const result = await runCodexComputerUseTask(payload, { env: process.env, cwd: process.cwd(), }); writeJson(result); } const currentFile = fileURLToPath(import.meta.url); if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(currentFile)) { main().catch((error) => { writeJson({ status: "failed", error: error instanceof Error ? error.message : String(error), computerUseProvider: "codex-computer-use", }); process.exitCode = 1; }); }